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); });