@ondc/automation-mock-runner 1.3.42 → 1.3.44
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/lib/MockRunner.d.ts +12 -2
- package/dist/lib/MockRunner.js +53 -13
- package/dist/lib/configHelper.js +21 -64
- package/dist/lib/constants/function-registry.js +1 -1
- package/dist/lib/helpers/default-helpers.d.ts +13 -0
- package/dist/lib/helpers/default-helpers.js +173 -0
- package/dist/lib/helpers/default-helpers.test.d.ts +9 -0
- package/dist/lib/helpers/default-helpers.test.js +257 -0
- package/dist/lib/helpers/index.d.ts +1 -0
- package/dist/lib/helpers/index.js +47 -0
- package/dist/lib/runners/node-runner.d.ts +15 -6
- package/dist/lib/runners/node-runner.js +4 -0
- package/dist/lib/runners/runner-factory.d.ts +3 -2
- package/dist/lib/validators/code-validator.js +3 -1
- package/dist/test/GenerateFetchAndTimeout.test.d.ts +4 -0
- package/dist/test/GenerateFetchAndTimeout.test.js +240 -0
- package/dist/test/MockRunner.test.js +64 -0
- package/package.json +2 -2
- package/public/node-worker.js +98 -6
package/dist/lib/MockRunner.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseCodeRunner } from "./runners/base-runner";
|
|
2
|
+
import { RunnerOptions } from "./runners/runner-factory";
|
|
2
3
|
import { MockPlaygroundConfigType } from "./types/mock-config";
|
|
3
4
|
import { Logger } from "./utils/logger";
|
|
4
5
|
import { ExecutionResult } from "./types/execution-results";
|
|
@@ -7,6 +8,14 @@ export declare class MockRunner {
|
|
|
7
8
|
private static sharedRunner;
|
|
8
9
|
logger: Logger;
|
|
9
10
|
private static getSharedRunner;
|
|
11
|
+
/**
|
|
12
|
+
* Initialize (or replace) the process-wide shared runner with explicit
|
|
13
|
+
* options. Call this once at service boot — before constructing any
|
|
14
|
+
* MockRunner — to configure the fetch allowlist and worker pool settings.
|
|
15
|
+
*
|
|
16
|
+
* If a shared runner already exists it is terminated and replaced.
|
|
17
|
+
*/
|
|
18
|
+
static initSharedRunner(options?: RunnerOptions): BaseCodeRunner;
|
|
10
19
|
constructor(config: MockPlaygroundConfigType, skipValidation?: boolean);
|
|
11
20
|
getRunnerInstance(): BaseCodeRunner;
|
|
12
21
|
getConfig(): {
|
|
@@ -69,9 +78,9 @@ export declare class MockRunner {
|
|
|
69
78
|
success: boolean;
|
|
70
79
|
errors?: import("zod/v4/core").$ZodIssue[];
|
|
71
80
|
};
|
|
72
|
-
runGeneratePayload(actionId: string, inputs?: any): Promise<ExecutionResult>;
|
|
81
|
+
runGeneratePayload(actionId: string, inputs?: any, extraSessionData?: Record<string, any>): Promise<ExecutionResult>;
|
|
73
82
|
runGeneratePayloadWithSession(actionId: string, sessionData: any): Promise<ExecutionResult>;
|
|
74
|
-
runValidatePayload(actionId: string, targetPayload: any): Promise<ExecutionResult>;
|
|
83
|
+
runValidatePayload(actionId: string, targetPayload: any, extraSessionData?: Record<string, any>): Promise<ExecutionResult>;
|
|
75
84
|
runValidatePayloadWithSession(actionId: string, targetPayload: any, sessionData: any): Promise<ExecutionResult>;
|
|
76
85
|
runMeetRequirements(actionId: string): Promise<ExecutionResult>;
|
|
77
86
|
runMeetRequirementsWithSession(actionId: string, sessionData: any): Promise<ExecutionResult>;
|
|
@@ -81,5 +90,6 @@ export declare class MockRunner {
|
|
|
81
90
|
static runGetSave(payload: any, expression: string): Promise<ExecutionResult>;
|
|
82
91
|
static encodeBase64(input: string): string;
|
|
83
92
|
static decodeBase64(encoded: string): string;
|
|
93
|
+
private static resolveBaseActionId;
|
|
84
94
|
private static getIdFromSession;
|
|
85
95
|
}
|
package/dist/lib/MockRunner.js
CHANGED
|
@@ -18,6 +18,27 @@ class MockRunner {
|
|
|
18
18
|
}
|
|
19
19
|
return MockRunner.sharedRunner;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Initialize (or replace) the process-wide shared runner with explicit
|
|
23
|
+
* options. Call this once at service boot — before constructing any
|
|
24
|
+
* MockRunner — to configure the fetch allowlist and worker pool settings.
|
|
25
|
+
*
|
|
26
|
+
* If a shared runner already exists it is terminated and replaced.
|
|
27
|
+
*/
|
|
28
|
+
static initSharedRunner(options = {}) {
|
|
29
|
+
const logger = logger_1.Logger.getInstance();
|
|
30
|
+
if (MockRunner.sharedRunner) {
|
|
31
|
+
logger.warn("Replacing existing shared runner");
|
|
32
|
+
try {
|
|
33
|
+
MockRunner.sharedRunner.terminate();
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
logger.error("Failed to terminate previous shared runner", {}, e);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
MockRunner.sharedRunner = runner_factory_1.RunnerFactory.createRunner(options, logger);
|
|
40
|
+
return MockRunner.sharedRunner;
|
|
41
|
+
}
|
|
21
42
|
constructor(config, skipValidation = false) {
|
|
22
43
|
this.logger = logger_1.Logger.getInstance();
|
|
23
44
|
if (!skipValidation) {
|
|
@@ -55,23 +76,27 @@ class MockRunner {
|
|
|
55
76
|
const res = (0, validateConfig_1.validateConfigWithErrors)(this.config);
|
|
56
77
|
return res;
|
|
57
78
|
}
|
|
58
|
-
async runGeneratePayload(actionId, inputs = {}) {
|
|
79
|
+
async runGeneratePayload(actionId, inputs = {}, extraSessionData) {
|
|
59
80
|
const executionId = this.logger.createExecutionContext(actionId);
|
|
60
81
|
const startTime = Date.now();
|
|
61
82
|
try {
|
|
83
|
+
const baseActionId = MockRunner.resolveBaseActionId(actionId);
|
|
62
84
|
this.logger.logExecution(executionId, "Starting payload generation", {
|
|
63
85
|
actionId,
|
|
64
86
|
inputKeys: Object.keys(inputs),
|
|
65
87
|
});
|
|
66
|
-
const step = this.config.steps.find((s) => s.action_id ===
|
|
88
|
+
const step = this.config.steps.find((s) => s.action_id === baseActionId);
|
|
67
89
|
if (!step) {
|
|
68
90
|
const availableActions = this.config.steps.map((s) => s.action_id);
|
|
69
91
|
throw new errors_1.ActionNotFoundError(actionId, availableActions);
|
|
70
92
|
}
|
|
71
|
-
const index = this.config.steps.findIndex((s) => s.action_id ===
|
|
93
|
+
const index = this.config.steps.findIndex((s) => s.action_id === baseActionId);
|
|
72
94
|
// Deep clone to avoid mutations
|
|
73
95
|
const defaultPayload = JSON.parse(JSON.stringify(step.mock.defaultPayload));
|
|
74
96
|
const sessionData = await this.getSessionDataUpToStep(index);
|
|
97
|
+
if (extraSessionData) {
|
|
98
|
+
Object.assign(sessionData, extraSessionData);
|
|
99
|
+
}
|
|
75
100
|
// Validate inputs against schema if provided
|
|
76
101
|
if (step.mock.inputs?.jsonSchema && Object.keys(inputs).length > 0) {
|
|
77
102
|
// TODO: Add JSON schema validation for inputs
|
|
@@ -132,16 +157,17 @@ class MockRunner {
|
|
|
132
157
|
const executionId = this.logger.createExecutionContext(actionId);
|
|
133
158
|
const startTime = Date.now();
|
|
134
159
|
try {
|
|
160
|
+
const baseActionId = MockRunner.resolveBaseActionId(actionId);
|
|
135
161
|
this.logger.logExecution(executionId, "Starting payload generation with session data", {
|
|
136
162
|
actionId,
|
|
137
163
|
sessionKeys: Object.keys(sessionData),
|
|
138
164
|
});
|
|
139
|
-
const step = this.config.steps.find((s) => s.action_id ===
|
|
165
|
+
const step = this.config.steps.find((s) => s.action_id === baseActionId);
|
|
140
166
|
if (!step) {
|
|
141
167
|
const availableActions = this.config.steps.map((s) => s.action_id);
|
|
142
168
|
throw new errors_1.ActionNotFoundError(actionId, availableActions);
|
|
143
169
|
}
|
|
144
|
-
const index = this.config.steps.findIndex((s) => s.action_id ===
|
|
170
|
+
const index = this.config.steps.findIndex((s) => s.action_id === baseActionId);
|
|
145
171
|
// Deep clone to avoid mutations
|
|
146
172
|
const defaultPayload = JSON.parse(JSON.stringify(step.mock.defaultPayload));
|
|
147
173
|
const context = this.generateContext(step.action_id, step.api, sessionData);
|
|
@@ -189,15 +215,19 @@ class MockRunner {
|
|
|
189
215
|
};
|
|
190
216
|
}
|
|
191
217
|
}
|
|
192
|
-
async runValidatePayload(actionId, targetPayload) {
|
|
218
|
+
async runValidatePayload(actionId, targetPayload, extraSessionData) {
|
|
193
219
|
try {
|
|
194
|
-
const
|
|
220
|
+
const baseActionId = MockRunner.resolveBaseActionId(actionId);
|
|
221
|
+
const step = this.config.steps.find((s) => s.action_id === baseActionId);
|
|
195
222
|
if (!step) {
|
|
196
223
|
throw new Error(`Action step with ID ${actionId} not found.`);
|
|
197
224
|
}
|
|
198
|
-
const index = this.config.steps.findIndex((s) => s.action_id ===
|
|
225
|
+
const index = this.config.steps.findIndex((s) => s.action_id === baseActionId);
|
|
199
226
|
const schema = (0, function_registry_1.getFunctionSchema)("validate");
|
|
200
227
|
const sessionData = await this.getSessionDataUpToStep(index);
|
|
228
|
+
if (extraSessionData) {
|
|
229
|
+
Object.assign(sessionData, extraSessionData);
|
|
230
|
+
}
|
|
201
231
|
const result = await this.getRunnerInstance().execute(MockRunner.decodeBase64(step.mock.validate), schema, [targetPayload, sessionData]);
|
|
202
232
|
return result;
|
|
203
233
|
}
|
|
@@ -217,7 +247,8 @@ class MockRunner {
|
|
|
217
247
|
}
|
|
218
248
|
async runValidatePayloadWithSession(actionId, targetPayload, sessionData) {
|
|
219
249
|
try {
|
|
220
|
-
const
|
|
250
|
+
const baseActionId = MockRunner.resolveBaseActionId(actionId);
|
|
251
|
+
const step = this.config.steps.find((s) => s.action_id === baseActionId);
|
|
221
252
|
if (!step) {
|
|
222
253
|
throw new Error(`Action step with ID ${actionId} not found.`);
|
|
223
254
|
}
|
|
@@ -241,11 +272,12 @@ class MockRunner {
|
|
|
241
272
|
}
|
|
242
273
|
async runMeetRequirements(actionId) {
|
|
243
274
|
try {
|
|
244
|
-
const
|
|
275
|
+
const baseActionId = MockRunner.resolveBaseActionId(actionId);
|
|
276
|
+
const step = this.config.steps.find((s) => s.action_id === baseActionId);
|
|
245
277
|
if (!step) {
|
|
246
278
|
throw new Error(`Action step with ID ${actionId} not found.`);
|
|
247
279
|
}
|
|
248
|
-
const index = this.config.steps.findIndex((s) => s.action_id ===
|
|
280
|
+
const index = this.config.steps.findIndex((s) => s.action_id === baseActionId);
|
|
249
281
|
const schema = (0, function_registry_1.getFunctionSchema)("meetsRequirements");
|
|
250
282
|
const sessionData = await this.getSessionDataUpToStep(index);
|
|
251
283
|
const result = await this.getRunnerInstance().execute(MockRunner.decodeBase64(step.mock.requirements), schema, [sessionData]);
|
|
@@ -267,7 +299,8 @@ class MockRunner {
|
|
|
267
299
|
}
|
|
268
300
|
async runMeetRequirementsWithSession(actionId, sessionData) {
|
|
269
301
|
try {
|
|
270
|
-
const
|
|
302
|
+
const baseActionId = MockRunner.resolveBaseActionId(actionId);
|
|
303
|
+
const step = this.config.steps.find((s) => s.action_id === baseActionId);
|
|
271
304
|
if (!step) {
|
|
272
305
|
throw new Error(`Action step with ID ${actionId} not found.`);
|
|
273
306
|
}
|
|
@@ -290,6 +323,7 @@ class MockRunner {
|
|
|
290
323
|
}
|
|
291
324
|
}
|
|
292
325
|
getDefaultStep(api, actionId, formType) {
|
|
326
|
+
const baseActionId = MockRunner.resolveBaseActionId(actionId);
|
|
293
327
|
if (formType === "dynamic_form" || formType === "html_form") {
|
|
294
328
|
return {
|
|
295
329
|
api: api,
|
|
@@ -386,7 +420,10 @@ class MockRunner {
|
|
|
386
420
|
}
|
|
387
421
|
generateContext(actionId, action, sessionData) {
|
|
388
422
|
// Find the step configuration for this action
|
|
389
|
-
|
|
423
|
+
// GENERATED#1#on_search_full_page_gcr
|
|
424
|
+
// get the last by splitting on # and taking the last part
|
|
425
|
+
const baseActionId = MockRunner.resolveBaseActionId(actionId);
|
|
426
|
+
const step = this.config.steps.find((s) => s.action_id === baseActionId);
|
|
390
427
|
// Determine the message_id based on responseFor logic
|
|
391
428
|
let messageId = (0, uuid_1.v4)();
|
|
392
429
|
if (step?.responseFor) {
|
|
@@ -587,6 +624,9 @@ class MockRunner {
|
|
|
587
624
|
const bytes = new Uint8Array([...binaryString].map((char) => char.charCodeAt(0)));
|
|
588
625
|
return new TextDecoder().decode(bytes);
|
|
589
626
|
}
|
|
627
|
+
static resolveBaseActionId(actionId) {
|
|
628
|
+
return actionId.split("#").slice(-1)[0];
|
|
629
|
+
}
|
|
590
630
|
static getIdFromSession(sessionData, key) {
|
|
591
631
|
if (sessionData === undefined) {
|
|
592
632
|
return undefined;
|
package/dist/lib/configHelper.js
CHANGED
|
@@ -11,6 +11,7 @@ const MockRunner_1 = require("./MockRunner");
|
|
|
11
11
|
const uuid_1 = require("uuid");
|
|
12
12
|
const terser_1 = require("terser");
|
|
13
13
|
const validateConfig_1 = require("./utils/validateConfig");
|
|
14
|
+
const helpers_1 = require("./helpers");
|
|
14
15
|
function createInitialMockConfig(domain, version, flowId) {
|
|
15
16
|
return {
|
|
16
17
|
meta: {
|
|
@@ -32,65 +33,9 @@ function createInitialMockConfig(domain, version, flowId) {
|
|
|
32
33
|
steps: [],
|
|
33
34
|
transaction_history: [],
|
|
34
35
|
validationLib: "",
|
|
35
|
-
helperLib: MockRunner_1.MockRunner.encodeBase64(
|
|
36
|
+
helperLib: MockRunner_1.MockRunner.encodeBase64(helpers_1.DEFAULT_HELPER_LIB),
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
|
-
const defaultHelpers = `/*
|
|
39
|
-
Custom helper functions available in all mock generation functions.
|
|
40
|
-
these are appended below the generate function for each step.
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
const createFormURL = (domain,formId, sessionData) => {
|
|
44
|
-
const baseURL = sessionData.mockBaseUrl;
|
|
45
|
-
const transactionId = sessionData.transactionId[0];
|
|
46
|
-
const sessionId = sessionData.sessionId;
|
|
47
|
-
return \`\${baseURL}/forms/\${domain}/\${formId}/?transaction_id=\${transactionId}&session_id=\${sessionId}\`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Generates a UUID v4
|
|
51
|
-
function uuidv4() {
|
|
52
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
53
|
-
const r = Math.random() * 16 | 0;
|
|
54
|
-
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
55
|
-
return v.toString(16);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Generate a 6 digit string ID
|
|
60
|
-
function generate6DigitId() {
|
|
61
|
-
return Math.floor(100000 + Math.random() * 900000).toString();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Returns the current ISO timestamp
|
|
65
|
-
function currentTimestamp() {
|
|
66
|
-
return new Date().toISOString();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Converts ISO 8601 duration string to total seconds
|
|
70
|
-
const isoDurToSec = (duration) => {
|
|
71
|
-
const durRE = /P((\d+)Y)?((\d+)M)?((\d+)W)?((\d+)D)?T?((\d+)H)?((\d+)M)?((\d+)S)?/;
|
|
72
|
-
const s = durRE.exec(duration);
|
|
73
|
-
if (!s) return 0;
|
|
74
|
-
|
|
75
|
-
return (Number(s?.[2]) || 0) * 31536000 +
|
|
76
|
-
(Number(s?.[4]) || 0) * 2628288 +
|
|
77
|
-
(Number(s?.[6]) || 0) * 604800 +
|
|
78
|
-
(Number(s?.[8]) || 0) * 86400 +
|
|
79
|
-
(Number(s?.[10]) || 0) * 3600 +
|
|
80
|
-
(Number(s?.[12]) || 0) * 60 +
|
|
81
|
-
(Number(s?.[14]) || 0);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const setCityFromInputs = (payload, inputs) => {
|
|
85
|
-
if (!inputs) return "*";
|
|
86
|
-
let version = payload.context.version || payload.context.core_version || "2.0.0";
|
|
87
|
-
if (version.startsWith("1")) {
|
|
88
|
-
payload.context.city = inputs.city_code ?? "*";
|
|
89
|
-
} else {
|
|
90
|
-
payload.context.location.city.code = inputs.city_code ?? "*";
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
`;
|
|
94
39
|
function convertToFlowConfig(config) {
|
|
95
40
|
const flowConfig = {};
|
|
96
41
|
flowConfig.id = config.meta.flowId;
|
|
@@ -172,13 +117,25 @@ function convertToFlowConfig(config) {
|
|
|
172
117
|
if (step.mock.inputs !== undefined &&
|
|
173
118
|
step.mock.inputs !== null &&
|
|
174
119
|
Object.keys(step.mock.inputs).length > 0) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
120
|
+
if (step.mock.inputs.id == "finvu_verification") {
|
|
121
|
+
flowStep.input = [
|
|
122
|
+
{
|
|
123
|
+
name: "finvu_verification",
|
|
124
|
+
label: "Complete Account Aggregator Verification",
|
|
125
|
+
type: "FINVU_REDIRECT",
|
|
126
|
+
payloadField: "$.context.aa_consent_verified",
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
flowStep.input = [
|
|
132
|
+
{
|
|
133
|
+
name: step.mock.inputs.id,
|
|
134
|
+
type: step.mock.inputs.id,
|
|
135
|
+
schema: step.mock.inputs.jsonSchema,
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
}
|
|
182
139
|
}
|
|
183
140
|
if (step.mock.inputs?.oldInputs) {
|
|
184
141
|
flowStep.input = step.mock.inputs.oldInputs;
|
|
@@ -26,7 +26,7 @@ exports.FUNCTION_REGISTRY = {
|
|
|
26
26
|
description: "The generated payload object to be sent in the API request",
|
|
27
27
|
},
|
|
28
28
|
description: "Generates the mock payload for an API call",
|
|
29
|
-
timeout:
|
|
29
|
+
timeout: 45 * 1000,
|
|
30
30
|
defaultBody: ` return defaultPayload;`,
|
|
31
31
|
template: (body) => `/**
|
|
32
32
|
* Generates the mock payload for an API call in the transaction flow.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function getSubscriberUrl(sessionData: any, type: any): any;
|
|
2
|
+
export function uuidv4(): string;
|
|
3
|
+
export function generate6DigitId(): string;
|
|
4
|
+
export function currentTimestamp(): string;
|
|
5
|
+
export function isoDurToSec(duration: any): number;
|
|
6
|
+
export function setCityFromInputs(payload: any, inputs: any): "*" | undefined;
|
|
7
|
+
export function createFormURL(domain: any, formId: any, sessionData: any): string;
|
|
8
|
+
export function generateConsentHandler(sessionData: any, { custId, templateName, consentDescription, redirectUrl, }: {
|
|
9
|
+
custId: any;
|
|
10
|
+
templateName?: string | undefined;
|
|
11
|
+
consentDescription?: string | undefined;
|
|
12
|
+
redirectUrl?: string | undefined;
|
|
13
|
+
}): Promise<any>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Default helpers available to every `generate()` in a mock step.
|
|
4
|
+
*
|
|
5
|
+
* Authoring rules (enforced by how this file is consumed, not by tooling):
|
|
6
|
+
* 1. Use `function` declarations only. Arrow `const x = () => {}` stringifies
|
|
7
|
+
* as an expression — it won't hoist or concat cleanly when each function
|
|
8
|
+
* is `.toString()`-ed into the helper bundle.
|
|
9
|
+
* 2. No `require` / `import` inside function bodies. The VM sandbox has no
|
|
10
|
+
* module system. Only sandbox-whitelisted globals (Math, Date, JSON, …)
|
|
11
|
+
* and sibling helpers are in scope.
|
|
12
|
+
* 3. Cross-helper calls are fine — every function declaration lands in the
|
|
13
|
+
* same flat script after assembly, and declarations hoist.
|
|
14
|
+
* 4. Put docs INSIDE the function body (first statement). Leading comments
|
|
15
|
+
* above a declaration are dropped by `fn.toString()`, so they never
|
|
16
|
+
* reach the assembled bundle.
|
|
17
|
+
* 5. If the helper needs request-scope data, take `sessionData` as an
|
|
18
|
+
* explicit parameter. Free-variable references to `sessionData` do NOT
|
|
19
|
+
* resolve at runtime — helpers are declared at script scope, `sessionData`
|
|
20
|
+
* is only a parameter of `generate()`.
|
|
21
|
+
*
|
|
22
|
+
* The `module.exports = {...}` line at the bottom is only used by Node
|
|
23
|
+
* (index.ts and the Jest tests). `fn.toString()` never includes it, so it
|
|
24
|
+
* doesn't leak into the sandbox string.
|
|
25
|
+
*/
|
|
26
|
+
function getSubscriberUrl(sessionData, type) {
|
|
27
|
+
// Resolve the BPP or BAP subscriber URL from session data.
|
|
28
|
+
// Usage: getSubscriberUrl(sessionData, "bpp")
|
|
29
|
+
if (type === "bpp") {
|
|
30
|
+
return sessionData.bppUri;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return sessionData.bapUri;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function uuidv4() {
|
|
37
|
+
// Generates a UUID v4 (RFC 4122, random-based).
|
|
38
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
39
|
+
const r = (Math.random() * 16) | 0;
|
|
40
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
41
|
+
return v.toString(16);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function generate6DigitId() {
|
|
45
|
+
// Generate a 6-digit numeric string ID in [100000, 999999].
|
|
46
|
+
return Math.floor(100000 + Math.random() * 900000).toString();
|
|
47
|
+
}
|
|
48
|
+
function currentTimestamp() {
|
|
49
|
+
// Returns the current ISO-8601 UTC timestamp (e.g. "2026-04-23T12:34:56.789Z").
|
|
50
|
+
return new Date().toISOString();
|
|
51
|
+
}
|
|
52
|
+
function isoDurToSec(duration) {
|
|
53
|
+
/*
|
|
54
|
+
* Convert an ISO 8601 duration string (e.g. "PT1H30M", "P2DT3H") to total seconds.
|
|
55
|
+
* Returns 0 for unparseable input.
|
|
56
|
+
* Approximations used: 1 week = 7 days, 1 month ≈ 30.42 days (2628288 sec),
|
|
57
|
+
* 1 year = 365 days. Not calendar-exact.
|
|
58
|
+
*/
|
|
59
|
+
const durRE = /P((\d+)Y)?((\d+)M)?((\d+)W)?((\d+)D)?T?((\d+)H)?((\d+)M)?((\d+)S)?/;
|
|
60
|
+
const s = durRE.exec(duration);
|
|
61
|
+
if (!s)
|
|
62
|
+
return 0;
|
|
63
|
+
return ((Number(s?.[2]) || 0) * 31536000 +
|
|
64
|
+
(Number(s?.[4]) || 0) * 2628288 +
|
|
65
|
+
(Number(s?.[6]) || 0) * 604800 +
|
|
66
|
+
(Number(s?.[8]) || 0) * 86400 +
|
|
67
|
+
(Number(s?.[10]) || 0) * 3600 +
|
|
68
|
+
(Number(s?.[12]) || 0) * 60 +
|
|
69
|
+
(Number(s?.[14]) || 0));
|
|
70
|
+
}
|
|
71
|
+
function setCityFromInputs(payload, inputs) {
|
|
72
|
+
/*
|
|
73
|
+
* Mutates `payload.context` in place to set the city code from `inputs.city_code`.
|
|
74
|
+
* Version-aware: ONDC v1.x uses flat `context.city`, v2.x uses nested
|
|
75
|
+
* `context.location.city.code`. Falls back to "*" when city_code is missing.
|
|
76
|
+
* No-op when `inputs` is falsy.
|
|
77
|
+
*/
|
|
78
|
+
if (!inputs)
|
|
79
|
+
return "*";
|
|
80
|
+
const version = payload.context.version || payload.context.core_version || "2.0.0";
|
|
81
|
+
if (version.startsWith("1")) {
|
|
82
|
+
payload.context.city = inputs.city_code ?? "*";
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
payload.context.location.city.code = inputs.city_code ?? "*";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function createFormURL(domain, formId, sessionData) {
|
|
89
|
+
/*
|
|
90
|
+
* Build a form submission URL from session data.
|
|
91
|
+
* Reads sessionData.mockBaseUrl, sessionData.transactionId[0], sessionData.sessionId.
|
|
92
|
+
* Returns: `${baseURL}/forms/${domain}/${formId}/?transaction_id=...&session_id=...`
|
|
93
|
+
*/
|
|
94
|
+
const baseURL = sessionData.mockBaseUrl;
|
|
95
|
+
const transactionId = sessionData.transactionId[0];
|
|
96
|
+
const sessionId = sessionData.sessionId;
|
|
97
|
+
return `${baseURL}/forms/${domain}/${formId}/?transaction_id=${transactionId}&session_id=${sessionId}`;
|
|
98
|
+
}
|
|
99
|
+
async function generateConsentHandler(sessionData, { custId, templateName = "FINVUDEMO_TESTING", consentDescription = "Gold Loan Account Aggregator Consent", redirectUrl = "https://google.co.in", }) {
|
|
100
|
+
/*
|
|
101
|
+
* Generate a consent handler from the Finvu AA Service.
|
|
102
|
+
* Reads the service base URL from `sessionData.finvuUrl` — the installing
|
|
103
|
+
* service MUST include that origin in
|
|
104
|
+
* MockRunner.initSharedRunner({ allowedFetchBaseUrls: [...] })
|
|
105
|
+
* otherwise the sandboxed fetch will be blocked.
|
|
106
|
+
*
|
|
107
|
+
* Times out after 10s via AbortController.
|
|
108
|
+
*
|
|
109
|
+
* @param {Object} sessionData session data; sessionData.finvuUrl is required
|
|
110
|
+
* @param {Object} params
|
|
111
|
+
* @param {string} params.custId customer ID (required)
|
|
112
|
+
* @param {string} [params.templateName]
|
|
113
|
+
* @param {string} [params.consentDescription]
|
|
114
|
+
* @param {string} [params.redirectUrl]
|
|
115
|
+
* @returns {Promise<string>} consentHandler
|
|
116
|
+
*/
|
|
117
|
+
if (!custId) {
|
|
118
|
+
throw new Error("custId is required");
|
|
119
|
+
}
|
|
120
|
+
const baseUrl = sessionData && sessionData.finvuUrl;
|
|
121
|
+
if (!baseUrl) {
|
|
122
|
+
throw new Error("sessionData.finvuUrl is required");
|
|
123
|
+
}
|
|
124
|
+
const url = `${baseUrl}/finvu-aa/consent/generate`;
|
|
125
|
+
const payload = {
|
|
126
|
+
custId,
|
|
127
|
+
templateName,
|
|
128
|
+
consentDescription,
|
|
129
|
+
redirectUrl,
|
|
130
|
+
};
|
|
131
|
+
console.log("Calling Finvu AA Service:", url);
|
|
132
|
+
console.log("Consent request payload:", payload);
|
|
133
|
+
const controller = new AbortController();
|
|
134
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
135
|
+
try {
|
|
136
|
+
const res = await fetch(url, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: {
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
},
|
|
141
|
+
body: JSON.stringify(payload),
|
|
142
|
+
signal: controller.signal,
|
|
143
|
+
});
|
|
144
|
+
if (!res.ok) {
|
|
145
|
+
const text = await res.text();
|
|
146
|
+
throw new Error(`Request failed: ${res.status} ${text}`);
|
|
147
|
+
}
|
|
148
|
+
const data = await res.json();
|
|
149
|
+
if (!data || !data.consentHandler) {
|
|
150
|
+
throw new Error("Invalid response: consentHandler missing");
|
|
151
|
+
}
|
|
152
|
+
return data.consentHandler;
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
if (err && err.name === "AbortError") {
|
|
156
|
+
throw new Error("Request timed out after 10 seconds");
|
|
157
|
+
}
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
clearTimeout(timeout);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
module.exports = {
|
|
165
|
+
getSubscriberUrl,
|
|
166
|
+
uuidv4,
|
|
167
|
+
generate6DigitId,
|
|
168
|
+
currentTimestamp,
|
|
169
|
+
isoDurToSec,
|
|
170
|
+
setCityFromInputs,
|
|
171
|
+
createFormURL,
|
|
172
|
+
generateConsentHandler,
|
|
173
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the default helper functions + bundle integrity.
|
|
3
|
+
*
|
|
4
|
+
* Unit tests import the functions as a normal CJS module and call them.
|
|
5
|
+
* The bundle test loads DEFAULT_HELPER_LIB into a fresh vm context — this
|
|
6
|
+
* simulates how the string is actually used in the worker sandbox and
|
|
7
|
+
* catches any stringification / hoisting / free-variable regressions.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|