okmodel 0.5.1 → 0.6.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 (155) hide show
  1. package/README.md +29 -14
  2. package/dist/adapters/pg/no-inflight-timeout.d.ts +17 -0
  3. package/dist/adapters/pg/postgres-extra.d.ts +22 -0
  4. package/dist/contracts/catalog/document.d.ts +19 -5
  5. package/dist/contracts/catalog/object.d.ts +2 -1
  6. package/dist/contracts/catalog/policy.d.ts +48 -0
  7. package/dist/contracts/catalog/types.d.ts +25 -1
  8. package/dist/contracts/catalog/view.d.ts +2 -0
  9. package/dist/contracts/internal.js +1 -1
  10. package/dist/contracts/location.d.ts +10 -0
  11. package/dist/dialects/pg/column.d.ts +13 -2
  12. package/dist/dialects/pg/ddl.d.ts +37 -1
  13. package/dist/dialects/pg/emit.d.ts +19 -1
  14. package/dist/dialects/pg/errors.d.ts +32 -1
  15. package/dist/dialects/pg/ext/citext.js +1 -1
  16. package/dist/dialects/pg/ext/index.js +1 -1
  17. package/dist/dialects/pg/ext/pg-trgm.js +1 -1
  18. package/dist/dialects/pg/filter-parse.d.ts +5 -4
  19. package/dist/dialects/pg/fn/index.js +2 -2
  20. package/dist/dialects/pg/index.d.ts +2 -1
  21. package/dist/dialects/pg/index.js +23 -17
  22. package/dist/dialects/pg/introspect.d.ts +7 -0
  23. package/dist/dialects/pg/model.d.ts +7 -2
  24. package/dist/dialects/pg/owner.d.ts +42 -0
  25. package/dist/dialects/pg/path-endpoint.d.ts +20 -0
  26. package/dist/dialects/pg/role/check.d.ts +3 -0
  27. package/dist/dialects/pg/role/index.d.ts +71 -6
  28. package/dist/dialects/pg/role/sql.d.ts +11 -0
  29. package/dist/dialects/pg/schema.d.ts +2 -0
  30. package/dist/dialects/pg/table.d.ts +25 -3
  31. package/dist/dialects/pg/tenancy.d.ts +45 -3
  32. package/dist/dialects/pg/view/client.d.ts +19 -1
  33. package/dist/dialects/pg/view/index.d.ts +4 -0
  34. package/dist/dialects/pg/view/index.js +21 -178
  35. package/dist/dialects/pg/view/refresh.d.ts +24 -0
  36. package/dist/runtime/batch.d.ts +1 -1
  37. package/dist/runtime/conflict.d.ts +2 -2
  38. package/dist/runtime/drift.d.ts +9 -0
  39. package/dist/runtime/inflight.d.ts +14 -0
  40. package/dist/runtime/pg/bun.js +4 -3
  41. package/dist/runtime/pg/pg.js +3 -3
  42. package/dist/runtime/pg/pglite.js +4 -3
  43. package/dist/runtime/pg/postgresjs.js +3 -3
  44. package/dist/runtime/plan.d.ts +18 -10
  45. package/dist/runtime/predicate.d.ts +19 -0
  46. package/dist/runtime/safety/index.js +1 -1
  47. package/dist/runtime/tenancy/composite.d.ts +26 -0
  48. package/dist/runtime/tenancy/context.d.ts +16 -0
  49. package/dist/runtime/tenancy/index.d.ts +9 -5
  50. package/dist/runtime/tenancy/index.js +1508 -37
  51. package/dist/runtime/tenancy/path.d.ts +87 -0
  52. package/dist/runtime/tenancy/rls.d.ts +67 -0
  53. package/dist/runtime/tenancy/via.d.ts +22 -0
  54. package/dist/runtime/traits/index.js +17 -11
  55. package/dist/runtime/types.d.ts +13 -1
  56. package/dist/runtime/validate/index.js +4 -4
  57. package/dist/runtime/write.d.ts +1 -1
  58. package/dist/shared/09p049xa.js +702 -0
  59. package/dist/shared/{6qd27mx0.js → 1f0xa3cb.js} +5 -5
  60. package/dist/shared/1nw1f3vs.js +23 -0
  61. package/dist/shared/{96qcj21c.js → 1z04fa85.js} +19 -9
  62. package/dist/shared/{qm7cdtvr.js → 2cg3wnxc.js} +296 -60
  63. package/dist/shared/{dyj86cmj.js → 2jd1sn0e.js} +2 -2
  64. package/dist/shared/{gbtcc2d7.js → 2z7wv5tj.js} +10 -35
  65. package/dist/shared/{k8v5v5wj.js → 32cnm7kn.js} +6 -1
  66. package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
  67. package/dist/shared/{2pqx26h8.js → 3fqwmr09.js} +31 -22
  68. package/dist/shared/{zqrnhwsq.js → 3qnhc7w5.js} +16 -9
  69. package/dist/shared/{r2sp8hrc.js → 5d8t84hg.js} +12 -3
  70. package/dist/shared/{87jvvhnt.js → 5j36z8dn.js} +16 -5
  71. package/dist/shared/{1g0drakv.js → 5yv4k6a6.js} +1 -1
  72. package/dist/shared/8dqhth8c.js +12 -0
  73. package/dist/shared/8etn7d8w.js +62 -0
  74. package/dist/shared/{v36c5g6p.js → 8x72s86k.js} +400 -13
  75. package/dist/shared/{nxdp2ze3.js → 929h19xp.js} +107 -5
  76. package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
  77. package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
  78. package/dist/shared/{efmy2gcg.js → ag4c7516.js} +1 -1
  79. package/dist/shared/b3awxn7h.js +45 -0
  80. package/dist/shared/b4v57v3x.js +9 -0
  81. package/dist/shared/bdk4sty6.js +58 -0
  82. package/dist/shared/{51mvgj8w.js → c58secgj.js} +100 -7
  83. package/dist/shared/d5azex6s.js +6 -0
  84. package/dist/shared/{5fqx9yh4.js → dk8v79p3.js} +3 -1
  85. package/dist/shared/{4m9fn52z.js → ed0b8k4s.js} +1 -1
  86. package/dist/shared/{z2rnngj4.js → ek2wt2dz.js} +8 -5
  87. package/dist/shared/{sycnx8gt.js → f0f1n281.js} +18 -8
  88. package/dist/shared/{f5cw5q5r.js → g90m9b0g.js} +2 -1
  89. package/dist/shared/{pf0d8f77.js → gwn6v667.js} +1 -1
  90. package/dist/shared/{8rsy3hxz.js → h15jx9bc.js} +1 -1
  91. package/dist/shared/{5enne4ma.js → hazqqecq.js} +17 -93
  92. package/dist/shared/{jwtxdj06.js → j3rkk835.js} +32 -9
  93. package/dist/shared/{hf22e0wt.js → j60w0dvr.js} +19 -18
  94. package/dist/shared/{ycatkxmq.js → jgqj11w4.js} +1 -1
  95. package/dist/shared/k68eedmy.js +85 -0
  96. package/dist/shared/{szm0cksa.js → knmes2kh.js} +34 -6
  97. package/dist/shared/{0t22wwh1.js → mb2ycs1p.js} +4 -2
  98. package/dist/shared/{cr74e96e.js → n076ydsc.js} +1 -1
  99. package/dist/shared/{cctw17hj.js → nx9hh281.js} +31 -28
  100. package/dist/shared/{gde73es6.js → p248q79y.js} +56 -55
  101. package/dist/shared/{janx09m5.js → p6bjzq9q.js} +11 -7
  102. package/dist/shared/{6rk2gen5.js → pjr6xrm5.js} +2 -2
  103. package/dist/shared/{wq9n6pwr.js → pz6rj8ay.js} +82 -9
  104. package/dist/shared/{caw8b9d8.js → q3j1tq7a.js} +35 -22
  105. package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
  106. package/dist/shared/{tm5ab8we.js → r11n8grd.js} +2 -2
  107. package/dist/shared/r1fdd0f6.js +19 -0
  108. package/dist/shared/{8ktwd3rh.js → r3j8s5y0.js} +1 -1
  109. package/dist/shared/rhs3xe4k.js +7 -0
  110. package/dist/shared/{ntneex2a.js → rpycarcr.js} +6 -6
  111. package/dist/shared/{aj2qxyhj.js → sbdme3vy.js} +14 -2
  112. package/dist/shared/{qv2ydck9.js → t3g0k88x.js} +638 -67
  113. package/dist/shared/{ga3sfy93.js → ttmg09vd.js} +535 -200
  114. package/dist/shared/{kfjp0gck.js → venqrjna.js} +8 -4
  115. package/dist/shared/{fancm5pt.js → vjpnzwjz.js} +138 -15
  116. package/dist/shared/{32zqc1dw.js → vmfkhszw.js} +31 -7
  117. package/dist/shared/{krxx77g0.js → wkme6tep.js} +10 -8
  118. package/dist/shared/{ps7a6h14.js → wt5rzv1h.js} +12 -8
  119. package/dist/shared/wxr8cwns.js +6 -0
  120. package/dist/shared/{6mnp99hx.js → wynzeggs.js} +16 -12
  121. package/dist/shared/x3jc53kg.js +220 -0
  122. package/dist/shared/{sdc7r2ww.js → xq0mvhts.js} +8 -8
  123. package/dist/shared/xqve9598.js +53 -0
  124. package/dist/shared/{47x8x9ze.js → y2t3jktd.js} +2 -2
  125. package/dist/shared/{9x25fxhc.js → y34a8714.js} +1 -1
  126. package/dist/shared/{k7dwkhvq.js → y6jdds7g.js} +4 -1
  127. package/dist/shared/{nkaxvt9q.js → ys0xzyaj.js} +1 -1
  128. package/dist/shared/{c9z5g0fm.js → ywr8hmey.js} +5 -2
  129. package/dist/shared/{g7ez901n.js → ywspezzx.js} +1 -1
  130. package/dist/shared/{9m80mkac.js → ze4hxsmy.js} +1 -1
  131. package/dist/shared/{r8x5qhd7.js → zm0mafk4.js} +10 -4
  132. package/dist/shared/{y005b761.js → zy8r7qvz.js} +6 -2
  133. package/dist/tooling/cli.js +41 -15
  134. package/dist/tooling/migrate/apply.d.ts +14 -0
  135. package/dist/tooling/migrate/check.d.ts +3 -4
  136. package/dist/tooling/migrate/classify.d.ts +4 -0
  137. package/dist/tooling/migrate/config.d.ts +3 -2
  138. package/dist/tooling/migrate/files.d.ts +11 -1
  139. package/dist/tooling/migrate/index.js +14 -14
  140. package/dist/tooling/migrate/lint.d.ts +1 -1
  141. package/dist/tooling/migrate/lost.d.ts +28 -0
  142. package/dist/tooling/migrate/paths.d.ts +14 -0
  143. package/dist/tooling/migrate/plan.d.ts +6 -3
  144. package/dist/tooling/migrate/runner.d.ts +25 -0
  145. package/dist/tooling/migrate/sandbox.d.ts +57 -0
  146. package/dist/tooling/migrate/sql-lex.d.ts +20 -3
  147. package/dist/tooling/migrate/text.d.ts +42 -0
  148. package/dist/tooling/node-version.d.ts +21 -0
  149. package/dist/tooling/testing/facts.d.ts +33 -5
  150. package/dist/tooling/testing/index.d.ts +1 -1
  151. package/dist/tooling/testing/index.js +3 -3
  152. package/dist/tooling/testing/isolation.d.ts +11 -5
  153. package/package.json +5 -2
  154. package/dist/shared/7zwwyh0v.js +0 -448
  155. package/dist/shared/q58y17hy.js +0 -21
