@schmock/faker 2.3.0 → 2.3.1

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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/dist/constants.d.ts +82 -1
  4. package/dist/constants.d.ts.map +1 -1
  5. package/dist/field-name-matcher.d.ts +19 -0
  6. package/dist/field-name-matcher.d.ts.map +1 -1
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +4 -1
  10. package/dist/jsf-config.d.ts +19 -4
  11. package/dist/jsf-config.d.ts.map +1 -1
  12. package/dist/output-limits.d.ts +3 -0
  13. package/dist/output-limits.d.ts.map +1 -0
  14. package/dist/overrides.d.ts +2 -1
  15. package/dist/overrides.d.ts.map +1 -1
  16. package/dist/schema-children.d.ts +14 -0
  17. package/dist/schema-children.d.ts.map +1 -0
  18. package/dist/schema-enhancement.d.ts +1 -0
  19. package/dist/schema-enhancement.d.ts.map +1 -1
  20. package/dist/validation.d.ts +24 -5
  21. package/dist/validation.d.ts.map +1 -1
  22. package/package.json +17 -7
  23. package/dist/constants.js +0 -8
  24. package/dist/field-mappings.js +0 -1114
  25. package/dist/field-name-matcher.js +0 -148
  26. package/dist/jsf-config.js +0 -117
  27. package/dist/overrides.js +0 -137
  28. package/dist/schema-enhancement.js +0 -104
  29. package/dist/test-utils.d.ts +0 -57
  30. package/dist/test-utils.d.ts.map +0 -1
  31. package/dist/test-utils.js +0 -284
  32. package/dist/validation.js +0 -300
  33. package/src/advanced-features.test.ts +0 -912
  34. package/src/audit-faker-args.test.ts +0 -97
  35. package/src/constants.d.ts.map +0 -1
  36. package/src/constants.ts +0 -9
  37. package/src/data-quality.test.ts +0 -415
  38. package/src/error-handling.test.ts +0 -504
  39. package/src/field-mappings.test.ts +0 -351
  40. package/src/field-mappings.ts +0 -1148
  41. package/src/field-name-matcher.test.ts +0 -292
  42. package/src/field-name-matcher.ts +0 -187
  43. package/src/index.d.ts.map +0 -1
  44. package/src/index.test.ts +0 -1320
  45. package/src/index.ts +0 -185
  46. package/src/integration.test.ts +0 -634
  47. package/src/jsf-config.d.ts.map +0 -1
  48. package/src/jsf-config.test.ts +0 -65
  49. package/src/jsf-config.ts +0 -156
  50. package/src/overrides.d.ts.map +0 -1
  51. package/src/overrides.property.test.ts +0 -172
  52. package/src/overrides.test.ts +0 -38
  53. package/src/overrides.ts +0 -179
  54. package/src/performance.test.ts +0 -441
  55. package/src/plugin-integration.test.ts +0 -575
  56. package/src/post-process.test.ts +0 -177
  57. package/src/real-world.test.ts +0 -636
  58. package/src/schema-enhancement.d.ts.map +0 -1
  59. package/src/schema-enhancement.test.ts +0 -222
  60. package/src/schema-enhancement.ts +0 -144
  61. package/src/steps/deterministic-seeds.steps.ts +0 -112
  62. package/src/steps/faker-plugin.steps.ts +0 -301
  63. package/src/test-utils.ts +0 -385
  64. package/src/validation.d.ts.map +0 -1
  65. package/src/validation.ts +0 -417
