Add consolidated disclosure statement type

Create unified disclosure statement to organize footnote disclosures
separate from primary financial statements. Disclosures are now grouped
by type (tax, debt, securities, derivatives, leases, intangibles, ma,
revenue, cash_flow) in a dedicated statement type for cleaner UI
presentation.
This commit is contained in:
2026-03-16 18:54:23 -04:00
parent a58b07456e
commit 14a7773504
16 changed files with 5679 additions and 731 deletions

View File

@@ -8,7 +8,7 @@
## Project Snapshot
Neon Code is a comprehensive portoflio management and equity research platform. It helps analysts manage their portfolio, organize research and view individual stocks.
Neon Code is a comprehensive portfolio management and equity research platform. It helps analysts manage their portfolio, organize research and view individual stocks.
This repository is a VERY EARLY WIP. Proposing sweeping changes that improve long-term maintainability is encouraged.
@@ -22,4 +22,8 @@ If a tradeoff is required, choose correctness and robustness over short-term con
## Maintainability
Long term maintainability is a core priority. If you add new functionality, first check if there are shared logic that can be extracted to a separate module. Duplicate logic across mulitple files is a code smell and should be avoided. Don't be afraid to change existing code. Don't take shortcuts by just adding local logic to solve a problem.
Long term maintainability is a core priority. If you add new functionality, first check if there are shared logic that can be extracted to a separate module. Duplicate logic across multiple files is a code smell and should be avoided. Don't be afraid to change existing code. Don't take shortcuts by just adding local logic to solve a problem.
## Documentation
Documentation should be located in /doc folder. Make sure that the docs are up to date and ensure that robust architecture and strong coding patterns are propagated throughout.

View File

@@ -0,0 +1,361 @@
CREATE TABLE `company_financial_bundle` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`ticker` text NOT NULL,
`surface_kind` text NOT NULL,
`cadence` text NOT NULL,
`bundle_version` integer NOT NULL,
`source_snapshot_ids` text NOT NULL,
`source_signature` text NOT NULL,
`payload` text NOT NULL,
`created_at` text NOT NULL,
`updated_at` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `company_financial_bundle_uidx` ON `company_financial_bundle` (`ticker`,`surface_kind`,`cadence`);--> statement-breakpoint
CREATE INDEX `company_financial_bundle_ticker_idx` ON `company_financial_bundle` (`ticker`,`updated_at`);--> statement-breakpoint
CREATE TABLE `company_overview_cache` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`user_id` text NOT NULL,
`ticker` text NOT NULL,
`cache_version` integer NOT NULL,
`source_signature` text NOT NULL,
`payload` text NOT NULL,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `company_overview_cache_uidx` ON `company_overview_cache` (`user_id`,`ticker`);--> statement-breakpoint
CREATE INDEX `company_overview_cache_lookup_idx` ON `company_overview_cache` (`user_id`,`ticker`,`updated_at`);--> statement-breakpoint
CREATE TABLE `filing_statement_snapshot` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`filing_id` integer NOT NULL,
`ticker` text NOT NULL,
`filing_date` text NOT NULL,
`filing_type` text NOT NULL,
`period_end` text,
`statement_bundle` text,
`standardized_bundle` text,
`dimension_bundle` text,
`parse_status` text NOT NULL,
`parse_error` text,
`source` text NOT NULL,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`filing_id`) REFERENCES `filing`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `filing_stmt_filing_uidx` ON `filing_statement_snapshot` (`filing_id`);--> statement-breakpoint
CREATE INDEX `filing_stmt_ticker_date_idx` ON `filing_statement_snapshot` (`ticker`,`filing_date`);--> statement-breakpoint
CREATE INDEX `filing_stmt_date_idx` ON `filing_statement_snapshot` (`filing_date`);--> statement-breakpoint
CREATE INDEX `filing_stmt_status_idx` ON `filing_statement_snapshot` (`parse_status`);--> statement-breakpoint
CREATE TABLE `filing_taxonomy_asset` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`snapshot_id` integer NOT NULL,
`asset_type` text NOT NULL,
`name` text NOT NULL,
`url` text NOT NULL,
`size_bytes` integer,
`score` numeric,
`is_selected` integer DEFAULT false NOT NULL,
`created_at` text NOT NULL,
FOREIGN KEY (`snapshot_id`) REFERENCES `filing_taxonomy_snapshot`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `filing_taxonomy_asset_snapshot_idx` ON `filing_taxonomy_asset` (`snapshot_id`);--> statement-breakpoint
CREATE INDEX `filing_taxonomy_asset_type_idx` ON `filing_taxonomy_asset` (`snapshot_id`,`asset_type`);--> statement-breakpoint
CREATE TABLE `filing_taxonomy_concept` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`snapshot_id` integer NOT NULL,
`concept_key` text NOT NULL,
`qname` text NOT NULL,
`namespace_uri` text NOT NULL,
`local_name` text NOT NULL,
`label` text,
`is_extension` integer DEFAULT false NOT NULL,
`balance` text,
`period_type` text,
`data_type` text,
`statement_kind` text,
`role_uri` text,
`authoritative_concept_key` text,
`mapping_method` text,
`surface_key` text,
`detail_parent_surface_key` text,
`kpi_key` text,
`residual_flag` integer DEFAULT false NOT NULL,
`presentation_order` numeric,
`presentation_depth` integer,
`parent_concept_key` text,
`is_abstract` integer DEFAULT false NOT NULL,
`created_at` text NOT NULL,
FOREIGN KEY (`snapshot_id`) REFERENCES `filing_taxonomy_snapshot`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `filing_taxonomy_concept_snapshot_idx` ON `filing_taxonomy_concept` (`snapshot_id`);--> statement-breakpoint
CREATE INDEX `filing_taxonomy_concept_statement_idx` ON `filing_taxonomy_concept` (`snapshot_id`,`statement_kind`);--> statement-breakpoint
CREATE UNIQUE INDEX `filing_taxonomy_concept_uidx` ON `filing_taxonomy_concept` (`snapshot_id`,`concept_key`,`role_uri`,`presentation_order`);--> statement-breakpoint
CREATE TABLE `filing_taxonomy_context` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`snapshot_id` integer NOT NULL,
`context_id` text NOT NULL,
`entity_identifier` text,
`entity_scheme` text,
`period_start` text,
`period_end` text,
`period_instant` text,
`segment_json` text,
`scenario_json` text,
`created_at` text NOT NULL,
FOREIGN KEY (`snapshot_id`) REFERENCES `filing_taxonomy_snapshot`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `filing_taxonomy_context_snapshot_idx` ON `filing_taxonomy_context` (`snapshot_id`);--> statement-breakpoint
CREATE UNIQUE INDEX `filing_taxonomy_context_uidx` ON `filing_taxonomy_context` (`snapshot_id`,`context_id`);--> statement-breakpoint
CREATE TABLE `filing_taxonomy_fact` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`snapshot_id` integer NOT NULL,
`concept_key` text NOT NULL,
`qname` text NOT NULL,
`namespace_uri` text NOT NULL,
`local_name` text NOT NULL,
`data_type` text,
`statement_kind` text,
`role_uri` text,
`authoritative_concept_key` text,
`mapping_method` text,
`surface_key` text,
`detail_parent_surface_key` text,
`kpi_key` text,
`residual_flag` integer DEFAULT false NOT NULL,
`context_id` text NOT NULL,
`unit` text,
`decimals` text,
`precision` text,
`nil` integer DEFAULT false NOT NULL,
`value_num` numeric NOT NULL,
`period_start` text,
`period_end` text,
`period_instant` text,
`dimensions` text NOT NULL,
`is_dimensionless` integer DEFAULT true NOT NULL,
`source_file` text,
`created_at` text NOT NULL,
FOREIGN KEY (`snapshot_id`) REFERENCES `filing_taxonomy_snapshot`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `filing_taxonomy_fact_snapshot_idx` ON `filing_taxonomy_fact` (`snapshot_id`);--> statement-breakpoint
CREATE INDEX `filing_taxonomy_fact_concept_idx` ON `filing_taxonomy_fact` (`snapshot_id`,`concept_key`);--> statement-breakpoint
CREATE INDEX `filing_taxonomy_fact_period_idx` ON `filing_taxonomy_fact` (`snapshot_id`,`period_end`,`period_instant`);--> statement-breakpoint
CREATE INDEX `filing_taxonomy_fact_statement_idx` ON `filing_taxonomy_fact` (`snapshot_id`,`statement_kind`);--> statement-breakpoint
CREATE TABLE `filing_taxonomy_metric_validation` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`snapshot_id` integer NOT NULL,
`metric_key` text NOT NULL,
`taxonomy_value` numeric,
`llm_value` numeric,
`absolute_diff` numeric,
`relative_diff` numeric,
`status` text NOT NULL,
`evidence_pages` text NOT NULL,
`pdf_url` text,
`provider` text,
`model` text,
`error` text,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`snapshot_id`) REFERENCES `filing_taxonomy_snapshot`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `filing_taxonomy_metric_validation_snapshot_idx` ON `filing_taxonomy_metric_validation` (`snapshot_id`);--> statement-breakpoint
CREATE INDEX `filing_taxonomy_metric_validation_status_idx` ON `filing_taxonomy_metric_validation` (`snapshot_id`,`status`);--> statement-breakpoint
CREATE UNIQUE INDEX `filing_taxonomy_metric_validation_uidx` ON `filing_taxonomy_metric_validation` (`snapshot_id`,`metric_key`);--> statement-breakpoint
CREATE TABLE `filing_taxonomy_snapshot` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`filing_id` integer NOT NULL,
`ticker` text NOT NULL,
`filing_date` text NOT NULL,
`filing_type` text NOT NULL,
`parse_status` text NOT NULL,
`parse_error` text,
`source` text NOT NULL,
`parser_engine` text DEFAULT 'fiscal-xbrl' NOT NULL,
`parser_version` text DEFAULT 'unknown' NOT NULL,
`taxonomy_regime` text DEFAULT 'unknown' NOT NULL,
`fiscal_pack` text,
`periods` text,
`faithful_rows` text,
`statement_rows` text,
`surface_rows` text,
`detail_rows` text,
`kpi_rows` text,
`computed_definitions` text,
`derived_metrics` text,
`validation_result` text,
`normalization_summary` text,
`facts_count` integer DEFAULT 0 NOT NULL,
`concepts_count` integer DEFAULT 0 NOT NULL,
`dimensions_count` integer DEFAULT 0 NOT NULL,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`filing_id`) REFERENCES `filing`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `filing_taxonomy_snapshot_filing_uidx` ON `filing_taxonomy_snapshot` (`filing_id`);--> statement-breakpoint
CREATE INDEX `filing_taxonomy_snapshot_ticker_date_idx` ON `filing_taxonomy_snapshot` (`ticker`,`filing_date`);--> statement-breakpoint
CREATE INDEX `filing_taxonomy_snapshot_status_idx` ON `filing_taxonomy_snapshot` (`parse_status`);--> statement-breakpoint
CREATE TABLE `research_artifact` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`user_id` text NOT NULL,
`organization_id` text,
`ticker` text NOT NULL,
`accession_number` text,
`kind` text NOT NULL,
`source` text DEFAULT 'user' NOT NULL,
`subtype` text,
`title` text,
`summary` text,
`body_markdown` text,
`search_text` text,
`visibility_scope` text DEFAULT 'private' NOT NULL,
`tags` text,
`metadata` text,
`file_name` text,
`mime_type` text,
`file_size_bytes` integer,
`storage_path` text,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`organization_id`) REFERENCES `organization`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE INDEX `research_artifact_ticker_idx` ON `research_artifact` (`user_id`,`ticker`,`updated_at`);--> statement-breakpoint
CREATE INDEX `research_artifact_kind_idx` ON `research_artifact` (`user_id`,`kind`,`updated_at`);--> statement-breakpoint
CREATE INDEX `research_artifact_accession_idx` ON `research_artifact` (`user_id`,`accession_number`);--> statement-breakpoint
CREATE INDEX `research_artifact_source_idx` ON `research_artifact` (`user_id`,`source`,`updated_at`);--> statement-breakpoint
CREATE TABLE `research_journal_entry` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`user_id` text NOT NULL,
`ticker` text NOT NULL,
`accession_number` text,
`entry_type` text NOT NULL,
`title` text,
`body_markdown` text NOT NULL,
`metadata` text,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `research_journal_ticker_idx` ON `research_journal_entry` (`user_id`,`ticker`,`created_at`);--> statement-breakpoint
CREATE INDEX `research_journal_accession_idx` ON `research_journal_entry` (`user_id`,`accession_number`);--> statement-breakpoint
CREATE TABLE `research_memo` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`user_id` text NOT NULL,
`organization_id` text,
`ticker` text NOT NULL,
`rating` text,
`conviction` text,
`time_horizon_months` integer,
`packet_title` text,
`packet_subtitle` text,
`thesis_markdown` text DEFAULT '' NOT NULL,
`variant_view_markdown` text DEFAULT '' NOT NULL,
`catalysts_markdown` text DEFAULT '' NOT NULL,
`risks_markdown` text DEFAULT '' NOT NULL,
`disconfirming_evidence_markdown` text DEFAULT '' NOT NULL,
`next_actions_markdown` text DEFAULT '' NOT NULL,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`organization_id`) REFERENCES `organization`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE UNIQUE INDEX `research_memo_ticker_uidx` ON `research_memo` (`user_id`,`ticker`);--> statement-breakpoint
CREATE INDEX `research_memo_updated_idx` ON `research_memo` (`user_id`,`updated_at`);--> statement-breakpoint
CREATE TABLE `research_memo_evidence` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`memo_id` integer NOT NULL,
`artifact_id` integer NOT NULL,
`section` text NOT NULL,
`annotation` text,
`sort_order` integer DEFAULT 0 NOT NULL,
`created_at` text NOT NULL,
FOREIGN KEY (`memo_id`) REFERENCES `research_memo`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`artifact_id`) REFERENCES `research_artifact`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `research_memo_evidence_memo_idx` ON `research_memo_evidence` (`memo_id`,`section`,`sort_order`);--> statement-breakpoint
CREATE INDEX `research_memo_evidence_artifact_idx` ON `research_memo_evidence` (`artifact_id`);--> statement-breakpoint
CREATE UNIQUE INDEX `research_memo_evidence_unique_uidx` ON `research_memo_evidence` (`memo_id`,`artifact_id`,`section`);--> statement-breakpoint
CREATE TABLE `search_chunk` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`document_id` integer NOT NULL,
`chunk_index` integer NOT NULL,
`chunk_text` text NOT NULL,
`char_count` integer NOT NULL,
`start_offset` integer NOT NULL,
`end_offset` integer NOT NULL,
`heading_path` text,
`citation_label` text NOT NULL,
`created_at` text NOT NULL,
FOREIGN KEY (`document_id`) REFERENCES `search_document`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `search_chunk_document_chunk_uidx` ON `search_chunk` (`document_id`,`chunk_index`);--> statement-breakpoint
CREATE INDEX `search_chunk_document_idx` ON `search_chunk` (`document_id`);--> statement-breakpoint
CREATE TABLE `search_document` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`source_kind` text NOT NULL,
`source_ref` text NOT NULL,
`scope` text NOT NULL,
`user_id` text,
`ticker` text,
`accession_number` text,
`title` text,
`content_text` text NOT NULL,
`content_hash` text NOT NULL,
`metadata` text,
`index_status` text NOT NULL,
`indexed_at` text,
`last_error` text,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `search_document_source_uidx` ON `search_document` (`scope`,`ifnull("user_id"`,` '')`,`source_kind`,`source_ref`);--> statement-breakpoint
CREATE INDEX `search_document_scope_idx` ON `search_document` (`scope`,`source_kind`,`ticker`,`updated_at`);--> statement-breakpoint
CREATE INDEX `search_document_accession_idx` ON `search_document` (`accession_number`,`source_kind`);--> statement-breakpoint
CREATE TABLE `task_stage_event` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`task_id` text NOT NULL,
`user_id` text NOT NULL,
`stage` text NOT NULL,
`stage_detail` text,
`stage_context` text,
`status` text NOT NULL,
`created_at` text NOT NULL,
FOREIGN KEY (`task_id`) REFERENCES `task_run`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `task_stage_event_task_created_idx` ON `task_stage_event` (`task_id`,`created_at`);--> statement-breakpoint
CREATE INDEX `task_stage_event_user_created_idx` ON `task_stage_event` (`user_id`,`created_at`);--> statement-breakpoint
ALTER TABLE `holding` ADD `company_name` text;--> statement-breakpoint
ALTER TABLE `task_run` ADD `stage` text NOT NULL;--> statement-breakpoint
ALTER TABLE `task_run` ADD `stage_detail` text;--> statement-breakpoint
ALTER TABLE `task_run` ADD `stage_context` text;--> statement-breakpoint
ALTER TABLE `task_run` ADD `resource_key` text;--> statement-breakpoint
ALTER TABLE `task_run` ADD `notification_read_at` text;--> statement-breakpoint
ALTER TABLE `task_run` ADD `notification_silenced_at` text;--> statement-breakpoint
CREATE INDEX `task_user_updated_idx` ON `task_run` (`user_id`,`updated_at`);--> statement-breakpoint
CREATE INDEX `task_user_resource_status_idx` ON `task_run` (`user_id`,`task_type`,`resource_key`,`status`,`created_at`);--> statement-breakpoint
ALTER TABLE `watchlist_item` ADD `category` text;--> statement-breakpoint
ALTER TABLE `watchlist_item` ADD `tags` text;--> statement-breakpoint
ALTER TABLE `watchlist_item` ADD `status` text DEFAULT 'backlog' NOT NULL;--> statement-breakpoint
ALTER TABLE `watchlist_item` ADD `priority` text DEFAULT 'medium' NOT NULL;--> statement-breakpoint
ALTER TABLE `watchlist_item` ADD `updated_at` text NOT NULL;--> statement-breakpoint
ALTER TABLE `watchlist_item` ADD `last_reviewed_at` text;--> statement-breakpoint
CREATE INDEX `watchlist_user_updated_idx` ON `watchlist_item` (`user_id`,`updated_at`);

