@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
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"categories": [
|
|
4
|
+
{
|
|
5
|
+
"id": "telco-carrier",
|
|
6
|
+
"displayName": "Telco Carrier",
|
|
7
|
+
"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.",
|
|
8
|
+
"canonicalTable": "ihs_alt_data_telco",
|
|
9
|
+
"fields": [
|
|
10
|
+
{
|
|
11
|
+
"name": "telcoOnTimePaymentRatio24m",
|
|
12
|
+
"type": "number",
|
|
13
|
+
"unit": "ratio",
|
|
14
|
+
"range": [0, 1],
|
|
15
|
+
"description": "Fraction of bills paid on time over the last 24 months. Strongest single telco predictor; ≥0.95 is the clean-history signal."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "telcoTenureMonths",
|
|
19
|
+
"type": "number",
|
|
20
|
+
"unit": "months",
|
|
21
|
+
"range": [0, 600],
|
|
22
|
+
"description": "Account age. ≥48 months is the thin-file uplift trigger."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "telcoSuspensionsCount24m",
|
|
26
|
+
"type": "number",
|
|
27
|
+
"unit": "count",
|
|
28
|
+
"range": [0, 100],
|
|
29
|
+
"description": "Non-payment-driven account suspensions in the last 24 months. ≥3 is a strong distress signal."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "telcoLateDays24m",
|
|
33
|
+
"type": "number",
|
|
34
|
+
"unit": "days",
|
|
35
|
+
"range": [0, 800],
|
|
36
|
+
"description": "Cumulative days late across all bills in the trailing 24-month window."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "telcoHandsetFinancingActive",
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"description": "Has an active handset-EMI account currently. Proxy for financing capacity already extended."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "telcoHandsetFinancingDelinquent",
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"description": "Recent handset-EMI delinquency in the last 24 months. Distress signal even with otherwise clean bill payment."
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "telcoArpuMyr",
|
|
50
|
+
"type": "number",
|
|
51
|
+
"unit": "MYR",
|
|
52
|
+
"range": [0, 10000],
|
|
53
|
+
"description": "Average Revenue Per User (monthly) in Malaysian Ringgit. Coarse spending-capacity proxy."
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "payment-network",
|
|
59
|
+
"displayName": "Payment Network",
|
|
60
|
+
"description": "Merchant-side payment-flow signals from POS / gateway networks (iPay88, GHL, etc.). Captures actual transaction velocity rather than self-reported revenue.",
|
|
61
|
+
"canonicalTable": "ihs_alt_data_payments",
|
|
62
|
+
"fields": [
|
|
63
|
+
{
|
|
64
|
+
"name": "paymentsMonthlyVolumeMyrT3",
|
|
65
|
+
"type": "number",
|
|
66
|
+
"unit": "MYR",
|
|
67
|
+
"range": [0, 100000000],
|
|
68
|
+
"description": "Mean monthly inbound transaction volume (RM) over the trailing 3 months."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "paymentsMonthlyVolumeMyrT12",
|
|
72
|
+
"type": "number",
|
|
73
|
+
"unit": "MYR",
|
|
74
|
+
"range": [0, 100000000],
|
|
75
|
+
"description": "Mean monthly inbound transaction volume (RM) over the trailing 12 months. Pair with T3 for trend direction."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "paymentsArpuStability12m",
|
|
79
|
+
"type": "number",
|
|
80
|
+
"unit": "ratio",
|
|
81
|
+
"range": [0, 1],
|
|
82
|
+
"description": "Coefficient-of-variation inverse over monthly ARPU in the trailing 12 months. Closer to 1 = steadier; closer to 0 = volatile."
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "paymentsDisputeRate12m",
|
|
86
|
+
"type": "number",
|
|
87
|
+
"unit": "ratio",
|
|
88
|
+
"range": [0, 1],
|
|
89
|
+
"description": "Fraction of transactions disputed or refunded in the trailing 12 months."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "paymentsCustomerConcentrationTop5Pct",
|
|
93
|
+
"type": "number",
|
|
94
|
+
"unit": "ratio",
|
|
95
|
+
"range": [0, 1],
|
|
96
|
+
"description": "Revenue share from the top-5 recurring customers. Above ~0.7 is concentration risk."
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "paymentsActiveTenureMonths",
|
|
100
|
+
"type": "number",
|
|
101
|
+
"unit": "months",
|
|
102
|
+
"range": [0, 600],
|
|
103
|
+
"description": "Months since first transaction on the payment network. Establishment / continuity proxy."
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"id": "bank-statement",
|
|
109
|
+
"displayName": "Bank Statement",
|
|
110
|
+
"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).",
|
|
111
|
+
"canonicalTable": "ihs_alt_data_bank_statements",
|
|
112
|
+
"fields": [
|
|
113
|
+
{
|
|
114
|
+
"name": "bankStatementMonth",
|
|
115
|
+
"type": "string",
|
|
116
|
+
"description": "Statement period in YYYY-MM format. Used as the instance_key discriminator + by the `latest` aggregation operator."
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"name": "bankClosingBalanceMyr",
|
|
120
|
+
"type": "number",
|
|
121
|
+
"unit": "MYR",
|
|
122
|
+
"range": [-100000000, 100000000],
|
|
123
|
+
"description": "Closing balance for the statement period."
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "bankTotalCreditsMyr",
|
|
127
|
+
"type": "number",
|
|
128
|
+
"unit": "MYR",
|
|
129
|
+
"range": [0, 100000000],
|
|
130
|
+
"description": "Sum of credit transactions during the period."
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "bankTotalDebitsMyr",
|
|
134
|
+
"type": "number",
|
|
135
|
+
"unit": "MYR",
|
|
136
|
+
"range": [0, 100000000],
|
|
137
|
+
"description": "Sum of debit transactions during the period."
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"name": "bankLargestSingleCreditMyr",
|
|
141
|
+
"type": "number",
|
|
142
|
+
"unit": "MYR",
|
|
143
|
+
"range": [0, 100000000],
|
|
144
|
+
"description": "Largest single inbound transaction in the period. Useful for spotting one-off injections vs steady revenue."
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"name": "bankBouncedTransactionsCount",
|
|
148
|
+
"type": "number",
|
|
149
|
+
"unit": "count",
|
|
150
|
+
"range": [0, 1000],
|
|
151
|
+
"description": "Bounced / returned transactions in the period. Distress signal at counts > 0."
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
ALL_AGGREGATION_OPS: () => ALL_AGGREGATION_OPS,
|
|
34
|
+
AdapterError: () => AdapterError,
|
|
33
35
|
BASE_FIELD_SPECS: () => BASE_FIELD_SPECS,
|
|
34
36
|
BasicFormField: () => BasicFormField,
|
|
35
37
|
DEFAULT_VALIDATOR_DEFINITIONS: () => DEFAULT_VALIDATOR_DEFINITIONS,
|
|
@@ -50,8 +52,13 @@ __export(index_exports, {
|
|
|
50
52
|
IhsStatus: () => IhsStatus,
|
|
51
53
|
IhsValueFormat: () => IhsValueFormat,
|
|
52
54
|
Role: () => Role,
|
|
55
|
+
allCategories: () => allCategories,
|
|
56
|
+
applyAggregation: () => applyAggregation,
|
|
53
57
|
applyDynamicTitles: () => applyDynamicTitles,
|
|
54
58
|
buildFileFieldTables: () => buildFileFieldTables,
|
|
59
|
+
categoryFieldsOf: () => categoryFieldsOf,
|
|
60
|
+
categoryForField: () => categoryForField,
|
|
61
|
+
categorySchemaOf: () => categorySchemaOf,
|
|
55
62
|
evaluateExpression: () => evaluateExpression,
|
|
56
63
|
extractTimePeriods: () => extractTimePeriods,
|
|
57
64
|
generateRHFSchema: () => generateRHFSchema,
|
|
@@ -95,7 +102,7 @@ function getPastYearLabel(yearsAgo) {
|
|
|
95
102
|
const date = /* @__PURE__ */ new Date();
|
|
96
103
|
return (date.getFullYear() - yearsAgo).toString();
|
|
97
104
|
}
|
|
98
|
-
function evaluateExpression(expression,
|
|
105
|
+
function evaluateExpression(expression, data2) {
|
|
99
106
|
if (!expression) return true;
|
|
100
107
|
try {
|
|
101
108
|
let jsExpression = expression.replace(/\{([^}]+)\}/g, (_, key) => {
|
|
@@ -119,7 +126,7 @@ function evaluateExpression(expression, data) {
|
|
|
119
126
|
(_, ref, val) => `!(${ref} || []).includes('${val}')`
|
|
120
127
|
);
|
|
121
128
|
const func = new Function(`return ${jsExpression};`);
|
|
122
|
-
return !!func.call(
|
|
129
|
+
return !!func.call(data2 || {});
|
|
123
130
|
} catch (e) {
|
|
124
131
|
return false;
|
|
125
132
|
}
|
|
@@ -265,16 +272,16 @@ function groupFieldsByCategory(fields, categories) {
|
|
|
265
272
|
// src/rhf-generator.ts
|
|
266
273
|
var import_zod = require("zod");
|
|
267
274
|
function applyVisibilityValidation(baseObject, fields) {
|
|
268
|
-
return baseObject.superRefine((
|
|
275
|
+
return baseObject.superRefine((data2, ctx) => {
|
|
269
276
|
fields.forEach((field) => {
|
|
270
277
|
const isRequired = field.isRequired !== false && field.required !== false && field.type !== "html";
|
|
271
278
|
if (!isRequired) return;
|
|
272
279
|
let isVisible = field.visible !== false;
|
|
273
280
|
if (isVisible && field.visibleIf) {
|
|
274
|
-
isVisible = evaluateExpression(field.visibleIf,
|
|
281
|
+
isVisible = evaluateExpression(field.visibleIf, data2);
|
|
275
282
|
}
|
|
276
283
|
if (isVisible) {
|
|
277
|
-
const value =
|
|
284
|
+
const value = data2[field.name];
|
|
278
285
|
const isEmpty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
|
|
279
286
|
if (isEmpty) {
|
|
280
287
|
ctx.addIssue({
|
|
@@ -735,22 +742,22 @@ var ajv = new Ajv({ allErrors: true, strict: false });
|
|
|
735
742
|
var addFormatsFn = import_ajv_formats.default.default || import_ajv_formats.default;
|
|
736
743
|
addFormatsFn(ajv);
|
|
737
744
|
ajv.addSchema(unified_form_schema_default, "unified-form.schema.json");
|
|
738
|
-
function validateFormConfig(
|
|
745
|
+
function validateFormConfig(data2) {
|
|
739
746
|
const validate = ajv.getSchema("unified-form.schema.json");
|
|
740
747
|
if (!validate) {
|
|
741
748
|
return { valid: false, message: "Could not load unified form schema" };
|
|
742
749
|
}
|
|
743
|
-
const valid = validate(
|
|
750
|
+
const valid = validate(data2);
|
|
744
751
|
return {
|
|
745
752
|
valid: !!valid,
|
|
746
753
|
errors: validate.errors || void 0
|
|
747
754
|
};
|
|
748
755
|
}
|
|
749
|
-
function validateFormSpec(
|
|
750
|
-
return validateFormConfig(
|
|
756
|
+
function validateFormSpec(data2) {
|
|
757
|
+
return validateFormConfig(data2);
|
|
751
758
|
}
|
|
752
|
-
function validatePagesConfig(
|
|
753
|
-
return validateFormConfig(
|
|
759
|
+
function validatePagesConfig(data2) {
|
|
760
|
+
return validateFormConfig(data2);
|
|
754
761
|
}
|
|
755
762
|
|
|
756
763
|
// src/form-field-category.ts
|
|
@@ -1207,10 +1214,10 @@ var _FormSpec = class _FormSpec {
|
|
|
1207
1214
|
if (Array.isArray(jsonData.fields)) {
|
|
1208
1215
|
fieldObjects = jsonData.fields;
|
|
1209
1216
|
} else if (typeof jsonData.fields === "object" && jsonData.fields !== null) {
|
|
1210
|
-
fieldObjects = Object.entries(jsonData.fields).map(([name,
|
|
1217
|
+
fieldObjects = Object.entries(jsonData.fields).map(([name, data2]) => {
|
|
1211
1218
|
return {
|
|
1212
1219
|
name,
|
|
1213
|
-
...
|
|
1220
|
+
...data2
|
|
1214
1221
|
};
|
|
1215
1222
|
});
|
|
1216
1223
|
}
|
|
@@ -9468,18 +9475,18 @@ function buildTableForGroup(groupName, fields, ihsData) {
|
|
|
9468
9475
|
const items = [];
|
|
9469
9476
|
for (const [baseName, periodMap] of Object.entries(columnGroups)) {
|
|
9470
9477
|
const numeric = isNumericField(baseName);
|
|
9471
|
-
const
|
|
9478
|
+
const data2 = {};
|
|
9472
9479
|
const formattedData = {};
|
|
9473
9480
|
for (const period of periods) {
|
|
9474
9481
|
const colName = periodMap[period];
|
|
9475
9482
|
const value = colName ? ihsData[colName] ?? null : null;
|
|
9476
|
-
|
|
9483
|
+
data2[period] = value;
|
|
9477
9484
|
formattedData[period] = formatValue(value, numeric);
|
|
9478
9485
|
}
|
|
9479
9486
|
items.push({
|
|
9480
9487
|
displayName: getDisplayName(baseName),
|
|
9481
9488
|
timePeriods: periods,
|
|
9482
|
-
data,
|
|
9489
|
+
data: data2,
|
|
9483
9490
|
formattedData,
|
|
9484
9491
|
type: tableType,
|
|
9485
9492
|
isNumeric: numeric
|
|
@@ -9836,8 +9843,254 @@ function resolveExtractionStatus(ihsRecord, jobRecords) {
|
|
|
9836
9843
|
);
|
|
9837
9844
|
return { documents, summary };
|
|
9838
9845
|
}
|
|
9846
|
+
|
|
9847
|
+
// src/adapter.ts
|
|
9848
|
+
var AdapterError = class extends Error {
|
|
9849
|
+
constructor(reason, message, cause) {
|
|
9850
|
+
super(message);
|
|
9851
|
+
this.name = "AdapterError";
|
|
9852
|
+
this.reason = reason;
|
|
9853
|
+
this.cause = cause;
|
|
9854
|
+
}
|
|
9855
|
+
};
|
|
9856
|
+
|
|
9857
|
+
// src/adapter-aggregation.ts
|
|
9858
|
+
var ALL_AGGREGATION_OPS = [
|
|
9859
|
+
"sum",
|
|
9860
|
+
"mean",
|
|
9861
|
+
"latest",
|
|
9862
|
+
"max",
|
|
9863
|
+
"count"
|
|
9864
|
+
];
|
|
9865
|
+
function applyAggregation(op, instances) {
|
|
9866
|
+
if (instances.length === 0) {
|
|
9867
|
+
return op === "count" ? 0 : null;
|
|
9868
|
+
}
|
|
9869
|
+
if (op === "count") {
|
|
9870
|
+
return instances.reduce(
|
|
9871
|
+
(acc, i) => i.value !== null && i.value !== void 0 ? acc + 1 : acc,
|
|
9872
|
+
0
|
|
9873
|
+
);
|
|
9874
|
+
}
|
|
9875
|
+
if (op === "latest") {
|
|
9876
|
+
let pick = instances[0];
|
|
9877
|
+
for (let i = 1; i < instances.length; i++) {
|
|
9878
|
+
if (instances[i].observedAt > pick.observedAt) pick = instances[i];
|
|
9879
|
+
}
|
|
9880
|
+
return pick.value;
|
|
9881
|
+
}
|
|
9882
|
+
const nums = [];
|
|
9883
|
+
for (const i of instances) {
|
|
9884
|
+
if (typeof i.value === "number" && Number.isFinite(i.value)) {
|
|
9885
|
+
nums.push(i.value);
|
|
9886
|
+
} else if (i.value !== null && typeof i.value !== "undefined") {
|
|
9887
|
+
throw new Error(
|
|
9888
|
+
`applyAggregation: '${op}' requires numeric values; got ${typeof i.value}`
|
|
9889
|
+
);
|
|
9890
|
+
}
|
|
9891
|
+
}
|
|
9892
|
+
if (nums.length === 0) return null;
|
|
9893
|
+
switch (op) {
|
|
9894
|
+
case "sum":
|
|
9895
|
+
return nums.reduce((a, b) => a + b, 0);
|
|
9896
|
+
case "mean":
|
|
9897
|
+
return nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
9898
|
+
case "max":
|
|
9899
|
+
return nums.reduce((a, b) => a > b ? a : b);
|
|
9900
|
+
}
|
|
9901
|
+
}
|
|
9902
|
+
|
|
9903
|
+
// src/data/adapter-categories.json
|
|
9904
|
+
var adapter_categories_default = {
|
|
9905
|
+
schemaVersion: "1.0.0",
|
|
9906
|
+
categories: [
|
|
9907
|
+
{
|
|
9908
|
+
id: "telco-carrier",
|
|
9909
|
+
displayName: "Telco Carrier",
|
|
9910
|
+
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.",
|
|
9911
|
+
canonicalTable: "ihs_alt_data_telco",
|
|
9912
|
+
fields: [
|
|
9913
|
+
{
|
|
9914
|
+
name: "telcoOnTimePaymentRatio24m",
|
|
9915
|
+
type: "number",
|
|
9916
|
+
unit: "ratio",
|
|
9917
|
+
range: [0, 1],
|
|
9918
|
+
description: "Fraction of bills paid on time over the last 24 months. Strongest single telco predictor; \u22650.95 is the clean-history signal."
|
|
9919
|
+
},
|
|
9920
|
+
{
|
|
9921
|
+
name: "telcoTenureMonths",
|
|
9922
|
+
type: "number",
|
|
9923
|
+
unit: "months",
|
|
9924
|
+
range: [0, 600],
|
|
9925
|
+
description: "Account age. \u226548 months is the thin-file uplift trigger."
|
|
9926
|
+
},
|
|
9927
|
+
{
|
|
9928
|
+
name: "telcoSuspensionsCount24m",
|
|
9929
|
+
type: "number",
|
|
9930
|
+
unit: "count",
|
|
9931
|
+
range: [0, 100],
|
|
9932
|
+
description: "Non-payment-driven account suspensions in the last 24 months. \u22653 is a strong distress signal."
|
|
9933
|
+
},
|
|
9934
|
+
{
|
|
9935
|
+
name: "telcoLateDays24m",
|
|
9936
|
+
type: "number",
|
|
9937
|
+
unit: "days",
|
|
9938
|
+
range: [0, 800],
|
|
9939
|
+
description: "Cumulative days late across all bills in the trailing 24-month window."
|
|
9940
|
+
},
|
|
9941
|
+
{
|
|
9942
|
+
name: "telcoHandsetFinancingActive",
|
|
9943
|
+
type: "boolean",
|
|
9944
|
+
description: "Has an active handset-EMI account currently. Proxy for financing capacity already extended."
|
|
9945
|
+
},
|
|
9946
|
+
{
|
|
9947
|
+
name: "telcoHandsetFinancingDelinquent",
|
|
9948
|
+
type: "boolean",
|
|
9949
|
+
description: "Recent handset-EMI delinquency in the last 24 months. Distress signal even with otherwise clean bill payment."
|
|
9950
|
+
},
|
|
9951
|
+
{
|
|
9952
|
+
name: "telcoArpuMyr",
|
|
9953
|
+
type: "number",
|
|
9954
|
+
unit: "MYR",
|
|
9955
|
+
range: [0, 1e4],
|
|
9956
|
+
description: "Average Revenue Per User (monthly) in Malaysian Ringgit. Coarse spending-capacity proxy."
|
|
9957
|
+
}
|
|
9958
|
+
]
|
|
9959
|
+
},
|
|
9960
|
+
{
|
|
9961
|
+
id: "payment-network",
|
|
9962
|
+
displayName: "Payment Network",
|
|
9963
|
+
description: "Merchant-side payment-flow signals from POS / gateway networks (iPay88, GHL, etc.). Captures actual transaction velocity rather than self-reported revenue.",
|
|
9964
|
+
canonicalTable: "ihs_alt_data_payments",
|
|
9965
|
+
fields: [
|
|
9966
|
+
{
|
|
9967
|
+
name: "paymentsMonthlyVolumeMyrT3",
|
|
9968
|
+
type: "number",
|
|
9969
|
+
unit: "MYR",
|
|
9970
|
+
range: [0, 1e8],
|
|
9971
|
+
description: "Mean monthly inbound transaction volume (RM) over the trailing 3 months."
|
|
9972
|
+
},
|
|
9973
|
+
{
|
|
9974
|
+
name: "paymentsMonthlyVolumeMyrT12",
|
|
9975
|
+
type: "number",
|
|
9976
|
+
unit: "MYR",
|
|
9977
|
+
range: [0, 1e8],
|
|
9978
|
+
description: "Mean monthly inbound transaction volume (RM) over the trailing 12 months. Pair with T3 for trend direction."
|
|
9979
|
+
},
|
|
9980
|
+
{
|
|
9981
|
+
name: "paymentsArpuStability12m",
|
|
9982
|
+
type: "number",
|
|
9983
|
+
unit: "ratio",
|
|
9984
|
+
range: [0, 1],
|
|
9985
|
+
description: "Coefficient-of-variation inverse over monthly ARPU in the trailing 12 months. Closer to 1 = steadier; closer to 0 = volatile."
|
|
9986
|
+
},
|
|
9987
|
+
{
|
|
9988
|
+
name: "paymentsDisputeRate12m",
|
|
9989
|
+
type: "number",
|
|
9990
|
+
unit: "ratio",
|
|
9991
|
+
range: [0, 1],
|
|
9992
|
+
description: "Fraction of transactions disputed or refunded in the trailing 12 months."
|
|
9993
|
+
},
|
|
9994
|
+
{
|
|
9995
|
+
name: "paymentsCustomerConcentrationTop5Pct",
|
|
9996
|
+
type: "number",
|
|
9997
|
+
unit: "ratio",
|
|
9998
|
+
range: [0, 1],
|
|
9999
|
+
description: "Revenue share from the top-5 recurring customers. Above ~0.7 is concentration risk."
|
|
10000
|
+
},
|
|
10001
|
+
{
|
|
10002
|
+
name: "paymentsActiveTenureMonths",
|
|
10003
|
+
type: "number",
|
|
10004
|
+
unit: "months",
|
|
10005
|
+
range: [0, 600],
|
|
10006
|
+
description: "Months since first transaction on the payment network. Establishment / continuity proxy."
|
|
10007
|
+
}
|
|
10008
|
+
]
|
|
10009
|
+
},
|
|
10010
|
+
{
|
|
10011
|
+
id: "bank-statement",
|
|
10012
|
+
displayName: "Bank Statement",
|
|
10013
|
+
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).",
|
|
10014
|
+
canonicalTable: "ihs_alt_data_bank_statements",
|
|
10015
|
+
fields: [
|
|
10016
|
+
{
|
|
10017
|
+
name: "bankStatementMonth",
|
|
10018
|
+
type: "string",
|
|
10019
|
+
description: "Statement period in YYYY-MM format. Used as the instance_key discriminator + by the `latest` aggregation operator."
|
|
10020
|
+
},
|
|
10021
|
+
{
|
|
10022
|
+
name: "bankClosingBalanceMyr",
|
|
10023
|
+
type: "number",
|
|
10024
|
+
unit: "MYR",
|
|
10025
|
+
range: [-1e8, 1e8],
|
|
10026
|
+
description: "Closing balance for the statement period."
|
|
10027
|
+
},
|
|
10028
|
+
{
|
|
10029
|
+
name: "bankTotalCreditsMyr",
|
|
10030
|
+
type: "number",
|
|
10031
|
+
unit: "MYR",
|
|
10032
|
+
range: [0, 1e8],
|
|
10033
|
+
description: "Sum of credit transactions during the period."
|
|
10034
|
+
},
|
|
10035
|
+
{
|
|
10036
|
+
name: "bankTotalDebitsMyr",
|
|
10037
|
+
type: "number",
|
|
10038
|
+
unit: "MYR",
|
|
10039
|
+
range: [0, 1e8],
|
|
10040
|
+
description: "Sum of debit transactions during the period."
|
|
10041
|
+
},
|
|
10042
|
+
{
|
|
10043
|
+
name: "bankLargestSingleCreditMyr",
|
|
10044
|
+
type: "number",
|
|
10045
|
+
unit: "MYR",
|
|
10046
|
+
range: [0, 1e8],
|
|
10047
|
+
description: "Largest single inbound transaction in the period. Useful for spotting one-off injections vs steady revenue."
|
|
10048
|
+
},
|
|
10049
|
+
{
|
|
10050
|
+
name: "bankBouncedTransactionsCount",
|
|
10051
|
+
type: "number",
|
|
10052
|
+
unit: "count",
|
|
10053
|
+
range: [0, 1e3],
|
|
10054
|
+
description: "Bounced / returned transactions in the period. Distress signal at counts > 0."
|
|
10055
|
+
}
|
|
10056
|
+
]
|
|
10057
|
+
}
|
|
10058
|
+
]
|
|
10059
|
+
};
|
|
10060
|
+
|
|
10061
|
+
// src/adapter-categories.ts
|
|
10062
|
+
var data = adapter_categories_default;
|
|
10063
|
+
function categorySchemaOf(id) {
|
|
10064
|
+
const found = data.categories.find((c) => c.id === id);
|
|
10065
|
+
if (!found) {
|
|
10066
|
+
throw new Error(
|
|
10067
|
+
`Unknown adapter category: ${id}. Available: ${data.categories.map((c) => c.id).join(", ")}`
|
|
10068
|
+
);
|
|
10069
|
+
}
|
|
10070
|
+
return found;
|
|
10071
|
+
}
|
|
10072
|
+
function categoryFieldsOf(id) {
|
|
10073
|
+
return categorySchemaOf(id).fields.map((f) => f.name);
|
|
10074
|
+
}
|
|
10075
|
+
function allCategories() {
|
|
10076
|
+
return data.categories;
|
|
10077
|
+
}
|
|
10078
|
+
var fieldToCategory = (() => {
|
|
10079
|
+
const m = /* @__PURE__ */ new Map();
|
|
10080
|
+
for (const cat of data.categories) {
|
|
10081
|
+
for (const f of cat.fields) {
|
|
10082
|
+
m.set(f.name, cat.id);
|
|
10083
|
+
}
|
|
10084
|
+
}
|
|
10085
|
+
return m;
|
|
10086
|
+
})();
|
|
10087
|
+
function categoryForField(field) {
|
|
10088
|
+
return fieldToCategory.get(field) ?? null;
|
|
10089
|
+
}
|
|
9839
10090
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9840
10091
|
0 && (module.exports = {
|
|
10092
|
+
ALL_AGGREGATION_OPS,
|
|
10093
|
+
AdapterError,
|
|
9841
10094
|
BASE_FIELD_SPECS,
|
|
9842
10095
|
BasicFormField,
|
|
9843
10096
|
DEFAULT_VALIDATOR_DEFINITIONS,
|
|
@@ -9858,8 +10111,13 @@ function resolveExtractionStatus(ihsRecord, jobRecords) {
|
|
|
9858
10111
|
IhsStatus,
|
|
9859
10112
|
IhsValueFormat,
|
|
9860
10113
|
Role,
|
|
10114
|
+
allCategories,
|
|
10115
|
+
applyAggregation,
|
|
9861
10116
|
applyDynamicTitles,
|
|
9862
10117
|
buildFileFieldTables,
|
|
10118
|
+
categoryFieldsOf,
|
|
10119
|
+
categoryForField,
|
|
10120
|
+
categorySchemaOf,
|
|
9863
10121
|
evaluateExpression,
|
|
9864
10122
|
extractTimePeriods,
|
|
9865
10123
|
generateRHFSchema,
|