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.
- package/README.md +75 -0
- package/dist/db/index.d.ts +11 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +11 -0
- package/dist/db/queries/cik-lookup.d.ts +49 -0
- package/dist/db/queries/cik-lookup.d.ts.map +1 -0
- package/dist/db/queries/cik-lookup.js +78 -0
- package/dist/db/queries/company-lookup.d.ts +43 -0
- package/dist/db/queries/company-lookup.d.ts.map +1 -0
- package/dist/db/queries/company-lookup.js +65 -0
- package/dist/db/queries/index.d.ts +8 -0
- package/dist/db/queries/index.d.ts.map +1 -0
- package/dist/db/queries/index.js +7 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/instant.schema.d.ts +170 -0
- package/dist/instant.schema.d.ts.map +1 -0
- package/dist/instant.schema.js +222 -0
- package/dist/types/ids.d.ts +135 -0
- package/dist/types/ids.d.ts.map +1 -0
- package/dist/types/ids.js +188 -0
- package/dist/types/instant.schema.future.d.ts +39 -0
- package/dist/types/instant.schema.future.d.ts.map +1 -0
- package/dist/types/instant.schema.future.js +52 -0
- package/dist/types/types.d.ts +321 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/types.js +180 -0
- package/dist/types/validation.d.ts +7 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +14 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/logger-interface.d.ts +57 -0
- package/dist/utils/logger-interface.d.ts.map +1 -0
- package/dist/utils/logger-interface.js +38 -0
- package/package.json +54 -0
- package/types/ids.ts +249 -0
- package/types/instant.schema.future.js +55 -0
- package/types/instant.schema.future.ts +59 -0
- package/types/types.ts +292 -0
- package/types/validation.ts +32 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// Shared InstantDB schema
|
|
2
|
+
// Used by both backend and frontend
|
|
3
|
+
// Docs: https://www.instantdb.com/docs/modeling-data
|
|
4
|
+
import { i } from "@instantdb/core";
|
|
5
|
+
const _schema = i.schema({
|
|
6
|
+
entities: {
|
|
7
|
+
// InstantDB system entities
|
|
8
|
+
$files: i.entity({
|
|
9
|
+
path: i.string().unique().indexed(),
|
|
10
|
+
url: i.string(),
|
|
11
|
+
}),
|
|
12
|
+
$users: i.entity({
|
|
13
|
+
email: i.string().unique().indexed().optional(),
|
|
14
|
+
imageURL: i.string().optional(),
|
|
15
|
+
type: i.string().optional(),
|
|
16
|
+
}),
|
|
17
|
+
// === CURRENTLY USED ENTITIES ===
|
|
18
|
+
company: i.entity({
|
|
19
|
+
name: i.string().indexed(),
|
|
20
|
+
type: i.number().indexed(), // 1=public, 2=private, 3=issuer, 4=unknown, 5=trust
|
|
21
|
+
jurisdiction_raw: i.string().optional(),
|
|
22
|
+
jurisdiction_iso: i.string().optional(),
|
|
23
|
+
aliases: i.json().optional(),
|
|
24
|
+
identity: i.json().optional(),
|
|
25
|
+
updated_at: i.string(),
|
|
26
|
+
}),
|
|
27
|
+
filing: i.entity({
|
|
28
|
+
accession_number: i.string().unique(),
|
|
29
|
+
accession_number_nodashes: i.string().unique().indexed(),
|
|
30
|
+
file_url: i.string(),
|
|
31
|
+
form_type: i.string().indexed(),
|
|
32
|
+
source_quarter: i.number().indexed(), // When filed: 1-4
|
|
33
|
+
source_year: i.number().indexed(), // When filed: e.g., 2025
|
|
34
|
+
filing_date: i.string().indexed(), // Date when filed with SEC
|
|
35
|
+
period_of_report: i.string().optional(), // Period end date from the filing (renamed from period_end_date)
|
|
36
|
+
attachments: i.json().optional(),
|
|
37
|
+
file_name: i.string().optional(),
|
|
38
|
+
updated_at: i.string(),
|
|
39
|
+
}),
|
|
40
|
+
parent_of: i.entity({
|
|
41
|
+
source: i.number().indexed(), // 1=ma_event, 2=spinoff, 3=ipo, 4=manual, 5=sec_filing
|
|
42
|
+
ownership_percent: i.number().optional(),
|
|
43
|
+
established_date: i.string().optional(),
|
|
44
|
+
ended_date: i.string().optional(),
|
|
45
|
+
updated_at: i.string(),
|
|
46
|
+
}),
|
|
47
|
+
subsidiary_enrichment: i.entity({
|
|
48
|
+
footnoteRefs: i.json(),
|
|
49
|
+
footnotesHtml: i.string().optional(),
|
|
50
|
+
llmEnriched: i.boolean().indexed(),
|
|
51
|
+
llmEnrichedAt: i.string().optional(),
|
|
52
|
+
updated_at: i.string(),
|
|
53
|
+
}),
|
|
54
|
+
company_info: i.entity({
|
|
55
|
+
fiscal_year_end: i.string().optional(),
|
|
56
|
+
addresses: i.json().optional(),
|
|
57
|
+
phone: i.string().optional(),
|
|
58
|
+
former_names: i.json().optional(),
|
|
59
|
+
updated_at: i.string(),
|
|
60
|
+
}),
|
|
61
|
+
brand: i.entity({
|
|
62
|
+
category: i.string().indexed().optional(),
|
|
63
|
+
created_at: i.string(),
|
|
64
|
+
launch_date: i.string().optional(),
|
|
65
|
+
name: i.string().indexed(),
|
|
66
|
+
status: i.string().indexed(),
|
|
67
|
+
updated_at: i.string(),
|
|
68
|
+
}),
|
|
69
|
+
owns: i.entity({
|
|
70
|
+
acquired_date: i.string().optional(),
|
|
71
|
+
created_at: i.string(),
|
|
72
|
+
divested_date: i.string().indexed().optional(),
|
|
73
|
+
updated_at: i.string(),
|
|
74
|
+
}),
|
|
75
|
+
// Audit trail (optional - can be disabled via ENABLE_AUDIT_TRAIL=false)
|
|
76
|
+
audit: i.entity({
|
|
77
|
+
changed_at: i.string().indexed(),
|
|
78
|
+
changed_by: i.string().indexed(),
|
|
79
|
+
entity_id: i.string().indexed(),
|
|
80
|
+
entity_type: i.string().indexed(),
|
|
81
|
+
expires_at: i.string().indexed(),
|
|
82
|
+
fields_changed: i.json(),
|
|
83
|
+
operation: i.string().indexed(),
|
|
84
|
+
source_id: i.string().indexed().optional(),
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
links: {
|
|
88
|
+
// Company -> Filing (many-to-many: multiple companies can file the same document)
|
|
89
|
+
filing: {
|
|
90
|
+
forward: {
|
|
91
|
+
on: "company",
|
|
92
|
+
has: "many",
|
|
93
|
+
label: "filings",
|
|
94
|
+
},
|
|
95
|
+
reverse: {
|
|
96
|
+
on: "filing",
|
|
97
|
+
has: "many",
|
|
98
|
+
label: "companies",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
// Parent company link (parent_of.parentCompany -> company)
|
|
102
|
+
parentCompany: {
|
|
103
|
+
forward: {
|
|
104
|
+
on: "parent_of",
|
|
105
|
+
has: "one",
|
|
106
|
+
label: "parentCompany",
|
|
107
|
+
},
|
|
108
|
+
reverse: {
|
|
109
|
+
on: "company",
|
|
110
|
+
has: "many",
|
|
111
|
+
label: "subsidiaries",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
// Subsidiary company link (parent_of.subsidiaryCompany -> company)
|
|
115
|
+
subsidiaryCompany: {
|
|
116
|
+
forward: {
|
|
117
|
+
on: "parent_of",
|
|
118
|
+
has: "one",
|
|
119
|
+
label: "subsidiaryCompany",
|
|
120
|
+
},
|
|
121
|
+
reverse: {
|
|
122
|
+
on: "company",
|
|
123
|
+
has: "many", //Temporal tracking: a company could have different parents over time
|
|
124
|
+
label: "parents",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
// Source filing link (parent_of.sourceFiling -> filing, when source=5/sec_filing)
|
|
128
|
+
sourceFiling: {
|
|
129
|
+
forward: {
|
|
130
|
+
on: "parent_of",
|
|
131
|
+
has: "one",
|
|
132
|
+
label: "sourceFiling",
|
|
133
|
+
},
|
|
134
|
+
reverse: {
|
|
135
|
+
on: "filing",
|
|
136
|
+
has: "many",
|
|
137
|
+
label: "parentOfEdges",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
// Company -> Subsidiary Enrichment
|
|
141
|
+
subsidiaryEnrichment: {
|
|
142
|
+
forward: {
|
|
143
|
+
on: "company",
|
|
144
|
+
has: "many",
|
|
145
|
+
label: "subsidiaryEnrichments",
|
|
146
|
+
},
|
|
147
|
+
reverse: {
|
|
148
|
+
on: "subsidiary_enrichment",
|
|
149
|
+
has: "one",
|
|
150
|
+
label: "company",
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
// Filing -> Subsidiary Enrichment
|
|
154
|
+
filingEnrichment: {
|
|
155
|
+
forward: {
|
|
156
|
+
on: "filing",
|
|
157
|
+
has: "many",
|
|
158
|
+
label: "subsidiaryEnrichments",
|
|
159
|
+
},
|
|
160
|
+
reverse: {
|
|
161
|
+
on: "subsidiary_enrichment",
|
|
162
|
+
has: "one",
|
|
163
|
+
label: "filing",
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
// Company -> Company Info
|
|
167
|
+
companyInfo: {
|
|
168
|
+
forward: {
|
|
169
|
+
on: "company",
|
|
170
|
+
has: "one",
|
|
171
|
+
label: "companyInfo",
|
|
172
|
+
},
|
|
173
|
+
reverse: {
|
|
174
|
+
on: "company_info",
|
|
175
|
+
has: "one",
|
|
176
|
+
label: "company",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
// // Company -> Brand (direct ownership link)
|
|
180
|
+
// brandOwner: {
|
|
181
|
+
// forward: {
|
|
182
|
+
// on: "company",
|
|
183
|
+
// has: "many",
|
|
184
|
+
// label: "brands",
|
|
185
|
+
// },
|
|
186
|
+
// reverse: {
|
|
187
|
+
// on: "brand",
|
|
188
|
+
// has: "one",
|
|
189
|
+
// label: "owningCompany",
|
|
190
|
+
// },
|
|
191
|
+
// },
|
|
192
|
+
// // Company -> Brand (via owns edge for historical tracking)
|
|
193
|
+
// ownsLink: {
|
|
194
|
+
// forward: {
|
|
195
|
+
// on: "owns",
|
|
196
|
+
// has: "one",
|
|
197
|
+
// label: "company",
|
|
198
|
+
// },
|
|
199
|
+
// reverse: {
|
|
200
|
+
// on: "company",
|
|
201
|
+
// has: "many",
|
|
202
|
+
// label: "ownedBrands",
|
|
203
|
+
// },
|
|
204
|
+
// },
|
|
205
|
+
// // Brand -> Owns
|
|
206
|
+
// brandOwnership: {
|
|
207
|
+
// forward: {
|
|
208
|
+
// on: "owns",
|
|
209
|
+
// has: "one",
|
|
210
|
+
// label: "brand",
|
|
211
|
+
// },
|
|
212
|
+
// reverse: {
|
|
213
|
+
// on: "brand",
|
|
214
|
+
// has: "many",
|
|
215
|
+
// label: "ownership",
|
|
216
|
+
// },
|
|
217
|
+
// },
|
|
218
|
+
},
|
|
219
|
+
rooms: {},
|
|
220
|
+
});
|
|
221
|
+
const schema = _schema;
|
|
222
|
+
export default schema;
|
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
import { type CompanyIdentity } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Namespace UUIDs for each entity type.
|
|
13
|
+
* These are used with UUID v5 to generate deterministic IDs.
|
|
14
|
+
* Each namespace ensures IDs are unique within their entity type.
|
|
15
|
+
*/
|
|
16
|
+
export declare const NAMESPACES: {
|
|
17
|
+
/** Namespace for company entity IDs */
|
|
18
|
+
readonly COMPANY: "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
|
|
19
|
+
/** Namespace for filing entity IDs */
|
|
20
|
+
readonly FILING: "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
|
|
21
|
+
/** Namespace for parent_of relationship IDs */
|
|
22
|
+
readonly PARENT_OF: "6ba7b812-9dad-11d1-80b4-00c04fd430c8";
|
|
23
|
+
/** Namespace for subsidiary_enrichment entity IDs */
|
|
24
|
+
readonly SUBSIDIARY_ENRICHMENT: "6ba7b813-9dad-11d1-80b4-00c04fd430c8";
|
|
25
|
+
/** Namespace for business_segment entity IDs */
|
|
26
|
+
readonly BUSINESS_SEGMENT: "6ba7b814-9dad-11d1-80b4-00c04fd430c8";
|
|
27
|
+
/** Namespace for brand entity IDs */
|
|
28
|
+
readonly BRAND: "6ba7b815-9dad-11d1-80b4-00c04fd430c8";
|
|
29
|
+
/** Namespace for owns relationship IDs */
|
|
30
|
+
readonly OWNS: "6ba7b816-9dad-11d1-80b4-00c04fd430c8";
|
|
31
|
+
/** Namespace for company_info entity IDs */
|
|
32
|
+
readonly COMPANY_INFO: "6ba7b817-9dad-11d1-80b4-00c04fd430c8";
|
|
33
|
+
};
|
|
34
|
+
export type NamespaceKey = keyof typeof NAMESPACES;
|
|
35
|
+
/**
|
|
36
|
+
* Company data required for ID generation
|
|
37
|
+
*/
|
|
38
|
+
export interface CompanyIdInput {
|
|
39
|
+
type: number;
|
|
40
|
+
name?: string;
|
|
41
|
+
jurisdiction_raw?: string;
|
|
42
|
+
identity?: CompanyIdentity;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Generate a deterministic UUID v5 ID for a company.
|
|
46
|
+
* @param company - Company data with type and required fields
|
|
47
|
+
* @returns UUID v5 string
|
|
48
|
+
*/
|
|
49
|
+
export declare function generateCompanyId(company: CompanyIdInput): string;
|
|
50
|
+
/**
|
|
51
|
+
* Generate a deterministic UUID v5 ID for a filing.
|
|
52
|
+
*
|
|
53
|
+
* @param accessionNumber - SEC accession number (e.g., "0001193125-24-123456" or "0001193125241234567")
|
|
54
|
+
* @returns UUID v5 string
|
|
55
|
+
* @throws ZodError if accession number is invalid
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* generateFilingId("0001193125-24-123456") // Returns UUID v5
|
|
59
|
+
* generateFilingId("0001193125241234567") // Returns same UUID v5 (normalized)
|
|
60
|
+
*/
|
|
61
|
+
export declare function generateFilingId(accessionNumber: string): string;
|
|
62
|
+
/**
|
|
63
|
+
* Generate a deterministic UUID v5 ID for a parent_of relationship.
|
|
64
|
+
*
|
|
65
|
+
* @param parentId - UUID of the parent company
|
|
66
|
+
* @param subsidiaryId - UUID of the subsidiary company
|
|
67
|
+
* @returns UUID v5 string
|
|
68
|
+
* @throws ZodError if parentId or subsidiaryId is empty
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* generateParentOfId("parent-uuid", "subsidiary-uuid")
|
|
72
|
+
*/
|
|
73
|
+
export declare function generateParentOfId(parentId: string, subsidiaryId: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* Generate a deterministic UUID v5 ID for a subsidiary_enrichment.
|
|
76
|
+
*
|
|
77
|
+
* @param companyId - UUID of the company
|
|
78
|
+
* @param filingId - UUID of the filing
|
|
79
|
+
* @returns UUID v5 string
|
|
80
|
+
* @throws ZodError if companyId or filingId is empty
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* generateSubsidiaryEnrichmentId("company-uuid", "filing-uuid")
|
|
84
|
+
*/
|
|
85
|
+
export declare function generateSubsidiaryEnrichmentId(companyId: string, filingId: string): string;
|
|
86
|
+
/**
|
|
87
|
+
* Generate a deterministic UUID v5 ID for an owns relationship.
|
|
88
|
+
*
|
|
89
|
+
* @param companyId - UUID of the owning company
|
|
90
|
+
* @param brandId - UUID of the brand
|
|
91
|
+
* @returns UUID v5 string
|
|
92
|
+
* @throws ZodError if companyId or brandId is empty
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* generateOwnsId("company-uuid", "brand-uuid")
|
|
96
|
+
*/
|
|
97
|
+
export declare function generateOwnsId(companyId: string, brandId: string): string;
|
|
98
|
+
/**
|
|
99
|
+
* Generate a deterministic UUID v5 ID for a business_segment.
|
|
100
|
+
*
|
|
101
|
+
* @param companyId - UUID of the company
|
|
102
|
+
* @param segmentName - Name of the business segment
|
|
103
|
+
* @param fiscalYear - Fiscal year (integer)
|
|
104
|
+
* @param fiscalQuarter - Fiscal quarter (1-4) or null for annual
|
|
105
|
+
* @returns UUID v5 string
|
|
106
|
+
* @throws ZodError if required fields are invalid
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* generateBusinessSegmentId("company-uuid", "Cloud Services", 2024, 1)
|
|
110
|
+
* generateBusinessSegmentId("company-uuid", "Cloud Services", 2024, null) // Annual
|
|
111
|
+
*/
|
|
112
|
+
export declare function generateBusinessSegmentId(companyId: string, segmentName: string, fiscalYear: number, fiscalQuarter: number | null): string;
|
|
113
|
+
/**
|
|
114
|
+
* Generate a deterministic UUID v5 ID for a brand.
|
|
115
|
+
*
|
|
116
|
+
* @param companyId - UUID of the owning company
|
|
117
|
+
* @param name - Name of the brand
|
|
118
|
+
* @returns UUID v5 string
|
|
119
|
+
* @throws ZodError if companyId or name is empty
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* generateBrandId("company-uuid", "iPhone")
|
|
123
|
+
*/
|
|
124
|
+
export declare function generateBrandId(companyId: string, name: string): string;
|
|
125
|
+
/**
|
|
126
|
+
* Generate a deterministic UUID v5 ID for company_info.
|
|
127
|
+
*
|
|
128
|
+
* @param companyId - UUID of the company
|
|
129
|
+
* @returns UUID v5 string
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* generateCompanyInfoId("company-uuid")
|
|
133
|
+
*/
|
|
134
|
+
export declare function generateCompanyInfoId(companyId: string): string;
|
|
135
|
+
//# sourceMappingURL=ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../types/ids.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAEL,KAAK,eAAe,EAWrB,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,eAAO,MAAM,UAAU;IACrB,uCAAuC;;IAEvC,sCAAsC;;IAEtC,+CAA+C;;IAE/C,qDAAqD;;IAErD,gDAAgD;;IAEhD,qCAAqC;;IAErC,0CAA0C;;IAE1C,4CAA4C;;CAEpC,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,UAAU,CAAC;AAMnD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CA6BjE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAGhE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,CAIR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,CAIR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CAIR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,GAAG,IAAI,GAC3B,MAAM,CAMR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX,MAAM,CAKR;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAK/D"}
|
|
@@ -0,0 +1,188 @@
|
|
|
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
|
+
import { v5 as uuidv5 } from 'uuid';
|
|
11
|
+
import { CompanyType, PublicCompanySchema, IssuerCompanySchema, PrivateCompanySchema, UnknownCompanySchema, AccessionNumberString, ParentOfParamsSchema, SubsidiaryEnrichmentParamsSchema, BusinessSegmentParamsSchema, BrandParamsSchema, OwnsParamsSchema, } from './types';
|
|
12
|
+
/**
|
|
13
|
+
* Namespace UUIDs for each entity type.
|
|
14
|
+
* These are used with UUID v5 to generate deterministic IDs.
|
|
15
|
+
* Each namespace ensures IDs are unique within their entity type.
|
|
16
|
+
*/
|
|
17
|
+
export const NAMESPACES = {
|
|
18
|
+
/** Namespace for company entity IDs */
|
|
19
|
+
COMPANY: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
|
|
20
|
+
/** Namespace for filing entity IDs */
|
|
21
|
+
FILING: '6ba7b811-9dad-11d1-80b4-00c04fd430c8',
|
|
22
|
+
/** Namespace for parent_of relationship IDs */
|
|
23
|
+
PARENT_OF: '6ba7b812-9dad-11d1-80b4-00c04fd430c8',
|
|
24
|
+
/** Namespace for subsidiary_enrichment entity IDs */
|
|
25
|
+
SUBSIDIARY_ENRICHMENT: '6ba7b813-9dad-11d1-80b4-00c04fd430c8',
|
|
26
|
+
/** Namespace for business_segment entity IDs */
|
|
27
|
+
BUSINESS_SEGMENT: '6ba7b814-9dad-11d1-80b4-00c04fd430c8',
|
|
28
|
+
/** Namespace for brand entity IDs */
|
|
29
|
+
BRAND: '6ba7b815-9dad-11d1-80b4-00c04fd430c8',
|
|
30
|
+
/** Namespace for owns relationship IDs */
|
|
31
|
+
OWNS: '6ba7b816-9dad-11d1-80b4-00c04fd430c8',
|
|
32
|
+
/** Namespace for company_info entity IDs */
|
|
33
|
+
COMPANY_INFO: '6ba7b817-9dad-11d1-80b4-00c04fd430c8',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Generate a deterministic UUID v5 ID for a company.
|
|
37
|
+
* @param company - Company data with type and required fields
|
|
38
|
+
* @returns UUID v5 string
|
|
39
|
+
*/
|
|
40
|
+
export function generateCompanyId(company) {
|
|
41
|
+
switch (company.type) {
|
|
42
|
+
case CompanyType.PUBLIC: {
|
|
43
|
+
const validated = PublicCompanySchema.parse(company);
|
|
44
|
+
return uuidv5(`${company.type}:${validated.identity.primaryCIK}`, NAMESPACES.COMPANY);
|
|
45
|
+
}
|
|
46
|
+
case CompanyType.ISSUER: {
|
|
47
|
+
const validated = IssuerCompanySchema.parse(company);
|
|
48
|
+
return uuidv5(`${company.type}:${validated.identity.primaryCIK}`, NAMESPACES.COMPANY);
|
|
49
|
+
}
|
|
50
|
+
case CompanyType.PRIVATE: {
|
|
51
|
+
const validated = PrivateCompanySchema.parse(company);
|
|
52
|
+
const normalizedName = validated.name.trim().toLowerCase();
|
|
53
|
+
const normalizedJurisdiction = validated.jurisdiction_raw.trim().toLowerCase();
|
|
54
|
+
return uuidv5(`${company.type}:${normalizedName}:${normalizedJurisdiction}`, NAMESPACES.COMPANY);
|
|
55
|
+
}
|
|
56
|
+
default: {
|
|
57
|
+
const validated = UnknownCompanySchema.parse(company);
|
|
58
|
+
const normalizedName = validated.name.trim().toLowerCase();
|
|
59
|
+
const normalizedJurisdiction = validated.jurisdiction_raw?.trim().toLowerCase() || '';
|
|
60
|
+
const compositeString = normalizedJurisdiction
|
|
61
|
+
? `${company.type}:${normalizedName}:${normalizedJurisdiction}`
|
|
62
|
+
: `${company.type}:${normalizedName}`;
|
|
63
|
+
return uuidv5(compositeString, NAMESPACES.COMPANY);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// FILING ID GENERATION
|
|
69
|
+
// ============================================================================
|
|
70
|
+
/**
|
|
71
|
+
* Generate a deterministic UUID v5 ID for a filing.
|
|
72
|
+
*
|
|
73
|
+
* @param accessionNumber - SEC accession number (e.g., "0001193125-24-123456" or "0001193125241234567")
|
|
74
|
+
* @returns UUID v5 string
|
|
75
|
+
* @throws ZodError if accession number is invalid
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* generateFilingId("0001193125-24-123456") // Returns UUID v5
|
|
79
|
+
* generateFilingId("0001193125241234567") // Returns same UUID v5 (normalized)
|
|
80
|
+
*/
|
|
81
|
+
export function generateFilingId(accessionNumber) {
|
|
82
|
+
const normalized = AccessionNumberString.parse(accessionNumber);
|
|
83
|
+
return uuidv5(normalized, NAMESPACES.FILING);
|
|
84
|
+
}
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// RELATIONSHIP ENTITY ID GENERATION
|
|
87
|
+
// ============================================================================
|
|
88
|
+
/**
|
|
89
|
+
* Generate a deterministic UUID v5 ID for a parent_of relationship.
|
|
90
|
+
*
|
|
91
|
+
* @param parentId - UUID of the parent company
|
|
92
|
+
* @param subsidiaryId - UUID of the subsidiary company
|
|
93
|
+
* @returns UUID v5 string
|
|
94
|
+
* @throws ZodError if parentId or subsidiaryId is empty
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* generateParentOfId("parent-uuid", "subsidiary-uuid")
|
|
98
|
+
*/
|
|
99
|
+
export function generateParentOfId(parentId, subsidiaryId) {
|
|
100
|
+
ParentOfParamsSchema.parse({ parentId, subsidiaryId });
|
|
101
|
+
const compositeString = `${parentId}:${subsidiaryId}`;
|
|
102
|
+
return uuidv5(compositeString, NAMESPACES.PARENT_OF);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Generate a deterministic UUID v5 ID for a subsidiary_enrichment.
|
|
106
|
+
*
|
|
107
|
+
* @param companyId - UUID of the company
|
|
108
|
+
* @param filingId - UUID of the filing
|
|
109
|
+
* @returns UUID v5 string
|
|
110
|
+
* @throws ZodError if companyId or filingId is empty
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* generateSubsidiaryEnrichmentId("company-uuid", "filing-uuid")
|
|
114
|
+
*/
|
|
115
|
+
export function generateSubsidiaryEnrichmentId(companyId, filingId) {
|
|
116
|
+
SubsidiaryEnrichmentParamsSchema.parse({ companyId, filingId });
|
|
117
|
+
const compositeString = `${companyId}:${filingId}`;
|
|
118
|
+
return uuidv5(compositeString, NAMESPACES.SUBSIDIARY_ENRICHMENT);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Generate a deterministic UUID v5 ID for an owns relationship.
|
|
122
|
+
*
|
|
123
|
+
* @param companyId - UUID of the owning company
|
|
124
|
+
* @param brandId - UUID of the brand
|
|
125
|
+
* @returns UUID v5 string
|
|
126
|
+
* @throws ZodError if companyId or brandId is empty
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* generateOwnsId("company-uuid", "brand-uuid")
|
|
130
|
+
*/
|
|
131
|
+
export function generateOwnsId(companyId, brandId) {
|
|
132
|
+
OwnsParamsSchema.parse({ companyId, brandId });
|
|
133
|
+
const compositeString = `${companyId}:${brandId}`;
|
|
134
|
+
return uuidv5(compositeString, NAMESPACES.OWNS);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Generate a deterministic UUID v5 ID for a business_segment.
|
|
138
|
+
*
|
|
139
|
+
* @param companyId - UUID of the company
|
|
140
|
+
* @param segmentName - Name of the business segment
|
|
141
|
+
* @param fiscalYear - Fiscal year (integer)
|
|
142
|
+
* @param fiscalQuarter - Fiscal quarter (1-4) or null for annual
|
|
143
|
+
* @returns UUID v5 string
|
|
144
|
+
* @throws ZodError if required fields are invalid
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* generateBusinessSegmentId("company-uuid", "Cloud Services", 2024, 1)
|
|
148
|
+
* generateBusinessSegmentId("company-uuid", "Cloud Services", 2024, null) // Annual
|
|
149
|
+
*/
|
|
150
|
+
export function generateBusinessSegmentId(companyId, segmentName, fiscalYear, fiscalQuarter) {
|
|
151
|
+
BusinessSegmentParamsSchema.parse({ companyId, segmentName, fiscalYear, fiscalQuarter });
|
|
152
|
+
const normalizedName = segmentName.trim().toLowerCase();
|
|
153
|
+
const quarter = fiscalQuarter !== null ? fiscalQuarter.toString() : 'null';
|
|
154
|
+
const compositeString = `${companyId}:${normalizedName}:${fiscalYear}:${quarter}`;
|
|
155
|
+
return uuidv5(compositeString, NAMESPACES.BUSINESS_SEGMENT);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Generate a deterministic UUID v5 ID for a brand.
|
|
159
|
+
*
|
|
160
|
+
* @param companyId - UUID of the owning company
|
|
161
|
+
* @param name - Name of the brand
|
|
162
|
+
* @returns UUID v5 string
|
|
163
|
+
* @throws ZodError if companyId or name is empty
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* generateBrandId("company-uuid", "iPhone")
|
|
167
|
+
*/
|
|
168
|
+
export function generateBrandId(companyId, name) {
|
|
169
|
+
BrandParamsSchema.parse({ companyId, name });
|
|
170
|
+
const normalizedName = name.trim().toLowerCase();
|
|
171
|
+
const compositeString = `${companyId}:${normalizedName}`;
|
|
172
|
+
return uuidv5(compositeString, NAMESPACES.BRAND);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Generate a deterministic UUID v5 ID for company_info.
|
|
176
|
+
*
|
|
177
|
+
* @param companyId - UUID of the company
|
|
178
|
+
* @returns UUID v5 string
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* generateCompanyInfoId("company-uuid")
|
|
182
|
+
*/
|
|
183
|
+
export function generateCompanyInfoId(companyId) {
|
|
184
|
+
if (!companyId || companyId.trim().length === 0) {
|
|
185
|
+
throw new Error("companyId is required for company_info ID generation");
|
|
186
|
+
}
|
|
187
|
+
return uuidv5(companyId, NAMESPACES.COMPANY_INFO);
|
|
188
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare const futureEntities: {
|
|
2
|
+
ma_events: import("@instantdb/core").EntityDef<{
|
|
3
|
+
acquirer_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
4
|
+
announced_date: import("@instantdb/core").DataAttrDef<string, false, false>;
|
|
5
|
+
created_at: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
6
|
+
deal_value: import("@instantdb/core").DataAttrDef<number, false, false>;
|
|
7
|
+
deal_value_currency: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
8
|
+
effective_date: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
9
|
+
event_type: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
10
|
+
status: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
11
|
+
target_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
12
|
+
updated_at: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
13
|
+
}, {}, void>;
|
|
14
|
+
acquired: import("@instantdb/core").EntityDef<{
|
|
15
|
+
created_at: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
16
|
+
from_company_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
17
|
+
ma_event_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
18
|
+
to_company_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
19
|
+
}, {}, void>;
|
|
20
|
+
was_acquired_by: import("@instantdb/core").EntityDef<{
|
|
21
|
+
created_at: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
22
|
+
from_company_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
23
|
+
ma_event_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
24
|
+
to_company_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
25
|
+
}, {}, void>;
|
|
26
|
+
company_snapshots: import("@instantdb/core").EntityDef<{
|
|
27
|
+
aliases: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
28
|
+
change_reason: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
29
|
+
company_id: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
30
|
+
created_at: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
31
|
+
identity: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
32
|
+
ma_event_id: import("@instantdb/core").DataAttrDef<string, false, true>;
|
|
33
|
+
name: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
34
|
+
type: import("@instantdb/core").DataAttrDef<any, true, false>;
|
|
35
|
+
valid_from: import("@instantdb/core").DataAttrDef<string, true, true>;
|
|
36
|
+
valid_to: import("@instantdb/core").DataAttrDef<string, false, true>;
|
|
37
|
+
}, {}, void>;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=instant.schema.future.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instant.schema.future.d.ts","sourceRoot":"","sources":["../../types/instant.schema.future.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2C1B,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
import { i } from "@instantdb/core";
|
|
5
|
+
// === M&A Event Tracking ===
|
|
6
|
+
// Track mergers, acquisitions, spinoffs, and divestitures
|
|
7
|
+
export const futureEntities = {
|
|
8
|
+
ma_events: i.entity({
|
|
9
|
+
acquirer_id: i.string().indexed(),
|
|
10
|
+
announced_date: i.string().optional(),
|
|
11
|
+
created_at: i.string(),
|
|
12
|
+
deal_value: i.number().optional(),
|
|
13
|
+
deal_value_currency: i.string(),
|
|
14
|
+
effective_date: i.string().indexed(),
|
|
15
|
+
event_type: i.string().indexed(), // "acquisition" | "merger" | "spinoff" | "divestiture"
|
|
16
|
+
status: i.string().indexed(), // "pending" | "completed" | "terminated"
|
|
17
|
+
target_id: i.string().indexed(),
|
|
18
|
+
updated_at: i.string(),
|
|
19
|
+
}),
|
|
20
|
+
// Edge: Acquirer acquired Target
|
|
21
|
+
acquired: i.entity({
|
|
22
|
+
created_at: i.string(),
|
|
23
|
+
from_company_id: i.string().indexed(), // Acquirer
|
|
24
|
+
ma_event_id: i.string().indexed(),
|
|
25
|
+
to_company_id: i.string().indexed(), // Target
|
|
26
|
+
}),
|
|
27
|
+
// Edge: Target was acquired by Acquirer (reverse)
|
|
28
|
+
was_acquired_by: i.entity({
|
|
29
|
+
created_at: i.string(),
|
|
30
|
+
from_company_id: i.string().indexed(), // Target
|
|
31
|
+
ma_event_id: i.string().indexed(),
|
|
32
|
+
to_company_id: i.string().indexed(), // Acquirer
|
|
33
|
+
}),
|
|
34
|
+
// Temporal snapshots of company state
|
|
35
|
+
company_snapshots: i.entity({
|
|
36
|
+
aliases: i.any(),
|
|
37
|
+
change_reason: i.string().indexed(), // "ma_event" | "spinoff" | "ipo" | "delisting" | "name_change"
|
|
38
|
+
company_id: i.string().indexed(),
|
|
39
|
+
created_at: i.string(),
|
|
40
|
+
identity: i.any(),
|
|
41
|
+
ma_event_id: i.string().indexed().optional(),
|
|
42
|
+
name: i.string(),
|
|
43
|
+
type: i.string(),
|
|
44
|
+
valid_from: i.string().indexed(),
|
|
45
|
+
valid_to: i.string().indexed().optional(),
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
// Usage example:
|
|
49
|
+
// 1. Copy entities above into instant.schema.ts entities section
|
|
50
|
+
// 2. Implement M&A ingestion pipeline
|
|
51
|
+
// 3. Create repo functions for M&A operations
|
|
52
|
+
// 4. Update frontend to display M&A events
|