@amritk/generate-examples 0.5.2 → 0.5.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.
@@ -1,740 +1,567 @@
1
- import { getMjstInstanceOf, getMjstPrimitive } from '@amritk/helpers/mjst-extension';
2
- import { resolveRef } from '@amritk/helpers/resolve-ref';
3
- import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasDefault, hasDependentRequired, hasDependentSchemas, hasEnum, hasExamples, hasExclusiveMaximum, hasExclusiveMinimum, hasFormat, hasItems, hasMaxItems, hasMaximum, hasMaxLength, hasMaxProperties, hasMinItems, hasMinimum, hasMinLength, hasMinProperties, hasMultipleOf, hasOneOf, hasPattern, hasPatternProperties, hasProperties, hasPropertyNames, hasRef, hasRequired, hasType, hasUniqueItems, isSchemaObject, } from '@amritk/helpers/schema-guards';
4
- import { makeInstanceCheck, needsValidationFilter } from './schema-validation.js';
5
- /** Lowercases the first character of a name. e.g. "User" → "user" */
1
+ import { getMjstInstanceOf, getMjstPrimitive } from "@amritk/helpers/mjst-extension";
2
+ import { resolveRef } from "@amritk/helpers/resolve-ref";
3
+ import { hasAdditionalProperties, hasAllOf, hasAnyOf, hasConst, hasDefault, hasDependentRequired, hasDependentSchemas, hasEnum, hasExamples, hasExclusiveMaximum, hasExclusiveMinimum, hasFormat, hasItems, hasMaxItems, hasMaximum, hasMaxLength, hasMaxProperties, hasMinItems, hasMinimum, hasMinLength, hasMinProperties, hasMultipleOf, hasOneOf, hasPattern, hasPatternProperties, hasProperties, hasPropertyNames, hasRef, hasRequired, hasType, hasUniqueItems, isSchemaObject } from "@amritk/helpers/schema-guards";
4
+ import { makeInstanceCheck, needsValidationFilter } from "./schema-validation.js";
6
5
  const lowerFirst = (name) => name.charAt(0).toLowerCase() + name.slice(1);
7
- /** Derives the example const name from a type name. e.g. "User" → "userExample" */
8
6
  const exampleName = (typeName) => `${lowerFirst(typeName)}Example`;
9
- /** A representative character for a single regex atom (class body / escape). */
10
7
  const charForClass = (inner) => {
11
- if (/a-z/.test(inner))
12
- return 'a';
13
- if (/A-Z/.test(inner))
14
- return 'A';
15
- if (/0-9|\\d/.test(inner))
16
- return '5';
17
- const first = inner.replace(/^\^/, '')[0];
18
- return first && first !== '\\' ? first : 'a';
8
+ if (/a-z/.test(inner))
9
+ return "a";
10
+ if (/A-Z/.test(inner))
11
+ return "A";
12
+ if (/0-9|\\d/.test(inner))
13
+ return "5";
14
+ const first = inner.replace(/^\^/, "")[0];
15
+ return first && first !== "\\" ? first : "a";
19
16
  };
20
17
  const charForEscape = (esc) => {
21
- if (esc === '\\d')
22
- return '5';
23
- if (esc === '\\w')
24
- return 'a';
25
- if (esc === '\\s')
26
- return ' ';
27
- return esc[1] ?? 'a';
18
+ if (esc === "\\d")
19
+ return "5";
20
+ if (esc === "\\w")
21
+ return "a";
22
+ if (esc === "\\s")
23
+ return " ";
24
+ return esc[1] ?? "a";
28
25
  };
