@schmock/faker 2.2.3 → 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-mappings.d.ts.map +1 -1
  6. package/dist/field-name-matcher.d.ts +19 -0
  7. package/dist/field-name-matcher.d.ts.map +1 -1
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +4 -1
  11. package/dist/jsf-config.d.ts +27 -2
  12. package/dist/jsf-config.d.ts.map +1 -1
  13. package/dist/output-limits.d.ts +3 -0
  14. package/dist/output-limits.d.ts.map +1 -0
  15. package/dist/overrides.d.ts +2 -1
  16. package/dist/overrides.d.ts.map +1 -1
  17. package/dist/schema-children.d.ts +14 -0
  18. package/dist/schema-children.d.ts.map +1 -0
  19. package/dist/schema-enhancement.d.ts +1 -0
  20. package/dist/schema-enhancement.d.ts.map +1 -1
  21. package/dist/validation.d.ts +24 -5
  22. package/dist/validation.d.ts.map +1 -1
  23. package/package.json +19 -9
  24. package/dist/constants.js +0 -8
  25. package/dist/field-mappings.js +0 -1107
  26. package/dist/field-name-matcher.js +0 -149
  27. package/dist/jsf-config.js +0 -35
  28. package/dist/overrides.js +0 -131
  29. package/dist/schema-enhancement.js +0 -89
  30. package/dist/test-utils.d.ts +0 -57
  31. package/dist/test-utils.d.ts.map +0 -1
  32. package/dist/test-utils.js +0 -271
  33. package/dist/validation.js +0 -300
  34. package/src/advanced-features.test.ts +0 -912
  35. package/src/audit-faker-args.test.ts +0 -97
  36. package/src/constants.d.ts.map +0 -1
  37. package/src/constants.ts +0 -9
  38. package/src/data-quality.test.ts +0 -415
  39. package/src/error-handling.test.ts +0 -504
  40. package/src/field-mappings.test.ts +0 -351
  41. package/src/field-mappings.ts +0 -1141
  42. package/src/field-name-matcher.test.ts +0 -292
  43. package/src/field-name-matcher.ts +0 -188
  44. package/src/index.d.ts.map +0 -1
  45. package/src/index.test.ts +0 -1218
  46. package/src/index.ts +0 -168
  47. package/src/integration.test.ts +0 -634
  48. package/src/jsf-config.d.ts.map +0 -1
  49. package/src/jsf-config.ts +0 -48
  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 -176
  54. package/src/performance.test.ts +0 -494
  55. package/src/plugin-integration.test.ts +0 -575
  56. package/src/post-process.test.ts +0 -151
  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 -216
  60. package/src/schema-enhancement.ts +0 -128
  61. package/src/steps/deterministic-seeds.steps.ts +0 -35
  62. package/src/steps/faker-plugin.steps.ts +0 -109
  63. package/src/test-utils.ts +0 -365
  64. package/src/validation.d.ts.map +0 -1
  65. package/src/validation.ts +0 -417
