turbine-orm 0.50.0 → 0.51.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 (186) hide show
  1. package/README.md +66 -66
  2. package/dist/adapters/cockroachdb.d.ts +5 -5
  3. package/dist/adapters/cockroachdb.js +10 -10
  4. package/dist/adapters/index.d.ts +5 -5
  5. package/dist/adapters/index.js +7 -7
  6. package/dist/adapters/yugabytedb.d.ts +7 -7
  7. package/dist/adapters/yugabytedb.js +10 -10
  8. package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
  9. package/dist/cjs/adapters/cockroachdb.js +10 -10
  10. package/dist/cjs/adapters/index.d.ts +5 -5
  11. package/dist/cjs/adapters/index.js +7 -7
  12. package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
  13. package/dist/cjs/adapters/yugabytedb.js +10 -10
  14. package/dist/cjs/cli/config.d.ts +13 -2
  15. package/dist/cjs/cli/config.js +3 -2
  16. package/dist/cjs/cli/destructive.d.ts +1 -1
  17. package/dist/cjs/cli/destructive.js +1 -1
  18. package/dist/cjs/cli/index.d.ts +10 -10
  19. package/dist/cjs/cli/index.js +49 -45
  20. package/dist/cjs/cli/loader.d.ts +7 -7
  21. package/dist/cjs/cli/loader.js +9 -9
  22. package/dist/cjs/cli/mcp.js +4 -4
  23. package/dist/cjs/cli/migrate.d.ts +5 -5
  24. package/dist/cjs/cli/migrate.js +11 -11
  25. package/dist/cjs/cli/studio-ui.generated.js +1 -1
  26. package/dist/cjs/cli/ui.d.ts +2 -2
  27. package/dist/cjs/cli/ui.js +2 -2
  28. package/dist/cjs/client.d.ts +49 -38
  29. package/dist/cjs/client.js +57 -56
  30. package/dist/cjs/dialect.d.ts +62 -18
  31. package/dist/cjs/dialect.js +40 -2
  32. package/dist/cjs/errors.d.ts +5 -5
  33. package/dist/cjs/errors.js +11 -11
  34. package/dist/cjs/generate.d.ts +6 -6
  35. package/dist/cjs/generate.js +31 -29
  36. package/dist/cjs/index-advisor.d.ts +5 -5
  37. package/dist/cjs/index-advisor.js +0 -0
  38. package/dist/cjs/index.d.ts +1 -1
  39. package/dist/cjs/index.js +7 -7
  40. package/dist/cjs/introspect.d.ts +35 -9
  41. package/dist/cjs/introspect.js +83 -32
  42. package/dist/cjs/mssql.d.ts +11 -11
  43. package/dist/cjs/mssql.js +64 -29
  44. package/dist/cjs/mysql.d.ts +8 -8
  45. package/dist/cjs/mysql.js +61 -23
  46. package/dist/cjs/nested-write.d.ts +21 -2
  47. package/dist/cjs/nested-write.js +51 -14
  48. package/dist/cjs/optional-peer-import.cjs +7 -7
  49. package/dist/cjs/optional-peer-import.d.cts +7 -7
  50. package/dist/cjs/pipeline-submittable.d.ts +2 -2
  51. package/dist/cjs/pipeline-submittable.js +6 -6
  52. package/dist/cjs/pipeline.d.ts +1 -1
  53. package/dist/cjs/pipeline.js +4 -4
  54. package/dist/cjs/powdb-introspect.d.ts +1 -1
  55. package/dist/cjs/powdb-introspect.js +1 -1
  56. package/dist/cjs/powdb.d.ts +28 -28
  57. package/dist/cjs/powdb.js +66 -66
  58. package/dist/cjs/powql.d.ts +27 -27
  59. package/dist/cjs/powql.js +73 -52
  60. package/dist/cjs/query/aggregates.d.ts +1 -1
  61. package/dist/cjs/query/aggregates.js +5 -5
  62. package/dist/cjs/query/batched-loader.d.ts +11 -11
  63. package/dist/cjs/query/batched-loader.js +24 -24
  64. package/dist/cjs/query/builder.d.ts +39 -21
  65. package/dist/cjs/query/builder.js +99 -57
  66. package/dist/cjs/query/compound-unique.d.ts +1 -1
  67. package/dist/cjs/query/compound-unique.js +0 -0
  68. package/dist/cjs/query/deferred.d.ts +12 -6
  69. package/dist/cjs/query/deferred.js +1 -1
  70. package/dist/cjs/query/filters.d.ts +31 -11
  71. package/dist/cjs/query/filters.js +67 -14
  72. package/dist/cjs/query/index.d.ts +1 -1
  73. package/dist/cjs/query/index.js +1 -1
  74. package/dist/cjs/query/relations.d.ts +9 -9
  75. package/dist/cjs/query/relations.js +164 -57
  76. package/dist/cjs/query/types.d.ts +86 -35
  77. package/dist/cjs/query/types.js +1 -1
  78. package/dist/cjs/query/utils.d.ts +27 -10
  79. package/dist/cjs/query/utils.js +86 -14
  80. package/dist/cjs/query/where.d.ts +47 -28
  81. package/dist/cjs/query/where.js +130 -31
  82. package/dist/cjs/query/writes.d.ts +24 -5
  83. package/dist/cjs/query/writes.js +102 -13
  84. package/dist/cjs/realtime.d.ts +7 -7
  85. package/dist/cjs/realtime.js +9 -9
  86. package/dist/cjs/schema-builder.d.ts +18 -7
  87. package/dist/cjs/schema-builder.js +17 -10
  88. package/dist/cjs/schema-metadata.d.ts +3 -3
  89. package/dist/cjs/schema-metadata.js +9 -9
  90. package/dist/cjs/schema-sql.d.ts +9 -9
  91. package/dist/cjs/schema-sql.js +20 -20
  92. package/dist/cjs/schema.d.ts +19 -9
  93. package/dist/cjs/schema.js +6 -6
  94. package/dist/cjs/serverless.d.ts +15 -15
  95. package/dist/cjs/serverless.js +16 -16
  96. package/dist/cjs/sqlite.d.ts +8 -8
  97. package/dist/cjs/sqlite.js +53 -22
  98. package/dist/cjs/typed-sql.d.ts +4 -4
  99. package/dist/cjs/typed-sql.js +5 -5
  100. package/dist/cli/config.d.ts +13 -2
  101. package/dist/cli/config.js +3 -2
  102. package/dist/cli/destructive.d.ts +1 -1
  103. package/dist/cli/destructive.js +1 -1
  104. package/dist/cli/index.d.ts +10 -10
  105. package/dist/cli/index.js +49 -45
  106. package/dist/cli/loader.d.ts +7 -7
  107. package/dist/cli/loader.js +9 -9
  108. package/dist/cli/mcp.js +4 -4
  109. package/dist/cli/migrate.d.ts +5 -5
  110. package/dist/cli/migrate.js +11 -11
  111. package/dist/cli/studio-ui.generated.js +1 -1
  112. package/dist/cli/ui.d.ts +2 -2
  113. package/dist/cli/ui.js +2 -2
  114. package/dist/client.d.ts +49 -38
  115. package/dist/client.js +57 -56
  116. package/dist/dialect.d.ts +62 -18
  117. package/dist/dialect.js +40 -2
  118. package/dist/errors.d.ts +5 -5
  119. package/dist/errors.js +11 -11
  120. package/dist/generate.d.ts +6 -6
  121. package/dist/generate.js +31 -29
  122. package/dist/index-advisor.d.ts +5 -5
  123. package/dist/index-advisor.js +0 -0
  124. package/dist/index.d.ts +1 -1
  125. package/dist/index.js +7 -7
  126. package/dist/introspect.d.ts +35 -9
  127. package/dist/introspect.js +82 -32
  128. package/dist/mssql.d.ts +11 -11
  129. package/dist/mssql.js +64 -29
  130. package/dist/mysql.d.ts +8 -8
  131. package/dist/mysql.js +61 -23
  132. package/dist/nested-write.d.ts +21 -2
  133. package/dist/nested-write.js +51 -14
  134. package/dist/optional-peer-import.cjs +7 -7
  135. package/dist/optional-peer-import.d.cts +7 -7
  136. package/dist/pipeline-submittable.d.ts +2 -2
  137. package/dist/pipeline-submittable.js +6 -6
  138. package/dist/pipeline.d.ts +1 -1
  139. package/dist/pipeline.js +4 -4
  140. package/dist/powdb-introspect.d.ts +1 -1
  141. package/dist/powdb-introspect.js +1 -1
  142. package/dist/powdb.d.ts +28 -28
  143. package/dist/powdb.js +66 -66
  144. package/dist/powql.d.ts +27 -27
  145. package/dist/powql.js +73 -52
  146. package/dist/query/aggregates.d.ts +1 -1
  147. package/dist/query/aggregates.js +5 -5
  148. package/dist/query/batched-loader.d.ts +11 -11
  149. package/dist/query/batched-loader.js +24 -24
  150. package/dist/query/builder.d.ts +39 -21
  151. package/dist/query/builder.js +100 -58
  152. package/dist/query/compound-unique.d.ts +1 -1
  153. package/dist/query/compound-unique.js +0 -0
  154. package/dist/query/deferred.d.ts +12 -6
  155. package/dist/query/deferred.js +1 -1
  156. package/dist/query/filters.d.ts +31 -11
  157. package/dist/query/filters.js +66 -13
  158. package/dist/query/index.d.ts +1 -1
  159. package/dist/query/index.js +1 -1
  160. package/dist/query/relations.d.ts +9 -9
  161. package/dist/query/relations.js +165 -58
  162. package/dist/query/types.d.ts +86 -35
  163. package/dist/query/types.js +1 -1
  164. package/dist/query/utils.d.ts +27 -10
  165. package/dist/query/utils.js +84 -14
  166. package/dist/query/where.d.ts +47 -28
  167. package/dist/query/where.js +129 -32
  168. package/dist/query/writes.d.ts +24 -5
  169. package/dist/query/writes.js +101 -13
  170. package/dist/realtime.d.ts +7 -7
  171. package/dist/realtime.js +9 -9
  172. package/dist/schema-builder.d.ts +18 -7
  173. package/dist/schema-builder.js +17 -10
  174. package/dist/schema-metadata.d.ts +3 -3
  175. package/dist/schema-metadata.js +9 -9
  176. package/dist/schema-sql.d.ts +9 -9
  177. package/dist/schema-sql.js +20 -20
  178. package/dist/schema.d.ts +19 -9
  179. package/dist/schema.js +6 -6
  180. package/dist/serverless.d.ts +15 -15
  181. package/dist/serverless.js +16 -16
  182. package/dist/sqlite.d.ts +8 -8
  183. package/dist/sqlite.js +53 -22
  184. package/dist/typed-sql.d.ts +4 -4
  185. package/dist/typed-sql.js +5 -5
  186. package/package.json +2 -2
