@robosystems/report-components 0.1.4 → 0.2.1

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.
@@ -0,0 +1,721 @@
1
+ import { DataFactory, Store, Parser } from 'n3';
2
+ import jsonld from 'jsonld';
3
+
4
+ // src/constants.ts
5
+ var NS = {
6
+ rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
7
+ rs: "https://robosystems.ai/vocab/",
8
+ rsgaap: "https://robosystems.ai/taxonomy/rs-gaap/v1/",
9
+ skos: "http://www.w3.org/2004/02/skos/core#",
10
+ xlink: "http://www.w3.org/1999/xlink#",
11
+ xbrli: "http://www.xbrl.org/2003/instance#",
12
+ link: "http://www.xbrl.org/2003/linkbase#",
13
+ iso4217: "http://www.xbrl.org/2003/iso4217#"
14
+ };
15
+ var IRI = {
16
+ type: NS.rdf + "type",
17
+ // rs: classes
18
+ Report: NS.rs + "Report",
19
+ Fact: NS.rs + "Fact",
20
+ InformationBlock: NS.rs + "InformationBlock",
21
+ Structure: NS.rs + "Structure",
22
+ Association: NS.rs + "Association",
23
+ Element: NS.rs + "Element",
24
+ Period: NS.rs + "Period",
25
+ Unit: NS.rs + "Unit",
26
+ Entity: NS.rs + "Entity",
27
+ // rs: predicates
28
+ element: NS.rs + "element",
29
+ period: NS.rs + "period",
30
+ unit: NS.rs + "unit",
31
+ entity: NS.rs + "entity",
32
+ factSet: NS.rs + "factSet",
33
+ numericValue: NS.rs + "numericValue",
34
+ decimals: NS.rs + "decimals",
35
+ blockType: NS.rs + "blockType",
36
+ associationType: NS.rs + "associationType",
37
+ hasAssociation: NS.rs + "hasAssociation",
38
+ roleUri: NS.rs + "roleUri",
39
+ structureName: NS.rs + "structureName",
40
+ abstract: NS.rs + "abstract",
41
+ monetary: NS.rs + "monetary",
42
+ legalName: NS.rs + "legalName",
43
+ country: NS.rs + "country",
44
+ // skos:
45
+ prefLabel: NS.skos + "prefLabel",
46
+ // xlink:
47
+ from: NS.xlink + "from",
48
+ to: NS.xlink + "to",
49
+ role: NS.xlink + "role",
50
+ // xbrli:
51
+ instant: NS.xbrli + "instant",
52
+ startDate: NS.xbrli + "startDate",
53
+ endDate: NS.xbrli + "endDate",
54
+ periodType: NS.xbrli + "periodType",
55
+ balance: NS.xbrli + "balance",
56
+ measure: NS.xbrli + "measure",
57
+ // link:
58
+ order: NS.link + "order",
59
+ weight: NS.link + "weight"
60
+ };
61
+ var PREFIXES = [
62
+ ["rs-gaap", NS.rsgaap],
63
+ ["disclosures", "https://robosystems.ai/taxonomy/rs-gaap/disclosures/v1/"],
64
+ ["us-gaap", "http://fasb.org/us-gaap/"],
65
+ ["dei", "http://xbrl.sec.gov/dei/"],
66
+ ["rs", NS.rs],
67
+ ["skos", NS.skos],
68
+ ["xbrli", NS.xbrli],
69
+ ["xlink", NS.xlink],
70
+ ["link", NS.link],
71
+ ["iso4217", NS.iso4217]
72
+ ];
73
+ function qname(iri) {
74
+ for (const [prefix, ns] of PREFIXES) {
75
+ if (iri.startsWith(ns)) {
76
+ return `${prefix}:${iri.slice(ns.length)}`;
77
+ }
78
+ }
79
+ const slash = iri.lastIndexOf("/");
80
+ const hash = iri.lastIndexOf("#");
81
+ return iri.slice(Math.max(slash, hash) + 1);
82
+ }
83
+ function humanize(iri) {
84
+ const local = qname(iri).split(":").pop() ?? iri;
85
+ return local.replace(/(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/g, " ");
86
+ }
87
+ function isExternalFactUrl(value) {
88
+ return !!value && /^https?:\/\/\S+\/fact_[0-9a-f]+\.(html|txt)$/i.test(value);
89
+ }
90
+ var BLOCK_ORDER = {
91
+ balance_sheet: 0,
92
+ income_statement: 1,
93
+ cash_flow_statement: 2,
94
+ equity_statement: 3
95
+ };
96
+ var BLOCK_TITLES = {
97
+ balance_sheet: "Balance Sheet",
98
+ income_statement: "Income Statement",
99
+ cash_flow_statement: "Cash Flow Statement",
100
+ equity_statement: "Statement of Changes in Equity"
101
+ };
102
+
103
+ // src/format.ts
104
+ function formatMoney(value, opts = {}) {
105
+ if (value === null || value === void 0 || Number.isNaN(value)) return "\u2014";
106
+ const symbol = opts.currencySymbol ?? "$";
107
+ const abs = Math.abs(value).toLocaleString("en-US", {
108
+ minimumFractionDigits: 2,
109
+ maximumFractionDigits: 2
110
+ });
111
+ return value < 0 ? `${symbol}(${abs})` : `${symbol}${abs}`;
112
+ }
113
+ var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
114
+ function formatDate(iso) {
115
+ if (!iso) return "";
116
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
117
+ if (!m) return iso;
118
+ const [, year, month, day] = m;
119
+ const idx = Number(month) - 1;
120
+ return `${MONTHS[idx] ?? month} ${Number(day)}, ${year}`;
121
+ }
122
+ function humanizeDuration(value) {
123
+ if (!value) return null;
124
+ const m = /^(-)?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/.exec(
125
+ value.trim()
126
+ );
127
+ if (!m) return null;
128
+ const [, sign, y, mo, w, d, h, min, s] = m;
129
+ const parts = [];
130
+ const push = (n, unit) => {
131
+ if (n) parts.push(`${n} ${unit}${Number(n) === 1 ? "" : "s"}`);
132
+ };
133
+ push(y, "year");
134
+ push(mo, "month");
135
+ push(w, "week");
136
+ push(d, "day");
137
+ push(h, "hour");
138
+ push(min, "minute");
139
+ push(s, "second");
140
+ if (!parts.length) return null;
141
+ return `${sign ?? ""}${parts.join(" ")}`;
142
+ }
143
+ function formatPeriod(period) {
144
+ if (period.type === "instant") {
145
+ return `As of ${formatDate(period.instant ?? period.end)}`;
146
+ }
147
+ return `${formatDate(period.startDate)} \u2013 ${formatDate(period.endDate ?? period.end)}`;
148
+ }
149
+ function numericKindOf(el) {
150
+ return el.numericKind ?? (el.monetary ? "monetary" : "other");
151
+ }
152
+ var CURRENCY_SYMBOLS = {
153
+ USD: "$",
154
+ EUR: "\u20AC",
155
+ GBP: "\xA3",
156
+ JPY: "\xA5",
157
+ CNY: "\xA5",
158
+ CHF: "CHF ",
159
+ CAD: "CA$",
160
+ AUD: "A$",
161
+ HKD: "HK$",
162
+ INR: "\u20B9",
163
+ KRW: "\u20A9"
164
+ };
165
+ function currencySymbolFor(measure) {
166
+ if (!measure) return null;
167
+ const code = (measure.includes(":") ? measure.split(":").pop() : measure).toUpperCase();
168
+ if (CURRENCY_SYMBOLS[code]) return CURRENCY_SYMBOLS[code];
169
+ return /^[A-Z]{3}$/.test(code) ? `${code} ` : null;
170
+ }
171
+ function formatValue(value, fmt = {}) {
172
+ if (value === null || value === void 0 || Number.isNaN(value)) return "\u2014";
173
+ const kind = fmt.numericKind ?? "monetary";
174
+ const scale = kind === "monetary" ? fmt.scaleFactor ?? 1 : 1;
175
+ const scaled = kind === "percent" ? value * 100 : value / scale;
176
+ let minFrac = 0;
177
+ let maxFrac = 0;
178
+ if (kind === "perShare") {
179
+ minFrac = maxFrac = 2;
180
+ } else if (kind === "monetary") {
181
+ minFrac = maxFrac = scale > 1 ? 0 : 2;
182
+ } else if (kind === "percent") {
183
+ maxFrac = 2;
184
+ } else if (kind === "pure") {
185
+ maxFrac = 4;
186
+ }
187
+ const abs = Math.abs(scaled).toLocaleString("en-US", {
188
+ minimumFractionDigits: minFrac,
189
+ maximumFractionDigits: maxFrac
190
+ });
191
+ const symbol = kind === "monetary" || kind === "perShare" ? fmt.symbol ?? "" : "";
192
+ const suffix = kind === "percent" ? "%" : "";
193
+ return scaled < 0 ? `${symbol}(${abs}${suffix})` : `${symbol}${abs}${suffix}`;
194
+ }
195
+
196
+ // src/adapters/sec.ts
197
+ var ENTITY_META_Q = `
198
+ MATCH (ent:Entity)-[:ENTITY_HAS_REPORT]->(r:Report {identifier: $rid})
199
+ RETURN ent.identifier AS entity_id, ent.name AS entity_name,
200
+ ent.legal_name AS legal_name, ent.cik AS cik, ent.ticker AS ticker,
201
+ r.form AS form, r.report_date AS report_date`;
202
+ var SECTIONS_Q = `
203
+ MATCH (r:Report {identifier: $rid})-[:REPORT_HAS_FACT]->(f:Fact)<-[:FACT_SET_CONTAINS_FACT]-(fs:FactSet)<-[:STRUCTURE_HAS_FACT_SET]-(s:Structure)
204
+ RETURN s.identifier AS sid, s.canonical_type AS canonical_type,
205
+ s.definition AS definition, s.number AS number,
206
+ collect(DISTINCT fs.identifier) AS factsets
207
+ ORDER BY number`;
208
+ var FACTS_Q = `
209
+ MATCH (fs:FactSet {identifier: $fsid})-[:FACT_SET_CONTAINS_FACT]->(f:Fact)-[:FACT_HAS_ELEMENT]->(e:Element),
210
+ (f)-[:FACT_HAS_PERIOD]->(p:Period)
211
+ WHERE f.value IS NOT NULL
212
+ RETURN f.identifier AS fid, e.qname AS qname, e.name AS ename, e.balance AS balance,
213
+ e.period_type AS e_period_type, e.is_abstract AS is_abstract, e.is_numeric AS is_numeric,
214
+ e.item_type AS item_type, e.is_shares AS is_shares,
215
+ f.numeric_value AS numeric_value, f.value AS raw_value, f.decimals AS decimals,
216
+ p.identifier AS pid, p.period_type AS period_type,
217
+ p.start_date AS start_date, p.end_date AS end_date
218
+ LIMIT 2000`;
219
+ var UNITS_Q = `
220
+ MATCH (fs:FactSet {identifier: $fsid})-[:FACT_SET_CONTAINS_FACT]->(f:Fact)-[:FACT_HAS_UNIT]->(u:Unit)
221
+ WHERE f.value IS NOT NULL
222
+ RETURN f.identifier AS fid, u.measure AS unit
223
+ LIMIT 2000`;
224
+ var DIMS_Q = `
225
+ MATCH (fs:FactSet {identifier: $fsid})-[:FACT_SET_CONTAINS_FACT]->(f:Fact)-[:FACT_HAS_DIMENSION]->(d:Dimension)
226
+ WHERE f.value IS NOT NULL
227
+ RETURN f.identifier AS fid, d.axis AS axis, d.member AS member,
228
+ d.axis_uri AS axis_uri, d.member_uri AS member_uri,
229
+ d.is_explicit AS is_explicit, d.is_typed AS is_typed
230
+ LIMIT 4000`;
231
+ var ASSOC_Q = `
232
+ MATCH (s:Structure {identifier: $sid})-[:STRUCTURE_HAS_ASSOCIATION]->(a:Association),
233
+ (a)-[:ASSOCIATION_HAS_FROM_ELEMENT]->(pe:Element),
234
+ (a)-[:ASSOCIATION_HAS_TO_ELEMENT]->(ce:Element)
235
+ RETURN a.association_type AS association_type, a.order_value AS order_value, a.weight AS weight,
236
+ pe.qname AS parent, pe.name AS parent_name, pe.is_abstract AS parent_abstract,
237
+ ce.qname AS child, ce.name AS child_name, ce.is_abstract AS child_abstract`;
238
+ function str(row, key) {
239
+ const v = row[key];
240
+ return typeof v === "string" && v !== "" ? v : v == null ? null : String(v);
241
+ }
242
+ function num(row, key) {
243
+ const v = row[key];
244
+ if (v === null || v === void 0 || v === "") return null;
245
+ const n = typeof v === "number" ? v : Number(v);
246
+ return Number.isNaN(n) ? null : n;
247
+ }
248
+ function bool(row, key) {
249
+ const v = row[key];
250
+ return v === true || v === "true";
251
+ }
252
+ function periodType(raw) {
253
+ return raw === "instant" || raw === "duration" ? raw : null;
254
+ }
255
+ function xbrlQname(uri, local) {
256
+ if (local) {
257
+ const ns = (uri ?? "").split("#")[0];
258
+ if (/fasb\.org\/us-gaap/.test(ns)) return `us-gaap:${local}`;
259
+ if (/fasb\.org\/srt/.test(ns)) return `srt:${local}`;
260
+ if (/xbrl\.sec\.gov\/dei/.test(ns)) return `dei:${local}`;
261
+ if (/robosystems\.ai\/taxonomy\/rs-gaap/.test(ns)) return `rs-gaap:${local}`;
262
+ return local;
263
+ }
264
+ return uri ? qname(uri) : "";
265
+ }
266
+ function dimLabel(local) {
267
+ if (!local) return "";
268
+ return humanize(local.replace(/(Axis|Member|Domain)$/, "")) || humanize(local);
269
+ }
270
+ function deriveNumericKind(row) {
271
+ const itemType = str(row, "item_type") ?? "";
272
+ if (/perShareItemType/i.test(itemType)) return "perShare";
273
+ if (/sharesItemType/i.test(itemType) || bool(row, "is_shares")) return "shares";
274
+ if (/percentItemType/i.test(itemType)) return "percent";
275
+ if (/pureItemType/i.test(itemType)) return "pure";
276
+ if (/monetaryItemType/i.test(itemType)) return "monetary";
277
+ if (/integerItemType/i.test(itemType)) return "integer";
278
+ if (!bool(row, "is_numeric")) return void 0;
279
+ const balance = str(row, "balance");
280
+ return balance === "debit" || balance === "credit" ? "monetary" : "other";
281
+ }
282
+ var CORE_STATEMENT_TYPES = /* @__PURE__ */ new Set([
283
+ "balance_sheet",
284
+ "income_statement",
285
+ "cash_flow_statement",
286
+ "equity_statement",
287
+ "comprehensive_income"
288
+ ]);
289
+ function parseStructureDefinition(definition, canonicalType) {
290
+ const raw = (definition ?? "").trim();
291
+ const parts = raw.split(" - ");
292
+ let type = "";
293
+ let title = raw;
294
+ if (parts.length >= 3) {
295
+ type = parts[1].trim();
296
+ title = parts.slice(2).join(" - ").trim();
297
+ } else if (parts.length === 2) {
298
+ title = parts[1].trim();
299
+ }
300
+ let kind = "Other";
301
+ if (/cover/i.test(title)) kind = "Cover";
302
+ else if (type === "Statement") kind = "Statement";
303
+ else if (type === "Disclosure") kind = "Disclosure";
304
+ else if (type === "Document") kind = "Document";
305
+ if (canonicalType && CORE_STATEMENT_TYPES.has(canonicalType)) kind = "Statement";
306
+ return { title: title || raw || canonicalType || "Section", kind };
307
+ }
308
+ function mapEntity(row) {
309
+ if (!row) return null;
310
+ const id = str(row, "entity_id");
311
+ if (!id) return null;
312
+ return {
313
+ id,
314
+ name: str(row, "entity_name") ?? str(row, "legal_name") ?? "Entity",
315
+ legalName: str(row, "legal_name"),
316
+ country: null
317
+ };
318
+ }
319
+ async function fetchSecReportShell(query, reportId) {
320
+ const [entityRows, sectionRows] = await Promise.all([
321
+ query(ENTITY_META_Q, { rid: reportId }),
322
+ query(SECTIONS_Q, { rid: reportId })
323
+ ]);
324
+ const sections = sectionRows.map((row, order) => {
325
+ const { title, kind } = parseStructureDefinition(
326
+ str(row, "definition"),
327
+ str(row, "canonical_type")
328
+ );
329
+ const factsets = Array.isArray(row.factsets) ? row.factsets.filter((v) => typeof v === "string") : [];
330
+ return {
331
+ id: str(row, "sid") ?? "",
332
+ title,
333
+ kind,
334
+ canonicalType: str(row, "canonical_type"),
335
+ order,
336
+ factsetIds: factsets
337
+ };
338
+ }).filter((s) => s.id && s.factsetIds.length > 0);
339
+ return { reportId, entity: mapEntity(entityRows[0]), sections };
340
+ }
341
+ function accumulateElement(elements, row) {
342
+ const q = str(row, "qname");
343
+ if (!q || elements[q]) return;
344
+ const balance = str(row, "balance");
345
+ elements[q] = {
346
+ id: q,
347
+ qname: q,
348
+ label: str(row, "ename") ? humanize(str(row, "ename")) : humanize(q),
349
+ balance: balance === "debit" || balance === "credit" ? balance : null,
350
+ periodType: periodType(str(row, "e_period_type")),
351
+ abstract: bool(row, "is_abstract"),
352
+ monetary: bool(row, "is_numeric"),
353
+ numericKind: deriveNumericKind(row)
354
+ };
355
+ }
356
+ function registerPresentationElement(elements, qname2, name, isAbstract) {
357
+ if (!qname2 || elements[qname2]) return;
358
+ elements[qname2] = {
359
+ id: qname2,
360
+ qname: qname2,
361
+ label: name ? humanize(isAbstract ? name.replace(/Abstract$/, "") : name) : humanize(qname2),
362
+ balance: null,
363
+ periodType: null,
364
+ abstract: isAbstract,
365
+ monetary: false
366
+ };
367
+ }
368
+ function accumulatePeriod(periods, row) {
369
+ const id = str(row, "pid");
370
+ if (!id || periods[id]) return;
371
+ const type = periodType(str(row, "period_type")) ?? "duration";
372
+ const startDate = str(row, "start_date");
373
+ const endDate = str(row, "end_date");
374
+ periods[id] = {
375
+ id,
376
+ type,
377
+ instant: type === "instant" ? endDate : null,
378
+ startDate,
379
+ endDate,
380
+ end: endDate ?? ""
381
+ };
382
+ }
383
+ async function fetchSecSection(query, shell, section) {
384
+ const [factRowGroups, unitRowGroups, dimRowGroups, assocRows] = await Promise.all([
385
+ Promise.all(section.factsetIds.map((fsid) => query(FACTS_Q, { fsid }))),
386
+ Promise.all(section.factsetIds.map((fsid) => query(UNITS_Q, { fsid }))),
387
+ Promise.all(section.factsetIds.map((fsid) => query(DIMS_Q, { fsid }))),
388
+ query(ASSOC_Q, { sid: section.id })
389
+ ]);
390
+ const unitByFid = /* @__PURE__ */ new Map();
391
+ for (const row of unitRowGroups.flat()) {
392
+ const fid = str(row, "fid");
393
+ const measure = str(row, "unit");
394
+ if (fid && measure) unitByFid.set(fid, measure);
395
+ }
396
+ const dimsByFid = /* @__PURE__ */ new Map();
397
+ for (const row of dimRowGroups.flat()) {
398
+ const fid = str(row, "fid");
399
+ if (!fid) continue;
400
+ const memberLocal = str(row, "member");
401
+ const qual = {
402
+ axis: xbrlQname(str(row, "axis_uri"), str(row, "axis")),
403
+ member: memberLocal ? xbrlQname(str(row, "member_uri"), memberLocal) : null,
404
+ axisLabel: dimLabel(str(row, "axis")),
405
+ memberLabel: dimLabel(memberLocal),
406
+ explicit: bool(row, "is_explicit"),
407
+ typedValue: bool(row, "is_typed") ? str(row, "member") : null
408
+ };
409
+ const list = dimsByFid.get(fid) ?? [];
410
+ list.push(qual);
411
+ dimsByFid.set(fid, list);
412
+ }
413
+ for (const list of dimsByFid.values()) list.sort((a, b) => a.axis.localeCompare(b.axis));
414
+ const elements = {};
415
+ const periods = {};
416
+ const units = {};
417
+ const facts = [];
418
+ const seenFacts = /* @__PURE__ */ new Set();
419
+ for (const row of factRowGroups.flat()) {
420
+ const fid = str(row, "fid");
421
+ const element = str(row, "qname");
422
+ const period = str(row, "pid");
423
+ if (!fid || !element || !period || seenFacts.has(fid)) continue;
424
+ seenFacts.add(fid);
425
+ accumulateElement(elements, row);
426
+ accumulatePeriod(periods, row);
427
+ const measure = unitByFid.get(fid) ?? null;
428
+ if (measure && !units[measure]) {
429
+ const compact = qname(measure);
430
+ units[measure] = {
431
+ id: measure,
432
+ measure: compact,
433
+ label: compact.includes(":") ? compact.split(":").pop() : compact,
434
+ symbol: currencySymbolFor(compact)
435
+ };
436
+ }
437
+ const value = num(row, "numeric_value");
438
+ facts.push({
439
+ id: fid,
440
+ element,
441
+ period,
442
+ unit: measure,
443
+ entity: shell.entity?.id ?? null,
444
+ factSet: section.id,
445
+ value,
446
+ decimals: str(row, "decimals"),
447
+ textValue: value === null ? str(row, "raw_value") : null,
448
+ dimensions: dimsByFid.get(fid)
449
+ });
450
+ }
451
+ const calcAssociations = [];
452
+ const presAssociations = [];
453
+ for (const row of assocRows) {
454
+ const parent = str(row, "parent");
455
+ const child = str(row, "child");
456
+ if (!parent || !child) continue;
457
+ registerPresentationElement(
458
+ elements,
459
+ parent,
460
+ str(row, "parent_name"),
461
+ bool(row, "parent_abstract")
462
+ );
463
+ registerPresentationElement(
464
+ elements,
465
+ child,
466
+ str(row, "child_name"),
467
+ bool(row, "child_abstract")
468
+ );
469
+ const order = num(row, "order_value") ?? 0;
470
+ const type = str(row, "association_type")?.toLowerCase();
471
+ if (type === "calculation") {
472
+ calcAssociations.push({
473
+ parent,
474
+ child,
475
+ weight: num(row, "weight") ?? 1,
476
+ order,
477
+ role: null,
478
+ structure: section.id
479
+ });
480
+ } else if (type === "presentation") {
481
+ presAssociations.push({ parent, child, order, role: null, structure: section.id });
482
+ }
483
+ }
484
+ const informationBlock = {
485
+ id: section.id,
486
+ blockType: section.canonicalType ?? "",
487
+ factSet: section.id,
488
+ label: section.title,
489
+ structureId: section.id
490
+ };
491
+ const structure = {
492
+ id: section.id,
493
+ blockType: section.canonicalType ?? "",
494
+ roleUri: null,
495
+ structureName: section.title,
496
+ order: section.order
497
+ };
498
+ return {
499
+ reportId: shell.reportId,
500
+ reportIri: null,
501
+ entity: shell.entity,
502
+ informationBlocks: [informationBlock],
503
+ structures: [structure],
504
+ facts,
505
+ elements,
506
+ periods,
507
+ units,
508
+ calcAssociations,
509
+ presAssociations
510
+ };
511
+ }
512
+ function mergeSecSections(shell, sections) {
513
+ const elements = {};
514
+ const periods = {};
515
+ const units = {};
516
+ const informationBlocks = [];
517
+ const structures = [];
518
+ const facts = [];
519
+ const calcAssociations = [];
520
+ const presAssociations = [];
521
+ for (const part of sections) {
522
+ Object.assign(elements, part.elements);
523
+ Object.assign(periods, part.periods);
524
+ Object.assign(units, part.units);
525
+ informationBlocks.push(...part.informationBlocks);
526
+ structures.push(...part.structures);
527
+ facts.push(...part.facts);
528
+ calcAssociations.push(...part.calcAssociations);
529
+ presAssociations.push(...part.presAssociations);
530
+ }
531
+ return {
532
+ reportId: shell.reportId,
533
+ reportIri: null,
534
+ entity: shell.entity,
535
+ informationBlocks,
536
+ structures,
537
+ facts,
538
+ elements,
539
+ periods,
540
+ units,
541
+ calcAssociations,
542
+ presAssociations
543
+ };
544
+ }
545
+
546
+ // src/adapters/cypher.ts
547
+ function cypherAdapter(config) {
548
+ return {
549
+ source: `sec:${config.reportId}`,
550
+ load: async () => {
551
+ const shell = await fetchSecReportShell(config.query, config.reportId);
552
+ const sections = await Promise.all(
553
+ shell.sections.map((section) => fetchSecSection(config.query, shell, section))
554
+ );
555
+ return mergeSecSections(shell, sections);
556
+ }
557
+ };
558
+ }
559
+ var { namedNode } = DataFactory;
560
+ function makeReaders(store) {
561
+ const objects = (s, p) => store.getObjects(namedNode(s), namedNode(p), null);
562
+ const firstValue = (s, p) => {
563
+ const os = objects(s, p);
564
+ return os.length ? os[0].value : null;
565
+ };
566
+ const subjectsOfType = (type) => store.getSubjects(namedNode(IRI.type), namedNode(type), null).map((t) => t.value);
567
+ return { objects, firstValue, subjectsOfType };
568
+ }
569
+ function toNumber(raw) {
570
+ if (raw === null || raw === "") return null;
571
+ const n = Number(raw);
572
+ return Number.isNaN(n) ? null : n;
573
+ }
574
+ function parseStore(store) {
575
+ const { objects, firstValue, subjectsOfType } = makeReaders(store);
576
+ const elements = {};
577
+ for (const id of subjectsOfType(IRI.Element)) {
578
+ const balance = firstValue(id, IRI.balance);
579
+ const periodType2 = firstValue(id, IRI.periodType);
580
+ elements[id] = {
581
+ id,
582
+ qname: qname(id),
583
+ label: firstValue(id, IRI.prefLabel) ?? humanize(id),
584
+ balance: balance === "debit" || balance === "credit" ? balance : null,
585
+ periodType: periodType2 === "instant" || periodType2 === "duration" ? periodType2 : null,
586
+ abstract: firstValue(id, IRI.abstract) === "true",
587
+ monetary: firstValue(id, IRI.monetary) === "true"
588
+ };
589
+ }
590
+ const periods = {};
591
+ for (const id of subjectsOfType(IRI.Period)) {
592
+ const declared = firstValue(id, IRI.periodType);
593
+ const instant = firstValue(id, IRI.instant);
594
+ const startDate = firstValue(id, IRI.startDate);
595
+ const endDate = firstValue(id, IRI.endDate);
596
+ const type = declared === "instant" ? "instant" : declared === "duration" ? "duration" : instant ? "instant" : "duration";
597
+ const end = (type === "instant" ? instant : endDate) ?? instant ?? endDate ?? "";
598
+ periods[id] = { id, type, instant, startDate, endDate, end };
599
+ }
600
+ const units = {};
601
+ for (const id of subjectsOfType(IRI.Unit)) {
602
+ const measureTerm = objects(id, IRI.measure)[0];
603
+ const measure = measureTerm ? qname(measureTerm.value) : "unknown";
604
+ units[id] = {
605
+ id,
606
+ measure,
607
+ label: measure.includes(":") ? measure.split(":").pop() : measure
608
+ };
609
+ }
610
+ let entity = null;
611
+ const entityIds = subjectsOfType(IRI.Entity);
612
+ if (entityIds.length) {
613
+ const id = entityIds[0];
614
+ entity = {
615
+ id,
616
+ name: firstValue(id, IRI.prefLabel) ?? firstValue(id, IRI.legalName) ?? "Entity",
617
+ legalName: firstValue(id, IRI.legalName),
618
+ country: firstValue(id, IRI.country)
619
+ };
620
+ }
621
+ const facts = [];
622
+ for (const id of subjectsOfType(IRI.Fact)) {
623
+ facts.push({
624
+ id,
625
+ element: firstValue(id, IRI.element) ?? "",
626
+ period: firstValue(id, IRI.period) ?? "",
627
+ unit: firstValue(id, IRI.unit),
628
+ entity: firstValue(id, IRI.entity),
629
+ factSet: firstValue(id, IRI.factSet),
630
+ value: toNumber(firstValue(id, IRI.numericValue)),
631
+ decimals: firstValue(id, IRI.decimals)
632
+ });
633
+ }
634
+ const informationBlocks = [];
635
+ for (const id of subjectsOfType(IRI.InformationBlock)) {
636
+ informationBlocks.push({
637
+ id,
638
+ blockType: firstValue(id, IRI.blockType) ?? "",
639
+ factSet: firstValue(id, IRI.factSet),
640
+ label: firstValue(id, IRI.prefLabel)
641
+ });
642
+ }
643
+ const structures = [];
644
+ const assocStructure = /* @__PURE__ */ new Map();
645
+ for (const id of subjectsOfType(IRI.Structure)) {
646
+ structures.push({
647
+ id,
648
+ blockType: firstValue(id, IRI.blockType) ?? "",
649
+ roleUri: firstValue(id, IRI.roleUri),
650
+ structureName: firstValue(id, IRI.structureName)
651
+ });
652
+ for (const assoc of objects(id, IRI.hasAssociation)) {
653
+ assocStructure.set(assoc.value, id);
654
+ }
655
+ }
656
+ const calcAssociations = [];
657
+ const presAssociations = [];
658
+ for (const id of subjectsOfType(IRI.Association)) {
659
+ const parent = firstValue(id, IRI.from);
660
+ const child = firstValue(id, IRI.to);
661
+ if (!parent || !child) continue;
662
+ const type = firstValue(id, IRI.associationType);
663
+ const order = toNumber(firstValue(id, IRI.order)) ?? 0;
664
+ const role = firstValue(id, IRI.role);
665
+ const structure = assocStructure.get(id) ?? null;
666
+ if (type === "calculation") {
667
+ calcAssociations.push({
668
+ parent,
669
+ child,
670
+ weight: toNumber(firstValue(id, IRI.weight)) ?? 1,
671
+ order,
672
+ role,
673
+ structure
674
+ });
675
+ } else if (type === "presentation") {
676
+ presAssociations.push({ parent, child, order, role, structure });
677
+ }
678
+ }
679
+ let reportIri = null;
680
+ for (const graph of store.getGraphs(null, null, null)) {
681
+ const hash = graph.value.indexOf("#");
682
+ if (hash > 0) {
683
+ reportIri = graph.value.slice(0, hash);
684
+ break;
685
+ }
686
+ }
687
+ if (!reportIri) {
688
+ const reports = subjectsOfType(IRI.Report);
689
+ reportIri = reports.length ? reports[0] : null;
690
+ }
691
+ const reportId = reportIri ? reportIri.split("/").pop() ?? null : null;
692
+ return {
693
+ reportId,
694
+ reportIri,
695
+ entity,
696
+ informationBlocks,
697
+ structures,
698
+ facts,
699
+ elements,
700
+ periods,
701
+ units,
702
+ calcAssociations,
703
+ presAssociations
704
+ };
705
+ }
706
+ async function parseJsonld(doc) {
707
+ const json = typeof doc === "string" ? JSON.parse(doc) : doc;
708
+ const nquads = await jsonld.toRDF(json, { format: "application/n-quads" });
709
+ const store = new Store(new Parser({ format: "application/n-quads" }).parse(nquads));
710
+ return parseStore(store);
711
+ }
712
+ function jsonldFileAdapter(doc, source = "holon.jsonld") {
713
+ return {
714
+ source,
715
+ load: () => parseJsonld(doc)
716
+ };
717
+ }
718
+
719
+ export { BLOCK_ORDER, BLOCK_TITLES, NS, currencySymbolFor, cypherAdapter, fetchSecReportShell, fetchSecSection, formatDate, formatMoney, formatPeriod, formatValue, humanize, humanizeDuration, isExternalFactUrl, jsonldFileAdapter, mergeSecSections, numericKindOf, parseJsonld, parseStore, parseStructureDefinition, qname };
720
+ //# sourceMappingURL=chunk-DGP4KRWF.js.map
721
+ //# sourceMappingURL=chunk-DGP4KRWF.js.map