okmodel 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +263 -23
  2. package/dist/contracts/catalog/document.d.ts +26 -2
  3. package/dist/contracts/driver.d.ts +7 -0
  4. package/dist/dialects/pg/ext/citext.js +1 -1
  5. package/dist/dialects/pg/ext/index.js +1 -1
  6. package/dist/dialects/pg/ext/pg-trgm.js +1 -1
  7. package/dist/dialects/pg/index.js +3 -3
  8. package/dist/dialects/pg/introspect.d.ts +6 -0
  9. package/dist/dialects/pg/role/sql.d.ts +21 -4
  10. package/dist/dialects/pg/schema.d.ts +15 -6
  11. package/dist/dialects/pg/table.d.ts +17 -1
  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 -1
  15. package/dist/runtime/catalog-cache.d.ts +17 -0
  16. package/dist/runtime/compat-words.d.ts +23 -0
  17. package/dist/runtime/drift-detail.d.ts +20 -4
  18. package/dist/runtime/drift.d.ts +4 -2
  19. package/dist/runtime/pg/bun.d.ts +12 -7
  20. package/dist/runtime/pg/bun.js +5 -2
  21. package/dist/runtime/pg/pg.d.ts +12 -6
  22. package/dist/runtime/pg/pg.js +5 -2
  23. package/dist/runtime/pg/pglite.d.ts +11 -3
  24. package/dist/runtime/pg/pglite.js +5 -2
  25. package/dist/runtime/pg/postgresjs.d.ts +12 -6
  26. package/dist/runtime/pg/postgresjs.js +5 -2
  27. package/dist/runtime/plan.d.ts +3 -1
  28. package/dist/runtime/tenancy/index.js +1 -1
  29. package/dist/runtime/topology.d.ts +191 -0
  30. package/dist/runtime/traits/index.js +7 -6
  31. package/dist/runtime/types.d.ts +59 -2
  32. package/dist/runtime/validate/index.js +2 -2
  33. package/dist/shared/{x6wysh83.js → 3p4b1fg0.js} +3 -5
  34. package/dist/shared/{ty3v3hfk.js → 47xzb9fc.js} +2 -2
  35. package/dist/shared/{fs0ex00p.js → 4ef7mmqf.js} +2 -2
  36. package/dist/shared/{qybh48nd.js → 5fqx9yh4.js} +6 -2
  37. package/dist/shared/5mtf6cj3.js +94 -0
  38. package/dist/shared/{999ade2q.js → 5vdm4sgp.js} +1 -1
  39. package/dist/shared/{bqha5cg6.js → 6p5k20sm.js} +2 -2
  40. package/dist/shared/{t616ynfn.js → 8bwbwy8h.js} +71 -13
  41. package/dist/shared/{th14ygv2.js → 8ktwd3rh.js} +29 -1
  42. package/dist/shared/{wekze34d.js → 8xsbagad.js} +74 -2
  43. package/dist/shared/{bryqk9k9.js → 922qbgnw.js} +3 -3
  44. package/dist/shared/{meck53jv.js → 990a291j.js} +6 -5
  45. package/dist/shared/{jns2wx4t.js → 9tgmr1gh.js} +1 -1
  46. package/dist/shared/9yn6a2vd.js +11 -0
  47. package/dist/shared/{2ssqrv66.js → ayz64vzj.js} +1037 -181
  48. package/dist/shared/{k34e0xf2.js → bt9sfq0z.js} +16 -14
  49. package/dist/shared/{scwxrpx2.js → bzsjtd7c.js} +84 -15
  50. package/dist/shared/{m9tv7xn7.js → ckjbyrt9.js} +4 -4
  51. package/dist/shared/{ydaz1sv3.js → dxjp14rk.js} +10 -2
  52. package/dist/shared/{49ypnfwt.js → fancm5pt.js} +24 -229
  53. package/dist/shared/{6bazvqwm.js → fb00y8fp.js} +2 -2
  54. package/dist/shared/{1tfdfvhh.js → fyqkk1m0.js} +6 -4
  55. package/dist/shared/g516zdsb.js +905 -0
  56. package/dist/shared/{jkc6ws1p.js → g7ez901n.js} +83 -2
  57. package/dist/shared/{kgnc8grt.js → gdh9mrtp.js} +1 -1
  58. package/dist/shared/{p24x2334.js → h4nqvjkp.js} +5 -3
  59. package/dist/shared/{edwqqryd.js → h8ne16fq.js} +1 -1
  60. package/dist/shared/htnaj77v.js +17 -0
  61. package/dist/shared/jdgqqx31.js +72 -0
  62. package/dist/shared/{6w5j844a.js → jsd07prz.js} +2 -2
  63. package/dist/shared/{m78s7898.js → k7cc9nzk.js} +24 -7
  64. package/dist/shared/kdkxh3kb.js +758 -0
  65. package/dist/shared/nkc9sfqb.js +1161 -0
  66. package/dist/shared/{xva8v7qk.js → p7pfx624.js} +5 -5
  67. package/dist/shared/{xrfnq5k8.js → qgvt8nx1.js} +11 -2
  68. package/dist/shared/{ne02ng5c.js → qy9rdq0g.js} +9 -5
  69. package/dist/shared/{3ekj9x92.js → r6pj4fq2.js} +5 -3
  70. package/dist/shared/r73pt594.js +426 -0
  71. package/dist/shared/rasvvzgz.js +21 -0
  72. package/dist/shared/{01yr20s5.js → sf8m6tfy.js} +2 -2
  73. package/dist/shared/{hcmdtwgh.js → szm0cksa.js} +4 -1
  74. package/dist/shared/{axhk06q1.js → tq9afp8d.js} +1 -1
  75. package/dist/shared/{xw1d69vd.js → wqem4jed.js} +6 -6
  76. package/dist/shared/wxn4b7pp.js +252 -0
  77. package/dist/shared/xdpj5kkm.js +2203 -0
  78. package/dist/shared/xhh1srw3.js +589 -0
  79. package/dist/shared/{mt5wswjz.js → xsq7v6wp.js} +2 -2
  80. package/dist/shared/{7fvtrkwr.js → ycatkxmq.js} +1 -1
  81. package/dist/shared/ykv7wh5b.js +14 -0
  82. package/dist/shared/{fptpmvxp.js → z2rnngj4.js} +26 -21
  83. package/dist/tooling/cli.js +46 -17
  84. package/dist/tooling/migrate/apply.d.ts +61 -5
  85. package/dist/tooling/migrate/backfill.d.ts +136 -0
  86. package/dist/tooling/migrate/check.d.ts +65 -0
  87. package/dist/tooling/migrate/classify.d.ts +108 -0
  88. package/dist/tooling/migrate/commands.d.ts +2 -2
  89. package/dist/tooling/migrate/config.d.ts +22 -0
  90. package/dist/tooling/migrate/doctor.d.ts +3 -0
  91. package/dist/tooling/migrate/drift.d.ts +18 -2
  92. package/dist/tooling/migrate/estimate.d.ts +50 -0
  93. package/dist/tooling/migrate/extensions.d.ts +2 -0
  94. package/dist/tooling/migrate/files.d.ts +19 -0
  95. package/dist/tooling/migrate/index.d.ts +3 -2
  96. package/dist/tooling/migrate/index.js +69 -1
  97. package/dist/tooling/migrate/lint-rules.d.ts +51 -0
  98. package/dist/tooling/migrate/lint.d.ts +71 -0
  99. package/dist/tooling/migrate/managed.d.ts +1 -1
  100. package/dist/tooling/migrate/plan.d.ts +64 -9
  101. package/dist/tooling/migrate/project.d.ts +42 -7
  102. package/dist/tooling/migrate/provision.d.ts +18 -0
  103. package/dist/tooling/migrate/reference.d.ts +40 -0
  104. package/dist/tooling/migrate/runner.d.ts +108 -0
  105. package/dist/tooling/migrate/safe.d.ts +77 -0
  106. package/dist/tooling/migrate/seed.d.ts +20 -0
  107. package/dist/tooling/migrate/snapshot.d.ts +78 -0
  108. package/dist/tooling/migrate/status.d.ts +25 -6
  109. package/dist/tooling/testing/factories.d.ts +117 -0
  110. package/dist/tooling/testing/facts.d.ts +75 -0
  111. package/dist/tooling/testing/index.d.ts +89 -0
  112. package/dist/tooling/testing/index.js +8 -0
  113. package/dist/tooling/testing/isolation.d.ts +30 -0
  114. package/dist/tooling/testing/random.d.ts +40 -0
  115. package/dist/tooling/testing/record.d.ts +38 -0
  116. package/package.json +6 -2
  117. package/dist/shared/5nmdenb6.js +0 -1229
  118. package/dist/shared/dbjej1tw.js +0 -456
  119. package/dist/shared/g4f5kcx9.js +0 -37
  120. package/dist/shared/qbxzvwwc.js +0 -88
  121. package/dist/shared/zwmqxb0g.js +0 -79
  122. /package/dist/shared/{75xkjv0t.js → fa9vt245.js} +0 -0
  123. /package/dist/shared/{0jc3ffzf.js → zvbjc949.js} +0 -0