@@ -1,51 +1,51 @@
1
1
  import {
2
2
  open2
3
- } from "./krxx77g0.js";
3
+ } from "./wkme6tep.js";
4
4
  import {
5
5
  startupHash2
6
- } from "./8bwbwy8h.js";
6
+ } from "./qq3899ht.js";
7
7
  import {
8
8
  runTarget,
9
9
  assumeRole,
10
+ grantAppMeta,
10
11
  statementOutsideTransaction,
11
- loadMigrations,
12
- loadHeadSnapshot,
13
12
  readReference,
14
13
  referenceInserts,
15
14
  provisionMark,
16
15
  expandProvisioned,
17
16
  installSnapshot
18
- } from "./gde73es6.js";
17
+ } from "./p248q79y.js";
19
18
  import {
20
19
  assertCreateRole,
21
20
  currentUser,
22
21
  roleExists
23
- } from "./szm0cksa.js";
22
+ } from "./knmes2kh.js";
24
23
  import {
24
+ projectPath,
25
+ loadMigrations,
26
+ loadHeadSnapshot,
25
27
  lintMigrationDirectory,
26
28
  hasError,
27
29
  lintRefusal,
28
30
  projectHead,
29
31
  loadBuiltSchema,
30
32
  loadConfig
31
- } from "./qv2ydck9.js";
33
+ } from "./t3g0k88x.js";
32
34
  import {
33
35
  changesRole,
34
36
  batchSizeField,
35
37
  assertExtensionsAvailable,
36
38
  planMigration,
39
+ finishQuiet,
37
40
  assertTargetPolicy,
38
41
  assertDirectConnection,
39
42
  assertStableBackend,
40
43
  selectTarget
41
- } from "./ga3sfy93.js";
44
+ } from "./ttmg09vd.js";
42
45
  import {
43
46
  OkmError2
44
47
  } from "./52szzakm.js";
