@effected/schemastore-cli 0.19.0 → 0.20.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.
package/README.md CHANGED
@@ -119,7 +119,7 @@ schemastore validate <payload.json> [config] [--schema <path|$id|url>] [--format
119
119
 
120
120
  - Before anything is generated, every frozen label is verified — present on disk, and self-identified by the derived `$id`.
121
121
  - `build` generates every schema, runs the gates (the structural lint and ajv strict mode), applies the drift policy, and writes what passes — content-compared, so an unchanged file is untouched — plus the config's catalog slice when any entry declares one, and the merged catalog over every slice.
122
- - `check` is the identical walk with no writes: it reports what `build` would do under the same flags and exits the same way, and also fails (exit `1`) whenever a build would write anything — a stale or missing document is fixed by running `schemastore build` and committing the result. A catalog slice left behind after the config's last `catalog` block was removed is reported `orphaned` and fails `check` the same way, but `build` never deletes it — and the merged catalog keeps advertising its entries until it is gone: delete the file by hand, or restore a `catalog` block; a merged `catalog.json` with no slice left is orphaned the same way. So is a document left behind under an old derived name — an `appendVersion` flip or a `layout` change moved its path, and nothing claims the old file any more: both commands probe the sibling shapes (`<name>.json`, `<name>-<v>.json`, `<v>/<name>.json`, `<v>/<name>-<v>.json`) of every label the config still declares and report each one that exists as an orphaned document, failed by `check`, never deleted by `build`. Nothing else in `outputDir` is looked at, so sharing it with another config, a deploy folder, or the repository root is safe (unless two configs derive the same schema name and version under different layouts into it); a `name` change or a dropped label leaves a file the command cannot know about — delete those by hand. A catalog URL advertised by two slices, or a slice that cannot be read or is not a catalog entry array (an undeclared key included), blocks the merged catalog: both commands fail (exit `1`) naming the URL and its slices or the invalid slice, and the merged file is left as it is until the configs or slices are fixed.
122
+ - `check` is the identical walk with no writes: it reports what `build` would do under the same flags and exits the same way, and also fails (exit `1`) whenever a build would write anything — a stale or missing document is fixed by running `schemastore build` and committing the result. A catalog slice left behind after the config's last `catalog` block was removed is reported `orphaned` and fails `check` the same way, but `build` never deletes it — and the merged catalog keeps advertising its entries until it is gone: delete the file by hand, or restore a `catalog` block; a merged `catalog.json` with no slice left is orphaned the same way. So is a document left behind under an old derived name — an `appendVersion` flip or a `layout` change moved its path, and nothing claims the old file any more: both commands probe the sibling shapes (`<name>.json`, `<name>-<v>.json`, `<v>/<name>.json`, `<v>/<name>-<v>.json`) of every label the config still declares and report each one that exists as an orphaned document, failed by `check`, never deleted by `build`. Nothing else in `outputDir` is looked at, so sharing it with another config, a deploy folder, or the repository root is safe (unless two configs derive the same schema name and version under different layouts into it); a `name` change or a dropped label leaves a file the command cannot know about — delete those by hand. A catalog URL or entry name advertised more than once across the slices, or a slice that cannot be read or is not a catalog entry array (an undeclared key included), blocks the merged catalog: both commands fail (exit `1`) naming the URL or name and its slices or the invalid slice, and the merged file is left as it is until the configs or slices are fixed.
123
123
  - `validate` answers the question the publication story exists for: does THIS payload conform to the published document it names? The reference is the `--schema` flag or the payload's own `$schema`, resolved file-first and then against every identity a config schema derives (a target `$id`, a frozen version's `$id`/`url`, the catalog `url`) — CI validates an action's output against the committed document with no third-party tool and no network fetch. The payload's `$schema` self-reference is the pointer naming the document: it is stripped before validating only when the resolved document does not declare `$schema` as a root property, since a generated document's `additionalProperties: false` would otherwise reject the very self-reference that names it. A document that does declare `$schema` — the `HostedSchema` pattern above, where the source struct carries `$schema: Schema.Literal(...)` and the generated document requires and const-constrains the key — validates the payload verbatim. A non-conforming payload fails (exit `1`) with one finding per problem, each carrying the JSON pointer into the instance and the keyword; `--format=json` writes one report document to stdout and moves the human lines to stderr.
