okmodel 0.5.1 → 0.7.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 (233) hide show
  1. package/README.md +100 -1116
  2. package/dist/adapters/capabilities.d.ts +3 -0
  3. package/dist/adapters/failure.d.ts +13 -2
  4. package/dist/adapters/lazy-errors.d.ts +22 -0
  5. package/dist/adapters/pg/bunsql-extra.d.ts +54 -3
  6. package/dist/adapters/pg/bunsql.d.ts +14 -1
  7. package/dist/adapters/pg/cursor.d.ts +17 -0
  8. package/dist/adapters/pg/no-inflight-timeout.d.ts +20 -0
  9. package/dist/adapters/pg/nodepostgres-extra.d.ts +22 -1
  10. package/dist/adapters/pg/nodepostgres.d.ts +12 -0
  11. package/dist/adapters/pg/pglite-wire.d.ts +24 -0
  12. package/dist/adapters/pg/pglite.d.ts +10 -0
  13. package/dist/adapters/pg/postgres-extra.d.ts +41 -3
  14. package/dist/adapters/pg/reserved.d.ts +42 -0
  15. package/dist/adapters/pg/session.d.ts +0 -18
  16. package/dist/contracts/catalog/document.d.ts +19 -5
  17. package/dist/contracts/catalog/object.d.ts +2 -1
  18. package/dist/contracts/catalog/policy.d.ts +48 -0
  19. package/dist/contracts/catalog/types.d.ts +25 -1
  20. package/dist/contracts/catalog/view.d.ts +2 -0
  21. package/dist/contracts/connection.d.ts +7 -6
  22. package/dist/contracts/internal.js +1 -1
  23. package/dist/contracts/location.d.ts +10 -0
  24. package/dist/dialects/pg/column.d.ts +13 -2
  25. package/dist/dialects/pg/ddl.d.ts +37 -1
  26. package/dist/dialects/pg/emit.d.ts +19 -1
  27. package/dist/dialects/pg/errors.d.ts +32 -1
  28. package/dist/dialects/pg/ext/citext.js +1 -1
  29. package/dist/dialects/pg/ext/index.js +1 -1
  30. package/dist/dialects/pg/ext/pg-trgm.js +1 -1
  31. package/dist/dialects/pg/filter-parse.d.ts +5 -4
  32. package/dist/dialects/pg/floor.d.ts +68 -0
  33. package/dist/dialects/pg/fn/index.js +2 -2
  34. package/dist/dialects/pg/index.d.ts +8 -2
  35. package/dist/dialects/pg/index.js +67 -171
  36. package/dist/dialects/pg/introspect.d.ts +7 -0
  37. package/dist/dialects/pg/keys.d.ts +11 -8
  38. package/dist/dialects/pg/model.d.ts +47 -2
  39. package/dist/dialects/pg/morph.d.ts +43 -0
  40. package/dist/dialects/pg/operator-fit.d.ts +2 -2
  41. package/dist/dialects/pg/operators.d.ts +14 -7
  42. package/dist/dialects/pg/ops/bucket.d.ts +65 -0
  43. package/dist/dialects/pg/ops/inc.d.ts +2 -2
  44. package/dist/dialects/pg/ops/matches.d.ts +9 -12
  45. package/dist/dialects/pg/ops/relevance.d.ts +13 -0
  46. package/dist/dialects/pg/ops/search-query.d.ts +22 -0
  47. package/dist/dialects/pg/ops/window.d.ts +103 -0
  48. package/dist/dialects/pg/owner.d.ts +42 -0
  49. package/dist/dialects/pg/path-endpoint.d.ts +20 -0
  50. package/dist/dialects/pg/preset.d.ts +1 -1
  51. package/dist/dialects/pg/relations.d.ts +100 -3
  52. package/dist/dialects/pg/role/check.d.ts +3 -0
  53. package/dist/dialects/pg/role/index.d.ts +71 -6
  54. package/dist/dialects/pg/role/sql.d.ts +11 -0
  55. package/dist/dialects/pg/schema.d.ts +33 -3
  56. package/dist/dialects/pg/table.d.ts +25 -3
  57. package/dist/dialects/pg/tenancy.d.ts +45 -3
  58. package/dist/dialects/pg/view/client.d.ts +19 -1
  59. package/dist/dialects/pg/view/index.d.ts +4 -0
  60. package/dist/dialects/pg/view/index.js +21 -178
  61. package/dist/dialects/pg/view/refresh.d.ts +24 -0
  62. package/dist/runtime/aggregate.d.ts +8 -2
  63. package/dist/runtime/batch.d.ts +1 -1
  64. package/dist/runtime/bucket-group.d.ts +70 -0
  65. package/dist/runtime/conflict.d.ts +24 -6
  66. package/dist/runtime/drift.d.ts +9 -0
  67. package/dist/runtime/include.d.ts +0 -8
  68. package/dist/runtime/inflight.d.ts +14 -0
  69. package/dist/runtime/page.d.ts +4 -1
  70. package/dist/runtime/pg/bun.js +12 -5
  71. package/dist/runtime/pg/pg.js +5 -5
  72. package/dist/runtime/pg/pglite.d.ts +1 -0
  73. package/dist/runtime/pg/pglite.js +9 -8
  74. package/dist/runtime/pg/postgresjs.js +5 -5
  75. package/dist/runtime/plan.d.ts +113 -15
  76. package/dist/runtime/predicate.d.ts +19 -0
  77. package/dist/runtime/read-stream.d.ts +13 -16
  78. package/dist/runtime/result-name.d.ts +17 -0
  79. package/dist/runtime/safety/index.js +1 -1
  80. package/dist/runtime/server-check.d.ts +3 -3
  81. package/dist/runtime/sort-bind.d.ts +21 -0
  82. package/dist/runtime/sort.d.ts +56 -0
  83. package/dist/runtime/sync.d.ts +70 -0
  84. package/dist/runtime/tenancy/composite.d.ts +26 -0
  85. package/dist/runtime/tenancy/context.d.ts +16 -0
  86. package/dist/runtime/tenancy/index.d.ts +9 -5
  87. package/dist/runtime/tenancy/index.js +1511 -38
  88. package/dist/runtime/tenancy/path.d.ts +87 -0
  89. package/dist/runtime/tenancy/rls.d.ts +67 -0
  90. package/dist/runtime/tenancy/via.d.ts +22 -0
  91. package/dist/runtime/text-search.d.ts +20 -0
  92. package/dist/runtime/traits/index.d.ts +80 -0
  93. package/dist/runtime/traits/index.js +187 -17
  94. package/dist/runtime/tree.d.ts +38 -0
  95. package/dist/runtime/types.d.ts +231 -23
  96. package/dist/runtime/validate/index.js +4 -4
  97. package/dist/runtime/version.d.ts +39 -0
  98. package/dist/runtime/window.d.ts +21 -0
  99. package/dist/runtime/write.d.ts +55 -3
  100. package/dist/shared/{krxx77g0.js → 14vx95q4.js} +12 -18
  101. package/dist/shared/1est4ezg.js +313 -0
  102. package/dist/shared/{4az561fn.js → 1qa0qajz.js} +8 -23
  103. package/dist/shared/1w508jbr.js +1611 -0
  104. package/dist/shared/2c13q96h.js +18 -0
  105. package/dist/shared/2d3xq4ps.js +9 -0
  106. package/dist/shared/2shdbn8q.js +23 -0
  107. package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
  108. package/dist/shared/3v7t1yrv.js +208 -0
  109. package/dist/shared/5e3tf63j.js +220 -0
  110. package/dist/shared/5fe3kbsh.js +10 -0
  111. package/dist/shared/5v3xf80k.js +37 -0
  112. package/dist/shared/{51mvgj8w.js → 63qtkrca.js} +130 -3
  113. package/dist/shared/{gbtcc2d7.js → 67txcrcw.js} +62 -66
  114. package/dist/shared/79dt02vc.js +12 -0
  115. package/dist/shared/{v36c5g6p.js → 7k9grvf6.js} +407 -14
  116. package/dist/shared/7kms6fvj.js +20 -0
  117. package/dist/shared/7mnme5tn.js +8 -0
  118. package/dist/shared/7n7ddwt4.js +6 -0
  119. package/dist/shared/{87jvvhnt.js → 81ebdx9p.js} +48 -68
  120. package/dist/shared/{szm0cksa.js → 8skf0cam.js} +34 -6
  121. package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
  122. package/dist/shared/9a4ae4p8.js +31 -0
  123. package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
  124. package/dist/shared/9y3mmmh1.js +436 -0
  125. package/dist/shared/ab9gmbwy.js +213 -0
  126. package/dist/shared/{sycnx8gt.js → ax7qv55k.js} +18 -8
  127. package/dist/shared/{f5cw5q5r.js → aytqh5y2.js} +6 -4
  128. package/dist/shared/{fancm5pt.js → b5pftnn2.js} +138 -15
  129. package/dist/shared/{efmy2gcg.js → b76b8d95.js} +2 -2
  130. package/dist/shared/bdk4sty6.js +58 -0
  131. package/dist/shared/{32zqc1dw.js → bg3rym3g.js} +63 -32
  132. package/dist/shared/{0t22wwh1.js → bm22fvzr.js} +16 -14
  133. package/dist/shared/bz84tkzm.js +342 -0
  134. package/dist/shared/{31vm914h.js → cm3w0ywp.js} +15 -16
  135. package/dist/shared/cn957f8d.js +179 -0
  136. package/dist/shared/{pf0d8f77.js → cnkky55j.js} +25 -14
  137. package/dist/shared/{5fqx9yh4.js → dk8v79p3.js} +3 -1
  138. package/dist/shared/{nkaxvt9q.js → dkxgzaqd.js} +224 -19
  139. package/dist/shared/dzj09cye.js +10 -0
  140. package/dist/shared/{4m9fn52z.js → ed0b8k4s.js} +1 -1
  141. package/dist/shared/{z2rnngj4.js → ek2wt2dz.js} +8 -5
  142. package/dist/shared/exn3sd90.js +359 -0
  143. package/dist/shared/g6evj1j3.js +66 -0
  144. package/dist/shared/ga9j92qj.js +25 -0
  145. package/dist/shared/{qv2ydck9.js → gs74dbj5.js} +651 -76
  146. package/dist/shared/gzaveazc.js +285 -0
  147. package/dist/shared/{8rsy3hxz.js → h15jx9bc.js} +1 -1
  148. package/dist/shared/{dyj86cmj.js → hyh73qk6.js} +3 -3
  149. package/dist/shared/je4axnm8.js +22 -0
  150. package/dist/shared/{ycatkxmq.js → jgqj11w4.js} +1 -1
  151. package/dist/shared/{6qd27mx0.js → jvd0mqag.js} +5 -5
  152. package/dist/shared/jznfax0y.js +155 -0
  153. package/dist/shared/{cgbamx3e.js → kb2sdhfb.js} +7 -4
  154. package/dist/shared/{ps7a6h14.js → kr0jzbw2.js} +12 -8
  155. package/dist/shared/{janx09m5.js → ktz42yez.js} +20 -8
  156. package/dist/shared/{gde73es6.js → m6p12kbr.js} +56 -55
  157. package/dist/shared/{r6nwkxnt.js → m7n9h2a5.js} +4 -4
  158. package/dist/shared/{672t5vym.js → mss6ctgv.js} +2 -2
  159. package/dist/shared/{nxdp2ze3.js → mtaszv5a.js} +144 -12
  160. package/dist/shared/n816peh1.js +45 -0
  161. package/dist/shared/nbw1z4yd.js +18 -0
  162. package/dist/shared/{cctw17hj.js → p6qc5gcp.js} +38 -34
  163. package/dist/shared/{6rk2gen5.js → pbp1ss85.js} +20 -17
  164. package/dist/shared/pt94kz7f.js +6 -0
  165. package/dist/shared/{wq9n6pwr.js → pz6rj8ay.js} +82 -9
  166. package/dist/shared/q5c6ngn3.js +13 -0
  167. package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
  168. package/dist/shared/{8ktwd3rh.js → r3j8s5y0.js} +1 -1
  169. package/dist/shared/rhs3xe4k.js +7 -0
  170. package/dist/shared/{ntneex2a.js → rpycarcr.js} +6 -6
  171. package/dist/shared/{sdc7r2ww.js → rs73fca9.js} +8 -8
  172. package/dist/shared/{tm5ab8we.js → sfz1ksn6.js} +7 -4
  173. package/dist/shared/{6mnp99hx.js → svncdeh5.js} +16 -12
  174. package/dist/shared/{hf22e0wt.js → tbrvgw6z.js} +62 -42
  175. package/dist/shared/te5as7z7.js +622 -0
  176. package/dist/shared/{ga3sfy93.js → tsvww22q.js} +535 -200
  177. package/dist/shared/{47x8x9ze.js → ttsqgp4p.js} +20 -13
  178. package/dist/shared/{x72jb7ad.js → v8g1vgv8.js} +1 -1
  179. package/dist/shared/vb6qfyve.js +152 -0
  180. package/dist/shared/{r2sp8hrc.js → vczs8gd4.js} +22 -13
  181. package/dist/shared/{1g0drakv.js → vqrjpcqn.js} +145 -3
  182. package/dist/shared/{2pqx26h8.js → w72kgg4q.js} +31 -22
  183. package/dist/shared/{96qcj21c.js → w79hsckn.js} +16 -51
  184. package/dist/shared/wa07eas9.js +730 -0
  185. package/dist/shared/{k8v5v5wj.js → x933k9a3.js} +75 -34
  186. package/dist/shared/{cr74e96e.js → xhfsfkgk.js} +62 -28
  187. package/dist/shared/xjk7hnn5.js +272 -0
  188. package/dist/shared/{5enne4ma.js → xnxsed62.js} +66 -113
  189. package/dist/shared/xqve9598.js +53 -0
  190. package/dist/shared/{9x25fxhc.js → y34a8714.js} +1 -1
  191. package/dist/shared/{k7dwkhvq.js → y6jdds7g.js} +4 -1
  192. package/dist/shared/y6njgyda.js +249 -0
  193. package/dist/shared/{c9z5g0fm.js → ywr8hmey.js} +5 -2
  194. package/dist/shared/{g7ez901n.js → ywspezzx.js} +1 -1
  195. package/dist/shared/z7s6fpxv.js +62 -0
  196. package/dist/shared/{n7mcc1gk.js → zchaghkf.js} +4 -3
  197. package/dist/shared/{9m80mkac.js → ze4hxsmy.js} +1 -1
  198. package/dist/shared/zm0ptry0.js +40 -0
  199. package/dist/shared/{nsva2a6b.js → znm0bpp5.js} +3 -0
  200. package/dist/shared/zp9fmend.js +145 -0
  201. package/dist/shared/{y005b761.js → zy8r7qvz.js} +6 -2
  202. package/dist/tooling/cli.js +41 -15
  203. package/dist/tooling/migrate/apply.d.ts +14 -0
  204. package/dist/tooling/migrate/check.d.ts +3 -4
  205. package/dist/tooling/migrate/classify.d.ts +4 -0
  206. package/dist/tooling/migrate/config.d.ts +3 -2
  207. package/dist/tooling/migrate/engine.d.ts +3 -2
  208. package/dist/tooling/migrate/files.d.ts +11 -1
  209. package/dist/tooling/migrate/index.js +14 -14
  210. package/dist/tooling/migrate/lint.d.ts +1 -1
  211. package/dist/tooling/migrate/lost.d.ts +28 -0
  212. package/dist/tooling/migrate/paths.d.ts +14 -0
  213. package/dist/tooling/migrate/plan.d.ts +6 -3
  214. package/dist/tooling/migrate/runner.d.ts +25 -0
  215. package/dist/tooling/migrate/sandbox.d.ts +57 -0
  216. package/dist/tooling/migrate/sql-lex.d.ts +20 -3
  217. package/dist/tooling/migrate/text.d.ts +42 -0
  218. package/dist/tooling/node-version.d.ts +21 -0
  219. package/dist/tooling/testing/facts.d.ts +33 -5
  220. package/dist/tooling/testing/index.d.ts +1 -1
  221. package/dist/tooling/testing/index.js +3 -3
  222. package/dist/tooling/testing/isolation.d.ts +11 -5
  223. package/package.json +8 -3
  224. package/dist/shared/7m88x3e9.js +0 -45
  225. package/dist/shared/7zwwyh0v.js +0 -448
  226. package/dist/shared/aj2qxyhj.js +0 -90
  227. package/dist/shared/caw8b9d8.js +0 -204
  228. package/dist/shared/jwtxdj06.js +0 -76
  229. package/dist/shared/kfjp0gck.js +0 -212
  230. package/dist/shared/q58y17hy.js +0 -21
  231. package/dist/shared/qm7cdtvr.js +0 -912
  232. package/dist/shared/r8x5qhd7.js +0 -86
  233. package/dist/shared/zqrnhwsq.js +0 -31
