okmodel 0.5.0 → 0.5.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 (80) hide show
  1. package/README.md +9 -6
  2. package/dist/dialects/pg/filter-parse.d.ts +8 -2
  3. package/dist/dialects/pg/fn/index.js +8 -0
  4. package/dist/dialects/pg/index.js +8 -2
  5. package/dist/dialects/pg/ops/or.d.ts +1 -1
  6. package/dist/dialects/pg/temporal.d.ts +5 -76
  7. package/dist/dialects/pg/view/index.js +1 -1
  8. package/dist/runtime/client.d.ts +6 -1
  9. package/dist/runtime/conflict.d.ts +2 -1
  10. package/dist/runtime/pg/bun.js +5 -5
  11. package/dist/runtime/pg/pg.js +5 -5
  12. package/dist/runtime/pg/pglite.js +5 -5
  13. package/dist/runtime/pg/postgresjs.js +5 -5
  14. package/dist/runtime/plan.d.ts +13 -0
  15. package/dist/runtime/tenancy/index.js +1 -1
  16. package/dist/runtime/traits/index.js +4 -4
  17. package/dist/runtime/validate/index.js +3 -3
  18. package/dist/shared/{ckjbyrt9.js → 0t22wwh1.js} +5 -9
  19. package/dist/shared/{jdgqqx31.js → 2pqx26h8.js} +23 -8
  20. package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
  21. package/dist/shared/{9tgmr1gh.js → 32zqc1dw.js} +18 -7
  22. package/dist/shared/{47xzb9fc.js → 47x8x9ze.js} +1 -1
  23. package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
  24. package/dist/shared/{s6gqfn5j.js → 51mvgj8w.js} +27 -3
  25. package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
  26. package/dist/shared/{3p4b1fg0.js → 5enne4ma.js} +1 -1
  27. package/dist/shared/{bzsjtd7c.js → 6mnp99hx.js} +7 -6
  28. package/dist/shared/{ykv7wh5b.js → 6qd27mx0.js} +2 -2
  29. package/dist/shared/{h4nqvjkp.js → 6rk2gen5.js} +2 -2
  30. package/dist/shared/{r73pt594.js → 7zwwyh0v.js} +32 -10
  31. package/dist/shared/{fa9vt245.js → 87jvvhnt.js} +2 -2
  32. package/dist/shared/{c8aghh05.js → 8rsy3hxz.js} +7 -1
  33. package/dist/shared/{6p5k20sm.js → 96qcj21c.js} +3 -3
  34. package/dist/shared/{xsq7v6wp.js → aj2qxyhj.js} +2 -2
  35. package/dist/shared/{5vdm4sgp.js → caw8b9d8.js} +4 -4
  36. package/dist/shared/{xhh1srw3.js → cctw17hj.js} +23 -8
  37. package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
  38. package/dist/shared/{tq9afp8d.js → cr74e96e.js} +1 -1
  39. package/dist/shared/{b066g80h.js → efmy2gcg.js} +2 -2
  40. package/dist/shared/{gdh9mrtp.js → f5cw5q5r.js} +2 -2
  41. package/dist/shared/{ayz64vzj.js → ga3sfy93.js} +357 -3
  42. package/dist/shared/{qgvt8nx1.js → gbtcc2d7.js} +44 -3
  43. package/dist/shared/{kdkxh3kb.js → gde73es6.js} +5 -7
  44. package/dist/shared/{bt9sfq0z.js → hf22e0wt.js} +51 -17
  45. package/dist/shared/{990a291j.js → janx09m5.js} +6 -5
  46. package/dist/shared/{4ef7mmqf.js → jwtxdj06.js} +5 -5
  47. package/dist/shared/{sf8m6tfy.js → k8v5v5wj.js} +3 -3
  48. package/dist/shared/{r6pj4fq2.js → kfjp0gck.js} +2 -2
  49. package/dist/shared/{wqem4jed.js → krxx77g0.js} +7 -7
  50. package/dist/shared/{h8ne16fq.js → nkaxvt9q.js} +1 -1
  51. package/dist/shared/{jsd07prz.js → ntneex2a.js} +1 -1
  52. package/dist/shared/{nkc9sfqb.js → nxdp2ze3.js} +40 -4
  53. package/dist/shared/{tbszx8mz.js → pf0d8f77.js} +1 -1
  54. package/dist/shared/{qy9rdq0g.js → ps7a6h14.js} +6 -6
  55. package/dist/shared/{rasvvzgz.js → q58y17hy.js} +5 -5
  56. package/dist/shared/{p7pfx624.js → qm7cdtvr.js} +22 -9
  57. package/dist/shared/{xdpj5kkm.js → qv2ydck9.js} +149 -209
  58. package/dist/shared/{k7cc9nzk.js → r2sp8hrc.js} +6 -3
  59. package/dist/shared/{fb00y8fp.js → r8x5qhd7.js} +5 -5
  60. package/dist/shared/{fyqkk1m0.js → sdc7r2ww.js} +6 -5
  61. package/dist/shared/{wxn4b7pp.js → sycnx8gt.js} +42 -5
  62. package/dist/shared/{dxjp14rk.js → tm5ab8we.js} +2 -2
  63. package/dist/shared/{g516zdsb.js → v36c5g6p.js} +78 -19
  64. package/dist/shared/wq9n6pwr.js +139 -0
  65. package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
  66. package/dist/shared/{922qbgnw.js → zqrnhwsq.js} +3 -3
  67. package/dist/tooling/cli.js +68 -21
  68. package/dist/tooling/migrate/commands.d.ts +1 -2
  69. package/dist/tooling/migrate/drift.d.ts +1 -1
  70. package/dist/tooling/migrate/estimate.d.ts +14 -1
  71. package/dist/tooling/migrate/index.js +8 -7
  72. package/dist/tooling/migrate/lint.d.ts +3 -3
  73. package/dist/tooling/migrate/plan.d.ts +7 -3
  74. package/dist/tooling/migrate/policy.d.ts +11 -0
  75. package/dist/tooling/migrate/sql-lex.d.ts +22 -0
  76. package/dist/tooling/testing/factories.d.ts +10 -0
  77. package/dist/tooling/testing/index.js +1 -1
  78. package/dist/tooling/testing/isolation.d.ts +4 -3
  79. package/package.json +6 -5
  80. package/dist/shared/0v1g424p.js +0 -26
