@metaobjectsdev/cli 1.0.5-rc.9 → 1.0.7-rc.1

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 (43) hide show
  1. package/README.md +1 -0
  2. package/dist/src/commands/export.d.ts.map +1 -1
  3. package/dist/src/commands/export.js +18 -3
  4. package/dist/src/commands/export.js.map +1 -1
  5. package/dist/src/commands/migrate.d.ts +1 -1
  6. package/dist/src/commands/migrate.d.ts.map +1 -1
  7. package/dist/src/commands/migrate.js +72 -17
  8. package/dist/src/commands/migrate.js.map +1 -1
  9. package/dist/src/commands/verify.js +1 -1
  10. package/dist/src/commands/verify.js.map +1 -1
  11. package/dist/src/index.d.ts.map +1 -1
  12. package/dist/src/index.js +2 -0
  13. package/dist/src/index.js.map +1 -1
  14. package/dist/src/lib/args.d.ts +11 -0
  15. package/dist/src/lib/args.d.ts.map +1 -1
  16. package/dist/src/lib/args.js +35 -0
  17. package/dist/src/lib/args.js.map +1 -1
  18. package/dist/src/lib/catalog-listing.d.ts +5 -0
  19. package/dist/src/lib/catalog-listing.d.ts.map +1 -1
  20. package/dist/src/lib/catalog-listing.js +10 -0
  21. package/dist/src/lib/catalog-listing.js.map +1 -1
  22. package/dist/src/lib/config.d.ts +3 -0
  23. package/dist/src/lib/config.d.ts.map +1 -1
  24. package/dist/src/lib/config.js +1 -0
  25. package/dist/src/lib/config.js.map +1 -1
  26. package/dist/src/lib/kysely.d.ts +12 -0
  27. package/dist/src/lib/kysely.d.ts.map +1 -1
  28. package/dist/src/lib/kysely.js +33 -4
  29. package/dist/src/lib/kysely.js.map +1 -1
  30. package/dist/src/lib/output.d.ts +7 -0
  31. package/dist/src/lib/output.d.ts.map +1 -1
  32. package/dist/src/lib/output.js +6 -1
  33. package/dist/src/lib/output.js.map +1 -1
  34. package/package.json +10 -10
  35. package/src/commands/export.ts +18 -5
  36. package/src/commands/migrate.ts +76 -18
  37. package/src/commands/verify.ts +1 -1
  38. package/src/index.ts +2 -0
  39. package/src/lib/args.ts +43 -0
  40. package/src/lib/catalog-listing.ts +11 -0
  41. package/src/lib/config.ts +4 -0
  42. package/src/lib/kysely.ts +38 -8
  43. package/src/lib/output.ts +13 -1
package/src/lib/kysely.ts CHANGED
@@ -1,7 +1,43 @@
1
1
  import { Kysely } from "kysely";
2
2
  import { BunSqliteDialect, isBun } from "./bun-sqlite-dialect.js";
3
+ import { sep } from "node:path";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { installCommand } from "./pm-detect.js";
4
6
 
7
+ /**
8
+ * The directory a dynamic `import()` of a DB driver resolves from: the project whose
9
+ * `node_modules` holds `@metaobjectsdev/cli`. `undefined` when the CLI is not running from
10
+ * an installed package (a source checkout), where there is no one place to name.
11
+ */
12
+ export function cliInstallRoot(moduleUrl: string): string | undefined {
13
+ const path = moduleUrl.startsWith("file:") ? fileURLToPath(moduleUrl) : moduleUrl;
14
+ const marker = `${sep}node_modules${sep}@metaobjectsdev${sep}`;
15
+ const i = path.lastIndexOf(marker);
16
+ return i === -1 ? undefined : path.slice(0, i);
17
+ }
18
+
19
+ /**
20
+ * A missing driver says WHERE it must be installed. In a monorepo the package that
21
+ * depends on the CLI need not be the one that runs the app, and installing the driver in
22
+ * the wrong one changes nothing.
23
+ */
24
+ export function missingDriverMessage(
25
+ dialect: string, pkg: string, root: string | undefined, cmd: string,
26
+ ): string {
27
+ const where = root === undefined
28
+ ? "where @metaobjectsdev/cli is installed"
29
+ : `where @metaobjectsdev/cli is installed (${root})`;
30
+ return `dialect '${dialect}' requires '${pkg}', resolvable from ${where} — in a monorepo, ` +
31
+ `the package that depends on the CLI, not necessarily the one that runs your app. ` +
32
+ `Install it there: '${cmd}'`;
33
+ }
34
+
35
+ async function missingDriver(dialect: string, pkg: string): Promise<Error> {
36
+ const root = cliInstallRoot(import.meta.url);
37
+ const cmd = await installCommand(pkg, root ?? process.cwd());
38
+ return new Error(missingDriverMessage(dialect, pkg, root, cmd));
39
+ }
40
+
5
41
  export type Dialect = "sqlite" | "postgres" | "d1";
