@finsys/core 4.6.0 → 4.7.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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "schemaVersion": "1.0.0",
2
+ "schemaVersion": "1.1.0",
3
3
  "categories": [
4
4
  {
5
5
  "id": "telco-carrier",
@@ -653,7 +653,8 @@
653
653
  {
654
654
  "name": "companyName",
655
655
  "type": "string",
656
- "description": "Company name as stated on the document."
656
+ "fact": "companyName",
657
+ "description": "Company name as stated on the document. Attestation of the shared companyName fact — the SSM Form 9 extraction category attests the same fact from its own document."
657
658
  },
658
659
  {
659
660
  "name": "financialYearEnd",
@@ -1372,6 +1373,121 @@
1372
1373
  "description": "Cash-flow statement: cash and cash equivalents at the end of the financial year."
1373
1374
  }
1374
1375
  ]
1376
+ },
1377
+ {
1378
+ "id": "finxtract-form9",
1379
+ "displayName": "SSM Form 9 (Document Extraction)",
1380
+ "description": "Fields extracted from an uploaded SSM Form 9 (Certificate of Incorporation of Private Company) document by the host's extraction pipeline. One instance per uploaded document. The company name and incorporation date are attestations of shared facts — the company-profile and financial-statement extraction categories attest the same real-world facts from their own documents.",
1381
+ "canonicalTable": "ihs_alt_data_form9",
1382
+ "fields": [
1383
+ {
1384
+ "name": "companyName",
1385
+ "type": "string",
1386
+ "fact": "companyName",
1387
+ "description": "Company name as printed on the certificate. Attestation of the shared companyName fact."
1388
+ },
1389
+ {
1390
+ "name": "companyRegNo",
1391
+ "type": "string",
1392
+ "description": "Company registration number as printed on the certificate."
1393
+ },
1394
+ {
1395
+ "name": "companyIncorporationDate",
1396
+ "type": "string",
1397
+ "fact": "companyIncorporationDate",
1398
+ "description": "Date of incorporation as printed on the certificate, normalized to ISO YYYY-MM-DD. Attestation of the shared companyIncorporationDate fact — a company has exactly one incorporation date regardless of which document it was extracted from."
1399
+ }
1400
+ ]
1401
+ },
1402
+ {
1403
+ "id": "finxtract-ssm",
1404
+ "displayName": "SSM Company Profile (Document Extraction)",
1405
+ "description": "Fields extracted from an uploaded SSM company-profile document (company or business registration extract) by the host's extraction pipeline. One instance per uploaded document. Covers registration identity, status, key dates, registered address, capital, and the officer/shareholder registers. The incorporation date is an attestation of a shared fact — the Form 9 extraction category attests the same real-world fact from its own document.",
1406
+ "canonicalTable": "ihs_alt_data_ssm",
1407
+ "fields": [
1408
+ {
1409
+ "name": "ssmCompanyEntityType",
1410
+ "type": "string",
1411
+ "description": "Entity type stated on the profile (e.g. private limited company, enterprise)."
1412
+ },
1413
+ {
1414
+ "name": "ssmCompanyName",
1415
+ "type": "string",
1416
+ "description": "Business or company name as stated on the profile."
1417
+ },
1418
+ {
1419
+ "name": "ssmCompanyRegNo",
1420
+ "type": "string",
1421
+ "description": "Business or company registration number as stated on the profile."
1422
+ },
1423
+ {
1424
+ "name": "companyStatus",
1425
+ "type": "string",
1426
+ "description": "Registration status as stated on the profile (e.g. existing, dissolved)."
1427
+ },
1428
+ {
1429
+ "name": "businessOrigin",
1430
+ "type": "string",
1431
+ "description": "Business origin as stated on the profile."
1432
+ },
1433
+ {
1434
+ "name": "companyIncorporationDate",
1435
+ "type": "string",
1436
+ "fact": "companyIncorporationDate",
1437
+ "description": "Incorporation (or registration) date as stated on the profile, normalized to ISO YYYY-MM-DD. Attestation of the shared companyIncorporationDate fact — a company has exactly one incorporation date regardless of which document it was extracted from."
1438
+ },
1439
+ {
1440
+ "name": "businessCommencementDate",
1441
+ "type": "string",
1442
+ "description": "Business commencement (or start) date as stated on the profile, normalized to ISO YYYY-MM-DD."
1443
+ },
1444
+ {
1445
+ "name": "businessNature",
1446
+ "type": "string",
1447
+ "description": "Nature of business as stated on the profile."
1448
+ },
1449
+ {
1450
+ "name": "registeredAddress",
1451
+ "type": "string",
1452
+ "description": "Registered business address as stated on the profile."
1453
+ },
1454
+ {
1455
+ "name": "totalShareIssued",
1456
+ "type": "number",
1457
+ "description": "Total shares issued as stated on the profile."
1458
+ },
1459
+ {
1460
+ "name": "ssmPaidUpCapital",
1461
+ "type": "number",
1462
+ "unit": "MYR",
1463
+ "description": "Paid-up capital as stated on the profile (falls back to total shares issued when the profile omits an explicit paid-up figure)."
1464
+ },
1465
+ {
1466
+ "name": "directors",
1467
+ "type": "string",
1468
+ "description": "JSON-encoded list of current directors/officers (for enterprises, the business owner normalized into the same shape)."
1469
+ },
1470
+ {
1471
+ "name": "shareholders",
1472
+ "type": "string",
1473
+ "description": "JSON-encoded list of shareholders (for enterprises, the business owner normalized into the same shape)."
1474
+ },
1475
+ {
1476
+ "name": "previousDirectors",
1477
+ "type": "string",
1478
+ "description": "JSON-encoded list of previous directors/officers."
1479
+ },
1480
+ {
1481
+ "name": "companyLastOldName",
1482
+ "type": "string",
1483
+ "description": "The company's most recent former name, when the profile records a name change."
1484
+ },
1485
+ {
1486
+ "name": "companyNameDateOfChange",
1487
+ "type": "string",
1488
+ "description": "Date of the most recent name change, normalized to ISO YYYY-MM-DD."
1489
+ }
1490
+ ]
1375
1491
  }