package/dist/cli/index.js CHANGED
@@ -3,21 +3,21 @@
3
3
  * turbine-orm CLI
4
4
  *
5
5
  * Commands:
6
- * turbine init — Initialize a Turbine project
7
- * turbine generate | pull — Introspect database and generate TypeScript types
6
+ * turbine init , Initialize a Turbine project
7
+ * turbine generate | pull , Introspect database and generate TypeScript types
8
8
  * turbine migrate-from-prisma - Parse a schema.prisma and emit a Prisma->Turbine name map + report
9
9
  * turbine push - Apply schema-builder definitions to database (destructive ops gated)
10
10
  * turbine migrate create <name> - Create a new SQL migration file (--auto | --from-diff | --recipe <name>)
11
- * turbine migrate up — Apply pending migrations
12
- * turbine migrate deploy — Apply pending migrations without prompts
13
- * turbine migrate down — Rollback last migration
14
- * turbine migrate status — Show migration status
15
- * turbine seed — Run seed file
16
- * turbine status — Show schema summary
11
+ * turbine migrate up , Apply pending migrations
12
+ * turbine migrate deploy , Apply pending migrations without prompts
13
+ * turbine migrate down , Rollback last migration
14
+ * turbine migrate status , Show migration status
15
+ * turbine seed , Run seed file
16
+ * turbine status , Show schema summary
17
17
  * turbine doctor - Cost-aware missing-FK-index triage (--fix, --json, --no-concurrently, --unused, --audit)
18
18
  * turbine studio : Launch local read-only web UI (--demo for a seeded sample DB)
19
- * turbine mcp — Start read-only MCP server over JSON-RPC stdio
20
- * turbine observe — Launch metrics dashboard (requires TURBINE_OBSERVE_URL)
19
+ * turbine mcp , Start read-only MCP server over JSON-RPC stdio
20
+ * turbine observe , Launch metrics dashboard (requires TURBINE_OBSERVE_URL)
21
21
  *