6
42
 
7
43
  export interface KyselyHandle {
@@ -81,10 +117,7 @@ export async function buildKyselyFromUrl(
81
117
  const mod = await import("@libsql/kysely-libsql");
82
118
  LibsqlDialect = mod.LibsqlDialect as unknown as LibsqlDialectCtor;
83
119
  } catch {
84
- const cmd = await installCommand("@libsql/kysely-libsql", process.cwd());
85
- throw new Error(
86
- `dialect 'sqlite' requires '@libsql/kysely-libsql'; install it: '${cmd}'`,
87
- );
120
+ throw await missingDriver("sqlite", "@libsql/kysely-libsql");
88
121
  }
89
122
  sqliteDialect = new LibsqlDialect({ url });
90
123
  }
@@ -110,10 +143,7 @@ export async function buildKyselyFromUrl(
110
143
  pg = await import("pg") as unknown as PgPoolModule;
111
144
  ({ PostgresDialect } = await import("kysely"));
112
145
  } catch {
113
- const cmd = await installCommand("pg", process.cwd());
114
- throw new Error(
115
- `dialect 'postgres' requires 'pg'; install it: '${cmd}'`,
116
- );
146
+ throw await missingDriver("postgres", "pg");
117
147
  }
118
148
  const PoolCtor = pg.Pool ?? pg.default?.Pool;
119
149
  if (PoolCtor === undefined) {
package/src/lib/output.ts CHANGED
@@ -181,6 +181,12 @@ export interface MigrateResultShape {
181
181
  * that cannot work.
182
182
  */
183
183
  format?: "default" | "flyway";
184
+ /**
185
+ * Data-hazard warnings: changes that apply to an empty table and fail on a populated one.
186
+ * Text output already carries them on stderr; JSON/toon carry them here so a programmatic
187
+ * caller sees the risk before applying.
188
+ */
189
+ warnings?: string[];
184
190
  }
185
191
 
186
192
  export function formatMigrateResult(result: MigrateResultShape, _opts: FormatOptions): string {
@@ -298,6 +304,7 @@ export function migrateResultToData(result: MigrateResultShape): {
298
304
  written: string[];
299
305
  summary: string;
300
306
  help: string[];
307
+ warnings?: string[];
301
308
  } {
302
309
  const changeEntries = Object.entries(result.changeCounts).filter(([, v]) => v > 0);
303
310
  const changes = changeEntries.map(([kind, count]) => ({ kind, count }));
@@ -352,7 +359,12 @@ export function migrateResultToData(result: MigrateResultShape): {
352
359
  help = ["re-run with --slug <name> to write the migration"];
353
360
  }
354
361
 
355
- return { changes, written: result.writtenPaths, summary, help };
362
+ const warnings = result.warnings ?? [];
363
+ return {
364
+ changes, written: result.writtenPaths, summary, help,
365
+ // Only when present, so a run with no hazard keeps its existing shape.
366
+ ...(warnings.length > 0 ? { warnings } : {}),
367
+ };
356
368
  }
357
369
 
358
370
  export function formatMigrateResultToon(result: MigrateResultShape): string {