@maroonedog/luq 2.2.0 → 2.3.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 (133) hide show
  1. package/README.md +47 -602
  2. package/dist/builder/compile-declarations.d.ts +7 -1
  3. package/dist/builder/compile-declarations.js +18 -8
  4. package/dist/builder/compile-declarations.mjs +18 -8
  5. package/dist/builder/create-builder.js +2 -3
  6. package/dist/builder/create-builder.mjs +2 -3
  7. package/dist/builder/create-field-builder.js +13 -1
  8. package/dist/builder/create-field-builder.mjs +13 -1
  9. package/dist/builder/declared-calls-store.d.ts +9 -0
  10. package/dist/builder/declared-calls-store.js +19 -0
  11. package/dist/builder/declared-calls-store.mjs +15 -0
  12. package/dist/builder/field-declared-calls.types.d.ts +6 -0
  13. package/dist/builder/field-declared-calls.types.js +2 -0
  14. package/dist/builder/field-declared-calls.types.mjs +1 -0
  15. package/dist/builder/field-entry.types.d.ts +9 -3
  16. package/dist/builder/field-options.types.d.ts +26 -0
  17. package/dist/chain/bundle-paths.types.d.ts +9 -12
  18. package/dist/chain/chain-node-store.d.ts +5 -0
  19. package/dist/chain/chain-node-store.js +15 -0
  20. package/dist/chain/chain-node-store.mjs +11 -0
  21. package/dist/chain/collect-field-rules.d.ts +13 -2
  22. package/dist/chain/collect-field-rules.js +10 -3
  23. package/dist/chain/collect-field-rules.mjs +10 -3
  24. package/dist/chain/create-chain-node.js +19 -9
  25. package/dist/chain/create-chain-node.mjs +19 -9
  26. package/dist/chain/declaration-recorder.port.d.ts +31 -0
  27. package/dist/chain/declaration-recorder.port.js +17 -0
  28. package/dist/chain/declaration-recorder.port.mjs +13 -0
  29. package/dist/chain/declared-call.types.d.ts +15 -0
  30. package/dist/chain/declared-call.types.js +2 -0
  31. package/dist/chain/declared-call.types.mjs +1 -0
  32. package/dist/chain/index.d.ts +3 -1
  33. package/dist/compile/compile-array-node.js +1 -0
  34. package/dist/compile/compile-array-node.mjs +1 -0
  35. package/dist/compile/compile-field.d.ts +1 -0
  36. package/dist/compile/compile-field.js +8 -6
  37. package/dist/compile/compile-field.mjs +8 -6
  38. package/dist/compile/compile-schema.js +4 -0
  39. package/dist/compile/compile-schema.mjs +4 -0
  40. package/dist/compile/group-array-fields.d.ts +1 -0
  41. package/dist/compile/split-rules-by-kind.js +10 -14
  42. package/dist/compile/split-rules-by-kind.mjs +10 -14
  43. package/dist/compile/validation-plan.types.d.ts +19 -6
  44. package/dist/core/type-erasure.d.ts +36 -30
  45. package/dist/core/type-erasure.js +36 -30
  46. package/dist/core/type-erasure.mjs +36 -30
  47. package/dist/json-schema/build-from-schema.js +8 -1
  48. package/dist/json-schema/build-from-schema.mjs +8 -1
  49. package/dist/json-schema/declare-additional-properties.d.ts +7 -7
  50. package/dist/json-schema/declare-additional-properties.js +7 -7
  51. package/dist/json-schema/declare-additional-properties.mjs +7 -7
  52. package/dist/json-schema/declare-object-keywords.js +4 -4
  53. package/dist/json-schema/declare-object-keywords.mjs +4 -4
  54. package/dist/json-schema/follow-json-pointer.d.ts +7 -6
  55. package/dist/json-schema/follow-json-pointer.js +24 -24
  56. package/dist/json-schema/follow-json-pointer.mjs +24 -24
  57. package/dist/json-schema/ref-resolution-error.js +3 -3
  58. package/dist/json-schema/ref-resolution-error.mjs +3 -3
  59. package/dist/json-schema/schema-registry.js +12 -11
  60. package/dist/json-schema/schema-registry.mjs +12 -11
  61. package/dist/json-schema/uri-reference.js +12 -12
  62. package/dist/json-schema/uri-reference.mjs +12 -12
  63. package/dist/path/create-value-writer.js +12 -12
  64. package/dist/path/create-value-writer.mjs +12 -12
  65. package/dist/path/reserved-segment.d.ts +16 -16
  66. package/dist/path/reserved-segment.js +17 -21
  67. package/dist/path/reserved-segment.mjs +17 -21
  68. package/dist/plugins/index.generated.js +2 -2
  69. package/dist/plugins/index.generated.mjs +2 -2
  70. package/dist/plugins/manifest.generated.js +2 -2
  71. package/dist/plugins/manifest.generated.mjs +2 -2
  72. package/dist/plugins/object-additional-properties/select-additional-keys.d.ts +8 -8
  73. package/dist/plugins/object-additional-properties/select-additional-keys.js +16 -16
  74. package/dist/plugins/object-additional-properties/select-additional-keys.mjs +16 -16
  75. package/dist/plugins/stitch/stitch.d.ts +10 -14
  76. package/dist/plugins/stitch-with/stitch-with.d.ts +1 -1
  77. package/dist/plugins/stitch-with/stitch-with.js +22 -24
  78. package/dist/plugins/stitch-with/stitch-with.mjs +22 -24
  79. package/dist/plugins/string-min/string-min.js +6 -8
  80. package/dist/plugins/string-min/string-min.mjs +6 -8
  81. package/dist/presets/presets.d.ts +10 -11
  82. package/dist/presets/presets.js +22 -23
  83. package/dist/presets/presets.mjs +22 -23
  84. package/dist/runtime/create-field-validator.js +4 -6
  85. package/dist/runtime/create-field-validator.mjs +4 -6
  86. package/dist/runtime/create-validator.js +11 -11
  87. package/dist/runtime/create-validator.mjs +11 -11
  88. package/dist/runtime/output-writer.js +5 -1
  89. package/dist/runtime/output-writer.mjs +5 -1
  90. package/dist/runtime/run-array-node.js +6 -6
  91. package/dist/runtime/run-array-node.mjs +6 -6
  92. package/dist/runtime/run-field.js +18 -19
  93. package/dist/runtime/run-field.mjs +18 -19
  94. package/dist/standard-schema/assemble-json-schema.d.ts +4 -0
  95. package/dist/standard-schema/assemble-json-schema.js +95 -0
  96. package/dist/standard-schema/assemble-json-schema.mjs +92 -0
  97. package/dist/standard-schema/declaration-recorder.d.ts +6 -0
  98. package/dist/standard-schema/declaration-recorder.js +30 -0
  99. package/dist/standard-schema/declaration-recorder.mjs +27 -0
  100. package/dist/standard-schema/declarations-unavailable-error.d.ts +4 -0
  101. package/dist/standard-schema/declarations-unavailable-error.js +32 -0
  102. package/dist/standard-schema/declarations-unavailable-error.mjs +28 -0
  103. package/dist/standard-schema/emit-field-schema.d.ts +9 -0
  104. package/dist/standard-schema/emit-field-schema.js +68 -0
  105. package/dist/standard-schema/emit-field-schema.mjs +65 -0
  106. package/dist/standard-schema/index.d.ts +5 -0
  107. package/dist/standard-schema/index.js +9 -1
  108. package/dist/standard-schema/index.mjs +4 -0
  109. package/dist/standard-schema/json-schema-target.d.ts +6 -0
  110. package/dist/standard-schema/json-schema-target.js +44 -0
  111. package/dist/standard-schema/json-schema-target.mjs +39 -0
  112. package/dist/standard-schema/plugin-keyword-map.d.ts +3 -0
  113. package/dist/standard-schema/plugin-keyword-map.js +93 -0
  114. package/dist/standard-schema/plugin-keyword-map.mjs +90 -0
  115. package/dist/standard-schema/split-issue-path.d.ts +6 -4
  116. package/dist/standard-schema/split-issue-path.js +15 -13
  117. package/dist/standard-schema/split-issue-path.mjs +15 -13
  118. package/dist/standard-schema/standard-schema.types.d.ts +8 -7
  119. package/dist/standard-schema/standard-schema.types.js +6 -6
  120. package/dist/standard-schema/standard-schema.types.mjs +6 -6
  121. package/dist/standard-schema/to-standard-json-schema.d.ts +19 -0
  122. package/dist/standard-schema/to-standard-json-schema.js +36 -0
  123. package/dist/standard-schema/to-standard-json-schema.mjs +33 -0
  124. package/dist/standard-schema/to-standard-schema.d.ts +16 -15
  125. package/dist/standard-schema/to-standard-schema.js +15 -22
  126. package/dist/standard-schema/to-standard-schema.mjs +15 -22
  127. package/dist/standard-schema/unrepresentable-rule-error.d.ts +15 -0
  128. package/dist/standard-schema/unrepresentable-rule-error.js +43 -0
  129. package/dist/standard-schema/unrepresentable-rule-error.mjs +38 -0
  130. package/dist/types/index.d.ts +12 -12
  131. package/dist/types/index.js +7 -7
  132. package/dist/types/index.mjs +7 -7
  133. package/package.json +1 -1