124
124
  - `--drift` and `--on-drift` override the config for one run; `--force` is sugar for `--drift=allow` (combined with a different explicit `--drift` it is a usage error).
125
125
  - `--format=json` emits one JSON document on stdout (per-schema outcome and effective tolerance, the catalog slice and merged-catalog outcomes, the `orphaned` document paths when any, `drift: { onDrift, policy? }`); human text moves to stderr. When `GITHUB_STEP_SUMMARY` is set, both commands append a markdown table.
@@ -157,7 +157,7 @@ Findings come back as values — `ValidationFinding` pointers into the document
157
157
  | code | meaning |
158
158
  | ---- | -------------------------------------------------------------------------- |
159
159
  | 0 | success, including drift under `onDrift: warn` |
160
- | 1 | drift under `onDrift: error` (one line per drifting schema: `$id`, change, current and next version), a gate failure, a missing or mis-identified frozen version, a merged catalog blocked by a URL two slices advertise or an invalid slice, — for `check` — anything `build` would write or an output nothing claims (an orphaned catalog slice or merged catalog, or an orphaned document at a sibling shape of a derived path), or — for `validate` — a payload that does not conform to the resolved document (one finding per problem, pointer and keyword each) |
160
+ | 1 | drift under `onDrift: error` (one line per drifting schema: `$id`, change, current and next version), a gate failure, a missing or mis-identified frozen version, a merged catalog blocked by a URL or name advertised twice or an invalid slice, — for `check` — anything `build` would write or an output nothing claims (an orphaned catalog slice or merged catalog, or an orphaned document at a sibling shape of a derived path), or — for `validate` — a payload that does not conform to the resolved document (one finding per problem, pointer and keyword each) |
161
161
  | 2 | config not found, failed to load, failed `defineConfig` validation, a `catalogDir` that is a file or cannot be listed (checked before anything is written), or — for `validate` — a payload that cannot be read or parsed, or a `--schema`/`$schema` reference that is neither an existing file nor an identity any config schema derives, or names a document that cannot be read or parsed |
162
162
  | 3 | infrastructure failure (for `validate`, an engine mechanism failure — a document the instance engine cannot compile — included) |
163
163
  | 64 | usage error (for `validate`, a payload with no `$schema` and no `--schema` given, included) |
package/Report.js CHANGED
@@ -29,6 +29,7 @@ const sliceLine = (slice) => {
29
29
  default: return slice.outcome;
30
30
  }
31
31
  };
