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
@@ -0,0 +1,139 @@
1
+ import {
2
+ tag
3
+ } from "./cn0n68ft.js";
4
+ import {
5
+ OkmError2
6
+ } from "./52szzakm.js";
7
+
8
+ // src/dialects/pg/filter-parse.ts
9
+ var PARSED_OPS = ",eq,in,inList,notIn,lt,lte,gt,gte,between,startsWith,endsWith,contains,like,ilike,";
10
+ var PARSED_FIX = "Use eq, in, notIn, lt, lte, gt, gte, between, startsWith, endsWith, contains, like, or ilike.";
11
+ function parseQuery(allow, sort, query) {
12
+ if (typeof query !== "object" || query === null || Array.isArray(query)) {
13
+ throw new OkmError2("OKM1121", "filters parse expects an object of fields.");
14
+ }
15
+ for (const field of Object.keys(allow)) {
16
+ for (const op of allow[field] ?? []) {
17
+ if (PARSED_OPS.includes(`,${op},`))
18
+ continue;
19
+ throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
20
+ fix: { summary: PARSED_FIX }
21
+ });
22
+ }
23
+ }
24
+ const whereParts = [];
25
+ let orderBy;
26
+ for (const key of Object.keys(query)) {
27
+ const value = query[key];
28
+ if (key === "sort" && typeof value === "string" && sort.includes(value)) {
29
+ orderBy = { [value]: "asc" };
30
+ continue;
31
+ }
32
+ if (!Object.hasOwn(allow, key)) {
33
+ throw new OkmError2("OKM1123", `Field ${key} is not in the allow list.`, {
34
+ fix: { summary: "Remove the field, or add it to filters({ allow })." }
35
+ });
36
+ }
37
+ const ops = allow[key] ?? [];
38
+ whereParts.push(...clauses(key, ops, value));
39
+ }
40
+ const where = combine(whereParts);
41
+ return orderBy === undefined ? { where } : { where, orderBy };
42
+ }
43
+ function clauses(field, ops, value) {
44
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
45
+ if (!ops.includes("eq")) {
46
+ throw new OkmError2("OKM1120", `filters() cannot apply eq on ${field}.`, {
47
+ fix: { summary: "Add eq to that field, or send an object of allowed operators." }
48
+ });
49
+ }
50
+ return [{ [field]: value }];
51
+ }
52
+ const applied = [];
53
+ for (const op of Object.keys(value)) {
54
+ if (!Object.hasOwn(value, op))
55
+ continue;
56
+ if (!ops.includes(op) && !(op === "inList" && ops.includes("in"))) {
57
+ throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
58
+ fix: { summary: "Send an operator that field's allow list names." }
59
+ });
60
+ }
61
+ applied.push(one(field, op, value[op]));
62
+ }
63
+ if (applied.length === 0) {
64
+ throw new OkmError2("OKM1120", `filters() cannot apply a value on ${field}.`, {
65
+ fix: { summary: "Send eq as a scalar, or an object of allowed operators." }
66
+ });
67
+ }
68
+ return applied;
69
+ }
70
+ function one(field, op, value) {
71
+ switch (op) {
72
+ case "eq":
73
+ return {
74
+ [field]: value !== null && typeof value === "object" ? tag("eq", value) : value
75
+ };
76
+ case "in":
77
+ case "inList":
78
+ return { [field]: tag("inList", list(value, op)) };
79
+ case "notIn":
80
+ return { [field]: tag("notIn", list(value, op)) };
81
+ case "lt":
82
+ return { [field]: tag("lt", value) };
83
+ case "lte":
84
+ return { [field]: tag("lte", value) };
85
+ case "gt":
86
+ return { [field]: tag("gt", value) };
87
+ case "gte":
88
+ return { [field]: tag("gte", value) };
89
+ case "between": {
90
+ if (!Array.isArray(value) || value.length !== 2) {
91
+ throw new OkmError2("OKM1120", `between on ${field} needs two bounds.`, {
92
+ fix: { summary: "Send between as [from, to]." }
93
+ });
94
+ }
95
+ return { [field]: tag("between", [value[0], value[1]]) };
96
+ }
97
+ case "startsWith":
98
+ return { [field]: tag("startsWith", text(value, op)) };
99
+ case "endsWith":
100
+ return { [field]: tag("endsWith", text(value, op)) };
101
+ case "contains":
102
+ return { [field]: tag("contains", value) };
103
+ case "like":
104
+ return { [field]: tag("like", text(value, op)) };
105
+ case "ilike":
106
+ return { [field]: tag("ilike", text(value, op)) };
107
+ default:
108
+ throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
109
+ fix: { summary: "Use an operator parse applies." }
110
+ });
111
+ }
112
+ }
113
+ function combine(parts) {
114
+ const first = parts[0];
115
+ if (first === undefined)
116
+ return {};
117
+ if (parts.length === 1)
118
+ return first;
119
+ return tag("and", parts);
120
+ }
121
+ function list(value, op) {
122
+ if (!Array.isArray(value)) {
123
+ throw new OkmError2("OKM1120", `${op} needs an array.`, {
124
+ fix: { summary: "Send that operator an array of values." }
125
+ });
126
+ }
127
+ return value;
128
+ }
129
+ function text(value, op) {
130
+ if (typeof value !== "string") {
131
+ throw new OkmError2("OKM1120", `${op} needs a string.`, {
132
+ fix: { summary: "Send that operator a string." }
133
+ });
134
+ }
135
+ return value;
136
+ }
137
+ export {
138
+ parseQuery
139
+ };
@@ -6,7 +6,7 @@ import {
6
6
  acquireTimeout2,
7
7
  errorField2,
8
8
  mapDriverError2
9
- } from "./kyxb62mc.js";
9
+ } from "./31vm914h.js";
10
10
  export {
11
11
  DriverError2 as DriverError,
12
12
  acquireTimeout2 as acquireTimeout,
@@ -11,7 +11,7 @@ async function assertCompatible(pool, schema, recordedHash, source, options) {
11
11
  if (code.artifact !== undefined) {
12
12
  const artifact = code.artifact;
13
13
  await loadOnce(`text\x00${artifact.hash}`, async () => {
14
- const { loadTrustedCatalog } = await import("./6693j9rs.js");
14
+ const { loadTrustedCatalog } = await import("./5fqx9yh4.js");
15
15
  loadTrustedCatalog(artifact.text, artifact.hash);
16
16
  });
17
17
  } else if (code.fromFile) {
@@ -25,7 +25,7 @@ async function assertCompatible(pool, schema, recordedHash, source, options) {
25
25
  }
26
26
  });
27
27
  }
