@palbase/backend 33.0.2 → 34.0.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 (81) hide show
  1. package/dist/bin/palbase-backend.cjs +2020 -350
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +9 -7
  4. package/dist/bin/palbase-backend.js.map +1 -1
  5. package/dist/{chunk-SI4KGEM3.js → chunk-FOWA7PF4.js} +97 -96
  6. package/dist/chunk-FOWA7PF4.js.map +1 -0
  7. package/dist/chunk-GHNC2UHS.js +20 -0
  8. package/dist/chunk-GHNC2UHS.js.map +1 -0
  9. package/dist/{chunk-AAT5G7KY.js → chunk-JJW3L62T.js} +2 -2
  10. package/dist/chunk-JJW3L62T.js.map +1 -0
  11. package/dist/{chunk-TVCCR6SO.js → chunk-LOGL2ZHD.js} +58 -9
  12. package/dist/chunk-LOGL2ZHD.js.map +1 -0
  13. package/dist/{chunk-C6COAB3E.js → chunk-MNGZE2MM.js} +1651 -186
  14. package/dist/chunk-MNGZE2MM.js.map +1 -0
  15. package/dist/{chunk-XOX6RFPZ.js → chunk-PRH45NZU.js} +46 -3
  16. package/dist/chunk-PRH45NZU.js.map +1 -0
  17. package/dist/{chunk-26GH54D2.js → chunk-RZLY4BOY.js} +5 -3
  18. package/dist/{chunk-26GH54D2.js.map → chunk-RZLY4BOY.js.map} +1 -1
  19. package/dist/{chunk-YIQ4RS4F.js → chunk-SEM36BWY.js} +283 -5
  20. package/dist/chunk-SEM36BWY.js.map +1 -0
  21. package/dist/{chunk-KATPXCJ5.js → chunk-UMESJJRS.js} +19 -40
  22. package/dist/chunk-UMESJJRS.js.map +1 -0
  23. package/dist/chunk-VXPNPVAG.js +40 -0
  24. package/dist/chunk-VXPNPVAG.js.map +1 -0
  25. package/dist/{chunk-WWUG2QXF.js → chunk-YX7UA7VC.js} +53 -4
  26. package/dist/chunk-YX7UA7VC.js.map +1 -0
  27. package/dist/db/env.cjs.map +1 -1
  28. package/dist/db/env.d.cts +2 -0
  29. package/dist/db/env.d.ts +2 -0
  30. package/dist/db/index.cjs +261 -2
  31. package/dist/db/index.cjs.map +1 -1
  32. package/dist/db/index.d.cts +46 -3
  33. package/dist/db/index.d.ts +46 -3
  34. package/dist/db/index.js +14 -5
  35. package/dist/{index-DMZG3kpo.d.ts → endpoint-DpNUx-ON.d.ts} +315 -57
  36. package/dist/{index-DB_nW-AV.d.cts → endpoint-kYgEHrrV.d.cts} +315 -57
  37. package/dist/engine/index.cjs +2026 -348
  38. package/dist/engine/index.cjs.map +1 -1
  39. package/dist/engine/index.d.cts +5 -5
  40. package/dist/engine/index.d.ts +5 -5
  41. package/dist/engine/index.js +17 -7
  42. package/dist/{index-D-3duy8Y.d.ts → index-JrHqNsjQ.d.ts} +108 -50
  43. package/dist/{index-DLveQoOf.d.cts → index-pnDsEfxJ.d.cts} +108 -50
  44. package/dist/index.cjs +616 -254
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.d.cts +7 -6
  47. package/dist/index.d.ts +7 -6
  48. package/dist/index.js +25 -15
  49. package/dist/index.js.map +1 -1
  50. package/dist/openapi/index.cjs +7 -0
  51. package/dist/openapi/index.cjs.map +1 -1
  52. package/dist/openapi/index.d.cts +3 -3
  53. package/dist/openapi/index.d.ts +3 -3
  54. package/dist/openapi/index.js +4 -3
  55. package/dist/openapi/index.js.map +1 -1
  56. package/dist/{registry-DSTThhKf.d.cts → registry-BcOd8Zhf.d.cts} +3 -1
  57. package/dist/{registry-JjF5lcj4.d.ts → registry-BoX5iNyY.d.ts} +3 -1
  58. package/dist/test/index.cjs +103 -66
  59. package/dist/test/index.cjs.map +1 -1
  60. package/dist/test/index.d.cts +3 -3
  61. package/dist/test/index.d.ts +3 -3
  62. package/dist/test/index.js +33 -7
  63. package/dist/test/index.js.map +1 -1
  64. package/docs/README.md +1 -1
  65. package/docs/database.md +308 -0
  66. package/docs/examples/check-query-contracts.ts +5 -0
  67. package/docs/examples/database-dx.contract.json +65 -0
  68. package/docs/examples/database-dx.ts +63 -0
  69. package/docs/examples/money-transfer.ts +73 -0
  70. package/docs/llms-full.txt +309 -1
  71. package/package.json +1 -2
  72. package/template/package.json +1 -1
  73. package/dist/chunk-AAT5G7KY.js.map +0 -1
  74. package/dist/chunk-C6COAB3E.js.map +0 -1
  75. package/dist/chunk-KATPXCJ5.js.map +0 -1
  76. package/dist/chunk-SI4KGEM3.js.map +0 -1
  77. package/dist/chunk-TVCCR6SO.js.map +0 -1
  78. package/dist/chunk-WWUG2QXF.js.map +0 -1
  79. package/dist/chunk-XOX6RFPZ.js.map +0 -1
  80. package/dist/chunk-YIQ4RS4F.js.map +0 -1
  81. package/stack-images.json +0 -24
@@ -1,17 +1,23 @@
1
1
  import {
2
2
  DeclarationRefused,
3
3
  assertNoOrphanEntryPoints,
4
+ assertUniqueLookup,
4
5
  buildContainer,
5
- buildRelations
6
- } from "./chunk-WWUG2QXF.js";
6
+ buildRelations,
7
+ uniqueKeysOf
8
+ } from "./chunk-YX7UA7VC.js";
9
+ import {
10
+ validateInsertManyOptions
11
+ } from "./chunk-GHNC2UHS.js";
7
12
  import {
8
13
  __requestALS,
9
14
  __runStartHooks,
10
15
  __runWithRuntime
11
- } from "./chunk-TVCCR6SO.js";
16
+ } from "./chunk-LOGL2ZHD.js";
12
17
  import {
13
- CODECS
14
- } from "./chunk-XOX6RFPZ.js";
18
+ CODECS,
19
+ assertReadContract
20
+ } from "./chunk-PRH45NZU.js";
15
21
  import {
16
22
  assertNoExpressionHandles,
17
23
  assertUsableFilter,
@@ -24,21 +30,31 @@ import {
24
30
  isSqlFragment,
25
31
  looksLikeUnbrandedPlanRef,
26
32
  looksLikeUnbrandedRef,
27
- qualifiedTableKey
28
- } from "./chunk-SI4KGEM3.js";
33
+ qualifiedTableKey,
34
+ retryBudget,
35
+ validateAtomicOptions,
36
+ validateWorkload
37
+ } from "./chunk-FOWA7PF4.js";
29
38
  import {
30
39
  getRoutes,
31
40
  resolveEffectiveAuth
32
- } from "./chunk-AAT5G7KY.js";
41
+ } from "./chunk-JJW3L62T.js";
33
42
  import {
43
+ BadRequest,
34
44
  DeadlockDetected,
45
+ HttpError,
46
+ SQLSTATE,
35
47
  SerializationFailure,
48
+ TooManyRequests,
36
49
  UniqueViolation,
37
- __name,
38
50
  isEngineRaised,
39
51
  isHttpError,
52
+ isRetryable,
40
53
  markEngineRaised
41
- } from "./chunk-KATPXCJ5.js";
54
+ } from "./chunk-UMESJJRS.js";
55
+ import {
56
+ __name
57
+ } from "./chunk-VXPNPVAG.js";
42
58
 
43
59
  // src/engine/config.ts
44
60
  var BootRefused = class extends Error {
@@ -90,6 +106,8 @@ ${detail}`);
90
106
  uploadSecret: env.PALBASE_UPLOAD_SECRET ?? "",
91
107
  anonKey: env.PALBASE_ANON_KEY ?? "",
92
108
  serviceRoleKey: env.PALBASE_SERVICE_ROLE_KEY ?? "",
109
+ cursorSecret: env.PALBASE_CURSOR_SECRET,
110
+ enableCommands: env.PALBASE_DB_COMMANDS === "true",
93
111
  realtimeSecret: env.REALTIME_INGESTION_SECRET ?? "",
94
112
  port,
95
113
  dbRole: env.DB_ROLE ?? "backend_authenticated",
@@ -98,7 +116,14 @@ ${detail}`);
98
116
  // (`roleBackendServiceRole = "backend_service_role"`, NOLOGIN BYPASSRLS),
99
117
  // and the live database agrees (pg_roles.rolbypassrls = true).
100
118
  dbServiceRole: env.DB_SERVICE_ROLE ?? "backend_service_role",
101
- poolMax
119
+ poolMax,
120
+ ...env.PALBASE_DB_DIAGNOSTICS === "true" ? {
121
+ databaseDiagnostics: {
122
+ onComplete: /* @__PURE__ */ __name((diagnostics) => {
123
+ console.log("palbase.database", JSON.stringify(diagnostics));
124
+ }, "onComplete")
125
+ }
126
+ } : {}
102
127
  };
103
128
  }
104
129
  __name(loadConfig, "loadConfig");
@@ -196,14 +221,14 @@ var AuthVerifier = class {
196
221
  return null;
197
222
  }
198
223
  if (header.alg !== "ES256" || !header.kid) return null;
199
- const key = await this.key(header.kid);
200
- if (!key) return null;
224
+ const key2 = await this.key(header.kid);
225
+ if (!key2) return null;
201
226
  let ok = false;
202
227
  try {
203
228
  ok = await crypto.subtle.verify({
204
229
  name: "ECDSA",
205
230
  hash: "SHA-256"
206
- }, key, b64urlToBytes(sig), new TextEncoder().encode(`${h}.${p}`));
231
+ }, key2, b64urlToBytes(sig), new TextEncoder().encode(`${h}.${p}`));
207
232
  } catch {
208
233
  return null;
209
234
  }
