@nekostack/cli 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 (80) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +202 -0
  3. package/README.md +89 -0
  4. package/bin/neko +6 -0
  5. package/dist/.build.tsbuildinfo +1 -0
  6. package/dist/.tsbuildinfo +1 -0
  7. package/dist/cli.d.ts +60 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +355 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/commands/init.d.ts +8 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +11 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/commands/schema/check.d.ts +55 -0
  16. package/dist/commands/schema/check.d.ts.map +1 -0
  17. package/dist/commands/schema/check.js +197 -0
  18. package/dist/commands/schema/check.js.map +1 -0
  19. package/dist/commands/schema/diff.d.ts +48 -0
  20. package/dist/commands/schema/diff.d.ts.map +1 -0
  21. package/dist/commands/schema/diff.js +245 -0
  22. package/dist/commands/schema/diff.js.map +1 -0
  23. package/dist/commands/schema/generate.d.ts +59 -0
  24. package/dist/commands/schema/generate.d.ts.map +1 -0
  25. package/dist/commands/schema/generate.js +135 -0
  26. package/dist/commands/schema/generate.js.map +1 -0
  27. package/dist/commands/schema/list.d.ts +50 -0
  28. package/dist/commands/schema/list.d.ts.map +1 -0
  29. package/dist/commands/schema/list.js +115 -0
  30. package/dist/commands/schema/list.js.map +1 -0
  31. package/dist/commands/schema/migrate/list.d.ts +65 -0
  32. package/dist/commands/schema/migrate/list.d.ts.map +1 -0
  33. package/dist/commands/schema/migrate/list.js +148 -0
  34. package/dist/commands/schema/migrate/list.js.map +1 -0
  35. package/dist/commands/schema/migrate/plan.d.ts +79 -0
  36. package/dist/commands/schema/migrate/plan.d.ts.map +1 -0
  37. package/dist/commands/schema/migrate/plan.js +255 -0
  38. package/dist/commands/schema/migrate/plan.js.map +1 -0
  39. package/dist/commands/schema/migrate/stub.d.ts +59 -0
  40. package/dist/commands/schema/migrate/stub.d.ts.map +1 -0
  41. package/dist/commands/schema/migrate/stub.js +195 -0
  42. package/dist/commands/schema/migrate/stub.js.map +1 -0
  43. package/dist/commands/schema/migrate/verify.d.ts +59 -0
  44. package/dist/commands/schema/migrate/verify.d.ts.map +1 -0
  45. package/dist/commands/schema/migrate/verify.js +268 -0
  46. package/dist/commands/schema/migrate/verify.js.map +1 -0
  47. package/dist/exit-codes.d.ts +47 -0
  48. package/dist/exit-codes.d.ts.map +1 -0
  49. package/dist/exit-codes.js +46 -0
  50. package/dist/exit-codes.js.map +1 -0
  51. package/dist/formatters/json.d.ts +25 -0
  52. package/dist/formatters/json.d.ts.map +1 -0
  53. package/dist/formatters/json.js +27 -0
  54. package/dist/formatters/json.js.map +1 -0
  55. package/dist/formatters/pretty.d.ts +131 -0
  56. package/dist/formatters/pretty.d.ts.map +1 -0
  57. package/dist/formatters/pretty.js +229 -0
  58. package/dist/formatters/pretty.js.map +1 -0
  59. package/dist/index.d.ts +2 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +2 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/loaders/read-artifacts.d.ts +55 -0
  64. package/dist/loaders/read-artifacts.d.ts.map +1 -0
  65. package/dist/loaders/read-artifacts.js +99 -0
  66. package/dist/loaders/read-artifacts.js.map +1 -0
  67. package/dist/loaders/read-migrations.d.ts +70 -0
  68. package/dist/loaders/read-migrations.d.ts.map +1 -0
  69. package/dist/loaders/read-migrations.js +206 -0
  70. package/dist/loaders/read-migrations.js.map +1 -0
  71. package/dist/loaders/tsx-loader.d.ts +116 -0
  72. package/dist/loaders/tsx-loader.d.ts.map +1 -0
  73. package/dist/loaders/tsx-loader.js +250 -0
  74. package/dist/loaders/tsx-loader.js.map +1 -0
  75. package/dist/loaders/walk-workspace.d.ts +62 -0
  76. package/dist/loaders/walk-workspace.d.ts.map +1 -0
  77. package/dist/loaders/walk-workspace.js +133 -0
  78. package/dist/loaders/walk-workspace.js.map +1 -0
  79. package/docs/SCOPE.md +42 -0
  80. package/package.json +39 -0