@@ -1,222 +0,0 @@
1
- import type { JSONSchema7 } from "json-schema";
2
- import { describe, expect, it } from "vitest";
3
- import { enhanceSchemaWithSmartMapping } from "./schema-enhancement";
4
-
5
- describe("enhanceSchemaWithSmartMapping", () => {
6
- it("does NOT mutate the original schema", () => {
7
- const original: JSONSchema7 = {
8
- type: "object",
9
- properties: {
10
- email: { type: "string" },
11
- firstName: { type: "string" },
12
- createdAt: { type: "string" },
13
- },
14
- };
15
-
16
- // Deep snapshot before enhancement
17
- const snapshot = JSON.parse(JSON.stringify(original));
18
-
19
- enhanceSchemaWithSmartMapping(original);
20
-
21
- // Original should be unchanged
22
- expect(original).toEqual(snapshot);
23
- });
24
-
25
- it("enhancement of object with properties adds faker methods to appropriate fields", () => {
26
- const schema: JSONSchema7 = {
27
- type: "object",
28
- properties: {
29
- email: { type: "string" },
30
- firstName: { type: "string" },
31
- lastName: { type: "string" },
32
- phone: { type: "string" },
33
- uuid: { type: "string" },
34
- },
35
- };
36
-
37
- const enhanced = enhanceSchemaWithSmartMapping(schema) as Record<
38
- string,
39
- unknown
40
- >;
41
- const props = enhanced.properties as Record<
42
- string,
43
- JSONSchema7 & { faker?: string }
44
- >;
45
-
46
- // Well-known fields should receive faker methods
47
- expect(props.email.faker).toBeDefined();
48
- expect(props.firstName.faker).toBeDefined();
49
- expect(props.lastName.faker).toBeDefined();
50
- expect(props.phone.faker).toBeDefined();
51
- expect(props.uuid.faker).toBeDefined();
52
-
53
- // Verify they are plausible faker method strings (namespace.method format)
54
- for (const field of ["email", "firstName", "lastName", "phone", "uuid"]) {
55
- const fakerMethod = props[field].faker as string;
56
- expect(fakerMethod).toContain(".");
57
- expect(fakerMethod.split(".").length).toBeGreaterThanOrEqual(2);
58
- }
59
- });
60
-
61
- it("enhancement recurses into allOf branches", () => {
62
- const schema: JSONSchema7 = {
63
- allOf: [
64
- {
65
- type: "object",
66
- properties: {
67
- email: { type: "string" },
68
- },
69
- },
70
- {
71
- type: "object",
72
- properties: {
73
- phone: { type: "string" },
74
- },
75
- },
76
- ],
77
- };
78
-
79
- const enhanced = enhanceSchemaWithSmartMapping(schema);
80
- const allOf = enhanced.allOf as Array<
81
- JSONSchema7 & { properties: Record<string, { faker?: string }> }
82
- >;
83
-
84
- expect(allOf).toHaveLength(2);
85
- expect(allOf[0].properties.email.faker).toBeDefined();
86
- expect(allOf[1].properties.phone.faker).toBeDefined();
87
- });
88
-
89
- it("enhancement recurses into anyOf branches", () => {
90
- const schema: JSONSchema7 = {
91
- anyOf: [
92
- {
93
- type: "object",
94
- properties: {
95
- email: { type: "string" },
96
- },
97
- },
98
- ],
99
- };
100
-
101
- const enhanced = enhanceSchemaWithSmartMapping(schema);
102
- const anyOf = enhanced.anyOf as Array<
103
- JSONSchema7 & { properties: Record<string, { faker?: string }> }
104
- >;
105
-
106
- expect(anyOf).toHaveLength(1);
107
- expect(anyOf[0].properties.email.faker).toBeDefined();
108
- });
109
-
110
- it("enhancement recurses into oneOf branches", () => {
111
- const schema: JSONSchema7 = {
112
- oneOf: [
113
- {
114
- type: "object",
115
- properties: {
116
- firstName: { type: "string" },
117
- },
118
- },
119
- ],
120
- };
121
-
122
- const enhanced = enhanceSchemaWithSmartMapping(schema);
123
- const oneOf = enhanced.oneOf as Array<
124
- JSONSchema7 & { properties: Record<string, { faker?: string }> }
125
- >;
126
-
127
- expect(oneOf).toHaveLength(1);
128
- expect(oneOf[0].properties.firstName.faker).toBeDefined();
129
- });
130
-
131
- it("enhancement recurses into array items", () => {
132
- const schema: JSONSchema7 = {
133
- type: "array",
134
- items: {
135
- type: "object",
136
- properties: {
137
- email: { type: "string" },
138
- createdAt: { type: "string" },
139
- },
140
- },
141
- };
142
-
143
- const enhanced = enhanceSchemaWithSmartMapping(schema);
144
- const items = enhanced.items as JSONSchema7 & {
145
- properties: Record<string, { faker?: string }>;
146
- };
147
-
148
- expect(items.properties.email.faker).toBeDefined();
149
- expect(items.properties.createdAt.faker).toBeDefined();
150
- });
151
-
152
- it("fields with existing faker property are not overridden", () => {
153
- const schema: JSONSchema7 = {
154
- type: "object",
155
- properties: {
156
- email: {
157
- type: "string",
158
- faker: "lorem.word",
159
- } as JSONSchema7 & { faker: string },
160
- },
161
- };
162
-
163
- const enhanced = enhanceSchemaWithSmartMapping(schema);
164
- const props = enhanced.properties as Record<
165
- string,
166
- JSONSchema7 & { faker?: string }
167
- >;
168
-
169
- // Should keep the explicit faker method, not replace with email generator
170
- expect(props.email.faker).toBe("lorem.word");
171
- });
172
-
173
- it("adds useful text generation to an unconstrained root string", () => {
174
- const schema: JSONSchema7 = { type: "string" };
175
- const enhanced = enhanceSchemaWithSmartMapping(schema);
176
- expect(enhanced).toEqual({ type: "string", faker: "lorem.word" });
177
- });
178
-
179
- it("preserves an explicit zero minimum length", () => {
180
- const schema: JSONSchema7 = { type: "string", minLength: 0 };
181
- const enhanced = enhanceSchemaWithSmartMapping(schema);
182
- expect(enhanced).toEqual(schema);
183
- });
184
-
185
- it("handles null/undefined schema gracefully", () => {
186
- expect(enhanceSchemaWithSmartMapping(null as unknown as JSONSchema7)).toBe(
187
- null,
188
- );
189
- expect(
190
- enhanceSchemaWithSmartMapping(undefined as unknown as JSONSchema7),
191
- ).toBe(undefined);
192
- });
193
-
194
- it("enhancement recurses into array items when items is a tuple (array)", () => {
195
- const schema: JSONSchema7 = {
196
- type: "array",
197
- items: [
198
- {
199
- type: "object",
200
- properties: {
201
- email: { type: "string" },
202
- },
203
- },
204
- {
205
- type: "object",
206
- properties: {
207
- phone: { type: "string" },
208
- },
209
- },
210
- ],
211
- };
212
-
213
- const enhanced = enhanceSchemaWithSmartMapping(schema);
214
- const items = enhanced.items as Array<
215
- JSONSchema7 & { properties: Record<string, { faker?: string }> }
216
- >;
217
-
218
- expect(Array.isArray(items)).toBe(true);
219
- expect(items[0].properties.email.faker).toBeDefined();
220
- expect(items[1].properties.phone.faker).toBeDefined();
221
- });
222
- });
@@ -1,144 +0,0 @@
1
- import type { JSONSchema7 } from "json-schema";
2
- import { findBestMapping } from "./field-name-matcher.js";
3
- import { isJSONSchema7, validateFakerMethod } from "./validation.js";
4
-
5
- /** JSONSchema7 extended with json-schema-faker's `faker` property and schmock markers */
6
- interface FakerSchema extends JSONSchema7 {
7
- faker?: string | Record<string, unknown>;
8
- schmockNullable?: boolean;
9
- schmockTrueProbability?: number;
10
- }
11
-
12
- function needsStringFallback(schema: FakerSchema): boolean {
13
- return (
14
- schema.type === "string" &&
15
- schema.faker === undefined &&
16
- schema.format === undefined &&
17
- schema.pattern === undefined &&
18
- schema.enum === undefined &&
19
- schema.const === undefined &&
20
- schema.$ref === undefined &&
21
- schema.minLength === undefined &&
22
- schema.maxLength === undefined
23
- );
24
- }
25
-
26
- export function enhanceSchemaWithSmartMapping(
27
- schema: JSONSchema7,
28
- ): JSONSchema7 {
29
- if (!schema || typeof schema !== "object") {
30
- return schema;
31
- }
32
-
33
- const enhanced: FakerSchema = { ...schema };
34
-
35
- // Handle object properties
36
- if (enhanced.properties) {
37
- enhanced.properties = { ...enhanced.properties };
38
-
39
- for (const [fieldName, fieldSchema] of Object.entries(
40
- enhanced.properties,
41
- )) {
42
- if (isJSONSchema7(fieldSchema)) {
43
- enhanced.properties[fieldName] = enhanceFieldSchema(
44
- fieldName,
45
- fieldSchema,
46
- );
47
- }
48
- }
49
- }
50
-
51
- // Recurse into composition keywords
52
- for (const keyword of ["allOf", "anyOf", "oneOf"] as const) {
53
- const branches = enhanced[keyword];
54
- if (Array.isArray(branches)) {
55
- enhanced[keyword] = branches.map((branch) =>
56
- isJSONSchema7(branch) ? enhanceSchemaWithSmartMapping(branch) : branch,
57
- );
58
- }
59
- }
60
-
61
- // Recurse into array items
62
- if (enhanced.items) {
63
- if (Array.isArray(enhanced.items)) {
64
- enhanced.items = enhanced.items.map((item) =>
65
- isJSONSchema7(item) ? enhanceSchemaWithSmartMapping(item) : item,
66
- );
67
- } else if (isJSONSchema7(enhanced.items)) {
68
- enhanced.items = enhanceSchemaWithSmartMapping(enhanced.items);
69
- }
70
- }
71
-
72
- // Recurse into additionalProperties
73
- if (isJSONSchema7(enhanced.additionalProperties)) {
74
- enhanced.additionalProperties = enhanceSchemaWithSmartMapping(
75
- enhanced.additionalProperties,
76
- );
77
- }
78
-
79
- if (needsStringFallback(enhanced)) {
80
- enhanced.faker = "lorem.word";
81
- }
82
-
83
- return enhanced;
84
- }
85
-
86
- function enhanceFieldSchema(
87
- fieldName: string,
88
- fieldSchema: JSONSchema7,
89
- ): FakerSchema {
90
- const enhanced: FakerSchema = { ...fieldSchema };
91
-
92
- // If already has faker extension, validate it and don't override.
93
- // User-supplied faker values are always strings; the object form is only
94
- // produced internally by this function when fakerArgs are present.
95
- if (enhanced.faker) {
96
- if (typeof enhanced.faker === "string") {
97
- validateFakerMethod(enhanced.faker);
98
- }
99
- return enhanced;
100
- }
101
-
102
- // Recursively enhance nested schemas first
103
- const hasComposition = enhanced.allOf || enhanced.anyOf || enhanced.oneOf;
104
- if (enhanced.properties || hasComposition || enhanced.items) {
105
- const recursed = enhanceSchemaWithSmartMapping(enhanced);
106
- Object.assign(enhanced, recursed);
107
- }
108
-
109
- // Don't apply field-level faker mapping to composition schemas — the branches define their own types
110
- if (hasComposition) {
111
- return enhanced;
112
- }
113
-
114
- // Don't apply smart mapping when const or enum is defined — these have fixed values
115
- if (enhanced.const !== undefined || enhanced.enum) {
116
- return enhanced;
117
- }
118
-
119
- // Apply smart field name mapping via the scoring matcher
120
- const match = findBestMapping(fieldName, enhanced);
121
- if (match) {
122
- const { fakerMethod, format, trueProbability, fakerArgs } = match.mapping;
123
- // Use the JSF object form when fakerArgs are present so the options object
124
- // is forwarded to the faker method (e.g. number.int({ min, max })).
125
- // JSF calls Q(...J) where J is the value, so we must wrap fakerArgs in an
126
- // array: { "number.int": [{ min, max }] } → faker.number.int({ min, max }).
127
- // Fall back to the plain string form when there are no args.
128
- if (fakerArgs) {
129
- enhanced.faker = { [fakerMethod]: [fakerArgs] };
130
- } else {
131
- enhanced.faker = fakerMethod;
132
- }
133
- if (format) {
134
- enhanced.format = format;
135
- }
136
- if (trueProbability !== undefined) {
137
- enhanced.schmockTrueProbability = trueProbability;
138
- }
139
- } else if (needsStringFallback(enhanced)) {
140
- enhanced.faker = "lorem.word";
141
- }
142
-
143
- return enhanced;
144
- }
@@ -1,112 +0,0 @@
1
- import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
2
- import { expect } from "vitest";
3
- import { generateFromSchema } from "../index";
4
-
5
- const feature = await loadFeature("../../features/deterministic-seeds.feature");
6
-
7
- const testSchema = {
8
- type: "object" as const,
9
- properties: {
10
- name: { type: "string" as const },
11
- age: { type: "integer" as const, minimum: 1, maximum: 100 },
12
- },
13
- required: ["name", "age"],
14
- };
15
-
16
- describeFeature(feature, ({ Scenario }) => {
17
- let result1: unknown;
18
- let result2: unknown;
19
- let seededArray1: unknown;
20
- let seededArray2: unknown;
21
- let differentSeedResult1: unknown;
22
- let differentSeedResult2: unknown;
23
-
24
- Scenario("Same seed produces same output", ({ Given, When, Then }) => {
25
- Given("a schema plugin with seed 42", () => {
26
- // seed will be passed during generation
27
- });
28
-
29
- When("I generate data twice with the same seed", async () => {
30
- result1 = await generateFromSchema({ schema: testSchema, seed: 42 });
31
- result2 = await generateFromSchema({ schema: testSchema, seed: 42 });
32
- });
33
-
34
- Then("both outputs are identical", () => {
35
- expect(result1).toEqual(result2);
36
- });
37
- });
38
-
39
- Scenario(
40
- "Different seeds produce different output",
41
- ({ Given, When, Then }) => {
42
- let firstSeed = 0;
43
- let secondSeed = 0;
44
-
45
- Given(
46
- "a schema plugin using seeds {int} and {int}",
47
- (_, seedA: number, seedB: number) => {
48
- firstSeed = seedA;
49
- secondSeed = seedB;
50
- },
51
- );
52
-
53
- When("I generate data once with each seed", async () => {
54
- differentSeedResult1 = await generateFromSchema({
55
- schema: testSchema,
56
- seed: firstSeed,
57
- });
58
- differentSeedResult2 = await generateFromSchema({
59
- schema: testSchema,
60
- seed: secondSeed,
61
- });
62
- });
63
-
64
- Then("the differently seeded outputs are distinct", () => {
65
- expect(differentSeedResult1).not.toEqual(differentSeedResult2);
66
- });
67
- },
68
- );
69
-
70
- Scenario(
71
- "Seeded arrays are reproducible without repeating every item",
72
- ({ Given, When, Then, And }) => {
73
- let count = 0;
74
-
75
- Given(
76
- "an array schema plugin with seed 42 and count {int}",
77
- (_, requestedCount: number) => {
78
- count = requestedCount;
79
- },
80
- );
81
-
82
- When("I generate the seeded array twice", async () => {
83
- const schema = {
84
- type: "array" as const,
85
- items: {
86
- type: "object" as const,
87
- properties: {
88
- id: { type: "integer" as const },
89
- name: { type: "string" as const },
90
- },
91
- required: ["id", "name"],
92
- },
93
- };
94
- seededArray1 = await generateFromSchema({ schema, count, seed: 42 });
95
- seededArray2 = await generateFromSchema({ schema, count, seed: 42 });
96
- });
97
-
98
- Then("both seeded arrays are identical", () => {
99
- expect(seededArray1).toEqual(seededArray2);
100
- });
101
-
102
- And("the seeded array contains varied items", () => {
103
- expect(Array.isArray(seededArray1)).toBe(true);
104
- if (!Array.isArray(seededArray1)) {
105
- throw new Error("Expected seeded output to be an array");
106
- }
107
- const serialized = seededArray1.map((item) => JSON.stringify(item));
108
- expect(new Set(serialized).size).toBeGreaterThan(1);
109
- });
110
- },
111
- );
112
- });