okmodel 0.4.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 (102) hide show
  1. package/README.md +95 -12
  2. package/dist/contracts/catalog/document.d.ts +20 -0
  3. package/dist/contracts/driver.d.ts +7 -0
  4. package/dist/dialects/pg/filter-parse.d.ts +8 -2
  5. package/dist/dialects/pg/fn/index.js +8 -0
  6. package/dist/dialects/pg/index.js +8 -2
  7. package/dist/dialects/pg/introspect.d.ts +6 -0
  8. package/dist/dialects/pg/ops/or.d.ts +1 -1
  9. package/dist/dialects/pg/role/sql.d.ts +10 -0
  10. package/dist/dialects/pg/schema.d.ts +15 -6
  11. package/dist/dialects/pg/temporal.d.ts +5 -76
  12. package/dist/dialects/pg/view/index.d.ts +20 -6
  13. package/dist/dialects/pg/view/index.js +1 -1
  14. package/dist/dialects/pg/view/scratch.d.ts +2 -2
  15. package/dist/runtime/client.d.ts +6 -1
  16. package/dist/runtime/conflict.d.ts +2 -1
  17. package/dist/runtime/pg/bun.d.ts +12 -7
  18. package/dist/runtime/pg/bun.js +7 -4
  19. package/dist/runtime/pg/pg.d.ts +12 -6
  20. package/dist/runtime/pg/pg.js +7 -4
  21. package/dist/runtime/pg/pglite.d.ts +11 -3
  22. package/dist/runtime/pg/pglite.js +7 -4
  23. package/dist/runtime/pg/postgresjs.d.ts +12 -6
  24. package/dist/runtime/pg/postgresjs.js +7 -4
  25. package/dist/runtime/plan.d.ts +16 -1
  26. package/dist/runtime/tenancy/index.js +1 -1
  27. package/dist/runtime/topology.d.ts +191 -0
  28. package/dist/runtime/traits/index.js +7 -7
  29. package/dist/runtime/types.d.ts +57 -2
  30. package/dist/runtime/validate/index.js +3 -3
  31. package/dist/shared/{radd3z4g.js → 0t22wwh1.js} +5 -9
  32. package/dist/shared/{ady1ms74.js → 2pqx26h8.js} +25 -10
  33. package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
  34. package/dist/shared/{jns2wx4t.js → 32zqc1dw.js} +18 -7
  35. package/dist/shared/{nxb42q7h.js → 47x8x9ze.js} +2 -2
  36. package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
  37. package/dist/shared/{s6gqfn5j.js → 51mvgj8w.js} +27 -3
  38. package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
  39. package/dist/shared/{3p4b1fg0.js → 5enne4ma.js} +1 -1
  40. package/dist/shared/{6693j9rs.js → 5fqx9yh4.js} +6 -2
  41. package/dist/shared/{c5g81ngb.js → 6mnp99hx.js} +11 -8
  42. package/dist/shared/{hqjsxnz7.js → 6qd27mx0.js} +5 -4
  43. package/dist/shared/{v7tpnhnz.js → 6rk2gen5.js} +5 -3
  44. package/dist/shared/{2321avmn.js → 7zwwyh0v.js} +46 -16
  45. package/dist/shared/{fa9vt245.js → 87jvvhnt.js} +2 -2
  46. package/dist/shared/{8jb4ymh2.js → 8bwbwy8h.js} +12 -1
  47. package/dist/shared/{c8aghh05.js → 8rsy3hxz.js} +7 -1
  48. package/dist/shared/{41kv2xdm.js → 8xsbagad.js} +1 -1
  49. package/dist/shared/{6p5k20sm.js → 96qcj21c.js} +3 -3
  50. package/dist/shared/{jgxzjzcz.js → aj2qxyhj.js} +2 -2
  51. package/dist/shared/{5vdm4sgp.js → caw8b9d8.js} +4 -4
  52. package/dist/shared/{s4mb6a4b.js → cctw17hj.js} +74 -15
  53. package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
  54. package/dist/shared/{axhk06q1.js → cr74e96e.js} +1 -1
  55. package/dist/shared/{b066g80h.js → efmy2gcg.js} +2 -2
  56. package/dist/shared/{gdh9mrtp.js → f5cw5q5r.js} +2 -2
  57. package/dist/shared/{xghkt7a9.js → fancm5pt.js} +6 -233
  58. package/dist/shared/{n5y9zyj1.js → ga3sfy93.js} +369 -14
  59. package/dist/shared/{xrfnq5k8.js → gbtcc2d7.js} +55 -5
  60. package/dist/shared/{7nk68fj8.js → gde73es6.js} +20 -15
  61. package/dist/shared/{kkzwp3d2.js → hf22e0wt.js} +55 -19
  62. package/dist/shared/{tny2t3sm.js → janx09m5.js} +8 -7
  63. package/dist/shared/{4ef7mmqf.js → jwtxdj06.js} +5 -5
  64. package/dist/shared/{sf8m6tfy.js → k8v5v5wj.js} +3 -3
  65. package/dist/shared/{qn0srwwq.js → kfjp0gck.js} +5 -3
  66. package/dist/shared/{wqem4jed.js → krxx77g0.js} +7 -7
  67. package/dist/shared/{edwqqryd.js → nkaxvt9q.js} +1 -1
  68. package/dist/shared/{jsd07prz.js → ntneex2a.js} +1 -1
  69. package/dist/shared/nxdp2ze3.js +1197 -0
  70. package/dist/shared/{tbszx8mz.js → pf0d8f77.js} +1 -1
  71. package/dist/shared/{84ptn35q.js → ps7a6h14.js} +10 -9
  72. package/dist/shared/{kj680nxv.js → q58y17hy.js} +9 -7
  73. package/dist/shared/{awrsqrn7.js → qm7cdtvr.js} +22 -9
  74. package/dist/shared/{q6zsyh31.js → qv2ydck9.js} +156 -205
  75. package/dist/shared/{kafaa5zv.js → r2sp8hrc.js} +28 -8
  76. package/dist/shared/{fb00y8fp.js → r8x5qhd7.js} +5 -5
  77. package/dist/shared/{yjw02enp.js → sdc7r2ww.js} +8 -7
  78. package/dist/shared/sycnx8gt.js +289 -0
  79. package/dist/shared/{hcmdtwgh.js → szm0cksa.js} +4 -1
  80. package/dist/shared/{zg24x0hm.js → tm5ab8we.js} +10 -2
  81. package/dist/shared/{ygem90rn.js → v36c5g6p.js} +85 -21
  82. package/dist/shared/wq9n6pwr.js +139 -0
  83. package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
  84. package/dist/shared/{q795afwa.js → z2rnngj4.js} +3 -3
  85. package/dist/shared/{sr5ms7b3.js → zqrnhwsq.js} +3 -3
  86. package/dist/tooling/cli.js +70 -23
  87. package/dist/tooling/migrate/apply.d.ts +2 -0
  88. package/dist/tooling/migrate/commands.d.ts +1 -2
  89. package/dist/tooling/migrate/drift.d.ts +1 -1
  90. package/dist/tooling/migrate/estimate.d.ts +14 -1
  91. package/dist/tooling/migrate/files.d.ts +7 -0
  92. package/dist/tooling/migrate/index.js +12 -9
  93. package/dist/tooling/migrate/lint.d.ts +3 -3
  94. package/dist/tooling/migrate/plan.d.ts +7 -3
  95. package/dist/tooling/migrate/policy.d.ts +11 -0
  96. package/dist/tooling/migrate/sql-lex.d.ts +22 -0
  97. package/dist/tooling/testing/factories.d.ts +10 -0
  98. package/dist/tooling/testing/facts.d.ts +3 -0
  99. package/dist/tooling/testing/index.js +3 -3
  100. package/dist/tooling/testing/isolation.d.ts +4 -3
  101. package/package.json +6 -5
  102. package/dist/shared/0v1g424p.js +0 -26
