@maroonedog/luq 2.0.0 → 2.2.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 (165) hide show
  1. package/README.md +364 -57
  2. package/dist/builder/builder-surface.types.d.ts +1 -0
  3. package/dist/builder/create-builder.js +9 -0
  4. package/dist/builder/create-builder.mjs +9 -0
  5. package/dist/builder/field-builder.types.d.ts +13 -0
  6. package/dist/chain/bundle-paths.types.d.ts +26 -0
  7. package/dist/chain/bundle-paths.types.js +2 -0
  8. package/dist/chain/bundle-paths.types.mjs +1 -0
  9. package/dist/chain/chain-method.types.d.ts +8 -3
  10. package/dist/chain/create-chain-node.js +22 -1
  11. package/dist/chain/create-chain-node.mjs +22 -1
  12. package/dist/chain/resolve-args.types.d.ts +4 -2
  13. package/dist/compile/compile-array-node.d.ts +0 -7
  14. package/dist/compile/compile-array-node.js +4 -0
  15. package/dist/compile/compile-array-node.mjs +4 -0
  16. package/dist/compile/compile-field.js +10 -1
  17. package/dist/compile/compile-field.mjs +10 -1
  18. package/dist/compile/resolve-conditional-presence.js +1 -0
  19. package/dist/compile/resolve-conditional-presence.mjs +1 -0
  20. package/dist/compile/resolve-presence.js +4 -0
  21. package/dist/compile/resolve-presence.mjs +4 -0
  22. package/dist/compile/split-rules-by-kind.js +31 -7
  23. package/dist/compile/split-rules-by-kind.mjs +31 -7
  24. package/dist/compile/validation-plan.types.d.ts +33 -0
  25. package/dist/json-schema/build-from-schema.js +2 -3
  26. package/dist/json-schema/build-from-schema.mjs +2 -3
  27. package/dist/json-schema/collect-definitions.d.ts +30 -0
  28. package/dist/json-schema/collect-definitions.js +47 -3
  29. package/dist/json-schema/collect-definitions.mjs +46 -4
  30. package/dist/json-schema/collect-sub-schema-rules.d.ts +12 -1
  31. package/dist/json-schema/collect-sub-schema-rules.js +37 -7
  32. package/dist/json-schema/collect-sub-schema-rules.mjs +38 -8
  33. package/dist/json-schema/create-structural-context.d.ts +13 -3
  34. package/dist/json-schema/create-structural-context.js +72 -6
  35. package/dist/json-schema/create-structural-context.mjs +72 -7
  36. package/dist/json-schema/declare-additional-properties.d.ts +19 -0
  37. package/dist/json-schema/declare-additional-properties.js +42 -0
  38. package/dist/json-schema/declare-additional-properties.mjs +37 -0
  39. package/dist/json-schema/declare-object-keywords.d.ts +0 -2
  40. package/dist/json-schema/declare-object-keywords.js +6 -13
  41. package/dist/json-schema/declare-object-keywords.mjs +7 -13
  42. package/dist/json-schema/declare-presence.d.ts +9 -3
  43. package/dist/json-schema/declare-presence.js +12 -6
  44. package/dist/json-schema/declare-presence.mjs +12 -6
  45. package/dist/json-schema/extensions/json-schema/index.d.ts +1 -0
  46. package/dist/json-schema/extensions/json-schema/json-schema.d.ts +17 -2
  47. package/dist/json-schema/extensions/json-schema/json-schema.js +15 -7
  48. package/dist/json-schema/extensions/json-schema/json-schema.mjs +16 -8
  49. package/dist/json-schema/extensions/json-schema-full-feature/index.d.ts +1 -0
  50. package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.d.ts +2 -1
  51. package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.js +3 -1
  52. package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.mjs +3 -1
  53. package/dist/json-schema/flatten-schema.js +1 -1
  54. package/dist/json-schema/flatten-schema.mjs +1 -1
  55. package/dist/json-schema/follow-json-pointer.d.ts +23 -0
  56. package/dist/json-schema/follow-json-pointer.js +87 -0
  57. package/dist/json-schema/follow-json-pointer.mjs +82 -0
  58. package/dist/json-schema/index.d.ts +7 -2
  59. package/dist/json-schema/index.js +10 -2
  60. package/dist/json-schema/index.mjs +5 -2
  61. package/dist/json-schema/keyword-map-object.d.ts +1 -1
  62. package/dist/json-schema/ref-resolution-error.d.ts +4 -0
  63. package/dist/json-schema/ref-resolution-error.js +20 -0
  64. package/dist/json-schema/ref-resolution-error.mjs +16 -0
  65. package/dist/json-schema/ref-scope.d.ts +18 -0
  66. package/dist/json-schema/ref-scope.js +37 -0
  67. package/dist/json-schema/ref-scope.mjs +33 -0
  68. package/dist/json-schema/resolve-ref.d.ts +14 -9
  69. package/dist/json-schema/resolve-ref.js +89 -59
  70. package/dist/json-schema/resolve-ref.mjs +86 -55
  71. package/dist/json-schema/schema-registry.d.ts +30 -0
  72. package/dist/json-schema/schema-registry.js +142 -0
  73. package/dist/json-schema/schema-registry.mjs +139 -0
  74. package/dist/json-schema/schema-to-declarations.js +2 -1
  75. package/dist/json-schema/schema-to-declarations.mjs +2 -1
  76. package/dist/json-schema/structural-expansion.types.d.ts +8 -1
  77. package/dist/json-schema/uri-reference.d.ts +29 -0
  78. package/dist/json-schema/uri-reference.js +118 -0
  79. package/dist/json-schema/uri-reference.mjs +111 -0
  80. package/dist/path/create-value-writer.js +27 -2
  81. package/dist/path/create-value-writer.mjs +27 -2
  82. package/dist/path/reserved-segment.d.ts +22 -4
  83. package/dist/path/reserved-segment.js +27 -7
  84. package/dist/path/reserved-segment.mjs +27 -7
  85. package/dist/plugin-kit/compiled-rule.d.ts +13 -0
  86. package/dist/plugin-kit/create-rule.d.ts +11 -0
  87. package/dist/plugin-kit/create-rule.js +1 -0
  88. package/dist/plugin-kit/create-rule.mjs +1 -0
  89. package/dist/plugin-kit/marker.types.d.ts +18 -0
  90. package/dist/plugin-kit/plugin-definition.d.ts +15 -0
  91. package/dist/plugin-kit/plugin-definition.js +1 -0
  92. package/dist/plugin-kit/plugin-definition.mjs +1 -0
  93. package/dist/plugins/index.generated.d.ts +1 -0
  94. package/dist/plugins/index.generated.js +4 -2
  95. package/dist/plugins/index.generated.mjs +1 -0
  96. package/dist/plugins/manifest.generated.js +1 -0
  97. package/dist/plugins/manifest.generated.mjs +1 -0
  98. package/dist/plugins/object-additional-properties/object-additional-properties-schema.d.ts +1 -1
  99. package/dist/plugins/object-additional-properties/object-additional-properties-schema.js +4 -2
  100. package/dist/plugins/object-additional-properties/object-additional-properties-schema.mjs +4 -2
  101. package/dist/plugins/object-additional-properties/object-additional-properties.d.ts +1 -1
  102. package/dist/plugins/object-additional-properties/object-additional-properties.js +4 -2
  103. package/dist/plugins/object-additional-properties/object-additional-properties.mjs +4 -2
  104. package/dist/plugins/object-additional-properties/select-additional-keys.d.ts +15 -0
  105. package/dist/plugins/object-additional-properties/select-additional-keys.js +51 -0
  106. package/dist/plugins/object-additional-properties/select-additional-keys.mjs +47 -0
  107. package/dist/plugins/stitch/stitch.d.ts +28 -8
  108. package/dist/plugins/stitch-with/index.d.ts +2 -0
  109. package/dist/plugins/stitch-with/index.js +5 -0
  110. package/dist/plugins/stitch-with/index.mjs +1 -0
  111. package/dist/plugins/stitch-with/stitch-with.d.ts +12 -0
  112. package/dist/plugins/stitch-with/stitch-with.js +94 -0
  113. package/dist/plugins/stitch-with/stitch-with.mjs +91 -0
  114. package/dist/plugins/stitchWith.d.ts +1 -0
  115. package/dist/plugins/stitchWith.js +2 -0
  116. package/dist/plugins/stitchWith.mjs +1 -0
  117. package/dist/plugins/string-exact-length/string-exact-length.js +2 -2
  118. package/dist/plugins/string-exact-length/string-exact-length.mjs +3 -3
  119. package/dist/plugins/string-max/string-max.js +2 -2
  120. package/dist/plugins/string-max/string-max.mjs +3 -3
  121. package/dist/plugins/string-min/string-min.js +21 -3
  122. package/dist/plugins/string-min/string-min.mjs +21 -3
  123. package/dist/presets/index.d.ts +1 -0
  124. package/dist/presets/index.js +9 -0
  125. package/dist/presets/index.mjs +1 -0
  126. package/dist/presets/presets.d.ts +165 -0
  127. package/dist/presets/presets.js +80 -0
  128. package/dist/presets/presets.mjs +77 -0
  129. package/dist/runtime/create-field-validator.js +24 -8
  130. package/dist/runtime/create-field-validator.mjs +24 -8
  131. package/dist/runtime/create-validator.js +38 -8
  132. package/dist/runtime/create-validator.mjs +38 -8
  133. package/dist/runtime/decide-presence.js +2 -1
  134. package/dist/runtime/decide-presence.mjs +2 -1
  135. package/dist/runtime/field-rule-context.d.ts +32 -0
  136. package/dist/runtime/field-rule-context.js +48 -0
  137. package/dist/runtime/field-rule-context.mjs +44 -0
  138. package/dist/runtime/index-stack.d.ts +29 -4
  139. package/dist/runtime/index-stack.js +76 -15
  140. package/dist/runtime/index-stack.mjs +76 -15
  141. package/dist/runtime/run-array-node.js +22 -10
  142. package/dist/runtime/run-array-node.mjs +22 -10
  143. package/dist/runtime/run-field.js +47 -17
  144. package/dist/runtime/run-field.mjs +47 -17
  145. package/dist/runtime/run-plan.js +5 -1
  146. package/dist/runtime/run-plan.mjs +5 -1
  147. package/dist/standard-schema/index.d.ts +6 -0
  148. package/dist/standard-schema/index.js +7 -0
  149. package/dist/standard-schema/index.mjs +2 -0
  150. package/dist/standard-schema/split-issue-path.d.ts +9 -0
  151. package/dist/standard-schema/split-issue-path.js +66 -0
  152. package/dist/standard-schema/split-issue-path.mjs +63 -0
  153. package/dist/standard-schema/standard-schema.types.d.ts +46 -0
  154. package/dist/standard-schema/standard-schema.types.js +13 -0
  155. package/dist/standard-schema/standard-schema.types.mjs +12 -0
  156. package/dist/standard-schema/to-standard-schema.d.ts +41 -0
  157. package/dist/standard-schema/to-standard-schema.js +60 -0
  158. package/dist/standard-schema/to-standard-schema.mjs +57 -0
  159. package/dist/standard-schema.d.ts +1 -0
  160. package/dist/standard-schema.js +2 -0
  161. package/dist/standard-schema.mjs +1 -0
  162. package/dist/types/index.d.ts +24 -0
  163. package/dist/types/index.js +18 -0
  164. package/dist/types/index.mjs +17 -0
  165. package/package.json +42 -19