File diff suppressed because it is too large Load Diff

View File

@@ -85,6 +85,13 @@
"when": 1773180000000,
"tag": "0011_remove_legacy_xbrl_defaults",
"breakpoints": true
},
{
"idx": 12,
"version": "6",
"when": 1773695414874,
"tag": "0012_crazy_molecule_man",
"breakpoints": true
}
]
}

View File

@@ -99,6 +99,7 @@ type FinancialStatementKind =
| "income"
| "balance"
| "cash_flow"
| "disclosure"
| "equity"
| "comprehensive_income";

View File

@@ -1,13 +1,22 @@
import { and, desc, eq, gte, lt, sql } from 'drizzle-orm';
import { db } from '@/lib/server/db';
import { filingStatementSnapshot } from '@/lib/server/db/schema';
import { and, desc, eq, gte, lt, sql } from "drizzle-orm";
import { db } from "@/lib/server/db";
import { filingStatementSnapshot } from "@/lib/server/db/schema";
type FilingStatementSnapshotRow = typeof filingStatementSnapshot.$inferSelect;
type ParseStatus = 'ready' | 'partial' | 'failed';
type SnapshotSource = 'sec_filing_summary' | 'xbrl_instance' | 'companyfacts_fallback';
type ParseStatus = "ready" | "partial" | "failed";
type SnapshotSource =
| "sec_filing_summary"
| "xbrl_instance"
| "companyfacts_fallback";
type FinancialStatementKind = 'income' | 'balance' | 'cash_flow' | 'equity' | 'comprehensive_income';
type FinancialStatementKind =
| "income"
| "balance"
| "cash_flow"
| "disclosure"
| "equity"
| "comprehensive_income";
type StatementValuesByPeriod = Record<string, number | null>;
@@ -18,7 +27,7 @@ export type FilingStatementSnapshotPeriod = {
filingDate: string;
periodStart: string | null;
periodEnd: string | null;
filingType: '10-K' | '10-Q';
filingType: "10-K" | "10-Q";
periodLabel: string;
};
@@ -53,12 +62,18 @@ export type DimensionStatementSnapshotRow = {
export type FilingStatementBundle = {
periods: FilingStatementSnapshotPeriod[];
statements: Record<FinancialStatementKind, FilingFaithfulStatementSnapshotRow[]>;
statements: Record<
FinancialStatementKind,
FilingFaithfulStatementSnapshotRow[]
>;
};
export type StandardizedStatementBundle = {
periods: FilingStatementSnapshotPeriod[];
statements: Record<FinancialStatementKind, StandardizedStatementSnapshotRow[]>;
statements: Record<
FinancialStatementKind,
StandardizedStatementSnapshotRow[]
>;
};
export type DimensionStatementBundle = {
@@ -70,7 +85,7 @@ export type FilingStatementSnapshotRecord = {
filing_id: number;
ticker: string;
filing_date: string;
filing_type: '10-K' | '10-Q';
filing_type: "10-K" | "10-Q";
period_end: string | null;
statement_bundle: FilingStatementBundle | null;
standardized_bundle: StandardizedStatementBundle | null;
@@ -86,7 +101,7 @@ export type UpsertFilingStatementSnapshotInput = {
filing_id: number;
ticker: string;
filing_date: string;
filing_type: '10-K' | '10-Q';
filing_type: "10-K" | "10-Q";
period_end: string | null;
statement_bundle: FilingStatementBundle | null;
standardized_bundle: StandardizedStatementBundle | null;
@@ -96,7 +111,9 @@ export type UpsertFilingStatementSnapshotInput = {
source: SnapshotSource;
};
function toSnapshotRecord(row: FilingStatementSnapshotRow): FilingStatementSnapshotRecord {
function toSnapshotRecord(
row: FilingStatementSnapshotRow,
): FilingStatementSnapshotRecord {
return {
id: row.id,
filing_id: row.filing_id,
@@ -111,7 +128,7 @@ function toSnapshotRecord(row: FilingStatementSnapshotRow): FilingStatementSnaps
parse_error: row.parse_error,
source: row.source,
created_at: row.created_at,
updated_at: row.updated_at
updated_at: row.updated_at,
};
}
@@ -131,7 +148,9 @@ export async function getFilingStatementSnapshotByFilingId(filingId: number) {
return row ? toSnapshotRecord(row) : null;
}
export async function upsertFilingStatementSnapshot(input: UpsertFilingStatementSnapshotInput) {
export async function upsertFilingStatementSnapshot(
input: UpsertFilingStatementSnapshotInput,
) {
const now = new Date().toISOString();
const [saved] = await db
@@ -149,7 +168,7 @@ export async function upsertFilingStatementSnapshot(input: UpsertFilingStatement
parse_error: input.parse_error,
source: input.source,
created_at: now,
updated_at: now
updated_at: now,
})
.onConflictDoUpdate({
target: filingStatementSnapshot.filing_id,
@@ -164,8 +183,8 @@ export async function upsertFilingStatementSnapshot(input: UpsertFilingStatement
parse_status: input.parse_status,
parse_error: input.parse_error,
source: input.source,
updated_at: now
}
updated_at: now,
},
})
.returning();
@@ -174,16 +193,20 @@ export async function upsertFilingStatementSnapshot(input: UpsertFilingStatement
export async function listFilingStatementSnapshotsByTicker(input: {
ticker: string;
window: '10y' | 'all';
window: "10y" | "all";
limit?: number;
cursor?: string | null;
}) {
const safeLimit = Math.min(Math.max(Math.trunc(input.limit ?? 40), 1), 120);
const cursorId = input.cursor ? Number.parseInt(input.cursor, 10) : null;
const constraints = [eq(filingStatementSnapshot.ticker, input.ticker.trim().toUpperCase())];
const constraints = [
eq(filingStatementSnapshot.ticker, input.ticker.trim().toUpperCase()),
];
if (input.window === '10y') {
constraints.push(gte(filingStatementSnapshot.filing_date, tenYearsAgoIso()));
if (input.window === "10y") {
constraints.push(
gte(filingStatementSnapshot.filing_date, tenYearsAgoIso()),
);
}
if (cursorId && Number.isFinite(cursorId) && cursorId > 0) {
@@ -194,18 +217,21 @@ export async function listFilingStatementSnapshotsByTicker(input: {
.select()
.from(filingStatementSnapshot)
.where(and(...constraints))
.orderBy(desc(filingStatementSnapshot.filing_date), desc(filingStatementSnapshot.id))
.orderBy(
desc(filingStatementSnapshot.filing_date),
desc(filingStatementSnapshot.id),
)
.limit(safeLimit + 1);
const hasMore = rows.length > safeLimit;
const usedRows = hasMore ? rows.slice(0, safeLimit) : rows;
const nextCursor = hasMore
? String(usedRows[usedRows.length - 1]?.id ?? '')
? String(usedRows[usedRows.length - 1]?.id ?? "")
: null;
return {
snapshots: usedRows.map(toSnapshotRecord),
nextCursor
nextCursor,
};
}
@@ -213,18 +239,21 @@ export async function countFilingStatementSnapshotStatuses(ticker: string) {
const rows = await db
.select({
status: filingStatementSnapshot.parse_status,
count: sql<string>`count(*)`
count: sql<string>`count(*)`,
})
.from(filingStatementSnapshot)
.where(eq(filingStatementSnapshot.ticker, ticker.trim().toUpperCase()))
.groupBy(filingStatementSnapshot.parse_status);
return rows.reduce<Record<ParseStatus, number>>((acc, row) => {
acc[row.status] = Number(row.count);
return acc;
}, {
ready: 0,
partial: 0,
failed: 0
});
return rows.reduce<Record<ParseStatus, number>>(
(acc, row) => {
acc[row.status] = Number(row.count);
return acc;
},
{
ready: 0,
partial: 0,
failed: 0,
},
);
}

View File

@@ -897,6 +897,7 @@ function emptyStatementRows(): StatementRowMap {
income: [],
balance: [],
cash_flow: [],
disclosure: [],
equity: [],
comprehensive_income: [],
};
@@ -907,6 +908,7 @@ function emptySurfaceRows(): SurfaceRowMap {
income: [],
balance: [],
cash_flow: [],
disclosure: [],
equity: [],
comprehensive_income: [],
};
@@ -917,6 +919,7 @@ function emptyDetailRows(): DetailRowMap {
income: {},
balance: {},
cash_flow: {},
disclosure: {},
equity: {},
comprehensive_income: {},
};

File diff suppressed because it is too large Load Diff

View File

@@ -894,6 +894,7 @@ async function processSyncFilings(task: Task) {
cash_flow: [],
equity: [],
comprehensive_income: [],
disclosure: [],
},
statement_rows: {
income: [],
@@ -901,6 +902,7 @@ async function processSyncFilings(task: Task) {
cash_flow: [],
equity: [],
comprehensive_income: [],
disclosure: [],
},
surface_rows: {
income: [],
@@ -908,6 +910,7 @@ async function processSyncFilings(task: Task) {
cash_flow: [],
equity: [],
comprehensive_income: [],
disclosure: [],
},
detail_rows: {
income: {},
@@ -915,6 +918,7 @@ async function processSyncFilings(task: Task) {
cash_flow: {},
equity: {},
comprehensive_income: {},
disclosure: {},
},
kpi_rows: [],
computed_definitions: [],

View File

@@ -13,6 +13,7 @@ function createStatementRecord<T>(
income: factory(),
balance: factory(),
cash_flow: factory(),
disclosure: factory(),
equity: factory(),
comprehensive_income: factory(),
};

View File

@@ -35,6 +35,7 @@ function sampleHydrationResult(): TaxonomyHydrationResult {
income: [],
balance: [],
cash_flow: [],
disclosure: [],
equity: [],
comprehensive_income: [],
},
@@ -42,6 +43,7 @@ function sampleHydrationResult(): TaxonomyHydrationResult {
income: [],
balance: [],
cash_flow: [],
disclosure: [],
equity: [],
comprehensive_income: [],
},
@@ -49,6 +51,7 @@ function sampleHydrationResult(): TaxonomyHydrationResult {
income: [],
balance: [],
cash_flow: [],
disclosure: [],
equity: [],
comprehensive_income: [],
},
@@ -56,6 +59,7 @@ function sampleHydrationResult(): TaxonomyHydrationResult {
income: {},
balance: {},
cash_flow: {},
disclosure: {},
equity: {},
comprehensive_income: {},
},

View File

@@ -5,22 +5,28 @@ export type User = {
image: string | null;
};
export type CoverageStatus = 'backlog' | 'active' | 'watch' | 'archive';
export type CoveragePriority = 'low' | 'medium' | 'high';
export type ResearchJournalEntryType = 'note' | 'filing_note' | 'status_change';
export type NumberScaleUnit = 'thousands' | 'millions' | 'billions';
export type ResearchArtifactKind = 'filing' | 'ai_report' | 'note' | 'upload' | 'memo_snapshot' | 'status_change';
export type ResearchArtifactSource = 'system' | 'user';
export type ResearchVisibilityScope = 'private' | 'organization';
export type ResearchMemoRating = 'strong_buy' | 'buy' | 'hold' | 'sell';
export type ResearchMemoConviction = 'low' | 'medium' | 'high';
export type CoverageStatus = "backlog" | "active" | "watch" | "archive";
export type CoveragePriority = "low" | "medium" | "high";
export type ResearchJournalEntryType = "note" | "filing_note" | "status_change";
export type NumberScaleUnit = "thousands" | "millions" | "billions";
export type ResearchArtifactKind =
| "filing"
| "ai_report"
| "note"
| "upload"
| "memo_snapshot"
| "status_change";
export type ResearchArtifactSource = "system" | "user";
export type ResearchVisibilityScope = "private" | "organization";
export type ResearchMemoRating = "strong_buy" | "buy" | "hold" | "sell";
export type ResearchMemoConviction = "low" | "medium" | "high";
export type ResearchMemoSection =
| 'thesis'
| 'variant_view'
| 'catalysts'
| 'risks'
| 'disconfirming_evidence'
| 'next_actions';
| "thesis"
| "variant_view"
| "catalysts"
| "risks"
| "disconfirming_evidence"
| "next_actions";
export type WatchlistItem = {
id: number;
@@ -78,14 +84,14 @@ export type FilingExtraction = {
export type FilingExtractionMeta = {
provider: string;
model: string;
source: 'primary_document' | 'metadata_fallback';
source: "primary_document" | "metadata_fallback";
generatedAt: string;
};
export type Filing = {
id: number;
ticker: string;
filing_type: '10-K' | '10-Q' | '8-K';
filing_type: "10-K" | "10-Q" | "8-K";
filing_date: string;
accession_number: string;
cik: string;
@@ -113,44 +119,44 @@ export type Filing = {
updated_at: string;
};
export type TaskStatus = 'queued' | 'running' | 'completed' | 'failed';
export type TaskStatus = "queued" | "running" | "completed" | "failed";
export type TaskType =
| 'sync_filings'
| 'refresh_prices'
| 'analyze_filing'
| 'portfolio_insights'
| 'index_search';
| "sync_filings"
| "refresh_prices"
| "analyze_filing"
| "portfolio_insights"
| "index_search";
export type TaskStage =
| 'queued'
| 'running'
| 'completed'
| 'failed'
| 'sync.fetch_filings'
| 'sync.discover_assets'
| 'sync.extract_taxonomy'
| 'sync.normalize_taxonomy'
| 'sync.derive_metrics'
| 'sync.validate_pdf_metrics'
| 'sync.persist_taxonomy'
| 'sync.fetch_metrics'
| 'sync.persist_filings'
| 'sync.hydrate_statements'
| 'refresh.load_holdings'
| 'refresh.fetch_quotes'
| 'refresh.persist_prices'
| 'analyze.load_filing'
| 'analyze.fetch_document'
| 'analyze.extract'
| 'analyze.generate_report'
| 'analyze.persist_report'
| 'search.collect_sources'
| 'search.fetch_documents'
| 'search.chunk'
| 'search.embed'
| 'search.persist'
| 'insights.load_holdings'
| 'insights.generate'
| 'insights.persist';
| "queued"
| "running"
| "completed"
| "failed"
| "sync.fetch_filings"
| "sync.discover_assets"
| "sync.extract_taxonomy"
| "sync.normalize_taxonomy"
| "sync.derive_metrics"
| "sync.validate_pdf_metrics"
| "sync.persist_taxonomy"
| "sync.fetch_metrics"
| "sync.persist_filings"
| "sync.hydrate_statements"
| "refresh.load_holdings"
| "refresh.fetch_quotes"
| "refresh.persist_prices"
| "analyze.load_filing"
| "analyze.fetch_document"
| "analyze.extract"
| "analyze.generate_report"
| "analyze.persist_report"
| "search.collect_sources"
| "search.fetch_documents"
| "search.chunk"
| "search.embed"
| "search.persist"
| "insights.load_holdings"
| "insights.generate"
| "insights.persist";
export type TaskStageContext = {
progress?: {
@@ -173,12 +179,12 @@ export type TaskNotificationStat = {
export type TaskNotificationAction = {
id:
| 'open_details'
| 'open_filings'
| 'open_analysis'
| 'open_analysis_report'
| 'open_search'
| 'open_portfolio';
| "open_details"
| "open_filings"
| "open_analysis"
| "open_analysis_report"
| "open_search"
| "open_portfolio";
label: string;
href: string | null;
primary?: boolean;
@@ -188,7 +194,7 @@ export type TaskNotificationView = {
title: string;
statusLine: string;
detailLine: string | null;
tone: 'info' | 'success' | 'error';
tone: "info" | "success" | "error";
progress: {
current: number;
total: number;
@@ -201,12 +207,12 @@ export type TaskNotificationView = {
export type TaskNotificationEntry = {
id: string;
kind: 'single' | 'filing_sync_batch';
kind: "single" | "filing_sync_batch";
status: TaskStatus;
title: string;
statusLine: string;
detailLine: string | null;
progress: TaskNotificationView['progress'];
progress: TaskNotificationView["progress"];
stats: TaskNotificationStat[];
updatedAt: string;
primaryTaskId: string;
@@ -284,12 +290,12 @@ export type ResearchJournalEntry = {
updated_at: string;
};
export type SearchSource = 'documents' | 'filings' | 'research';
export type SearchSource = "documents" | "filings" | "research";
export type SearchResult = {
chunkId: number;
documentId: number;
source: SearchSource;
sourceKind: 'filing_document' | 'filing_brief' | 'research_note';
sourceKind: "filing_document" | "filing_brief" | "research_note";
sourceRef: string;
title: string | null;
ticker: string | null;
@@ -407,7 +413,7 @@ export type ResearchWorkspace = {
export type CompanyFinancialPoint = {
filingDate: string;
filingType: Filing['filing_type'];
filingType: Filing["filing_type"];
revenue: number | null;
netIncome: number | null;
totalAssets: number | null;
@@ -415,19 +421,30 @@ export type CompanyFinancialPoint = {
debt: number | null;
};
export type FinancialStatementKind = 'income' | 'balance' | 'cash_flow' | 'equity' | 'comprehensive_income';
export type FinancialHistoryWindow = '10y' | 'all';
export type FinancialCadence = 'annual' | 'quarterly' | 'ltm';
export type FinancialDisplayMode = 'faithful' | 'standardized';
export type FinancialStatementKind =
| "income"
| "balance"
| "cash_flow"
| "disclosure"
| "equity"
| "comprehensive_income";
export type FinancialHistoryWindow = "10y" | "all";
export type FinancialCadence = "annual" | "quarterly" | "ltm";
export type FinancialDisplayMode = "faithful" | "standardized";
export type FinancialSurfaceKind =
| 'income_statement'
| 'balance_sheet'
| 'cash_flow_statement'
| 'ratios'
| 'segments_kpis'
| 'adjusted'
| 'custom_metrics';
export type FinancialUnit = 'currency' | 'count' | 'shares' | 'percent' | 'ratio';
| "income_statement"
| "balance_sheet"
| "cash_flow_statement"
| "ratios"
| "segments_kpis"
| "adjusted"
| "custom_metrics";
export type FinancialUnit =
| "currency"
| "count"
| "shares"
| "percent"
| "ratio";
export type FinancialCategory = string;
export type FinancialStatementPeriod = {
@@ -437,7 +454,7 @@ export type FinancialStatementPeriod = {
filingDate: string;
periodStart: string | null;
periodEnd: string | null;
filingType: Extract<Filing['filing_type'], '10-K' | '10-Q'>;
filingType: Extract<Filing["filing_type"], "10-K" | "10-Q">;
periodLabel: string;
};
@@ -486,10 +503,17 @@ export type DerivedFinancialRow = {
export type StandardizedFinancialRow = DerivedFinancialRow;
export type StandardizedStatementRow = StandardizedFinancialRow;
export type SurfaceFinancialRow = StandardizedFinancialRow & {
statement?: Extract<FinancialStatementKind, 'income' | 'balance' | 'cash_flow'>;
statement?: Extract<
FinancialStatementKind,
"income" | "balance" | "cash_flow"
>;
detailCount?: number;
resolutionMethod?: 'direct' | 'surface_bridge' | 'formula_derived' | 'not_meaningful';
confidence?: 'high' | 'medium' | 'low';
resolutionMethod?:
| "direct"
| "surface_bridge"
| "formula_derived"
| "not_meaningful";
confidence?: "high" | "medium" | "low";
warningCodes?: string[];
};
@@ -522,7 +546,7 @@ export type NormalizationSummary = {
export type NormalizationMetadata = {
parserEngine: string;
regime: 'us-gaap' | 'ifrs-full' | 'unknown';
regime: "us-gaap" | "ifrs-full" | "unknown";
fiscalPack: string | null;
parserVersion: string;
surfaceRowCount: number;
@@ -549,7 +573,7 @@ export type StructuredKpiRow = {
values: Record<string, number | null>;
sourceConcepts: string[];
sourceFactIds: number[];
provenanceType: 'taxonomy' | 'structured_note';
provenanceType: "taxonomy" | "structured_note";
hasDimensions: boolean;
};
@@ -577,12 +601,12 @@ export type TaxonomyFactRow = {
};
export type MetricValidationCheck = {
metricKey: keyof NonNullable<Filing['metrics']>;
metricKey: keyof NonNullable<Filing["metrics"]>;
taxonomyValue: number | null;
llmValue: number | null;
absoluteDiff: number | null;
relativeDiff: number | null;
status: 'not_run' | 'matched' | 'mismatch' | 'error';
status: "not_run" | "matched" | "mismatch" | "error";
evidencePages: number[];
pdfUrl: string | null;
provider: string | null;
@@ -591,7 +615,7 @@ export type MetricValidationCheck = {
};
export type MetricValidationResult = {
status: 'not_run' | 'matched' | 'mismatch' | 'error';
status: "not_run" | "matched" | "mismatch" | "error";
checks: MetricValidationCheck[];
validatedAt: string | null;
};
@@ -617,7 +641,7 @@ export type DimensionBreakdownRow = {
member: string;
value: number | null;
unit: string | null;
provenanceType?: 'taxonomy' | 'structured_note';
provenanceType?: "taxonomy" | "structured_note";
};
export type TrendSeries = {
@@ -676,7 +700,7 @@ export type CompanyFinancialStatementsResponse = {
queuedSync: boolean;
};
metrics: {
taxonomy: Filing['metrics'];
taxonomy: Filing["metrics"];
validation: MetricValidationResult | null;
};
normalization: NormalizationMetadata;
@@ -686,7 +710,7 @@ export type CompanyFinancialStatementsResponse = {
export type CompanyAiReport = {
accessionNumber: string;
filingDate: string;
filingType: Filing['filing_type'];
filingType: Filing["filing_type"];
provider: string;
model: string;
summary: string;
@@ -708,7 +732,7 @@ export type CompanyProfile = {
website: string | null;
fiscalYearEnd: string | null;
employeeCount: number | null;
source: 'sec_derived' | 'unavailable';
source: "sec_derived" | "unavailable";
};
export type CompanyValuationSnapshot = {
@@ -718,17 +742,22 @@ export type CompanyValuationSnapshot = {
trailingPe: number | null;
evToRevenue: number | null;
evToEbitda: number | null;
source: 'derived' | 'partial' | 'unavailable';
source: "derived" | "partial" | "unavailable";
};
export type CompanyBullBear = {
source: 'ai_synthesized' | 'memo_fallback' | 'unavailable';
source: "ai_synthesized" | "memo_fallback" | "unavailable";
bull: string[];
bear: string[];
updatedAt: string | null;
};
export type RecentDevelopmentKind = '8-K' | '10-K' | '10-Q' | 'press_release' | 'news';
export type RecentDevelopmentKind =
| "8-K"
| "10-K"
| "10-Q"
| "press_release"
| "news";
export type RecentDevelopmentItem = {
id: string;
@@ -748,11 +777,11 @@ export type RecentDevelopmentsWeeklySnapshot = {
startDate: string;
endDate: string;
updatedAt: string;
source: 'ai_synthesized' | 'heuristic';
source: "ai_synthesized" | "heuristic";
};
export type RecentDevelopments = {
status: 'ready' | 'partial' | 'unavailable';
status: "ready" | "partial" | "unavailable";
items: RecentDevelopmentItem[];
weeklySnapshot: RecentDevelopmentsWeeklySnapshot | null;
};
@@ -784,7 +813,7 @@ export type CompanyAnalysis = {
latestFilingSummary: {
accessionNumber: string;
filingDate: string;
filingType: Filing['filing_type'];
filingType: Filing["filing_type"];
filingUrl: string | null;
submissionUrl: string | null;
summary: string | null;
@@ -805,8 +834,8 @@ export type CompanyAnalysis = {
recentDevelopments: RecentDevelopments;
};
export type NavGroup = 'overview' | 'research' | 'portfolio';
export type NavMatchMode = 'exact' | 'prefix';
export type NavGroup = "overview" | "research" | "portfolio";
export type NavMatchMode = "exact" | "prefix";
export type NavItem = {
id: string;
@@ -824,8 +853,8 @@ export type ActiveContext = {
};
// Chart Types
export type ChartType = 'line' | 'combination';
export type TimeRange = '1W' | '1M' | '3M' | '1Y' | '3Y' | '5Y' | '10Y' | '20Y';
export type ChartType = "line" | "combination";
export type TimeRange = "1W" | "1M" | "3M" | "1Y" | "3Y" | "5Y" | "10Y" | "20Y";
// Chart Data Formats
export type PriceDataPoint = {
@@ -849,7 +878,7 @@ export type DataSeries<T extends ChartDataPoint = ChartDataPoint> = {
label: string;
data: T[];
color?: string;
type?: 'line' | 'area' | 'bar';
type?: "line" | "area" | "bar";
visible?: boolean;
};

View File

@@ -333,6 +333,250 @@
"us-gaap:TotalSalesAssetAndAccountExpense": {
"surface_key": "distribution_and_servicing_expense",
"authoritative_concept_key": "us-gaap:TotalSalesAssetAndAccountExpense"
},
"us-gaap:InvestmentIncomeNet": {
"surface_key": "interest_income",
"authoritative_concept_key": "us-gaap:InvestmentIncomeNet"
},
"us-gaap:FinanceLeaseInterestExpense": {
"surface_key": "interest_expense",
"authoritative_concept_key": "us-gaap:FinanceLeaseInterestExpense"
},
"us-gaap:InterestExpenseNonoperating": {
"surface_key": "interest_expense",
"authoritative_concept_key": "us-gaap:InterestExpenseNonoperating"
},
"us-gaap:RevenueRemainingPerformanceObligation": {
"surface_key": "revenue_disclosure",
"authoritative_concept_key": "us-gaap:RevenueRemainingPerformanceObligation"
},
"us-gaap:RevenueRemainingPerformanceObligationPercentage": {
"surface_key": "revenue_disclosure",
"authoritative_concept_key": "us-gaap:RevenueRemainingPerformanceObligationPercentage"
},
"us-gaap:CurrentIncomeTaxExpenseBenefit": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:CurrentIncomeTaxExpenseBenefit"
},
"us-gaap:DeferredIncomeTaxExpenseBenefit": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:DeferredIncomeTaxExpenseBenefit"
},
"us-gaap:CurrentFederalTaxExpenseBenefit": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:CurrentFederalTaxExpenseBenefit"
},
"us-gaap:CurrentForeignTaxExpenseBenefit": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:CurrentForeignTaxExpenseBenefit"
},
"us-gaap:CurrentStateAndLocalTaxExpenseBenefit": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:CurrentStateAndLocalTaxExpenseBenefit"
},
"us-gaap:DeferredFederalIncomeTaxExpenseBenefit": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:DeferredFederalIncomeTaxExpenseBenefit"
},
"us-gaap:DeferredForeignIncomeTaxExpenseBenefit": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:DeferredForeignIncomeTaxExpenseBenefit"
},
"us-gaap:DeferredStateAndLocalIncomeTaxExpenseBenefit": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:DeferredStateAndLocalIncomeTaxExpenseBenefit"
},
"us-gaap:EffectiveIncomeTaxRateContinuingOperations": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateContinuingOperations"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationAtFederalStatutoryIncomeTaxRate": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationAtFederalStatutoryIncomeTaxRate"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationDeductionsExcessTaxBenefitsStockBasedCompensation": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationDeductionsExcessTaxBenefitsStockBasedCompensation"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationFdiiPercent": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationFdiiPercent"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationForeignIncomeTaxRateDifferential": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationForeignIncomeTaxRateDifferential"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationIntangiblePropertyTransfers": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationIntangiblePropertyTransfers"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationInterestIncomeExpense": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationInterestIncomeExpense"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationOtherAdjustments": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationOtherAdjustments"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationStateAndLocalIncomeTaxes": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationStateAndLocalIncomeTaxes"
},
"us-gaap:EffectiveIncomeTaxRateReconciliationTaxCreditsResearch": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EffectiveIncomeTaxRateReconciliationTaxCreditsResearch"
},
"us-gaap:EmployeeServiceShareBasedCompensationTaxBenefitFromCompensationExpense": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:EmployeeServiceShareBasedCompensationTaxBenefitFromCompensationExpense"
},
"us-gaap:UnrecognizedTaxBenefitsIncomeTaxPenaltiesAndInterestExpense": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:UnrecognizedTaxBenefitsIncomeTaxPenaltiesAndInterestExpense"
},
"us-gaap:IncomeTaxesPaidNet": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:IncomeTaxesPaidNet"
},
"us-gaap:IncomeLossFromContinuingOperationsBeforeIncomeTaxesDomestic": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:IncomeLossFromContinuingOperationsBeforeIncomeTaxesDomestic"
},
"us-gaap:IncomeLossFromContinuingOperationsBeforeIncomeTaxesForeign": {
"surface_key": "income_tax_disclosure",
"authoritative_concept_key": "us-gaap:IncomeLossFromContinuingOperationsBeforeIncomeTaxesForeign"
},
"us-gaap:BusinessAcquisitionsProFormaNetIncomeLoss": {
"surface_key": "business_combinations_disclosure",
"authoritative_concept_key": "us-gaap:BusinessAcquisitionsProFormaNetIncomeLoss"
},
"us-gaap:BusinessAcquisitionsProFormaRevenue": {
"surface_key": "business_combinations_disclosure",
"authoritative_concept_key": "us-gaap:BusinessAcquisitionsProFormaRevenue"
},
"us-gaap:BusinessCombinationProFormaInformationRevenueOfAcquireeSinceAcquisitionDateActual": {
"surface_key": "business_combinations_disclosure",
"authoritative_concept_key": "us-gaap:BusinessCombinationProFormaInformationRevenueOfAcquireeSinceAcquisitionDateActual"
},
"us-gaap:LongTermDebtFairValue": {
"surface_key": "debt_disclosure",
"authoritative_concept_key": "us-gaap:LongTermDebtFairValue"
},
"us-gaap:LongTermDebtMaturitiesRepaymentsOfPrincipalInNextTwelveMonths": {
"surface_key": "debt_disclosure",
"authoritative_concept_key": "us-gaap:LongTermDebtMaturitiesRepaymentsOfPrincipalInNextTwelveMonths"
},
"us-gaap:DebtInstrumentFaceAmount": {
"surface_key": "debt_disclosure",
"authoritative_concept_key": "us-gaap:DebtInstrumentFaceAmount"
},
"us-gaap:DebtInstrumentInterestRateEffectivePercentage": {
"surface_key": "debt_disclosure",
"authoritative_concept_key": "us-gaap:DebtInstrumentInterestRateEffectivePercentage"
},
"us-gaap:DebtInstrumentInterestRateStatedPercentage": {
"surface_key": "debt_disclosure",
"authoritative_concept_key": "us-gaap:DebtInstrumentInterestRateStatedPercentage"
},
"us-gaap:DebtInstrumentUnamortizedDiscountPremiumAndDebtIssuanceCostsNet": {
"surface_key": "debt_disclosure",
"authoritative_concept_key": "us-gaap:DebtInstrumentUnamortizedDiscountPremiumAndDebtIssuanceCostsNet"
},
"us-gaap:AvailableForSaleDebtSecuritiesAmortizedCostBasis": {
"surface_key": "investment_securities_disclosure",
"authoritative_concept_key": "us-gaap:AvailableForSaleDebtSecuritiesAmortizedCostBasis"
},
"us-gaap:AvailableForSaleDebtSecuritiesAccumulatedGrossUnrealizedGainBeforeTax": {
"surface_key": "investment_securities_disclosure",
"authoritative_concept_key": "us-gaap:AvailableForSaleDebtSecuritiesAccumulatedGrossUnrealizedGainBeforeTax"
},
"us-gaap:AvailableForSaleDebtSecuritiesAccumulatedGrossUnrealizedLossBeforeTax": {
"surface_key": "investment_securities_disclosure",
"authoritative_concept_key": "us-gaap:AvailableForSaleDebtSecuritiesAccumulatedGrossUnrealizedLossBeforeTax"
},
"us-gaap:FiniteLivedIntangibleAssetsGross": {
"surface_key": "intangible_assets_disclosure",
"authoritative_concept_key": "us-gaap:FiniteLivedIntangibleAssetsGross"
},
"us-gaap:FiniteLivedIntangibleAssetsAccumulatedAmortization": {
"surface_key": "intangible_assets_disclosure",
"authoritative_concept_key": "us-gaap:FiniteLivedIntangibleAssetsAccumulatedAmortization"
},
"us-gaap:AmortizationOfIntangibleAssets": {
"surface_key": "intangible_assets_disclosure",
"authoritative_concept_key": "us-gaap:AmortizationOfIntangibleAssets"
},
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDue": {
"surface_key": "lease_disclosure",
"authoritative_concept_key": "us-gaap:LesseeOperatingLeaseLiabilityPaymentsDue"
},
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsRemainderOfFiscalYear": {
"surface_key": "lease_disclosure",
"authoritative_concept_key": "us-gaap:LesseeOperatingLeaseLiabilityPaymentsRemainderOfFiscalYear"
},
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueNextTwelveMonths": {
"surface_key": "lease_disclosure",
"authoritative_concept_key": "us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueNextTwelveMonths"
},
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueYearTwo": {
"surface_key": "lease_disclosure",
"authoritative_concept_key": "us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueYearTwo"
},
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueYearThree": {
"surface_key": "lease_disclosure",
"authoritative_concept_key": "us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueYearThree"
},
"us-gaap:FinanceLeaseLiabilityPaymentsDue": {
"surface_key": "lease_disclosure",
"authoritative_concept_key": "us-gaap:FinanceLeaseLiabilityPaymentsDue"
},
"us-gaap:FinanceLeaseRightOfUseAssetAmortization": {
"surface_key": "lease_disclosure",
"authoritative_concept_key": "us-gaap:FinanceLeaseRightOfUseAssetAmortization"
},
"us-gaap:DerivativeAssets": {
"surface_key": "derivative_disclosure",
"authoritative_concept_key": "us-gaap:DerivativeAssets"
},
"us-gaap:DerivativeLiabilities": {
"surface_key": "derivative_disclosure",
"authoritative_concept_key": "us-gaap:DerivativeLiabilities"
},
"us-gaap:DerivativeAssetsCurrent": {
"surface_key": "derivative_disclosure",
"authoritative_concept_key": "us-gaap:DerivativeAssetsCurrent"
},
"us-gaap:DerivativeLiabilitiesCurrent": {
"surface_key": "derivative_disclosure",
"authoritative_concept_key": "us-gaap:DerivativeLiabilitiesCurrent"
},
"us-gaap:IncreaseDecreaseInContractWithCustomerLiability": {
"surface_key": "operating_cash_flow",
"authoritative_concept_key": "us-gaap:IncreaseDecreaseInContractWithCustomerLiability"
},
"us-gaap:PaymentsToAcquireInvestments": {
"surface_key": "investing_cash_flow",
"authoritative_concept_key": "us-gaap:PaymentsToAcquireInvestments"
},
"us-gaap:ProceedsFromMaturitiesPrepaymentsAndCallsOfAvailableForSaleSecurities": {
"surface_key": "investing_cash_flow",
"authoritative_concept_key": "us-gaap:ProceedsFromMaturitiesPrepaymentsAndCallsOfAvailableForSaleSecurities"
},
"us-gaap:ProceedsFromIssuanceOfCommonStock": {
"surface_key": "financing_cash_flow",
"authoritative_concept_key": "us-gaap:ProceedsFromIssuanceOfCommonStock"
},
"us-gaap:PaymentsRelatedToTaxWithholdingForShareBasedCompensation": {
"surface_key": "financing_cash_flow",
"authoritative_concept_key": "us-gaap:PaymentsRelatedToTaxWithholdingForShareBasedCompensation"
},
"us-gaap:RepaymentsOfDebtMaturingInMoreThanThreeMonths": {
"surface_key": "financing_cash_flow",
"authoritative_concept_key": "us-gaap:RepaymentsOfDebtMaturingInMoreThanThreeMonths"
},
"us-gaap:CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsPeriodIncreaseDecreaseIncludingExchangeRateEffect": {
"surface_key": "cash_flow_disclosure",
"authoritative_concept_key": "us-gaap:CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsPeriodIncreaseDecreaseIncludingExchangeRateEffect"
}
}
}

View File

@@ -10,8 +10,13 @@
"order": 10,
"unit": "currency",
"rollup_policy": "direct_or_formula",
"allowed_source_concepts": ["us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax", "us-gaap:SalesRevenueNet"],
"allowed_authoritative_concepts": ["us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax"],
"allowed_source_concepts": [
"us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax",
"us-gaap:SalesRevenueNet"
],
"allowed_authoritative_concepts": [
"us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax"
],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "income_default"
@@ -100,7 +105,9 @@
"us-gaap:SellingGeneralAndAdministrativeExpense",
"us-gaap:SellingGeneralAndAdministrativeExpenseExcludingEmployeeStockOptionPlanSpecialDividendCompensation"
],
"allowed_authoritative_concepts": ["us-gaap:SellingGeneralAndAdministrativeExpense"],
"allowed_authoritative_concepts": [
"us-gaap:SellingGeneralAndAdministrativeExpense"
],
"formula_fallback": {
"op": "sum",
"sources": ["sales_and_marketing", "general_and_administrative"],
@@ -118,7 +125,9 @@
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": ["us-gaap:ResearchAndDevelopmentExpense"],
"allowed_authoritative_concepts": ["us-gaap:ResearchAndDevelopmentExpense"],
"allowed_authoritative_concepts": [
"us-gaap:ResearchAndDevelopmentExpense"
],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "income_default"
@@ -215,14 +224,15 @@
"category": "surface",
"order": 70,
"unit": "currency",
"rollup_policy": "direct_only",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:InterestIncomeOther",
"us-gaap:InvestmentIncomeInterest"
"us-gaap:InvestmentIncomeInterest",
"us-gaap:InvestmentIncomeNet"
],
"allowed_authoritative_concepts": ["us-gaap:InterestIncomeOther"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"detail_grouping_policy": "group_all_children",
"materiality_policy": "income_default"
},
{
@@ -232,15 +242,17 @@
"category": "surface",
"order": 75,
"unit": "currency",
"rollup_policy": "direct_only",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:InterestIncomeExpenseNonoperatingNet",
"us-gaap:InterestExpense",
"us-gaap:InterestAndDebtExpense"
"us-gaap:InterestAndDebtExpense",
"us-gaap:FinanceLeaseInterestExpense",
"us-gaap:InterestExpenseNonoperating"
],
"allowed_authoritative_concepts": ["us-gaap:InterestExpense"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"detail_grouping_policy": "group_all_children",
"materiality_policy": "income_default",
"sign_transform": "absolute"
},
@@ -256,7 +268,9 @@
"us-gaap:OtherNonoperatingIncomeExpense",
"us-gaap:NonoperatingIncomeExpense"
],
"allowed_authoritative_concepts": ["us-gaap:OtherNonoperatingIncomeExpense"],
"allowed_authoritative_concepts": [
"us-gaap:OtherNonoperatingIncomeExpense"
],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "income_default"
@@ -274,7 +288,9 @@
"us-gaap:IncomeBeforeTaxExpenseBenefit",
"us-gaap:PretaxIncome"
],
"allowed_authoritative_concepts": ["us-gaap:IncomeBeforeTaxExpenseBenefit"],
"allowed_authoritative_concepts": [
"us-gaap:IncomeBeforeTaxExpenseBenefit"
],
"formula_fallback": {
"op": "sum",
"sources": ["net_income", "income_tax_expense"],
@@ -377,9 +393,7 @@
"us-gaap:EarningsPerShareDiluted",
"us-gaap:DilutedEarningsPerShare"
],
"allowed_authoritative_concepts": [
"us-gaap:EarningsPerShareDiluted"
],
"allowed_authoritative_concepts": ["us-gaap:EarningsPerShareDiluted"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "income_default"
@@ -396,9 +410,7 @@
"us-gaap:EarningsPerShareBasic",
"us-gaap:BasicEarningsPerShare"
],
"allowed_authoritative_concepts": [
"us-gaap:EarningsPerShareBasic"
],
"allowed_authoritative_concepts": ["us-gaap:EarningsPerShareBasic"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "income_default"
@@ -535,12 +547,8 @@
"order": 50,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:InventoryNet"
],
"allowed_authoritative_concepts": [
"us-gaap:InventoryNet"
],
"allowed_source_concepts": ["us-gaap:InventoryNet"],
"allowed_authoritative_concepts": ["us-gaap:InventoryNet"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "balance_default"
@@ -553,12 +561,8 @@
"order": 60,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:OtherAssetsCurrent"
],
"allowed_authoritative_concepts": [
"us-gaap:OtherAssetsCurrent"
],
"allowed_source_concepts": ["us-gaap:OtherAssetsCurrent"],
"allowed_authoritative_concepts": ["us-gaap:OtherAssetsCurrent"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "balance_default"
@@ -571,12 +575,8 @@
"order": 70,
"unit": "currency",
"rollup_policy": "direct_or_formula",
"allowed_source_concepts": [
"us-gaap:AssetsCurrent"
],
"allowed_authoritative_concepts": [
"us-gaap:AssetsCurrent"
],
"allowed_source_concepts": ["us-gaap:AssetsCurrent"],
"allowed_authoritative_concepts": ["us-gaap:AssetsCurrent"],
"formula_fallback": {
"op": "sum",
"sources": [
@@ -618,9 +618,7 @@
"order": 90,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:OperatingLeaseRightOfUseAsset"
],
"allowed_source_concepts": ["us-gaap:OperatingLeaseRightOfUseAsset"],
"allowed_authoritative_concepts": [
"us-gaap:OperatingLeaseRightOfUseAsset"
],
@@ -658,12 +656,8 @@
"order": 110,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:Goodwill"
],
"allowed_authoritative_concepts": [
"us-gaap:Goodwill"
],
"allowed_source_concepts": ["us-gaap:Goodwill"],
"allowed_authoritative_concepts": ["us-gaap:Goodwill"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "balance_default"
@@ -718,12 +712,8 @@
"order": 140,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:OtherAssetsNoncurrent"
],
"allowed_authoritative_concepts": [
"us-gaap:OtherAssetsNoncurrent"
],
"allowed_source_concepts": ["us-gaap:OtherAssetsNoncurrent"],
"allowed_authoritative_concepts": ["us-gaap:OtherAssetsNoncurrent"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "balance_default"
@@ -736,12 +726,8 @@
"order": 150,
"unit": "currency",
"rollup_policy": "direct_or_formula",
"allowed_source_concepts": [
"us-gaap:Assets"
],
"allowed_authoritative_concepts": [
"us-gaap:Assets"
],
"allowed_source_concepts": ["us-gaap:Assets"],
"allowed_authoritative_concepts": ["us-gaap:Assets"],
"formula_fallback": {
"op": "sum",
"sources": [
@@ -767,12 +753,8 @@
"order": 160,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:AccountsPayableCurrent"
],
"allowed_authoritative_concepts": [
"us-gaap:AccountsPayableCurrent"
],
"allowed_source_concepts": ["us-gaap:AccountsPayableCurrent"],
"allowed_authoritative_concepts": ["us-gaap:AccountsPayableCurrent"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "balance_default"
@@ -793,9 +775,7 @@
"us-gaap:OtherLiabilitiesCurrent",
"us-gaap:AccruedPropertyTaxes"
],
"allowed_authoritative_concepts": [
"us-gaap:AccruedLiabilitiesCurrent"
],
"allowed_authoritative_concepts": ["us-gaap:AccruedLiabilitiesCurrent"],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "balance_default"
@@ -992,9 +972,7 @@
"us-gaap:AssetRetirementObligationsNoncurrent",
"us-gaap:OtherLiabilitiesNoncurrent"
],
"allowed_authoritative_concepts": [
"us-gaap:OtherLiabilitiesNoncurrent"
],
"allowed_authoritative_concepts": ["us-gaap:OtherLiabilitiesNoncurrent"],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "balance_default"
@@ -1007,12 +985,8 @@
"order": 270,
"unit": "currency",
"rollup_policy": "direct_or_formula",
"allowed_source_concepts": [
"us-gaap:LiabilitiesCurrent"
],
"allowed_authoritative_concepts": [
"us-gaap:LiabilitiesCurrent"
],
"allowed_source_concepts": ["us-gaap:LiabilitiesCurrent"],
"allowed_authoritative_concepts": ["us-gaap:LiabilitiesCurrent"],
"formula_fallback": {
"op": "sum",
"sources": [
@@ -1036,12 +1010,8 @@
"order": 280,
"unit": "currency",
"rollup_policy": "direct_or_formula",
"allowed_source_concepts": [
"us-gaap:LiabilitiesCurrent"
],
"allowed_authoritative_concepts": [
"us-gaap:LiabilitiesCurrent"
],
"allowed_source_concepts": ["us-gaap:LiabilitiesCurrent"],
"allowed_authoritative_concepts": ["us-gaap:LiabilitiesCurrent"],
"formula_fallback": {
"op": "sum",
"sources": [
@@ -1091,12 +1061,8 @@
"order": 300,
"unit": "currency",
"rollup_policy": "direct_or_formula",
"allowed_source_concepts": [
"us-gaap:Liabilities"
],
"allowed_authoritative_concepts": [
"us-gaap:Liabilities"
],
"allowed_source_concepts": ["us-gaap:Liabilities"],
"allowed_authoritative_concepts": ["us-gaap:Liabilities"],
"formula_fallback": {
"op": "sum",
"sources": [
@@ -1159,9 +1125,7 @@
"order": 330,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:RetainedEarningsAccumulatedDeficit"
],
"allowed_source_concepts": ["us-gaap:RetainedEarningsAccumulatedDeficit"],
"allowed_authoritative_concepts": [
"us-gaap:RetainedEarningsAccumulatedDeficit"
],
@@ -1177,12 +1141,8 @@
"order": 340,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:StockholdersEquity"
],
"allowed_authoritative_concepts": [
"us-gaap:StockholdersEquity"
],
"allowed_source_concepts": ["us-gaap:StockholdersEquity"],
"allowed_authoritative_concepts": ["us-gaap:StockholdersEquity"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "balance_default"
@@ -1221,9 +1181,7 @@
"order": 360,
"unit": "currency",
"rollup_policy": "direct_or_formula",
"allowed_source_concepts": [
"us-gaap:LiabilitiesAndStockholdersEquity"
],
"allowed_source_concepts": ["us-gaap:LiabilitiesAndStockholdersEquity"],
"allowed_authoritative_concepts": [
"us-gaap:LiabilitiesAndStockholdersEquity"
],
@@ -1389,9 +1347,7 @@
"order": 60,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:IncreaseDecreaseInInventories"
],
"allowed_source_concepts": ["us-gaap:IncreaseDecreaseInInventories"],
"allowed_authoritative_concepts": [
"us-gaap:IncreaseDecreaseInInventories"
],
@@ -1408,9 +1364,7 @@
"order": 70,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:IncreaseDecreaseInAccountsPayable"
],
"allowed_source_concepts": ["us-gaap:IncreaseDecreaseInAccountsPayable"],
"allowed_authoritative_concepts": [
"us-gaap:IncreaseDecreaseInAccountsPayable"
],
@@ -1464,9 +1418,7 @@
"order": 100,
"unit": "currency",
"rollup_policy": "direct_or_formula",
"allowed_source_concepts": [
"us-gaap:IncreaseDecreaseInDeferredRevenue"
],
"allowed_source_concepts": ["us-gaap:IncreaseDecreaseInDeferredRevenue"],
"allowed_authoritative_concepts": [
"us-gaap:IncreaseDecreaseInDeferredRevenue"
],
@@ -1587,23 +1539,29 @@
"include_in_output": false
},
{
"surface_key": "changes_other_noncurrent_assets",
"statement": "cash_flow",
"label": "Other Noncurrent Assets",
"category": "helper",
"order": 103,
"surface_key": "derivative_disclosure",
"statement": "balance",
"label": "Derivative Instruments Disclosure",
"category": "disclosure",
"order": 181,
"unit": "currency",
"rollup_policy": "direct_only",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:IncreaseDecreaseInOtherNoncurrentAssets"
],
"allowed_authoritative_concepts": [
"us-gaap:IncreaseDecreaseInOtherNoncurrentAssets"
"us-gaap:DerivativeAssets",
"us-gaap:DerivativeAssetsCurrent",
"us-gaap:DerivativeAssetsNoncurrent",
"us-gaap:DerivativeLiabilities",
"us-gaap:DerivativeLiabilitiesCurrent",
"us-gaap:DerivativeLiabilitiesNoncurrent",
"us-gaap:DerivativeFairValueOfDerivativeAsset",
"us-gaap:DerivativeFairValueOfDerivativeLiability",
"us-gaap:DerivativeAssetFairValueGrossAssetIncludingNotSubjectToMasterNettingArrangement",
"us-gaap:DerivativeLiabilityFairValueGrossLiabilityIncludingNotSubjectToMasterNettingArrangement"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "cash_flow_default",
"include_in_output": false
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "changes_other_noncurrent_liabilities",
@@ -1631,17 +1589,18 @@
"category": "operating",
"order": 120,
"unit": "currency",
"rollup_policy": "direct_only",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:NetCashProvidedByUsedInOperatingActivities",
"us-gaap:NetCashProvidedByUsedInOperatingActivitiesContinuingOperations"
"us-gaap:NetCashProvidedByUsedInOperatingActivitiesContinuingOperations",
"us-gaap:IncreaseDecreaseInContractWithCustomerLiability"
],
"allowed_authoritative_concepts": [
"us-gaap:NetCashProvidedByUsedInOperatingActivities",
"us-gaap:NetCashProvidedByUsedInOperatingActivitiesContinuingOperations"
],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"detail_grouping_policy": "group_all_children",
"materiality_policy": "cash_flow_default"
},
{
@@ -1753,15 +1712,17 @@
"category": "investing",
"order": 180,
"unit": "currency",
"rollup_policy": "direct_only",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:NetCashProvidedByUsedInInvestingActivities"
"us-gaap:NetCashProvidedByUsedInInvestingActivities",
"us-gaap:PaymentsToAcquireInvestments",
"us-gaap:ProceedsFromMaturitiesPrepaymentsAndCallsOfAvailableForSaleSecurities"
],
"allowed_authoritative_concepts": [
"us-gaap:NetCashProvidedByUsedInInvestingActivities"
],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"detail_grouping_policy": "group_all_children",
"materiality_policy": "cash_flow_default"
},
{
@@ -1772,12 +1733,8 @@
"order": 190,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:ProceedsFromShortTermDebt"
],
"allowed_authoritative_concepts": [
"us-gaap:ProceedsFromShortTermDebt"
],
"allowed_source_concepts": ["us-gaap:ProceedsFromShortTermDebt"],
"allowed_authoritative_concepts": ["us-gaap:ProceedsFromShortTermDebt"],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"materiality_policy": "cash_flow_default"
@@ -1790,9 +1747,7 @@
"order": 200,
"unit": "currency",
"rollup_policy": "direct_only",
"allowed_source_concepts": [
"us-gaap:ProceedsFromIssuanceOfLongTermDebt"
],
"allowed_source_concepts": ["us-gaap:ProceedsFromIssuanceOfLongTermDebt"],
"allowed_authoritative_concepts": [
"us-gaap:ProceedsFromIssuanceOfLongTermDebt"
],
@@ -1890,15 +1845,18 @@
"category": "financing",
"order": 250,
"unit": "currency",
"rollup_policy": "direct_only",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:NetCashProvidedByUsedInFinancingActivities"
"us-gaap:NetCashProvidedByUsedInFinancingActivities",
"us-gaap:ProceedsFromIssuanceOfCommonStock",
"us-gaap:PaymentsRelatedToTaxWithholdingForShareBasedCompensation",
"us-gaap:RepaymentsOfDebtMaturingInMoreThanThreeMonths"
],
"allowed_authoritative_concepts": [
"us-gaap:NetCashProvidedByUsedInFinancingActivities"
],
"formula_fallback": null,
"detail_grouping_policy": "top_level_only",
"detail_grouping_policy": "group_all_children",
"materiality_policy": "cash_flow_default"
},
{
@@ -1913,14 +1871,230 @@
"allowed_authoritative_concepts": [],
"formula_fallback": {
"op": "sum",
"sources": [
"operating_cash_flow",
"capital_expenditures"
],
"sources": ["operating_cash_flow", "capital_expenditures"],
"treat_null_as_zero": true
},
"detail_grouping_policy": "top_level_only",
"materiality_policy": "cash_flow_default"
},
{
"surface_key": "income_tax_disclosure",
"statement": "disclosure",
"label": "Income Tax Disclosures",
"category": "tax",
"order": 100,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:CurrentIncomeTaxExpenseBenefit",
"us-gaap:DeferredIncomeTaxExpenseBenefit",
"us-gaap:CurrentFederalTaxExpenseBenefit",
"us-gaap:CurrentForeignTaxExpenseBenefit",
"us-gaap:CurrentStateAndLocalTaxExpenseBenefit",
"us-gaap:DeferredFederalIncomeTaxExpenseBenefit",
"us-gaap:DeferredForeignIncomeTaxExpenseBenefit",
"us-gaap:DeferredStateAndLocalIncomeTaxExpenseBenefit",
"us-gaap:EffectiveIncomeTaxRateContinuingOperations",
"us-gaap:EffectiveIncomeTaxRateReconciliationAtFederalStatutoryIncomeTaxRate",
"us-gaap:EffectiveIncomeTaxRateReconciliationDeductionsExcessTaxBenefitsStockBasedCompensation",
"us-gaap:EffectiveIncomeTaxRateReconciliationFdiiPercent",
"us-gaap:EffectiveIncomeTaxRateReconciliationForeignIncomeTaxRateDifferential",
"us-gaap:EffectiveIncomeTaxRateReconciliationIntangiblePropertyTransfers",
"us-gaap:EffectiveIncomeTaxRateReconciliationInterestIncomeExpense",
"us-gaap:EffectiveIncomeTaxRateReconciliationOtherAdjustments",
"us-gaap:EffectiveIncomeTaxRateReconciliationStateAndLocalIncomeTaxes",
"us-gaap:EffectiveIncomeTaxRateReconciliationTaxCreditsResearch",
"us-gaap:EmployeeServiceShareBasedCompensationTaxBenefitFromCompensationExpense",
"us-gaap:UnrecognizedTaxBenefitsIncomeTaxPenaltiesAndInterestExpense",
"us-gaap:IncomeTaxesPaidNet",
"us-gaap:IncomeLossFromContinuingOperationsBeforeIncomeTaxesDomestic",
"us-gaap:IncomeLossFromContinuingOperationsBeforeIncomeTaxesForeign"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "debt_disclosure",
"statement": "disclosure",
"label": "Debt Disclosures",
"category": "debt",
"order": 200,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:LongTermDebtFairValue",
"us-gaap:LongTermDebtMaturitiesRepaymentsOfPrincipalInNextTwelveMonths",
"us-gaap:DebtInstrumentFaceAmount",
"us-gaap:DebtInstrumentInterestRateEffectivePercentage",
"us-gaap:DebtInstrumentInterestRateStatedPercentage",
"us-gaap:DebtInstrumentUnamortizedDiscountPremiumAndDebtIssuanceCostsNet"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "investment_securities_disclosure",
"statement": "disclosure",
"label": "Investment Securities Disclosures",
"category": "securities",
"order": 300,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:AvailableForSaleDebtSecuritiesAmortizedCostBasis",
"us-gaap:AvailableForSaleDebtSecuritiesAccumulatedGrossUnrealizedGainBeforeTax",
"us-gaap:AvailableForSaleDebtSecuritiesAccumulatedGrossUnrealizedLossBeforeTax",
"us-gaap:AvailableForSaleSecuritiesDebtMaturitiesNextRollingTwelveMonthsAmortizedCostBasis",
"us-gaap:AvailableForSaleSecuritiesDebtMaturitiesNextRollingTwelveMonthsFairValue",
"us-gaap:AvailableForSaleSecuritiesDebtMaturitiesRollingYearTwoThroughFiveAmortizedCostBasis",
"us-gaap:AvailableForSaleSecuritiesDebtMaturitiesRollingYearTwoThroughFiveFairValue",
"us-gaap:AvailableForSaleSecuritiesDebtMaturitiesRollingYearSixThroughTenAmortizedCostBasis",
"us-gaap:AvailableForSaleSecuritiesDebtMaturitiesRollingYearSixThroughTenFairValue",
"us-gaap:AvailableForSaleSecuritiesDebtMaturitiesRollingAfterYearTenAmortizedCostBasis",
"us-gaap:AvailableForSaleSecuritiesDebtMaturitiesRollingAfterYearTenFairValue",
"us-gaap:DebtSecuritiesAvailableForSaleContinuousUnrealizedLossPosition12MonthsOrLonger",
"us-gaap:DebtSecuritiesAvailableForSaleContinuousUnrealizedLossPosition12MonthsOrLongerAccumulatedLoss",
"us-gaap:DebtSecuritiesAvailableForSaleContinuousUnrealizedLossPositionLessThan12Months",
"us-gaap:DebtSecuritiesAvailableForSaleContinuousUnrealizedLossPositionLessThan12MonthsAccumulatedLoss",
"us-gaap:DebtSecuritiesAvailableForSaleRealizedGain",
"us-gaap:DebtSecuritiesAvailableForSaleRealizedLoss"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "derivative_instruments_disclosure",
"statement": "disclosure",
"label": "Derivative Instruments Disclosures",
"category": "derivatives",
"order": 400,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:DerivativeAssets",
"us-gaap:DerivativeAssetsCurrent",
"us-gaap:DerivativeAssetsNoncurrent",
"us-gaap:DerivativeLiabilities",
"us-gaap:DerivativeLiabilitiesCurrent",
"us-gaap:DerivativeLiabilitiesNoncurrent",
"us-gaap:DerivativeFairValueOfDerivativeAsset",
"us-gaap:DerivativeFairValueOfDerivativeLiability",
"us-gaap:DerivativeAssetFairValueGrossAssetIncludingNotSubjectToMasterNettingArrangement",
"us-gaap:DerivativeLiabilityFairValueGrossLiabilityIncludingNotSubjectToMasterNettingArrangement"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "lease_disclosure",
"statement": "disclosure",
"label": "Lease Obligations Disclosures",
"category": "leases",
"order": 500,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDue",
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsRemainderOfFiscalYear",
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueNextTwelveMonths",
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueYearTwo",
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueYearThree",
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueYearFour",
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsDueYearFive",
"us-gaap:LesseeOperatingLeaseLiabilityPaymentsThereafter",
"us-gaap:FinanceLeaseLiabilityPaymentsDue",
"us-gaap:FinanceLeaseLiabilityPaymentsRemainderOfFiscalYear",
"us-gaap:FinanceLeaseLiabilityPaymentsDueNextTwelveMonths",
"us-gaap:FinanceLeaseLiabilityPaymentsDueYearTwo",
"us-gaap:FinanceLeaseLiabilityPaymentsDueYearThree",
"us-gaap:FinanceLeaseLiabilityPaymentsDueYearFour",
"us-gaap:FinanceLeaseRightOfUseAssetAmortization"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "intangible_assets_disclosure",
"statement": "disclosure",
"label": "Intangible Assets Disclosures",
"category": "intangibles",
"order": 600,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:FiniteLivedIntangibleAssetsGross",
"us-gaap:FiniteLivedIntangibleAssetsAccumulatedAmortization",
"us-gaap:AmortizationOfIntangibleAssets",
"us-gaap:FiniteLivedIntangibleAssetsAmortizationExpenseRemainderOfFiscalYear",
"us-gaap:FiniteLivedIntangibleAssetsAmortizationExpenseNextTwelveMonths",
"us-gaap:FiniteLivedIntangibleAssetsAmortizationExpenseYearTwo",
"us-gaap:FiniteLivedIntangibleAssetsAmortizationExpenseYearThree"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "business_combinations_disclosure",
"statement": "disclosure",
"label": "Business Combinations Disclosures",
"category": "ma",
"order": 700,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:BusinessAcquisitionsProFormaNetIncomeLoss",
"us-gaap:BusinessAcquisitionsProFormaRevenue",
"us-gaap:BusinessCombinationProFormaInformationRevenueOfAcquireeSinceAcquisitionDateActual"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "revenue_disclosure",
"statement": "disclosure",
"label": "Revenue Disclosures",
"category": "revenue",
"order": 800,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:RevenueRemainingPerformanceObligation",
"us-gaap:RevenueRemainingPerformanceObligationPercentage"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
},
{
"surface_key": "cash_flow_disclosure",
"statement": "disclosure",
"label": "Cash Flow Disclosures",
"category": "cash_flow",
"order": 900,
"unit": "currency",
"rollup_policy": "aggregate_children",
"allowed_source_concepts": [
"us-gaap:CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsPeriodIncreaseDecreaseIncludingExchangeRateEffect"
],
"allowed_authoritative_concepts": [],
"formula_fallback": null,
"detail_grouping_policy": "group_all_children",
"materiality_policy": "disclosure"
}
]
}

View File

@@ -82,7 +82,8 @@ function bootstrapFreshDatabase(databaseUrl: string) {
'watchlist_item',
'filing_statement_snapshot',
'filing_taxonomy_snapshot',
'task_run'
'task_run',
'company_financial_bundle'
];
if (existingCoreTables.some((tableName) => hasTable(database, tableName))) {

View File

@@ -1,10 +1,22 @@
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import {
existsSync,
mkdirSync,
readdirSync,
readFileSync,
writeFileSync,
} from "node:fs";
import { join } from "node:path";
type FinancialUnit = 'currency' | 'percent' | 'ratio' | 'shares' | 'count';
type FinancialCadence = 'annual' | 'quarterly' | 'ltm';
type FinancialStatementKind = 'income' | 'balance' | 'cash_flow' | 'equity' | 'comprehensive_income';
type SignTransform = 'invert' | 'absolute';
type FinancialUnit = "currency" | "percent" | "ratio" | "shares" | "count";
type FinancialCadence = "annual" | "quarterly" | "ltm";
type FinancialStatementKind =
| "income"
| "balance"
| "cash_flow"
| "disclosure"
| "equity"
| "comprehensive_income";
type SignTransform = "invert" | "absolute";
type SurfaceDefinition = {
surface_key: string;
@@ -16,15 +28,18 @@ type SurfaceDefinition = {
rollup_policy?: string;
allowed_source_concepts: string[];
allowed_authoritative_concepts?: string[];
formula_fallback?: {
op: 'sum' | 'subtract' | 'divide';
sources: string[];
treat_null_as_zero?: boolean;
} | string | null;
formula_fallback?:
| {
op: "sum" | "subtract" | "divide";
sources: string[];
treat_null_as_zero?: boolean;
}
| string
| null;
detail_grouping_policy?: string;
materiality_policy?: string;
include_in_output?: boolean;
sign_transform?: 'invert';
sign_transform?: "invert";
};
type SurfacePackFile = {
@@ -34,11 +49,11 @@ type SurfacePackFile = {
};
type ComputationSpec =
| { type: 'ratio'; numerator: string; denominator: string }
| { type: 'yoy_growth'; source: string }
| { type: 'cagr'; source: string; years: number }
| { type: 'per_share'; source: string; shares_key: string }
| { type: 'simple'; formula: string };
| { type: "ratio"; numerator: string; denominator: string }
| { type: "yoy_growth"; source: string }
| { type: "cagr"; source: string; years: number }
| { type: "per_share"; source: string; shares_key: string }
| { type: "simple"; formula: string };
type ComputedDefinition = {
key: string;
@@ -69,10 +84,16 @@ type KpiPackFile = {
kpis: KpiDefinition[];
};
const TAXONOMY_DIR = join(process.cwd(), 'rust', 'taxonomy', 'fiscal', 'v1');
const OUTPUT_DIR = join(process.cwd(), 'lib', 'generated');
const TAXONOMY_DIR = join(process.cwd(), "rust", "taxonomy", "fiscal", "v1");
const OUTPUT_DIR = join(process.cwd(), "lib", "generated");
const PACK_ORDER = ['core', 'bank_lender', 'insurance', 'reit_real_estate', 'broker_asset_manager'] as const;
const PACK_ORDER = [
"core",
"bank_lender",
"insurance",
"reit_real_estate",
"broker_asset_manager",
] as const;
type PackName = (typeof PACK_ORDER)[number];
function log(message: string) {
@@ -88,7 +109,7 @@ function loadSurfacePacks(): Map<PackName, SurfacePackFile> {
continue;
}
const raw = readFileSync(path, 'utf8');
const raw = readFileSync(path, "utf8");
const file = JSON.parse(raw) as SurfacePackFile;
packs.set(pack, file);
}
@@ -105,7 +126,7 @@ function loadComputedPacks(): Map<PackName, ComputedPackFile> {
continue;
}
const raw = readFileSync(path, 'utf8');
const raw = readFileSync(path, "utf8");
const file = JSON.parse(raw) as ComputedPackFile;
packs.set(pack, file);
}
@@ -117,12 +138,12 @@ function loadKpiPacks(): Map<PackName, KpiPackFile> {
const packs = new Map<PackName, KpiPackFile>();
for (const pack of PACK_ORDER) {
const path = join(TAXONOMY_DIR, 'kpis', `${pack}.kpis.json`);
const path = join(TAXONOMY_DIR, "kpis", `${pack}.kpis.json`);
if (!existsSync(path)) {
continue;
}
const raw = readFileSync(path, 'utf8');
const raw = readFileSync(path, "utf8");
const file = JSON.parse(raw) as KpiPackFile;
packs.set(pack, file);
}
@@ -136,23 +157,40 @@ function validateSurfacePack(pack: SurfacePackFile, errors: string[]) {
for (const surface of pack.surfaces) {
const keySet = keysByStatement.get(surface.statement) || new Set<string>();
if (keySet.has(surface.surface_key)) {
errors.push(`${pack.pack}: duplicate surface_key "${surface.surface_key}" in statement "${surface.statement}"`);
errors.push(
`${pack.pack}: duplicate surface_key "${surface.surface_key}" in statement "${surface.statement}"`,
);
}
keySet.add(surface.surface_key);
keysByStatement.set(surface.statement, keySet);
if (!surface.label) {
errors.push(`${pack.pack}: surface "${surface.surface_key}" missing label`);
errors.push(
`${pack.pack}: surface "${surface.surface_key}" missing label`,
);
}
const validStatements: FinancialStatementKind[] = ['income', 'balance', 'cash_flow', 'equity', 'comprehensive_income'];
const validStatements: FinancialStatementKind[] = [
"income",
"balance",
"cash_flow",
"disclosure",
"equity",
"comprehensive_income",
];
if (!validStatements.includes(surface.statement)) {
errors.push(`${pack.pack}: surface "${surface.surface_key}" has invalid statement "${surface.statement}"`);
errors.push(
`${pack.pack}: surface "${surface.surface_key}" has invalid statement "${surface.statement}"`,
);
}
}
}
function validateComputedPack(pack: ComputedPackFile, surfaceKeys: Set<string>, errors: string[]) {
function validateComputedPack(
pack: ComputedPackFile,
surfaceKeys: Set<string>,
errors: string[],
) {
const keys = new Set<string>();
for (const computed of pack.computed) {
@@ -167,26 +205,39 @@ function validateComputedPack(pack: ComputedPackFile, surfaceKeys: Set<string>,
const spec = computed.computation;
switch (spec.type) {
case 'ratio':
if (!surfaceKeys.has(spec.numerator) && !spec.numerator.includes('_')) {
errors.push(`${pack.pack}: computed "${computed.key}" references unknown numerator "${spec.numerator}"`);
case "ratio":
if (!surfaceKeys.has(spec.numerator) && !spec.numerator.includes("_")) {
errors.push(
`${pack.pack}: computed "${computed.key}" references unknown numerator "${spec.numerator}"`,
);
}
if (!surfaceKeys.has(spec.denominator) && !spec.denominator.includes('_')) {
errors.push(`${pack.pack}: computed "${computed.key}" references unknown denominator "${spec.denominator}"`);
if (
!surfaceKeys.has(spec.denominator) &&
!spec.denominator.includes("_")
) {
errors.push(
`${pack.pack}: computed "${computed.key}" references unknown denominator "${spec.denominator}"`,
);
}
break;
case 'yoy_growth':
case 'cagr':
case "yoy_growth":
case "cagr":
if (!surfaceKeys.has(spec.source)) {
errors.push(`${pack.pack}: computed "${computed.key}" references unknown source "${spec.source}"`);
errors.push(
`${pack.pack}: computed "${computed.key}" references unknown source "${spec.source}"`,
);
}
break;
case 'per_share':
case "per_share":
if (!surfaceKeys.has(spec.source)) {
errors.push(`${pack.pack}: computed "${computed.key}" references unknown source "${spec.source}"`);
errors.push(
`${pack.pack}: computed "${computed.key}" references unknown source "${spec.source}"`,
);
}
if (!surfaceKeys.has(spec.shares_key)) {
errors.push(`${pack.pack}: computed "${computed.key}" references unknown shares_key "${spec.shares_key}"`);
errors.push(
`${pack.pack}: computed "${computed.key}" references unknown shares_key "${spec.shares_key}"`,
);
}
break;
}
@@ -201,7 +252,7 @@ export type FinancialUnit = 'currency' | 'percent' | 'ratio' | 'shares' | 'count
export type FinancialCadence = 'annual' | 'quarterly' | 'ltm';
export type FinancialStatementKind = 'income' | 'balance' | 'cash_flow' | 'equity' | 'comprehensive_income';
export type FinancialStatementKind = 'income' | 'balance' | 'cash_flow' | 'disclosure' | 'equity' | 'comprehensive_income';
export type SignTransform = 'invert' | 'absolute';
@@ -255,7 +306,10 @@ export type RatioCategory = (typeof RATIO_CATEGORIES)[number];
`;
}
function generateSurfaceFile(statement: string, surfaces: SurfaceDefinition[]): string {
function generateSurfaceFile(
statement: string,
surfaces: SurfaceDefinition[],
): string {
const sorted = [...surfaces].sort((a, b) => a.order - b.order);
const constName = `${statement.toUpperCase()}_SURFACES`;
@@ -268,14 +322,18 @@ export const ${constName}: SurfaceDefinition[] = ${JSON.stringify(sorted, null,
`;
}
function generateSurfacesIndex(surfacesByStatement: Map<string, SurfaceDefinition[]>): string {
function generateSurfacesIndex(
surfacesByStatement: Map<string, SurfaceDefinition[]>,
): string {
const statements = [...surfacesByStatement.keys()].sort();
const imports = statements
.map((s) => `import { ${s.toUpperCase()}_SURFACES } from './${s}';`)
.join('\n');
.join("\n");
const exports = statements.map((s) => ` ${s}: ${s.toUpperCase()}_SURFACES,`).join('\n');
const exports = statements
.map((s) => ` ${s}: ${s.toUpperCase()}_SURFACES,`)
.join("\n");
return `// Auto-generated by scripts/generate-taxonomy.ts
// DO NOT EDIT MANUALLY - changes will be overwritten
@@ -286,16 +344,16 @@ export const ALL_SURFACES_BY_STATEMENT = {
${exports}
} as const;
export { ${statements.map((s) => `${s.toUpperCase()}_SURFACES`).join(', ')} };
export { ${statements.map((s) => `${s.toUpperCase()}_SURFACES`).join(", ")} };
`;
}
function generateComputedFile(
name: string,
definitions: ComputedDefinition[]
definitions: ComputedDefinition[],
): string {
const sorted = [...definitions].sort((a, b) => a.order - b.order);
const constName = name.toUpperCase().replace(/-/g, '_');
const constName = name.toUpperCase().replace(/-/g, "_");
return `// Auto-generated by scripts/generate-taxonomy.ts
// DO NOT EDIT MANUALLY - changes will be overwritten
@@ -306,26 +364,32 @@ export const ${constName}: ComputedDefinition[] = ${JSON.stringify(sorted, null,
`;
}
function generateComputedIndex(files: { name: string; definitions: ComputedDefinition[] }[]): string {
function generateComputedIndex(
files: { name: string; definitions: ComputedDefinition[] }[],
): string {
const imports = files
.map((f) => {
const constName = f.name.toUpperCase().replace(/-/g, '_');
const constName = f.name.toUpperCase().replace(/-/g, "_");
return `import { ${constName} } from './${f.name}';`;
})
.join('\n');
.join("\n");
const allExports = files
.map((f) => ` ...${f.name.toUpperCase().replace(/-/g, '_')},`)
.join('\n');
.map((f) => ` ...${f.name.toUpperCase().replace(/-/g, "_")},`)
.join("\n");
const filingDerived = files
.flatMap((f) => f.definitions)
.filter((d) => !d.requires_external_data || d.requires_external_data.length === 0)
.filter(
(d) => !d.requires_external_data || d.requires_external_data.length === 0,
)
.sort((a, b) => a.order - b.order);
const marketDerived = files
.flatMap((f) => f.definitions)
.filter((d) => d.requires_external_data && d.requires_external_data.length > 0)
.filter(
(d) => d.requires_external_data && d.requires_external_data.length > 0,
)
.sort((a, b) => a.order - b.order);
return `// Auto-generated by scripts/generate-taxonomy.ts
@@ -343,12 +407,12 @@ export const FILING_DERIVED_COMPUTED: ComputedDefinition[] = ${JSON.stringify(fi
export const MARKET_DERIVED_COMPUTED: ComputedDefinition[] = ${JSON.stringify(marketDerived, null, 2)};
export { ${files.map((f) => f.name.toUpperCase().replace(/-/g, '_')).join(', ')} };
export { ${files.map((f) => f.name.toUpperCase().replace(/-/g, "_")).join(", ")} };
`;
}
function generateKpiFile(pack: string, kpis: KpiDefinition[]): string {
const constName = `${pack.toUpperCase().replace(/-/g, '_')}_KPIS`;
const constName = `${pack.toUpperCase().replace(/-/g, "_")}_KPIS`;
return `// Auto-generated by scripts/generate-taxonomy.ts
// DO NOT EDIT MANUALLY - changes will be overwritten
@@ -359,15 +423,19 @@ export const ${constName}: KpiDefinition[] = ${JSON.stringify(kpis, null, 2)};
`;
}
function generateKpiIndex(packs: { pack: string; kpis: KpiDefinition[] }[]): string {
function generateKpiIndex(
packs: { pack: string; kpis: KpiDefinition[] }[],
): string {
const imports = packs
.map((p) => {
const constName = p.pack.toUpperCase().replace(/-/g, '_');
const constName = p.pack.toUpperCase().replace(/-/g, "_");
return `import { ${constName}_KPIS } from './${p.pack}';`;
})
.join('\n');
.join("\n");
const exports = packs.map((p) => ` ...${p.pack.toUpperCase().replace(/-/g, '_')}_KPIS,`).join('\n');
const exports = packs
.map((p) => ` ...${p.pack.toUpperCase().replace(/-/g, "_")}_KPIS,`)
.join("\n");
return `// Auto-generated by scripts/generate-taxonomy.ts
// DO NOT EDIT MANUALLY - changes will be overwritten
@@ -380,7 +448,7 @@ export const ALL_KPIS: KpiDefinition[] = [
${exports}
];
export { ${packs.map((p) => `${p.pack.toUpperCase().replace(/-/g, '_')}_KPIS`).join(', ')} };
export { ${packs.map((p) => `${p.pack.toUpperCase().replace(/-/g, "_")}_KPIS`).join(", ")} };
`;
}
@@ -419,17 +487,19 @@ export { ALL_KPIS, CORE_KPIS } from './kpis';
}
async function main() {
log('Loading taxonomy files...');
log("Loading taxonomy files...");
const surfacePacks = loadSurfacePacks();
const computedPacks = loadComputedPacks();
const kpiPacks = loadKpiPacks();
log(`Loaded ${surfacePacks.size} surface packs, ${computedPacks.size} computed packs, ${kpiPacks.size} KPI packs`);
log(
`Loaded ${surfacePacks.size} surface packs, ${computedPacks.size} computed packs, ${kpiPacks.size} KPI packs`,
);
const errors: string[] = [];
log('Validating taxonomy files...');
log("Validating taxonomy files...");
for (const [, pack] of surfacePacks) {
validateSurfacePack(pack, errors);
@@ -447,23 +517,23 @@ async function main() {
}
if (errors.length > 0) {
console.error('Validation errors:');
console.error("Validation errors:");
for (const error of errors) {
console.error(` - ${error}`);
}
process.exit(1);
}
log('Creating output directories...');
mkdirSync(join(OUTPUT_DIR, 'surfaces'), { recursive: true });
mkdirSync(join(OUTPUT_DIR, 'computed'), { recursive: true });
mkdirSync(join(OUTPUT_DIR, 'kpis'), { recursive: true });
log("Creating output directories...");
mkdirSync(join(OUTPUT_DIR, "surfaces"), { recursive: true });
mkdirSync(join(OUTPUT_DIR, "computed"), { recursive: true });
mkdirSync(join(OUTPUT_DIR, "kpis"), { recursive: true });
log('Generating types...');
writeFileSync(join(OUTPUT_DIR, 'types.ts'), generateTypesFile());
log("Generating types...");
writeFileSync(join(OUTPUT_DIR, "types.ts"), generateTypesFile());
log('Generating surfaces...');
const coreSurfaces = surfacePacks.get('core');
log("Generating surfaces...");
const coreSurfaces = surfacePacks.get("core");
if (coreSurfaces) {
const surfacesByStatement = new Map<string, SurfaceDefinition[]>();
@@ -475,55 +545,67 @@ async function main() {
for (const [statement, surfaces] of surfacesByStatement) {
writeFileSync(
join(OUTPUT_DIR, 'surfaces', `${statement}.ts`),
generateSurfaceFile(statement, surfaces)
join(OUTPUT_DIR, "surfaces", `${statement}.ts`),
generateSurfaceFile(statement, surfaces),
);
}
writeFileSync(
join(OUTPUT_DIR, 'surfaces', 'index.ts'),
generateSurfacesIndex(surfacesByStatement)
join(OUTPUT_DIR, "surfaces", "index.ts"),
generateSurfacesIndex(surfacesByStatement),
);
}
log('Generating computed definitions...');
const computedFiles: { name: string; definitions: ComputedDefinition[] }[] = [];
log("Generating computed definitions...");
const computedFiles: { name: string; definitions: ComputedDefinition[] }[] =
[];
for (const [pack, file] of computedPacks) {
computedFiles.push({ name: pack, definitions: file.computed });
writeFileSync(
join(OUTPUT_DIR, 'computed', `${pack}.ts`),
generateComputedFile(pack, file.computed)
join(OUTPUT_DIR, "computed", `${pack}.ts`),
generateComputedFile(pack, file.computed),
);
}
writeFileSync(join(OUTPUT_DIR, 'computed', 'index.ts'), generateComputedIndex(computedFiles));
writeFileSync(
join(OUTPUT_DIR, "computed", "index.ts"),
generateComputedIndex(computedFiles),
);
log('Generating KPI definitions...');
log("Generating KPI definitions...");
const kpiFiles: { pack: string; kpis: KpiDefinition[] }[] = [];
for (const [pack, file] of kpiPacks) {
kpiFiles.push({ pack, kpis: file.kpis });
writeFileSync(
join(OUTPUT_DIR, 'kpis', `${pack}.ts`),
generateKpiFile(pack, file.kpis)
join(OUTPUT_DIR, "kpis", `${pack}.ts`),
generateKpiFile(pack, file.kpis),
);
}
writeFileSync(join(OUTPUT_DIR, 'kpis', 'index.ts'), generateKpiIndex(kpiFiles));
writeFileSync(
join(OUTPUT_DIR, "kpis", "index.ts"),
generateKpiIndex(kpiFiles),
);
log('Generating main index...');
writeFileSync(join(OUTPUT_DIR, 'index.ts'), generateMainIndex());
log("Generating main index...");
writeFileSync(join(OUTPUT_DIR, "index.ts"), generateMainIndex());
const surfaceCount = coreSurfaces?.surfaces.length || 0;
const computedCount = computedFiles.reduce((sum, f) => sum + f.definitions.length, 0);
const computedCount = computedFiles.reduce(
(sum, f) => sum + f.definitions.length,
0,
);
const kpiCount = kpiFiles.reduce((sum, f) => sum + f.kpis.length, 0);
log(`Generated ${surfaceCount} surfaces, ${computedCount} computed definitions, ${kpiCount} KPIs`);
log(
`Generated ${surfaceCount} surfaces, ${computedCount} computed definitions, ${kpiCount} KPIs`,
);
log(`Output written to ${OUTPUT_DIR}`);
}
main().catch((error) => {
console.error('Generation failed:', error);
console.error("Generation failed:", error);
process.exit(1);
});