@finsys/core 2.4.0 → 2.6.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/dist/index.js CHANGED
@@ -9,7 +9,7 @@ function getPastYearLabel(yearsAgo) {
9
9
  const date = /* @__PURE__ */ new Date();
10
10
  return (date.getFullYear() - yearsAgo).toString();
11
11
  }
12
- function evaluateExpression(expression, data) {
12
+ function evaluateExpression(expression, data2) {
13
13
  if (!expression) return true;
14
14
  try {
15
15
  let jsExpression = expression.replace(/\{([^}]+)\}/g, (_, key) => {
@@ -33,7 +33,7 @@ function evaluateExpression(expression, data) {
33
33
  (_, ref, val) => `!(${ref} || []).includes('${val}')`
34
34
  );
35
35
  const func = new Function(`return ${jsExpression};`);
36
- return !!func.call(data || {});
36
+ return !!func.call(data2 || {});
37
37
  } catch (e) {
38
38
  return false;
39
39
  }
@@ -179,16 +179,16 @@ function groupFieldsByCategory(fields, categories) {
179
179
  // src/rhf-generator.ts
180
180
  import { z } from "zod";
181
181
  function applyVisibilityValidation(baseObject, fields) {
182
- return baseObject.superRefine((data, ctx) => {
182
+ return baseObject.superRefine((data2, ctx) => {
183
183
  fields.forEach((field) => {
184
184
  const isRequired = field.isRequired !== false && field.required !== false && field.type !== "html";
185
185
  if (!isRequired) return;
186
186
  let isVisible = field.visible !== false;
187
187
  if (isVisible && field.visibleIf) {
188
- isVisible = evaluateExpression(field.visibleIf, data);
188
+ isVisible = evaluateExpression(field.visibleIf, data2);
189
189
  }
190
190
  if (isVisible) {
191
- const value = data[field.name];
191
+ const value = data2[field.name];
192
192
  const isEmpty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
193
193
  if (isEmpty) {
194
194
  ctx.addIssue({
@@ -649,22 +649,22 @@ var ajv = new Ajv({ allErrors: true, strict: false });
649
649
  var addFormatsFn = addFormats.default || addFormats;
650
650
  addFormatsFn(ajv);
651
651
  ajv.addSchema(unified_form_schema_default, "unified-form.schema.json");
652
- function validateFormConfig(data) {
652
+ function validateFormConfig(data2) {
653
653
  const validate = ajv.getSchema("unified-form.schema.json");
654
654
  if (!validate) {
655
655
  return { valid: false, message: "Could not load unified form schema" };
656
656
  }
657
- const valid = validate(data);
657
+ const valid = validate(data2);
658
658
  return {
659
659
  valid: !!valid,
660
660
  errors: validate.errors || void 0
661
661
  };
662
662
  }
663
- function validateFormSpec(data) {
664
- return validateFormConfig(data);
663
+ function validateFormSpec(data2) {
664
+ return validateFormConfig(data2);
665
665
  }
666
- function validatePagesConfig(data) {
667
- return validateFormConfig(data);
666
+ function validatePagesConfig(data2) {
667
+ return validateFormConfig(data2);
668
668
  }
669
669
 
670
670
  // src/form-field-category.ts
@@ -1121,10 +1121,10 @@ var _FormSpec = class _FormSpec {
1121
1121
  if (Array.isArray(jsonData.fields)) {
1122
1122
  fieldObjects = jsonData.fields;
1123
1123
  } else if (typeof jsonData.fields === "object" && jsonData.fields !== null) {
1124
- fieldObjects = Object.entries(jsonData.fields).map(([name, data]) => {
1124
+ fieldObjects = Object.entries(jsonData.fields).map(([name, data2]) => {
1125
1125
  return {
1126
1126
  name,
1127
- ...data
1127
+ ...data2
1128
1128
  };
1129
1129
  });
1130
1130
  }
@@ -7888,7 +7888,6 @@ var form_field_base_specs_default = {
7888
7888
  name: "bank_statement_t1",
7889
7889
  displayName: "Bank Statement (Month T-1)",
7890
7890
  type: "file",
7891
- enableIf: "{formOfDisclosure} contains 'consented'",
7892
7891
  ihs_column_names: [
7893
7892
  "accountHolderNameT1",
7894
7893
  "statementDateT1",
@@ -7905,7 +7904,6 @@ var form_field_base_specs_default = {
7905
7904
  name: "bank_statement_t2",
7906
7905
  displayName: "Bank Statement (Month T-2)",
7907
7906
  type: "file",
7908
- enableIf: "{formOfDisclosure} contains 'consented'",
7909
7907
  ihs_column_names: [
7910
7908
  "accountHolderNameT2",
7911
7909
  "statementDateT2",
@@ -7922,7 +7920,6 @@ var form_field_base_specs_default = {
7922
7920
  name: "bank_statement_t3",
7923
7921
  displayName: "Bank Statement (Month T-3)",
7924
7922
  type: "file",
7925
- enableIf: "{formOfDisclosure} contains 'consented'",
7926
7923
  ihs_column_names: [
7927
7924
  "accountHolderNameT3",
7928
7925
  "statementDateT3",
@@ -7939,7 +7936,6 @@ var form_field_base_specs_default = {
7939
7936
  name: "bank_statement_t4",
7940
7937
  displayName: "Bank Statement (Month T-4)",
7941
7938
  type: "file",
7942
- enableIf: "{formOfDisclosure} contains 'consented'",
7943
7939
  ihs_column_names: [
7944
7940
  "accountHolderNameT4",
7945
7941
  "statementDateT4",
@@ -7956,7 +7952,6 @@ var form_field_base_specs_default = {
7956
7952
  name: "bank_statement_t5",
7957
7953
  displayName: "Bank Statement (Month T-5)",
7958
7954
  type: "file",
7959
- enableIf: "{formOfDisclosure} contains 'consented'",
7960
7955
  ihs_column_names: [
7961
7956
  "accountHolderNameT5",
7962
7957
  "statementDateT5",
@@ -7973,7 +7968,6 @@ var form_field_base_specs_default = {
7973
7968
  name: "bank_statement_t6",
7974
7969
  displayName: "Bank Statement (Month T-6)",
7975
7970
  type: "file",
7976
- enableIf: "{formOfDisclosure} contains 'consented'",
7977
7971
  ihs_column_names: [
7978
7972
  "accountHolderNameT6",
7979
7973
  "statementDateT6",
@@ -7990,7 +7984,6 @@ var form_field_base_specs_default = {
7990
7984
  name: "financials",
7991
7985
  displayName: "Audited Financial Statement",
7992
7986
  type: "file",
7993
- enableIf: "{formOfDisclosure} contains 'consented'",
7994
7987
  ihs_column_names: [
7995
7988
  "shareCapital",
7996
7989
  "totalEquityT1",
@@ -8012,7 +8005,6 @@ var form_field_base_specs_default = {
8012
8005
  name: "financials_fincap_t1",
8013
8006
  displayName: "Audited Financial Statement (T-1) (FinCap)",
8014
8007
  type: "file",
8015
- enableIf: "{formOfDisclosure} contains 'consented'",
8016
8008
  ihs_column_names: [
8017
8009
  "nonCurrentAssetsSubsidiaryCompaniesT1",
8018
8010
  "nonCurrentAssetsAssociatedCompaniesT1",
@@ -8257,7 +8249,6 @@ var form_field_base_specs_default = {
8257
8249
  name: "financials_fincap_t2",
8258
8250
  displayName: "Audited Financial Statement (T-2) (FinCap)",
8259
8251
  type: "file",
8260
- enableIf: "{formOfDisclosure} contains 'consented'",
8261
8252
  ihs_column_names: [
8262
8253
  "nonCurrentAssetsSubsidiaryCompaniesT3",
8263
8254
  "nonCurrentAssetsAssociatedCompaniesT3",
@@ -8384,7 +8375,6 @@ var form_field_base_specs_default = {
8384
8375
  name: "form9",
8385
8376
  displayName: "Form 9 / Section 17 / Form D",
8386
8377
  type: "file",
8387
- enableIf: "{formOfDisclosure} contains 'consented'",
8388
8378
  ihs_column_names: [
8389
8379
  "incorporatedDate",
8390
8380
  "companyName",
@@ -8396,7 +8386,6 @@ var form_field_base_specs_default = {
8396
8386
  name: "ssm",
8397
8387
  displayName: "SSM Company Profile",
8398
8388
  type: "file",
8399
- enableIf: "{formOfDisclosure} contains 'consented'",
8400
8389
  ihs_column_names: [
8401
8390
  "ssmCompanyName",
8402
8391
  "ssmCompanyRegNo",
@@ -8421,7 +8410,6 @@ var form_field_base_specs_default = {
8421
8410
  name: "ic",
8422
8411
  displayName: "Identification Card (Front)",
8423
8412
  type: "file",
8424
- enableIf: "{formOfDisclosure} contains 'consented'",
8425
8413
  ihs_column_names: [
8426
8414
  "icNumber",
8427
8415
  "icName",
@@ -8439,7 +8427,6 @@ var form_field_base_specs_default = {
8439
8427
  name: "epf_statement_t1",
8440
8428
  displayName: "Employees' Provident Fund (T-1)",
8441
8429
  type: "file",
8442
- enableIf: "{formOfDisclosure} contains 'consented'",
8443
8430
  ihs_column_names: [
8444
8431
  "epfAccountHolderNameT1",
8445
8432
  "epfAccountHolderAddressT1",
@@ -8457,7 +8444,6 @@ var form_field_base_specs_default = {
8457
8444
  name: "epf_statement_t2",
8458
8445
  displayName: "Employees' Provident Fund (T-2)",
8459
8446
  type: "file",
8460
- enableIf: "{formOfDisclosure} contains 'consented'",
8461
8447
  ihs_column_names: [
8462
8448
  "epfAccountHolderNameT2",
8463
8449
  "epfAccountHolderAddressT2",
@@ -8475,7 +8461,6 @@ var form_field_base_specs_default = {
8475
8461
  name: "payslip_statement_t1",
8476
8462
  displayName: "Payslip Statement (Month T-1)",
8477
8463
  type: "file",
8478
- enableIf: "{formOfDisclosure} contains 'consented'",
8479
8464
  ihs_column_names: [
8480
8465
  "payslipEmployerNameT1",
8481
8466
  "payslipEmployeeNameT1",
@@ -8499,7 +8484,6 @@ var form_field_base_specs_default = {
8499
8484
  name: "payslip_statement_t2",
8500
8485
  displayName: "Payslip Statement (Month T-2)",
8501
8486
  type: "file",
8502
- enableIf: "{formOfDisclosure} contains 'consented'",
8503
8487
  ihs_column_names: [
8504
8488
  "payslipEmployerNameT2",
8505
8489
  "payslipEmployeeNameT2",
@@ -8523,7 +8507,6 @@ var form_field_base_specs_default = {
8523
8507
  name: "payslip_statement_t3",
8524
8508
  displayName: "Payslip Statement (Month T-3)",
8525
8509
  type: "file",
8526
- enableIf: "{formOfDisclosure} contains 'consented'",
8527
8510
  ihs_column_names: [
8528
8511
  "payslipEmployerNameT3",
8529
8512
  "payslipEmployeeNameT3",
@@ -8547,7 +8530,6 @@ var form_field_base_specs_default = {
8547
8530
  name: "payslip_statement_t4",
8548
8531
  displayName: "Payslip Statement (Month T-4)",
8549
8532
  type: "file",
8550
- enableIf: "{formOfDisclosure} contains 'consented'",
8551
8533
  ihs_column_names: [
8552
8534
  "payslipEmployerNameT4",
8553
8535
  "payslipEmployeeNameT4",
@@ -8571,7 +8553,6 @@ var form_field_base_specs_default = {
8571
8553
  name: "payslip_statement_t5",
8572
8554
  displayName: "Payslip Statement (Month T-5)",
8573
8555
  type: "file",
8574
- enableIf: "{formOfDisclosure} contains 'consented'",
8575
8556
  ihs_column_names: [
8576
8557
  "payslipEmployerNameT5",
8577
8558
  "payslipEmployeeNameT5",
@@ -8595,7 +8576,6 @@ var form_field_base_specs_default = {
8595
8576
  name: "payslip_statement_t6",
8596
8577
  displayName: "Payslip Statement (Month T-6)",
8597
8578
  type: "file",
8598
- enableIf: "{formOfDisclosure} contains 'consented'",
8599
8579
  ihs_column_names: [
8600
8580
  "payslipEmployerNameT6",
8601
8581
  "payslipEmployeeNameT6",
@@ -9402,18 +9382,18 @@ function buildTableForGroup(groupName, fields, ihsData) {
9402
9382
  const items = [];
9403
9383
  for (const [baseName, periodMap] of Object.entries(columnGroups)) {
9404
9384
  const numeric = isNumericField(baseName);
9405
- const data = {};
9385
+ const data2 = {};
9406
9386
  const formattedData = {};
9407
9387
  for (const period of periods) {
9408
9388
  const colName = periodMap[period];
9409
9389
  const value = colName ? ihsData[colName] ?? null : null;
9410
- data[period] = value;
9390
+ data2[period] = value;
9411
9391
  formattedData[period] = formatValue(value, numeric);
9412
9392
  }
9413
9393
  items.push({
9414
9394
  displayName: getDisplayName(baseName),
9415
9395
  timePeriods: periods,
9416
- data,
9396
+ data: data2,
9417
9397
  formattedData,
9418
9398
  type: tableType,
9419
9399
  isNumeric: numeric
@@ -9770,7 +9750,253 @@ function resolveExtractionStatus(ihsRecord, jobRecords) {
9770
9750
  );
9771
9751
  return { documents, summary };
9772
9752
  }
9753
+
9754
+ // src/adapter.ts
9755
+ var AdapterError = class extends Error {
9756
+ constructor(reason, message, cause) {
9757
+ super(message);
9758
+ this.name = "AdapterError";
9759
+ this.reason = reason;
9760
+ this.cause = cause;
9761
+ }
9762
+ };
9763
+
9764
+ // src/adapter-aggregation.ts
9765
+ var ALL_AGGREGATION_OPS = [
9766
+ "sum",
9767
+ "mean",
9768
+ "latest",
9769
+ "max",
9770
+ "count"
9771
+ ];
9772
+ function applyAggregation(op, instances) {
9773
+ if (instances.length === 0) {
9774
+ return op === "count" ? 0 : null;
9775
+ }
9776
+ if (op === "count") {
9777
+ return instances.reduce(
9778
+ (acc, i) => i.value !== null && i.value !== void 0 ? acc + 1 : acc,
9779
+ 0
9780
+ );
9781
+ }
9782
+ if (op === "latest") {
9783
+ let pick = instances[0];
9784
+ for (let i = 1; i < instances.length; i++) {
9785
+ if (instances[i].observedAt > pick.observedAt) pick = instances[i];
9786
+ }
9787
+ return pick.value;
9788
+ }
9789
+ const nums = [];
9790
+ for (const i of instances) {
9791
+ if (typeof i.value === "number" && Number.isFinite(i.value)) {
9792
+ nums.push(i.value);
9793
+ } else if (i.value !== null && typeof i.value !== "undefined") {
9794
+ throw new Error(
9795
+ `applyAggregation: '${op}' requires numeric values; got ${typeof i.value}`
9796
+ );
9797
+ }
9798
+ }
9799
+ if (nums.length === 0) return null;
9800
+ switch (op) {
9801
+ case "sum":
9802
+ return nums.reduce((a, b) => a + b, 0);
9803
+ case "mean":
9804
+ return nums.reduce((a, b) => a + b, 0) / nums.length;
9805
+ case "max":
9806
+ return nums.reduce((a, b) => a > b ? a : b);
9807
+ }
9808
+ }
9809
+
9810
+ // src/data/adapter-categories.json
9811
+ var adapter_categories_default = {
9812
+ schemaVersion: "1.0.0",
9813
+ categories: [
9814
+ {
9815
+ id: "telco-carrier",
9816
+ displayName: "Telco Carrier",
9817
+ description: "Mobile carrier bill-payment + account-history signals. Any telco vendor (Celcom, Maxis, DiGi, U-Mobile, etc.) implementing this category produces the same canonical field set.",
9818
+ canonicalTable: "ihs_alt_data_telco",
9819
+ fields: [
9820
+ {
9821
+ name: "telcoOnTimePaymentRatio24m",
9822
+ type: "number",
9823
+ unit: "ratio",
9824
+ range: [0, 1],
9825
+ description: "Fraction of bills paid on time over the last 24 months. Strongest single telco predictor; \u22650.95 is the clean-history signal."
9826
+ },
9827
+ {
9828
+ name: "telcoTenureMonths",
9829
+ type: "number",
9830
+ unit: "months",
9831
+ range: [0, 600],
9832
+ description: "Account age. \u226548 months is the thin-file uplift trigger."
9833
+ },
9834
+ {
9835
+ name: "telcoSuspensionsCount24m",
9836
+ type: "number",
9837
+ unit: "count",
9838
+ range: [0, 100],
9839
+ description: "Non-payment-driven account suspensions in the last 24 months. \u22653 is a strong distress signal."
9840
+ },
9841
+ {
9842
+ name: "telcoLateDays24m",
9843
+ type: "number",
9844
+ unit: "days",
9845
+ range: [0, 800],
9846
+ description: "Cumulative days late across all bills in the trailing 24-month window."
9847
+ },
9848
+ {
9849
+ name: "telcoHandsetFinancingActive",
9850
+ type: "boolean",
9851
+ description: "Has an active handset-EMI account currently. Proxy for financing capacity already extended."
9852
+ },
9853
+ {
9854
+ name: "telcoHandsetFinancingDelinquent",
9855
+ type: "boolean",
9856
+ description: "Recent handset-EMI delinquency in the last 24 months. Distress signal even with otherwise clean bill payment."
9857
+ },
9858
+ {
9859
+ name: "telcoArpuMyr",
9860
+ type: "number",
9861
+ unit: "MYR",
9862
+ range: [0, 1e4],
9863
+ description: "Average Revenue Per User (monthly) in Malaysian Ringgit. Coarse spending-capacity proxy."
9864
+ }
9865
+ ]
9866
+ },
9867
+ {
9868
+ id: "payment-network",
9869
+ displayName: "Payment Network",
9870
+ description: "Merchant-side payment-flow signals from POS / gateway networks (iPay88, GHL, etc.). Captures actual transaction velocity rather than self-reported revenue.",
9871
+ canonicalTable: "ihs_alt_data_payments",
9872
+ fields: [
9873
+ {
9874
+ name: "paymentsMonthlyVolumeMyrT3",
9875
+ type: "number",
9876
+ unit: "MYR",
9877
+ range: [0, 1e8],
9878
+ description: "Mean monthly inbound transaction volume (RM) over the trailing 3 months."
9879
+ },
9880
+ {
9881
+ name: "paymentsMonthlyVolumeMyrT12",
9882
+ type: "number",
9883
+ unit: "MYR",
9884
+ range: [0, 1e8],
9885
+ description: "Mean monthly inbound transaction volume (RM) over the trailing 12 months. Pair with T3 for trend direction."
9886
+ },
9887
+ {
9888
+ name: "paymentsArpuStability12m",
9889
+ type: "number",
9890
+ unit: "ratio",
9891
+ range: [0, 1],
9892
+ description: "Coefficient-of-variation inverse over monthly ARPU in the trailing 12 months. Closer to 1 = steadier; closer to 0 = volatile."
9893
+ },
9894
+ {
9895
+ name: "paymentsDisputeRate12m",
9896
+ type: "number",
9897
+ unit: "ratio",
9898
+ range: [0, 1],
9899
+ description: "Fraction of transactions disputed or refunded in the trailing 12 months."
9900
+ },
9901
+ {
9902
+ name: "paymentsCustomerConcentrationTop5Pct",
9903
+ type: "number",
9904
+ unit: "ratio",
9905
+ range: [0, 1],
9906
+ description: "Revenue share from the top-5 recurring customers. Above ~0.7 is concentration risk."
9907
+ },
9908
+ {
9909
+ name: "paymentsActiveTenureMonths",
9910
+ type: "number",
9911
+ unit: "months",
9912
+ range: [0, 600],
9913
+ description: "Months since first transaction on the payment network. Establishment / continuity proxy."
9914
+ }
9915
+ ]
9916
+ },
9917
+ {
9918
+ id: "bank-statement",
9919
+ displayName: "Bank Statement",
9920
+ description: "Per-month bank-statement extractions. Naturally multi-instance: one statement per (account, month). Eval components typically aggregate across instances (sum closing balance, max debit, count of bounced transactions).",
9921
+ canonicalTable: "ihs_alt_data_bank_statements",
9922
+ fields: [
9923
+ {
9924
+ name: "bankStatementMonth",
9925
+ type: "string",
9926
+ description: "Statement period in YYYY-MM format. Used as the instance_key discriminator + by the `latest` aggregation operator."
9927
+ },
9928
+ {
9929
+ name: "bankClosingBalanceMyr",
9930
+ type: "number",
9931
+ unit: "MYR",
9932
+ range: [-1e8, 1e8],
9933
+ description: "Closing balance for the statement period."
9934
+ },
9935
+ {
9936
+ name: "bankTotalCreditsMyr",
9937
+ type: "number",
9938
+ unit: "MYR",
9939
+ range: [0, 1e8],
9940
+ description: "Sum of credit transactions during the period."
9941
+ },
9942
+ {
9943
+ name: "bankTotalDebitsMyr",
9944
+ type: "number",
9945
+ unit: "MYR",
9946
+ range: [0, 1e8],
9947
+ description: "Sum of debit transactions during the period."
9948
+ },
9949
+ {
9950
+ name: "bankLargestSingleCreditMyr",
9951
+ type: "number",
9952
+ unit: "MYR",
9953
+ range: [0, 1e8],
9954
+ description: "Largest single inbound transaction in the period. Useful for spotting one-off injections vs steady revenue."
9955
+ },
9956
+ {
9957
+ name: "bankBouncedTransactionsCount",
9958
+ type: "number",
9959
+ unit: "count",
9960
+ range: [0, 1e3],
9961
+ description: "Bounced / returned transactions in the period. Distress signal at counts > 0."
9962
+ }
9963
+ ]
9964
+ }
9965
+ ]
9966
+ };
9967
+
9968
+ // src/adapter-categories.ts
9969
+ var data = adapter_categories_default;
9970
+ function categorySchemaOf(id) {
9971
+ const found = data.categories.find((c) => c.id === id);
9972
+ if (!found) {
9973
+ throw new Error(
9974
+ `Unknown adapter category: ${id}. Available: ${data.categories.map((c) => c.id).join(", ")}`
9975
+ );
9976
+ }
9977
+ return found;
9978
+ }
9979
+ function categoryFieldsOf(id) {
9980
+ return categorySchemaOf(id).fields.map((f) => f.name);
9981
+ }
9982
+ function allCategories() {
9983
+ return data.categories;
9984
+ }
9985
+ var fieldToCategory = (() => {
9986
+ const m = /* @__PURE__ */ new Map();
9987
+ for (const cat of data.categories) {
9988
+ for (const f of cat.fields) {
9989
+ m.set(f.name, cat.id);
9990
+ }
9991
+ }
9992
+ return m;
9993
+ })();
9994
+ function categoryForField(field) {
9995
+ return fieldToCategory.get(field) ?? null;
9996
+ }
9773
9997
  export {
9998
+ ALL_AGGREGATION_OPS,
9999
+ AdapterError,
9774
10000
  BASE_FIELD_SPECS,
9775
10001
  BasicFormField,
9776
10002
  DEFAULT_VALIDATOR_DEFINITIONS,
@@ -9791,8 +10017,13 @@ export {
9791
10017
  IhsStatus,
9792
10018
  IhsValueFormat,
9793
10019
  Role,
10020
+ allCategories,
10021
+ applyAggregation,
9794
10022
  applyDynamicTitles,
9795
10023
  buildFileFieldTables,
10024
+ categoryFieldsOf,
10025
+ categoryForField,
10026
+ categorySchemaOf,
9796
10027
  evaluateExpression,
9797
10028
  extractTimePeriods,
9798
10029
  generateRHFSchema,