28
- const { loadTrustedCatalog } = await import("./6693j9rs.js");
28
+ const { loadTrustedCatalog } = await import("./5fqx9yh4.js");
29
29
  loadTrustedCatalog(text, code.hash);
30
30
  });
31
31
  }
@@ -39,7 +39,7 @@ async function codeHash(schema, source) {
39
39
  const listed = await readHashFile(source.catalogDir);
40
40
  if (listed !== undefined)
41
41
  return { hash: listed, fromFile: true };
42
- const { catalogHash } = await import("./6693j9rs.js");
42
+ const { catalogHash } = await import("./5fqx9yh4.js");
43
43
  return { hash: catalogHash(schemaCatalog(schema)), fromFile: false };
44
44
  }
45
45
  function schemaCatalog(schema) {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  withHttp
3
- } from "./kkzwp3d2.js";
3
+ } from "./hf22e0wt.js";
4
4
  import {
5
5
  decodeResult
6
- } from "./xrfnq5k8.js";
6
+ } from "./gbtcc2d7.js";
7
7
  import {
8
8
  OkmError2
9
9
  } from "./52szzakm.js";
@@ -17,7 +17,7 @@ async function* streamRows(session, call, prepare, log) {
17
17
  })));
18
18
  }
19
19
  const { plan, params } = await prepare();
