@ondc/automation-mock-runner 0.0.2 โ†’ 0.0.3

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # ONDC Automation Mock Runner
1
+ # @ondc/automation-mock-runner
2
2
 
3
3
  A robust TypeScript library designed for testing and validating ONDC (Open Network for Digital Commerce) transaction flows. This tool helps developers build reliable ONDC integrations by providing a comprehensive framework for generating, validating, and testing API payloads across different transaction scenarios.
4
4
 
@@ -7,11 +7,12 @@ A robust TypeScript library designed for testing and validating ONDC (Open Netwo
7
7
  When building applications that integrate with the ONDC network, you need to handle complex multi-step transaction flows where each API call depends on data from previous steps. This library provides a structured way to:
8
8
 
9
9
  - **Generate realistic test payloads** for ONDC APIs (search, select, init, confirm, etc.)
10
- - **Validate incoming requests** against your business logic
10
+ - **Validate incoming requests** against your business logic with custom validation functions
11
11
  - **Check prerequisites** before proceeding with each transaction step
12
12
  - **Maintain session state** across the entire transaction flow
13
+ - **Execute code securely** using sandboxed Worker Threads with base64-encoded functions
13
14
 
14
- The core concept is simple: define your transaction flow once, then let the runner handle payload generation, validation, and state management automatically.
15
+ The core concept is simple: define your transaction flow with base64-encoded functions, then let the runner handle payload generation, validation, and state management automatically in a secure environment.
15
16
 
16
17
  ## Key Features
17
18
 
@@ -21,23 +22,27 @@ The core concept is simple: define your transaction flow once, then let the runn
21
22
  - Automatic context generation with proper ONDC headers and metadata
22
23
  - Session data persistence across transaction steps
23
24
 
24
- ### ๐Ÿงช Secure Code Execution
25
+ ### ๐Ÿ”’ Secure Code Execution
25
26
 
26
- - Safe execution of custom JavaScript functions for payload generation and validation
27
- - Multiple execution environments: Node.js worker threads, VM sandboxes, or browser workers
27
+ - **Base64-encoded functions** for secure storage and transmission
28
+ - **Sandboxed Worker Threads** with isolated VM contexts
29
+ - **Complete function declarations** required (not just function bodies)
28
30
  - Built-in timeout protection and error isolation
31
+ - Memory limits and resource monitoring
29
32
 
30
33
  ### โœ… Schema Validation
31
34
 
32
- - Zod-based configuration validation
35
+ - Zod-based configuration validation with base64 string validation
33
36
  - Runtime type checking for all inputs and outputs
34
37
  - Detailed error reporting with line-by-line feedback
38
+ - JSON Schema validation for user inputs
35
39
 
36
40
  ### ๐ŸŽฏ ONDC-Specific Features
37
41
 
38
42
  - Built-in support for BAP (Buyer App) and BPP (Seller App) roles
39
43
  - Automatic message ID correlation for request-response pairs
40
44
  - Version-aware context generation (supports ONDC v1.x and v2.x)
45
+ - Domain-specific helper utilities
41
46
 
42
47
  ## Installation
43
48
 
@@ -47,20 +52,26 @@ npm install @ondc/automation-mock-runner
47
52
 
48
53
  ## Quick Start
49
54
 
50
- Here's how to set up a basic ONDC search-to-confirm flow:
55
+ Here's how to set up a basic ONDC search flow with base64-encoded functions:
51
56
 
52
57
  ```typescript
53
58
  import { MockRunner } from "@ondc/automation-mock-runner";
59
+ import { MockPlaygroundConfigType } from "@ondc/automation-mock-runner";
60
+
61
+ // Helper function to encode functions as base64
62
+ function encodeFunction(functionCode: string): string {
63
+ return MockRunner.encodeBase64(functionCode);
64
+ }
54
65
 
55
66
  // Define your transaction configuration
56
- const config = {
67
+ const config: MockPlaygroundConfigType = {
57
68
  meta: {
58
- domain: "retail",
69
+ domain: "ONDC:RET11",
59
70
  version: "1.2.0",
60
71
  flowId: "search-select-init-confirm",
61
72
  },
62
73
  transaction_data: {
63
- transaction_id: "uuid-here",
74
+ transaction_id: "550e8400-e29b-41d4-a716-446655440000",
64
75
  latest_timestamp: new Date().toISOString(),
65
76
  bap_id: "buyer-app.example.com",
66
77
  bap_uri: "https://buyer-app.example.com",
@@ -76,47 +87,75 @@ const config = {
76
87
  unsolicited: false,
77
88
  description: "Search for products in electronics category",
78
89
  mock: {
79
- generate: `
80
- // Add search intent to the payload
81
- defaultPayload.message = {
82
- intent: {
83
- category: { descriptor: { name: "Electronics" } },
84
- location: { country: { code: "IND" }, city: { code: "std:080" } }
90
+ generate: encodeFunction(`
91
+ async function generate(defaultPayload, sessionData) {
92
+ // Add search intent to the payload
93
+ defaultPayload.message = {
94
+ intent: {
95
+ category: { descriptor: { name: "Electronics" } },
96
+ location: { country: { code: "IND" }, city: { code: "std:080" } }
97
+ }
98
+ };
99
+ return defaultPayload;
100
+ }
101
+ `),
102
+ validate: encodeFunction(`
103
+ function validate(targetPayload, sessionData) {
104
+ if (!targetPayload.message?.catalog?.providers?.length) {
105
+ return { valid: false, code: 400, description: "No providers found" };
85
106
  }
86
- };
87
- return defaultPayload;
88
- `,
89
- validate: `
90
- if (!targetPayload.message?.catalog?.providers?.length) {
91
- return { valid: false, code: 400, description: "No providers found" };
107
+ return { valid: true, code: 200, description: "Valid catalog response" };
108
+ }
109
+ `),
110
+ requirements: encodeFunction(`
111
+ function meetsRequirements(sessionData) {
112
+ return { valid: true, code: 200, description: "Ready to search" };
92
113
  }
93
- return { valid: true, code: 200, description: "Valid catalog response" };
94
- `,
95
- requirements: `return { valid: true, code: 200, description: "Ready to search" };`,
114
+ `),
96
115
  defaultPayload: { context: {}, message: {} },
97
116
  saveData: {
98
117
  providers: "$.message.catalog.providers",
99
118
  },
100
- inputs: {},
119
+ inputs: {
120
+ id: "search_inputs",
121
+ jsonSchema: {
122
+ type: "object",
123
+ properties: {
124
+ category: { type: "string", default: "Electronics" },
125
+ },
126
+ },
127
+ },
101
128
  },
102
129
  },
103
130
  ],
104
131
  transaction_history: [],
105
- validationLib: "",
106
- helperLib: "",
132
+ validationLib: encodeFunction(`
133
+ // Shared validation utilities
134
+ function validateONDCContext(context) {
135
+ return context && context.domain && context.action && context.message_id;
136
+ }
137
+ `),
138
+ helperLib: encodeFunction(`
139
+ // Shared helper functions
140
+ function generateMessageId() {
141
+ return crypto.randomUUID();
142
+ }
143
+ `),
107
144
  };
108
145
 
109
146
  // Initialize the runner
110
147
  const runner = new MockRunner(config);
111
148
 
112
149
  // Generate a search payload
113
- const searchResult = await runner.runGeneratePayload("search_001", {});
150
+ const searchResult = await runner.runGeneratePayload("search_001", {
151
+ category: "Electronics",
152
+ });
114
153
  console.log("Generated search payload:", searchResult.result);
115
154
 
116
155
  // Validate a response
117
156
  const validationResult = await runner.runValidatePayload(
118
157
  "search_001",
119
- responsePayload
158
+ responsePayload,
120
159
  );
121
160
  console.log("Validation passed:", validationResult.success);
122
161
  ```
@@ -159,9 +198,9 @@ Each step represents one API call in your transaction flow:
159
198
  unsolicited: boolean, // Whether this is an unsolicited call
160
199
  description: string, // Human-readable description
161
200
  mock: {
162
- generate: string, // JavaScript code to generate payload
163
- validate: string, // JavaScript code to validate response
164
- requirements: string, // JavaScript code to check prerequisites
201
+ generate: string, // Base64-encoded complete function for payload generation
202
+ validate: string, // Base64-encoded complete function for response validation
203
+ requirements: string, // Base64-encoded complete function for prerequisite checks
165
204
  defaultPayload: object, // Base payload structure
166
205
  saveData: object, // JSONPath expressions to save data
167
206
  inputs: object // Input schema for user data
@@ -169,6 +208,96 @@ Each step represents one API call in your transaction flow:
169
208
  }
170
209
  ```
171
210
 
211
+ ## ๐Ÿ”‘ Base64 Function Requirements
212
+
213
+ **IMPORTANT**: All mock functions must be:
214
+
215
+ 1. **Complete function declarations** with proper function names:
216
+ - `generate` functions: `async function generate(defaultPayload, sessionData) { ... }`
217
+ - `validate` functions: `function validate(targetPayload, sessionData) { ... }`
218
+ - `requirements` functions: `function meetsRequirements(sessionData) { ... }`
219
+
220
+ 2. **Base64 encoded** using `MockRunner.encodeBase64()` utility
221
+
222
+ 3. **Properly formatted** with return statements and error handling
223
+
224
+ ### Function Signatures
225
+
226
+ Each function type has a specific signature that must be followed:
227
+
228
+ #### Generate Functions
229
+
230
+ ```typescript
231
+ async function generate(defaultPayload: any, sessionData: any): Promise<any> {
232
+ // Parameters:
233
+ // - defaultPayload: Base payload with context already populated
234
+ // - sessionData: Contains user_inputs and data from previous steps
235
+
236
+ // Must return the complete payload to be sent
237
+ return defaultPayload;
238
+ }
239
+ ```
240
+
241
+ #### Validate Functions
242
+
243
+ ```typescript
244
+ function validate(targetPayload: any, sessionData: any): ValidationResult {
245
+ // Parameters:
246
+ // - targetPayload: The incoming payload to validate
247
+ // - sessionData: Data from previous steps
248
+
249
+ // Must return validation result object
250
+ return {
251
+ valid: true,
252
+ code: 200,
253
+ description: "Validation passed",
254
+ };
255
+ }
256
+ ```
257
+
258
+ #### Requirements Functions
259
+
260
+ ```typescript
261
+ function meetsRequirements(sessionData: any): RequirementResult {
262
+ // Parameters:
263
+ // - sessionData: Data from previous steps
264
+
265
+ // Must return requirement check result
266
+ return {
267
+ valid: true,
268
+ code: 200,
269
+ description: "Requirements met",
270
+ };
271
+ }
272
+ ```
273
+
274
+ ### Example Function Creation:
275
+
276
+ ```typescript
277
+ // Create a complete function
278
+ const generateFunction = `
279
+ async function generate(defaultPayload, sessionData) {
280
+ // Your logic here
281
+ defaultPayload.message = {
282
+ intent: { category: { descriptor: { name: "Electronics" } } }
283
+ };
284
+ return defaultPayload;
285
+ }
286
+ `;
287
+
288
+ // Encode it as base64
289
+ const encodedFunction = MockRunner.encodeBase64(generateFunction);
290
+
291
+ // Use in configuration
292
+ const step = {
293
+ // ...other properties...
294
+ mock: {
295
+ generate: encodedFunction,
296
+ // ...other mock properties...
297
+ },
298
+ };
299
+ ```
300
+
172
301
  ## Advanced Usage
173
302
 
174
303
  ### Chaining Transaction Steps
@@ -211,40 +340,51 @@ const steps = [
211
340
  ### Custom Validation Logic
212
341
 
213
342
  ```typescript
214
- const validatePayload = `
215
- // Check if order total matches expected amount
216
- const expectedTotal = sessionData.calculatedTotal;
217
- const actualTotal = targetPayload.message.order.quote.total;
218
-
219
- if (Math.abs(expectedTotal - actualTotal) > 0.01) {
220
- return {
221
- valid: false,
222
- code: 400,
223
- description: \`Total mismatch: expected \${expectedTotal}, got \${actualTotal}\`
224
- };
343
+ // Create complete validation function
344
+ const validateFunction = `
345
+ function validate(targetPayload, sessionData) {
346
+ // Check if order total matches expected amount
347
+ const expectedTotal = sessionData.calculatedTotal;
348
+ const actualTotal = targetPayload.message.order.quote.total;
349
+
350
+ if (Math.abs(expectedTotal - actualTotal) > 0.01) {
351
+ return {
352
+ valid: false,
353
+ code: 400,
354
+ description: \`Total mismatch: expected \${expectedTotal}, got \${actualTotal}\`
355
+ };
356
+ }
357
+
358
+ return { valid: true, code: 200, description: "Order total validated" };
225
359
  }
226
-
227
- return { valid: true, code: 200, description: "Order total validated" };
228
360
  `;
361
+
362
+ // Encode for use in configuration
363
+ const encodedValidateFunction = MockRunner.encodeBase64(validateFunction);
229
364
  ```
230
365
 
231
366
  ### User Input Handling
232
367
 
233
368
  ```typescript
369
+ // Create function that uses user inputs
370
+ const generateWithInputs = `
371
+ async function generate(defaultPayload, sessionData) {
372
+ // Access user inputs
373
+ const { email, deliveryAddress } = sessionData.user_inputs;
374
+
375
+ defaultPayload.message.order.billing = {
376
+ email: email,
377
+ address: deliveryAddress
378
+ };
379
+
380
+ return defaultPayload;
381
+ }
382
+ `;
383
+
234
384
  const stepWithInputs = {
235
385
  // ... other config
236
386
  mock: {
237
- generate: `
238
- // Access user inputs
239
- const { email, deliveryAddress } = sessionData.user_inputs;
240
-
241
- defaultPayload.message.order.billing = {
242
- email: email,
243
- address: deliveryAddress
244
- };
245
-
246
- return defaultPayload;
247
- `,
387
+ generate: MockRunner.encodeBase64(generateWithInputs),
248
388
  inputs: {
249
389
  id: "user_details",
250
390
  jsonSchema: {
@@ -297,7 +437,7 @@ Validates an incoming payload against the specified action step.
297
437
  ```typescript
298
438
  const result = await runner.runValidatePayload(
299
439
  "on_search_001",
300
- responsePayload
440
+ responsePayload,
301
441
  );
302
442
  ```
303
443
 
@@ -309,10 +449,30 @@ const result = await runner.runMeetRequirements("select_001", {});
309
449
  ```
310
450
 
311
451
  **`getDefaultStep(api: string, actionId: string)`**
312
- Creates a new step configuration with sensible defaults.
452
+ Creates a new step configuration with sensible defaults and base64-encoded template functions.
313
453
 
314
454
  ```typescript
315
455
  const newStep = runner.getDefaultStep("search", "search_002");
456
+ // Returns a step with properly encoded template functions
457
+ ```
458
+
459
+ **`MockRunner.encodeBase64(functionString: string)`**
460
+ Static utility to encode functions as base64.
461
+
462
+ ```typescript
463
+ const encodedFunction = MockRunner.encodeBase64(`
464
+ async function generate(defaultPayload, sessionData) {
465
+ // Your function logic here
466
+ return defaultPayload;
467
+ }
468
+ `);
469
+ ```
470
+
471
+ **`MockRunner.decodeBase64(encodedString: string)`**
472
+ Static utility to decode base64-encoded functions (used internally).
473
+
474
+ ```typescript
475
+ const decodedFunction = MockRunner.decodeBase64(encodedFunction);
316
476
  ```
317
477
 
318
478
  ## Error Handling
@@ -349,10 +509,13 @@ npm run test:coverage # Generate coverage report
349
509
 
350
510
  ## Security Considerations
351
511
 
352
- - All user code runs in isolated environments (worker threads or VM sandboxes)
353
- - Dangerous functions like `eval`, `require`, and file system access are blocked
354
- - Execution timeouts prevent infinite loops
355
- - Memory limits prevent resource exhaustion
512
+ - **Base64 encoding** prevents code injection through configuration
513
+ - **Complete function declarations** required - no arbitrary code execution
514
+ - **Sandboxed Worker Threads** with isolated VM contexts
515
+ - **Restricted global access** - dangerous functions like `eval`, `require`, and file system access are blocked
516
+ - **Execution timeouts** prevent infinite loops and hanging processes
517
+ - **Memory limits** prevent resource exhaustion
518
+ - **Input validation** using Zod schemas for all configuration data
356
519
 
357
520
  ## Contributing
358
521
 
@@ -15,4 +15,6 @@ export declare class MockRunner {
15
15
  getDefaultStep(api: string, actionId: string): MockPlaygroundConfigType["steps"][0];
16
16
  generateContext(actionId: string, action: string): any;
17
17
  getSessionDataUpToStep(index: number, config: MockPlaygroundConfigType): Record<string, any>;
18
+ static encodeBase64(input: string): string;
19
+ static decodeBase64(encoded: string): string;
18
20
  }
@@ -65,10 +65,7 @@ class MockRunner {
65
65
  const context = this.generateContext(step.action_id, step.api);
66
66
  defaultPayload.context = context;
67
67
  const schema = (0, function_registry_1.getFunctionSchema)("generate");
68
- const result = await this.runner.execute(step.mock.generate, schema, [
69
- defaultPayload,
70
- sessionData,
71
- ]);
68
+ const result = await this.runner.execute(MockRunner.decodeBase64(step.mock.generate), schema, [defaultPayload, sessionData]);
72
69
  const executionTime = Date.now() - startTime;
73
70
  this.logger.logExecution(executionId, "Payload generation completed", {
74
71
  actionId,
@@ -104,10 +101,7 @@ class MockRunner {
104
101
  const index = this.config.steps.findIndex((s) => s.action_id === actionId);
105
102
  const schema = (0, function_registry_1.getFunctionSchema)("validate");
106
103
  const sessionData = this.getSessionDataUpToStep(index, this.config);
107
- const result = await this.runner.execute(step.mock.validate, schema, [
108
- targetPayload,
109
- sessionData,
110
- ]);
104
+ const result = await this.runner.execute(MockRunner.decodeBase64(step.mock.validate), schema, [targetPayload, sessionData]);
111
105
  return result;
112
106
  }
113
107
  catch (error) {
@@ -133,10 +127,7 @@ class MockRunner {
133
127
  const index = this.config.steps.findIndex((s) => s.action_id === actionId);
134
128
  const schema = (0, function_registry_1.getFunctionSchema)("meetsRequirements");
135
129
  const sessionData = this.getSessionDataUpToStep(index, this.config);
136
- const result = await this.runner.execute(step.mock.requirements, schema, [
137
- targetPayload,
138
- sessionData,
139
- ]);
130
+ const result = await this.runner.execute(MockRunner.decodeBase64(step.mock.requirements), schema, [targetPayload, sessionData]);
140
131
  return result;
141
132
  }
142
133
  catch (error) {
@@ -162,9 +153,9 @@ class MockRunner {
162
153
  unsolicited: false,
163
154
  description: "please add relevant description",
164
155
  mock: {
165
- generate: (0, function_registry_1.getFunctionSchema)("generate").template((0, function_registry_1.getFunctionSchema)("generate").defaultBody),
166
- validate: (0, function_registry_1.getFunctionSchema)("validate").template((0, function_registry_1.getFunctionSchema)("validate").defaultBody),
167
- requirements: (0, function_registry_1.getFunctionSchema)("meetsRequirements").template((0, function_registry_1.getFunctionSchema)("meetsRequirements").defaultBody),
156
+ generate: MockRunner.encodeBase64((0, function_registry_1.getFunctionSchema)("generate").template((0, function_registry_1.getFunctionSchema)("generate").defaultBody)),
157
+ validate: MockRunner.encodeBase64((0, function_registry_1.getFunctionSchema)("validate").template((0, function_registry_1.getFunctionSchema)("validate").defaultBody)),
158
+ requirements: MockRunner.encodeBase64((0, function_registry_1.getFunctionSchema)("meetsRequirements").template((0, function_registry_1.getFunctionSchema)("meetsRequirements").defaultBody)),
168
159
  defaultPayload: {
169
160
  context: this.generateContext(actionId, api),
170
161
  message: {},
@@ -331,5 +322,14 @@ class MockRunner {
331
322
  });
332
323
  return sessionData;
333
324
  }
325
+ static encodeBase64(input) {
326
+ const bytes = new TextEncoder().encode(input);
327
+ return btoa(String.fromCharCode(...bytes));
328
+ }
329
+ static decodeBase64(encoded) {
330
+ const binaryString = atob(encoded);
331
+ const bytes = new Uint8Array([...binaryString].map((char) => char.charCodeAt(0)));
332
+ return new TextDecoder().decode(bytes);
333
+ }
334
334
  }
335
335
  exports.MockRunner = MockRunner;
@@ -17,9 +17,9 @@ export declare const TransactionDataSchema: z.ZodObject<{
17
17
  bpp_uri: z.ZodOptional<z.ZodString>;
18
18
  }, z.core.$strip>;
19
19
  export declare const MockConfigSchema: z.ZodObject<{
20
- generate: z.ZodString;
21
- validate: z.ZodString;
22
- requirements: z.ZodString;
20
+ generate: z.ZodBase64;
21
+ validate: z.ZodBase64;
22
+ requirements: z.ZodBase64;
23
23
  defaultPayload: z.ZodAny;
24
24
  saveData: z.ZodRecord<z.ZodString, z.ZodString>;
25
25
  inputs: z.ZodObject<{
@@ -38,9 +38,9 @@ export declare const PlaygroundActionStepSchema: z.ZodObject<{
38
38
  unsolicited: z.ZodBoolean;
39
39
  description: z.ZodString;
40
40
  mock: z.ZodObject<{
41
- generate: z.ZodString;
42
- validate: z.ZodString;
43
- requirements: z.ZodString;
41
+ generate: z.ZodBase64;
42
+ validate: z.ZodBase64;
43
+ requirements: z.ZodBase64;
44
44
  defaultPayload: z.ZodAny;
45
45
  saveData: z.ZodRecord<z.ZodString, z.ZodString>;
46
46
  inputs: z.ZodObject<{
@@ -79,9 +79,9 @@ export declare const MockPlaygroundConfigSchema: z.ZodObject<{
79
79
  unsolicited: z.ZodBoolean;
80
80
  description: z.ZodString;
81
81
  mock: z.ZodObject<{
82
- generate: z.ZodString;
83
- validate: z.ZodString;
84
- requirements: z.ZodString;
82
+ generate: z.ZodBase64;
83
+ validate: z.ZodBase64;
84
+ requirements: z.ZodBase64;
85
85
  defaultPayload: z.ZodAny;
86
86
  saveData: z.ZodRecord<z.ZodString, z.ZodString>;
87
87
  inputs: z.ZodObject<{
@@ -18,9 +18,9 @@ exports.TransactionDataSchema = zod_1.z.object({
18
18
  bpp_uri: zod_1.z.string().min(1, "BPP URI is required").optional(),
19
19
  });
20
20
  exports.MockConfigSchema = zod_1.z.object({
21
- generate: zod_1.z.string(),
22
- validate: zod_1.z.string(),
23
- requirements: zod_1.z.string(),
21
+ generate: zod_1.z.base64(),
22
+ validate: zod_1.z.base64(),
23
+ requirements: zod_1.z.base64(),
24
24
  defaultPayload: zod_1.z.any(),
25
25
  saveData: zod_1.z.record(zod_1.z.string(), zod_1.z.string()),
26
26
  inputs: zod_1.z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ondc/automation-mock-runner",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "A TypeScript library for ONDC automation mock runner",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -75,6 +75,7 @@
75
75
  "dependencies": {
76
76
  "acorn": "^8.15.0",
77
77
  "acorn-walk": "^8.3.4",
78
+ "base-64": "^1.0.0",
78
79
  "jsonpath": "^1.1.1",
79
80
  "uuid": "^10.0.0",
80
81
  "zod": "^4.1.12"