@@ -3,6 +3,9 @@ import {
3
3
  textArray,
4
4
  arrayElementType
5
5
  } from "./n7mcc1gk.js";
6
+ import {
7
+ stack
8
+ } from "./672t5vym.js";
6
9
  import {
7
10
  touchFields,
8
11
  fieldSealed,
@@ -11,9 +14,6 @@ import {
11
14
  import {
12
15
  runWrite
13
16
  } from "./n4jqhpqz.js";
14
- import {
15
- stack
16
- } from "./672t5vym.js";
17
17
  import {
18
18
  writeWouldValidate
19
19
  } from "./9m80mkac.js";
@@ -34,7 +34,7 @@ import {
34
34
  isRecord,
35
35
  registerFailFix,
36
36
  fail
37
- } from "./xrfnq5k8.js";
37
+ } from "./qgvt8nx1.js";
38
38
  import {
39
39
  safetyInstalled,
40
40
  runSafety
@@ -179,7 +179,7 @@ async function planInsert(table, input, options, mods, generators, scope, tenanc
179
179
  keyFields: undefined
180
180
  };
181
181
  }
182
- const conflictMod = options.onConflict === undefined || options.onConflict === "error" ? undefined : await import("./jns2wx4t.js");
182
+ const conflictMod = options.onConflict === undefined || options.onConflict === "error" ? undefined : await import("./9tgmr1gh.js");
183
183
  const conflict = conflictMod?.readConflict(table, options.onConflict, allow);
184
184
  const chunks = chunkRows(rows.map((row) => cellsFor(columns, row)));
185
185
  const statements = [];
@@ -42,12 +42,21 @@ var FIND_OPTIONS = [
42
42
  "limit",
43
43
  "orderBy",
44
44
  "select",
45
+ "route",
45
46
  "signal",
46
47
  "timeout",
47
48
  "where"
48
49
  ];
49
- var ONE_OPTIONS = ["include", "orderBy", "select", "signal", "timeout", "where"];
50
- var FILTER_OPTIONS = ["signal", "timeout", "where"];
50
+ var ONE_OPTIONS = [
51
+ "include",
52
+ "orderBy",
53
+ "route",
54
+ "select",
55
+ "signal",
56
+ "timeout",
57
+ "where"
58
+ ];
59
+ var FILTER_OPTIONS = ["route", "signal", "timeout", "where"];
51
60
  function readOptionNames(op) {
52
61
  if (op === "find")
53
62
  return FIND_OPTIONS;
@@ -1,19 +1,22 @@
1
1
  import {
2
2
  open2
3
- } from "./xw1d69vd.js";
3
+ } from "./wqem4jed.js";
4
+ import"./8bwbwy8h.js";
4
5
  import"./n442h54c.js";
5
- import"./t616ynfn.js";
6
6
  import {
7
7
  assertRoleHealth
8
- } from "./hcmdtwgh.js";
8
+ } from "./szm0cksa.js";
9
+ import"./fancm5pt.js";
9
10
  import {
10
11
  errorDoc,
12
+ assertTargetPolicy,
11
13
  selectTarget,
12
14
  openProject
13
- } from "./5nmdenb6.js";
15
+ } from "./xdpj5kkm.js";
14
16
  import {
15
17
  omitManagedObjects
16
- } from "./2ssqrv66.js";
18
+ } from "./ayz64vzj.js";
19
+ import"./8xsbagad.js";
17
20
  import {
18
21
  OkmError2
19
22
  } from "./52szzakm.js";
