Integrate crabrl parser into taxonomy hydration
This commit is contained in:
@@ -1,120 +1,146 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { __filingTaxonomyInternals } from './filing-taxonomy';
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { __filingTaxonomyInternals } from "./filing-taxonomy";
|
||||
|
||||
describe('filing taxonomy snapshot normalization', () => {
|
||||
it('normalizes legacy snake_case nested snapshot payloads in toSnapshotRecord', () => {
|
||||
describe("filing taxonomy snapshot normalization", () => {
|
||||
it("normalizes legacy snake_case nested snapshot payloads in toSnapshotRecord", () => {
|
||||
const record = __filingTaxonomyInternals.toSnapshotRecord({
|
||||
id: 1,
|
||||
filing_id: 10,
|
||||
ticker: 'MSFT',
|
||||
filing_date: '2026-01-28',
|
||||
filing_type: '10-Q',
|
||||
parse_status: 'ready',
|
||||
ticker: "MSFT",
|
||||
filing_date: "2026-01-28",
|
||||
filing_type: "10-Q",
|
||||
parse_status: "ready",
|
||||
parse_error: null,
|
||||
source: 'xbrl_instance',
|
||||
parser_engine: 'fiscal-xbrl',
|
||||
parser_version: '0.1.0',
|
||||
taxonomy_regime: 'us-gaap',
|
||||
fiscal_pack: 'core',
|
||||
periods: [{
|
||||
id: 'fy-2025',
|
||||
filing_id: 10,
|
||||
accession_number: '0001',
|
||||
filing_date: '2026-01-28',
|
||||
period_start: '2025-01-01',
|
||||
period_end: '2025-12-31',
|
||||
filing_type: '10-Q',
|
||||
period_label: 'FY 2025'
|
||||
}],
|
||||
source: "xbrl_instance",
|
||||
parser_engine: "fiscal-xbrl",
|
||||
parser_version: "0.1.0",
|
||||
taxonomy_regime: "us-gaap",
|
||||
fiscal_pack: "core",
|
||||
periods: [
|
||||
{
|
||||
id: "fy-2025",
|
||||
filing_id: 10,
|
||||
accession_number: "0001",
|
||||
filing_date: "2026-01-28",
|
||||
period_start: "2025-01-01",
|
||||
period_end: "2025-12-31",
|
||||
filing_type: "10-Q",
|
||||
period_label: "FY 2025",
|
||||
},
|
||||
],
|
||||
faithful_rows: {
|
||||
income: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
concept_key: 'us-gaap:Revenue',
|
||||
qname: 'us-gaap:Revenue',
|
||||
namespace_uri: 'http://fasb.org/us-gaap/2025',
|
||||
local_name: 'Revenue',
|
||||
is_extension: false,
|
||||
statement: 'income',
|
||||
role_uri: 'income',
|
||||
order: 10,
|
||||
depth: 0,
|
||||
parent_key: null,
|
||||
values: { 'fy-2025': 10 },
|
||||
units: { 'fy-2025': 'iso4217:USD' },
|
||||
has_dimensions: false,
|
||||
source_fact_ids: [1]
|
||||
}],
|
||||
income: [
|
||||
{
|
||||
key: "revenue",
|
||||
label: "Revenue",
|
||||
concept_key: "us-gaap:Revenue",
|
||||
qname: "us-gaap:Revenue",
|
||||
namespace_uri: "http://fasb.org/us-gaap/2025",
|
||||
local_name: "Revenue",
|
||||
is_extension: false,
|
||||
statement: "income",
|
||||
role_uri: "income",
|
||||
order: 10,
|
||||
depth: 0,
|
||||
parent_key: null,
|
||||
values: { "fy-2025": 10 },
|
||||
units: { "fy-2025": "iso4217:USD" },
|
||||
has_dimensions: false,
|
||||
source_fact_ids: [1],
|
||||
},
|
||||
],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
comprehensive_income: [],
|
||||
},
|
||||
statement_rows: null,
|
||||
surface_rows: {
|
||||
income: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
category: 'revenue',
|
||||
template_section: 'revenue',
|
||||
order: 10,
|
||||
unit: 'currency',
|
||||
values: { 'fy-2025': 10 },
|
||||
source_concepts: ['us-gaap:Revenue'],
|
||||
source_row_keys: ['revenue'],
|
||||
source_fact_ids: [1],
|
||||
formula_key: null,
|
||||
has_dimensions: false,
|
||||
resolved_source_row_keys: { 'fy-2025': 'revenue' },
|
||||
statement: 'income',
|
||||
detail_count: 1,
|
||||
resolution_method: 'direct',
|
||||
confidence: 'high',
|
||||
warning_codes: ['legacy_surface']
|
||||
}],
|
||||
income: [
|
||||
{
|
||||
key: "revenue",
|
||||
label: "Revenue",
|
||||
category: "revenue",
|
||||
template_section: "revenue",
|
||||
order: 10,
|
||||
unit: "currency",
|
||||
values: { "fy-2025": 10 },
|
||||
source_concepts: ["us-gaap:Revenue"],
|
||||
source_row_keys: ["revenue"],
|
||||
source_fact_ids: [1],
|
||||
formula_key: null,
|
||||
has_dimensions: false,
|
||||
resolved_source_row_keys: { "fy-2025": "revenue" },
|
||||
statement: "income",
|
||||
detail_count: 1,
|
||||
resolution_method: "direct",
|
||||
confidence: "high",
|
||||
warning_codes: ["legacy_surface"],
|
||||
},
|
||||
],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
comprehensive_income: [],
|
||||
},
|
||||
detail_rows: {
|
||||
income: {
|
||||
revenue: [{
|
||||
key: 'revenue_detail',
|
||||
parent_surface_key: 'revenue',
|
||||
label: 'Revenue Detail',
|
||||
concept_key: 'us-gaap:RevenueDetail',
|
||||
qname: 'us-gaap:RevenueDetail',
|
||||
namespace_uri: 'http://fasb.org/us-gaap/2025',
|
||||
local_name: 'RevenueDetail',
|
||||
unit: 'iso4217:USD',
|
||||
values: { 'fy-2025': 10 },
|
||||
source_fact_ids: [2],
|
||||
is_extension: false,
|
||||
dimensions_summary: ['region:americas'],
|
||||
residual_flag: false
|
||||
}]
|
||||
revenue: [
|
||||
{
|
||||
key: "revenue_detail",
|
||||
parent_surface_key: "revenue",
|
||||
label: "Revenue Detail",
|
||||
concept_key: "us-gaap:RevenueDetail",
|
||||
qname: "us-gaap:RevenueDetail",
|
||||
namespace_uri: "http://fasb.org/us-gaap/2025",
|
||||
local_name: "RevenueDetail",
|
||||
unit: "iso4217:USD",
|
||||
values: { "fy-2025": 10 },
|
||||
source_fact_ids: [2],
|
||||
is_extension: false,
|
||||
dimensions_summary: ["region:americas"],
|
||||
residual_flag: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
balance: {},
|
||||
cash_flow: {},
|
||||
equity: {},
|
||||
comprehensive_income: {}
|
||||
comprehensive_income: {},
|
||||
},
|
||||
kpi_rows: [{
|
||||
key: 'cloud_growth',
|
||||
label: 'Cloud Growth',
|
||||
category: 'operating_kpi',
|
||||
unit: 'percent',
|
||||
order: 10,
|
||||
segment: null,
|
||||
axis: null,
|
||||
member: null,
|
||||
values: { 'fy-2025': 0.25 },
|
||||
source_concepts: ['msft:CloudGrowth'],
|
||||
source_fact_ids: [3],
|
||||
provenance_type: 'taxonomy',
|
||||
has_dimensions: false
|
||||
}],
|
||||
kpi_rows: [
|
||||
{
|
||||
key: "cloud_growth",
|
||||
label: "Cloud Growth",
|
||||
category: "operating_kpi",
|
||||
unit: "percent",
|
||||
order: 10,
|
||||
segment: null,
|
||||
axis: null,
|
||||
member: null,
|
||||
values: { "fy-2025": 0.25 },
|
||||
source_concepts: ["msft:CloudGrowth"],
|
||||
source_fact_ids: [3],
|
||||
provenance_type: "taxonomy",
|
||||
has_dimensions: false,
|
||||
},
|
||||
],
|
||||
computed_definitions: [
|
||||
{
|
||||
key: "gross_margin",
|
||||
label: "Gross Margin",
|
||||
category: "margins",
|
||||
order: 10,
|
||||
unit: "percent",
|
||||
computation: {
|
||||
type: "ratio",
|
||||
numerator: "gross_profit",
|
||||
denominator: "revenue",
|
||||
},
|
||||
supported_cadences: ["annual", "quarterly"],
|
||||
requires_external_data: [],
|
||||
},
|
||||
],
|
||||
derived_metrics: null,
|
||||
validation_result: null,
|
||||
normalization_summary: {
|
||||
@@ -123,169 +149,228 @@ describe('filing taxonomy snapshot normalization', () => {
|
||||
kpi_row_count: 1,
|
||||
unmapped_row_count: 0,
|
||||
material_unmapped_row_count: 0,
|
||||
warnings: ['legacy_warning']
|
||||
warnings: ["legacy_warning"],
|
||||
},
|
||||
facts_count: 3,
|
||||
concepts_count: 3,
|
||||
dimensions_count: 1,
|
||||
created_at: '2026-01-28T00:00:00.000Z',
|
||||
updated_at: '2026-01-28T00:00:00.000Z'
|
||||
created_at: "2026-01-28T00:00:00.000Z",
|
||||
updated_at: "2026-01-28T00:00:00.000Z",
|
||||
} as never);
|
||||
|
||||
expect(record.periods[0]).toMatchObject({
|
||||
filingId: 10,
|
||||
accessionNumber: '0001',
|
||||
filingDate: '2026-01-28',
|
||||
periodStart: '2025-01-01',
|
||||
periodEnd: '2025-12-31',
|
||||
periodLabel: 'FY 2025'
|
||||
accessionNumber: "0001",
|
||||
filingDate: "2026-01-28",
|
||||
periodStart: "2025-01-01",
|
||||
periodEnd: "2025-12-31",
|
||||
periodLabel: "FY 2025",
|
||||
});
|
||||
expect(record.faithful_rows.income[0]).toMatchObject({
|
||||
conceptKey: 'us-gaap:Revenue',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2025',
|
||||
localName: 'Revenue',
|
||||
roleUri: 'income',
|
||||
conceptKey: "us-gaap:Revenue",
|
||||
namespaceUri: "http://fasb.org/us-gaap/2025",
|
||||
localName: "Revenue",
|
||||
roleUri: "income",
|
||||
parentKey: null,
|
||||
hasDimensions: false,
|
||||
sourceFactIds: [1]
|
||||
sourceFactIds: [1],
|
||||
});
|
||||
expect(record.surface_rows.income[0]).toMatchObject({
|
||||
templateSection: 'revenue',
|
||||
sourceConcepts: ['us-gaap:Revenue'],
|
||||
sourceRowKeys: ['revenue'],
|
||||
templateSection: "revenue",
|
||||
sourceConcepts: ["us-gaap:Revenue"],
|
||||
sourceRowKeys: ["revenue"],
|
||||
sourceFactIds: [1],
|
||||
formulaKey: null,
|
||||
hasDimensions: false,
|
||||
resolvedSourceRowKeys: { 'fy-2025': 'revenue' },
|
||||
resolvedSourceRowKeys: { "fy-2025": "revenue" },
|
||||
detailCount: 1,
|
||||
resolutionMethod: 'direct',
|
||||
warningCodes: ['legacy_surface']
|
||||
resolutionMethod: "direct",
|
||||
warningCodes: ["legacy_surface"],
|
||||
});
|
||||
expect(record.detail_rows.income.revenue?.[0]).toMatchObject({
|
||||
parentSurfaceKey: 'revenue',
|
||||
conceptKey: 'us-gaap:RevenueDetail',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2025',
|
||||
parentSurfaceKey: "revenue",
|
||||
conceptKey: "us-gaap:RevenueDetail",
|
||||
namespaceUri: "http://fasb.org/us-gaap/2025",
|
||||
sourceFactIds: [2],
|
||||
dimensionsSummary: ['region:americas'],
|
||||
residualFlag: false
|
||||
dimensionsSummary: ["region:americas"],
|
||||
residualFlag: false,
|
||||
});
|
||||
expect(record.kpi_rows[0]).toMatchObject({
|
||||
sourceConcepts: ['msft:CloudGrowth'],
|
||||
sourceConcepts: ["msft:CloudGrowth"],
|
||||
sourceFactIds: [3],
|
||||
provenanceType: 'taxonomy',
|
||||
hasDimensions: false
|
||||
provenanceType: "taxonomy",
|
||||
hasDimensions: false,
|
||||
});
|
||||
expect(record.computed_definitions).toEqual([
|
||||
{
|
||||
key: "gross_margin",
|
||||
label: "Gross Margin",
|
||||
category: "margins",
|
||||
order: 10,
|
||||
unit: "percent",
|
||||
computation: {
|
||||
type: "ratio",
|
||||
numerator: "gross_profit",
|
||||
denominator: "revenue",
|
||||
},
|
||||
supported_cadences: ["annual", "quarterly"],
|
||||
requires_external_data: [],
|
||||
},
|
||||
]);
|
||||
expect(record.normalization_summary).toEqual({
|
||||
surfaceRowCount: 1,
|
||||
detailRowCount: 1,
|
||||
kpiRowCount: 1,
|
||||
unmappedRowCount: 0,
|
||||
materialUnmappedRowCount: 0,
|
||||
warnings: ['legacy_warning']
|
||||
warnings: ["legacy_warning"],
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps mixed camelCase and snake_case payloads compatible', () => {
|
||||
const normalized = __filingTaxonomyInternals.normalizeFilingTaxonomySnapshotPayload({
|
||||
periods: [{
|
||||
id: 'fy-2025',
|
||||
filingId: 10,
|
||||
accessionNumber: '0001',
|
||||
filingDate: '2026-01-28',
|
||||
periodStart: '2025-01-01',
|
||||
periodEnd: '2025-12-31',
|
||||
filingType: '10-K',
|
||||
periodLabel: 'FY 2025'
|
||||
}],
|
||||
faithful_rows: {
|
||||
income: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
conceptKey: 'us-gaap:Revenue',
|
||||
qname: 'us-gaap:Revenue',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2025',
|
||||
localName: 'Revenue',
|
||||
isExtension: false,
|
||||
statement: 'income',
|
||||
roleUri: 'income',
|
||||
order: 10,
|
||||
depth: 0,
|
||||
parentKey: null,
|
||||
values: { 'fy-2025': 10 },
|
||||
units: { 'fy-2025': 'iso4217:USD' },
|
||||
hasDimensions: false,
|
||||
sourceFactIds: [1]
|
||||
}],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
},
|
||||
statement_rows: null,
|
||||
surface_rows: {
|
||||
income: [{
|
||||
key: 'revenue',
|
||||
label: 'Revenue',
|
||||
category: 'revenue',
|
||||
order: 10,
|
||||
unit: 'currency',
|
||||
values: { 'fy-2025': 10 },
|
||||
source_concepts: ['us-gaap:Revenue'],
|
||||
source_row_keys: ['revenue'],
|
||||
source_fact_ids: [1],
|
||||
formula_key: null,
|
||||
has_dimensions: false,
|
||||
resolved_source_row_keys: { 'fy-2025': 'revenue' }
|
||||
}],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: []
|
||||
},
|
||||
detail_rows: {
|
||||
income: {
|
||||
revenue: [{
|
||||
key: 'revenue_detail',
|
||||
parentSurfaceKey: 'revenue',
|
||||
label: 'Revenue Detail',
|
||||
conceptKey: 'us-gaap:RevenueDetail',
|
||||
qname: 'us-gaap:RevenueDetail',
|
||||
namespaceUri: 'http://fasb.org/us-gaap/2025',
|
||||
localName: 'RevenueDetail',
|
||||
unit: 'iso4217:USD',
|
||||
values: { 'fy-2025': 10 },
|
||||
sourceFactIds: [2],
|
||||
isExtension: false,
|
||||
dimensionsSummary: [],
|
||||
residualFlag: false
|
||||
}]
|
||||
it("keeps mixed camelCase and snake_case payloads compatible", () => {
|
||||
const normalized =
|
||||
__filingTaxonomyInternals.normalizeFilingTaxonomySnapshotPayload({
|
||||
periods: [
|
||||
{
|
||||
id: "fy-2025",
|
||||
filingId: 10,
|
||||
accessionNumber: "0001",
|
||||
filingDate: "2026-01-28",
|
||||
periodStart: "2025-01-01",
|
||||
periodEnd: "2025-12-31",
|
||||
filingType: "10-K",
|
||||
periodLabel: "FY 2025",
|
||||
},
|
||||
],
|
||||
faithful_rows: {
|
||||
income: [
|
||||
{
|
||||
key: "revenue",
|
||||
label: "Revenue",
|
||||
conceptKey: "us-gaap:Revenue",
|
||||
qname: "us-gaap:Revenue",
|
||||
namespaceUri: "http://fasb.org/us-gaap/2025",
|
||||
localName: "Revenue",
|
||||
isExtension: false,
|
||||
statement: "income",
|
||||
roleUri: "income",
|
||||
order: 10,
|
||||
depth: 0,
|
||||
parentKey: null,
|
||||
values: { "fy-2025": 10 },
|
||||
units: { "fy-2025": "iso4217:USD" },
|
||||
hasDimensions: false,
|
||||
sourceFactIds: [1],
|
||||
},
|
||||
],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: [],
|
||||
},
|
||||
balance: {},
|
||||
cash_flow: {},
|
||||
equity: {},
|
||||
comprehensive_income: {}
|
||||
},
|
||||
kpi_rows: [],
|
||||
normalization_summary: {
|
||||
surfaceRowCount: 1,
|
||||
detail_row_count: 1,
|
||||
kpiRowCount: 0,
|
||||
unmapped_row_count: 0,
|
||||
materialUnmappedRowCount: 0,
|
||||
warnings: []
|
||||
}
|
||||
});
|
||||
statement_rows: null,
|
||||
surface_rows: {
|
||||
income: [
|
||||
{
|
||||
key: "revenue",
|
||||
label: "Revenue",
|
||||
category: "revenue",
|
||||
order: 10,
|
||||
unit: "currency",
|
||||
values: { "fy-2025": 10 },
|
||||
source_concepts: ["us-gaap:Revenue"],
|
||||
source_row_keys: ["revenue"],
|
||||
source_fact_ids: [1],
|
||||
formula_key: null,
|
||||
has_dimensions: false,
|
||||
resolved_source_row_keys: { "fy-2025": "revenue" },
|
||||
},
|
||||
],
|
||||
balance: [],
|
||||
cash_flow: [],
|
||||
equity: [],
|
||||
comprehensive_income: [],
|
||||
},
|
||||
detail_rows: {
|
||||
income: {
|
||||
revenue: [
|
||||
{
|
||||
key: "revenue_detail",
|
||||
parentSurfaceKey: "revenue",
|
||||
label: "Revenue Detail",
|
||||
conceptKey: "us-gaap:RevenueDetail",
|
||||
qname: "us-gaap:RevenueDetail",
|
||||
namespaceUri: "http://fasb.org/us-gaap/2025",
|
||||
localName: "RevenueDetail",
|
||||
unit: "iso4217:USD",
|
||||
values: { "fy-2025": 10 },
|
||||
sourceFactIds: [2],
|
||||
isExtension: false,
|
||||
dimensionsSummary: [],
|
||||
residualFlag: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
balance: {},
|
||||
cash_flow: {},
|
||||
equity: {},
|
||||
comprehensive_income: {},
|
||||
},
|
||||
kpi_rows: [],
|
||||
computed_definitions: [
|
||||
{
|
||||
key: "revenue_yoy",
|
||||
label: "Revenue YoY",
|
||||
category: "growth",
|
||||
order: 20,
|
||||
unit: "percent",
|
||||
computation: {
|
||||
type: "yoy_growth",
|
||||
source: "revenue",
|
||||
},
|
||||
supportedCadences: ["annual"],
|
||||
requiresExternalData: [],
|
||||
},
|
||||
],
|
||||
normalization_summary: {
|
||||
surfaceRowCount: 1,
|
||||
detail_row_count: 1,
|
||||
kpiRowCount: 0,
|
||||
unmapped_row_count: 0,
|
||||
materialUnmappedRowCount: 0,
|
||||
warnings: [],
|
||||
},
|
||||
});
|
||||
|
||||
expect(normalized.periods[0]?.filingId).toBe(10);
|
||||
expect(normalized.surface_rows.income[0]?.sourceConcepts).toEqual(['us-gaap:Revenue']);
|
||||
expect(normalized.detail_rows.income.revenue?.[0]?.parentSurfaceKey).toBe('revenue');
|
||||
expect(normalized.surface_rows.income[0]?.sourceConcepts).toEqual([
|
||||
"us-gaap:Revenue",
|
||||
]);
|
||||
expect(normalized.detail_rows.income.revenue?.[0]?.parentSurfaceKey).toBe(
|
||||
"revenue",
|
||||
);
|
||||
expect(normalized.normalization_summary).toEqual({
|
||||
surfaceRowCount: 1,
|
||||
detailRowCount: 1,
|
||||
kpiRowCount: 0,
|
||||
unmappedRowCount: 0,
|
||||
materialUnmappedRowCount: 0,
|
||||
warnings: []
|
||||
warnings: [],
|
||||
});
|
||||
expect(normalized.computed_definitions).toEqual([
|
||||
{
|
||||
key: "revenue_yoy",
|
||||
label: "Revenue YoY",
|
||||
category: "growth",
|
||||
order: 20,
|
||||
unit: "percent",
|
||||
computation: {
|
||||
type: "yoy_growth",
|
||||
source: "revenue",
|
||||
},
|
||||
supported_cadences: ["annual"],
|
||||
requires_external_data: [],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user