@@ -8,21 +8,27 @@ import {
8
8
  defaultPrivilegeObject,
9
9
  serializeCatalog2,
10
10
  catalogHash2,
11
+ startupCatalog2,
11
12
  parseCatalog2
12
- } from "./8jb4ymh2.js";
13
+ } from "./8bwbwy8h.js";
13
14
  import {
14
15
  open2
15
- } from "./wqem4jed.js";
16
+ } from "./krxx77g0.js";
16
17
  import {
17
18
  canonicalTypeName,
18
19
  batchSizeField,
19
20
  aboutBatchCount,
21
+ sqlStatements,
20
22
  parseReplace,
21
23
  planMigration,
22
24
  formatPlan,
23
25
  parsePlan,
24
- staleRenames
25
- } from "./n5y9zyj1.js";
26
+ staleRenames,
27
+ assertTargetPolicy,
28
+ assertTargetAlias,
29
+ listTargets,
30
+ selectTarget
31
+ } from "./ga3sfy93.js";
26
32
  import {
27
33
  contributedFields
28
34
  } from "./n0dgs0pb.js";
@@ -37,7 +43,7 @@ import {
37
43
  import {
38
44
  snakeCase,
39
45
  emittedTypeName
40
- } from "./jsd07prz.js";
46
+ } from "./ntneex2a.js";
41
47
  import {
42
48
  ColumnBuilder2
43
49
  } from "./c9z5g0fm.js";