20
- const decode = plan.outputs.includes.length === 0 ? decodeResult : (await import("./edwqqryd.js")).decodeIncluded;
20
+ const decode = plan.outputs.includes.length === 0 ? decodeResult : (await import("./nkaxvt9q.js")).decodeIncluded;
21
21
  for await (const chunk of session.pool.stream(plan.text, params)) {
22
22
  const decoded = decode(plan, chunk, call.table);
23
23
  if (!Array.isArray(decoded))
@@ -1,6 +1,6 @@
1
- import"../shared/8jb4ymh2.js";
1
+ import"../shared/8bwbwy8h.js";
2
2
  import"../shared/n442h54c.js";
3
- import"../shared/wqem4jed.js";
3
+ import"../shared/krxx77g0.js";
4
4
  import {
5
5
  formatFindings,
6
6
  hasError,
@@ -9,9 +9,9 @@ import {
9
9
  generateProject,
10
10
  planProject,
11
11
  checkProject
12
- } from "../shared/q6zsyh31.js";
13
- import"../shared/n5y9zyj1.js";
14
- import"../shared/41kv2xdm.js";
12
+ } from "../shared/qv2ydck9.js";
13
+ import"../shared/ga3sfy93.js";
14
+ import"../shared/8xsbagad.js";
15
15
  import {
16
16
  OkmError2
17
17
  } from "../shared/52szzakm.js";
@@ -22,14 +22,28 @@ import { dirname, join } from "node:path";
22
22
  import { fileURLToPath } from "node:url";
23
23
 
24
24
  // src/tooling/migrate/commands.ts
25
- function formatFailure(error) {
26
- return `${error.code}: ${error.message}
27
- ${error.fix.summary}
25
+ function formatFailure(error, verbose = false) {
26
+ const mapped = OkmError2.from(error);
27
+ let text = `error ${mapped.code}: ${mapped.message}
28
+ ${mapped.fix.summary}
28
29
  `;
30
+ if (verbose && error instanceof Error && error.stack !== undefined) {
31
+ text += `${error.stack}
32
+ `;
33
+ }
34
+ return text;
29
35
  }
30
36
  async function run(argv, io) {
31
37
  const cwd = io?.cwd ?? process.cwd();
32
38
  const stdout = io?.stdout ?? ((text) => process.stdout.write(text));
39
+ if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
40
+ stdout(commandsTable());
41
+ return;
42
+ }
43
+ if (argv.includes("--help") || argv.includes("-h")) {
44
+ stdout(commandHelp(argv.filter((arg) => arg !== "--help" && arg !== "-h")));
45
+ return;
46
+ }
33
47
  const [command, ...rest] = argv;
34
48
  if (command === "build") {
35
49
  const directory = await buildProject(cwd);
@@ -57,12 +71,12 @@ async function run(argv, io) {
57
71
  return;
58
72
  }
59
73
  if (command === "dev") {
60
- const { devProject } = await import("../shared/yjw02enp.js");
74
+ const { devProject } = await import("../shared/sdc7r2ww.js");
61
75
  stdout(await devProject(cwd));
62
76
  return;
63
77
  }
64
78
  if (command === "push") {
65
- const { pushProject } = await import("../shared/kj680nxv.js");
79
+ const { pushProject } = await import("../shared/q58y17hy.js");
66
80
  stdout(await pushProject(cwd, invoke(splitFlags(rest))));
67
81
  return;
68
82
  }
@@ -81,17 +95,17 @@ async function run(argv, io) {
81
95
  return;
82
96
  }
83
97
  if (sub === "apply") {
84
- const { applyProject } = await import("../shared/kj680nxv.js");
98
+ const { applyProject } = await import("../shared/q58y17hy.js");
85
99
  stdout(await applyProject(cwd, invoke(parsed), stdout));
86
100
  return;
87
101
  }
88
102
  if (sub === "status") {
89
- const { statusProject } = await import("../shared/c5g81ngb.js");
103
+ const { statusProject } = await import("../shared/6mnp99hx.js");
90
104
  stdout(await statusProject(cwd, invoke(parsed)));
91
105
  return;
92
106
  }
93
107
  if (sub === "check") {
94
- const { checkMigrations } = await import("../shared/2321avmn.js");
108
+ const { checkMigrations } = await import("../shared/7zwwyh0v.js");
95
109
  const provision = parsed.flags.includes("--provision") || rest.slice(1).includes("--provision");
96
110
  stdout(await checkMigrations(cwd, invoke(parsed), { provision }));
97
111
  return;
@@ -104,13 +118,13 @@ async function run(argv, io) {
104
118
  fix: { summary: "Pass okm seed <file>." }
105
119
  });
106
120
  }
107
- const { seedProject } = await import("../shared/ady1ms74.js");
121
+ const { seedProject } = await import("../shared/2pqx26h8.js");
108
122
  stdout(await seedProject(cwd, parsed.name, invoke(parsed)));
109
123
  return;
110
124
  }
111
125
  if (command === "doctor") {
112
126
  const parsed = splitFlags(rest);
113
- const { doctorProject } = await import("../shared/84ptn35q.js");
127
+ const { doctorProject } = await import("../shared/ps7a6h14.js");
114
128
  stdout(await doctorProject(cwd, parsed.name, invoke(parsed)));
115
129
  return;
116
130
  }
@@ -122,7 +136,7 @@ async function run(argv, io) {
122
136
  });
123
137
  }
124
138
  if (sub === "list" || sub === "check") {
125
- const { extProject } = await import("../shared/tny2t3sm.js");
139
+ const { extProject } = await import("../shared/janx09m5.js");
126
140
  stdout(await extProject(cwd, sub, invoke(splitFlags(rest.slice(1)))));
127
141
  return;
128
142
  }
@@ -210,6 +224,42 @@ function splitFlags(args) {
210
224
  statementTimeoutMs
211
225
  };
212
226
  }
227
+ var COMMANDS = [
228
+ ["build", "Write the schema build."],
229
+ ["check", "Lint the schema and compare it with one target."],
230
+ ["dev", "Watch the schema and reload."],
231
+ ["doctor [code]", "Print a target report, or one error code."],
232
+ ["ext list", "List extensions the server can install."],
233
+ ["ext check", "Compare declared extensions with the server."],
234
+ ["generate [name]", "Write the next migration."],
235
+ ["migrate plan <name>", "Print the plan and its lock estimates."],
236
+ ["migrate apply", "Apply pending migrations."],
237
+ ["migrate status", "Print whether the target is current."],
238
+ ["migrate check", "Replay migration history in a scratch schema."],
239
+ ["push", "Apply the schema diff to one target."],
240
+ ["seed <file>", "Run a seed file on one target."]
241
+ ];
242
+ function commandsTable() {
243
+ const lines = ["okm <command>", ""];
244
+ for (const [name, summary] of COMMANDS)
245
+ lines.push(` ${name.padEnd(24)} ${summary}`);
246
+ lines.push("", "okm <command> --help prints that command.", "");
247
+ return `${lines.join(`
248
+ `)}
249
+ `;
250
+ }
251
+ function commandHelp(argv) {
252
+ const key = argv.join(" ");
253
+ const found = COMMANDS.find(([name]) => name === key || name.startsWith(`${key} `));
254
+ if (found === undefined) {
255
+ return `Unknown command ${key}.
256
+
257
+ ${commandsTable()}`;
258
+ }
259
+ return `${found[0]}
260
+ ${found[1]}
261
+ `;
262
+ }
213
263
  function numberFlag(flag, value) {
214
264
  const parsed = value === undefined ? Number.NaN : Number(value);
215
265
  if (!Number.isFinite(parsed) || parsed < 0) {
@@ -226,16 +276,13 @@ var packageJsonPath = join(here, "..", "..", "package.json");
226
276
  if (process.argv.includes("--version")) {
227
277
  process.stdout.write(`${readPackageVersion(packageJsonPath)}
228
278
  `);
229
- } else if (process.argv.length > 2) {
279
+ } else {
230
280
  try {
231
281
  await run(process.argv.slice(2));
232
282
  } catch (error) {
233
- if (error instanceof OkmError2) {
234
- process.stderr.write(formatFailure(error));
235
- process.exitCode = 1;
236
- } else {
237
- throw error;
238
- }
283
+ const verbose = process.argv.includes("--verbose") || process.env.OKM_DEBUG === "1";
284
+ process.stderr.write(formatFailure(error, verbose));
285
+ process.exitCode = 1;
239
286
  }
240
287
  }
241
288
  function readPackageVersion(path) {
@@ -90,6 +90,8 @@ export type ApplyRequest = {
90
90
  export type ApplyReport = {
91
91
  readonly target: string;
92
92
  readonly applied: readonly string[];
93
+ /** Migrations whose stored hash moved from the pre-D208 hash to the startup hash. */
94
+ readonly restamped?: readonly string[];
93
95
  /** Set when the runner issued `SET ROLE` at session start. */
94
96
  readonly setRole?: string;
95
97
  };
@@ -4,7 +4,6 @@
4
4
  * The bin wrapper imports {@link run}. Importing this module does not parse
5
5
  * `process.argv`.
6
6
  */
7
- import { OkmError } from "../../contracts/error.js";
8
7
  /** Where a command reads the project and writes its text. */
9
8
  export type CommandIo = {
10
9
  readonly cwd?: string;
@@ -18,7 +17,7 @@ export type CommandIo = {
18
17
  * @param error - Failure from a command
19
18
  * @returns The stderr text, including the trailing newline
20
19
  */
21
- export declare function formatFailure(error: OkmError): string;
20
+ export declare function formatFailure(error: unknown, verbose?: boolean): string;
22
21
  /**
23
22
  * Runs one `okm` invocation.
24
23
  *
@@ -18,7 +18,7 @@ import type { RolesInput } from "../../dialects/pg/role/index.js";
18
18
  * @param author - Schema catalog, including roles when the config names them
19
19
  * @param roles - `defineConfig({ roles })`, when set
20
20
  */
21
- export declare function assertAuthorDrift(url: string, author: Catalog, roles?: RolesInput): Promise<void>;
21
+ export declare function assertAuthorDrift(url: string, author: Catalog, roles?: RolesInput, allowPooler?: boolean): Promise<void>;
22
22
  /**
23
23
  * Turns a postgres.js handle into the query shape introspection uses.
24
24
  *
@@ -40,7 +40,20 @@ export declare function readRowEstimates(url: string, schema: string, tables: re
40
40
  * @param estimates - `reltuples` for tables that exist on the target
41
41
  * @returns The text after `-- lock: `
42
42
  */
43
- export declare function annotateLock(step: PlanStep, estimates: ReadonlyMap<string, RowEstimate>): string;
43
+ export declare function annotateLock(step: PlanStep, estimates: ReadonlyMap<string, RowEstimate>, aliases?: ReadonlyMap<string, string>): string;
44
+ /**
45
+ * Maps a rename destination to the table that still has `reltuples`.
46
+ *
47
+ * The rename statement names the old table. Later steps name the new one,
48
+ * which is not in `pg_class` yet.
49
+ *
50
+ * @param steps - Plan steps
51
+ * @returns New name to old name
52
+ */
53
+ export declare function renameAliases(steps: readonly {
54
+ readonly kind?: string;
55
+ readonly sql: string;
56
+ }[]): ReadonlyMap<string, string>;
44
57
  /**
45
58
  * Compact phrase for one estimate.
46
59
  *
@@ -7,6 +7,13 @@ import { type PlanStep } from "./plan.js";
7
7
  export type StoredMigration = {
8
8
  readonly id: string;
9
9
  readonly catalogHash: string;
10
+ /**
11
+ * The hash written before D208, which counted roles and grants.
12
+ *
13
+ * Set only when the catalog has them. Apply moves a stored copy of it to
14
+ * {@link StoredMigration.catalogHash}.
15
+ */
16
+ readonly legacyHash?: string;
10
17
  readonly steps: readonly PlanStep[];
11
18
  };
12
19
  /**
@@ -1,23 +1,26 @@
1
- import"../../shared/wqem4jed.js";
2
- import"../../shared/8jb4ymh2.js";
1
+ import"../../shared/krxx77g0.js";
2
+ import"../../shared/8bwbwy8h.js";
3
3
  import"../../shared/n442h54c.js";
4
4
  import {
5
5
  applyTarget2,
6
6
  backfillTiming2
7
- } from "../../shared/s4mb6a4b.js";
7
+ } from "../../shared/cctw17hj.js";
8
8
  import {
9
9
  loadMigrations,
10
10
  loadHeadSnapshot,
11
11
  readReference
12
- } from "../../shared/7nk68fj8.js";
12
+ } from "../../shared/gde73es6.js";
13
+ import"../../shared/szm0cksa.js";
14
+ import"../../shared/fancm5pt.js";
13
15
  import {
14
- assertTargetPolicy,
15
- selectTarget,
16
16
  loadBuiltSchema,
17
17
  loadConfig
18
- } from "../../shared/q6zsyh31.js";
19
- import"../../shared/n5y9zyj1.js";
20
- import"../../shared/41kv2xdm.js";
18
+ } from "../../shared/qv2ydck9.js";
19
+ import {
20
+ assertTargetPolicy,
21
+ selectTarget
22
+ } from "../../shared/ga3sfy93.js";
23
+ import"../../shared/8xsbagad.js";
21
24
  import {
22
25
  OkmError2
23
26
  } from "../../shared/52szzakm.js";
@@ -2,14 +2,14 @@
2
2
  * Runs the migration linter.
3
3
  *
4
4
  * The linter reads a plan and the two catalogs. It does not connect.
5
- * An override is a `-- okm-allow` line on the statement. It silences only
6
- * the code it names, and only with a reason.
5
+ * An override is a `-- okm-allow` line on the step. It silences that code
6
+ * on every statement of the step, and only with a reason.
7
7
  */
8
8
  import type { Catalog } from "../../contracts/catalog/types.js";
9
9
  import { OkmError } from "../../contracts/error.js";
10
10
  import { type LintSeverity } from "./lint-rules.js";
11
11
  import { type MigrationPlan } from "./plan.js";
12
- /** One finding. `place` is `step N` or `table.column`, optionally prefixed with the file. */
12
+ /** One finding. `place` is `step N`, `step N statement M`, or `table.column`. */
13
13
  export type Finding = {
14
14
  readonly severity: LintSeverity;
15
15
  readonly code: string;
@@ -70,10 +70,10 @@ export type PlanStep = {
70
70
  */
71
71
  readonly behavior?: "change";
72
72
  /**
73
- * `-- okm-allow` lines directly above the statement.
73
+ * `-- okm-allow` lines on the step.
74
74
  *
75
- * A reason silences that code only. An empty reason, or a code the
76
- * statement did not trigger, is OKM1510 and silences nothing.
75
+ * A reason silences that code on every statement of the step. An empty
76
+ * reason, or a code no statement triggered, is OKM1510 and silences nothing.
77
77
  */
78
78
  readonly allows?: readonly StepAllow[];
79
79
  };
@@ -128,6 +128,10 @@ export declare function formatPlan(plan: MigrationPlan, lockText?: (step: PlanSt
128
128
  /**
129
129
  * Reads a plan written by {@link formatPlan}.
130
130
  *
131
+ * A blank line ends a step. A blank line inside a quote, a dollar quote, or
132
+ * a block comment stays in that step, and so does a `--` line while the
133
+ * quote is still open.
134
+ *
131
135
  * @param text - SQL file text
132
136
  * @returns The plan. The header class is kept when the file has no steps
133
137
  */
@@ -56,6 +56,17 @@ export declare function assertTargetAlias(targets: readonly TargetRecord[]): voi
56
56
  * @param allowPooler - The invocation opted in
57
57
  */
58
58
  export declare function assertDirectConnection(url: string, allowPooler: boolean): void;
59
+ /**
60
+ * Refuses a session whose backend pid changes between two statements.
61
+ *
62
+ * The two reads are separate round trips and are not wrapped in a
63
+ * transaction. A transaction-mode pooler can hand back a different backend
64
+ * for the second one. `--allow-pooler` skips the probe.
65
+ *
66
+ * @param readPid - Reads `pg_backend_pid()` on the session under test
67
+ * @param allowPooler - The invocation opted in
68
+ */
69
+ export declare function assertStableBackend(readPid: () => Promise<string>, allowPooler: boolean): Promise<void>;
59
70
  /**
60
71
  * Lists configured targets.
61
72
  *
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Splits one migration step into statements.
3
+ *
4
+ * Comments are removed. A semicolon splits only at the top level.
5
+ * `standard_conforming_strings` is on: a plain `'...'` ends on a single
6
+ * quote, and `''` is one quote inside it. `E'...'` still treats a backslash
7
+ * as an escape. A dollar quote runs until the same tag; a different tag
8
+ * inside it is text, so `$outer$ ... $inner$ ... $inner$ ... $outer$` stays
9
+ * one span. An unterminated string, quoted identifier, block comment, or
10
+ * dollar quote is one opaque tail. This function does not throw.
11
+ */
12
+ /** Statements of `sql`, comments removed, with no trailing semicolon. */
13
+ export declare function sqlStatements(sql: string): readonly string[];
14
+ /**
15
+ * Whether `sql` still has an open quote or block comment.
16
+ *
17
+ * A line comment does not count: it ends at the newline or at the end.
18
+ *
19
+ * @param sql - Step text accumulated so far
20
+ * @returns `true` when a later blank line is still inside that quote
21
+ */
22
+ export declare function sqlQuoteOpen(sql: string): boolean;
@@ -114,4 +114,14 @@ export declare function bindFactories(host: FactoryHost, definitions: Readonly<R
114
114
  * @returns The inserted row
115
115
  */
116
116
  export declare function insertRow(host: FactoryHost, table: string, overrides: Readonly<Record<string, unknown>> | undefined, children: Readonly<Record<string, number>> | undefined, inherited: string | undefined): Promise<Record<string, unknown>>;
117
+ /**
118
+ * Drops a cached factory row.
119
+ *
120
+ * `isolation()` deletes the row it inserted. A later table must not reuse that id.
121
+ *
122
+ * @param host - The open harness
123
+ * @param table - Table name
124
+ * @param tenant - Tenant scope, when the table is tenanted
125
+ */
126
+ export declare function forgetCached(host: FactoryHost, table: string, tenant: string | undefined): void;
117
127
  export {};
@@ -56,6 +56,9 @@ export declare const tenantProbe = "00000000-0000-4000-8000-000000000001";
56
56
  /**
57
57
  * Reads every table in declaration order.
58
58
  *
59
+ * Views and materialized views are in the query model too. They have no rows
60
+ * of their own to insert, so they are left out.
61
+ *
59
62
  * @param schema - Compiled schema
60
63
  * @returns One facts object per table
61
64
  */
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  testing2
3
- } from "../../shared/ygem90rn.js";
4
- import"../../shared/41kv2xdm.js";
5
- import"../../shared/8jb4ymh2.js";
3
+ } from "../../shared/v36c5g6p.js";
4
+ import"../../shared/8xsbagad.js";
5
+ import"../../shared/8bwbwy8h.js";
6
6
  export {
7
7
  testing2 as testing
8
8
  };
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Cross-tenant isolation check.
3
3
  *
4
- * For every tenant table, insert one row as tenant B and run the basic reads
5
- * and writes as tenant A. A returned row, a count above zero, or an update or
6
- * delete that touches a row is a leak. Global tables are skipped and listed.
4
+ * For every tenant table, insert one row as tenant B and run the reads and
5
+ * writes as tenant A against that row's key. A returned row, or an update or
6
+ * delete that touches it, is a leak. The row is deleted afterwards. Global
7
+ * tables are skipped and listed.
7
8
  * Column tenancy is the strategy this version has. A table is tenant when its
8
9
  * predicate says so (D199).
9
10
  */