@maroonedog/luq 2.4.4 → 2.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.
Files changed (64) hide show
  1. package/README.md +64 -10
  2. package/dist/chain/slot-type-guard.js +20 -6
  3. package/dist/chain/slot-type-guard.mjs +20 -6
  4. package/dist/compile/resolve-presence.js +36 -5
  5. package/dist/compile/resolve-presence.mjs +36 -5
  6. package/dist/json-schema/array-keyword-guards.d.ts +31 -0
  7. package/dist/json-schema/array-keyword-guards.js +120 -0
  8. package/dist/json-schema/array-keyword-guards.mjs +114 -0
  9. package/dist/json-schema/assert-object-keyword-values.d.ts +59 -0
  10. package/dist/json-schema/assert-object-keyword-values.js +149 -0
  11. package/dist/json-schema/assert-object-keyword-values.mjs +141 -0
  12. package/dist/json-schema/assert-supported-dialect.d.ts +37 -0
  13. package/dist/json-schema/assert-supported-dialect.js +113 -0
  14. package/dist/json-schema/assert-supported-dialect.mjs +109 -0
  15. package/dist/json-schema/build-from-schema.d.ts +22 -3
  16. package/dist/json-schema/build-from-schema.js +27 -5
  17. package/dist/json-schema/build-from-schema.mjs +27 -5
  18. package/dist/json-schema/collect-definitions.d.ts +7 -0
  19. package/dist/json-schema/collect-definitions.js +9 -0
  20. package/dist/json-schema/collect-definitions.mjs +10 -1
  21. package/dist/json-schema/declare-additional-properties.d.ts +11 -1
  22. package/dist/json-schema/declare-additional-properties.js +13 -1
  23. package/dist/json-schema/declare-additional-properties.mjs +13 -1
  24. package/dist/json-schema/declare-object-keywords.d.ts +4 -0
  25. package/dist/json-schema/declare-object-keywords.js +11 -4
  26. package/dist/json-schema/declare-object-keywords.mjs +11 -4
  27. package/dist/json-schema/declare-required-properties.js +6 -0
  28. package/dist/json-schema/declare-required-properties.mjs +6 -0
  29. package/dist/json-schema/declare-value-keywords.d.ts +10 -1
  30. package/dist/json-schema/declare-value-keywords.js +43 -4
  31. package/dist/json-schema/declare-value-keywords.mjs +43 -4
  32. package/dist/json-schema/extensions/json-schema/index.d.ts +2 -2
  33. package/dist/json-schema/extensions/json-schema/index.js +4 -1
  34. package/dist/json-schema/extensions/json-schema/index.mjs +1 -1
  35. package/dist/json-schema/extensions/json-schema/json-schema.d.ts +2 -2
  36. package/dist/json-schema/extensions/json-schema/json-schema.js +3 -0
  37. package/dist/json-schema/extensions/json-schema/json-schema.mjs +4 -1
  38. package/dist/json-schema/extensions/json-schema-full-feature/index.d.ts +3 -1
  39. package/dist/json-schema/extensions/json-schema-full-feature/index.js +18 -1
  40. package/dist/json-schema/extensions/json-schema-full-feature/index.mjs +11 -0
  41. package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.d.ts +9 -1
  42. package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.js +9 -2
  43. package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.mjs +9 -2
  44. package/dist/json-schema/flatten-array-schema.d.ts +11 -1
  45. package/dist/json-schema/flatten-array-schema.js +20 -7
  46. package/dist/json-schema/flatten-array-schema.mjs +20 -7
  47. package/dist/json-schema/index.d.ts +4 -0
  48. package/dist/json-schema/index.js +9 -1
  49. package/dist/json-schema/index.mjs +3 -0
  50. package/dist/json-schema/keyword-map-core.js +3 -1
  51. package/dist/json-schema/keyword-map-core.mjs +3 -1
  52. package/dist/json-schema/keyword-map-string.js +30 -1
  53. package/dist/json-schema/keyword-map-string.mjs +30 -1
  54. package/dist/json-schema/keyword-map.js +5 -0
  55. package/dist/json-schema/keyword-map.mjs +5 -0
  56. package/dist/json-schema/malformed-schema-error.d.ts +38 -0
  57. package/dist/json-schema/malformed-schema-error.js +103 -0
  58. package/dist/json-schema/malformed-schema-error.mjs +98 -0
  59. package/dist/json-schema/unsupported-dialect-error.d.ts +21 -0
  60. package/dist/json-schema/unsupported-dialect-error.js +58 -0
  61. package/dist/json-schema/unsupported-dialect-error.mjs +54 -0
  62. package/dist/plugins/one-of/one-of.js +18 -4
  63. package/dist/plugins/one-of/one-of.mjs +18 -4
  64. package/package.json +8 -5
@@ -2,7 +2,7 @@ import type { Rule } from "../../../plugin-kit/compiled-rule";
2
2
  import { type MessageContextExtra, type TypeName } from "../../../types";
3
3
  import type { RuleBuildContext } from "../../../plugin-kit/rule-build-context";
4
4
  import type { Unchanged } from "../../../plugin-kit/marker.types";
5
- import { type JsonSchemaBag } from "../../index";
5
+ import { type DialectOptions, type JsonSchemaBag } from "../../index";
6
6
  /** The label the whole document carries inside the composite it becomes. */
7
7
  export declare const SCHEMA_BRANCH_LABEL = "schema";
8
8
  /**
@@ -17,7 +17,7 @@ export declare const SCHEMA_BRANCH_LABEL = "schema";
17
17
  * The caller fetches, reads from disk, or bundles — whichever is right for
18
18
  * their deployment — and hands over what they already have.
19
19
  */
