@nekostack/schema 1.0.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 (214) hide show
  1. package/CHANGELOG.md +422 -0
  2. package/LICENSE +202 -0
  3. package/README.md +656 -0
  4. package/dist/src/builders/array.d.ts +12 -0
  5. package/dist/src/builders/array.d.ts.map +1 -0
  6. package/dist/src/builders/array.js +29 -0
  7. package/dist/src/builders/array.js.map +1 -0
  8. package/dist/src/builders/object.d.ts +62 -0
  9. package/dist/src/builders/object.d.ts.map +1 -0
  10. package/dist/src/builders/object.js +263 -0
  11. package/dist/src/builders/object.js.map +1 -0
  12. package/dist/src/builders/primitives.d.ts +37 -0
  13. package/dist/src/builders/primitives.d.ts.map +1 -0
  14. package/dist/src/builders/primitives.js +125 -0
  15. package/dist/src/builders/primitives.js.map +1 -0
  16. package/dist/src/builders/s.d.ts +27 -0
  17. package/dist/src/builders/s.d.ts.map +1 -0
  18. package/dist/src/builders/s.js +39 -0
  19. package/dist/src/builders/s.js.map +1 -0
  20. package/dist/src/builders/schema.d.ts +70 -0
  21. package/dist/src/builders/schema.d.ts.map +1 -0
  22. package/dist/src/builders/schema.js +98 -0
  23. package/dist/src/builders/schema.js.map +1 -0
  24. package/dist/src/cli-integration.d.ts +43 -0
  25. package/dist/src/cli-integration.d.ts.map +1 -0
  26. package/dist/src/cli-integration.js +48 -0
  27. package/dist/src/cli-integration.js.map +1 -0
  28. package/dist/src/errors/issue.d.ts +34 -0
  29. package/dist/src/errors/issue.d.ts.map +1 -0
  30. package/dist/src/errors/issue.js +89 -0
  31. package/dist/src/errors/issue.js.map +1 -0
  32. package/dist/src/generators/errors.d.ts +31 -0
  33. package/dist/src/generators/errors.d.ts.map +1 -0
  34. package/dist/src/generators/errors.js +34 -0
  35. package/dist/src/generators/errors.js.map +1 -0
  36. package/dist/src/generators/header.d.ts +42 -0
  37. package/dist/src/generators/header.d.ts.map +1 -0
  38. package/dist/src/generators/header.js +43 -0
  39. package/dist/src/generators/header.js.map +1 -0
  40. package/dist/src/generators/json-schema-meta.d.ts +36 -0
  41. package/dist/src/generators/json-schema-meta.d.ts.map +1 -0
  42. package/dist/src/generators/json-schema-meta.js +35 -0
  43. package/dist/src/generators/json-schema-meta.js.map +1 -0
  44. package/dist/src/generators/json-schema.d.ts +26 -0
  45. package/dist/src/generators/json-schema.d.ts.map +1 -0
  46. package/dist/src/generators/json-schema.js +88 -0
  47. package/dist/src/generators/json-schema.js.map +1 -0
  48. package/dist/src/generators/openapi.d.ts +33 -0
  49. package/dist/src/generators/openapi.d.ts.map +1 -0
  50. package/dist/src/generators/openapi.js +61 -0
  51. package/dist/src/generators/openapi.js.map +1 -0
  52. package/dist/src/generators/schema-fragment.d.ts +55 -0
  53. package/dist/src/generators/schema-fragment.d.ts.map +1 -0
  54. package/dist/src/generators/schema-fragment.js +253 -0
  55. package/dist/src/generators/schema-fragment.js.map +1 -0
  56. package/dist/src/generators/ts.d.ts +19 -0
  57. package/dist/src/generators/ts.d.ts.map +1 -0
  58. package/dist/src/generators/ts.js +252 -0
  59. package/dist/src/generators/ts.js.map +1 -0
  60. package/dist/src/generators/types.d.ts +96 -0
  61. package/dist/src/generators/types.d.ts.map +1 -0
  62. package/dist/src/generators/types.js +10 -0
  63. package/dist/src/generators/types.js.map +1 -0
  64. package/dist/src/generators/version.d.ts +11 -0
  65. package/dist/src/generators/version.d.ts.map +1 -0
  66. package/dist/src/generators/version.js +11 -0
  67. package/dist/src/generators/version.js.map +1 -0
  68. package/dist/src/generators/zod-mapping.d.ts +90 -0
  69. package/dist/src/generators/zod-mapping.d.ts.map +1 -0
  70. package/dist/src/generators/zod-mapping.js +174 -0
  71. package/dist/src/generators/zod-mapping.js.map +1 -0
  72. package/dist/src/generators/zod.d.ts +17 -0
  73. package/dist/src/generators/zod.d.ts.map +1 -0
  74. package/dist/src/generators/zod.js +118 -0
  75. package/dist/src/generators/zod.js.map +1 -0
  76. package/dist/src/index.d.ts +21 -0
  77. package/dist/src/index.d.ts.map +1 -0
  78. package/dist/src/index.js +43 -0
  79. package/dist/src/index.js.map +1 -0
  80. package/dist/src/ir/hash.d.ts +19 -0
  81. package/dist/src/ir/hash.d.ts.map +1 -0
  82. package/dist/src/ir/hash.js +22 -0
  83. package/dist/src/ir/hash.js.map +1 -0
  84. package/dist/src/ir/nodes.d.ts +121 -0
  85. package/dist/src/ir/nodes.d.ts.map +1 -0
  86. package/dist/src/ir/nodes.js +14 -0
  87. package/dist/src/ir/nodes.js.map +1 -0
  88. package/dist/src/ir/serialize.d.ts +8 -0
  89. package/dist/src/ir/serialize.d.ts.map +1 -0
  90. package/dist/src/ir/serialize.js +23 -0
  91. package/dist/src/ir/serialize.js.map +1 -0
  92. package/dist/src/migrations/build-migration-registry.d.ts +46 -0
  93. package/dist/src/migrations/build-migration-registry.d.ts.map +1 -0
  94. package/dist/src/migrations/build-migration-registry.js +134 -0
  95. package/dist/src/migrations/build-migration-registry.js.map +1 -0
  96. package/dist/src/migrations/handlers/list.d.ts +35 -0
  97. package/dist/src/migrations/handlers/list.d.ts.map +1 -0
  98. package/dist/src/migrations/handlers/list.js +55 -0
  99. package/dist/src/migrations/handlers/list.js.map +1 -0
  100. package/dist/src/migrations/handlers/plan.d.ts +26 -0
  101. package/dist/src/migrations/handlers/plan.d.ts.map +1 -0
  102. package/dist/src/migrations/handlers/plan.js +28 -0
  103. package/dist/src/migrations/handlers/plan.js.map +1 -0
  104. package/dist/src/migrations/handlers/stub.d.ts +22 -0
  105. package/dist/src/migrations/handlers/stub.d.ts.map +1 -0
  106. package/dist/src/migrations/handlers/stub.js +24 -0
  107. package/dist/src/migrations/handlers/stub.js.map +1 -0
  108. package/dist/src/migrations/handlers/verify.d.ts +25 -0
  109. package/dist/src/migrations/handlers/verify.d.ts.map +1 -0
  110. package/dist/src/migrations/handlers/verify.js +27 -0
  111. package/dist/src/migrations/handlers/verify.js.map +1 -0
  112. package/dist/src/migrations/parse-provenance.d.ts +78 -0
  113. package/dist/src/migrations/parse-provenance.d.ts.map +1 -0
  114. package/dist/src/migrations/parse-provenance.js +157 -0
  115. package/dist/src/migrations/parse-provenance.js.map +1 -0
  116. package/dist/src/migrations/plan-migration.d.ts +50 -0
  117. package/dist/src/migrations/plan-migration.d.ts.map +1 -0
  118. package/dist/src/migrations/plan-migration.js +256 -0
  119. package/dist/src/migrations/plan-migration.js.map +1 -0
  120. package/dist/src/migrations/stub.d.ts +55 -0
  121. package/dist/src/migrations/stub.d.ts.map +1 -0
  122. package/dist/src/migrations/stub.js +201 -0
  123. package/dist/src/migrations/stub.js.map +1 -0
  124. package/dist/src/migrations/types.d.ts +297 -0
  125. package/dist/src/migrations/types.d.ts.map +1 -0
  126. package/dist/src/migrations/types.js +28 -0
  127. package/dist/src/migrations/types.js.map +1 -0
  128. package/dist/src/migrations/verify-provenance.d.ts +46 -0
  129. package/dist/src/migrations/verify-provenance.d.ts.map +1 -0
  130. package/dist/src/migrations/verify-provenance.js +158 -0
  131. package/dist/src/migrations/verify-provenance.js.map +1 -0
  132. package/dist/src/registry/build-registry.d.ts +65 -0
  133. package/dist/src/registry/build-registry.d.ts.map +1 -0
  134. package/dist/src/registry/build-registry.js +172 -0
  135. package/dist/src/registry/build-registry.js.map +1 -0
  136. package/dist/src/registry/diff.d.ts +25 -0
  137. package/dist/src/registry/diff.d.ts.map +1 -0
  138. package/dist/src/registry/diff.js +497 -0
  139. package/dist/src/registry/diff.js.map +1 -0
  140. package/dist/src/registry/handlers/check.d.ts +57 -0
  141. package/dist/src/registry/handlers/check.d.ts.map +1 -0
  142. package/dist/src/registry/handlers/check.js +181 -0
  143. package/dist/src/registry/handlers/check.js.map +1 -0
  144. package/dist/src/registry/handlers/diff.d.ts +33 -0
  145. package/dist/src/registry/handlers/diff.d.ts.map +1 -0
  146. package/dist/src/registry/handlers/diff.js +61 -0
  147. package/dist/src/registry/handlers/diff.js.map +1 -0
  148. package/dist/src/registry/handlers/generate.d.ts +87 -0
  149. package/dist/src/registry/handlers/generate.d.ts.map +1 -0
  150. package/dist/src/registry/handlers/generate.js +223 -0
  151. package/dist/src/registry/handlers/generate.js.map +1 -0
  152. package/dist/src/registry/handlers/list.d.ts +36 -0
  153. package/dist/src/registry/handlers/list.d.ts.map +1 -0
  154. package/dist/src/registry/handlers/list.js +84 -0
  155. package/dist/src/registry/handlers/list.js.map +1 -0
  156. package/dist/src/registry/parse-provenance.d.ts +63 -0
  157. package/dist/src/registry/parse-provenance.d.ts.map +1 -0
  158. package/dist/src/registry/parse-provenance.js +182 -0
  159. package/dist/src/registry/parse-provenance.js.map +1 -0
  160. package/dist/src/registry/source-hash.d.ts +28 -0
  161. package/dist/src/registry/source-hash.d.ts.map +1 -0
  162. package/dist/src/registry/source-hash.js +32 -0
  163. package/dist/src/registry/source-hash.js.map +1 -0
  164. package/dist/src/registry/types.d.ts +185 -0
  165. package/dist/src/registry/types.d.ts.map +1 -0
  166. package/dist/src/registry/types.js +22 -0
  167. package/dist/src/registry/types.js.map +1 -0
  168. package/dist/src/runtime/compile.d.ts +38 -0
  169. package/dist/src/runtime/compile.d.ts.map +1 -0
  170. package/dist/src/runtime/compile.js +45 -0
  171. package/dist/src/runtime/compile.js.map +1 -0
  172. package/dist/src/runtime/errors.d.ts +25 -0
  173. package/dist/src/runtime/errors.d.ts.map +1 -0
  174. package/dist/src/runtime/errors.js +43 -0
  175. package/dist/src/runtime/errors.js.map +1 -0
  176. package/dist/src/runtime/normalize-issues.d.ts +65 -0
  177. package/dist/src/runtime/normalize-issues.d.ts.map +1 -0
  178. package/dist/src/runtime/normalize-issues.js +208 -0
  179. package/dist/src/runtime/normalize-issues.js.map +1 -0
  180. package/dist/src/runtime/parse.d.ts +62 -0
  181. package/dist/src/runtime/parse.d.ts.map +1 -0
  182. package/dist/src/runtime/parse.js +107 -0
  183. package/dist/src/runtime/parse.js.map +1 -0
  184. package/dist/src/runtime/strip-defaults.d.ts +51 -0
  185. package/dist/src/runtime/strip-defaults.d.ts.map +1 -0
  186. package/dist/src/runtime/strip-defaults.js +81 -0
  187. package/dist/src/runtime/strip-defaults.js.map +1 -0
  188. package/dist/src/runtime/zod-compile.d.ts +27 -0
  189. package/dist/src/runtime/zod-compile.d.ts.map +1 -0
  190. package/dist/src/runtime/zod-compile.js +92 -0
  191. package/dist/src/runtime/zod-compile.js.map +1 -0
  192. package/dist/src/types.d.ts +116 -0
  193. package/dist/src/types.d.ts.map +1 -0
  194. package/dist/src/types.js +2 -0
  195. package/dist/src/types.js.map +1 -0
  196. package/docs/ABSENCE_SEMANTICS.md +37 -0
  197. package/docs/BENCHMARKS.md +64 -0
  198. package/docs/COMPOSITION.md +206 -0
  199. package/docs/DIFF_CLASSIFICATION.md +137 -0
  200. package/docs/EXAMPLES.md +221 -0
  201. package/docs/HEADER_FORMAT.md +66 -0
  202. package/docs/INVARIANTS.md +58 -0
  203. package/docs/IR_CONTRACT.md +67 -0
  204. package/docs/ISSUE_CODES.md +99 -0
  205. package/docs/JSON_SCHEMA_MAPPING.md +149 -0
  206. package/docs/MIGRATIONS.md +406 -0
  207. package/docs/MIGRATION_GUIDE.md +150 -0
  208. package/docs/OPENAPI_MAPPING.md +66 -0
  209. package/docs/REGISTRY.md +336 -0
  210. package/docs/RUNTIME.md +279 -0
  211. package/docs/SCOPE.md +119 -0
  212. package/docs/USAGE.md +376 -0
  213. package/docs/ZOD_MODIFIER_ORDERING.md +77 -0
  214. package/package.json +45 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * `checkHandler({ registry, committedArtifacts })` — freshness check
