okmodel 0.7.0 → 0.9.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 (258) hide show
  1. package/README.md +14 -0
  2. package/dist/adapters/capabilities.d.ts +32 -0
  3. package/dist/adapters/failure.d.ts +1 -1
  4. package/dist/adapters/pg/bun-sql.d.ts +53 -0
  5. package/dist/adapters/pg/bunsql-extra.d.ts +1 -1
  6. package/dist/adapters/pg/bunsql.d.ts +1 -1
  7. package/dist/adapters/pg/neon-extra.d.ts +18 -0
  8. package/dist/adapters/pg/neon.d.ts +104 -0
  9. package/dist/adapters/pg/no-inflight-timeout.d.ts +1 -1
  10. package/dist/contracts/error.d.ts +1 -1
  11. package/dist/dialects/pg/ext/btree-gin.d.ts +26 -0
  12. package/dist/dialects/pg/ext/btree-gin.js +20 -0
  13. package/dist/dialects/pg/ext/btree-gist.d.ts +26 -0
  14. package/dist/dialects/pg/ext/btree-gist.js +20 -0
  15. package/dist/dialects/pg/ext/call.d.ts +53 -0
  16. package/dist/dialects/pg/ext/citext.js +1 -1
  17. package/dist/dialects/pg/ext/feature.d.ts +26 -0
  18. package/dist/dialects/pg/ext/fuzzystrmatch.d.ts +34 -0
  19. package/dist/dialects/pg/ext/fuzzystrmatch.js +33 -0
  20. package/dist/dialects/pg/ext/hstore.d.ts +41 -0
  21. package/dist/dialects/pg/ext/hstore.js +29 -0
  22. package/dist/dialects/pg/ext/index.d.ts +56 -0
  23. package/dist/dialects/pg/ext/index.js +1 -1
  24. package/dist/dialects/pg/ext/ltree.d.ts +41 -0
  25. package/dist/dialects/pg/ext/ltree.js +29 -0
  26. package/dist/dialects/pg/ext/pg-trgm.js +4 -13
  27. package/dist/dialects/pg/ext/pgcrypto.d.ts +33 -0
  28. package/dist/dialects/pg/ext/pgcrypto.js +33 -0
  29. package/dist/dialects/pg/ext/unaccent.d.ts +26 -0
  30. package/dist/dialects/pg/ext/unaccent.js +21 -0
  31. package/dist/dialects/pg/ext/using.d.ts +23 -0
  32. package/dist/dialects/pg/ext/versions.d.ts +36 -0
  33. package/dist/dialects/pg/external.d.ts +19 -0
  34. package/dist/dialects/pg/fn/index.d.ts +39 -1
  35. package/dist/dialects/pg/fn/index.js +118 -26
  36. package/dist/dialects/pg/index.d.ts +3 -0
  37. package/dist/dialects/pg/index.js +394 -14
  38. package/dist/dialects/pg/introspect.d.ts +11 -0
  39. package/dist/dialects/pg/operators.d.ts +1 -1
  40. package/dist/dialects/pg/ops/fragment.d.ts +119 -0
  41. package/dist/dialects/pg/ops/lane.d.ts +49 -0
  42. package/dist/dialects/pg/view/client.d.ts +14 -6
  43. package/dist/dialects/pg/view/index.d.ts +73 -1
  44. package/dist/dialects/pg/view/index.js +156 -5
  45. package/dist/dialects/pg/view/scratch.d.ts +11 -5
  46. package/dist/runtime/artifact-file.d.ts +22 -0
  47. package/dist/runtime/client.d.ts +10 -0
  48. package/dist/runtime/drift-detail.d.ts +2 -1
  49. package/dist/runtime/drift.d.ts +3 -10
  50. package/dist/runtime/otel.d.ts +105 -0
  51. package/dist/runtime/otel.js +97 -0
  52. package/dist/runtime/pg/bun.js +4 -4
  53. package/dist/runtime/pg/neon.d.ts +38 -0
  54. package/dist/runtime/pg/neon.js +64 -0
  55. package/dist/runtime/pg/pg.js +4 -4
  56. package/dist/runtime/pg/pglite.js +4 -4
  57. package/dist/runtime/pg/postgresjs.js +4 -4
  58. package/dist/runtime/plan.d.ts +1 -1
  59. package/dist/runtime/server-check.d.ts +36 -6
  60. package/dist/runtime/sql-fragment.d.ts +43 -0
  61. package/dist/runtime/tenancy/index.js +10 -10
  62. package/dist/runtime/topology.d.ts +1 -1
  63. package/dist/runtime/traits/index.js +16 -17
  64. package/dist/runtime/types.d.ts +15 -1
  65. package/dist/runtime/validate/index.js +4 -4
  66. package/dist/shared/{81ebdx9p.js → 0j6qe91y.js} +4 -4
  67. package/dist/shared/{qq3899ht.js → 12z7aznj.js} +243 -14
  68. package/dist/shared/{gzaveazc.js → 1d9q9cv2.js} +29 -2
  69. package/dist/shared/{w79hsckn.js → 1mxm04fy.js} +5 -5
  70. package/dist/shared/{7k9grvf6.js → 2szgv6yj.js} +5 -5
  71. package/dist/shared/{gs74dbj5.js → 2zdxkt7b.js} +933 -111
  72. package/dist/shared/{m6p12kbr.js → 3456242y.js} +23 -23
  73. package/dist/shared/3bb7wsqt.js +99 -0
  74. package/dist/shared/{y6njgyda.js → 3t6kw83m.js} +2 -2
  75. package/dist/shared/{xhfsfkgk.js → 4vbf83xx.js} +9 -2
  76. package/dist/shared/{pt94kz7f.js → 5kyb3tqx.js} +1 -1
  77. package/dist/shared/61bmrmhr.js +35 -0
  78. package/dist/shared/{x933k9a3.js → 62b603gr.js} +10 -10
  79. package/dist/shared/{jgqj11w4.js → 6f0z3hzh.js} +4 -0
  80. package/dist/shared/{tsvww22q.js → 6nae0spm.js} +156 -95
  81. package/dist/shared/{mtaszv5a.js → 71n0jmhc.js} +102 -4
  82. package/dist/shared/{5e3tf63j.js → 760ppr76.js} +14 -6
  83. package/dist/shared/7ba5h6pb.js +17 -0
  84. package/dist/shared/{sfz1ksn6.js → 8jesqsrk.js} +2 -2
  85. package/dist/shared/{5v3xf80k.js → 8n688yjg.js} +1 -1
  86. package/dist/shared/{rs73fca9.js → 8qzkd9f3.js} +6 -6
  87. package/dist/shared/{ax7qv55k.js → 90sg0w5a.js} +202 -25
  88. package/dist/shared/{zm0ptry0.js → 99qp9vtg.js} +1 -1
  89. package/dist/shared/{xnxsed62.js → 9dr2qnx7.js} +14 -14
  90. package/dist/shared/{1w508jbr.js → 9dtgn1z2.js} +21 -17
  91. package/dist/shared/{h15jx9bc.js → 9dvh1dr6.js} +1 -1
  92. package/dist/shared/{q5c6ngn3.js → a62rb2da.js} +1 -1
  93. package/dist/shared/{te5as7z7.js → aksb89mn.js} +3 -3
  94. package/dist/shared/{bz84tkzm.js → bphrgwz9.js} +2 -2
  95. package/dist/shared/bsek72fg.js +225 -0
  96. package/dist/shared/{7mnme5tn.js → c3mvnq7a.js} +1 -1
  97. package/dist/shared/{p6qc5gcp.js → ccsmhv9r.js} +25 -7
  98. package/dist/shared/{pbp1ss85.js → cexb7z6b.js} +2 -2
  99. package/dist/shared/{ab9gmbwy.js → cf8zdz5p.js} +5 -5
  100. package/dist/shared/{jvd0mqag.js → ckz05z15.js} +5 -5
  101. package/dist/shared/{dk8v79p3.js → d5nmj7vv.js} +1 -1
  102. package/dist/shared/{14vx95q4.js → dd5pmnky.js} +7 -7
  103. package/dist/shared/{vb6qfyve.js → ds229x4r.js} +2 -2
  104. package/dist/shared/{1qa0qajz.js → e275q7sd.js} +1 -1
  105. package/dist/shared/{zp9fmend.js → ebhz8vh6.js} +4 -4
  106. package/dist/shared/{hyh73qk6.js → ecn8mfdq.js} +5 -5
  107. package/dist/shared/{je4axnm8.js → fdsz3w80.js} +2 -2
  108. package/dist/shared/{9s0ypp87.js → fk9vef30.js} +85 -74
  109. package/dist/shared/{g6evj1j3.js → g89fmz5s.js} +1 -1
  110. package/dist/shared/{aytqh5y2.js → ghqzdp94.js} +2 -2
  111. package/dist/shared/h9811jjm.js +17 -0
  112. package/dist/shared/{dkxgzaqd.js → hqpxf4a4.js} +1 -1
  113. package/dist/shared/{8skf0cam.js → jpybhf98.js} +1 -1
  114. package/dist/shared/{67txcrcw.js → kgwp4jdd.js} +4 -4
  115. package/dist/shared/kzgrnckd.js +29 -0
  116. package/dist/shared/mazpbcdj.js +233 -0
  117. package/dist/shared/{kb2sdhfb.js → mfvx6kjx.js} +17 -1
  118. package/dist/shared/{svncdeh5.js → mgaq2sd3.js} +11 -8
  119. package/dist/shared/{5mtf6cj3.js → mvmm3xpb.js} +5 -2
  120. package/dist/shared/{b5pftnn2.js → myk8fswx.js} +66 -21
  121. package/dist/shared/{2shdbn8q.js → n306gqkd.js} +9 -9
  122. package/dist/shared/{n816peh1.js → n454kcnw.js} +1 -1
  123. package/dist/shared/n5whk3mt.js +665 -0
  124. package/dist/shared/n7kg8mmx.js +61 -0
  125. package/dist/shared/{vqrjpcqn.js → nmnsgj7x.js} +1 -1
  126. package/dist/shared/{tbrvgw6z.js → npdftanb.js} +19 -26
  127. package/dist/shared/{ek2wt2dz.js → pg4ezgyf.js} +11 -30
  128. package/dist/shared/{5fe3kbsh.js → pgsak1yw.js} +1 -1
  129. package/dist/shared/{bm22fvzr.js → qfw14tna.js} +2 -2
  130. package/dist/shared/{wa07eas9.js → qvwwp1mp.js} +62 -41
  131. package/dist/shared/rbxt795q.js +1766 -0
  132. package/dist/shared/{xjk7hnn5.js → s7he5e5d.js} +2 -2
  133. package/dist/shared/{w72kgg4q.js → ss2kv2rs.js} +6 -6
  134. package/dist/shared/stfa9xcw.js +450 -0
  135. package/dist/shared/{3v7t1yrv.js → th3be82n.js} +2 -2
  136. package/dist/shared/{79dt02vc.js → tr64w5p0.js} +1 -1
  137. package/dist/shared/{cn957f8d.js → tw1nvvfj.js} +3 -3
  138. package/dist/shared/{ed0b8k4s.js → typ3vy7t.js} +1 -1
  139. package/dist/shared/{9628a9xt.js → vk7rag46.js} +1 -1
  140. package/dist/shared/{ze4hxsmy.js → w9ejzaqf.js} +1 -1
  141. package/dist/shared/{vczs8gd4.js → wvtjg5m3.js} +2 -2
  142. package/dist/shared/{9y3mmmh1.js → xfhe73sb.js} +1 -1
  143. package/dist/shared/xqacq37x.js +61 -0
  144. package/dist/shared/{1est4ezg.js → xtsydxjg.js} +3 -3
  145. package/dist/shared/xv8qj97g.js +96 -0
  146. package/dist/shared/{bg3rym3g.js → xwhdcrm4.js} +2 -2
  147. package/dist/shared/{kr0jzbw2.js → yvabyg9k.js} +7 -7
  148. package/dist/shared/yybhre6n.js +136 -0
  149. package/dist/shared/zdqf8k5d.js +8 -0
  150. package/dist/shared/{jznfax0y.js → zq1he88x.js} +4 -4
  151. package/dist/tooling/cli.js +108 -23
  152. package/dist/tooling/driver-run.js +4654 -0
  153. package/dist/tooling/driver-test.d.ts +36 -0
  154. package/dist/tooling/github/comment.d.ts +50 -0
  155. package/dist/tooling/github/post-comment.d.ts +17 -0
  156. package/dist/tooling/migrate/commands.d.ts +3 -2
  157. package/dist/tooling/migrate/config.d.ts +13 -0
  158. package/dist/tooling/migrate/export.d.ts +165 -0
  159. package/dist/tooling/migrate/ext-surface.d.ts +80 -0
  160. package/dist/tooling/migrate/ext.d.ts +31 -4
  161. package/dist/tooling/migrate/import-drizzle.d.ts +40 -0
  162. package/dist/tooling/migrate/index.d.ts +3 -0
  163. package/dist/tooling/migrate/index.js +55 -10
  164. package/dist/tooling/migrate/lint-rules.d.ts +18 -1
  165. package/dist/tooling/migrate/lint.d.ts +14 -1
  166. package/dist/tooling/migrate/place.d.ts +18 -0
  167. package/dist/tooling/migrate/plan-json.d.ts +65 -0
  168. package/dist/tooling/migrate/project.d.ts +13 -1
  169. package/dist/tooling/migrate/pull-emit.d.ts +98 -0
  170. package/dist/tooling/migrate/pull-routines.d.ts +126 -0
  171. package/dist/tooling/migrate/pull-verify.d.ts +26 -0
  172. package/dist/tooling/migrate/pull.d.ts +62 -0
  173. package/dist/tooling/migrate/reprint.d.ts +32 -0
  174. package/dist/tooling/migrate/sandbox.d.ts +8 -2
  175. package/dist/tooling/migrate/schema-api.d.ts +43 -0
  176. package/dist/tooling/migrate/sql-lex.d.ts +18 -0
  177. package/dist/tooling/shared/04svnxxj.js +344 -0
  178. package/dist/tooling/shared/0j0jtse1.js +13 -0
  179. package/dist/tooling/shared/0r05zhyq.js +618 -0
  180. package/dist/tooling/shared/1800eymw.js +49 -0
  181. package/dist/tooling/shared/35zemrmf.js +12 -0
  182. package/dist/tooling/shared/3rmaz3pq.js +140 -0
  183. package/dist/tooling/shared/3yn79q5m.js +12 -0
  184. package/dist/tooling/shared/54tqkxja.js +51 -0
  185. package/dist/tooling/shared/57btdkgm.js +209 -0
  186. package/dist/tooling/shared/5bmksndh.js +118 -0
  187. package/dist/tooling/shared/5p8km5jb.js +38 -0
  188. package/dist/tooling/shared/5x3pf400.js +185 -0
  189. package/dist/tooling/shared/5zrzffnk.js +6 -0
  190. package/dist/tooling/shared/62fxpc1p.js +332 -0
  191. package/dist/tooling/shared/68tf18v4.js +20 -0
  192. package/dist/tooling/shared/6vk28rjp.js +98 -0
  193. package/dist/tooling/shared/76pv6qff.js +9 -0
  194. package/dist/tooling/shared/7aews1zg.js +311 -0
  195. package/dist/tooling/shared/7b3xp9gc.js +195 -0
  196. package/dist/tooling/shared/7kms6fvj.js +20 -0
  197. package/dist/tooling/shared/7pxncnzs.js +40 -0
  198. package/dist/tooling/shared/829fjxyw.js +198 -0
  199. package/dist/tooling/shared/8m5znpdt.js +66 -0
  200. package/dist/tooling/shared/8yr7ges1.js +211 -0
  201. package/dist/tooling/shared/96ps4g1z.js +32 -0
  202. package/dist/tooling/shared/9qr9rkbh.js +143 -0
  203. package/dist/tooling/shared/9sfbtwsk.js +49 -0
  204. package/dist/tooling/shared/9wsnsvbf.js +10 -0
  205. package/dist/tooling/shared/a2xe04bm.js +18 -0
  206. package/dist/tooling/shared/a4pzghha.js +20 -0
  207. package/dist/tooling/shared/am04v8na.js +233 -0
  208. package/dist/tooling/shared/asgr07mp.js +194 -0
  209. package/dist/tooling/shared/c0xevn1h.js +312 -0
  210. package/dist/tooling/shared/dhatassp.js +134 -0
  211. package/dist/tooling/shared/ezqn88nj.js +1696 -0
  212. package/dist/tooling/shared/f77nnfqy.js +231 -0
  213. package/dist/tooling/shared/fh07z1dt.js +15 -0
  214. package/dist/tooling/shared/gqwevek4.js +81 -0
  215. package/dist/tooling/shared/hfq3n3bd.js +4116 -0
  216. package/dist/tooling/shared/htnaj77v.js +17 -0
  217. package/dist/tooling/shared/j664qjrt.js +64 -0
  218. package/dist/tooling/shared/jpgmjsx3.js +38 -0
  219. package/dist/tooling/shared/jr2nmr81.js +497 -0
  220. package/dist/tooling/shared/jsjjz875.js +139 -0
  221. package/dist/tooling/shared/kbqnrs1x.js +1425 -0
  222. package/dist/tooling/shared/kz715295.js +94 -0
  223. package/dist/tooling/shared/m3xt8b1h.js +22 -0
  224. package/dist/tooling/shared/m4hhb8aa.js +9 -0
  225. package/dist/tooling/shared/mw5npye0.js +16 -0
  226. package/dist/tooling/shared/n4jqhpqz.js +27 -0
  227. package/dist/tooling/shared/nxq95eb6.js +42 -0
  228. package/dist/tooling/shared/qartgazh.js +70 -0
  229. package/dist/tooling/shared/rcywj2w8.js +190 -0
  230. package/dist/tooling/shared/s6qha57m.js +16 -0
  231. package/dist/tooling/shared/shjs0379.js +150 -0
  232. package/dist/tooling/shared/sjwkt1pg.js +16 -0
  233. package/dist/tooling/shared/stebqyyd.js +401 -0
  234. package/dist/tooling/shared/t4xveemq.js +250 -0
  235. package/dist/tooling/shared/t6zjeake.js +261 -0
  236. package/dist/tooling/shared/tcwpjvxe.js +9 -0
  237. package/dist/tooling/shared/tpssp7ac.js +160 -0
  238. package/dist/tooling/shared/v6j1ep6w.js +42 -0
  239. package/dist/tooling/shared/vwr2y833.js +270 -0
  240. package/dist/tooling/shared/w17sj05d.js +7 -0
  241. package/dist/tooling/shared/w5d89rb4.js +131 -0
  242. package/dist/tooling/shared/wbwq1jsa.js +157 -0
  243. package/dist/tooling/shared/wezxf3h8.js +17 -0
  244. package/dist/tooling/shared/yb4kd19s.js +12 -0
  245. package/dist/tooling/shared/ybdew6j7.js +294 -0
  246. package/dist/tooling/shared/yfedr1zb.js +6 -0
  247. package/dist/tooling/shared/yhp829bx.js +177 -0
  248. package/dist/tooling/shared/ytk1tjs0.js +604 -0
  249. package/dist/tooling/shared/zs9aymcp.js +210 -0
  250. package/dist/tooling/shared/zx3v9phv.js +8 -0
  251. package/dist/tooling/testing/index.js +3 -3
  252. package/package.json +50 -9
  253. package/dist/shared/ktz42yez.js +0 -113
  254. package/dist/shared/ttsqgp4p.js +0 -71
  255. package/dist/shared/y34a8714.js +0 -150
  256. package/dist/shared/zy8r7qvz.js +0 -116
  257. package/dist/shared/{ywr8hmey.js → 606g0nbf.js} +3 -3
  258. package/dist/shared/{rpycarcr.js → gcefca6p.js} +4 -4