45
48
 
46
- // src/tooling/migrate/apply.ts
47
- import { join } from "node:path";
48
-
49
49
  // src/tooling/migrate/engine.ts
50
50
  var IDENT = String.raw`"(?:[^"]|"")+"|[A-Za-z_][\w$]*`;
51
51
  var DEFAULT_UUIDV7 = /\bdefault\s+uuidv7\s*\(\s*\)/i;
@@ -121,6 +121,11 @@ var BACKFILL = `create table if not exists okm_backfill (
121
121
  updated_at timestamptz not null default now(),
122
122
  primary key (migration_id, step_index)
123
123
  )`;
124
+ async function ensureMigrationTables2(execute) {
125
+ await execute(META);
126
+ await execute(HISTORY);
127
+ await execute(BACKFILL);
128
+ }
124
129
  function applyUnits(migration, done) {
125
130
  const pending = [];
126
131
  for (let index = 0;index < migration.steps.length; index += 1) {
@@ -175,7 +180,8 @@ async function applyTarget2(request) {
175
180
  fix: { summary: "Apply needs a driver with interactive transactions." }
176
181
  });
177
182
  }
178
- const connection = await pool.reserve();
183
+ const reserved = await pool.reserve();
184
+ const connection = reserved;
179
185
  let locked = false;
180
186
  try {
181
187
  await assertStableBackend(() => backendPid(connection), request.allowPooler === true);
@@ -202,9 +208,7 @@ async function applyTarget2(request) {
202
208
  throw notEmpty(request.target, state.kind === "occupied" ? state.object : "okm_meta");
203
209
  }
204
210
  }