@@ -36,6 +39,7 @@ ${doc.fix}
36
39
  const objects = omitManagedObjects(opened.built.catalog).objects;
37
40
  if (opened.config.roles !== undefined) {
38
41
  const target = selectTarget(opened.config, flags?.target);
42
+ assertTargetPolicy(target, "check", flags?.allowProtected ?? false);
39
43
  const pool = open2({ url: target.url, max: 1 });
40
44
  try {
41
45
  await assertRoleHealth(pool, opened.config.roles, objects);
@@ -2,7 +2,7 @@ import {
2
2
  readHandle,
3
3
  readCall,
4
4
  attachHttp
5
- } from "./k34e0xf2.js";
5
+ } from "./bt9sfq0z.js";
6
6
  import {
7
7
  emitWhere,
8
8
  emitOrder,
@@ -15,7 +15,7 @@ import {
15
15
  list,
16
16
  isRecord,
17
17
  fail
18
- } from "./xrfnq5k8.js";
18
+ } from "./qgvt8nx1.js";
19
19
  import {
20
20
  OkmError2,
21
21
  throwNamed2
@@ -32,6 +32,7 @@ var OPTIONS = [
32
32
  "max",
33
33
  "orderBy",
34
34
  "limit",
35
+ "route",
35
36
  "signal",
36
37
  "timeout"
37
38
  ];
@@ -79,7 +80,8 @@ function plan(session, table, options, mods, view) {
79
80
  orderBy,
80
81
  limit: record.limit,
81
82
  signal: record.signal,
82
- timeout: record.timeout
83
+ timeout: record.timeout,
84
+ route: record.route
83
85
  }, mods.all, session.scope, session.schema.model[table], session.schema, view);
84
86
  const call = { ...base, op: "aggregate", build: statement(spec) };
85
87
  return readHandle(session, table, call, mods, (compiled, rows) => decode(compiled, rows));
@@ -0,0 +1,426 @@
1
+ import {
2
+ parseCatalog2
3
+ } from "./8bwbwy8h.js";
4
+ import"./wqem4jed.js";
5
+ import"./n442h54c.js";
6
+ import {
7
+ applyTarget2,
8
+ backfillTiming2
9
+ } from "./xhh1srw3.js";
10
+ import {
11
+ sealViews,
12
+ catalogQuery2,
13
+ managedRoleOptions2
14
+ } from "./wxn4b7pp.js";
15
+ import {
16
+ loadMigrations,
17
+ catalogsEqual,
18
+ snapshotDifference
19
+ } from "./kdkxh3kb.js";
20
+ import"./szm0cksa.js";
21
+ import {
22
+ introspectSchema
23
+ } from "./fancm5pt.js";
24
+ import {
25
+ lintMigrationDirectory,
26
+ hasError,
27
+ lintRefusal,
28
+ selectTarget,
29
+ openProject
30
+ } from "./xdpj5kkm.js";
31
+ import {
32
+ canonicalTypeName,
33
+ classOf,
34
+ strictestClass,
35
+ planMigration,
36
+ parsePlan
37
+ } from "./ayz64vzj.js";
38
+ import {
39
+ quoteIdent
40
+ } from "./8xsbagad.js";
41
+ import {
42
+ catalog,
43
+ isDomain
44
+ } from "./g7ez901n.js";
45
+ import {
46
+ identityKey2
47
+ } from "./t4tbsy9h.js";
48
+ import {
49
+ OkmError2
50
+ } from "./52szzakm.js";
51
+
52
+ // src/tooling/migrate/check.ts
53
+ import { readFileSync } from "node:fs";
54
+ import { join } from "node:path";
55
+ import postgres from "postgres";
56
+ var CHECK_SCHEMA_PREFIX = "okm_check_";
57
+ var HISTORY_CODE = "OKM1547";
58
+ var PREVIOUS_CODE = "OKM1548";
59
+ var STALE_CODE = "OKM1549";
60
+ var HISTORY_FIX = "Regenerate the migration so the stored catalog matches the schema the SQL applies.";
61
+ var FORK_FIX = "Generate the later migration from the previous migration's catalog, not from an older parent.";
62
+ var PREVIOUS_FIX = "Keep every table, column, constraint, and type the previous catalog relies on, or classify the migration as contract.";
63
+ var STALE_FIX = "Run okm generate.";
64
+ async function checkMigrations(cwd, flags, options) {
65
+ const opened = await openProject(cwd);
66
+ const target = selectTarget(opened.config, flags.target);
67
+ if (target.protected) {
68
+ throw new OkmError2("OKM1850", `okm migrate check writes a scratch schema and refuses protected target ${target.name}. Point it at a throwaway Postgres.`, {
69
+ kind: "forbidden",
70
+ fix: {
71
+ summary: "Point okm migrate check at a throwaway Postgres. --allow-protected does not apply."
72
+ }
73
+ });
74
+ }
75
+ const directory = join(cwd, opened.config.migrations ?? "migrations");
76
+ const history = readHistory(directory);
77
+ if (options?.provision === true) {
78
+ const equivalence = await proveSnapshot(opened.config, target.url, target.name, flags, history);
79
+ if (equivalence !== undefined)
80
+ throwFailure([equivalence]);
81
+ }
82
+ const previous = previousFailures(history);
83
+ const stale = staleFailure(history, opened.built.catalog);
84
+ const lint = lintFailure(directory, history, opened.config.lintFrom);
85
+ const replayed = await replayHistory(opened.config, target.url, target.name, flags, history);
86
+ const failures = [...replayed, ...previous, ...stale, ...lint];
87
+ if (failures.length > 0)
88
+ throwFailure(failures);
89
+ return `ok ${String(history.length)} migrations
90
+ `;
91
+ }
92
+ function previousCatalogGaps(before, after) {
93
+ const afterBy = new Map(after.objects.map((object) => [identityKey2(object.identity), object]));
94
+ const gaps = [];
95
+ for (const object of before.objects) {
96
+ if (!checkedKind(object.kind))
97
+ continue;
98
+ const next = afterBy.get(identityKey2(object.identity));
99
+ if (next === undefined || next.kind !== object.kind) {
100
+ gaps.push(`${objectLabel(object)} is missing`);
101
+ continue;
102
+ }
103
+ if (object.kind === "column" && next.kind === "column") {
104
+ gaps.push(...columnGaps(object, next));
105
+ }
106
+ if (object.kind === "type" && next.kind === "type" && !sameType(object.definition, next.definition)) {
107
+ gaps.push(`${objectLabel(object)} changed`);
108
+ }
109
+ }
110
+ return gaps;
111
+ }
112
+ function recomputedMigrationClass(plan, before, after) {
113
+ if (plan.steps.length === 0)
114
+ return "expand";
115
+ if (plan.steps.some((step) => step.kind === undefined)) {
116
+ return planMigration({ before, after, name: plan.name }).class;
117
+ }
118
+ return strictestClass(plan.steps.map((step) => classOf(step.kind ?? "raw-sql")));
119
+ }
120
+ function migrationAllowsPreviousGaps(plan, before, after) {
121
+ return recomputedMigrationClass(plan, before, after) === "contract" && plan.class !== "expand";
122
+ }
123
+ function previousFailures(history) {
124
+ const failures = [];
125
+ let before = catalog([]);
126
+ for (const migration of history) {
127
+ const gaps = previousCatalogGaps(before, migration.catalog);
128
+ if (gaps.length > 0 && !migrationAllowsPreviousGaps(migration.plan, before, migration.catalog)) {
129
+ const stated = migration.plan.class === "expand" ? "expand" : recomputedMigrationClass(migration.plan, before, migration.catalog);
130
+ failures.push({
131
+ code: PREVIOUS_CODE,
132
+ message: `${migration.id}: previous catalog is not satisfied (${gaps.join("; ")}). The migration is classified ${stated}.`,
133
+ fix: PREVIOUS_FIX
134
+ });
135
+ }
136
+ before = migration.catalog;
137
+ }
138
+ return failures;
139
+ }
140
+ function staleFailure(history, head) {
141
+ const last = history.at(-1)?.catalog ?? catalog([]);
142
+ if (catalogsEqual(last, head))
143
+ return [];
144
+ return [
145
+ {
146
+ code: STALE_CODE,
147
+ message: "The last migration's catalog does not match the schema.",
148
+ fix: STALE_FIX
149
+ }
150
+ ];
151
+ }
152
+ function lintFailure(directory, history, lintFrom) {
153
+ const findings = lintMigrationDirectory(directory, lintIds(history, lintFrom));
154
+ if (!hasError(findings))
155
+ return [];
156
+ const refusal = lintRefusal(findings);
157
+ return [{ code: refusal.code, message: refusal.message, fix: refusal.fix.summary }];
158
+ }
159
+ function lintIds(history, lintFrom) {
160
+ if (lintFrom === undefined)
161
+ return;
162
+ const ids = history.map((migration) => migration.id);
163
+ const start = ids.indexOf(lintFrom);
164
+ if (start < 0) {
165
+ throw new OkmError2("invalid", `lintFrom ${lintFrom} is not a migration id.`, {
166
+ fix: {
167
+ summary: "Set lintFrom to a migration id. Files before that id are the adoption baseline and are not linted."
168
+ }
169
+ });
170
+ }
171
+ return new Set(ids.slice(start));
172
+ }
173
+ async function proveSnapshot(config, url, target, flags, history) {
174
+ const head = history.at(-1);
175
+ if (head === undefined)
176
+ return;
177
+ const sql = postgres(url, {
178
+ max: 1,
179
+ connect_timeout: 5,
180
+ idle_timeout: 1,
181
+ onnotice: () => {}
182
+ });
183
+ const provided = `${CHECK_SCHEMA_PREFIX}p${crypto.randomUUID().replaceAll("-", "")}`;
184
+ const replayed = `${CHECK_SCHEMA_PREFIX}r${crypto.randomUUID().replaceAll("-", "")}`;
185
+ const timing = {
186
+ ...flags.lockTimeoutMs !== undefined ? { lockTimeoutMs: flags.lockTimeoutMs } : config.timeouts?.lock !== undefined ? { lockTimeoutMs: config.timeouts.lock } : {},
187
+ ...flags.statementTimeoutMs !== undefined ? { statementTimeoutMs: flags.statementTimeoutMs } : config.timeouts?.statement !== undefined ? { statementTimeoutMs: config.timeouts.statement } : {},
188
+ ...backfillTiming2(config.backfill)
189
+ };
190
+ try {
191
+ await sql.unsafe(`create schema ${quoteIdent(provided)}`);
192
+ await sql.unsafe(`create schema ${quoteIdent(replayed)}`);
193
+ const role = config.roles === undefined ? {} : { migrationRole: config.roles.migration, schema: provided };
194
+ await applyTarget2({
195
+ url,
196
+ target,
197
+ protected: false,
198
+ allowProtected: false,
199
+ allowPooler: flags.allowPooler || config.allowPooler === true,
200
+ searchPath: provided,
201
+ ...timing,
202
+ migrations: history.map((migration) => ({
203
+ id: migration.id,
204
+ catalogHash: migration.catalogHash,
205
+ steps: migration.steps
206
+ })),
207
+ snapshot: {
208
+ catalog: head.catalog,
209
+ migrationId: head.id,
210
+ catalogHash: head.catalogHash,
211
+ reference: [],
212
+ only: true
213
+ },
214
+ ...role
215
+ });
216
+ await applyTarget2({
217
+ url,
218
+ target,
219
+ protected: false,
220
+ allowProtected: false,
221
+ allowPooler: flags.allowPooler || config.allowPooler === true,
222
+ searchPath: replayed,
223
+ ...timing,
224
+ migrations: history.map((migration) => ({
225
+ id: migration.id,
226
+ catalogHash: migration.catalogHash,
227
+ steps: retargetSteps(migration.steps, replayed)
228
+ })),
229
+ ...config.roles !== undefined ? { migrationRole: config.roles.migration, schema: replayed } : {}
230
+ });
231
+ const liveProvided = await introspectScratch(sql, provided, config);
232
+ const liveReplayed = await introspectScratch(sql, replayed, config);
233
+ const difference = snapshotDifference(liveProvided, liveReplayed);
234
+ if (difference === undefined)
235
+ return;
236
+ return {
237
+ code: "OKM1521",
238
+ message: `Provisioning from the snapshot does not match the replayed history.
239
+ ${difference}`,
240
+ fix: "Regenerate the snapshot from the history. okm migrate check --provision is the command that reports this."
241
+ };
242
+ } finally {
243
+ await sql.unsafe(`drop schema if exists ${quoteIdent(provided)} cascade`).catch(() => {
244
+ return;
245
+ });
246
+ await sql.unsafe(`drop schema if exists ${quoteIdent(replayed)} cascade`).catch(() => {
247
+ return;
248
+ });
249
+ await sql.end({ timeout: 5 });
250
+ }
251
+ }
252
+ async function replayHistory(config, url, target, flags, history) {
253
+ if (history.length === 0)
254
+ return [];
255
+ const sql = postgres(url, {
256
+ max: 1,
257
+ connect_timeout: 5,
258
+ idle_timeout: 1,
259
+ onnotice: () => {}
260
+ });
261
+ const scratch = `${CHECK_SCHEMA_PREFIX}${crypto.randomUUID().replaceAll("-", "")}`;
262
+ const failures = [];
263
+ try {
264
+ await sql.unsafe(`create schema ${quoteIdent(scratch)}`);
265
+ const runner = catalogQuery2(sql);
266
+ for (let index = 0;index < history.length; index += 1) {
267
+ const migration = history[index];
268
+ if (migration === undefined)
269
+ continue;
270
+ await applyTarget2({
271
+ url,
272
+ target,
273
+ protected: false,
274
+ allowProtected: false,
275
+ allowPooler: flags.allowPooler || config.allowPooler === true,
276
+ searchPath: scratch,
277
+ ...flags.lockTimeoutMs !== undefined ? { lockTimeoutMs: flags.lockTimeoutMs } : config.timeouts?.lock !== undefined ? { lockTimeoutMs: config.timeouts.lock } : {},
278
+ ...flags.statementTimeoutMs !== undefined ? { statementTimeoutMs: flags.statementTimeoutMs } : config.timeouts?.statement !== undefined ? { statementTimeoutMs: config.timeouts.statement } : {},
279
+ ...backfillTiming2(config.backfill),
280
+ migrations: [
281
+ {
282
+ id: migration.id,
283
+ catalogHash: migration.catalogHash,
284
+ steps: retargetSteps(migration.steps, scratch)
285
+ }
286
+ ],
287
+ ...config.roles !== undefined ? { migrationRole: config.roles.migration, schema: scratch } : {}
288
+ });
289
+ const live = await introspectScratch(sql, scratch, config);
290
+ const sealed = await sealViews(runner, migration.catalog);
291
+ const back = planMigration({ before: live, after: sealed, name: migration.id });
292
+ if (back.steps.length === 0)
293
+ continue;
294
+ failures.push(historyFailure(history, index, back));
295
+ break;
296
+ }
297
+ } finally {
298
+ await sql.unsafe(`drop schema if exists ${quoteIdent(scratch)} cascade`).catch(() => {
299
+ return;
300
+ });
301
+ await sql.end({ timeout: 5 });
302
+ }
303
+ return failures;
304
+ }
305
+ async function introspectScratch(sql, schema, config) {
306
+ await sql.unsafe(`set search_path to ${quoteIdent(schema)}`);
307
+ return introspectSchema(catalogQuery2(sql), schema, "public", managedRoleOptions2(config.roles));
308
+ }
309
+ function historyFailure(history, index, back) {
310
+ const migration = history[index];
311
+ const id = migration?.id ?? "migration";
312
+ const fork = migration === undefined ? undefined : forkMessage(history, index);
313
+ if (fork !== undefined)
314
+ return { code: HISTORY_CODE, message: fork, fix: FORK_FIX };
315
+ const steps = back.steps.map((step) => step.sql).join(`
316
+ `);
317
+ return {
318
+ code: HISTORY_CODE,
319
+ message: `${id}: history differs from the stored catalog
320
+ ${steps}`,
321
+ fix: HISTORY_FIX
322
+ };
323
+ }
324
+ function forkMessage(history, index) {
325
+ const current = history[index];
326
+ const previous = history[index - 1];
327
+ if (current === undefined || previous === undefined)
328
+ return;
329
+ if (samePlan(previous.catalog, current.catalog, current.plan))
330
+ return;
331
+ for (let parentIndex = 0;parentIndex < index; parentIndex += 1) {
332
+ const parent = parentIndex === 0 ? catalog([]) : history[parentIndex - 1]?.catalog;
333
+ if (parent === undefined)
334
+ continue;
335
+ if (!samePlan(parent, current.catalog, current.plan))
336
+ continue;
337
+ return `migration ${current.id} was generated from a different parent than ${previous.id}`;
338
+ }
339
+ return;
340
+ }
341
+ function samePlan(before, after, file) {
342
+ try {
343
+ return sameSql(planMigration({ before, after, name: "parent" }), file);
344
+ } catch (error) {
345
+ if (error instanceof OkmError2 && error.code === "OKM1530")
346
+ return false;
347
+ throw error;
348
+ }
349
+ }
350
+ function sameSql(planned, file) {
351
+ if (planned.steps.length !== file.steps.length)
352
+ return false;
353
+ return planned.steps.every((step, index) => step.sql === file.steps[index]?.sql);
354
+ }
355
+ function retargetSteps(steps, schema) {
356
+ const from = `${quoteIdent("public")}.`;
357
+ const to = `${quoteIdent(schema)}.`;
358
+ const fromDefaults = `in schema ${quoteIdent("public")} `;
359
+ const toDefaults = `in schema ${quoteIdent(schema)} `;
360
+ return steps.map((step) => ({
361
+ ...step,
362
+ sql: step.sql.replaceAll(from, to).replaceAll(fromDefaults, toDefaults),
363
+ ...step.backfill === undefined ? {} : { backfill: { ...step.backfill, table: step.backfill.table.replaceAll(from, to) } }
364
+ }));
365
+ }
366
+ function readHistory(directory) {
367
+ return loadMigrations(directory).map((migration) => ({
368
+ id: migration.id,
369
+ catalogHash: migration.catalogHash,
370
+ steps: migration.steps,
371
+ plan: parsePlan(readFileSync(join(directory, `${migration.id}.sql`), "utf8")),
372
+ catalog: parseCatalog2(readFileSync(join(directory, `${migration.id}.catalog.json`), "utf8"))
373
+ }));
374
+ }
375
+ function throwFailure(failures) {
376
+ const first = failures[0];
377
+ if (first === undefined)
378
+ throw new Error("okm migrate check has no failure to report.");
379
+ const message = failures.length === 1 ? first.message : failures.map((failure) => `${failure.code} ${failure.message}`).join(`
380
+ `);
381
+ throw new OkmError2(first.code, message, { fix: { summary: first.fix } });
382
+ }
383
+ function checkedKind(kind) {
384
+ return kind === "table" || kind === "column" || kind === "constraint" || kind === "type";
385
+ }
386
+ function columnGaps(before, after) {
387
+ const gaps = [];
388
+ const place = objectLabel(before);
389
+ if (canonicalTypeName(before.definition.dataType) !== canonicalTypeName(after.definition.dataType)) {
390
+ gaps.push(`${place} changed type from ${before.definition.dataType} to ${after.definition.dataType}`);
391
+ }
392
+ if (before.definition.nullable && !after.definition.nullable && !columnHasDefault(after)) {
393
+ gaps.push(`${place} nullability tightened without a default`);
394
+ }
395
+ return gaps;
396
+ }
397
+ function columnHasDefault(column) {
398
+ return column.definition.defaultExpression !== undefined || column.definition.identity !== undefined || column.definition.generated !== undefined;
399
+ }
400
+ function sameType(before, after) {
401
+ const beforeDomain = isDomain(before);
402
+ const afterDomain = isDomain(after);
403
+ if (beforeDomain !== afterDomain)
404
+ return false;
405
+ if (beforeDomain && afterDomain) {
406
+ return canonicalTypeName(before.base) === canonicalTypeName(after.base) && before.check === after.check;
407
+ }
408
+ if (!beforeDomain && !afterDomain) {
409
+ if (before.labels.length !== after.labels.length)
410
+ return false;
411
+ return before.labels.every((label, index) => label === after.labels[index]);
412
+ }
413
+ return false;
414
+ }
415
+ function objectLabel(object) {
416
+ if (object.kind === "column" || object.kind === "constraint") {
417
+ return `${object.kind} ${object.identity.parent.name}.${object.identity.name}`;
418
+ }
419
+ if (object.kind === "table" || object.kind === "type") {
420
+ return `${object.kind} ${object.identity.name}`;
421
+ }
422
+ return object.kind;
423
+ }
424
+ export {
425
+ checkMigrations
426
+ };
@@ -0,0 +1,21 @@
1
+ import {
2
+ applyTarget2,
3
+ applyProject2,
4
+ pushProject2,
5
+ backfillTiming2
6
+ } from "./xhh1srw3.js";
7
+ import"./kdkxh3kb.js";
8
+ import"./szm0cksa.js";
9
+ import"./fancm5pt.js";
10
+ import"./xdpj5kkm.js";
11
+ import"./ayz64vzj.js";
12
+ import"./n442h54c.js";
13
+ import"./8xsbagad.js";
14
+ import"./8bwbwy8h.js";
15
+ import"./wqem4jed.js";
16
+ export {
17
+ applyProject2 as applyProject,
18
+ applyTarget2 as applyTarget,
19
+ backfillTiming2 as backfillTiming,
20
+ pushProject2 as pushProject
21
+ };
@@ -2,7 +2,7 @@ import {
2
2
  noticeBuffer,
3
3
  WireSession,
4
4
  cursorStream
5
- } from "./0jc3ffzf.js";
5
+ } from "./zvbjc949.js";
6
6
  import {
7
7
  BUNSQL_CAPABILITIES,
8
8
  driverErrors,
@@ -41,7 +41,7 @@ function open5(config) {
41
41
  counters.busy = Math.max(0, counters.busy - 1);
42
42
  });