22
22
  * Usage:
23
23
  * DATABASE_URL=postgres://... npx turbine generate
@@ -228,7 +228,7 @@ export function parseArgs(argv = process.argv.slice(2)) {
228
228
  return result;
229
229
  }
230
230
  // ---------------------------------------------------------------------------
231
- // TypeScript loader — user-facing error helper
231
+ // TypeScript loader, user-facing error helper
232
232
  // ---------------------------------------------------------------------------
233
233
  /**
234
234
  * Print a friendly error explaining how to install tsx, then exit.
@@ -244,7 +244,7 @@ function failMissingTsLoader(filePath, reason) {
244
244
  }
245
245
  else if (reason === 'failed') {
246
246
  // tsx IS installed but registering its loader threw. Report the real
247
- // cause — telling the user to install tsx here would be a misdiagnosis.
247
+ // cause, telling the user to install tsx here would be a misdiagnosis.
248
248
  console.log(` ${dim('tsx is installed, but registering its TypeScript loader failed:')}`);
249
249
  newline();
250
250
  console.log(` ${getTsLoaderError() ?? '(unknown error)'}`);
@@ -758,6 +758,7 @@ async function runInitGenerate(config, url) {
758
758
  schema: config.schema,
759
759
  include: config.include.length ? config.include : undefined,
760
760
  exclude: config.exclude.length ? config.exclude : undefined,
761
+ relationNames: config.relationNames,
761
762
  });
762
763
  spinner.succeed(`Found ${bold(String(Object.keys(schema.tables).length))} tables`);
763
764
  const genSpinner = new Spinner('Generating TypeScript client').start();
@@ -1065,7 +1066,7 @@ async function cmdGenerate(args, config) {
1065
1066
  const url = requireUrl(config);
1066
1067
  const startTime = performance.now();
1067
1068
  // Guard: `schema` is the Postgres NAMESPACE to introspect (default `public`),
1068
- // NOT the path to your schema-builder file — that goes in `schemaFile`. If the
1069
+ // NOT the path to your schema-builder file, that goes in `schemaFile`. If the
1069
1070
  // configured `schema` looks like a file path, introspection would silently
1070
1071
  // match zero tables and emit an empty client. Fail loudly instead.
1071
1072
  if (!args.allowEmpty && looksLikeSchemaFilePath(config.schema)) {
@@ -1075,7 +1076,7 @@ async function cmdGenerate(args, config) {
1075
1076
  console.log(` ${dim('The path to your defineSchema() file belongs in')} ${cyan('schemaFile')}${dim('.')}`);
1076
1077
  newline();
1077
1078
  console.log(` ${dim('Fix your')} ${cyan('turbine.config.ts')}${dim(':')}`);
1078
- console.log(` ${green('schema:')} ${cyan("'public'")}${dim(", // or omit — introspects the 'public' schema")}`);
1079
+ console.log(` ${green('schema:')} ${cyan("'public'")}${dim(", // or omit, introspects the 'public' schema")}`);
1079
1080
  console.log(` ${green('schemaFile:')} ${cyan(`'${config.schema}'`)}${dim(', // your defineSchema() file (used by `turbine push`)')}`);
1080
1081
  newline();
1081
1082
  console.log(` ${dim('Re-run with')} ${cyan('--allow-empty')} ${dim('to introspect this literal schema name anyway.')}`);
@@ -1094,6 +1095,7 @@ async function cmdGenerate(args, config) {
1094
1095
  schema: config.schema,
1095
1096
  include: config.include.length ? config.include : undefined,
1096
1097
  exclude: config.exclude.length ? config.exclude : undefined,
1098
+ relationNames: config.relationNames,
1097
1099
  includeViews: args.includeViews,
1098
1100
  legacyToManyUniques: config.legacyToManyUniques,
1099
1101
  onDefaultTableExclusion: (tables) => skippedInternalTables.push(...tables),
@@ -1113,13 +1115,13 @@ async function cmdGenerate(args, config) {
1113
1115
  // instead of silently emitting an empty typed client.
1114
1116
  if (tableNames.length === 0 && !args.allowEmpty) {
1115
1117
  newline();
1116
- error(`Introspection matched 0 tables in schema ${cyan(config.schema)} — refusing to generate an empty client.`);
1118
+ error(`Introspection matched 0 tables in schema ${cyan(config.schema)}, refusing to generate an empty client.`);
1117
1119
  newline();
1118
1120
  console.log(` ${dim('Common causes:')}`);
1119
1121
  console.log(` ${dim('•')} ${cyan('schema')} ${dim('points at the wrong Postgres namespace')} ${dim('(it is the schema NAME, default')} ${cyan('public')}${dim(').')}`);
1120
1122
  console.log(` ${dim('•')} ${dim('You meant to set')} ${cyan('schemaFile')} ${dim('(your defineSchema() file), not')} ${cyan('schema')}${dim('.')}`);
1121
1123
  console.log(` ${dim('•')} ${cyan('include')}/${cyan('exclude')} ${dim('filtered out every table.')}`);
1122
- console.log(` ${dim('•')} ${dim('The database has no tables yet — run')} ${cyan('turbine push')} ${dim('or a migration first.')}`);
1124
+ console.log(` ${dim('•')} ${dim('The database has no tables yet, run')} ${cyan('turbine push')} ${dim('or a migration first.')}`);
1123
1125
  newline();
1124
1126
  console.log(` ${dim('If an empty client is genuinely what you want, re-run with')} ${cyan('--allow-empty')}${dim('.')}`);
1125
1127
  newline();
@@ -1246,6 +1248,7 @@ async function cmdMigrateFromPrisma(args, config) {
1246
1248
  includeViews: true,
1247
1249
  // Inherit the shared bookkeeping-table exclusions (Turbine + Prisma).
1248
1250
  exclude: [...new Set([...config.exclude, ...DEFAULT_EXCLUDED_TABLES])],
1251
+ relationNames: config.relationNames,
1249
1252
  });
1250
1253
  spinner.succeed(`Introspected ${bold(String(Object.keys(schemaMeta.tables).length))} tables`);
1251
1254
  }
@@ -1381,7 +1384,7 @@ async function cmdPush(args, config) {
1381
1384
  newline();
1382
1385
  }
1383
1386
  if (args.dryRun) {
1384
- info('Dry run — no changes applied.');
1387
+ info('Dry run, no changes applied.');
1385
1388
  newline();
1386
1389
  return;
1387
1390
  }
@@ -1430,7 +1433,7 @@ async function cmdMigrate(args, config) {
1430
1433
  const sub = args.subcommand;
1431
1434
  if (!sub || sub === 'help') {
1432
1435
  banner();
1433
- console.log(` ${bold('turbine migrate')} ${dim('— SQL-first migration system')}`);
1436
+ console.log(` ${bold('turbine migrate')} ${dim(', SQL-first migration system')}`);
1434
1437
  newline();
1435
1438
  console.log(` ${bold('Commands:')}`);
1436
1439
  console.log(` ${cyan('create <name>')} Create a new migration file`);
@@ -1709,16 +1712,16 @@ async function cmdMigrateUp(args, config) {
1709
1712
  newline();
1710
1713
  return;
1711
1714
  }
1712
- // Big, loud warning when bypassing drift detection — this is a deliberately
1715
+ // Big, loud warning when bypassing drift detection, this is a deliberately
1713
1716
  // dangerous operation and the user should see it on every invocation.
1714
1717
  if (args.allowDrift) {
1715
- warn('--allow-drift is set — checksum validation is DISABLED for this run.');
1718
+ warn('--allow-drift is set, checksum validation is DISABLED for this run.');
1716
1719
  console.log(` ${dim('Applied migrations may have been modified or deleted on disk.')}`);
1717
1720
  console.log(` ${dim('Proceed only if you are intentionally rewriting migration history.')}`);
1718
1721
  newline();
1719
1722
  }
1720
1723
  if (args.allowDestructive) {
1721
- warn('--allow-destructive is set — data-destroying statements in migrations WILL run.');
1724
+ warn('--allow-destructive is set, data-destroying statements in migrations WILL run.');
1722
1725
  newline();
1723
1726
  }
1724
1727
  const spinner = new Spinner('Applying migrations').start();
@@ -1747,7 +1750,7 @@ async function cmdMigrateUp(args, config) {
1747
1750
  throw err;
1748
1751
  spinner.stop();
1749
1752
  if (!(await confirmDestructive(err.message))) {
1750
- error('Aborted — no migrations were applied and no data was touched.');
1753
+ error('Aborted, no migrations were applied and no data was touched.');
1751
1754
  newline();
1752
1755
  process.exit(1);
1753
1756
  }
@@ -1895,7 +1898,7 @@ function isDestructiveRefusal(err) {
1895
1898
  * 1. show the full itemized report (statement kinds + targets),
1896
1899
  * 2. require typing the literal phrase `destroy my data`,
1897
1900
  * 3. require a final explicit `yes`.
1898
- * Non-interactive shells (CI, pipes) can never pass this — they must use the
1901
+ * Non-interactive shells (CI, pipes) can never pass this, they must use the
1899
1902
  * explicit `--allow-destructive` flag instead. Anything but exact answers aborts.
1900
1903
  */