@@ -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 "./8skf0cam.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 "./tsvww22q.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,9 +1,9 @@
1
1
  import {
2
- stack
3
- } from "./672t5vym.js";
2
+ stack2
3
+ } from "./mss6ctgv.js";
4
4
  import {
5
5
  checkPresetNames
6
- } from "./nsva2a6b.js";
6
+ } from "./znm0bpp5.js";
7
7
  import {
8
8
  isOperator,
9
9
  operatorValue
@@ -60,7 +60,7 @@ function refine(schema, call, uses) {
60
60
  const { wheres, rules } = resolve(schema, call.table, uses);
61
61
  return {
62
62
  ...call,
63
- where: stack(call.where, wheres),
63
+ where: stack2(call.where, wheres),
64
64
  ruleLines: [...call.ruleLines ?? [], ...rules]
65
65
  };
66
66
  }
@@ -3,10 +3,10 @@ import {
3
3
  } from "./cn0n68ft.js";
4
4
 
5
5
  // src/runtime/preset-stack.ts
6
- function stack(where, presets) {
6
+ function stack2(where, presets) {
7
7
  if (presets === undefined || presets.length === 0)
8
8
  return where;
9
9
  return tag("and", where === undefined ? presets : [where, ...presets]);
10
10
  }
11
11
 
12
- export { stack };
12
+ export { stack2 };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  isConnectionSqlstate,
3
3
  isConnectionErrno
4
- } from "./pf0d8f77.js";
4
+ } from "./cnkky55j.js";
5
5
  import {
6
6
  createClient
7
- } from "./hf22e0wt.js";
7
+ } from "./tbrvgw6z.js";
8
8
  import {
9
9
  OkmError2
10
10
  } from "./52szzakm.js";
