@schmock/faker 1.9.1 → 1.9.2
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.d.ts.map +1 -1
- package/dist/index.js +52 -52
- package/dist/jsf-config.d.ts.map +1 -1
- package/dist/jsf-config.js +3 -0
- package/dist/overrides.d.ts +1 -1
- package/dist/overrides.d.ts.map +1 -1
- package/dist/overrides.js +9 -2
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +10 -11
- package/package.json +3 -3
- package/src/index.ts +1 -2
- package/src/jsf-config.ts +2 -0
- package/src/overrides.ts +7 -1
- package/src/validation.ts +13 -14
package/dist/jsf-config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsf-config.d.ts","sourceRoot":"","sources":["../src/jsf-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,GAAG,MAAM,mBAAmB,CAAC;AAEpC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,MAAM,SAMhD;AAoBD,wBAAgB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"jsf-config.d.ts","sourceRoot":"","sources":["../src/jsf-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,GAAG,MAAM,mBAAmB,CAAC;AAEpC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,MAAM,SAMhD;AAoBD,wBAAgB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,CAuBhD"}
|
package/dist/jsf-config.js
CHANGED
package/dist/overrides.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { JSONSchema7 } from "json-schema";
|
|
|
6
6
|
* @param explicitCount - Explicit count override from plugin options
|
|
7
7
|
* @returns Number of array items to generate
|
|
8
8
|
*/
|
|
9
|
-
export declare function determineArrayCount(schema: JSONSchema7, explicitCount?: number): number;
|
|
9
|
+
export declare function determineArrayCount(schema: JSONSchema7, explicitCount?: number, random?: () => number): number;
|
|
10
10
|
/**
|
|
11
11
|
* Apply overrides to generated data with support for templates
|
|
12
12
|
* Supports nested paths (dot notation), templates with {{params.id}}, and state access
|
package/dist/overrides.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../src/overrides.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../src/overrides.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,EACnB,aAAa,CAAC,EAAE,MAAM,EACtB,MAAM,GAAE,MAAM,MAAoB,GACjC,MAAM,CAyBR;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,GAAG,EACT,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,KAAK,CAAC,EAAE,GAAG,EACX,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,GAAG,CAuCL"}
|
package/dist/overrides.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_ARRAY_COUNT } from "./constants.js";
|
|
2
|
+
const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
2
3
|
/**
|
|
3
4
|
* Determine number of items to generate for array schema
|
|
4
5
|
* Prefers explicit count, then schema minItems/maxItems, with sane defaults
|
|
@@ -6,7 +7,7 @@ import { DEFAULT_ARRAY_COUNT } from "./constants.js";
|
|
|
6
7
|
* @param explicitCount - Explicit count override from plugin options
|
|
7
8
|
* @returns Number of array items to generate
|
|
8
9
|
*/
|
|
9
|
-
export function determineArrayCount(schema, explicitCount) {
|
|
10
|
+
export function determineArrayCount(schema, explicitCount, random = Math.random) {
|
|
10
11
|
if (explicitCount !== undefined) {
|
|
11
12
|
// Handle negative or invalid counts
|
|
12
13
|
if (explicitCount < 0) {
|
|
@@ -15,7 +16,7 @@ export function determineArrayCount(schema, explicitCount) {
|
|
|
15
16
|
return explicitCount;
|
|
16
17
|
}
|
|
17
18
|
if (schema.minItems !== undefined && schema.maxItems !== undefined) {
|
|
18
|
-
return (Math.floor(
|
|
19
|
+
return (Math.floor(random() * (schema.maxItems - schema.minItems + 1)) +
|
|
19
20
|
schema.minItems);
|
|
20
21
|
}
|
|
21
22
|
if (schema.minItems !== undefined) {
|
|
@@ -41,6 +42,8 @@ export function applyOverrides(data, overrides, params, state, query) {
|
|
|
41
42
|
return data;
|
|
42
43
|
const result = structuredClone(data);
|
|
43
44
|
for (const [key, value] of Object.entries(overrides)) {
|
|
45
|
+
if (DANGEROUS_KEYS.has(key))
|
|
46
|
+
continue;
|
|
44
47
|
// Handle nested paths like "data.id" or "pagination.page"
|
|
45
48
|
if (key.includes(".")) {
|
|
46
49
|
setNestedProperty(result, key, value, { params, state, query });
|
|
@@ -75,6 +78,8 @@ function setNestedProperty(obj, path, value, context) {
|
|
|
75
78
|
// Navigate to the parent of the target property
|
|
76
79
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
77
80
|
const part = parts[i];
|
|
81
|
+
if (DANGEROUS_KEYS.has(part))
|
|
82
|
+
return;
|
|
78
83
|
if (!(part in current) ||
|
|
79
84
|
typeof current[part] !== "object" ||
|
|
80
85
|
current[part] === null) {
|
|
@@ -84,6 +89,8 @@ function setNestedProperty(obj, path, value, context) {
|
|
|
84
89
|
}
|
|
85
90
|
// Set the final property
|
|
86
91
|
const finalKey = parts[parts.length - 1];
|
|
92
|
+
if (DANGEROUS_KEYS.has(finalKey))
|
|
93
|
+
return;
|
|
87
94
|
if (typeof value === "string" && value.includes("{{")) {
|
|
88
95
|
current[finalKey] = processTemplate(value, context);
|
|
89
96
|
}
|
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAY/C,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,SAAM,GAAG,IAAI,CAiJpE;AAwMD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAmC7D"}
|
package/dist/validation.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ResourceLimitError, SchemaValidationError } from "@schmock/core";
|
|
2
2
|
import { DEEP_NESTING_THRESHOLD, DEFAULT_ARRAY_COUNT, LARGE_ARRAY_THRESHOLD, MAX_ARRAY_SIZE, MAX_NESTING_DEPTH, } from "./constants.js";
|
|
3
3
|
import { createFakerInstance } from "./jsf-config.js";
|
|
4
|
+
let validationFaker;
|
|
4
5
|
export function isJSONSchema7(value) {
|
|
5
6
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
7
|
}
|
|
@@ -103,7 +104,7 @@ export function validateSchema(schema, path = "$") {
|
|
|
103
104
|
throw new ResourceLimitError("schema_nesting_depth", MAX_NESTING_DEPTH, depth);
|
|
104
105
|
}
|
|
105
106
|
// Check for dangerous combination of deep nesting + large arrays
|
|
106
|
-
if (depth >=
|
|
107
|
+
if (depth >= DEEP_NESTING_THRESHOLD) {
|
|
107
108
|
checkForDeepNestingWithArrays(schema, path);
|
|
108
109
|
}
|
|
109
110
|
// Check for potentially dangerous array sizes in schema definition
|
|
@@ -211,24 +212,19 @@ function checkForDeepNestingWithArrays(schema, _path) {
|
|
|
211
212
|
const items = Array.isArray(node.items) ? node.items : [node.items];
|
|
212
213
|
for (const item of items) {
|
|
213
214
|
if (isJSONSchema7(item)) {
|
|
214
|
-
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
215
|
+
findArraysInDeepNesting(item, currentDepth + 1);
|
|
217
216
|
}
|
|
218
217
|
}
|
|
219
218
|
}
|
|
220
|
-
return
|
|
219
|
+
return;
|
|
221
220
|
}
|
|
222
221
|
if (schemaType === "object" && node.properties) {
|
|
223
222
|
for (const prop of Object.values(node.properties)) {
|
|
224
223
|
if (isJSONSchema7(prop)) {
|
|
225
|
-
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
224
|
+
findArraysInDeepNesting(prop, currentDepth + 1);
|
|
228
225
|
}
|
|
229
226
|
}
|
|
230
227
|
}
|
|
231
|
-
return false;
|
|
232
228
|
}
|
|
233
229
|
findArraysInDeepNesting(schema, 0);
|
|
234
230
|
}
|
|
@@ -281,8 +277,11 @@ export function validateFakerMethod(fakerMethod) {
|
|
|
281
277
|
if (parts.length < 2) {
|
|
282
278
|
throw new SchemaValidationError("$.faker", `Invalid faker method format: "${fakerMethod}"`, "Use format like 'person.firstName' or 'internet.email'");
|
|
283
279
|
}
|
|
284
|
-
// Validate by resolving the method path on a
|
|
285
|
-
|
|
280
|
+
// Validate by resolving the method path on a cached faker instance
|
|
281
|
+
if (!validationFaker) {
|
|
282
|
+
validationFaker = createFakerInstance();
|
|
283
|
+
}
|
|
284
|
+
const faker = validationFaker;
|
|
286
285
|
let current = faker;
|
|
287
286
|
for (const part of parts) {
|
|
288
287
|
if (current && typeof current === "object" && part in current) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schmock/faker",
|
|
3
3
|
"description": "Faker-powered automatic data generation for Schmock",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"json-schema-faker": "^0.5.6"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@schmock/core": "^1.9.
|
|
37
|
+
"@schmock/core": "^1.9.2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@amiceli/vitest-cucumber": "^6.2.0",
|
|
41
41
|
"@types/json-schema": "^7.0.15",
|
|
42
|
-
"@types/node": "^25.
|
|
42
|
+
"@types/node": "^25.3.0",
|
|
43
43
|
"vitest": "^4.0.18"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface FakerPluginOptions {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function fakerPlugin(options: FakerPluginOptions): Schmock.Plugin {
|
|
33
|
-
// Validate schema immediately when plugin is created
|
|
33
|
+
// Validate schema immediately when plugin is created (fail-fast)
|
|
34
34
|
validateSchema(options.schema);
|
|
35
35
|
|
|
36
36
|
return {
|
|
@@ -81,7 +81,6 @@ export function fakerPlugin(options: FakerPluginOptions): Schmock.Plugin {
|
|
|
81
81
|
export function generateFromSchema(options: SchemaGenerationContext): any {
|
|
82
82
|
const { schema, count, overrides, params, state, query, seed } = options;
|
|
83
83
|
|
|
84
|
-
// Validate schema
|
|
85
84
|
validateSchema(schema);
|
|
86
85
|
|
|
87
86
|
let generated: any;
|
package/src/jsf-config.ts
CHANGED
|
@@ -51,6 +51,8 @@ export function getJsf(seed?: number): typeof jsf {
|
|
|
51
51
|
if (seed !== undefined) {
|
|
52
52
|
jsf.option({ random: createSeededRandom(seed) });
|
|
53
53
|
jsf.extend("faker", () => createFakerInstance(seed));
|
|
54
|
+
} else {
|
|
55
|
+
jsf.option({ random: Math.random });
|
|
54
56
|
}
|
|
55
57
|
return jsf;
|
|
56
58
|
}
|
package/src/overrides.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { JSONSchema7 } from "json-schema";
|
|
2
2
|
import { DEFAULT_ARRAY_COUNT } from "./constants.js";
|
|
3
3
|
|
|
4
|
+
const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Determine number of items to generate for array schema
|
|
6
8
|
* Prefers explicit count, then schema minItems/maxItems, with sane defaults
|
|
@@ -11,6 +13,7 @@ import { DEFAULT_ARRAY_COUNT } from "./constants.js";
|
|
|
11
13
|
export function determineArrayCount(
|
|
12
14
|
schema: JSONSchema7,
|
|
13
15
|
explicitCount?: number,
|
|
16
|
+
random: () => number = Math.random,
|
|
14
17
|
): number {
|
|
15
18
|
if (explicitCount !== undefined) {
|
|
16
19
|
// Handle negative or invalid counts
|
|
@@ -22,7 +25,7 @@ export function determineArrayCount(
|
|
|
22
25
|
|
|
23
26
|
if (schema.minItems !== undefined && schema.maxItems !== undefined) {
|
|
24
27
|
return (
|
|
25
|
-
Math.floor(
|
|
28
|
+
Math.floor(random() * (schema.maxItems - schema.minItems + 1)) +
|
|
26
29
|
schema.minItems
|
|
27
30
|
);
|
|
28
31
|
}
|
|
@@ -60,6 +63,7 @@ export function applyOverrides(
|
|
|
60
63
|
const result = structuredClone(data);
|
|
61
64
|
|
|
62
65
|
for (const [key, value] of Object.entries(overrides)) {
|
|
66
|
+
if (DANGEROUS_KEYS.has(key)) continue;
|
|
63
67
|
// Handle nested paths like "data.id" or "pagination.page"
|
|
64
68
|
if (key.includes(".")) {
|
|
65
69
|
setNestedProperty(result, key, value, { params, state, query });
|
|
@@ -110,6 +114,7 @@ function setNestedProperty(
|
|
|
110
114
|
// Navigate to the parent of the target property
|
|
111
115
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
112
116
|
const part = parts[i];
|
|
117
|
+
if (DANGEROUS_KEYS.has(part)) return;
|
|
113
118
|
if (
|
|
114
119
|
!(part in current) ||
|
|
115
120
|
typeof current[part] !== "object" ||
|
|
@@ -122,6 +127,7 @@ function setNestedProperty(
|
|
|
122
127
|
|
|
123
128
|
// Set the final property
|
|
124
129
|
const finalKey = parts[parts.length - 1];
|
|
130
|
+
if (DANGEROUS_KEYS.has(finalKey)) return;
|
|
125
131
|
if (typeof value === "string" && value.includes("{{")) {
|
|
126
132
|
current[finalKey] = processTemplate(value, context);
|
|
127
133
|
} else {
|
package/src/validation.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Faker } from "@faker-js/faker";
|
|
1
2
|
import { ResourceLimitError, SchemaValidationError } from "@schmock/core";
|
|
2
3
|
import type { JSONSchema7 } from "json-schema";
|
|
3
4
|
import {
|
|
@@ -9,6 +10,8 @@ import {
|
|
|
9
10
|
} from "./constants.js";
|
|
10
11
|
import { createFakerInstance } from "./jsf-config.js";
|
|
11
12
|
|
|
13
|
+
let validationFaker: Faker | undefined;
|
|
14
|
+
|
|
12
15
|
export function isJSONSchema7(value: unknown): value is JSONSchema7 {
|
|
13
16
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
17
|
}
|
|
@@ -153,7 +156,7 @@ export function validateSchema(schema: JSONSchema7, path = "$"): void {
|
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
// Check for dangerous combination of deep nesting + large arrays
|
|
156
|
-
if (depth >=
|
|
159
|
+
if (depth >= DEEP_NESTING_THRESHOLD) {
|
|
157
160
|
checkForDeepNestingWithArrays(schema, path);
|
|
158
161
|
}
|
|
159
162
|
|
|
@@ -271,7 +274,7 @@ function checkForDeepNestingWithArrays(
|
|
|
271
274
|
function findArraysInDeepNesting(
|
|
272
275
|
node: JSONSchema7,
|
|
273
276
|
currentDepth: number,
|
|
274
|
-
):
|
|
277
|
+
): void {
|
|
275
278
|
const schemaType = node.type;
|
|
276
279
|
const isArray = Array.isArray(schemaType)
|
|
277
280
|
? schemaType.includes("array")
|
|
@@ -296,27 +299,20 @@ function checkForDeepNestingWithArrays(
|
|
|
296
299
|
const items = Array.isArray(node.items) ? node.items : [node.items];
|
|
297
300
|
for (const item of items) {
|
|
298
301
|
if (isJSONSchema7(item)) {
|
|
299
|
-
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
+
findArraysInDeepNesting(item, currentDepth + 1);
|
|
302
303
|
}
|
|
303
304
|
}
|
|
304
305
|
}
|
|
305
|
-
|
|
306
|
-
return true;
|
|
306
|
+
return;
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
if (schemaType === "object" && node.properties) {
|
|
310
310
|
for (const prop of Object.values(node.properties)) {
|
|
311
311
|
if (isJSONSchema7(prop)) {
|
|
312
|
-
|
|
313
|
-
return true;
|
|
314
|
-
}
|
|
312
|
+
findArraysInDeepNesting(prop, currentDepth + 1);
|
|
315
313
|
}
|
|
316
314
|
}
|
|
317
315
|
}
|
|
318
|
-
|
|
319
|
-
return false;
|
|
320
316
|
}
|
|
321
317
|
|
|
322
318
|
findArraysInDeepNesting(schema, 0);
|
|
@@ -391,8 +387,11 @@ export function validateFakerMethod(fakerMethod: string): void {
|
|
|
391
387
|
);
|
|
392
388
|
}
|
|
393
389
|
|
|
394
|
-
// Validate by resolving the method path on a
|
|
395
|
-
|
|
390
|
+
// Validate by resolving the method path on a cached faker instance
|
|
391
|
+
if (!validationFaker) {
|
|
392
|
+
validationFaker = createFakerInstance();
|
|
393
|
+
}
|
|
394
|
+
const faker = validationFaker;
|
|
396
395
|
let current: any = faker;
|
|
397
396
|
for (const part of parts) {
|
|
398
397
|
if (current && typeof current === "object" && part in current) {
|