1901
1904
  async function confirmDestructive(report) {
@@ -1917,7 +1920,7 @@ async function confirmDestructive(report) {
1917
1920
  const phrase = await rl.question(` Type ${bold('destroy my data')} to continue, anything else to abort: `);
1918
1921
  if (phrase.trim() !== 'destroy my data')
1919
1922
  return false;
1920
- const finalAnswer = await rl.question(` Final confirmation — apply the destructive statements above? Type ${bold('yes')}: `);
1923
+ const finalAnswer = await rl.question(` Final confirmation, apply the destructive statements above? Type ${bold('yes')}: `);
1921
1924
  return finalAnswer.trim() === 'yes';
1922
1925
  }
1923
1926
  finally {
@@ -1943,7 +1946,7 @@ async function cmdMigrateDown(args, config) {
1943
1946
  throw err;
1944
1947
  spinner.stop();
1945
1948
  if (!(await confirmDestructive(err.message))) {
1946
- error('Aborted — nothing was rolled back and no data was touched.');
1949
+ error('Aborted, nothing was rolled back and no data was touched.');
1947
1950
  newline();
1948
1951
  process.exit(1);
1949
1952
  }
@@ -2033,7 +2036,7 @@ async function cmdMigrateStatus(_args, config) {
2033
2036
  .toISOString()
2034
2037
  .replace('T', ' ')
2035
2038
  .replace(/\.\d+Z$/, ' UTC'))
2036
- : dim('—'),
2039
+ : dim('-'),
2037
2040
  ];
2038
2041
  });
2039
2042
  console.log(formatTable(headers, rows));