@@ -0,0 +1,1425 @@
1
+ import {
2
+ createClient
3
+ } from "./jr2nmr81.js";
4
+ import {
5
+ OkmError,
6
+ isConnectionSqlstate,
7
+ isConnectionErrno
8
+ } from "./hfq3n3bd.js";
9
+
10
+ // src/runtime/topology.ts
11
+ var CIRCUIT_AT = 2;
12
+ var BACKOFF_CAP_MS = 30000;
13
+ var DEFAULT_PROBE_MS = 1000;
14
+ var CAPABILITY_SQL = "select case when to_regprocedure('pg_last_wal_replay_lsn()') is null then false when to_regprocedure('pg_current_wal_insert_lsn()') is null then false else has_function_privilege(to_regprocedure('pg_last_wal_replay_lsn()'), 'execute') and has_function_privilege(to_regprocedure('pg_current_wal_insert_lsn()'), 'execute') end";
15
+ var HEALTH_SQL = "select 1, pg_last_wal_replay_lsn()::text";
16
+ var HEALTH_TIME_SQL = "select 1, pg_last_wal_replay_lsn()::text, pg_last_xact_replay_timestamp()::text";
17
+ var INSERT_LSN_SQL = "select pg_current_wal_insert_lsn()::text";
18
+ var PING_SQL = "select 1";
19
+ var SELECT_NAMES = ["weighted", "roundRobin", "leastConnections", "latencyAware"];
20
+ var LATENCY_ALPHA = 0.2;
21
+ var ROUTING_KEYS = "probe, select, fallback, consistency, maxLag";
22
+ var handles = new WeakMap;
23
+ async function connectTopology(target, options, open) {
24
+ if (Object.prototype.hasOwnProperty.call(options.schema.model, "using")) {
25
+ throw new OkmError("OKM1120", 'Table "using" collides with client.using(). Rename the table.');
26
+ }
27
+ refuseRouting(options.routing);
28
+ const consistency = readConsistency(options.routing?.consistency);
29
+ const maxLag = readMaxLag(options.routing?.maxLag);
30
+ const select = readSelect(options.routing?.select);
31
+ const primaryUrl = readPrimary(target);
32
+ const replicas = readReplicas(target);
33
+ const probeMs = readProbe(options.routing?.probe);
34
+ const defaults = driverFields(options);
35
+ const opened = [];
36
+ try {
37
+ opened.push(await openEndpoint(open, primaryUrl, defaults.max, defaults, "primary", "primary", 1, probeMs));
38
+ for (let index = 0;index < replicas.length; index += 1) {
39
+ const replica = replicas[index];
40
+ if (replica === undefined)
41
+ continue;
42
+ const name = replica.name ?? `replica-${String(index + 1)}`;
43
+ opened.push(await openEndpoint(open, replica.url, replica.max ?? defaults.max, defaults, name, "replica", replica.weight, probeMs));
44
+ }
45
+ assertNames(opened);
46
+ await Promise.all(opened.map((endpoint) => detectPosition(endpoint)));
47
+ const replicaEndpoints = opened.filter((endpoint) => endpoint.role === "replica");
48
+ const handle = {
49
+ closed: false,
50
+ probeMs,
51
+ consistency,
52
+ maxLag,
53
+ hasReplicas: replicaEndpoints.length > 0,
54
+ watermark: undefined,
55
+ positionUnknown: false,
56
+ unknownEpoch: 0,
57
+ primaryLsn: undefined,
58
+ fallback: readFallback(options.routing?.fallback),
59
+ select,
60
+ rrLast: undefined,
61
+ scratch: [],
62
+ onRoute: options.onRoute,
63
+ replicaState: options.replicaState,
64
+ endpoints: opened,
65
+ closing: undefined
66
+ };
67
+ await Promise.all([
68
+ ...replicaEndpoints.map((endpoint) => sample(handle, endpoint)),
69
+ samplePrimary(handle)
70
+ ]);
71
+ for (const endpoint of replicaEndpoints) {
72
+ endpoint.nextDelayMs = delayFor(probeMs, endpoint);
73
+ schedule(handle, endpoint);
74
+ }
75
+ schedulePrimary(handle);
76
+ const primary = opened[0];
77
+ if (primary === undefined) {
78
+ throw new OkmError("OKM1120", "A topology needs one primary URL.");
79
+ }
80
+ const shared = {
81
+ http: options.errors?.http,
82
+ includeValues: options.errors?.includeValues,
83
+ logger: options.logger,
84
+ signal: options.signal,
85
+ timeout: options.timeout,
86
+ timeouts: options.timeouts,
87
+ hookm: options.hookm,
88
+ catalog: options.catalog,
89
+ catalogDir: options.catalogDir,
90
+ requireMeta: options.requireMeta,
91
+ generators: options.generators
92
+ };
93
+ const router = routePool(handle);
94
+ const client = createClient(options.schema, router, { ownsPool: true, ...shared });
95
+ const routed = client;
96
+ routed.using = (route) => derive(options.schema, handle, router, shared, route);
97
+ return instrument(routed, handle);
98
+ } catch (error) {
99
+ for (const endpoint of opened) {
100
+ if (endpoint.timer !== undefined)
101
+ clearTimeout(endpoint.timer);
102
+ endpoint.timer = undefined;
103
+ }
104
+ await Promise.all(opened.map((endpoint) => endpoint.pool.close()));
105
+ throw error;
106
+ }
107
+ }
108
+ function instrument(client, handle, forced) {
109
+ const proxy = new Proxy(client, {
110
+ get(target, key, receiver) {
111
+ if (key === "using")
112
+ return Reflect.get(target, key, receiver);
113
+ const value = Reflect.get(target, key, receiver);
114
+ if (typeof value !== "function")
115
+ return retarget(value, handle, forced);
116
+ if (key === "tx") {
117
+ return (...args) => {
118
+ const index = typeof args[0] === "function" ? 0 : 1;
119
+ const fn = args[index];
120
+ if (typeof fn === "function") {
121
+ args[index] = (inner, ...rest) => fn(typeof inner === "object" && inner !== null ? instrument(inner, handle, "primary") : inner, ...rest);
122
+ }
123
+ return value.apply(target, args);
124
+ };
125
+ }
126
+ return (...args) => retarget(value.apply(target, args), handle, forced);
127
+ }
128
+ });
129
+ handles.set(proxy, handle);
130
+ return proxy;
131
+ }
132
+ function wrapTable(table, handle, forced) {
133
+ return new Proxy(table, {
134
+ get(target, key, receiver) {
135
+ const value = Reflect.get(target, key, receiver);
136
+ if (typeof value !== "function")
137
+ return value;
138
+ return (...args) => {
139
+ const route = forced ?? readCallRoute(args[0]);
140
+ return retarget(value.apply(target, args), handle, route);
141
+ };
142
+ }
143
+ });
144
+ }
145
+ function retarget(value, handle, route) {
146
+ if (typeof value !== "object" || value === null)
147
+ return value;
148
+ if (typeof Reflect.get(value, "inspect") === "function" && typeof Reflect.get(value, "then") === "function") {
149
+ return showReason(value, handle, route);
150
+ }
151
+ if (typeof Reflect.get(value, "find") === "function")
152
+ return wrapTable(value, handle, route);
153
+ if (typeof Reflect.get(value, "table") === "function")
154
+ return instrument(value, handle, route);
155
+ return value;
156
+ }
157
+ function showReason(query, handle, route) {
158
+ return new Proxy(query, {
159
+ get(target, key, receiver) {
160
+ const value = Reflect.get(target, key, receiver);
161
+ if (key === "inspect" && typeof value === "function") {
162
+ const inspect = value;
163
+ return () => Promise.resolve(inspect.call(target)).then(async (view) => {
164
+ if (typeof view !== "object" || view === null)
165
+ return view;
166
+ const sql = Reflect.get(view, "sql");
167
+ const text = typeof sql === "object" && sql !== null && typeof Reflect.get(sql, "text") === "string" ? Reflect.get(sql, "text") : "";
168
+ const choice = await preview(handle, text, route);
169
+ return {
170
+ ...view,
171
+ routing: {
172
+ endpoint: choice.endpoint.name,
173
+ role: choice.endpoint.role,
174
+ reason: choice.reason
175
+ }
176
+ };
177
+ });
178
+ }
179
+ if ((key === "all" || key === "required") && typeof value === "function") {
180
+ const method = value;
181
+ return (...args) => retarget(method.apply(target, args), handle, route);
182
+ }
183
+ return typeof value === "function" ? value.bind(target) : value;
184
+ }
185
+ });
186
+ }
187
+ function readCallRoute(value) {
188
+ if (typeof value !== "object" || value === null)
189
+ return;
190
+ const route = Reflect.get(value, "route");
191
+ return route === "primary" || route === "replica" ? route : undefined;
192
+ }
193
+ async function preview(handle, text, route) {
194
+ const currents = handle.endpoints.map((endpoint) => endpoint.current);
195
+ const rrLast = handle.rrLast;
196
+ try {
197
+ return await choose(handle, text, route);
198
+ } finally {
199
+ for (let index = 0;index < handle.endpoints.length; index += 1) {
200
+ const endpoint = handle.endpoints[index];
201
+ if (endpoint !== undefined)
202
+ endpoint.current = currents[index] ?? 0;
203
+ }
204
+ handle.rrLast = rrLast;
205
+ }
206
+ }
207
+ function refuseRouting(routing) {
208
+ if (routing === undefined)
209
+ return;
210
+ if (typeof routing !== "object" || routing === null || Array.isArray(routing)) {
211
+ throw new OkmError("OKM1120", `routing must be an object. Accepted keys: ${ROUTING_KEYS}.`);
212
+ }
213
+ for (const key of Object.keys(routing)) {
214
+ if (key === "probe" || key === "fallback" || key === "select" || key === "consistency" || key === "maxLag") {
215
+ continue;
216
+ }
217
+ throw new OkmError("OKM1120", `routing.${key} is not a routing option. Accepted keys: ${ROUTING_KEYS}.`);
218
+ }
219
+ }
220
+ function readSelect(value) {
221
+ if (value === undefined)
222
+ return "weighted";
223
+ if (typeof value === "function")
224
+ return value;
225
+ if (typeof value === "string" && isSelectName(value))
226
+ return value;
227
+ throw new OkmError("OKM1120", `routing.select must be "${SELECT_NAMES.join('", "')}", or a function.`);
228
+ }
229
+ function isSelectName(value) {
230
+ return SELECT_NAMES.includes(value);
231
+ }
232
+ function readConsistency(value) {
233
+ if (value === undefined || value === "session")
234
+ return "session";
235
+ if (value === "eventual")
236
+ return "eventual";
237
+ throw new OkmError("OKM1120", `routing.consistency must be "session" or "eventual". ${shown(value)} is not one of those.`);
238
+ }
239
+ function readMaxLag(value) {
240
+ if (value === undefined)
241
+ return;
242
+ if (typeof value !== "string") {
243
+ throw new OkmError("OKM1120", maxLagMessage(shown(value)));
244
+ }
245
+ const text = value.trim();
246
+ const time = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(text);
247
+ if (time !== null) {
248
+ const amount = Number(time[1]);
249
+ const unit = time[2];
250
+ if (!Number.isFinite(amount) || amount < 0 || unit === undefined) {
251
+ throw new OkmError("OKM1120", maxLagMessage(value));
252
+ }
253
+ const ms = unit === "ms" ? amount : unit === "s" ? amount * 1000 : amount * 60000;
254
+ return { kind: "time", ms };
255
+ }
256
+ const size = /^(\d+(?:\.\d+)?)(B|KB|MB|GB)$/.exec(text);
257
+ if (size !== null) {
258
+ const amount = Number(size[1]);
259
+ const unit = size[2];
260
+ if (!Number.isFinite(amount) || amount < 0 || unit === undefined) {
261
+ throw new OkmError("OKM1120", maxLagMessage(value));
262
+ }
263
+ return { kind: "bytes", bytes: scaleBytes(amount, unit) };
264
+ }
265
+ throw new OkmError("OKM1120", maxLagMessage(value));
266
+ }
267
+ function maxLagMessage(value) {
268
+ return `routing.maxLag must be a duration ("5s", "500ms", "2m") or a size ("16MB", "512KB", "1GB", "4096B"). ${value} is not one of those forms.`;
269
+ }
270
+ function scaleBytes(amount, unit) {
271
+ const scale = unit === "B" ? 1n : unit === "KB" ? 1024n : unit === "MB" ? 1048576n : 1073741824n;
272
+ if (Number.isInteger(amount))
273
+ return BigInt(amount) * scale;
274
+ return BigInt(Math.trunc(amount * Number(scale)));
275
+ }
276
+ function readPrimary(target) {
277
+ const value = fields(target);
278
+ for (const key of Object.keys(value)) {
279
+ if (key !== "primary" && key !== "replicas") {
280
+ throw new OkmError("OKM1120", `Topology key ${key} is not accepted. Accepted keys: primary, replicas.`);
281
+ }
282
+ }
283
+ const primary = value.primary;
284
+ if (typeof primary !== "string" || primary.length === 0) {
285
+ throw new OkmError("OKM1120", "A topology needs one primary URL. Accepted shape: { primary, replicas }.");
286
+ }
287
+ return primary;
288
+ }
289
+ function readReplicas(target) {
290
+ const value = fields(target).replicas;
291
+ if (value === undefined)
292
+ return [];
293
+ if (!Array.isArray(value)) {
294
+ throw new OkmError("OKM1120", "replicas must be an array of URLs or { url, weight, name, pool }.");
295
+ }
296
+ return value.map((entry, index) => readReplica(entry, index));
297
+ }
298
+ function readReplica(entry, index) {
299
+ if (typeof entry === "string") {
300
+ if (entry.length === 0) {
301
+ throw new OkmError("OKM1120", `replicas[${String(index)}] is an empty URL.`);
302
+ }
303
+ return { url: entry, weight: 1, name: undefined, max: undefined };
304
+ }
305
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
306
+ throw new OkmError("OKM1120", `replicas[${String(index)}] must be a URL or { url, weight, name, pool }.`);
307
+ }
308
+ const value = fields(entry);
309
+ for (const key of Object.keys(value)) {
310
+ if (key !== "url" && key !== "weight" && key !== "name" && key !== "pool") {
311
+ throw new OkmError("OKM1120", `replicas[${String(index)}].${key} is not accepted. Accepted keys: url, weight, name, pool.`);
312
+ }
313
+ }
314
+ const url = value.url;
315
+ if (typeof url !== "string" || url.length === 0) {
316
+ throw new OkmError("OKM1120", `replicas[${String(index)}] needs a url.`);
317
+ }
318
+ return {
319
+ url,
320
+ weight: readWeight(value.weight, index),
321
+ name: readName(value.name, index),
322
+ max: readPoolMax(value.pool, index)
323
+ };
324
+ }
325
+ function readWeight(value, index) {
326
+ if (value === undefined)
327
+ return 1;
328
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
329
+ throw new OkmError("OKM1120", `replicas[${String(index)}].weight must be a positive number.`);
330
+ }
331
+ return value;
332
+ }
333
+ function readName(value, index) {
334
+ if (value === undefined)
335
+ return;
336
+ if (typeof value !== "string" || value.length === 0) {
337
+ throw new OkmError("OKM1120", `replicas[${String(index)}].name must be a non-empty string.`);
338
+ }
339
+ return value;
340
+ }
341
+ function readPoolMax(value, index) {
342
+ if (value === undefined)
343
+ return;
344
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
345
+ throw new OkmError("OKM1120", `replicas[${String(index)}].pool must be { max }.`);
346
+ }
347
+ const pool = fields(value);
348
+ for (const key of Object.keys(pool)) {
349
+ if (key !== "max") {
350
+ throw new OkmError("OKM1120", `replicas[${String(index)}].pool.${key} is not accepted. Accepted key: max.`);
351
+ }
352
+ }
353
+ const max = pool.max;
354
+ if (typeof max !== "number" || !Number.isInteger(max) || max < 1) {
355
+ throw new OkmError("OKM1120", `replicas[${String(index)}].pool.max must be a positive integer.`);
356
+ }
357
+ return max;
358
+ }
359
+ function readProbe(value) {
360
+ if (value === undefined)
361
+ return DEFAULT_PROBE_MS;
362
+ if (typeof value === "number") {
363
+ if (!Number.isFinite(value) || value <= 0) {
364
+ throw new OkmError("OKM1120", "routing.probe must be a positive number of milliseconds.");
365
+ }
366
+ return value;
367
+ }
368
+ const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(value.trim());
369
+ const amount = match?.[1];
370
+ const unit = match?.[2];
371
+ if (amount === undefined || unit === undefined) {
372
+ throw new OkmError("OKM1120", 'routing.probe must be a number of milliseconds, or "1s", "500ms", or "1m".');
373
+ }
374
+ const parsed = Number(amount);
375
+ if (!Number.isFinite(parsed) || parsed <= 0) {
376
+ throw new OkmError("OKM1120", "routing.probe must be positive.");
377
+ }
378
+ if (unit === "ms")
379
+ return parsed;
380
+ if (unit === "s")
381
+ return parsed * 1000;
382
+ return parsed * 60000;
383
+ }
384
+ function assertNames(endpoints) {
385
+ const seen = new Set;
386
+ for (const endpoint of endpoints) {
387
+ if (seen.has(endpoint.name)) {
388
+ throw new OkmError("OKM1120", `Endpoint name ${endpoint.name} is used twice.`);
389
+ }
390
+ seen.add(endpoint.name);
391
+ }
392
+ }
393
+ function driverFields(options) {
394
+ const value = fields(options);
395
+ const prepared = value.prepared;
396
+ return {
397
+ max: typeof value.max === "number" ? value.max : undefined,
398
+ timeouts: typeof value.timeouts === "object" && value.timeouts !== null ? value.timeouts : undefined,
399
+ prepared: prepared === "named" || prepared === "unnamed" ? prepared : undefined,
400
+ searchPath: typeof value.searchPath === "string" ? value.searchPath : undefined,
401
+ ssl: value.ssl
402
+ };
403
+ }
404
+ async function openEndpoint(open, url, max, defaults, name, role, weight, probeMs) {
405
+ const pool = await open({
406
+ url,
407
+ dataDir: url,
408
+ ...max !== undefined ? { max } : {},
409
+ ...defaults.timeouts !== undefined ? { timeouts: defaults.timeouts } : {},
410
+ ...defaults.prepared !== undefined ? { prepared: defaults.prepared } : {},
411
+ ...defaults.searchPath !== undefined ? { searchPath: defaults.searchPath } : {},
412
+ ...defaults.ssl !== undefined ? { ssl: defaults.ssl } : {}
413
+ });
414
+ return {
415
+ name,
416
+ role,
417
+ weight,
418
+ max,
419
+ pool,
420
+ position: false,
421
+ circuit: "closed",
422
+ failures: 0,
423
+ replayLsn: null,
424
+ replayPos: undefined,
425
+ replayAt: null,
426
+ nextDelayMs: role === "replica" ? probeMs : 0,
427
+ timer: undefined,
428
+ current: 0,
429
+ inflight: 0,
430
+ latencyMs: null,
431
+ generation: 0
432
+ };
433
+ }
434
+ async function detectPosition(endpoint) {
435
+ try {
436
+ const result = await endpoint.pool.execute(CAPABILITY_SQL);
437
+ endpoint.position = truth(result.rows[0]?.[0]);
438
+ } catch {
439
+ endpoint.position = false;
440
+ }
441
+ }
442
+ async function sample(handle, endpoint) {
443
+ try {
444
+ const sampled = await readPosition(handle, endpoint);
445
+ const reopened = endpoint.circuit === "open";
446
+ endpoint.failures = 0;
447
+ endpoint.circuit = "closed";
448
+ if (reopened)
449
+ endpoint.generation += 1;
450
+ applySample(endpoint, sampled);
451
+ } catch {
452
+ endpoint.failures += 1;
453
+ if (endpoint.failures >= CIRCUIT_AT)
454
+ endpoint.circuit = "open";
455
+ }
456
+ }
457
+ async function readPosition(handle, endpoint) {
458
+ const state = handle.replicaState;
459
+ const started = performance.now();
460
+ if (state === undefined) {
461
+ const sql = handle.maxLag?.kind === "time" ? HEALTH_TIME_SQL : HEALTH_SQL;
462
+ const result = await endpoint.pool.execute(sql);
463
+ observe(endpoint, performance.now() - started);
464
+ const row = result.rows[0];
465
+ return { lsn: row?.[1] ?? null, time: row?.[2] ?? null };
466
+ }
467
+ await endpoint.pool.execute(PING_SQL);
468
+ observe(endpoint, performance.now() - started);
469
+ const lsn = await state.replayLsn({ name: endpoint.name });
470
+ if (typeof lsn !== "string" && lsn !== null) {
471
+ throw new OkmError("OKM1120", "ReplicaState.replayLsn must return a WAL position or null.");
472
+ }
473
+ return { lsn, time: await seamTime(handle, endpoint, state) };
474
+ }
475
+ async function seamTime(handle, endpoint, state) {
476
+ if (handle.maxLag?.kind !== "time" || state.replayTime === undefined)
477
+ return null;
478
+ const value = await state.replayTime({ name: endpoint.name });
479
+ if (value instanceof Date)
480
+ return value.toISOString();
481
+ if (typeof value === "string" || value === null)
482
+ return value;
483
+ throw new OkmError("OKM1120", "ReplicaState.replayTime must return a timestamp or null.");
484
+ }
485
+ function applySample(endpoint, sampled) {
486
+ if (sampled.lsn !== null)
487
+ noteReplay(endpoint, sampled.lsn);
488
+ if (sampled.time !== null)
489
+ noteTime(endpoint, sampled.time);
490
+ }
491
+ function noteReplay(endpoint, text) {
492
+ const pos = parseLsn(text);
493
+ if (pos === undefined)
494
+ return;
495
+ if (endpoint.replayPos !== undefined && pos < endpoint.replayPos)
496
+ return;
497
+ endpoint.replayPos = pos;
498
+ endpoint.replayLsn = text.trim();
499
+ }
500
+ function noteTime(endpoint, text) {
501
+ const parsed = parseTime(text);
502
+ if (parsed === undefined)
503
+ return;
504
+ endpoint.replayAt = parsed;
505
+ }
506
+ function observe(endpoint, sampleMs) {
507
+ const previous = endpoint.latencyMs;
508
+ endpoint.latencyMs = previous === null ? sampleMs : LATENCY_ALPHA * sampleMs + (1 - LATENCY_ALPHA) * previous;
509
+ }
510
+ function delayFor(probeMs, endpoint) {
511
+ if (endpoint.circuit === "closed")
512
+ return probeMs;
513
+ const steps = Math.min(Math.max(endpoint.failures - 1, 1), 8);
514
+ const delay = probeMs * 2 ** steps;
515
+ return delay > BACKOFF_CAP_MS ? BACKOFF_CAP_MS : delay;
516
+ }
517
+ function schedule(handle, endpoint) {
518
+ if (handle.closed || endpoint.role !== "replica")
519
+ return;
520
+ const timer = setTimeout(() => {
521
+ endpoint.timer = undefined;
522
+ runProbe(handle, endpoint);
523
+ }, endpoint.nextDelayMs);
524
+ timer.unref();
525
+ endpoint.timer = timer;
526
+ }
527
+ function schedulePrimary(handle) {
528
+ const primary = handle.endpoints[0];
529
+ if (handle.closed || primary === undefined || !probesPrimary(handle))
530
+ return;
531
+ const timer = setTimeout(() => {
532
+ primary.timer = undefined;
533
+ runPrimary(handle);
534
+ }, handle.probeMs);
535
+ timer.unref();
536
+ primary.timer = timer;
537
+ }
538
+ async function runPrimary(handle) {
539
+ if (handle.closed)
540
+ return;
541
+ await samplePrimary(handle);
542
+ if (handle.closed)
543
+ return;
544
+ schedulePrimary(handle);
545
+ }
546
+ async function samplePrimary(handle) {
547
+ if (!probesPrimary(handle))
548
+ return;
549
+ const primary = handle.endpoints[0];
550
+ if (primary === undefined)
551
+ return;
552
+ const epoch = handle.unknownEpoch;
553
+ const wasUnknown = handle.positionUnknown;
554
+ try {
555
+ const lsn = await readInsertLsn(primary.pool);
556
+ if (lsn === undefined)
557
+ return;
558
+ if (wasUnknown && handle.unknownEpoch === epoch && handle.positionUnknown) {
559
+ raiseWatermark(handle, lsn);
560
+ return;
561
+ }
562
+ notePrimary(handle, lsn);
563
+ } catch {}
564
+ }
565
+ function probesPrimary(handle) {
566
+ if (!handle.hasReplicas)
567
+ return false;
568
+ const primary = handle.endpoints[0];
569
+ if (primary === undefined || !primary.position)
570
+ return false;
571
+ return handle.consistency === "session" || handle.maxLag !== undefined;
572
+ }
573
+ async function runProbe(handle, endpoint) {
574
+ if (handle.closed)
575
+ return;
576
+ await sample(handle, endpoint);
577
+ if (handle.closed)
578
+ return;
579
+ endpoint.nextDelayMs = delayFor(handle.probeMs, endpoint);
580
+ schedule(handle, endpoint);
581
+ }
582
+ function closeHandle(handle) {
583
+ handle.closing ??= shutdown(handle);
584
+ return handle.closing;
585
+ }
586
+ async function shutdown(handle) {
587
+ handle.closed = true;
588
+ for (const endpoint of handle.endpoints) {
589
+ if (endpoint.timer !== undefined)
590
+ clearTimeout(endpoint.timer);
591
+ endpoint.timer = undefined;
592
+ }
593
+ await Promise.all(handle.endpoints.map((endpoint) => endpoint.pool.close()));
594
+ }
595
+ function routePool(handle) {
596
+ const primary = handle.endpoints[0];
597
+ if (primary === undefined) {
598
+ throw new OkmError("OKM1120", "A topology needs one primary URL.");
599
+ }
600
+ const pool = {
601
+ capabilities: primary.pool.capabilities,
602
+ execute: async (text, params, options) => {
603
+ if (handle.closed)
604
+ closed();
605
+ const choice = await resolve(handle, text, options);
606
+ const result = await attempt(handle, choice, (endpoint) => endpoint.pool.execute(text, params, strip(options)));
607
+ if (choice.op === "write")
608
+ await noteWrite(handle, primary.pool);
609
+ return result;
610
+ },
611
+ batch: async (statements, options) => {
612
+ if (handle.closed)
613
+ closed();
614
+ if (options?.route === "replica")
615
+ replicaRefused();
616
+ const reason = options?.route === "primary" ? "constraint:primary" : "primary-required";
617
+ tell(handle, { op: "write", endpoint: primary.name, reason });
618
+ await callGuard(primary);
619
+ const result = await primary.pool.batch(statements, strip(options));
620
+ await noteWrite(handle, primary.pool);
621
+ return result;
622
+ },
623
+ stats: () => primary.pool.stats(),
624
+ close: () => closeHandle(handle)
625
+ };
626
+ if (primary.pool.reserve !== undefined) {
627
+ pool.reserve = async () => {
628
+ if (handle.closed)
629
+ closed();
630
+ tell(handle, { op: "tx", endpoint: primary.name, reason: "primary-required" });
631
+ return wrap(handle, await primary.pool.reserve());
632
+ };
633
+ }
634
+ if (primary.pool.describe !== undefined) {
635
+ pool.describe = async (text, params) => {
636
+ const choice = await choose(handle, text, undefined);
637
+ tell(handle, { op: choice.op, endpoint: choice.endpoint.name, reason: choice.reason });
638
+ if (choice.endpoint.pool.describe !== undefined) {
639
+ return choice.endpoint.pool.describe(text, params);
640
+ }
641
+ return primary.pool.describe(text, params);
642
+ };
643
+ }
644
+ if (primary.pool.stream !== undefined) {
645
+ pool.stream = (text, params) => readStream(handle, text, undefined, params, primary.pool);
646
+ }
647
+ if (primary.pool.listen !== undefined) {
648
+ pool.listen = (channel, onNotify) => {
649
+ tell(handle, { op: "read", endpoint: primary.name, reason: "primary-required" });
650
+ return primary.pool.listen(channel, onNotify);
651
+ };
652
+ }
653
+ if (primary.pool.cancel !== undefined) {
654
+ pool.cancel = () => {
655
+ for (const endpoint of handle.endpoints)
656
+ endpoint.pool.cancel?.();
657
+ };
658
+ }
659
+ const routed = pool;
660
+ routed.okmReadOnlyUnit = (text, params, prelude, options) => readOnlyUnit(handle, text, params, prelude, options);
661
+ routed.okmReadOnlyStream = (text, params, prelude, options) => readOnlyStream(handle, text, params, prelude, options);
662
+ routed.okmEndpoints = () => handle.endpoints.map((endpoint) => ({
663
+ name: endpoint.name,
664
+ generation: endpoint.generation,
665
+ pool: endpoint.pool
666
+ }));
667
+ return pool;
668
+ }
669
+ function derive(schema, handle, router, shared, route) {
670
+ const name = readRoute(route);
671
+ const client = createClient(schema, force(handle, router, name), { ...shared, ownsPool: false });
672
+ delete client.close;
673
+ return instrument(client, handle, name);
674
+ }
675
+ function force(handle, router, route) {
676
+ const pool = {
677
+ forcedRoute: route,
678
+ capabilities: router.capabilities,
679
+ execute: (text, params, options) => router.execute(text, params, { ...options, route }),
680
+ batch: (statements, options) => router.batch(statements, { ...options, route }),
681
+ stats: () => router.stats(),
682
+ close: () => Promise.resolve()
683
+ };
684
+ if (router.reserve !== undefined) {
685
+ pool.reserve = () => {
686
+ if (route === "replica")
687
+ replicaRefused();
688
+ return router.reserve();
689
+ };
690
+ }
691
+ if (router.describe !== undefined) {
692
+ pool.describe = async (text, params) => {
693
+ const choice = await choose(handle, text, route);
694
+ tell(handle, { op: choice.op, endpoint: choice.endpoint.name, reason: choice.reason });
695
+ if (choice.endpoint.pool.describe !== undefined) {
696
+ return choice.endpoint.pool.describe(text, params);
697
+ }
698
+ return router.describe(text, params);
699
+ };
700
+ }
701
+ if (router.stream !== undefined) {
702
+ pool.stream = (text, params) => readStream(handle, text, route, params, router);
703
+ }
704
+ if (router.listen !== undefined) {
705
+ pool.listen = (channel, onNotify) => {
706
+ if (route === "replica")
707
+ replicaRefused();
708
+ return router.listen(channel, onNotify);
709
+ };
710
+ }
711
+ if (router.cancel !== undefined)
712
+ pool.cancel = () => router.cancel();
713
+ const unit = router.okmReadOnlyUnit;
714
+ const stream = router.okmReadOnlyStream;
715
+ const forced = pool;
716
+ if (unit !== undefined) {
717
+ forced.okmReadOnlyUnit = (text, params, prelude, options) => unit(text, params, prelude, { ...options, route });
718
+ }
719
+ if (stream !== undefined) {
720
+ forced.okmReadOnlyStream = (text, params, prelude, options) => stream(text, params, prelude, { ...options, route });
721
+ }
722
+ return pool;
723
+ }
724
+ async function readOnlyUnit(handle, text, params, prelude, options) {
725
+ if (handle.closed)
726
+ closed();
727
+ const choice = await resolve(handle, text, options);
728
+ return attempt(handle, choice, async (endpoint) => {
729
+ if (endpoint.pool.reserve === undefined)
730
+ replicaReserve();
731
+ const conn = await endpoint.pool.reserve();
732
+ try {
733
+ await conn.execute("begin read only");
734
+ try {
735
+ await conn.execute(prelude.text, prelude.params);
736
+ const result = await conn.execute(text, params, strip(options));
737
+ await conn.execute("commit");
738
+ return result;
739
+ } catch (error) {
740
+ await conn.execute("rollback").catch(() => {
741
+ return;
742
+ });
743
+ throw error;
744
+ }
745
+ } finally {
746
+ await conn.release();
747
+ }
748
+ });
749
+ }
750
+ function readOnlyStream(handle, text, params, prelude, options) {
751
+ return async function* () {
752
+ if (handle.closed)
753
+ closed();
754
+ const choice = await choose(handle, text, options?.route);
755
+ tell(handle, { op: choice.op, endpoint: choice.endpoint.name, reason: choice.reason });
756
+ await callGuard(choice.endpoint);
757
+ if (choice.endpoint.pool.reserve === undefined)
758
+ replicaReserve();
759
+ const conn = await choice.endpoint.pool.reserve();
760
+ try {
761
+ await conn.execute("begin read only");
762
+ try {
763
+ await conn.execute(prelude.text, prelude.params);
764
+ await conn.execute(`declare okm_rls no scroll cursor for ${text}`, params);
765
+ for (;; ) {
766
+ const chunk = await conn.execute("fetch 64 from okm_rls");
767
+ if (chunk.rows.length === 0)
768
+ break;
769
+ yield chunk.rows;
770
+ }
771
+ await conn.execute("close okm_rls");
772
+ await conn.execute("commit");
773
+ } catch (error) {
774
+ await conn.execute("rollback").catch(() => {
775
+ return;
776
+ });
777
+ throw error;
778
+ }
779
+ } finally {
780
+ await conn.release();
781
+ }
782
+ }();
783
+ }
784
+ function replicaReserve() {
785
+ throw new OkmError("OKM1111", "Row-level security needs a driver that can reserve a connection.", {
786
+ fix: { summary: "Use a Postgres driver that supports interactive transactions." }
787
+ });
788
+ }
789
+ function classOf(text) {
790
+ const head = text.trimStart().toLowerCase().replace(/\s+/g, " ");
791
+ if (/^(begin|start transaction|commit|rollback|savepoint|release|set|reset)\b/.test(head)) {
792
+ return "tx";
793
+ }
794
+ if (head.includes("pg_advisory"))
795
+ return "lock";
796
+ if (/\sfor (no key update|key share|update|share)\b/.test(head))
797
+ return "lock";
798
+ if (/^(insert|update|delete|refresh|with|create|alter|drop|truncate|grant|revoke|comment|vacuum|analyze|reindex|copy)\b/.test(head)) {
799
+ return "write";
800
+ }
801
+ return "read";
802
+ }
803
+ async function choose(handle, text, route) {
804
+ if (route !== undefined && route !== "primary" && route !== "replica") {
805
+ throw new OkmError("OKM1120", 'route must be "primary" or "replica".');
806
+ }
807
+ const op = classOf(text);
808
+ const primary = handle.endpoints[0];
809
+ if (primary === undefined)
810
+ throw new OkmError("OKM1120", "A topology needs one primary URL.");
811
+ if (text.trimStart().toLowerCase().startsWith("select current_setting")) {
812
+ return { endpoint: primary, op: "read", reason: "primary-required", skipGuard: true };
813
+ }
814
+ if (route === "replica" && op !== "read")
815
+ replicaRefused();
816
+ if (op !== "read") {
817
+ return {
818
+ endpoint: primary,
819
+ op,
820
+ reason: route === "primary" ? "constraint:primary" : "primary-required"
821
+ };
822
+ }
823
+ if (route === "primary")
824
+ return { endpoint: primary, op, reason: "constraint:primary" };
825
+ if (handle.positionUnknown && handle.consistency === "session" && handle.hasReplicas) {
826
+ if (route === "replica")
827
+ noReplica();
828
+ if (handle.fallback === "error")
829
+ fallbackRefused("position-unknown");
830
+ return { endpoint: primary, op, reason: "fallback:position-unknown" };
831
+ }
832
+ const explicit = route === "replica";
833
+ const gated = await gate(handle, undefined, !explicit);
834
+ const replica = pick(handle, gated.ready, !explicit);
835
+ if (replica !== undefined) {
836
+ return {
837
+ endpoint: replica,
838
+ op,
839
+ reason: explicit ? "constraint:replica" : `auto:${replica.name}`
840
+ };
841
+ }
842
+ if (explicit)
843
+ noReplica();
844
+ if (handle.fallback === "error")
845
+ fallbackRefused(gated.miss);
846
+ return { endpoint: primary, op, reason: `fallback:${gated.miss}` };
847
+ }
848
+ async function* readStream(handle, text, route, params, fallback) {
849
+ const choice = await choose(handle, text, route);
850
+ tell(handle, { op: choice.op, endpoint: choice.endpoint.name, reason: choice.reason });
851
+ const pool = choice.endpoint.pool.stream !== undefined ? choice.endpoint.pool : fallback;
852
+ if (pool.stream === undefined)
853
+ return;
854
+ yield* pool.stream(text, params);
855
+ }
856
+ async function attempt(handle, choice, run) {
857
+ tell(handle, { op: choice.op, endpoint: choice.endpoint.name, reason: choice.reason });
858
+ try {
859
+ if (choice.skipGuard !== true)
860
+ await callGuard(choice.endpoint);
861
+ return await tracked(choice.endpoint, choice.op === "read", () => run(choice.endpoint));
862
+ } catch (error) {
863
+ if (choice.pinned === true || choice.op !== "read" || !isUnreachable(error))
864
+ throw error;
865
+ choice.endpoint.generation += 1;
866
+ const next = await retryOf(handle, choice);
867
+ if (next === undefined)
868
+ throw error;
869
+ tell(handle, { op: "read", endpoint: next.endpoint.name, reason: next.reason });
870
+ if (choice.skipGuard !== true)
871
+ await callGuard(next.endpoint);
872
+ return tracked(next.endpoint, true, () => run(next.endpoint));
873
+ }
874
+ }
875
+ function callGuard(endpoint) {
876
+ const guard = endpoint.pool.okmGuard;
877
+ if (guard === undefined)
878
+ return Promise.resolve();
879
+ return guard({
880
+ name: endpoint.name,
881
+ generation: endpoint.generation,
882
+ pool: endpoint.pool
883
+ });
884
+ }
885
+ var CHAIN_DEPTH = 8;
886
+ function isUnreachable(error) {
887
+ const seen = new Set;
888
+ const visit = (value, depth) => {
889
+ if (typeof value !== "object" || value === null || depth > CHAIN_DEPTH)
890
+ return false;
891
+ if (seen.has(value))
892
+ return false;
893
+ seen.add(value);
894
+ if (unreachableNode(value))
895
+ return true;
896
+ if (visit(Reflect.get(value, "cause"), depth + 1))
897
+ return true;
898
+ const errors = Reflect.get(value, "errors");
899
+ return Array.isArray(errors) && errors.some((item) => visit(item, depth + 1));
900
+ };
901
+ return visit(error, 0);
902
+ }
903
+ function unreachableNode(node) {
904
+ for (const key of ["sqlstate", "code", "errno"]) {
905
+ const code = Reflect.get(node, key);
906
+ if (typeof code !== "string" || code.length === 0)
907
+ continue;
908
+ if (isConnectionSqlstate(code) || isConnectionErrno(code))
909
+ return true;
910
+ if (code.startsWith("ERR_POSTGRES_CONNECTION_"))
911
+ return true;
912
+ if (code === "ERR_POSTGRES_IDLE_TIMEOUT" || code === "ERR_POSTGRES_LIFETIME_TIMEOUT") {
913
+ return true;
914
+ }
915
+ }
916
+ return false;
917
+ }
918
+ async function tracked(endpoint, time, run) {
919
+ const replica = endpoint.role === "replica";
920
+ if (replica)
921
+ endpoint.inflight += 1;
922
+ const started = replica && time ? performance.now() : 0;
923
+ try {
924
+ const result = await run();
925
+ if (replica && time)
926
+ observe(endpoint, performance.now() - started);
927
+ return result;
928
+ } finally {
929
+ if (replica)
930
+ endpoint.inflight -= 1;
931
+ }
932
+ }
933
+ async function retryOf(handle, choice) {
934
+ if (choice.endpoint.role === "replica")
935
+ noteFailure(choice.endpoint);
936
+ const explicit = choice.reason.startsWith("constraint:");
937
+ const gated = await gate(handle, choice.endpoint, !explicit);
938
+ const replica = pick(handle, gated.ready, !explicit);
939
+ if (replica !== undefined) {
940
+ return {
941
+ endpoint: replica,
942
+ op: "read",
943
+ reason: explicit ? "constraint:replica" : `auto:${replica.name}`
944
+ };
945
+ }
946
+ if (explicit)
947
+ return;
948
+ if (handle.fallback === "error")
949
+ fallbackRefused(gated.miss);
950
+ const primary = handle.endpoints[0];
951
+ if (primary === undefined)
952
+ return;
953
+ return { endpoint: primary, op: "read", reason: `fallback:${gated.miss}` };
954
+ }
955
+ async function gate(handle, except, capacity) {
956
+ const healthy = takeHealthy(handle, except);
957
+ if (healthy.length === 0)
958
+ return { ready: healthy, miss: emptyReason(handle, capacity) };
959
+ const lagged = await consistent(handle, healthy);
960
+ if (lagged.length === 0)
961
+ return { ready: lagged, miss: "behind" };
962
+ if (!capacity)
963
+ return { ready: lagged, miss: "behind" };
964
+ const room = [];
965
+ for (const endpoint of lagged) {
966
+ if (!saturated(endpoint))
967
+ room.push(endpoint);
968
+ }
969
+ if (room.length === 0)
970
+ return { ready: room, miss: "saturated" };
971
+ return { ready: room, miss: "behind" };
972
+ }
973
+ function takeHealthy(handle, except) {
974
+ const scratch = handle.scratch;
975
+ scratch.length = 0;
976
+ for (const endpoint of handle.endpoints) {
977
+ if (endpoint.role !== "replica" || endpoint === except || endpoint.circuit !== "closed") {
978
+ continue;
979
+ }
980
+ scratch.push(endpoint);
981
+ }
982
+ const copy = scratch.slice();
983
+ scratch.length = 0;
984
+ return copy;
985
+ }
986
+ async function consistent(handle, healthy) {
987
+ if (!filters(handle))
988
+ return healthy;
989
+ const kept = [];
990
+ for (const endpoint of healthy) {
991
+ if (freshEnough(handle, endpoint)) {
992
+ kept.push(endpoint);
993
+ continue;
994
+ }
995
+ if (!endpoint.position)
996
+ continue;
997
+ try {
998
+ applySample(endpoint, await readPosition(handle, endpoint));
999
+ } catch (error) {
1000
+ if (error instanceof OkmError)
1001
+ throw error;
1002
+ noteFailure(endpoint);
1003
+ continue;
1004
+ }
1005
+ if (freshEnough(handle, endpoint))
1006
+ kept.push(endpoint);
1007
+ }
1008
+ return kept;
1009
+ }
1010
+ function filters(handle) {
1011
+ if (handle.maxLag !== undefined)
1012
+ return true;
1013
+ return handle.consistency === "session" && handle.watermark !== undefined;
1014
+ }
1015
+ function freshEnough(handle, endpoint) {
1016
+ return coversWatermark(handle, endpoint) && coversLag(handle, endpoint);
1017
+ }
1018
+ function coversWatermark(handle, endpoint) {
1019
+ if (handle.consistency !== "session" || handle.watermark === undefined)
1020
+ return true;
1021
+ if (!endpoint.position)
1022
+ return false;
1023
+ const replay = endpoint.replayPos;
1024
+ if (replay === undefined)
1025
+ return false;
1026
+ return replay >= handle.watermark;
1027
+ }
1028
+ function coversLag(handle, endpoint) {
1029
+ const lag = handle.maxLag;
1030
+ if (lag === undefined)
1031
+ return true;
1032
+ if (!endpoint.position)
1033
+ return false;
1034
+ if (lag.kind === "bytes") {
1035
+ const diff = byteLag(handle, endpoint);
1036
+ return diff !== null && diff <= lag.bytes;
1037
+ }
1038
+ const ms = timeLag(handle, endpoint);
1039
+ return ms !== null && ms <= lag.ms;
1040
+ }
1041
+ function saturated(endpoint) {
1042
+ const max = endpoint.max;
1043
+ if (max === undefined)
1044
+ return false;
1045
+ const stats = endpoint.pool.stats();
1046
+ return stats.idle === 0 && stats.waiting > 0 && stats.size >= max;
1047
+ }
1048
+ function emptyReason(handle, capacity) {
1049
+ let replicas = 0;
1050
+ let healthy = 0;
1051
+ let room = 0;
1052
+ for (const endpoint of handle.endpoints) {
1053
+ if (endpoint.role !== "replica")
1054
+ continue;
1055
+ replicas += 1;
1056
+ if (endpoint.circuit !== "closed")
1057
+ continue;
1058
+ healthy += 1;
1059
+ if (!capacity || !saturated(endpoint))
1060
+ room += 1;
1061
+ }
1062
+ if (replicas === 0)
1063
+ return "no-replicas";
1064
+ if (healthy === 0)
1065
+ return "unhealthy";
1066
+ if (capacity && room === 0)
1067
+ return "saturated";
1068
+ return "unhealthy";
1069
+ }
1070
+ function pick(handle, candidates, automatic) {
1071
+ if (candidates.length === 0)
1072
+ return;
1073
+ const select = handle.select;
1074
+ if (typeof select === "function") {
1075
+ if (!automatic || candidates.length < 2)
1076
+ return candidates[0];
1077
+ return fromSelect(handle, select, candidates);
1078
+ }
1079
+ if (select === "roundRobin")
1080
+ return roundRobin(handle, candidates);
1081
+ if (select === "leastConnections")
1082
+ return leastConnections(candidates);
1083
+ if (select === "latencyAware")
1084
+ return latencyAware(candidates);
1085
+ return weighted(handle, candidates);
1086
+ }
1087
+ function weighted(handle, candidates) {
1088
+ const live = new Set(candidates);
1089
+ for (const endpoint of handle.endpoints) {
1090
+ if (endpoint.role === "replica" && !live.has(endpoint))
1091
+ endpoint.current = 0;
1092
+ }
1093
+ const first = candidates[0];
1094
+ if (first === undefined)
1095
+ throw new Error("selection saw no candidate");
1096
+ let best = first;
1097
+ let total = 0;
1098
+ for (const endpoint of candidates) {
1099
+ endpoint.current += endpoint.weight;
1100
+ total += endpoint.weight;
1101
+ if (endpoint.current > best.current)
1102
+ best = endpoint;
1103
+ }
1104
+ best.current -= total;
1105
+ return best;
1106
+ }
1107
+ function roundRobin(handle, candidates) {
1108
+ const next = after(handle, handle.rrLast, candidates);
1109
+ handle.rrLast = next;
1110
+ return next;
1111
+ }
1112
+ function after(handle, last, candidates) {
1113
+ const first = candidates[0];
1114
+ if (first === undefined)
1115
+ throw new Error("selection saw no candidate");
1116
+ if (last === undefined)
1117
+ return first;
1118
+ const start = handle.endpoints.indexOf(last);
1119
+ if (start === -1)
1120
+ return first;
1121
+ for (let step = 1;step <= handle.endpoints.length; step += 1) {
1122
+ const endpoint = handle.endpoints[(start + step) % handle.endpoints.length];
1123
+ if (endpoint !== undefined && candidates.includes(endpoint))
1124
+ return endpoint;
1125
+ }
1126
+ return first;
1127
+ }
1128
+ function leastConnections(candidates) {
1129
+ let best = candidates[0];
1130
+ if (best === undefined)
1131
+ throw new Error("selection saw no candidate");
1132
+ for (let index = 1;index < candidates.length; index += 1) {
1133
+ const endpoint = candidates[index];
1134
+ if (endpoint !== undefined && endpoint.inflight < best.inflight)
1135
+ best = endpoint;
1136
+ }
1137
+ return best;
1138
+ }
1139
+ function latencyAware(candidates) {
1140
+ let best = candidates[0];
1141
+ if (best === undefined)
1142
+ throw new Error("selection saw no candidate");
1143
+ for (let index = 1;index < candidates.length; index += 1) {
1144
+ const endpoint = candidates[index];
1145
+ if (endpoint !== undefined && lowerLatency(endpoint, best))
1146
+ best = endpoint;
1147
+ }
1148
+ return best;
1149
+ }
1150
+ function lowerLatency(endpoint, best) {
1151
+ if (endpoint.latencyMs === null)
1152
+ return false;
1153
+ if (best.latencyMs === null)
1154
+ return true;
1155
+ return endpoint.latencyMs < best.latencyMs;
1156
+ }
1157
+ function fromSelect(handle, select, candidates) {
1158
+ const endpoints = candidates.slice();
1159
+ const offered = [];
1160
+ for (const endpoint of endpoints) {
1161
+ offered.push({
1162
+ name: endpoint.name,
1163
+ weight: endpoint.weight,
1164
+ inflight: endpoint.inflight,
1165
+ latencyMs: endpoint.latencyMs,
1166
+ lag: byteLagNumber(handle, endpoint)
1167
+ });
1168
+ }
1169
+ const returned = select(offered, { op: "read" });
1170
+ if (typeof returned === "string") {
1171
+ const endpoint = endpoints.find((item) => item.name === returned);
1172
+ if (endpoint !== undefined)
1173
+ return endpoint;
1174
+ } else if (offered.includes(returned)) {
1175
+ const index = offered.indexOf(returned);
1176
+ const endpoint = endpoints[index];
1177
+ if (endpoint !== undefined)
1178
+ return endpoint;
1179
+ }
1180
+ throw new OkmError("OKM1120", `routing.select returned ${shown(returned)}. It must return a candidate or its name.`);
1181
+ }
1182
+ function shown(value) {
1183
+ if (typeof value === "string")
1184
+ return JSON.stringify(value);
1185
+ if (value === null)
1186
+ return "null";
1187
+ if (value === undefined)
1188
+ return "undefined";
1189
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1190
+ return String(value);
1191
+ }
1192
+ if (typeof value === "object") {
1193
+ const name = Reflect.get(value, "name");
1194
+ if (typeof name === "string")
1195
+ return `an object named ${JSON.stringify(name)}`;
1196
+ return "an object";
1197
+ }
1198
+ return typeof value;
1199
+ }
1200
+ function noteFailure(endpoint) {
1201
+ endpoint.failures += 1;
1202
+ if (endpoint.failures >= CIRCUIT_AT)
1203
+ endpoint.circuit = "open";
1204
+ }
1205
+ function strip(options) {
1206
+ if (options === undefined)
1207
+ return;
1208
+ if (!("route" in options) && !("pin" in options))
1209
+ return options;
1210
+ const bag = options;
1211
+ const { route: _route, pin: _pin, ...rest } = bag;
1212
+ if (rest.signal === undefined && rest.timeout === undefined)
1213
+ return;
1214
+ return rest;
1215
+ }
1216
+ async function resolve(handle, text, options) {
1217
+ const pin = taken(options);
1218
+ if (pin?.name !== undefined)
1219
+ return held(handle, pin.name);
1220
+ const choice = await choose(handle, text, options?.route);
1221
+ if (pin !== undefined && choice.op === "read") {
1222
+ pin.name = choice.endpoint.name;
1223
+ return { ...choice, pinned: true };
1224
+ }
1225
+ return choice;
1226
+ }
1227
+ function taken(options) {
1228
+ if (options === undefined || !("pin" in options))
1229
+ return;
1230
+ const pin = options.pin;
1231
+ if (typeof pin !== "object" || pin === null)
1232
+ return;
1233
+ return pin;
1234
+ }
1235
+ function held(handle, name) {
1236
+ for (const endpoint of handle.endpoints) {
1237
+ if (endpoint.name !== name)
1238
+ continue;
1239
+ return { endpoint, op: "read", reason: `stream:${name}`, pinned: true };
1240
+ }
1241
+ throw new OkmError("OKM1120", `stream() stays on ${name}. That target is not in this topology.`, {
1242
+ fix: { summary: "The first batch chose this target. Later batches do not move." }
1243
+ });
1244
+ }
1245
+ async function noteWrite(handle, runner) {
1246
+ if (!tracks(handle))
1247
+ return;
1248
+ const primary = handle.endpoints[0];
1249
+ if (primary === undefined || !primary.position) {
1250
+ markUnknown(handle);
1251
+ return;
1252
+ }
1253
+ try {
1254
+ const lsn = await readInsertLsn(runner);
1255
+ if (lsn === undefined) {
1256
+ markUnknown(handle);
1257
+ return;
1258
+ }
1259
+ raiseWatermark(handle, lsn);
1260
+ } catch {
1261
+ markUnknown(handle);
1262
+ }
1263
+ }
1264
+ function markUnknown(handle) {
1265
+ handle.positionUnknown = true;
1266
+ handle.unknownEpoch += 1;
1267
+ }
1268
+ function tracks(handle) {
1269
+ return handle.hasReplicas && handle.consistency === "session";
1270
+ }
1271
+ async function readInsertLsn(runner) {
1272
+ const result = await runner.execute(INSERT_LSN_SQL);
1273
+ const text = result.rows[0]?.[0];
1274
+ if (text === null || text === undefined)
1275
+ return;
1276
+ return parseLsn(text);
1277
+ }
1278
+ function notePrimary(handle, lsn) {
1279
+ if (handle.primaryLsn === undefined || lsn > handle.primaryLsn)
1280
+ handle.primaryLsn = lsn;
1281
+ }
1282
+ function raiseWatermark(handle, lsn) {
1283
+ notePrimary(handle, lsn);
1284
+ if (handle.watermark === undefined || lsn > handle.watermark)
1285
+ handle.watermark = lsn;
1286
+ handle.positionUnknown = false;
1287
+ }
1288
+ function byteLag(handle, endpoint) {
1289
+ const primary = handle.primaryLsn;
1290
+ const replay = endpoint.replayPos;
1291
+ if (primary === undefined || replay === undefined)
1292
+ return null;
1293
+ if (replay >= primary)
1294
+ return 0n;
1295
+ return primary - replay;
1296
+ }
1297
+ function byteLagNumber(handle, endpoint) {
1298
+ const diff = byteLag(handle, endpoint);
1299
+ if (diff === null)
1300
+ return null;
1301
+ if (diff > BigInt(Number.MAX_SAFE_INTEGER))
1302
+ return Number.MAX_SAFE_INTEGER;
1303
+ return Number(diff);
1304
+ }
1305
+ function timeLag(handle, endpoint) {
1306
+ const primary = handle.primaryLsn;
1307
+ const replay = endpoint.replayPos;
1308
+ if (primary === undefined || replay === undefined)
1309
+ return null;
1310
+ if (replay >= primary)
1311
+ return 0;
1312
+ if (endpoint.replayAt === null)
1313
+ return null;
1314
+ const lag = Date.now() - endpoint.replayAt;
1315
+ return lag < 0 ? 0 : lag;
1316
+ }
1317
+ function parseLsn(text) {
1318
+ const match = /^([0-9A-Fa-f]+)\/([0-9A-Fa-f]+)$/.exec(text.trim());
1319
+ const high = match?.[1];
1320
+ const low = match?.[2];
1321
+ if (high === undefined || low === undefined)
1322
+ return;
1323
+ return (BigInt(`0x${high}`) << 32n) + BigInt(`0x${low}`);
1324
+ }
1325
+ function parseTime(text) {
1326
+ const direct = Date.parse(text);
1327
+ if (!Number.isNaN(direct))
1328
+ return direct;
1329
+ const iso = Date.parse(text.replace(" ", "T"));
1330
+ if (!Number.isNaN(iso))
1331
+ return iso;
1332
+ return;
1333
+ }
1334
+ function controlKind(text) {
1335
+ const head = text.trimStart().toLowerCase().replace(/\s+/g, " ");
1336
+ if (/^commit\b/.test(head))
1337
+ return "commit";
1338
+ if (/^rollback\b/.test(head) && !/^rollback\s+to\b/.test(head))
1339
+ return "rollback";
1340
+ return "other";
1341
+ }
1342
+ function wrap(handle, conn) {
1343
+ let pending = false;
1344
+ return {
1345
+ execute: async (text, params, options) => {
1346
+ if (options?.route === "replica")
1347
+ replicaRefused();
1348
+ const result = await conn.execute(text, params, strip(options));
1349
+ const kind = controlKind(text);
1350
+ if (kind === "commit") {
1351
+ if (pending) {
1352
+ pending = false;
1353
+ await noteWrite(handle, conn);
1354
+ }
1355
+ } else if (kind === "rollback") {
1356
+ pending = false;
1357
+ } else if (classOf(text) === "write") {
1358
+ pending = true;
1359
+ }
1360
+ return result;
1361
+ },
1362
+ batch: async (statements, options) => {
1363
+ if (options?.route === "replica")
1364
+ replicaRefused();
1365
+ const result = await conn.batch(statements, strip(options));
1366
+ pending = true;
1367
+ return result;
1368
+ },
1369
+ release: () => conn.release(),
1370
+ ...conn.cancel !== undefined ? { cancel: () => conn.cancel() } : {}
1371
+ };
1372
+ }
1373
+ function tell(handle, event) {
1374
+ if (handle.onRoute === undefined)
1375
+ return;
1376
+ try {
1377
+ handle.onRoute(event);
1378
+ } catch {}
1379
+ }
1380
+ function readRoute(route) {
1381
+ if (route === "primary" || route === "replica")
1382
+ return route;
1383
+ throw new OkmError("OKM1120", 'using takes "primary" or "replica".');
1384
+ }
1385
+ function readFallback(value) {
1386
+ if (value === undefined || value === "primary")
1387
+ return "primary";
1388
+ if (value === "error")
1389
+ return "error";
1390
+ throw new OkmError("OKM1120", 'routing.fallback must be "primary" or "error".');
1391
+ }
1392
+ function closed() {
1393
+ throw new OkmError("OKM1120", "The client is closed.");
1394
+ }
1395
+ function replicaRefused() {
1396
+ throw new OkmError("OKM1840", "This operation needs the primary.", {
1397
+ fix: {
1398
+ summary: "Run the operation on the primary. Writes, batch, locks, and tx() never go to a replica."
1399
+ }
1400
+ });
1401
+ }
1402
+ function noReplica() {
1403
+ throw new OkmError("OKM1843", "No replica is eligible. This call does not read the primary.", {
1404
+ fix: {
1405
+ summary: "Configure a replica or drop the route. This call does not read the primary."
1406
+ }
1407
+ });
1408
+ }
1409
+ function fallbackRefused(why) {
1410
+ throw new OkmError("OKM1844", `No replica is eligible (${why}) and fallback is error.`, {
1411
+ fix: {
1412
+ summary: "Restore a replica, or set fallback to primary if the primary should absorb the read."
1413
+ }
1414
+ });
1415
+ }
1416
+ function truth(value) {
1417
+ return value === "t" || value === "true" || value === "1";
1418
+ }
1419
+ function fields(value) {
1420
+ return value;
1421
+ }
1422
+ var topology_default = connectTopology;
1423
+ export {
1424
+ topology_default as default
1425
+ };