32
+ const conflictSubject = (conflict) => conflict.kind === "url" ? `url ${conflict.url}` : `name ${conflict.name}`;
32
33
  const mergedLines = (merged) => {
33
34
  const counts = `(${merged.entries} entries from ${merged.slices.length} slice(s))`;
34
35
  switch (merged.outcome) {
@@ -39,7 +40,7 @@ const mergedLines = (merged) => {
39
40
  case "orphaned": return [`orphaned catalog ${merged.path} (no catalog slice remains — delete it by hand; build never will)`];
40
41
  case "blocked": return [
41
42
  `CATALOG BLOCKED ${merged.path} (not written: fix the slices below)`,
42
- ...merged.conflicts.map((conflict) => ` url ${conflict.url} advertised by ${conflict.slices.join(", ")}`),
43
+ ...merged.conflicts.map((conflict) => ` ${conflictSubject(conflict)} advertised by ${conflict.slices.join(", ")}`),
43
44
  ...merged.invalid.map(({ path, reason }) => ` slice ${path} is invalid: ${reason}`)
44
45
  ];
45
46
  default: return merged.outcome;
@@ -58,10 +59,15 @@ const catalogJson = (catalog) => ({
58
59
  entries: catalog.merged.entries,
59
60
  outcome: catalog.merged.outcome,
60
61
  slices: catalog.merged.slices,
61
- ...catalog.merged.conflicts.length > 0 ? { conflicts: catalog.merged.conflicts.map(({ url, slices }) => ({
62
- url,
63
- slices
64
- })) } : {},
62
+ ...catalog.merged.conflicts.length > 0 ? { conflicts: catalog.merged.conflicts.map((conflict) => conflict.kind === "url" ? {
63
+ kind: conflict.kind,
64
+ url: conflict.url,
65
+ slices: conflict.slices
66
+ } : {
67
+ kind: conflict.kind,
68
+ name: conflict.name,
69
+ slices: conflict.slices
70
+ }) } : {},
65
71
  ...catalog.merged.invalid.length > 0 ? { invalid: catalog.merged.invalid.map(({ path, reason }) => ({
66
72
  path,
67
73
  reason
@@ -200,7 +206,7 @@ var Report = class {
200
206
  String(merged.entries),
201
207
  merged.outcome
202
208
  ])] : []);
203
- if (merged !== void 0 && (merged.conflicts.length > 0 || merged.invalid.length > 0)) lines.push("", tableRow(["catalog problem", "slices"]), tableRow(["---", "---"]), ...merged.conflicts.map((conflict) => tableRow([`url ${conflict.url}`, conflict.slices.join(", ")])), ...merged.invalid.map(({ path, reason }) => tableRow([`invalid: ${reason}`, path])));
209
+ if (merged !== void 0 && (merged.conflicts.length > 0 || merged.invalid.length > 0)) lines.push("", tableRow(["catalog problem", "slices"]), tableRow(["---", "---"]), ...merged.conflicts.map((conflict) => tableRow([conflictSubject(conflict), conflict.slices.join(", ")])), ...merged.invalid.map(({ path, reason }) => tableRow([`invalid: ${reason}`, path])));
204
210
  }
205
211
  if (report.orphaned !== void 0) lines.push("", tableRow(["orphaned document", "claimed by"]), tableRow(["---", "---"]), ...report.orphaned.map((orphan) => tableRow([orphan, "nothing — delete by hand"])));
206
212
  lines.push("");
package/Runner.js CHANGED
@@ -221,18 +221,31 @@ const syncCatalog = Effect.fn("Runner.syncCatalog")(function* (config, names, wr
221
221
  };
222
222
  } else {
223
223
  const claims = /* @__PURE__ */ new Map();
224
+ const nameClaims = /* @__PURE__ */ new Map();
224
225
  const union = [];
225
226
  for (const source of sources) for (const entry of source.entries) {
226
227
  const claimants = claims.get(entry.url);
227
228
  if (claimants === void 0) {
228
229
  claims.set(entry.url, [source.slice]);
229
230
  union.push(entry);
231
+ const nameClaimants = nameClaims.get(entry.name);
232
+ if (nameClaimants === void 0) nameClaims.set(entry.name, [source.slice]);
233
+ else nameClaimants.push(source.slice);
230
234
  } else claimants.push(source.slice);
231
235
  }
232
- const conflicts = [...claims].filter(([, slices]) => slices.length > 1).map(([url, slices]) => ({
233
- url,
236
+ const repeated = (claimed) => [...claimed].filter(([, slices]) => slices.length > 1).sort(([a], [b]) => byCodeUnit(a, b)).map(([value, slices]) => ({
237
+ value,
234
238
  slices: [...slices].sort(byCodeUnit)
235
- })).sort((a, b) => byCodeUnit(a.url, b.url));
239
+ }));
240
+ const conflicts = [...repeated(claims).map(({ value, slices }) => ({
241
+ kind: "url",
242
+ url: value,
243
+ slices
244
+ })), ...repeated(nameClaims).map(({ value, slices }) => ({
245
+ kind: "name",
246
+ name: value,
247
+ slices
248
+ }))];
236
249
  union.sort((a, b) => byCodeUnit(a.url, b.url));
237
250
  const outcome = conflicts.length > 0 || invalid.length > 0 ? "blocked" : yield* syncFile(mergedPath, yield* encodeEntries(union), writing, refused);
238
251
  merged = {
package/cli/execute.js CHANGED
@@ -45,8 +45,8 @@ var GateError = class extends Schema.TaggedError()("GateError", { count: Schema.
45
45
  }
46
46
  };
47
47
  /**
48
- * The merged catalog could not be assembled: a catalog URL is advertised by
49
- * more than one slice, or a slice in `catalogDir` is not a catalog entry
48
+ * The merged catalog could not be assembled: a catalog URL or entry name is
49
+ * advertised more than once across the slices, or a slice in `catalogDir` is not a catalog entry
50
50
  * array. Nothing is merged silently, so the merged catalog was left as it
51
51
  * is. Exit `1` under both `build` and `check`: only an edit to the slices
52
52
  * or the configs clears it.
@@ -55,18 +55,23 @@ var GateError = class extends Schema.TaggedError()("GateError", { count: Schema.
55
55
  */
56
56
  var CatalogMergeError = class extends Schema.TaggedError()("CatalogMergeError", {
57
57
  path: Schema.String,
58
- conflicts: Schema.Array(Schema.Struct({
58
+ conflicts: Schema.Array(Schema.Union([Schema.Struct({
59
+ kind: Schema.Literal("url"),
59
60
  url: Schema.String,
60
61
  slices: Schema.Array(Schema.String)
61
- })),
62
+ }), Schema.Struct({
63
+ kind: Schema.Literal("name"),
64
+ name: Schema.String,
65
+ slices: Schema.Array(Schema.String)
66
+ })])),
62
67
  invalid: Schema.Array(Schema.Struct({
63
68
  path: Schema.String,
64
69
  reason: Schema.String
65
70
  }))
66
71
  }) {
67
72
  get message() {
68
- const lines = [...this.conflicts.map((conflict) => ` url ${conflict.url} is advertised by ${conflict.slices.join(", ")}`), ...this.invalid.map(({ path, reason }) => ` ${path} is invalid: ${reason}`)];
69
- return `The merged catalog ${this.path} was not written.\n${lines.join("\n")}\nGive each catalog URL to exactly one config, and fix or delete every invalid slice.`;
73
+ const lines = [...this.conflicts.map((conflict) => ` ${conflict.kind === "url" ? `url ${conflict.url}` : `name ${conflict.name}`} is advertised by ${conflict.slices.join(", ")}`), ...this.invalid.map(({ path, reason }) => ` ${path} is invalid: ${reason}`)];
74
+ return `The merged catalog ${this.path} was not written.\n${lines.join("\n")}\nGive each catalog URL and name to exactly one config, and fix or delete every invalid slice.`;
70
75
  }
71
76
  };
72
77
  /**
@@ -126,7 +131,7 @@ const emit = Effect.fn("schemastore.emit")(function* (report, format) {
126
131
  * requested format, appends the step summary, and fails typed —
127
132
  * `GateError`, then `DriftError`, then `CatalogMergeError`, then (for
128
133
  * `check` only) `StaleError`, each carrying exit `1` — when the report says
129
- * the run refused to write, the merged catalog was blocked by a URL
134
+ * the run refused to write, the merged catalog was blocked by a URL or name
130
135
  * conflict or an invalid slice, or, under `check`, that a build would
131
136
  * write. `SchemaFile` is built here
132
137
  * over the environment's `FileSystem`; the validator is `deps.validator` or
package/main.js CHANGED
@@ -10,7 +10,7 @@ const render = (error) => CliError.isCliError(error) && error._tag === "ShowHelp
10
10
  const main = () => {
11
11
  const run = program(process.argv.slice(2), {
12
12
  cwd: process.cwd(),
13
- version: "0.19.0"
13
+ version: "0.20.0"
14
14
  }).pipe(Effect.provide(NodeServices.layer), CliRuntime.reportFailures({
15
15
  exitCode: 3,
16
16
  render
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/schemastore-cli",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "private": false,
5
5
  "description": "The schemastore command: build and check SchemaStore-shaped JSON Schema documents from a schemastore.config.ts, with a per-schema published flag and a drift policy.",
6
6
  "keywords": [
@@ -50,7 +50,7 @@
50
50
  "jiti": "^2.6.0"
51
51
  },
52
52
  "peerDependencies": {
53
- "@effected/schemastore": "0.19.0",
53
+ "@effected/schemastore": "0.20.0",
54
54
  "effect": "^4.0.0"
55
55
  },
56
56
  "engines": {