@@ -2063,7 +2066,7 @@ async function runSeedPlan(plan, config) {
2063
2066
  try {
2064
2067
  if (plan.kind === 'tsx') {
2065
2068
  if (!canResolveTsx()) {
2066
- throw new Error('TypeScript seed files require tsx — install tsx or use seed.js/seed.sql.');
2069
+ throw new Error('TypeScript seed files require tsx, install tsx or use seed.js/seed.sql.');
2067
2070
  }
2068
2071
  // The seed runs in a child process, so we cannot observe its callback
2069
2072
  // directly. Hand it a sentinel path: `defineSeed`'s runner writes the file
@@ -2164,6 +2167,7 @@ async function cmdStatus(_args, config) {
2164
2167
  schema: config.schema,
2165
2168
  include: config.include.length ? config.include : undefined,
2166
2169
  exclude: config.exclude.length ? config.exclude : undefined,
2170
+ relationNames: config.relationNames,
2167
2171
  });
2168
2172
  const tableNames = Object.keys(schema.tables);
2169
2173
  spinner.succeed(`Found ${bold(String(tableNames.length))} tables`);
@@ -2627,7 +2631,7 @@ export function isLoopbackHost(host) {
2627
2631
  return h === '127.0.0.1' || h === 'localhost' || h === '::1' || h === '[::1]';
2628
2632
  }
2629
2633
  // ---------------------------------------------------------------------------
2630
- // Command: studio — local read-only web UI
2634
+ // Command: studio, local read-only web UI
2631
2635
  // ---------------------------------------------------------------------------
2632
2636
  async function cmdStudio(args, config) {
2633
2637
  banner();
@@ -2643,7 +2647,7 @@ async function cmdStudio(args, config) {
2643
2647
  process.exit(1);
2644
2648
  }
2645
2649
  // Non-loopback binds require an explicit --allow-remote opt-in. Studio has
2646
- // only a random session token — exposing it on a LAN interface is foot-gun
2650
+ // only a random session token, exposing it on a LAN interface is foot-gun
2647
2651
  // territory, so we refuse rather than warn-and-proceed.
2648
2652
  if (!isLoopbackHost(host)) {
2649
2653
  if (!args.allowRemote) {
@@ -2654,7 +2658,7 @@ async function cmdStudio(args, config) {
2654
2658
  newline();
2655
2659
  process.exit(1);
2656
2660
  }
2657
- console.log(warn(`Studio is binding to ${yellow(host)} — this is NOT loopback. ` +
2661
+ console.log(warn(`Studio is binding to ${yellow(host)}, this is NOT loopback. ` +
2658
2662
  `Anyone on your network who can reach this port + guess the session token can read your database.`));
2659
2663
  }
2660
2664
  const spinner = new Spinner(demo ? 'Seeding demo dataset' : 'Introspecting database').start();
@@ -2754,7 +2758,7 @@ async function cmdStudio(args, config) {
2754
2758
  });
2755
2759
  }
2756
2760
  // ---------------------------------------------------------------------------
2757
- // Command: mcp — read-only JSON-RPC stdio server
2761
+ // Command: mcp, read-only JSON-RPC stdio server
2758
2762
  // ---------------------------------------------------------------------------
2759
2763
  async function cmdMcp(_args, config) {
2760
2764
  const url = requireUrl(config);
@@ -2799,7 +2803,7 @@ async function cmdObserve(args) {
2799
2803
  newline();
2800
2804
  process.exit(1);
2801
2805
  }
2802
- console.log(warn(`Observe is binding to ${yellow(host)} — this is NOT loopback. ` +
2806
+ console.log(warn(`Observe is binding to ${yellow(host)}, this is NOT loopback. ` +
2803
2807
  `Anyone on your network who can reach this port + guess the session token can read your metrics.`));
2804
2808
  }
2805
2809
  const spinner = new Spinner('Connecting to metrics database').start();
@@ -2814,7 +2818,7 @@ async function cmdObserve(args) {
2814
2818
  }
2815
2819
  newline();
2816
2820
  console.log(box([
2817
- `${bold('Turbine Observe')} ${dim('— query metrics dashboard')}`,
2821
+ `${bold('Turbine Observe')} ${dim(', query metrics dashboard')}`,
2818
2822
  '',
2819
2823
  ` ${cyan('URL:')} ${bold(handle.url)}`,
2820
2824
  '',
@@ -2861,7 +2865,7 @@ function showSubcommandHelp(command) {
2861
2865
  }
2862
2866
  function showInitHelp() {
2863
2867
  banner();
2864
- console.log(` ${bold('turbine init')} — Initialize a Turbine project`);
2868
+ console.log(` ${bold('turbine init')}, Initialize a Turbine project`);
2865
2869
  newline();
2866
2870
  console.log(` ${bold('Usage:')}`);
2867
2871
  console.log(` npx turbine init ${dim('[options]')}`);
@@ -2886,16 +2890,16 @@ function showInitHelp() {
2886
2890
  }
2887
2891
  function showGenerateHelp() {
2888
2892
  banner();
2889
- console.log(` ${bold('turbine generate')} — Introspect database and generate TypeScript types`);
2893
+ console.log(` ${bold('turbine generate')}, Introspect database and generate TypeScript types`);
2890
2894
  newline();
2891
2895
  console.log(` ${bold('Usage:')}`);
2892
2896
  console.log(` npx turbine generate ${dim('[options]')}`);
2893
2897
  newline();
2894
2898
  console.log(` Connects to your database, reads the schema, and generates:`);
2895
- console.log(` ${dim('•')} ${cyan('types.ts')} — Entity interfaces, Create/Update input types`);
2896
- console.log(` ${dim('•')} ${cyan('metadata.ts')} — Runtime schema metadata`);
2897
- console.log(` ${dim('•')} ${cyan('index.ts')} — Configured client with typed table accessors`);
2898
- console.log(` ${dim('•')} ${cyan('zod.ts')} — Zod schemas ${dim('(with --zod)')}`);
2899
+ console.log(` ${dim('•')} ${cyan('types.ts')} , Entity interfaces, Create/Update input types`);
2900
+ console.log(` ${dim('•')} ${cyan('metadata.ts')}, Runtime schema metadata`);
2901
+ console.log(` ${dim('•')} ${cyan('index.ts')} , Configured client with typed table accessors`);
2902
+ console.log(` ${dim('•')} ${cyan('zod.ts')} , Zod schemas ${dim('(with --zod)')}`);
2899
2903
  newline();
2900
2904
  console.log(` ${bold('Options:')}`);
2901
2905
  console.log(` ${cyan('--url, -u')} ${dim('<url>')} Postgres connection string`);
@@ -2943,7 +2947,7 @@ function showMigrateFromPrismaHelp() {
2943
2947
  }
2944
2948
  function showPushHelp() {
2945
2949
  banner();
2946
- console.log(` ${bold('turbine push')} — Apply schema-builder definitions to database`);
2950
+ console.log(` ${bold('turbine push')}, Apply schema-builder definitions to database`);
2947
2951
  newline();
2948
2952
  console.log(` ${bold('Usage:')}`);
2949
2953
  console.log(` npx turbine push ${dim('[options]')}`);
@@ -2960,7 +2964,7 @@ function showPushHelp() {
2960
2964
  }
2961
2965
  function showMigrateHelp() {
2962
2966
  banner();
2963
- console.log(` ${bold('turbine migrate')} — SQL migration management`);
2967
+ console.log(` ${bold('turbine migrate')}, SQL migration management`);
2964
2968
  newline();
2965
2969
  console.log(` ${bold('Usage:')}`);
2966
2970
  console.log(` npx turbine migrate ${cyan('<subcommand>')} ${dim('[options]')}`);
@@ -2979,7 +2983,7 @@ function showMigrateHelp() {
2979
2983
  console.log(` ${cyan('--recipe')} ${dim('<name>')} Scaffold a sanctioned migration pattern ${dim('(create only, e.g. backfill)')}`);
2980
2984
  console.log(` ${cyan('--step, -n')} ${dim('<N>')} Number of migrations to apply/rollback`);
2981
2985
  console.log(` ${cyan('--dry-run')} Show SQL without executing`);
2982
- console.log(` ${cyan('--allow-drift')} Bypass checksum validation ${dim('(migrate up only — advanced)')}`);
2986
+ console.log(` ${cyan('--allow-drift')} Bypass checksum validation ${dim('(migrate up only, advanced)')}`);
2983
2987
  console.log(` ${cyan('--allow-destructive')} Run data-destroying migration statements without the interactive confirm`);
2984
2988
  console.log(` ${cyan('--verbose, -v')} Show detailed output`);
2985
2989
  newline();
@@ -2996,7 +3000,7 @@ function showMigrateHelp() {
2996
3000
  }
2997
3001
  function showSeedHelp() {
2998
3002
  banner();
2999
- console.log(` ${bold('turbine seed')} — Run seed file`);
3003
+ console.log(` ${bold('turbine seed')}, Run seed file`);
3000
3004
  newline();
3001
3005
  console.log(` ${bold('Usage:')}`);
3002
3006
  console.log(` npx turbine seed ${dim('[options]')}`);
@@ -3012,7 +3016,7 @@ function showSeedHelp() {
3012
3016
  }
3013
3017
  function showStatusHelp() {
3014
3018
  banner();
3015
- console.log(` ${bold('turbine status')} — Show database schema summary`);
3019
+ console.log(` ${bold('turbine status')}, Show database schema summary`);
3016
3020
  newline();
3017
3021
  console.log(` ${bold('Usage:')}`);
3018
3022
  console.log(` npx turbine status ${dim('[options]')}`);
@@ -3027,7 +3031,7 @@ function showStatusHelp() {
3027
3031
  }
3028
3032
  function showMcpHelp() {
3029
3033
  banner();
3030
- console.log(` ${bold('turbine mcp')} — Start read-only MCP server over stdio`);
3034
+ console.log(` ${bold('turbine mcp')}, Start read-only MCP server over stdio`);
3031
3035
  newline();
3032
3036
  console.log(` ${bold('Usage:')}`);
3033
3037
  console.log(` npx turbine mcp ${dim('[options]')}`);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm CLI — TypeScript loader registration
2
+ * turbine-orm CLI, TypeScript loader registration
3
3
  *
4
4
  * The CLI loads user-supplied config and schema files via dynamic `import()`.
5
5
  * Plain Node has no built-in `.ts` loader, so importing `turbine.config.ts`
@@ -12,16 +12,16 @@
12
12
  * 2. Prefer tsx's supported programmatic API, `tsx/esm/api`'s `register()`.
13
13
  * Calling Node's `module.register('tsx/esm', ...)` directly throws
14
14
  * "tsx must be loaded with --import instead of --loader" on every Node
15
- * version that has `module.register()` (>= 20.6) — tsx's hook file
15
+ * version that has `module.register()` (>= 20.6), tsx's hook file
16
16
  * guards against being loaded that way. The `tsx/esm/api` entry point
17
17
  * is the documented path and works everywhere `module.register()` does.
18
18
  * 3. Fall back to `module.register('tsx/esm', ...)` only for very old tsx
19
19
  * versions (< 4.0) that predate `tsx/esm/api`.
20
20
  * 4. If tsx isn't installed, or registration genuinely fails, surface an
21
- * actionable error — including the REAL underlying error message, never
21
+ * actionable error, including the REAL underlying error message, never
22
22
  * a misdiagnosed "tsx is not installed".
23
23
  *
24
- * `tsx` is intentionally NOT a runtime dependency — many projects already
24
+ * `tsx` is intentionally NOT a runtime dependency, many projects already
25
25
  * have it, and adding a heavy dev tool to a 1-dependency ORM would be silly.
26
26
  */
27
27
  /**
@@ -45,7 +45,7 @@ export type TsLoaderStatus = 'registered' | 'already' | 'unsupported' | 'missing
45
45
  export declare function getTsLoaderError(): string | null;
46
46
  /**
47
47
  * Register the tsx ESM loader so subsequent dynamic imports of `.ts` files
48
- * work. Safe to call multiple times — internal flag prevents double registration.
48
+ * work. Safe to call multiple times, internal flag prevents double registration.
49
49
  *
50
50
  * Returns:
51
51
  * - 'registered' loader was successfully registered this call
@@ -53,9 +53,9 @@ export declare function getTsLoaderError(): string | null;
53
53
  * - 'unsupported' Node lacks `module.register()` (Node < 20.6) and tsx has
54
54
  * no programmatic API to fall back to
55
55
  * - 'missing' `tsx` is not installed in the user's project
56
- * - 'failed' tsx IS installed but registration threw — see
56
+ * - 'failed' tsx IS installed but registration threw, see
57
57
  * {@link getTsLoaderError} for the underlying message
58
58
  */
59
59
  export declare function registerTsLoader(): Promise<TsLoaderStatus>;
60
- /** Reset the loader state — used by unit tests only. */
60
+ /** Reset the loader state, used by unit tests only. */
61
61
  export declare function _resetTsLoaderStateForTests(): void;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm CLI — TypeScript loader registration
2
+ * turbine-orm CLI, TypeScript loader registration
3
3
  *
4
4
  * The CLI loads user-supplied config and schema files via dynamic `import()`.
5
5
  * Plain Node has no built-in `.ts` loader, so importing `turbine.config.ts`
@@ -12,16 +12,16 @@
12
12
  * 2. Prefer tsx's supported programmatic API, `tsx/esm/api`'s `register()`.
13
13
  * Calling Node's `module.register('tsx/esm', ...)` directly throws
14
14
  * "tsx must be loaded with --import instead of --loader" on every Node
15
- * version that has `module.register()` (>= 20.6) — tsx's hook file
15
+ * version that has `module.register()` (>= 20.6), tsx's hook file
16
16
  * guards against being loaded that way. The `tsx/esm/api` entry point
17
17
  * is the documented path and works everywhere `module.register()` does.
18
18
  * 3. Fall back to `module.register('tsx/esm', ...)` only for very old tsx
19
19
  * versions (< 4.0) that predate `tsx/esm/api`.
20
20
  * 4. If tsx isn't installed, or registration genuinely fails, surface an
21
- * actionable error — including the REAL underlying error message, never
21
+ * actionable error, including the REAL underlying error message, never
22
22
  * a misdiagnosed "tsx is not installed".
23
23
  *
24
- * `tsx` is intentionally NOT a runtime dependency — many projects already
24
+ * `tsx` is intentionally NOT a runtime dependency, many projects already
25
25
  * have it, and adding a heavy dev tool to a 1-dependency ORM would be silly.
26
26
  */
27
27
  import { createRequire } from 'node:module';
@@ -69,7 +69,7 @@ export function getTsLoaderError() {
69
69
  }
70
70
  /**
71
71
  * Register the tsx ESM loader so subsequent dynamic imports of `.ts` files
72
- * work. Safe to call multiple times — internal flag prevents double registration.
72
+ * work. Safe to call multiple times, internal flag prevents double registration.
73
73
  *
74
74
  * Returns:
75
75
  * - 'registered' loader was successfully registered this call
@@ -77,7 +77,7 @@ export function getTsLoaderError() {
77
77
  * - 'unsupported' Node lacks `module.register()` (Node < 20.6) and tsx has
78
78
  * no programmatic API to fall back to
79
79
  * - 'missing' `tsx` is not installed in the user's project
80
- * - 'failed' tsx IS installed but registration threw — see
80
+ * - 'failed' tsx IS installed but registration threw, see
81
81
  * {@link getTsLoaderError} for the underlying message
82
82
  */
83
83
  export async function registerTsLoader() {
@@ -110,14 +110,14 @@ export async function registerTsLoader() {
110
110
  return 'failed';
111
111
  }
112
112
  }
113
- // tsx/esm/api not resolvable — is tsx installed at all?
113
+ // tsx/esm/api not resolvable, is tsx installed at all?
114
114
  if (!canResolveTsx()) {
115
115
  tsLoaderState = 'missing';
116
116
  return 'missing';
117
117
  }
118
118
  // Legacy fallback for tsx < 4.0 (no tsx/esm/api): Node's module.register.
119
119
  // On tsx >= 4.19 this path throws ("tsx must be loaded with --import
120
- // instead of --loader") — but those versions all ship tsx/esm/api, so we
120
+ // instead of --loader"), but those versions all ship tsx/esm/api, so we
121
121
  // only land here for genuinely old installs.
122
122
  try {
123
123
  const mod = await import('node:module');
@@ -137,7 +137,7 @@ export async function registerTsLoader() {
137
137
  return 'failed';
138
138
  }
139
139
  }
140
- /** Reset the loader state — used by unit tests only. */
140
+ /** Reset the loader state, used by unit tests only. */
141
141
  export function _resetTsLoaderStateForTests() {
142
142
  tsLoaderState = null;
143
143
  tsLoaderError = null;
package/dist/cli/mcp.js CHANGED
@@ -72,7 +72,7 @@ const TOOLS = [
72
72
  },
73
73
  {
74
74
  name: 'explain_query',
75
- description: 'Run EXPLAIN (FORMAT JSON) for a schema-validated findMany query. Pass table + optional where/orderBy/limit/select — free-form SQL is rejected.',
75
+ description: 'Run EXPLAIN (FORMAT JSON) for a schema-validated findMany query. Pass table + optional where/orderBy/limit/select, free-form SQL is rejected.',
76
76
  inputSchema: {
77
77
  type: 'object',
78
78
  properties: {
@@ -322,7 +322,7 @@ async function doctorReport(ctx) {
322
322
  });
323
323
  }
324
324
  /**
325
- * EXPLAIN a schema-validated findMany query. Free-form SQL is never accepted —
325
+ * EXPLAIN a schema-validated findMany query. Free-form SQL is never accepted -
326
326
  * table/field identifiers are checked against introspected metadata and the
327
327
  * SELECT is compiled by QueryInterface (same stance as Studio `/api/builder`).
328
328
  */
@@ -364,7 +364,7 @@ async function explainQuery(ctx, args) {
364
364
  }
365
365
  /**
366
366
  * Extract the allowed findMany subset for explain_query (no `with` / raw SQL).
367
- * Returns a plain object cast at the buildFindMany call site — same pattern as Studio.
367
+ * Returns a plain object cast at the buildFindMany call site, same pattern as Studio.
368
368
  */
369
369
  function parseExplainFindManyArgs(args) {
370
370
  const findManyArgs = {};
@@ -642,7 +642,7 @@ export function buildRelations(tableNames, columnsByTable, pkByTable, rows, enum
642
642
  for (const [tbl, cols] of columnsByTable) {
643
643
  columnFieldsByTable.set(tbl, new Set(cols.map((c) => c.field)));
644
644
  // Enum-typed columns also report tsType 'unknown', but the generated type
645
- // layer gives them a concrete union — only json/jsonb qualify as shadows.
645
+ // layer gives them a concrete union, only json/jsonb qualify as shadows.
646
646
  unknownTypedFieldsByTable.set(tbl, new Set(cols.filter((c) => isUnknownTsType(c.tsType) && !Object.hasOwn(enums, c.pgType)).map((c) => c.field)));
647
647
  }
648
648
  const relations = buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable, undefined, unknownTypedFieldsByTable);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm CLI — Migration system
2
+ * turbine-orm CLI, Migration system
3
3
  *
4
4
  * SQL-first migrations with UP/DOWN sections, tracked in _turbine_migrations.
5
5
  * Migration files are timestamp-prefixed .sql files.
@@ -22,7 +22,7 @@ export interface MigrationFile {
22
22
  path: string;
23
23
  /** Extracted name portion (e.g. "20260325120000_create_users") */
24
24
  name: string;
25
- /** Timestamp prefix (e.g. "20260325120000") — YYYYMMDDHHMMSS */
25
+ /** Timestamp prefix (e.g. "20260325120000"), YYYYMMDDHHMMSS */
26
26
  timestamp: string;
27
27
  }
28
28
  export interface AppliedMigration {
@@ -95,7 +95,7 @@ export declare function sanitizeName(name: string): string;
95
95
  */
96
96
  export declare function formatTimestamp(date: Date): string;
97
97
  /**
98
- * Get pending migration files — those not yet applied.
98
+ * Get pending migration files, those not yet applied.
99
99
  * Returns files sorted by timestamp (ascending).
100
100
  */
101
101
  export declare function getPendingMigrations(migrationsDir: string, applied: string[]): MigrationFile[];
@@ -227,7 +227,7 @@ export declare function createMigration(migrationsDir: string, name: string, aut
227
227
  /**
228
228
  * Derive a Postgres advisory lock ID (positive int4) from the database name.
229
229
  *
230
- * Uses FNV-1a 32-bit hash — a well-known, stable, non-cryptographic hash with
230
+ * Uses FNV-1a 32-bit hash, a well-known, stable, non-cryptographic hash with
231
231
  * excellent distribution over short strings (database names are typically <64
232
232
  * chars). Chosen over alternatives because it's:
233
233
  * - deterministic (same input → same output, across processes/machines)
@@ -297,7 +297,7 @@ export declare function inspectMigrationDeploy(connectionString: string, migrati
297
297
  * Features:
298
298
  * - Idempotent: running twice is safe (already-applied migrations are skipped)
299
299
  * - Advisory lock: prevents concurrent migration runs
300
- * - Checksum validation: detects modified migration files (BLOCKING — use
300
+ * - Checksum validation: detects modified migration files (BLOCKING, use
301
301
  * `allowDrift: true` to bypass when intentionally rewriting history)
302
302
  * - Each migration runs in its own transaction
303
303
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm CLI — Migration system
2
+ * turbine-orm CLI, Migration system
3
3
  *
4
4
  * SQL-first migrations with UP/DOWN sections, tracked in _turbine_migrations.
5
5
  * Migration files are timestamp-prefixed .sql files.
@@ -108,7 +108,7 @@ export function formatTimestamp(date) {
108
108
  ].join('');
109
109
  }
110
110
  /**
111
- * Get pending migration files — those not yet applied.
111
+ * Get pending migration files, those not yet applied.
112
112
  * Returns files sorted by timestamp (ascending).
113
113
  */
114
114
  export function getPendingMigrations(migrationsDir, applied) {
@@ -612,7 +612,7 @@ ${autoContent.down}
612
612
  /**
613
613
  * Derive a Postgres advisory lock ID (positive int4) from the database name.
614
614
  *
615
- * Uses FNV-1a 32-bit hash — a well-known, stable, non-cryptographic hash with
615
+ * Uses FNV-1a 32-bit hash, a well-known, stable, non-cryptographic hash with
616
616
  * excellent distribution over short strings (database names are typically <64
617
617
  * chars). Chosen over alternatives because it's:
618
618
  * - deterministic (same input → same output, across processes/machines)
@@ -729,7 +729,7 @@ export function formatChecksumMismatchError(mismatches) {
729
729
  const modified = mismatches.filter((m) => m.type === 'modified');
730
730
  const missing = mismatches.filter((m) => m.type === 'missing');
731
731
  const lines = [
732
- '[turbine] Migration drift detected — refusing to apply pending migrations.',
732
+ '[turbine] Migration drift detected, refusing to apply pending migrations.',
733
733
  '',
734
734
  'Applied migrations should be immutable. The following files no longer match their applied state:',
735
735
  '',
@@ -751,7 +751,7 @@ export function formatChecksumMismatchError(mismatches) {
751
751
  if (missing.length > 0) {
752
752
  lines.push(' (deleted files cannot be rolled back: restore the file, then run `migrate down` if needed), OR');
753
753
  }
754
- lines.push(' 3. Pass `--allow-drift` to bypass this check (advanced — make sure you know what you are doing).');
754
+ lines.push(' 3. Pass `--allow-drift` to bypass this check (advanced, make sure you know what you are doing).');
755
755
  return lines.join('\n');
756
756
  }
757
757
  /**
@@ -820,7 +820,7 @@ export async function inspectMigrationDeploy(connectionString, migrationsDir) {
820
820
  * Features:
821
821
  * - Idempotent: running twice is safe (already-applied migrations are skipped)
822
822
  * - Advisory lock: prevents concurrent migration runs
823
- * - Checksum validation: detects modified migration files (BLOCKING — use
823
+ * - Checksum validation: detects modified migration files (BLOCKING, use
824
824
  * `allowDrift: true` to bypass when intentionally rewriting history)
825
825
  * - Each migration runs in its own transaction
826
826
  *
@@ -844,7 +844,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
844
844
  const adapter = options?.adapter;
845
845
  const gotLock = await acquireLock(client, lockId, adapter);
846
846
  if (!gotLock) {
847
- throw new MigrationError('[turbine] Could not acquire migration lock — another migration is already running');
847
+ throw new MigrationError('[turbine] Could not acquire migration lock, another migration is already running');
848
848
  }
849
849
  try {
850
850
  await ensureTrackingTable(client, dialect);
@@ -881,7 +881,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
881
881
  // Data-loss gate: refuse to run pending migrations containing destructive
882
882
  // statements unless the caller has EXPLICITLY opted in. The CLI layers an
883
883
  // interactive typed confirmation on top of this; programmatic callers must
884
- // pass `allowDestructive: true`. Safe-by-default is the whole point — a
884
+ // pass `allowDestructive: true`. Safe-by-default is the whole point, a
885
885
  // DROP TABLE should never run just because a file exists.
886
886
  if (!options?.allowDestructive && destructive.length > 0) {
887
887
  const lines = ['[turbine] Refusing to apply migrations containing DESTRUCTIVE statements:', ''];
@@ -995,7 +995,7 @@ export async function migrateDown(connectionString, migrationsDir, options) {
995
995
  const adapter = options?.adapter;
996
996
  const gotLock = await acquireLock(client, lockId, adapter);
997
997
  if (!gotLock) {
998
- throw new MigrationError('[turbine] Could not acquire migration lock — another migration is already running');
998
+ throw new MigrationError('[turbine] Could not acquire migration lock, another migration is already running');
999
999
  }
1000
1000
  try {
1001
1001
  await ensureTrackingTable(client, dialect);
@@ -1005,9 +1005,9 @@ export async function migrateDown(connectionString, migrationsDir, options) {
1005
1005
  }
1006
1006
  const allFiles = listMigrationFiles(migrationsDir);
1007
1007
  const fileMap = new Map(allFiles.map((f) => [f.name, f]));
1008
- // Reverse order — rollback most recent first
1008
+ // Reverse order, rollback most recent first
1009
1009
  const toRollback = applied.reverse().slice(0, options?.step ?? 1);
1010
- // Same data-loss gate as migrateUp — DOWN sections routinely contain
1010
+ // Same data-loss gate as migrateUp, DOWN sections routinely contain
1011
1011
  // DROP TABLE (the legitimate reverse of a CREATE), which still destroys
1012
1012
  // every row written since the migration ran. Explicit opt-in required.
1013
1013
  if (!options?.allowDestructive) {