@@ -13,17 +13,22 @@ import {
13
13
  } from "./8bwbwy8h.js";
14
14
  import {
15
15
  open2
16
- } from "./wqem4jed.js";
16
+ } from "./krxx77g0.js";
17
17
  import {
18
18
  canonicalTypeName,
19
19
  batchSizeField,
20
20
  aboutBatchCount,
21
+ sqlStatements,
21
22
  parseReplace,
22
23
  planMigration,
23
24
  formatPlan,
24
25
  parsePlan,
25
- staleRenames
26
- } from "./ayz64vzj.js";
26
+ staleRenames,
27
+ assertTargetPolicy,
28
+ assertTargetAlias,
29
+ listTargets,
30
+ selectTarget
31
+ } from "./ga3sfy93.js";
27
32
  import {
28
33
  contributedFields
29
34
  } from "./n0dgs0pb.js";
@@ -38,7 +43,7 @@ import {
38
43
  import {
39
44
  snakeCase,
40
45
  emittedTypeName
41
- } from "./jsd07prz.js";
46
+ } from "./ntneex2a.js";
42
47
  import {
43
48
  ColumnBuilder2
44
49
  } from "./c9z5g0fm.js";
@@ -150,8 +155,8 @@ var ERROR_DOCS = [
150
155
  {
151
156
  code: "OKM1102",
152
157
  title: "Unfiltered write",
153
- summary: "update or delete would run with an empty where.",
154
- fix: "Pass a filter. undefined in a write filter does not mean every row."
158
+ summary: "A write with no effective predicate, including undefined values, is OKM1102.",
159
+ fix: "Pass a predicate, or .all(reason). An undefined-only where is the same as {}."
155
160
  },
156
161
  {
157
162
  code: "OKM1104",
@@ -180,14 +185,14 @@ var ERROR_DOCS = [
180
185
  {
181
186
  code: "OKM1120",
182
187
  title: "Unknown field",
183
- summary: "A field name in where, select, orderBy, or include is not on the table.",
184
- fix: "Use a field from the catalog. The error includes a nearest-name hint when one is close."
188
+ summary: "The name is not a field or an option. timeout and signal go in the second argument of update. Options go first: tx(options, fn).",
189
+ fix: "Use a field from the catalog, or move the option. The error includes a nearest-name hint when one is close."
185
190
  },
186
191
  {
187
192
  code: "OKM1121",
188
193
  title: "Object as value",
189
- summary: "A plain object was passed where a value is required. JSON cannot create an operator.",
190
- fix: "Wrap the value with eq. eq is the equality form for json and jsonb."
194
+ summary: "An empty or() branch is OKM1121. or() takes one array: or([a, b]). A plain object was passed where a value is required.",
195
+ fix: "Give every or() branch a predicate. Pass an array: or([a, b]). Wrap a json value with eq."
191
196
  },
192
197
  {
193
198
  code: "OKM1122",
@@ -252,8 +257,8 @@ var ERROR_DOCS = [
252
257
  {
253
258
  code: "OKM1510",
254
259
  title: "Unsafe migration",
255
- summary: "A migration has an unresolved lint error, or an override with an empty reason or a code the statement did not trigger.",
256
- fix: "Put `-- okm-allow OKM15xx: reason` on the line above the statement. The code must be one that statement triggered, and the reason must not be empty."
260
+ summary: "A migration has an unresolved lint error, or an override with an empty reason or a code no statement in the step triggered.",
261
+ fix: "Put `-- okm-allow OKM15xx: reason` on the step. The code must be one a statement in that step triggered, and the reason must not be empty. The line silences that code on every statement of the step."
257
262
  },
258
263
  {
259
264
  code: "OKM1511",
@@ -444,8 +449,8 @@ var ERROR_DOCS = [
444
449
  {
445
450
  code: "OKM1542",
446
451
  title: "Data statement",
447
- summary: "A migration contains a data statement outside backfill().",
448
- fix: "Move the statement into backfill(). Data changes are batched and resumable there."
452
+ summary: "A migration contains a data statement outside backfill(), or a DO or CALL whose body is not analysed.",
453
+ fix: "Move insert, update, delete, merge, and truncate into backfill(). DO and CALL are refused because the body is not analysed. Allow OKM1542 with a reason to keep one."
449
454
  },
450
455
  {
451
456
  code: "OKM1543",
@@ -686,6 +691,12 @@ var ERROR_DOCS = [
686
691
  title: "Target not named",
687
692
  summary: "Several targets are configured and the command was not given --target.",
688
693
  fix: "Pass --target <name>. The command does not guess."
694
+ },
695
+ {
696
+ code: "OKM1854",
697
+ title: "Pooler session",
698
+ summary: "Two statements on the same connection saw different backend pids. A transaction-mode pooler is in the way.",
699
+ fix: "Use a direct connection, or pass --allow-pooler."
689
700
  }
690
701
  ];
691
702
  var byCode;
@@ -740,7 +751,7 @@ var STEP_RULES = [
740
751
  rule("OKM1536", "locking", "error", "Adding a foreign key without NOT VALID locks the table while it scans.", (input) => hit(foreignKeyWithoutNotValid(input), "adds a foreign key without NOT VALID")),
741
752
  rule("OKM1537", "locking", "error", "SET NOT NULL locks the table while it scans for nulls.", (input) => hit(unsafeSetNotNull(input), "sets NOT NULL")),
742
753
  rule("OKM1538", "locking", "warning", "A type change that rewrites the table locks it for the rewrite.", (input) => typeChange(input, "rewrite")),
743
- rule("OKM1542", "data-dependent", "error", "A data statement outside backfill() is not a migration step.", (input) => hit(dataStatement(input.step), "changes rows outside backfill()"))
754
+ rule("OKM1542", "data-dependent", "error", "A data statement outside backfill(), or DO or CALL, is not a migration step.", (input) => dataStatement(input.step))
744
755
  ];
745
756
  var COLUMN_RULES = [
746
757
  {
@@ -800,12 +811,18 @@ function normalized(sql) {
800
811
  return out;
801
812
  }
802
813
  function dataStatement(step) {
803
- if (step.backfill !== undefined)
804
- return false;
805
814
  const text = normalized(step.sql);
806
- if (/^(?:insert|update|delete|merge|truncate)\b/.test(text))
807
- return true;
808
- return /^with\b/.test(text) && /\b(?:insert|update|delete|merge)\b/.test(text);
815
+ if (/^(?:do|call)\b/.test(text))
816
+ return ["the body is not analysed"];
817
+ if (step.backfill !== undefined)
818
+ return [];
819
+ if (/^(?:insert|update|delete|merge|truncate)\b/.test(text)) {
820
+ return ["changes rows outside backfill()"];
821
+ }
822
+ if (/^with\b/.test(text) && /\b(?:insert|update|delete|merge)\b/.test(text)) {
823
+ return ["changes rows outside backfill()"];
824
+ }
825
+ return [];
809
826
  }
810
827
  function matches(step, kind, pattern) {
811
828
  return pattern.test(normalized(step.sql)) || step.kind === kind;
@@ -980,9 +997,10 @@ function validates(input, kind) {
980
997
  return true;
981
998
  return false;
982
999
  }
1000
+ var TYPE_CHANGE = /^alter table\s+(\S+)\s+alter column\s+(\S+)\s+(?:set data type|type)\b/;
983
1001
  function typeChange(input, mode) {
984
1002
  const text = normalized(input.step.sql);
985
- const changing = /^alter table\s+\S+\s+alter column\s+\S+\s+set data type\b/.test(text) || input.step.kind === "set-column-type";
1003
+ const changing = TYPE_CHANGE.test(text) || input.step.kind === "set-column-type";
986
1004
  if (!changing)
987
1005
  return [];
988
1006
  const compared = columnTypes(input);
@@ -1009,7 +1027,7 @@ function typeChange(input, mode) {
1009
1027
  }
1010
1028
  function columnTypes(input) {
1011
1029
  const text = normalized(input.step.sql);
1012
- const match = /^alter table\s+(\S+)\s+alter column\s+(\S+)\s+set data type\b/.exec(text);
1030
+ const match = TYPE_CHANGE.exec(text);
1013
1031
  const table = lastName(match?.[1]);
1014
1032
  const column = lastName(match?.[2]);
1015
1033
  if (table === undefined || column === undefined)
@@ -1178,34 +1196,76 @@ function lintRefusal(findings) {
1178
1196
  });
1179
1197
  }
1180
1198
  function lintStep(step, number, before, after, existingTables, earlier, later) {
1181
- const hits = [];
1182
- for (const rule of STEP_RULES) {
1183
- for (const reason of rule.check({ step, before, after, existingTables, earlier, later })) {
1184
- hits.push({ code: rule.code, severity: rule.severity, reason });
1199
+ const texts = statementTexts(step);
1200
+ const multi = texts.length > 1;
1201
+ const earlierStatements = earlier.flatMap(contextSteps);
1202
+ const laterStatements = later.flatMap(contextSteps);
1203
+ const perStatement = [];
1204
+ const triggered = new Set;
1205
+ texts.forEach((sql, index) => {
1206
+ const statement = multi ? withoutKind(step, sql) : withSql(step, sql);
1207
+ const hits = [];
1208
+ for (const rule of STEP_RULES) {
1209
+ for (const reason of rule.check({
1210
+ step: statement,
1211
+ before,
1212
+ after,
1213
+ existingTables,
1214
+ earlier: earlierStatements,
1215
+ later: laterStatements
1216
+ })) {
1217
+ const hit = { code: rule.code, severity: rule.severity, reason };
1218
+ hits.push(hit);
1219
+ triggered.add(hit.code);
1220
+ }
1185
1221
  }
1186
- }
1187
- const triggered = new Set(hits.map((item) => item.code));
1222
+ const place = multi ? `step ${String(number)} statement ${String(index + 1)}` : `step ${String(number)}`;
1223
+ perStatement.push({ place, hits });
1224
+ });
1188
1225
  const silenced = new Set;
1189
1226
  const findings = [];
1190
- const place = `step ${String(number)}`;
1227
+ const stepPlace = `step ${String(number)}`;
1191
1228
  for (const allow of step.allows ?? []) {
1192
1229
  if (allow.reason.trim().length === 0 || allow.code.length === 0) {
1193
- findings.push(finding("error", "OKM1510", place, "override needs a reason"));
1230
+ findings.push(finding("error", "OKM1510", stepPlace, "override needs a reason"));
1194
1231
  continue;
1195
1232
  }
1196
1233
  if (!triggered.has(allow.code)) {
1197
- findings.push(finding("error", "OKM1510", place, `override ${allow.code} does not match this statement`));
1234
+ findings.push(finding("error", "OKM1510", stepPlace, `override ${allow.code} does not match this statement`));
1198
1235
  continue;
1199
1236
  }
1200
1237
  silenced.add(allow.code);
1201
1238
  }
1202
- for (const hit of hits) {
1203
- if (silenced.has(hit.code))
1204
- continue;
1205
- findings.push(finding(hit.severity, hit.code, place, hit.reason));
1239
+ for (const item of perStatement) {
1240
+ for (const hit of item.hits) {
1241
+ if (silenced.has(hit.code))
1242
+ continue;
1243
+ findings.push(finding(hit.severity, hit.code, item.place, hit.reason));
1244
+ }
1206
1245
  }
1207
1246
  return findings;
1208
1247
  }
1248
+ function statementTexts(step) {
1249
+ const statements = sqlStatements(step.sql);
1250
+ return statements.length === 0 ? [step.sql] : statements;
1251
+ }
1252
+ function contextSteps(step) {
1253
+ const statements = sqlStatements(step.sql);
1254
+ if (statements.length === 0)
1255
+ return [step];
1256
+ if (statements.length === 1 && statements[0] === step.sql)
1257
+ return [step];
1258
+ return statements.map((sql) => withSql(step, sql));
1259
+ }
1260
+ function withSql(step, sql) {
1261
+ if (sql === step.sql)
1262
+ return step;
1263
+ return { ...step, sql };
1264
+ }
1265
+ function withoutKind(step, sql) {
1266
+ const { kind: _kind, ...rest } = step;
1267
+ return { ...rest, sql };
1268
+ }
1209
1269
  function finding(severity, code, place, reason) {
1210
1270
  const doc = errorDoc(code);
1211
1271
  if (doc === undefined)
@@ -1227,158 +1287,6 @@ function columnPlace(column) {
1227
1287
  return `${column.identity.parent.name}.${column.identity.name}`;
1228
1288
  }
1229
1289
 
1230
- // src/tooling/migrate/policy.ts
1231
- var READ = new Set([
1232
- "plan",
1233
- "status",
1234
- "check",
1235
- "drift",
1236
- "verify",
1237
- "pull",
1238
- "catalog-export",
1239
- "inspect"
1240
- ]);
1241
- var ALLOWED = new Set(["expand", "reference", "provision"]);
1242
- var FORWARD_ONLY = new Set(["drop", "rollback"]);
1243
- function assertTargetPolicy(target, operation, allowProtected = false) {
1244
- if (FORWARD_ONLY.has(operation)) {
1245
- throw new OkmError2("invalid", "There is no down migration.", {
1246
- fix: { summary: "Recovery is a new forward migration." }
1247
- });
1248
- }
1249
- if (!target.protected || allowProtected)
1250
- return;
1251
- if (READ.has(operation) || ALLOWED.has(operation))
1252
- return;
1253
- throw new OkmError2("OKM1850", `${operation} is blocked on protected target ${target.name}.`, {
1254
- kind: "forbidden",
1255
- fix: {
1256
- summary: "Run a read-only command or an expand migration, or pass --allow-protected for this invocation."
1257
- }
1258
- });
1259
- }
1260
- function assertTargetAlias(targets) {
1261
- const groups = new Map;
1262
- for (const target of targets) {
1263
- const key = `${target.host}
1264
- ${target.port}
1265
- ${target.database}`;
1266
- const group = groups.get(key);
1267
- if (group === undefined)
1268
- groups.set(key, [target]);
1269
- else
1270
- group.push(target);
1271
- }
1272
- for (const group of groups.values()) {
1273
- const protectedTarget = group.find((target) => target.protected);
1274
- const open = group.find((target) => !target.protected);
1275
- if (protectedTarget !== undefined && open !== undefined) {
1276
- throw new OkmError2("OKM1852", `${open.name} and ${protectedTarget.name} resolve to the same database and disagree about protection.`, {
1277
- fix: {
1278
- summary: "Give those targets the same protection flag. okm check and okm doctor both report this."
1279
- }
1280
- });
1281
- }
1282
- }
1283
- }
1284
- function assertDirectConnection(url, allowPooler) {
1285
- if (allowPooler)
1286
- return;
1287
- const endpoint = endpointOf(url);
1288
- const parsed = new URL(url);
1289
- const host = endpoint.host;
1290
- if (host.includes("pooler") || host.includes("pgbouncer") || host.includes("-pooler") || endpoint.port === "6543" || parsed.searchParams.get("pgbouncer") === "true") {
1291
- throw new OkmError2("invalid", `Refusing pooler host ${host}. Apply needs a direct connection.`, { fix: { summary: "Use a direct connection, or pass --allow-pooler." } });
1292
- }
1293
- }
1294
- function listTargets(config) {
1295
- if (config.database !== undefined && config.targets !== undefined) {
1296
- throw new OkmError2("invalid", "Set database or targets, not both.", {
1297
- fix: { summary: "database is the one target named default. Use targets for more than one." }
1298
- });
1299
- }
1300
- const entries = [];
1301
- if (config.database !== undefined)
1302
- entries.push(["default", config.database]);
1303
- if (config.targets !== undefined) {
1304
- for (const name of Object.keys(config.targets).sort()) {
1305
- const input = config.targets[name];
1306
- if (input !== undefined)
1307
- entries.push([name, input]);
1308
- }
1309
- }
1310
- return entries.map(([name, input]) => resolveTarget(name, input));
1311
- }
1312
- function selectTarget(config, name) {
1313
- const targets = listTargets(config);
1314
- if (targets.length === 0) {
1315
- throw new OkmError2("OKM1845", "No target is configured.", {
1316
- fix: { summary: "Set database or targets in okmodel.config.ts." }
1317
- });
1318
- }
1319
- if (name === undefined) {
1320
- if (targets.length === 1) {
1321
- const only = targets[0];
1322
- if (only !== undefined)
1323
- return only;
1324
- }
1325
- throw new OkmError2("OKM1853", "Several targets are configured and --target was not passed.", {
1326
- fix: { summary: "Pass --target <name>. The command does not guess." }
1327
- });
1328
- }
1329
- const found = targets.find((target) => target.name === name);
1330
- if (found === undefined) {
1331
- throw new OkmError2("OKM1845", `Target ${name} is not configured.`, {
1332
- fix: { summary: "Pass a name from targets, or database for the target named default." }
1333
- });
1334
- }
1335
- return found;
1336
- }
1337
- function resolveTarget(name, input) {
1338
- if (typeof input !== "string" && isReplicaTarget(input)) {
1339
- throw new OkmError2("OKM1845", `Target ${name} is a replica. Migrations run on the primary only.`, {
1340
- fix: {
1341
- summary: "Set database or targets to the primary URL. A replica is not a migration target."
1342
- }
1343
- });
1344
- }
1345
- const url = typeof input === "string" ? input : input.url;
1346
- const protectedTarget = typeof input === "string" ? false : input.protected === true;
1347
- if (url === undefined || url.length === 0) {
1348
- throw new OkmError2("OKM1845", `Target ${name} has no url.`, {
1349
- fix: { summary: "Set the target url. A target is not inferred from the environment." }
1350
- });
1351
- }
1352
- const endpoint = endpointOf(url);
1353
- return {
1354
- name,
1355
- url,
1356
- protected: protectedTarget,
1357
- host: endpoint.host,
1358
- port: endpoint.port,
1359
- database: endpoint.database
1360
- };
1361
- }
1362
- function isReplicaTarget(input) {
1363
- return "primary" in input || "replicas" in input || "weight" in input || "pool" in input;
1364
- }
1365
- function endpointOf(url) {
1366
- let parsed;
1367
- try {
1368
- parsed = new URL(url);
1369
- } catch {
1370
- throw new OkmError2("OKM1845", "The target URL is not a Postgres URL.", {
1371
- fix: { summary: "Pass a postgres:// URL for the target." }
1372
- });
1373
- }
1374
- const database = decodeURIComponent(parsed.pathname.replace(/^\//, ""));
1375
- return {
1376
- host: parsed.hostname.toLowerCase(),
1377
- port: parsed.port.length > 0 ? parsed.port : "5432",
1378
- database
1379
- };
1380
- }
1381
-
1382
1290
  // src/tooling/migrate/project.ts
1383
1291
  import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
1384
1292
  import { basename, join as join2 } from "node:path";
@@ -1462,9 +1370,20 @@ function emitRowTypes(source) {
1462
1370
  lines.push(updates.join(`
1463
1371
  `));
1464
1372
  lines.push("}");
1465
- return `${lines.join(`
1373
+ const body = `${lines.join(`
1466
1374
  `)}
1467
1375
  `;
1376
+ const aliases = [];
1377
+ if (body.includes("jsonb<"))
1378
+ aliases.push("export type jsonb<T> = T;");
1379
+ if (body.includes("json<"))
1380
+ aliases.push("export type json<T> = T;");
1381
+ if (aliases.length === 0)
1382
+ return body;
1383
+ return `${aliases.join(`
1384
+ `)}
1385
+
1386
+ ${body}`;
1468
1387
  }
1469
1388
  function propertyName(name) {
1470
1389
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : JSON.stringify(name);
@@ -1487,7 +1406,7 @@ function fields(item, kind, source) {
1487
1406
  const state = builder.state;
1488
1407
  if (kind === "row") {
1489
1408
  if (!state.hidden) {
1490
- printed.push(` readonly ${field}: ${valueType(state, "required")};`);
1409
+ printed.push(` readonly ${field}: ${valueType(state)};`);
1491
1410
  }
1492
1411
  continue;
1493
1412
  }
@@ -1496,7 +1415,8 @@ function fields(item, kind, source) {
1496
1415
  continue;
1497
1416
  }
1498
1417
  const mode = kind === "update" ? "optional" : written;
1499
- printed.push(` readonly ${field}: ${valueType(state, mode)};`);
1418
+ const label = valueType(state);
1419
+ printed.push(mode === "optional" ? ` readonly ${field}?: ${label};` : ` readonly ${field}: ${label};`);
1500
1420
  }
1501
1421
  return printed.join(`
1502
1422
  `);
@@ -1515,7 +1435,7 @@ function updateMode(state) {
1515
1435
  return "omit";
1516
1436
  return "optional";
1517
1437
  }
1518
- function valueType(state, mode) {
1438
+ function valueType(state) {
1519
1439
  let label = scalarLabel(state);
1520
1440
  if (state.dims > 0) {
1521
1441
  for (let rank = 0;rank < state.dims; rank += 1) {
@@ -1525,9 +1445,6 @@ function valueType(state, mode) {
1525
1445
  if (state.nullable) {
1526
1446
  label += " | null";
1527
1447
  }
1528
- if (mode === "optional") {
1529
- label += " | undefined";
1530
- }
1531
1448
  return label;
1532
1449
  }
1533
1450
  function scalarLabel(state) {
@@ -1563,8 +1480,9 @@ function scalarLabel(state) {
1563
1480
  case "bytea":
1564
1481
  return "Uint8Array";
1565
1482
  case "json":
1483
+ return "json<unknown>";
1566
1484
  case "jsonb":
1567
- return "unknown";
1485
+ return "jsonb<unknown>";
1568
1486
  case "timestamptz":
1569
1487
  return "Temporal.Instant";
1570
1488
  case "timestamp":
@@ -1610,7 +1528,7 @@ function baseName(dataType) {
1610
1528
  // src/dialects/pg/role/index.ts
1611
1529
  var PROVENANCE = { origin: "file", name: "roles" };
1612
1530
  var RELATION = ["SELECT", "INSERT", "UPDATE", "DELETE"];
1613
- var READ2 = ["SELECT"];
1531
+ var READ = ["SELECT"];
1614
1532
  var ROUTINE = ["EXECUTE"];
1615
1533
  var SEQUENCE = ["USAGE", "SELECT"];
1616
1534
  function attachRoles(source, input) {
@@ -1712,7 +1630,7 @@ function privilegesFor(kind) {
1712
1630
  if (kind === "sequence")
1713
1631
  return SEQUENCE;
1714
1632
  if (kind === "materializedView")
1715
- return READ2;
1633
+ return READ;
1716
1634
  if (kind === "namespace")
1717
1635
  return [];
1718
1636
  return RELATION;
@@ -1770,20 +1688,40 @@ async function readRowEstimates(url, schema, tables) {
1770
1688
  await pool.close();
1771
1689
  }
1772
1690
  }
1773
- function annotateLock(step, estimates) {
1691
+ function annotateLock(step, estimates, aliases = new Map) {
1774
1692
  const pieces = lockPieces(step);
1775
1693
  const text = withBatches(step, pieces.map((piece) => {
1776
1694
  if (piece.table === undefined)
1777
1695
  return piece.mode;
1778
- return `${piece.mode} on ${piece.table}, ${estimatePhrase(estimates.get(piece.table))}`;
1779
- }).join("; "), estimates);
1696
+ return `${piece.mode} on ${piece.table}, ${estimatePhrase(lookupEstimate(piece.table, estimates, aliases))}`;
1697
+ }).join("; "), estimates, aliases);
1780
1698
  if (step.safeRewrite === true)
1781
1699
  return `${text}; safe rewrite applied`;
1782
- if (exclusiveOverLarge(pieces, estimates)) {
1700
+ if (exclusiveOverLarge(pieces, estimates, aliases)) {
1783
1701
  return `${text}; note: more than ${String(LARGE_TABLE_ROWS)} estimated rows`;
1784
1702
  }
1785
1703
  return text;
1786
1704
  }
1705
+ function renameAliases(steps) {
1706
+ const map = new Map;
1707
+ for (const step of steps) {
1708
+ if (step.kind !== "rename-table")
1709
+ continue;
1710
+ const match = /^alter table "(?:(?:[^"]|"")*)"\."((?:[^"]|"")*)" rename to "((?:[^"]|"")*)"/.exec(step.sql);
1711
+ const from = match?.[1]?.replaceAll('""', '"');
1712
+ const to = match?.[2]?.replaceAll('""', '"');
1713
+ if (from !== undefined && to !== undefined)
1714
+ map.set(to, from);
1715
+ }
1716
+ return map;
1717
+ }
1718
+ function lookupEstimate(table, estimates, aliases) {
1719
+ const direct = estimates.get(table);
1720
+ if (direct !== undefined)
1721
+ return direct;
1722
+ const previous = aliases.get(table);
1723
+ return previous === undefined ? undefined : estimates.get(previous);
1724
+ }
1787
1725
  function aboutRows(count) {
1788
1726
  const rounded = Math.max(0, Math.round(count));
1789
1727
  if (rounded < 1000)
@@ -1803,12 +1741,12 @@ function aboutRows(count) {
1803
1741
  }
1804
1742
  return `about ${String(rounded)} rows`;
1805
1743
  }
1806
- function withBatches(step, text, estimates) {
1744
+ function withBatches(step, text, estimates, aliases = new Map) {
1807
1745
  const batch = step.backfill?.batch;
1808
1746
  if (batch === undefined)
1809
1747
  return text;
1810
1748
  const name = step.tables?.[0];
1811
- const estimate = name === undefined ? undefined : estimates.get(name);
1749
+ const estimate = name === undefined ? undefined : lookupEstimate(name, estimates, aliases);
1812
1750
  if (estimate === undefined || estimate.kind !== "rows")
1813
1751
  return text;
1814
1752
  return `${text}, about ${String(aboutBatchCount(estimate.reltuples, batch))} batches`;
@@ -1870,11 +1808,11 @@ function lockPieces(step) {
1870
1808
  }
1871
1809
  return pieces;
1872
1810
  }
1873
- function exclusiveOverLarge(pieces, estimates) {
1811
+ function exclusiveOverLarge(pieces, estimates, aliases = new Map) {
1874
1812
  for (const piece of pieces) {
1875
1813
  if (piece.mode !== "ACCESS EXCLUSIVE" || piece.table === undefined)
1876
1814
  continue;
1877
- const estimate = estimates.get(piece.table);
1815
+ const estimate = lookupEstimate(piece.table, estimates, aliases);
1878
1816
  if (estimate === undefined || estimate.kind !== "rows")
1879
1817
  continue;
1880
1818
  if (estimate.reltuples > LARGE_TABLE_ROWS)
@@ -1956,8 +1894,9 @@ async function checkProject(cwd, flags) {
1956
1894
  const target = selectTarget(opened.config, named);
1957
1895
  assertTargetPolicy(target, "check", flags?.allowProtected ?? false);
1958
1896
  assertTargetPolicy(target, "drift", flags?.allowProtected ?? false);
1959
- const { assertAuthorDrift } = await import("./ykv7wh5b.js");
1960
- await assertAuthorDrift(target.url, opened.built.catalog, opened.config.roles);
1897
+ const { assertAuthorDrift } = await import("./6qd27mx0.js");
1898
+ const allowPooler = flags?.allowPooler === true || opened.config.allowPooler === true;
1899
+ await assertAuthorDrift(target.url, opened.built.catalog, opened.config.roles, allowPooler);
1961
1900
  return findings;
1962
1901
  }
1963
1902
  function unlistedTableFiles(schemaSource, fileNames) {
@@ -2016,7 +1955,8 @@ async function displayPlan(config, plan, invoke) {
2016
1955
  } catch {
2017
1956
  return offline;
2018
1957
  }
2019
- return formatPlan(plan, (step) => annotateLock(step, estimates));
1958
+ const aliases = renameAliases(plan.steps);
1959
+ return formatPlan(plan, (step) => annotateLock(step, estimates, aliases));
2020
1960
  }
2021
1961
  function lintOpened(opened) {
2022
1962
  const plan = planMigration({
@@ -2200,4 +2140,4 @@ function runtimeImportsValidate(source) {
2200
2140
  return false;
2201
2141
  }
2202
2142
 
2203
- export { errorDoc, lintMigrationDirectory, formatFindings, hasError, lintRefusal, assertTargetPolicy, assertDirectConnection, listTargets, selectTarget, buildProject, generateProject, planProject, checkProject, projectHead, openProject, loadBuiltSchema, loadConfig, loadSchema };
2143
+ export { errorDoc, lintMigrationDirectory, formatFindings, hasError, lintRefusal, buildProject, generateProject, planProject, checkProject, projectHead, openProject, loadBuiltSchema, loadConfig, loadSchema };
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  isConnectionLost
3
- } from "./tbszx8mz.js";
3
+ } from "./pf0d8f77.js";
4
4
  import {
5
5
  openClient,
6
6
  withHttp,
7
7
  settleCall
8
- } from "./bt9sfq0z.js";
8
+ } from "./hf22e0wt.js";
9
9
  import {
10
10
  rejectKeys,
11
11
  isRecord,
12
12
  fail
13
- } from "./qgvt8nx1.js";
13
+ } from "./gbtcc2d7.js";
14
14
  import {
15
15
  OkmError2
16
16
  } from "./52szzakm.js";
@@ -92,6 +92,9 @@ class Frame {
92
92
  }
93
93
  }
94
94
  async function tx(session, args) {
95
+ if (typeof args[0] === "function" && args.length > 1) {
96
+ fail("OKM1120", "options go first: tx(options, fn).");
97
+ }
95
98
  const fn = typeof args[0] === "function" ? args[0] : args[1];
96
99
  const given = typeof args[0] === "function" ? {} : args[0] ?? {};
97
100
  try {
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  send,
3
3
  connectClient
4
- } from "./6p5k20sm.js";
4
+ } from "./96qcj21c.js";
5
5
  import {
6
6
  isConnectionLost
7
- } from "./tbszx8mz.js";
7
+ } from "./pf0d8f77.js";
8
8
  import {
9
9
  WireSession
10
- } from "./zvbjc949.js";
10
+ } from "./4az561fn.js";
11
11
  import {
12
12
  rejectClosed,
13
13
  runCall
14
- } from "./rrstp3kb.js";
14
+ } from "./cgbamx3e.js";
15
15
 
16
16
  // src/adapters/pg/nodepostgres-extra.ts
17
17
  var RESET_ALL = "RESET ALL";
@@ -43,7 +43,7 @@ async function checkout(held) {
43
43
  return {
44
44
  execute: (text, params, options) => guard(() => runCall(isClosed(), options, (watch) => session.query(text, params, watch))),
45
45
  batch: (statements, options) => guard(() => runCall(isClosed(), options, async (watch) => {
46
- const { runAtomicBatch } = await import("./b066g80h.js");
46
+ const { runAtomicBatch } = await import("./efmy2gcg.js");
47
47
  return runAtomicBatch(session, statements, watch);
48
48
  })),
49
49
  cancel() {
@@ -1,11 +1,12 @@
1
1
  import"./8bwbwy8h.js";
2
2
  import"./n442h54c.js";
3
- import"./wqem4jed.js";
3
+ import"./krxx77g0.js";
4
4
  import {
5
- listTargets,
6
5
  loadConfig
7
- } from "./xdpj5kkm.js";
8
- import"./ayz64vzj.js";
6
+ } from "./qv2ydck9.js";
7
+ import {
8
+ listTargets
9
+ } from "./ga3sfy93.js";
9
10
  import"./8xsbagad.js";
10
11
 
11
12
  // src/tooling/migrate/dev.ts
@@ -19,7 +20,7 @@ async function devProject(cwd) {
19
20
  `;
20
21
  const directory = join(cwd, config.out ?? ".okm", "dev-db");
21
22
  mkdirSync(directory, { recursive: true });
22
- const { open } = await import("./gdh9mrtp.js");
23
+ const { open } = await import("./f5cw5q5r.js");
23
24
  const pool = await open({ dataDir: directory });
24
25
  try {
25
26
  await pool.execute("select 1");