okmodel 0.4.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 (64) hide show
  1. package/README.md +86 -6
  2. package/dist/contracts/catalog/document.d.ts +20 -0
  3. package/dist/contracts/driver.d.ts +7 -0
  4. package/dist/dialects/pg/introspect.d.ts +6 -0
  5. package/dist/dialects/pg/role/sql.d.ts +10 -0
  6. package/dist/dialects/pg/schema.d.ts +15 -6
  7. package/dist/dialects/pg/view/index.d.ts +20 -6
  8. package/dist/dialects/pg/view/scratch.d.ts +2 -2
  9. package/dist/runtime/pg/bun.d.ts +12 -7
  10. package/dist/runtime/pg/bun.js +4 -1
  11. package/dist/runtime/pg/pg.d.ts +12 -6
  12. package/dist/runtime/pg/pg.js +4 -1
  13. package/dist/runtime/pg/pglite.d.ts +11 -3
  14. package/dist/runtime/pg/pglite.js +4 -1
  15. package/dist/runtime/pg/postgresjs.d.ts +12 -6
  16. package/dist/runtime/pg/postgresjs.js +4 -1
  17. package/dist/runtime/plan.d.ts +3 -1
  18. package/dist/runtime/tenancy/index.js +1 -1
  19. package/dist/runtime/topology.d.ts +191 -0
  20. package/dist/runtime/traits/index.js +6 -6
  21. package/dist/runtime/types.d.ts +57 -2
  22. package/dist/runtime/validate/index.js +1 -1
  23. package/dist/shared/{nxb42q7h.js → 47xzb9fc.js} +2 -2
  24. package/dist/shared/{6693j9rs.js → 5fqx9yh4.js} +6 -2
  25. package/dist/shared/{8jb4ymh2.js → 8bwbwy8h.js} +12 -1
  26. package/dist/shared/{41kv2xdm.js → 8xsbagad.js} +1 -1
  27. package/dist/shared/{sr5ms7b3.js → 922qbgnw.js} +3 -3
  28. package/dist/shared/{tny2t3sm.js → 990a291j.js} +4 -4
  29. package/dist/shared/{jns2wx4t.js → 9tgmr1gh.js} +1 -1
  30. package/dist/shared/{n5y9zyj1.js → ayz64vzj.js} +13 -12
  31. package/dist/shared/{kkzwp3d2.js → bt9sfq0z.js} +16 -14
  32. package/dist/shared/{c5g81ngb.js → bzsjtd7c.js} +7 -5
  33. package/dist/shared/{radd3z4g.js → ckjbyrt9.js} +1 -1
  34. package/dist/shared/{zg24x0hm.js → dxjp14rk.js} +10 -2
  35. package/dist/shared/{xghkt7a9.js → fancm5pt.js} +6 -233
  36. package/dist/shared/{yjw02enp.js → fyqkk1m0.js} +4 -4
  37. package/dist/shared/{ygem90rn.js → g516zdsb.js} +8 -3
  38. package/dist/shared/{v7tpnhnz.js → h4nqvjkp.js} +5 -3
  39. package/dist/shared/{edwqqryd.js → h8ne16fq.js} +1 -1
  40. package/dist/shared/{ady1ms74.js → jdgqqx31.js} +5 -5
  41. package/dist/shared/{kafaa5zv.js → k7cc9nzk.js} +24 -7
  42. package/dist/shared/{7nk68fj8.js → kdkxh3kb.js} +17 -10
  43. package/dist/shared/nkc9sfqb.js +1161 -0
  44. package/dist/shared/{awrsqrn7.js → p7pfx624.js} +2 -2
  45. package/dist/shared/{xrfnq5k8.js → qgvt8nx1.js} +11 -2
  46. package/dist/shared/{84ptn35q.js → qy9rdq0g.js} +6 -5
  47. package/dist/shared/{qn0srwwq.js → r6pj4fq2.js} +5 -3
  48. package/dist/shared/{2321avmn.js → r73pt594.js} +21 -13
  49. package/dist/shared/{kj680nxv.js → rasvvzgz.js} +8 -6
  50. package/dist/shared/{hcmdtwgh.js → szm0cksa.js} +4 -1
  51. package/dist/shared/{axhk06q1.js → tq9afp8d.js} +1 -1
  52. package/dist/shared/wxn4b7pp.js +252 -0
  53. package/dist/shared/{q6zsyh31.js → xdpj5kkm.js} +19 -8
  54. package/dist/shared/{s4mb6a4b.js → xhh1srw3.js} +54 -10
  55. package/dist/shared/{jgxzjzcz.js → xsq7v6wp.js} +2 -2
  56. package/dist/shared/{hqjsxnz7.js → ykv7wh5b.js} +5 -4
  57. package/dist/shared/{q795afwa.js → z2rnngj4.js} +3 -3
  58. package/dist/tooling/cli.js +12 -12
  59. package/dist/tooling/migrate/apply.d.ts +2 -0
  60. package/dist/tooling/migrate/files.d.ts +7 -0
  61. package/dist/tooling/migrate/index.js +8 -6
  62. package/dist/tooling/testing/facts.d.ts +3 -0
  63. package/dist/tooling/testing/index.js +3 -3
  64. package/package.json +1 -1