@@ -269,12 +294,12 @@ var RateLimiter = class {
269
294
  * wait (never 0 — a caller told to wait 0 comes straight back to the same
270
295
  * refusal).
271
296
  */
272
- check(rule, key, now) {
297
+ check(rule, key2, now) {
273
298
  if (!rule || !(rule.max > 0) || !(rule.window > 0)) return null;
274
- const bucket = this.buckets.get(key);
299
+ const bucket = this.buckets.get(key2);
275
300
  if (!bucket || now >= bucket.resetAt) {
276
301
  if (this.buckets.size >= this.maxKeys) this.evict(now);
277
- this.buckets.set(key, {
302
+ this.buckets.set(key2, {
278
303
  count: 1,
279
304
  resetAt: now + rule.window * 1e3
280
305
  });
@@ -317,11 +342,11 @@ function makeMemoryCache(opts = {}) {
317
342
  const now = opts.now ?? (() => Date.now());
318
343
  const store = /* @__PURE__ */ new Map();
319
344
  const inflight = /* @__PURE__ */ new Map();
320
- const live = /* @__PURE__ */ __name((key) => {
321
- const e = store.get(key);
345
+ const live = /* @__PURE__ */ __name((key2) => {
346
+ const e = store.get(key2);
322
347
  if (!e) return void 0;
323
348
  if (e.expiresAt !== 0 && e.expiresAt <= now()) {
324
- store.delete(key);
349
+ store.delete(key2);
325
350
  return void 0;
326
351
  }
327
352
  return e;
@@ -344,61 +369,742 @@ function makeMemoryCache(opts = {}) {
344
369
  if (victim) store.delete(victim[0]);
345
370
  }
346
371
  }, "evict");
347
- const set = /* @__PURE__ */ __name(async (key, value, ttl) => {
348
- if (store.size >= maxEntries && !store.has(key)) evict();
349
- store.set(key, {
372
+ const set = /* @__PURE__ */ __name(async (key2, value, ttl) => {
373
+ if (store.size >= maxEntries && !store.has(key2)) evict();
374
+ store.set(key2, {
350
375
  value,
351
376
  expiresAt: ttl && ttl > 0 ? now() + ttl * 1e3 : 0
352
377
  });
353
378
  }, "set");
354
379
  return {
355
- async get(key) {
356
- const e = live(key);
380
+ async get(key2) {
381
+ const e = live(key2);
357
382
  return e ? e.value : null;
358
383
  },
359
384
  set,
360
- async del(key) {
361
- store.delete(key);
385
+ async del(key2) {
386
+ store.delete(key2);
362
387
  },
363
- async incr(key) {
364
- const e = live(key);
388
+ async incr(key2) {
389
+ const e = live(key2);
365
390
  const next = (typeof e?.value === "number" ? e.value : 0) + 1;
366
- store.set(key, {
391
+ store.set(key2, {
367
392
  value: next,
368
393
  expiresAt: e?.expiresAt ?? 0
369
394
  });
370
395
  return next;
371
396
  },
372
- async getOrSet(key, ttl, fn) {
373
- const hit = live(key);
397
+ async getOrSet(key2, ttl, fn) {
398
+ const hit = live(key2);
374
399
  if (hit) return hit.value;
375
- const running = inflight.get(key);
400
+ const running = inflight.get(key2);
376
401
  if (running) return running;
377
402
  const fill = (async () => {
378
403
  try {
379
404
  const value = await fn();
380
- await set(key, value, ttl);
405
+ await set(key2, value, ttl);
381
406
  return value;
382
407
  } finally {
383
- inflight.delete(key);
408
+ inflight.delete(key2);
384
409
  }
385
410
  })();
386
- inflight.set(key, fill);
411
+ inflight.set(key2, fill);
387
412
  return fill;
388
413
  }
389
414
  };
390
415
  }
391
416
  __name(makeMemoryCache, "makeMemoryCache");
392
417
 
418
+ // src/engine/db.ts
419
+ import { AsyncLocalStorage } from "async_hooks";
420
+
421
+ // src/engine/schema-cache.ts
422
+ function memoTable(cache, schema, table, compute) {
423
+ if (!schema.tables || !Object.hasOwn(schema.tables, table)) return compute();
424
+ let tables = cache.get(schema);
425
+ if (!tables) {
426
+ tables = /* @__PURE__ */ new Map();
427
+ cache.set(schema, tables);
428
+ }
429
+ if (!tables.has(table)) tables.set(table, compute());
430
+ return tables.get(table);
431
+ }
432
+ __name(memoTable, "memoTable");
433
+ function snapshotDeclaration(value, seen = /* @__PURE__ */ new WeakMap()) {
434
+ if (value === null || typeof value !== "object") return value;
435
+ const previous = seen.get(value);
436
+ if (previous) return previous;
437
+ if (value instanceof Date) return new Date(value.getTime());
438
+ const result = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
439
+ seen.set(value, result);
440
+ for (const key2 of Reflect.ownKeys(value)) {
441
+ if (Array.isArray(value) && key2 === "length") continue;
442
+ const descriptor = Object.getOwnPropertyDescriptor(value, key2);
443
+ if ("value" in descriptor) descriptor.value = snapshotDeclaration(descriptor.value, seen);
444
+ Object.defineProperty(result, key2, descriptor);
445
+ }
446
+ return Object.freeze(result);
447
+ }
448
+ __name(snapshotDeclaration, "snapshotDeclaration");
449
+
450
+ // src/engine/cursor.ts
451
+ import { createCipheriv, createDecipheriv, createHash, randomBytes } from "crypto";
452
+ var key = null;
453
+ var invalidPage = /* @__PURE__ */ __name((message, field = "page") => new BadRequest({
454
+ fields: [
455
+ {
456
+ field,
457
+ message
458
+ }
459
+ ]
460
+ }, message), "invalidPage");
461
+ function setCursorKey(secret) {
462
+ key = secret ? createHash("sha256").update("palbase:cursor:v1\0").update(secret).digest() : null;
463
+ }
464
+ __name(setCursorKey, "setCursorKey");
465
+ function cursorKey() {
466
+ if (!key) throw new Error("Database.page needs PALBASE_CURSOR_SECRET or PALBASE_SERVICE_ROLE_KEY shared by all replicas");
467
+ return key;
468
+ }
469
+ __name(cursorKey, "cursorKey");
470
+ function fingerprint(value) {
471
+ const stable = /* @__PURE__ */ __name((v) => {
472
+ if (typeof v === "bigint") return {
473
+ $bigint: v.toString()
474
+ };
475
+ if (v instanceof Date) return {
476
+ $date: v.toISOString()
477
+ };
478
+ if (Array.isArray(v)) return v.map(stable);
479
+ if (v && typeof v === "object") {
480
+ return Object.fromEntries(Object.keys(v).sort().map((k) => [
481
+ k,
482
+ stable(v[k])
483
+ ]));
484
+ }
485
+ return v;
486
+ }, "stable");
487
+ return createHash("sha256").update(JSON.stringify(stable(value))).digest("base64url");
488
+ }
489
+ __name(fingerprint, "fingerprint");
490
+ function cursorIdentity(role, claimsJson) {
491
+ const claims = JSON.parse(claimsJson);
492
+ const { iat, exp, nbf, jti, ...authorization } = claims;
493
+ return fingerprint({
494
+ role,
495
+ authorization
496
+ });
497
+ }
498
+ __name(cursorIdentity, "cursorIdentity");
499
+ function encodeCursor(secret, scope, values) {
500
+ const nonce = randomBytes(12);
501
+ const cipher = createCipheriv("aes-256-gcm", secret, nonce);
502
+ cipher.setAAD(Buffer.from(scope));
503
+ const body = Buffer.concat([
504
+ cipher.update(JSON.stringify(values), "utf8"),
505
+ cipher.final()
506
+ ]);
507
+ const token = `p1.${Buffer.concat([
508
+ nonce,
509
+ cipher.getAuthTag(),
510
+ body
511
+ ]).toString("base64url")}`;
512
+ if (token.length > 16384) throw invalidPage("page cursor values are too large; choose shorter ordering columns");
513
+ return token;
514
+ }
515
+ __name(encodeCursor, "encodeCursor");
516
+ function decodeCursor(secret, scope, token, count) {
517
+ try {
518
+ if (typeof token !== "string" || token.length > 16384 || !/^p1\.[\w-]+$/.test(token)) throw new Error();
519
+ const data = Buffer.from(token.slice(3), "base64url");
520
+ if (data.length < 30 || data.toString("base64url") !== token.slice(3)) throw new Error();
521
+ const cipher = createDecipheriv("aes-256-gcm", secret, data.subarray(0, 12));
522
+ cipher.setAAD(Buffer.from(scope));
523
+ cipher.setAuthTag(data.subarray(12, 28));
524
+ const values = JSON.parse(Buffer.concat([
525
+ cipher.update(data.subarray(28)),
526
+ cipher.final()
527
+ ]).toString("utf8"));
528
+ if (!Array.isArray(values) || values.length !== count || values.some((v) => v !== null && typeof v !== "string")) throw new Error();
529
+ return values;
530
+ } catch {
531
+ throw invalidPage("Invalid cursor for this query or identity; restart from the first page", "cursor");
532
+ }
533
+ }
534
+ __name(decodeCursor, "decodeCursor");
535
+ function pageOrder(columns, primaryKey, opts) {
536
+ const raw = opts;
537
+ if ("limit" in raw || "offset" in raw) throw invalidPage("page uses first/after or last/before, not limit/offset");
538
+ const backward = opts.last !== void 0 || opts.before !== void 0;
539
+ if (backward && (opts.first !== void 0 || opts.after !== void 0)) throw invalidPage("Use first/after or last/before in one page request");
540
+ const size = (backward ? opts.last : opts.first) ?? 50;
541
+ if (!Number.isInteger(size) || size < 1 || size > 1e3) throw invalidPage("page size must be an integer between 1 and 1000");
542
+ if (primaryKey.length === 0) throw new Error("page requires a declared primary key for a unique, stable ordering");
543
+ const input = opts.orderBy === void 0 ? [] : Array.isArray(opts.orderBy) ? opts.orderBy : [
544
+ opts.orderBy
545
+ ];
546
+ if (input.length > 16) throw invalidPage("page supports at most 16 ordering columns");
547
+ const seen = /* @__PURE__ */ new Set();
548
+ const order = [];
549
+ const add = /* @__PURE__ */ __name((o) => {
550
+ const def = columns[o?.column];
551
+ if (!Object.hasOwn(columns, o?.column) || !def) throw invalidPage(`page order column ${o?.column} is not declared`);
552
+ if (!(/* @__PURE__ */ new Set([
553
+ "uuid",
554
+ "text",
555
+ "integer",
556
+ "bigint",
557
+ "boolean",
558
+ "timestamp",
559
+ "numeric",
560
+ "enum"
561
+ ])).has(def.type ?? "")) {
562
+ throw invalidPage(`page cannot order ${o.column}: ${def.type ?? "unknown"} is not a supported scalar column`);
563
+ }
564
+ if (seen.has(o.column)) throw invalidPage(`page order column ${o.column} is repeated`);
565
+ if (o.direction !== void 0 && o.direction !== "asc" && o.direction !== "desc") throw invalidPage("page direction must be asc or desc");
566
+ if (o.nulls !== void 0 && o.nulls !== "first" && o.nulls !== "last") throw invalidPage("page nulls must be first or last");
567
+ const direction = o.direction ?? "asc";
568
+ seen.add(o.column);
569
+ order.push({
570
+ column: o.column,
571
+ direction,
572
+ nulls: o.nulls ?? (direction === "asc" ? "last" : "first"),
573
+ nullable: def.nullable !== false
574
+ });
575
+ }, "add");
576
+ for (const o of input) add(o);
577
+ for (const column of primaryKey) if (!seen.has(column)) add({
578
+ column,
579
+ direction: order.at(-1)?.direction ?? "asc"
580
+ });
581
+ return {
582
+ order,
583
+ size,
584
+ backward,
585
+ token: backward ? opts.before : opts.after
586
+ };
587
+ }
588
+ __name(pageOrder, "pageOrder");
589
+ function pageBoundary(order, values, backward, bind, column, inclusive = false) {
590
+ const refs = values.map((v) => bind(v));
591
+ if (order.every((o) => !o.nullable && o.direction === order[0].direction) && values.every((v) => v !== null)) {
592
+ const greater = order[0].direction === "asc" !== backward;
593
+ return `(${order.map((o) => column(o.column)).join(", ")}) ${greater ? ">" : "<"}${inclusive ? "=" : ""} (${refs.join(", ")})`;
594
+ }
595
+ const parts = [];
596
+ const prefix = [];
597
+ for (let i = 0; i < order.length; i++) {
598
+ const o = order[i], c = column(o.column), r = refs[i];
599
+ const nullsLast = o.nulls === "last" !== backward;
600
+ const cmp = o.direction === "asc" !== backward ? ">" : "<";
601
+ const tail = values[i] === null ? nullsLast ? "false" : `${c} IS NOT NULL` : `(${c} ${cmp} ${r}${nullsLast ? ` OR ${c} IS NULL` : ""})`;
602
+ parts.push(`(${[
603
+ ...prefix,
604
+ tail
605
+ ].join(" AND ")})`);
606
+ prefix.push(`${c} IS NOT DISTINCT FROM ${r}`);
607
+ }
608
+ if (inclusive) parts.push(`(${prefix.join(" AND ")})`);
609
+ return `(${parts.join(" OR ")})`;
610
+ }
611
+ __name(pageBoundary, "pageBoundary");
612
+
613
+ // src/engine/commands.ts
614
+ var installed = /* @__PURE__ */ new WeakSet();
615
+ var hasCommandExecutor = /* @__PURE__ */ __name((sql) => installed.has(sql), "hasCommandExecutor");
616
+ var COMMAND_EXECUTOR_DDL = `
617
+ CREATE SCHEMA IF NOT EXISTS palbase_runtime;
618
+ CREATE OR REPLACE FUNCTION palbase_runtime.command_v1(steps jsonb) RETURNS jsonb
619
+ LANGUAGE plpgsql VOLATILE SECURITY INVOKER SET search_path = pg_catalog, public AS $pb_command$
620
+ DECLARE
621
+ results jsonb := '[]'::jsonb;
622
+ step jsonb; chunk jsonb; binding jsonb; source jsonb; rows jsonb;
623
+ statement text; rendered text; detail text; guard jsonb;
624
+ index integer := 0; affected integer; passed boolean;
625
+ BEGIN
626
+ FOR step IN SELECT value FROM jsonb_array_elements(steps) LOOP
627
+ IF step->>'empty' = 'true' THEN rows := '[]'::jsonb;
628
+ ELSE
629
+ statement := '';
630
+ FOR chunk IN SELECT value FROM jsonb_array_elements(step->'chunks') LOOP
631
+ IF jsonb_typeof(chunk) = 'string' THEN statement := statement || (chunk #>> '{}');
632
+ ELSE
633
+ binding := step->'bindings'->((chunk->>'bind')::integer);
634
+ IF binding ? 'ref' THEN
635
+ source := results->((binding->'ref'->>'op')::integer)->'rows'->0;
636
+ IF source IS NULL OR NOT source ? (binding->'ref'->>'field') THEN
637
+ RAISE EXCEPTION 'Command reference has no source row/column; use expectOne before reading it'
638
+ USING ERRCODE = 'PBC02', DETAIL = jsonb_build_object('error_code','tx_ref_unresolved','op',index)::text;
639
+ END IF;
640
+ source := source->(binding->'ref'->>'field');
641
+ ELSE source := binding->'value'; END IF;
642
+ rendered := CASE WHEN source IS NULL OR source = 'null'::jsonb THEN NULL
643
+ WHEN binding->>'json' = 'true' THEN source::text
644
+ WHEN jsonb_typeof(source) = 'string' THEN source #>> '{}' ELSE source::text END;
645
+ -- PostgreSQL quotes the value itself. Values reach this function as
646
+ -- one bound JSON parameter, never as application-generated SQL text.
647
+ statement := statement || quote_nullable(rendered);
648
+ END IF;
649
+ END LOOP;
650
+ EXECUTE statement INTO rows;
651
+ END IF;
652
+ affected := jsonb_array_length(rows);
653
+ guard := step->'guard';
654
+ IF guard IS NOT NULL AND guard <> 'null'::jsonb THEN
655
+ passed := CASE guard->>'kind'
656
+ WHEN 'one' THEN affected = 1 WHEN 'none' THEN affected = 0
657
+ WHEN 'atLeast' THEN affected >= (guard->>'n')::integer
658
+ WHEN 'atMost' THEN affected <= (guard->>'n')::integer ELSE false END;
659
+ IF NOT passed THEN
660
+ RAISE EXCEPTION 'Command expectation failed'
661
+ USING ERRCODE = 'PBC01', DETAIL = jsonb_build_object('error_code','tx_guard_failed','slot',guard->'slot','op',index)::text;
662
+ END IF;
663
+ END IF;
664
+ results := results || jsonb_build_array(jsonb_build_object('rows',rows,'rows_affected',affected));
665
+ index := index + 1;
666
+ END LOOP;
667
+ RETURN jsonb_build_object('results',results);
668
+ EXCEPTION WHEN SQLSTATE 'PBC01' OR SQLSTATE 'PBC02' THEN
669
+ GET STACKED DIAGNOSTICS detail = PG_EXCEPTION_DETAIL;
670
+ RETURN jsonb_build_object('error',detail::jsonb);
671
+ END
672
+ $pb_command$;
673
+ REVOKE ALL ON FUNCTION palbase_runtime.command_v1(jsonb) FROM PUBLIC;
674
+ CREATE OR REPLACE FUNCTION palbase_runtime.command_rows_v1(rows jsonb, guard jsonb) RETURNS jsonb
675
+ LANGUAGE plpgsql VOLATILE SECURITY INVOKER SET search_path = pg_catalog, public AS $pb_rows$
676
+ DECLARE affected integer := jsonb_array_length(rows); passed boolean;
677
+ BEGIN
678
+ IF guard IS NOT NULL AND guard <> 'null'::jsonb THEN
679
+ passed := CASE guard->>'kind'
680
+ WHEN 'one' THEN affected = 1 WHEN 'none' THEN affected = 0
681
+ WHEN 'atLeast' THEN affected >= (guard->>'n')::integer
682
+ WHEN 'atMost' THEN affected <= (guard->>'n')::integer ELSE false END;
683
+ IF NOT passed THEN
684
+ RAISE EXCEPTION 'Command expectation failed'
685
+ USING ERRCODE = 'PBC01', DETAIL = jsonb_build_object('error_code','tx_guard_failed','slot',guard->'slot')::text;
686
+ END IF;
687
+ END IF;
688
+ RETURN rows;
689
+ END
690
+ $pb_rows$;
691
+ REVOKE ALL ON FUNCTION palbase_runtime.command_rows_v1(jsonb,jsonb) FROM PUBLIC;
692
+ `;
693
+ async function installCommandExecutor(sql, roles) {
694
+ const quote = /* @__PURE__ */ __name((s) => `"${s.replace(/"/g, '""')}"`, "quote");
695
+ if (roles.length === 0) throw new Error("Command executor needs explicit runtime roles");
696
+ await sql.begin(async (tx) => {
697
+ await tx.unsafe("SELECT pg_advisory_xact_lock(196754721, 1)");
698
+ await tx.unsafe(COMMAND_EXECUTOR_DDL);
699
+ const list = [
700
+ ...new Set(roles)
701
+ ].map(quote).join(", ");
702
+ await tx.unsafe(`GRANT USAGE ON SCHEMA palbase_runtime TO ${list}`);
703
+ await tx.unsafe(`GRANT EXECUTE ON FUNCTION palbase_runtime.command_v1(jsonb) TO ${list}`);
704
+ await tx.unsafe(`GRANT EXECUTE ON FUNCTION palbase_runtime.command_rows_v1(jsonb,jsonb) TO ${list}`);
705
+ });
706
+ installed.add(sql);
707
+ }
708
+ __name(installCommandExecutor, "installCommandExecutor");
709
+ function parameterChunks(sql) {
710
+ const chunks = [];
711
+ let start = 0, quote = null;
712
+ for (let i = 0; i < sql.length; i++) {
713
+ const c = sql[i];
714
+ if (quote) {
715
+ if (c === quote) {
716
+ if (sql[i + 1] === quote) i++;
717
+ else quote = null;
718
+ }
719
+ continue;
720
+ }
721
+ if (c === "'" || c === '"') {
722
+ quote = c;
723
+ continue;
724
+ }
725
+ if (c !== "$") continue;
726
+ const parameter = /^\$(\d+)\b/.exec(sql.slice(i));
727
+ if (!parameter) continue;
728
+ chunks.push(sql.slice(start, i), {
729
+ bind: Number(parameter[1]) - 1
730
+ });
731
+ i += parameter[0].length - 1;
732
+ start = i + 1;
733
+ }
734
+ chunks.push(sql.slice(start));
735
+ return chunks;
736
+ }
737
+ __name(parameterChunks, "parameterChunks");
738
+
739
+ // src/engine/admission.ts
740
+ var WorkloadAdmission = class {
741
+ static {
742
+ __name(this, "WorkloadAdmission");
743
+ }
744
+ definition;
745
+ pending = [];
746
+ active = 0;
747
+ activeKeys = /* @__PURE__ */ new Set();
748
+ tenants = /* @__PURE__ */ new Map();
749
+ draining = false;
750
+ constructor(definition) {
751
+ this.definition = definition;
752
+ validateWorkload(definition);
753
+ }
754
+ get snapshot() {
755
+ return {
756
+ active: this.active,
757
+ queued: this.pending.length,
758
+ tenants: this.tenants.size,
759
+ keys: this.activeKeys.size
760
+ };
761
+ }
762
+ acquire(tenant, keys, signal) {
763
+ if (signal?.aborted) return Promise.reject(signal.reason ?? new Error("Database admission cancelled"));
764
+ const normalized = [
765
+ ...new Set(keys)
766
+ ].sort();
767
+ if (normalized.length > 64 || normalized.some((k) => typeof k !== "string" || k.length === 0 || k.length > 256)) return Promise.reject(new Error("conflictKeys must contain at most 64 nonempty strings of at most 256 characters"));
768
+ const scoped = normalized;
769
+ const globalFull = this.pending.length >= this.definition.queueLimit;
770
+ const tenantFull = this.pending.filter((w) => w.tenant === tenant).length >= this.definition.tenantQueueLimit;
771
+ const canStart = this.active < this.definition.concurrency && (this.tenants.get(tenant) ?? 0) < this.definition.tenantConcurrency && scoped.every((k) => !this.activeKeys.has(k) && !this.pending.some((w) => w.keys.includes(k)));
772
+ if (!canStart && (globalFull || tenantFull)) {
773
+ return Promise.reject(new TooManyRequests({
774
+ retryAfter: 1
775
+ }, `Database workload ${this.definition.name}: ${tenantFull ? "tenant" : "global"} queue is full`));
776
+ }
777
+ return new Promise((grant, reject) => {
778
+ const waiter = {
779
+ tenant,
780
+ keys: scoped,
781
+ entered: performance.now(),
782
+ grant,
783
+ reject,
784
+ cleanup: /* @__PURE__ */ __name(() => {
785
+ }, "cleanup")
786
+ };
787
+ const remove = /* @__PURE__ */ __name((reason) => {
788
+ const index = this.pending.indexOf(waiter);
789
+ if (index === -1) return;
790
+ this.pending.splice(index, 1);
791
+ waiter.cleanup();
792
+ reject(reason);
793
+ this.drain();
794
+ }, "remove");
795
+ const abort = /* @__PURE__ */ __name(() => remove(signal?.reason ?? new Error("Database admission cancelled")), "abort");
796
+ const timer = setTimeout(() => remove(new TooManyRequests({
797
+ retryAfter: 1
798
+ }, `Database workload ${this.definition.name}: queue deadline exceeded`)), this.definition.queueTimeoutMs);
799
+ waiter.cleanup = () => {
800
+ clearTimeout(timer);
801
+ signal?.removeEventListener("abort", abort);
802
+ };
803
+ signal?.addEventListener("abort", abort, {
804
+ once: true
805
+ });
806
+ this.pending.push(waiter);
807
+ if (signal?.aborted) abort();
808
+ else this.drain();
809
+ });
810
+ }
811
+ drain() {
812
+ if (this.draining) return;
813
+ this.draining = true;
814
+ try {
815
+ const blockedKeys = /* @__PURE__ */ new Set();
816
+ for (let index = 0; index < this.pending.length && this.active < this.definition.concurrency; ) {
817
+ const waiter = this.pending[index];
818
+ if ((this.tenants.get(waiter.tenant) ?? 0) >= this.definition.tenantConcurrency || waiter.keys.some((key2) => this.activeKeys.has(key2) || blockedKeys.has(key2))) {
819
+ for (const key2 of waiter.keys) blockedKeys.add(key2);
820
+ index++;
821
+ continue;
822
+ }
823
+ this.pending.splice(index, 1);
824
+ waiter.cleanup();
825
+ this.active++;
826
+ this.tenants.set(waiter.tenant, (this.tenants.get(waiter.tenant) ?? 0) + 1);
827
+ for (const key2 of waiter.keys) this.activeKeys.add(key2);
828
+ let released = false;
829
+ waiter.grant({
830
+ queueMs: performance.now() - waiter.entered,
831
+ release: /* @__PURE__ */ __name(() => {
832
+ if (released) return;
833
+ released = true;
834
+ this.active--;
835
+ const left = this.tenants.get(waiter.tenant) - 1;
836
+ if (left === 0) this.tenants.delete(waiter.tenant);
837
+ else this.tenants.set(waiter.tenant, left);
838
+ for (const key2 of waiter.keys) this.activeKeys.delete(key2);
839
+ this.drain();
840
+ }, "release")
841
+ });
842
+ }
843
+ } finally {
844
+ this.draining = false;
845
+ }
846
+ }
847
+ };
848
+ var registries = /* @__PURE__ */ new WeakMap();
849
+ function workloadAdmission(owner, definition) {
850
+ validateWorkload(definition);
851
+ let registry = registries.get(owner);
852
+ if (!registry) {
853
+ registry = /* @__PURE__ */ new Map();
854
+ registries.set(owner, registry);
855
+ }
856
+ const existing = registry.get(definition.name);
857
+ if (existing) {
858
+ const d = existing.definition;
859
+ if (Object.keys(d).some((k) => d[k] !== definition[k])) throw new Error(`Workload ${definition.name} was already registered with different limits`);
860
+ return existing;
861
+ }
862
+ if (registry.size >= 64) throw new Error("At most 64 named database workloads may be registered per engine; declare and reuse them at module scope");
863
+ const admission = new WorkloadAdmission(Object.freeze({
864
+ ...definition
865
+ }));
866
+ registry.set(definition.name, admission);
867
+ return admission;
868
+ }
869
+ __name(workloadAdmission, "workloadAdmission");
870
+ function workloadTenant(definition, claimsJson) {
871
+ const claims = JSON.parse(claimsJson);
872
+ const value = claims[definition.tenantClaim] ?? claims.sub;
873
+ return typeof value === "string" && value.length > 0 ? value : "__anonymous__";
874
+ }
875
+ __name(workloadTenant, "workloadTenant");
876
+
877
+ // src/engine/transaction-control.ts
878
+ var TransactionControl = class {
879
+ static {
880
+ __name(this, "TransactionControl");
881
+ }
882
+ controller = new AbortController();
883
+ active = /* @__PURE__ */ new Set();
884
+ cancelBackend;
885
+ cancellation = Promise.resolve();
886
+ timer;
887
+ unlink;
888
+ rejection;
889
+ deadline;
890
+ get signal() {
891
+ return this.controller.signal;
892
+ }
893
+ constructor(timeoutMs, external) {
894
+ this.deadline = timeoutMs === void 0 ? void 0 : performance.now() + timeoutMs;
895
+ this.rejection = new Promise((_, reject) => {
896
+ this.signal.addEventListener("abort", () => {
897
+ reject(this.signal.reason);
898
+ if (this.active.size && this.cancelBackend) {
899
+ this.cancellation = this.cancelBackend().catch(() => void 0);
900
+ } else {
901
+ for (const query of this.active) {
902
+ try {
903
+ query.cancel?.();
904
+ } catch {
905
+ }
906
+ }
907
+ }
908
+ }, {
909
+ once: true
910
+ });
911
+ });
912
+ void this.rejection.catch(() => void 0);
913
+ const abort = /* @__PURE__ */ __name(() => this.controller.abort(external?.reason ?? new DOMException("Database transaction cancelled", "AbortError")), "abort");
914
+ external?.addEventListener("abort", abort, {
915
+ once: true
916
+ });
917
+ this.unlink = () => external?.removeEventListener("abort", abort);
918
+ if (external?.aborted) abort();
919
+ if (timeoutMs !== void 0) this.timer = setTimeout(() => this.controller.abort(new HttpError(503, "database_deadline_exceeded", "Database transaction exceeded its deadline")), timeoutMs);
920
+ }
921
+ check() {
922
+ if (this.deadline !== void 0 && performance.now() >= this.deadline && !this.signal.aborted) {
923
+ this.controller.abort(new HttpError(503, "database_deadline_exceeded", "Database transaction exceeded its deadline"));
924
+ }
925
+ this.signal.throwIfAborted();
926
+ }
927
+ remainingMs() {
928
+ this.check();
929
+ return this.deadline === void 0 ? void 0 : Math.max(1, Math.ceil(this.deadline - performance.now()));
930
+ }
931
+ race(work) {
932
+ return Promise.race([
933
+ work,
934
+ this.rejection
935
+ ]);
936
+ }
937
+ bindBackend(cancel) {
938
+ this.cancelBackend = cancel;
939
+ }
940
+ /** Keep the application's connection parked until the cancellation request
941
+ * and all statements settle; a late cancel must not hit its next borrower. */
942
+ async drainCancellation() {
943
+ await this.cancellation;
944
+ await Promise.allSettled([
945
+ ...this.active
946
+ ]);
947
+ }
948
+ async watch(query) {
949
+ this.active.add(query);
950
+ try {
951
+ if (this.signal.aborted) query.cancel?.();
952
+ return await query;
953
+ } finally {
954
+ this.active.delete(query);
955
+ }
956
+ }
957
+ dispose() {
958
+ if (this.timer !== void 0) clearTimeout(this.timer);
959
+ this.unlink();
960
+ }
961
+ };
962
+
963
+ // src/engine/diagnostics.ts
964
+ import { createHash as createHash2 } from "crypto";
965
+ var DatabaseTrace = class {
966
+ static {
967
+ __name(this, "DatabaseTrace");
968
+ }
969
+ options;
970
+ start = performance.now();
971
+ ended;
972
+ failure;
973
+ state;
974
+ constructor(options) {
975
+ this.options = options;
976
+ for (const key2 of [
977
+ "maxQueries",
978
+ "maxRows",
979
+ "maxBytes"
980
+ ]) {
981
+ const value = options[key2];
982
+ if (value !== void 0 && (!Number.isSafeInteger(value) || value < 0)) throw new Error(`database ${key2} must be a nonnegative safe integer`);
983
+ }
984
+ this.state = {
985
+ endpoint: options.endpoint,
986
+ requestId: options.requestId,
987
+ outcome: "active",
988
+ elapsedMs: 0,
989
+ queries: 0,
990
+ rows: 0,
991
+ bytes: 0,
992
+ queueMs: 0,
993
+ poolMs: 0,
994
+ setupMs: 0,
995
+ sqlMs: 0,
996
+ commitMs: 0,
997
+ rollbackMs: 0,
998
+ commits: 0,
999
+ commitFailures: 0,
1000
+ rollbackRequests: 0,
1001
+ retries: 0,
1002
+ statements: []
1003
+ };
1004
+ }
1005
+ check() {
1006
+ if (this.failure) throw this.failure;
1007
+ }
1008
+ time(key2, started) {
1009
+ this.state[key2] += performance.now() - started;
1010
+ }
1011
+ retry() {
1012
+ this.state.retries++;
1013
+ }
1014
+ committed() {
1015
+ this.state.commits++;
1016
+ }
1017
+ commitFailed() {
1018
+ this.state.commitFailures++;
1019
+ }
1020
+ failed() {
1021
+ this.state.outcome = "failed";
1022
+ }
1023
+ rolledBack() {
1024
+ this.state.rollbackRequests++;
1025
+ }
1026
+ refuse(kind) {
1027
+ this.failure ??= new HttpError(500, "database_budget_exceeded", `Database ${kind} budget exceeded${this.options.endpoint ? ` in ${this.options.endpoint}` : ""}`);
1028
+ throw this.failure;
1029
+ }
1030
+ emit(callback, event) {
1031
+ try {
1032
+ const result = callback?.(event);
1033
+ if (result) void Promise.resolve(result).catch(() => void 0);
1034
+ } catch {
1035
+ }
1036
+ }
1037
+ async query(sql, name, run) {
1038
+ this.check();
1039
+ if (this.options.maxQueries !== void 0 && this.state.queries >= this.options.maxQueries) this.refuse("query count");
1040
+ const ordinal = ++this.state.queries;
1041
+ const event = {
1042
+ ordinal,
1043
+ fingerprint: createHash2("sha256").update(sql).digest("hex").slice(0, 24),
1044
+ ...name === void 0 ? {} : {
1045
+ name: name.slice(0, 128)
1046
+ },
1047
+ ...this.options.includeSql ? {
1048
+ sql: sql.slice(0, 4096)
1049
+ } : {},
1050
+ durationMs: 0,
1051
+ rows: 0,
1052
+ bytes: 0
1053
+ };
1054
+ if (this.options.captureSource) event.source = new Error().stack?.split("\n").slice(1, 9).join("\n").slice(0, 2500);
1055
+ const started = performance.now();
1056
+ let measured = false;
1057
+ try {
1058
+ const result = await run();
1059
+ event.durationMs = performance.now() - started;
1060
+ measured = true;
1061
+ if (Array.isArray(result)) {
1062
+ event.rows = result.length;
1063
+ event.bytes = Buffer.byteLength(JSON.stringify(result, (_, value) => typeof value === "bigint" ? value.toString() : value));
1064
+ }
1065
+ this.state.rows += event.rows;
1066
+ this.state.bytes += event.bytes;
1067
+ if (this.options.maxRows !== void 0 && this.state.rows > this.options.maxRows) this.refuse("returned rows");
1068
+ if (this.options.maxBytes !== void 0 && this.state.bytes > this.options.maxBytes) this.refuse("result bytes");
1069
+ return result;
1070
+ } catch (error) {
1071
+ const e = error;
1072
+ event.errorCode = String(e?.sqlstate ?? e?.errno ?? e?.code ?? "query_failed").slice(0, 80);
1073
+ throw error;
1074
+ } finally {
1075
+ if (!measured) event.durationMs = performance.now() - started;
1076
+ this.state.sqlMs += event.durationMs;
1077
+ const frozen = Object.freeze(event);
1078
+ if (this.state.statements.length < 200) this.state.statements.push(frozen);
1079
+ this.emit(this.options.onQuery, frozen);
1080
+ }
1081
+ }
1082
+ get snapshot() {
1083
+ return Object.freeze({
1084
+ ...this.state,
1085
+ elapsedMs: (this.ended ?? performance.now()) - this.start,
1086
+ statements: Object.freeze([
1087
+ ...this.state.statements
1088
+ ].sort((a, b) => a.ordinal - b.ordinal))
1089
+ });
1090
+ }
1091
+ complete(outcome) {
1092
+ if (this.ended !== void 0) return;
1093
+ this.ended = performance.now();
1094
+ this.state.outcome = outcome;
1095
+ this.emit(this.options.onComplete, this.snapshot);
1096
+ }
1097
+ };
1098
+
393
1099
  // src/engine/db.ts
394
1100
  function quoteIdent(name) {
395
1101
  return `"${name.replace(/"/g, '""')}"`;
396
1102
  }
397
1103
  __name(quoteIdent, "quoteIdent");
398
- function quoteTable(key) {
399
- const dot = key.indexOf(".");
400
- if (dot === -1) return quoteIdent(key);
401
- return `${quoteIdent(key.slice(0, dot))}.${quoteIdent(key.slice(dot + 1))}`;
1104
+ function quoteTable(key2) {
1105
+ const dot = key2.indexOf(".");
1106
+ if (dot === -1) return quoteIdent(key2);
1107
+ return `${quoteIdent(key2.slice(0, dot))}.${quoteIdent(key2.slice(dot + 1))}`;
402
1108
  }
403
1109
  __name(quoteTable, "quoteTable");
404
1110
  function encodePgArray(value) {
@@ -411,9 +1117,10 @@ function encodePgArray(value) {
411
1117
  }
412
1118
  __name(encodePgArray, "encodePgArray");
413
1119
  var BIND_SQL = "select set_config('role',$1,true), set_config('search_path','public',true), set_config('request.jwt.claims',$2,true)";
1120
+ var atomicScope = new AsyncLocalStorage();
414
1121
  function createLazyTransaction(sql, role, claimsJson, options = {}) {
415
1122
  const { lockTimeout } = options;
416
- const bindSql = lockTimeout ? `${BIND_SQL}, set_config('lock_timeout',$3,true)` : BIND_SQL;
1123
+ let bindSql = lockTimeout ? `${BIND_SQL}, set_config('lock_timeout',$3,true)` : BIND_SQL;
417
1124
  const bindParams = lockTimeout ? [
418
1125
  role,
419
1126
  claimsJson,
@@ -422,25 +1129,118 @@ function createLazyTransaction(sql, role, claimsJson, options = {}) {
422
1129
  role,
423
1130
  claimsJson
424
1131
  ];
1132
+ if (options.statementTimeout !== void 0) {
1133
+ bindParams.push(options.statementTimeout);
1134
+ bindSql += `, set_config('statement_timeout',$${bindParams.length},true)`;
1135
+ }
425
1136
  let opening = null;
426
1137
  let release = null;
427
1138
  let fail = null;
428
1139
  let settled = null;
429
- const ensure = /* @__PURE__ */ __name(() => {
1140
+ let closed = false;
1141
+ const assertAccess = /* @__PURE__ */ __name(() => {
1142
+ if (closed) throw new Error("Database transaction has settled; its handle cannot be used again");
1143
+ options.control?.check();
1144
+ options.trace?.check();
1145
+ if (atomicScope.getStore() !== options.scope) {
1146
+ throw new Error("Database.$atomic cannot use a handle from another transaction or identity; use its tx argument");
1147
+ }
1148
+ }, "assertAccess");
1149
+ let savepointALS;
1150
+ let activeSavepoint;
1151
+ const pendingSavepoints = /* @__PURE__ */ new Set();
1152
+ const scoped = /* @__PURE__ */ __name((tx, checkParent = assertAccess, owner, children = /* @__PURE__ */ new Set()) => {
1153
+ const access = /* @__PURE__ */ __name(() => {
1154
+ checkParent();
1155
+ if (activeSavepoint !== owner || savepointALS?.getStore() !== owner) {
1156
+ throw new Error("Database connection belongs to another active savepoint; use its tx argument and await it before using the parent handle");
1157
+ }
1158
+ }, "access");
1159
+ return {
1160
+ async unsafe(text, params, metadata) {
1161
+ access();
1162
+ const run = /* @__PURE__ */ __name(() => {
1163
+ const query = tx.unsafe(text, params);
1164
+ return options.control ? options.control.watch(query) : query;
1165
+ }, "run");
1166
+ return options.trace ? options.trace.query(text, metadata?.name, run) : run();
1167
+ },
1168
+ async savepoint(fn) {
1169
+ access();
1170
+ const token = /* @__PURE__ */ Symbol("savepoint"), descendants = /* @__PURE__ */ new Set();
1171
+ savepointALS ??= new AsyncLocalStorage();
1172
+ activeSavepoint = token;
1173
+ const work = (async () => {
1174
+ try {
1175
+ return await tx.savepoint(async (sp) => {
1176
+ let ended = false;
1177
+ const check = /* @__PURE__ */ __name(() => {
1178
+ checkParent();
1179
+ if (ended) throw new Error("Database savepoint has settled; its handle cannot be used again");
1180
+ }, "check");
1181
+ try {
1182
+ const callback = savepointALS.run(token, () => fn(scoped(sp, check, token, descendants)));
1183
+ return await (options.control ? options.control.race(callback) : callback);
1184
+ } finally {
1185
+ if (descendants.size) await Promise.allSettled([
1186
+ ...descendants
1187
+ ]);
1188
+ ended = true;
1189
+ }
1190
+ });
1191
+ } finally {
1192
+ activeSavepoint = owner;
1193
+ }
1194
+ })();
1195
+ pendingSavepoints.add(work);
1196
+ children.add(work);
1197
+ try {
1198
+ return await work;
1199
+ } finally {
1200
+ pendingSavepoints.delete(work);
1201
+ children.delete(work);
1202
+ }
1203
+ }
1204
+ };
1205
+ }, "scoped");
1206
+ const ensure = /* @__PURE__ */ __name(async () => {
1207
+ assertAccess();
430
1208
  if (opening) return opening;
431
1209
  opening = new Promise((resolveTx2, rejectTx) => {
432
1210
  const parked = new Promise((res, rej) => {
433
1211
  release = res;
434
1212
  fail = rej;
435
1213
  });
1214
+ void parked.catch(() => void 0);
1215
+ const openingAt = options.trace ? performance.now() : 0;
436
1216
  settled = sql.begin(async (tx) => {
437
- await tx.unsafe(bindSql, bindParams);
438
- resolveTx2(tx);
1217
+ options.trace?.time("poolMs", openingAt);
1218
+ const setupAt = options.trace ? performance.now() : 0;
1219
+ options.control?.check();
1220
+ if (options.isolation !== void 0 || options.readOnly !== void 0) {
1221
+ const isolation = options.isolation ?? "read committed";
1222
+ validateAtomicOptions({
1223
+ isolation,
1224
+ readOnly: options.readOnly
1225
+ });
1226
+ await tx.unsafe(`SET TRANSACTION ISOLATION LEVEL ${isolation.toUpperCase()}${options.readOnly === void 0 ? "" : options.readOnly ? " READ ONLY" : " READ WRITE"}`);
1227
+ }
1228
+ if (options.control && sql.cancelBackend) {
1229
+ const ticket = await tx.unsafe("SELECT pg_backend_pid() AS pid, backend_start::text AS started FROM pg_stat_activity WHERE pid=pg_backend_pid()");
1230
+ if (!Number.isInteger(ticket[0]?.pid) || typeof ticket[0]?.started !== "string") throw new Error("Database cancellation cannot identify its own backend");
1231
+ options.control.bindBackend(() => sql.cancelBackend(ticket[0].pid, ticket[0].started));
1232
+ options.control.check();
1233
+ }
1234
+ const binding = tx.unsafe(bindSql, bindParams);
1235
+ await (options.control ? options.control.watch(binding) : binding);
1236
+ options.trace?.time("setupMs", setupAt);
1237
+ resolveTx2(scoped(tx));
439
1238
  await parked;
440
1239
  }).catch((e) => {
441
1240
  rejectTx(e);
442
1241
  throw e;
443
1242
  });
1243
+ void settled.catch(() => void 0);
444
1244
  });
445
1245
  return opening;
446
1246
  }, "ensure");
@@ -450,14 +1250,37 @@ function createLazyTransaction(sql, role, claimsJson, options = {}) {
450
1250
  return opening !== null;
451
1251
  },
452
1252
  async commit() {
1253
+ if (pendingSavepoints.size) await Promise.allSettled([
1254
+ ...pendingSavepoints
1255
+ ]);
1256
+ options.control?.check();
1257
+ options.trace?.check();
1258
+ closed = true;
453
1259
  if (!opening) return;
1260
+ const started = options.trace ? performance.now() : 0;
454
1261
  release();
455
- await settled;
1262
+ try {
1263
+ await settled;
1264
+ options.trace?.committed();
1265
+ } catch (error) {
1266
+ options.trace?.commitFailed();
1267
+ throw error;
1268
+ } finally {
1269
+ options.trace?.time("commitMs", started);
1270
+ }
456
1271
  },
457
1272
  async rollback(reason) {
1273
+ closed = true;
458
1274
  if (!opening) return;
1275
+ await options.control?.drainCancellation();
1276
+ if (pendingSavepoints.size) await Promise.allSettled([
1277
+ ...pendingSavepoints
1278
+ ]);
1279
+ const started = options.trace ? performance.now() : 0;
459
1280
  fail(reason);
460
1281
  await settled?.catch(() => void 0);
1282
+ options.trace?.time("rollbackMs", started);
1283
+ options.trace?.rolledBack();
461
1284
  }
462
1285
  };
463
1286
  }
@@ -472,7 +1295,7 @@ __name(asWireValue, "asWireValue");
472
1295
  function asWireRow(row) {
473
1296
  if (row === null || typeof row !== "object") return row;
474
1297
  const out = {};
475
- for (const [key, value] of Object.entries(row)) out[key] = asWireValue(value);
1298
+ for (const [key2, value] of Object.entries(row)) out[key2] = asWireValue(value);
476
1299
  return out;
477
1300
  }
478
1301
  __name(asWireRow, "asWireRow");
@@ -484,7 +1307,7 @@ function toVectorLiteral(v) {
484
1307
  return `[${v.join(",")}]`;
485
1308
  }
486
1309
  __name(toVectorLiteral, "toVectorLiteral");
487
- function vectorColumnsOf(schema, table) {
1310
+ function computeVectorColumns(schema, table) {
488
1311
  const out = /* @__PURE__ */ new Set();
489
1312
  const def = tableDefOf(table, schema);
490
1313
  if (def) {
@@ -495,9 +1318,9 @@ function vectorColumnsOf(schema, table) {
495
1318
  }
496
1319
  return out;
497
1320
  }
498
- __name(vectorColumnsOf, "vectorColumnsOf");
499
- function tableDefOf(key, schema = currentSchema) {
500
- return schema.tables?.[key] ?? null;
1321
+ __name(computeVectorColumns, "computeVectorColumns");
1322
+ function tableDefOf(key2, schema = currentSchema) {
1323
+ return schema.tables?.[key2] ?? null;
501
1324
  }
502
1325
  __name(tableDefOf, "tableDefOf");
503
1326
  function reviveVectors(row, vectorCols) {
@@ -510,7 +1333,7 @@ function reviveVectors(row, vectorCols) {
510
1333
  return row;
511
1334
  }
512
1335
  __name(reviveVectors, "reviveVectors");
513
- function transformsOf(schema, table) {
1336
+ function computeTransforms(schema, table) {
514
1337
  const out = /* @__PURE__ */ new Map();
515
1338
  const def = tableDefOf(table, schema);
516
1339
  if (def) {
@@ -533,7 +1356,7 @@ function transformsOf(schema, table) {
533
1356
  }
534
1357
  return out;
535
1358
  }
536
- __name(transformsOf, "transformsOf");
1359
+ __name(computeTransforms, "computeTransforms");
537
1360
  function applyFromDb(row, transforms) {
538
1361
  if (transforms.size === 0) return row;
539
1362
  for (const [col, t] of transforms) {
@@ -550,10 +1373,23 @@ function asTableRow(table, row) {
550
1373
  return applyFromDb(revived, transformsOf(currentSchema, table));
551
1374
  }
552
1375
  __name(asTableRow, "asTableRow");
553
- function asTableRows(table, rows) {
1376
+ function asTableRows(table, rows, withSpec) {
554
1377
  const vectorCols = vectorColumnsOf(currentSchema, table);
555
1378
  const transforms = transformsOf(currentSchema, table);
556
- return rows.map((row) => applyFromDb(reviveVectors(asWireRow(row), vectorCols), transforms));
1379
+ const result = rows.map((row) => applyFromDb(reviveVectors(asWireRow(row), vectorCols), transforms));
1380
+ if (withSpec) for (const rel of currentRelations.get(table) ?? []) {
1381
+ const spec = withSpec[rel.name];
1382
+ if (!spec) continue;
1383
+ const nested = typeof spec === "object" ? spec.with : void 0;
1384
+ for (const row of result) {
1385
+ const value = row[rel.name];
1386
+ if (Array.isArray(value)) row[rel.name] = asTableRows(rel.to, value, nested);
1387
+ else if (value && typeof value === "object") row[rel.name] = asTableRows(rel.to, [
1388
+ value
1389
+ ], nested)[0];
1390
+ }
1391
+ }
1392
+ return result;
557
1393
  }
558
1394
  __name(asTableRows, "asTableRows");
559
1395
  function asBindParams(table, cols, data, caller) {
@@ -796,7 +1632,7 @@ function excludeSql(pk, ids, add) {
796
1632
  return ` AND t.${quoteIdent(pk)}::text <> ALL(ARRAY[${ids.map((x) => add(x)).join(", ")}])`;
797
1633
  }
798
1634
  __name(excludeSql, "excludeSql");
799
- function primaryKeyOf(table) {
1635
+ function computePrimaryKey(table) {
800
1636
  const t = tableDefOf(table);
801
1637
  const columns = t?.columns ?? {};
802
1638
  const cols = Object.keys(columns);
@@ -816,7 +1652,7 @@ function primaryKeyOf(table) {
816
1652
  const pks = cols.filter((c) => pkOf(columns[c]));
817
1653
  return pks.length === 1 ? pks[0] : null;
818
1654
  }
819
- __name(primaryKeyOf, "primaryKeyOf");
1655
+ __name(computePrimaryKey, "computePrimaryKey");
820
1656
  function isSinglePkEquality(where, pk) {
821
1657
  if (pk === null) return false;
822
1658
  const keys = Object.keys(where);
@@ -831,12 +1667,43 @@ function assertNotAppendOnly(caller, table) {
831
1667
  throw new Error(`${caller}(${table}): "${table}" append-only \u2014 sat\u0131r g\xFCncellenemez ya da silinemez. D\xFCzeltme i\xE7in TELAF\u0130 KAYDI ekleyin: insert("${table}", { \u2026 }) ile ters i\u015Fareti ta\u015F\u0131yan yeni bir sat\u0131r yaz\u0131n; ge\xE7mi\u015F oldu\u011Fu gibi kals\u0131n diye tablo b\xF6yle bildirilmi\u015F (appendOnly: true).`);
832
1668
  }
833
1669
  __name(assertNotAppendOnly, "assertNotAppendOnly");
834
- function schemaColumns(table) {
1670
+ function computeSchemaColumns(table) {
835
1671
  const t = tableDefOf(table);
836
1672
  const cols = t?.columns ? Object.keys(t.columns) : [];
837
1673
  return cols.length > 0 ? new Set(cols) : null;
838
1674
  }
839
- __name(schemaColumns, "schemaColumns");
1675
+ __name(computeSchemaColumns, "computeSchemaColumns");
1676
+ function timestampResult(expr) {
1677
+ return `CASE WHEN isfinite(${expr}) AND extract(year FROM ${expr} AT TIME ZONE 'UTC') BETWEEN 1 AND 9999 THEN to_char(${expr} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"') ELSE ${expr}::text END`;
1678
+ }
1679
+ __name(timestampResult, "timestampResult");
1680
+ function columnResult(table, column, alias) {
1681
+ const expr = `${alias ? `${quoteIdent(alias)}.` : ""}${quoteIdent(column)}`;
1682
+ return columnTypeOf(table, column) === "timestamp" ? timestampResult(expr) : expr;
1683
+ }
1684
+ __name(columnResult, "columnResult");
1685
+ function projectedColumn(table, column, alias) {
1686
+ const expr = columnResult(table, column, alias);
1687
+ return columnTypeOf(table, column) === "timestamp" ? `${expr} AS ${quoteIdent(column)}` : expr;
1688
+ }
1689
+ __name(projectedColumn, "projectedColumn");
1690
+ function rowProjection(table, alias, wildcard = alias ? `${alias}.*` : "*") {
1691
+ const cols = schemaColumns(table);
1692
+ if (!cols || !memoTable(timestampMemo, currentSchema, table, () => [
1693
+ ...cols
1694
+ ].some((c) => columnTypeOf(table, c) === "timestamp"))) return wildcard;
1695
+ const projections = memoTable(projectionMemo, currentSchema, table, () => /* @__PURE__ */ new Map());
1696
+ const key2 = alias ?? "";
1697
+ let sql = projections.get(key2);
1698
+ if (sql === void 0) {
1699
+ sql = [
1700
+ ...cols
1701
+ ].map((c) => projectedColumn(table, c, alias)).join(", ");
1702
+ projections.set(key2, sql);
1703
+ }
1704
+ return sql;
1705
+ }
1706
+ __name(rowProjection, "rowProjection");
840
1707
  function colRefSql(table, known, ref, caller, alias = "t") {
841
1708
  const name = ref.$col;
842
1709
  const shapeOK = /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
@@ -850,13 +1717,13 @@ function selectClause(table, known, select) {
850
1717
  if (select !== void 0 && select.length === 0) {
851
1718
  throw new Error(`findMany(${table}): select bo\u015F bir liste ald\u0131 \u2014 bu "hepsini ver" DE\u011E\u0130L, "hi\xE7birini saymad\u0131m" demek. Projeksiyon istemiyorsan\u0131z anahtar\u0131 hi\xE7 koymay\u0131n; istiyorsan\u0131z en az bir kolon ad\u0131 verin.`);
852
1719
  }
853
- if (select === void 0) return "*";
1720
+ if (select === void 0) return rowProjection(table, "t", "*");
854
1721
  return select.map((col) => {
855
1722
  const shapeOK = /^[A-Za-z_][A-Za-z0-9_]*$/.test(col);
856
1723
  if (known !== null ? !known.has(col) : !shapeOK) {
857
1724
  throw new Error(`findMany(${table}): select kolonu "${col}" tabloda yok`);
858
1725
  }
859
- return `t.${quoteIdent(col)}`;
1726
+ return columnTypeOf(table, col) === "timestamp" ? projectedColumn(table, col, "t") : `t.${quoteIdent(col)}`;
860
1727
  }).join(", ");
861
1728
  }
862
1729
  __name(selectClause, "selectClause");
@@ -926,7 +1793,7 @@ function compileWhereBare(table, colSet, where, add, caller = "search", alias =
926
1793
  const transforms = transformsOf(currentSchema, table);
927
1794
  const bindFor = /* @__PURE__ */ __name((col) => {
928
1795
  const t = transforms.get(col);
929
- return t?.toDb === void 0 ? add : (v) => add(v === null || v === void 0 ? v : t.toDb(v));
1796
+ return (v) => add(t?.toDb === void 0 || v === null || v === void 0 ? v : t.toDb(v), col, table);
930
1797
  }, "bindFor");
931
1798
  assertUsableFilter(caller, table, where);
932
1799
  if (isSqlFragment(where)) {
@@ -957,8 +1824,7 @@ function compileWhereBare(table, colSet, where, add, caller = "search", alias =
957
1824
  const known = rels.map((r) => r.name).sort();
958
1825
  throw new Error(`${caller}(${table}): has.${relName} diye bir ili\u015Fki yok` + (known.length > 0 ? ` (bilinenler: ${known.join(", ")}). \u0130li\u015Fkiler yabanc\u0131 anahtarlardan T\xDCRET\u0130L\u0130R; ad\u0131n\u0131 de\u011Fi\u015Ftirmek i\xE7in references(..., { as, reverseAs }).` : `. Bu tablo H\u0130\xC7 yabanc\u0131 anahtar bildirmiyor, ve ili\u015Fkiler onlardan T\xDCRET\u0130L\u0130R. Var olan bir veritaban\u0131ndan introspect edilmi\u015F \u015Femalarda bu s\u0131k olur: kolon \`text()\`/\`uuid()\` olarak durur, \`references(() => <tablo>.<kolon>)\` yoktur. Bildirdi\u011Finiz anda ili\u015Fki iki tarafta da adland\u0131r\u0131l\u0131r \u2014 ama bu CANLI veritaban\u0131na giden bir k\u0131s\u0131tt\u0131r ve mevcut yetim sat\u0131rlar onu d\xFC\u015F\xFCr\xFCr; \xF6nce onlar\u0131 say\u0131n.`));
959
1826
  }
960
- const ourCol = rel.kind === "many" ? primaryKeyOf(table) : rel.via;
961
- const theirCol = rel.kind === "many" ? rel.via : primaryKeyOf(rel.to);
1827
+ const [ourCol, theirCol] = relationColumns(table, rel);
962
1828
  if (ourCol === null || theirCol === null) {
963
1829
  const missing = ourCol === null ? table : rel.to;
964
1830
  throw new Error(`${caller}(${table}): has.${relName} kurulam\u0131yor \u2014 "${missing}" tablosunun tek kolonlu bir primary key'i yok. Korelasyon bir anahtar ister; \xE7ok kolonlu anahtarda ili\u015Fki \xFCzerinden s\xFCzmek yerine sqlFragment ile yaz\u0131n.`);
@@ -1096,8 +1962,8 @@ async function embedQuery(embed, text) {
1096
1962
  if (secretReader === null) {
1097
1963
  throw new Error(`query embed: secret reader ba\u011Flanmam\u0131\u015F \u2014 ${embed.apiKeyName} okunam\u0131yor`);
1098
1964
  }
1099
- const key = await secretReader(embed.apiKeyName);
1100
- if (key === null || key === "") {
1965
+ const key2 = await secretReader(embed.apiKeyName);
1966
+ if (key2 === null || key2 === "") {
1101
1967
  throw new Error(`query embed: vault'ta ${embed.apiKeyName} yok (FR-021/FR-025)`);
1102
1968
  }
1103
1969
  const url = (embed.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "") + "/embeddings";
@@ -1108,7 +1974,7 @@ async function embedQuery(embed, text) {
1108
1974
  method: "POST",
1109
1975
  headers: {
1110
1976
  "Content-Type": "application/json",
1111
- Authorization: `Bearer ${key}`
1977
+ Authorization: `Bearer ${key2}`
1112
1978
  },
1113
1979
  body: JSON.stringify({
1114
1980
  model: embed.model,
@@ -1166,14 +2032,14 @@ function buildValueTuple(table, cols, data, add, caller) {
1166
2032
  }).join(", ");
1167
2033
  }
1168
2034
  __name(buildValueTuple, "buildValueTuple");
1169
- function columnTypeOf(table, col) {
2035
+ function computeColumnType(table, col) {
1170
2036
  const def = tableDefOf(table);
1171
2037
  const c = def?.columns?.[col];
1172
2038
  if (c === void 0 || c === null) return null;
1173
2039
  const d = typeof c === "object" && "_def" in c ? c._def : c;
1174
2040
  return d !== null && typeof d === "object" && typeof d.type === "string" ? d.type : null;
1175
2041
  }
1176
- __name(columnTypeOf, "columnTypeOf");
2042
+ __name(computeColumnType, "computeColumnType");
1177
2043
  var NUMERIC_PG_TYPES = /* @__PURE__ */ new Set([
1178
2044
  "integer",
1179
2045
  "bigint",
@@ -1183,23 +2049,44 @@ function aggregateExpr(table, fn, col, alias) {
1183
2049
  const q = `t.${quoteIdent(col)}`;
1184
2050
  const t = columnTypeOf(table, col);
1185
2051
  if (fn === "sum" || fn === "avg") return `${fn}(${q})::text AS ${quoteIdent(alias)}`;
2052
+ if (t === "timestamp") return `${timestampResult(`${fn}(${q})`)} AS ${quoteIdent(alias)}`;
1186
2053
  return t !== null && NUMERIC_PG_TYPES.has(t) && t !== "integer" ? `${fn}(${q})::text AS ${quoteIdent(alias)}` : `${fn}(${q}) AS ${quoteIdent(alias)}`;
1187
2054
  }
1188
2055
  __name(aggregateExpr, "aggregateExpr");
1189
- function jsonObjectFor(table, cols, alias) {
2056
+ function jsonObjectFor(table, cols, alias, extra = []) {
1190
2057
  const pairs = cols.map((c) => {
1191
2058
  const t = columnTypeOf(table, c);
1192
- const expr = t !== null && (t === "numeric" || t === "bigint") ? `${alias}.${quoteIdent(c)}::text` : `${alias}.${quoteIdent(c)}`;
2059
+ const expr = t !== null && (t === "numeric" || t === "bigint") ? `${alias}.${quoteIdent(c)}::text` : t === "timestamp" ? timestampResult(`${alias}.${quoteIdent(c)}`) : `${alias}.${quoteIdent(c)}`;
1193
2060
  return `${quoteLiteral(c)}, ${expr}`;
1194
2061
  });
1195
- return `json_build_object(${pairs.join(", ")})`;
2062
+ return `json_build_object(${[
2063
+ ...pairs,
2064
+ ...extra
2065
+ ].join(", ")})`;
1196
2066
  }
1197
2067
  __name(jsonObjectFor, "jsonObjectFor");
1198
- function withClauses(table, spec, add, caller) {
2068
+ function relationColumns(table, relation) {
2069
+ const child = relation.kind === "many" ? relation.to : table;
2070
+ const value = tableDefOf(child)?.columns?.[relation.via];
2071
+ const column = value && typeof value === "object" && "_def" in value ? value._def : value;
2072
+ const target = column?.references?.column ?? primaryKeyOf(relation.kind === "many" ? table : relation.to);
2073
+ return relation.kind === "many" ? [
2074
+ target,
2075
+ relation.via
2076
+ ] : [
2077
+ relation.via,
2078
+ target
2079
+ ];
2080
+ }
2081
+ __name(relationColumns, "relationColumns");
2082
+ function withClauses(table, spec, add, caller, parentAlias = "t", depth = 0, counter = {
2083
+ value: 0
2084
+ }) {
2085
+ if (depth >= 3 && Object.values(spec).some(Boolean)) throw new Error(`${caller}(${table}): with supports at most three relation levels`);
1199
2086
  const rels = currentRelations.get(table) ?? [];
1200
2087
  const select = [];
1201
2088
  const joins = [];
1202
- let i = 0;
2089
+ const jsonPairs = [];
1203
2090
  for (const [name, opt] of Object.entries(spec)) {
1204
2091
  if (opt === false || opt === void 0) continue;
1205
2092
  const rel = rels.find((r) => r.name === name);
@@ -1207,13 +2094,13 @@ function withClauses(table, spec, add, caller) {
1207
2094
  const known = rels.map((r) => r.name).sort();
1208
2095
  throw new Error(`${caller}(${table}): with.${name} diye bir ili\u015Fki yok` + (known.length > 0 ? ` (bilinenler: ${known.join(", ")})` : `. Bu tablo hi\xE7 yabanc\u0131 anahtar bildirmiyor ve ili\u015Fkiler onlardan T\xDCRET\u0130L\u0130R`) + `. \u0130li\u015Fkiler references(() => <tablo>.<kolon>) ile bildirilir.`);
1209
2096
  }
1210
- const ourCol = rel.kind === "many" ? primaryKeyOf(table) : rel.via;
1211
- const theirCol = rel.kind === "many" ? rel.via : primaryKeyOf(rel.to);
2097
+ const [ourCol, theirCol] = relationColumns(table, rel);
1212
2098
  if (ourCol === null || theirCol === null) {
1213
2099
  throw new Error(`${caller}(${table}): with.${name} birincil anahtar bilmeden kurulamaz \u2014 ili\u015Fkinin bir ucu tablonun PK's\u0131 ve bu tablo tek kolonlu bir PK bildirmiyor.`);
1214
2100
  }
1215
2101
  const o = typeof opt === "object" && opt !== null ? opt : {};
1216
- const childAlias = `w${++i}`;
2102
+ const i = ++counter.value;
2103
+ const childAlias = `w${i}`;
1217
2104
  const childKnown = schemaColumns(rel.to);
1218
2105
  const childCols = Array.isArray(o["select"]) && o["select"].length > 0 ? o["select"] : childKnown !== null ? [
1219
2106
  ...childKnown
@@ -1226,28 +2113,100 @@ function withClauses(table, spec, add, caller) {
1226
2113
  throw new Error(`${caller}(${table}): with.${name}.select kolonu "${c}" "${rel.to}" tablosunda yok`);
1227
2114
  }
1228
2115
  }
1229
- const obj = jsonObjectFor(rel.to, childCols, childAlias);
2116
+ const nestedSpec = o["with"];
2117
+ if (nestedSpec !== void 0 && (nestedSpec === null || typeof nestedSpec !== "object" || Array.isArray(nestedSpec))) {
2118
+ throw new Error(`${caller}(${table}): with.${name}.with must be a relation object`);
2119
+ }
2120
+ const nested = nestedSpec === void 0 ? {
2121
+ joins: [],
2122
+ jsonPairs: []
2123
+ } : withClauses(rel.to, nestedSpec, add, caller, childAlias, depth + 1, counter);
2124
+ const nestedJoins = nested.joins.length ? ` ${nested.joins.join(" ")}` : "";
2125
+ const obj = jsonObjectFor(rel.to, childCols, childAlias, nested.jsonPairs);
1230
2126
  const innerWhere = o["where"] !== void 0 && o["where"] !== null ? compileWhereBare(rel.to, childKnown, o["where"], add, caller, childAlias, i) : "";
1231
- const link = `${childAlias}.${quoteIdent(theirCol)} = t.${quoteIdent(ourCol)}`;
2127
+ const link = `${childAlias}.${quoteIdent(theirCol)} = ${parentAlias}.${quoteIdent(ourCol)}`;
1232
2128
  const cond = innerWhere === "" ? link : `${link} AND ${innerWhere}`;
1233
- const inner = orderClauseFor(rel.to, childKnown, o["orderBy"], childAlias) + limitClause(o["limit"]);
2129
+ const requestedLimit = o["limit"];
2130
+ limitClause(requestedLimit);
2131
+ const relationLimit = rel.kind === "many" ? requestedLimit : Math.min(1, requestedLimit ?? 1);
2132
+ const inner = orderClauseFor(rel.to, childKnown, o["orderBy"], childAlias) + limitClause(relationLimit);
1234
2133
  const outAlias = `wj${i}`;
1235
2134
  if (rel.kind === "many") {
1236
- joins.push(`LEFT JOIN LATERAL (SELECT coalesce(json_agg(x.o), '[]'::json) AS d FROM (SELECT ${obj} AS o FROM ${quoteTable(rel.to)} ${childAlias} WHERE ${cond}${inner}) x) ${quoteIdent(outAlias)} ON true`);
2135
+ joins.push(`LEFT JOIN LATERAL (SELECT coalesce(json_agg(x.o), '[]'::json) AS d FROM (SELECT ${obj} AS o FROM ${quoteTable(rel.to)} ${childAlias}${nestedJoins} WHERE ${cond}${inner}) x) ${quoteIdent(outAlias)} ON true`);
1237
2136
  } else {
1238
- joins.push(`LEFT JOIN LATERAL (SELECT ${obj} AS d FROM ${quoteTable(rel.to)} ${childAlias} WHERE ${cond}${inner} LIMIT 1) ${quoteIdent(outAlias)} ON true`);
2137
+ joins.push(`LEFT JOIN LATERAL (SELECT ${obj} AS d FROM ${quoteTable(rel.to)} ${childAlias}${nestedJoins} WHERE ${cond}${inner}) ${quoteIdent(outAlias)} ON true`);
1239
2138
  }
1240
2139
  select.push(`${quoteIdent(outAlias)}."d" AS ${quoteIdent(name)}`);
2140
+ jsonPairs.push(`${quoteLiteral(name)}, ${quoteIdent(outAlias)}."d"`);
1241
2141
  }
1242
2142
  return {
1243
2143
  select,
1244
- joins
2144
+ joins,
2145
+ jsonPairs
1245
2146
  };
1246
2147
  }
1247
2148
  __name(withClauses, "withClauses");
1248
- function createOps(tx) {
2149
+ function compileFindMany(table, query = {}, opts = {}) {
2150
+ const params = [];
2151
+ const add = /* @__PURE__ */ __name((v) => {
2152
+ params.push(v);
2153
+ return `$${params.length}`;
2154
+ }, "add");
2155
+ const known = schemaColumns(table);
2156
+ const whereSql = compileWhere(table, known, query, add, "findMany");
2157
+ const order = orderClause(table, known, opts.orderBy);
2158
+ const limit = limitClause(opts.limit);
2159
+ const offset = offsetClause(opts.offset, opts.limit);
2160
+ const cols = selectClause(table, known, opts.select);
2161
+ const w = opts.with !== void 0 && Object.keys(opts.with).length > 0 ? withClauses(table, opts.with, add, "findMany") : {
2162
+ select: [],
2163
+ joins: []
2164
+ };
2165
+ const selectList = w.select.length > 0 ? `${cols === "*" ? "t.*" : cols}, ${w.select.join(", ")}` : cols;
2166
+ const joinSql = w.joins.length > 0 ? ` ${w.joins.join(" ")}` : "";
2167
+ const sql = `SELECT ${selectList} FROM ${quoteTable(table)} t${joinSql} WHERE true${whereSql}${order}${limit}${offset}`;
2168
+ return {
2169
+ sql,
2170
+ params
2171
+ };
2172
+ }
2173
+ __name(compileFindMany, "compileFindMany");
2174
+ var uniqueKeysCache = /* @__PURE__ */ new WeakMap();
2175
+ var pageSchemaCache = /* @__PURE__ */ new WeakMap();
2176
+ function pageSchema(table) {
2177
+ return memoTable(pageSchemaCache, currentSchema, table, () => {
2178
+ const def = tableDefOf(table);
2179
+ if (!def?.columns) throw new Error(`page(${table}) requires an installed table declaration`);
2180
+ const columns = /* @__PURE__ */ Object.create(null);
2181
+ const inlineKeys = [];
2182
+ for (const [name, column] of Object.entries(def.columns)) {
2183
+ const c = column;
2184
+ const d = c._def ?? c;
2185
+ columns[name] = {
2186
+ type: d.type,
2187
+ nullable: d.nullable
2188
+ };
2189
+ if (d.primaryKey) inlineKeys.push(name);
2190
+ }
2191
+ const primaryKey = def.primaryKey ?? inlineKeys;
2192
+ for (const name of primaryKey) if (columns[name]) columns[name].nullable = false;
2193
+ return {
2194
+ columns,
2195
+ primaryKey,
2196
+ stamp: fingerprint({
2197
+ columns,
2198
+ primaryKey
2199
+ })
2200
+ };
2201
+ });
2202
+ }
2203
+ __name(pageSchema, "pageSchema");
2204
+ function createOps(tx, context = {}) {
1249
2205
  const at = /* @__PURE__ */ __name(() => resolveTx(tx), "at");
1250
2206
  const ops = {
2207
+ diagnostics() {
2208
+ return context.trace?.snapshot ?? null;
2209
+ },
1251
2210
  // Generic dönüş, çağıranın İDDİASIDIR (FR-021). Motor her zaman satır objeleri
1252
2211
  // üretir; `T` onlara bir ad vermekten ibarettir ve verilmezse `unknown` kalır —
1253
2212
  // yani "bilmiyorum", ki ham SQL için doğru cevap odur.
@@ -1261,7 +2220,7 @@ function createOps(tx) {
1261
2220
  const params = [];
1262
2221
  const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
1263
2222
  const tuple = buildValueTuple(table, cols, data, add, "insert");
1264
- const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`;
2223
+ const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING ${rowProjection(table)}`;
1265
2224
  const rows = await (await at()).unsafe(sql, params);
1266
2225
  const inserted = rows[0];
1267
2226
  if (!inserted) {
@@ -1283,8 +2242,9 @@ function createOps(tx) {
1283
2242
  * bir satırın fazla kolonu SESSİZCE YAZILMAZDI. Eksik kolon için `null`
1284
2243
  * yazın ya da varsayılanı istiyorsanız o satırları ayrı çağrıda ekleyin.
1285
2244
  */
1286
- async insertMany(table, rows, opts) {
1287
- if (rows.length === 0) return [];
2245
+ insertMany: /* @__PURE__ */ __name(async function(table, rows, opts) {
2246
+ validateInsertManyOptions(opts);
2247
+ if (rows.length === 0) return opts?.returning === false ? 0 : [];
1288
2248
  const first = rows[0];
1289
2249
  const cols = Object.keys(first);
1290
2250
  if (cols.length === 0) throw new Error(`insertMany into ${table}: no columns given`);
@@ -1299,11 +2259,15 @@ function createOps(tx) {
1299
2259
  const params = [];
1300
2260
  const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
1301
2261
  const tuples = rows.map((r) => `(${buildValueTuple(table, cols, r, add, "insertMany")})`);
1302
- const tail = opts !== void 0 && opts.onConflict.length > 0 ? ` ${onConflictTail(cols, opts.onConflict, opts.action ?? "ignore")}` : "";
1303
- const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail} RETURNING *`;
2262
+ const tail = opts?.onConflict !== void 0 && opts.onConflict.length > 0 ? ` ${onConflictTail(cols, opts.onConflict, opts.action ?? "ignore")}` : "";
2263
+ const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail}${opts?.returning === false ? "" : ` RETURNING ${rowProjection(table)}`}`;
1304
2264
  const out = await (await at()).unsafe(sql, params);
2265
+ if (opts?.returning === false) {
2266
+ if (!Number.isSafeInteger(out.count) || out.count < 0) throw new Error("insertMany returning:false requires a driver with an affected row count");
2267
+ return out.count;
2268
+ }
1305
2269
  return out.map((r) => asTableRow(table, r));
1306
- },
2270
+ }, "insertMany"),
1307
2271
  /**
1308
2272
  * Bir ADI kilitle — satırı değil (FR-054).
1309
2273
  *
@@ -1317,9 +2281,9 @@ function createOps(tx) {
1317
2281
  * Transaction kapsamlısı commit/rollback'te Postgres tarafından bırakılır,
1318
2282
  * yani unutulması imkânsız.
1319
2283
  */
1320
- async advisoryXactLock(key) {
2284
+ async advisoryXactLock(key2) {
1321
2285
  await (await at()).unsafe("select pg_advisory_xact_lock(hashtextextended($1, 0))", [
1322
- key
2286
+ key2
1323
2287
  ]);
1324
2288
  },
1325
2289
  /**
@@ -1393,7 +2357,7 @@ function createOps(tx) {
1393
2357
  const params = [];
1394
2358
  const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
1395
2359
  const tuple = buildValueTuple(table, cols, data, add, "claim");
1396
- const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`, params);
2360
+ const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING ${rowProjection(table)}`, params);
1397
2361
  if (!rows[0]) {
1398
2362
  throw new Error(`claim(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si).`);
1399
2363
  }
@@ -1410,7 +2374,7 @@ function createOps(tx) {
1410
2374
  return `$${params.length}`;
1411
2375
  }, "add");
1412
2376
  const where = keyCols.map((c) => `t.${quoteIdent(c)} = ${add(unique[c])}`).join(" AND ");
1413
- const found = await (await at()).unsafe(`SELECT * FROM ${quoteTable(table)} t WHERE ${where} LIMIT 1`, params);
2377
+ const found = await (await at()).unsafe(`SELECT ${rowProjection(table, "t", "*")} FROM ${quoteTable(table)} t WHERE ${where} LIMIT 1`, params);
1414
2378
  if (!found[0]) {
1415
2379
  const named = constraintOf(e);
1416
2380
  throw new Error(`claim(${table}): benzersizlik ihlali${named ? ` (${named})` : ""} yakaland\u0131 ama ${keyCols.join(", ")} ile sat\u0131r bulunamad\u0131. Ya ihlal BA\u015EKA bir unique k\u0131s\u0131ttan geliyor \u2014 o zaman claim() bu \xE7a\u011Fr\u0131 i\xE7in yanl\u0131\u015F ara\xE7 \u2014 ya da sat\u0131r var ve bir RLS policy'si onu bu \xE7a\u011F\u0131randan gizliyor.`);
@@ -1447,13 +2411,13 @@ function createOps(tx) {
1447
2411
  const bindData = [];
1448
2412
  const addPut = /* @__PURE__ */ __name((v) => `$${bindData.push(v)}`, "addPut");
1449
2413
  const ph = buildValueTuple(table, cols, data, addPut, "put");
1450
- const insertSql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${ph}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) WHERE "valid_to" IS NULL DO NOTHING RETURNING *`;
2414
+ const insertSql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${ph}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) WHERE "valid_to" IS NULL DO NOTHING RETURNING ${rowProjection(table)}`;
1451
2415
  const live = await at();
1452
2416
  const first = await live.unsafe(insertSql, bindData);
1453
2417
  if (first[0]) return asTableRow(table, first[0]);
1454
2418
  const condSql = conflict.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ");
1455
2419
  const condBind = conflict.map((c) => data[c]);
1456
- const cur = await live.unsafe(`SELECT * FROM ${quoteTable(table)} WHERE ${condSql} AND "valid_to" IS NULL`, condBind);
2420
+ const cur = await live.unsafe(`SELECT ${rowProjection(table)} FROM ${quoteTable(table)} WHERE ${condSql} AND "valid_to" IS NULL`, condBind);
1457
2421
  const old = cur[0];
1458
2422
  if (old === void 0) {
1459
2423
  throw new Error(`upsert into ${table}: e\u015Fzamanl\u0131 supersede yar\u0131\u015F\u0131 \u2014 ge\xE7erli sat\u0131r bulunamad\u0131, iste\u011Fi yineleyin (D-020)`);
@@ -1479,7 +2443,7 @@ function createOps(tx) {
1479
2443
  const conflictSet = new Set(conflict);
1480
2444
  const assignments = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
1481
2445
  const action = assignments.length ? `DO UPDATE SET ${assignments.join(", ")}` : `DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
1482
- const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
2446
+ const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING ${rowProjection(table)}`;
1483
2447
  const rows = await (await at()).unsafe(sql, params);
1484
2448
  const row = rows[0];
1485
2449
  if (!row) {
@@ -1497,7 +2461,7 @@ function createOps(tx) {
1497
2461
  return `$${params.length}`;
1498
2462
  }, "add");
1499
2463
  const assignments = buildSetAssignments(table, cols, data, add, "update");
1500
- const sql = `UPDATE ${quoteTable(table)} SET ${assignments} WHERE id = ${add(id)} RETURNING *`;
2464
+ const sql = `UPDATE ${quoteTable(table)} SET ${assignments} WHERE id = ${add(id)} RETURNING ${rowProjection(table)}`;
1501
2465
  const rows = await (await at()).unsafe(sql, params);
1502
2466
  return rows[0] ? asTableRow(table, rows[0]) : null;
1503
2467
  },
@@ -1549,9 +2513,9 @@ function createOps(tx) {
1549
2513
  assertHasPredicate(whereSql, "updateMany", table);
1550
2514
  const pk = primaryKeyOf(table);
1551
2515
  const wantRows = opts?.returning !== false;
1552
- const tail = wantRows ? "RETURNING *" : "RETURNING 1";
2516
+ const tail = wantRows ? `RETURNING ${rowProjection(table, "t", "*")}` : "RETURNING 1";
1553
2517
  const simple = isSinglePkEquality(where, pk) || pk === null;
1554
- const body = simple ? `UPDATE ${quoteTable(table)} AS t SET ${assignments} WHERE true${whereSql} ${tail}` : `UPDATE ${quoteTable(table)} AS t SET ${assignments} FROM locked WHERE t.${quoteIdent(pk)} = locked.${quoteIdent(pk)} ` + (wantRows ? "RETURNING t.*" : "RETURNING 1");
2518
+ const body = simple ? `UPDATE ${quoteTable(table)} AS t SET ${assignments} WHERE true${whereSql} ${tail}` : `UPDATE ${quoteTable(table)} AS t SET ${assignments} FROM locked WHERE t.${quoteIdent(pk)} = locked.${quoteIdent(pk)} ` + (wantRows ? `RETURNING ${rowProjection(table, "t")}` : "RETURNING 1");
1555
2519
  const lockedCte = simple ? "" : `WITH locked AS (SELECT t.${quoteIdent(pk)} FROM ${quoteTable(table)} t WHERE true${whereSql} ORDER BY t.${quoteIdent(pk)} FOR NO KEY UPDATE) `;
1556
2520
  const sql = wantRows ? `${lockedCte}${body}` : simple ? `WITH upd AS (${body}) SELECT count(*)::int AS n FROM upd` : `${lockedCte.replace(/ $/, "")}, upd AS (${body}) SELECT count(*)::int AS n FROM upd`;
1557
2521
  const out = await (await at()).unsafe(sql, params);
@@ -1636,7 +2600,7 @@ function createOps(tx) {
1636
2600
  }
1637
2601
  const groups = q.groupBy ?? [];
1638
2602
  for (const col of groups) check(col, "groupBy");
1639
- const groupSel = groups.map((c) => `t.${quoteIdent(c)} AS ${quoteIdent(`g__${c}`)}`);
2603
+ const groupSel = groups.map((c) => `${columnTypeOf(table, c) === "timestamp" ? columnResult(table, c, "t") : `t.${quoteIdent(c)}`} AS ${quoteIdent(`g__${c}`)}`);
1640
2604
  const params = [];
1641
2605
  const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
1642
2606
  const whereSql = compileWhere(table, known, q.where ?? {}, add, "aggregate");
@@ -1670,32 +2634,114 @@ function createOps(tx) {
1670
2634
  const rows = await (await at()).unsafe(sql, params);
1671
2635
  return Number(rows[0]?.n ?? 0);
1672
2636
  },
2637
+ async findUnique(table, where, opts = {}) {
2638
+ const keys = memoTable(uniqueKeysCache, currentSchema, table, () => uniqueKeysOf(tableDefOf(table) ?? {}));
2639
+ assertUniqueLookup(table, keys, where);
2640
+ const rows = await ops.findMany(table, where, {
2641
+ ...opts,
2642
+ select: opts.select === void 0 ? void 0 : [
2643
+ ...opts.select
2644
+ ],
2645
+ limit: 2
2646
+ });
2647
+ if (rows.length > 1) throw new Error(`findUnique(${table}): deployed constraints disagree with the schema; multiple rows matched`);
2648
+ return rows[0] ?? null;
2649
+ },
1673
2650
  async findById(table, id) {
1674
- const rows = await (await at()).unsafe(`SELECT * FROM ${quoteTable(table)} WHERE id = $1`, [
2651
+ const rows = await (await at()).unsafe(`SELECT ${rowProjection(table)} FROM ${quoteTable(table)} WHERE id = $1`, [
1675
2652
  id
1676
2653
  ]);
1677
2654
  return rows[0] ? asTableRow(table, rows[0]) : null;
1678
2655
  },
2656
+ compileQuery(query) {
2657
+ assertReadContract(query, tableDefOf(query.table)?.columns);
2658
+ return compileFindMany(query.table, query.where, query.options);
2659
+ },
2660
+ async readQuery(query) {
2661
+ const compiled = ops.compileQuery(query);
2662
+ return asTableRows(query.table, await (await at()).unsafe(compiled.sql, compiled.params, {
2663
+ name: query.name
2664
+ }));
2665
+ },
2666
+ async explainQuery(query) {
2667
+ const compiled = ops.compileQuery(query);
2668
+ return await (await at()).unsafe(`EXPLAIN (FORMAT JSON) ${compiled.sql}`, compiled.params, {
2669
+ name: `${query.name}.explain`
2670
+ });
2671
+ },
1679
2672
  async findMany(table, query = {}, opts = {}) {
2673
+ const compiled = compileFindMany(table, query, opts);
2674
+ return asTableRows(table, await (await at()).unsafe(compiled.sql, compiled.params), opts.with);
2675
+ },
2676
+ async page(table, query = {}, opts = {}) {
2677
+ const secret = cursorKey();
2678
+ const schema = pageSchema(table);
2679
+ const { order, size, backward, token } = pageOrder(schema.columns, schema.primaryKey, opts);
1680
2680
  const params = [];
1681
- const add = /* @__PURE__ */ __name((v) => {
1682
- params.push(v);
1683
- return `$${params.length}`;
1684
- }, "add");
2681
+ const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
1685
2682
  const known = schemaColumns(table);
1686
- const whereSql = compileWhere(table, known, query, add, "findMany");
1687
- const order = orderClause(table, known, opts.orderBy);
1688
- const limit = limitClause(opts.limit);
1689
- const offset = offsetClause(opts.offset, opts.limit);
1690
- const cols = selectClause(table, known, opts.select);
1691
- const w = opts.with !== void 0 && Object.keys(opts.with).length > 0 ? withClauses(table, opts.with, add, "findMany") : {
2683
+ const where = compileWhere(table, known, query, add, "page");
2684
+ const cols = selectClause(table, known, opts.select === void 0 ? void 0 : [
2685
+ ...opts.select
2686
+ ]);
2687
+ const scope = fingerprint({
2688
+ table,
2689
+ schema: schema.stamp,
2690
+ order,
2691
+ where,
2692
+ params,
2693
+ select: opts.select,
2694
+ with: opts.with,
2695
+ identity: context.cursorScope ?? "direct"
2696
+ });
2697
+ const values = token === void 0 ? void 0 : decodeCursor(secret, scope, token, order.length);
2698
+ const column = /* @__PURE__ */ __name((name) => `t.${quoteIdent(name)}`, "column");
2699
+ const boundary = values ? ` AND ${pageBoundary(order, values, backward, add, column)}` : "";
2700
+ const opposite = values ? `EXISTS (SELECT 1 FROM ${quoteTable(table)} t WHERE true${where} AND ${pageBoundary(order, values, !backward, add, column, true)})` : "false";
2701
+ let prefix = "__pb_page_";
2702
+ const names = [
2703
+ ...Object.keys(schema.columns),
2704
+ ...Object.keys(opts.with ?? {})
2705
+ ];
2706
+ while (names.some((n) => n.startsWith(prefix))) prefix += "_";
2707
+ const ordinal = `${prefix}ordinal`, other = `${prefix}opposite`;
2708
+ const hidden = order.map((o, i) => `${prefix}${i}`);
2709
+ const cursorCols = order.map((o, i) => `${columnTypeOf(table, o.column) === "timestamp" ? timestampResult(column(o.column)) : `${column(o.column)}::text`} AS ${quoteIdent(hidden[i])}`);
2710
+ const w = opts.with && Object.keys(opts.with).length ? withClauses(table, opts.with, add, "page") : {
1692
2711
  select: [],
1693
2712
  joins: []
1694
2713
  };
1695
- const selectList = w.select.length > 0 ? `${cols === "*" ? "t.*" : cols}, ${w.select.join(", ")}` : cols;
1696
- const joinSql = w.joins.length > 0 ? ` ${w.joins.join(" ")}` : "";
1697
- const sql = `SELECT ${selectList} FROM ${quoteTable(table)} t${joinSql} WHERE true${whereSql}${order}${limit}${offset}`;
1698
- return asTableRows(table, await (await at()).unsafe(sql, params));
2714
+ const nativeOrder = order.map((o) => `${column(o.column)} ${o.direction === "asc" !== backward ? "ASC" : "DESC"} NULLS ${o.nulls === "first" !== backward ? "FIRST" : "LAST"}`).join(", ");
2715
+ const visible = cols === "*" ? "t.*" : cols;
2716
+ const inner = `SELECT ${[
2717
+ visible,
2718
+ ...w.select,
2719
+ ...cursorCols,
2720
+ `row_number() OVER (ORDER BY ${nativeOrder}) AS ${quoteIdent(ordinal)}`
2721
+ ].join(", ")} FROM ${quoteTable(table)} t ${w.joins.join(" ")} WHERE true${where}${boundary} ORDER BY ${nativeOrder} LIMIT ${size + 1}`;
2722
+ const sql = `SELECT p.*, b.${quoteIdent(other)} FROM (SELECT ${opposite} AS ${quoteIdent(other)}) b LEFT JOIN LATERAL (${inner}) p ON true ORDER BY p.${quoteIdent(ordinal)}`;
2723
+ const result = await (await at()).unsafe(sql, params);
2724
+ const hasOpposite = result[0]?.[other] === true;
2725
+ let rows = result.filter((row) => row[ordinal] !== null && row[ordinal] !== void 0);
2726
+ const hasMore = rows.length > size;
2727
+ rows = rows.slice(0, size);
2728
+ if (backward) rows.reverse();
2729
+ const cursor = /* @__PURE__ */ __name((row) => row ? encodeCursor(secret, scope, hidden.map((name) => row[name] === null ? null : String(row[name]))) : null, "cursor");
2730
+ const pageInfo = {
2731
+ startCursor: cursor(rows[0]),
2732
+ endCursor: cursor(rows.at(-1)),
2733
+ hasNextPage: backward ? hasOpposite : hasMore,
2734
+ hasPreviousPage: backward ? hasMore : hasOpposite
2735
+ };
2736
+ for (const row of rows) for (const name of [
2737
+ ...hidden,
2738
+ ordinal,
2739
+ other
2740
+ ]) delete row[name];
2741
+ return {
2742
+ rows: asTableRows(table, rows, opts.with),
2743
+ pageInfo
2744
+ };
1699
2745
  },
1700
2746
  /**
1701
2747
  * Tek-SQL hibrit arama (FR-014): iki kol CTE + FULL OUTER JOIN + RRF
@@ -1800,7 +2846,7 @@ function createOps(tx) {
1800
2846
  kwC = `SELECT c.${pidQ} AS pid, c.chunk_seq, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(c.palbase_fts, websearch_to_tsquery('simple', ${qpC})) DESC) AS r FROM ${quoteTable(ck.table)} c${parentJoinC}c.palbase_fts @@ websearch_to_tsquery('simple', ${qpC}) ORDER BY r LIMIT ${cpool}`;
1801
2847
  }
1802
2848
  const msC = params.minScore === void 0 ? "" : addC(params.minScore);
1803
- const colListC = cfg.cols.map((c) => `t.${quoteIdent(c)}`).join(", ");
2849
+ const colListC = cfg.cols.map((c) => columnTypeOf(table, c) === "timestamp" ? projectedColumn(table, c, "t") : `t.${quoteIdent(c)}`).join(", ");
1804
2850
  const scoreC = scoreMul === "" ? "g._score" : `(g._score)${scoreMul}`;
1805
2851
  const assembleC = /* @__PURE__ */ __name((kwIn, withKwn) => {
1806
2852
  const kwnCol = withKwn ? `, (SELECT count(*) FROM kw)::int AS _kwn` : "";
@@ -1882,7 +2928,7 @@ function createOps(tx) {
1882
2928
  qpRef = qp;
1883
2929
  kwSql = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(t.palbase_fts, websearch_to_tsquery('simple', ${qp})) DESC) AS r FROM ${quoteTable(table)} t WHERE t.palbase_fts @@ websearch_to_tsquery('simple', ${qp})${whereSql} ORDER BY r LIMIT ${pool}`;
1884
2930
  }
1885
- const colList = cfg.cols.map((c) => `t.${quoteIdent(c)}`).join(", ");
2931
+ const colList = cfg.cols.map((c) => columnTypeOf(table, c) === "timestamp" ? projectedColumn(table, c, "t") : `t.${quoteIdent(c)}`).join(", ");
1886
2932
  const msP = params.minScore === void 0 ? "" : add(params.minScore);
1887
2933
  const hlCol = params.highlight === true && wantText ? `, ts_headline('simple', ${ftsExprOf(cfg.ftsCols)}, websearch_to_tsquery('simple', ${qpRef})) AS _highlight` : "";
1888
2934
  const assemble = /* @__PURE__ */ __name((kwIn, withKwn) => {
@@ -2090,7 +3136,7 @@ function createOps(tx) {
2090
3136
  const params = [];
2091
3137
  const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
2092
3138
  const tuple = buildValueTuple(table, cols, data, add, "supersede");
2093
- const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`, params);
3139
+ const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING ${rowProjection(table)}`, params);
2094
3140
  if (!rows[0]) {
2095
3141
  throw new Error(`supersede(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si)`);
2096
3142
  }
@@ -2100,7 +3146,7 @@ function createOps(tx) {
2100
3146
  /** A real SAVEPOINT inside the request's transaction. */
2101
3147
  async transaction(cb) {
2102
3148
  const live = await at();
2103
- return live.savepoint(async (sp) => cb(withTables(createOps(sp), currentSchema)));
3149
+ return live.savepoint(async (sp) => cb(withTables(createOps(sp, context), currentSchema)));
2104
3150
  },
2105
3151
  /**
2106
3152
  * Run `fn` against a handle bound to a SAVEPOINT, so a failure inside it
@@ -2121,7 +3167,7 @@ function createOps(tx) {
2121
3167
  */
2122
3168
  async attempt(fn) {
2123
3169
  const live = await at();
2124
- return live.savepoint(async (sp) => fn(withTables(createOps(sp), currentSchema)));
3170
+ return live.savepoint(async (sp) => fn(withTables(createOps(sp, context), currentSchema)));
2125
3171
  },
2126
3172
  /**
2127
3173
  * Execute a whole transaction plan — what `Database.$transaction(fn)` builds.
@@ -2141,6 +3187,62 @@ function createOps(tx) {
2141
3187
  * "transport.txPlan is not a function" while every test that covered
2142
3188
  * transactions passed against a mock that did implement it.
2143
3189
  */
3190
+ async command(plan, options = {}) {
3191
+ if (options.mode !== void 0 && ![
3192
+ "auto",
3193
+ "compiled",
3194
+ "sequential"
3195
+ ].includes(options.mode)) throw new Error("command mode must be auto, compiled or sequential");
3196
+ if (options.mode === "sequential") return ops.txPlan(plan);
3197
+ const compiled = context.commands ? compileCommand(plan) : {
3198
+ reason: "the PostgreSQL command executor is not installed"
3199
+ };
3200
+ if ("reason" in compiled) {
3201
+ if (options.mode === "compiled") throw new Error(`Command cannot compile: ${compiled.reason}. Use auto/sequential mode or installCommandExecutor at boot.`);
3202
+ return ops.txPlan(plan);
3203
+ }
3204
+ if (compiled.steps.length === 0) return {
3205
+ results: []
3206
+ };
3207
+ if (compiled.pipeline) {
3208
+ return (await at()).savepoint(async (sp) => {
3209
+ const settled = await Promise.allSettled(compiled.pipeline.map((statement) => sp.unsafe(statement.sql, statement.params)));
3210
+ const failed = settled.find((result) => result.status === "rejected");
3211
+ if (failed?.status === "rejected") {
3212
+ const error = failed.reason;
3213
+ if (sqlstateOf(error) === SQLSTATE.commandGuardFailed && typeof error.detail === "string") {
3214
+ const detail = JSON.parse(error.detail);
3215
+ if (detail.error_code === "tx_guard_failed" && Number.isInteger(detail.slot)) {
3216
+ throw Object.assign(new Error("Command expectation failed"), detail);
3217
+ }
3218
+ }
3219
+ throw error;
3220
+ }
3221
+ return {
3222
+ results: settled.map((result, i) => {
3223
+ const rows2 = result.value[0].rows;
3224
+ return {
3225
+ rows: asTableRows(plan.ops[i].table, rows2),
3226
+ rows_affected: rows2.length
3227
+ };
3228
+ })
3229
+ };
3230
+ });
3231
+ }
3232
+ const rows = await (await at()).savepoint((sp) => sp.unsafe("SELECT palbase_runtime.command_v1($1::jsonb -> 'steps') AS result", [
3233
+ {
3234
+ steps: compiled.steps
3235
+ }
3236
+ ]));
3237
+ const response = rows[0].result;
3238
+ if (response.error) throw Object.assign(new Error(response.error.error_code === "tx_ref_unresolved" ? "Command reference has no source row/column; use expectOne before reading it" : "Command expectation failed"), response.error);
3239
+ return {
3240
+ results: response.results.map((result, i) => ({
3241
+ ...result,
3242
+ rows: asTableRows(plan.ops[i].table, result.rows)
3243
+ }))
3244
+ };
3245
+ },
2144
3246
  async txPlan(plan) {
2145
3247
  const live = await at();
2146
3248
  return live.savepoint(async (sp) => {
@@ -2164,27 +3266,55 @@ function createOps(tx) {
2164
3266
  }
2165
3267
  __name(createOps, "createOps");
2166
3268
  var currentSchema = {};
3269
+ var columnsMemo = /* @__PURE__ */ new WeakMap();
3270
+ var primaryMemo = /* @__PURE__ */ new WeakMap();
3271
+ var vectorMemo = /* @__PURE__ */ new WeakMap();
3272
+ var transformMemo = /* @__PURE__ */ new WeakMap();
3273
+ var typeMemo = /* @__PURE__ */ new WeakMap();
3274
+ var timestampMemo = /* @__PURE__ */ new WeakMap();
3275
+ var projectionMemo = /* @__PURE__ */ new WeakMap();
3276
+ function schemaColumns(table) {
3277
+ return memoTable(columnsMemo, currentSchema, table, () => computeSchemaColumns(table));
3278
+ }
3279
+ __name(schemaColumns, "schemaColumns");
3280
+ function primaryKeyOf(table) {
3281
+ return memoTable(primaryMemo, currentSchema, table, () => computePrimaryKey(table));
3282
+ }
3283
+ __name(primaryKeyOf, "primaryKeyOf");
3284
+ function vectorColumnsOf(schema, table) {
3285
+ return memoTable(vectorMemo, schema, table, () => computeVectorColumns(schema, table));
3286
+ }
3287
+ __name(vectorColumnsOf, "vectorColumnsOf");
3288
+ function transformsOf(schema, table) {
3289
+ return memoTable(transformMemo, schema, table, () => computeTransforms(schema, table));
3290
+ }
3291
+ __name(transformsOf, "transformsOf");
3292
+ function columnTypeOf(table, col) {
3293
+ const types = memoTable(typeMemo, currentSchema, table, () => new Map([
3294
+ ...schemaColumns(table) ?? []
3295
+ ].map((c) => [
3296
+ c,
3297
+ computeColumnType(table, c)
3298
+ ])));
3299
+ return types.get(col) ?? null;
3300
+ }
3301
+ __name(columnTypeOf, "columnTypeOf");
2167
3302
  var currentRelations = /* @__PURE__ */ new Map();
2168
3303
  function setSchema(schemas) {
2169
- cachedVectorSchema = null;
2170
- cachedTrgmSchema = null;
2171
- const tables = {};
3304
+ const tables = /* @__PURE__ */ Object.create(null);
2172
3305
  for (const entry of schemas) {
2173
3306
  const mod = entry;
2174
3307
  const def = (mod && "default" in mod ? mod.default : mod) ?? {};
2175
3308
  const schemaName = def.name ?? "public";
2176
- for (const [key, table] of Object.entries(def.tables ?? {})) {
2177
- tables[qualifiedTableKey(schemaName, table.name ?? key)] = table;
3309
+ for (const [key2, table] of Object.entries(def.tables ?? {})) {
3310
+ tables[qualifiedTableKey(schemaName, table.name ?? key2)] = table;
2178
3311
  }
2179
3312
  }
2180
- currentSchema = {
2181
- tables
2182
- };
2183
- currentRelations = buildRelations(schemas.map((entry) => {
3313
+ const relations = buildRelations(schemas.map((entry) => {
2184
3314
  const mod = entry;
2185
3315
  const def = (mod && "default" in mod ? mod.default : mod) ?? {};
2186
- const tables2 = {};
2187
- for (const [key, t] of Object.entries(def.tables ?? {})) {
3316
+ const tables2 = /* @__PURE__ */ Object.create(null);
3317
+ for (const [key2, t] of Object.entries(def.tables ?? {})) {
2188
3318
  const table = t ?? {};
2189
3319
  const columns = {};
2190
3320
  for (const [cname, c] of Object.entries(table.columns ?? {})) {
@@ -2194,9 +3324,9 @@ function setSchema(schemas) {
2194
3324
  _def: cd
2195
3325
  };
2196
3326
  }
2197
- tables2[key] = {
3327
+ tables2[key2] = {
2198
3328
  ...table,
2199
- name: table.name ?? key,
3329
+ name: table.name ?? key2,
2200
3330
  columns
2201
3331
  };
2202
3332
  }
@@ -2205,40 +3335,86 @@ function setSchema(schemas) {
2205
3335
  tables: tables2
2206
3336
  };
2207
3337
  }));
3338
+ for (const key2 of Object.keys(tables)) tables[key2] = snapshotDeclaration(tables[key2]);
3339
+ currentSchema = Object.freeze({
3340
+ tables: Object.freeze(tables)
3341
+ });
3342
+ currentRelations = relations;
3343
+ cachedVectorSchema = null;
3344
+ cachedTrgmSchema = null;
2208
3345
  }
2209
3346
  __name(setSchema, "setSchema");
3347
+ var tableNamespaces = /* @__PURE__ */ new WeakMap();
2210
3348
  function withTables(ops, schema = currentSchema) {
2211
- const bind = /* @__PURE__ */ __name((key) => ({
2212
- insert: /* @__PURE__ */ __name((data) => ops.insert(key, data), "insert"),
2213
- update: /* @__PURE__ */ __name((id, data) => ops.update(key, id, data), "update"),
2214
- delete: /* @__PURE__ */ __name((id) => ops.delete(key, id), "delete"),
2215
- findById: /* @__PURE__ */ __name((id) => ops.findById(key, id), "findById"),
2216
- findMany: /* @__PURE__ */ __name((query, opts) => ops.findMany(key, query ?? {}, opts), "findMany"),
2217
- put: /* @__PURE__ */ __name((q) => ops.put(key, q.data, {
3349
+ let namespaces = tableNamespaces.get(schema);
3350
+ if (!namespaces) {
3351
+ namespaces = /* @__PURE__ */ new Map();
3352
+ for (const key2 of Object.keys(schema.tables ?? {})) {
3353
+ const dot = key2.indexOf(".");
3354
+ const name = dot === -1 ? "public" : key2.slice(0, dot);
3355
+ const short = dot === -1 ? key2 : key2.slice(dot + 1);
3356
+ if (!namespaces.has(name)) namespaces.set(name, /* @__PURE__ */ new Map());
3357
+ namespaces.get(name).set(short, key2);
3358
+ }
3359
+ if (Object.isFrozen(schema) && Object.isFrozen(schema.tables)) tableNamespaces.set(schema, namespaces);
3360
+ }
3361
+ const bind = /* @__PURE__ */ __name((key2) => ({
3362
+ insert: /* @__PURE__ */ __name((data) => ops.insert(key2, data), "insert"),
3363
+ update: /* @__PURE__ */ __name((id, data) => ops.update(key2, id, data), "update"),
3364
+ delete: /* @__PURE__ */ __name((id) => ops.delete(key2, id), "delete"),
3365
+ findById: /* @__PURE__ */ __name((id) => ops.findById(key2, id), "findById"),
3366
+ findUnique: /* @__PURE__ */ __name((where, opts) => ops.findUnique(key2, where, opts), "findUnique"),
3367
+ findMany: /* @__PURE__ */ __name((query, opts) => ops.findMany(key2, query ?? {}, opts), "findMany"),
3368
+ page: /* @__PURE__ */ __name((query, opts) => ops.page(key2, query ?? {}, opts), "page"),
3369
+ put: /* @__PURE__ */ __name((q) => ops.put(key2, q.data, {
2218
3370
  onConflict: q.onConflict
2219
3371
  }), "put")
2220
3372
  }), "bind");
2221
- const tables = {};
2222
- for (const key of Object.keys(schema.tables ?? {})) {
2223
- if (!key.includes(".")) tables[key] = bind(key);
2224
- }
3373
+ const surfaces = /* @__PURE__ */ new Map();
2225
3374
  const schemaOf = /* @__PURE__ */ __name((name) => {
2226
- const out = {};
2227
- const prefix = `${name}.`;
2228
- for (const key of Object.keys(schema.tables ?? {})) {
2229
- if (name === "public") {
2230
- if (!key.includes(".")) out[key] = bind(key);
2231
- } else if (key.startsWith(prefix)) {
2232
- out[key.slice(prefix.length)] = bind(key);
3375
+ const cached = surfaces.get(name);
3376
+ if (cached) return cached;
3377
+ const names = namespaces.get(name) ?? /* @__PURE__ */ new Map();
3378
+ const target = /* @__PURE__ */ Object.create(null);
3379
+ const get = /* @__PURE__ */ __name((key2) => {
3380
+ if (!Object.hasOwn(target, key2) && names.has(key2)) {
3381
+ Object.defineProperty(target, key2, {
3382
+ value: bind(names.get(key2)),
3383
+ configurable: true,
3384
+ enumerable: true,
3385
+ writable: true
3386
+ });
2233
3387
  }
2234
- }
2235
- return {
2236
- tables: out
3388
+ return Reflect.get(target, key2);
3389
+ }, "get");
3390
+ const tables = new Proxy(target, {
3391
+ get: /* @__PURE__ */ __name((t, key2, receiver) => typeof key2 === "string" ? get(key2) : Reflect.get(t, key2, receiver), "get"),
3392
+ has: /* @__PURE__ */ __name((t, key2) => typeof key2 === "string" && names.has(key2) || Reflect.has(t, key2), "has"),
3393
+ ownKeys: /* @__PURE__ */ __name((t) => [
3394
+ .../* @__PURE__ */ new Set([
3395
+ ...names.keys(),
3396
+ ...Reflect.ownKeys(t)
3397
+ ])
3398
+ ], "ownKeys"),
3399
+ getOwnPropertyDescriptor: /* @__PURE__ */ __name((t, key2) => Reflect.getOwnPropertyDescriptor(t, key2) ?? (typeof key2 === "string" && names.has(key2) ? {
3400
+ configurable: true,
3401
+ enumerable: true,
3402
+ get: /* @__PURE__ */ __name(() => get(key2), "get")
3403
+ } : void 0), "getOwnPropertyDescriptor"),
3404
+ preventExtensions: /* @__PURE__ */ __name((t) => {
3405
+ for (const key2 of names.keys()) get(key2);
3406
+ return Reflect.preventExtensions(t);
3407
+ }, "preventExtensions")
3408
+ });
3409
+ const surface = {
3410
+ tables
2237
3411
  };
3412
+ if (namespaces.has(name) || name === "public") surfaces.set(name, surface);
3413
+ return surface;
2238
3414
  }, "schemaOf");
2239
3415
  const base = /* @__PURE__ */ Object.create(null);
2240
3416
  return Object.assign(base, ops, {
2241
- tables,
3417
+ tables: schemaOf("public").tables,
2242
3418
  schema: schemaOf
2243
3419
  });
2244
3420
  }
@@ -2296,8 +3472,8 @@ function diagnosingDriver(sql, surface = "service") {
2296
3472
  return markEngineRaised(new UniqueViolation(constraintOf(e)));
2297
3473
  }
2298
3474
  const state = sqlstateOf(e);
2299
- if (state === "40001") return markEngineRaised(new SerializationFailure(state));
2300
- if (state === "40P01") return markEngineRaised(new DeadlockDetected(state));
3475
+ if (state === SQLSTATE.serializationFailure) return markEngineRaised(new SerializationFailure(state));
3476
+ if (state === SQLSTATE.deadlockDetected) return markEngineRaised(new DeadlockDetected(state));
2301
3477
  if (surface === "service" && isForeignKeyViolation(e)) {
2302
3478
  return new Error(`Database.$asService() hit a foreign key with nothing to point at. It runs in its OWN transaction on its OWN connection, so a row this request wrote through \`Database.*\` is not visible to it until the request commits \u2014 and the request cannot commit until the handler returns. If the row it needs is one this request just created, do both writes on ONE surface. (${original(e)})`);
2303
3479
  }
@@ -2307,11 +3483,16 @@ function diagnosingDriver(sql, surface = "service") {
2307
3483
  return e;
2308
3484
  }, "explain");
2309
3485
  const wrapTx = /* @__PURE__ */ __name((tx) => ({
2310
- async unsafe(text, params) {
3486
+ unsafe(text, params) {
2311
3487
  try {
2312
- return await tx.unsafe(text, params);
3488
+ const query = tx.unsafe(text, params);
3489
+ const mapped = query.catch((e) => {
3490
+ throw explain(e);
3491
+ });
3492
+ if (query.cancel) mapped.cancel = () => query.cancel();
3493
+ return mapped;
2313
3494
  } catch (e) {
2314
- throw explain(e);
3495
+ return Promise.reject(explain(e));
2315
3496
  }
2316
3497
  },
2317
3498
  savepoint(cb) {
@@ -2320,34 +3501,120 @@ function diagnosingDriver(sql, surface = "service") {
2320
3501
  }), "wrapTx");
2321
3502
  return {
2322
3503
  unsafe: /* @__PURE__ */ __name((text, params) => sql.unsafe(text, params), "unsafe"),
2323
- begin: /* @__PURE__ */ __name((cb) => sql.begin((tx) => cb(wrapTx(tx))), "begin")
3504
+ cancelBackend: sql.cancelBackend?.bind(sql),
3505
+ begin: /* @__PURE__ */ __name((cb) => sql.begin((tx) => cb(wrapTx(tx))).catch((e) => {
3506
+ throw explain(e);
3507
+ }), "begin")
2324
3508
  };
2325
3509
  }
2326
3510
  __name(diagnosingDriver, "diagnosingDriver");
2327
- function createRequestDatabase(sql, identity) {
2328
- const tx = createLazyTransaction(diagnosingDriver(sql, "user"), identity.role, identity.claimsJson);
3511
+ function createRequestDatabase(sql, identity, diagnostics) {
3512
+ const trace = diagnostics === void 0 ? void 0 : new DatabaseTrace(diagnostics);
3513
+ let requestClosed = false;
3514
+ const contextFor = /* @__PURE__ */ __name((role) => {
3515
+ let scope;
3516
+ return {
3517
+ trace,
3518
+ get cursorScope() {
3519
+ return scope ??= cursorIdentity(role, identity.claimsJson);
3520
+ },
3521
+ get commands() {
3522
+ return hasCommandExecutor(sql);
3523
+ }
3524
+ };
3525
+ }, "contextFor");
3526
+ const userContext = contextFor(identity.role);
3527
+ const serviceContext = contextFor(identity.serviceRole);
3528
+ const atomic = /* @__PURE__ */ __name((role, surface) => async (fn, options = {}) => {
3529
+ validateAtomicOptions(options);
3530
+ if (requestClosed) throw new Error("Database request has settled; it cannot open another transaction");
3531
+ if (atomicScope.getStore() !== void 0) throw new Error("Nested Database.$atomic is not supported; use tx.$attempt for a savepoint");
3532
+ const retries = retryBudget(options.retry);
3533
+ const control = options.timeoutMs !== void 0 || options.signal !== void 0 ? new TransactionControl(options.timeoutMs, options.signal) : void 0;
3534
+ let lease;
3535
+ try {
3536
+ control?.check();
3537
+ if (options.workload) {
3538
+ const queuedAt = trace ? performance.now() : 0;
3539
+ try {
3540
+ lease = await workloadAdmission(sql, options.workload).acquire(workloadTenant(options.workload, identity.claimsJson), options.conflictKeys ?? [], control?.signal);
3541
+ } finally {
3542
+ trace?.time("queueMs", queuedAt);
3543
+ }
3544
+ }
3545
+ for (let attempt = 0; ; attempt++) {
3546
+ control?.check();
3547
+ const timeouts = [
3548
+ options.statementTimeoutMs,
3549
+ control?.remainingMs()
3550
+ ].filter((n) => n !== void 0);
3551
+ const scope = /* @__PURE__ */ Symbol("atomic transaction");
3552
+ const root = createLazyTransaction(diagnosingDriver(sql, surface), role, identity.claimsJson, {
3553
+ scope,
3554
+ isolation: options.isolation,
3555
+ readOnly: options.readOnly,
3556
+ control,
3557
+ trace,
3558
+ lockTimeout: `${options.lockTimeoutMs ?? 5e3}ms`,
3559
+ statementTimeout: timeouts.length === 0 ? void 0 : `${Math.min(...timeouts)}ms`
3560
+ });
3561
+ try {
3562
+ const callback = atomicScope.run(scope, () => fn(withTables(createOps(root, surface === "user" ? userContext : serviceContext))));
3563
+ const value = await (control ? control.race(callback) : callback);
3564
+ control?.check();
3565
+ await root.commit();
3566
+ return value;
3567
+ } catch (error) {
3568
+ await root.rollback(error);
3569
+ if (attempt >= retries || !isRetryable(error)) throw error;
3570
+ trace?.retry();
3571
+ const delay = new Promise((resolve) => setTimeout(resolve, Math.min(100, 2 ** attempt)));
3572
+ await (control ? control.race(delay) : delay);
3573
+ }
3574
+ }
3575
+ } finally {
3576
+ control?.dispose();
3577
+ lease?.release();
3578
+ }
3579
+ }, "atomic");
3580
+ const tx = createLazyTransaction(diagnosingDriver(sql, "user"), identity.role, identity.claimsJson, {
3581
+ trace
3582
+ });
2329
3583
  let serviceTx = null;
2330
3584
  let serviceClient = null;
2331
3585
  const asService = /* @__PURE__ */ __name(() => {
2332
3586
  if (serviceClient === null) {
2333
3587
  serviceTx = createLazyTransaction(diagnosingDriver(sql, "service"), identity.serviceRole, identity.claimsJson, {
2334
- lockTimeout: SERVICE_LOCK_TIMEOUT
3588
+ lockTimeout: SERVICE_LOCK_TIMEOUT,
3589
+ trace
3590
+ });
3591
+ serviceClient = Object.assign(withTables(createOps(serviceTx, serviceContext)), {
3592
+ atomic: atomic(identity.serviceRole, "service")
2335
3593
  });
2336
- serviceClient = withTables(createOps(serviceTx));
2337
3594
  }
2338
3595
  return serviceClient;
2339
3596
  }, "asService");
2340
3597
  return {
2341
- client: Object.assign(withTables(createOps(tx)), {
2342
- asService
3598
+ client: Object.assign(withTables(createOps(tx, userContext)), {
3599
+ asService,
3600
+ atomic: atomic(identity.role, "user")
2343
3601
  }),
2344
3602
  async commit() {
2345
- await tx.commit();
2346
- await serviceTx?.commit();
3603
+ requestClosed = true;
3604
+ try {
3605
+ await tx.commit();
3606
+ await serviceTx?.commit();
3607
+ trace?.complete("committed");
3608
+ } catch (error) {
3609
+ trace?.failed();
3610
+ throw error;
3611
+ }
2347
3612
  },
2348
3613
  async rollback(reason) {
3614
+ requestClosed = true;
2349
3615
  await tx.rollback(reason);
2350
3616
  await serviceTx?.rollback(reason);
3617
+ trace?.complete("failed");
2351
3618
  }
2352
3619
  };
2353
3620
  }
@@ -2357,8 +3624,13 @@ var Args = class Args2 {
2357
3624
  __name(this, "Args");
2358
3625
  }
2359
3626
  values = [];
2360
- bind(value) {
3627
+ columns = [];
3628
+ bind(value, column, table) {
2361
3629
  this.values.push(value);
3630
+ this.columns.push({
3631
+ column,
3632
+ table
3633
+ });
2362
3634
  return `$${this.values.length}`;
2363
3635
  }
2364
3636
  };
@@ -2444,7 +3716,7 @@ function planWhere(table, where, args, results) {
2444
3716
  }
2445
3717
  const resolved = resolveRefsDeep(where, results);
2446
3718
  const known = schemaColumns(table);
2447
- const bare = compileWhereBare(table, known, resolved, (v) => args.bind(v), "transaction plan");
3719
+ const bare = compileWhereBare(table, known, resolved, (v, column, from) => args.bind(v, column, from), "transaction plan");
2448
3720
  return {
2449
3721
  sql: bare === "" ? "" : ` AND ${bare}`,
2450
3722
  resolved
@@ -2453,16 +3725,16 @@ function planWhere(table, where, args, results) {
2453
3725
  __name(planWhere, "planWhere");
2454
3726
  function bindMaybeVector(args, column, vectorCols, value, transforms) {
2455
3727
  if (column !== void 0 && vectorCols?.has(column) && Array.isArray(value)) {
2456
- return args.bind(toVectorLiteral(value));
3728
+ return args.bind(toVectorLiteral(value), column);
2457
3729
  }
2458
3730
  const t = column !== void 0 ? transforms?.get(column) : void 0;
2459
3731
  if (t?.toDb !== void 0 && value !== null && value !== void 0) {
2460
- return args.bind(t.toDb(value));
3732
+ return args.bind(t.toDb(value), column);
2461
3733
  }
2462
- return args.bind(value);
3734
+ return args.bind(value, column);
2463
3735
  }
2464
3736
  __name(bindMaybeVector, "bindMaybeVector");
2465
- async function runPlanOp(sp, op, results) {
3737
+ function compilePlanOp(op, results) {
2466
3738
  const opVectorCols = vectorColumnsOf(currentSchema, op.table);
2467
3739
  const opTransforms = transformsOf(currentSchema, op.table);
2468
3740
  const args = new Args();
@@ -2472,7 +3744,7 @@ async function runPlanOp(sp, op, results) {
2472
3744
  case "insert": {
2473
3745
  const cols = Object.keys(op.values ?? {});
2474
3746
  const rendered = cols.map((c) => renderValue(op.values[c], c, args, results, opVectorCols, opTransforms));
2475
- sql = cols.length ? `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) RETURNING *` : `INSERT INTO ${table} DEFAULT VALUES RETURNING *`;
3747
+ sql = cols.length ? `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) RETURNING ${rowProjection(op.table)}` : `INSERT INTO ${table} DEFAULT VALUES RETURNING ${rowProjection(op.table)}`;
2476
3748
  break;
2477
3749
  }
2478
3750
  case "upsert": {
@@ -2485,17 +3757,17 @@ async function runPlanOp(sp, op, results) {
2485
3757
  });
2486
3758
  }
2487
3759
  const rendered = cols.map((c) => renderValue(op.values[c], c, args, results, opVectorCols, opTransforms));
2488
- sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) ${onConflictTail(cols, conflict, "update")} RETURNING *`;
3760
+ sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) ${onConflictTail(cols, conflict, "update")} RETURNING ${rowProjection(op.table)}`;
2489
3761
  break;
2490
3762
  }
2491
3763
  case "insertMany": {
2492
3764
  const rows = op.rows ?? [];
2493
- if (rows.length === 0 || !rows[0]) return [];
3765
+ if (rows.length === 0 || !rows[0]) return null;
2494
3766
  const cols = Object.keys(rows[0]);
2495
3767
  const tuples = rows.map((r) => `(${cols.map((c) => renderValue(r[c], c, args, results, opVectorCols, opTransforms)).join(", ")})`);
2496
3768
  const conflictCols = op.onConflict ?? [];
2497
3769
  const tail = op.action !== void 0 && conflictCols.length > 0 ? ` ${onConflictTail(cols, conflictCols, op.action)}` : "";
2498
- sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail} RETURNING *`;
3770
+ sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail} RETURNING ${rowProjection(op.table)}`;
2499
3771
  break;
2500
3772
  }
2501
3773
  case "update": {
@@ -2511,12 +3783,12 @@ async function runPlanOp(sp, op, results) {
2511
3783
  const assignments = cols.map((c) => `${quoteIdent(c)} = ${renderValue(op.set[c], c, args, results, opVectorCols, opTransforms)}`);
2512
3784
  const planned = planWhere(op.table, op.where, args, results);
2513
3785
  const planPk = primaryKeyOf(op.table);
2514
- sql = isSinglePkEquality(planned.resolved, planPk) || planPk === null ? `UPDATE ${table} AS t SET ${assignments.join(", ")} WHERE true${planned.sql} RETURNING t.*` : `WITH locked AS (SELECT t.${quoteIdent(planPk)} FROM ${table} t WHERE true${planned.sql} ORDER BY t.${quoteIdent(planPk)} FOR NO KEY UPDATE) UPDATE ${table} AS t SET ${assignments.join(", ")} FROM locked WHERE t.${quoteIdent(planPk)} = locked.${quoteIdent(planPk)} RETURNING t.*`;
3786
+ sql = isSinglePkEquality(planned.resolved, planPk) || planPk === null ? `UPDATE ${table} AS t SET ${assignments.join(", ")} WHERE true${planned.sql} RETURNING ${rowProjection(op.table, "t")}` : `WITH locked AS (SELECT t.${quoteIdent(planPk)} FROM ${table} t WHERE true${planned.sql} ORDER BY t.${quoteIdent(planPk)} FOR NO KEY UPDATE) UPDATE ${table} AS t SET ${assignments.join(", ")} FROM locked WHERE t.${quoteIdent(planPk)} = locked.${quoteIdent(planPk)} RETURNING ${rowProjection(op.table, "t")}`;
2515
3787
  break;
2516
3788
  }
2517
3789
  case "delete": {
2518
3790
  assertNotAppendOnly("deleteWhere", op.table);
2519
- sql = `DELETE FROM ${table} AS t WHERE true${renderWhere(op.table, op.where, args, results)} RETURNING t.*`;
3791
+ sql = `DELETE FROM ${table} AS t WHERE true${renderWhere(op.table, op.where, args, results)} RETURNING ${rowProjection(op.table, "t")}`;
2520
3792
  break;
2521
3793
  }
2522
3794
  case "select": {
@@ -2529,15 +3801,157 @@ async function runPlanOp(sp, op, results) {
2529
3801
  limit = ` LIMIT ${n}`;
2530
3802
  }
2531
3803
  const lock = op.lock === "update" ? " FOR UPDATE" : "";
2532
- sql = `SELECT t.* FROM ${table} t WHERE true${renderWhere(op.table, op.where, args, results)}${limit}${lock}`;
3804
+ sql = `SELECT ${rowProjection(op.table, "t")} FROM ${table} t WHERE true${renderWhere(op.table, op.where, args, results)}${limit}${lock}`;
2533
3805
  break;
2534
3806
  }
2535
3807
  default:
2536
3808
  throw new Error(`unknown operation "${String(op.op)}" in a transaction plan`);
2537
3809
  }
2538
- return await sp.unsafe(sql, args.values);
3810
+ return {
3811
+ sql,
3812
+ params: args.values,
3813
+ columns: args.columns
3814
+ };
3815
+ }
3816
+ __name(compilePlanOp, "compilePlanOp");
3817
+ async function runPlanOp(sp, op, results) {
3818
+ const statement = compilePlanOp(op, results);
3819
+ return statement ? await sp.unsafe(statement.sql, statement.params) : [];
2539
3820
  }
2540
3821
  __name(runPlanOp, "runPlanOp");
3822
+ function compileCommand(plan) {
3823
+ const symbols = /* @__PURE__ */ new Map();
3824
+ const results = [];
3825
+ const steps = [];
3826
+ const pipeline = [];
3827
+ let hasRefs = false;
3828
+ const prefix = `command:${crypto.randomUUID()}:`;
3829
+ const containsFragment = /* @__PURE__ */ __name((v) => isSqlFragment(v) || v !== null && typeof v === "object" && Object.values(v).some(containsFragment), "containsFragment");
3830
+ const hasNullableFilterRef = /* @__PURE__ */ __name((v) => {
3831
+ if (isRef(v)) {
3832
+ const source = plan.ops[v.$ref.op];
3833
+ return source !== void 0 && pageSchema(source.table).columns[v.$ref.field]?.nullable !== false;
3834
+ }
3835
+ return v !== null && typeof v === "object" && Object.values(v).some(hasNullableFilterRef);
3836
+ }, "hasNullableFilterRef");
3837
+ for (const op of plan.ops) {
3838
+ if (!schemaColumns(op.table)?.size) return {
3839
+ reason: `table ${op.table} has no installed declaration`
3840
+ };
3841
+ if (transformsOf(currentSchema, op.table).size || vectorColumnsOf(currentSchema, op.table).size) return {
3842
+ reason: `${op.table} uses JavaScript codecs/transforms or vector values`
3843
+ };
3844
+ if (containsFragment(op)) return {
3845
+ reason: "SQL fragments require the sequential path"
3846
+ };
3847
+ if (hasNullableFilterRef(op.where)) return {
3848
+ reason: "nullable references in filters require the sequential path"
3849
+ };
3850
+ }
3851
+ for (const [index, op] of plan.ops.entries()) {
3852
+ const statement = compilePlanOp(op, results);
3853
+ if (statement === null) {
3854
+ steps.push({
3855
+ empty: true,
3856
+ guard: op.guard
3857
+ });
3858
+ pipeline.push({
3859
+ sql: "SELECT palbase_runtime.command_rows_v1('[]'::jsonb,$1::jsonb) AS rows",
3860
+ params: [
3861
+ op.guard ?? null
3862
+ ]
3863
+ });
3864
+ } else {
3865
+ if (statement.params.some((v) => v === void 0 || typeof v === "symbol" || typeof v === "function" || typeof v === "number" && (!Number.isInteger(v) || v < -2147483648 || v > 2147483647))) {
3866
+ return {
3867
+ reason: "non-integer/wide numeric or non-JSON parameters require the sequential driver path; use exact decimal strings"
3868
+ };
3869
+ }
3870
+ for (const [i, value] of statement.params.entries()) {
3871
+ const target = statement.columns[i];
3872
+ if (target.column === void 0 || columnTypeOf(target.table ?? op.table, target.column) !== "jsonb") continue;
3873
+ const source = typeof value === "string" ? symbols.get(value) : void 0;
3874
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || source && "ref" in source && ![
3875
+ "text",
3876
+ "uuid",
3877
+ "timestamp",
3878
+ "enum"
3879
+ ].includes(columnTypeOf(plan.ops[source.ref.op].table, source.ref.field) ?? "")) {
3880
+ return {
3881
+ reason: "JSONB scalar/ref binding requires the sequential driver path"
3882
+ };
3883
+ }
3884
+ }
3885
+ const wide = [
3886
+ ...schemaColumns(op.table)
3887
+ ].filter((c) => [
3888
+ "bigint",
3889
+ "numeric"
3890
+ ].includes(columnTypeOf(op.table, c) ?? ""));
3891
+ const exact = wide.length ? ` || jsonb_build_object(${wide.map((c) => `${quoteLiteral(c)}, r.${quoteIdent(c)}::text`).join(", ")})` : "";
3892
+ const sql = `WITH command_rows AS (${statement.sql}) SELECT coalesce(jsonb_agg(to_jsonb(r)${exact}), '[]'::jsonb) FROM command_rows r`;
3893
+ pipeline.push({
3894
+ sql: `WITH command_rows AS (${statement.sql}) SELECT palbase_runtime.command_rows_v1(coalesce(jsonb_agg(to_jsonb(r)${exact}), '[]'::jsonb),$${statement.params.length + 1}::jsonb) AS rows FROM command_rows r`,
3895
+ params: [
3896
+ ...statement.params,
3897
+ op.guard ?? null
3898
+ ]
3899
+ });
3900
+ const bindings = statement.params.map((value, i) => {
3901
+ const target = statement.columns[i];
3902
+ const json = target.column !== void 0 && columnTypeOf(target.table ?? op.table, target.column) === "jsonb";
3903
+ if (typeof value === "string" && symbols.has(value)) {
3904
+ hasRefs = true;
3905
+ return {
3906
+ ...symbols.get(value),
3907
+ json
3908
+ };
3909
+ }
3910
+ if (typeof value === "bigint") return {
3911
+ value: value.toString(),
3912
+ json
3913
+ };
3914
+ if (value instanceof Date) return {
3915
+ value: value.toISOString(),
3916
+ json
3917
+ };
3918
+ return {
3919
+ value,
3920
+ json
3921
+ };
3922
+ });
3923
+ steps.push({
3924
+ chunks: parameterChunks(sql),
3925
+ bindings,
3926
+ guard: op.guard
3927
+ });
3928
+ }
3929
+ const row = {};
3930
+ for (const field of schemaColumns(op.table)) {
3931
+ const token = `${prefix}${index}:${field}`;
3932
+ symbols.set(token, {
3933
+ ref: {
3934
+ op: index,
3935
+ field
3936
+ }
3937
+ });
3938
+ row[field] = token;
3939
+ }
3940
+ results.push({
3941
+ rows: [
3942
+ row
3943
+ ],
3944
+ rows_affected: 1
3945
+ });
3946
+ }
3947
+ return {
3948
+ steps,
3949
+ ...hasRefs ? {} : {
3950
+ pipeline
3951
+ }
3952
+ };
3953
+ }
3954
+ __name(compileCommand, "compileCommand");
2541
3955
  function assertGuard(op, result) {
2542
3956
  const guard = op.guard;
2543
3957
  if (!guard) return;
@@ -2551,6 +3965,33 @@ function assertGuard(op, result) {
2551
3965
  }
2552
3966
  __name(assertGuard, "assertGuard");
2553
3967
 
3968
+ // src/engine/cancellation.ts
3969
+ function withQueryCancellation(application, control) {
3970
+ if (application === control) throw new Error("Query cancellation requires a separate control pool");
3971
+ const close = /* @__PURE__ */ __name(async (driver) => {
3972
+ const d = driver;
3973
+ if (d.close) await d.close();
3974
+ else if (d.end) await d.end();
3975
+ }, "close");
3976
+ return {
3977
+ unsafe: /* @__PURE__ */ __name((sql, params) => application.unsafe(sql, params), "unsafe"),
3978
+ begin: /* @__PURE__ */ __name((fn) => application.begin(fn), "begin"),
3979
+ async cancelBackend(pid, startedAt) {
3980
+ await control.unsafe(`SELECT pg_cancel_backend(pid) FROM pg_stat_activity
3981
+ WHERE pid=$1::integer AND backend_start=$2::timestamptz
3982
+ AND usename=session_user AND datname=current_database() AND backend_type='client backend'`, [
3983
+ pid,
3984
+ startedAt
3985
+ ]);
3986
+ },
3987
+ async close() {
3988
+ await close(application);
3989
+ await close(control);
3990
+ }
3991
+ };
3992
+ }
3993
+ __name(withQueryCancellation, "withQueryCancellation");
3994
+
2554
3995
  // src/engine/router.ts
2555
3996
  var ROOM_META = /* @__PURE__ */ Symbol.for("palbase.backend.room");
2556
3997
  var CONTROLLER_META = /* @__PURE__ */ Symbol.for("palbase.backend.controllerMeta");
@@ -3055,10 +4496,13 @@ async function defaultSqlDriver(config) {
3055
4496
  if (!g.Bun?.SQL) {
3056
4497
  throw new BootRefused([], "boot refused: no SQL driver. Running outside Bun means the driver must be supplied \u2014 pass `sql` to createApp().");
3057
4498
  }
3058
- return new g.Bun.SQL({
4499
+ return withQueryCancellation(new g.Bun.SQL({
3059
4500
  url: config.databaseUrl,
3060
4501
  max: config.poolMax
3061
- });
4502
+ }), new g.Bun.SQL({
4503
+ url: config.databaseUrl,
4504
+ max: 2
4505
+ }));
3062
4506
  }
3063
4507
  __name(defaultSqlDriver, "defaultSqlDriver");
3064
4508
  var AUTHOR_ANSWERED = /* @__PURE__ */ new Set([
@@ -3117,6 +4561,11 @@ async function createApp(opts) {
3117
4561
  const sql = opts.sql ?? await defaultSqlDriver(config);
3118
4562
  await sql.unsafe("select 1");
3119
4563
  setSchema(opts.schemas ?? []);
4564
+ setCursorKey(config.cursorSecret || config.serviceRoleKey || null);
4565
+ if (config.enableCommands) await installCommandExecutor(sql, [
4566
+ config.dbRole,
4567
+ config.dbServiceRole
4568
+ ]);
3120
4569
  setSecretReader(opts.secretReader ?? null);
3121
4570
  const auth = new AuthVerifier({
3122
4571
  jwksUrl: config.authJwksUrl,
@@ -3162,6 +4611,9 @@ async function createApp(opts) {
3162
4611
  role: config.dbRole,
3163
4612
  serviceRole: config.dbServiceRole,
3164
4613
  claimsJson: "{}"
4614
+ }, config.databaseDiagnostics === void 0 ? void 0 : {
4615
+ ...config.databaseDiagnostics,
4616
+ endpoint: "scheduled job"
3165
4617
  });
3166
4618
  try {
3167
4619
  const out = await runWithRuntime(buildServices(db), fn);
@@ -3191,6 +4643,10 @@ async function createApp(opts) {
3191
4643
  role: config.dbRole,
3192
4644
  serviceRole: config.dbServiceRole,
3193
4645
  claimsJson: JSON.stringify(callerClaims ?? {})
4646
+ }, config.databaseDiagnostics === void 0 ? void 0 : {
4647
+ ...config.databaseDiagnostics,
4648
+ endpoint: "upload authorization",
4649
+ requestId
3194
4650
  });
3195
4651
  try {
3196
4652
  const refusal = await refuseUnlessAuthorized(uploadDb.client, spec, requestId, makeRoleResolver(uploadDb.client));
@@ -3238,6 +4694,11 @@ async function createApp(opts) {
3238
4694
  role: config.dbRole,
3239
4695
  serviceRole: config.dbServiceRole,
3240
4696
  claimsJson: JSON.stringify(claims ?? {})
4697
+ }, config.databaseDiagnostics === void 0 && meta.options.databaseBudget === void 0 ? void 0 : {
4698
+ ...config.databaseDiagnostics,
4699
+ ...meta.options.databaseBudget,
4700
+ endpoint: hit.entry.id,
4701
+ requestId
3241
4702
  });
3242
4703
  const rolesOfCaller = makeRoleResolver(db.client);
3243
4704
  let dispatched = false;
@@ -3667,11 +5128,15 @@ export {
3667
5128
  effectiveAuth,
3668
5129
  RateLimiter,
3669
5130
  makeMemoryCache,
5131
+ setCursorKey,
5132
+ COMMAND_EXECUTOR_DDL,
5133
+ installCommandExecutor,
3670
5134
  quoteIdent,
3671
5135
  createLazyTransaction,
3672
5136
  createOps,
3673
5137
  withTables,
3674
5138
  createRequestDatabase,
5139
+ withQueryCancellation,
3675
5140
  buildRouteTable,
3676
5141
  matchRoute,
3677
5142
  scrubSecrets,
@@ -3679,4 +5144,4 @@ export {
3679
5144
  installEgressFence,
3680
5145
  createApp
3681
5146
  };
3682
- //# sourceMappingURL=chunk-C6COAB3E.js.map
5147
+ //# sourceMappingURL=chunk-MNGZE2MM.js.map