@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
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ // ===========================================================================
3
+ // L10 src/standard-schema/split-issue-path.ts
4
+ //
5
+ // Luq の issue path ("items[1].productId") を Standard Schema の path
6
+ // (["items", 1, "productId"]) に開く。
7
+ //
8
+ // 方向に注意: これは formatIssuePath の逆で、宣言パス (parseFieldPath が
9
+ // 読む "items[*].productId") のパーサではない。issue path には [*] が
10
+ // 決して現れず、代わりに実インデックスが入る。二つの文法を一つの関数で
11
+ // 扱おうとすると、[*] を 0 と読むような取り違えが静かに入るので分けてある。
12
+ //
13
+ // 配列インデックスは number として出す。仕様の PropertyKey は
14
+ // string | number | symbol を許し、消費側 (フォームライブラリ) は
15
+ // 添字を number として受け取る前提で書かれているため。
16
+ // ===========================================================================
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.splitIssuePath = splitIssuePath;
19
+ const INDEX_PATTERN = /^\[(\d+)\]/;
20
+ /**
21
+ * ルート ("") は空配列。空配列は仕様上「ルート自身への issue」を意味する。
22
+ *
23
+ * 解釈できない形が来たら、握り潰さずに path 全体を1つの文字列セグメントとして
24
+ * 返す。issue を落とすより、開けなかったことが分かる形で渡すほうがまし。
25
+ */
26
+ function splitIssuePath(path) {
27
+ if (path === "")
28
+ return [];
29
+ const segments = [];
30
+ let rest = path;
31
+ let expectKey = true;
32
+ while (rest !== "") {
33
+ if (expectKey) {
34
+ const key = rest.slice(0, findKeyEnd(rest));
35
+ if (key === "")
36
+ return [path];
37
+ segments.push(key);
38
+ rest = rest.slice(key.length);
39
+ expectKey = false;
40
+ continue;
41
+ }
42
+ const index = INDEX_PATTERN.exec(rest);
43
+ if (index !== null) {
44
+ segments.push(Number(index[1]));
45
+ rest = rest.slice(index[0].length);
46
+ continue;
47
+ }
48
+ if (rest.startsWith(".")) {
49
+ rest = rest.slice(1);
50
+ expectKey = true;
51
+ continue;
52
+ }
53
+ return [path];
54
+ }
55
+ return expectKey ? [path] : segments;
56
+ }
57
+ /** キーは "." か "[" の手前まで。どちらも無ければ末尾まで。 */
58
+ function findKeyEnd(rest) {
59
+ const dot = rest.indexOf(".");
60
+ const bracket = rest.indexOf("[");
61
+ if (dot === -1)
62
+ return bracket === -1 ? rest.length : bracket;
63
+ if (bracket === -1)
64
+ return dot;
65
+ return Math.min(dot, bracket);
66
+ }
@@ -0,0 +1,63 @@
1
+ // ===========================================================================
2
+ // L10 src/standard-schema/split-issue-path.ts
3
+ //
4
+ // Luq の issue path ("items[1].productId") を Standard Schema の path
5
+ // (["items", 1, "productId"]) に開く。
6
+ //
7
+ // 方向に注意: これは formatIssuePath の逆で、宣言パス (parseFieldPath が
8
+ // 読む "items[*].productId") のパーサではない。issue path には [*] が
9
+ // 決して現れず、代わりに実インデックスが入る。二つの文法を一つの関数で
10
+ // 扱おうとすると、[*] を 0 と読むような取り違えが静かに入るので分けてある。
11
+ //
12
+ // 配列インデックスは number として出す。仕様の PropertyKey は
13
+ // string | number | symbol を許し、消費側 (フォームライブラリ) は
14
+ // 添字を number として受け取る前提で書かれているため。
15
+ // ===========================================================================
16
+ const INDEX_PATTERN = /^\[(\d+)\]/;
17
+ /**
18
+ * ルート ("") は空配列。空配列は仕様上「ルート自身への issue」を意味する。
19
+ *
20
+ * 解釈できない形が来たら、握り潰さずに path 全体を1つの文字列セグメントとして
21
+ * 返す。issue を落とすより、開けなかったことが分かる形で渡すほうがまし。
22
+ */
23
+ export function splitIssuePath(path) {
24
+ if (path === "")
25
+ return [];
26
+ const segments = [];
27
+ let rest = path;
28
+ let expectKey = true;
29
+ while (rest !== "") {
30
+ if (expectKey) {
31
+ const key = rest.slice(0, findKeyEnd(rest));
32
+ if (key === "")
33
+ return [path];
34
+ segments.push(key);
35
+ rest = rest.slice(key.length);
36
+ expectKey = false;
37
+ continue;
38
+ }
39
+ const index = INDEX_PATTERN.exec(rest);
40
+ if (index !== null) {
41
+ segments.push(Number(index[1]));
42
+ rest = rest.slice(index[0].length);
43
+ continue;
44
+ }
45
+ if (rest.startsWith(".")) {
46
+ rest = rest.slice(1);
47
+ expectKey = true;
48
+ continue;
49
+ }
50
+ return [path];
51
+ }
52
+ return expectKey ? [path] : segments;
53
+ }
54
+ /** キーは "." か "[" の手前まで。どちらも無ければ末尾まで。 */
55
+ function findKeyEnd(rest) {
56
+ const dot = rest.indexOf(".");
57
+ const bracket = rest.indexOf("[");
58
+ if (dot === -1)
59
+ return bracket === -1 ? rest.length : bracket;
60
+ if (bracket === -1)
61
+ return dot;
62
+ return Math.min(dot, bracket);
63
+ }
@@ -0,0 +1,46 @@
1
+ /** 仕様の StandardSchemaV1。Input と Output を型として運ぶ。 */
2
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
3
+ readonly "~standard": StandardSchemaProps<Input, Output>;
4
+ }
5
+ export interface StandardSchemaProps<Input = unknown, Output = Input> {
6
+ readonly version: 1;
7
+ readonly vendor: string;
8
+ readonly validate: (value: unknown, options?: StandardSchemaOptions | undefined) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
9
+ /**
10
+ * 実行時には存在しない。型を運ぶためだけのメンバーで、仕様がそう定めている。
11
+ * InferInput / InferOutput はここから読む。
12
+ */
13
+ readonly types?: StandardSchemaTypes<Input, Output> | undefined;
14
+ }
15
+ export interface StandardSchemaTypes<Input = unknown, Output = Input> {
16
+ readonly input: Input;
17
+ readonly output: Output;
18
+ }
19
+ export type StandardSchemaResult<Output> = StandardSchemaSuccess<Output> | StandardSchemaFailure;
20
+ export interface StandardSchemaSuccess<Output> {
21
+ readonly value: Output;
22
+ readonly issues?: undefined;
23
+ }
24
+ export interface StandardSchemaFailure {
25
+ readonly issues: readonly StandardSchemaIssue[];
26
+ }
27
+ /**
28
+ * path は仕様上 optional。Luq は必ず入れる (root への issue は空配列)。
29
+ * 省略と「ルートを指す」を呼び出し側が区別できるようにするため。
30
+ */
31
+ export interface StandardSchemaIssue {
32
+ readonly message: string;
33
+ readonly path?: readonly (PropertyKey | StandardSchemaPathSegment)[] | undefined;
34
+ }
35
+ export interface StandardSchemaPathSegment {
36
+ readonly key: PropertyKey;
37
+ }
38
+ export type InferStandardInput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["input"];
39
+ export type InferStandardOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
40
+ /**
41
+ * validate の第2引数。仕様が定めているので受ける。
42
+ * libraryOptions はベンダーごとの追加パラメータで、Luq はまだ何も定義していない。
43
+ */
44
+ export interface StandardSchemaOptions {
45
+ readonly libraryOptions?: Record<string, unknown> | undefined;
46
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ // ===========================================================================
3
+ // L10 src/standard-schema/standard-schema.types.ts
4
+ //
5
+ // Standard Schema v1 の型を自前で宣言する。@standard-schema/spec に依存しない。
6
+ // 仕様が「型だけのパッケージなので inline してよい」と明示しており、依存を
7
+ // 足せば利用者の node_modules に1つ増える一方で、得るものは何も無いため。
8
+ // 形が仕様からずれていないことは test/type/standard-schema/ が固定する。
9
+ //
10
+ // この層が L10 なのは、Validator (L6) の上に載る変換であり、コアの誰も
11
+ // これを import しないから。import の向きは L6 -> L10 ではなく L10 -> L6。
12
+ // ===========================================================================
13
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,12 @@
1
+ // ===========================================================================
2
+ // L10 src/standard-schema/standard-schema.types.ts
3
+ //
4
+ // Standard Schema v1 の型を自前で宣言する。@standard-schema/spec に依存しない。
5
+ // 仕様が「型だけのパッケージなので inline してよい」と明示しており、依存を
6
+ // 足せば利用者の node_modules に1つ増える一方で、得るものは何も無いため。
7
+ // 形が仕様からずれていないことは test/type/standard-schema/ が固定する。
8
+ //
9
+ // この層が L10 なのは、Validator (L6) の上に載る変換であり、コアの誰も
10
+ // これを import しないから。import の向きは L6 -> L10 ではなく L10 -> L6。
11
+ // ===========================================================================
12
+ export {};
@@ -0,0 +1,41 @@
1
+ import type { Validator } from "../builder/validator.types";
2
+ import type { StandardSchemaOptions, StandardSchemaResult } from "./standard-schema.types";
3
+ /**
4
+ * 仕様の Props は validate が Promise を返すことも許すが、Luq は必ず同期で返す。
5
+ * ここで同期に絞っておくと、消費側が `await` も型の絞り込みも書かずに済む。
6
+ * 狭めた型は広い型に代入できるので、StandardSchemaV1 としての互換は保たれる
7
+ * (test/type/standard-schema/ の AssignableToSpec がそれを固定している)。
8
+ */
9
+ interface SynchronousStandardProps<T extends object, TParsed> {
10
+ readonly version: 1;
11
+ readonly vendor: string;
12
+ /**
13
+ * 第2引数 options は仕様にある。Luq は今のところ読まないが、**受けること
14
+ * 自体に意味がある**: 引数の少ない関数は多い方に代入できてしまうので、
15
+ * 書かないと「仕様に合っている」と型が言うのに消費側の options が黙って
16
+ * 捨てられる。受けた上で無視しているのだと分かる形にしてある。
17
+ *
18
+ * libraryOptions に何を入れるかはベンダーごとの取り決めで、Luq はまだ
19
+ * 何も定義していない。定義したらここで読む。
20
+ */
21
+ readonly validate: (value: unknown, options?: StandardSchemaOptions | undefined) => StandardSchemaResult<TParsed>;
22
+ readonly types?: {
23
+ readonly input: T;
24
+ readonly output: TParsed;
25
+ } | undefined;
26
+ }
27
+ /**
28
+ * Standard Schema としての Luq バリデータ。
29
+ *
30
+ * Input は `.for<T>()` に渡した型、Output は transform 適用後の型。
31
+ * 推論ではなく宣言から来るので、InferInput が利用者の書いた型そのものを指す。
32
+ */
33
+ export type StandardLuqSchema<T extends object, TParsed = T> = Validator<T, TParsed> & {
34
+ readonly "~standard": SynchronousStandardProps<T, TParsed>;
35
+ };
36
+ /**
37
+ * 元の Validator のメンバー (validate / parse / pick / pickAll) はそのまま残る。
38
+ * 返り値は Validator でもあるので、片方のためにもう片方を諦める必要は無い。
39
+ */
40
+ export declare function toStandardSchema<T extends object, TParsed = T>(validator: Validator<T, TParsed>): StandardLuqSchema<T, TParsed>;
41
+ export {};
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toStandardSchema = toStandardSchema;
4
+ const split_issue_path_1 = require("./split-issue-path");
5
+ /** package.json の name と揃える。消費側がエラー表示に使う。 */
6
+ const VENDOR = "luq";
7
+ const COLLECT_EVERY_ISSUE = {
8
+ abortEarly: false,
9
+ abortEarlyOnEachField: false,
10
+ };
11
+ /**
12
+ * 元の Validator のメンバー (validate / parse / pick / pickAll) はそのまま残る。
13
+ * 返り値は Validator でもあるので、片方のためにもう片方を諦める必要は無い。
14
+ */
15
+ function toStandardSchema(validator) {
16
+ // アサーションを書かずに組み立てる。src/core/type-erasure.ts が
17
+ // 「型を破ってよい唯一の場所」で、ここはその場所ではない。
18
+ // 各メンバーを明示的に写すので、Validator に新しいメンバーが増えたときは
19
+ // ここがコンパイルエラーになる — 黙って落ちるより良い。
20
+ const props = {
21
+ version: 1,
22
+ vendor: VENDOR,
23
+ validate: (value) => {
24
+ const outcome = validator.parse(value, COLLECT_EVERY_ISSUE);
25
+ return outcome.valid
26
+ ? { value: outcome.data }
27
+ : { issues: outcome.issues.map(toStandardIssue) };
28
+ },
29
+ };
30
+ return {
31
+ validate: (value, options) => validator.validate(value, options),
32
+ parse: (value, options) => validator.parse(value, options),
33
+ pick: (key) => validator.pick(key),
34
+ pickAll: (paths) => validator.pickAll(paths),
35
+ "~standard": props,
36
+ };
37
+ }
38
+ /**
39
+ * code と severity は仕様に置き場所が無いので落ちる。message は Luq が
40
+ * 組み立て済みのものをそのまま渡す。
41
+ */
42
+ function toStandardIssue(issue) {
43
+ return { message: issue.message, path: (0, split_issue_path_1.splitIssuePath)(issue.path) };
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // なぜコアの build() が ~standard を常に生やさないのか(実測に基づく判断)
47
+ //
48
+ // core-only gzip 7,420 B
49
+ // core + standard-schema gzip 7,732 B (+312 B)
50
+ // six-plugin gzip 8,373 B
51
+ // six-plugin + standard-schema gzip 8,692 B (+319 B)
52
+ //
53
+ // コアに同梱すると、Standard Schema を使わない利用者も 312 B を払う。中核が
54
+ // 7,420 B なので 4.2% にあたり、「使った分しか入らない」という約束と噛み合わない。
55
+ // サブパスにしておけば import しない限り 0 B で、tRPC などに渡したい人だけが払う。
56
+ //
57
+ // 代償は、利用者が toStandardSchema() で1回包む必要があること。build() が
58
+ // 直接 ~standard を持つほうが体験は良いが、その体験のために全員に課金する形に
59
+ // なるので取らなかった。将来コアに入れるなら、この 312 B が判断材料になる。
60
+ // ---------------------------------------------------------------------------
@@ -0,0 +1,57 @@
1
+ import { splitIssuePath } from "./split-issue-path.mjs";
2
+ /** package.json の name と揃える。消費側がエラー表示に使う。 */
3
+ const VENDOR = "luq";
4
+ const COLLECT_EVERY_ISSUE = {
5
+ abortEarly: false,
6
+ abortEarlyOnEachField: false,
7
+ };
8
+ /**
9
+ * 元の Validator のメンバー (validate / parse / pick / pickAll) はそのまま残る。
10
+ * 返り値は Validator でもあるので、片方のためにもう片方を諦める必要は無い。
11
+ */
12
+ export function toStandardSchema(validator) {
13
+ // アサーションを書かずに組み立てる。src/core/type-erasure.ts が
14
+ // 「型を破ってよい唯一の場所」で、ここはその場所ではない。
15
+ // 各メンバーを明示的に写すので、Validator に新しいメンバーが増えたときは
16
+ // ここがコンパイルエラーになる — 黙って落ちるより良い。
17
+ const props = {
18
+ version: 1,
19
+ vendor: VENDOR,
20
+ validate: (value) => {
21
+ const outcome = validator.parse(value, COLLECT_EVERY_ISSUE);
22
+ return outcome.valid
23
+ ? { value: outcome.data }
24
+ : { issues: outcome.issues.map(toStandardIssue) };
25
+ },
26
+ };
27
+ return {
28
+ validate: (value, options) => validator.validate(value, options),
29
+ parse: (value, options) => validator.parse(value, options),
30
+ pick: (key) => validator.pick(key),
31
+ pickAll: (paths) => validator.pickAll(paths),
32
+ "~standard": props,
33
+ };
34
+ }
35
+ /**
36
+ * code と severity は仕様に置き場所が無いので落ちる。message は Luq が
37
+ * 組み立て済みのものをそのまま渡す。
38
+ */
39
+ function toStandardIssue(issue) {
40
+ return { message: issue.message, path: splitIssuePath(issue.path) };
41
+ }
42
+ // ---------------------------------------------------------------------------
43
+ // なぜコアの build() が ~standard を常に生やさないのか(実測に基づく判断)
44
+ //
45
+ // core-only gzip 7,420 B
46
+ // core + standard-schema gzip 7,732 B (+312 B)
47
+ // six-plugin gzip 8,373 B
48
+ // six-plugin + standard-schema gzip 8,692 B (+319 B)
49
+ //
50
+ // コアに同梱すると、Standard Schema を使わない利用者も 312 B を払う。中核が
51
+ // 7,420 B なので 4.2% にあたり、「使った分しか入らない」という約束と噛み合わない。
52
+ // サブパスにしておけば import しない限り 0 B で、tRPC などに渡したい人だけが払う。
53
+ //
54
+ // 代償は、利用者が toStandardSchema() で1回包む必要があること。build() が
55
+ // 直接 ~standard を持つほうが体験は良いが、その体験のために全員に課金する形に
56
+ // なるので取らなかった。将来コアに入れるなら、この 312 B が判断材料になる。
57
+ // ---------------------------------------------------------------------------
@@ -0,0 +1 @@
1
+ export * from "./standard-schema/index";
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ module.exports = require("./standard-schema/index");
@@ -0,0 +1 @@
1
+ export * from "./standard-schema/index.mjs";
@@ -43,6 +43,18 @@ export type CheckOutcome = {
43
43
  };
44
44
  export declare const PASS: CheckOutcome;
45
45
  export declare function fail(detail: IssueDetail): CheckOutcome;
46
+ /**
47
+ * クロスフィールドの判定が返せるもの。真偽だけでも、メッセージ付きでもよい。
48
+ *
49
+ * ここ (L0) にあるのは、チェーン層が `stitch` の呼び出し側の型を組むときに
50
+ * 参照するからである。プラグイン (L7) に置いたままだと L3 -> L7 の import に
51
+ * なり、層が逆流する。プラグイン側の `StitchOutcome` はこれの別名で、
52
+ * 公開名は変えていない。
53
+ */
54
+ export interface CrossFieldOutcome {
55
+ readonly valid: boolean;
56
+ readonly message?: string;
57
+ }
46
58
  export interface ArrayItemContext {
47
59
  readonly index: number;
48
60
  readonly item: unknown;
@@ -66,3 +78,15 @@ export declare function isNumber(value: unknown): value is number;
66
78
  export declare function isArray(value: unknown): value is readonly unknown[];
67
79
  export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
68
80
  export declare function isStringArray(value: unknown): value is readonly string[];
81
+ /**
82
+ * 文字列の長さをコードポイントで数える。
83
+ *
84
+ * `.length` は UTF-16 のコード単位を数えるので、絵文字や星域文字が 2 と数えられる。
85
+ * JSON Schema §6.3.1 は minLength / maxLength をコードポイントで数えると定めており、
86
+ * 利用者が「3文字」と書くときに期待するのもコードポイントのほう。
87
+ *
88
+ * 1.x は `.length` を使っていた (docs/legacy-spec/plugin-catalog-core.md が
89
+ * 「暗黙の挙動で、まわりに落ちるテストがある。明示的な決定にすべき」と記録している)。
90
+ * ここでその決定をした。星域文字を含む文字列では 1.x と判定が変わる。
91
+ */
92
+ export declare function countCodePoints(value: string): number;
@@ -7,6 +7,7 @@ exports.isNumber = isNumber;
7
7
  exports.isArray = isArray;
8
8
  exports.isPlainObject = isPlainObject;
9
9
  exports.isStringArray = isStringArray;
10
+ exports.countCodePoints = countCodePoints;
10
11
  exports.PASS = { ok: true };
11
12
  function fail(detail) {
12
13
  return { ok: false, detail };
@@ -26,3 +27,20 @@ function isPlainObject(value) {
26
27
  function isStringArray(value) {
27
28
  return isArray(value) && value.every(isString);
28
29
  }
30
+ /**
31
+ * 文字列の長さをコードポイントで数える。
32
+ *
33
+ * `.length` は UTF-16 のコード単位を数えるので、絵文字や星域文字が 2 と数えられる。
34
+ * JSON Schema §6.3.1 は minLength / maxLength をコードポイントで数えると定めており、
35
+ * 利用者が「3文字」と書くときに期待するのもコードポイントのほう。
36
+ *
37
+ * 1.x は `.length` を使っていた (docs/legacy-spec/plugin-catalog-core.md が
38
+ * 「暗黙の挙動で、まわりに落ちるテストがある。明示的な決定にすべき」と記録している)。
39
+ * ここでその決定をした。星域文字を含む文字列では 1.x と判定が変わる。
40
+ */
41
+ function countCodePoints(value) {
42
+ let count = 0;
43
+ for (const _character of value)
44
+ count += 1;
45
+ return count;
46
+ }
@@ -17,3 +17,20 @@ export function isPlainObject(value) {
17
17
  export function isStringArray(value) {
18
18
  return isArray(value) && value.every(isString);
19
19
  }
20
+ /**
21
+ * 文字列の長さをコードポイントで数える。
22
+ *
23
+ * `.length` は UTF-16 のコード単位を数えるので、絵文字や星域文字が 2 と数えられる。
24
+ * JSON Schema §6.3.1 は minLength / maxLength をコードポイントで数えると定めており、
25
+ * 利用者が「3文字」と書くときに期待するのもコードポイントのほう。
26
+ *
27
+ * 1.x は `.length` を使っていた (docs/legacy-spec/plugin-catalog-core.md が
28
+ * 「暗黙の挙動で、まわりに落ちるテストがある。明示的な決定にすべき」と記録している)。
29
+ * ここでその決定をした。星域文字を含む文字列では 1.x と判定が変わる。
30
+ */
31
+ export function countCodePoints(value) {
32
+ let count = 0;
33
+ for (const _character of value)
34
+ count += 1;
35
+ return count;
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maroonedog/luq",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Universal Model & API Definition Platform - TypeScript validation library evolving into cross-language code generation",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -32,6 +32,16 @@
32
32
  "import": "./dist/async.mjs",
33
33
  "require": "./dist/async.js"
34
34
  },
35
+ "./standard-schema": {
36
+ "types": "./dist/standard-schema.d.ts",
37
+ "import": "./dist/standard-schema.mjs",
38
+ "require": "./dist/standard-schema.js"
39
+ },
40
+ "./presets": {
41
+ "types": "./dist/presets/index.d.ts",
42
+ "import": "./dist/presets/index.mjs",
43
+ "require": "./dist/presets/index.js"
44
+ },
35
45
  "./plugins": {
36
46
  "types": "./dist/plugins/index.d.ts",
37
47
  "import": "./dist/plugins/index.mjs",
@@ -247,6 +257,11 @@
247
257
  "import": "./dist/plugins/stitch.mjs",
248
258
  "require": "./dist/plugins/stitch.js"
249
259
  },
260
+ "./plugins/stitchWith": {
261
+ "types": "./dist/plugins/stitchWith.d.ts",
262
+ "import": "./dist/plugins/stitchWith.mjs",
263
+ "require": "./dist/plugins/stitchWith.js"
264
+ },
250
265
  "./plugins/stringAlphanumeric": {
251
266
  "types": "./dist/plugins/stringAlphanumeric.d.ts",
252
267
  "import": "./dist/plugins/stringAlphanumeric.mjs",
@@ -427,14 +442,14 @@
427
442
  "build": "npx ts-node --project scripts/tsconfig.json scripts/build.ts",
428
443
  "test": "jest --no-watch --no-watchman --passWithNoTests",
429
444
  "test:watch": "jest --watch",
430
- "lint": "eslint \"src/**/*.ts\" \"bench/**/*.ts\"",
445
+ "lint": "npm run lint:ox && npm run lint:naming",
431
446
  "prepare": "npm run build",
432
447
  "prepublishOnly": "npm run verify",
433
448
  "generate-docs": "npx ts-node --project scripts/tsconfig.json scripts/generate-docs.ts",
434
- "lint:fix": "eslint \"src/**/*.ts\" \"bench/**/*.ts\" --fix",
449
+ "lint:fix": "oxlint -c .oxlintrc.json --fix src bench",
435
450
  "lint:filenames": "node scripts/check-filenames.js",
436
- "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"scripts/**/*.ts\" \"bench/**/*.ts\"",
437
- "format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" \"scripts/**/*.ts\" \"bench/**/*.ts\"",
451
+ "format": "oxfmt src test scripts bench",
452
+ "format:check": "oxfmt --check src test scripts bench",
438
453
  "typecheck": "npm run typecheck:src && npm run typecheck:scripts && npm run typecheck:bench",
439
454
  "typecheck:src": "tsc --noEmit -p tsconfig.json",
440
455
  "typecheck:scripts": "tsc --noEmit -p scripts/tsconfig.json",
@@ -465,9 +480,16 @@
465
480
  "check:doc-examples": "npx ts-node --project scripts/tsconfig.json scripts/check-doc-examples.ts",
466
481
  "check:doc-imports": "npx ts-node --project scripts/tsconfig.json scripts/check-doc-imports.ts",
467
482
  "check:generated-docs": "npx ts-node --project scripts/tsconfig.json scripts/generate-docs.ts --check",
468
- "check:docs": "npm run check:generated-docs && npm run check:doc-examples && npm run check:doc-imports",
483
+ "check:docs": "npm run check:generated-docs && npm run check:doc-examples && npm run check:doc-imports && npm run check:conformance-figures && npm run check:perf-figures",
469
484
  "bench:record": "npx ts-node --project scripts/tsconfig.json scripts/run-bench.ts --record",
470
- "bench:gate": "npx ts-node --project scripts/tsconfig.json scripts/run-bench.ts --gate"
485
+ "bench:gate": "npx ts-node --project scripts/tsconfig.json scripts/run-bench.ts --gate",
486
+ "check:conformance-figures": "npx ts-node --project scripts/tsconfig.json scripts/check-conformance-figures.ts",
487
+ "bench:competitors": "npx ts-node --project bench/tsconfig.json bench/competitors/report-competitors.ts",
488
+ "bench:competitors:check": "npx ts-node --project bench/tsconfig.json bench/competitors/check-competitors.ts",
489
+ "lint:ox": "oxlint -c .oxlintrc.json src bench",
490
+ "lint:naming": "eslint \"src/**/*.ts\" \"bench/**/*.ts\"",
491
+ "generate:perf-figures": "npx ts-node --project scripts/tsconfig.json scripts/check-perf-figures.ts --write",
492
+ "check:perf-figures": "npx ts-node --project scripts/tsconfig.json scripts/check-perf-figures.ts"
471
493
  },
472
494
  "repository": {
473
495
  "type": "git",
@@ -489,27 +511,28 @@
489
511
  "files": [
490
512
  "dist"
491
513
  ],
514
+ "publishConfig": {
515
+ "access": "public",
516
+ "provenance": true
517
+ },
492
518
  "devDependencies": {
493
- "@playwright/test": "^1.54.2",
494
- "@swc/cli": "^0.4.0",
495
- "@swc/core": "^1.7.36",
496
- "@swc/jest": "^0.2.36",
519
+ "@standard-schema/spec": "^1.1.0",
497
520
  "@types/jest": "^29.5.14",
498
- "@typescript-eslint/eslint-plugin": "^6.21.0",
499
- "@typescript-eslint/parser": "^6.21.0",
500
- "dts-bundle-generator": "^9.5.1",
521
+ "@typescript-eslint/eslint-plugin": "^8.70.0",
522
+ "@typescript-eslint/parser": "^8.70.0",
523
+ "ajv": "^8.20.0",
524
+ "ajv-formats": "^3.0.1",
501
525
  "esbuild": "^0.25.5",
502
526
  "eslint": "^8.57.1",
503
- "expect-type": "^1.4.0",
504
527
  "jest": "^29.7.0",
505
- "prettier": "^3.6.2",
506
- "rollup": "^4.46.2",
507
- "rollup-plugin-dts": "^6.2.1",
508
- "sharp": "^0.34.3",
528
+ "oxfmt": "^0.67.0",
529
+ "oxlint": "^1.82.0",
509
530
  "ts-jest": "^29.4.0",
510
531
  "ts-node": "^10.9.2",
511
532
  "tsx": "^4.20.3",
512
533
  "typescript": "^5.8.3",
534
+ "valibot": "^1.4.2",
535
+ "yup": "^1.7.1",
513
536
  "zod": "^4.0.14"
514
537
  },
515
538
  "sideEffects": false