43
43
  const held = { sql, parked, acquireMs, counters, notices, isClosed };
44
- const checkout = () => closed ? rejectClosed() : import("./fs0ex00p.js").then((mod) => mod.checkout(held));
44
+ const checkout = () => closed ? rejectClosed() : import("./4ef7mmqf.js").then((mod) => mod.checkout(held));
45
45
  return {
46
46
  capabilities: BUNSQL_CAPABILITIES,
47
47
  execute(text, params, options) {
@@ -1,3 +1,6 @@
1
+ import {
2
+ FUNCTION_ARG_TYPES
3
+ } from "./fancm5pt.js";
1
4
  import {
2
5
  OkmError2
3
6
  } from "./52szzakm.js";
@@ -92,7 +95,7 @@ async function functionExists(runner, schema, name, args) {
92
95
  const result = await runner.execute(`select 1 from pg_proc p
93
96
  join pg_namespace n on n.oid = p.pronamespace
94
97
  where n.nspname = $1 and p.proname = $2
95
- and replace(pg_get_function_identity_arguments(p.oid), ' ', '') = $3`, [schema, name, args]);
98
+ and ${FUNCTION_ARG_TYPES} = $3`, [schema, name, args]);
96
99
  return result.rows.length > 0;
97
100
  }
98
101
  function truthy(value) {
@@ -11,7 +11,7 @@ import {
11
11
  quote,
12
12
  isRecord,
13
13
  fail
14
- } from "./xrfnq5k8.js";
14
+ } from "./qgvt8nx1.js";
15
15
  import {
16
16
  isOperator,
17
17
  operatorName,
@@ -117,7 +117,7 @@ function open2(config) {
117
117
  },
118
118
  async batch(statements, options) {
119
119
  return sockets.occupy(async () => {
120
- const { checkout } = await import("./999ade2q.js");
120
+ const { checkout } = await import("./5vdm4sgp.js");
121
121
  const connection = await checkout(held);
122
122
  try {
123
123
  return await connection.batch(statements, options);
@@ -129,12 +129,12 @@ function open2(config) {
129
129
  reserve() {
130
130
  const gate = sockets.busyGate();
131
131
  gate.enter();
132
- return import("./999ade2q.js").then((mod) => mod.holdCheckout(gate, () => mod.checkout(held)), (error) => {
132
+ return import("./5vdm4sgp.js").then((mod) => mod.holdCheckout(gate, () => mod.checkout(held)), (error) => {
133
133
  gate.leave();
134
134
  throw error;
135
135
  });
136
136
  },
137
- describe: (text, params) => sockets.occupy(() => import("./999ade2q.js").then((mod) => mod.describe(sql, text, params))),
137
+ describe: (text, params) => sockets.occupy(() => import("./5vdm4sgp.js").then((mod) => mod.describe(sql, text, params))),
138
138
  stream(text, params) {
139
139
  const gate = sockets.busyGate();
140
140
  return async function* () {
@@ -149,7 +149,7 @@ function open2(config) {
149
149
  listen(channel, onNotify) {
150
150
  const gate = sockets.listenerGate();
151
151
  gate.enter();
152
- return import("./999ade2q.js").then((mod) => mod.holdListener(gate, async () => {
152
+ return import("./5vdm4sgp.js").then((mod) => mod.holdListener(gate, async () => {
153
153
  const listening = await sql.listen(channel, onNotify);
154
154
  return async () => {
155
155
  await listening.unlisten();
@@ -219,7 +219,7 @@ class PgSession {
219
219
  onAbort();
220
220
  else
221
221
  watch.signal.addEventListener("abort", onAbort, { once: true });
222
- return import("./999ade2q.js").then((mod) => mod.watchQuery(pending, watch, onAbort, {
222
+ return import("./5vdm4sgp.js").then((mod) => mod.watchQuery(pending, watch, onAbort, {
223
223
  start,
224
224
  text,
225
225
  notices: this.notices,
@@ -271,7 +271,7 @@ class PgSession {
271
271
  }
272
272
  }
273
273
  async function* loadStream(sql, text, params) {
274
- const { stream } = await import("./999ade2q.js");
274
+ const { stream } = await import("./5vdm4sgp.js");
275
275
  yield* stream(sql, text, params);
276
276
  }
277
277
  function send(sql, text, params, named) {