@@ -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,21 +1,22 @@
1
1
  import {
2
2
  open2
3
3
  } from "./wqem4jed.js";
4
+ import"./8bwbwy8h.js";
4
5
  import"./n442h54c.js";
5
- import"./8jb4ymh2.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,
11
12
  assertTargetPolicy,
12
13
  selectTarget,
13
14
  openProject
14
- } from "./q6zsyh31.js";
15
+ } from "./xdpj5kkm.js";
15
16
  import {
16
17
  omitManagedObjects
17
- } from "./n5y9zyj1.js";
18
- import"./41kv2xdm.js";
18
+ } from "./ayz64vzj.js";
19
+ import"./8xsbagad.js";
19
20
  import {
20
21
  OkmError2
21
22
  } from "./52szzakm.js";
@@ -2,7 +2,7 @@ import {
2
2
  readHandle,
3
3
  readCall,
4
4
  attachHttp
5
- } from "./kkzwp3d2.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));
@@ -1,40 +1,43 @@
1
1
  import {
2
2
  parseCatalog2
3
- } from "./8jb4ymh2.js";
3
+ } from "./8bwbwy8h.js";
4
4
  import"./wqem4jed.js";
5
5
  import"./n442h54c.js";
6
6
  import {
7
7
  applyTarget2,
8
8
  backfillTiming2
9
- } from "./s4mb6a4b.js";
9
+ } from "./xhh1srw3.js";
10
10
  import {
11
- introspectSchema,
12
11
  sealViews,
13
12
  catalogQuery2,
14
13
  managedRoleOptions2
15
- } from "./xghkt7a9.js";
14
+ } from "./wxn4b7pp.js";
16
15
  import {
17
16
  loadMigrations,
18
17
  catalogsEqual,
19
18
  snapshotDifference
20
- } from "./7nk68fj8.js";
19
+ } from "./kdkxh3kb.js";
20
+ import"./szm0cksa.js";
21
+ import {
22
+ introspectSchema
23
+ } from "./fancm5pt.js";
21
24
  import {
22
25
  lintMigrationDirectory,
23
26
  hasError,
24
27
  lintRefusal,
25
28
  selectTarget,
26
29
  openProject
27
- } from "./q6zsyh31.js";
30
+ } from "./xdpj5kkm.js";
28
31
  import {
29
32
  canonicalTypeName,
30
33
  classOf,
31
34
  strictestClass,
32
35
  planMigration,
33
36
  parsePlan
34
- } from "./n5y9zyj1.js";
37
+ } from "./ayz64vzj.js";
35
38
  import {
36
39
  quoteIdent
37
- } from "./41kv2xdm.js";
40
+ } from "./8xsbagad.js";
38
41
  import {
39
42
  catalog,
40
43
  isDomain
@@ -225,9 +228,8 @@ async function proveSnapshot(config, url, target, flags, history) {
225
228
  })),
226
229
  ...config.roles !== undefined ? { migrationRole: config.roles.migration, schema: replayed } : {}
227
230
  });
228
- const runner = catalogQuery2(sql);
229
- const liveProvided = await introspectSchema(runner, provided, "public", managedRoleOptions2(config.roles));
230
- const liveReplayed = await introspectSchema(runner, replayed, "public", managedRoleOptions2(config.roles));
231
+ const liveProvided = await introspectScratch(sql, provided, config);
232
+ const liveReplayed = await introspectScratch(sql, replayed, config);
231
233
  const difference = snapshotDifference(liveProvided, liveReplayed);
