@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,115 @@
1
+ /**
2
+ * `neko schema list` command implementation (v0.7 Step 29).
3
+ *
4
+ * First fully-wired schema verb. Replaces the Step 25 placeholder.
5
+ * Flow:
6
+ *
7
+ * 1. `walkWorkspace({ root })` discovers and loads every
8
+ * `*.schema.{ts,js}` file under `root`.
9
+ * 2. If any file failed to load → render failures, exit IO_ERROR.
10
+ * 3. `buildRegistry(entries)` indexes the loaded schemas.
11
+ * 4. If the build returned `duplicate_schema_id` Issues → render
12
+ * issues, exit LOGICAL_FAILURE.
13
+ * 5. `listHandler({ registry })` enumerates entries in
14
+ * schemaId-ascending order (unversioned last).
15
+ * 6. Format and write:
16
+ * - `--json` → one-line JSON `{ schemas: [...] }` to stdout
17
+ * - default → padded table to stdout via `formatListPretty`
18
+ * 7. Return SUCCESS.
19
+ *
20
+ * Pure: no `process.exit`, no `console.*`, no direct `process.stdout`
21
+ * / `process.stderr` writes. The caller (the commander action wired
22
+ * up in `cli.ts`) injects writers; tests pass collector functions.
23
+ *
24
+ * JSON output shape is locked here so machine consumers have a
25
+ * stable contract. The `schema` field of `RegistryEntry` (the live
26
+ * `Schema` instance) is deliberately NOT included — it's not
27
+ * JSON-serializable and not a useful identifier downstream.
28
+ *
29
+ * {
30
+ * "schemas": [
31
+ * { "schemaId": "com.x.User", "schemaVersion": "1.0.0",
32
+ * "sourcePath": "schemas/user.schema.ts",
33
+ * "irHash": "sha256:...", "sourceHash": "sha256:..." },
34
+ * …
35
+ * ]
36
+ * }
37
+ *
38
+ * Unversioned entries surface as `"schemaVersion": null` so the
39
+ * JSON shape stays uniform.
40
+ */
41
+ import { buildRegistry, listHandler } from "@nekostack/schema/cli";
42
+ import { EXIT_CODES } from "../../exit-codes.js";
43
+ import { formatJson } from "../../formatters/json.js";
44
+ import { formatIssuesPretty, formatListPretty, formatLoadFailuresPretty, } from "../../formatters/pretty.js";
45
+ import { walkWorkspace } from "../../loaders/walk-workspace.js";
46
+ /**
47
+ * Project a `LoadFailure` to a stable, JSON-safe shape. The
48
+ * underlying `LoadFailure.cause` is an `unknown` thrown by tsx /
49
+ * esbuild and may not serialize cleanly (or at all — circular
50
+ * references, function values, etc.). The CLI's `--json` contract
51
+ * for load failures is `{ path, reason, message }` only.
52
+ */
53
+ function toSerializableLoadFailure(f) {
54
+ return { path: f.path, reason: f.reason, message: f.message };
55
+ }
56
+ export async function runList(opts) {
57
+ // 1. Walk the workspace and load every matching schema file.
58
+ const walk = await walkWorkspace({ root: opts.root });
59
+ // 2. Per-file load failures → IO_ERROR. Render before returning.
60
+ // Failures are essential diagnostics; `--quiet` does not
61
+ // suppress them per the audit guidance for Step 29.
62
+ if (walk.failures.length > 0) {
63
+ if (opts.json) {
64
+ opts.stdout(formatJson({
65
+ failures: walk.failures.map(toSerializableLoadFailure),
66
+ }));
67
+ }
68
+ else {
69
+ opts.stderr(formatLoadFailuresPretty(walk.failures));
70
+ }
71
+ return EXIT_CODES.IO_ERROR;
72
+ }
73
+ // 3. Build the in-memory registry. `buildRegistry` returns a
74
+ // `Result` so duplicate-id collisions surface as `Issue[]`
75
+ // rather than throws.
76
+ const reg = buildRegistry(walk.entries);
77
+ if (!reg.success) {
78
+ if (opts.json) {
79
+ opts.stdout(formatJson({ issues: reg.issues }));
80
+ }
81
+ else {
82
+ opts.stderr(formatIssuesPretty(reg.issues));
83
+ }
84
+ return EXIT_CODES.LOGICAL_FAILURE;
85
+ }
86
+ // 4. Enumerate. `listHandler` has no failure mode, but treat any
87
+ // future failure defensively as LOGICAL_FAILURE.
88
+ const result = listHandler({ registry: reg.data });
89
+ if (!result.success) {
90
+ if (opts.json) {
91
+ opts.stdout(formatJson({ issues: result.issues }));
92
+ }
93
+ else {
94
+ opts.stderr(formatIssuesPretty(result.issues));
95
+ }
96
+ return EXIT_CODES.LOGICAL_FAILURE;
97
+ }
98
+ // 5. Success — write the entries in the chosen format.
99
+ if (opts.json) {
100
+ opts.stdout(formatJson({
101
+ schemas: result.data.entries.map((e) => ({
102
+ schemaId: e.schemaId,
103
+ schemaVersion: e.schemaVersion ?? null,
104
+ sourcePath: e.sourcePath,
105
+ irHash: e.irHash,
106
+ sourceHash: e.sourceHash,
107
+ })),
108
+ }));
109
+ }
110
+ else {
111
+ opts.stdout(formatListPretty(result.data.entries));
112
+ }
113
+ return EXIT_CODES.SUCCESS;
114
+ }
115
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/schema/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `neko schema migrate list` command implementation (v0.8 Step 20).
3
+ *
4
+ * First fully-wired v0.8 verb. Mirrors the v0.7 `runList` shape exactly
5
+ * — same pure / writer-injected discipline (Master plan Decision #1).
6
+ *
7
+ * Flow:
8
+ *
9
+ * 1. `readMigrations({ root })` discovers and loads every
10
+ * `*.migration.ts` file under `root`.
11
+ * 2. If any file failed to load → render failures, exit IO_ERROR.
12
+ * 3. `buildMigrationRegistry(entries)` indexes the loaded migrations
13
+ * and parses each one's JSDoc provenance header.
14
+ * 4. If the build returned issues:
15
+ * - any `integrity_error` → INTEGRITY_ERROR (4)
16
+ * - otherwise (e.g., `duplicate_migration`) → LOGICAL_FAILURE (1)
17
+ * 5. `listMigrationsHandler({ migrationRegistry })` enumerates
18
+ * entries in `(schemaId, fromVersion, toVersion)` ascending order.
19
+ * 6. Format and write:
20
+ * - `--json` → one-line JSON `{ migrations: [...] }` to stdout
21
+ * - default → padded table to stdout via `formatMigrationListPretty`
22
+ * 7. Return SUCCESS.
23
+ *
24
+ * **Pure.** No `process.exit`, no `console.*`, no direct
25
+ * `process.stdout` / `process.stderr` writes. The caller (the commander
26
+ * action wired up in `cli.ts` — landing in a later step) injects
27
+ * writers; tests pass collector functions.
28
+ *
29
+ * **`migration.transform` is NEVER invoked here.** The v0.8 boundary
30
+ * (INVARIANTS — "no apply, no transform execution") stays in force.
31
+ * Static-scan asserted by [`../../../../tests/commands/schema-migrate-list.test.ts`](../../../../tests/commands/schema-migrate-list.test.ts).
32
+ *
33
+ * JSON output shape is locked here so machine consumers have a stable
34
+ * contract. The `migration` field on `MigrationEntry` — the live
35
+ * `AnyMigration` carrying the `transform` closure — is deliberately
36
+ * NOT included; it's not JSON-serializable, not a useful downstream
37
+ * identifier, and touching it would tempt callers across the v0.8
38
+ * boundary.
39
+ *
40
+ * {
41
+ * "migrations": [
42
+ * {
43
+ * "schemaId": "com.x.Tenant",
44
+ * "fromVersion": "1.0.0",
45
+ * "toVersion": "2.0.0",
46
+ * "sourcePath": "schemas/tenant.1-0-0-to-2-0-0.migration.ts",
47
+ * "fromIrHash": "sha256:...",
48
+ * "toIrHash": "sha256:...",
49
+ * "fromSourceHash": "sha256:...",
50
+ * "toSourceHash": "sha256:..."
51
+ * },
52
+ * …
53
+ * ]
54
+ * }
55
+ */
56
+ import { type ExitCode } from "../../../exit-codes.js";
57
+ export interface RunMigrateListOptions {
58
+ readonly root: string;
59
+ readonly json: boolean;
60
+ readonly quiet: boolean;
61
+ readonly stdout: (s: string) => void;
62
+ readonly stderr: (s: string) => void;
63
+ }
64
+ export declare function runMigrateList(opts: RunMigrateListOptions): Promise<ExitCode>;
65
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/commands/schema/migrate/list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAIH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAoCnE,MAAM,WAAW,qBAAqB;IACpC,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;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CAqEnB"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * `neko schema migrate list` command implementation (v0.8 Step 20).
3
+ *
4
+ * First fully-wired v0.8 verb. Mirrors the v0.7 `runList` shape exactly
5
+ * — same pure / writer-injected discipline (Master plan Decision #1).
6
+ *
7
+ * Flow:
8
+ *
9
+ * 1. `readMigrations({ root })` discovers and loads every
10
+ * `*.migration.ts` file under `root`.
11
+ * 2. If any file failed to load → render failures, exit IO_ERROR.
12
+ * 3. `buildMigrationRegistry(entries)` indexes the loaded migrations
13
+ * and parses each one's JSDoc provenance header.
14
+ * 4. If the build returned issues:
15
+ * - any `integrity_error` → INTEGRITY_ERROR (4)
16
+ * - otherwise (e.g., `duplicate_migration`) → LOGICAL_FAILURE (1)
17
+ * 5. `listMigrationsHandler({ migrationRegistry })` enumerates
18
+ * entries in `(schemaId, fromVersion, toVersion)` ascending order.
19
+ * 6. Format and write:
20
+ * - `--json` → one-line JSON `{ migrations: [...] }` to stdout
21
+ * - default → padded table to stdout via `formatMigrationListPretty`
22
+ * 7. Return SUCCESS.
23
+ *
24
+ * **Pure.** No `process.exit`, no `console.*`, no direct
25
+ * `process.stdout` / `process.stderr` writes. The caller (the commander
26
+ * action wired up in `cli.ts` — landing in a later step) injects
27
+ * writers; tests pass collector functions.
28
+ *
29
+ * **`migration.transform` is NEVER invoked here.** The v0.8 boundary
30
+ * (INVARIANTS — "no apply, no transform execution") stays in force.
31
+ * Static-scan asserted by [`../../../../tests/commands/schema-migrate-list.test.ts`](../../../../tests/commands/schema-migrate-list.test.ts).
32
+ *
33
+ * JSON output shape is locked here so machine consumers have a stable
34
+ * contract. The `migration` field on `MigrationEntry` — the live
35
+ * `AnyMigration` carrying the `transform` closure — is deliberately
36
+ * NOT included; it's not JSON-serializable, not a useful downstream
37
+ * identifier, and touching it would tempt callers across the v0.8
38
+ * boundary.
39
+ *
40
+ * {
41
+ * "migrations": [
42
+ * {
43
+ * "schemaId": "com.x.Tenant",
44
+ * "fromVersion": "1.0.0",
45
+ * "toVersion": "2.0.0",
46
+ * "sourcePath": "schemas/tenant.1-0-0-to-2-0-0.migration.ts",
47
+ * "fromIrHash": "sha256:...",
48
+ * "toIrHash": "sha256:...",
49
+ * "fromSourceHash": "sha256:...",
50
+ * "toSourceHash": "sha256:..."
51
+ * },
52
+ * …
53
+ * ]
54
+ * }
55
+ */
56
+ import { buildMigrationRegistry, listMigrationsHandler } from "@nekostack/schema/cli";
57
+ import { EXIT_CODES } from "../../../exit-codes.js";
58
+ import { formatJson } from "../../../formatters/json.js";
59
+ import { formatIssuesPretty, formatLoadFailuresPretty, formatMigrationListPretty, } from "../../../formatters/pretty.js";
60
+ import { readMigrations } from "../../../loaders/read-migrations.js";
61
+ /**
62
+ * Project a `LoadFailure` to a JSON-safe shape. `LoadFailure.cause` is
63
+ * an `unknown` thrown by tsx/esbuild and may not serialize cleanly
64
+ * (circular references, function values, etc.). The CLI's `--json`
65
+ * contract for load failures is `{ path, reason, message }` only —
66
+ * same projection rule as the v0.7 `runList`.
67
+ */
68
+ function toSerializableLoadFailure(f) {
69
+ return { path: f.path, reason: f.reason, message: f.message };
70
+ }
71
+ /**
72
+ * Pick the exit code for a `buildMigrationRegistry` failure:
73
+ * - any `integrity_error` → INTEGRITY_ERROR
74
+ * - otherwise (e.g., `duplicate_migration`) → LOGICAL_FAILURE
75
+ * Same dispatch rule as v0.7 `runCheck`'s handler-failure mapping.
76
+ */
77
+ function pickRegistryFailureExitCode(issues) {
78
+ for (const i of issues) {
79
+ if (i.code === "integrity_error")
80
+ return EXIT_CODES.INTEGRITY_ERROR;
81
+ }
82
+ return EXIT_CODES.LOGICAL_FAILURE;
83
+ }
84
+ export async function runMigrateList(opts) {
85
+ // 1. Walk the workspace and load every `*.migration.ts` file.
86
+ const walk = await readMigrations({ root: opts.root });
87
+ // 2. Per-file load failures → IO_ERROR. Render before returning.
88
+ // Failures are essential diagnostics; `--quiet` does not suppress
89
+ // them, matching v0.7 `runList`.
90
+ if (walk.failures.length > 0) {
91
+ if (opts.json) {
92
+ opts.stdout(formatJson({
93
+ failures: walk.failures.map(toSerializableLoadFailure),
94
+ }));
95
+ }
96
+ else {
97
+ opts.stderr(formatLoadFailuresPretty(walk.failures, {
98
+ noun: { singular: "migration file", plural: "migration files" },
99
+ }));
100
+ }
101
+ return EXIT_CODES.IO_ERROR;
102
+ }
103
+ // 3. Build the in-memory migration registry. `buildMigrationRegistry`
104
+ // returns a `Result` so `duplicate_migration` and provenance
105
+ // parse failures surface as `Issue[]` rather than throws.
106
+ const reg = buildMigrationRegistry(walk.entries);
107
+ if (!reg.success) {
108
+ if (opts.json) {
109
+ opts.stdout(formatJson({ issues: reg.issues }));
110
+ }
111
+ else {
112
+ opts.stderr(formatIssuesPretty(reg.issues));
113
+ }
114
+ return pickRegistryFailureExitCode(reg.issues);
115
+ }
116
+ // 4. Enumerate. `listMigrationsHandler` has no failure mode today
117
+ // but treat any future failure defensively as LOGICAL_FAILURE.
118
+ const result = listMigrationsHandler({ migrationRegistry: reg.data });
119
+ if (!result.success) {
120
+ if (opts.json) {
121
+ opts.stdout(formatJson({ issues: result.issues }));
122
+ }
123
+ else {
124
+ opts.stderr(formatIssuesPretty(result.issues));
125
+ }
126
+ return EXIT_CODES.LOGICAL_FAILURE;
127
+ }
128
+ // 5. Success — write the entries in the chosen format.
129
+ if (opts.json) {
130
+ opts.stdout(formatJson({
131
+ migrations: result.data.entries.map((e) => ({
132
+ schemaId: e.schemaId,
133
+ fromVersion: e.fromVersion,
134
+ toVersion: e.toVersion,
135
+ sourcePath: e.sourcePath,
136
+ fromIrHash: e.fromIrHash,
137
+ toIrHash: e.toIrHash,
138
+ fromSourceHash: e.fromSourceHash,
139
+ toSourceHash: e.toSourceHash,
140
+ })),
141
+ }));
142
+ }
143
+ else {
144
+ opts.stdout(formatMigrationListPretty(result.data.entries));
145
+ }
146
+ return EXIT_CODES.SUCCESS;
147
+ }
148
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../src/commands/schema/migrate/list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEtF,OAAO,EAAE,UAAU,EAAiB,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAGrE;;;;;;GAMG;AACH,SAAS,yBAAyB,CAChC,CAAc;IAEd,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,SAAS,2BAA2B,CAAC,MAAwB;IAC3D,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI,KAAK,iBAAiB;YAAE,OAAO,UAAU,CAAC,eAAe,CAAC;IACtE,CAAC;IACD,OAAO,UAAU,CAAC,eAAe,CAAC;AACpC,CAAC;AAUD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAA2B;IAE3B,8DAA8D;IAC9D,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAEvD,iEAAiE;IACjE,qEAAqE;IACrE,oCAAoC;IACpC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CACT,UAAU,CAAC;gBACT,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,yBAAyB,CAAC;aACvD,CAAC,CACH,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CACT,wBAAwB,CAAC,IAAI,CAAC,QAAQ,EAAE;gBACtC,IAAI,EAAE,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,iBAAiB,EAAE;aAChE,CAAC,CACH,CAAC;QACJ,CAAC;QACD,OAAO,UAAU,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED,sEAAsE;IACtE,gEAAgE;IAChE,6DAA6D;IAC7D,MAAM,GAAG,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,2BAA2B,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,kEAAkE;IAClE,kEAAkE;IAClE,MAAM,MAAM,GAAG,qBAAqB,CAAC,EAAE,iBAAiB,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACtE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACrD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,UAAU,CAAC,eAAe,CAAC;IACpC,CAAC;IAED,uDAAuD;IACvD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,IAAI,CAAC,MAAM,CACT,UAAU,CAAC;YACT,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC1C,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,cAAc,EAAE,CAAC,CAAC,cAAc;gBAChC,YAAY,EAAE,CAAC,CAAC,YAAY;aAC7B,CAAC,CAAC;SACJ,CAAC,CACH,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,MAAM,CAAC,yBAAyB,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,UAAU,CAAC,OAAO,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,79 @@
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 { type ExitCode } from "../../../exit-codes.js";
68
+ export interface RunMigratePlanOptions {
69
+ readonly root: string;
70
+ readonly schemaId: string;
71
+ readonly fromVersion: string;
72
+ readonly toVersion: string;
73
+ readonly json: boolean;
74
+ readonly quiet: boolean;
75
+ readonly stdout: (s: string) => void;
76
+ readonly stderr: (s: string) => void;
77
+ }
78
+ export declare function runMigratePlan(opts: RunMigratePlanOptions): Promise<ExitCode>;
79
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../../src/commands/schema/migrate/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAaH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAcnE,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;AAwJD,wBAAsB,cAAc,CAClC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CA6FnB"}