@@ -149,8 +155,8 @@ var ERROR_DOCS = [
149
155
  {
150
156
  code: "OKM1102",
151
157
  title: "Unfiltered write",
152
- summary: "update or delete would run with an empty where.",
153
- 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 {}."
154
160
  },
155
161
  {
156
162
  code: "OKM1104",
@@ -179,14 +185,14 @@ var ERROR_DOCS = [
179
185
  {
180
186
  code: "OKM1120",
181
187
  title: "Unknown field",
182
- summary: "A field name in where, select, orderBy, or include is not on the table.",
183
- 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."
184
190
  },
185
191
  {
186
192
  code: "OKM1121",
187
193
  title: "Object as value",
188
- summary: "A plain object was passed where a value is required. JSON cannot create an operator.",
189
- 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."
190
196
  },
191
197
  {
192
198
  code: "OKM1122",
@@ -251,8 +257,8 @@ var ERROR_DOCS = [
251
257
  {
252
258
  code: "OKM1510",
253
259
  title: "Unsafe migration",
254
- summary: "A migration has an unresolved lint error, or an override with an empty reason or a code the statement did not trigger.",
255
- 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."
256
262
  },
257
263
  {
258
264
  code: "OKM1511",
@@ -443,8 +449,8 @@ var ERROR_DOCS = [
443
449
  {
444
450
  code: "OKM1542",
445
451
  title: "Data statement",
446
- summary: "A migration contains a data statement outside backfill().",
447
- 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."
448
454
  },
449
455
  {
450
456
  code: "OKM1543",
@@ -623,7 +629,7 @@ var ERROR_DOCS = [
623
629
  {
624
630
  code: "OKM1840",
625
631
  title: "Replica not allowed",
626
- summary: ".replica() was used on an operation that requires the primary, or inside tx().",
632
+ summary: "A replica route was used on an operation that requires the primary, or inside tx().",
627
633
  fix: "Run the operation on the primary. Writes, batch, locks, and tx() never go to a replica."
628
634
  },
629
635
  {
@@ -642,7 +648,7 @@ var ERROR_DOCS = [
642
648
  code: "OKM1843",
643
649
  title: "No eligible replica",
644
650
  summary: "A replica was required and none is eligible: unhealthy, behind the session, or none configured.",
645
- fix: "Configure a replica or drop .replica(). This call does not read the primary."
651
+ fix: "Configure a replica or drop the route. This call does not read the primary."
646
652
  },
647
653
  {
648
654
  code: "OKM1844",
@@ -685,6 +691,12 @@ var ERROR_DOCS = [
685
691
  title: "Target not named",
686
692
  summary: "Several targets are configured and the command was not given --target.",
687
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."
688
700
  }
689
701
  ];
690
702
  var byCode;
@@ -739,7 +751,7 @@ var STEP_RULES = [
739
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")),
740
752
  rule("OKM1537", "locking", "error", "SET NOT NULL locks the table while it scans for nulls.", (input) => hit(unsafeSetNotNull(input), "sets NOT NULL")),
741
753
  rule("OKM1538", "locking", "warning", "A type change that rewrites the table locks it for the rewrite.", (input) => typeChange(input, "rewrite")),
742
- 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))
743
755
  ];
744
756
  var COLUMN_RULES = [
745
757
  {
@@ -799,12 +811,18 @@ function normalized(sql) {
799
811
  return out;
800
812
  }
801
813
  function dataStatement(step) {
802
- if (step.backfill !== undefined)
803
- return false;
804
814
  const text = normalized(step.sql);
805
- if (/^(?:insert|update|delete|merge|truncate)\b/.test(text))
806
- return true;
807
- 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 [];
808
826
  }
809
827
  function matches(step, kind, pattern) {
810
828
  return pattern.test(normalized(step.sql)) || step.kind === kind;
@@ -979,9 +997,10 @@ function validates(input, kind) {
979
997
  return true;
980
998
  return false;
981
999
  }
1000
+ var TYPE_CHANGE = /^alter table\s+(\S+)\s+alter column\s+(\S+)\s+(?:set data type|type)\b/;
982
1001
  function typeChange(input, mode) {
983
1002
  const text = normalized(input.step.sql);
984
- 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";
985
1004
  if (!changing)
986
1005
  return [];
987
1006
  const compared = columnTypes(input);
@@ -1008,7 +1027,7 @@ function typeChange(input, mode) {
1008
1027
  }
1009
1028
  function columnTypes(input) {
1010
1029
  const text = normalized(input.step.sql);
1011
- 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);
1012
1031
  const table = lastName(match?.[1]);
1013
1032
  const column = lastName(match?.[2]);
1014
1033
  if (table === undefined || column === undefined)
@@ -1177,34 +1196,76 @@ function lintRefusal(findings) {
1177
1196
  });
1178
1197
  }
1179
1198
  function lintStep(step, number, before, after, existingTables, earlier, later) {
1180
- const hits = [];
1181
- for (const rule of STEP_RULES) {
1182
- for (const reason of rule.check({ step, before, after, existingTables, earlier, later })) {
1183
- 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
+ }
1184
1221
  }
1185
- }
1186
- 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
+ });
1187
1225
  const silenced = new Set;
1188
1226
  const findings = [];
1189
- const place = `step ${String(number)}`;
1227
+ const stepPlace = `step ${String(number)}`;
1190
1228
  for (const allow of step.allows ?? []) {
1191
1229
  if (allow.reason.trim().length === 0 || allow.code.length === 0) {
1192
- findings.push(finding("error", "OKM1510", place, "override needs a reason"));
1230
+ findings.push(finding("error", "OKM1510", stepPlace, "override needs a reason"));
1193
1231
  continue;
1194
1232
  }
1195
1233
  if (!triggered.has(allow.code)) {
1196
- 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`));
1197
1235
  continue;
1198
1236
  }
1199
1237
  silenced.add(allow.code);
1200
1238
  }
1201
- for (const hit of hits) {
1202
- if (silenced.has(hit.code))
1203
- continue;
1204
- 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
+ }
1205
1245
  }
1206
1246
  return findings;
1207
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
+ }
1208
1269
  function finding(severity, code, place, reason) {
1209
1270
  const doc = errorDoc(code);
1210
1271
  if (doc === undefined)
@@ -1226,148 +1287,6 @@ function columnPlace(column) {
1226
1287
  return `${column.identity.parent.name}.${column.identity.name}`;
1227
1288
  }
1228
1289
 
1229
- // src/tooling/migrate/policy.ts
1230
- var READ = new Set([
1231
- "plan",
1232
- "status",
1233
- "check",
1234
- "drift",
1235
- "verify",
1236
- "pull",
1237
- "catalog-export",
1238
- "inspect"
1239
- ]);
1240
- var ALLOWED = new Set(["expand", "reference", "provision"]);
1241
- var FORWARD_ONLY = new Set(["drop", "rollback"]);
1242
- function assertTargetPolicy(target, operation, allowProtected = false) {
1243
- if (FORWARD_ONLY.has(operation)) {
1244
- throw new OkmError2("invalid", "There is no down migration.", {
1245
- fix: { summary: "Recovery is a new forward migration." }
1246
- });
1247
- }
1248
- if (!target.protected || allowProtected)
1249
- return;
1250
- if (READ.has(operation) || ALLOWED.has(operation))
1251
- return;
1252
- throw new OkmError2("OKM1850", `${operation} is blocked on protected target ${target.name}.`, {
1253
- kind: "forbidden",
1254
- fix: {
1255
- summary: "Run a read-only command or an expand migration, or pass --allow-protected for this invocation."
1256
- }
1257
- });
1258
- }
1259
- function assertTargetAlias(targets) {
1260
- const groups = new Map;
1261
- for (const target of targets) {
1262
- const key = `${target.host}
1263
- ${target.port}
1264
- ${target.database}`;
1265
- const group = groups.get(key);
1266
- if (group === undefined)
1267
- groups.set(key, [target]);
1268
- else
1269
- group.push(target);
1270
- }
1271
- for (const group of groups.values()) {
1272
- const protectedTarget = group.find((target) => target.protected);
1273
- const open = group.find((target) => !target.protected);
1274
- if (protectedTarget !== undefined && open !== undefined) {
1275
- throw new OkmError2("OKM1852", `${open.name} and ${protectedTarget.name} resolve to the same database and disagree about protection.`, {
1276
- fix: {
1277
- summary: "Give those targets the same protection flag. okm check and okm doctor both report this."
1278
- }
1279
- });
1280
- }
1281
- }
1282
- }
1283
- function assertDirectConnection(url, allowPooler) {
1284
- if (allowPooler)
1285
- return;
1286
- const endpoint = endpointOf(url);
1287
- const parsed = new URL(url);
1288
- const host = endpoint.host;
1289
- if (host.includes("pooler") || host.includes("pgbouncer") || host.includes("-pooler") || endpoint.port === "6543" || parsed.searchParams.get("pgbouncer") === "true") {
1290
- throw new OkmError2("invalid", `Refusing pooler host ${host}. Apply needs a direct connection.`, { fix: { summary: "Use a direct connection, or pass --allow-pooler." } });
1291
- }
1292
- }
1293
- function listTargets(config) {
1294
- if (config.database !== undefined && config.targets !== undefined) {
1295
- throw new OkmError2("invalid", "Set database or targets, not both.", {
1296
- fix: { summary: "database is the one target named default. Use targets for more than one." }
1297
- });
1298
- }
1299
- const entries = [];
1300
- if (config.database !== undefined)
1301
- entries.push(["default", config.database]);
1302
- if (config.targets !== undefined) {
1303
- for (const name of Object.keys(config.targets).sort()) {
1304
- const input = config.targets[name];
1305
- if (input !== undefined)
1306
- entries.push([name, input]);
1307
- }
1308
- }
1309
- return entries.map(([name, input]) => resolveTarget(name, input));
1310
- }
1311
- function selectTarget(config, name) {
1312
- const targets = listTargets(config);
1313
- if (targets.length === 0) {
1314
- throw new OkmError2("OKM1845", "No target is configured.", {
1315
- fix: { summary: "Set database or targets in okmodel.config.ts." }
1316
- });
1317
- }
1318
- if (name === undefined) {
1319
- if (targets.length === 1) {
1320
- const only = targets[0];
1321
- if (only !== undefined)
1322
- return only;
1323
- }
1324
- throw new OkmError2("OKM1853", "Several targets are configured and --target was not passed.", {
1325
- fix: { summary: "Pass --target <name>. The command does not guess." }
1326
- });
1327
- }
1328
- const found = targets.find((target) => target.name === name);
1329
- if (found === undefined) {
1330
- throw new OkmError2("OKM1845", `Target ${name} is not configured.`, {
1331
- fix: { summary: "Pass a name from targets, or database for the target named default." }
1332
- });
1333
- }
1334
- return found;
1335
- }
1336
- function resolveTarget(name, input) {
1337
- const url = typeof input === "string" ? input : input.url;
1338
- const protectedTarget = typeof input === "string" ? false : input.protected === true;
1339
- if (url === undefined || url.length === 0) {
1340
- throw new OkmError2("OKM1845", `Target ${name} has no url.`, {
1341
- fix: { summary: "Set the target url. A target is not inferred from the environment." }
1342
- });
1343
- }
1344
- const endpoint = endpointOf(url);
1345
- return {
1346
- name,
1347
- url,
1348
- protected: protectedTarget,
1349
- host: endpoint.host,
1350
- port: endpoint.port,
1351
- database: endpoint.database
1352
- };
1353
- }
1354
- function endpointOf(url) {
1355
- let parsed;
1356
- try {
1357
- parsed = new URL(url);
1358
- } catch {
1359
- throw new OkmError2("OKM1845", "The target URL is not a Postgres URL.", {
1360
- fix: { summary: "Pass a postgres:// URL for the target." }
1361
- });
1362
- }
1363
- const database = decodeURIComponent(parsed.pathname.replace(/^\//, ""));
1364
- return {
1365
- host: parsed.hostname.toLowerCase(),
1366
- port: parsed.port.length > 0 ? parsed.port : "5432",
1367
- database
1368
- };
1369
- }
1370
-
1371
1290
  // src/tooling/migrate/project.ts
1372
1291
  import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
1373
1292
  import { basename, join as join2 } from "node:path";
@@ -1451,9 +1370,20 @@ function emitRowTypes(source) {
1451
1370
  lines.push(updates.join(`
1452
1371
  `));
1453
1372
  lines.push("}");
1454
- return `${lines.join(`
1373
+ const body = `${lines.join(`
1455
1374
  `)}
1456
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}`;
1457
1387
  }
1458
1388
  function propertyName(name) {
1459
1389
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : JSON.stringify(name);
@@ -1476,7 +1406,7 @@ function fields(item, kind, source) {
1476
1406
  const state = builder.state;
1477
1407
  if (kind === "row") {
1478
1408
  if (!state.hidden) {
1479
- printed.push(` readonly ${field}: ${valueType(state, "required")};`);
1409
+ printed.push(` readonly ${field}: ${valueType(state)};`);
1480
1410
  }
1481
1411
  continue;
1482
1412
  }
@@ -1485,7 +1415,8 @@ function fields(item, kind, source) {
1485
1415
  continue;
1486
1416
  }
1487
1417
  const mode = kind === "update" ? "optional" : written;
1488
- printed.push(` readonly ${field}: ${valueType(state, mode)};`);
1418
+ const label = valueType(state);
1419
+ printed.push(mode === "optional" ? ` readonly ${field}?: ${label};` : ` readonly ${field}: ${label};`);
1489
1420
  }
1490
1421
  return printed.join(`
1491
1422
  `);
@@ -1504,7 +1435,7 @@ function updateMode(state) {
1504
1435
  return "omit";
1505
1436
  return "optional";
1506
1437
  }
1507
- function valueType(state, mode) {
1438
+ function valueType(state) {
1508
1439
  let label = scalarLabel(state);
1509
1440
  if (state.dims > 0) {
1510
1441
  for (let rank = 0;rank < state.dims; rank += 1) {
@@ -1514,9 +1445,6 @@ function valueType(state, mode) {
1514
1445
  if (state.nullable) {
1515
1446
  label += " | null";
1516
1447
  }
1517
- if (mode === "optional") {
1518
- label += " | undefined";
1519
- }
1520
1448
  return label;
1521
1449
  }
1522
1450
  function scalarLabel(state) {
@@ -1552,8 +1480,9 @@ function scalarLabel(state) {
1552
1480
  case "bytea":
1553
1481
  return "Uint8Array";
1554
1482
  case "json":
1483
+ return "json<unknown>";
1555
1484
  case "jsonb":
1556
- return "unknown";
1485
+ return "jsonb<unknown>";
1557
1486
  case "timestamptz":
1558
1487
  return "Temporal.Instant";
1559
1488
  case "timestamp":
@@ -1599,7 +1528,7 @@ function baseName(dataType) {
1599
1528
  // src/dialects/pg/role/index.ts
1600
1529
  var PROVENANCE = { origin: "file", name: "roles" };
1601
1530
  var RELATION = ["SELECT", "INSERT", "UPDATE", "DELETE"];
1602
- var READ2 = ["SELECT"];
1531
+ var READ = ["SELECT"];
1603
1532
  var ROUTINE = ["EXECUTE"];
1604
1533
  var SEQUENCE = ["USAGE", "SELECT"];
1605
1534
  function attachRoles(source, input) {
@@ -1701,7 +1630,7 @@ function privilegesFor(kind) {
1701
1630
  if (kind === "sequence")
1702
1631
  return SEQUENCE;
1703
1632
  if (kind === "materializedView")
1704
- return READ2;
1633
+ return READ;
1705
1634
  if (kind === "namespace")
1706
1635
  return [];
1707
1636
  return RELATION;
@@ -1759,20 +1688,40 @@ async function readRowEstimates(url, schema, tables) {
1759
1688
  await pool.close();
1760
1689
  }
1761
1690
  }
1762
- function annotateLock(step, estimates) {
1691
+ function annotateLock(step, estimates, aliases = new Map) {
1763
1692
  const pieces = lockPieces(step);
1764
1693
  const text = withBatches(step, pieces.map((piece) => {
1765
1694
  if (piece.table === undefined)
1766
1695
  return piece.mode;
1767
- return `${piece.mode} on ${piece.table}, ${estimatePhrase(estimates.get(piece.table))}`;
1768
- }).join("; "), estimates);
1696
+ return `${piece.mode} on ${piece.table}, ${estimatePhrase(lookupEstimate(piece.table, estimates, aliases))}`;
1697
+ }).join("; "), estimates, aliases);
1769
1698
  if (step.safeRewrite === true)
1770
1699
  return `${text}; safe rewrite applied`;
1771
- if (exclusiveOverLarge(pieces, estimates)) {
1700
+ if (exclusiveOverLarge(pieces, estimates, aliases)) {
1772
1701
  return `${text}; note: more than ${String(LARGE_TABLE_ROWS)} estimated rows`;
1773
1702
  }
1774
1703
  return text;
1775
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
+ }
1776
1725
  function aboutRows(count) {
1777
1726
  const rounded = Math.max(0, Math.round(count));
1778
1727
  if (rounded < 1000)
@@ -1792,12 +1741,12 @@ function aboutRows(count) {
1792
1741
  }
1793
1742
  return `about ${String(rounded)} rows`;
1794
1743
  }
1795
- function withBatches(step, text, estimates) {
1744
+ function withBatches(step, text, estimates, aliases = new Map) {
1796
1745
  const batch = step.backfill?.batch;
1797
1746
  if (batch === undefined)
1798
1747
  return text;
1799
1748
  const name = step.tables?.[0];
1800
- const estimate = name === undefined ? undefined : estimates.get(name);
1749
+ const estimate = name === undefined ? undefined : lookupEstimate(name, estimates, aliases);
1801
1750
  if (estimate === undefined || estimate.kind !== "rows")
1802
1751
  return text;
1803
1752
  return `${text}, about ${String(aboutBatchCount(estimate.reltuples, batch))} batches`;
@@ -1859,11 +1808,11 @@ function lockPieces(step) {
1859
1808
  }
1860
1809
  return pieces;
1861
1810
  }
1862
- function exclusiveOverLarge(pieces, estimates) {
1811
+ function exclusiveOverLarge(pieces, estimates, aliases = new Map) {
1863
1812
  for (const piece of pieces) {
1864
1813
  if (piece.mode !== "ACCESS EXCLUSIVE" || piece.table === undefined)
1865
1814
  continue;
1866
- const estimate = estimates.get(piece.table);
1815
+ const estimate = lookupEstimate(piece.table, estimates, aliases);
1867
1816
  if (estimate === undefined || estimate.kind !== "rows")
1868
1817
  continue;
1869
1818
  if (estimate.reltuples > LARGE_TABLE_ROWS)
@@ -1945,8 +1894,9 @@ async function checkProject(cwd, flags) {
1945
1894
  const target = selectTarget(opened.config, named);
1946
1895
  assertTargetPolicy(target, "check", flags?.allowProtected ?? false);
1947
1896
  assertTargetPolicy(target, "drift", flags?.allowProtected ?? false);
1948
- const { assertAuthorDrift } = await import("./hqjsxnz7.js");
1949
- 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);
1950
1900
  return findings;
1951
1901
  }
1952
1902
  function unlistedTableFiles(schemaSource, fileNames) {
@@ -2005,7 +1955,8 @@ async function displayPlan(config, plan, invoke) {
2005
1955
  } catch {
2006
1956
  return offline;
2007
1957
  }
2008
- return formatPlan(plan, (step) => annotateLock(step, estimates));
1958
+ const aliases = renameAliases(plan.steps);
1959
+ return formatPlan(plan, (step) => annotateLock(step, estimates, aliases));
2009
1960
  }
2010
1961
  function lintOpened(opened) {
2011
1962
  const plan = planMigration({
@@ -2046,9 +1997,9 @@ function withRoles(built, config) {
2046
1997
  function writeArtifact(cwd, config, built) {
2047
1998
  const directory = join2(cwd, config.out ?? ".okm");
2048
1999
  mkdirSync(directory, { recursive: true });
2049
- const text = serializeCatalog2(built.catalog);
2050
- writeFileSync(join2(directory, "catalog.json"), text);
2051
- writeFileSync(join2(directory, "catalog.hash"), `${catalogHash2(built.catalog)}
2000
+ const startup = startupCatalog2(built.catalog);
2001
+ writeFileSync(join2(directory, "catalog.json"), serializeCatalog2(startup));
2002
+ writeFileSync(join2(directory, "catalog.hash"), `${catalogHash2(startup)}
2052
2003
  `);
2053
2004
  writeFileSync(join2(directory, "types.d.ts"), `${emitRowTypes(built)}${referenceAugmentation(built)}`);
2054
2005
  return directory;
@@ -2189,4 +2140,4 @@ function runtimeImportsValidate(source) {
2189
2140
  return false;
2190
2141
  }
2191
2142
 
2192
- 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 };