205
- await connection.execute(META);
206
- await connection.execute(HISTORY);
207
- await connection.execute(BACKFILL);
211
+ await ensureMigrationTables2((sql) => connection.execute(sql));
208
212
  const restamped = await restampLegacy(connection, request);
209
213
  const history = await readHistory(connection);
210
214
  const done = expandProvisioned(request.migrations, history.done, history.ids);
@@ -225,14 +229,14 @@ async function applyTarget2(request) {
225
229
  if (request.snapshot !== undefined) {
226
230
  await insertReference(connection, request);
227
231
  }
232
+ await grantAppMeta(connection, request.appRole, session);
228
233
  return restamped.length === 0 ? report : { ...report, restamped };
229
234
  } finally {
230
- if (locked)
231
- await connection.execute("select pg_advisory_unlock(hashtext($1))", [
232
- lockKey(request.target)
233
- ]);
234
- await connection.release();
235
- await pool.close();
235
+ if (locked) {
236
+ await finishQuiet(() => connection.execute("select pg_advisory_unlock(hashtext($1))", [lockKey(request.target)]));
237
+ }
238
+ await finishQuiet(() => connection.release());
239
+ await finishQuiet(() => pool.close());
236
240
  }
237
241
  }
238
242
  async function applyProject2(cwd, flags, write) {
@@ -267,7 +271,7 @@ nothing to apply
267
271
  catalogHash: head.catalogHash,
268
272
  reference: built === undefined ? [] : readReference(built.tables, built.casing, head.catalog)
269
273
  },
270
- ...config.roles !== undefined ? { migrationRole: config.roles.migration } : {},
274
+ ...config.roles !== undefined ? { migrationRole: config.roles.migration, appRole: config.roles.app } : {},
271
275
  lintDirectory: directory,
272
276
  onProgress: (line) => {
273
277
  if (write !== undefined)
@@ -314,7 +318,7 @@ no changes
314
318
  steps: plan.steps
315
319
  }
316
320
  ],
317
- ...config.roles !== undefined ? { migrationRole: config.roles.migration } : {}
321
+ ...config.roles !== undefined ? { migrationRole: config.roles.migration, appRole: config.roles.app } : {}
318
322
  });
319
323
  return formatReport(report);
320
324
  }
@@ -380,7 +384,7 @@ var RESTAMP = `with pairs as (
380
384
  )
381
385
  select distinct id from history order by id`;
382
386
  function joinMigrations(cwd, migrations) {
383
- return join(cwd, migrations ?? "migrations");
387
+ return projectPath(cwd, migrations ?? "migrations");
384
388
  }
385
389
  async function backendPid(connection) {
386
390
  const result = await connection.execute("select pg_backend_pid()::text");
@@ -464,10 +468,9 @@ async function provisionEmpty(connection, request, session) {
464
468
  assertTargetPolicy({ name: request.target, protected: request.protected }, "reference", request.allowProtected === true);
465
469
  }
466
470
  const schema = concreteSchema(request);
467
- await connection.execute(META);
468
- await connection.execute(HISTORY);
469
- await connection.execute(BACKFILL);
471
+ await ensureMigrationTables2((sql) => connection.execute(sql));
470
472
  await installSnapshot(connection, snapshot.catalog, schema, snapshot.reference, session, request.migrationRole);
473
+ await grantAppMeta(connection, request.appRole, session);
471
474
  const mark = provisionMark(snapshot.migrationId);
472
475
  await connection.execute("begin");
473
476
  try {
@@ -601,4 +604,4 @@ function wireTrue(value) {
601
604
  return value === "t" || value === "true";
602
605
  }
603
606
 
604
- export { applyTarget2, applyProject2, pushProject2, backfillTiming2 };
607
+ export { ensureMigrationTables2, applyTarget2, applyProject2, pushProject2, backfillTiming2 };
@@ -1,12 +1,9 @@
1
1
  import {
2
- serializeCatalog2,
3
- catalogHash2,
4
- startupCatalog2,
5
- parseCatalog2
6
- } from "./8bwbwy8h.js";
2
+ serializeCatalog2
3
+ } from "./qq3899ht.js";
7
4
  import {
8
5
  roleExists
9
- } from "./szm0cksa.js";
6
+ } from "./knmes2kh.js";
10
7
  import {
11
8
  canonicalTypeName,
12
9
  createdRoleName,
@@ -17,30 +14,30 @@ import {
17
14
  checkedCast,
18
15
  omitManagedObjects,
19
16
  planMigration,
20
- parsePlan,
17
+ sessionFailure,
21
18
  assertTargetPolicy
22
- } from "./ga3sfy93.js";
19
+ } from "./ttmg09vd.js";
23
20
  import {
24
21
  quoteIdent,
25
22
  qualify
26
- } from "./8xsbagad.js";
23
+ } from "./9s0ypp87.js";
27
24
  import {
28
25
  alignExtensionVersions
29
- } from "./8ktwd3rh.js";
26
+ } from "./r3j8s5y0.js";
30
27
  import {
31
28
  catalog,
32
29
  isDomain
33
- } from "./g7ez901n.js";
30
+ } from "./ywspezzx.js";
34
31
  import {
35
32
  snakeCase
36
- } from "./ntneex2a.js";
33
+ } from "./rpycarcr.js";
37
34
  import {
38
35
  quoteLiteral
39
36
  } from "./94ez8kzh.js";
