@effected/schemastore-cli 0.12.0 → 0.13.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 +3 -3
- package/Report.js +4 -0
- package/Runner.js +37 -0
- package/cli/commands/check.js +5 -2
- package/cli/execute.js +18 -6
- package/main.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -114,9 +114,9 @@ schemastore check [config] [--drift=strict|semantic|allow] [--on-drift=error|war
|
|
|
114
114
|
|
|
115
115
|
- Before anything is generated, every frozen label is verified — present on disk, and self-identified by the derived `$id`.
|
|
116
116
|
- `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 catalog file when any entry declares one.
|
|
117
|
-
- `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.json` left behind after the last `catalog` block was removed is reported `orphaned` and fails `check` the same way, but `build` never deletes it: delete the file by hand, or restore a `catalog` block.
|
|
117
|
+
- `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.json` left behind after the last `catalog` block was removed is reported `orphaned` and fails `check` the same way, but `build` never deletes it: delete the file by hand, or restore a `catalog` block. 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.
|
|
118
118
|
- `--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).
|
|
119
|
-
- `--format=json` emits one JSON document on stdout (per-schema outcome and effective tolerance, the catalog outcome, `drift: { onDrift, policy? }`); human text moves to stderr. When `GITHUB_STEP_SUMMARY` is set, both commands append a markdown table.
|
|
119
|
+
- `--format=json` emits one JSON document on stdout (per-schema outcome and effective tolerance, the catalog outcome, 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.
|
|
120
120
|
|
|
121
121
|
## The engine, as a library export
|
|
122
122
|
|
|
@@ -140,7 +140,7 @@ Findings come back as values; the error channel carries `SchemaValidatorError` o
|
|
|
140
140
|
| code | meaning |
|
|
141
141
|
| ---- | -------------------------------------------------------------------------- |
|
|
142
142
|
| 0 | success, including drift under `onDrift: warn` |
|
|
143
|
-
| 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, or — for `check` — anything `build` would write |
|
|
143
|
+
| 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, or — for `check` — anything `build` would write or an output nothing claims (an orphaned catalog file, or an orphaned document at a sibling shape of a derived path) |
|
|
144
144
|
| 2 | config not found, failed to load, or failed `defineConfig` validation |
|
|
145
145
|
| 3 | infrastructure failure |
|
|
146
146
|
| 64 | usage error |
|
package/Report.js
CHANGED
|
@@ -27,6 +27,7 @@ const catalogLine = (entry) => {
|
|
|
27
27
|
default: return entry.outcome;
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
+
const orphanedLine = (orphan) => `orphaned document ${orphan} (no target, frozen version, or catalog entry claims it — delete it by hand; build never will)`;
|
|
30
31
|
const driftClause = (report) => `drift ${report.policy !== void 0 ? `${report.policy} (flag)` : "per schema (config)"}, on-drift ${report.onDrift}`;
|
|
31
32
|
const summaryLine = (report) => {
|
|
32
33
|
const written = report.schemas.filter((schema) => schema.outcome === "written").length;
|
|
@@ -58,6 +59,7 @@ var Report = class {
|
|
|
58
59
|
const lines = [];
|
|
59
60
|
for (const schema of report.schemas) lines.push(...schemaLines(schema, report));
|
|
60
61
|
if (report.catalog !== void 0) lines.push(catalogLine(report.catalog));
|
|
62
|
+
for (const orphan of report.orphaned ?? []) lines.push(orphanedLine(orphan));
|
|
61
63
|
lines.push(summaryLine(report));
|
|
62
64
|
return lines;
|
|
63
65
|
}
|
|
@@ -104,6 +106,7 @@ var Report = class {
|
|
|
104
106
|
entries: report.catalog.entries,
|
|
105
107
|
outcome: report.catalog.outcome
|
|
106
108
|
} } : {},
|
|
109
|
+
...report.orphaned !== void 0 ? { orphaned: report.orphaned } : {},
|
|
107
110
|
drifted: report.drifted,
|
|
108
111
|
gateFailed: report.gateFailed,
|
|
109
112
|
wrote: report.wrote
|
|
@@ -149,6 +152,7 @@ var Report = class {
|
|
|
149
152
|
String(report.catalog.entries),
|
|
150
153
|
report.catalog.outcome
|
|
151
154
|
]));
|
|
155
|
+
if (report.orphaned !== void 0) lines.push("", tableRow(["orphaned document", "claimed by"]), tableRow(["---", "---"]), ...report.orphaned.map((orphan) => tableRow([orphan, "nothing — delete by hand"])));
|
|
152
156
|
lines.push("");
|
|
153
157
|
if (report.gateFailed) {
|
|
154
158
|
const failed = report.schemas.filter((schema) => schema.outcome === "gate-failed").length;
|
package/Runner.js
CHANGED
|
@@ -127,6 +127,22 @@ const parsesEqual = (existing, text) => {
|
|
|
127
127
|
* `orphaned` (stale under `check`) and left in place, and the report omits
|
|
128
128
|
* `catalog` entirely only when there is no such file either.
|
|
129
129
|
*
|
|
130
|
+
* **A moved path leaves an orphan the derivation cannot see**: an
|
|
131
|
+
* `appendVersion` flip or a `layout` change renames a document's derived
|
|
132
|
+
* path, and the previously written file stays on disk under the old name —
|
|
133
|
+
* for a `published` label, its advertised URL keeps serving a stale
|
|
134
|
+
* document with no report. The derivation has exactly four shapes for a
|
|
135
|
+
* name and label (`<name>.json`, `<name>-<v>.json`, `<v>/<name>.json`,
|
|
136
|
+
* `<v>/<name>-<v>.json`), so both modes probe the sibling shapes of every
|
|
137
|
+
* label the config still knows and report each one that exists as a FILE
|
|
138
|
+
* and that no target, frozen version, or `catalogPath` claims in
|
|
139
|
+
* {@link RunReport.orphaned}. Nothing else on disk is looked at: an
|
|
140
|
+
* `outputDir` shared with another config, a deploy folder, or the
|
|
141
|
+
* repository root holds documents this config cannot tell from its own
|
|
142
|
+
* leftovers, so they are never reported. A `name` change is therefore not
|
|
143
|
+
* caught either — the old name is unknowable. Orphans are reported, never
|
|
144
|
+
* deleted: the CLI may not have written them.
|
|
145
|
+
*
|
|
130
146
|
* @public
|
|
131
147
|
*/
|
|
132
148
|
var Runner = class {
|
|
@@ -233,6 +249,26 @@ var Runner = class {
|
|
|
233
249
|
outcome
|
|
234
250
|
};
|
|
235
251
|
}
|
|
252
|
+
const claimed = /* @__PURE__ */ new Set([path.normalize(config.catalogPath)]);
|
|
253
|
+
for (const schema of config.schemas) {
|
|
254
|
+
claimed.add(path.normalize(schema.target.path));
|
|
255
|
+
for (const frozen of schema.frozen) claimed.add(path.normalize(frozen.path));
|
|
256
|
+
}
|
|
257
|
+
const orphaned = [];
|
|
258
|
+
for (const schema of config.schemas) {
|
|
259
|
+
const labels = [schema.target.version, ...schema.frozen.map((frozen) => frozen.version)];
|
|
260
|
+
const shapes = [SchemaVersioning.fileName(schema.name), ...labels.flatMap((version) => version === void 0 ? [] : [
|
|
261
|
+
SchemaVersioning.fileName(schema.name, version, "flat"),
|
|
262
|
+
SchemaVersioning.fileName(schema.name, version, "versioned"),
|
|
263
|
+
SchemaVersioning.fileName(schema.name, version, "versioned", false)
|
|
264
|
+
])];
|
|
265
|
+
for (const shape of shapes) {
|
|
266
|
+
const file = path.normalize(path.join(config.outputDir, shape));
|
|
267
|
+
if (claimed.has(file)) continue;
|
|
268
|
+
const info = yield* orNone(fs.stat(file));
|
|
269
|
+
if (Option.isSome(info) && info.value.type === "File") orphaned.push(file);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
236
272
|
return {
|
|
237
273
|
mode: options.mode,
|
|
238
274
|
configPath: options.configPath,
|
|
@@ -240,6 +276,7 @@ var Runner = class {
|
|
|
240
276
|
...options.policy !== void 0 ? { policy: options.policy } : {},
|
|
241
277
|
schemas,
|
|
242
278
|
...catalog !== void 0 ? { catalog } : {},
|
|
279
|
+
...orphaned.length > 0 ? { orphaned } : {},
|
|
243
280
|
drifted,
|
|
244
281
|
gateFailed,
|
|
245
282
|
wrote: schemas.some((s) => s.outcome === "written") || catalog?.outcome === "written"
|
package/cli/commands/check.js
CHANGED
|
@@ -6,11 +6,14 @@ import { Command } from "effect/unstable/cli";
|
|
|
6
6
|
/**
|
|
7
7
|
* `schemastore check`: the same walk as `build`, reported and never written.
|
|
8
8
|
* The CI drift gate: it also fails when the committed documents are stale,
|
|
9
|
-
* i.e. whenever `build` would write anything
|
|
9
|
+
* i.e. whenever `build` would write anything — and on the outputs nothing
|
|
10
|
+
* claims (an orphaned catalog file, or a document left behind at a sibling
|
|
11
|
+
* shape of a derived path), which `build` never deletes: remove them by
|
|
12
|
+
* hand.
|
|
10
13
|
*
|
|
11
14
|
* @public
|
|
12
15
|
*/
|
|
13
|
-
const makeCheckCommand = (deps) => Command.make("check", commandFlags, (input) => execute("check", input, deps)).pipe(Command.withDescription("Report what build would do, fail when it would write anything or refuse to, and write nothing"));
|
|
16
|
+
const makeCheckCommand = (deps) => Command.make("check", commandFlags, (input) => execute("check", input, deps)).pipe(Command.withDescription("Report what build would do, fail when it would write anything or refuse to, or when a catalog file or document is orphaned, and write nothing"));
|
|
14
17
|
|
|
15
18
|
//#endregion
|
|
16
19
|
export { makeCheckCommand };
|
package/cli/execute.js
CHANGED
|
@@ -46,14 +46,22 @@ var GateError = class extends Schema.TaggedError()("GateError", { count: Schema.
|
|
|
46
46
|
};
|
|
47
47
|
/**
|
|
48
48
|
* `check` found committed documents that differ from what the config
|
|
49
|
-
* generates (or are missing), so a `build` would write
|
|
50
|
-
*
|
|
49
|
+
* generates (or are missing), so a `build` would write — or outputs
|
|
50
|
+
* nothing claims (an orphaned catalog file, an orphaned document), which
|
|
51
|
+
* `build` never deletes. `check` is the CI drift gate, so a stale tree
|
|
52
|
+
* fails it. Exit `1`. `count` is every finding; `orphaned` the part of it
|
|
53
|
+
* a build cannot clear, so the message names both remedies.
|
|
51
54
|
*
|
|
52
55
|
* @public
|
|
53
56
|
*/
|
|
54
|
-
var StaleError = class extends Schema.TaggedError()("StaleError", {
|
|
57
|
+
var StaleError = class extends Schema.TaggedError()("StaleError", {
|
|
58
|
+
count: Schema.Number,
|
|
59
|
+
orphaned: Schema.optionalKey(Schema.Number)
|
|
60
|
+
}) {
|
|
55
61
|
get message() {
|
|
56
|
-
|
|
62
|
+
const orphaned = this.orphaned ?? 0;
|
|
63
|
+
const stale = this.count - orphaned;
|
|
64
|
+
return [...stale > 0 ? [`${stale} document(s) are stale; run \`schemastore build\` and commit the result.`] : [], ...orphaned > 0 ? [`${orphaned} orphaned output(s) must be deleted by hand; build never will.`] : []].join(" ");
|
|
57
65
|
}
|
|
58
66
|
};
|
|
59
67
|
/**
|
|
@@ -132,8 +140,12 @@ const execute = Effect.fn("schemastore.execute")(function* (mode, input, deps) {
|
|
|
132
140
|
return yield* Effect.fail(CliRuntime.reported(new DriftError({ drifted }), 1));
|
|
133
141
|
}
|
|
134
142
|
if (mode === "check") {
|
|
135
|
-
const
|
|
136
|
-
|
|
143
|
+
const orphaned = (report.catalog?.outcome === "orphaned" ? 1 : 0) + (report.orphaned?.length ?? 0);
|
|
144
|
+
const count = report.schemas.filter((schema) => schema.outcome === "would-write").length + (report.catalog?.outcome === "would-write" ? 1 : 0) + orphaned;
|
|
145
|
+
if (count > 0) return yield* Effect.fail(CliRuntime.reported(new StaleError({
|
|
146
|
+
count,
|
|
147
|
+
...orphaned > 0 ? { orphaned } : {}
|
|
148
|
+
}), 1));
|
|
137
149
|
}
|
|
138
150
|
});
|
|
139
151
|
|
package/main.js
CHANGED
|
@@ -15,7 +15,7 @@ const render = (error) => CliError.isCliError(error) && error._tag === "ShowHelp
|
|
|
15
15
|
const main = () => {
|
|
16
16
|
const run = program(process.argv.slice(2), {
|
|
17
17
|
cwd: process.cwd(),
|
|
18
|
-
version: "0.
|
|
18
|
+
version: "0.13.0"
|
|
19
19
|
}).pipe(Effect.provide(NodeServices.layer), CliRuntime.reportFailures({
|
|
20
20
|
exitCode: 3,
|
|
21
21
|
render
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/schemastore-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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": [
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"jiti": "^2.6.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@effected/schemastore": "0.
|
|
50
|
+
"@effected/schemastore": "0.13.0",
|
|
51
51
|
"effect": "4.0.0-rc.115"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|