@@ -1,6 +1,5 @@
1
1
  /**
2
- * 在る・無い・null。ほぼ全ての宣言がこの三つのどれかを使う。
3
- * 使用実績: required 138 / optional 26 / nullable 12。
2
+ * Present, absent, null. Nearly every declaration uses one of the three.
4
3
  */
5
4
  export declare const presence: Readonly<{
6
5
  required: import("..").PluginDefinition<"required", "required", readonly import("..").TypeName[], {
@@ -19,7 +18,7 @@ export declare const presence: Readonly<{
19
18
  context: import("..").MessageContextExtra;
20
19
  }>;
21
20
  }>;
22
- /** 文字列の定番。使用実績: stringMin 95 / stringEmail 33 / stringPattern 12。 */
21
+ /** The everyday string checks. */
23
22
  export declare const strings: Readonly<{
24
23
  stringMin: import("..").PluginDefinition<"stringMin", "min", readonly ["string"], {
25
24
  args: readonly [min: number];
@@ -46,7 +45,7 @@ export declare const strings: Readonly<{
46
45
  };
47
46
  }>;
48
47
  }>;
49
- /** 数値の定番。使用実績: numberMin 43 / numberMax 8。 */
48
+ /** The everyday number checks. */
50
49
  export declare const numbers: Readonly<{
51
50
  numberMin: import("..").PluginDefinition<"numberMin", "min", readonly ["number"], {
52
51
  args: readonly [min: number, exclusive?: boolean];
@@ -64,7 +63,7 @@ export declare const numbers: Readonly<{
64
63
  context: import("..").MessageContextExtra;
65
64
  }>;
66
65
  }>;
67
- /** 配列の定番。要素ごとの規則は arrayEach が運ぶ。 */
66
+ /** The everyday array checks. Per-element rules ride on arrayEach. */
68
67
  export declare const arrays: Readonly<{
69
68
  arrayMinLength: import("..").PluginDefinition<"arrayMinLength", "minLength", readonly ["array", "tuple"], {
70
69
  args: readonly [min: number];
@@ -83,14 +82,14 @@ export declare const arrays: Readonly<{
83
82
  }>;
84
83
  }>;
85
84
  /**
86
- * 上の四つを合わせたもの。13プラグイン、gzip +1,452 B。
85
+ * The four bundles above, together.
87
86
  *
88
- * 名前が「common」でないのは、それが何も言っていないからである
89
- * (lint の禁止語彙にも入っている)。入っているのは presence と、文字列・数値・
90
- * 配列それぞれの定番で、毎日書くのはこの範囲だ、という主張がこの名前である。
87
+ * Not called "common", because that name says nothing — and lint bans it. It
88
+ * holds presence plus the everyday string, number and array checks, and the
89
+ * name is the claim that this is the range people write day to day.
91
90
  *
92
- * これでも「全部入り」ではない。全部入りが要るなら `@maroonedog/luq/plugins`
93
- * のバレルがあり、そちらは 77 個ぶん、gzip で +17,986 B を払う。
91
+ * Still not everything. For everything there is the `@maroonedog/luq/plugins`
92
+ * barrel, which costs accordingly.
94
93
  */
95
94
  export declare const everydayRules: Readonly<{
96
95
  arrayMinLength: import("..").PluginDefinition<"arrayMinLength", "minLength", readonly ["array", "tuple"], {
@@ -2,24 +2,24 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.everydayRules = exports.arrays = exports.numbers = exports.strings = exports.presence = void 0;
4
4
  // ===========================================================================
5
- // L7 src/presets/presets.ts — 事前定義したプラグイン束。
5
+ // L7 src/presets/presets.ts — predefined plugin bundles.
6
6
  //
7
- // なぜ要るのか。プラグインを1つずつ import するのは「使った分しか入らない」
8
- // を成り立たせている仕組みそのものだが、最初の一本を書くのに15行の use()
9
- // 並べさせるのは、その正しさの押し付けである。
7
+ // Why they exist. Importing plugins one at a time is the very mechanism behind
8
+ // "you only ship what you used", but making someone write fifteen use() lines
9
+ // for their first validator is imposing that correctness on them.
10
10
  //
11
- // なぜ「小さいのを複数」なのか。全部入りを1つ置くと、5個しか要らない人が
12
- // 40個分のバイトを払う。束は型としてはただの `PluginBag` なので、必要な束
13
- // だけを `useAll()` すればよく、混ぜても first-wins で衝突しない:
11
+ // Why several small ones rather than one big one. A single everything-bundle
12
+ // bills the person who needs five plugins for forty. A bundle is just a
13
+ // `PluginBag`, so useAll() takes only the ones wanted, and mixing them is
14
+ // first-wins rather than a collision:
14
15
  //
15
16
  // Builder().useAll(presence).useAll(strings).for<User>()
16
17
  //
17
- // 何を入れるかはリポジトリ内の使用実績から決めた (test/ docs-site/
18
- // use() を数えたもの)。required 138 / stringMin 95 / numberMin 43 /
19
- // stringEmail 33 / optional 26 という並びで、上位が束の中身になっている。
18
+ // Membership was chosen from how often each plugin is actually used across
19
+ // this repository, most-used first.
20
20
  //
21
- // バイト数は config/size-budget.json が測る。プリセットは便利さと引き換えに
22
- // バイトを払うものなので、いくら払うかは書いてあるべきである。
21
+ // What a bundle costs in bytes is measured by the size budget. A preset trades
22
+ // bytes for convenience, so the price should be measured rather than asserted.
23
23
  // ===========================================================================
24
24
  const required_1 = require("../plugins/required");
25
25
  const optional_1 = require("../plugins/optional");
@@ -35,42 +35,41 @@ const array_min_length_1 = require("../plugins/array-min-length");
35
35
  const array_max_length_1 = require("../plugins/array-max-length");
36
36
  const array_each_1 = require("../plugins/array-each");
37
37
  /**
38
- * 在る・無い・null。ほぼ全ての宣言がこの三つのどれかを使う。
39
- * 使用実績: required 138 / optional 26 / nullable 12。
38
+ * Present, absent, null. Nearly every declaration uses one of the three.
40
39
  */
41
40
  exports.presence = Object.freeze({
42
41
  required: required_1.requiredPlugin,
43
42
  optional: optional_1.optionalPlugin,
44
43
  nullable: nullable_1.nullablePlugin,
45
44
  });
46
- /** 文字列の定番。使用実績: stringMin 95 / stringEmail 33 / stringPattern 12。 */
45
+ /** The everyday string checks. */
47
46
  exports.strings = Object.freeze({
48
47
  stringMin: string_min_1.stringMinPlugin,
49
48
  stringMax: string_max_1.stringMaxPlugin,
50
49
  stringPattern: string_pattern_1.stringPatternPlugin,
51
50
  stringEmail: string_email_1.stringEmailPlugin,
52
51
  });
53
- /** 数値の定番。使用実績: numberMin 43 / numberMax 8。 */
52
+ /** The everyday number checks. */
54
53
  exports.numbers = Object.freeze({
55
54
  numberMin: number_min_1.numberMinPlugin,
56
55
  numberMax: number_max_1.numberMaxPlugin,
57
56
  numberInteger: number_integer_1.numberIntegerPlugin,
58
57
  });
59
- /** 配列の定番。要素ごとの規則は arrayEach が運ぶ。 */
58
+ /** The everyday array checks. Per-element rules ride on arrayEach. */
60
59
  exports.arrays = Object.freeze({
61
60
  arrayMinLength: array_min_length_1.arrayMinLengthPlugin,
62
61
  arrayMaxLength: array_max_length_1.arrayMaxLengthPlugin,
63
62
  arrayEach: array_each_1.arrayEachPlugin,
64
63
  });
65
64
  /**
66
- * 上の四つを合わせたもの。13プラグイン、gzip +1,452 B。
65
+ * The four bundles above, together.
67
66
  *
68
- * 名前が「common」でないのは、それが何も言っていないからである
69
- * (lint の禁止語彙にも入っている)。入っているのは presence と、文字列・数値・
70
- * 配列それぞれの定番で、毎日書くのはこの範囲だ、という主張がこの名前である。
67
+ * Not called "common", because that name says nothing — and lint bans it. It
68
+ * holds presence plus the everyday string, number and array checks, and the
69
+ * name is the claim that this is the range people write day to day.
71
70
  *
72
- * これでも「全部入り」ではない。全部入りが要るなら `@maroonedog/luq/plugins`
73
- * のバレルがあり、そちらは 77 個ぶん、gzip で +17,986 B を払う。
71
+ * Still not everything. For everything there is the `@maroonedog/luq/plugins`
72
+ * barrel, which costs accordingly.
74
73
  */
75
74
  exports.everydayRules = Object.freeze({
76
75
  ...exports.presence,
@@ -1,22 +1,22 @@
1
1
  // ===========================================================================
2
- // L7 src/presets/presets.ts — 事前定義したプラグイン束。
2
+ // L7 src/presets/presets.ts — predefined plugin bundles.
3
3
  //
4
- // なぜ要るのか。プラグインを1つずつ import するのは「使った分しか入らない」
5
- // を成り立たせている仕組みそのものだが、最初の一本を書くのに15行の use()
6
- // 並べさせるのは、その正しさの押し付けである。
4
+ // Why they exist. Importing plugins one at a time is the very mechanism behind
5
+ // "you only ship what you used", but making someone write fifteen use() lines
6
+ // for their first validator is imposing that correctness on them.
7
7
  //
8
- // なぜ「小さいのを複数」なのか。全部入りを1つ置くと、5個しか要らない人が
9
- // 40個分のバイトを払う。束は型としてはただの `PluginBag` なので、必要な束
10
- // だけを `useAll()` すればよく、混ぜても first-wins で衝突しない:
8
+ // Why several small ones rather than one big one. A single everything-bundle
9
+ // bills the person who needs five plugins for forty. A bundle is just a
10
+ // `PluginBag`, so useAll() takes only the ones wanted, and mixing them is
11
+ // first-wins rather than a collision:
11
12
  //
12
13
  // Builder().useAll(presence).useAll(strings).for<User>()
13
14
  //
14
- // 何を入れるかはリポジトリ内の使用実績から決めた (test/ docs-site/
15
- // use() を数えたもの)。required 138 / stringMin 95 / numberMin 43 /
16
- // stringEmail 33 / optional 26 という並びで、上位が束の中身になっている。
15
+ // Membership was chosen from how often each plugin is actually used across
16
+ // this repository, most-used first.
17
17
  //
18
- // バイト数は config/size-budget.json が測る。プリセットは便利さと引き換えに
19
- // バイトを払うものなので、いくら払うかは書いてあるべきである。
18
+ // What a bundle costs in bytes is measured by the size budget. A preset trades
19
+ // bytes for convenience, so the price should be measured rather than asserted.
20
20
  // ===========================================================================
21
21
  import { requiredPlugin } from "../plugins/required/index.mjs";
22
22
  import { optionalPlugin } from "../plugins/optional/index.mjs";
@@ -32,42 +32,41 @@ import { arrayMinLengthPlugin } from "../plugins/array-min-length/index.mjs";
32
32
  import { arrayMaxLengthPlugin } from "../plugins/array-max-length/index.mjs";
33
33
  import { arrayEachPlugin } from "../plugins/array-each/index.mjs";
34
34
  /**
35
- * 在る・無い・null。ほぼ全ての宣言がこの三つのどれかを使う。
36
- * 使用実績: required 138 / optional 26 / nullable 12。
35
+ * Present, absent, null. Nearly every declaration uses one of the three.
37
36
  */
38
37
  export const presence = Object.freeze({
39
38
  required: requiredPlugin,
40
39
  optional: optionalPlugin,
41
40
  nullable: nullablePlugin,
42
41
  });
43
- /** 文字列の定番。使用実績: stringMin 95 / stringEmail 33 / stringPattern 12。 */
42
+ /** The everyday string checks. */
44
43
  export const strings = Object.freeze({
45
44
  stringMin: stringMinPlugin,
46
45
  stringMax: stringMaxPlugin,
47
46
  stringPattern: stringPatternPlugin,
48
47
  stringEmail: stringEmailPlugin,
49
48
  });
50
- /** 数値の定番。使用実績: numberMin 43 / numberMax 8。 */
49
+ /** The everyday number checks. */
51
50
  export const numbers = Object.freeze({
52
51
  numberMin: numberMinPlugin,
53
52
  numberMax: numberMaxPlugin,
54
53
  numberInteger: numberIntegerPlugin,
55
54
  });
56
- /** 配列の定番。要素ごとの規則は arrayEach が運ぶ。 */
55
+ /** The everyday array checks. Per-element rules ride on arrayEach. */
57
56
  export const arrays = Object.freeze({
58
57
  arrayMinLength: arrayMinLengthPlugin,
59
58
  arrayMaxLength: arrayMaxLengthPlugin,
60
59
  arrayEach: arrayEachPlugin,
61
60
  });
62
61
  /**
63
- * 上の四つを合わせたもの。13プラグイン、gzip +1,452 B。
62
+ * The four bundles above, together.
64
63
  *
65
- * 名前が「common」でないのは、それが何も言っていないからである
66
- * (lint の禁止語彙にも入っている)。入っているのは presence と、文字列・数値・
67
- * 配列それぞれの定番で、毎日書くのはこの範囲だ、という主張がこの名前である。
64
+ * Not called "common", because that name says nothing — and lint bans it. It
65
+ * holds presence plus the everyday string, number and array checks, and the
66
+ * name is the claim that this is the range people write day to day.
68
67
  *
69
- * これでも「全部入り」ではない。全部入りが要るなら `@maroonedog/luq/plugins`
70
- * のバレルがあり、そちらは 77 個ぶん、gzip で +17,986 B を払う。
68
+ * Still not everything. For everything there is the `@maroonedog/luq/plugins`
69
+ * barrel, which costs accordingly.
71
70
  */
72
71
  export const everydayRules = Object.freeze({
73
72
  ...presence,
@@ -60,13 +60,11 @@ function createSubjectComposer(template) {
60
60
  return (siblings, value) => mergeOntoSiblings(siblings, nest(value));
61
61
  }
62
62
  /**
63
- * テンプレートを **一度だけ** 歩いて、包む関数に畳んでおく。
63
+ * Walks the template **once** and folds it into a wrapping function.
64
64
  *
65
- * 以前はここが `nestValue(template, 0, value)` で、検証のたびにテンプレートを
66
- * 歩き直して `segment.kind` を読み直していた。src/compile/validation-plan.types.ts
67
- * が書いているとおり「実行時はルールが何であるかを決め直してはならない。
68
- * 決めるのはコンパイルが済ませたこと」であり、ここはその例外になっていた。
69
- * 非ワイルドカードの経路 (createValueWriter) は最初からこの形である。
65
+ * Walking it per validation re-read each segment's kind every time, which is
66
+ * exactly the thing validation time must not do: what something IS was settled
67
+ * at compile time. The non-wildcard route was always shaped this way.
70
68
  */
71
69
  function compileNester(template) {
72
70
  let nest = (value) => value;
@@ -57,13 +57,11 @@ function createSubjectComposer(template) {
57
57
  return (siblings, value) => mergeOntoSiblings(siblings, nest(value));
58
58
  }
59
59
  /**
60
- * テンプレートを **一度だけ** 歩いて、包む関数に畳んでおく。
60
+ * Walks the template **once** and folds it into a wrapping function.
61
61
  *
62
- * 以前はここが `nestValue(template, 0, value)` で、検証のたびにテンプレートを
63
- * 歩き直して `segment.kind` を読み直していた。src/compile/validation-plan.types.ts
64
- * が書いているとおり「実行時はルールが何であるかを決め直してはならない。
65
- * 決めるのはコンパイルが済ませたこと」であり、ここはその例外になっていた。
66
- * 非ワイルドカードの経路 (createValueWriter) は最初からこの形である。
62
+ * Walking it per validation re-read each segment's kind every time, which is
63
+ * exactly the thing validation time must not do: what something IS was settled
64
+ * at compile time. The non-wildcard route was always shaped this way.
67
65
  */
68
66
  function compileNester(template) {
69
67
  let nest = (value) => value;
@@ -40,23 +40,23 @@ function hasRejectingIssue(issues) {
40
40
  return issues.some((issue) => issue.severity === "error");
41
41
  }
42
42
  /**
43
- * 再帰しないプランのための、共有の何もしないランナー。
43
+ * The shared do-nothing runner for a plan that cannot recurse.
44
44
  *
45
- * createRecursionRunner WeakSet と四つのクロージャを作る。プランに
46
- * 再帰規則が一つも無くても validate() のたびに作っていて、それが固定コストの
47
- * 88% を占めていた 計測で 9.7%。プランが再帰を含むかは build() 時に
48
- * 分かることで、実行時に問い直すことではない。
45
+ * Building a real runner allocates a WeakSet and several closures. Doing that
46
+ * on every validate() of a plan with no recursive rule in it was most of the
47
+ * fixed per-call cost. Whether a plan can recurse is known at build time and
48
+ * is not a question to re-ask at validation time.
49
49
  *
50
- * 呼ばれることはない。呼ばれるのは field.recursion null でない場合だけで、
51
- * そのときは planCanRecurse true を返しているので本物が渡っている。
50
+ * Never actually called: a field only reaches its runner when it declares
51
+ * recursion, and a plan containing one gets the real runner instead.
52
52
  */
53
53
  const NO_RECURSION = () => { };
54
54
  /**
55
- * ルートのランナーが要るか。
55
+ * Whether the root needs a real runner.
56
56
  *
57
- * 見るのはルートの直下と配列ノードの要素だけでよい。合成 (oneOf など)
58
- * 枝は run-branch が自前のランナーを作って入れ子のプランを回すので、
59
- * ここで渡すものを使わない。
57
+ * Only the fields directly under the root and the elements of array nodes
58
+ * matter. A composite branch makes its own runner for the nested plan, so it
59
+ * never uses the one passed from here.
60
60
  */
61
61
  function planCanRecurse(plan) {
62
62
  return (plan.fields.some((field) => field.recursion !== null) ||
@@ -35,23 +35,23 @@ export function hasRejectingIssue(issues) {
35
35
  return issues.some((issue) => issue.severity === "error");
36
36
  }
37
37
  /**
38
- * 再帰しないプランのための、共有の何もしないランナー。
38
+ * The shared do-nothing runner for a plan that cannot recurse.
39
39
  *
40
- * createRecursionRunner WeakSet と四つのクロージャを作る。プランに
41
- * 再帰規則が一つも無くても validate() のたびに作っていて、それが固定コストの
42
- * 88% を占めていた 計測で 9.7%。プランが再帰を含むかは build() 時に
43
- * 分かることで、実行時に問い直すことではない。
40
+ * Building a real runner allocates a WeakSet and several closures. Doing that
41
+ * on every validate() of a plan with no recursive rule in it was most of the
42
+ * fixed per-call cost. Whether a plan can recurse is known at build time and
43
+ * is not a question to re-ask at validation time.
44
44
  *
45
- * 呼ばれることはない。呼ばれるのは field.recursion null でない場合だけで、
46
- * そのときは planCanRecurse true を返しているので本物が渡っている。
45
+ * Never actually called: a field only reaches its runner when it declares
46
+ * recursion, and a plan containing one gets the real runner instead.
47
47
  */
48
48
  const NO_RECURSION = () => { };
49
49
  /**
50
- * ルートのランナーが要るか。
50
+ * Whether the root needs a real runner.
51
51
  *
52
- * 見るのはルートの直下と配列ノードの要素だけでよい。合成 (oneOf など)
53
- * 枝は run-branch が自前のランナーを作って入れ子のプランを回すので、
54
- * ここで渡すものを使わない。
52
+ * Only the fields directly under the root and the elements of array nodes
53
+ * matter. A composite branch makes its own runner for the nested plan, so it
54
+ * never uses the one passed from here.
55
55
  */
56
56
  function planCanRecurse(plan) {
57
57
  return (plan.fields.some((field) => field.recursion !== null) ||
@@ -24,8 +24,12 @@ function createArrayWriteTargets(nodes) {
24
24
  * hands back the very object it was given.
25
25
  */
26
26
  function createPlanWriteTargets(plan) {
27
- if (!plan.hasTransforms && !plan.hasDefaults)
27
+ // A field declaring only a normalizer is still written back by parse().
28
+ // Leave it out of this condition and no writer is made, so the value read
29
+ // comes back unchanged and in silence.
30
+ if (!plan.hasTransforms && !plan.hasDefaults && !plan.hasNormalizers) {
28
31
  return null;
32
+ }
29
33
  return createArrayWriteTargets(plan.arrays);
30
34
  }
31
35
  /**
@@ -17,8 +17,12 @@ export function createArrayWriteTargets(nodes) {
17
17
  * hands back the very object it was given.
18
18
  */
19
19
  export function createPlanWriteTargets(plan) {
20
- if (!plan.hasTransforms && !plan.hasDefaults)
20
+ // A field declaring only a normalizer is still written back by parse().
21
+ // Leave it out of this condition and no writer is made, so the value read
22
+ // comes back unchanged and in silence.
23
+ if (!plan.hasTransforms && !plan.hasDefaults && !plan.hasNormalizers) {
21
24
  return null;
25
+ }
22
26
  return createArrayWriteTargets(plan.arrays);
23
27
  }
24
28
  /**
@@ -57,12 +57,12 @@ function runElements(node, array, context, target) {
57
57
  const nodePath = node.renderedPath;
58
58
  const elementSink = context.sink.forArrayElements();
59
59
  const nested = target === undefined ? output_writer_1.NO_WRITE_TARGETS : target.nested;
60
- // 要素コンテキストはノードごとに一つ。7つの成員のうち要素ごとに変わるのは
61
- // `item` だけで、残る6つは validate() 一回のあいだ、あるいはこのノードの
62
- // あいだ動かない。毎要素で組み直していたのは、動かないものを動くものと
63
- // 同じ場所に置いていたからにすぎない。**プラグインには渡らない**
64
- // プラグインが見るのは runField が組む RuleContext で、そちらは `item`
65
- // 都度読み直すので、この使い回しは外から観測できない。
60
+ // One element context per node. `item` is the only member that changes per
61
+ // element; the rest hold still for the whole node, and rebuilding them per
62
+ // element only happened because the fixed parts sat beside the moving one.
63
+ // **It never reaches a plugin** a plugin sees the RuleContext that running
64
+ // a field assembles, and that reads `item` afresh, so reusing this is not
65
+ // observable from outside.
66
66
  const elementContext = {
67
67
  root: context.root,
68
68
  sink: elementSink,
@@ -54,12 +54,12 @@ function runElements(node, array, context, target) {
54
54
  const nodePath = node.renderedPath;
55
55
  const elementSink = context.sink.forArrayElements();
56
56
  const nested = target === undefined ? NO_WRITE_TARGETS : target.nested;
57
- // 要素コンテキストはノードごとに一つ。7つの成員のうち要素ごとに変わるのは
58
- // `item` だけで、残る6つは validate() 一回のあいだ、あるいはこのノードの
59
- // あいだ動かない。毎要素で組み直していたのは、動かないものを動くものと
60
- // 同じ場所に置いていたからにすぎない。**プラグインには渡らない**
61
- // プラグインが見るのは runField が組む RuleContext で、そちらは `item`
62
- // 都度読み直すので、この使い回しは外から観測できない。
57
+ // One element context per node. `item` is the only member that changes per
58
+ // element; the rest hold still for the whole node, and rebuilding them per
59
+ // element only happened because the fixed parts sat beside the moving one.
60
+ // **It never reaches a plugin** a plugin sees the RuleContext that running
61
+ // a field assembles, and that reads `item` afresh, so reusing this is not
62
+ // observable from outside.
63
63
  const elementContext = {
64
64
  root: context.root,
65
65
  sink: elementSink,
@@ -12,7 +12,7 @@ exports.FIELD_VALUE_UNCHANGED = Object.freeze({
12
12
  function runField(field, subject, context) {
13
13
  const ruleContext = new field_rule_context_1.FieldRuleContext(context.root, context.indices, field.renderedPath, context.item, context.external);
14
14
  const read = field.read(subject);
15
- const value = applyDefault(field, read, context.root);
15
+ const value = applyNormalize(field, applyDefault(field, read, context.root));
16
16
  if (!(0, decide_presence_1.decidePresence)(field, value, ruleContext, context.sink)) {
17
17
  return exports.FIELD_VALUE_UNCHANGED;
18
18
  }
@@ -40,15 +40,21 @@ function applyDefault(field, value, root) {
40
40
  return field.defaultOf(root);
41
41
  return value;
42
42
  }
43
+ /**
44
+ * Tidies the value before anything judges it: after default, before presence.
45
+ *
46
+ * Never called for undefined or null. That closes the accident where a
47
+ * caller's `(v) => String(v).trim()` turns a missing field into the string
48
+ * `"undefined"` and walks it past `.required()`. Absence is default's job.
49
+ */
50
+ function applyNormalize(field, value) {
51
+ if (field.normalize === null || value === undefined || value === null) {
52
+ return value;
53
+ }
54
+ return field.normalize(value);
55
+ }
43
56
  /** A closed gate ends the field successfully: no check, no transform. */
44
57
  function openGates(field, value, ruleContext) {
45
- // 添字ループである。for-of ではない。ここが回るのはコンパイル済みの
46
- // 凍結配列で、凍結配列は V8 では PACKED_FROZEN_ELEMENTS になり、配列
47
- // イテレータの高速化パスから外れる — イテレータと IteratorResult が
48
- // 消去されず、要素×フィールドの回数だけ確保される。配列シェイプでは
49
- // それだけで全ゴミの 45〜54% を占めていた (独立に5通りの改変で -45%
50
- // 〜 -54%)。凍結は落とさない: コンパイル層の不変条件であり、凍結を
51
- // 外しても添字ループより速くはならない。
52
58
  const gates = field.gates;
53
59
  for (let i = 0; i < gates.length; i += 1) {
54
60
  const gate = gates[i];
@@ -60,17 +66,10 @@ function openGates(field, value, ruleContext) {
60
66
  return true;
61
67
  }
62
68
  /**
63
- * 失敗したときだけ通る側。ループ本体から出してある。
64
- *
65
- * runChecks はバイトコードで 302 バイトあり、TurboFan の呼び出し先
66
- * インライン予算 (既定で累計 920 バイト) の最大の落選候補として
67
- * --trace-turbo-inlining に名指しされていた。その 302 バイトの大半が、
68
- * 受理された値では一度も走らない issue の組み立てである。ここへ出すと
69
- * 残るループ本体が縮み、受理パスで 6.4% 速くなった。
70
- *
71
- * 中断の判定はここに含めない。issue を足したあとに shouldStopField を
72
- * 見るという順序が abortEarlyOnEachField の意味そのものなので、呼び出し側に
73
- * 並べて置いておく。
69
+ * The side taken only on failure. Lifted out of the loop body because most of
70
+ * that body was issue construction an accepted value never runs, and its size
71
+ * was what pushed the check loop past the inlining budget. Stopping is not
72
+ * decided here: add the issue, then ask — that order is what abortEarly means.
74
73
  */
75
74
  function reportCheckFailure(check, detail, value, ruleContext, context) {
76
75
  context.sink.add((0, create_issue_1.createIssue)({
@@ -8,7 +8,7 @@ export const FIELD_VALUE_UNCHANGED = Object.freeze({
8
8
  export function runField(field, subject, context) {
9
9
  const ruleContext = new FieldRuleContext(context.root, context.indices, field.renderedPath, context.item, context.external);
10
10
  const read = field.read(subject);
11
- const value = applyDefault(field, read, context.root);
11
+ const value = applyNormalize(field, applyDefault(field, read, context.root));
12
12
  if (!decidePresence(field, value, ruleContext, context.sink)) {
13
13
  return FIELD_VALUE_UNCHANGED;
14
14
  }
@@ -36,15 +36,21 @@ function applyDefault(field, value, root) {
36
36
  return field.defaultOf(root);
37
37
  return value;
38
38
  }
39
+ /**
40
+ * Tidies the value before anything judges it: after default, before presence.
41
+ *
42
+ * Never called for undefined or null. That closes the accident where a
43
+ * caller's `(v) => String(v).trim()` turns a missing field into the string
44
+ * `"undefined"` and walks it past `.required()`. Absence is default's job.
45
+ */
46
+ function applyNormalize(field, value) {
47
+ if (field.normalize === null || value === undefined || value === null) {
48
+ return value;
49
+ }
50
+ return field.normalize(value);
51
+ }
39
52
  /** A closed gate ends the field successfully: no check, no transform. */
40
53
  function openGates(field, value, ruleContext) {
41
- // 添字ループである。for-of ではない。ここが回るのはコンパイル済みの
42
- // 凍結配列で、凍結配列は V8 では PACKED_FROZEN_ELEMENTS になり、配列
43
- // イテレータの高速化パスから外れる — イテレータと IteratorResult が
44
- // 消去されず、要素×フィールドの回数だけ確保される。配列シェイプでは
45
- // それだけで全ゴミの 45〜54% を占めていた (独立に5通りの改変で -45%
46
- // 〜 -54%)。凍結は落とさない: コンパイル層の不変条件であり、凍結を
47
- // 外しても添字ループより速くはならない。
48
54
  const gates = field.gates;
49
55
  for (let i = 0; i < gates.length; i += 1) {
50
56
  const gate = gates[i];
@@ -56,17 +62,10 @@ function openGates(field, value, ruleContext) {
56
62
  return true;
57
63
  }
58
64
  /**
59
- * 失敗したときだけ通る側。ループ本体から出してある。
60
- *
61
- * runChecks はバイトコードで 302 バイトあり、TurboFan の呼び出し先
62
- * インライン予算 (既定で累計 920 バイト) の最大の落選候補として
63
- * --trace-turbo-inlining に名指しされていた。その 302 バイトの大半が、
64
- * 受理された値では一度も走らない issue の組み立てである。ここへ出すと
65
- * 残るループ本体が縮み、受理パスで 6.4% 速くなった。
66
- *
67
- * 中断の判定はここに含めない。issue を足したあとに shouldStopField を
68
- * 見るという順序が abortEarlyOnEachField の意味そのものなので、呼び出し側に
69
- * 並べて置いておく。
65
+ * The side taken only on failure. Lifted out of the loop body because most of
66
+ * that body was issue construction an accepted value never runs, and its size
67
+ * was what pushed the check loop past the inlining budget. Stopping is not
68
+ * decided here: add the issue, then ask — that order is what abortEarly means.
70
69
  */
71
70
  function reportCheckFailure(check, detail, value, ruleContext, context) {
72
71
  context.sink.add(createIssue({
@@ -0,0 +1,4 @@
1
+ import type { FieldDeclaredCalls } from "../builder/field-declared-calls.types";
2
+ import type { UnrepresentablePolicy } from "./unrepresentable-rule-error";
3
+ /** Assembles the root schema from the list of declarations. */
4
+ export declare function assembleJsonSchema(fields: readonly FieldDeclaredCalls[], policy: UnrepresentablePolicy): Record<string, unknown>;