@amritk/generate-examples 0.6.3 → 0.7.0

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.
@@ -16,23 +16,47 @@ const FILTER_KEYWORDS = /* @__PURE__ */ new Set([
16
16
  "maxProperties",
17
17
  "contains"
18
18
  ]);
19
- const SKIP_RECURSE = /* @__PURE__ */ new Set(["enum", "const", "examples", "default", "$ref", "required", "$defs", "definitions"]);
19
+ const SCHEMA_MAPS = /* @__PURE__ */ new Set([
20
+ "properties",
21
+ "patternProperties",
22
+ "$defs",
23
+ "definitions",
24
+ "dependentSchemas",
25
+ "dependencies"
26
+ ]);
27
+ const SKIP_RECURSE = /* @__PURE__ */ new Set([
28
+ // The data keywords, matching `@amritk/helpers`' `DATA_KEYWORDS` —
29
+ // `example` is OpenAPI 3.0's singular spelling and belongs with `examples`.
30
+ "enum",
31
+ "const",
32
+ "examples",
33
+ "example",
34
+ "default",
35
+ // Plus this walker's own additions, which are not data but are equally not
36
+ // applied here.
37
+ "$ref",
38
+ "required",
39
+ "$defs",
40
+ "definitions"
41
+ ]);
20
42
  const filterAnswers = /* @__PURE__ */ new WeakMap();
21
43
  const needsValidationFilter = (schema) => {
22
- const walk = (node) => {
44
+ const walk = (node, inSchemaMap = false) => {
23
45
  if (Array.isArray(node))
24
- return node.some(walk);
46
+ return node.some((item) => walk(item, inSchemaMap));
25
47
  if (node === null || typeof node !== "object")
26
48
  return false;
27
49
  const obj = node;
28
- for (const key of Object.keys(obj)) {
29
- if (FILTER_KEYWORDS.has(key))
30
- return true;
50
+ if (!inSchemaMap) {
51
+ for (const key of Object.keys(obj)) {
52
+ if (FILTER_KEYWORDS.has(key))
53
+ return true;
54
+ }
31
55
  }
32
- for (const [key, value] of Object.entries(obj)) {
33
- if (SKIP_RECURSE.has(key))
56
+ for (const key of Object.keys(obj)) {
57
+ if (!inSchemaMap && SKIP_RECURSE.has(key))
34
58
  continue;
35
- if (walk(value))
59
+ if (walk(obj[key], !inSchemaMap && SCHEMA_MAPS.has(key)))
36
60
  return true;
37
61
  }
38
62
  return false;
@@ -138,6 +162,7 @@ const spliceDefs = (schema, rootSchema) => {
138
162
  };
139
163
  const WARNING_SCHEMA_LIMIT = 240;
140
164
  const reportedGuardFailures = /* @__PURE__ */ new Set();
165
+ const MAX_REPORTED_GUARD_FAILURES = 1e3;
141
166
  const describeSchema = (schema) => {
142
167
  const serialized = (() => {
143
168
  try {
@@ -155,6 +180,8 @@ const warnUndecidableSchema = (schema, reason) => {
155
180
  ${described}`;
156
181
  if (reportedGuardFailures.has(key))
157
182
  return;
183
+ if (reportedGuardFailures.size >= MAX_REPORTED_GUARD_FAILURES)
184
+ reportedGuardFailures.clear();
158
185
  reportedGuardFailures.add(key);
159
186
  console.warn(`Warning: cannot validate generated values against ${described} \u2014 ${message}. Every candidate value is accepted for this subschema, so constraints only the validator enforces (\`oneOf\`, \`not\`, \`pattern\`, \`if\`/\`then\`, \u2026) go unchecked there and the emitted example may not satisfy its own schema. Generation continues regardless; the usual causes are a \`$ref\` pointing outside this fragment and a \`pattern\` the ReDoS screen rejects.`);
160
187
  };
@@ -166,6 +193,39 @@ const tryGuard = (schema, checkFormats) => {
166
193
  return void 0;
167
194
  }
168
195
  };
196
+ const everyPatternCompiles = (node) => {
197
+ if (node === null || typeof node !== "object")
198
+ return true;
199
+ if (Array.isArray(node))
200
+ return node.every(everyPatternCompiles);
201
+ const compiles = (source) => {
202
+ try {
203
+ new RegExp(source);
204
+ return true;
205
+ } catch {
206
+ return false;
207
+ }
208
+ };
209
+ for (const [key, value] of Object.entries(node)) {
210
+ if (key === "pattern" && typeof value === "string" && !compiles(value))
211
+ return false;
212
+ if (key === "patternProperties" && value !== null && typeof value === "object") {
213
+ if (!Object.keys(value).every(compiles))
214
+ return false;
215
+ }
216
+ if (!everyPatternCompiles(value))
217
+ return false;
218
+ }
219
+ return true;
220
+ };
221
+ const canBuildGuard = (schema, rootSchema) => {
222
+ const resolved = withResolvableDefs(schema, rootSchema);
223
+ if (!everyPatternCompiles(resolved)) {
224
+ warnUndecidableSchema(resolved, "the schema carries a `pattern` that is not a valid regular expression");
225
+ return false;
226
+ }
227
+ return tryGuard(resolved, false) !== void 0;
228
+ };
169
229
  const makeInstanceCheck = (schema, rootSchema, options) => {
170
230
  const resolved = withResolvableDefs(schema, rootSchema);
171
231
  const guard = tryGuard(resolved, options?.checkFormats === true);
@@ -181,6 +241,7 @@ const makeInstanceCheck = (schema, rootSchema, options) => {
181
241
  };
182
242
  };
183
243
  export {
244
+ canBuildGuard,
184
245
  makeInstanceCheck,
185
246
  needsValidationFilter,
186
247
  withResolvableDefs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/generate-examples",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "description": "Generate fast-check arbitraries and example values from JSON Schemas.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -55,8 +55,8 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "json-schema-typed": "^8.0.1",
58
- "@amritk/helpers": "^0.15.3",
59
- "@amritk/runtime-validators": "^0.10.1"
58
+ "@amritk/helpers": "^0.16.0",
59
+ "@amritk/runtime-validators": "^0.12.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "ajv": "^8.17.1"