@amritk/validation 0.4.1 → 0.6.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.
@@ -0,0 +1,33 @@
1
+ import type { JSONSchema } from 'json-schema-typed/draft-2020-12';
2
+ /**
3
+ * Assertions the walk leaves to the validator's own test of the position, and
4
+ * repairs by replacing the position — which is what the loop does with the
5
+ * errors they produce, since each is reported at the position it judges.
6
+ * Their subschemas (an `anyOf` branch, a `not`) are never descended into, so
7
+ * whatever they contain is the validator's business alone.
8
+ */
9
+ export declare const LEAF_KEYWORDS: Set<string>;
10
+ /**
11
+ * Why the single-pass repair walk cannot be emitted for `schema`, or `null` when
12
+ * it can.
13
+ *
14
+ * Asked of every definition in the graph, because the walk crosses files the
15
+ * same way validation does: one definition the walk cannot cover means none of
16
+ * them get it, and `repairX` keeps the loop everywhere. The answer names the
17
+ * keyword and where it is, for the error a schema that asked for something only
18
+ * the walk does (`"x-repair": "ref"`) gets.
19
+ */
20
+ export declare const repairWalkBlocker: (schema: JSONSchema, isRoot?: boolean) => string | null;
21
+ /**
22
+ * Whether any schema in `schema` opts into instance `$ref` following. Values
23
+ * under `default`, `const`, `enum` and `examples` are documents, not schemas, so
24
+ * the same words in an example opt nothing in.
25
+ */
26
+ export declare const asksForRefs: (schema: unknown) => boolean;
27
+ /**
28
+ * Whether `schema` is — directly or through a chain of `$ref`s — a Reference
29
+ * schema that opted into instance `$ref` following with `"x-repair": "ref"`.
30
+ */
31
+ export declare const isReferenceSchema: (schema: JSONSchema, rootSchema: Record<string, unknown> | undefined) => boolean;
32
+ /** The keywords the walk checks through the validator's own test. */
33
+ export declare const leafKeywords: (node: Record<string, unknown>) => Record<string, unknown> | null;
@@ -0,0 +1,148 @@
1
+ import { getMjstInstanceOf, getMjstPrimitive } from "@amritk/helpers/mjst-extension";
2
+ import { readKey } from "@amritk/helpers/read-key";
3
+ import { resolveRef } from "@amritk/helpers/resolve-ref";
4
+ import { isSchemaObject } from "@amritk/helpers/schema-guards";
5
+ const LEAF_KEYWORDS = /* @__PURE__ */ new Set([
6
+ "minLength",
7
+ "maxLength",
8
+ "pattern",
9
+ "format",
10
+ "minimum",
11
+ "maximum",
12
+ "exclusiveMinimum",
13
+ "exclusiveMaximum",
14
+ "multipleOf",
15
+ "maxItems",
16
+ "uniqueItems",
17
+ "contains",
18
+ "minContains",
19
+ "maxContains",
20
+ "minProperties",
21
+ "maxProperties",
22
+ "dependentRequired",
23
+ "not",
24
+ "anyOf",
25
+ "oneOf"
26
+ ]);
27
+ const BLOCKING_KEYWORDS = /* @__PURE__ */ new Set([
28
+ "prefixItems",
29
+ "additionalItems",
30
+ "unevaluatedProperties",
31
+ "unevaluatedItems",
32
+ "dependentSchemas",
33
+ "dependencies",
34
+ "propertyNames",
35
+ "minItems",
36
+ "$dynamicRef",
37
+ "$dynamicAnchor",
38
+ "$recursiveRef",
39
+ "$recursiveAnchor",
40
+ "nullable"
41
+ ]);
42
+ const KNOWN_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null", "array", "object"]);
43
+ const repairWalkBlocker = (schema, isRoot = true) => blockerAt(schema, "#", isRoot);
44
+ const blockerAt = (schema, at, isRoot) => {
45
+ if (typeof schema === "boolean")
46
+ return null;
47
+ if (!isSchemaObject(schema))
48
+ return `${at} is not a schema`;
49
+ const node = schema;
50
+ for (const key of Object.keys(node)) {
51
+ if (BLOCKING_KEYWORDS.has(key))
52
+ return `${at}/${key}`;
53
+ if (key === "x-mjst" && (getMjstInstanceOf(schema) !== void 0 || getMjstPrimitive(schema) !== void 0)) {
54
+ return `${at}/x-mjst`;
55
+ }
56
+ if (key === "$id" && !isRoot)
57
+ return `${at}/$id`;
58
+ }
59
+ const type = readKey(node, "type");
60
+ if (type !== void 0) {
61
+ const types = Array.isArray(type) ? type : [type];
62
+ if (types.length === 0 || !types.every((entry) => typeof entry === "string" && KNOWN_TYPES.has(entry))) {
63
+ return `${at}/type`;
64
+ }
65
+ }
66
+ const items = readKey(node, "items");
67
+ if (Array.isArray(items))
68
+ return `${at}/items`;
69
+ for (const key of ["properties", "patternProperties"]) {
70
+ const map = readKey(node, key);
71
+ if (map === void 0)
72
+ continue;
73
+ if (typeof map !== "object" || map === null || Array.isArray(map))
74
+ return `${at}/${key}`;
75
+ for (const [name, child] of Object.entries(map)) {
76
+ const found = blockerAt(child, `${at}/${key}/${name}`, false);
77
+ if (found !== null)
78
+ return found;
79
+ }
80
+ }
81
+ for (const key of ["additionalProperties", "items", "then", "else"]) {
82
+ const child = readKey(node, key);
83
+ if (child === void 0)
84
+ continue;
85
+ const found = blockerAt(child, `${at}/${key}`, false);
86
+ if (found !== null)
87
+ return found;
88
+ }
89
+ const allOf = readKey(node, "allOf");
90
+ if (allOf !== void 0) {
91
+ if (!Array.isArray(allOf))
92
+ return `${at}/allOf`;
93
+ for (let i = 0; i < allOf.length; i++) {
94
+ const found = blockerAt(allOf[i], `${at}/allOf/${i}`, false);
95
+ if (found !== null)
96
+ return found;
97
+ }
98
+ }
99
+ return null;
100
+ };
101
+ const DATA_KEYWORDS = /* @__PURE__ */ new Set(["default", "const", "enum", "examples", "example"]);
102
+ const asksForRefs = (schema) => {
103
+ if (Array.isArray(schema))
104
+ return schema.some(asksForRefs);
105
+ if (typeof schema !== "object" || schema === null)
106
+ return false;
107
+ const node = schema;
108
+ if (readKey(node, "x-repair") === "ref")
109
+ return true;
110
+ for (const [key, value] of Object.entries(node)) {
111
+ if (!DATA_KEYWORDS.has(key) && asksForRefs(value))
112
+ return true;
113
+ }
114
+ return false;
115
+ };
116
+ const isReferenceSchema = (schema, rootSchema) => {
117
+ let node = schema;
118
+ for (let hops = 0; hops < 32; hops++) {
119
+ if (!isSchemaObject(node))
120
+ return false;
121
+ const record = node;
122
+ if (readKey(record, "x-repair") === "ref")
123
+ return true;
124
+ const ref = readKey(record, "$ref");
125
+ if (typeof ref !== "string" || rootSchema === void 0)
126
+ return false;
127
+ node = resolveRef(ref, rootSchema);
128
+ }
129
+ return false;
130
+ };
131
+ const leafKeywords = (node) => {
132
+ const leaf = {};
133
+ let any = false;
134
+ for (const key of Object.keys(node)) {
135
+ if (!LEAF_KEYWORDS.has(key))
136
+ continue;
137
+ leaf[key] = node[key];
138
+ any = true;
139
+ }
140
+ return any ? leaf : null;
141
+ };
142
+ export {
143
+ LEAF_KEYWORDS,
144
+ asksForRefs,
145
+ isReferenceSchema,
146
+ leafKeywords,
147
+ repairWalkBlocker
148
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritk/validation",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "One generator surface over mjst's types, validators, coercers, repairers and parsers.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -73,11 +73,11 @@
73
73
  }
74
74
  },
75
75
  "dependencies": {
76
- "@amritk/helpers": "^0.24.0",
76
+ "@amritk/helpers": "^0.25.0",
77
77
  "json-schema-typed": "^8.0.1"
78
78
  },
79
79
  "devDependencies": {
80
- "@amritk/runtime-validators": "^0.15.1",
80
+ "@amritk/runtime-validators": "^0.15.2",
81
81
  "@ryoppippi/unplugin-typia": "^2.6.5",
82
82
  "@scalar/openapi-parser": "^0.26.1",
83
83
  "@sinclair/typebox": "^0.34.49",