From 14a7773504c045c066c7444b48ecceb218ec2644 Mon Sep 17 00:00:00 2001 From: francy51 Date: Mon, 16 Mar 2026 18:54:23 -0400 Subject: [PATCH] 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. --- agents.md | 8 +- drizzle/0012_crazy_molecule_man.sql | 361 ++ drizzle/meta/0012_snapshot.json | 3885 +++++++++++++++++++++ drizzle/meta/_journal.json | 9 +- lib/server/db/schema.ts | 1 + lib/server/repos/filing-statements.ts | 95 +- lib/server/repos/filing-taxonomy.ts | 3 + lib/server/sec.ts | 835 +++-- lib/server/task-processors.ts | 4 + lib/server/taxonomy/engine.test.ts | 1 + lib/server/taxonomy/parser-client.test.ts | 4 + lib/types.ts | 231 +- rust/taxonomy/crosswalk/us-gaap.json | 244 ++ rust/taxonomy/fiscal/v1/core.surface.json | 460 ++- scripts/dev.ts | 3 +- scripts/generate-taxonomy.ts | 266 +- 16 files changed, 5679 insertions(+), 731 deletions(-) create mode 100644 drizzle/0012_crazy_molecule_man.sql create mode 100644 drizzle/meta/0012_snapshot.json diff --git a/agents.md b/agents.md index b672244..4a029a4 100644 --- a/agents.md +++ b/agents.md @@ -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. diff --git a/drizzle/0012_crazy_molecule_man.sql b/drizzle/0012_crazy_molecule_man.sql new file mode 100644 index 0000000..84f8a4e --- /dev/null +++ b/drizzle/0012_crazy_molecule_man.sql @@ -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`); \ No newline at end of file diff --git a/drizzle/meta/0012_snapshot.json b/drizzle/meta/0012_snapshot.json new file mode 100644 index 0000000..0d6c50a --- /dev/null +++ b/drizzle/meta/0012_snapshot.json @@ -0,0 +1,3885 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "04c33cca-ec39-45cb-9b92-cf593a31664b", + "prevId": "cf403080-e012-41c0-93a2-52333bb44df1", + "tables": { + "account": { + "name": "account", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "accountId": { + "name": "accountId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerId": { + "name": "providerId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accessToken": { + "name": "accessToken", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "idToken": { + "name": "idToken", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "accessTokenExpiresAt": { + "name": "accessTokenExpiresAt", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refreshTokenExpiresAt": { + "name": "refreshTokenExpiresAt", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "company_financial_bundle": { + "name": "company_financial_bundle", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "surface_kind": { + "name": "surface_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cadence": { + "name": "cadence", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "bundle_version": { + "name": "bundle_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_snapshot_ids": { + "name": "source_snapshot_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_signature": { + "name": "source_signature", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "company_financial_bundle_uidx": { + "name": "company_financial_bundle_uidx", + "columns": [ + "ticker", + "surface_kind", + "cadence" + ], + "isUnique": true + }, + "company_financial_bundle_ticker_idx": { + "name": "company_financial_bundle_ticker_idx", + "columns": [ + "ticker", + "updated_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "company_overview_cache": { + "name": "company_overview_cache", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cache_version": { + "name": "cache_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_signature": { + "name": "source_signature", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "company_overview_cache_uidx": { + "name": "company_overview_cache_uidx", + "columns": [ + "user_id", + "ticker" + ], + "isUnique": true + }, + "company_overview_cache_lookup_idx": { + "name": "company_overview_cache_lookup_idx", + "columns": [ + "user_id", + "ticker", + "updated_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "company_overview_cache_user_id_user_id_fk": { + "name": "company_overview_cache_user_id_user_id_fk", + "tableFrom": "company_overview_cache", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing": { + "name": "filing", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filing_type": { + "name": "filing_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filing_date": { + "name": "filing_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accession_number": { + "name": "accession_number", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cik": { + "name": "cik", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "company_name": { + "name": "company_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filing_url": { + "name": "filing_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "submission_url": { + "name": "submission_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "primary_document": { + "name": "primary_document", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metrics": { + "name": "metrics", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "analysis": { + "name": "analysis", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_accession_uidx": { + "name": "filing_accession_uidx", + "columns": [ + "accession_number" + ], + "isUnique": true + }, + "filing_ticker_date_idx": { + "name": "filing_ticker_date_idx", + "columns": [ + "ticker", + "filing_date" + ], + "isUnique": false + }, + "filing_date_idx": { + "name": "filing_date_idx", + "columns": [ + "filing_date" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing_link": { + "name": "filing_link", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "filing_id": { + "name": "filing_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "link_type": { + "name": "link_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'sec'" + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_link_unique_uidx": { + "name": "filing_link_unique_uidx", + "columns": [ + "filing_id", + "url" + ], + "isUnique": true + }, + "filing_link_filing_idx": { + "name": "filing_link_filing_idx", + "columns": [ + "filing_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "filing_link_filing_id_filing_id_fk": { + "name": "filing_link_filing_id_filing_id_fk", + "tableFrom": "filing_link", + "tableTo": "filing", + "columnsFrom": [ + "filing_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing_statement_snapshot": { + "name": "filing_statement_snapshot", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "filing_id": { + "name": "filing_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filing_date": { + "name": "filing_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filing_type": { + "name": "filing_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "period_end": { + "name": "period_end", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "statement_bundle": { + "name": "statement_bundle", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "standardized_bundle": { + "name": "standardized_bundle", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dimension_bundle": { + "name": "dimension_bundle", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "parse_status": { + "name": "parse_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parse_error": { + "name": "parse_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_stmt_filing_uidx": { + "name": "filing_stmt_filing_uidx", + "columns": [ + "filing_id" + ], + "isUnique": true + }, + "filing_stmt_ticker_date_idx": { + "name": "filing_stmt_ticker_date_idx", + "columns": [ + "ticker", + "filing_date" + ], + "isUnique": false + }, + "filing_stmt_date_idx": { + "name": "filing_stmt_date_idx", + "columns": [ + "filing_date" + ], + "isUnique": false + }, + "filing_stmt_status_idx": { + "name": "filing_stmt_status_idx", + "columns": [ + "parse_status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "filing_statement_snapshot_filing_id_filing_id_fk": { + "name": "filing_statement_snapshot_filing_id_filing_id_fk", + "tableFrom": "filing_statement_snapshot", + "tableTo": "filing", + "columnsFrom": [ + "filing_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing_taxonomy_asset": { + "name": "filing_taxonomy_asset", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "snapshot_id": { + "name": "snapshot_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "asset_type": { + "name": "asset_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "score": { + "name": "score", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_selected": { + "name": "is_selected", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_taxonomy_asset_snapshot_idx": { + "name": "filing_taxonomy_asset_snapshot_idx", + "columns": [ + "snapshot_id" + ], + "isUnique": false + }, + "filing_taxonomy_asset_type_idx": { + "name": "filing_taxonomy_asset_type_idx", + "columns": [ + "snapshot_id", + "asset_type" + ], + "isUnique": false + } + }, + "foreignKeys": { + "filing_taxonomy_asset_snapshot_id_filing_taxonomy_snapshot_id_fk": { + "name": "filing_taxonomy_asset_snapshot_id_filing_taxonomy_snapshot_id_fk", + "tableFrom": "filing_taxonomy_asset", + "tableTo": "filing_taxonomy_snapshot", + "columnsFrom": [ + "snapshot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing_taxonomy_concept": { + "name": "filing_taxonomy_concept", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "snapshot_id": { + "name": "snapshot_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "concept_key": { + "name": "concept_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qname": { + "name": "qname", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "namespace_uri": { + "name": "namespace_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "local_name": { + "name": "local_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_extension": { + "name": "is_extension", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "balance": { + "name": "balance", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "period_type": { + "name": "period_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "data_type": { + "name": "data_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "statement_kind": { + "name": "statement_kind", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role_uri": { + "name": "role_uri", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "authoritative_concept_key": { + "name": "authoritative_concept_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mapping_method": { + "name": "mapping_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "surface_key": { + "name": "surface_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "detail_parent_surface_key": { + "name": "detail_parent_surface_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "kpi_key": { + "name": "kpi_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "residual_flag": { + "name": "residual_flag", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "presentation_order": { + "name": "presentation_order", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "presentation_depth": { + "name": "presentation_depth", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "parent_concept_key": { + "name": "parent_concept_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_abstract": { + "name": "is_abstract", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_taxonomy_concept_snapshot_idx": { + "name": "filing_taxonomy_concept_snapshot_idx", + "columns": [ + "snapshot_id" + ], + "isUnique": false + }, + "filing_taxonomy_concept_statement_idx": { + "name": "filing_taxonomy_concept_statement_idx", + "columns": [ + "snapshot_id", + "statement_kind" + ], + "isUnique": false + }, + "filing_taxonomy_concept_uidx": { + "name": "filing_taxonomy_concept_uidx", + "columns": [ + "snapshot_id", + "concept_key", + "role_uri", + "presentation_order" + ], + "isUnique": true + } + }, + "foreignKeys": { + "filing_taxonomy_concept_snapshot_id_filing_taxonomy_snapshot_id_fk": { + "name": "filing_taxonomy_concept_snapshot_id_filing_taxonomy_snapshot_id_fk", + "tableFrom": "filing_taxonomy_concept", + "tableTo": "filing_taxonomy_snapshot", + "columnsFrom": [ + "snapshot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing_taxonomy_context": { + "name": "filing_taxonomy_context", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "snapshot_id": { + "name": "snapshot_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "context_id": { + "name": "context_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "entity_identifier": { + "name": "entity_identifier", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "entity_scheme": { + "name": "entity_scheme", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "period_start": { + "name": "period_start", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "period_end": { + "name": "period_end", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "period_instant": { + "name": "period_instant", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "segment_json": { + "name": "segment_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scenario_json": { + "name": "scenario_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_taxonomy_context_snapshot_idx": { + "name": "filing_taxonomy_context_snapshot_idx", + "columns": [ + "snapshot_id" + ], + "isUnique": false + }, + "filing_taxonomy_context_uidx": { + "name": "filing_taxonomy_context_uidx", + "columns": [ + "snapshot_id", + "context_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "filing_taxonomy_context_snapshot_id_filing_taxonomy_snapshot_id_fk": { + "name": "filing_taxonomy_context_snapshot_id_filing_taxonomy_snapshot_id_fk", + "tableFrom": "filing_taxonomy_context", + "tableTo": "filing_taxonomy_snapshot", + "columnsFrom": [ + "snapshot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing_taxonomy_fact": { + "name": "filing_taxonomy_fact", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "snapshot_id": { + "name": "snapshot_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "concept_key": { + "name": "concept_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "qname": { + "name": "qname", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "namespace_uri": { + "name": "namespace_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "local_name": { + "name": "local_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "data_type": { + "name": "data_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "statement_kind": { + "name": "statement_kind", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role_uri": { + "name": "role_uri", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "authoritative_concept_key": { + "name": "authoritative_concept_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mapping_method": { + "name": "mapping_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "surface_key": { + "name": "surface_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "detail_parent_surface_key": { + "name": "detail_parent_surface_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "kpi_key": { + "name": "kpi_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "residual_flag": { + "name": "residual_flag", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "context_id": { + "name": "context_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "decimals": { + "name": "decimals", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "precision": { + "name": "precision", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "nil": { + "name": "nil", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "value_num": { + "name": "value_num", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "period_start": { + "name": "period_start", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "period_end": { + "name": "period_end", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "period_instant": { + "name": "period_instant", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dimensions": { + "name": "dimensions", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_dimensionless": { + "name": "is_dimensionless", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "source_file": { + "name": "source_file", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_taxonomy_fact_snapshot_idx": { + "name": "filing_taxonomy_fact_snapshot_idx", + "columns": [ + "snapshot_id" + ], + "isUnique": false + }, + "filing_taxonomy_fact_concept_idx": { + "name": "filing_taxonomy_fact_concept_idx", + "columns": [ + "snapshot_id", + "concept_key" + ], + "isUnique": false + }, + "filing_taxonomy_fact_period_idx": { + "name": "filing_taxonomy_fact_period_idx", + "columns": [ + "snapshot_id", + "period_end", + "period_instant" + ], + "isUnique": false + }, + "filing_taxonomy_fact_statement_idx": { + "name": "filing_taxonomy_fact_statement_idx", + "columns": [ + "snapshot_id", + "statement_kind" + ], + "isUnique": false + } + }, + "foreignKeys": { + "filing_taxonomy_fact_snapshot_id_filing_taxonomy_snapshot_id_fk": { + "name": "filing_taxonomy_fact_snapshot_id_filing_taxonomy_snapshot_id_fk", + "tableFrom": "filing_taxonomy_fact", + "tableTo": "filing_taxonomy_snapshot", + "columnsFrom": [ + "snapshot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing_taxonomy_metric_validation": { + "name": "filing_taxonomy_metric_validation", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "snapshot_id": { + "name": "snapshot_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metric_key": { + "name": "metric_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "taxonomy_value": { + "name": "taxonomy_value", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "llm_value": { + "name": "llm_value", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "absolute_diff": { + "name": "absolute_diff", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "relative_diff": { + "name": "relative_diff", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "evidence_pages": { + "name": "evidence_pages", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pdf_url": { + "name": "pdf_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_taxonomy_metric_validation_snapshot_idx": { + "name": "filing_taxonomy_metric_validation_snapshot_idx", + "columns": [ + "snapshot_id" + ], + "isUnique": false + }, + "filing_taxonomy_metric_validation_status_idx": { + "name": "filing_taxonomy_metric_validation_status_idx", + "columns": [ + "snapshot_id", + "status" + ], + "isUnique": false + }, + "filing_taxonomy_metric_validation_uidx": { + "name": "filing_taxonomy_metric_validation_uidx", + "columns": [ + "snapshot_id", + "metric_key" + ], + "isUnique": true + } + }, + "foreignKeys": { + "filing_taxonomy_metric_validation_snapshot_id_filing_taxonomy_snapshot_id_fk": { + "name": "filing_taxonomy_metric_validation_snapshot_id_filing_taxonomy_snapshot_id_fk", + "tableFrom": "filing_taxonomy_metric_validation", + "tableTo": "filing_taxonomy_snapshot", + "columnsFrom": [ + "snapshot_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "filing_taxonomy_snapshot": { + "name": "filing_taxonomy_snapshot", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "filing_id": { + "name": "filing_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filing_date": { + "name": "filing_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filing_type": { + "name": "filing_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parse_status": { + "name": "parse_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parse_error": { + "name": "parse_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parser_engine": { + "name": "parser_engine", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'fiscal-xbrl'" + }, + "parser_version": { + "name": "parser_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unknown'" + }, + "taxonomy_regime": { + "name": "taxonomy_regime", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unknown'" + }, + "fiscal_pack": { + "name": "fiscal_pack", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "periods": { + "name": "periods", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "faithful_rows": { + "name": "faithful_rows", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "statement_rows": { + "name": "statement_rows", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "surface_rows": { + "name": "surface_rows", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "detail_rows": { + "name": "detail_rows", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "kpi_rows": { + "name": "kpi_rows", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "computed_definitions": { + "name": "computed_definitions", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "derived_metrics": { + "name": "derived_metrics", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "validation_result": { + "name": "validation_result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "normalization_summary": { + "name": "normalization_summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "facts_count": { + "name": "facts_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "concepts_count": { + "name": "concepts_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "dimensions_count": { + "name": "dimensions_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "filing_taxonomy_snapshot_filing_uidx": { + "name": "filing_taxonomy_snapshot_filing_uidx", + "columns": [ + "filing_id" + ], + "isUnique": true + }, + "filing_taxonomy_snapshot_ticker_date_idx": { + "name": "filing_taxonomy_snapshot_ticker_date_idx", + "columns": [ + "ticker", + "filing_date" + ], + "isUnique": false + }, + "filing_taxonomy_snapshot_status_idx": { + "name": "filing_taxonomy_snapshot_status_idx", + "columns": [ + "parse_status" + ], + "isUnique": false + } + }, + "foreignKeys": { + "filing_taxonomy_snapshot_filing_id_filing_id_fk": { + "name": "filing_taxonomy_snapshot_filing_id_filing_id_fk", + "tableFrom": "filing_taxonomy_snapshot", + "tableTo": "filing", + "columnsFrom": [ + "filing_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "holding": { + "name": "holding", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "company_name": { + "name": "company_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "shares": { + "name": "shares", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avg_cost": { + "name": "avg_cost", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "current_price": { + "name": "current_price", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "market_value": { + "name": "market_value", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gain_loss": { + "name": "gain_loss", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gain_loss_pct": { + "name": "gain_loss_pct", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_price_at": { + "name": "last_price_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "holding_user_ticker_uidx": { + "name": "holding_user_ticker_uidx", + "columns": [ + "user_id", + "ticker" + ], + "isUnique": true + }, + "holding_user_idx": { + "name": "holding_user_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "holding_user_id_user_id_fk": { + "name": "holding_user_id_user_id_fk", + "tableFrom": "holding", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invitation": { + "name": "invitation", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "expiresAt": { + "name": "expiresAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inviterId": { + "name": "inviterId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "invitation_organizationId_idx": { + "name": "invitation_organizationId_idx", + "columns": [ + "organizationId" + ], + "isUnique": false + }, + "invitation_email_idx": { + "name": "invitation_email_idx", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": { + "invitation_organizationId_organization_id_fk": { + "name": "invitation_organizationId_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviterId_user_id_fk": { + "name": "invitation_inviterId_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviterId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "member": { + "name": "member", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "member_organizationId_idx": { + "name": "member_organizationId_idx", + "columns": [ + "organizationId" + ], + "isUnique": false + }, + "member_userId_idx": { + "name": "member_userId_idx", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "member_organizationId_organization_id_fk": { + "name": "member_organizationId_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_userId_user_id_fk": { + "name": "member_userId_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organization": { + "name": "organization", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "organization_slug_uidx": { + "name": "organization_slug_uidx", + "columns": [ + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "portfolio_insight": { + "name": "portfolio_insight", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "insight_user_created_idx": { + "name": "insight_user_created_idx", + "columns": [ + "user_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "portfolio_insight_user_id_user_id_fk": { + "name": "portfolio_insight_user_id_user_id_fk", + "tableFrom": "portfolio_insight", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "research_artifact": { + "name": "research_artifact", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accession_number": { + "name": "accession_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'user'" + }, + "subtype": { + "name": "subtype", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "body_markdown": { + "name": "body_markdown", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "search_text": { + "name": "search_text", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "visibility_scope": { + "name": "visibility_scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'private'" + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "file_size_bytes": { + "name": "file_size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "storage_path": { + "name": "storage_path", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "research_artifact_ticker_idx": { + "name": "research_artifact_ticker_idx", + "columns": [ + "user_id", + "ticker", + "updated_at" + ], + "isUnique": false + }, + "research_artifact_kind_idx": { + "name": "research_artifact_kind_idx", + "columns": [ + "user_id", + "kind", + "updated_at" + ], + "isUnique": false + }, + "research_artifact_accession_idx": { + "name": "research_artifact_accession_idx", + "columns": [ + "user_id", + "accession_number" + ], + "isUnique": false + }, + "research_artifact_source_idx": { + "name": "research_artifact_source_idx", + "columns": [ + "user_id", + "source", + "updated_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "research_artifact_user_id_user_id_fk": { + "name": "research_artifact_user_id_user_id_fk", + "tableFrom": "research_artifact", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "research_artifact_organization_id_organization_id_fk": { + "name": "research_artifact_organization_id_organization_id_fk", + "tableFrom": "research_artifact", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "research_journal_entry": { + "name": "research_journal_entry", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accession_number": { + "name": "accession_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "entry_type": { + "name": "entry_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "body_markdown": { + "name": "body_markdown", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "research_journal_ticker_idx": { + "name": "research_journal_ticker_idx", + "columns": [ + "user_id", + "ticker", + "created_at" + ], + "isUnique": false + }, + "research_journal_accession_idx": { + "name": "research_journal_accession_idx", + "columns": [ + "user_id", + "accession_number" + ], + "isUnique": false + } + }, + "foreignKeys": { + "research_journal_entry_user_id_user_id_fk": { + "name": "research_journal_entry_user_id_user_id_fk", + "tableFrom": "research_journal_entry", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "research_memo": { + "name": "research_memo", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rating": { + "name": "rating", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "conviction": { + "name": "conviction", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_horizon_months": { + "name": "time_horizon_months", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "packet_title": { + "name": "packet_title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "packet_subtitle": { + "name": "packet_subtitle", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "thesis_markdown": { + "name": "thesis_markdown", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "variant_view_markdown": { + "name": "variant_view_markdown", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "catalysts_markdown": { + "name": "catalysts_markdown", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "risks_markdown": { + "name": "risks_markdown", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "disconfirming_evidence_markdown": { + "name": "disconfirming_evidence_markdown", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "next_actions_markdown": { + "name": "next_actions_markdown", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "research_memo_ticker_uidx": { + "name": "research_memo_ticker_uidx", + "columns": [ + "user_id", + "ticker" + ], + "isUnique": true + }, + "research_memo_updated_idx": { + "name": "research_memo_updated_idx", + "columns": [ + "user_id", + "updated_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "research_memo_user_id_user_id_fk": { + "name": "research_memo_user_id_user_id_fk", + "tableFrom": "research_memo", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "research_memo_organization_id_organization_id_fk": { + "name": "research_memo_organization_id_organization_id_fk", + "tableFrom": "research_memo", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "research_memo_evidence": { + "name": "research_memo_evidence", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "memo_id": { + "name": "memo_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "artifact_id": { + "name": "artifact_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "section": { + "name": "section", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "annotation": { + "name": "annotation", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "research_memo_evidence_memo_idx": { + "name": "research_memo_evidence_memo_idx", + "columns": [ + "memo_id", + "section", + "sort_order" + ], + "isUnique": false + }, + "research_memo_evidence_artifact_idx": { + "name": "research_memo_evidence_artifact_idx", + "columns": [ + "artifact_id" + ], + "isUnique": false + }, + "research_memo_evidence_unique_uidx": { + "name": "research_memo_evidence_unique_uidx", + "columns": [ + "memo_id", + "artifact_id", + "section" + ], + "isUnique": true + } + }, + "foreignKeys": { + "research_memo_evidence_memo_id_research_memo_id_fk": { + "name": "research_memo_evidence_memo_id_research_memo_id_fk", + "tableFrom": "research_memo_evidence", + "tableTo": "research_memo", + "columnsFrom": [ + "memo_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "research_memo_evidence_artifact_id_research_artifact_id_fk": { + "name": "research_memo_evidence_artifact_id_research_artifact_id_fk", + "tableFrom": "research_memo_evidence", + "tableTo": "research_artifact", + "columnsFrom": [ + "artifact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "search_chunk": { + "name": "search_chunk", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "document_id": { + "name": "document_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "chunk_index": { + "name": "chunk_index", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "chunk_text": { + "name": "chunk_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "char_count": { + "name": "char_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_offset": { + "name": "start_offset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_offset": { + "name": "end_offset", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "heading_path": { + "name": "heading_path", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "citation_label": { + "name": "citation_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "search_chunk_document_chunk_uidx": { + "name": "search_chunk_document_chunk_uidx", + "columns": [ + "document_id", + "chunk_index" + ], + "isUnique": true + }, + "search_chunk_document_idx": { + "name": "search_chunk_document_idx", + "columns": [ + "document_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "search_chunk_document_id_search_document_id_fk": { + "name": "search_chunk_document_id_search_document_id_fk", + "tableFrom": "search_chunk", + "tableTo": "search_document", + "columnsFrom": [ + "document_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "search_document": { + "name": "search_document", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "source_kind": { + "name": "source_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_ref": { + "name": "source_ref", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "accession_number": { + "name": "accession_number", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content_text": { + "name": "content_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content_hash": { + "name": "content_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "index_status": { + "name": "index_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "indexed_at": { + "name": "indexed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "search_document_source_uidx": { + "name": "search_document_source_uidx", + "columns": [ + "scope", + "ifnull(\"user_id\", '')", + "source_kind", + "source_ref" + ], + "isUnique": true + }, + "search_document_scope_idx": { + "name": "search_document_scope_idx", + "columns": [ + "scope", + "source_kind", + "ticker", + "updated_at" + ], + "isUnique": false + }, + "search_document_accession_idx": { + "name": "search_document_accession_idx", + "columns": [ + "accession_number", + "source_kind" + ], + "isUnique": false + } + }, + "foreignKeys": { + "search_document_user_id_user_id_fk": { + "name": "search_document_user_id_user_id_fk", + "tableFrom": "search_document", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expiresAt": { + "name": "expiresAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "userAgent": { + "name": "userAgent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "impersonatedBy": { + "name": "impersonatedBy", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "activeOrganizationId": { + "name": "activeOrganizationId", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "session_token_uidx": { + "name": "session_token_uidx", + "columns": [ + "token" + ], + "isUnique": true + }, + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "task_run": { + "name": "task_run", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "task_type": { + "name": "task_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "stage": { + "name": "stage", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "stage_detail": { + "name": "stage_detail", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stage_context": { + "name": "stage_context", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "resource_key": { + "name": "resource_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notification_read_at": { + "name": "notification_read_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notification_silenced_at": { + "name": "notification_silenced_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "max_attempts": { + "name": "max_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workflow_run_id": { + "name": "workflow_run_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "finished_at": { + "name": "finished_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "task_user_created_idx": { + "name": "task_user_created_idx", + "columns": [ + "user_id", + "created_at" + ], + "isUnique": false + }, + "task_user_updated_idx": { + "name": "task_user_updated_idx", + "columns": [ + "user_id", + "updated_at" + ], + "isUnique": false + }, + "task_status_idx": { + "name": "task_status_idx", + "columns": [ + "status" + ], + "isUnique": false + }, + "task_user_resource_status_idx": { + "name": "task_user_resource_status_idx", + "columns": [ + "user_id", + "task_type", + "resource_key", + "status", + "created_at" + ], + "isUnique": false + }, + "task_workflow_run_uidx": { + "name": "task_workflow_run_uidx", + "columns": [ + "workflow_run_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "task_run_user_id_user_id_fk": { + "name": "task_run_user_id_user_id_fk", + "tableFrom": "task_run", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "task_stage_event": { + "name": "task_stage_event", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "task_id": { + "name": "task_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "stage": { + "name": "stage", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "stage_detail": { + "name": "stage_detail", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stage_context": { + "name": "stage_context", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "task_stage_event_task_created_idx": { + "name": "task_stage_event_task_created_idx", + "columns": [ + "task_id", + "created_at" + ], + "isUnique": false + }, + "task_stage_event_user_created_idx": { + "name": "task_stage_event_user_created_idx", + "columns": [ + "user_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "task_stage_event_task_id_task_run_id_fk": { + "name": "task_stage_event_task_id_task_run_id_fk", + "tableFrom": "task_stage_event", + "tableTo": "task_run", + "columnsFrom": [ + "task_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "task_stage_event_user_id_user_id_fk": { + "name": "task_stage_event_user_id_user_id_fk", + "tableFrom": "task_stage_event", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "banned": { + "name": "banned", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "banReason": { + "name": "banReason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "banExpires": { + "name": "banExpires", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "user_email_uidx": { + "name": "user_email_uidx", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expiresAt": { + "name": "expiresAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "watchlist_item": { + "name": "watchlist_item", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ticker": { + "name": "ticker", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "company_name": { + "name": "company_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sector": { + "name": "sector", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'backlog'" + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'medium'" + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_reviewed_at": { + "name": "last_reviewed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "watchlist_user_ticker_uidx": { + "name": "watchlist_user_ticker_uidx", + "columns": [ + "user_id", + "ticker" + ], + "isUnique": true + }, + "watchlist_user_created_idx": { + "name": "watchlist_user_created_idx", + "columns": [ + "user_id", + "created_at" + ], + "isUnique": false + }, + "watchlist_user_updated_idx": { + "name": "watchlist_user_updated_idx", + "columns": [ + "user_id", + "updated_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "watchlist_item_user_id_user_id_fk": { + "name": "watchlist_item_user_id_user_id_fk", + "tableFrom": "watchlist_item", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": { + "search_document_source_uidx": { + "columns": { + "ifnull(\"user_id\", '')": { + "isExpression": true + } + } + } + } + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 1924a18..af99de4 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -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 } ] -} +} \ No newline at end of file diff --git a/lib/server/db/schema.ts b/lib/server/db/schema.ts index 396c81a..89bd00c 100644 --- a/lib/server/db/schema.ts +++ b/lib/server/db/schema.ts @@ -99,6 +99,7 @@ type FinancialStatementKind = | "income" | "balance" | "cash_flow" + | "disclosure" | "equity" | "comprehensive_income"; diff --git a/lib/server/repos/filing-statements.ts b/lib/server/repos/filing-statements.ts index 6d0e4ab..115be68 100644 --- a/lib/server/repos/filing-statements.ts +++ b/lib/server/repos/filing-statements.ts @@ -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; @@ -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; + statements: Record< + FinancialStatementKind, + FilingFaithfulStatementSnapshotRow[] + >; }; export type StandardizedStatementBundle = { periods: FilingStatementSnapshotPeriod[]; - statements: Record; + 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`count(*)` + count: sql`count(*)`, }) .from(filingStatementSnapshot) .where(eq(filingStatementSnapshot.ticker, ticker.trim().toUpperCase())) .groupBy(filingStatementSnapshot.parse_status); - return rows.reduce>((acc, row) => { - acc[row.status] = Number(row.count); - return acc; - }, { - ready: 0, - partial: 0, - failed: 0 - }); + return rows.reduce>( + (acc, row) => { + acc[row.status] = Number(row.count); + return acc; + }, + { + ready: 0, + partial: 0, + failed: 0, + }, + ); } diff --git a/lib/server/repos/filing-taxonomy.ts b/lib/server/repos/filing-taxonomy.ts index 9eb7456..a4149eb 100644 --- a/lib/server/repos/filing-taxonomy.ts +++ b/lib/server/repos/filing-taxonomy.ts @@ -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: {}, }; diff --git a/lib/server/sec.ts b/lib/server/sec.ts index 58b945e..5431b3c 100644 --- a/lib/server/sec.ts +++ b/lib/server/sec.ts @@ -1,4 +1,4 @@ -import type { Filing, FinancialStatementKind } from '@/lib/types'; +import type { Filing, FinancialStatementKind } from "@/lib/types"; import type { DimensionStatementBundle, DimensionStatementSnapshotRow, @@ -6,11 +6,11 @@ import type { FilingStatementBundle, FilingStatementSnapshotPeriod, StandardizedStatementBundle, - StandardizedStatementSnapshotRow -} from '@/lib/server/repos/filing-statements'; + StandardizedStatementSnapshotRow, +} from "@/lib/server/repos/filing-statements"; -type FilingType = Filing['filing_type']; -type FilingMetrics = NonNullable; +type FilingType = Filing["filing_type"]; +type FilingMetrics = NonNullable; type TickerDirectoryRecord = { cik_str: number; @@ -33,7 +33,7 @@ type RecentFilingsPayload = { type CompanyFactsPayload = { facts?: { - 'us-gaap'?: Record }>; + "us-gaap"?: Record }>; }; }; @@ -73,7 +73,7 @@ type FetchPrimaryFilingTextOptions = { }; export type FilingDocumentText = { - source: 'primary_document'; + source: "primary_document"; url: string; text: string; truncated: boolean; @@ -85,35 +85,35 @@ type FilingMetricsLookupInput = { filingType: FilingType; }; -const SUPPORTED_FORMS: FilingType[] = ['10-K', '10-Q', '8-K']; +const SUPPORTED_FORMS: FilingType[] = ["10-K", "10-Q", "8-K"]; const TICKER_CACHE_TTL_MS = 1000 * 60 * 60 * 12; const FILING_TEXT_MAX_CHARS = 24_000; const METRIC_TAGS = { revenue: [ - 'Revenues', - 'SalesRevenueNet', - 'RevenueFromContractWithCustomerExcludingAssessedTax', - 'TotalRevenuesAndOtherIncome' + "Revenues", + "SalesRevenueNet", + "RevenueFromContractWithCustomerExcludingAssessedTax", + "TotalRevenuesAndOtherIncome", ], - netIncome: ['NetIncomeLoss', 'ProfitLoss'], - totalAssets: ['Assets'], + netIncome: ["NetIncomeLoss", "ProfitLoss"], + totalAssets: ["Assets"], cash: [ - 'CashAndCashEquivalentsAtCarryingValue', - 'CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents' + "CashAndCashEquivalentsAtCarryingValue", + "CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents", ], debt: [ - 'LongTermDebtAndCapitalLeaseObligations', - 'LongTermDebtNoncurrent', - 'LongTermDebt', - 'DebtAndFinanceLeaseLiabilities' - ] + "LongTermDebtAndCapitalLeaseObligations", + "LongTermDebtNoncurrent", + "LongTermDebt", + "DebtAndFinanceLeaseLiabilities", + ], } as const; let tickerCache = new Map(); let tickerCacheLoadedAt = 0; function envUserAgent() { - return process.env.SEC_USER_AGENT || 'Fiscal Clone '; + return process.env.SEC_USER_AGENT || "Fiscal Clone "; } function todayIso() { @@ -123,23 +123,23 @@ function todayIso() { function decodeHtmlEntities(value: string) { const decodeCodePoint = (code: number) => { if (!Number.isFinite(code) || code < 0 || code > 0x10ffff) { - return ' '; + return " "; } try { return String.fromCodePoint(code); } catch { - return ' '; + return " "; } }; return value - .replace(/ | /gi, ' ') - .replace(/&/gi, '&') - .replace(/</gi, '<') - .replace(/>/gi, '>') + .replace(/ | /gi, " ") + .replace(/&/gi, "&") + .replace(/</gi, "<") + .replace(/>/gi, ">") .replace(/"/gi, '"') - .replace(/'/gi, '\'') + .replace(/'/gi, "'") .replace(/&#x([0-9a-f]+);/gi, (_match, rawCode: string) => { const code = Number.parseInt(rawCode, 16); return decodeCodePoint(code); @@ -153,54 +153,60 @@ function decodeHtmlEntities(value: string) { export function normalizeSecDocumentText(raw: string) { return decodeHtmlEntities( raw - .replace(/\r/g, '\n') - .replace(//gi, ' ') - .replace(//gi, ' ') - .replace(//gi, ' ') - .replace(//g, ' ') - .replace(/<\/?(p|div|section|article|li|tr|td|th|h[1-6]|br|hr)[^>]*>/gi, '\n') - .replace(/<[^>]+>/g, ' ') + .replace(/\r/g, "\n") + .replace(//gi, " ") + .replace(//gi, " ") + .replace(//gi, " ") + .replace(//g, " ") + .replace( + /<\/?(p|div|section|article|li|tr|td|th|h[1-6]|br|hr)[^>]*>/gi, + "\n", + ) + .replace(/<[^>]+>/g, " "), ) - .replace(/[ \t]+\n/g, '\n') - .replace(/\n[ \t]+/g, '\n') - .replace(/[ \t]{2,}/g, ' ') - .replace(/\n{3,}/g, '\n\n') + .replace(/[ \t]+\n/g, "\n") + .replace(/\n[ \t]+/g, "\n") + .replace(/[ \t]{2,}/g, " ") + .replace(/\n{3,}/g, "\n\n") .trim(); } -export function trimSecDocumentTextForPrompt(text: string, maxChars = FILING_TEXT_MAX_CHARS) { +export function trimSecDocumentTextForPrompt( + text: string, + maxChars = FILING_TEXT_MAX_CHARS, +) { const safeMax = Math.max(Math.trunc(maxChars), 1_000); if (text.length <= safeMax) { return { text, truncated: false }; } const slice = text.slice(0, safeMax); - const newlineBoundary = slice.lastIndexOf('\n'); - const wordBoundary = slice.lastIndexOf(' '); + const newlineBoundary = slice.lastIndexOf("\n"); + const wordBoundary = slice.lastIndexOf(" "); const boundary = Math.max(newlineBoundary, wordBoundary); - const clipped = (boundary > safeMax * 0.7 ? slice.slice(0, boundary) : slice).trimEnd(); + const clipped = ( + boundary > safeMax * 0.7 ? slice.slice(0, boundary) : slice + ).trimEnd(); return { text: clipped, truncated: true }; } function compactAccessionNumber(value: string) { - return value.replace(/-/g, ''); + return value.replace(/-/g, ""); } function normalizeAccessionKey(value: string | undefined | null) { - return (value ?? '').replace(/\D/g, ''); + return (value ?? "").replace(/\D/g, ""); } function normalizeForm(value: string | undefined | null) { - const normalized = (value ?? '').trim().toUpperCase(); + const normalized = (value ?? "").trim().toUpperCase(); if (!normalized) { - return ''; + return ""; } - return normalized.endsWith('/A') - ? normalized.slice(0, -2) - : normalized; + return normalized.endsWith("/A") ? normalized.slice(0, -2) : normalized; } function parseDate(value: string | undefined | null) { @@ -212,7 +218,7 @@ function parseDate(value: string | undefined | null) { } function normalizeCikForPath(value: string) { - const digits = value.replace(/\D/g, ''); + const digits = value.replace(/\D/g, ""); if (!digits) { return null; } @@ -246,7 +252,7 @@ export function resolvePrimaryFilingUrl(input: FilingDocumentInput) { export async function fetchPrimaryFilingText( input: FilingDocumentInput, - options?: FetchPrimaryFilingTextOptions + options?: FetchPrimaryFilingTextOptions, ): Promise { const url = resolvePrimaryFilingUrl(input); if (!url) { @@ -256,10 +262,10 @@ export async function fetchPrimaryFilingText( const doFetch = options?.fetchImpl ?? fetch; const response = await doFetch(url, { headers: { - 'User-Agent': envUserAgent(), - Accept: 'text/html, text/plain;q=0.9, */*;q=0.8' + "User-Agent": envUserAgent(), + Accept: "text/html, text/plain;q=0.9, */*;q=0.8", }, - cache: 'no-store' + cache: "no-store", }); if (!response.ok) { @@ -272,33 +278,36 @@ export async function fetchPrimaryFilingText( return null; } - const clipped = trimSecDocumentTextForPrompt(normalized, options?.maxChars ?? FILING_TEXT_MAX_CHARS); + const clipped = trimSecDocumentTextForPrompt( + normalized, + options?.maxChars ?? FILING_TEXT_MAX_CHARS, + ); if (!clipped.text) { return null; } return { - source: 'primary_document', + source: "primary_document", url, text: clipped.text, - truncated: clipped.truncated + truncated: clipped.truncated, }; } async function fetchJson(url: string): Promise { const response = await fetch(url, { headers: { - 'User-Agent': envUserAgent(), - Accept: 'application/json' + "User-Agent": envUserAgent(), + Accept: "application/json", }, - cache: 'no-store' + cache: "no-store", }); if (!response.ok) { throw new Error(`SEC request failed (${response.status})`); } - return await response.json() as T; + return (await response.json()) as T; } async function ensureTickerCache() { @@ -307,7 +316,9 @@ async function ensureTickerCache() { return; } - const payload = await fetchJson>('https://www.sec.gov/files/company_tickers.json'); + const payload = await fetchJson>( + "https://www.sec.gov/files/company_tickers.json", + ); const next = new Map(); for (const record of Object.values(payload)) { @@ -331,20 +342,26 @@ async function resolveTicker(ticker: string) { return { ticker: normalized, cik: String(record.cik_str), - companyName: record.title + companyName: record.title, }; } -function pickLatestFact(payload: CompanyFactsPayload, tag: string): number | null { +function pickLatestFact( + payload: CompanyFactsPayload, + tag: string, +): number | null { return pickFactForFiling(payload, tag, { - accessionNumber: '', - filingDate: '', - filingType: '10-Q' + accessionNumber: "", + filingDate: "", + filingType: "10-Q", }); } -function collectFactSeries(payload: CompanyFactsPayload, tag: string): CompanyFactPoint[] { - const unitCollections = payload.facts?.['us-gaap']?.[tag]?.units; +function collectFactSeries( + payload: CompanyFactsPayload, + tag: string, +): CompanyFactPoint[] { + const unitCollections = payload.facts?.["us-gaap"]?.[tag]?.units; if (!unitCollections) { return []; } @@ -357,7 +374,7 @@ function collectFactSeries(payload: CompanyFactsPayload, tag: string): CompanyFa continue; } - if (unit === 'USD' || /^USD(?!\/shares)/i.test(unit)) { + if (unit === "USD" || /^USD(?!\/shares)/i.test(unit)) { usdSeries.push(...series); continue; } @@ -367,28 +384,32 @@ function collectFactSeries(payload: CompanyFactsPayload, tag: string): CompanyFa const points = usdSeries.length > 0 ? usdSeries : fallbackSeries; - return points.filter((point) => typeof point.val === 'number' && Number.isFinite(point.val)); + return points.filter( + (point) => typeof point.val === "number" && Number.isFinite(point.val), + ); } function pickMostRecentFact(points: CompanyFactPoint[]) { - return [...points].sort((a, b) => { - const aDate = parseDate(a.filed ?? a.end); - const bDate = parseDate(b.filed ?? b.end); + return ( + [...points].sort((a, b) => { + const aDate = parseDate(a.filed ?? a.end); + const bDate = parseDate(b.filed ?? b.end); - if (Number.isFinite(aDate) && Number.isFinite(bDate)) { - return bDate - aDate; - } + if (Number.isFinite(aDate) && Number.isFinite(bDate)) { + return bDate - aDate; + } - if (Number.isFinite(bDate)) { - return 1; - } + if (Number.isFinite(bDate)) { + return 1; + } - if (Number.isFinite(aDate)) { - return -1; - } + if (Number.isFinite(aDate)) { + return -1; + } - return 0; - })[0] ?? null; + return 0; + })[0] ?? null + ); } function pickClosestByDate(points: CompanyFactPoint[], targetDate: number) { @@ -413,20 +434,23 @@ function pickClosestByDate(points: CompanyFactPoint[], targetDate: number) { return beforeTarget.sort((a, b) => b.date - a.date)[0]?.point ?? null; } - return dated.sort((a, b) => { - const distance = Math.abs(a.date - targetDate) - Math.abs(b.date - targetDate); - if (distance !== 0) { - return distance; - } + return ( + dated.sort((a, b) => { + const distance = + Math.abs(a.date - targetDate) - Math.abs(b.date - targetDate); + if (distance !== 0) { + return distance; + } - return b.date - a.date; - })[0]?.point ?? null; + return b.date - a.date; + })[0]?.point ?? null + ); } function pickFactForFiling( payload: CompanyFactsPayload, tag: string, - filing: FilingMetricsLookupInput + filing: FilingMetricsLookupInput, ): number | null { const points = collectFactSeries(payload, tag); if (points.length === 0) { @@ -435,10 +459,12 @@ function pickFactForFiling( const accessionKey = normalizeAccessionKey(filing.accessionNumber); if (accessionKey) { - const byAccession = points.filter((point) => normalizeAccessionKey(point.accn) === accessionKey); + const byAccession = points.filter( + (point) => normalizeAccessionKey(point.accn) === accessionKey, + ); if (byAccession.length > 0) { const matched = pickMostRecentFact(byAccession); - if (typeof matched?.val === 'number' && Number.isFinite(matched.val)) { + if (typeof matched?.val === "number" && Number.isFinite(matched.val)) { return matched.val; } } @@ -451,12 +477,12 @@ function pickFactForFiling( const targetDate = parseDate(filing.filingDate); const bestByForm = pickClosestByDate(byForm, targetDate); - if (typeof bestByForm?.val === 'number' && Number.isFinite(bestByForm.val)) { + if (typeof bestByForm?.val === "number" && Number.isFinite(bestByForm.val)) { return bestByForm.val; } const bestAny = pickClosestByDate(points, targetDate); - return typeof bestAny?.val === 'number' && Number.isFinite(bestAny.val) + return typeof bestAny?.val === "number" && Number.isFinite(bestAny.val) ? bestAny.val : null; } @@ -464,7 +490,7 @@ function pickFactForFiling( function pickFactByTags( payload: CompanyFactsPayload, tags: readonly string[], - filing: FilingMetricsLookupInput + filing: FilingMetricsLookupInput, ) { for (const tag of tags) { const value = pickFactForFiling(payload, tag, filing); @@ -482,16 +508,21 @@ function emptyMetrics(): FilingMetrics { netIncome: null, totalAssets: null, cash: null, - debt: null + debt: null, }; } -export async function fetchRecentFilings(ticker: string, limit = 20): Promise { +export async function fetchRecentFilings( + ticker: string, + limit = 20, +): Promise { const safeLimit = Math.min(Math.max(Math.trunc(limit), 1), 50); const company = await resolveTicker(ticker); - const cikPadded = company.cik.padStart(10, '0'); - const payload = await fetchJson(`https://data.sec.gov/submissions/CIK${cikPadded}.json`); + const cikPadded = company.cik.padStart(10, "0"); + const payload = await fetchJson( + `https://data.sec.gov/submissions/CIK${cikPadded}.json`, + ); const recent = payload.filings?.recent; const submissionUrl = `https://data.sec.gov/submissions/CIK${cikPadded}.json`; @@ -516,7 +547,7 @@ export async function fetchRecentFilings(ticker: string, limit = 20): Promise= safeLimit) { @@ -543,22 +574,24 @@ export async function fetchRecentFilings(ticker: string, limit = 20): Promise(`https://data.sec.gov/api/xbrl/companyfacts/CIK${normalized}.json`); + const normalized = cik.padStart(10, "0"); + const payload = await fetchJson( + `https://data.sec.gov/api/xbrl/companyfacts/CIK${normalized}.json`, + ); return { - revenue: pickLatestFact(payload, 'Revenues'), - netIncome: pickLatestFact(payload, 'NetIncomeLoss'), - totalAssets: pickLatestFact(payload, 'Assets'), - cash: pickLatestFact(payload, 'CashAndCashEquivalentsAtCarryingValue'), - debt: pickLatestFact(payload, 'LongTermDebt') + revenue: pickLatestFact(payload, "Revenues"), + netIncome: pickLatestFact(payload, "NetIncomeLoss"), + totalAssets: pickLatestFact(payload, "Assets"), + cash: pickLatestFact(payload, "CashAndCashEquivalentsAtCarryingValue"), + debt: pickLatestFact(payload, "LongTermDebt"), } satisfies FilingMetrics; } export async function fetchFilingMetricsForFilings( cik: string, _ticker: string, - filings: FilingMetricsLookupInput[] + filings: FilingMetricsLookupInput[], ) { const metricsByAccession = new Map(); if (filings.length === 0) { @@ -566,8 +599,10 @@ export async function fetchFilingMetricsForFilings( } try { - const normalized = cik.padStart(10, '0'); - const payload = await fetchJson(`https://data.sec.gov/api/xbrl/companyfacts/CIK${normalized}.json`); + const normalized = cik.padStart(10, "0"); + const payload = await fetchJson( + `https://data.sec.gov/api/xbrl/companyfacts/CIK${normalized}.json`, + ); for (const filing of filings) { metricsByAccession.set(filing.accessionNumber, { @@ -575,7 +610,7 @@ export async function fetchFilingMetricsForFilings( netIncome: pickFactByTags(payload, METRIC_TAGS.netIncome, filing), totalAssets: pickFactByTags(payload, METRIC_TAGS.totalAssets, filing), cash: pickFactByTags(payload, METRIC_TAGS.cash, filing), - debt: pickFactByTags(payload, METRIC_TAGS.debt, filing) + debt: pickFactByTags(payload, METRIC_TAGS.debt, filing), }); } @@ -595,24 +630,24 @@ type FilingStatementHydrationInput = { cik: string; accessionNumber: string; filingDate: string; - filingType: '10-K' | '10-Q'; + filingType: "10-K" | "10-Q"; filingUrl: string | null; primaryDocument: string | null; - metrics: Filing['metrics']; + metrics: Filing["metrics"]; }; type FilingStatementHydrationResult = { 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; dimension_bundle: DimensionStatementBundle | null; - parse_status: 'ready' | 'partial' | 'failed'; + parse_status: "ready" | "partial" | "failed"; parse_error: string | null; - source: 'sec_filing_summary' | 'xbrl_instance' | 'companyfacts_fallback'; + source: "sec_filing_summary" | "xbrl_instance" | "companyfacts_fallback"; }; type StatementReportDescriptor = { @@ -646,195 +681,217 @@ type CanonicalRowDefinition = { }; const FINANCIAL_STATEMENT_KINDS: FinancialStatementKind[] = [ - 'income', - 'balance', - 'cash_flow', - 'equity', - 'comprehensive_income' + "income", + "balance", + "cash_flow", + "equity", + "comprehensive_income", ]; const STATEMENT_REPORT_PATTERNS: Record = { income: [ /\bstatements?\s+of\s+operations?\b/i, /\bstatements?\s+of\s+income\b/i, - /\bincome\s+statement/i + /\bincome\s+statement/i, ], balance: [ /\bbalance\s+sheets?\b/i, - /\bstatement\s+of\s+financial\s+position\b/i - ], - cash_flow: [ - /\bstatements?\s+of\s+cash\s+flows?\b/i, - /\bcash\s+flows?\b/i + /\bstatement\s+of\s+financial\s+position\b/i, ], + cash_flow: [/\bstatements?\s+of\s+cash\s+flows?\b/i, /\bcash\s+flows?\b/i], equity: [ - /\bstatements?\s+of\s+(stockholders|shareholders)['’]?\s+equity\b/i, - /\bchanges\s+in\s+equity\b/i + /\bstatements?\s+of\s+(stockholders|shareholders)['']?\s+equity\b/i, + /\bchanges\s+in\s+equity\b/i, ], comprehensive_income: [ /\bstatements?\s+of\s+comprehensive\s+income\b/i, - /\bcomprehensive\s+income\b/i - ] + /\bcomprehensive\s+income\b/i, + ], + disclosure: [], }; -const STANDARDIZED_ROW_DEFINITIONS: Record = { +const STANDARDIZED_ROW_DEFINITIONS: Record< + FinancialStatementKind, + CanonicalRowDefinition[] +> = { income: [ { - key: 'revenue', - label: 'Revenue', - category: 'core', + key: "revenue", + label: "Revenue", + category: "core", conceptPatterns: [/revenue/i, /salesrevenuenet/i], - labelPatterns: [/\brevenue\b/i, /\bsales\b/i] + labelPatterns: [/\brevenue\b/i, /\bsales\b/i], }, { - key: 'cost-of-revenue', - label: 'Cost of Revenue', - category: 'core', + key: "cost-of-revenue", + label: "Cost of Revenue", + category: "core", conceptPatterns: [/costofrevenue/i, /costofgoods/i], - labelPatterns: [/\bcost of revenue\b/i, /\bcost of sales\b/i] + labelPatterns: [/\bcost of revenue\b/i, /\bcost of sales\b/i], }, { - key: 'gross-profit', - label: 'Gross Profit', - category: 'core', + key: "gross-profit", + label: "Gross Profit", + category: "core", conceptPatterns: [/grossprofit/i], - labelPatterns: [/\bgross profit\b/i] + labelPatterns: [/\bgross profit\b/i], }, { - key: 'operating-income', - label: 'Operating Income', - category: 'core', + key: "operating-income", + label: "Operating Income", + category: "core", conceptPatterns: [/operatingincome/i, /incomefromoperations/i], - labelPatterns: [/\boperating income\b/i, /\bincome from operations\b/i] + labelPatterns: [/\boperating income\b/i, /\bincome from operations\b/i], }, { - key: 'net-income', - label: 'Net Income', - category: 'core', + key: "net-income", + label: "Net Income", + category: "core", conceptPatterns: [/netincomeloss/i, /profitloss/i], - labelPatterns: [/\bnet income\b/i, /\bnet earnings\b/i] - } + labelPatterns: [/\bnet income\b/i, /\bnet earnings\b/i], + }, ], balance: [ { - key: 'total-assets', - label: 'Total Assets', - category: 'core', + key: "total-assets", + label: "Total Assets", + category: "core", conceptPatterns: [/^assets$/i], - labelPatterns: [/\btotal assets\b/i] + labelPatterns: [/\btotal assets\b/i], }, { - key: 'total-liabilities', - label: 'Total Liabilities', - category: 'core', + key: "total-liabilities", + label: "Total Liabilities", + category: "core", conceptPatterns: [/liabilities/i], - labelPatterns: [/\btotal liabilities\b/i] + labelPatterns: [/\btotal liabilities\b/i], }, { - key: 'stockholders-equity', - label: 'Stockholders Equity', - category: 'core', - conceptPatterns: [/stockholdersequity/i, /shareholdersequity/i, /equity/i], - labelPatterns: [/\bequity\b/i] + key: "stockholders-equity", + label: "Stockholders Equity", + category: "core", + conceptPatterns: [ + /stockholdersequity/i, + /shareholdersequity/i, + /equity/i, + ], + labelPatterns: [/\bequity\b/i], }, { - key: 'cash-and-equivalents', - label: 'Cash and Equivalents', - category: 'liquidity', + key: "cash-and-equivalents", + label: "Cash and Equivalents", + category: "liquidity", conceptPatterns: [/cashandcashequivalents/i, /cashandequivalents/i], - labelPatterns: [/\bcash\b/i, /\bcash equivalents\b/i] + labelPatterns: [/\bcash\b/i, /\bcash equivalents\b/i], }, { - key: 'total-debt', - label: 'Total Debt', - category: 'leverage', + key: "total-debt", + label: "Total Debt", + category: "leverage", conceptPatterns: [/longtermdebt/i, /debt/i, /borrowings/i], - labelPatterns: [/\btotal debt\b/i, /\blong-term debt\b/i, /\bdebt\b/i] - } + labelPatterns: [/\btotal debt\b/i, /\blong-term debt\b/i, /\bdebt\b/i], + }, ], cash_flow: [ { - key: 'net-cash-operating', - label: 'Net Cash from Operating Activities', - category: 'core', - conceptPatterns: [/netcashprovidedbyusedinoperatingactivities/i, /netcashfromoperatingactivities/i], - labelPatterns: [/\boperating activities\b/i] + key: "net-cash-operating", + label: "Net Cash from Operating Activities", + category: "core", + conceptPatterns: [ + /netcashprovidedbyusedinoperatingactivities/i, + /netcashfromoperatingactivities/i, + ], + labelPatterns: [/\boperating activities\b/i], }, { - key: 'net-cash-investing', - label: 'Net Cash from Investing Activities', - category: 'core', + key: "net-cash-investing", + label: "Net Cash from Investing Activities", + category: "core", conceptPatterns: [/netcashprovidedbyusedininvestingactivities/i], - labelPatterns: [/\binvesting activities\b/i] + labelPatterns: [/\binvesting activities\b/i], }, { - key: 'net-cash-financing', - label: 'Net Cash from Financing Activities', - category: 'core', + key: "net-cash-financing", + label: "Net Cash from Financing Activities", + category: "core", conceptPatterns: [/netcashprovidedbyusedinfinancingactivities/i], - labelPatterns: [/\bfinancing activities\b/i] + labelPatterns: [/\bfinancing activities\b/i], }, { - key: 'net-change-cash', - label: 'Net Change in Cash', - category: 'core', - conceptPatterns: [/cashandcashequivalentsperiodincrease/i, /increase.*cash/i], - labelPatterns: [/\bnet change\b/i, /\bincrease.*cash\b/i] - } + key: "net-change-cash", + label: "Net Change in Cash", + category: "core", + conceptPatterns: [ + /cashandcashequivalentsperiodincrease/i, + /increase.*cash/i, + ], + labelPatterns: [/\bnet change\b/i, /\bincrease.*cash\b/i], + }, ], equity: [ { - key: 'equity-balance', - label: 'Total Equity', - category: 'core', - conceptPatterns: [/stockholdersequity/i, /shareholdersequity/i, /equity/i], - labelPatterns: [/\btotal equity\b/i, /\bequity\b/i] + key: "equity-balance", + label: "Total Equity", + category: "core", + conceptPatterns: [ + /stockholdersequity/i, + /shareholdersequity/i, + /equity/i, + ], + labelPatterns: [/\btotal equity\b/i, /\bequity\b/i], }, { - key: 'retained-earnings', - label: 'Retained Earnings', - category: 'core', + key: "retained-earnings", + label: "Retained Earnings", + category: "core", conceptPatterns: [/retainedearnings/i], - labelPatterns: [/\bretained earnings\b/i] - } + labelPatterns: [/\bretained earnings\b/i], + }, ], comprehensive_income: [ { - key: 'comprehensive-income', - label: 'Comprehensive Income', - category: 'core', + key: "comprehensive-income", + label: "Comprehensive Income", + category: "core", conceptPatterns: [/comprehensiveincome/i], - labelPatterns: [/\bcomprehensive income\b/i] + labelPatterns: [/\bcomprehensive income\b/i], }, { - key: 'other-comprehensive-income', - label: 'Other Comprehensive Income', - category: 'core', + key: "other-comprehensive-income", + label: "Other Comprehensive Income", + category: "core", conceptPatterns: [/othercomprehensiveincome/i], - labelPatterns: [/\bother comprehensive income\b/i] - } - ] + labelPatterns: [/\bother comprehensive income\b/i], + }, + ], + disclosure: [], }; -function createStatementRecord(factory: () => T): Record { - return FINANCIAL_STATEMENT_KINDS.reduce((acc, kind) => { - acc[kind] = factory(); - return acc; - }, {} as Record); +function createStatementRecord( + factory: () => T, +): Record { + const record = {} as Record; + for (const kind of FINANCIAL_STATEMENT_KINDS) { + record[kind] = factory(); + } + record.disclosure = factory(); + return record; } function statementKindLabel(kind: FinancialStatementKind) { switch (kind) { - case 'income': - return 'Income Statement'; - case 'balance': - return 'Balance Sheet'; - case 'cash_flow': - return 'Cash Flow Statement'; - case 'equity': - return 'Statement of Equity'; - case 'comprehensive_income': - return 'Comprehensive Income'; + case "income": + return "Income Statement"; + case "balance": + return "Balance Sheet"; + case "cash_flow": + return "Cash Flow Statement"; + case "equity": + return "Statement of Equity"; + case "comprehensive_income": + return "Comprehensive Income"; + case "disclosure": + return "Disclosures"; default: return kind; } @@ -847,8 +904,8 @@ function resolveFilingDirectoryUrl(input: { }) { const direct = input.filingUrl?.trim(); if (direct) { - const lastSlash = direct.lastIndexOf('/'); - if (lastSlash > 'https://'.length) { + const lastSlash = direct.lastIndexOf("/"); + if (lastSlash > "https://".length) { return direct.slice(0, lastSlash + 1); } } @@ -863,7 +920,7 @@ function resolveFilingDirectoryUrl(input: { } function toAbsoluteArchiveUrl(baseUrl: string, relativePath: string | null) { - const normalized = (relativePath ?? '').trim(); + const normalized = (relativePath ?? "").trim(); if (!normalized) { return null; } @@ -872,16 +929,16 @@ function toAbsoluteArchiveUrl(baseUrl: string, relativePath: string | null) { return normalized; } - return `${baseUrl}${normalized.replace(/^\/+/, '')}`; + return `${baseUrl}${normalized.replace(/^\/+/, "")}`; } async function fetchText(url: string, fetchImpl: typeof fetch) { const response = await fetchImpl(url, { headers: { - 'User-Agent': envUserAgent(), - Accept: 'text/xml, text/html, text/plain;q=0.9, */*;q=0.8' + "User-Agent": envUserAgent(), + Accept: "text/xml, text/html, text/plain;q=0.9, */*;q=0.8", }, - cache: 'no-store' + cache: "no-store", }); if (!response.ok) { @@ -892,14 +949,14 @@ async function fetchText(url: string, fetchImpl: typeof fetch) { } function xmlTextValue(block: string, tagName: string) { - const escaped = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const pattern = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, 'i'); + const escaped = tagName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const pattern = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, "i"); const match = block.match(pattern); if (!match) { - return ''; + return ""; } - return decodeHtmlEntities(match[1] ?? '').trim(); + return decodeHtmlEntities(match[1] ?? "").trim(); } function parseFilingSummaryReports(xml: string) { @@ -907,19 +964,22 @@ function parseFilingSummaryReports(xml: string) { const reportPattern = /([\s\S]*?)<\/Report>/gi; for (const match of xml.matchAll(reportPattern)) { - const block = match[1] ?? ''; + const block = match[1] ?? ""; reports.push({ - shortName: xmlTextValue(block, 'ShortName'), - longName: xmlTextValue(block, 'LongName'), - htmlFileName: xmlTextValue(block, 'HtmlFileName') || null, - xmlFileName: xmlTextValue(block, 'XmlFileName') || null + shortName: xmlTextValue(block, "ShortName"), + longName: xmlTextValue(block, "LongName"), + htmlFileName: xmlTextValue(block, "HtmlFileName") || null, + xmlFileName: xmlTextValue(block, "XmlFileName") || null, }); } return reports; } -function scoreReport(kind: FinancialStatementKind, report: StatementReportDescriptor) { +function scoreReport( + kind: FinancialStatementKind, + report: StatementReportDescriptor, +) { const haystack = `${report.shortName} ${report.longName}`.trim(); if (!haystack) { return 0; @@ -939,7 +999,10 @@ function scoreReport(kind: FinancialStatementKind, report: StatementReportDescri return score; } -function chooseStatementReport(kind: FinancialStatementKind, reports: StatementReportDescriptor[]) { +function chooseStatementReport( + kind: FinancialStatementKind, + reports: StatementReportDescriptor[], +) { let best: StatementReportDescriptor | null = null; let bestScore = 0; @@ -956,19 +1019,17 @@ function chooseStatementReport(kind: FinancialStatementKind, reports: StatementR function sanitizeCellText(raw: string) { return decodeHtmlEntities( - raw - .replace(//gi, '\n') - .replace(/<[^>]+>/g, ' ') + raw.replace(//gi, "\n").replace(/<[^>]+>/g, " "), ) - .replace(/[ \t]+/g, ' ') - .replace(/\n+/g, ' ') + .replace(/[ \t]+/g, " ") + .replace(/\n+/g, " ") .trim(); } function extractConceptFromMarkup(markup: string) { const defref = markup.match(/defref[_:-]([a-z0-9_:.:-]+)/i); if (defref?.[1]) { - return defref[1].replace(/_/g, ':'); + return defref[1].replace(/_/g, ":"); } const nameAttr = markup.match(/\bname=[\"']([a-z0-9_:.:-]+)[\"']/i); @@ -980,7 +1041,7 @@ function extractConceptFromMarkup(markup: string) { } function parseIndentDepth(attrs: string) { - const style = attrs.match(/\bstyle=[\"']([^\"']+)[\"']/i)?.[1] ?? ''; + const style = attrs.match(/\bstyle=[\"']([^\"']+)[\"']/i)?.[1] ?? ""; const padding = style.match(/padding-left:\s*([0-9.]+)px/i)?.[1]; if (padding) { const numeric = Number.parseFloat(padding); @@ -1010,11 +1071,11 @@ function parseStatementNumber(raw: string): number | null { return null; } - const negative = trimmed.startsWith('(') && trimmed.endsWith(')'); + const negative = trimmed.startsWith("(") && trimmed.endsWith(")"); const cleaned = trimmed - .replace(/[$,\s]/g, '') - .replace(/[()]/g, '') - .replace(/\u2212/g, '-'); + .replace(/[$,\s]/g, "") + .replace(/[()]/g, "") + .replace(/\u2212/g, "-"); const value = Number.parseFloat(cleaned); if (!Number.isFinite(value)) { @@ -1027,8 +1088,8 @@ function parseStatementNumber(raw: string): number | null { function slug(value: string) { return value .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, ''); + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); } function parseStatementRowsFromReport(content: string): StatementParseRow[] { @@ -1040,20 +1101,22 @@ function parseStatementRowsFromReport(content: string): StatementParseRow[] { let bestRows: StatementParseRow[] = []; for (const tableMatch of tables) { - const table = tableMatch[0] ?? ''; + const table = tableMatch[0] ?? ""; const rows: StatementParseRow[] = []; let order = 0; for (const rowMatch of table.matchAll(/]*>([\s\S]*?)<\/tr>/gi)) { - const rowMarkup = rowMatch[0] ?? ''; - const cells = [...rowMarkup.matchAll(/]*)>([\s\S]*?)<\/t[dh]>/gi)]; + const rowMarkup = rowMatch[0] ?? ""; + const cells = [ + ...rowMarkup.matchAll(/]*)>([\s\S]*?)<\/t[dh]>/gi), + ]; if (cells.length < 2) { continue; } const labelCell = cells[0]; - const labelAttrs = labelCell?.[1] ?? ''; - const labelRaw = labelCell?.[2] ?? ''; + const labelAttrs = labelCell?.[1] ?? ""; + const labelRaw = labelCell?.[2] ?? ""; const label = sanitizeCellText(labelRaw); if (!label || /^(years ended|assets|liabilities|equity)$/i.test(label)) { continue; @@ -1061,7 +1124,7 @@ function parseStatementRowsFromReport(content: string): StatementParseRow[] { let value: number | null = null; for (let i = 1; i < cells.length; i += 1) { - const text = sanitizeCellText(cells[i]?.[2] ?? ''); + const text = sanitizeCellText(cells[i]?.[2] ?? ""); const parsed = parseStatementNumber(text); if (parsed !== null) { value = parsed; @@ -1083,7 +1146,7 @@ function parseStatementRowsFromReport(content: string): StatementParseRow[] { order, depth: parseIndentDepth(labelAttrs), isSubtotal: /^total\b/i.test(label) || /\bsubtotal\b/i.test(label), - value + value, }); } @@ -1095,7 +1158,10 @@ function parseStatementRowsFromReport(content: string): StatementParseRow[] { return bestRows; } -function toSnapshotRows(periodId: string, rows: StatementParseRow[]): FilingFaithfulStatementSnapshotRow[] { +function toSnapshotRows( + periodId: string, + rows: StatementParseRow[], +): FilingFaithfulStatementSnapshotRow[] { return rows.map((row) => ({ key: row.key, label: row.label, @@ -1104,46 +1170,48 @@ function toSnapshotRows(periodId: string, rows: StatementParseRow[]): FilingFait depth: row.depth, isSubtotal: row.isSubtotal, values: { - [periodId]: row.value - } + [periodId]: row.value, + }, })); } function matchStandardizedDefinition( row: FilingFaithfulStatementSnapshotRow, - definition: CanonicalRowDefinition + definition: CanonicalRowDefinition, ) { - const concept = row.concept ?? ''; - return definition.conceptPatterns.some((pattern) => pattern.test(concept)) - || definition.labelPatterns.some((pattern) => pattern.test(row.label)); + const concept = row.concept ?? ""; + return ( + definition.conceptPatterns.some((pattern) => pattern.test(concept)) || + definition.labelPatterns.some((pattern) => pattern.test(row.label)) + ); } function fallbackMetricValue( kind: FinancialStatementKind, rowKey: string, - metrics: Filing['metrics'] + metrics: Filing["metrics"], ) { if (!metrics) { return null; } - if (kind === 'income' && rowKey === 'revenue') { + if (kind === "income" && rowKey === "revenue") { return metrics.revenue ?? null; } - if (kind === 'income' && rowKey === 'net-income') { + if (kind === "income" && rowKey === "net-income") { return metrics.netIncome ?? null; } - if (kind === 'balance' && rowKey === 'total-assets') { + if (kind === "balance" && rowKey === "total-assets") { return metrics.totalAssets ?? null; } - if (kind === 'balance' && rowKey === 'cash-and-equivalents') { + if (kind === "balance" && rowKey === "cash-and-equivalents") { return metrics.cash ?? null; } - if (kind === 'balance' && rowKey === 'total-debt') { + if (kind === "balance" && rowKey === "total-debt") { return metrics.debt ?? null; } @@ -1154,7 +1222,7 @@ function toStandardizedRows( kind: FinancialStatementKind, periodId: string, rows: FilingFaithfulStatementSnapshotRow[], - metrics: Filing['metrics'] + metrics: Filing["metrics"], ): StandardizedStatementSnapshotRow[] { const definitions = STANDARDIZED_ROW_DEFINITIONS[kind]; const normalizedRows = [...rows]; @@ -1162,11 +1230,15 @@ function toStandardizedRows( const standardizedRows: StandardizedStatementSnapshotRow[] = []; for (const definition of definitions) { - const matched = normalizedRows.find((row) => !usedKeys.has(row.key) && matchStandardizedDefinition(row, definition)); + const matched = normalizedRows.find( + (row) => + !usedKeys.has(row.key) && matchStandardizedDefinition(row, definition), + ); const matchedValue = matched?.values[periodId] ?? null; - const fallbackValue = matchedValue === null - ? fallbackMetricValue(kind, definition.key, metrics) - : null; + const fallbackValue = + matchedValue === null + ? fallbackMetricValue(kind, definition.key, metrics) + : null; if (matched) { usedKeys.add(matched.key); @@ -1179,8 +1251,8 @@ function toStandardizedRows( category: definition.category, sourceConcepts: matched?.concept ? [matched.concept] : [], values: { - [periodId]: matchedValue ?? fallbackValue - } + [periodId]: matchedValue ?? fallbackValue, + }, }); } @@ -1193,11 +1265,11 @@ function toStandardizedRows( key: `other-${row.key}`, label: row.label, concept: row.concept ?? row.key, - category: 'other', + category: "other", sourceConcepts: row.concept ? [row.concept] : [], values: { - [periodId]: row.values[periodId] ?? null - } + [periodId]: row.values[periodId] ?? null, + }, }); } @@ -1206,22 +1278,29 @@ function toStandardizedRows( function parseContextsWithDimensions(raw: string) { const contexts = new Map(); - const contextPattern = /<(?:[a-z0-9]+:)?context\b[^>]*\bid=["']([^"']+)["'][^>]*>([\s\S]*?)<\/(?:[a-z0-9]+:)?context>/gi; + const contextPattern = + /<(?:[a-z0-9]+:)?context\b[^>]*\bid=["']([^"']+)["'][^>]*>([\s\S]*?)<\/(?:[a-z0-9]+:)?context>/gi; for (const match of raw.matchAll(contextPattern)) { - const contextId = match[1] ?? ''; - const block = match[2] ?? ''; + const contextId = match[1] ?? ""; + const block = match[2] ?? ""; if (!contextId) { continue; } - const endDate = block.match(/<(?:[a-z0-9]+:)?endDate>([^<]+)<\/(?:[a-z0-9]+:)?endDate>/i)?.[1]?.trim() ?? null; + const endDate = + block + .match( + /<(?:[a-z0-9]+:)?endDate>([^<]+)<\/(?:[a-z0-9]+:)?endDate>/i, + )?.[1] + ?.trim() ?? null; const dimensions: Array<{ axis: string; member: string }> = []; - const dimPattern = /<(?:[a-z0-9]+:)?explicitMember\b[^>]*\bdimension=["']([^"']+)["'][^>]*>([^<]+)<\/(?:[a-z0-9]+:)?explicitMember>/gi; + const dimPattern = + /<(?:[a-z0-9]+:)?explicitMember\b[^>]*\bdimension=["']([^"']+)["'][^>]*>([^<]+)<\/(?:[a-z0-9]+:)?explicitMember>/gi; for (const dimMatch of block.matchAll(dimPattern)) { - const axis = (dimMatch[1] ?? '').trim(); - const member = (dimMatch[2] ?? '').trim(); + const axis = (dimMatch[1] ?? "").trim(); + const member = (dimMatch[2] ?? "").trim(); if (!axis || !member) { continue; } @@ -1239,36 +1318,39 @@ function parseContextsWithDimensions(raw: string) { return contexts; } -function statementKindFromConcept(concept: string): FinancialStatementKind | null { +function statementKindFromConcept( + concept: string, +): FinancialStatementKind | null { const normalized = concept.toLowerCase(); - if (/cash|operatingactivities|investingactivities|financingactivities/.test(normalized)) { - return 'cash_flow'; + if ( + /cash|operatingactivities|investingactivities|financingactivities/.test( + normalized, + ) + ) { + return "cash_flow"; } if (/equity|retainedearnings|additionalpaidincapital/.test(normalized)) { - return 'equity'; + return "equity"; } if (/comprehensiveincome/.test(normalized)) { - return 'comprehensive_income'; + return "comprehensive_income"; } if (/asset|liabilit|debt/.test(normalized)) { - return 'balance'; + return "balance"; } if (/revenue|income|profit|expense|costof/.test(normalized)) { - return 'income'; + return "income"; } return null; } -function parseDimensionFacts( - raw: string, - fallbackPeriodId: string -) { +function parseDimensionFacts(raw: string, fallbackPeriodId: string) { const contexts = parseContextsWithDimensions(raw); if (contexts.size === 0) { return createStatementRecord(() => []); @@ -1285,11 +1367,11 @@ function parseDimensionFacts( break; } - const attrs = match[1] ?? ''; - const body = sanitizeCellText(match[2] ?? ''); + const attrs = match[1] ?? ""; + const body = sanitizeCellText(match[2] ?? ""); - const contextRef = attrs.match(/\bcontextref=["']([^"']+)["']/i)?.[1] ?? ''; - const concept = attrs.match(/\bname=["']([^"']+)["']/i)?.[1] ?? ''; + const contextRef = attrs.match(/\bcontextref=["']([^"']+)["']/i)?.[1] ?? ""; + const concept = attrs.match(/\bname=["']([^"']+)["']/i)?.[1] ?? ""; const unit = attrs.match(/\bunitref=["']([^"']+)["']/i)?.[1] ?? null; if (!contextRef || !concept) { @@ -1321,7 +1403,7 @@ function parseDimensionFacts( axis: dimension.axis, member: dimension.member, value, - unit + unit, }); } } @@ -1329,40 +1411,47 @@ function parseDimensionFacts( return rows; } -function markHasDimensions( - rows: T[], - dimensions: DimensionStatementSnapshotRow[] -) { - const dimensionConcepts = new Set(dimensions.map((item) => item.concept?.toLowerCase() ?? '').filter(Boolean)); +function markHasDimensions< + T extends { key: string; concept: string | null; hasDimensions?: boolean }, +>(rows: T[], dimensions: DimensionStatementSnapshotRow[]) { + const dimensionConcepts = new Set( + dimensions.map((item) => item.concept?.toLowerCase() ?? "").filter(Boolean), + ); const dimensionRowKeys = new Set(dimensions.map((item) => item.rowKey)); return rows.map((row) => { - const concept = row.concept?.toLowerCase() ?? ''; - const hasDimensions = dimensionRowKeys.has(row.key) || (concept ? dimensionConcepts.has(concept) : false); + const concept = row.concept?.toLowerCase() ?? ""; + const hasDimensions = + dimensionRowKeys.has(row.key) || + (concept ? dimensionConcepts.has(concept) : false); return { ...row, - hasDimensions + hasDimensions, }; }); } -function emptyStatementBundle(period: FilingStatementSnapshotPeriod): FilingStatementBundle { +function emptyStatementBundle( + period: FilingStatementSnapshotPeriod, +): FilingStatementBundle { return { periods: [period], - statements: createStatementRecord(() => []) + statements: createStatementRecord(() => []), }; } -function emptyStandardizedBundle(period: FilingStatementSnapshotPeriod): StandardizedStatementBundle { +function emptyStandardizedBundle( + period: FilingStatementSnapshotPeriod, +): StandardizedStatementBundle { return { periods: [period], - statements: createStatementRecord(() => []) + statements: createStatementRecord(() => []), }; } function emptyDimensionBundle(): DimensionStatementBundle { return { - statements: createStatementRecord(() => []) + statements: createStatementRecord(() => []), }; } @@ -1370,7 +1459,7 @@ export async function hydrateFilingStatementSnapshot( input: FilingStatementHydrationInput, options?: { fetchImpl?: typeof fetch; - } + }, ): Promise { const periodId = `${input.filingDate}-${compactAccessionNumber(input.accessionNumber)}`; const period: FilingStatementSnapshotPeriod = { @@ -1381,25 +1470,30 @@ export async function hydrateFilingStatementSnapshot( periodStart: null, periodEnd: input.filingDate, filingType: input.filingType, - periodLabel: input.filingType === '10-Q' ? 'Quarter End' : 'Fiscal Year End' + periodLabel: + input.filingType === "10-Q" ? "Quarter End" : "Fiscal Year End", }; const fetchImpl = options?.fetchImpl ?? fetch; const statementBundle = emptyStatementBundle(period); const standardizedBundle = emptyStandardizedBundle(period); const dimensionBundle = emptyDimensionBundle(); - let source: FilingStatementHydrationResult['source'] = 'companyfacts_fallback'; + let source: FilingStatementHydrationResult["source"] = + "companyfacts_fallback"; let parseError: string | null = null; try { const filingDirectory = resolveFilingDirectoryUrl({ filingUrl: input.filingUrl, cik: input.cik, - accessionNumber: input.accessionNumber + accessionNumber: input.accessionNumber, }); if (filingDirectory) { - const summaryXml = await fetchText(`${filingDirectory}FilingSummary.xml`, fetchImpl); + const summaryXml = await fetchText( + `${filingDirectory}FilingSummary.xml`, + fetchImpl, + ); const reports = parseFilingSummaryReports(summaryXml); for (const kind of FINANCIAL_STATEMENT_KINDS) { @@ -1408,7 +1502,10 @@ export async function hydrateFilingStatementSnapshot( continue; } - const reportUrl = toAbsoluteArchiveUrl(filingDirectory, report.htmlFileName ?? report.xmlFileName); + const reportUrl = toAbsoluteArchiveUrl( + filingDirectory, + report.htmlFileName ?? report.xmlFileName, + ); if (!reportUrl) { continue; } @@ -1420,15 +1517,19 @@ export async function hydrateFilingStatementSnapshot( continue; } - source = 'sec_filing_summary'; - statementBundle.statements[kind] = toSnapshotRows(periodId, parsedRows); + source = "sec_filing_summary"; + statementBundle.statements[kind] = toSnapshotRows( + periodId, + parsedRows, + ); } catch { // Continue to other statements when one report fails. } } } } catch (error) { - parseError = error instanceof Error ? error.message : 'Failed to parse filing summary'; + parseError = + error instanceof Error ? error.message : "Failed to parse filing summary"; } try { @@ -1436,7 +1537,7 @@ export async function hydrateFilingStatementSnapshot( filingUrl: input.filingUrl, cik: input.cik, accessionNumber: input.accessionNumber, - primaryDocument: input.primaryDocument + primaryDocument: input.primaryDocument, }); if (primaryUrl) { @@ -1446,39 +1547,54 @@ export async function hydrateFilingStatementSnapshot( dimensionBundle.statements[kind] = dimensions[kind]; } - const hasAnyDimensions = FINANCIAL_STATEMENT_KINDS.some((kind) => dimensionBundle.statements[kind].length > 0); - if (hasAnyDimensions && source === 'companyfacts_fallback') { - source = 'xbrl_instance'; + const hasAnyDimensions = FINANCIAL_STATEMENT_KINDS.some( + (kind) => dimensionBundle.statements[kind].length > 0, + ); + if (hasAnyDimensions && source === "companyfacts_fallback") { + source = "xbrl_instance"; } } } catch (error) { if (!parseError) { - parseError = error instanceof Error ? error.message : 'Failed to parse inline XBRL dimensions'; + parseError = + error instanceof Error + ? error.message + : "Failed to parse inline XBRL dimensions"; } } for (const kind of FINANCIAL_STATEMENT_KINDS) { const faithfulRows = statementBundle.statements[kind]; - standardizedBundle.statements[kind] = toStandardizedRows(kind, periodId, faithfulRows, input.metrics); + standardizedBundle.statements[kind] = toStandardizedRows( + kind, + periodId, + faithfulRows, + input.metrics, + ); statementBundle.statements[kind] = markHasDimensions( faithfulRows, - dimensionBundle.statements[kind] + dimensionBundle.statements[kind], ); standardizedBundle.statements[kind] = markHasDimensions( standardizedBundle.statements[kind], - dimensionBundle.statements[kind] + dimensionBundle.statements[kind], ); } - const statementCount = FINANCIAL_STATEMENT_KINDS.filter((kind) => statementBundle.statements[kind].length > 0).length; - const standardizedCount = FINANCIAL_STATEMENT_KINDS.filter((kind) => standardizedBundle.statements[kind].length > 0).length; - const parseStatus: FilingStatementHydrationResult['parse_status'] = statementCount === FINANCIAL_STATEMENT_KINDS.length - ? 'ready' - : (statementCount > 0 || standardizedCount > 0) - ? 'partial' - : 'failed'; + const statementCount = FINANCIAL_STATEMENT_KINDS.filter( + (kind) => statementBundle.statements[kind].length > 0, + ).length; + const standardizedCount = FINANCIAL_STATEMENT_KINDS.filter( + (kind) => standardizedBundle.statements[kind].length > 0, + ).length; + const parseStatus: FilingStatementHydrationResult["parse_status"] = + statementCount === FINANCIAL_STATEMENT_KINDS.length + ? "ready" + : statementCount > 0 || standardizedCount > 0 + ? "partial" + : "failed"; return { filing_id: input.filingId, @@ -1490,7 +1606,10 @@ export async function hydrateFilingStatementSnapshot( standardized_bundle: standardizedBundle, dimension_bundle: dimensionBundle, parse_status: parseStatus, - parse_error: parseStatus === 'failed' ? (parseError ?? 'No financial statement tables found') : parseError, + parse_error: + parseStatus === "failed" + ? (parseError ?? "No financial statement tables found") + : parseError, source, }; } @@ -1499,5 +1618,5 @@ export const __statementInternals = { parseFilingSummaryReports, parseStatementRowsFromReport, parseDimensionFacts, - statementKindLabel + statementKindLabel, }; diff --git a/lib/server/task-processors.ts b/lib/server/task-processors.ts index ce96955..7fec23b 100644 --- a/lib/server/task-processors.ts +++ b/lib/server/task-processors.ts @@ -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: [], diff --git a/lib/server/taxonomy/engine.test.ts b/lib/server/taxonomy/engine.test.ts index 4f7b98a..a061269 100644 --- a/lib/server/taxonomy/engine.test.ts +++ b/lib/server/taxonomy/engine.test.ts @@ -13,6 +13,7 @@ function createStatementRecord( income: factory(), balance: factory(), cash_flow: factory(), + disclosure: factory(), equity: factory(), comprehensive_income: factory(), }; diff --git a/lib/server/taxonomy/parser-client.test.ts b/lib/server/taxonomy/parser-client.test.ts index 3efd645..4dda9e4 100644 --- a/lib/server/taxonomy/parser-client.test.ts +++ b/lib/server/taxonomy/parser-client.test.ts @@ -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: {}, }, diff --git a/lib/types.ts b/lib/types.ts index 9c90f08..363e51a 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -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; + filingType: Extract; periodLabel: string; }; @@ -486,10 +503,17 @@ export type DerivedFinancialRow = { export type StandardizedFinancialRow = DerivedFinancialRow; export type StandardizedStatementRow = StandardizedFinancialRow; export type SurfaceFinancialRow = StandardizedFinancialRow & { - statement?: Extract; + 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; 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; + metricKey: keyof NonNullable; 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 = { label: string; data: T[]; color?: string; - type?: 'line' | 'area' | 'bar'; + type?: "line" | "area" | "bar"; visible?: boolean; }; diff --git a/rust/taxonomy/crosswalk/us-gaap.json b/rust/taxonomy/crosswalk/us-gaap.json index 0d8132b..ebb454c 100644 --- a/rust/taxonomy/crosswalk/us-gaap.json +++ b/rust/taxonomy/crosswalk/us-gaap.json @@ -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" } } } diff --git a/rust/taxonomy/fiscal/v1/core.surface.json b/rust/taxonomy/fiscal/v1/core.surface.json index 468e443..a05b8f9 100644 --- a/rust/taxonomy/fiscal/v1/core.surface.json +++ b/rust/taxonomy/fiscal/v1/core.surface.json @@ -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" } ] } diff --git a/scripts/dev.ts b/scripts/dev.ts index 9bf1e82..0a817a0 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -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))) { diff --git a/scripts/generate-taxonomy.ts b/scripts/generate-taxonomy.ts index 176fbe9..98acf40 100644 --- a/scripts/generate-taxonomy.ts +++ b/scripts/generate-taxonomy.ts @@ -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 { 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 { 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 { const packs = new Map(); 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(); 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, errors: string[]) { +function validateComputedPack( + pack: ComputedPackFile, + surfaceKeys: Set, + errors: string[], +) { const keys = new Set(); for (const computed of pack.computed) { @@ -167,26 +205,39 @@ function validateComputedPack(pack: ComputedPackFile, surfaceKeys: Set, 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 { +function generateSurfacesIndex( + surfacesByStatement: Map, +): 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(); @@ -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); });