232
234
  if (difference === undefined)
233
235
  return;
@@ -284,7 +286,7 @@ async function replayHistory(config, url, target, flags, history) {
284
286
  ],
285
287
  ...config.roles !== undefined ? { migrationRole: config.roles.migration, schema: scratch } : {}
286
288
  });
287
- const live = await introspectSchema(runner, scratch, "public", managedRoleOptions2(config.roles));
289
+ const live = await introspectScratch(sql, scratch, config);
288
290
  const sealed = await sealViews(runner, migration.catalog);
289
291
  const back = planMigration({ before: live, after: sealed, name: migration.id });
290
292
  if (back.steps.length === 0)
@@ -300,6 +302,10 @@ async function replayHistory(config, url, target, flags, history) {
300
302
  }
301
303
  return failures;
302
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
+ }
303
309
  function historyFailure(history, index, back) {
304
310
  const migration = history[index];
305
311
  const id = migration?.id ?? "migration";
@@ -349,9 +355,11 @@ function sameSql(planned, file) {
349
355
  function retargetSteps(steps, schema) {
350
356
  const from = `${quoteIdent("public")}.`;
351
357
  const to = `${quoteIdent(schema)}.`;
358
+ const fromDefaults = `in schema ${quoteIdent("public")} `;
359
+ const toDefaults = `in schema ${quoteIdent(schema)} `;
352
360
  return steps.map((step) => ({
353
361
  ...step,
354
- sql: step.sql.replaceAll(from, to),
362
+ sql: step.sql.replaceAll(from, to).replaceAll(fromDefaults, toDefaults),
355
363
  ...step.backfill === undefined ? {} : { backfill: { ...step.backfill, table: step.backfill.table.replaceAll(from, to) } }
356
364
  }));
357
365
  }
@@ -3,13 +3,15 @@ import {
3
3
  applyProject2,
4
4
  pushProject2,
5
5
  backfillTiming2
6
- } from "./s4mb6a4b.js";
7
- import"./7nk68fj8.js";
8
- import"./q6zsyh31.js";
9
- import"./n5y9zyj1.js";
6
+ } from "./xhh1srw3.js";
7
+ import"./kdkxh3kb.js";
8
+ import"./szm0cksa.js";
9
+ import"./fancm5pt.js";
10
+ import"./xdpj5kkm.js";
11
+ import"./ayz64vzj.js";
10
12
  import"./n442h54c.js";
11
- import"./41kv2xdm.js";
12
- import"./8jb4ymh2.js";
13
+ import"./8xsbagad.js";
14
+ import"./8bwbwy8h.js";
13
15
  import"./wqem4jed.js";
14
16
  export {
15
17
  applyProject2 as applyProject,
@@ -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,
@@ -0,0 +1,252 @@
1
+ import {
2
+ cellText,
3
+ VIEW_DEPENDENCIES,
4
+ viewDependencyEdges,
5
+ PUBLIC,
6
+ introspectSchema
7
+ } from "./fancm5pt.js";
8
+ import {
9
+ planMigration
10
+ } from "./ayz64vzj.js";
11
+ import {
12
+ quoteIdent,
13
+ createObjectSql
14
+ } from "./8xsbagad.js";
15
+ import {
16
+ normaliseViewQuery,
17
+ viewObject,
18
+ materializedViewObject
19
+ } from "./y005b761.js";
20
+ import {
21
+ catalog
22
+ } from "./g7ez901n.js";
23
+ import {
24
+ OkmError2
25
+ } from "./52szzakm.js";
26
+
27
+ // src/tooling/migrate/drift.ts
28
+ import postgres from "postgres";
29
+
30
+ // src/dialects/pg/view/scratch.ts
31
+ var SCRATCH = "okm_view_seal";
32
+ var VIEW_SHAPE = `
33
+ select c.relname as name, c.relkind as kind, pg_get_viewdef(c.oid, true) as query,
34
+ (
35
+ select coalesce(string_agg(
36
+ a.attname || '|' || format_type(a.atttypid, a.atttypmod),
37
+ E'\\n' order by a.attnum
38
+ ), '')
39
+ from pg_attribute a
40
+ where a.attrelid = c.oid and a.attnum > 0 and not a.attisdropped
41
+ ) as columns
42
+ from pg_class c
43
+ join pg_namespace n on n.oid = c.relnamespace
44
+ where n.nspname = $1 and c.relkind in ('v', 'm')
45
+ `;
46
+ async function sealViews(runner, source) {
47
+ const views = source.objects.filter((object) => object.kind === "view" || object.kind === "materializedView");
48
+ if (views.length === 0)
49
+ return source;
50
+ await dropScratch(runner);
51
+ await runner.query(`create schema ${quoteIdent(SCRATCH)}`);
52
+ try {
53
+ await runner.query(`set search_path to ${quoteIdent(SCRATCH)}`);
54
+ await createObjects(runner, source, "type");
55
+ for (const object of source.objects) {
56
+ if (object.kind !== "table")
57
+ continue;
58
+ await runner.query(stubTable(object, source));
59
+ }
60
+ await createObjects(runner, source, "function");
61
+ await createViews(runner, views);
62
+ const shapes = await runner.query(VIEW_SHAPE, [SCRATCH]);
63
+ const deps = await runner.query(VIEW_DEPENDENCIES, [SCRATCH]);
64
+ const edges = viewDependencyEdges(deps, PUBLIC, source.objects);
65
+ const byName = new Map(shapes.map((row) => [cellText(row.name), row]));
66
+ const next = source.objects.map((object) => {
67
+ if (object.kind !== "view" && object.kind !== "materializedView")
68
+ return object;
69
+ const row = byName.get(object.identity.name);
70
+ if (row === undefined) {
71
+ throw new OkmError2("OKM1020", `View ${object.identity.name} was not created in the scratch schema.`, {
72
+ fix: { summary: "The query has to run against the tables in this schema." }
73
+ });
74
+ }
75
+ const columns = readColumns(object, cellText(row.columns));
76
+ const query = normaliseViewQuery(cellText(row.query));
77
+ const dependencies = edges.get(object.identity.name) ?? [];
78
+ if (object.kind === "view") {
79
+ return viewObject({
80
+ namespace: object.identity.namespace,
81
+ name: object.identity.name,
82
+ columns,
83
+ query,
84
+ owner: object.owner,
85
+ provenance: object.provenance,
86
+ dependencies
87
+ });
88
+ }
89
+ return materializedViewObject({
90
+ namespace: object.identity.namespace,
91
+ name: object.identity.name,
92
+ columns,
93
+ query,
94
+ owner: object.owner,
95
+ provenance: object.provenance,
96
+ dependencies,
97
+ ...object.definition.refresh !== undefined ? { refresh: object.definition.refresh } : {}
98
+ });
99
+ });
100
+ return catalog(next);
101
+ } finally {
102
+ await runner.query("set search_path to public");
103
+ await dropScratch(runner);
104
+ }
105
+ }
106
+ async function createObjects(runner, source, kind) {
107
+ for (const object of source.objects) {
108
+ if (object.kind !== kind)
109
+ continue;
110
+ const statement = createObjectSql(object, SCRATCH);
111
+ if (statement !== undefined)
112
+ await runner.query(statement);
113
+ }
114
+ }
115
+ async function createViews(runner, views) {
116
+ const pending = [...views];
117
+ let last = "";
118
+ for (let pass = 0;pass < views.length && pending.length > 0; pass += 1) {
119
+ const failed = [];
120
+ let created = 0;
121
+ for (const object of pending) {
122
+ const sql = object.kind === "materializedView" ? `create materialized view ${quoteIdent(object.identity.name)} as ${object.definition.query} with no data` : `create view ${quoteIdent(object.identity.name)} as ${object.definition.query}`;
123
+ try {
124
+ await runner.query(sql);
125
+ created += 1;
126
+ } catch (error) {
127
+ last = error instanceof Error ? error.message : String(error);
128
+ failed.push(object);
129
+ }
130
+ }
131
+ if (created === 0) {
132
+ throw new OkmError2("OKM1020", `View ${failed[0]?.identity.name ?? ""} could not be created. ${last}`, {
133
+ fix: { summary: "The query has to run against the tables in this schema." }
134
+ });
135
+ }
136
+ pending.length = 0;
137
+ pending.push(...failed);
138
+ }
139
+ }
140
+ function stubTable(object, source) {
141
+ const columns = source.objects.filter((item) => item.kind === "column" && item.identity.parent.name === object.identity.name);
142
+ const lines = columns.map((column) => `${quoteIdent(column.identity.name)} ${column.definition.dataType}`);
143
+ return `create table ${quoteIdent(object.identity.name)} (${lines.join(", ")})`;
144
+ }
145
+ function readColumns(object, printed) {
146
+ const live = printed.length === 0 ? [] : printed.split(`
147
+ `);
148
+ const declared = object.definition.columns;
149
+ if (live.length !== declared.length) {
150
+ throw mismatch(object.identity.name, declared.map((column) => column.name).join(", "), printed);
151
+ }
152
+ return declared.map((column, index) => {
153
+ const line = live[index] ?? "";
154
+ const splitAt = line.indexOf("|");
155
+ const name = splitAt < 0 ? line : line.slice(0, splitAt);
156
+ const type = splitAt < 0 ? "" : line.slice(splitAt + 1);
157
+ if (name !== column.name || type !== column.dataType) {
158
+ throw mismatch(object.identity.name, `${column.name}:${column.dataType}`, live[index] ?? "");
159
+ }
160
+ return column;
161
+ });
162
+ }
163
+ function mismatch(view, declared, live) {
164
+ return new OkmError2("OKM1020", `View ${view} declares ${declared} and the database has ${live}.`, { fix: { summary: "Declare the columns the query returns, in that order, with those types." } });
165
+ }
166
+ async function dropScratch(runner) {
167
+ const present = await runner.query(`select 1 as ok from pg_namespace where nspname = $1`, [
168
+ SCRATCH
169
+ ]);
170
+ if (present.length === 0)
171
+ return;
172
+ const rows = await runner.query(`
173
+ select c.relname as name, c.relkind as kind
174
+ from pg_class c
175
+ join pg_namespace n on n.oid = c.relnamespace
176
+ where n.nspname = $1 and c.relkind in ('v', 'm', 'r')
177
+ `, [SCRATCH]);
178
+ const views = rows.filter((row) => cellText(row.kind) === "v" || cellText(row.kind) === "m");
179
+ for (let pass = 0;pass < Math.max(views.length, 1); pass += 1) {
180
+ for (const row of views) {
181
+ const kind = cellText(row.kind) === "m" ? "materialized view" : "view";
182
+ try {
183
+ await runner.query(`drop ${kind} if exists ${quoteIdent(SCRATCH)}.${quoteIdent(cellText(row.name))}`);
184
+ } catch {}
185
+ }
186
+ }
187
+ for (const row of rows) {
188
+ if (cellText(row.kind) !== "r")
189
+ continue;
190
+ await runner.query(`drop table if exists ${quoteIdent(SCRATCH)}.${quoteIdent(cellText(row.name))}`);
191
+ }
192
+ await runner.query(`drop schema if exists ${quoteIdent(SCRATCH)} cascade`);
193
+ }
194
+
195
+ // src/tooling/migrate/drift.ts
196
+ async function assertAuthorDrift2(url, author, roles) {
197
+ const sql = postgres(url, {
198
+ max: 1,
199
+ connect_timeout: 5,
200
+ idle_timeout: 1,
201
+ onnotice: () => {}
202
+ });
203
+ try {
204
+ const present = await sql`
205
+ select to_regclass('public.okm_meta')::text as reg
206
+ `;
207
+ if (present[0]?.reg == null)
208
+ return;
209
+ const runner = catalogQuery2(sql);
210
+ const authorOnServer = await sealViews(runner, author);
211
+ const live = await introspectSchema(runner, "public", "public", managedRoleOptions2(roles));
212
+ const plan = planMigration({ before: live, after: authorOnServer, name: "check" });
213
+ if (plan.steps.length === 0)
214
+ return;
215
+ throw new OkmError2("OKM1520", `The database differs from the schema.
216
+ ${plan.steps.map((step) => step.sql).join(`
217
+ `)}`, {
218
+ fix: {
219
+ summary: "Change the schema or the database so okm check reports no difference."
220
+ }
221
+ });
222
+ } finally {
223
+ await sql.end({ timeout: 5 });
224
+ }
225
+ }
226
+ function catalogQuery2(sql) {
227
+ return {
228
+ async query(text, params) {
229
+ const rows = await sql.unsafe(text, params === undefined ? undefined : [...params]);
230
+ return rows.map((row) => {
231
+ const copy = {};
232
+ for (const [key, value] of Object.entries(row))
233
+ copy[key] = value;
234
+ return copy;
235
+ });
236
+ }
237
+ };
238
+ }
239
+ function managedRoleOptions2(roles) {
240
+ const names = managedRoleNames(roles);
241
+ return names === undefined ? undefined : { managedRoles: names };
242
+ }
243
+ function managedRoleNames(roles) {
244
+ if (roles === undefined)
245
+ return;
246
+ const names = new Set([roles.migration, roles.app]);
247
+ for (const role of roles.managed ?? [])
248
+ names.add(role.name);
249
+ return [...names];
250
+ }
251
+
252
+ export { sealViews, assertAuthorDrift2, catalogQuery2, managedRoleOptions2 };
@@ -8,8 +8,9 @@ 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
16
  } from "./wqem4jed.js";
@@ -22,7 +23,7 @@ import {
22
23
  formatPlan,
23
24
  parsePlan,
24
25
  staleRenames
25
- } from "./n5y9zyj1.js";
26
+ } from "./ayz64vzj.js";
26
27
  import {
27
28
  contributedFields
28
29
  } from "./n0dgs0pb.js";
