@prisma-idb/family-idb 0.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/dist/bin/prisma-idb.d.mts +1 -0
  4. package/dist/bin/prisma-idb.mjs +13 -0
  5. package/dist/bin/prisma-idb.mjs.map +1 -0
  6. package/dist/chain-order-CaHXo5jz.mjs +44 -0
  7. package/dist/chain-order-CaHXo5jz.mjs.map +1 -0
  8. package/dist/cli-Dlaym_DD.mjs +342 -0
  9. package/dist/cli-Dlaym_DD.mjs.map +1 -0
  10. package/dist/contract-space-codegen--gfuyap2.mjs +128 -0
  11. package/dist/contract-space-codegen--gfuyap2.mjs.map +1 -0
  12. package/dist/exports/cli.d.mts +26 -0
  13. package/dist/exports/cli.d.mts.map +1 -0
  14. package/dist/exports/cli.mjs +2 -0
  15. package/dist/exports/config-types.d.mts +38 -0
  16. package/dist/exports/config-types.d.mts.map +1 -0
  17. package/dist/exports/config-types.mjs +66 -0
  18. package/dist/exports/config-types.mjs.map +1 -0
  19. package/dist/exports/contract-psl.d.mts +59 -0
  20. package/dist/exports/contract-psl.d.mts.map +1 -0
  21. package/dist/exports/contract-psl.mjs +97 -0
  22. package/dist/exports/contract-psl.mjs.map +1 -0
  23. package/dist/exports/contract-ts.d.mts +98 -0
  24. package/dist/exports/contract-ts.d.mts.map +1 -0
  25. package/dist/exports/contract-ts.mjs +275 -0
  26. package/dist/exports/contract-ts.mjs.map +1 -0
  27. package/dist/exports/control.d.mts +107 -0
  28. package/dist/exports/control.d.mts.map +1 -0
  29. package/dist/exports/control.mjs +573 -0
  30. package/dist/exports/control.mjs.map +1 -0
  31. package/dist/exports/pack.d.mts +10 -0
  32. package/dist/exports/pack.d.mts.map +1 -0
  33. package/dist/exports/pack.mjs +11 -0
  34. package/dist/exports/pack.mjs.map +1 -0
  35. package/dist/migration-plan-BWhd8BdV.mjs +330 -0
  36. package/dist/migration-plan-BWhd8BdV.mjs.map +1 -0
  37. package/dist/preflight-4jDCNvS0.mjs +119 -0
  38. package/dist/preflight-4jDCNvS0.mjs.map +1 -0
  39. package/dist/psl-interpreter-C0suaXu2.d.mts +45 -0
  40. package/dist/psl-interpreter-C0suaXu2.d.mts.map +1 -0
  41. package/dist/psl-interpreter-C6Q4okuO.mjs +809 -0
  42. package/dist/psl-interpreter-C6Q4okuO.mjs.map +1 -0
  43. package/dist/validate-BLnItEKs.mjs +48 -0
  44. package/dist/validate-BLnItEKs.mjs.map +1 -0
  45. package/package.json +64 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Prisma IDB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # `@prisma-idb/family-idb`