@@ -0,0 +1,255 @@
1
+ /**
2
+ * `neko schema migrate plan` command implementation (v0.8 Step 21).
3
+ *
4
+ * The diff-aware planner CLI surface. Mirrors `runMigrateList`'s
5
+ * pure / writer-injected discipline (Master plan Decision #1).
6
+ *
7
+ * Flow:
8
+ *
9
+ * 1. `walkWorkspace({ root })` — load every `*.schema.{ts,js}`.
10
+ * Any per-file load failure → IO_ERROR.
11
+ * 2. `buildRegistry(walk.entries)` — index the schemas.
12
+ * `duplicate_schema_id` → LOGICAL_FAILURE; any `integrity_error`
13
+ * → INTEGRITY_ERROR.
14
+ * 3. `readMigrations({ root })` — load every `*.migration.ts`.
15
+ * Any per-file load failure → IO_ERROR.
16
+ * 4. `buildMigrationRegistry(migrationWalk.entries)` — index the
17
+ * migrations and parse each one's provenance.
18
+ * `duplicate_migration` → LOGICAL_FAILURE; any `integrity_error`
19
+ * → INTEGRITY_ERROR.
20
+ * 5. `planMigrationHandler({ schemaRegistry, migrationRegistry,
21
+ * schemaId, fromVersion, toVersion })`. Failure-issue mapping:
22
+ * - `migration_missing_endpoint` → LOGICAL_FAILURE
23
+ * - `migration_not_found` → LOGICAL_FAILURE
24
+ * - `migration_chain_broken` → LOGICAL_FAILURE
25
+ * - `migration_ambiguous_chain` → LOGICAL_FAILURE
26
+ * 6. Format and write:
27
+ * - `--json` → one-line `{ plan: {...} }` to stdout
28
+ * - default → pretty paragraph distinguishing the four success
29
+ * shapes (no-change / over_specified /
30
+ * additive_no_migration / chain).
31
+ * 7. Return SUCCESS.
32
+ *
33
+ * **Pure.** No `process.exit`, no `console.*`, no direct
34
+ * `process.stdout` / `process.stderr` writes. Writers are injected.
35
+ *
36
+ * **`migration.transform` is NEVER invoked here.** The planner is
37
+ * structural — `diffNodes` + DFS chain enumeration. The v0.8
38
+ * INVARIANTS ("no apply, no transform execution") stay in force.
39
+ * Static-scan asserted by [`../../../../tests/commands/schema-migrate-plan.test.ts`](../../../../tests/commands/schema-migrate-plan.test.ts).
40
+ *
41
+ * **JSON output shape is locked.** The `MigrationEntry.migration`
42
+ * field — the live `AnyMigration` carrying the `transform` closure —
43
+ * is NEVER serialized (neither inside `chain` rows nor inside
44
+ * `notes[].migration` projections):
45
+ *
46
+ * {
47
+ * "plan": {
48
+ * "schemaId": "com.x.Tenant",
49
+ * "fromVersion": "1.0.0",
50
+ * "toVersion": "2.0.0",
51
+ * "worstSeverity": "breaking" | "additive" | "cosmetic" | null,
52
+ * "versionPath": ["1.0.0", "1.5.0", "2.0.0"],
53
+ * "notes": [
54
+ * { "kind": "over_specified", "migration": { schemaId, fromVersion, toVersion,
55
+ * sourcePath, fromIrHash, toIrHash, fromSourceHash, toSourceHash } },
56
+ * { "kind": "additive_no_migration", "worstSeverity": "additive" }
57
+ * ],
58
+ * "chain": [
59
+ * { "schemaId": "...", "fromVersion": "1.0.0", "toVersion": "1.5.0",
60
+ * "sourcePath": "...", "fromIrHash": "sha256:...", "toIrHash": "sha256:...",
61
+ * "fromSourceHash": "sha256:...", "toSourceHash": "sha256:..." },
62
+ * …
63
+ * ]
64
+ * }
65
+ * }
66
+ */
67
+ import { buildMigrationRegistry, buildRegistry, planMigrationHandler, } from "@nekostack/schema/cli";
68
+ import { EXIT_CODES } from "../../../exit-codes.js";
69
+ import { formatJson } from "../../../formatters/json.js";
70
+ import { formatIssuesPretty, formatLoadFailuresPretty, } from "../../../formatters/pretty.js";
71
+ import { readMigrations } from "../../../loaders/read-migrations.js";
72
+ import { walkWorkspace } from "../../../loaders/walk-workspace.js";
73
+ function projectMigrationEntry(e) {
74
+ return {
75
+ schemaId: e.schemaId,
76
+ fromVersion: e.fromVersion,
77
+ toVersion: e.toVersion,
78
+ sourcePath: e.sourcePath,
79
+ fromIrHash: e.fromIrHash,
80
+ toIrHash: e.toIrHash,
81
+ fromSourceHash: e.fromSourceHash,
82
+ toSourceHash: e.toSourceHash,
83
+ };
84
+ }
85
+ function projectPlanNote(note) {
86
+ if (note.kind === "over_specified") {
87
+ return { kind: "over_specified", migration: projectMigrationEntry(note.migration) };
88
+ }
89
+ return { kind: "additive_no_migration", worstSeverity: note.worstSeverity };
90
+ }
91
+ function projectPlan(plan, opts) {
92
+ return {
93
+ schemaId: plan.schemaId,
94
+ fromVersion: opts.fromVersion,
95
+ toVersion: opts.toVersion,
96
+ worstSeverity: plan.worstSeverity,
97
+ versionPath: plan.versionPath,
98
+ notes: plan.notes.map(projectPlanNote),
99
+ chain: plan.chain.map(projectMigrationEntry),
100
+ };
101
+ }
102
+ // =============================================================================
103
+ // Load-failure projection (strip `cause`)
104
+ // =============================================================================
105
+ function toSerializableLoadFailure(f) {
106
+ return { path: f.path, reason: f.reason, message: f.message };
107
+ }
108
+ // =============================================================================
109
+ // Exit-code mapping for issue lists
110
+ // =============================================================================
111
+ /**
112
+ * `buildRegistry` / `buildMigrationRegistry` failure → exit code.
113
+ * Any `integrity_error` → INTEGRITY_ERROR; otherwise LOGICAL_FAILURE
114
+ * (covers `duplicate_schema_id` / `duplicate_migration`). Same rule
115
+ * as the v0.7 `runCheck` handler-failure mapping and Step 20's
116
+ * `runMigrateList`.
117
+ */
118
+ function pickRegistryFailureExitCode(issues) {
119
+ for (const i of issues) {
120
+ if (i.code === "integrity_error")
121
+ return EXIT_CODES.INTEGRITY_ERROR;
122
+ }
123
+ return EXIT_CODES.LOGICAL_FAILURE;
124
+ }
125
+ /**
126
+ * `planMigrationHandler` failure → exit code. All four locked
127
+ * failure codes (`migration_missing_endpoint` / `migration_not_found`
128
+ * / `migration_chain_broken` / `migration_ambiguous_chain`) map to
129
+ * `LOGICAL_FAILURE`. Defensive fallback also returns LOGICAL_FAILURE
130
+ * — the v0.8 planner does not emit `integrity_error`.
131
+ */
132
+ function pickPlannerFailureExitCode(_issues) {
133
+ return EXIT_CODES.LOGICAL_FAILURE;
134
+ }
135
+ // =============================================================================
136
+ // Pretty formatter
137
+ // =============================================================================
138
+ function formatMigrationPlanPretty(plan, opts) {
139
+ const header = `Plan: ${plan.schemaId} ${opts.fromVersion} → ${opts.toVersion}` +
140
+ ` (worstSeverity: ${plan.worstSeverity ?? "none"})`;
141
+ const lines = [header];
142
+ if (plan.chain.length === 0) {
143
+ if (plan.notes.length === 0) {
144
+ lines.push(" No migration needed — diff has no breaking or additive changes.");
145
+ }
146
+ else {
147
+ for (const note of plan.notes) {
148
+ if (note.kind === "over_specified") {
149
+ lines.push(` No migration needed — diff is cosmetic/none, but a migration is registered at \`${note.migration.sourcePath}\`. (over_specified)`);
150
+ }
151
+ else {
152
+ lines.push(` No migration registered — diff is additive (\`${note.worstSeverity}\`); a migration is optional. (additive_no_migration)`);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ else {
158
+ lines.push(` Chain (${plan.chain.length} hop${plan.chain.length === 1 ? "" : "s"}):`);
159
+ for (let i = 0; i < plan.chain.length; i++) {
160
+ const hop = plan.chain[i];
161
+ lines.push(` ${i + 1}. ${hop.fromVersion} → ${hop.toVersion} ${hop.sourcePath}`);
162
+ }
163
+ lines.push(` Version path: ${plan.versionPath.join(" → ")}`);
164
+ }
165
+ return lines.join("\n") + "\n";
166
+ }
167
+ // =============================================================================
168
+ // Public entry
169
+ // =============================================================================
170
+ export async function runMigratePlan(opts) {
171
+ // 1. Schema walk + load failures → IO_ERROR.
172
+ const walk = await walkWorkspace({ root: opts.root });
173
+ if (walk.failures.length > 0) {
174
+ if (opts.json) {
175
+ opts.stdout(formatJson({
176
+ failures: walk.failures.map(toSerializableLoadFailure),
177
+ }));
178
+ }
179
+ else {
180
+ opts.stderr(formatLoadFailuresPretty(walk.failures));
181
+ }
182
+ return EXIT_CODES.IO_ERROR;
183
+ }
184
+ // 2. Build the schema registry.
185
+ const reg = buildRegistry(walk.entries);
186
+ if (!reg.success) {
187
+ if (opts.json) {
188
+ opts.stdout(formatJson({ issues: reg.issues }));
189
+ }
190
+ else {
191
+ opts.stderr(formatIssuesPretty(reg.issues));
192
+ }
193
+ return pickRegistryFailureExitCode(reg.issues);
194
+ }
195
+ // 3. Migration walk + load failures → IO_ERROR.
196
+ const migWalk = await readMigrations({ root: opts.root });
197
+ if (migWalk.failures.length > 0) {
198
+ if (opts.json) {
199
+ opts.stdout(formatJson({
200
+ failures: migWalk.failures.map(toSerializableLoadFailure),
201
+ }));
202
+ }
203
+ else {
204
+ opts.stderr(formatLoadFailuresPretty(migWalk.failures, {
205
+ noun: { singular: "migration file", plural: "migration files" },
206
+ }));
207
+ }
208
+ return EXIT_CODES.IO_ERROR;
209
+ }
210
+ // 4. Build the migration registry.
211
+ const migReg = buildMigrationRegistry(migWalk.entries);
212
+ if (!migReg.success) {
213
+ if (opts.json) {
214
+ opts.stdout(formatJson({ issues: migReg.issues }));
215
+ }
216
+ else {
217
+ opts.stderr(formatIssuesPretty(migReg.issues));
218
+ }
219
+ return pickRegistryFailureExitCode(migReg.issues);
220
+ }
221
+ // 5. Dispatch to the planner.
222
+ const result = planMigrationHandler({
223
+ schemaRegistry: reg.data,
224
+ migrationRegistry: migReg.data,
225
+ schemaId: opts.schemaId,
226
+ fromVersion: opts.fromVersion,
227
+ toVersion: opts.toVersion,
228
+ });
229
+ if (!result.success) {
230
+ if (opts.json) {
231
+ opts.stdout(formatJson({ issues: result.issues }));
232
+ }
233
+ else {
234
+ opts.stderr(formatIssuesPretty(result.issues));
235
+ }
236
+ return pickPlannerFailureExitCode(result.issues);
237
+ }
238
+ // 6. Success.
239
+ if (opts.json) {
240
+ opts.stdout(formatJson({
241
+ plan: projectPlan(result.data, {
242
+ fromVersion: opts.fromVersion,
243
+ toVersion: opts.toVersion,
244
+ }),
245
+ }));
246
+ }
247
+ else {
248
+ opts.stdout(formatMigrationPlanPretty(result.data, {
249
+ fromVersion: opts.fromVersion,
250
+ toVersion: opts.toVersion,
251
+ }));
252
+ }
253
+ return EXIT_CODES.SUCCESS;
254
+ }
255
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../src/commands/schema/migrate/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `neko schema migrate stub` command implementation (v0.8 Step 23).
3
+ *
4
+ * The only v0.8 migrate verb that writes a file. Mirrors v0.7's
5
+ * `runGenerate` shape: the schema-side handler is pure and returns
6
+ * `{ suggestedPath, content }`; this command is responsible for the
7
+ * actual filesystem persistence. Master plan Decision #1 keeps the
8
+ * filesystem on the CLI side.
9
+ *
10
+ * Flow:
11
+ *
12
+ * 1. `walkWorkspace({ root })` — load every `*.schema.{ts,js}`.
13
+ * Any per-file load failure → IO_ERROR.
14
+ * 2. `buildRegistry(walk.entries)` — index the schemas.
15
+ * `duplicate_schema_id` → LOGICAL_FAILURE.
16
+ * 3. `stubMigrationHandler({ schemaRegistry, schemaId, fromVersion,
17
+ * toVersion })` — generate the stub payload. Failure modes
18
+ * surface as `migration_missing_endpoint` →
19
+ * LOGICAL_FAILURE.
20
+ * 4. **Refuse to overwrite.** Unlike `runGenerate` (which clobbers
21
+ * generated artifacts by default), `runMigrateStub` writes a
22
+ * file that contains hand-authored code. If the destination
23
+ * already exists, the command returns LOGICAL_FAILURE without
24
+ * modifying it. Decision #5 of the v0.8 phase plan locks this
25
+ * contract.
26
+ * 5. `mkdir -p` the destination's parent + `writeFile` the
27
+ * stub content. Any thrown error (EACCES, ENOSPC, etc.) →
28
+ * IO_ERROR.
29
+ * 6. Format and write:
30
+ * - `--json` (success) → `{ stub: { schemaId, fromVersion,
31
+ * toVersion, suggestedPath } }`. The `content` field is
32
+ * deliberately omitted — machine consumers don't need the
33
+ * full source body, and the file is already on disk.
34
+ * - pretty → `Wrote stub: <suggestedPath>`.
35
+ *
36
+ * **Pure helpers + bounded I/O.** `mkdir` and `writeFile` (and a
37
+ * `stat` for the overwrite check) are the only `node:fs` calls.
38
+ * `process.exit`, `console.*`, and direct stdout/stderr writes are
39
+ * forbidden — static-scan asserted by
40
+ * [`../../../../tests/commands/schema-migrate-stub.test.ts`](../../../../tests/commands/schema-migrate-stub.test.ts).
41
+ *
42
+ * **No `migration.transform`.** The stub command doesn't even own
43
+ * a registry of authored migrations — only the schema registry. The
44
+ * `.transform(` pattern still appears as a forbidden token in the
45
+ * static scan so the v0.8 boundary stays enforced everywhere.
46
+ */
47
+ import { type ExitCode } from "../../../exit-codes.js";
48
+ export interface RunMigrateStubOptions {
49
+ readonly root: string;
50
+ readonly schemaId: string;
51
+ readonly fromVersion: string;
52
+ readonly toVersion: string;
53
+ readonly json: boolean;
54
+ readonly quiet: boolean;
55
+ readonly stdout: (s: string) => void;
56
+ readonly stderr: (s: string) => void;
57
+ }
58
+ export declare function runMigrateStub(opts: RunMigrateStubOptions): Promise<ExitCode>;
59
+ //# sourceMappingURL=stub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stub.d.ts","sourceRoot":"","sources":["../../../../src/commands/schema/migrate/stub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AASH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAanE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AA6BD,wBAAsB,cAAc,CAClC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CA6EnB"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * `neko schema migrate stub` command implementation (v0.8 Step 23).
3
+ *
4
+ * The only v0.8 migrate verb that writes a file. Mirrors v0.7's
5
+ * `runGenerate` shape: the schema-side handler is pure and returns
6
+ * `{ suggestedPath, content }`; this command is responsible for the
7
+ * actual filesystem persistence. Master plan Decision #1 keeps the
8
+ * filesystem on the CLI side.
9
+ *
10
+ * Flow:
11
+ *
12
+ * 1. `walkWorkspace({ root })` — load every `*.schema.{ts,js}`.
13
+ * Any per-file load failure → IO_ERROR.
14
+ * 2. `buildRegistry(walk.entries)` — index the schemas.
15
+ * `duplicate_schema_id` → LOGICAL_FAILURE.
16
+ * 3. `stubMigrationHandler({ schemaRegistry, schemaId, fromVersion,
17
+ * toVersion })` — generate the stub payload. Failure modes
18
+ * surface as `migration_missing_endpoint` →
19
+ * LOGICAL_FAILURE.
20
+ * 4. **Refuse to overwrite.** Unlike `runGenerate` (which clobbers
21
+ * generated artifacts by default), `runMigrateStub` writes a
22
+ * file that contains hand-authored code. If the destination
23
+ * already exists, the command returns LOGICAL_FAILURE without
24
+ * modifying it. Decision #5 of the v0.8 phase plan locks this
25
+ * contract.
26
+ * 5. `mkdir -p` the destination's parent + `writeFile` the
27
+ * stub content. Any thrown error (EACCES, ENOSPC, etc.) →
28
+ * IO_ERROR.
29
+ * 6. Format and write:
30
+ * - `--json` (success) → `{ stub: { schemaId, fromVersion,
31
+ * toVersion, suggestedPath } }`. The `content` field is
32
+ * deliberately omitted — machine consumers don't need the
33
+ * full source body, and the file is already on disk.
34
+ * - pretty → `Wrote stub: <suggestedPath>`.
35
+ *
36
+ * **Pure helpers + bounded I/O.** `mkdir` and `writeFile` (and a
37
+ * `stat` for the overwrite check) are the only `node:fs` calls.
38
+ * `process.exit`, `console.*`, and direct stdout/stderr writes are
39
+ * forbidden — static-scan asserted by
40
+ * [`../../../../tests/commands/schema-migrate-stub.test.ts`](../../../../tests/commands/schema-migrate-stub.test.ts).
41
+ *
42
+ * **No `migration.transform`.** The stub command doesn't even own
43
+ * a registry of authored migrations — only the schema registry. The
44
+ * `.transform(` pattern still appears as a forbidden token in the
45
+ * static scan so the v0.8 boundary stays enforced everywhere.
46
+ */
47
+ import { dirname, isAbsolute, resolve, sep } from "node:path";
48
+ import { mkdir, stat, writeFile } from "node:fs/promises";
49
+ import { buildRegistry, stubMigrationHandler, } from "@nekostack/schema/cli";
50
+ import { EXIT_CODES } from "../../../exit-codes.js";
51
+ import { formatJson } from "../../../formatters/json.js";
52
+ import { formatIssuesPretty, formatLoadFailuresPretty, } from "../../../formatters/pretty.js";
53
+ import { walkWorkspace } from "../../../loaders/walk-workspace.js";
54
+ // =============================================================================
55
+ // Public entry
56
+ // =============================================================================
57
+ export async function runMigrateStub(opts) {
58
+ // 1. Schema walk + load failures → IO_ERROR.
59
+ const walk = await walkWorkspace({ root: opts.root });
60
+ if (walk.failures.length > 0) {
61
+ writeLoadFailures(opts, walk.failures);
62
+ return EXIT_CODES.IO_ERROR;
63
+ }
64
+ // 2. Build the schema registry.
65
+ const reg = buildRegistry(walk.entries);
66
+ if (!reg.success) {
67
+ writeIssues(opts, reg.issues);
68
+ return pickRegistryFailureExitCode(reg.issues);
69
+ }
70
+ // 3. Plan the stub. Failures (e.g., `migration_missing_endpoint`)
71
+ // surface as `LOGICAL_FAILURE`.
72
+ const result = stubMigrationHandler({
73
+ schemaRegistry: reg.data,
74
+ schemaId: opts.schemaId,
75
+ fromVersion: opts.fromVersion,
76
+ toVersion: opts.toVersion,
77
+ });
78
+ if (!result.success) {
79
+ writeIssues(opts, result.issues);
80
+ return EXIT_CODES.LOGICAL_FAILURE;
81
+ }
82
+ const stub = result.data;
83
+ const rootAbs = isAbsolute(opts.root) ? opts.root : resolve(opts.root);
84
+ const destAbs = resolve(rootAbs, stub.suggestedPath.split("/").join(sep));
85
+ // 4. Refuse to overwrite. The destination is hand-authored migration
86
+ // code; clobbering it would silently destroy the author's work.
87
+ if (await pathExists(destAbs)) {
88
+ const failure = {
89
+ path: stub.suggestedPath,
90
+ reason: "stub_path_exists",
91
+ message: `Refusing to overwrite existing file at \`${stub.suggestedPath}\`. ` +
92
+ `Move or delete it first, or pick a different (from, to) pair.`,
93
+ };
94
+ writeLocalFailures(opts, [failure]);
95
+ return EXIT_CODES.LOGICAL_FAILURE;
96
+ }
97
+ // 5. Persist. `mkdir -p` + `writeFile` may throw on I/O errors
98
+ // (EACCES, ENOSPC, etc.); surface those as IO_ERROR.
99
+ try {
100
+ await mkdir(dirname(destAbs), { recursive: true });
101
+ await writeFile(destAbs, stub.content, "utf8");
102
+ }
103
+ catch (cause) {
104
+ const failure = {
105
+ path: stub.suggestedPath,
106
+ reason: "stub_write_failed",
107
+ message: errorMessageOf(cause),
108
+ };
109
+ writeLocalFailures(opts, [failure]);
110
+ return EXIT_CODES.IO_ERROR;
111
+ }
112
+ // 6. Success — emit the locked machine-readable shape (no `content`).
113
+ if (opts.json) {
114
+ opts.stdout(formatJson({
115
+ stub: {
116
+ schemaId: stub.schemaId,
117
+ fromVersion: stub.fromVersion,
118
+ toVersion: stub.toVersion,
119
+ suggestedPath: stub.suggestedPath,
120
+ },
121
+ }));
122
+ }
123
+ else {
124
+ opts.stdout(`Wrote stub: ${stub.suggestedPath}\n`);
125
+ }
126
+ return EXIT_CODES.SUCCESS;
127
+ }
128
+ // =============================================================================
129
+ // Helpers
130
+ // =============================================================================
131
+ async function pathExists(abs) {
132
+ try {
133
+ await stat(abs);
134
+ return true;
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ }
140
+ function errorMessageOf(err) {
141
+ if (err instanceof Error)
142
+ return err.message;
143
+ if (typeof err === "string")
144
+ return err;
145
+ try {
146
+ return JSON.stringify(err);
147
+ }
148
+ catch {
149
+ return String(err);
150
+ }
151
+ }
152
+ /**
153
+ * Pick exit code for `buildRegistry` failure. Same mapping as
154
+ * `runMigratePlan` / `runMigrateVerify`.
155
+ */
156
+ function pickRegistryFailureExitCode(issues) {
157
+ for (const i of issues) {
158
+ if (i.code === "integrity_error")
159
+ return EXIT_CODES.INTEGRITY_ERROR;
160
+ }
161
+ return EXIT_CODES.LOGICAL_FAILURE;
162
+ }
163
+ function writeLoadFailures(opts, failures) {
164
+ if (opts.json) {
165
+ opts.stdout(formatJson({
166
+ failures: failures.map((f) => ({
167
+ path: f.path,
168
+ reason: f.reason,
169
+ message: f.message,
170
+ })),
171
+ }));
172
+ }
173
+ else {
174
+ opts.stderr(formatLoadFailuresPretty(failures));
175
+ }
176
+ }
177
+ function writeIssues(opts, issues) {
178
+ if (opts.json) {
179
+ opts.stdout(formatJson({ issues }));
180
+ }
181
+ else {
182
+ opts.stderr(formatIssuesPretty(issues));
183
+ }
184
+ }
185
+ function writeLocalFailures(opts, failures) {
186
+ if (opts.json) {
187
+ opts.stdout(formatJson({ failures }));
188
+ }
189
+ else {
190
+ const header = failures.length === 1 ? "Stub failed:" : "Stub failed:";
191
+ const lines = failures.map((f) => ` [${f.reason}] ${f.path} — ${f.message}`);
192
+ opts.stderr([header, ...lines].join("\n") + "\n");
193
+ }
194
+ }
195
+ //# sourceMappingURL=stub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stub.js","sourceRoot":"","sources":["../../../../src/commands/schema/migrate/stub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `neko schema migrate verify` command implementation (v0.8 Step 22).
3
+ *
4
+ * Mirrors `runMigratePlan`'s pure / writer-injected discipline
5
+ * (Master plan Decision #1).
6
+ *
7
+ * Flow:
8
+ *
9
+ * 1. `walkWorkspace({ root })` — load every `*.schema.{ts,js}`.
10
+ * Any per-file load failure → IO_ERROR.
11
+ * 2. `buildRegistry(walk.entries)` — index the schemas.
12
+ * `duplicate_schema_id` → LOGICAL_FAILURE; `integrity_error` →
13
+ * INTEGRITY_ERROR.
14
+ * 3. `readMigrations({ root })` — load every `*.migration.ts`.
15
+ * Any per-file load failure → IO_ERROR.
16
+ * 4. `buildMigrationRegistry(migWalk.entries)` — index migrations
17
+ * and parse each one's provenance. `duplicate_migration` →
18
+ * LOGICAL_FAILURE; `integrity_error` → INTEGRITY_ERROR.
19
+ * 5. **CLI-side verdict derivation** — walk the migration registry
20
+ * with the same classification rule as
21
+ * `verifyMigrationProvenance` so the CLI can surface verdicts +
22
+ * summary on BOTH the success and failure JSON branches. (The
23
+ * schema-side handler discards verdicts on failure; the v0.8
24
+ * verify-CLI contract requires them on both branches.)
25
+ * 6. `verifyMigrationsHandler({ schemaRegistry, migrationRegistry })`
26
+ * — gets the official success/failure determination and any
27
+ * `migration_drift` / `migration_missing_endpoint` issues.
28
+ * 7. Format and write:
29
+ * - `--json` (success) → `{ verdicts, summary }`
30
+ * - `--json` (failure) → `{ verdicts, summary, issues }`
31
+ * - pretty → summary header + per-verdict rows
32
+ * 8. Exit-code mapping:
33
+ * - any drift / missing_endpoint → LOGICAL_FAILURE
34
+ * - only bound / cosmetic_drift (or empty registry) → SUCCESS
35
+ *
36
+ * **Pure.** No `process.exit`, no `console.*`, no direct
37
+ * `process.stdout` / `process.stderr` writes. Writers are injected.
38
+ *
39
+ * **`migration.transform` is NEVER invoked here.** Verification is
40
+ * provenance-only (the v0.8 INVARIANTS contract). Static-scan
41
+ * asserted by [`../../../../tests/commands/schema-migrate-verify.test.ts`](../../../../tests/commands/schema-migrate-verify.test.ts).
42
+ *
43
+ * **JSON output never serializes `MigrationEntry.migration` or its
44
+ * `transform` closure.** Verdicts are already shaped as
45
+ * `{ status, schemaId, fromVersion, toVersion, sourcePath }` per the
46
+ * `MigrationVerdict` type — no migration reference — so the projection
47
+ * is the identity. Issue records likewise carry only structured
48
+ * metadata.
49
+ */
50
+ import { type ExitCode } from "../../../exit-codes.js";
51
+ export interface RunMigrateVerifyOptions {
52
+ readonly root: string;
53
+ readonly json: boolean;
54
+ readonly quiet: boolean;
55
+ readonly stdout: (s: string) => void;
56
+ readonly stderr: (s: string) => void;
57
+ }
58
+ export declare function runMigrateVerify(opts: RunMigrateVerifyOptions): Promise<ExitCode>;
59
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../../src/commands/schema/migrate/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAeH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAcnE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AA+KD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,QAAQ,CAAC,CA+FnB"}