40
37
  import {
41
38
  identityKey2,
42
39
  identityLabel2
43
- } from "./t4tbsy9h.js";
40
+ } from "./358zsv4r.js";
44
41
  import {
45
42
  OkmError2
46
43
  } from "./52szzakm.js";
@@ -76,6 +73,16 @@ async function assumeRole(connection, role, session) {
76
73
  await connection.execute(`set role ${quoteIdent(role)}`);
77
74
  session.setRole = role;
78
75
  }
76
+ async function grantAppMeta(connection, role, session) {
77
+ if (role === undefined)
78
+ return;
79
+ const previous = session.setRole;
80
+ if (previous !== undefined)
81
+ await connection.execute("reset role");
82
+ await connection.execute(`grant select on okm_meta to ${quoteIdent(role)}`);
83
+ if (previous !== undefined)
84
+ await connection.execute(`set role ${quoteIdent(previous)}`);
85
+ }
79
86
  async function grantSchema(connection, schema, role) {
80
87
  await connection.execute(`grant usage, create on schema ${quoteIdent(schema)} to ${quoteIdent(role)}`);
81
88
  }
@@ -98,9 +105,10 @@ async function runUnit(connection, unit, run, session) {
98
105
  await rebuildInvalidIndex(connection, item.step.sql);
99
106
  const created = createdRoleName(item.step.sql);
100
107
  const existed = created !== undefined && await roleExists(connection, created);
101
- if (created === undefined)
108
+ if (created === undefined) {
102
109
  await connection.execute(item.step.sql);
103
- else if (!existed)
110
+ await assertSchemaUsageLanded(connection, item.step.sql);
111
+ } else if (!existed)
104
112
  await connection.execute(createRoleOnce(item.step.sql));
105
113
  if (created !== undefined && created === run.migrationRole) {
106
114
  if (!existed) {
@@ -303,7 +311,35 @@ function concurrentIndexName(sql) {
303
311
  const match = /^create\s+(?:unique\s+)?index\s+concurrently\s+(?:if\s+not\s+exists\s+)?(?:"([^"]+)"|([A-Za-z_][\w$]*))/i.exec(sql.trim());
304
312
  return match?.[1] ?? match?.[2];
305
313
  }
314
+ var SCHEMA_USAGE = /^\s*grant\s+usage\s+on\s+schema\s+(?:"((?:[^"]|"")*)"|([A-Za-z_][\w$]*))\s+to\s+(?:"((?:[^"]|"")*)"|([A-Za-z_][\w$]*))\s*;?\s*$/i;
315
+ async function assertSchemaUsageLanded(connection, sql) {
316
+ const match = SCHEMA_USAGE.exec(sql.trim());
317
+ if (match === null)
318
+ return;
319
+ const schema = (match[1] ?? match[2] ?? "").replaceAll('""', '"');
320
+ const role = (match[3] ?? match[4] ?? "").replaceAll('""', '"');
321
+ const found = await connection.execute(`select exists (
322
+ select 1
323
+ from pg_namespace n
324
+ cross join lateral aclexplode(n.nspacl) a
325
+ join pg_roles r on r.oid = a.grantee
326
+ where n.nspname = $1 and r.rolname = $2 and a.privilege_type = 'USAGE'
327
+ )`, [schema, role]);
328
+ const landed = found.rows[0]?.[0];
329
+ if (landed === "t" || landed === "true")
330
+ return;
331
+ throw new OkmError2("OKM1825", `Grant usage on schema ${schema} to ${role} did not take effect.`, {
332
+ kind: "forbidden",
333
+ fix: {
334
+ summary: `Make the migration role the owner of schema ${schema}. ALTER SCHEMA ${quoteIdent(schema)} OWNER TO <migration role>.`
335
+ }
336
+ });
337
+ }
306
338
  function failStep(unit, index, error) {
339
+ const lost = sessionFailure(error);
340
+ if (lost !== undefined) {
341
+ return new OkmError2(lost.code, `Migration ${unit.migrationId} failed at step ${String(index + 1)}: ${lost.message}`, { kind: lost.kind, cause: error, fix: lost.fix });
342
+ }
307
343
  if (error instanceof OkmError2)
308
344
  return error;
309
345
  const message = error instanceof Error ? error.message : "The step failed.";
@@ -360,44 +396,6 @@ function delay(ms) {
360
396
  });
361
397
  }
362
398
 
363
- // src/tooling/migrate/files.ts
364
- import { existsSync, readdirSync, readFileSync } from "node:fs";
365
- import { join } from "node:path";
366
- function loadMigrations(directory) {
367
- if (!existsSync(directory))
368
- return [];
369
- const names = readdirSync(directory).filter((file) => file.endsWith(".sql")).sort();
370
- const migrations = [];
371
- for (const file of names) {
372
- const id = file.slice(0, -".sql".length);
373
- const catalogPath = join(directory, `${id}.catalog.json`);
374
- if (!existsSync(catalogPath)) {
375
- throw new OkmError2("invalid", `Migration ${id} is missing its catalog snapshot.`, {
376
- fix: {
377
- summary: "Generate the migration again so the SQL and the catalog are written together."
378
- }
379
- });
380
- }
381
- const plan = parsePlan(readFileSync(join(directory, file), "utf8"));
382
- const stored = parseCatalog2(readFileSync(catalogPath, "utf8"));
383
- const startup = startupCatalog2(stored);
384
- const hash = catalogHash2(startup);
385
- migrations.push(startup === stored ? { id, catalogHash: hash, steps: plan.steps } : { id, catalogHash: hash, legacyHash: catalogHash2(stored), steps: plan.steps });
386
- }
387
- return migrations;
388
- }
389
- function loadHeadSnapshot(directory) {
390
- const migrations = loadMigrations(directory);
391
- const last = migrations.at(-1);
392
- if (last === undefined)
393
- return;
394
- return {
395
- id: last.id,
396
- catalogHash: last.catalogHash,
397
- catalog: parseCatalog2(readFileSync(join(directory, `${last.id}.catalog.json`), "utf8"))
398
- };
399
- }
400
-
401
399
  // src/tooling/migrate/reference.ts
402
400
  function readReference(tables, casing, head) {
403
401
  const declared = [];
@@ -588,8 +586,10 @@ async function installSnapshot(connection, head, schema, reference, session, mig
588
586
  return;
589
587
  await connection.execute("begin");
590
588
  began = true;
591
- for (const sql of batch)
589
+ for (const sql of batch) {
592
590
  await connection.execute(sql);
591
+ await assertSchemaUsageLanded(connection, sql);
592
+ }
593
593
  await connection.execute("commit");
594
594
  began = false;
595
595
  batch.length = 0;
@@ -611,6 +611,7 @@ async function installSnapshot(connection, head, schema, reference, session, mig
611
611
  } else {
612
612
  await flush();
613
613
  await connection.execute(sql);
614
+ await assertSchemaUsageLanded(connection, sql);
614
615
  }
615
616
  if (created !== undefined && created === migrationRole) {
616
617
  await flush();
@@ -753,4 +754,4 @@ function normaliseFunction(object) {
753
754
  };
754
755
  }
755
756
 
756
- export { runTarget, assumeRole, statementOutsideTransaction, loadMigrations, loadHeadSnapshot, readReference, referenceInserts, catalogsEqual, provisionMark, expandProvisioned, provisionHistoryIsKnown, installSnapshot, snapshotDifference };
757
+ export { runTarget, assumeRole, grantAppMeta, grantSchema, assertSchemaUsageLanded, statementOutsideTransaction, readReference, referenceInserts, catalogsEqual, provisionMark, expandProvisioned, provisionHistoryIsKnown, installSnapshot, snapshotDifference };
@@ -1,20 +1,22 @@
1
1
  import {
2
2
  open2
3
- } from "./krxx77g0.js";
4
- import"./8bwbwy8h.js";
3
+ } from "./wkme6tep.js";
4
+ import"./qq3899ht.js";
5
5
  import"./n442h54c.js";
6
6
  import {
7
7
  openProject
8
- } from "./qv2ydck9.js";
8
+ } from "./t3g0k88x.js";
9
9
  import {
10
+ sessionFailure,
11
+ finishQuiet,
10
12
  assertTargetPolicy,
11
13
  selectTarget
12
- } from "./ga3sfy93.js";
13
- import"./8xsbagad.js";
14
+ } from "./ttmg09vd.js";
15
+ import"./9s0ypp87.js";
14
16
  import {
15
17
  isExactVersion,
16
18
  compareExtensionVersions
17
- } from "./8ktwd3rh.js";
19
+ } from "./r3j8s5y0.js";
18
20
  import {
19
21
  OkmError2
20
22
  } from "./52szzakm.js";