@@ -1,216 +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("handles schema without properties gracefully", () => {
174
- const schema: JSONSchema7 = { type: "string" };
175
- const enhanced = enhanceSchemaWithSmartMapping(schema);
176
- expect(enhanced).toEqual({ type: "string" });
177
- });
178
-
179
- it("handles null/undefined schema gracefully", () => {
180
- expect(enhanceSchemaWithSmartMapping(null as unknown as JSONSchema7)).toBe(
181
- null,
182
- );
183
- expect(
184
- enhanceSchemaWithSmartMapping(undefined as unknown as JSONSchema7),
185
- ).toBe(undefined);
186
- });
187
-
188
- it("enhancement recurses into array items when items is a tuple (array)", () => {
189
- const schema: JSONSchema7 = {
190
- type: "array",
191
- items: [
192
- {
193
- type: "object",
194
- properties: {
195
- email: { type: "string" },
196
- },
197
- },
198
- {
199
- type: "object",
200
- properties: {
201
- phone: { type: "string" },
202
- },
203
- },
204
- ],
205
- };
206
-
207
- const enhanced = enhanceSchemaWithSmartMapping(schema);
208
- const items = enhanced.items as Array<
209
- JSONSchema7 & { properties: Record<string, { faker?: string }> }
210
- >;
211
-
212
- expect(Array.isArray(items)).toBe(true);
213
- expect(items[0].properties.email.faker).toBeDefined();
214
- expect(items[1].properties.phone.faker).toBeDefined();
215
- });
216
- });
@@ -1,128 +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
- export function enhanceSchemaWithSmartMapping(
13
- schema: JSONSchema7,
14
- ): JSONSchema7 {
15
- if (!schema || typeof schema !== "object") {
16
- return schema;
17
- }
18
-
19
- const enhanced = { ...schema } as FakerSchema;
20
-
21
- // Handle object properties
22
- if (enhanced.properties) {
23
- enhanced.properties = { ...enhanced.properties };
24
-
25
- for (const [fieldName, fieldSchema] of Object.entries(
26
- enhanced.properties,
27
- )) {
28
- if (isJSONSchema7(fieldSchema)) {
29
- enhanced.properties[fieldName] = enhanceFieldSchema(
30
- fieldName,
31
- fieldSchema,
32
- );
33
- }
34
- }
35
- }
36
-
37
- // Recurse into composition keywords
38
- for (const keyword of ["allOf", "anyOf", "oneOf"] as const) {
39
- const branches = enhanced[keyword];
40
- if (Array.isArray(branches)) {
41
- (enhanced as Record<string, unknown>)[keyword] = branches.map((branch) =>
42
- isJSONSchema7(branch) ? enhanceSchemaWithSmartMapping(branch) : branch,
43
- );
44
- }
45
- }
46
-
47
- // Recurse into array items
48
- if (enhanced.items) {
49
- if (Array.isArray(enhanced.items)) {
50
- enhanced.items = enhanced.items.map((item) =>
51
- isJSONSchema7(item) ? enhanceSchemaWithSmartMapping(item) : item,
52
- );
53
- } else if (isJSONSchema7(enhanced.items)) {
54
- enhanced.items = enhanceSchemaWithSmartMapping(enhanced.items);
55
- }
56
- }
57
-
58
- // Recurse into additionalProperties
59
- if (
60
- enhanced.additionalProperties &&
61
- typeof enhanced.additionalProperties === "object" &&
62
- !Array.isArray(enhanced.additionalProperties)
63
- ) {
64
- enhanced.additionalProperties = enhanceSchemaWithSmartMapping(
65
- enhanced.additionalProperties as JSONSchema7,
66
- );
67
- }
68
-
69
- return enhanced;
70
- }
71
-
72
- function enhanceFieldSchema(
73
- fieldName: string,
74
- fieldSchema: JSONSchema7,
75
- ): FakerSchema {
76
- const enhanced: FakerSchema = { ...fieldSchema };
77
-
78
- // If already has faker extension, validate it and don't override.
79
- // User-supplied faker values are always strings; the object form is only
80
- // produced internally by this function when fakerArgs are present.
81
- if (enhanced.faker) {
82
- if (typeof enhanced.faker === "string") {
83
- validateFakerMethod(enhanced.faker);
84
- }
85
- return enhanced;
86
- }
87
-
88
- // Recursively enhance nested schemas first
89
- const hasComposition = enhanced.allOf || enhanced.anyOf || enhanced.oneOf;
90
- if (enhanced.properties || hasComposition || enhanced.items) {
91
- const recursed = enhanceSchemaWithSmartMapping(enhanced);
92
- Object.assign(enhanced, recursed);
93
- }
94
-
95
- // Don't apply field-level faker mapping to composition schemas — the branches define their own types
96
- if (hasComposition) {
97
- return enhanced;
98
- }
99
-
100
- // Don't apply smart mapping when const or enum is defined — these have fixed values
101
- if (enhanced.const !== undefined || enhanced.enum) {
102
- return enhanced;
103
- }
104
-
105
- // Apply smart field name mapping via the scoring matcher
106
- const match = findBestMapping(fieldName, enhanced);
107
- if (match) {
108
- const { fakerMethod, format, trueProbability, fakerArgs } = match.mapping;
109
- // Use the JSF object form when fakerArgs are present so the options object
110
- // is forwarded to the faker method (e.g. number.int({ min, max })).
111
- // JSF calls Q(...J) where J is the value, so we must wrap fakerArgs in an
112
- // array: { "number.int": [{ min, max }] } → faker.number.int({ min, max }).
113
- // Fall back to the plain string form when there are no args.
114
- if (fakerArgs) {
115
- enhanced.faker = { [fakerMethod]: [fakerArgs] };
116
- } else {
117
- enhanced.faker = fakerMethod;
118
- }
119
- if (format) {
120
- enhanced.format = format;
121
- }
122
- if (trueProbability !== undefined) {
123
- enhanced.schmockTrueProbability = trueProbability;
124
- }
125
- }
126
-
127
- return enhanced;
128
- }
@@ -1,35 +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
-
20
- Scenario("Same seed produces same output", ({ Given, When, Then }) => {
21
- Given("a schema plugin with seed 42", () => {
22
- // seed will be passed during generation
23
- });
24
-
25
- When("I generate data twice with the same seed", async () => {
26
- result1 = await generateFromSchema({ schema: testSchema, seed: 42 });
27
- result2 = await generateFromSchema({ schema: testSchema, seed: 42 });
28
- });
29
-
30
- Then("both outputs are identical", () => {
31
- expect(result1).toEqual(result2);
32
- });
33
- });
34
-
35
- });
@@ -1,109 +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/faker-plugin.feature");
6
-
7
- describeFeature(feature, ({ Scenario }) => {
8
- let generated: any;
9
-
10
- Scenario(
11
- "Generate object from simple schema",
12
- ({ Given, When, Then, And }) => {
13
- let schema: any;
14
-
15
- Given("I create a schema plugin with:", (_, docString: string) => {
16
- schema = JSON.parse(docString);
17
- });
18
-
19
- When("I generate data from the schema", async () => {
20
- generated = await generateFromSchema({ schema });
21
- });
22
-
23
- Then(
24
- "the generated data should have property {string} of type {string}",
25
- (_, prop: string, type: string) => {
26
- expect(generated).toHaveProperty(prop);
27
- expect(typeof generated[prop]).toBe(type);
28
- },
29
- );
30
-
31
- And(
32
- "the generated data should have property {string} of type {string}",
33
- (_, prop: string, type: string) => {
34
- expect(generated).toHaveProperty(prop);
35
- expect(typeof generated[prop]).toBe(type);
36
- },
37
- );
38
- },
39
- );
40
-
41
- Scenario(
42
- "Generate array of items with explicit count",
43
- ({ Given, When, Then }) => {
44
- let schema: any;
45
- let count: number;
46
-
47
- Given(
48
- "I create a schema plugin for array with count {int}:",
49
- (_, cnt: number, docString: string) => {
50
- schema = JSON.parse(docString);
51
- count = cnt;
52
- },
53
- );
54
-
55
- When("I generate data from the schema", async () => {
56
- generated = await generateFromSchema({ schema, count });
57
- });
58
-
59
- Then(
60
- "the generated data should be an array of length {int}",
61
- (_, length: number) => {
62
- expect(Array.isArray(generated)).toBe(true);
63
- expect(generated).toHaveLength(length);
64
- },
65
- );
66
- },
67
- );
68
-
69
- Scenario(
70
- "Template preserves string values for mixed templates",
71
- ({ Given, When, Then }) => {
72
- let template: string;
73
- let result: any;
74
-
75
- Given(
76
- "I create a schema plugin with template override {string}",
77
- (_, tmpl: string) => {
78
- template = tmpl;
79
- },
80
- );
81
-
82
- When(
83
- "I generate data with param {string} set to {string}",
84
- async (_, paramName: string, paramValue: string) => {
85
- const schema = {
86
- type: "object" as const,
87
- properties: {
88
- value: { type: "string" as const },
89
- },
90
- };
91
- result = await generateFromSchema({
92
- schema,
93
- overrides: { value: template },
94
- params: { [paramName]: paramValue },
95
- });
96
- },
97
- );
98
-
99
- Then(
100
- "the template result should be the string {string}",
101
- (_, expected: string) => {
102
- expect(result.value).toBe(expected);
103
- expect(typeof result.value).toBe("string");
104
- },
105
- );
106
- },
107
- );
108
-
109
- });