@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.
@@ -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,CAqBhD"}
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"}
@@ -48,5 +48,8 @@ export function getJsf(seed) {
48
48
  jsf.option({ random: createSeededRandom(seed) });
49
49
  jsf.extend("faker", () => createFakerInstance(seed));
50
50
  }
51
+ else {
52
+ jsf.option({ random: Math.random });
53
+ }
51
54
  return jsf;
52
55
  }
@@ -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
@@ -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;AAG/C;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,EACnB,aAAa,CAAC,EAAE,MAAM,GACrB,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,CAsCL"}
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(Math.random() * (schema.maxItems - schema.minItems + 1)) +
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAU/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;AA+MD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAgC7D"}
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"}
@@ -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 >= 4) {
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
- if (findArraysInDeepNesting(item, currentDepth + 1)) {
215
- return true;
216
- }
215
+ findArraysInDeepNesting(item, currentDepth + 1);
217
216
  }
218
217
  }
219
218
  }
220
- return true;
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
- if (findArraysInDeepNesting(prop, currentDepth + 1)) {
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 real faker instance
285
- const faker = createFakerInstance();
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.1",
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.1"
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.2.3",
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(Math.random() * (schema.maxItems - schema.minItems + 1)) +
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 >= 4) {
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
- ): boolean {
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
- if (findArraysInDeepNesting(item, currentDepth + 1)) {
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
- if (findArraysInDeepNesting(prop, currentDepth + 1)) {
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 real faker instance
395
- const faker = createFakerInstance();
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) {