financial-graph-shared 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +75 -0
  2. package/dist/db/index.d.ts +11 -0
  3. package/dist/db/index.d.ts.map +1 -0
  4. package/dist/db/index.js +11 -0
  5. package/dist/db/queries/cik-lookup.d.ts +49 -0
  6. package/dist/db/queries/cik-lookup.d.ts.map +1 -0
  7. package/dist/db/queries/cik-lookup.js +78 -0
  8. package/dist/db/queries/company-lookup.d.ts +43 -0
  9. package/dist/db/queries/company-lookup.d.ts.map +1 -0
  10. package/dist/db/queries/company-lookup.js +65 -0
  11. package/dist/db/queries/index.d.ts +8 -0
  12. package/dist/db/queries/index.d.ts.map +1 -0
  13. package/dist/db/queries/index.js +7 -0
  14. package/dist/index.d.ts +14 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +18 -0
  17. package/dist/instant.schema.d.ts +170 -0
  18. package/dist/instant.schema.d.ts.map +1 -0
  19. package/dist/instant.schema.js +222 -0
  20. package/dist/types/ids.d.ts +135 -0
  21. package/dist/types/ids.d.ts.map +1 -0
  22. package/dist/types/ids.js +188 -0
  23. package/dist/types/instant.schema.future.d.ts +39 -0
  24. package/dist/types/instant.schema.future.d.ts.map +1 -0
  25. package/dist/types/instant.schema.future.js +52 -0
  26. package/dist/types/types.d.ts +321 -0
  27. package/dist/types/types.d.ts.map +1 -0
  28. package/dist/types/types.js +180 -0
  29. package/dist/types/validation.d.ts +7 -0
  30. package/dist/types/validation.d.ts.map +1 -0
  31. package/dist/types/validation.js +14 -0
  32. package/dist/utils/index.d.ts +5 -0
  33. package/dist/utils/index.d.ts.map +1 -0
  34. package/dist/utils/index.js +4 -0
  35. package/dist/utils/logger-interface.d.ts +57 -0
  36. package/dist/utils/logger-interface.d.ts.map +1 -0
  37. package/dist/utils/logger-interface.js +38 -0
  38. package/package.json +54 -0
  39. package/types/ids.ts +249 -0
  40. package/types/instant.schema.future.js +55 -0
  41. package/types/instant.schema.future.ts +59 -0
  42. package/types/types.ts +292 -0
  43. package/types/validation.ts +32 -0
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "financial-graph-shared",
3
+ "version": "1.0.0",
4
+ "description": "Shared types, schema, and utilities for financial-graph",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./types": "./dist/types/types.js",
14
+ "./schema": "./dist/types/schema.js",
15
+ "./validation": "./dist/types/validation.js",
16
+ "./composite-keys": "./dist/types/composite-keys.js",
17
+ "./ids": "./dist/types/ids.js",
18
+ "./db": {
19
+ "types": "./dist/db/index.d.ts",
20
+ "default": "./dist/db/index.js"
21
+ }
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "watch": "tsc --watch",
26
+ "clean": "rm -rf dist",
27
+ "test": "bun run build && bun test tests/",
28
+ "test:watch": "bun test --watch tests/",
29
+ "test:only": "bun test tests/",
30
+ "schema:push": "bunx instant-cli@latest push schema"
31
+ },
32
+ "keywords": ["types", "schema", "instantdb"],
33
+ "author": "",
34
+ "license": "ISC",
35
+ "engines": {
36
+ "node": ">=24.0.0"
37
+ },
38
+ "dependencies": {
39
+ "@instantdb/core": "^0.22.96",
40
+ "fast-check": "^4.5.3",
41
+ "uuid": "^13.0.0",
42
+ "zod": "^4.3.5"
43
+ },
44
+ "devDependencies": {
45
+ "@types/bun": "^1.3.5",
46
+ "@types/node": "^25.0.3",
47
+ "@types/uuid": "^11.0.0",
48
+ "typescript": "^5.9.3"
49
+ },
50
+ "files": [
51
+ "dist",
52
+ "types"
53
+ ]
54
+ }
package/types/ids.ts ADDED
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Deterministic ID Generation for InstantDB
3
+ *
4
+ * Benefits:
5
+ * - Deterministic: Same input always produces the same UUID
6
+ * - Idempotent: Upserts with identical data update the same record
7
+ * - No redundancy: No need for separate composite_key column
8
+ * - Compatible: UUID v5 format works with InstantDB's ID requirements
9
+ */
10
+
11
+ import { v5 as uuidv5 } from 'uuid';
12
+ import {
13
+ CompanyType,
14
+ type CompanyIdentity,
15
+ PublicCompanySchema,
16
+ IssuerCompanySchema,
17
+ PrivateCompanySchema,
18
+ UnknownCompanySchema,
19
+ AccessionNumberString,
20
+ ParentOfParamsSchema,
21
+ SubsidiaryEnrichmentParamsSchema,
22
+ BusinessSegmentParamsSchema,
23
+ BrandParamsSchema,
24
+ OwnsParamsSchema,
25
+ } from './types';
26
+
27
+ /**
28
+ * Namespace UUIDs for each entity type.
29
+ * These are used with UUID v5 to generate deterministic IDs.
30
+ * Each namespace ensures IDs are unique within their entity type.
31
+ */
32
+ export const NAMESPACES = {
33
+ /** Namespace for company entity IDs */
34
+ COMPANY: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
35
+ /** Namespace for filing entity IDs */
36
+ FILING: '6ba7b811-9dad-11d1-80b4-00c04fd430c8',
37
+ /** Namespace for parent_of relationship IDs */
38
+ PARENT_OF: '6ba7b812-9dad-11d1-80b4-00c04fd430c8',
39
+ /** Namespace for subsidiary_enrichment entity IDs */
40
+ SUBSIDIARY_ENRICHMENT: '6ba7b813-9dad-11d1-80b4-00c04fd430c8',
41
+ /** Namespace for business_segment entity IDs */
42
+ BUSINESS_SEGMENT: '6ba7b814-9dad-11d1-80b4-00c04fd430c8',
43
+ /** Namespace for brand entity IDs */
44
+ BRAND: '6ba7b815-9dad-11d1-80b4-00c04fd430c8',
45
+ /** Namespace for owns relationship IDs */
46
+ OWNS: '6ba7b816-9dad-11d1-80b4-00c04fd430c8',
47
+ /** Namespace for company_info entity IDs */
48
+ COMPANY_INFO: '6ba7b817-9dad-11d1-80b4-00c04fd430c8',
49
+ } as const;
50
+
51
+ export type NamespaceKey = keyof typeof NAMESPACES;
52
+
53
+ // ============================================================================
54
+ // COMPANY ID GENERATION
55
+ // ============================================================================
56
+
57
+ /**
58
+ * Company data required for ID generation
59
+ */
60
+ export interface CompanyIdInput {
61
+ type: number;
62
+ name?: string;
63
+ jurisdiction_raw?: string;
64
+ identity?: CompanyIdentity;
65
+ }
66
+
67
+ /**
68
+ * Generate a deterministic UUID v5 ID for a company.
69
+ * @param company - Company data with type and required fields
70
+ * @returns UUID v5 string
71
+ */
72
+ export function generateCompanyId(company: CompanyIdInput): string {
73
+ switch (company.type) {
74
+ case CompanyType.PUBLIC: {
75
+ const validated = PublicCompanySchema.parse(company);
76
+ return uuidv5(`${company.type}:${validated.identity.primaryCIK}`, NAMESPACES.COMPANY);
77
+ }
78
+
79
+ case CompanyType.ISSUER: {
80
+ const validated = IssuerCompanySchema.parse(company);
81
+ return uuidv5(`${company.type}:${validated.identity.primaryCIK}`, NAMESPACES.COMPANY);
82
+ }
83
+
84
+ case CompanyType.PRIVATE: {
85
+ const validated = PrivateCompanySchema.parse(company);
86
+ const normalizedName = validated.name.trim().toLowerCase();
87
+ const normalizedJurisdiction = validated.jurisdiction_raw.trim().toLowerCase();
88
+ return uuidv5(`${company.type}:${normalizedName}:${normalizedJurisdiction}`, NAMESPACES.COMPANY);
89
+ }
90
+
91
+ default: {
92
+ const validated = UnknownCompanySchema.parse(company);
93
+ const normalizedName = validated.name.trim().toLowerCase();
94
+ const normalizedJurisdiction = validated.jurisdiction_raw?.trim().toLowerCase() || '';
95
+ const compositeString = normalizedJurisdiction
96
+ ? `${company.type}:${normalizedName}:${normalizedJurisdiction}`
97
+ : `${company.type}:${normalizedName}`;
98
+ return uuidv5(compositeString, NAMESPACES.COMPANY);
99
+ }
100
+ }
101
+ }
102
+
103
+ // ============================================================================
104
+ // FILING ID GENERATION
105
+ // ============================================================================
106
+
107
+ /**
108
+ * Generate a deterministic UUID v5 ID for a filing.
109
+ *
110
+ * @param accessionNumber - SEC accession number (e.g., "0001193125-24-123456" or "0001193125241234567")
111
+ * @returns UUID v5 string
112
+ * @throws ZodError if accession number is invalid
113
+ *
114
+ * @example
115
+ * generateFilingId("0001193125-24-123456") // Returns UUID v5
116
+ * generateFilingId("0001193125241234567") // Returns same UUID v5 (normalized)
117
+ */
118
+ export function generateFilingId(accessionNumber: string): string {
119
+ const normalized = AccessionNumberString.parse(accessionNumber);
120
+ return uuidv5(normalized, NAMESPACES.FILING);
121
+ }
122
+
123
+ // ============================================================================
124
+ // RELATIONSHIP ENTITY ID GENERATION
125
+ // ============================================================================
126
+
127
+ /**
128
+ * Generate a deterministic UUID v5 ID for a parent_of relationship.
129
+ *
130
+ * @param parentId - UUID of the parent company
131
+ * @param subsidiaryId - UUID of the subsidiary company
132
+ * @returns UUID v5 string
133
+ * @throws ZodError if parentId or subsidiaryId is empty
134
+ *
135
+ * @example
136
+ * generateParentOfId("parent-uuid", "subsidiary-uuid")
137
+ */
138
+ export function generateParentOfId(
139
+ parentId: string,
140
+ subsidiaryId: string
141
+ ): string {
142
+ ParentOfParamsSchema.parse({ parentId, subsidiaryId });
143
+ const compositeString = `${parentId}:${subsidiaryId}`;
144
+ return uuidv5(compositeString, NAMESPACES.PARENT_OF);
145
+ }
146
+
147
+ /**
148
+ * Generate a deterministic UUID v5 ID for a subsidiary_enrichment.
149
+ *
150
+ * @param companyId - UUID of the company
151
+ * @param filingId - UUID of the filing
152
+ * @returns UUID v5 string
153
+ * @throws ZodError if companyId or filingId is empty
154
+ *
155
+ * @example
156
+ * generateSubsidiaryEnrichmentId("company-uuid", "filing-uuid")
157
+ */
158
+ export function generateSubsidiaryEnrichmentId(
159
+ companyId: string,
160
+ filingId: string
161
+ ): string {
162
+ SubsidiaryEnrichmentParamsSchema.parse({ companyId, filingId });
163
+ const compositeString = `${companyId}:${filingId}`;
164
+ return uuidv5(compositeString, NAMESPACES.SUBSIDIARY_ENRICHMENT);
165
+ }
166
+
167
+ /**
168
+ * Generate a deterministic UUID v5 ID for an owns relationship.
169
+ *
170
+ * @param companyId - UUID of the owning company
171
+ * @param brandId - UUID of the brand
172
+ * @returns UUID v5 string
173
+ * @throws ZodError if companyId or brandId is empty
174
+ *
175
+ * @example
176
+ * generateOwnsId("company-uuid", "brand-uuid")
177
+ */
178
+ export function generateOwnsId(
179
+ companyId: string,
180
+ brandId: string
181
+ ): string {
182
+ OwnsParamsSchema.parse({ companyId, brandId });
183
+ const compositeString = `${companyId}:${brandId}`;
184
+ return uuidv5(compositeString, NAMESPACES.OWNS);
185
+ }
186
+
187
+ /**
188
+ * Generate a deterministic UUID v5 ID for a business_segment.
189
+ *
190
+ * @param companyId - UUID of the company
191
+ * @param segmentName - Name of the business segment
192
+ * @param fiscalYear - Fiscal year (integer)
193
+ * @param fiscalQuarter - Fiscal quarter (1-4) or null for annual
194
+ * @returns UUID v5 string
195
+ * @throws ZodError if required fields are invalid
196
+ *
197
+ * @example
198
+ * generateBusinessSegmentId("company-uuid", "Cloud Services", 2024, 1)
199
+ * generateBusinessSegmentId("company-uuid", "Cloud Services", 2024, null) // Annual
200
+ */
201
+ export function generateBusinessSegmentId(
202
+ companyId: string,
203
+ segmentName: string,
204
+ fiscalYear: number,
205
+ fiscalQuarter: number | null
206
+ ): string {
207
+ BusinessSegmentParamsSchema.parse({ companyId, segmentName, fiscalYear, fiscalQuarter });
208
+ const normalizedName = segmentName.trim().toLowerCase();
209
+ const quarter = fiscalQuarter !== null ? fiscalQuarter.toString() : 'null';
210
+ const compositeString = `${companyId}:${normalizedName}:${fiscalYear}:${quarter}`;
211
+ return uuidv5(compositeString, NAMESPACES.BUSINESS_SEGMENT);
212
+ }
213
+
214
+ /**
215
+ * Generate a deterministic UUID v5 ID for a brand.
216
+ *
217
+ * @param companyId - UUID of the owning company
218
+ * @param name - Name of the brand
219
+ * @returns UUID v5 string
220
+ * @throws ZodError if companyId or name is empty
221
+ *
222
+ * @example
223
+ * generateBrandId("company-uuid", "iPhone")
224
+ */
225
+ export function generateBrandId(
226
+ companyId: string,
227
+ name: string
228
+ ): string {
229
+ BrandParamsSchema.parse({ companyId, name });
230
+ const normalizedName = name.trim().toLowerCase();
231
+ const compositeString = `${companyId}:${normalizedName}`;
232
+ return uuidv5(compositeString, NAMESPACES.BRAND);
233
+ }
234
+
235
+ /**
236
+ * Generate a deterministic UUID v5 ID for company_info.
237
+ *
238
+ * @param companyId - UUID of the company
239
+ * @returns UUID v5 string
240
+ *
241
+ * @example
242
+ * generateCompanyInfoId("company-uuid")
243
+ */
244
+ export function generateCompanyInfoId(companyId: string): string {
245
+ if (!companyId || companyId.trim().length === 0) {
246
+ throw new Error("companyId is required for company_info ID generation");
247
+ }
248
+ return uuidv5(companyId, NAMESPACES.COMPANY_INFO);
249
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ // Future schema additions - M&A tracking entities
3
+ // These are not yet implemented but defined for future use
4
+ // To use: merge these entities into instant.schema.ts when ready
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.futureEntities = void 0;
7
+ const core_1 = require("@instantdb/core");
8
+ // === M&A Event Tracking ===
9
+ // Track mergers, acquisitions, spinoffs, and divestitures
10
+ exports.futureEntities = {
11
+ ma_events: core_1.i.entity({
12
+ acquirer_id: core_1.i.string().indexed(),
13
+ announced_date: core_1.i.string().optional(),
14
+ created_at: core_1.i.string(),
15
+ deal_value: core_1.i.number().optional(),
16
+ deal_value_currency: core_1.i.string(),
17
+ effective_date: core_1.i.string().indexed(),
18
+ event_type: core_1.i.string().indexed(), // "acquisition" | "merger" | "spinoff" | "divestiture"
19
+ status: core_1.i.string().indexed(), // "pending" | "completed" | "terminated"
20
+ target_id: core_1.i.string().indexed(),
21
+ updated_at: core_1.i.string(),
22
+ }),
23
+ // Edge: Acquirer acquired Target
24
+ acquired: core_1.i.entity({
25
+ created_at: core_1.i.string(),
26
+ from_company_id: core_1.i.string().indexed(), // Acquirer
27
+ ma_event_id: core_1.i.string().indexed(),
28
+ to_company_id: core_1.i.string().indexed(), // Target
29
+ }),
30
+ // Edge: Target was acquired by Acquirer (reverse)
31
+ was_acquired_by: core_1.i.entity({
32
+ created_at: core_1.i.string(),
33
+ from_company_id: core_1.i.string().indexed(), // Target
34
+ ma_event_id: core_1.i.string().indexed(),
35
+ to_company_id: core_1.i.string().indexed(), // Acquirer
36
+ }),
37
+ // Temporal snapshots of company state
38
+ company_snapshots: core_1.i.entity({
39
+ aliases: core_1.i.any(),
40
+ change_reason: core_1.i.string().indexed(), // "ma_event" | "spinoff" | "ipo" | "delisting" | "name_change"
41
+ company_id: core_1.i.string().indexed(),
42
+ created_at: core_1.i.string(),
43
+ identity: core_1.i.any(),
44
+ ma_event_id: core_1.i.string().indexed().optional(),
45
+ name: core_1.i.string(),
46
+ type: core_1.i.string(),
47
+ valid_from: core_1.i.string().indexed(),
48
+ valid_to: core_1.i.string().indexed().optional(),
49
+ }),
50
+ };
51
+ // Usage example:
52
+ // 1. Copy entities above into instant.schema.ts entities section
53
+ // 2. Implement M&A ingestion pipeline
54
+ // 3. Create repo functions for M&A operations
55
+ // 4. Update frontend to display M&A events
@@ -0,0 +1,59 @@
1
+ // Future schema additions - M&A tracking entities
2
+ // These are not yet implemented but defined for future use
3
+ // To use: merge these entities into instant.schema.ts when ready
4
+
5
+ import { i } from "@instantdb/core";
6
+
7
+ // === M&A Event Tracking ===
8
+ // Track mergers, acquisitions, spinoffs, and divestitures
9
+
10
+ export const futureEntities = {
11
+ ma_events: i.entity({
12
+ acquirer_id: i.string().indexed(),
13
+ announced_date: i.string().optional(),
14
+ created_at: i.string(),
15
+ deal_value: i.number().optional(),
16
+ deal_value_currency: i.string(),
17
+ effective_date: i.string().indexed(),
18
+ event_type: i.string().indexed(), // "acquisition" | "merger" | "spinoff" | "divestiture"
19
+ status: i.string().indexed(), // "pending" | "completed" | "terminated"
20
+ target_id: i.string().indexed(),
21
+ updated_at: i.string(),
22
+ }),
23
+
24
+ // Edge: Acquirer acquired Target
25
+ acquired: i.entity({
26
+ created_at: i.string(),
27
+ from_company_id: i.string().indexed(), // Acquirer
28
+ ma_event_id: i.string().indexed(),
29
+ to_company_id: i.string().indexed(), // Target
30
+ }),
31
+
32
+ // Edge: Target was acquired by Acquirer (reverse)
33
+ was_acquired_by: i.entity({
34
+ created_at: i.string(),
35
+ from_company_id: i.string().indexed(), // Target
36
+ ma_event_id: i.string().indexed(),
37
+ to_company_id: i.string().indexed(), // Acquirer
38
+ }),
39
+
40
+ // Temporal snapshots of company state
41
+ company_snapshots: i.entity({
42
+ aliases: i.any(),
43
+ change_reason: i.string().indexed(), // "ma_event" | "spinoff" | "ipo" | "delisting" | "name_change"
44
+ company_id: i.string().indexed(),
45
+ created_at: i.string(),
46
+ identity: i.any(),
47
+ ma_event_id: i.string().indexed().optional(),
48
+ name: i.string(),
49
+ type: i.string(),
50
+ valid_from: i.string().indexed(),
51
+ valid_to: i.string().indexed().optional(),
52
+ }),
53
+ };
54
+
55
+ // Usage example:
56
+ // 1. Copy entities above into instant.schema.ts entities section
57
+ // 2. Implement M&A ingestion pipeline
58
+ // 3. Create repo functions for M&A operations
59
+ // 4. Update frontend to display M&A events