@@ -331,7 +331,8 @@ async function openEndpoint(open, url, max, defaults, name, role, weight, probeM
331
331
  timer: undefined,
332
332
  current: 0,
333
333
  inflight: 0,
334
- latencyMs: null
334
+ latencyMs: null,
335
+ generation: 0
335
336
  };
336
337
  }
337
338
  async function detectPosition(endpoint) {
@@ -345,8 +346,11 @@ async function detectPosition(endpoint) {
345
346
  async function sample(handle, endpoint) {
346
347
  try {
347
348
  const sampled = await readPosition(handle, endpoint);
349
+ const reopened = endpoint.circuit === "open";
348
350
  endpoint.failures = 0;
349
351
  endpoint.circuit = "closed";
352
+ if (reopened)
353
+ endpoint.generation += 1;
350
354
  applySample(endpoint, sampled);
351
355
  } catch {
352
356
  endpoint.failures += 1;
@@ -502,7 +506,7 @@ function routePool(handle) {
502
506
  execute: async (text, params, options) => {
503
507
  if (handle.closed)
504
508
  closed();
505
- const choice = await choose(handle, text, options?.route);
509
+ const choice = await resolve(handle, text, options);
506
510
  const result = await attempt(handle, choice, (endpoint) => endpoint.pool.execute(text, params, strip(options)));
507
511
  if (choice.op === "write")
508
512
  await noteWrite(handle, primary.pool);
@@ -515,6 +519,7 @@ function routePool(handle) {
515
519
  replicaRefused();
516
520
  const reason = options?.route === "primary" ? "constraint:primary" : "primary-required";
517
521
  tell(handle, { op: "write", endpoint: primary.name, reason });
522
+ await callGuard(primary);
518
523
  const result = await primary.pool.batch(statements, strip(options));
519
524
  await noteWrite(handle, primary.pool);
520
525
  return result;
@@ -555,6 +560,14 @@ function routePool(handle) {
555
560
  endpoint.pool.cancel?.();
556
561
  };
557
562
  }
563
+ const routed = pool;
564
+ routed.okmReadOnlyUnit = (text, params, prelude, options) => readOnlyUnit(handle, text, params, prelude, options);
565
+ routed.okmReadOnlyStream = (text, params, prelude, options) => readOnlyStream(handle, text, params, prelude, options);
566
+ routed.okmEndpoints = () => handle.endpoints.map((endpoint) => ({
567
+ name: endpoint.name,
568
+ generation: endpoint.generation,
569
+ pool: endpoint.pool
570
+ }));
558
571
  return pool;
559
572
  }
560
573
  function derive(schema, handle, router, shared, route) {
@@ -601,8 +614,82 @@ function force(handle, router, route) {
601
614
  }
602
615
  if (router.cancel !== undefined)
603
616
  pool.cancel = () => router.cancel();
617
+ const unit = router.okmReadOnlyUnit;
618
+ const stream = router.okmReadOnlyStream;
619
+ const forced = pool;
620
+ if (unit !== undefined) {
621
+ forced.okmReadOnlyUnit = (text, params, prelude, options) => unit(text, params, prelude, { ...options, route });
622
+ }
623
+ if (stream !== undefined) {
624
+ forced.okmReadOnlyStream = (text, params, prelude, options) => stream(text, params, prelude, { ...options, route });
625
+ }
604
626
  return pool;
605
627
  }
628
+ async function readOnlyUnit(handle, text, params, prelude, options) {
629
+ if (handle.closed)
630
+ closed();
631
+ const choice = await resolve(handle, text, options);
632
+ return attempt(handle, choice, async (endpoint) => {
633
+ if (endpoint.pool.reserve === undefined)
634
+ replicaReserve();
635
+ const conn = await endpoint.pool.reserve();
636
+ try {
637
+ await conn.execute("begin read only");
638
+ try {
639
+ await conn.execute(prelude.text, prelude.params);
640
+ const result = await conn.execute(text, params, strip(options));
641
+ await conn.execute("commit");
642
+ return result;
643
+ } catch (error) {
644
+ await conn.execute("rollback").catch(() => {
645
+ return;
646
+ });
647
+ throw error;
648
+ }
649
+ } finally {
650
+ await conn.release();
651
+ }
652
+ });
653
+ }
654
+ function readOnlyStream(handle, text, params, prelude, options) {
655
+ return async function* () {
656
+ if (handle.closed)
657
+ closed();
658
+ const choice = await choose(handle, text, options?.route);
659
+ tell(handle, { op: choice.op, endpoint: choice.endpoint.name, reason: choice.reason });
660
+ await callGuard(choice.endpoint);
661
+ if (choice.endpoint.pool.reserve === undefined)
662
+ replicaReserve();
663
+ const conn = await choice.endpoint.pool.reserve();
664
+ try {
665
+ await conn.execute("begin read only");
666
+ try {
667
+ await conn.execute(prelude.text, prelude.params);
668
+ await conn.execute(`declare okm_rls no scroll cursor for ${text}`, params);
669
+ for (;; ) {
670
+ const chunk = await conn.execute("fetch 64 from okm_rls");
671
+ if (chunk.rows.length === 0)
672
+ break;
673
+ yield chunk.rows;
674
+ }
675
+ await conn.execute("close okm_rls");
676
+ await conn.execute("commit");
677
+ } catch (error) {
678
+ await conn.execute("rollback").catch(() => {
679
+ return;
680
+ });
681
+ throw error;
682
+ }
683
+ } finally {
684
+ await conn.release();
685
+ }
686
+ }();
687
+ }
688
+ function replicaReserve() {
689
+ throw new OkmError2("OKM1111", "Row-level security needs a driver that can reserve a connection.", {
690
+ fix: { summary: "Use a Postgres driver that supports interactive transactions." }
691
+ });
692
+ }
606
693
  function classOf(text) {
607
694
  const head = text.trimStart().toLowerCase().replace(/\s+/g, " ");
608
695
  if (/^(begin|start transaction|commit|rollback|savepoint|release|set|reset)\b/.test(head)) {
@@ -612,7 +699,7 @@ function classOf(text) {
612
699
  return "lock";
613
700
  if (/\sfor (no key update|key share|update|share)\b/.test(head))
614
701
  return "lock";
615
- if (/^(insert|update|delete|with|create|alter|drop|truncate|grant|revoke|comment|vacuum|analyze|reindex|copy)\b/.test(head)) {
702
+ if (/^(insert|update|delete|refresh|with|create|alter|drop|truncate|grant|revoke|comment|vacuum|analyze|reindex|copy)\b/.test(head)) {
616
703
  return "write";
617
704
  }
618
705
  return "read";
@@ -626,7 +713,7 @@ async function choose(handle, text, route) {
626
713
  if (primary === undefined)
627
714
  throw new OkmError2("OKM1120", "A topology needs one primary URL.");
628
715
  if (text.trimStart().toLowerCase().startsWith("select current_setting")) {
629
- return { endpoint: primary, op: "read", reason: "primary-required" };
716
+ return { endpoint: primary, op: "read", reason: "primary-required", skipGuard: true };
630
717
  }
631
718
  if (route === "replica" && op !== "read")
632
719
  replicaRefused();
@@ -673,17 +760,32 @@ async function* readStream(handle, text, route, params, fallback) {
673
760
  async function attempt(handle, choice, run) {
674
761
  tell(handle, { op: choice.op, endpoint: choice.endpoint.name, reason: choice.reason });
675
762
  try {
763
+ if (choice.skipGuard !== true)
764
+ await callGuard(choice.endpoint);
676
765
  return await tracked(choice.endpoint, choice.op === "read", () => run(choice.endpoint));
677
766
  } catch (error) {
678
- if (choice.op !== "read" || !isUnreachable(error))
767
+ if (choice.pinned === true || choice.op !== "read" || !isUnreachable(error))
679
768
  throw error;
769
+ choice.endpoint.generation += 1;
680
770
  const next = await retryOf(handle, choice);
681
771
  if (next === undefined)
682
772
  throw error;
683
773
  tell(handle, { op: "read", endpoint: next.endpoint.name, reason: next.reason });
774
+ if (choice.skipGuard !== true)
775
+ await callGuard(next.endpoint);
684
776
  return tracked(next.endpoint, true, () => run(next.endpoint));
685
777
  }
686
778
  }
779
+ function callGuard(endpoint) {
780
+ const guard = endpoint.pool.okmGuard;
781
+ if (guard === undefined)
782
+ return Promise.resolve();
783
+ return guard({
784
+ name: endpoint.name,
785
+ generation: endpoint.generation,
786
+ pool: endpoint.pool
787
+ });
788
+ }
687
789
  var CHAIN_DEPTH = 8;
688
790
  function isUnreachable(error) {
689
791
  const seen = new Set;
@@ -715,9 +817,7 @@ function unreachableNode(node) {
715
817
  return true;
716
818
  }
717
819
  }
718
- if (!(node instanceof Error))
719
- return false;
720
- return node.message.includes("ECONNREFUSED") || node.message.includes("ECONNRESET") || node.message.includes("CONNECTION_CLOSED") || node.message.includes("terminating connection") || node.message.includes("The pool is closed");
820
+ return false;
721
821
  }
722
822
  async function tracked(endpoint, time, run) {
723
823
  const replica = endpoint.role === "replica";
@@ -1007,13 +1107,45 @@ function noteFailure(endpoint) {
1007
1107
  endpoint.circuit = "open";
1008
1108
  }
1009
1109
  function strip(options) {
1010
- if (options === undefined || options.route === undefined)
1110
+ if (options === undefined)
1111
+ return;
1112
+ if (!("route" in options) && !("pin" in options))
1011
1113
  return options;
1012
- const { route: _route, ...rest } = options;
1114
+ const bag = options;
1115
+ const { route: _route, pin: _pin, ...rest } = bag;
1013
1116
  if (rest.signal === undefined && rest.timeout === undefined)
1014
1117
  return;
1015
1118
  return rest;
1016
1119
  }
1120
+ async function resolve(handle, text, options) {
1121
+ const pin = taken(options);
1122
+ if (pin?.name !== undefined)
1123
+ return held(handle, pin.name);
1124
+ const choice = await choose(handle, text, options?.route);
1125
+ if (pin !== undefined && choice.op === "read") {
1126
+ pin.name = choice.endpoint.name;
1127
+ return { ...choice, pinned: true };
1128
+ }
1129
+ return choice;
1130
+ }
1131
+ function taken(options) {
1132
+ if (options === undefined || !("pin" in options))
1133
+ return;
1134
+ const pin = options.pin;
1135
+ if (typeof pin !== "object" || pin === null)
1136
+ return;
1137
+ return pin;
1138
+ }
1139
+ function held(handle, name) {
1140
+ for (const endpoint of handle.endpoints) {
1141
+ if (endpoint.name !== name)
1142
+ continue;
1143
+ return { endpoint, op: "read", reason: `stream:${name}`, pinned: true };
1144
+ }
1145
+ throw new OkmError2("OKM1120", `stream() stays on ${name}. That target is not in this topology.`, {
1146
+ fix: { summary: "The first batch chose this target. Later batches do not move." }
1147
+ });
1148
+ }
1017
1149
  async function noteWrite(handle, runner) {
1018
1150
  if (!tracks(handle))
1019
1151
  return;
@@ -0,0 +1,45 @@
1
+ import {
2
+ isRecord,
3
+ orFail
4
+ } from "./67txcrcw.js";
5
+ import {
6
+ isOperator,
7
+ operatorName,
8
+ operatorValue
9
+ } from "./cn0n68ft.js";
10
+
11
+ // src/runtime/predicate.ts
12
+ function effectivePredicate(where) {
13
+ if (where === undefined)
14
+ return false;
15
+ if (isOperator(where)) {
16
+ const name = operatorName(where);
17
+ const value = operatorValue(where);
18
+ if (name === "or") {
19
+ if (!Array.isArray(value))
20
+ orFail(false);
21
+ if (value.length === 0)
22
+ return true;
23
+ for (const branch of value) {
24
+ if (!effectivePredicate(branch))
25
+ orFail(true);
26
+ }
27
+ return true;
28
+ }
29
+ if (name === "and") {
30
+ return Array.isArray(value) && value.some((part) => effectivePredicate(part));
31
+ }
32
+ if (name === "not")
33
+ return effectivePredicate(value);
34
+ return true;
35
+ }
36
+ if (!isRecord(where))
37
+ return true;
38
+ for (const key of Object.keys(where)) {
39
+ if (where[key] !== undefined)
40
+ return true;
41
+ }
42
+ return false;
43
+ }
44
+
45
+ export { effectivePredicate };
@@ -0,0 +1,18 @@
1
+ // src/dialects/pg/ops/bucket.ts
2
+ var RUNTIME = Symbol.for("okmodel.bucket");
3
+ function bucket2(column, unit, options) {
4
+ const spec = options ?? { as: undefined, tz: undefined, weekStart: undefined };
5
+ return {
6
+ [RUNTIME]: true,
7
+ column,
8
+ unit,
9
+ as: spec.as,
10
+ tz: spec.tz,
11
+ weekStart: spec.weekStart
12
+ };
13
+ }
14
+ function isBucket(value) {
15
+ return typeof value === "object" && value !== null && RUNTIME in value;
16
+ }
17
+
18
+ export { bucket2, isBucket };