@@ -29,8 +31,10 @@ async function extProject(cwd, command, flags) {
29
31
  if (command === "list")
30
32
  return formatList(await readExtensions(pool));
31
33
  return formatCheck(opened.built.catalog.objects, await readExtensions(pool));
34
+ } catch (error) {
35
+ throw sessionFailure(error) ?? error;
32
36
  } finally {
33
- await pool.close();
37
+ await finishQuiet(() => pool.close());
34
38
  }
35
39
  }
36
40
  var LIST_SQL = `
@@ -3,7 +3,7 @@ import {
3
3
  readHandle,
4
4
  readCall,
5
5
  attachHttp
6
- } from "./hf22e0wt.js";
6
+ } from "./j60w0dvr.js";
7
7
  import {
8
8
  selectedColumns,
9
9
  parseOrder,
@@ -15,7 +15,7 @@ import {
15
15
  isRecord,
16
16
  registerFailFix,
17
17
  fail
18
- } from "./gbtcc2d7.js";
18
+ } from "./2z7wv5tj.js";
19
19
  import {
20
20
  OkmError2,
21
21
  throwNamed2
@@ -6,9 +6,30 @@ import {
6
6
  } from "./52szzakm.js";
7
7
 
8
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) {
9
+ var PARSED = [
10
+ "eq",
11
+ "in",
12
+ "inList",
13
+ "notIn",
14
+ "lt",
15
+ "lte",
16
+ "gt",
17
+ "gte",
18
+ "between",
19
+ "startsWith",
20
+ "endsWith",
21
+ "contains",
22
+ "like",
23
+ "ilike",
24
+ "not",
25
+ "containedBy",
26
+ "overlaps",
27
+ "hasKey",
28
+ "hasAnyKey"
29
+ ];
30
+ var PARSED_OPS = `,${PARSED.join(",")},`;
31
+ var PARSED_FIX = "Use eq, in, notIn, lt, lte, gt, gte, between, startsWith, endsWith, contains, like, ilike, not, containedBy, overlaps, hasKey, or hasAnyKey.";
32
+ function parseQuery(allow, sort, query, relations) {
12
33
  if (typeof query !== "object" || query === null || Array.isArray(query)) {
13
34
  throw new OkmError2("OKM1121", "filters parse expects an object of fields.");
14
35
  }
@@ -21,6 +42,7 @@ function parseQuery(allow, sort, query) {
21
42
  });
22
43
  }
23
44
  }
45
+ const nested = relations ?? {};
24
46
  const whereParts = [];
25
47
  let orderBy;
26
48
  for (const key of Object.keys(query)) {
@@ -29,13 +51,17 @@ function parseQuery(allow, sort, query) {
29
51
  orderBy = { [value]: "asc" };
30
52
  continue;
31
53
  }
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
- });
54
+ if (Object.hasOwn(allow, key)) {
55
+ whereParts.push(...clauses(key, allow[key] ?? [], value));
56
+ continue;
36
57
  }
37
- const ops = allow[key] ?? [];
38
- whereParts.push(...clauses(key, ops, value));
58
+ if (Object.hasOwn(nested, key)) {
59
+ whereParts.push(relationClause(key, nested[key] ?? [], value));
60
+ continue;
61
+ }
62
+ throw new OkmError2("OKM1123", `Field ${key} is not in the allow list.`, {
63
+ fix: { summary: "Remove the field, or add it to filters({ allow }) or relations." }
64
+ });
39
65
  }
40
66
  const where = combine(whereParts);
41
67
  return orderBy === undefined ? { where } : { where, orderBy };
@@ -100,6 +126,21 @@ function one(field, op, value) {
100
126
  return { [field]: tag("endsWith", text(value, op)) };
101
127
  case "contains":
102
128
  return { [field]: tag("contains", value) };
129
+ case "not":
130
+ if (value !== null && typeof value === "object") {
131
+ throw new OkmError2("OKM1120", `not on ${field} takes a scalar.`, {
132
+ fix: { summary: "Send not a scalar or null. Nested operators are not accepted." }
133
+ });
134
+ }
135
+ return { [field]: tag("not", value) };
136
+ case "containedBy":
137
+ return { [field]: tag("containedBy", value) };
138
+ case "overlaps":
139
+ return { [field]: tag("overlaps", value) };
140
+ case "hasKey":
141
+ return { [field]: tag("hasKey", text(value, "hasKey")) };
142
+ case "hasAnyKey":
143
+ return { [field]: tag("hasAnyKey", strings(value, field)) };
103
144
  case "like":
104
145
  return { [field]: tag("like", text(value, op)) };
105
146
  case "ilike":
@@ -110,6 +151,30 @@ function one(field, op, value) {
110
151
  });
111
152
  }
112
153
  }
154
+ function relationClause(name, fields, value) {
155
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
156
+ throw new OkmError2("OKM1120", `filters() cannot apply a value on ${name}.`, {
157
+ fix: { summary: "Send the relation an object of allowlisted fields." }
158
+ });
159
+ }
160
+ const parts = [];
161
+ for (const field of Object.keys(value)) {
162
+ if (!Object.hasOwn(value, field))
163
+ continue;
164
+ if (!fields.includes(field)) {
165
+ throw new OkmError2("OKM1123", `Field ${field} is not in the allow list.`, {
166
+ fix: { summary: "Remove the field, or add it to filters({ relations })." }
167
+ });
168
+ }
169
+ parts.push(...clauses(field, PARSED, value[field]));
170
+ }
171
+ if (parts.length === 0) {
172
+ throw new OkmError2("OKM1120", `filters() cannot apply a value on ${name}.`, {
173
+ fix: { summary: "Send the relation an object of allowlisted fields." }
174
+ });
175
+ }
176
+ return { [name]: tag("has", combine(parts)) };
177
+ }
113
178
  function combine(parts) {
114
179
  const first = parts[0];
115
180
  if (first === undefined)
@@ -126,6 +191,14 @@ function list(value, op) {
126
191
  }
127
192
  return value;
128
193
  }
194
+ function strings(value, field) {
195
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
196
+ throw new OkmError2("OKM1120", `hasAnyKey on ${field} needs an array of strings.`, {
197
+ fix: { summary: "Send hasAnyKey an array of strings." }
198
+ });
199
+ }
200
+ return value;
201
+ }
129
202
  function text(value, op) {
130
203
  if (typeof value !== "string") {
131
204
  throw new OkmError2("OKM1120", `${op} needs a string.`, {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  PgSession
3
- } from "./krxx77g0.js";
3
+ } from "./wkme6tep.js";
4
4
  import {
5
5
  isConnectionLost
6
- } from "./pf0d8f77.js";
6
+ } from "./gwn6v667.js";
7
7
  import {
8
8
  driverErrors,
9
9
  rejectClosed,
@@ -16,7 +16,7 @@ import {
16
16
  // src/adapters/pg/postgres-extra.ts
17
17
  var RESET_ALL = "RESET ALL";
18
18
  var UNLOCK = "SELECT pg_advisory_unlock_all()";
19
- async function checkout(pool) {
19
+ async function checkout2(pool) {
20
20
  if (pool.isClosed())
21
21
  return rejectClosed();
22
22
  const reserved = await reserveConnection(pool.sql, pool.acquireMs, pool.counters);
@@ -24,16 +24,17 @@ async function checkout(pool) {
24
24
  const local = new Set;
25
25
  const session = new PgSession(reserved, pool.counters, "reserved", local, pool.isClosed, pool.poolInflight, pool.notices, pool.named);
26
26
  let released = false;
27
- let lost = false;
28
- const guard = (run) => lost ? rejectClosed("The connection was lost.") : run().catch((error) => {
27
+ const lost = { on: false };
28
+ arm(reserved, lost);
29
+ const guard = (run) => lost.on ? rejectClosed("CONNECTION_CLOSED") : run().catch((error) => {
29
30
  if (isConnectionLost(error))
30
- lost = true;
31
+ lost.on = true;
31
32
  throw error;
32
33
  });
33
34
  return {
34
35
  execute: (text, params, options) => guard(() => runCall(pool.isClosed(), options, (watch) => session.query(text, params, watch))),
35
36
  batch: (statements, options) => guard(() => runCall(pool.isClosed(), options, async (watch) => {
36
- const { runAtomicBatch } = await import("./efmy2gcg.js");
37
+ const { runAtomicBatch } = await import("./ag4c7516.js");
37
38
  return runAtomicBatch(session, statements, watch);
38
39
  })),
39
40
  cancel() {
@@ -44,17 +45,21 @@ async function checkout(pool) {
44
45
  return;
45
46
  released = true;
46
47
  try {
47
- if (!lost)
48
+ if (!lost.on)
48
49
  await resetConnection(reserved, session.depth());
49
50
  } finally {
50
51
  pool.counters.reserved = Math.max(0, pool.counters.reserved - 1);
51
- if (!lost)
52
+ if (lost.on)
53
+ await pool.sql.unsafe("select 1").catch(() => {
54
+ return;
55
+ });
56
+ else
52
57
  reserved.release();
53
58
  }
54
59
  }
55
60
  };
56
61
  }
57
- async function describe(sql, text, params) {
62
+ async function describe2(sql, text, params) {
58
63
  const pending = sql.unsafe(text, asParams(params), { prepare: true });
59
64
  const described = await pending.describe();
60
65
  return {
@@ -62,7 +67,7 @@ async function describe(sql, text, params) {
62
67
  parameterCount: described.types.length
63
68
  };
64
69
  }
65
- async function* stream(sql, text, params) {
70
+ async function* stream2(sql, text, params) {
66
71
  const pending = sql.unsafe(text, asParams(params));
67
72
  for await (const chunk of pending.cursor(64)) {
68
73
  yield rowsFrom(chunk);
@@ -124,7 +129,21 @@ function asParams(params) {
124
129
  return [];
125
130
  return params;
126
131
  }
127
- async function holdCheckout(gate, checkout) {
132
+ function watchReserved2(sql, lost) {
133
+ arm(sql, lost);
134
+ }
135
+ function arm(sql, lost) {
136
+ const unsafe = sql.unsafe.bind(sql);
137
+ sql.unsafe = (text, params, options) => unsafe(text, params, {
138
+ ...options,
139
+ onexecute(connection) {
140
+ connection.onclose = () => {
141
+ lost.on = true;
142
+ };
143
+ }
144
+ });
145
+ }
146
+ async function holdCheckout2(gate, checkout) {
128
147
  try {
129
148
  const connection = await checkout();
130
149
  let released = false;
@@ -143,7 +162,7 @@ async function holdCheckout(gate, checkout) {
143
162
  throw error;
144
163
  }
145
164
  }
146
- async function holdListener(gate, start) {
165
+ async function holdListener2(gate, start) {
147
166
  try {
148
167
  const stop = await start();
149
168
  let stopped = false;
@@ -162,7 +181,7 @@ async function holdListener(gate, start) {
162
181
  throw error;
163
182
  }
164
183
  }
165
- function watchQuery(pending, watch, onAbort, ctx) {
184
+ function watchQuery2(pending, watch, onAbort, ctx) {
166
185
  return pending.then((result) => {
167
186
  finishWatch(pending, watch, onAbort, ctx);
168
187
  const why = watch.reason();
@@ -194,11 +213,5 @@ async function classify(error, watch) {
194
213
  }
195
214
  return Promise.reject(errors.mapDriverError(error));
196
215
  }
197
- export {
198
- checkout,
199
- describe,
200
- holdCheckout,
201
- holdListener,
202
- stream,
203
- watchQuery
204
- };
216
+
217
+ export { checkout2, describe2, stream2, watchReserved2, holdCheckout2, holdListener2, watchQuery2 };