1376
1492
  ]
1377
1493
  }
package/dist/index.cjs CHANGED
@@ -61,11 +61,13 @@ __export(index_exports, {
61
61
  buildDocumentRows: () => buildDocumentRows,
62
62
  buildFileFieldTables: () => buildFileFieldTables,
63
63
  buildFileFieldTablesFromInstances: () => buildFileFieldTablesFromInstances,
64
+ categoriesAttestingFact: () => categoriesAttestingFact,
64
65
  categoryFieldsOf: () => categoryFieldsOf,
65
66
  categoryForField: () => categoryForField,
66
67
  categorySchemaOf: () => categorySchemaOf,
67
68
  evaluateExpression: () => evaluateExpression,
68
69
  extractTimePeriods: () => extractTimePeriods,
70
+ factOf: () => factOf,
69
71
  formatDocumentSize: () => formatDocumentSize,
70
72
  formatDocumentType: () => formatDocumentType,
71
73
  formatDocumentUploaded: () => formatDocumentUploaded,
@@ -10321,7 +10323,7 @@ function applyAggregation(op, instances) {
10321
10323
 
10322
10324
  // src/data/adapter-categories.json
10323
10325
  var adapter_categories_default = {
10324
- schemaVersion: "1.0.0",
10326
+ schemaVersion: "1.1.0",
10325
10327
  categories: [
10326
10328
  {
10327
10329
  id: "telco-carrier",
@@ -10975,7 +10977,8 @@ var adapter_categories_default = {
10975
10977
  {
10976
10978
  name: "companyName",
10977
10979
  type: "string",
10978
- description: "Company name as stated on the document."
10980
+ fact: "companyName",
10981
+ description: "Company name as stated on the document. Attestation of the shared companyName fact \u2014 the SSM Form 9 extraction category attests the same fact from its own document."
10979
10982
  },
10980
10983
  {
10981
10984
  name: "financialYearEnd",
@@ -11694,6 +11697,121 @@ var adapter_categories_default = {
11694
11697
  description: "Cash-flow statement: cash and cash equivalents at the end of the financial year."
11695
11698
  }
11696
11699
  ]
11700
+ },
11701
+ {
11702
+ id: "finxtract-form9",
11703
+ displayName: "SSM Form 9 (Document Extraction)",
11704
+ description: "Fields extracted from an uploaded SSM Form 9 (Certificate of Incorporation of Private Company) document by the host's extraction pipeline. One instance per uploaded document. The company name and incorporation date are attestations of shared facts \u2014 the company-profile and financial-statement extraction categories attest the same real-world facts from their own documents.",
11705
+ canonicalTable: "ihs_alt_data_form9",
11706
+ fields: [
11707
+ {
11708
+ name: "companyName",
11709
+ type: "string",
11710
+ fact: "companyName",
11711
+ description: "Company name as printed on the certificate. Attestation of the shared companyName fact."
11712
+ },
11713
+ {
11714
+ name: "companyRegNo",
11715
+ type: "string",
11716
+ description: "Company registration number as printed on the certificate."
11717
+ },
11718
+ {
11719
+ name: "companyIncorporationDate",
11720
+ type: "string",
11721
+ fact: "companyIncorporationDate",
11722
+ description: "Date of incorporation as printed on the certificate, normalized to ISO YYYY-MM-DD. Attestation of the shared companyIncorporationDate fact \u2014 a company has exactly one incorporation date regardless of which document it was extracted from."
11723
+ }
11724
+ ]
11725
+ },
11726
+ {
11727
+ id: "finxtract-ssm",
11728
+ displayName: "SSM Company Profile (Document Extraction)",
11729
+ description: "Fields extracted from an uploaded SSM company-profile document (company or business registration extract) by the host's extraction pipeline. One instance per uploaded document. Covers registration identity, status, key dates, registered address, capital, and the officer/shareholder registers. The incorporation date is an attestation of a shared fact \u2014 the Form 9 extraction category attests the same real-world fact from its own document.",
11730
+ canonicalTable: "ihs_alt_data_ssm",
11731
+ fields: [
11732
+ {
11733
+ name: "ssmCompanyEntityType",
11734
+ type: "string",
11735
+ description: "Entity type stated on the profile (e.g. private limited company, enterprise)."
11736
+ },
11737
+ {
11738
+ name: "ssmCompanyName",
11739
+ type: "string",
11740
+ description: "Business or company name as stated on the profile."
11741
+ },
11742
+ {
11743
+ name: "ssmCompanyRegNo",
11744
+ type: "string",
11745
+ description: "Business or company registration number as stated on the profile."
11746
+ },
11747
+ {
11748
+ name: "companyStatus",
11749
+ type: "string",
11750
+ description: "Registration status as stated on the profile (e.g. existing, dissolved)."
11751
+ },
11752
+ {
11753
+ name: "businessOrigin",
11754
+ type: "string",
11755
+ description: "Business origin as stated on the profile."
11756
+ },
11757
+ {
11758
+ name: "companyIncorporationDate",
11759
+ type: "string",
11760
+ fact: "companyIncorporationDate",
11761
+ description: "Incorporation (or registration) date as stated on the profile, normalized to ISO YYYY-MM-DD. Attestation of the shared companyIncorporationDate fact \u2014 a company has exactly one incorporation date regardless of which document it was extracted from."
11762
+ },
11763
+ {
11764
+ name: "businessCommencementDate",
11765
+ type: "string",
11766
+ description: "Business commencement (or start) date as stated on the profile, normalized to ISO YYYY-MM-DD."
11767
+ },
11768
+ {
11769
+ name: "businessNature",
11770
+ type: "string",
11771
+ description: "Nature of business as stated on the profile."
11772
+ },
11773
+ {
11774
+ name: "registeredAddress",
11775
+ type: "string",
11776
+ description: "Registered business address as stated on the profile."
11777
+ },
11778
+ {
11779
+ name: "totalShareIssued",
11780
+ type: "number",
11781
+ description: "Total shares issued as stated on the profile."
11782
+ },
11783
+ {
11784
+ name: "ssmPaidUpCapital",
11785
+ type: "number",
11786
+ unit: "MYR",
11787
+ description: "Paid-up capital as stated on the profile (falls back to total shares issued when the profile omits an explicit paid-up figure)."
11788
+ },
11789
+ {
11790
+ name: "directors",
11791
+ type: "string",
11792
+ description: "JSON-encoded list of current directors/officers (for enterprises, the business owner normalized into the same shape)."
11793
+ },
11794
+ {
11795
+ name: "shareholders",
11796
+ type: "string",
11797
+ description: "JSON-encoded list of shareholders (for enterprises, the business owner normalized into the same shape)."
11798
+ },
11799
+ {
11800
+ name: "previousDirectors",
11801
+ type: "string",
11802
+ description: "JSON-encoded list of previous directors/officers."
11803
+ },
11804
+ {
11805
+ name: "companyLastOldName",
11806
+ type: "string",
11807
+ description: "The company's most recent former name, when the profile records a name change."
11808
+ },
11809
+ {
11810
+ name: "companyNameDateOfChange",
11811
+ type: "string",
11812
+ description: "Date of the most recent name change, normalized to ISO YYYY-MM-DD."
11813
+ }
11814
+ ]
11697
11815
  }
11698
11816
  ]
11699
11817
  };
@@ -11716,6 +11834,9 @@ function buildCategoryRegistry(raw) {
11716
11834
  }
11717
11835
  const byId = /* @__PURE__ */ new Map();
11718
11836
  const fieldToCategory = /* @__PURE__ */ new Map();
11837
+ const fieldToFact = /* @__PURE__ */ new Map();
11838
+ const factToCategories = /* @__PURE__ */ new Map();
11839
+ const declarations = /* @__PURE__ */ new Map();
11719
11840
  const tables = /* @__PURE__ */ new Set();
11720
11841
  const all = [];
11721
11842
  for (const cat of raw.categories) {
@@ -11750,11 +11871,34 @@ function buildCategoryRegistry(raw) {
11750
11871
  if (typeof f.name !== "string" || f.name.length === 0) {
11751
11872
  throw new Error(`adapter category data: ${where} has a field with no name`);
11752
11873
  }
11753
- if (fieldToCategory.has(f.name)) {
11874
+ if (f.fact !== void 0 && (typeof f.fact !== "string" || f.fact.length === 0)) {
11754
11875
  throw new Error(
11755
- `adapter category data: canonical field "${f.name}" declared by more than one category (${fieldToCategory.get(f.name)} + ${cat.id}) \u2014 field names must be globally unique`
11876
+ `adapter category data: field "${f.name}" (${where}) has an invalid fact \u2014 expected a non-empty string`
11756
11877
  );
11757
11878
  }
11879
+ const prior = declarations.get(f.name);
11880
+ if (prior) {
11881
+ if (prior.categories.includes(cat.id)) {
11882
+ throw new Error(
11883
+ `adapter category data: ${where} declares field "${f.name}" more than once`
11884
+ );
11885
+ }
11886
+ if (prior.fact === void 0 || f.fact === void 0 || prior.fact !== f.fact) {
11887
+ const describeFact = (fact) => fact === void 0 ? "no fact" : `fact "${fact}"`;
11888
+ throw new Error(
11889
+ `adapter category data: canonical field "${f.name}" declared by more than one category (${prior.categories.join(" + ")} with ${describeFact(prior.fact)} + ${cat.id} with ${describeFact(f.fact)}) \u2014 field names must be globally unique unless every declaring category attests the same fact`
11890
+ );
11891
+ }
11892
+ }
11893
+ if (f.fact !== void 0) {
11894
+ for (const [otherName, otherFact] of fieldToFact) {
11895
+ if (otherFact === f.fact && otherName !== f.name) {
11896
+ throw new Error(
11897
+ `adapter category data: fact "${f.fact}" is carried by two different field names ("${otherName}" + "${f.name}") \u2014 a fact id must map to exactly one canonical field name`
11898
+ );
11899
+ }
11900
+ }
11901
+ }
11758
11902
  if (!VALID_FIELD_TYPES.includes(f.type)) {
11759
11903
  throw new Error(
11760
11904
  `adapter category data: field "${f.name}" (${where}) has invalid type "${f.type}"`
@@ -11777,10 +11921,26 @@ function buildCategoryRegistry(raw) {
11777
11921
  type: f.type,
11778
11922
  ...f.unit !== void 0 ? { unit: f.unit } : {},
11779
11923
  ...f.range !== void 0 ? { range: Object.freeze([f.range[0], f.range[1]]) } : {},
11780
- description: f.description
11924
+ description: f.description,
11925
+ ...f.fact !== void 0 ? { fact: f.fact } : {}
11781
11926
  });
11782
11927
  fields.push(spec);
11783
- fieldToCategory.set(f.name, cat.id);
11928
+ if (prior) {
11929
+ prior.categories.push(cat.id);
11930
+ fieldToCategory.delete(f.name);
11931
+ } else {
11932
+ declarations.set(f.name, { fact: f.fact, categories: [cat.id] });
11933
+ fieldToCategory.set(f.name, cat.id);
11934
+ }
11935
+ if (f.fact !== void 0) {
11936
+ fieldToFact.set(f.name, f.fact);
11937
+ const attesters = factToCategories.get(f.fact);
11938
+ if (attesters) {
11939
+ attesters.push(cat.id);
11940
+ } else {
11941
+ factToCategories.set(f.fact, [cat.id]);
11942
+ }
11943
+ }
11784
11944
  }
11785
11945
  const schema = Object.freeze({
11786
11946
  id: cat.id,
@@ -11797,7 +11957,14 @@ function buildCategoryRegistry(raw) {
11797
11957
  all: Object.freeze(all),
11798
11958
  ids: Object.freeze(all.map((c) => c.id)),
11799
11959
  byId,
11800
- fieldToCategory
11960
+ fieldToCategory,
11961
+ fieldToFact,
11962
+ factToCategories: new Map(
11963
+ [...factToCategories].map(([fact, cats]) => [
11964
+ fact,
11965
+ Object.freeze(cats)
11966
+ ])
11967
+ )
11801
11968
  });
11802
11969
  }
11803
11970
  var registry = buildCategoryRegistry(adapter_categories_default);
@@ -11820,6 +11987,12 @@ function allCategories() {
11820
11987
  function categoryForField(field) {
11821
11988
  return registry.fieldToCategory.get(field) ?? null;
11822
11989
  }
11990
+ function factOf(field) {
11991
+ return registry.fieldToFact.get(field) ?? null;
11992
+ }
11993
+ function categoriesAttestingFact(factId) {
11994
+ return registry.factToCategories.get(factId) ?? [];
11995
+ }
11823
11996
  function isAdapterCategory(id) {
11824
11997
  return registry.byId.has(id);
11825
11998
  }
@@ -11864,11 +12037,13 @@ function assertAdapterCategory(id) {
11864
12037
  buildDocumentRows,
11865
12038
  buildFileFieldTables,
11866
12039
  buildFileFieldTablesFromInstances,
12040
+ categoriesAttestingFact,
11867
12041
  categoryFieldsOf,
11868
12042
  categoryForField,
11869
12043
  categorySchemaOf,
11870
12044
  evaluateExpression,
11871
12045
  extractTimePeriods,
12046
+ factOf,
11872
12047
  formatDocumentSize,
11873
12048
  formatDocumentType,
11874
12049
  formatDocumentUploaded,