29
- /** Splits `s` on top-level `|`, respecting `[...]` and `(...)` nesting. */
30
26
  const topLevelAlternatives = (s) => {
31
- const parts = [];
32
- let depth = 0;
33
- let inClass = false;
34
- let cur = '';
35
- for (let i = 0; i < s.length; i++) {
36
- const c = s[i];
37
- if (c === '\\') {
38
- cur += c + (s[i + 1] ?? '');
39
- i++;
40
- continue;
41
- }
42
- if (inClass) {
43
- cur += c;
44
- if (c === ']')
45
- inClass = false;
27
+ const parts = [];
28
+ let depth = 0;
29
+ let inClass = false;
30
+ let cur = "";
31
+ for (let i = 0; i < s.length; i++) {
32
+ const c = s[i];
33
+ if (c === "\\") {
34
+ cur += c + (s[i + 1] ?? "");
35
+ i++;
36
+ continue;
37
+ }
38
+ if (inClass) {
39
+ cur += c;
40
+ if (c === "]")
41
+ inClass = false;
42
+ continue;
43
+ }
44
+ if (c === "[")
45
+ inClass = true;
46
+ else if (c === "(")
47
+ depth++;
48
+ else if (c === ")")
49
+ depth--;
50
+ else if (c === "|" && depth === 0) {
51
+ parts.push(cur);
52
+ cur = "";
53
+ continue;
54
+ }
55
+ cur += c;
56
+ }
57
+ parts.push(cur);
58
+ return parts;
59
+ };
60
+ const sampleFromPattern = (pattern, minLength) => {
61
+ let body = pattern;
62
+ if (body.startsWith("^"))
63
+ body = body.slice(1);
64
+ if (body.endsWith("$") && !body.endsWith("\\$"))
65
+ body = body.slice(0, -1);
66
+ const sampleAlt = (s) => {
67
+ for (const alt of topLevelAlternatives(s)) {
68
+ const r = sampleSeq(alt);
69
+ if (r !== void 0)
70
+ return r;
71
+ }
72
+ return void 0;
73
+ };
74
+ const sampleSeq = (seq) => {
75
+ let out = "";
76
+ let i = 0;
77
+ while (i < seq.length) {
78
+ let unit;
79
+ const c = seq[i];
80
+ if (c === "(") {
81
+ let depth = 1;
82
+ let j = i + 1;
83
+ for (; j < seq.length && depth > 0; j++) {
84
+ const cj = seq[j];
85
+ if (cj === "\\") {
86
+ j++;
46
87
  continue;
47
- }
48
- if (c === '[')
49
- inClass = true;
50
- else if (c === '(')
88
+ }
89
+ if (cj === "(")
51
90
  depth++;
52
- else if (c === ')')
91
+ else if (cj === ")")
53
92
  depth--;
54
- else if (c === '|' && depth === 0) {
55
- parts.push(cur);
56
- cur = '';
57
- continue;
58
93
  }
59
- cur += c;
94
+ if (depth !== 0)
95
+ return void 0;
96
+ let inner = seq.slice(i + 1, j - 1);
97
+ if (/^\?[=!]/.test(inner) || /^\?<[=!]/.test(inner))
98
+ return void 0;
99
+ inner = inner.replace(/^\?:/, "").replace(/^\?<[^>]*>/, "");
100
+ unit = sampleAlt(inner);
101
+ if (unit === void 0)
102
+ return void 0;
103
+ i = j;
104
+ } else if (c === "[") {
105
+ const end = seq.indexOf("]", i + 1);
106
+ if (end === -1)
107
+ return void 0;
108
+ unit = charForClass(seq.slice(i + 1, end));
109
+ i = end + 1;
110
+ } else if (c === "\\") {
111
+ const esc = seq.slice(i, i + 2);
112
+ if (/\d/.test(esc[1] ?? ""))
113
+ return void 0;
114
+ unit = charForEscape(esc);
115
+ i += 2;
116
+ } else if (c === ".") {
117
+ unit = "a";
118
+ i++;
119
+ } else {
120
+ unit = c;
121
+ i++;
122
+ }
123
+ let reps = 1;
124
+ const q = seq[i];
125
+ if (q === "+") {
126
+ reps = Math.max(1, minLength);
127
+ i++;
128
+ } else if (q === "*") {
129
+ reps = Math.max(0, minLength);
130
+ i++;
131
+ } else if (q === "?") {
132
+ reps = 1;
133
+ i++;
134
+ } else if (q === "{") {
135
+ const end = seq.indexOf("}", i + 1);
136
+ if (end === -1)
137
+ return void 0;
138
+ reps = Number.parseInt(seq.slice(i + 1, end), 10) || 0;
139
+ i = end + 1;
140
+ }
141
+ out += unit.repeat(reps);
60
142
  }
61
- parts.push(cur);
62
- return parts;
63
- };
64
- /**
65
- * Best-effort generator of a string matching a `pattern`, via recursive descent:
66
- * anchors, literals, `.`, escapes (`\d`/`\w`/`\s`), character classes, groups
67
- * (capturing / non-capturing / named), alternation (`a|b` — picks the first
68
- * usable branch), and the `+`/`*`/`?`/`{n}`/`{n,m}` quantifiers. Lookarounds and
69
- * backreferences fall through to `undefined`. The caller verifies the result
70
- * against the real regex and only uses it on a match, so a partial sampler never
71
- * makes the example worse — it just upgrades the cases it understands.
72
- */
73
- const sampleFromPattern = (pattern, minLength) => {
74
- let body = pattern;
75
- if (body.startsWith('^'))
76
- body = body.slice(1);
77
- if (body.endsWith('$') && !body.endsWith('\\$'))
78
- body = body.slice(0, -1);
79
- // Samples one alternation, preferring the first branch that samples cleanly.
80
- const sampleAlt = (s) => {
81
- for (const alt of topLevelAlternatives(s)) {
82
- const r = sampleSeq(alt);
83
- if (r !== undefined)
84
- return r;
85
- }
86
- return undefined;
87
- };
88
- // Samples one concatenation (no top-level `|`).
89
- const sampleSeq = (seq) => {
90
- let out = '';
91
- let i = 0;
92
- while (i < seq.length) {
93
- let unit;
94
- const c = seq[i];
95
- if (c === '(') {
96
- // Find the matching close paren.
97
- let depth = 1;
98
- let j = i + 1;
99
- for (; j < seq.length && depth > 0; j++) {
100
- const cj = seq[j];
101
- if (cj === '\\') {
102
- j++;
103
- continue;
104
- }
105
- if (cj === '(')
106
- depth++;
107
- else if (cj === ')')
108
- depth--;
109
- }
110
- if (depth !== 0)
111
- return undefined;
112
- let inner = seq.slice(i + 1, j - 1);
113
- if (/^\?[=!]/.test(inner) || /^\?<[=!]/.test(inner))
114
- return undefined; // lookaround
115
- inner = inner.replace(/^\?:/, '').replace(/^\?<[^>]*>/, '');
116
- unit = sampleAlt(inner);
117
- if (unit === undefined)
118
- return undefined;
119
- i = j;
120
- }
121
- else if (c === '[') {
122
- const end = seq.indexOf(']', i + 1);
123
- if (end === -1)
124
- return undefined;
125
- unit = charForClass(seq.slice(i + 1, end));
126
- i = end + 1;
127
- }
128
- else if (c === '\\') {
129
- const esc = seq.slice(i, i + 2);
130
- if (/\d/.test(esc[1] ?? ''))
131
- return undefined; // backreference
132
- unit = charForEscape(esc);
133
- i += 2;
134
- }
135
- else if (c === '.') {
136
- unit = 'a';
137
- i++;
138
- }
139
- else {
140
- unit = c;
141
- i++;
142
- }
143
- // Optional quantifier.
144
- let reps = 1;
145
- const q = seq[i];
146
- if (q === '+') {
147
- reps = Math.max(1, minLength);
148
- i++;
149
- }
150
- else if (q === '*') {
151
- reps = Math.max(0, minLength);
152
- i++;
153
- }
154
- else if (q === '?') {
155
- reps = 1;
156
- i++;
157
- }
158
- else if (q === '{') {
159
- const end = seq.indexOf('}', i + 1);
160
- if (end === -1)
161
- return undefined;
162
- reps = Number.parseInt(seq.slice(i + 1, end), 10) || 0;
163
- i = end + 1;
164
- }
165
- out += unit.repeat(reps);
166
- }
167
- return out;
168
- };
169
- return sampleAlt(body);
143
+ return out;
144
+ };
145
+ return sampleAlt(body);
170
146
  };
171
- /** Returns a representative string honouring `format`, `pattern`, and length. */
172
147
  const exampleString = (schema) => {
173
- if (hasFormat(schema)) {
174
- switch (schema.format) {
175
- case 'email':
176
- return 'user@example.com';
177
- case 'uuid':
178
- return '00000000-0000-0000-0000-000000000000';
179
- case 'uri':
180
- case 'url':
181
- return 'https://example.com';
182
- case 'date-time':
183
- return '1970-01-01T00:00:00.000Z';
184
- case 'date':
185
- return '1970-01-01';
186
- case 'time':
187
- return '00:00:00.000Z';
188
- case 'hostname':
189
- return 'example.com';
190
- case 'ipv4':
191
- return '127.0.0.1';
192
- case 'ipv6':
193
- return '::1';
194
- }
148
+ if (hasFormat(schema)) {
149
+ switch (schema.format) {
150
+ case "email":
151
+ return "user@example.com";
152
+ case "uuid":
153
+ return "00000000-0000-0000-0000-000000000000";
154
+ case "uri":
155
+ case "url":
156
+ return "https://example.com";
157
+ case "date-time":
158
+ return "1970-01-01T00:00:00.000Z";
159
+ case "date":
160
+ return "1970-01-01";
161
+ case "time":
162
+ return "00:00:00.000Z";
163
+ case "hostname":
164
+ return "example.com";
165
+ case "ipv4":
166
+ return "127.0.0.1";
167
+ case "ipv6":
168
+ return "::1";
195
169
  }
196
- const minLength = hasMinLength(schema) ? schema.minLength : 0;
197
- if (hasPattern(schema)) {
198
- const sampled = sampleFromPattern(schema.pattern, minLength);
199
- // Only trust the sampler when it actually matches and fits the length bound.
200
- if (sampled !== undefined && new RegExp(schema.pattern).test(sampled)) {
201
- if (!(hasMaxLength(schema) && sampled.length > schema.maxLength))
202
- return sampled;
203
- }
170
+ }
171
+ const minLength = hasMinLength(schema) ? schema.minLength : 0;
172
+ if (hasPattern(schema)) {
173
+ const sampled = sampleFromPattern(schema.pattern, minLength);
174
+ if (sampled !== void 0 && new RegExp(schema.pattern).test(sampled)) {
175
+ if (!(hasMaxLength(schema) && sampled.length > schema.maxLength))
176
+ return sampled;
204
177
  }
205
- let value = 'string';
206
- if (value.length < minLength)
207
- value = value.padEnd(minLength, 'x');
208
- if (hasMaxLength(schema) && value.length > schema.maxLength)
209
- value = value.slice(0, schema.maxLength);
210
- return value;
178
+ }
179
+ let value = "string";
180
+ if (value.length < minLength)
181
+ value = value.padEnd(minLength, "x");
182
+ if (hasMaxLength(schema) && value.length > schema.maxLength)
183
+ value = value.slice(0, schema.maxLength);
184
+ return value;
211
185
  };
212
- /**
213
- * Derives a single concrete, schema-valid value from a JSON Schema.
214
- *
215
- * Prefers explicit hints in this order: `const`, `examples[0]`, `default`,
216
- * `enum[0]`; otherwise produces a canonical value for the declared type.
217
- * `$ref`s are resolved and inlined by value; recursive refs short-circuit to
218
- * `null` (tracked via `seen`).
219
- *
220
- * Note: values constrained only by `pattern` are not guaranteed to match the
221
- * pattern — use the generated arbitrary when pattern fidelity matters.
222
- */
223
- export const deriveExample = (schema, rootSchema, seen = new Set()) => {
224
- if (!isSchemaObject(schema))
225
- return null;
226
- const base = deriveBase(schema, rootSchema, seen);
227
- // Keywords the structural deriver can't fully honour (`if`/`then`/`else`,
228
- // `not`, `oneOf` exclusivity) are reconciled by validating candidates against a
229
- // real validator and picking the first that passes.
230
- return needsValidationFilter(schema) ? refineExample(schema, base, rootSchema, seen) : base;
186
+ const deriveExample = (schema, rootSchema, seen = /* @__PURE__ */ new Set()) => {
187
+ if (!isSchemaObject(schema))
188
+ return null;
189
+ const base = deriveBase(schema, rootSchema, seen);
190
+ return needsValidationFilter(schema) ? refineExample(schema, base, rootSchema, seen) : base;
231
191
  };
232
- /** Structural derivation for a node, before any validating refinement. */
233
192
  const deriveBase = (schema, rootSchema, seen) => {
234
- if (!isSchemaObject(schema))
235
- return null;
236
- if (hasConst(schema))
237
- return schema.const;
238
- if (hasExamples(schema) && Array.isArray(schema.examples) && schema.examples.length > 0)
239
- return schema.examples[0];
240
- if (hasDefault(schema))
241
- return schema.default;
242
- if (hasEnum(schema) && schema.enum.length > 0) {
243
- // Prefer the first member that also satisfies any sibling length/range
244
- // constraints (e.g. `enum` + `minLength`), falling back to the first member.
245
- const fitting = schema.enum.find((value) => satisfiesScalarConstraints(schema, value));
246
- return fitting !== undefined ? fitting : schema.enum[0];
247
- }
248
- if (hasRef(schema)) {
249
- const ref = schema.$ref;
250
- if (seen.has(ref) || !rootSchema)
251
- return null;
252
- const resolved = resolveRef(ref, rootSchema);
253
- if (!resolved)
254
- return null;
255
- return deriveExample(resolved, rootSchema, new Set([...seen, ref]));
256
- }
257
- const instanceOf = getMjstInstanceOf(schema);
258
- if (instanceOf === 'Date')
259
- return new Date(0);
260
- const primitive = getMjstPrimitive(schema);
261
- if (primitive === 'bigint')
262
- return 0n;
263
- // `allOf` must satisfy every branch at once, so derive from a single schema
264
- // that merges the branches (and the node's own keywords) rather than picking
265
- // one branch — picking one would ignore the others' constraints.
266
- if (hasAllOf(schema))
267
- return deriveExample(mergeAllOf(schema), rootSchema, seen);
268
- if (hasOneOf(schema) && schema.oneOf[0] !== undefined)
269
- return deriveExample(schema.oneOf[0], rootSchema, seen);
270
- if (hasAnyOf(schema) && schema.anyOf[0] !== undefined)
271
- return deriveExample(schema.anyOf[0], rootSchema, seen);
272
- if (hasType(schema))
273
- return deriveForType(schema.type, schema, rootSchema, seen);
274
- // Multi-type schemas (`type: ['string', 'null']`) derive from their first
275
- // member type; `hasType` only matches a single string `type`.
276
- if (Array.isArray(schema.type) && schema.type.length > 0) {
277
- return deriveForType(schema.type[0], schema, rootSchema, seen);
278
- }
193
+ if (!isSchemaObject(schema))
279
194
  return null;
195
+ if (hasConst(schema))
196
+ return schema.const;
197
+ if (hasExamples(schema) && Array.isArray(schema.examples) && schema.examples.length > 0)
198
+ return schema.examples[0];
199
+ if (hasDefault(schema))
200
+ return schema.default;
201
+ if (hasEnum(schema) && schema.enum.length > 0) {
202
+ const fitting = schema.enum.find((value) => satisfiesScalarConstraints(schema, value));
203
+ return fitting !== void 0 ? fitting : schema.enum[0];
204
+ }
205
+ if (hasRef(schema)) {
206
+ const ref = schema.$ref;
207
+ if (seen.has(ref) || !rootSchema)
208
+ return null;
209
+ const resolved = resolveRef(ref, rootSchema);
210
+ if (!resolved)
211
+ return null;
212
+ return deriveExample(resolved, rootSchema, /* @__PURE__ */ new Set([...seen, ref]));
213
+ }
214
+ const instanceOf = getMjstInstanceOf(schema);
215
+ if (instanceOf === "Date")
216
+ return /* @__PURE__ */ new Date(0);
217
+ const primitive = getMjstPrimitive(schema);
218
+ if (primitive === "bigint")
219
+ return 0n;
220
+ if (hasAllOf(schema))
221
+ return deriveExample(mergeAllOf(schema), rootSchema, seen);
222
+ if (hasOneOf(schema) && schema.oneOf[0] !== void 0)
223
+ return deriveExample(schema.oneOf[0], rootSchema, seen);
224
+ if (hasAnyOf(schema) && schema.anyOf[0] !== void 0)
225
+ return deriveExample(schema.anyOf[0], rootSchema, seen);
226
+ if (hasType(schema))
227
+ return deriveForType(schema.type, schema, rootSchema, seen);
228
+ if (Array.isArray(schema.type) && schema.type.length > 0) {
229
+ return deriveForType(schema.type[0], schema, rootSchema, seen);
230
+ }
231
+ return null;
280
232
  };
281
- /** The applicator keywords whose satisfaction is reconciled by {@link refineExample}. */
282
- const REFINED_APPLICATORS = ['if', 'then', 'else', 'not', 'oneOf'];
283
- /** A shallow copy of `schema` with the refined applicator keywords removed. */
233
+ const REFINED_APPLICATORS = ["if", "then", "else", "not", "oneOf"];
284
234
  const structuralOnly = (schema) => {
285
- const clone = { ...schema };
286
- for (const key of REFINED_APPLICATORS)
287
- delete clone[key];
288
- return clone;
235
+ const clone = { ...schema };
236
+ for (const key of REFINED_APPLICATORS)
237
+ delete clone[key];
238
+ return clone;
289
239
  };
290
- /**
291
- * Reconciles keywords the structural deriver can't satisfy on its own
292
- * (`if`/`then`/`else`, `not`, `oneOf` exclusivity). It validates the structural
293
- * `base` against the full schema and, if it fails, tries alternative candidates —
294
- * each `oneOf` branch, and the `then`/`else` branches merged with the structural
295
- * siblings — returning the first that validates. Falls back to `base` when none
296
- * do (a best-effort for schemas that can't be satisfied structurally, e.g. an
297
- * adversarial `not`).
298
- */
299
240
  const refineExample = (schema, base, rootSchema, seen) => {
300
- const check = makeInstanceCheck(schema, rootSchema);
301
- if (check(base))
302
- return base;
303
- const raw = schema;
304
- const structural = structuralOnly(schema);
305
- const combine = (branch) => deriveExample({ allOf: [structural, branch] }, rootSchema, seen);
306
- const candidates = [];
307
- if (hasOneOf(schema)) {
308
- for (const branch of schema.oneOf)
309
- if (branch !== undefined)
310
- candidates.push(combine(branch));
311
- }
312
- if ('if' in raw) {
313
- if (raw['then'] !== undefined)
314
- candidates.push(combine(raw['then']));
315
- if (raw['else'] !== undefined)
316
- candidates.push(combine(raw['else']));
317
- // `if` failing (so neither `then` nor a matched `else` applies) is also valid.
318
- candidates.push(deriveExample(structural, rootSchema, seen));
319
- }
320
- for (const candidate of candidates)
321
- if (check(candidate))
322
- return candidate;
241
+ const check = makeInstanceCheck(schema, rootSchema);
242
+ if (check(base))
323
243
  return base;
244
+ const raw = schema;
245
+ const structural = structuralOnly(schema);
246
+ const combine = (branch) => deriveExample({ allOf: [structural, branch] }, rootSchema, seen);
247
+ const candidates = [];
248
+ if (hasOneOf(schema)) {
249
+ for (const branch of schema.oneOf)
250
+ if (branch !== void 0)
251
+ candidates.push(combine(branch));
252
+ }
253
+ if ("if" in raw) {
254
+ if (raw["then"] !== void 0)
255
+ candidates.push(combine(raw["then"]));
256
+ if (raw["else"] !== void 0)
257
+ candidates.push(combine(raw["else"]));
258
+ candidates.push(deriveExample(structural, rootSchema, seen));
259
+ }
260
+ for (const candidate of candidates)
261
+ if (check(candidate))
262
+ return candidate;
263
+ return base;
324
264
  };
325
- /**
326
- * True when a candidate value (e.g. an `enum`/`const` member) satisfies the
327
- * node's simple string-length and numeric-range constraints. Used to pick an
328
- * `enum` member that also meets a sibling `minLength`/`minimum`/etc.
329
- */
330
265
  const satisfiesScalarConstraints = (schema, value) => {
331
- if (typeof value === 'string') {
332
- if (hasMinLength(schema) && value.length < schema.minLength)
333
- return false;
334
- if (hasMaxLength(schema) && value.length > schema.maxLength)
335
- return false;
336
- }
337
- else if (typeof value === 'number') {
338
- if (hasMinimum(schema) && value < schema.minimum)
339
- return false;
340
- if (hasMaximum(schema) && value > schema.maximum)
341
- return false;
342
- if (hasExclusiveMinimum(schema) && value <= schema.exclusiveMinimum)
343
- return false;
344
- if (hasExclusiveMaximum(schema) && value >= schema.exclusiveMaximum)
345
- return false;
346
- if (hasMultipleOf(schema) && schema.multipleOf > 0 && value % schema.multipleOf !== 0)
347
- return false;
348
- }
349
- return true;
266
+ if (typeof value === "string") {
267
+ if (hasMinLength(schema) && value.length < schema.minLength)
268
+ return false;
269
+ if (hasMaxLength(schema) && value.length > schema.maxLength)
270
+ return false;
271
+ } else if (typeof value === "number") {
272
+ if (hasMinimum(schema) && value < schema.minimum)
273
+ return false;
274
+ if (hasMaximum(schema) && value > schema.maximum)
275
+ return false;
276
+ if (hasExclusiveMinimum(schema) && value <= schema.exclusiveMinimum)
277
+ return false;
278
+ if (hasExclusiveMaximum(schema) && value >= schema.exclusiveMaximum)
279
+ return false;
280
+ if (hasMultipleOf(schema) && schema.multipleOf > 0 && value % schema.multipleOf !== 0)
281
+ return false;
282
+ }
283
+ return true;
350
284
  };
351
- /** Derives a canonical value for a single declared `type`. */
352
285
  const deriveForType = (type, schema, rootSchema, seen) => {
353
- switch (type) {
354
- case 'string':
355
- return exampleString(schema);
356
- case 'number':
357
- case 'integer':
358
- return deriveNumber(schema, type === 'integer');
359
- case 'boolean':
360
- return true;
361
- case 'null':
362
- return null;
363
- case 'array':
364
- return deriveArray(schema, rootSchema, seen);
365
- case 'object':
366
- return deriveObject(schema, rootSchema, seen);
367
- default:
368
- return null;
369
- }
286
+ switch (type) {
287
+ case "string":
288
+ return exampleString(schema);
289
+ case "number":
290
+ case "integer":
291
+ return deriveNumber(schema, type === "integer");
292
+ case "boolean":
293
+ return true;
294
+ case "null":
295
+ return null;
296
+ case "array":
297
+ return deriveArray(schema, rootSchema, seen);
298
+ case "object":
299
+ return deriveObject(schema, rootSchema, seen);
300
+ default:
301
+ return null;
302
+ }
370
303
  };
371
- /**
372
- * Builds an object value honouring `properties`/`required`/`additionalProperties`
373
- * plus the presence-gated and key-shaped keywords: `patternProperties` and
374
- * `additionalProperties` pick the value schema for synthesized keys,
375
- * `propertyNames` constrains those keys, `dependentRequired`/`dependentSchemas`
376
- * add keys once their trigger is present, and `minProperties`/`maxProperties`
377
- * bound the key count.
378
- */
379
304
  const deriveObject = (schema, rootSchema, seen) => {
380
- const out = {};
381
- const patternEntries = hasPatternProperties(schema)
382
- ? Object.entries(schema.patternProperties).flatMap(([source, sub]) => {
383
- try {
384
- return [[new RegExp(source), sub]];
385
- }
386
- catch {
387
- return [];
388
- }
389
- })
390
- : [];
391
- const additional = hasAdditionalProperties(schema) ? schema.additionalProperties : false;
392
- const additionalSchema = isSchemaObject(additional) ? additional : undefined;
393
- const additionalClosed = hasAdditionalProperties(schema) && schema.additionalProperties === false;
394
- // With `additionalProperties: false`, extra keys are still allowed when they
395
- // match a `patternProperties` entry; only a fully closed object forbids all.
396
- const extrasAllowed = !additionalClosed || patternEntries.length > 0;
397
- const nameCheck = hasPropertyNames(schema) ? makeInstanceCheck(schema.propertyNames, rootSchema) : undefined;
398
- // The value schema for a key not declared in `properties`: the matching
399
- // `patternProperties` (intersected when several match), else `additionalProperties`.
400
- const valueSchemaFor = (key) => {
401
- const matches = patternEntries.filter(([re]) => re.test(key)).map(([, sub]) => sub);
402
- if (matches.length === 1)
403
- return matches[0];
404
- if (matches.length > 1)
405
- return { allOf: matches };
406
- return additionalSchema;
407
- };
408
- const addKey = (key) => {
409
- const sub = valueSchemaFor(key);
410
- out[key] = sub !== undefined ? deriveExample(sub, rootSchema, seen) : null;
411
- };
412
- if (hasProperties(schema)) {
413
- for (const [key, propSchema] of Object.entries(schema.properties)) {
414
- out[key] = deriveExample(propSchema, rootSchema, seen);
415
- }
305
+ const out = {};
306
+ const patternEntries = hasPatternProperties(schema) ? Object.entries(schema.patternProperties).flatMap(([source, sub]) => {
307
+ try {
308
+ return [[new RegExp(source), sub]];
309
+ } catch {
310
+ return [];
416
311
  }
417
- // A required key with no `properties` entry still needs a value.
418
- if (hasRequired(schema)) {
419
- for (const key of schema.required)
420
- if (!(key in out))
421
- addKey(key);
312
+ }) : [];
313
+ const additional = hasAdditionalProperties(schema) ? schema.additionalProperties : false;
314
+ const additionalSchema = isSchemaObject(additional) ? additional : void 0;
315
+ const additionalClosed = hasAdditionalProperties(schema) && schema.additionalProperties === false;
316
+ const extrasAllowed = !additionalClosed || patternEntries.length > 0;
317
+ const nameCheck = hasPropertyNames(schema) ? makeInstanceCheck(schema.propertyNames, rootSchema) : void 0;
318
+ const valueSchemaFor = (key) => {
319
+ const matches = patternEntries.filter(([re]) => re.test(key)).map(([, sub]) => sub);
320
+ if (matches.length === 1)
321
+ return matches[0];
322
+ if (matches.length > 1)
323
+ return { allOf: matches };
324
+ return additionalSchema;
325
+ };
326
+ const addKey = (key) => {
327
+ const sub = valueSchemaFor(key);
328
+ out[key] = sub !== void 0 ? deriveExample(sub, rootSchema, seen) : null;
329
+ };
330
+ if (hasProperties(schema)) {
331
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
332
+ out[key] = deriveExample(propSchema, rootSchema, seen);
422
333
  }
423
- // `dependentRequired`: once a trigger key is present, its dependencies must be too.
424
- if (hasDependentRequired(schema)) {
425
- for (const [trigger, deps] of Object.entries(schema.dependentRequired)) {
426
- if (!(trigger in out))
427
- continue;
428
- for (const dep of deps)
429
- if (!(dep in out))
430
- addKey(dep);
431
- }
334
+ }
335
+ if (hasRequired(schema)) {
336
+ for (const key of schema.required)
337
+ if (!(key in out))
338
+ addKey(key);
339
+ }
340
+ if (hasDependentRequired(schema)) {
341
+ for (const [trigger, deps] of Object.entries(schema.dependentRequired)) {
342
+ if (!(trigger in out))
343
+ continue;
344
+ for (const dep of deps)
345
+ if (!(dep in out))
346
+ addKey(dep);
432
347
  }
433
- // `dependentSchemas`: once a trigger key is present, the object must also
434
- // satisfy the dependency subschema — apply its `properties`/`required`.
435
- if (hasDependentSchemas(schema)) {
436
- for (const [trigger, sub] of Object.entries(schema.dependentSchemas)) {
437
- if (trigger in out && isSchemaObject(sub))
438
- applyDependentSchema(out, sub, rootSchema, seen);
439
- }
348
+ }
349
+ if (hasDependentSchemas(schema)) {
350
+ for (const [trigger, sub] of Object.entries(schema.dependentSchemas)) {
351
+ if (trigger in out && isSchemaObject(sub))
352
+ applyDependentSchema(out, sub, rootSchema, seen);
440
353
  }
441
- // Synthesize filler keys to reach `minProperties` when extras are allowed.
442
- if (hasMinProperties(schema) && extrasAllowed) {
443
- let n = 0;
444
- let guard = 0;
445
- while (Object.keys(out).length < schema.minProperties && guard++ < schema.minProperties + 50) {
446
- const key = synthKey(n++, patternEntries, schema, nameCheck);
447
- if (key === undefined || key in out)
448
- continue;
449
- addKey(key);
450
- }
354
+ }
355
+ if (hasMinProperties(schema) && extrasAllowed) {
356
+ let n = 0;
357
+ let guard = 0;
358
+ while (Object.keys(out).length < schema.minProperties && guard++ < schema.minProperties + 50) {
359
+ const key = synthKey(n++, patternEntries, schema, nameCheck);
360
+ if (key === void 0 || key in out)
361
+ continue;
362
+ addKey(key);
451
363
  }
452
- // Enforce `maxProperties` by dropping keys that aren't required (directly or via
453
- // a present `dependentRequired` trigger) until the count fits.
454
- if (hasMaxProperties(schema))
455
- enforceMaxProperties(out, schema, schema.maxProperties);
456
- return out;
364
+ }
365
+ if (hasMaxProperties(schema))
366
+ enforceMaxProperties(out, schema, schema.maxProperties);
367
+ return out;
457
368
  };
458
- /** Applies a `dependentSchemas` branch's object shape (`properties`/`required`) in place. */
459
369
  const applyDependentSchema = (out, sub, rootSchema, seen) => {
460
- if (hasProperties(sub)) {
461
- for (const [key, propSchema] of Object.entries(sub.properties)) {
462
- if (!(key in out))
463
- out[key] = deriveExample(propSchema, rootSchema, seen);
464
- }
370
+ if (hasProperties(sub)) {
371
+ for (const [key, propSchema] of Object.entries(sub.properties)) {
372
+ if (!(key in out))
373
+ out[key] = deriveExample(propSchema, rootSchema, seen);
465
374
  }
466
- if (hasRequired(sub)) {
467
- const propSchemas = hasProperties(sub) ? sub.properties : {};
468
- for (const key of sub.required) {
469
- if (key in out)
470
- continue;
471
- const propSchema = propSchemas[key];
472
- out[key] = propSchema !== undefined ? deriveExample(propSchema, rootSchema, seen) : null;
473
- }
375
+ }
376
+ if (hasRequired(sub)) {
377
+ const propSchemas = hasProperties(sub) ? sub.properties : {};
378
+ for (const key of sub.required) {
379
+ if (key in out)
380
+ continue;
381
+ const propSchema = propSchemas[key];
382
+ out[key] = propSchema !== void 0 ? deriveExample(propSchema, rootSchema, seen) : null;
474
383
  }
384
+ }
475
385
  };
476
- /**
477
- * Produces a candidate key name for the i-th synthesized property. Prefers a key
478
- * matching a `patternProperties` entry (so the entry supplies its value schema),
479
- * then one matching `propertyNames`, then a plain `extraN`. Returns `undefined`
480
- * when the candidate can't satisfy a `propertyNames` constraint.
481
- */
482
386
  const synthKey = (i, patternEntries, schema, nameCheck) => {
483
- // Distinct keys are produced by *repeating* a base seed (`x-` `x-x-`) rather
484
- // than appending a digit: repetition is far likelier to keep matching a key
485
- // pattern (`^[a-z]+$`, `^x-`) that a digit suffix would break.
486
- const vary = (seed) => seed.repeat(i + 1);
487
- const seeds = [];
488
- for (const [re] of patternEntries) {
489
- const sampled = sampleFromPattern(re.source, 0);
490
- if (sampled !== undefined && sampled.length > 0)
491
- seeds.push(vary(sampled));
492
- }
493
- const propertyNames = isSchemaObject(schema) && hasPropertyNames(schema) ? schema.propertyNames : undefined;
494
- if (propertyNames !== undefined && isSchemaObject(propertyNames) && hasPattern(propertyNames)) {
495
- const sampled = sampleFromPattern(propertyNames.pattern, 0);
496
- if (sampled !== undefined && sampled.length > 0)
497
- seeds.push(vary(sampled));
498
- }
499
- seeds.push(vary('extra'));
500
- for (const seed of seeds) {
501
- if (!nameCheck || nameCheck(seed))
502
- return seed;
503
- }
504
- return undefined;
387
+ const vary = (seed) => seed.repeat(i + 1);
388
+ const seeds = [];
389
+ for (const [re] of patternEntries) {
390
+ const sampled = sampleFromPattern(re.source, 0);
391
+ if (sampled !== void 0 && sampled.length > 0)
392
+ seeds.push(vary(sampled));
393
+ }
394
+ const propertyNames = isSchemaObject(schema) && hasPropertyNames(schema) ? schema.propertyNames : void 0;
395
+ if (propertyNames !== void 0 && isSchemaObject(propertyNames) && hasPattern(propertyNames)) {
396
+ const sampled = sampleFromPattern(propertyNames.pattern, 0);
397
+ if (sampled !== void 0 && sampled.length > 0)
398
+ seeds.push(vary(sampled));
399
+ }
400
+ seeds.push(vary("extra"));
401
+ for (const seed of seeds) {
402
+ if (!nameCheck || nameCheck(seed))
403
+ return seed;
404
+ }
405
+ return void 0;
505
406
  };
506
- /** Drops non-required keys until `out` has at most `max` properties. */
507
407
  const enforceMaxProperties = (out, schema, max) => {
508
- if (Object.keys(out).length <= max)
509
- return;
510
- const protectedKeys = new Set(hasRequired(schema) ? schema.required : []);
511
- if (hasDependentRequired(schema)) {
512
- for (const [trigger, deps] of Object.entries(schema.dependentRequired)) {
513
- if (trigger in out)
514
- for (const dep of deps)
515
- protectedKeys.add(dep);
516
- }
517
- }
518
- for (const key of Object.keys(out)) {
519
- if (Object.keys(out).length <= max)
520
- break;
521
- if (!protectedKeys.has(key))
522
- delete out[key];
408
+ if (Object.keys(out).length <= max)
409
+ return;
410
+ const protectedKeys = new Set(hasRequired(schema) ? schema.required : []);
411
+ if (hasDependentRequired(schema)) {
412
+ for (const [trigger, deps] of Object.entries(schema.dependentRequired)) {
413
+ if (trigger in out)
414
+ for (const dep of deps)
415
+ protectedKeys.add(dep);
523
416
  }
417
+ }
418
+ for (const key of Object.keys(out)) {
419
+ if (Object.keys(out).length <= max)
420
+ break;
421
+ if (!protectedKeys.has(key))
422
+ delete out[key];
423
+ }
524
424
  };
525
- /**
526
- * Picks a number satisfying the node's bounds and `multipleOf`. Starts at the
527
- * lower bound (or 0 when unbounded), nudges past an exclusive bound, then rounds
528
- * up to the nearest multiple. An unsatisfiable range (e.g. `minimum > maximum`)
529
- * can't be met and falls back to the lower bound.
530
- */
531
425
  const deriveNumber = (schema, isInteger) => {
532
- const step = isInteger ? 1 : 0.5;
533
- let lo = -Infinity;
534
- if (hasMinimum(schema))
535
- lo = Math.max(lo, schema.minimum);
536
- if (hasExclusiveMinimum(schema))
537
- lo = Math.max(lo, schema.exclusiveMinimum + step);
538
- const hi = hasMaximum(schema)
539
- ? schema.maximum
540
- : hasExclusiveMaximum(schema)
541
- ? schema.exclusiveMaximum - step
542
- : Number.POSITIVE_INFINITY;
543
- // Base candidate: the lower bound, or 0 (or the upper bound) when unbounded below.
544
- let value = Number.isFinite(lo) ? lo : Number.isFinite(hi) ? Math.min(0, hi) : 0;
545
- if (isInteger)
546
- value = Math.ceil(value);
547
- if (hasMultipleOf(schema) && schema.multipleOf > 0) {
548
- const m = schema.multipleOf;
549
- value = Math.ceil(value / m - 1e-9) * m;
550
- // Rounding up can overshoot the upper bound; drop to the largest multiple
551
- // that fits. (If even that falls below `lo`, the range has no multiple — an
552
- // unsatisfiable schema — and we return the in-range candidate as best effort.)
553
- if (value > hi && Number.isFinite(hi))
554
- value = Math.floor(hi / m + 1e-9) * m;
555
- }
556
- // `+ 0` normalizes a `-0` (which `Math.ceil`/`Math.floor` can produce) to `0`.
557
- return (isInteger ? Math.round(value) : value) + 0;
426
+ const step = isInteger ? 1 : 0.5;
427
+ let lo = -Infinity;
428
+ if (hasMinimum(schema))
429
+ lo = Math.max(lo, schema.minimum);
430
+ if (hasExclusiveMinimum(schema))
431
+ lo = Math.max(lo, schema.exclusiveMinimum + step);
432
+ const hi = hasMaximum(schema) ? schema.maximum : hasExclusiveMaximum(schema) ? schema.exclusiveMaximum - step : Number.POSITIVE_INFINITY;
433
+ let value = Number.isFinite(lo) ? lo : Number.isFinite(hi) ? Math.min(0, hi) : 0;
434
+ if (isInteger)
435
+ value = Math.ceil(value);
436
+ if (hasMultipleOf(schema) && schema.multipleOf > 0) {
437
+ const m = schema.multipleOf;
438
+ value = Math.ceil(value / m - 1e-9) * m;
439
+ if (value > hi && Number.isFinite(hi))
440
+ value = Math.floor(hi / m + 1e-9) * m;
441
+ }
442
+ return (isInteger ? Math.round(value) : value) + 0;
558
443
  };
559
- /**
560
- * Derives an array value. A tuple schema (`prefixItems`, or the draft-07
561
- * array-form `items`) derives one value per position; a uniform array repeats a
562
- * single item value. The count is clamped into `[minItems, maxItems]` so a
563
- * `maxItems: 0` yields `[]` and a `minItems` is always met.
564
- */
565
444
  const deriveArray = (schema, rootSchema, seen) => {
566
- const items = hasItems(schema) ? schema.items : undefined;
567
- const prefixItems = schema['prefixItems'];
568
- // A tuple is `prefixItems` (2020-12) or the draft-07 array-form `items`.
569
- const prefix = Array.isArray(prefixItems)
570
- ? prefixItems
571
- : Array.isArray(items)
572
- ? items
573
- : undefined;
574
- const min = hasMinItems(schema) ? schema.minItems : 0;
575
- const max = hasMaxItems(schema) ? schema.maxItems : Number.POSITIVE_INFINITY;
576
- // The rest/uniform element schema (the singular object-form `items`). A boolean
577
- // `items` (`true`/`false`) is not a value-producing schema, so it is not `rest`.
578
- const rest = items !== undefined && !Array.isArray(items) && isSchemaObject(items) ? items : undefined;
579
- if (prefix) {
580
- const tuple = prefix.map((item) => deriveExample(item, rootSchema, seen));
581
- // Pad up to `minItems`: with the rest schema when one is present, otherwise
582
- // (in 2020-12 additional items past `prefixItems` are unconstrained unless
583
- // `items: false`) with a plain `null`.
584
- const itemsClosed = schema['items'] === false;
585
- while (tuple.length < min && tuple.length < max) {
586
- if (rest !== undefined)
587
- tuple.push(deriveExample(rest, rootSchema, seen));
588
- else if (itemsClosed)
589
- break;
590
- else
591
- tuple.push(null);
592
- }
593
- return tuple.length > max ? tuple.slice(0, max) : tuple;
445
+ const items = hasItems(schema) ? schema.items : void 0;
446
+ const prefixItems = schema["prefixItems"];
447
+ const prefix = Array.isArray(prefixItems) ? prefixItems : Array.isArray(items) ? items : void 0;
448
+ const min = hasMinItems(schema) ? schema.minItems : 0;
449
+ const max = hasMaxItems(schema) ? schema.maxItems : Number.POSITIVE_INFINITY;
450
+ const rest = items !== void 0 && !Array.isArray(items) && isSchemaObject(items) ? items : void 0;
451
+ if (prefix) {
452
+ const tuple = prefix.map((item) => deriveExample(item, rootSchema, seen));
453
+ const itemsClosed = schema["items"] === false;
454
+ while (tuple.length < min && tuple.length < max) {
455
+ if (rest !== void 0)
456
+ tuple.push(deriveExample(rest, rootSchema, seen));
457
+ else if (itemsClosed)
458
+ break;
459
+ else
460
+ tuple.push(null);
594
461
  }
595
- const raw = schema;
596
- const containsRaw = raw['contains'];
597
- const contains = containsRaw !== undefined && isSchemaObject(containsRaw) ? containsRaw : undefined;
598
- const minContains = contains !== undefined ? (typeof raw['minContains'] === 'number' ? raw['minContains'] : 1) : 0;
599
- const unique = hasUniqueItems(schema) && schema.uniqueItems === true;
600
- // The element schema: the uniform `items`, else the `contains` subschema.
601
- const elem = rest ?? contains;
602
- // Prefer a non-empty example, satisfy `minItems` and `minContains`, never exceed `maxItems`.
603
- const count = Math.min(Math.max(min, minContains, max === 0 ? 0 : 1), max);
604
- const result = [];
605
- for (let i = 0; i < count; i++) {
606
- // Make the first `minContains` items satisfy `contains`; the rest use `items`.
607
- const itemSchema = contains !== undefined && i < minContains ? contains : elem;
608
- const base = itemSchema !== undefined ? deriveExample(itemSchema, rootSchema, seen) : null;
609
- result.push(unique ? distinctify(base, i, itemSchema) : base);
610
- }
611
- return result;
462
+ return tuple.length > max ? tuple.slice(0, max) : tuple;
463
+ }
464
+ const raw = schema;
465
+ const containsRaw = raw["contains"];
466
+ const contains = containsRaw !== void 0 && isSchemaObject(containsRaw) ? containsRaw : void 0;
467
+ const minContains = contains !== void 0 ? typeof raw["minContains"] === "number" ? raw["minContains"] : 1 : 0;
468
+ const unique = hasUniqueItems(schema) && schema.uniqueItems === true;
469
+ const elem = rest ?? contains;
470
+ const count = Math.min(Math.max(min, minContains, max === 0 ? 0 : 1), max);
471
+ const result = [];
472
+ for (let i = 0; i < count; i++) {
473
+ const itemSchema = contains !== void 0 && i < minContains ? contains : elem;
474
+ const base = itemSchema !== void 0 ? deriveExample(itemSchema, rootSchema, seen) : null;
475
+ result.push(unique ? distinctify(base, i, itemSchema) : base);
476
+ }
477
+ return result;
612
478
  };
613
- /**
614
- * Returns a value distinct from earlier ones for index `i`, used to satisfy
615
- * `uniqueItems`, while staying within the item schema's constraints: numbers step
616
- * by `multipleOf` (so the perturbed values remain valid multiples) rather than by
617
- * 1, strings are suffixed, booleans alternated. Values that can't be cheaply
618
- * varied are returned as-is (a best-effort the generated `fast-check` arbitrary
619
- * covers fully).
620
- */
621
479
  const distinctify = (base, i, itemSchema) => {
622
- if (i === 0)
623
- return base;
624
- if (typeof base === 'number') {
625
- const step = itemSchema && isSchemaObject(itemSchema) && hasMultipleOf(itemSchema) && itemSchema.multipleOf > 0
626
- ? itemSchema.multipleOf
627
- : 1;
628
- return base + i * step;
629
- }
630
- if (typeof base === 'string')
631
- return `${base}${i}`;
632
- if (typeof base === 'boolean')
633
- return i % 2 === 1 ? !base : base;
480
+ if (i === 0)
634
481
  return base;
482
+ if (typeof base === "number") {
483
+ const step = itemSchema && isSchemaObject(itemSchema) && hasMultipleOf(itemSchema) && itemSchema.multipleOf > 0 ? itemSchema.multipleOf : 1;
484
+ return base + i * step;
485
+ }
486
+ if (typeof base === "string")
487
+ return `${base}${i}`;
488
+ if (typeof base === "boolean")
489
+ return i % 2 === 1 ? !base : base;
490
+ return base;
635
491
  };
636
- /**
637
- * Flattens an `allOf` into a single schema: object `properties` are merged and
638
- * `required` unioned across branches, while scalar keywords from later branches
639
- * (and the node's own keywords) win. `allOf` itself is dropped so the merged
640
- * schema derives directly.
641
- */
642
- const TIGHTEST = new Map([
643
- ['minimum', 'max'],
644
- ['exclusiveMinimum', 'max'],
645
- ['minLength', 'max'],
646
- ['minItems', 'max'],
647
- ['minProperties', 'max'],
648
- ['maximum', 'min'],
649
- ['exclusiveMaximum', 'min'],
650
- ['maxLength', 'min'],
651
- ['maxItems', 'min'],
652
- ['maxProperties', 'min'],
492
+ const TIGHTEST = /* @__PURE__ */ new Map([
493
+ ["minimum", "max"],
494
+ ["exclusiveMinimum", "max"],
495
+ ["minLength", "max"],
496
+ ["minItems", "max"],
497
+ ["minProperties", "max"],
498
+ ["maximum", "min"],
499
+ ["exclusiveMaximum", "min"],
500
+ ["maxLength", "min"],
501
+ ["maxItems", "min"],
502
+ ["maxProperties", "min"]
653
503
  ]);
654
- export const mergeAllOf = (schema) => {
655
- const branches = hasAllOf(schema) ? schema.allOf : [];
656
- const merged = {};
657
- const properties = {};
658
- const required = new Set();
659
- for (const branch of [...branches, schema]) {
660
- if (!isSchemaObject(branch))
661
- continue;
662
- for (const [key, value] of Object.entries(branch)) {
663
- if (key === 'allOf')
664
- continue;
665
- if (key === 'properties' && value && typeof value === 'object') {
666
- // The same property constrained by several branches must satisfy all of
667
- // them, so collect each schema and combine them below rather than letting
668
- // a later branch's schema silently replace an earlier one.
669
- for (const [prop, propSchema] of Object.entries(value)) {
670
- const bucket = properties[prop];
671
- if (bucket)
672
- bucket.push(propSchema);
673
- else
674
- properties[prop] = [propSchema];
675
- }
676
- }
677
- else if (key === 'required' && Array.isArray(value)) {
678
- for (const r of value)
679
- required.add(r);
680
- }
681
- else if (key === 'enum' && Array.isArray(value)) {
682
- // A value must be in *every* branch's enum, so intersect rather than let
683
- // a later branch's enum replace an earlier one (which could pick a member
684
- // the earlier branch rejects).
685
- merged['enum'] = Array.isArray(merged['enum'])
686
- ? merged['enum'].filter((member) => value.includes(member))
687
- : value;
688
- }
689
- else if (TIGHTEST.has(key) && typeof value === 'number' && typeof merged[key] === 'number') {
690
- // Numeric bounds from different branches combine to the tightest one.
691
- merged[key] = TIGHTEST.get(key) === 'max' ? Math.max(merged[key], value) : Math.min(merged[key], value);
692
- }
693
- else {
694
- merged[key] = value;
695
- }
504
+ const mergeAllOf = (schema) => {
505
+ const branches = hasAllOf(schema) ? schema.allOf : [];
506
+ const merged = {};
507
+ const properties = {};
508
+ const required = /* @__PURE__ */ new Set();
509
+ for (const branch of [...branches, schema]) {
510
+ if (!isSchemaObject(branch))
511
+ continue;
512
+ for (const [key, value] of Object.entries(branch)) {
513
+ if (key === "allOf")
514
+ continue;
515
+ if (key === "properties" && value && typeof value === "object") {
516
+ for (const [prop, propSchema] of Object.entries(value)) {
517
+ const bucket = properties[prop];
518
+ if (bucket)
519
+ bucket.push(propSchema);
520
+ else
521
+ properties[prop] = [propSchema];
696
522
  }
523
+ } else if (key === "required" && Array.isArray(value)) {
524
+ for (const r of value)
525
+ required.add(r);
526
+ } else if (key === "enum" && Array.isArray(value)) {
527
+ merged["enum"] = Array.isArray(merged["enum"]) ? merged["enum"].filter((member) => value.includes(member)) : value;
528
+ } else if (TIGHTEST.has(key) && typeof value === "number" && typeof merged[key] === "number") {
529
+ merged[key] = TIGHTEST.get(key) === "max" ? Math.max(merged[key], value) : Math.min(merged[key], value);
530
+ } else {
531
+ merged[key] = value;
532
+ }
697
533
  }
698
- const mergedProps = {};
699
- for (const [prop, schemas] of Object.entries(properties)) {
700
- mergedProps[prop] = schemas.length === 1 ? schemas[0] : { allOf: schemas };
701
- }
702
- if (Object.keys(mergedProps).length > 0)
703
- merged['properties'] = mergedProps;
704
- if (required.size > 0)
705
- merged['required'] = [...required];
706
- return merged;
534
+ }
535
+ const mergedProps = {};
536
+ for (const [prop, schemas] of Object.entries(properties)) {
537
+ mergedProps[prop] = schemas.length === 1 ? schemas[0] : { allOf: schemas };
538
+ }
539
+ if (Object.keys(mergedProps).length > 0)
540
+ merged["properties"] = mergedProps;
541
+ if (required.size > 0)
542
+ merged["required"] = [...required];
543
+ return merged;
707
544
  };
708
- /**
709
- * Serializes a derived value into a TypeScript source expression. Handles the
710
- * non-JSON values `deriveExample` can produce (`Date`, `bigint`) in addition to
711
- * plain JSON.
712
- */
713
- export const serializeValue = (value) => {
714
- if (typeof value === 'bigint')
715
- return `${value}n`;
716
- if (value instanceof Date)
717
- return `new Date(${JSON.stringify(value.toISOString())})`;
718
- if (Array.isArray(value))
719
- return `[${value.map(serializeValue).join(', ')}]`;
720
- if (value !== null && typeof value === 'object') {
721
- const entries = Object.entries(value)
722
- .filter(([, v]) => v !== undefined)
723
- .map(([key, v]) => `${JSON.stringify(key)}: ${serializeValue(v)}`);
724
- return `{ ${entries.join(', ')} }`;
725
- }
726
- return JSON.stringify(value);
545
+ const serializeValue = (value) => {
546
+ if (typeof value === "bigint")
547
+ return `${value}n`;
548
+ if (value instanceof Date)
549
+ return `new Date(${JSON.stringify(value.toISOString())})`;
550
+ if (Array.isArray(value))
551
+ return `[${value.map(serializeValue).join(", ")}]`;
552
+ if (value !== null && typeof value === "object") {
553
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).map(([key, v]) => `${JSON.stringify(key)}: ${serializeValue(v)}`);
554
+ return `{ ${entries.join(", ")} }`;
555
+ }
556
+ return JSON.stringify(value);
557
+ };
558
+ const generateExampleConst = (schema, typeName, rootSchema) => {
559
+ const value = deriveExample(schema, rootSchema);
560
+ return `export const ${exampleName(typeName)}: ${typeName} = ${serializeValue(value)}`;
727
561
  };
728
- /**
729
- * Generates an exported const holding a concrete, schema-valid example value.
730
- *
731
- * @example
732
- * ```typescript
733
- * generateExampleConst({ type: 'object', properties: { name: { type: 'string' } } }, 'Info')
734
- * // export const infoExample: Info = { "name": "string" }
735
- * ```
736
- */
737
- export const generateExampleConst = (schema, typeName, rootSchema) => {
738
- const value = deriveExample(schema, rootSchema);
739
- return `export const ${exampleName(typeName)}: ${typeName} = ${serializeValue(value)}`;
562
+ export {
563
+ deriveExample,
564
+ generateExampleConst,
565
+ mergeAllOf,
566
+ serializeValue
740
567
  };