@@ -1,13 +1,18 @@
1
- import type { GuardOut, TransformOut } from "../plugin-kit/marker.types";
1
+ import type { BundleOut, GuardOut, StitchOut, TransformOut } from "../plugin-kit/marker.types";
2
+ import type { BundleChain, BundlePaths } from "./bundle-paths.types";
2
3
  import type { PluginDefinition, PluginSignature } from "../plugin-kit/plugin-definition";
3
- import type { Present, RuleOptions, TypeName } from "../types";
4
+ import type { CrossFieldOutcome, Present, RuleOptions, TypeName } from "../types";
5
+ import type { FieldPath } from "../path/field-path.types";
6
+ import type { PickPaths } from "../path/value-at-path.types";
4
7
  import type { PluginBag } from "./plugin-bag.types";
5
8
  import type { ChainState, CoverWith } from "./chain-state.types";
6
9
  import type { ResolveArgs, ResolveOut } from "./resolve-args.types";
7
10
  import type { AnyChain, FieldChain } from "./field-chain.types";
8
11
  import type { FieldSlots } from "./field-slots.types";
9
12
  /** One plugin definition -> one call signature. */
10
- export type ChainMethod<P, B extends PluginBag, S extends TypeName, TRoot, TValue, TState extends ChainState> = P extends PluginDefinition<string, string, readonly TypeName[], infer Sig extends PluginSignature> ? [Sig["out"]] extends [TransformOut] ? <R>(map: (value: Present<TValue, TState>) => R, options?: RuleOptions<Sig["context"]>) => FieldChain<B, S, TRoot, R, TState> : [Sig["out"]] extends [GuardOut] ? <X extends Present<TValue, TState>>(condition: (value: Present<TValue, TState>) => value is X, define: (b: FieldSlots<TRoot, B, X>) => AnyChain, options?: RuleOptions<Sig["context"]>) => FieldChain<B, S, TRoot, TValue, CoverWith<TState, X>> : (...args: [
13
+ export type ChainMethod<P, B extends PluginBag, S extends TypeName, TRoot, TValue, TState extends ChainState> = P extends PluginDefinition<string, string, readonly TypeName[], infer Sig extends PluginSignature> ? [Sig["out"]] extends [TransformOut] ? <R>(map: (value: Present<TValue, TState>) => R, options?: RuleOptions<Sig["context"]>) => FieldChain<B, S, TRoot, R, TState> : [Sig["out"]] extends [GuardOut] ? <X extends Present<TValue, TState>>(condition: (value: Present<TValue, TState>) => value is X, define: (b: FieldSlots<TRoot, B, X>) => AnyChain, options?: RuleOptions<Sig["context"]>) => FieldChain<B, S, TRoot, TValue, CoverWith<TState, X>> : [
14
+ Sig["out"]
15
+ ] extends [StitchOut] ? <const F extends readonly (FieldPath<TRoot> & string)[]>(fields: F, check: (fieldValues: PickPaths<TRoot, F>, value: Present<TValue, TState>, root: TRoot) => CrossFieldOutcome, options?: RuleOptions<Sig["context"]>) => FieldChain<B, S, TRoot, TValue, TState> : [Sig["out"]] extends [BundleOut] ? <const M extends BundlePaths<TRoot>>(fields: M, define: BundleChain<TRoot, M, B>, options?: RuleOptions<Sig["context"]>) => FieldChain<B, S, TRoot, TValue, TState> : (...args: [
11
16
  ...ResolveArgs<Sig["args"], B, TRoot, TValue, TState>,
12
17
  options?: RuleOptions<Sig["context"]>
13
18
  ]) => ResolveOut<Sig["out"], TValue, TState> extends [
@@ -22,6 +22,7 @@ exports.readChainRules = readChainRules;
22
22
  // needs no assertion and no runtime shape check.
23
23
  // ===========================================================================
24
24
  const types_1 = require("../types");
25
+ const create_rule_1 = require("../plugin-kit/create-rule");
25
26
  const refine_methods_types_1 = require("./refine-methods.types");
26
27
  const attach_slot_methods_1 = require("./attach-slot-methods");
27
28
  const chainRulesByNode = new WeakMap();
@@ -52,13 +53,33 @@ function buildRuleContext(wiring, plugin, rawOptions) {
52
53
  declaredSiblingKeys: wiring.context.declaredSiblingKeys,
53
54
  };
54
55
  }
56
+ /**
57
+ * The presence rule a `judgesNull` plugin brings with it. It forbids
58
+ * NOTHING — it only says that null must reach the checks instead of ending
59
+ * the field, which is the one thing a check cannot say for itself.
60
+ */
61
+ function nullIsAValue(severity) {
62
+ return (0, create_rule_1.presence)({
63
+ code: "type",
64
+ severity,
65
+ allowUndefined: true,
66
+ allowNull: true,
67
+ emptyStringIsMissing: false,
68
+ nullIsValue: true,
69
+ describe: (messageContext) => `${messageContext.path} must not be null`,
70
+ buildMessageContext: () => ({}),
71
+ });
72
+ }
55
73
  function createSlotMethod(wiring, slot, rules, plugin) {
56
74
  const arity = Math.max(plugin.build.length - 1, 0);
57
75
  return (...args) => {
58
76
  const rawOptions = args.length > arity ? args[arity] : undefined;
59
77
  const resolved = wiring.resolveArguments(plugin, args.slice(0, arity));
60
78
  const rule = plugin.build(buildRuleContext(wiring, plugin, rawOptions), ...resolved);
61
- return createChainNode(wiring, slot, [...rules, rule]);
79
+ const added = plugin.judgesNull === true
80
+ ? [nullIsAValue(wiring.context.config.defaultSeverity), rule]
81
+ : [rule];
82
+ return createChainNode(wiring, slot, [...rules, ...added]);
62
83
  };
63
84
  }
64
85
  function attachRefineMethods(target, wiring, rules) {
@@ -17,6 +17,7 @@
17
17
  // needs no assertion and no runtime shape check.
18
18
  // ===========================================================================
19
19
  import { isPlainObject, isString } from "../types/index.mjs";
20
+ import { presence } from "../plugin-kit/create-rule.mjs";
20
21
  import { REFINE_METHOD_SLOTS } from "./refine-methods.types.mjs";
21
22
  import { attachSlotMethods } from "./attach-slot-methods.mjs";
22
23
  const chainRulesByNode = new WeakMap();
@@ -47,13 +48,33 @@ function buildRuleContext(wiring, plugin, rawOptions) {
47
48
  declaredSiblingKeys: wiring.context.declaredSiblingKeys,
48
49
  };
49
50
  }
51
+ /**
52
+ * The presence rule a `judgesNull` plugin brings with it. It forbids
53
+ * NOTHING — it only says that null must reach the checks instead of ending
54
+ * the field, which is the one thing a check cannot say for itself.
55
+ */
56
+ function nullIsAValue(severity) {
57
+ return presence({
58
+ code: "type",
59
+ severity,
60
+ allowUndefined: true,
61
+ allowNull: true,
62
+ emptyStringIsMissing: false,
63
+ nullIsValue: true,
64
+ describe: (messageContext) => `${messageContext.path} must not be null`,
65
+ buildMessageContext: () => ({}),
66
+ });
67
+ }
50
68
  function createSlotMethod(wiring, slot, rules, plugin) {
51
69
  const arity = Math.max(plugin.build.length - 1, 0);
52
70
  return (...args) => {
53
71
  const rawOptions = args.length > arity ? args[arity] : undefined;
54
72
  const resolved = wiring.resolveArguments(plugin, args.slice(0, arity));
55
73
  const rule = plugin.build(buildRuleContext(wiring, plugin, rawOptions), ...resolved);
56
- return createChainNode(wiring, slot, [...rules, rule]);
74
+ const added = plugin.judgesNull === true
75
+ ? [nullIsAValue(wiring.context.config.defaultSeverity), rule]
76
+ : [rule];
77
+ return createChainNode(wiring, slot, [...rules, ...added]);
57
78
  };
58
79
  }
59
80
  function attachRefineMethods(target, wiring, rules) {
@@ -1,5 +1,5 @@
1
1
  import type { ArrayItemContext, Present } from "../types";
2
- import type { ElementChain, FieldRef, FieldRefs, GuardOut, MarkerRecord, NarrowedChain, PresenceShift, PropertyKeyChain, PropertyValueChain, RootPredicate, RootReader, SelfGuard, SelfReader, SelfValue, TransformOut, Unchanged } from "../plugin-kit/marker.types";
2
+ import type { ElementChain, FieldRef, FieldRefs, GuardOut, MarkerRecord, NarrowedChain, PresenceShift, PropertyKeyChain, PropertyValueChain, RootPredicate, RootReader, SelfGuard, SelfReader, SelfValue, BundleOut, StitchOut, TransformOut, Unchanged } from "../plugin-kit/marker.types";
3
3
  import type { ElementOf } from "../path/element-of.types";
4
4
  import type { PropertyValueOf } from "../path/property-value-of.types";
5
5
  import type { FieldPath } from "../path/field-path.types";
@@ -24,5 +24,7 @@ export type ResolveArgs<A extends readonly unknown[], B extends PluginBag, TRoot
24
24
  /** Presence shifts go through the NAMED operators over ChainState. */
25
25
  export type ResolveOut<O, TValue, TState extends ChainState> = [O] extends [
26
26
  Unchanged
27
- ] ? [TValue, TState] : [O] extends [TransformOut] ? [unknown, TState] : [O] extends [GuardOut] ? [TValue, TState] : [O] extends [PresenceShift<"excludeMissing">] ? [TValue, ExcludeMissing<TState>] : [O] extends [PresenceShift<"excludeUndefined">] ? [TValue, ExcludeUndefined<TState>] : [O] extends [PresenceShift<"excludeNull">] ? [TValue, ExcludeNull<TState>] : [O] extends [PresenceShift<"allowNull">] ? [TValue | null, AllowNull<TState>] : [O, TState];
27
+ ] ? [TValue, TState] : [O] extends [TransformOut] ? [unknown, TState] : [O] extends [GuardOut] ? [TValue, TState] : [
28
+ O
29
+ ] extends [StitchOut] ? [TValue, TState] : [O] extends [BundleOut] ? [TValue, TState] : [O] extends [PresenceShift<"excludeMissing">] ? [TValue, ExcludeMissing<TState>] : [O] extends [PresenceShift<"excludeUndefined">] ? [TValue, ExcludeUndefined<TState>] : [O] extends [PresenceShift<"excludeNull">] ? [TValue, ExcludeNull<TState>] : [O] extends [PresenceShift<"allowNull">] ? [TValue | null, AllowNull<TState>] : [O, TState];
28
30
  export {};
@@ -9,11 +9,4 @@ export interface NodeCompileContext {
9
9
  }
10
10
  /** The one adapter from a grouped declaration to compileField's request. */
11
11
  export declare function compileRelativeDeclaration(declaration: RelativeDeclaration, context: NodeCompileContext): CompiledField;
12
- /**
13
- * Nesting is a re-grouping of the members, not a second traversal strategy:
14
- * `items[*].sub[*].x` arrives here as the group `items` whose one member is
15
- * still `sub[*].x`, and that member groups again into the nested node `sub`.
16
- * `matrix[*][*]` reaches the same shape through an EMPTY nested template —
17
- * the element of the outer array is itself the inner array.
18
- */
19
12
  export declare function compileArrayNode(group: ArrayFieldGroup, context: NodeCompileContext): ArrayNode;
@@ -16,6 +16,7 @@ exports.compileArrayNode = compileArrayNode;
16
16
  // silently unvalidated.
17
17
  // ===========================================================================
18
18
  const create_array_reader_1 = require("../path/create-array-reader");
19
+ const format_issue_path_1 = require("../path/format-issue-path");
19
20
  const compile_field_1 = require("./compile-field");
20
21
  const group_array_fields_1 = require("./group-array-fields");
21
22
  /** The one adapter from a grouped declaration to compileField's request. */
@@ -37,11 +38,14 @@ function compileRelativeDeclaration(declaration, context) {
37
38
  * `matrix[*][*]` reaches the same shape through an EMPTY nested template —
38
39
  * the element of the outer array is itself the inner array.
39
40
  */
41
+ /** A grouped node template never keeps a wildcard, so rendering needs no index. */
42
+ const NO_INDICES = Object.freeze([]);
40
43
  function compileArrayNode(group, context) {
41
44
  const grouped = (0, group_array_fields_1.groupArrayFields)(group.members);
42
45
  const template = Object.freeze(group.template);
43
46
  const node = {
44
47
  template,
48
+ renderedPath: (0, format_issue_path_1.formatIssuePath)(template, NO_INDICES),
45
49
  read: (0, create_array_reader_1.createArrayReader)(template),
46
50
  elementFields: Object.freeze(grouped.direct.map((declaration) => compileRelativeDeclaration(declaration, context))),
47
51
  nested: Object.freeze(grouped.arrays.map((nested) => compileArrayNode(nested, context))),
@@ -12,6 +12,7 @@
12
12
  // silently unvalidated.
13
13
  // ===========================================================================
14
14
  import { createArrayReader } from "../path/create-array-reader.mjs";
15
+ import { formatIssuePath } from "../path/format-issue-path.mjs";
15
16
  import { compileField } from "./compile-field.mjs";
16
17
  import { groupArrayFields, } from "./group-array-fields.mjs";
17
18
  /** The one adapter from a grouped declaration to compileField's request. */
@@ -33,11 +34,14 @@ export function compileRelativeDeclaration(declaration, context) {
33
34
  * `matrix[*][*]` reaches the same shape through an EMPTY nested template —
34
35
  * the element of the outer array is itself the inner array.
35
36
  */
37
+ /** A grouped node template never keeps a wildcard, so rendering needs no index. */
38
+ const NO_INDICES = Object.freeze([]);
36
39
  export function compileArrayNode(group, context) {
37
40
  const grouped = groupArrayFields(group.members);
38
41
  const template = Object.freeze(group.template);
39
42
  const node = {
40
43
  template,
44
+ renderedPath: formatIssuePath(template, NO_INDICES),
41
45
  read: createArrayReader(template),
42
46
  elementFields: Object.freeze(grouped.direct.map((declaration) => compileRelativeDeclaration(declaration, context))),
43
47
  nested: Object.freeze(grouped.arrays.map((nested) => compileArrayNode(nested, context))),
@@ -4,6 +4,7 @@ exports.APPLIES_DEFAULT_TO_NULL_BY_DEFAULT = void 0;
4
4
  exports.compileField = compileField;
5
5
  exports.compileFieldDeclaration = compileFieldDeclaration;
6
6
  const create_value_reader_1 = require("../path/create-value-reader");
7
+ const format_issue_path_1 = require("../path/format-issue-path");
7
8
  const create_value_writer_1 = require("../path/create-value-writer");
8
9
  const parse_field_path_1 = require("../path/parse-field-path");
9
10
  const resolve_conditional_presence_1 = require("./resolve-conditional-presence");
@@ -12,14 +13,22 @@ const resolve_recursion_1 = require("./resolve-recursion");
12
13
  const split_rules_by_kind_1 = require("./split-rules-by-kind");
13
14
  /** Whether a field with no explicit setting replaces a null with its default. */
14
15
  exports.APPLIES_DEFAULT_TO_NULL_BY_DEFAULT = true;
16
+ /** 添字はここでは入らない。開いている添字は実行時に接頭辞が持つ。 */
17
+ const NO_INDICES = Object.freeze([]);
15
18
  function compileField(request) {
16
19
  const byKind = (0, split_rules_by_kind_1.splitRulesByKind)(request.rules);
17
20
  const hasDefault = request.defaultOf !== null;
18
21
  const needsWriter = byKind.transforms.length > 0 || hasDefault;
19
22
  const template = Object.freeze(request.template);
23
+ // read を先に作る。ワイルドカードを含むテンプレートを拒むのは
24
+ // createValueReader の役目で、それより先に formatIssuePath を呼ぶと
25
+ // 「添字が足りない」という RangeError が、本来の PathSyntaxError を
26
+ // 追い越して出てしまう (テストがそれを捕まえた)。
27
+ const read = (0, create_value_reader_1.createValueReader)(template);
20
28
  const field = {
21
29
  template,
22
- read: (0, create_value_reader_1.createValueReader)(template),
30
+ renderedPath: (0, format_issue_path_1.formatIssuePath)(template, NO_INDICES),
31
+ read,
23
32
  write: needsWriter ? (0, create_value_writer_1.createValueWriter)(template) : null,
24
33
  defaultOf: request.defaultOf,
25
34
  applyDefaultToNull: request.applyDefaultToNull,
@@ -1,4 +1,5 @@
1
1
  import { createValueReader } from "../path/create-value-reader.mjs";
2
+ import { formatIssuePath } from "../path/format-issue-path.mjs";
2
3
  import { createValueWriter } from "../path/create-value-writer.mjs";
3
4
  import { parseFieldPath } from "../path/parse-field-path.mjs";
4
5
  import { resolveConditionalPresence } from "./resolve-conditional-presence.mjs";
@@ -7,14 +8,22 @@ import { resolveRecursion } from "./resolve-recursion.mjs";
7
8
  import { splitRulesByKind } from "./split-rules-by-kind.mjs";
8
9
  /** Whether a field with no explicit setting replaces a null with its default. */
9
10
  export const APPLIES_DEFAULT_TO_NULL_BY_DEFAULT = true;
11
+ /** 添字はここでは入らない。開いている添字は実行時に接頭辞が持つ。 */
12
+ const NO_INDICES = Object.freeze([]);
10
13
  export function compileField(request) {
11
14
  const byKind = splitRulesByKind(request.rules);
12
15
  const hasDefault = request.defaultOf !== null;
13
16
  const needsWriter = byKind.transforms.length > 0 || hasDefault;
14
17
  const template = Object.freeze(request.template);
18
+ // read を先に作る。ワイルドカードを含むテンプレートを拒むのは
19
+ // createValueReader の役目で、それより先に formatIssuePath を呼ぶと
20
+ // 「添字が足りない」という RangeError が、本来の PathSyntaxError を
21
+ // 追い越して出てしまう (テストがそれを捕まえた)。
22
+ const read = createValueReader(template);
15
23
  const field = {
16
24
  template,
17
- read: createValueReader(template),
25
+ renderedPath: formatIssuePath(template, NO_INDICES),
26
+ read,
18
27
  write: needsWriter ? createValueWriter(template) : null,
19
28
  defaultOf: request.defaultOf,
20
29
  applyDefaultToNull: request.applyDefaultToNull,
@@ -33,6 +33,7 @@ function toPolicy(rule, allowance) {
33
33
  allowUndefined: allowance.allowUndefined,
34
34
  allowNull: allowance.allowNull,
35
35
  emptyStringIsMissing: allowance.emptyStringIsMissing,
36
+ nullIsValue: allowance.nullIsValue === true,
36
37
  // Wrapped rather than aliased, so a `describe` written as an object
37
38
  // method keeps its receiver.
38
39
  describe: (messageContext) => rule.describe(messageContext),
@@ -29,6 +29,7 @@ function toPolicy(rule, allowance) {
29
29
  allowUndefined: allowance.allowUndefined,
30
30
  allowNull: allowance.allowNull,
31
31
  emptyStringIsMissing: allowance.emptyStringIsMissing,
32
+ nullIsValue: allowance.nullIsValue === true,
32
33
  // Wrapped rather than aliased, so a `describe` written as an object
33
34
  // method keeps its receiver.
34
35
  describe: (messageContext) => rule.describe(messageContext),
@@ -11,6 +11,7 @@ const OPEN_POLICY = {
11
11
  allowUndefined: true,
12
12
  allowNull: true,
13
13
  emptyStringIsMissing: false,
14
+ nullIsValue: false,
14
15
  describe: () => "This field declares no presence rule",
15
16
  };
16
17
  exports.OPEN_PRESENCE = Object.freeze(OPEN_POLICY);
@@ -24,6 +25,9 @@ function resolvePresence(rules) {
24
25
  allowUndefined: rules.some((rule) => rule.allowUndefined),
25
26
  allowNull: rules.some((rule) => rule.allowNull),
26
27
  emptyStringIsMissing: rules.some((rule) => rule.emptyStringIsMissing),
28
+ // One rule saying "null is a value here" is enough: the checks then run
29
+ // and decide, which is strictly more judgement, never less.
30
+ nullIsValue: rules.some((rule) => rule.nullIsValue === true),
27
31
  describe: (ctx) => strictest.describe(ctx),
28
32
  };
29
33
  return Object.freeze(policy);
@@ -7,6 +7,7 @@ const OPEN_POLICY = {
7
7
  allowUndefined: true,
8
8
  allowNull: true,
9
9
  emptyStringIsMissing: false,
10
+ nullIsValue: false,
10
11
  describe: () => "This field declares no presence rule",
11
12
  };
12
13
  export const OPEN_PRESENCE = Object.freeze(OPEN_POLICY);
@@ -20,6 +21,9 @@ export function resolvePresence(rules) {
20
21
  allowUndefined: rules.some((rule) => rule.allowUndefined),
21
22
  allowNull: rules.some((rule) => rule.allowNull),
22
23
  emptyStringIsMissing: rules.some((rule) => rule.emptyStringIsMissing),
24
+ // One rule saying "null is a value here" is enough: the checks then run
25
+ // and decide, which is strictly more judgement, never less.
26
+ nullIsValue: rules.some((rule) => rule.nullIsValue === true),
23
27
  describe: (ctx) => strictest.describe(ctx),
24
28
  };
25
29
  return Object.freeze(policy);
@@ -11,6 +11,30 @@ class UnknownRuleKindError extends Error {
11
11
  }
12
12
  }
13
13
  exports.UnknownRuleKindError = UnknownRuleKindError;
14
+ /**
15
+ * 空の束は一つを共有する。
16
+ *
17
+ * 七つの種のうち、どのフィールドもたいてい二つか三つしか使わない。残りは
18
+ * 空配列だが、フィールドごとに新しく Object.freeze([]) を作っていたので、
19
+ * 空であること自体は同じなのにマップの同一性だけが全部違っていた。実行時の
20
+ * `field.gates.length` や `field.transforms` の読み出しは、フィールドを
21
+ * またぐたびに別の受け手を見ることになる。
22
+ *
23
+ * この置き換えは src/compile/resolve-conditional-presence.ts の
24
+ * NO_PRESENCE_OVERRIDES と src/runtime/output-writer.ts の NO_WRITE_TARGETS が
25
+ * 既にやっていることを、残りの種にも広げただけである。
26
+ */
27
+ const NO_RULES_OF_THIS_KIND = Object.freeze([]);
28
+ /**
29
+ * 空なら共有の一つを返す。中身があるならその配列を凍結して返す。
30
+ *
31
+ * `readonly never[]` はどの `readonly R[]` にも代入できるので、共有する
32
+ * ためにアサーションを書く必要はない。型アサーションを書いてよいのは
33
+ * src/core/type-erasure.ts だけである。
34
+ */
35
+ function freezeRules(rules) {
36
+ return rules.length === 0 ? NO_RULES_OF_THIS_KIND : Object.freeze(rules);
37
+ }
14
38
  function splitRulesByKind(rules) {
15
39
  const checks = [];
16
40
  const presences = [];
@@ -38,13 +62,13 @@ function splitRulesByKind(rules) {
38
62
  rejectUnknownRuleKind(rule);
39
63
  }
40
64
  return Object.freeze({
41
- checks: Object.freeze(checks),
42
- presences: Object.freeze(presences),
43
- conditionalPresences: Object.freeze(conditionalPresences),
44
- gates: Object.freeze(gates),
45
- transforms: Object.freeze(transforms),
46
- composites: Object.freeze(composites),
47
- recursions: Object.freeze(recursions),
65
+ checks: freezeRules(checks),
66
+ presences: freezeRules(presences),
67
+ conditionalPresences: freezeRules(conditionalPresences),
68
+ gates: freezeRules(gates),
69
+ transforms: freezeRules(transforms),
70
+ composites: freezeRules(composites),
71
+ recursions: freezeRules(recursions),
48
72
  });
49
73
  }
50
74
  /** The exhaustiveness proof: the parameter is `never`, so an eighth kind is a
@@ -6,6 +6,30 @@ export class UnknownRuleKindError extends Error {
6
6
  this.name = "UnknownRuleKindError";
7
7
  }
8
8
  }
9
+ /**
10
+ * 空の束は一つを共有する。
11
+ *
12
+ * 七つの種のうち、どのフィールドもたいてい二つか三つしか使わない。残りは
13
+ * 空配列だが、フィールドごとに新しく Object.freeze([]) を作っていたので、
14
+ * 空であること自体は同じなのにマップの同一性だけが全部違っていた。実行時の
15
+ * `field.gates.length` や `field.transforms` の読み出しは、フィールドを
16
+ * またぐたびに別の受け手を見ることになる。
17
+ *
18
+ * この置き換えは src/compile/resolve-conditional-presence.ts の
19
+ * NO_PRESENCE_OVERRIDES と src/runtime/output-writer.ts の NO_WRITE_TARGETS が
20
+ * 既にやっていることを、残りの種にも広げただけである。
21
+ */
22
+ const NO_RULES_OF_THIS_KIND = Object.freeze([]);
23
+ /**
24
+ * 空なら共有の一つを返す。中身があるならその配列を凍結して返す。
25
+ *
26
+ * `readonly never[]` はどの `readonly R[]` にも代入できるので、共有する
27
+ * ためにアサーションを書く必要はない。型アサーションを書いてよいのは
28
+ * src/core/type-erasure.ts だけである。
29
+ */
30
+ function freezeRules(rules) {
31
+ return rules.length === 0 ? NO_RULES_OF_THIS_KIND : Object.freeze(rules);
32
+ }
9
33
  export function splitRulesByKind(rules) {
10
34
  const checks = [];
11
35
  const presences = [];
@@ -33,13 +57,13 @@ export function splitRulesByKind(rules) {
33
57
  rejectUnknownRuleKind(rule);
34
58
  }
35
59
  return Object.freeze({
36
- checks: Object.freeze(checks),
37
- presences: Object.freeze(presences),
38
- conditionalPresences: Object.freeze(conditionalPresences),
39
- gates: Object.freeze(gates),
40
- transforms: Object.freeze(transforms),
41
- composites: Object.freeze(composites),
42
- recursions: Object.freeze(recursions),
60
+ checks: freezeRules(checks),
61
+ presences: freezeRules(presences),
62
+ conditionalPresences: freezeRules(conditionalPresences),
63
+ gates: freezeRules(gates),
64
+ transforms: freezeRules(transforms),
65
+ composites: freezeRules(composites),
66
+ recursions: freezeRules(recursions),
43
67
  });
44
68
  }
45
69
  /** The exhaustiveness proof: the parameter is `never`, so an eighth kind is a
@@ -23,6 +23,16 @@ export interface PresencePolicy {
23
23
  readonly allowUndefined: boolean;
24
24
  readonly allowNull: boolean;
25
25
  readonly emptyStringIsMissing: boolean;
26
+ /**
27
+ * True when `null` is a VALUE this subject has to judge, rather than an
28
+ * absence presence may settle. Draft-07 sub-schemas need it: `false`,
29
+ * `{"not": {}}` and an `enum` without null all forbid null WITHOUT saying
30
+ * anything about `type`, and presence — which runs first — was answering
31
+ * for them. `[null]` passed `{"items":{"not":{}}}` because no check ever
32
+ * ran. Only src/json-schema/ sets it; a field the user declared keeps the
33
+ * builder's meaning, where `.nullable()` ends the field.
34
+ */
35
+ readonly nullIsValue: boolean;
26
36
  describe(ctx: MessageContext): string;
27
37
  }
28
38
  /**
@@ -55,6 +65,17 @@ export interface RecursionPolicy {
55
65
  }
56
66
  export interface CompiledField {
57
67
  readonly template: readonly PathSegment[];
68
+ /**
69
+ * この フィールド自身のパスを、配列の添字を除いて描画したもの。
70
+ *
71
+ * コンパイル時に一度だけ作る。実行時に組み直していたのは、テンプレートが
72
+ * 固定なので **毎回同じ文字列を作る** ことを意味していた: 50要素・3
73
+ * フィールドの配列なら、1回の validate で 150 回、issue が1件も出なくても
74
+ * である。実測でそれが要素あたりの費用の 35% を占めていた。
75
+ *
76
+ * 実行時に残るのは、開いている添字の接頭辞と繋ぐ連結ひとつだけになる。
77
+ */
78
+ readonly renderedPath: string;
58
79
  readonly read: (subject: unknown) => unknown;
59
80
  /**
60
81
  * null unless the field declared a transform or a default — the runtime then
@@ -85,6 +106,18 @@ export interface CompiledField {
85
106
  /** Loop interchange: one array is read once however many element fields exist. */
86
107
  export interface ArrayNode {
87
108
  readonly template: readonly PathSegment[];
109
+ /**
110
+ * The node's own path, rendered once at build time — `lines`, never
111
+ * `lines[*]`, because the grouping already cut the wildcard off.
112
+ *
113
+ * CompiledField has carried its `renderedPath` since rebuilding it per
114
+ * element was measured at 35% of the per-element price. The array NODE was
115
+ * left behind and went on rendering the same string on every validate().
116
+ * Rendering can throw on a template with an unconsumed wildcard, so moving
117
+ * it here also moves that throw from validate() to build(), which is the
118
+ * direction this layer is supposed to push everything.
119
+ */
120
+ readonly renderedPath: string;
88
121
  readonly read: (subject: unknown) => unknown;
89
122
  readonly elementFields: readonly CompiledField[];
90
123
  readonly nested: readonly ArrayNode[];
@@ -8,7 +8,7 @@ const create_field_builder_1 = require("../builder/create-field-builder");
8
8
  const type_erasure_1 = require("../core/type-erasure");
9
9
  const create_structural_context_1 = require("./create-structural-context");
10
10
  const declare_presence_1 = require("./declare-presence");
11
- const declare_value_keywords_1 = require("./declare-value-keywords");
11
+ const ref_scope_1 = require("./ref-scope");
12
12
  const draft07_types_1 = require("./draft07.types");
13
13
  const flatten_schema_1 = require("./flatten-schema");
14
14
  const schema_to_declarations_1 = require("./schema-to-declarations");
@@ -25,11 +25,10 @@ class NotASchemaError extends Error {
25
25
  exports.NotASchemaError = NotASchemaError;
26
26
  /** The rules ONE declared path carries: its presence policy and its schema. */
27
27
  function collectDeclaredRules(declaration, bag, root, chain) {
28
- const context = (0, create_structural_context_1.createStructuralContext)({ bag, root, chain }, declaration.schema, Object.freeze([]));
28
+ const context = (0, create_structural_context_1.createStructuralContext)({ bag, scope: (0, ref_scope_1.createLocalScope)(root), chain }, declaration.schema, Object.freeze([]));
29
29
  return Object.freeze([
30
30
  ...(0, declare_presence_1.declarePresenceRules)({
31
31
  isRequired: declaration.isRequired,
32
- allowsNull: (0, declare_value_keywords_1.permitsNull)(declaration.schema),
33
32
  severity: chain.config.defaultSeverity,
34
33
  }),
35
34
  ...(0, schema_to_declarations_1.expandSchemaRules)(declaration.schema, context),
@@ -2,7 +2,7 @@ import { createFieldBuilderSurface } from "../builder/create-field-builder.mjs";
2
2
  import { eraseSchemaValidator } from "../core/type-erasure.mjs";
3
3
  import { createStructuralContext } from "./create-structural-context.mjs";
4
4
  import { declarePresenceRules } from "./declare-presence.mjs";
5
- import { permitsNull } from "./declare-value-keywords.mjs";
5
+ import { createLocalScope } from "./ref-scope.mjs";
6
6
  import { isDraft07Schema } from "./draft07.types.mjs";
7
7
  import { flattenSchema } from "./flatten-schema.mjs";
8
8
  import { expandSchemaRules, readChildSchemas } from "./schema-to-declarations.mjs";
@@ -18,11 +18,10 @@ export class NotASchemaError extends Error {
18
18
  }
19
19
  /** The rules ONE declared path carries: its presence policy and its schema. */
20
20
  function collectDeclaredRules(declaration, bag, root, chain) {
21
- const context = createStructuralContext({ bag, root, chain }, declaration.schema, Object.freeze([]));
21
+ const context = createStructuralContext({ bag, scope: createLocalScope(root), chain }, declaration.schema, Object.freeze([]));
22
22
  return Object.freeze([
23
23
  ...declarePresenceRules({
24
24
  isRequired: declaration.isRequired,
25
- allowsNull: permitsNull(declaration.schema),
26
25
  severity: chain.config.defaultSeverity,
27
26
  }),
28
27
  ...expandSchemaRules(declaration.schema, context),
@@ -1,4 +1,5 @@
1
1
  import type { Draft07Schema, Draft07SchemaObject } from "./draft07.types";
2
+ import type { RefScope } from "./ref-scope";
2
3
  /** The two container spellings, in the order a pointer would try them. */
3
4
  export declare const DEFINITION_CONTAINERS: readonly string[];
4
5
  export declare function isDefinitionContainer(keyword: string): boolean;
@@ -10,6 +11,35 @@ export declare function toSchemaObject(schema: Draft07Schema): Draft07SchemaObje
10
11
  * this draft `$ref` replaces the object it appears in.
11
12
  */
12
13
  export declare function resolveSchemaNode(schema: Draft07Schema, root: Draft07Schema): Draft07SchemaObject;
14
+ /** What a node resolved to, and the scope that is in force INSIDE it. */
15
+ export interface ResolvedNode {
16
+ readonly node: Draft07SchemaObject;
17
+ readonly scope: RefScope;
18
+ }
19
+ /**
20
+ * The scope-aware form, and the one the converter uses.
21
+ *
22
+ * The returned scope is not the one passed in: following a `$ref` can cross
23
+ * into another document, and a `$ref` written inside THAT document resolves
24
+ * against ITS base. Returning only the node — which is what the root-taking
25
+ * form above can do — loses that, and is why a two-document schema resolved
26
+ * its second hop against the first document.
27
+ */
28
+ export declare function resolveSchemaNodeInScope(schema: Draft07Schema, scope: RefScope): ResolvedNode;
29
+ /**
30
+ * The ONE place a base URI advances. It was also being done by the caller,
31
+ * and doing it in both meant a relative `$id` was applied twice: a `$ref`
32
+ * of "nested/foo.json" under `$id: "nested/"` went looking for
33
+ * ".../nested/nested/foo.json".
34
+ *
35
+ * A base-setting `$id` starts a new RESOURCE, so the node also becomes the
36
+ * document that `#/definitions/x` written under it resolves against. Moving
37
+ * the base without moving the document is why
38
+ * `{"$id":"a.json","properties":{"foo":{"$id":"b.json","definitions":{...},
39
+ * "allOf":[{"$ref":"#/definitions/inner"}]}}}` looked for `inner` in the
40
+ * OUTER document, where it does not exist.
41
+ */
42
+ export declare function advanceBase(scope: RefScope, node: Draft07SchemaObject): RefScope;
13
43
  /**
14
44
  * The pointer a node forwards to, or undefined. Used by the flattener to spot
15
45
  * a recursive definition BEFORE it walks into it: `#/definitions/node` inside
@@ -4,10 +4,14 @@ exports.DEFINITION_CONTAINERS = void 0;
4
4
  exports.isDefinitionContainer = isDefinitionContainer;
5
5
  exports.toSchemaObject = toSchemaObject;
6
6
  exports.resolveSchemaNode = resolveSchemaNode;
7
+ exports.resolveSchemaNodeInScope = resolveSchemaNodeInScope;
8
+ exports.advanceBase = advanceBase;
7
9
  exports.readRefPointer = readRefPointer;
8
10
  exports.collectDefinitionNames = collectDefinitionNames;
9
11
  const draft07_types_1 = require("./draft07.types");
12
+ const ref_scope_1 = require("./ref-scope");
10
13
  const resolve_ref_1 = require("./resolve-ref");
14
+ const uri_reference_1 = require("./uri-reference");
11
15
  /** `true`: matches every instance. */
12
16
  const ALWAYS_SCHEMA = Object.freeze({});
13
17
  /** `false`: matches nothing. `not` of the empty schema, so no new rule kind. */
@@ -36,10 +40,50 @@ function toSchemaObject(schema) {
36
40
  * this draft `$ref` replaces the object it appears in.
37
41
  */
38
42
  function resolveSchemaNode(schema, root) {
43
+ return resolveSchemaNodeInScope(schema, (0, ref_scope_1.createLocalScope)(root)).node;
44
+ }
45
+ /**
46
+ * The scope-aware form, and the one the converter uses.
47
+ *
48
+ * The returned scope is not the one passed in: following a `$ref` can cross
49
+ * into another document, and a `$ref` written inside THAT document resolves
50
+ * against ITS base. Returning only the node — which is what the root-taking
51
+ * form above can do — loses that, and is why a two-document schema resolved
52
+ * its second hop against the first document.
53
+ */
54
+ function resolveSchemaNodeInScope(schema, scope) {
39
55
  const node = toSchemaObject(schema);
40
- if (node.$ref === undefined)
41
- return node;
42
- return toSchemaObject((0, resolve_ref_1.resolveRef)(node.$ref, root));
56
+ if (node.$ref === undefined) {
57
+ // A node's own `$id` moves the base for everything inside it (§8.2).
58
+ return { node, scope: advanceBase(scope, node) };
59
+ }
60
+ // Following a `$ref` lands in whatever base the TARGET lives in, and
61
+ // resolve-ref already knows it — for a document fetched by URI that is the
62
+ // retrieval URI, which the draft says wins over the document's own `$id`.
63
+ const resolved = (0, resolve_ref_1.resolveRefInScope)(node.$ref, scope);
64
+ return { node: toSchemaObject(resolved.schema), scope: resolved.scope };
65
+ }
66
+ /**
67
+ * The ONE place a base URI advances. It was also being done by the caller,
68
+ * and doing it in both meant a relative `$id` was applied twice: a `$ref`
69
+ * of "nested/foo.json" under `$id: "nested/"` went looking for
70
+ * ".../nested/nested/foo.json".
71
+ *
72
+ * A base-setting `$id` starts a new RESOURCE, so the node also becomes the
73
+ * document that `#/definitions/x` written under it resolves against. Moving
74
+ * the base without moving the document is why
75
+ * `{"$id":"a.json","properties":{"foo":{"$id":"b.json","definitions":{...},
76
+ * "allOf":[{"$ref":"#/definitions/inner"}]}}}` looked for `inner` in the
77
+ * OUTER document, where it does not exist.
78
+ */
79
+ function advanceBase(scope, node) {
80
+ const id = node.$id;
81
+ if (typeof id !== "string" || id === "")
82
+ return scope;
83
+ const baseUri = (0, uri_reference_1.nextBaseUri)(scope.baseUri, id);
84
+ if (baseUri === scope.baseUri)
85
+ return scope;
86
+ return { ...scope, baseUri, document: node };
43
87
  }
44
88
  /**
45
89
  * The pointer a node forwards to, or undefined. Used by the flattener to spot