@@ -623,7 +624,7 @@ var ERROR_DOCS = [
623
624
  {
624
625
  code: "OKM1840",
625
626
  title: "Replica not allowed",
626
- summary: ".replica() was used on an operation that requires the primary, or inside tx().",
627
+ summary: "A replica route was used on an operation that requires the primary, or inside tx().",
627
628
  fix: "Run the operation on the primary. Writes, batch, locks, and tx() never go to a replica."
628
629
  },
629
630
  {
@@ -642,7 +643,7 @@ var ERROR_DOCS = [
642
643
  code: "OKM1843",
643
644
  title: "No eligible replica",
644
645
  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."
646
+ fix: "Configure a replica or drop the route. This call does not read the primary."
646
647
  },
647
648
  {
648
649
  code: "OKM1844",
@@ -1334,6 +1335,13 @@ function selectTarget(config, name) {
1334
1335
  return found;
1335
1336
  }
1336
1337
  function resolveTarget(name, input) {
1338
+ if (typeof input !== "string" && isReplicaTarget(input)) {
1339
+ throw new OkmError2("OKM1845", `Target ${name} is a replica. Migrations run on the primary only.`, {
1340
+ fix: {
1341
+ summary: "Set database or targets to the primary URL. A replica is not a migration target."
1342
+ }
1343
+ });
1344
+ }
1337
1345
  const url = typeof input === "string" ? input : input.url;
1338
1346
  const protectedTarget = typeof input === "string" ? false : input.protected === true;
1339
1347
  if (url === undefined || url.length === 0) {
@@ -1351,6 +1359,9 @@ function resolveTarget(name, input) {
1351
1359
  database: endpoint.database
1352
1360
  };
1353
1361
  }
1362
+ function isReplicaTarget(input) {
1363
+ return "primary" in input || "replicas" in input || "weight" in input || "pool" in input;
1364
+ }
1354
1365
  function endpointOf(url) {
1355
1366
  let parsed;
1356
1367
  try {
@@ -1945,7 +1956,7 @@ async function checkProject(cwd, flags) {
1945
1956
  const target = selectTarget(opened.config, named);
1946
1957
  assertTargetPolicy(target, "check", flags?.allowProtected ?? false);
1947
1958
  assertTargetPolicy(target, "drift", flags?.allowProtected ?? false);
1948
- const { assertAuthorDrift } = await import("./hqjsxnz7.js");
1959
+ const { assertAuthorDrift } = await import("./ykv7wh5b.js");
1949
1960
  await assertAuthorDrift(target.url, opened.built.catalog, opened.config.roles);
1950
1961
  return findings;
1951
1962
  }
@@ -2046,9 +2057,9 @@ function withRoles(built, config) {
2046
2057
  function writeArtifact(cwd, config, built) {
2047
2058
  const directory = join2(cwd, config.out ?? ".okm");
2048
2059
  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)}
2060
+ const startup = startupCatalog2(built.catalog);
2061
+ writeFileSync(join2(directory, "catalog.json"), serializeCatalog2(startup));
2062
+ writeFileSync(join2(directory, "catalog.hash"), `${catalogHash2(startup)}
2052
2063
  `);
2053
2064
  writeFileSync(join2(directory, "types.d.ts"), `${emitRowTypes(built)}${referenceAugmentation(built)}`);
2054
2065
  return directory;