@finsys/core 2.5.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/data/adapter-categories.json +156 -0
- package/dist/index.cjs +274 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +467 -1
- package/dist/index.d.ts +467 -1
- package/dist/index.js +267 -16
- package/dist/index.js.map +1 -1
- package/dist/schema/adapter-manifest.schema.json +101 -0
- package/package.json +6 -1
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,
|
|
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(
|
|
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((
|
|
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,
|
|
188
|
+
isVisible = evaluateExpression(field.visibleIf, data2);
|
|
189
189
|
}
|
|
190
190
|
if (isVisible) {
|
|
191
|
-
const value =
|
|
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(
|
|
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(
|
|
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(
|
|
664
|
-
return validateFormConfig(
|
|
663
|
+
function validateFormSpec(data2) {
|
|
664
|
+
return validateFormConfig(data2);
|
|
665
665
|
}
|
|
666
|
-
function validatePagesConfig(
|
|
667
|
-
return validateFormConfig(
|
|
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,
|
|
1124
|
+
fieldObjects = Object.entries(jsonData.fields).map(([name, data2]) => {
|
|
1125
1125
|
return {
|
|
1126
1126
|
name,
|
|
1127
|
-
...
|
|
1127
|
+
...data2
|
|
1128
1128
|
};
|
|
1129
1129
|
});
|
|
1130
1130
|
}
|
|
@@ -9382,18 +9382,18 @@ function buildTableForGroup(groupName, fields, ihsData) {
|
|
|
9382
9382
|
const items = [];
|
|
9383
9383
|
for (const [baseName, periodMap] of Object.entries(columnGroups)) {
|
|
9384
9384
|
const numeric = isNumericField(baseName);
|
|
9385
|
-
const
|
|
9385
|
+
const data2 = {};
|
|
9386
9386
|
const formattedData = {};
|
|
9387
9387
|
for (const period of periods) {
|
|
9388
9388
|
const colName = periodMap[period];
|
|
9389
9389
|
const value = colName ? ihsData[colName] ?? null : null;
|
|
9390
|
-
|
|
9390
|
+
data2[period] = value;
|
|
9391
9391
|
formattedData[period] = formatValue(value, numeric);
|
|
9392
9392
|
}
|
|
9393
9393
|
items.push({
|
|
9394
9394
|
displayName: getDisplayName(baseName),
|
|
9395
9395
|
timePeriods: periods,
|
|
9396
|
-
data,
|
|
9396
|
+
data: data2,
|
|
9397
9397
|
formattedData,
|
|
9398
9398
|
type: tableType,
|
|
9399
9399
|
isNumeric: numeric
|
|
@@ -9750,7 +9750,253 @@ function resolveExtractionStatus(ihsRecord, jobRecords) {
|
|
|
9750
9750
|
);
|
|
9751
9751
|
return { documents, summary };
|
|
9752
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
|
+
}
|
|
9753
9997
|
export {
|
|
9998
|
+
ALL_AGGREGATION_OPS,
|
|
9999
|
+
AdapterError,
|
|
9754
10000
|
BASE_FIELD_SPECS,
|
|
9755
10001
|
BasicFormField,
|
|
9756
10002
|
DEFAULT_VALIDATOR_DEFINITIONS,
|
|
@@ -9771,8 +10017,13 @@ export {
|
|
|
9771
10017
|
IhsStatus,
|
|
9772
10018
|
IhsValueFormat,
|
|
9773
10019
|
Role,
|
|
10020
|
+
allCategories,
|
|
10021
|
+
applyAggregation,
|
|
9774
10022
|
applyDynamicTitles,
|
|
9775
10023
|
buildFileFieldTables,
|
|
10024
|
+
categoryFieldsOf,
|
|
10025
|
+
categoryForField,
|
|
10026
|
+
categorySchemaOf,
|
|
9776
10027
|
evaluateExpression,
|
|
9777
10028
|
extractTimePeriods,
|
|
9778
10029
|
generateRHFSchema,
|