2
+
3
+ > The [Prisma 8](https://www.prisma.io/blog/prisma-next-call-for-extension-authors) driver + adapter + target stack for IndexedDB — offline-first, no server required.
4
+
5
+ **family-idb** is the main entry point for the `@prisma-idb` stack. It exposes the control-plane descriptor, the family instance, and the `prisma-idb` CLI for migration authoring and config integration. The former `prisma-idb` command remains an alias.
6
+
7
+ ## Stack
8
+
9
+ ```
10
+ family-idb ← you are here (CLI / config / entry point)
11
+ client-idb (ORM query builder)
12
+ runtime-idb (RuntimeCore)
13
+ adapter-idb (query AST → IDB plan)
14
+ driver-idb (window.indexedDB wrapper)
15
+ target-idb (identity + migrations)
16
+ ```
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @prisma-idb/family-idb
22
+ ```
23
+
24
+ ## Documentation
25
+
26
+ See [prisma-idb.dev](https://prisma-idb.dev/) for full setup and usage docs.
27
+
28
+ ## License
29
+
30
+ MIT
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { i as runIdbCli } from "../cli-Dlaym_DD.mjs";
3
+ //#region src/bin/prisma-idb.ts
4
+ runIdbCli(process).then((code) => {
5
+ process.exitCode = code;
6
+ }).catch((err) => {
7
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
8
+ process.exitCode = 1;
9
+ });
10
+ //#endregion
11
+ export {};
12
+
13
+ //# sourceMappingURL=prisma-idb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma-idb.mjs","names":[],"sources":["../../src/bin/prisma-idb.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runIdbCli } from \"../cli/cli\";\n\nrunIdbCli(process)\n .then((code) => {\n process.exitCode = code;\n })\n .catch((err: unknown) => {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`);\n process.exitCode = 1;\n });\n"],"mappings":";;;AAGA,UAAU,OAAO,CAAC,CACf,MAAM,SAAS;CACd,QAAQ,WAAW;AACrB,CAAC,CAAC,CACD,OAAO,QAAiB;CACvB,QAAQ,OAAO,MAAM,GAAG,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,GAAG;CAC5E,QAAQ,WAAW;AACrB,CAAC"}
@@ -0,0 +1,44 @@
1
+ //#region src/core/chain-order.ts
2
+ /**
3
+ * Walk an unordered set of migration packages by `metadata.from`/`to` edges
4
+ * to produce a chain-ordered list. Starts at the package whose
5
+ * `from === null` (the baseline) and follows each `to` to the next
6
+ * package's matching `from`.
7
+ *
8
+ * Throws on:
9
+ * - No baseline (no package with `from === null`)
10
+ * - Multiple baselines (two packages with `from === null`)
11
+ * - Broken edge (no package whose `from` matches the cursor)
12
+ * - Cycle (revisited hash)
13
+ * - Orphan package (package left unvisited after chain completes)
14
+ */
15
+ function chainOrderByMetadata(unordered) {
16
+ if (unordered.size === 0) return [];
17
+ const byFrom = /* @__PURE__ */ new Map();
18
+ for (const pkg of unordered.values()) {
19
+ const from = pkg.metadata.from;
20
+ const existing = byFrom.get(from);
21
+ if (existing) throw new Error(`Migration chain conflict: ${existing.dirName} and ${pkg.dirName} both declare from === ${JSON.stringify(from)}. The chain must be linear.`);
22
+ byFrom.set(from, pkg);
23
+ }
24
+ const ordered = [];
25
+ const visited = /* @__PURE__ */ new Set();
26
+ let cursor = null;
27
+ while (true) {
28
+ const next = byFrom.get(cursor);
29
+ if (!next) {
30
+ if (ordered.length === unordered.size) break;
31
+ const orphans = [...unordered.keys()].filter((d) => !visited.has(d));
32
+ throw new Error(`Migration chain broken: no package declares from === ${JSON.stringify(cursor)}. Orphan package(s) not reachable from the baseline: ${orphans.join(", ")}. Either the migrations directory has been hand-edited or a package was deleted; re-emit the affected migration with \`node migration.ts\`.`);
33
+ }
34
+ if (visited.has(next.dirName)) throw new Error(`Migration chain cycle detected at ${next.dirName}.`);
35
+ visited.add(next.dirName);
36
+ ordered.push(next);
37
+ cursor = next.metadata.to;
38
+ }
39
+ return ordered;
40
+ }
41
+ //#endregion
42
+ export { chainOrderByMetadata as t };
43
+
44
+ //# sourceMappingURL=chain-order-CaHXo5jz.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain-order-CaHXo5jz.mjs","names":[],"sources":["../src/core/chain-order.ts"],"sourcesContent":["import type { MigrationMetadata } from \"@prisma/orm-toolchain/migration-tools/metadata\";\n\n/**\n * Minimal shape that {@link chainOrderByMetadata} needs from each package.\n *\n * Callers typically have richer types (ops, file paths) — just make sure\n * `dirName` and `metadata.from` are present.\n */\nexport interface ChainablePackage {\n readonly dirName: string;\n readonly metadata: Pick<MigrationMetadata, \"from\" | \"to\">;\n}\n\n/**\n * Walk an unordered set of migration packages by `metadata.from`/`to` edges\n * to produce a chain-ordered list. Starts at the package whose\n * `from === null` (the baseline) and follows each `to` to the next\n * package's matching `from`.\n *\n * Throws on:\n * - No baseline (no package with `from === null`)\n * - Multiple baselines (two packages with `from === null`)\n * - Broken edge (no package whose `from` matches the cursor)\n * - Cycle (revisited hash)\n * - Orphan package (package left unvisited after chain completes)\n */\nexport function chainOrderByMetadata<P extends ChainablePackage>(unordered: ReadonlyMap<string, P>): P[] {\n if (unordered.size === 0) return [];\n\n const byFrom = new Map<string | null, P>();\n for (const pkg of unordered.values()) {\n const from = pkg.metadata.from;\n const existing = byFrom.get(from);\n if (existing) {\n throw new Error(\n `Migration chain conflict: ${existing.dirName} and ${pkg.dirName} both ` +\n `declare from === ${JSON.stringify(from)}. The chain must be linear.`\n );\n }\n byFrom.set(from, pkg);\n }\n\n const ordered: P[] = [];\n const visited = new Set<string>();\n let cursor: string | null = null;\n while (true) {\n const next = byFrom.get(cursor);\n if (!next) {\n if (ordered.length === unordered.size) break; // chain complete\n const orphans = [...unordered.keys()].filter((d) => !visited.has(d));\n throw new Error(\n `Migration chain broken: no package declares from === ${JSON.stringify(cursor)}. ` +\n `Orphan package(s) not reachable from the baseline: ${orphans.join(\", \")}. ` +\n \"Either the migrations directory has been hand-edited or a package was \" +\n \"deleted; re-emit the affected migration with `node migration.ts`.\"\n );\n }\n if (visited.has(next.dirName)) {\n throw new Error(`Migration chain cycle detected at ${next.dirName}.`);\n }\n visited.add(next.dirName);\n ordered.push(next);\n cursor = next.metadata.to;\n }\n\n return ordered;\n}\n"],"mappings":";;;;;;;;;;;;;;AA0BA,SAAgB,qBAAiD,WAAwC;CACvG,IAAI,UAAU,SAAS,GAAG,OAAO,CAAC;CAElC,MAAM,yBAAS,IAAI,IAAsB;CACzC,KAAK,MAAM,OAAO,UAAU,OAAO,GAAG;EACpC,MAAM,OAAO,IAAI,SAAS;EAC1B,MAAM,WAAW,OAAO,IAAI,IAAI;EAChC,IAAI,UACF,MAAM,IAAI,MACR,6BAA6B,SAAS,QAAQ,OAAO,IAAI,QAAQ,yBAC3C,KAAK,UAAU,IAAI,EAAE,4BAC7C;EAEF,OAAO,IAAI,MAAM,GAAG;CACtB;CAEA,MAAM,UAAe,CAAC;CACtB,MAAM,0BAAU,IAAI,IAAY;CAChC,IAAI,SAAwB;CAC5B,OAAO,MAAM;EACX,MAAM,OAAO,OAAO,IAAI,MAAM;EAC9B,IAAI,CAAC,MAAM;GACT,IAAI,QAAQ,WAAW,UAAU,MAAM;GACvC,MAAM,UAAU,CAAC,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;GACnE,MAAM,IAAI,MACR,wDAAwD,KAAK,UAAU,MAAM,EAAE,uDACvB,QAAQ,KAAK,IAAI,EAAE,4IAG7E;EACF;EACA,IAAI,QAAQ,IAAI,KAAK,OAAO,GAC1B,MAAM,IAAI,MAAM,qCAAqC,KAAK,QAAQ,EAAE;EAEtE,QAAQ,IAAI,KAAK,OAAO;EACxB,QAAQ,KAAK,IAAI;EACjB,SAAS,KAAK,SAAS;CACzB;CAEA,OAAO;AACT"}
@@ -0,0 +1,342 @@
1
+ import { dirname, join, resolve } from "pathe";
2
+ import { createCli, defineCommand, defineCommandFamily, flag, loadConfig } from "@prisma/cli-engine";
3
+ import { ormConfigSection } from "@prisma/orm-toolchain/cli";
4
+ import { CliStructuredError, notOk, ok } from "@prisma/cli-engine/protocol";
5
+ import { finalizeConfig } from "@prisma/orm-toolchain/config-loader";
6
+ //#region src/cli/collecting-sink.ts
7
+ /**
8
+ * Buffers writes the way a real stream does: a write with no trailing
9
+ * newline (e.g. the wrapped functions' `out(" ${dirName} … ")` followed
10
+ * later by a separate `out("ok\n")` call) stays pending and joins with
11
+ * whatever arrives next, rather than becoming its own line. Splitting each
12
+ * `out`/`err` call independently — the first cut of this — silently broke
13
+ * exactly this pattern in `preflight.ts`'s per-package progress line.
14
+ */
15
+ function createCollectingSink() {
16
+ const completedLines = [];
17
+ let buffer = "";
18
+ function push(text) {
19
+ buffer += text;
20
+ const parts = buffer.split("\n");
21
+ buffer = parts.pop() ?? "";
22
+ for (const part of parts) if (part.length > 0) completedLines.push(part);
23
+ }
24
+ const lines = () => buffer.length > 0 ? [...completedLines, buffer] : completedLines;
25
+ return {
26
+ out: (text) => push(text),
27
+ err: (text) => push(text),
28
+ lines,
29
+ fullText: () => lines().join("\n").trim()
30
+ };
31
+ }
32
+ /**
33
+ * Renders collected lines as a `drawing` block (verbatim, no reflow) in
34
+ * human mode; `stdout` stays empty since these commands carry no separate
35
+ * machine-pipeable data channel beyond the human log itself.
36
+ */
37
+ function presentationsFromSink(sink, exitCode) {
38
+ const lines = sink.lines();
39
+ return {
40
+ human: () => lines.length === 0 ? [] : [{
41
+ kind: "drawing",
42
+ lines
43
+ }],
44
+ stdout: () => [],
45
+ json: () => ({
46
+ exitCode,
47
+ lines
48
+ }),
49
+ next: () => []
50
+ };
51
+ }
52
+ //#endregion
53
+ //#region src/cli/define-command.ts
54
+ function normalizeError(error) {
55
+ if (CliStructuredError.is(error)) return error;
56
+ return new CliStructuredError("IDB-CLI.UNEXPECTED_ERROR", error instanceof Error ? error.message : String(error));
57
+ }
58
+ function finalizedConfigContext(ctx) {
59
+ if (ctx.config === void 0) return ctx;
60
+ return {
61
+ ...ctx,
62
+ config: finalizeConfig(ctx.config, ctx.cwd)
63
+ };
64
+ }
65
+ /**
66
+ * These 3 commands are IDB-specific (fake-indexeddb, IdbMigrationPlanner) —
67
+ * pointing them at a non-IDB `prisma.config.ts` would fail confusingly deep
68
+ * inside that logic rather than with a clear, actionable error. `orm`'s own
69
+ * config section is family-agnostic by design (ADR 150), so this guard is
70
+ * this shell's responsibility, not the section's.
71
+ */
72
+ function requireIdbFamily(config) {
73
+ const familyId = config.family?.familyId;
74
+ if (familyId !== "idb") throw new CliStructuredError("IDB-CLI.FAMILY_MISMATCH", `config family is "${familyId ?? "unknown"}", expected "idb".`, {
75
+ why: "This CLI's migration commands operate on IDB-family projects only.",
76
+ nextActions: [{
77
+ kind: "edit-file",
78
+ label: "Point --config at your browser-side prisma.config.ts (the one using @prisma-idb/family-idb/control)"
79
+ }]
80
+ });
81
+ }
82
+ /**
83
+ * `defineCommand` pre-bound to the `orm` config section (see
84
+ * `config-section.ts`) with a uniform error boundary and path
85
+ * finalization, mirroring `@prisma/orm-toolchain`'s own `defineOrmCommand`
86
+ * (`vendor/prisma/packages/1-framework/3-tooling/cli/src/orm/define-command.ts`):
87
+ * the engine's config loader hands a command `contract.output`/
88
+ * `migrations.dir` exactly as authored (usually relative); finalizing here,
89
+ * once, means handlers always read absolute paths.
90
+ */
91
+ function defineIdbCommand(def) {
92
+ return defineCommand({
93
+ ...def,
94
+ needs: { config: ormConfigSection },
95
+ handler: async (args, ctx) => {
96
+ try {
97
+ const finalized = finalizedConfigContext(ctx);
98
+ requireIdbFamily(finalized.config);
99
+ return await def.handler(args, finalized);
100
+ } catch (error) {
101
+ return notOk(normalizeError(error));
102
+ }
103
+ }
104
+ });
105
+ }
106
+ //#endregion
107
+ //#region src/cli/migration/paths.ts
108
+ /**
109
+ * Resolves the contract/migrations paths a command needs from the already
110
+ * finalized (absolute-paths) `orm` config section, with CLI flag overrides
111
+ * winning when supplied. `ctx.config` arrives finalized by
112
+ * `defineIdbCommand` (see `../define-command.ts`), so `config.contract.output`
113
+ * and `config.migrations.dir` are already absolute — only an explicit
114
+ * `--contract`/`--migrations-dir` flag (relative to `cwd`) needs resolving
115
+ * here.
116
+ */
117
+ function requireContractPath(config, cwd, override) {
118
+ if (override !== void 0) return resolve(cwd, override);
119
+ const output = config.contract?.output;
120
+ if (output === void 0) throw new CliStructuredError("IDB-CLI.CONTRACT_PATH_MISSING", "config.contract.output is required to resolve the contract path.", {
121
+ why: "Neither --contract nor prisma.config.ts's orm.contract.output supplied a path.",
122
+ nextActions: [{
123
+ kind: "edit-file",
124
+ label: "Set contract.output in prisma.config.ts, or pass --contract explicitly"
125
+ }]
126
+ });
127
+ return output;
128
+ }
129
+ /**
130
+ * `finalizeConfig` always resolves `migrations.dir` to an absolute path
131
+ * (defaulting to `<configDir>/migrations` when unset); the fallback here is
132
+ * defensive, not the expected path.
133
+ */
134
+ function resolveMigrationsDir(config, cwd, override) {
135
+ if (override !== void 0) return resolve(cwd, override);
136
+ return config.migrations?.dir ?? resolve(cwd, "migrations");
137
+ }
138
+ //#endregion
139
+ //#region src/cli/migration/contract-space.ts
140
+ const migrationContractSpaceCommand = defineIdbCommand({
141
+ help: {
142
+ summary: "Regenerate contract-space.generated.ts from migrations/app/",
143
+ description: "Reads every migration package under <migrationsDir>/app/, validates the\nchain's connectivity, and emits a generated TypeScript module that bundles\nthem into a ContractSpace.",
144
+ examples: ["migration contract-space", "migration contract-space --out src/lib/prisma/contract-space.ts"]
145
+ },
146
+ args: { flags: {
147
+ contract: flag.string({
148
+ brief: "Path to contract.json (default: orm.contract.output)",
149
+ placeholder: "path"
150
+ }),
151
+ migrationsDir: flag.string({
152
+ brief: "Path to the migrations root (default: orm.migrations.dir)",
153
+ placeholder: "path"
154
+ }),
155
+ out: flag.string({
156
+ brief: "Output file path (default: colocated with the resolved contract.json)",
157
+ placeholder: "path"
158
+ })
159
+ } },
160
+ handler: async (args, ctx) => {
161
+ const contractPath = requireContractPath(ctx.config, ctx.cwd, args.flags.contract);
162
+ const migrationsDir = resolveMigrationsDir(ctx.config, ctx.cwd, args.flags.migrationsDir);
163
+ const outPath = args.flags.out !== void 0 ? resolve(ctx.cwd, args.flags.out) : join(dirname(contractPath), "contract-space.generated.ts");
164
+ const { generateContractSpace } = await import("./contract-space-codegen--gfuyap2.mjs");
165
+ const sink = createCollectingSink();
166
+ const exitCode = await generateContractSpace({
167
+ migrationsDir,
168
+ contractPath,
169
+ outPath,
170
+ out: sink.out,
171
+ err: sink.err
172
+ });
173
+ if (exitCode !== 0) return notOk(new CliStructuredError("IDB-CLI.CONTRACT_SPACE_FAILED", sink.fullText() || "migration contract-space failed."));
174
+ return ok(ctx.present({ data: {
175
+ exitCode,
176
+ outPath
177
+ } }, presentationsFromSink(sink, exitCode)));
178
+ }
179
+ });
180
+ //#endregion
181
+ //#region src/cli/family.ts
182
+ const commands = {
183
+ "migration plan": defineIdbCommand({
184
+ help: {
185
+ summary: "Plan the next IDB migration package (auto-detects baseline vs. incremental)",
186
+ description: "Compares the emitted contract against the latest on-disk migration state and\nproduces a new migration package with the required operations. Offline —\nnever opens IndexedDB (there is no live database on the Node side to consult).",
187
+ examples: [
188
+ "migration plan",
189
+ "migration plan --name add-posts",
190
+ "migration plan --space idb-sync"
191
+ ]
192
+ },
193
+ args: { flags: {
194
+ name: flag.string({
195
+ brief: "Directory slug (default: \"baseline\" for a fresh space; required otherwise)",
196
+ placeholder: "slug"
197
+ }),
198
+ space: flag.string({
199
+ brief: "Contract-space id (default: \"app\"). See ADR 212 for extension-space migrations.",
200
+ placeholder: "id"
201
+ }),
202
+ contract: flag.string({
203
+ brief: "Path to contract.json (default: orm.contract.output)",
204
+ placeholder: "path"
205
+ }),
206
+ migrationsDir: flag.string({
207
+ brief: "Path to the migrations root (default: orm.migrations.dir)",
208
+ placeholder: "path"
209
+ })
210
+ } },
211
+ handler: async (args, ctx) => {
212
+ const contractPath = requireContractPath(ctx.config, ctx.cwd, args.flags.contract);
213
+ const migrationsDir = resolveMigrationsDir(ctx.config, ctx.cwd, args.flags.migrationsDir);
214
+ const { migrationPlan } = await import("./migration-plan-BWhd8BdV.mjs");
215
+ const sink = createCollectingSink();
216
+ const exitCode = await migrationPlan({
217
+ migrationsDir,
218
+ contractPath,
219
+ out: sink.out,
220
+ err: sink.err,
221
+ ...args.flags.name !== void 0 && { name: args.flags.name },
222
+ ...args.flags.space !== void 0 && { spaceId: args.flags.space }
223
+ });
224
+ if (exitCode === 2) return notOk(new CliStructuredError("IDB-CLI.MIGRATION_NAME_REQUIRED", sink.fullText() || "--name <slug> is required when generating an incremental migration."));
225
+ if (exitCode !== 0) return notOk(new CliStructuredError("IDB-CLI.MIGRATION_PLAN_FAILED", sink.fullText() || "migration plan failed."));
226
+ return ok(ctx.present({ data: { exitCode } }, presentationsFromSink(sink, exitCode)));
227
+ }
228
+ }),
229
+ "migration contract-space": migrationContractSpaceCommand,
230
+ "migration preflight": defineIdbCommand({
231
+ help: {
232
+ summary: "Validate the migration chain against fake-indexeddb",
233
+ description: "Walks every migration package under <migrationsDir>/app/ in chain order,\napplying each package's ops.json against a fresh fake-indexeddb instance.\nCatches 'the chain doesn't apply cleanly' — not schema drift.",
234
+ examples: ["migration preflight"]
235
+ },
236
+ args: { flags: { migrationsDir: flag.string({
237
+ brief: "Path to the migrations root (default: orm.migrations.dir)",
238
+ placeholder: "path"
239
+ }) } },
240
+ handler: async (args, ctx) => {
241
+ const migrationsDir = resolveMigrationsDir(ctx.config, ctx.cwd, args.flags.migrationsDir);
242
+ const { runPreflight } = await import("./preflight-4jDCNvS0.mjs");
243
+ const sink = createCollectingSink();
244
+ const exitCode = await runPreflight({
245
+ migrationsDir,
246
+ out: sink.out,
247
+ err: sink.err
248
+ });
249
+ if (exitCode !== 0) return notOk(new CliStructuredError("IDB-CLI.PREFLIGHT_FAILED", sink.fullText() || "Preflight failed."));
250
+ return ok(ctx.present({ data: { exitCode } }, presentationsFromSink(sink, exitCode)));
251
+ }
252
+ })
253
+ };
254
+ /**
255
+ * The unit this package contributes to a `@prisma/cli-engine` shell: its
256
+ * config section (reused from the ORM family, see `config-section.ts`) and
257
+ * its 3 commands. These have no generic equivalent — the real `prisma` CLI's
258
+ * `migration new`/`plan`/etc. work against a live database or a SQL/Mongo
259
+ * on-disk model; ours works against `fake-indexeddb` and a hand-rolled
260
+ * contract-space convention.
261
+ */
262
+ const idbCommandFamily = defineCommandFamily({
263
+ configSection: ormConfigSection,
264
+ commands
265
+ });
266
+ //#endregion
267
+ //#region package.json
268
+ var version = "0.6.0";
269
+ //#endregion
270
+ //#region src/cli/cli.ts
271
+ const BIN_NAME = "prisma-idb";
272
+ const BIN_GROUPS = { migration: {
273
+ brief: "On-disk migration management commands",
274
+ description: "Plan, bundle, and validate IDB migration packages. Migrations are\ncontract-to-contract edges stored as versioned directories under migrations/."
275
+ } };
276
+ function createIdbCli() {
277
+ return createCli({
278
+ name: BIN_NAME,
279
+ version,
280
+ commandFamilies: [idbCommandFamily],
281
+ groups: BIN_GROUPS,
282
+ commands: idbCommandFamily.commands,
283
+ help: {
284
+ tagline: "IDB-target tooling for Prisma 8",
285
+ description: "Migration authoring for IndexedDB projects — no live database on the Node side to consult."
286
+ }
287
+ });
288
+ }
289
+ /** Everything environmental the engine is given, adapted from the host process once. */
290
+ function runtimeFromProcess(proc) {
291
+ return {
292
+ stdout: { write: (text) => void proc.stdout.write(text) },
293
+ stderr: { write: (text) => void proc.stderr.write(text) },
294
+ stdin: proc.stdin,
295
+ cwd: proc.cwd(),
296
+ env: proc.env,
297
+ isTty: {
298
+ stdin: proc.stdin.isTTY === true,
299
+ stdout: proc.stdout.isTTY === true,
300
+ stderr: proc.stderr.isTTY === true
301
+ },
302
+ host: {
303
+ runtime: {
304
+ name: "node",
305
+ version: proc.version
306
+ },
307
+ platform: proc.platform,
308
+ arch: proc.arch
309
+ },
310
+ exit: (code) => proc.exit(code),
311
+ onSignal: (callback) => {
312
+ const onInterrupt = () => callback("SIGINT");
313
+ const onTerminate = () => callback("SIGTERM");
314
+ proc.on("SIGINT", onInterrupt);
315
+ proc.on("SIGTERM", onTerminate);
316
+ return () => {
317
+ proc.off("SIGINT", onInterrupt);
318
+ proc.off("SIGTERM", onTerminate);
319
+ };
320
+ },
321
+ loadConfig: (configPath) => loadConfig(proc.cwd(), configPath),
322
+ managementApi: { baseUrl: "https://api.prisma.io" }
323
+ };
324
+ }
325
+ const STARTUP_FAILURE_EXIT_CODE = 1;
326
+ function reportStartupFailure(proc, error) {
327
+ const normalized = CliStructuredError.is(error) ? error : new CliStructuredError("IDB-CLI.STARTUP_FAILURE", error instanceof Error ? error.message : String(error));
328
+ proc.stderr.write(`✘ [${normalized.code}] ${normalized.message}\n`);
329
+ return STARTUP_FAILURE_EXIT_CODE;
330
+ }
331
+ /** Parses, executes and settles one invocation; returns the exit code. */
332
+ async function runIdbCli(proc) {
333
+ try {
334
+ return await createIdbCli().run(proc.argv.slice(2), runtimeFromProcess(proc));
335
+ } catch (error) {
336
+ return reportStartupFailure(proc, error);
337
+ }
338
+ }
339
+ //#endregion
340
+ export { idbCommandFamily as a, runIdbCli as i, BIN_NAME as n, createIdbCli as r, BIN_GROUPS as t };
341
+
342
+ //# sourceMappingURL=cli-Dlaym_DD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-Dlaym_DD.mjs","names":["packageJson.version"],"sources":["../src/cli/collecting-sink.ts","../src/cli/define-command.ts","../src/cli/migration/paths.ts","../src/cli/migration/contract-space.ts","../src/cli/migration/plan.ts","../src/cli/migration/preflight.ts","../src/cli/family.ts","../package.json","../src/cli/cli.ts"],"sourcesContent":["import type { Presentations } from \"@prisma/cli-engine\";\n\n/**\n * Collects a wrapped core function's `out`/`err` sink writes so a command\n * handler can present them through the engine instead of writing to the\n * real `process.stdout`/`process.stderr` directly (which would corrupt\n * `--json` mode and be invisible to `createTestCli`'s in-memory harness).\n */\nexport interface CollectingSink {\n readonly out: (line: string) => void;\n readonly err: (line: string) => void;\n /** Every line written to either sink, in order, newline-split. */\n lines(): readonly string[];\n /**\n * `lines()` joined back into text — the full collected log (progress\n * AND error detail, not just the `err`-only parts), for use as a\n * `CliStructuredError`'s `summary` on a nonzero exit. A wrapped function\n * writes its error explanation to `err` only after already writing\n * per-step progress to `out` (e.g. preflight's `\"<dir> … \"` / `\"FAILED\"`\n * pair) — using only the `err` writes would silently drop that context.\n */\n fullText(): string;\n}\n\n/**\n * Buffers writes the way a real stream does: a write with no trailing\n * newline (e.g. the wrapped functions' `out(\" ${dirName} … \")` followed\n * later by a separate `out(\"ok\\n\")` call) stays pending and joins with\n * whatever arrives next, rather than becoming its own line. Splitting each\n * `out`/`err` call independently — the first cut of this — silently broke\n * exactly this pattern in `preflight.ts`'s per-package progress line.\n */\nexport function createCollectingSink(): CollectingSink {\n const completedLines: string[] = [];\n let buffer = \"\";\n\n function push(text: string): void {\n buffer += text;\n const parts = buffer.split(\"\\n\");\n buffer = parts.pop() ?? \"\";\n for (const part of parts) {\n if (part.length > 0) completedLines.push(part);\n }\n }\n\n const lines = (): readonly string[] => (buffer.length > 0 ? [...completedLines, buffer] : completedLines);\n\n return {\n out: (text) => push(text),\n err: (text) => push(text),\n lines,\n fullText: () => lines().join(\"\\n\").trim(),\n };\n}\n\n/**\n * Renders collected lines as a `drawing` block (verbatim, no reflow) in\n * human mode; `stdout` stays empty since these commands carry no separate\n * machine-pipeable data channel beyond the human log itself.\n */\nexport function presentationsFromSink(sink: CollectingSink, exitCode: number): Presentations {\n const lines = sink.lines();\n return {\n human: () => (lines.length === 0 ? [] : [{ kind: \"drawing\", lines }]),\n stdout: () => [],\n json: () => ({ exitCode, lines }),\n next: () => [],\n };\n}\n","import type { PrismaNextConfig } from \"@prisma/orm-toolchain/config-loader\";\nimport { finalizeConfig } from \"@prisma/orm-toolchain/config-loader\";\nimport type {\n ArgsSpec,\n CommandDefinition,\n FlagSpec,\n Handler,\n HelpSpec,\n PositionalSpec,\n SpawnDeclarations,\n} from \"@prisma/cli-engine\";\nimport { defineCommand } from \"@prisma/cli-engine\";\nimport { CliStructuredError, notOk } from \"@prisma/cli-engine/protocol\";\nimport { ormConfigSection } from \"./config-section\";\n\nfunction normalizeError(error: unknown): CliStructuredError {\n if (CliStructuredError.is(error)) return error;\n return new CliStructuredError(\"IDB-CLI.UNEXPECTED_ERROR\", error instanceof Error ? error.message : String(error));\n}\n\nfunction finalizedConfigContext<TCtx extends { readonly cwd: string; readonly config: unknown }>(ctx: TCtx): TCtx {\n if (ctx.config === undefined) return ctx;\n return { ...ctx, config: finalizeConfig(ctx.config as PrismaNextConfig, ctx.cwd) };\n}\n\n/**\n * These 3 commands are IDB-specific (fake-indexeddb, IdbMigrationPlanner) —\n * pointing them at a non-IDB `prisma.config.ts` would fail confusingly deep\n * inside that logic rather than with a clear, actionable error. `orm`'s own\n * config section is family-agnostic by design (ADR 150), so this guard is\n * this shell's responsibility, not the section's.\n */\nfunction requireIdbFamily(config: PrismaNextConfig): void {\n const familyId = config.family?.familyId;\n if (familyId !== \"idb\") {\n throw new CliStructuredError(\n \"IDB-CLI.FAMILY_MISMATCH\",\n `config family is \"${familyId ?? \"unknown\"}\", expected \"idb\".`,\n {\n why: \"This CLI's migration commands operate on IDB-family projects only.\",\n nextActions: [\n {\n kind: \"edit-file\",\n label:\n \"Point --config at your browser-side prisma.config.ts (the one using @prisma-idb/family-idb/control)\",\n },\n ],\n }\n );\n }\n}\n\n/**\n * `defineCommand` pre-bound to the `orm` config section (see\n * `config-section.ts`) with a uniform error boundary and path\n * finalization, mirroring `@prisma/orm-toolchain`'s own `defineOrmCommand`\n * (`vendor/prisma/packages/1-framework/3-tooling/cli/src/orm/define-command.ts`):\n * the engine's config loader hands a command `contract.output`/\n * `migrations.dir` exactly as authored (usually relative); finalizing here,\n * once, means handlers always read absolute paths.\n */\nexport function defineIdbCommand<\n TFlags extends Record<string, FlagSpec<unknown>> = Record<never, FlagSpec<unknown>>,\n TPositionals extends Record<string, PositionalSpec<unknown>> = Record<never, PositionalSpec<unknown>>,\n TCode extends number = never,\n>(\n def: {\n readonly help: HelpSpec;\n readonly args?: ArgsSpec<TFlags, TPositionals>;\n readonly exitCodes?: Readonly<Record<TCode, string>>;\n readonly handler: Handler<TFlags, TPositionals, PrismaNextConfig, TCode, false, false>;\n } & SpawnDeclarations\n): CommandDefinition<TFlags, TPositionals, PrismaNextConfig, TCode, false, false> {\n return defineCommand<TFlags, TPositionals, PrismaNextConfig, TCode, false, false>({\n ...def,\n needs: { config: ormConfigSection },\n handler: async (args, ctx) => {\n try {\n const finalized = finalizedConfigContext(ctx);\n requireIdbFamily(finalized.config);\n return await def.handler(args, finalized);\n } catch (error) {\n return notOk(normalizeError(error));\n }\n },\n });\n}\n","import type { PrismaNextConfig } from \"@prisma/orm-toolchain/config-loader\";\nimport { CliStructuredError } from \"@prisma/cli-engine/protocol\";\nimport { resolve } from \"pathe\";\n\n/**\n * Resolves the contract/migrations paths a command needs from the already\n * finalized (absolute-paths) `orm` config section, with CLI flag overrides\n * winning when supplied. `ctx.config` arrives finalized by\n * `defineIdbCommand` (see `../define-command.ts`), so `config.contract.output`\n * and `config.migrations.dir` are already absolute — only an explicit\n * `--contract`/`--migrations-dir` flag (relative to `cwd`) needs resolving\n * here.\n */\nexport function requireContractPath(config: PrismaNextConfig, cwd: string, override: string | undefined): string {\n if (override !== undefined) return resolve(cwd, override);\n const output = config.contract?.output;\n if (output === undefined) {\n throw new CliStructuredError(\n \"IDB-CLI.CONTRACT_PATH_MISSING\",\n \"config.contract.output is required to resolve the contract path.\",\n {\n why: \"Neither --contract nor prisma.config.ts's orm.contract.output supplied a path.\",\n nextActions: [\n { kind: \"edit-file\", label: \"Set contract.output in prisma.config.ts, or pass --contract explicitly\" },\n ],\n }\n );\n }\n return output;\n}\n\n/**\n * `finalizeConfig` always resolves `migrations.dir` to an absolute path\n * (defaulting to `<configDir>/migrations` when unset); the fallback here is\n * defensive, not the expected path.\n */\nexport function resolveMigrationsDir(config: PrismaNextConfig, cwd: string, override: string | undefined): string {\n if (override !== undefined) return resolve(cwd, override);\n return config.migrations?.dir ?? resolve(cwd, \"migrations\");\n}\n","import { dirname, join, resolve } from \"pathe\";\nimport { flag } from \"@prisma/cli-engine\";\nimport { CliStructuredError, notOk, ok } from \"@prisma/cli-engine/protocol\";\nimport { createCollectingSink, presentationsFromSink } from \"../collecting-sink\";\nimport { defineIdbCommand } from \"../define-command\";\nimport { requireContractPath, resolveMigrationsDir } from \"./paths\";\n\nexport const migrationContractSpaceCommand = defineIdbCommand({\n help: {\n summary: \"Regenerate contract-space.generated.ts from migrations/app/\",\n description:\n \"Reads every migration package under <migrationsDir>/app/, validates the\\n\" +\n \"chain's connectivity, and emits a generated TypeScript module that bundles\\n\" +\n \"them into a ContractSpace.\",\n examples: [\"migration contract-space\", \"migration contract-space --out src/lib/prisma/contract-space.ts\"],\n },\n args: {\n flags: {\n contract: flag.string({\n brief: \"Path to contract.json (default: orm.contract.output)\",\n placeholder: \"path\",\n }),\n migrationsDir: flag.string({\n brief: \"Path to the migrations root (default: orm.migrations.dir)\",\n placeholder: \"path\",\n }),\n out: flag.string({\n brief: \"Output file path (default: colocated with the resolved contract.json)\",\n placeholder: \"path\",\n }),\n },\n },\n handler: async (args, ctx) => {\n const contractPath = requireContractPath(ctx.config, ctx.cwd, args.flags.contract);\n const migrationsDir = resolveMigrationsDir(ctx.config, ctx.cwd, args.flags.migrationsDir);\n const outPath =\n args.flags.out !== undefined\n ? resolve(ctx.cwd, args.flags.out)\n : join(dirname(contractPath), \"contract-space.generated.ts\");\n\n const { generateContractSpace } = await import(\"../../core/contract-space-codegen\");\n const sink = createCollectingSink();\n const exitCode = await generateContractSpace({\n migrationsDir,\n contractPath,\n outPath,\n out: sink.out,\n err: sink.err,\n });\n\n if (exitCode !== 0) {\n return notOk(\n new CliStructuredError(\"IDB-CLI.CONTRACT_SPACE_FAILED\", sink.fullText() || \"migration contract-space failed.\")\n );\n }\n return ok(ctx.present({ data: { exitCode, outPath } }, presentationsFromSink(sink, exitCode)));\n },\n});\n","import { flag } from \"@prisma/cli-engine\";\nimport { CliStructuredError, notOk, ok } from \"@prisma/cli-engine/protocol\";\nimport { createCollectingSink, presentationsFromSink } from \"../collecting-sink\";\nimport { defineIdbCommand } from \"../define-command\";\nimport { requireContractPath, resolveMigrationsDir } from \"./paths\";\n\nexport const migrationPlanCommand = defineIdbCommand({\n help: {\n summary: \"Plan the next IDB migration package (auto-detects baseline vs. incremental)\",\n description:\n \"Compares the emitted contract against the latest on-disk migration state and\\n\" +\n \"produces a new migration package with the required operations. Offline —\\n\" +\n \"never opens IndexedDB (there is no live database on the Node side to consult).\",\n examples: [\"migration plan\", \"migration plan --name add-posts\", \"migration plan --space idb-sync\"],\n },\n args: {\n flags: {\n name: flag.string({\n brief: 'Directory slug (default: \"baseline\" for a fresh space; required otherwise)',\n placeholder: \"slug\",\n }),\n space: flag.string({\n brief: 'Contract-space id (default: \"app\"). See ADR 212 for extension-space migrations.',\n placeholder: \"id\",\n }),\n contract: flag.string({\n brief: \"Path to contract.json (default: orm.contract.output)\",\n placeholder: \"path\",\n }),\n migrationsDir: flag.string({\n brief: \"Path to the migrations root (default: orm.migrations.dir)\",\n placeholder: \"path\",\n }),\n },\n },\n handler: async (args, ctx) => {\n const contractPath = requireContractPath(ctx.config, ctx.cwd, args.flags.contract);\n const migrationsDir = resolveMigrationsDir(ctx.config, ctx.cwd, args.flags.migrationsDir);\n\n const { migrationPlan } = await import(\"../../core/migration-plan\");\n const sink = createCollectingSink();\n const exitCode = await migrationPlan({\n migrationsDir,\n contractPath,\n out: sink.out,\n err: sink.err,\n ...(args.flags.name !== undefined && { name: args.flags.name }),\n ...(args.flags.space !== undefined && { spaceId: args.flags.space }),\n });\n\n if (exitCode === 2) {\n return notOk(\n new CliStructuredError(\n \"IDB-CLI.MIGRATION_NAME_REQUIRED\",\n sink.fullText() || \"--name <slug> is required when generating an incremental migration.\"\n )\n );\n }\n if (exitCode !== 0) {\n return notOk(\n new CliStructuredError(\"IDB-CLI.MIGRATION_PLAN_FAILED\", sink.fullText() || \"migration plan failed.\")\n );\n }\n return ok(ctx.present({ data: { exitCode } }, presentationsFromSink(sink, exitCode)));\n },\n});\n","import { flag } from \"@prisma/cli-engine\";\nimport { CliStructuredError, notOk, ok } from \"@prisma/cli-engine/protocol\";\nimport { createCollectingSink, presentationsFromSink } from \"../collecting-sink\";\nimport { defineIdbCommand } from \"../define-command\";\nimport { resolveMigrationsDir } from \"./paths\";\n\nexport const migrationPreflightCommand = defineIdbCommand({\n help: {\n summary: \"Validate the migration chain against fake-indexeddb\",\n description:\n \"Walks every migration package under <migrationsDir>/app/ in chain order,\\n\" +\n \"applying each package's ops.json against a fresh fake-indexeddb instance.\\n\" +\n \"Catches 'the chain doesn't apply cleanly' — not schema drift.\",\n examples: [\"migration preflight\"],\n },\n args: {\n flags: {\n migrationsDir: flag.string({\n brief: \"Path to the migrations root (default: orm.migrations.dir)\",\n placeholder: \"path\",\n }),\n },\n },\n handler: async (args, ctx) => {\n const migrationsDir = resolveMigrationsDir(ctx.config, ctx.cwd, args.flags.migrationsDir);\n\n const { runPreflight } = await import(\"../../core/preflight\");\n const sink = createCollectingSink();\n const exitCode = await runPreflight({ migrationsDir, out: sink.out, err: sink.err });\n\n if (exitCode !== 0) {\n return notOk(new CliStructuredError(\"IDB-CLI.PREFLIGHT_FAILED\", sink.fullText() || \"Preflight failed.\"));\n }\n return ok(ctx.present({ data: { exitCode } }, presentationsFromSink(sink, exitCode)));\n },\n});\n","import type { AnyCommand } from \"@prisma/cli-engine\";\nimport { defineCommandFamily } from \"@prisma/cli-engine\";\nimport { ormConfigSection } from \"./config-section\";\nimport { migrationContractSpaceCommand } from \"./migration/contract-space\";\nimport { migrationPlanCommand } from \"./migration/plan\";\nimport { migrationPreflightCommand } from \"./migration/preflight\";\n\nconst commands: Readonly<Record<string, AnyCommand>> = {\n \"migration plan\": migrationPlanCommand,\n \"migration contract-space\": migrationContractSpaceCommand,\n \"migration preflight\": migrationPreflightCommand,\n};\n\n/**\n * The unit this package contributes to a `@prisma/cli-engine` shell: its\n * config section (reused from the ORM family, see `config-section.ts`) and\n * its 3 commands. These have no generic equivalent — the real `prisma` CLI's\n * `migration new`/`plan`/etc. work against a live database or a SQL/Mongo\n * on-disk model; ours works against `fake-indexeddb` and a hand-rolled\n * contract-space convention.\n */\nexport const idbCommandFamily = defineCommandFamily({\n configSection: ormConfigSection,\n commands,\n});\n","","import type { Cli, HostProcess, Runtime } from \"@prisma/cli-engine\";\nimport { createCli, loadConfig } from \"@prisma/cli-engine\";\nimport { CliStructuredError } from \"@prisma/cli-engine/protocol\";\nimport packageJson from \"../../package.json\" with { type: \"json\" };\nimport { idbCommandFamily } from \"./family\";\n\nexport const BIN_NAME = \"prisma-idb\";\n\nexport const BIN_GROUPS = {\n migration: {\n brief: \"On-disk migration management commands\",\n description:\n \"Plan, bundle, and validate IDB migration packages. Migrations are\\n\" +\n \"contract-to-contract edges stored as versioned directories under migrations/.\",\n },\n} as const;\n\nexport function createIdbCli(): Cli {\n return createCli({\n name: BIN_NAME,\n version: packageJson.version,\n commandFamilies: [idbCommandFamily],\n groups: BIN_GROUPS,\n commands: idbCommandFamily.commands,\n help: {\n tagline: \"IDB-target tooling for Prisma 8\",\n description: \"Migration authoring for IndexedDB projects — no live database on the Node side to consult.\",\n },\n });\n}\n\n/** Everything environmental the engine is given, adapted from the host process once. */\nexport function runtimeFromProcess(proc: HostProcess): Runtime {\n return {\n stdout: { write: (text) => void proc.stdout.write(text) },\n stderr: { write: (text) => void proc.stderr.write(text) },\n stdin: proc.stdin,\n cwd: proc.cwd(),\n env: proc.env,\n isTty: {\n stdin: proc.stdin.isTTY === true,\n stdout: proc.stdout.isTTY === true,\n stderr: proc.stderr.isTTY === true,\n },\n host: {\n runtime: { name: \"node\", version: proc.version },\n platform: proc.platform,\n arch: proc.arch,\n },\n exit: (code) => proc.exit(code),\n onSignal: (callback) => {\n const onInterrupt = () => callback(\"SIGINT\");\n const onTerminate = () => callback(\"SIGTERM\");\n proc.on(\"SIGINT\", onInterrupt);\n proc.on(\"SIGTERM\", onTerminate);\n return () => {\n proc.off(\"SIGINT\", onInterrupt);\n proc.off(\"SIGTERM\", onTerminate);\n };\n },\n loadConfig: (configPath) => loadConfig(proc.cwd(), configPath),\n // Never reached: no command here declares `credentials`/`managesCredentials`,\n // so ctx.api is never constructed — required on Runtime regardless.\n managementApi: { baseUrl: \"https://api.prisma.io\" },\n };\n}\n\nconst STARTUP_FAILURE_EXIT_CODE = 1;\n\nfunction reportStartupFailure(proc: HostProcess, error: unknown): number {\n const normalized = CliStructuredError.is(error)\n ? error\n : new CliStructuredError(\"IDB-CLI.STARTUP_FAILURE\", error instanceof Error ? error.message : String(error));\n proc.stderr.write(`✘ [${normalized.code}] ${normalized.message}\\n`);\n return STARTUP_FAILURE_EXIT_CODE;\n}\n\n/** Parses, executes and settles one invocation; returns the exit code. */\nexport async function runIdbCli(proc: HostProcess): Promise<number> {\n try {\n return await createIdbCli().run(proc.argv.slice(2), runtimeFromProcess(proc));\n } catch (error) {\n return reportStartupFailure(proc, error);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAgCA,SAAgB,uBAAuC;CACrD,MAAM,iBAA2B,CAAC;CAClC,IAAI,SAAS;CAEb,SAAS,KAAK,MAAoB;EAChC,UAAU;EACV,MAAM,QAAQ,OAAO,MAAM,IAAI;EAC/B,SAAS,MAAM,IAAI,KAAK;EACxB,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,GAAG,eAAe,KAAK,IAAI;CAEjD;CAEA,MAAM,cAAkC,OAAO,SAAS,IAAI,CAAC,GAAG,gBAAgB,MAAM,IAAI;CAE1F,OAAO;EACL,MAAM,SAAS,KAAK,IAAI;EACxB,MAAM,SAAS,KAAK,IAAI;EACxB;EACA,gBAAgB,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK;CAC1C;AACF;;;;;;AAOA,SAAgB,sBAAsB,MAAsB,UAAiC;CAC3F,MAAM,QAAQ,KAAK,MAAM;CACzB,OAAO;EACL,aAAc,MAAM,WAAW,IAAI,CAAC,IAAI,CAAC;GAAE,MAAM;GAAW;EAAM,CAAC;EACnE,cAAc,CAAC;EACf,aAAa;GAAE;GAAU;EAAM;EAC/B,YAAY,CAAC;CACf;AACF;;;ACrDA,SAAS,eAAe,OAAoC;CAC1D,IAAI,mBAAmB,GAAG,KAAK,GAAG,OAAO;CACzC,OAAO,IAAI,mBAAmB,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAClH;AAEA,SAAS,uBAAwF,KAAiB;CAChH,IAAI,IAAI,WAAW,KAAA,GAAW,OAAO;CACrC,OAAO;EAAE,GAAG;EAAK,QAAQ,eAAe,IAAI,QAA4B,IAAI,GAAG;CAAE;AACnF;;;;;;;;AASA,SAAS,iBAAiB,QAAgC;CACxD,MAAM,WAAW,OAAO,QAAQ;CAChC,IAAI,aAAa,OACf,MAAM,IAAI,mBACR,2BACA,qBAAqB,YAAY,UAAU,qBAC3C;EACE,KAAK;EACL,aAAa,CACX;GACE,MAAM;GACN,OACE;EACJ,CACF;CACF,CACF;AAEJ;;;;;;;;;;AAWA,SAAgB,iBAKd,KAMgF;CAChF,OAAO,cAA2E;EAChF,GAAG;EACH,OAAO,EAAE,QAAQ,iBAAiB;EAClC,SAAS,OAAO,MAAM,QAAQ;GAC5B,IAAI;IACF,MAAM,YAAY,uBAAuB,GAAG;IAC5C,iBAAiB,UAAU,MAAM;IACjC,OAAO,MAAM,IAAI,QAAQ,MAAM,SAAS;GAC1C,SAAS,OAAO;IACd,OAAO,MAAM,eAAe,KAAK,CAAC;GACpC;EACF;CACF,CAAC;AACH;;;;;;;;;;;;ACzEA,SAAgB,oBAAoB,QAA0B,KAAa,UAAsC;CAC/G,IAAI,aAAa,KAAA,GAAW,OAAO,QAAQ,KAAK,QAAQ;CACxD,MAAM,SAAS,OAAO,UAAU;CAChC,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,mBACR,iCACA,oEACA;EACE,KAAK;EACL,aAAa,CACX;GAAE,MAAM;GAAa,OAAO;EAAyE,CACvG;CACF,CACF;CAEF,OAAO;AACT;;;;;;AAOA,SAAgB,qBAAqB,QAA0B,KAAa,UAAsC;CAChH,IAAI,aAAa,KAAA,GAAW,OAAO,QAAQ,KAAK,QAAQ;CACxD,OAAO,OAAO,YAAY,OAAO,QAAQ,KAAK,YAAY;AAC5D;;;AChCA,MAAa,gCAAgC,iBAAiB;CAC5D,MAAM;EACJ,SAAS;EACT,aACE;EAGF,UAAU,CAAC,4BAA4B,iEAAiE;CAC1G;CACA,MAAM,EACJ,OAAO;EACL,UAAU,KAAK,OAAO;GACpB,OAAO;GACP,aAAa;EACf,CAAC;EACD,eAAe,KAAK,OAAO;GACzB,OAAO;GACP,aAAa;EACf,CAAC;EACD,KAAK,KAAK,OAAO;GACf,OAAO;GACP,aAAa;EACf,CAAC;CACH,EACF;CACA,SAAS,OAAO,MAAM,QAAQ;EAC5B,MAAM,eAAe,oBAAoB,IAAI,QAAQ,IAAI,KAAK,KAAK,MAAM,QAAQ;EACjF,MAAM,gBAAgB,qBAAqB,IAAI,QAAQ,IAAI,KAAK,KAAK,MAAM,aAAa;EACxF,MAAM,UACJ,KAAK,MAAM,QAAQ,KAAA,IACf,QAAQ,IAAI,KAAK,KAAK,MAAM,GAAG,IAC/B,KAAK,QAAQ,YAAY,GAAG,6BAA6B;EAE/D,MAAM,EAAE,0BAA0B,MAAM,OAAO;EAC/C,MAAM,OAAO,qBAAqB;EAClC,MAAM,WAAW,MAAM,sBAAsB;GAC3C;GACA;GACA;GACA,KAAK,KAAK;GACV,KAAK,KAAK;EACZ,CAAC;EAED,IAAI,aAAa,GACf,OAAO,MACL,IAAI,mBAAmB,iCAAiC,KAAK,SAAS,KAAK,kCAAkC,CAC/G;EAEF,OAAO,GAAG,IAAI,QAAQ,EAAE,MAAM;GAAE;GAAU;EAAQ,EAAE,GAAG,sBAAsB,MAAM,QAAQ,CAAC,CAAC;CAC/F;AACF,CAAC;;;AGlDD,MAAM,WAAiD;CACrD,kBFFkC,iBAAiB;EACnD,MAAM;GACJ,SAAS;GACT,aACE;GAGF,UAAU;IAAC;IAAkB;IAAmC;GAAiC;EACnG;EACA,MAAM,EACJ,OAAO;GACL,MAAM,KAAK,OAAO;IAChB,OAAO;IACP,aAAa;GACf,CAAC;GACD,OAAO,KAAK,OAAO;IACjB,OAAO;IACP,aAAa;GACf,CAAC;GACD,UAAU,KAAK,OAAO;IACpB,OAAO;IACP,aAAa;GACf,CAAC;GACD,eAAe,KAAK,OAAO;IACzB,OAAO;IACP,aAAa;GACf,CAAC;EACH,EACF;EACA,SAAS,OAAO,MAAM,QAAQ;GAC5B,MAAM,eAAe,oBAAoB,IAAI,QAAQ,IAAI,KAAK,KAAK,MAAM,QAAQ;GACjF,MAAM,gBAAgB,qBAAqB,IAAI,QAAQ,IAAI,KAAK,KAAK,MAAM,aAAa;GAExF,MAAM,EAAE,kBAAkB,MAAM,OAAO;GACvC,MAAM,OAAO,qBAAqB;GAClC,MAAM,WAAW,MAAM,cAAc;IACnC;IACA;IACA,KAAK,KAAK;IACV,KAAK,KAAK;IACV,GAAI,KAAK,MAAM,SAAS,KAAA,KAAa,EAAE,MAAM,KAAK,MAAM,KAAK;IAC7D,GAAI,KAAK,MAAM,UAAU,KAAA,KAAa,EAAE,SAAS,KAAK,MAAM,MAAM;GACpE,CAAC;GAED,IAAI,aAAa,GACf,OAAO,MACL,IAAI,mBACF,mCACA,KAAK,SAAS,KAAK,qEACrB,CACF;GAEF,IAAI,aAAa,GACf,OAAO,MACL,IAAI,mBAAmB,iCAAiC,KAAK,SAAS,KAAK,wBAAwB,CACrG;GAEF,OAAO,GAAG,IAAI,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,sBAAsB,MAAM,QAAQ,CAAC,CAAC;EACtF;CACF,CEzDoB;CAClB,4BAA4B;CAC5B,uBDJuC,iBAAiB;EACxD,MAAM;GACJ,SAAS;GACT,aACE;GAGF,UAAU,CAAC,qBAAqB;EAClC;EACA,MAAM,EACJ,OAAO,EACL,eAAe,KAAK,OAAO;GACzB,OAAO;GACP,aAAa;EACf,CAAC,EACH,EACF;EACA,SAAS,OAAO,MAAM,QAAQ;GAC5B,MAAM,gBAAgB,qBAAqB,IAAI,QAAQ,IAAI,KAAK,KAAK,MAAM,aAAa;GAExF,MAAM,EAAE,iBAAiB,MAAM,OAAO;GACtC,MAAM,OAAO,qBAAqB;GAClC,MAAM,WAAW,MAAM,aAAa;IAAE;IAAe,KAAK,KAAK;IAAK,KAAK,KAAK;GAAI,CAAC;GAEnF,IAAI,aAAa,GACf,OAAO,MAAM,IAAI,mBAAmB,4BAA4B,KAAK,SAAS,KAAK,mBAAmB,CAAC;GAEzG,OAAO,GAAG,IAAI,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,sBAAsB,MAAM,QAAQ,CAAC,CAAC;EACtF;CACF,CCzByB;AACzB;;;;;;;;;AAUA,MAAa,mBAAmB,oBAAoB;CAClD,eAAe;CACf;AACF,CAAC;;;;;;AElBD,MAAa,WAAW;AAExB,MAAa,aAAa,EACxB,WAAW;CACT,OAAO;CACP,aACE;AAEJ,EACF;AAEA,SAAgB,eAAoB;CAClC,OAAO,UAAU;EACf,MAAM;EACGA;EACT,iBAAiB,CAAC,gBAAgB;EAClC,QAAQ;EACR,UAAU,iBAAiB;EAC3B,MAAM;GACJ,SAAS;GACT,aAAa;EACf;CACF,CAAC;AACH;;AAGA,SAAgB,mBAAmB,MAA4B;CAC7D,OAAO;EACL,QAAQ,EAAE,QAAQ,SAAS,KAAK,KAAK,OAAO,MAAM,IAAI,EAAE;EACxD,QAAQ,EAAE,QAAQ,SAAS,KAAK,KAAK,OAAO,MAAM,IAAI,EAAE;EACxD,OAAO,KAAK;EACZ,KAAK,KAAK,IAAI;EACd,KAAK,KAAK;EACV,OAAO;GACL,OAAO,KAAK,MAAM,UAAU;GAC5B,QAAQ,KAAK,OAAO,UAAU;GAC9B,QAAQ,KAAK,OAAO,UAAU;EAChC;EACA,MAAM;GACJ,SAAS;IAAE,MAAM;IAAQ,SAAS,KAAK;GAAQ;GAC/C,UAAU,KAAK;GACf,MAAM,KAAK;EACb;EACA,OAAO,SAAS,KAAK,KAAK,IAAI;EAC9B,WAAW,aAAa;GACtB,MAAM,oBAAoB,SAAS,QAAQ;GAC3C,MAAM,oBAAoB,SAAS,SAAS;GAC5C,KAAK,GAAG,UAAU,WAAW;GAC7B,KAAK,GAAG,WAAW,WAAW;GAC9B,aAAa;IACX,KAAK,IAAI,UAAU,WAAW;IAC9B,KAAK,IAAI,WAAW,WAAW;GACjC;EACF;EACA,aAAa,eAAe,WAAW,KAAK,IAAI,GAAG,UAAU;EAG7D,eAAe,EAAE,SAAS,wBAAwB;CACpD;AACF;AAEA,MAAM,4BAA4B;AAElC,SAAS,qBAAqB,MAAmB,OAAwB;CACvE,MAAM,aAAa,mBAAmB,GAAG,KAAK,IAC1C,QACA,IAAI,mBAAmB,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;CAC5G,KAAK,OAAO,MAAM,MAAM,WAAW,KAAK,IAAI,WAAW,QAAQ,GAAG;CAClE,OAAO;AACT;;AAGA,eAAsB,UAAU,MAAoC;CAClE,IAAI;EACF,OAAO,MAAM,aAAa,CAAC,CAAC,IAAI,KAAK,KAAK,MAAM,CAAC,GAAG,mBAAmB,IAAI,CAAC;CAC9E,SAAS,OAAO;EACd,OAAO,qBAAqB,MAAM,KAAK;CACzC;AACF"}
@@ -0,0 +1,128 @@
1
+ import { t as chainOrderByMetadata } from "./chain-order-CaHXo5jz.mjs";
2
+ import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
+ import { dirname, join, relative } from "pathe";
4
+ //#region src/core/contract-space-codegen.ts
5
+ /**
6
+ * Read every migration package under `<migrationsDir>/app/`, validate the
7
+ * chain's connectivity, then emit a generated TypeScript module that
8
+ * JSON-imports each package's `migration.json` + `ops.json` and assembles
9
+ * them into a `ContractSpace` via `contractSpaceFromJson`. The head ref
10
+ * is inlined as `{ hash, invariants }` derived from the last package's
11
+ * metadata — no on-disk `refs/head.json` is written.
12
+ *
13
+ * (We deliberately avoid writing `migrations/refs/` because the framework's
14
+ * contract-space layout treats top-level `migrations/<subdir>/` as a
15
+ * per-space directory; an orphan `refs/` collides with that. Extension
16
+ * packages — postgis, paradedb, etc. — DO write `migrations/refs/head.json`
17
+ * because their package root IS their single space and there's no
18
+ * ambiguity. App-level layouts use `migrations/app/` for the app space, and
19
+ * the head ref for the app space comes from the latest package's `to`.)
20
+ *
21
+ * Idempotent: re-running produces byte-identical output (modulo
22
+ * migration package list changes). Exit code 0 on success.
23
+ */
24
+ async function generateContractSpace(opts) {
25
+ const out = opts.out ?? ((line) => process.stdout.write(line));
26
+ const err = opts.err ?? ((line) => process.stderr.write(line));
27
+ const migrationsDir = opts.migrationsDir;
28
+ const appDir = join(migrationsDir, "app");
29
+ const contractPath = opts.contractPath;
30
+ const outPath = opts.outPath;
31
+ const packages = await loadPackages(appDir, err);
32
+ if (packages.length === 0) err(`Warning: no migration packages found in ${relative(process.cwd(), appDir)}/.\nThe generated module will have an empty migrations list, which breaks
33
+ \`createAutoMigratingIdbClient\` on a fresh database.
34
+ Run \`prisma-idb migration plan\` first to create the initial migration.
35
+
36
+ `);
37
+ const outDir = dirname(outPath);
38
+ await mkdir(outDir, { recursive: true });
39
+ const source = renderModule({
40
+ outDir,
41
+ contractPath,
42
+ appDir,
43
+ packages
44
+ });
45
+ await writeFile(outPath, source, "utf-8");
46
+ out(`Wrote ${outPath} (${packages.length} migration${packages.length === 1 ? "" : "s"})\n`);
47
+ return 0;
48
+ }
49
+ async function loadPackages(appDir, err) {
50
+ let dirs;
51
+ try {
52
+ dirs = (await readdir(appDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
53
+ } catch (dirErr) {
54
+ if (dirErr.code === "ENOENT") return [];
55
+ throw dirErr;
56
+ }
57
+ const unordered = /* @__PURE__ */ new Map();
58
+ for (const dirName of dirs) {
59
+ const metaPath = join(appDir, dirName, "migration.json");
60
+ const opsPath = join(appDir, dirName, "ops.json");
61
+ try {
62
+ const metaRaw = await readFile(metaPath, "utf-8");
63
+ const metadata = JSON.parse(metaRaw);
64
+ await readFile(opsPath, "utf-8");
65
+ unordered.set(dirName, {
66
+ dirName,
67
+ metadata
68
+ });
69
+ } catch (loadErr) {
70
+ err(`Skipping ${dirName}: missing or unreadable migration.json/ops.json (${loadErr.message})\n`);
71
+ }
72
+ }
73
+ return chainOrderByMetadata(unordered);
74
+ }
75
+ function renderModule(input) {
76
+ const contractImportPath = toModuleSpecifier(relative(input.outDir, input.contractPath));
77
+ const importLines = [
78
+ "// THIS FILE IS AUTO-GENERATED — do not edit by hand.",
79
+ "// Regenerate with: prisma-idb migration contract-space",
80
+ "",
81
+ `import type { Contract } from "${contractImportPath.replace(/\.json$/i, "")}";`,
82
+ "import { contractSpaceFromJson } from \"@prisma/orm-toolchain/migration-tools/spaces\";",
83
+ `import contractJson from "${contractImportPath}" with { type: "json" };`
84
+ ];
85
+ for (const pkg of input.packages) {
86
+ const id = identFromDir(pkg.dirName);
87
+ const metaPath = toModuleSpecifier(relative(input.outDir, join(input.appDir, pkg.dirName, "migration.json")));
88
+ const opsPath = toModuleSpecifier(relative(input.outDir, join(input.appDir, pkg.dirName, "ops.json")));
89
+ importLines.push(`import ${id}_meta from "${metaPath}" with { type: "json" };`);
90
+ importLines.push(`import ${id}_ops from "${opsPath}" with { type: "json" };`);
91
+ }
92
+ const last = input.packages[input.packages.length - 1];
93
+ const headRefLiteral = last === void 0 ? "{ hash: \"\", invariants: [] as const }" : `{ hash: ${identFromDir(last.dirName)}_meta.to, invariants: (${identFromDir(last.dirName)}_meta.providedInvariants ?? []) as readonly string[] }`;
94
+ const migrationsBody = input.packages.length === 0 ? " migrations: []," : [
95
+ " migrations: [",
96
+ input.packages.map((pkg) => {
97
+ const id = identFromDir(pkg.dirName);
98
+ return ` { dirName: ${JSON.stringify(pkg.dirName)}, metadata: ${id}_meta, ops: ${id}_ops },`;
99
+ }).join("\n"),
100
+ " ],"
101
+ ].join("\n");
102
+ return [
103
+ ...importLines,
104
+ "",
105
+ "export const contractSpace = contractSpaceFromJson<Contract>({",
106
+ " contractJson,",
107
+ migrationsBody,
108
+ ` headRef: ${headRefLiteral},`,
109
+ "});",
110
+ ""
111
+ ].join("\n");
112
+ }
113
+ function identFromDir(dirName) {
114
+ return `mig_${dirName.replace(/[^a-zA-Z0-9_]/g, "_")}`;
115
+ }
116
+ /**
117
+ * Coerce a `pathe` relative path into a TypeScript module specifier.
118
+ * - Adds a leading `./` if the path doesn't already start with `.` or `/`.
119
+ * - Leaves absolute and parent-relative paths alone.
120
+ */
121
+ function toModuleSpecifier(p) {
122
+ if (p.startsWith(".") || p.startsWith("/")) return p;
123
+ return `./${p}`;
124
+ }
125
+ //#endregion
126
+ export { generateContractSpace };
127
+
128
+ //# sourceMappingURL=contract-space-codegen--gfuyap2.mjs.map