20
- export interface JsonSchemaOptions {
20
+ export interface JsonSchemaOptions extends DialectOptions {
21
21
  readonly externalDocuments?: Readonly<Record<string, unknown>> | undefined;
22
22
  }
23
23
  /**
@@ -66,6 +66,9 @@ const NO_EXTERNAL_DOCUMENTS = Object.freeze({});
66
66
  function collectDocumentRules(ctx, document, bag, options = {}) {
67
67
  if (!(0, index_1.isDraft07Schema)(document))
68
68
  throw new index_1.NotASchemaError(document);
69
+ // Before any keyword is read, and in the same place the function front door
70
+ // reads it: the dialect decides what the keywords below MEAN.
71
+ (0, index_1.assertSupportedDialect)(document, options);
69
72
  const root = document;
70
73
  const scope = (0, index_1.createDocumentScope)(root, options.externalDocuments ?? NO_EXTERNAL_DOCUMENTS);
71
74
  // The seed's `chain` is a ChainBuildContext, whose three members a
@@ -38,7 +38,7 @@
38
38
  import { branch, composite } from "../../../plugin-kit/create-rule.mjs";
39
39
  import { definePlugin } from "../../../plugin-kit/plugin-definition.mjs";
40
40
  import { PASS } from "../../../types/index.mjs";
41
- import { NotASchemaError, collectSubSchemaRules, createStructuralContext, createDocumentScope, isDraft07Schema, resolveSchemaNodeInScope, } from "../../index.mjs";
41
+ import { NotASchemaError, assertSupportedDialect, collectSubSchemaRules, createStructuralContext, createDocumentScope, isDraft07Schema, resolveSchemaNodeInScope, } from "../../index.mjs";
42
42
  /** Every slot: a JSON Schema document constrains a value of any shape. */
43
43
  const SCHEMA_SLOTS = [
44
44
  "string",
@@ -62,6 +62,9 @@ const NO_EXTERNAL_DOCUMENTS = Object.freeze({});
62
62
  export function collectDocumentRules(ctx, document, bag, options = {}) {
63
63
  if (!isDraft07Schema(document))
64
64
  throw new NotASchemaError(document);
65
+ // Before any keyword is read, and in the same place the function front door
66
+ // reads it: the dialect decides what the keywords below MEAN.
67
+ assertSupportedDialect(document, options);
65
68
  const root = document;
66
69
  const scope = createDocumentScope(root, options.externalDocuments ?? NO_EXTERNAL_DOCUMENTS);
67
70
  // The seed's `chain` is a ChainBuildContext, whose three members a
@@ -2,4 +2,6 @@ export { fromJsonSchema, jsonSchemaFullFeaturePlugin, } from "./json-schema-full
2
2
  export { jsonSchemaBag } from "./bundled-plugins";
3
3
  export { UNBOUND_BUNDLED_PLUGIN_NAMES, findStaleUnboundDeclarations, findUnbundledBoundPlugins, findUnexplainedBundledPlugins, listBundledPluginNames, } from "./bundle-coverage";
4
4
  export type { JsonSchemaOptions } from "../json-schema";
5
- export type { JsonSchemaBag } from "../../index";
5
+ export { DRAFT07_DIALECT_URI, UnsupportedDialectError, } from "../../unsupported-dialect-error";
6
+ export { MalformedSchemaError, NotASchemaError, RefResolutionError, UnsupportedKeywordError, } from "../../index";
7
+ export type { DialectOptions, JsonSchemaBag } from "../../index";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listBundledPluginNames = exports.findUnexplainedBundledPlugins = exports.findUnbundledBoundPlugins = exports.findStaleUnboundDeclarations = exports.UNBOUND_BUNDLED_PLUGIN_NAMES = exports.jsonSchemaBag = exports.jsonSchemaFullFeaturePlugin = exports.fromJsonSchema = void 0;
3
+ exports.UnsupportedKeywordError = exports.RefResolutionError = exports.NotASchemaError = exports.MalformedSchemaError = exports.UnsupportedDialectError = exports.DRAFT07_DIALECT_URI = exports.listBundledPluginNames = exports.findUnexplainedBundledPlugins = exports.findUnbundledBoundPlugins = exports.findStaleUnboundDeclarations = exports.UNBOUND_BUNDLED_PLUGIN_NAMES = exports.jsonSchemaBag = exports.jsonSchemaFullFeaturePlugin = exports.fromJsonSchema = void 0;
4
4
  // ===========================================================================
5
5
  // The public subpath `@maroonedog/luq/plugins/jsonSchemaFullFeature`.
6
6
  // Re-exports only.
@@ -23,3 +23,20 @@ Object.defineProperty(exports, "findStaleUnboundDeclarations", { enumerable: tru
23
23
  Object.defineProperty(exports, "findUnbundledBoundPlugins", { enumerable: true, get: function () { return bundle_coverage_1.findUnbundledBoundPlugins; } });
24
24
  Object.defineProperty(exports, "findUnexplainedBundledPlugins", { enumerable: true, get: function () { return bundle_coverage_1.findUnexplainedBundledPlugins; } });
25
25
  Object.defineProperty(exports, "listBundledPluginNames", { enumerable: true, get: function () { return bundle_coverage_1.listBundledPluginNames; } });
26
+ // Every error `fromJsonSchema` can throw, so a caller can tell them apart by
27
+ // class rather than by reading the message.
28
+ //
29
+ // This subpath is the one-import route and the one the documentation points at,
30
+ // which made it the worst place to be missing them: a document that is not
31
+ // Draft-07 at all, one the meta-schema forbids, one carrying a keyword Luq
32
+ // cannot honour and one that is not a schema are four different things to do
33
+ // about it, and a caller who can only match on message text has to re-derive
34
+ // that distinction from prose that is free to change.
35
+ var unsupported_dialect_error_1 = require("../../unsupported-dialect-error");
36
+ Object.defineProperty(exports, "DRAFT07_DIALECT_URI", { enumerable: true, get: function () { return unsupported_dialect_error_1.DRAFT07_DIALECT_URI; } });
37
+ Object.defineProperty(exports, "UnsupportedDialectError", { enumerable: true, get: function () { return unsupported_dialect_error_1.UnsupportedDialectError; } });
38
+ var index_1 = require("../../index");
39
+ Object.defineProperty(exports, "MalformedSchemaError", { enumerable: true, get: function () { return index_1.MalformedSchemaError; } });
40
+ Object.defineProperty(exports, "NotASchemaError", { enumerable: true, get: function () { return index_1.NotASchemaError; } });
41
+ Object.defineProperty(exports, "RefResolutionError", { enumerable: true, get: function () { return index_1.RefResolutionError; } });
42
+ Object.defineProperty(exports, "UnsupportedKeywordError", { enumerable: true, get: function () { return index_1.UnsupportedKeywordError; } });
@@ -12,3 +12,14 @@
12
12
  export { fromJsonSchema, jsonSchemaFullFeaturePlugin, } from "./json-schema-full-feature.mjs";
13
13
  export { jsonSchemaBag } from "./bundled-plugins.mjs";
14
14
  export { UNBOUND_BUNDLED_PLUGIN_NAMES, findStaleUnboundDeclarations, findUnbundledBoundPlugins, findUnexplainedBundledPlugins, listBundledPluginNames, } from "./bundle-coverage.mjs";
15
+ // Every error `fromJsonSchema` can throw, so a caller can tell them apart by
16
+ // class rather than by reading the message.
17
+ //
18
+ // This subpath is the one-import route and the one the documentation points at,
19
+ // which made it the worst place to be missing them: a document that is not
20
+ // Draft-07 at all, one the meta-schema forbids, one carrying a keyword Luq
21
+ // cannot honour and one that is not a schema are four different things to do
22
+ // about it, and a caller who can only match on message text has to re-derive
23
+ // that distinction from prose that is free to change.
24
+ export { DRAFT07_DIALECT_URI, UnsupportedDialectError, } from "../../unsupported-dialect-error.mjs";
25
+ export { MalformedSchemaError, NotASchemaError, RefResolutionError, UnsupportedKeywordError, } from "../../index.mjs";
@@ -1,6 +1,7 @@
1
1
  import type { MessageContextExtra, TypeName } from "../../../types";
2
2
  import type { Unchanged } from "../../../plugin-kit/marker.types";
3
3
  import type { JsonSchemaOptions } from "../json-schema";
4
+ import type { DialectOptions } from "../../index";
4
5
  import type { GlobalConfig } from "../../../types/global-config";
5
6
  export declare const jsonSchemaFullFeaturePlugin: import("../../../plugin-kit/plugin-definition").PluginDefinition<"jsonSchemaFullFeature", "jsonSchemaFullFeature", readonly TypeName[], {
6
7
  args: readonly [document: unknown, options?: JsonSchemaOptions];
@@ -17,8 +18,15 @@ export declare const jsonSchemaFullFeaturePlugin: import("../../../plugin-kit/pl
17
18
  * NO declared path is checked — see build-from-schema.ts, which owns that
18
19
  * escape hatch and its single overload.
19
20
  *
21
+ * THE INPUT LIMIT IS DRAFT-07. A document whose root `$schema` names 2019-09 or
22
+ * 2020-12 is refused with an `UnsupportedDialectError` rather than read as
23
+ * Draft-07, because the dialects disagree about what unchanged keywords mean;
24
+ * a document with no `$schema` is read as Draft-07 as before. Pass
25
+ * `{ assumeDraft07: true }` as the third argument to take the Draft-07 reading
26
+ * deliberately. build-from-schema.ts states the divergence in full.
27
+ *
20
28
  * The return type is INFERRED rather than written: naming `Validator<T>` would
21
29
  * mean importing src/builder, which tier `extension` forbids. The inferred type
22
30
  * is that same `Validator<T>` and the type test asserts it.
23
31
  */
24
- export declare function fromJsonSchema<T extends object = Record<string, unknown>>(schema: unknown, config?: GlobalConfig): import("../../..").Validator<T, T>;
32
+ export declare function fromJsonSchema<T extends object = Record<string, unknown>>(schema: unknown, config?: GlobalConfig, options?: DialectOptions): import("../../..").Validator<T, T>;
@@ -57,10 +57,17 @@ exports.jsonSchemaFullFeaturePlugin = (0, plugin_definition_1.definePlugin)()({
57
57
  * NO declared path is checked — see build-from-schema.ts, which owns that
58
58
  * escape hatch and its single overload.
59
59
  *
60
+ * THE INPUT LIMIT IS DRAFT-07. A document whose root `$schema` names 2019-09 or
61
+ * 2020-12 is refused with an `UnsupportedDialectError` rather than read as
62
+ * Draft-07, because the dialects disagree about what unchanged keywords mean;
63
+ * a document with no `$schema` is read as Draft-07 as before. Pass
64
+ * `{ assumeDraft07: true }` as the third argument to take the Draft-07 reading
65
+ * deliberately. build-from-schema.ts states the divergence in full.
66
+ *
60
67
  * The return type is INFERRED rather than written: naming `Validator<T>` would
61
68
  * mean importing src/builder, which tier `extension` forbids. The inferred type
62
69
  * is that same `Validator<T>` and the type test asserts it.
63
70
  */
64
- function fromJsonSchema(schema, config) {
65
- return (0, index_1.fromJsonSchema)(bundled_plugins_1.jsonSchemaBag, schema, config);
71
+ function fromJsonSchema(schema, config, options) {
72
+ return (0, index_1.fromJsonSchema)(bundled_plugins_1.jsonSchemaBag, schema, config, options);
66
73
  }
@@ -53,10 +53,17 @@ export const jsonSchemaFullFeaturePlugin = /*#__PURE__*/ definePlugin()({
53
53
  * NO declared path is checked — see build-from-schema.ts, which owns that
54
54
  * escape hatch and its single overload.
55
55
  *
56
+ * THE INPUT LIMIT IS DRAFT-07. A document whose root `$schema` names 2019-09 or
57
+ * 2020-12 is refused with an `UnsupportedDialectError` rather than read as
58
+ * Draft-07, because the dialects disagree about what unchanged keywords mean;
59
+ * a document with no `$schema` is read as Draft-07 as before. Pass
60
+ * `{ assumeDraft07: true }` as the third argument to take the Draft-07 reading
61
+ * deliberately. build-from-schema.ts states the divergence in full.
62
+ *
56
63
  * The return type is INFERRED rather than written: naming `Validator<T>` would
57
64
  * mean importing src/builder, which tier `extension` forbids. The inferred type
58
65
  * is that same `Validator<T>` and the type test asserts it.
59
66
  */
60
- export function fromJsonSchema(schema, config) {
61
- return convertWithBag(jsonSchemaBag, schema, config);
67
+ export function fromJsonSchema(schema, config, options) {
68
+ return convertWithBag(jsonSchemaBag, schema, config, options);
62
69
  }
@@ -6,6 +6,10 @@ export declare const EACH_STEP = "[*]";
6
6
  /**
7
7
  * The single form only. The tuple form gets no child path: element 0 and
8
8
  * element 1 obey DIFFERENT schemas, and one declared `[*]` cannot say that.
9
+ *
10
+ * Reading through readCheckedItems is what stops a non-schema value here from
11
+ * becoming a child declaration whose sub-schema yields no rules at all; see
12
+ * array-keyword-guards.ts.
9
13
  */
10
14
  export declare function readItemChildren(schema: Draft07SchemaObject): readonly ChildSchema[];
11
15
  export declare function composeTupleItems(schema: Draft07SchemaObject, context: StructuralContext): readonly Rule[];
@@ -13,6 +17,10 @@ export declare function composeTupleItems(schema: Draft07SchemaObject, context:
13
17
  * `contains` is EXISTENTIAL: at least one element matches. arrayContains owns
14
18
  * that reduction, and its default bounds (min 1, no max) are exactly the
15
19
  * draft's, so this is one call and no arithmetic.
20
+ *
21
+ * An element branch holding no rules would satisfy the existence check on the
22
+ * first element and turn the keyword into "the array is non-empty", so the
23
+ * value is read through array-keyword-guards.ts.
16
24
  */
17
25
  export declare function composeContains(schema: Draft07SchemaObject, context: StructuralContext): readonly Rule[];
18
26
  /**
@@ -20,6 +28,8 @@ export declare function composeContains(schema: Draft07SchemaObject, context: St
20
28
  * and `maxItems` to `maxLength`; writing "minItems" as the method name does not
21
29
  * compile (keyword-map-array.ts), which is the C2 regression made impossible.
22
30
  * `uniqueItems: false` never reaches its binding: §6.4.3 gives it no effect and
23
- * the binding's value type is `true`.
31
+ * the binding's value type is `true`. That is why the value is read through
32
+ * readCheckedUniqueItems: with a bare `=== true`, a non-boolean would take the
33
+ * same silent route as the lawful `false` and lose the constraint entirely.
24
34
  */
25
35
  export declare function declareArrayRules(schema: Draft07SchemaObject, context: StructuralContext): readonly Rule[];
@@ -33,6 +33,7 @@ const create_chain_node_1 = require("../chain/create-chain-node");
33
33
  const create_field_slots_1 = require("../chain/create-field-slots");
34
34
  const create_rule_1 = require("../plugin-kit/create-rule");
35
35
  const apply_keyword_binding_1 = require("./apply-keyword-binding");
36
+ const array_keyword_guards_1 = require("./array-keyword-guards");
36
37
  const keyword_map_array_1 = require("./keyword-map-array");
37
38
  const types_1 = require("../types");
38
39
  const collect_definitions_1 = require("./collect-definitions");
@@ -47,9 +48,13 @@ function isTupleItems(items) {
47
48
  /**
48
49
  * The single form only. The tuple form gets no child path: element 0 and
49
50
  * element 1 obey DIFFERENT schemas, and one declared `[*]` cannot say that.
51
+ *
52
+ * Reading through readCheckedItems is what stops a non-schema value here from
53
+ * becoming a child declaration whose sub-schema yields no rules at all; see
54
+ * array-keyword-guards.ts.
50
55
  */
51
56
  function readItemChildren(schema) {
52
- const items = schema.items;
57
+ const items = (0, array_keyword_guards_1.readCheckedItems)(schema);
53
58
  if (items === undefined || isTupleItems(items))
54
59
  return NO_CHILDREN;
55
60
  return Object.freeze([{ step: exports.EACH_STEP, schema: items, isRequired: false }]);
@@ -58,7 +63,9 @@ function readItemChildren(schema) {
58
63
  * Element i against position i, extra elements against `additionalItems`.
59
64
  * A non-array passes, a short array passes, and `additionalItems: false`
60
65
  * becomes the schema that matches nothing (collect-definitions), so the
61
- * "no extra elements" case needs no fourth branch kind.
66
+ * "no extra elements" case needs no fourth branch kind. A rest branch built
67
+ * from a non-schema would carry no rules and check none of those elements,
68
+ * which is why the value is read through array-keyword-guards.ts.
62
69
  */
63
70
  function applyPositionally(runners, positionCount, hasRest) {
64
71
  return (value, ctx) => {
@@ -85,11 +92,11 @@ function describePosition(index) {
85
92
  : `Element ${String(index)} does not match the schema declared for it`;
86
93
  }
87
94
  function composeTupleItems(schema, context) {
88
- const items = schema.items;
95
+ const items = (0, array_keyword_guards_1.readCheckedItems)(schema);
89
96
  if (items === undefined || !isTupleItems(items))
90
97
  return NO_RULES;
91
98
  const branches = items.map((position, index) => context.toBranch(`position:${String(index)}`, position));
92
- const rest = schema.additionalItems;
99
+ const rest = (0, array_keyword_guards_1.readCheckedAdditionalItems)(schema);
93
100
  const hasRest = rest !== undefined;
94
101
  if (rest !== undefined)
95
102
  branches.push(context.toBranch("rest", rest));
@@ -109,9 +116,13 @@ function composeTupleItems(schema, context) {
109
116
  * `contains` is EXISTENTIAL: at least one element matches. arrayContains owns
110
117
  * that reduction, and its default bounds (min 1, no max) are exactly the
111
118
  * draft's, so this is one call and no arithmetic.
119
+ *
120
+ * An element branch holding no rules would satisfy the existence check on the
121
+ * first element and turn the keyword into "the array is non-empty", so the
122
+ * value is read through array-keyword-guards.ts.
112
123
  */
113
124
  function composeContains(schema, context) {
114
- const contains = schema.contains;
125
+ const contains = (0, array_keyword_guards_1.readCheckedContains)(schema);
115
126
  if (contains === undefined)
116
127
  return NO_RULES;
117
128
  const element = context.collectSubSchemaRules((0, collect_definitions_1.toSchemaObject)(contains));
@@ -124,7 +135,9 @@ function composeContains(schema, context) {
124
135
  * and `maxItems` to `maxLength`; writing "minItems" as the method name does not
125
136
  * compile (keyword-map-array.ts), which is the C2 regression made impossible.
126
137
  * `uniqueItems: false` never reaches its binding: §6.4.3 gives it no effect and
127
- * the binding's value type is `true`.
138
+ * the binding's value type is `true`. That is why the value is read through
139
+ * readCheckedUniqueItems: with a bare `=== true`, a non-boolean would take the
140
+ * same silent route as the lawful `false` and lose the constraint entirely.
128
141
  */
129
142
  function declareArrayRules(schema, context) {
130
143
  let chain = (0, create_field_slots_1.createFieldSlots)(context.bag, context.build).array;
@@ -134,7 +147,7 @@ function declareArrayRules(schema, context) {
134
147
  if (schema.maxItems !== undefined) {
135
148
  chain = (0, apply_keyword_binding_1.applyKeywordBinding)(chain, keyword_map_array_1.maxItemsBinding, schema.maxItems);
136
149
  }
137
- if (schema.uniqueItems === true) {
150
+ if ((0, array_keyword_guards_1.readCheckedUniqueItems)(schema) === true) {
138
151
  chain = (0, apply_keyword_binding_1.applyKeywordBinding)(chain, keyword_map_array_1.uniqueItemsBinding, true);
139
152
  }
140
153
  return (0, create_chain_node_1.readChainRules)(chain) ?? NO_RULES;
@@ -26,6 +26,7 @@ import { readChainRules } from "../chain/create-chain-node.mjs";
26
26
  import { createFieldSlots } from "../chain/create-field-slots.mjs";
27
27
  import { composite } from "../plugin-kit/create-rule.mjs";
28
28
  import { applyKeywordBinding } from "./apply-keyword-binding.mjs";
29
+ import { readCheckedAdditionalItems, readCheckedContains, readCheckedItems, readCheckedUniqueItems, } from "./array-keyword-guards.mjs";
29
30
  import { maxItemsBinding, minItemsBinding, uniqueItemsBinding, } from "./keyword-map-array.mjs";
30
31
  import { PASS, fail, isArray, isNumber } from "../types/index.mjs";
31
32
  import { toSchemaObject } from "./collect-definitions.mjs";
@@ -40,9 +41,13 @@ function isTupleItems(items) {
40
41
  /**
41
42
  * The single form only. The tuple form gets no child path: element 0 and
42
43
  * element 1 obey DIFFERENT schemas, and one declared `[*]` cannot say that.
44
+ *
45
+ * Reading through readCheckedItems is what stops a non-schema value here from
46
+ * becoming a child declaration whose sub-schema yields no rules at all; see
47
+ * array-keyword-guards.ts.
43
48
  */
44
49
  export function readItemChildren(schema) {
45
- const items = schema.items;
50
+ const items = readCheckedItems(schema);
46
51
  if (items === undefined || isTupleItems(items))
47
52
  return NO_CHILDREN;
48
53
  return Object.freeze([{ step: EACH_STEP, schema: items, isRequired: false }]);
@@ -51,7 +56,9 @@ export function readItemChildren(schema) {
51
56
  * Element i against position i, extra elements against `additionalItems`.
52
57
  * A non-array passes, a short array passes, and `additionalItems: false`
53
58
  * becomes the schema that matches nothing (collect-definitions), so the
54
- * "no extra elements" case needs no fourth branch kind.
59
+ * "no extra elements" case needs no fourth branch kind. A rest branch built
60
+ * from a non-schema would carry no rules and check none of those elements,
61
+ * which is why the value is read through array-keyword-guards.ts.
55
62
  */
56
63
  function applyPositionally(runners, positionCount, hasRest) {
57
64
  return (value, ctx) => {
@@ -78,11 +85,11 @@ function describePosition(index) {
78
85
  : `Element ${String(index)} does not match the schema declared for it`;
79
86
  }
80
87
  export function composeTupleItems(schema, context) {
81
- const items = schema.items;
88
+ const items = readCheckedItems(schema);
82
89
  if (items === undefined || !isTupleItems(items))
83
90
  return NO_RULES;
84
91
  const branches = items.map((position, index) => context.toBranch(`position:${String(index)}`, position));
85
- const rest = schema.additionalItems;
92
+ const rest = readCheckedAdditionalItems(schema);
86
93
  const hasRest = rest !== undefined;
87
94
  if (rest !== undefined)
88
95
  branches.push(context.toBranch("rest", rest));
@@ -102,9 +109,13 @@ export function composeTupleItems(schema, context) {
102
109
  * `contains` is EXISTENTIAL: at least one element matches. arrayContains owns
103
110
  * that reduction, and its default bounds (min 1, no max) are exactly the
104
111
  * draft's, so this is one call and no arithmetic.
112
+ *
113
+ * An element branch holding no rules would satisfy the existence check on the
114
+ * first element and turn the keyword into "the array is non-empty", so the
115
+ * value is read through array-keyword-guards.ts.
105
116
  */
106
117
  export function composeContains(schema, context) {
107
- const contains = schema.contains;
118
+ const contains = readCheckedContains(schema);
108
119
  if (contains === undefined)
109
120
  return NO_RULES;
110
121
  const element = context.collectSubSchemaRules(toSchemaObject(contains));
@@ -117,7 +128,9 @@ export function composeContains(schema, context) {
117
128
  * and `maxItems` to `maxLength`; writing "minItems" as the method name does not
118
129
  * compile (keyword-map-array.ts), which is the C2 regression made impossible.
119
130
  * `uniqueItems: false` never reaches its binding: §6.4.3 gives it no effect and
120
- * the binding's value type is `true`.
131
+ * the binding's value type is `true`. That is why the value is read through
132
+ * readCheckedUniqueItems: with a bare `=== true`, a non-boolean would take the
133
+ * same silent route as the lawful `false` and lose the constraint entirely.
121
134
  */
122
135
  export function declareArrayRules(schema, context) {
123
136
  let chain = createFieldSlots(context.bag, context.build).array;
@@ -127,7 +140,7 @@ export function declareArrayRules(schema, context) {
127
140
  if (schema.maxItems !== undefined) {
128
141
  chain = applyKeywordBinding(chain, maxItemsBinding, schema.maxItems);
129
142
  }
130
- if (schema.uniqueItems === true) {
143
+ if (readCheckedUniqueItems(schema) === true) {
131
144
  chain = applyKeywordBinding(chain, uniqueItemsBinding, true);
132
145
  }
133
146
  return readChainRules(chain) ?? NO_RULES;
@@ -17,6 +17,10 @@ export { createDocumentScope, createLocalScope } from "./ref-scope";
17
17
  export type { SchemaRegistry } from "./schema-registry";
18
18
  export { createSchemaRegistry } from "./schema-registry";
19
19
  export { UnsupportedKeywordError } from "./unsupported-keyword-error";
20
+ export { MalformedSchemaError } from "./malformed-schema-error";
21
+ export { DRAFT07_DIALECT_URI, UnsupportedDialectError, } from "./unsupported-dialect-error";
22
+ export { NON_DRAFT07_DIALECTS, assertSupportedDialect, } from "./assert-supported-dialect";
23
+ export type { DialectOptions } from "./assert-supported-dialect";
20
24
  export { isDraft07Schema, isSchemaObject } from "./draft07.types";
21
25
  export type { Draft07Schema, Draft07SchemaObject, Draft07TypeKeyword, } from "./draft07.types";
22
26
  export { assertKeywordSupported, countKeywordHandlings, findKeywordHandling, isDraft07Keyword, listBoundPluginNames, listDraft07Keywords, } from "./keyword-map";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listFormatNames = exports.findFormatHandling = exports.listDraft07Keywords = exports.listBoundPluginNames = exports.isDraft07Keyword = exports.findKeywordHandling = exports.countKeywordHandlings = exports.assertKeywordSupported = exports.isSchemaObject = exports.isDraft07Schema = exports.UnsupportedKeywordError = exports.createSchemaRegistry = exports.createLocalScope = exports.createDocumentScope = exports.resolveRefInScope = exports.resolveRef = exports.isResolvableRef = exports.RefResolutionError = exports.declarePresenceRules = exports.declareRequiredProperties = exports.createStructuralContext = exports.toSchemaBranch = exports.collectSubSchemaRules = exports.readChildSchemas = exports.isStructuralKeyword = exports.expandSchemaRules = exports.STRUCTURAL_EXPANSIONS = exports.joinDeclaredPath = exports.flattenSchema = exports.toSchemaObject = exports.resolveSchemaNodeInScope = exports.resolveSchemaNode = exports.readRefPointer = exports.isDefinitionContainer = exports.collectDefinitionNames = exports.DEFINITION_CONTAINERS = exports.fromJsonSchema = exports.buildFromSchema = exports.buildFieldEntries = exports.NotASchemaError = void 0;
3
+ exports.listFormatNames = exports.findFormatHandling = exports.listDraft07Keywords = exports.listBoundPluginNames = exports.isDraft07Keyword = exports.findKeywordHandling = exports.countKeywordHandlings = exports.assertKeywordSupported = exports.isSchemaObject = exports.isDraft07Schema = exports.assertSupportedDialect = exports.NON_DRAFT07_DIALECTS = exports.UnsupportedDialectError = exports.DRAFT07_DIALECT_URI = exports.MalformedSchemaError = exports.UnsupportedKeywordError = exports.createSchemaRegistry = exports.createLocalScope = exports.createDocumentScope = exports.resolveRefInScope = exports.resolveRef = exports.isResolvableRef = exports.RefResolutionError = exports.declarePresenceRules = exports.declareRequiredProperties = exports.createStructuralContext = exports.toSchemaBranch = exports.collectSubSchemaRules = exports.readChildSchemas = exports.isStructuralKeyword = exports.expandSchemaRules = exports.STRUCTURAL_EXPANSIONS = exports.joinDeclaredPath = exports.flattenSchema = exports.toSchemaObject = exports.resolveSchemaNodeInScope = exports.resolveSchemaNode = exports.readRefPointer = exports.isDefinitionContainer = exports.collectDefinitionNames = exports.DEFINITION_CONTAINERS = exports.fromJsonSchema = exports.buildFromSchema = exports.buildFieldEntries = exports.NotASchemaError = void 0;
4
4
  // ===========================================================================
5
5
  // L8 src/json-schema/index.ts — re-exports only. Nothing is defined here.
6
6
  //
@@ -50,6 +50,14 @@ var schema_registry_1 = require("./schema-registry");
50
50
  Object.defineProperty(exports, "createSchemaRegistry", { enumerable: true, get: function () { return schema_registry_1.createSchemaRegistry; } });
51
51
  var unsupported_keyword_error_1 = require("./unsupported-keyword-error");
52
52
  Object.defineProperty(exports, "UnsupportedKeywordError", { enumerable: true, get: function () { return unsupported_keyword_error_1.UnsupportedKeywordError; } });
53
+ var malformed_schema_error_1 = require("./malformed-schema-error");
54
+ Object.defineProperty(exports, "MalformedSchemaError", { enumerable: true, get: function () { return malformed_schema_error_1.MalformedSchemaError; } });
55
+ var unsupported_dialect_error_1 = require("./unsupported-dialect-error");
56
+ Object.defineProperty(exports, "DRAFT07_DIALECT_URI", { enumerable: true, get: function () { return unsupported_dialect_error_1.DRAFT07_DIALECT_URI; } });
57
+ Object.defineProperty(exports, "UnsupportedDialectError", { enumerable: true, get: function () { return unsupported_dialect_error_1.UnsupportedDialectError; } });
58
+ var assert_supported_dialect_1 = require("./assert-supported-dialect");
59
+ Object.defineProperty(exports, "NON_DRAFT07_DIALECTS", { enumerable: true, get: function () { return assert_supported_dialect_1.NON_DRAFT07_DIALECTS; } });
60
+ Object.defineProperty(exports, "assertSupportedDialect", { enumerable: true, get: function () { return assert_supported_dialect_1.assertSupportedDialect; } });
53
61
  var draft07_types_1 = require("./draft07.types");
54
62
  Object.defineProperty(exports, "isDraft07Schema", { enumerable: true, get: function () { return draft07_types_1.isDraft07Schema; } });
55
63
  Object.defineProperty(exports, "isSchemaObject", { enumerable: true, get: function () { return draft07_types_1.isSchemaObject; } });
@@ -17,6 +17,9 @@ export { isResolvableRef, resolveRef, resolveRefInScope } from "./resolve-ref.mj
17
17
  export { createDocumentScope, createLocalScope } from "./ref-scope.mjs";
18
18
  export { createSchemaRegistry } from "./schema-registry.mjs";
19
19
  export { UnsupportedKeywordError } from "./unsupported-keyword-error.mjs";
20
+ export { MalformedSchemaError } from "./malformed-schema-error.mjs";
21
+ export { DRAFT07_DIALECT_URI, UnsupportedDialectError, } from "./unsupported-dialect-error.mjs";
22
+ export { NON_DRAFT07_DIALECTS, assertSupportedDialect, } from "./assert-supported-dialect.mjs";
20
23
  export { isDraft07Schema, isSchemaObject } from "./draft07.types.mjs";
21
24
  export { assertKeywordSupported, countKeywordHandlings, findKeywordHandling, isDraft07Keyword, listBoundPluginNames, listDraft07Keywords, } from "./keyword-map.mjs";
22
25
  export { findFormatHandling, listFormatNames } from "./format-map.mjs";
@@ -25,7 +25,9 @@ const ANNOTATION_ONLY = "an annotation with no validation effect in Draft-07";
25
25
  */
26
26
  exports.constBinding = (0, bind_keyword_1.bindKeyword)("any", "literal", literal_1.literalPlugin, (v) => [v]);
27
27
  exports.coreKeywordMap = {
28
- $schema: (0, bind_keyword_1.unsupported)("read and discarded: Luq targets Draft-07 and does not switch dialect"),
28
+ $schema: (0, bind_keyword_1.unsupported)("read at the document ROOT by assert-supported-dialect and then " +
29
+ "discarded: Luq implements Draft-07 and never switches dialect, so a " +
30
+ "root `$schema` naming another one is refused rather than ignored"),
29
31
  $id: (0, bind_keyword_1.unsupported)("no base-URI resolution; resolve-ref accepts local $ref only"),
30
32
  $comment: (0, bind_keyword_1.unsupported)(ANNOTATION_ONLY),
31
33
  $ref: (0, bind_keyword_1.structural)("resolved by resolve-ref before any keyword is read"),
@@ -22,7 +22,9 @@ const ANNOTATION_ONLY = "an annotation with no validation effect in Draft-07";
22
22
  */
23
23
  export const constBinding = bindKeyword("any", "literal", literalPlugin, (v) => [v]);
24
24
  export const coreKeywordMap = {
25
- $schema: unsupported("read and discarded: Luq targets Draft-07 and does not switch dialect"),
25
+ $schema: unsupported("read at the document ROOT by assert-supported-dialect and then " +
26
+ "discarded: Luq implements Draft-07 and never switches dialect, so a " +
27
+ "root `$schema` naming another one is refused rather than ignored"),
26
28
  $id: unsupported("no base-URI resolution; resolve-ref accepts local $ref only"),
27
29
  $comment: unsupported(ANNOTATION_ONLY),
28
30
  $ref: structural("resolved by resolve-ref before any keyword is read"),
@@ -10,7 +10,9 @@ exports.stringKeywordMap = exports.contentMediaTypeBinding = exports.contentEnco
10
10
  // table in this repository — 1.x shipped three of them and they disagreed
11
11
  // (docs/legacy-spec/json-schema-mapping.md records the disagreements).
12
12
  // ===========================================================================
13
+ const types_1 = require("../types");
13
14
  const bind_keyword_1 = require("./bind-keyword");
15
+ const malformed_schema_error_1 = require("./malformed-schema-error");
14
16
  const string_min_1 = require("../plugins/string-min");
15
17
  const string_max_1 = require("../plugins/string-max");
16
18
  const string_pattern_1 = require("../plugins/string-pattern");
@@ -18,13 +20,40 @@ const string_content_encoding_1 = require("../plugins/string-content-encoding");
18
20
  const string_content_media_type_1 = require("../plugins/string-content-media-type");
19
21
  exports.minLengthBinding = (0, bind_keyword_1.bindKeyword)("string", "min", string_min_1.stringMinPlugin, (v) => [v]);
20
22
  exports.maxLengthBinding = (0, bind_keyword_1.bindKeyword)("string", "max", string_max_1.stringMaxPlugin, (v) => [v]);
23
+ /**
24
+ * Compiles the document's source, refusing anything the meta-schema forbids.
25
+ *
26
+ * The parameter is `unknown` although the keyword's value type is `string`,
27
+ * and that is the whole point: compiling is a TOTAL operation on JSON values —
28
+ * every one of them has a string form — so a number becomes a literal digit
29
+ * pattern and an object becomes one that matches almost every string. Both
30
+ * then reach stringPattern as a perfectly valid RegExp, leaving its
31
+ * non-RegExp guard nothing to fire on. The refusal has to be here, before the
32
+ * value is turned into something well formed.
33
+ *
34
+ * A string that is not a valid ECMA-262 pattern is the same failure wearing a
35
+ * different coat: the compile step throws a SyntaxError naming neither the
36
+ * keyword nor the document, so it is caught and re-raised as the typed refusal
37
+ * every other malformed value gets.
38
+ */
39
+ function compilePattern(source) {
40
+ if (!(0, types_1.isString)(source)) {
41
+ throw new malformed_schema_error_1.MalformedSchemaError("pattern", "the value must be a string", source);
42
+ }
43
+ try {
44
+ return new RegExp(source);
45
+ }
46
+ catch {
47
+ throw new malformed_schema_error_1.MalformedSchemaError("pattern", "the value must be a valid ECMA-262 regular expression", source);
48
+ }
49
+ }
21
50
  /**
22
51
  * Draft-07 spells `pattern` as an ECMA-262 SOURCE string; the plugin accepts a
23
52
  * RegExp and only a RegExp, because 1.x's `new RegExp(source)` at the call site
24
53
  * dropped flags silently and gave the caller no compile-time check. Compiling
25
54
  * the schema's string therefore happens HERE, once, and nowhere else.
26
55
  */
27
- exports.patternBinding = (0, bind_keyword_1.bindKeyword)("string", "pattern", string_pattern_1.stringPatternPlugin, (v) => [new RegExp(v)]);
56
+ exports.patternBinding = (0, bind_keyword_1.bindKeyword)("string", "pattern", string_pattern_1.stringPatternPlugin, (v) => [compilePattern(v)]);
28
57
  /**
29
58
  * The value type is `ContentEncodingName`, not `string`: an encoding the
30
59
  * plugin does not know must be refused by the converter rather than accepted
@@ -7,7 +7,9 @@
7
7
  // table in this repository — 1.x shipped three of them and they disagreed
8
8
  // (docs/legacy-spec/json-schema-mapping.md records the disagreements).
9
9
  // ===========================================================================
10
+ import { isString } from "../types/index.mjs";
10
11
  import { bindKeyword, structural } from "./bind-keyword.mjs";
12
+ import { MalformedSchemaError } from "./malformed-schema-error.mjs";
11
13
  import { stringMinPlugin } from "../plugins/string-min/index.mjs";
12
14
  import { stringMaxPlugin } from "../plugins/string-max/index.mjs";
13
15
  import { stringPatternPlugin } from "../plugins/string-pattern/index.mjs";
@@ -15,13 +17,40 @@ import { stringContentEncodingPlugin, } from "../plugins/string-content-encoding
15
17
  import { stringContentMediaTypePlugin } from "../plugins/string-content-media-type/index.mjs";
16
18
  export const minLengthBinding = bindKeyword("string", "min", stringMinPlugin, (v) => [v]);
17
19
  export const maxLengthBinding = bindKeyword("string", "max", stringMaxPlugin, (v) => [v]);
20
+ /**
21
+ * Compiles the document's source, refusing anything the meta-schema forbids.
22
+ *
23
+ * The parameter is `unknown` although the keyword's value type is `string`,
24
+ * and that is the whole point: compiling is a TOTAL operation on JSON values —
25
+ * every one of them has a string form — so a number becomes a literal digit
26
+ * pattern and an object becomes one that matches almost every string. Both
27
+ * then reach stringPattern as a perfectly valid RegExp, leaving its
28
+ * non-RegExp guard nothing to fire on. The refusal has to be here, before the
29
+ * value is turned into something well formed.
30
+ *
31
+ * A string that is not a valid ECMA-262 pattern is the same failure wearing a
32
+ * different coat: the compile step throws a SyntaxError naming neither the
33
+ * keyword nor the document, so it is caught and re-raised as the typed refusal
34
+ * every other malformed value gets.
35
+ */
36
+ function compilePattern(source) {
37
+ if (!isString(source)) {
38
+ throw new MalformedSchemaError("pattern", "the value must be a string", source);
39
+ }
40
+ try {
41
+ return new RegExp(source);
42
+ }
43
+ catch {
44
+ throw new MalformedSchemaError("pattern", "the value must be a valid ECMA-262 regular expression", source);
45
+ }
46
+ }
18
47
  /**
19
48
  * Draft-07 spells `pattern` as an ECMA-262 SOURCE string; the plugin accepts a
20
49
  * RegExp and only a RegExp, because 1.x's `new RegExp(source)` at the call site
21
50
  * dropped flags silently and gave the caller no compile-time check. Compiling
22
51
  * the schema's string therefore happens HERE, once, and nowhere else.
23
52
  */
24
- export const patternBinding = bindKeyword("string", "pattern", stringPatternPlugin, (v) => [new RegExp(v)]);
53
+ export const patternBinding = bindKeyword("string", "pattern", stringPatternPlugin, (v) => [compilePattern(v)]);
25
54
  /**
26
55
  * The value type is `ContentEncodingName`, not `string`: an encoding the
27
56
  * plugin does not know must be refused by the converter rather than accepted
@@ -32,9 +32,14 @@ exports.NON_DRAFT07_KEYWORDS = {
32
32
  $defs: "2019-09 spelling of `definitions`; resolve-ref follows it anyway",
33
33
  $anchor: "2019-09 anchors; resolve-ref resolves JSON Pointers only",
34
34
  $dynamicRef: "2020-12 dynamic references",
35
+ $dynamicAnchor: "2020-12 dynamic anchors; the target half of $dynamicRef",
35
36
  $recursiveRef: "2019-09 recursive references",
37
+ $recursiveAnchor: "2019-09 recursive anchors; the target half of $recursiveRef",
38
+ $vocabulary: "2019-09 vocabulary declaration; Luq has one fixed vocabulary",
36
39
  dependentRequired: "2019-09 split of `dependencies`; use `dependencies`",
37
40
  dependentSchemas: "2019-09 split of `dependencies`; use `dependencies`",
41
+ minContains: "2019-09 bound on `contains`; Draft-07's is fixed at one",
42
+ maxContains: "2019-09 bound on `contains`; Draft-07 states no maximum",
38
43
  unevaluatedItems: "2019-09; no annotation-collection model in Luq",
39
44
  unevaluatedProperties: "2019-09; no annotation-collection model in Luq",
40
45
  contentSchema: "2019-09 companion to contentMediaType",
@@ -23,9 +23,14 @@ export const NON_DRAFT07_KEYWORDS = {
23
23
  $defs: "2019-09 spelling of `definitions`; resolve-ref follows it anyway",
24
24
  $anchor: "2019-09 anchors; resolve-ref resolves JSON Pointers only",
25
25
  $dynamicRef: "2020-12 dynamic references",
26
+ $dynamicAnchor: "2020-12 dynamic anchors; the target half of $dynamicRef",
26
27
  $recursiveRef: "2019-09 recursive references",
28
+ $recursiveAnchor: "2019-09 recursive anchors; the target half of $recursiveRef",
29
+ $vocabulary: "2019-09 vocabulary declaration; Luq has one fixed vocabulary",
27
30
  dependentRequired: "2019-09 split of `dependencies`; use `dependencies`",
28
31
  dependentSchemas: "2019-09 split of `dependencies`; use `dependencies`",
32
+ minContains: "2019-09 bound on `contains`; Draft-07's is fixed at one",
33
+ maxContains: "2019-09 bound on `contains`; Draft-07 states no maximum",
29
34
  unevaluatedItems: "2019-09; no annotation-collection model in Luq",
30
35
  unevaluatedProperties: "2019-09; no annotation-collection model in Luq",
31
36
  contentSchema: "2019-09 companion to contentMediaType",
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The two shapes §4.4 lets a schema take, worded once. Several keywords take a
3
+ * schema as their value — `properties` members, `additionalProperties`, every
4
+ * position of the tuple form of `items` — and each is read by a different
5
+ * module; sharing the phrase is what stops the same refusal being explained
6
+ * three different ways to the same caller.
7
+ *
8
+ * It is a fragment, so it composes: "the value ", `the schema under "a" `.
9
+ */
10
+ export declare const SCHEMA_FORMS = "must be an object or a boolean";
11
+ /**
12
+ * Renders an untrusted schema value for a message, in at most
13
+ * RECEIVED_MAX_LENGTH characters. The value comes from the same document that
14
+ * is already known to be malformed, so nothing about it can be assumed: it may
15
+ * be circular, hold a bigint, or be large enough to drown the message.
16
+ * Rendering must never be the thing that throws.
17
+ */
18
+ export declare function renderReceivedValue(value: unknown): string;
19
+ /**
20
+ * Raised when a keyword's value violates the Draft-07 meta-schema. Thrown at
21
+ * build time, before a validator that would enforce less than the document
22
+ * states can exist.
23
+ */
24
+ export declare class MalformedSchemaError extends Error {
25
+ /** The keyword whose value is malformed, for instance "type". */
26
+ readonly keyword: string;
27
+ /**
28
+ * What the meta-schema REQUIRES, written as a sentence fragment with no
29
+ * terminal punctuation — the message supplies it. Every keyword phrases it
30
+ * the same way, "<the thing> must be <the shape>", so that a caller reading
31
+ * two refusals from two different keywords reads one library rather than
32
+ * three; what was found instead is carried separately, in `received`.
33
+ */
34
+ readonly reason: string;
35
+ /** The offending value, rendered short and safe by renderReceivedValue. */
36
+ readonly received: string;
37
+ constructor(keyword: string, reason: string, value: unknown);
38
+ }