3
+ * via the two-hash matrix from Master plan §"Freshness verdict —
4
+ * two-hash discipline".
5
+ *
6
+ * Pure. No filesystem, no `import()`, no `process.*`, no `console.*`.
7
+ * The CLI reads each artifact's bytes from disk and hands them in;
8
+ * this function parses provenance, looks up the matching
9
+ * `RegistryEntry`, and produces one `FreshnessVerdict` per artifact.
10
+ * Master plan Decision #1 boundary.
11
+ *
12
+ * ## Two-hash matrix
13
+ *
14
+ * | artifact irHash | artifact sourceHash | verdict |
15
+ * |---|---|---|
16
+ * | matches registry | matches registry | `clean` |
17
+ * | matches registry | **differs** | `cosmetic_drift` |
18
+ * | **differs** | **differs** | `stale` |
19
+ * | **differs** | matches registry | `integrity_error` (the impossible row — should never happen unless the artifact was hand-edited or the recorded sourceHash was tampered with) |
20
+ *
21
+ * ## v0.6 backward compatibility (Master plan Decision #8)
22
+ *
23
+ * Artifacts emitted before Step 4 (`@nekostack/schema@0.6.0` and
24
+ * earlier) have no `sourceHash` field/line. `parseProvenanceFromText`
25
+ * returns `sourceHash: undefined` for those. Treat as:
26
+ *
27
+ * - `irHash` matches registry → `clean`
28
+ * - `irHash` differs from registry → `stale`
29
+ *
30
+ * Absent `sourceHash` is **never** an integrity error by itself — the
31
+ * artifact simply predates the two-hash discipline. Once the user
32
+ * regenerates the artifact at v0.7+, full matrix participation
33
+ * resumes.
34
+ *
35
+ * ## Failure paths (Result<...> failure)
36
+ *
37
+ * - **Malformed provenance** — `parseProvenanceFromText` already
38
+ * returns an `integrity_error` Issue; this handler forwards those
39
+ * issues with the artifact path attached.
40
+ * - **Anonymous artifact** (provenance `schemaId === null`) —
41
+ * `schema_not_found` with `metadata.reason = "anonymous_artifact"`.
42
+ * The registry never indexes anonymous schemas (Master plan
43
+ * Decision #5), so there's nothing to validate against.
44
+ * - **schemaId not in registry** — `schema_not_found`. The user
45
+ * likely deleted a schema source file but forgot to delete the
46
+ * generated artifacts.
47
+ * - **schemaId present but version not in registry** —
48
+ * `version_not_found`. Distinct code so the CLI can format
49
+ * orphan-by-id vs. orphan-by-version differently.
50
+ *
51
+ * Any artifact failure surfaces as `Result.failure` for the whole
52
+ * check call. Per-artifact verdicts are returned only when EVERY
53
+ * artifact parses + resolves cleanly.
54
+ */
55
+ import { findSchema } from "../build-registry.js";
56
+ import { parseProvenanceFromText } from "../parse-provenance.js";
57
+ export function checkHandler(opts) {
58
+ const verdicts = [];
59
+ const issues = [];
60
+ for (const artifact of opts.committedArtifacts) {
61
+ const r = classifyArtifact(artifact, opts.registry);
62
+ if (r.kind === "verdict") {
63
+ verdicts.push(r.verdict);
64
+ }
65
+ else {
66
+ issues.push(...r.issues);
67
+ }
68
+ }
69
+ if (issues.length > 0) {
70
+ return { success: false, issues };
71
+ }
72
+ return { success: true, data: { verdicts } };
73
+ }
74
+ function classifyArtifact(artifact, registry) {
75
+ // 1. Parse provenance. Forward failures with the artifact path.
76
+ const parsed = parseProvenanceFromText(artifact.content);
77
+ if (!parsed.success) {
78
+ return {
79
+ kind: "issues",
80
+ issues: parsed.issues.map((i) => ({
81
+ ...i,
82
+ path: [artifact.path],
83
+ metadata: { ...(i.metadata ?? {}), artifactPath: artifact.path },
84
+ })),
85
+ };
86
+ }
87
+ const { schemaId, schemaVersion, irHash, sourceHash } = parsed.data;
88
+ // 2. Anonymous artifact — registry never indexes anonymous schemas.
89
+ if (schemaId === null) {
90
+ return {
91
+ kind: "issues",
92
+ issues: [
93
+ {
94
+ code: "schema_not_found",
95
+ path: [artifact.path],
96
+ message: `Anonymous artifact \`${artifact.path}\` cannot be validated; registry does not index anonymous schemas.`,
97
+ severity: "error",
98
+ metadata: {
99
+ reason: "anonymous_artifact",
100
+ artifactPath: artifact.path,
101
+ },
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ // 3. Look up the (schemaId, schemaVersion) pair in the registry.
107
+ // Pass the version explicitly so `findSchema` does exact-match
108
+ // rather than highest-semver fallback.
109
+ const versionKey = schemaVersion ?? "";
110
+ const entry = findSchema(registry, schemaId, versionKey);
111
+ if (entry === undefined) {
112
+ const idKnown = registry.has(schemaId);
113
+ return {
114
+ kind: "issues",
115
+ issues: [
116
+ idKnown
117
+ ? versionNotFoundIssue(artifact, schemaId, schemaVersion)
118
+ : schemaNotFoundIssue(artifact, schemaId, schemaVersion),
119
+ ],
120
+ };
121
+ }
122
+ // 4. Apply the two-hash matrix.
123
+ return { kind: "verdict", verdict: verdictFor(artifact, irHash, sourceHash, entry) };
124
+ }
125
+ function verdictFor(artifact, artifactIrHash, artifactSourceHash, entry) {
126
+ const irMatch = artifactIrHash === entry.irHash;
127
+ // v0.6-era artifact: no sourceHash recorded.
128
+ // - irHash match → clean
129
+ // - irHash differ → stale
130
+ // Never integrity_error.
131
+ if (artifactSourceHash === undefined) {
132
+ return {
133
+ status: irMatch ? "clean" : "stale",
134
+ artifactPath: artifact.path,
135
+ };
136
+ }
137
+ const sourceMatch = artifactSourceHash === entry.sourceHash;
138
+ let status;
139
+ if (irMatch && sourceMatch)
140
+ status = "clean";
141
+ else if (irMatch && !sourceMatch)
142
+ status = "cosmetic_drift";
143
+ else if (!irMatch && !sourceMatch)
144
+ status = "stale";
145
+ else
146
+ status = "integrity_error";
147
+ return { status, artifactPath: artifact.path };
148
+ }
149
+ // =============================================================================
150
+ // Issue constructors
151
+ // =============================================================================
152
+ function schemaNotFoundIssue(artifact, schemaId, schemaVersion) {
153
+ return {
154
+ code: "schema_not_found",
155
+ path: [artifact.path],
156
+ message: `Schema \`${schemaId}\` referenced by artifact \`${artifact.path}\` is not in the registry. ` +
157
+ `The schema source may have been deleted; remove the generated artifact or restore the source.`,
158
+ severity: "error",
159
+ metadata: {
160
+ artifactPath: artifact.path,
161
+ schemaId,
162
+ schemaVersion: schemaVersion ?? null,
163
+ },
164
+ };
165
+ }
166
+ function versionNotFoundIssue(artifact, schemaId, schemaVersion) {
167
+ const versionLabel = schemaVersion === null ? "(unversioned)" : `v${schemaVersion}`;
168
+ return {
169
+ code: "version_not_found",
170
+ path: [artifact.path],
171
+ message: `Schema \`${schemaId}\` exists in the registry but version \`${versionLabel}\` (referenced by \`${artifact.path}\`) does not. ` +
172
+ `Regenerate the artifact against the current schema versions.`,
173
+ severity: "error",
174
+ metadata: {
175
+ artifactPath: artifact.path,
176
+ schemaId,
177
+ schemaVersion: schemaVersion ?? null,
178
+ },
179
+ };
180
+ }
181
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../../src/registry/handlers/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `diffHandler({ before, after })` — wraps Step 7's `diffNodes` and
3
+ * adds the `worstSeverity` aggregation that Master plan Decision #13
4
+ * relies on for CLI exit-code mapping.
5
+ *
6
+ * Pure. No filesystem, no `import()`, no `process.*`, no `console.*`.
7
+ * Master plan Decision #1 boundary.
8
+ *
9
+ * **Severity precedence**: `breaking` > `additive` > `cosmetic`. The
10
+ * `worstSeverity` field is `null` exactly when `changes.length === 0`.
11
+ * Per Decision #13: a `schemaVersion`-only change paired with
12
+ * structural changes inherits the worst structural severity — that
13
+ * rule is realized naturally here because every change keeps its own
14
+ * severity (set by `diffNodes`); `worstSeverity` is the max over the
15
+ * full list.
16
+ *
17
+ * **`UnsupportedNodeKindError` propagates.** When `diffNodes` throws
18
+ * for an unsupported IR kind (`date`, `union`, `recursiveRef`,
19
+ * `transform`), this handler does NOT catch — it lets the throw
20
+ * propagate to the CLI dispatcher (Step 28+), where it maps to a
21
+ * non-zero exit code at the CLI boundary. This matches Master plan
22
+ * Decision #14's "throw at the boundary" language and the v0.6
23
+ * convention (`runtime/parse.ts` also propagates rather than
24
+ * normalizing into `Result`). Treating an unsupported IR kind as an
25
+ * `integrity_error` (the closest existing v0.7 code) would be
26
+ * semantically wrong — the IR is well-formed; v0.7 just doesn't
27
+ * know how to diff it. Inventing a new code (`schema_diff_failed`)
28
+ * without it being in the locked plan would expand the v0.7 vocabulary
29
+ * casually.
30
+ */
31
+ import type { DiffOpts, DiffResult } from "../types.js";
32
+ export declare function diffHandler(opts: DiffOpts): DiffResult;
33
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../../src/registry/handlers/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAEV,QAAQ,EACR,UAAU,EAEX,MAAM,aAAa,CAAC;AAErB,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAStD"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * `diffHandler({ before, after })` — wraps Step 7's `diffNodes` and
3
+ * adds the `worstSeverity` aggregation that Master plan Decision #13
4
+ * relies on for CLI exit-code mapping.
5
+ *
6
+ * Pure. No filesystem, no `import()`, no `process.*`, no `console.*`.
7
+ * Master plan Decision #1 boundary.
8
+ *
9
+ * **Severity precedence**: `breaking` > `additive` > `cosmetic`. The
10
+ * `worstSeverity` field is `null` exactly when `changes.length === 0`.
11
+ * Per Decision #13: a `schemaVersion`-only change paired with
12
+ * structural changes inherits the worst structural severity — that
13
+ * rule is realized naturally here because every change keeps its own
14
+ * severity (set by `diffNodes`); `worstSeverity` is the max over the
15
+ * full list.
16
+ *
17
+ * **`UnsupportedNodeKindError` propagates.** When `diffNodes` throws
18
+ * for an unsupported IR kind (`date`, `union`, `recursiveRef`,
19
+ * `transform`), this handler does NOT catch — it lets the throw
20
+ * propagate to the CLI dispatcher (Step 28+), where it maps to a
21
+ * non-zero exit code at the CLI boundary. This matches Master plan
22
+ * Decision #14's "throw at the boundary" language and the v0.6
23
+ * convention (`runtime/parse.ts` also propagates rather than
24
+ * normalizing into `Result`). Treating an unsupported IR kind as an
25
+ * `integrity_error` (the closest existing v0.7 code) would be
26
+ * semantically wrong — the IR is well-formed; v0.7 just doesn't
27
+ * know how to diff it. Inventing a new code (`schema_diff_failed`)
28
+ * without it being in the locked plan would expand the v0.7 vocabulary
29
+ * casually.
30
+ */
31
+ import { diffNodes } from "../diff.js";
32
+ export function diffHandler(opts) {
33
+ const changes = diffNodes(opts.before, opts.after);
34
+ return {
35
+ success: true,
36
+ data: {
37
+ changes,
38
+ worstSeverity: computeWorstSeverity(changes),
39
+ },
40
+ };
41
+ }
42
+ // =============================================================================
43
+ // worstSeverity aggregation (Master plan Decision #13)
44
+ // =============================================================================
45
+ const SEVERITY_RANK = {
46
+ cosmetic: 0,
47
+ additive: 1,
48
+ breaking: 2,
49
+ };
50
+ function computeWorstSeverity(changes) {
51
+ if (changes.length === 0)
52
+ return null;
53
+ let worst = "cosmetic";
54
+ for (const c of changes) {
55
+ if (SEVERITY_RANK[c.severity] > SEVERITY_RANK[worst]) {
56
+ worst = c.severity;
57
+ }
58
+ }
59
+ return worst;
60
+ }
61
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../../src/registry/handlers/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAQvC,MAAM,UAAU,WAAW,CAAC,IAAc;IACxC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACnD,OAAO;QACL,OAAO,EAAE,IAAI;QACb,IAAI,EAAE;YACJ,OAAO;YACP,aAAa,EAAE,oBAAoB,CAAC,OAAO,CAAC;SAC7C;KACF,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,uDAAuD;AACvD,gFAAgF;AAEhF,MAAM,aAAa,GAAiC;IAClD,QAAQ,EAAE,CAAC;IACX,QAAQ,EAAE,CAAC;IACX,QAAQ,EAAE,CAAC;CACZ,CAAC;AAEF,SAAS,oBAAoB,CAC3B,OAA8B;IAE9B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,KAAK,GAAiB,UAAU,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `generateHandler({ entries })` — pure generation planner.
3
+ *
4
+ * For every **named** schema in every `RegistrySourceEntry`, emits
5
+ * all four artifact kinds (TypeScript / Zod / JSON Schema / OpenAPI)
6
+ * as `GeneratedArtifact` payloads with `suggestedPath`s relative to
7
+ * the schema's source location. The CLI is responsible for writing
8
+ * each payload to its suggested path; this handler never touches
9
+ * the filesystem.
10
+ *
11
+ * Master plan Decision #1 boundary: pure, data-in / data-out. No
12
+ * `fs.*`, no `import()`, no `process.*`, no `console.*`. The
13
+ * `sourceText` is hashed once per entry via `sourceHashFromText`
14
+ * (Step 2). `sourceHash` is passed through to each generator's
15
+ * `ProvenanceOptions` (Step 3 + Step 4) so the emitted artifact
16
+ * bytes carry provenance; `irHash(schema.node)` is recorded
17
+ * separately on each `GeneratedArtifact` and independently emitted
18
+ * by the generators from `schema.node` — the irHash field is NOT
19
+ * part of `ProvenanceOptions`.
20
+ *
21
+ * **Anonymous schemas (no `.id()`)** are silently skipped — they
22
+ * cannot participate in registry lookup downstream, so emitting
23
+ * artifacts for them is meaningless. The CLI warns; this handler
24
+ * is silent (Master plan Decision #5).
25
+ *
26
+ * **Partial generation is not supported in v0.7** (Master plan
27
+ * Decision #6). `GenerateOpts` carries no `kinds` filter; every
28
+ * named schema produces exactly 4 artifacts. The CLI's `check`
29
+ * verb expects all 4 to be present on disk.
30
+ *
31
+ * ## Suggested path convention (Master plan Decision #6 locked)
32
+ *
33
+ * **Single named schema in a source file:**
34
+ *
35
+ * <schema-dir>/<basename>.schema.{ts,js}
36
+ * ↓
37
+ * <schema-dir>/generated/<basename>.types.ts
38
+ * <schema-dir>/generated/<basename>.zod.ts
39
+ * <schema-dir>/generated/<basename>.json.schema.json
40
+ * <schema-dir>/generated/<basename>.openapi.json
41
+ *
42
+ * **Multiple named schemas in one source file** — paths gain a
43
+ * schemaId-derived discriminator slug so the four-artifact set per
44
+ * schema doesn't collide on disk:
45
+ *
46
+ * <schema-dir>/<basename>.schema.ts
47
+ * export const Tenant = s.object(...).id("com.x.Tenant");
48
+ * export const Audit = s.object(...).id("com.x.AuditEvent");
49
+ * ↓
50
+ * <schema-dir>/generated/<basename>.com-x-tenant.types.ts
51
+ * <schema-dir>/generated/<basename>.com-x-auditevent.types.ts
52
+ * <schema-dir>/generated/<basename>.com-x-tenant.zod.ts
53
+ * ... and so on for json/openapi.
54
+ *
55
+ * Slug rule: lowercase, non-alphanumeric runs collapse to `-`, with
56
+ * leading/trailing `-` trimmed. If a source file declares the same
57
+ * `schemaId` at multiple versions (rare; v0.7 doesn't endorse but
58
+ * does tolerate), the discriminator additionally includes a slugged
59
+ * version (`com-x-tenant-1-0-0`) so per-schema paths stay unique.
60
+ *
61
+ * The basename strips a `.schema.{ts,js,mts,cts}` suffix when
62
+ * present; falls back to "filename minus last extension" otherwise.
63
+ * Source-path parsing is plain string manipulation — no `node:path`
64
+ * import — to keep the handler trivially platform-agnostic.
65
+ */
66
+ import type { GenerateOpts, GenerateResult, GeneratorKind, RegistrySourceEntry } from "../types.js";
67
+ declare const GENERATOR_KINDS: readonly GeneratorKind[];
68
+ export declare function generateHandler(opts: GenerateOpts): GenerateResult;
69
+ /**
70
+ * `<schema-dir>/<basename>.schema.ts` → `<schema-dir>/generated/<basename>[.<discriminator>].<ext>`.
71
+ *
72
+ * Pure string manipulation; no `node:path` import. Forward slashes
73
+ * are the canonical separator in the suggested path even on Windows
74
+ * — the CLI normalizes back to platform separators on write
75
+ * (separate concern; Step 31).
76
+ *
77
+ * The optional `discriminator` is inserted between the basename and
78
+ * the artifact extension. The handler computes it per-entry via
79
+ * `discriminatorsFor` (see above); callers that know they have a
80
+ * single-schema source file may omit the option entirely.
81
+ */
82
+ export declare function suggestedPathFor(sourcePath: string, kind: GeneratorKind, options?: {
83
+ readonly discriminator?: string;
84
+ }): string;
85
+ export { GENERATOR_KINDS };
86
+ export type { RegistrySourceEntry };
87
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../../src/registry/handlers/generate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAQH,OAAO,KAAK,EAEV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAIrB,QAAA,MAAM,eAAe,EAAE,SAAS,aAAa,EAK5C,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,CA6BlE;AAqGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,aAAa,EACnB,OAAO,GAAE;IAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GAChD,MAAM,CAaR;AAaD,OAAO,EAAE,eAAe,EAAE,CAAC;AAG3B,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,223 @@
1
+ /**
2
+ * `generateHandler({ entries })` — pure generation planner.
3
+ *
4
+ * For every **named** schema in every `RegistrySourceEntry`, emits
5
+ * all four artifact kinds (TypeScript / Zod / JSON Schema / OpenAPI)
6
+ * as `GeneratedArtifact` payloads with `suggestedPath`s relative to
7
+ * the schema's source location. The CLI is responsible for writing
8
+ * each payload to its suggested path; this handler never touches
9
+ * the filesystem.
10
+ *
11
+ * Master plan Decision #1 boundary: pure, data-in / data-out. No
12
+ * `fs.*`, no `import()`, no `process.*`, no `console.*`. The
13
+ * `sourceText` is hashed once per entry via `sourceHashFromText`
14
+ * (Step 2). `sourceHash` is passed through to each generator's
15
+ * `ProvenanceOptions` (Step 3 + Step 4) so the emitted artifact
16
+ * bytes carry provenance; `irHash(schema.node)` is recorded
17
+ * separately on each `GeneratedArtifact` and independently emitted
18
+ * by the generators from `schema.node` — the irHash field is NOT
19
+ * part of `ProvenanceOptions`.
20
+ *
21
+ * **Anonymous schemas (no `.id()`)** are silently skipped — they
22
+ * cannot participate in registry lookup downstream, so emitting
23
+ * artifacts for them is meaningless. The CLI warns; this handler
24
+ * is silent (Master plan Decision #5).
25
+ *
26
+ * **Partial generation is not supported in v0.7** (Master plan
27
+ * Decision #6). `GenerateOpts` carries no `kinds` filter; every
28
+ * named schema produces exactly 4 artifacts. The CLI's `check`
29
+ * verb expects all 4 to be present on disk.
30
+ *
31
+ * ## Suggested path convention (Master plan Decision #6 locked)
32
+ *
33
+ * **Single named schema in a source file:**
34
+ *
35
+ * <schema-dir>/<basename>.schema.{ts,js}
36
+ * ↓
37
+ * <schema-dir>/generated/<basename>.types.ts
38
+ * <schema-dir>/generated/<basename>.zod.ts
39
+ * <schema-dir>/generated/<basename>.json.schema.json
40
+ * <schema-dir>/generated/<basename>.openapi.json
41
+ *
42
+ * **Multiple named schemas in one source file** — paths gain a
43
+ * schemaId-derived discriminator slug so the four-artifact set per
44
+ * schema doesn't collide on disk:
45
+ *
46
+ * <schema-dir>/<basename>.schema.ts
47
+ * export const Tenant = s.object(...).id("com.x.Tenant");
48
+ * export const Audit = s.object(...).id("com.x.AuditEvent");
49
+ * ↓
50
+ * <schema-dir>/generated/<basename>.com-x-tenant.types.ts
51
+ * <schema-dir>/generated/<basename>.com-x-auditevent.types.ts
52
+ * <schema-dir>/generated/<basename>.com-x-tenant.zod.ts
53
+ * ... and so on for json/openapi.
54
+ *
55
+ * Slug rule: lowercase, non-alphanumeric runs collapse to `-`, with
56
+ * leading/trailing `-` trimmed. If a source file declares the same
57
+ * `schemaId` at multiple versions (rare; v0.7 doesn't endorse but
58
+ * does tolerate), the discriminator additionally includes a slugged
59
+ * version (`com-x-tenant-1-0-0`) so per-schema paths stay unique.
60
+ *
61
+ * The basename strips a `.schema.{ts,js,mts,cts}` suffix when
62
+ * present; falls back to "filename minus last extension" otherwise.
63
+ * Source-path parsing is plain string manipulation — no `node:path`
64
+ * import — to keep the handler trivially platform-agnostic.
65
+ */
66
+ import { irHash } from "../../ir/hash.js";
67
+ import { generateTypeScript } from "../../generators/ts.js";
68
+ import { generateZod } from "../../generators/zod.js";
69
+ import { generateJsonSchema } from "../../generators/json-schema.js";
70
+ import { generateOpenApiSchemaComponent } from "../../generators/openapi.js";
71
+ import { sourceHashFromText } from "../source-hash.js";
72
+ const GENERATOR_KINDS = [
73
+ "typescript",
74
+ "zod",
75
+ "jsonSchema",
76
+ "openApi",
77
+ ];
78
+ export function generateHandler(opts) {
79
+ const artifacts = [];
80
+ for (const entry of opts.entries) {
81
+ const entrySourceHash = sourceHashFromText(entry.sourceText);
82
+ const discriminators = discriminatorsFor(entry);
83
+ for (const schema of entry.schemas) {
84
+ const schemaId = schema.node.metadata?.id;
85
+ if (schemaId === undefined)
86
+ continue; // anonymous → skip
87
+ const schemaIrHash = `sha256:${irHash(schema.node)}`;
88
+ const discriminator = discriminators.get(schema);
89
+ for (const kind of GENERATOR_KINDS) {
90
+ artifacts.push({
91
+ schemaId,
92
+ kind,
93
+ suggestedPath: suggestedPathFor(entry.sourcePath, kind, {
94
+ ...(discriminator !== undefined ? { discriminator } : {}),
95
+ }),
96
+ content: renderArtifact(kind, schema, entrySourceHash),
97
+ irHash: schemaIrHash,
98
+ sourceHash: entrySourceHash,
99
+ });
100
+ }
101
+ }
102
+ }
103
+ return { success: true, data: { artifacts } };
104
+ }
105
+ // =============================================================================
106
+ // Per-entry discriminator slugs (Master plan Decision #6 multi-schema rule)
107
+ // =============================================================================
108
+ /**
109
+ * For one `RegistrySourceEntry`, decide the discriminator slug each
110
+ * named schema needs on its `suggestedPath`:
111
+ *
112
+ * - Entry contains exactly 0 or 1 named schemas → no discriminator.
113
+ * - Entry contains 2+ named schemas with **distinct ids** →
114
+ * discriminator = `slugify(schemaId)`.
115
+ * - Entry contains 2+ named schemas where **at least one id appears
116
+ * more than once** (different versions of the same id in one
117
+ * source file — rare) → for the IDs that repeat, the discriminator
118
+ * additionally embeds the slugged version so per-schema paths
119
+ * stay unique. IDs that appear only once still use the id-only
120
+ * slug.
121
+ *
122
+ * Returned `Map` is keyed by the original `AnySchema` instance so
123
+ * the handler loop can look up each schema's discriminator without
124
+ * re-deriving it. Anonymous schemas are not represented in the map.
125
+ */
126
+ function discriminatorsFor(entry) {
127
+ const result = new Map();
128
+ const named = entry.schemas.filter((s) => s.node.metadata?.id !== undefined);
129
+ if (named.length <= 1) {
130
+ for (const s of named)
131
+ result.set(s, undefined);
132
+ return result;
133
+ }
134
+ // Multiple named schemas — disambiguation needed. Count id
135
+ // occurrences; if any id appears more than once, those entries
136
+ // get the version-suffixed slug to stay unique.
137
+ const idCounts = new Map();
138
+ for (const s of named) {
139
+ const id = s.node.metadata.id;
140
+ idCounts.set(id, (idCounts.get(id) ?? 0) + 1);
141
+ }
142
+ for (const s of named) {
143
+ const id = s.node.metadata.id;
144
+ const version = s.node.metadata?.version;
145
+ const idSlug = slugify(id);
146
+ const sameIdElsewhere = (idCounts.get(id) ?? 0) > 1;
147
+ const slug = sameIdElsewhere && version !== undefined
148
+ ? `${idSlug}-${slugify(version)}`
149
+ : idSlug;
150
+ result.set(s, slug);
151
+ }
152
+ return result;
153
+ }
154
+ /** Lowercase, non-alphanumeric runs → `-`, trim leading/trailing `-`. */
155
+ function slugify(s) {
156
+ return s
157
+ .toLowerCase()
158
+ .replace(/[^a-z0-9]+/g, "-")
159
+ .replace(/^-+|-+$/g, "");
160
+ }
161
+ // =============================================================================
162
+ // Per-kind dispatch
163
+ // =============================================================================
164
+ function renderArtifact(kind, schema, sourceHash) {
165
+ const node = schema.node;
166
+ switch (kind) {
167
+ case "typescript":
168
+ return generateTypeScript(node, { sourceHash });
169
+ case "zod":
170
+ return generateZod(node, { sourceHash });
171
+ case "jsonSchema":
172
+ return generateJsonSchema(node, { sourceHash });
173
+ case "openApi":
174
+ return generateOpenApiSchemaComponent(node, { sourceHash });
175
+ }
176
+ }
177
+ // =============================================================================
178
+ // Path convention
179
+ // =============================================================================
180
+ const KIND_TO_EXT = {
181
+ typescript: ".types.ts",
182
+ zod: ".zod.ts",
183
+ jsonSchema: ".json.schema.json",
184
+ openApi: ".openapi.json",
185
+ };
186
+ /**
187
+ * `<schema-dir>/<basename>.schema.ts` → `<schema-dir>/generated/<basename>[.<discriminator>].<ext>`.
188
+ *
189
+ * Pure string manipulation; no `node:path` import. Forward slashes
190
+ * are the canonical separator in the suggested path even on Windows
191
+ * — the CLI normalizes back to platform separators on write
192
+ * (separate concern; Step 31).
193
+ *
194
+ * The optional `discriminator` is inserted between the basename and
195
+ * the artifact extension. The handler computes it per-entry via
196
+ * `discriminatorsFor` (see above); callers that know they have a
197
+ * single-schema source file may omit the option entirely.
198
+ */
199
+ export function suggestedPathFor(sourcePath, kind, options = {}) {
200
+ const normalized = sourcePath.replace(/\\/g, "/");
201
+ const lastSlash = normalized.lastIndexOf("/");
202
+ const dir = lastSlash >= 0 ? normalized.slice(0, lastSlash) : "";
203
+ const filename = lastSlash >= 0 ? normalized.slice(lastSlash + 1) : normalized;
204
+ const basename = basenameOf(filename);
205
+ const stem = options.discriminator !== undefined
206
+ ? `${basename}.${options.discriminator}`
207
+ : basename;
208
+ const prefix = dir ? `${dir}/generated/${stem}` : `generated/${stem}`;
209
+ return `${prefix}${KIND_TO_EXT[kind]}`;
210
+ }
211
+ function basenameOf(filename) {
212
+ // Preferred: strip the `.schema.{ts,js,mts,cts}` suffix.
213
+ const schemaMatch = filename.match(/^(.+?)\.schema\.(ts|js|mts|cts)$/);
214
+ if (schemaMatch)
215
+ return schemaMatch[1];
216
+ // Fallback for non-conventional input: drop the last extension.
217
+ const lastDot = filename.lastIndexOf(".");
218
+ return lastDot > 0 ? filename.slice(0, lastDot) : filename;
219
+ }
220
+ // Exported so the CLI can advertise the same path convention for
221
+ // `check`'s artifact-lookup, without re-deriving the rule.
222
+ export { GENERATOR_KINDS };
223
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../../src/registry/handlers/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `listHandler({ registry })` — enumerate every `RegistryEntry` in a
3
+ * `Registry`, in a stable order, as a `Result<{ entries }>`.
4
+ *
5
+ * The simplest of the four v0.7 handlers; landing this commit first
6
+ * pins the handler shape and the `Result<T>` discriminated-union
7
+ * contract before `diff` / `check` / `generate` add per-handler
8
+ * complexity (Master plan sequencing step 8).
9
+ *
10
+ * **Pure.** No filesystem, no `import()`, no `process.*`, no
11
+ * `console.*`. Takes an already-built `Registry` (Step 6's
12
+ * `buildRegistry` output) and returns a `Result` over a flat array.
13
+ * Master plan Decision #1 boundary.
14
+ *
15
+ * Ordering (deterministic; documented in tests):
16
+ *
17
+ * 1. **Across `schemaId`**: alphabetical ascending.
18
+ * 2. **Within one `schemaId`**: versioned entries first, ascending
19
+ * by numeric major.minor.patch semver (so `1.0.0` < `2.0.0` <
20
+ * `10.0.0`, never lexicographic `10.0.0` < `2.0.0`). Unversioned
21
+ * entries (the empty-string inner key from Step 6) come **last**.
22
+ *
23
+ * Rationale for "unversioned last":
24
+ * - Putting unversioned first would sort empty-string `""` before
25
+ * any semver — visually surprising in CLI output.
26
+ * - Unversioned schemas are the v0.7 fallback for `.id()`-without-
27
+ * `.version()`. Most workspaces won't have any. Sorting them at
28
+ * the tail makes them visually distinct from the canonical
29
+ * versioned list.
30
+ *
31
+ * Empty registry: returns `{ success: true, data: { entries: [] } }`.
32
+ * Never returns failure — list is read-only, has no failure mode.
33
+ */
34
+ import type { ListOpts, ListResult } from "../types.js";
35
+ export declare function listHandler(opts: ListOpts): ListResult;
36
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/registry/handlers/list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EAEX,MAAM,aAAa,CAAC;AAErB,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAyBtD"}