@palbase/backend 25.1.0 → 27.1.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 (104) hide show
  1. package/dist/bin/palbase-backend.cjs +2432 -1039
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +87 -51
  4. package/dist/bin/palbase-backend.js.map +1 -1
  5. package/dist/chunk-BQN723PL.js +930 -0
  6. package/dist/chunk-BQN723PL.js.map +1 -0
  7. package/dist/chunk-CGNN2PUH.js +213 -0
  8. package/dist/chunk-CGNN2PUH.js.map +1 -0
  9. package/dist/{chunk-VDF2T4AS.js → chunk-EB3TUX5J.js} +1228 -591
  10. package/dist/chunk-EB3TUX5J.js.map +1 -0
  11. package/dist/chunk-JVZQCC77.js +728 -0
  12. package/dist/chunk-JVZQCC77.js.map +1 -0
  13. package/dist/chunk-OZKSM3JW.js +370 -0
  14. package/dist/chunk-OZKSM3JW.js.map +1 -0
  15. package/dist/{chunk-YOY5DFQS.js → chunk-TWX6JTGJ.js} +76 -34
  16. package/dist/{chunk-YOY5DFQS.js.map → chunk-TWX6JTGJ.js.map} +1 -1
  17. package/dist/{chunk-35PNTIRN.js → chunk-VVMJEVQP.js} +63 -162
  18. package/dist/chunk-VVMJEVQP.js.map +1 -0
  19. package/dist/{chunk-7D4SUZUM.js → chunk-VXPNPVAG.js} +3 -1
  20. package/dist/chunk-XABHGMUT.js +885 -0
  21. package/dist/chunk-XABHGMUT.js.map +1 -0
  22. package/dist/db/env.cjs.map +1 -1
  23. package/dist/db/env.d.cts +2 -2
  24. package/dist/db/env.d.ts +2 -2
  25. package/dist/db/index.cjs +780 -344
  26. package/dist/db/index.cjs.map +1 -1
  27. package/dist/db/index.d.cts +2 -2
  28. package/dist/db/index.d.ts +2 -2
  29. package/dist/db/index.js +7 -4
  30. package/dist/engine/index.cjs +2366 -1002
  31. package/dist/engine/index.cjs.map +1 -1
  32. package/dist/engine/index.d.cts +6 -6
  33. package/dist/engine/index.d.ts +6 -6
  34. package/dist/engine/index.js +7 -6
  35. package/dist/{index-CUomTA3e.d.ts → index-CAKOgAlP.d.ts} +171 -296
  36. package/dist/index-CgE4sVhg.d.cts +4864 -0
  37. package/dist/{index-ClpDeSos.d.cts → index-H-0qv5d4.d.cts} +171 -296
  38. package/dist/index-V7QRh1wg.d.ts +4864 -0
  39. package/dist/index.cjs +2720 -1169
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +165 -19
  42. package/dist/index.d.ts +165 -19
  43. package/dist/index.js +738 -477
  44. package/dist/index.js.map +1 -1
  45. package/dist/module-Dl1KFVtc.d.cts +54 -0
  46. package/dist/module-Dl1KFVtc.d.ts +54 -0
  47. package/dist/openapi/index.cjs +1330 -484
  48. package/dist/openapi/index.cjs.map +1 -1
  49. package/dist/openapi/index.d.cts +4 -2
  50. package/dist/openapi/index.d.ts +4 -2
  51. package/dist/openapi/index.js +1264 -474
  52. package/dist/openapi/index.js.map +1 -1
  53. package/dist/{registry-dZZ5JKYg.d.ts → registry-4EI8aaFs.d.ts} +1 -1
  54. package/dist/{registry-CC0WBQq6.d.cts → registry-DHsPDY0_.d.cts} +1 -1
  55. package/dist/stack.cjs.map +1 -1
  56. package/dist/test/index.cjs +732 -141
  57. package/dist/test/index.cjs.map +1 -1
  58. package/dist/test/index.d.cts +30 -4
  59. package/dist/test/index.d.ts +30 -4
  60. package/dist/test/index.js +490 -124
  61. package/dist/test/index.js.map +1 -1
  62. package/docs/README.md +33 -18
  63. package/docs/auth.md +1 -1
  64. package/docs/background.md +2 -2
  65. package/docs/database.md +255 -50
  66. package/docs/endpoints.md +3 -4
  67. package/docs/events.md +3 -3
  68. package/docs/getting-started.md +1 -1
  69. package/docs/llms-full.txt +435 -117
  70. package/docs/migrations.md +2 -2
  71. package/docs/schema.md +19 -10
  72. package/docs/services.md +116 -26
  73. package/package.json +8 -4
  74. package/stager/generics.js +205 -0
  75. package/stager/stage.js +39 -3
  76. package/template/AGENTS.md +110 -72
  77. package/template/db/public.ts +1 -1
  78. package/template/{controllers → modules/health}/health.controller.ts +1 -1
  79. package/template/modules/health/health.module.ts +24 -0
  80. package/template/modules/notes/note.service.test.ts +49 -0
  81. package/template/modules/notes/note.service.ts +108 -0
  82. package/template/{controllers → modules/notes}/notes.controller.ts +17 -11
  83. package/template/modules/notes/notes.module.ts +37 -0
  84. package/template/package.json +5 -3
  85. package/template/scripts/test.sh +33 -0
  86. package/template/tsconfig.json +29 -30
  87. package/dist/chunk-35PNTIRN.js.map +0 -1
  88. package/dist/chunk-CJSKYY76.js +0 -627
  89. package/dist/chunk-CJSKYY76.js.map +0 -1
  90. package/dist/chunk-CRQKCRGF.js +0 -276
  91. package/dist/chunk-CRQKCRGF.js.map +0 -1
  92. package/dist/chunk-G4R6BTLV.js +0 -662
  93. package/dist/chunk-G4R6BTLV.js.map +0 -1
  94. package/dist/chunk-VDF2T4AS.js.map +0 -1
  95. package/dist/chunk-XABBC7JP.js +0 -55
  96. package/dist/chunk-XABBC7JP.js.map +0 -1
  97. package/dist/endpoint-CTEHhb7A.d.ts +0 -2386
  98. package/dist/endpoint-DYHMo6cC.d.cts +0 -2386
  99. package/dist/index-CW21M9Z3.d.ts +0 -1222
  100. package/dist/index-CmBK76nx.d.cts +0 -1222
  101. package/template/services/note.service.test.ts +0 -45
  102. package/template/services/note.service.ts +0 -76
  103. /package/dist/{chunk-7D4SUZUM.js.map → chunk-VXPNPVAG.js.map} +0 -0
  104. /package/template/{models/notes → modules/notes/dto}/create.ts +0 -0
@@ -1,27 +1,48 @@
1
1
  import {
2
2
  __requestALS,
3
3
  __runStartHooks,
4
- __runWithRuntime
5
- } from "./chunk-CRQKCRGF.js";
4
+ __runWithRuntime,
5
+ assertNoOrphanEntryPoints,
6
+ buildContainer,
7
+ buildRelations
8
+ } from "./chunk-BQN723PL.js";
6
9
  import {
10
+ qualifiedTableKey
11
+ } from "./chunk-OZKSM3JW.js";
12
+ import {
13
+ assertNoExpressionHandles,
7
14
  assertUsableFilter,
8
- assertUsableWriteValues
9
- } from "./chunk-XABBC7JP.js";
15
+ assertUsableWriteValues,
16
+ columnExprOf,
17
+ isColRef,
18
+ isColumnExpr,
19
+ isNowExpr,
20
+ isPlanRef,
21
+ isSqlFragment,
22
+ looksLikeUnbrandedPlanRef,
23
+ looksLikeUnbrandedRef
24
+ } from "./chunk-JVZQCC77.js";
10
25
  import {
11
- qualifiedTableKey
12
- } from "./chunk-CJSKYY76.js";
26
+ getRoutes,
27
+ resolveEffectiveAuth
28
+ } from "./chunk-VVMJEVQP.js";
13
29
  import {
30
+ DeadlockDetected,
31
+ SerializationFailure,
14
32
  UniqueViolation,
15
- assertZeroArgConstructor,
16
- getRoutes,
17
33
  isEngineRaised,
18
34
  isHttpError,
19
- markEngineRaised,
20
- resolveEffectiveAuth
21
- } from "./chunk-35PNTIRN.js";
35
+ markEngineRaised
36
+ } from "./chunk-CGNN2PUH.js";
37
+ import {
38
+ __name
39
+ } from "./chunk-VXPNPVAG.js";
22
40
 
23
41
  // src/engine/config.ts
24
42
  var BootRefused = class extends Error {
43
+ static {
44
+ __name(this, "BootRefused");
45
+ }
25
46
  missing;
26
47
  constructor(missing, message) {
27
48
  super(message);
@@ -30,18 +51,21 @@ var BootRefused = class extends Error {
30
51
  }
31
52
  };
32
53
  var MANDATORY = [
33
- { key: "DATABASE_URL", what: "the stack's Postgres (Database module)" },
34
- { key: "AUTH_JWKS_URL", what: "where this stack publishes its token signing keys (Auth module)" }
54
+ {
55
+ key: "DATABASE_URL",
56
+ what: "the stack's Postgres (Database module)"
57
+ },
58
+ {
59
+ key: "AUTH_JWKS_URL",
60
+ what: "where this stack publishes its token signing keys (Auth module)"
61
+ }
35
62
  ];
36
63
  function loadConfig(env) {
37
64
  const missing = MANDATORY.filter((m) => !env[m.key]?.trim()).map((m) => m.key);
38
65
  if (missing.length > 0) {
39
66
  const detail = MANDATORY.filter((m) => missing.includes(m.key)).map((m) => ` ${m.key.padEnd(16)}${m.what}`).join("\n");
40
- throw new BootRefused(
41
- missing,
42
- `boot refused: mandatory module not configured \u2014 missing ${missing.join(", ")}.
43
- ${detail}`
44
- );
67
+ throw new BootRefused(missing, `boot refused: mandatory module not configured \u2014 missing ${missing.join(", ")}.
68
+ ${detail}`);
45
69
  }
46
70
  const port = Number(env.PORT ?? 3e3);
47
71
  if (!Number.isInteger(port) || port < 0 || port > 65535) {
@@ -75,6 +99,7 @@ ${detail}`
75
99
  poolMax
76
100
  };
77
101
  }
102
+ __name(loadConfig, "loadConfig");
78
103
 
79
104
  // src/engine/auth.ts
80
105
  function b64urlToBytes(s) {
@@ -85,7 +110,11 @@ function b64urlToBytes(s) {
85
110
  for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
86
111
  return out;
87
112
  }
113
+ __name(b64urlToBytes, "b64urlToBytes");
88
114
  var AuthVerifier = class {
115
+ static {
116
+ __name(this, "AuthVerifier");
117
+ }
89
118
  keys = /* @__PURE__ */ new Map();
90
119
  fetchedAt = 0;
91
120
  inflight = null;
@@ -111,16 +140,18 @@ var AuthVerifier = class {
111
140
  for (const jwk of body.keys ?? []) {
112
141
  if (jwk.kty !== "EC" || jwk.crv !== "P-256") continue;
113
142
  try {
114
- next.set(
115
- jwk.kid,
116
- await crypto.subtle.importKey(
117
- "jwk",
118
- { kty: "EC", crv: jwk.crv, x: jwk.x, y: jwk.y, ext: true },
119
- { name: "ECDSA", namedCurve: "P-256" },
120
- true,
121
- ["verify"]
122
- )
123
- );
143
+ next.set(jwk.kid, await crypto.subtle.importKey("jwk", {
144
+ kty: "EC",
145
+ crv: jwk.crv,
146
+ x: jwk.x,
147
+ y: jwk.y,
148
+ ext: true
149
+ }, {
150
+ name: "ECDSA",
151
+ namedCurve: "P-256"
152
+ }, true, [
153
+ "verify"
154
+ ]));
124
155
  } catch {
125
156
  }
126
157
  }
@@ -140,12 +171,12 @@ var AuthVerifier = class {
140
171
  return this.keys.get(kid) ?? null;
141
172
  }
142
173
  /**
143
- * Verify an `Authorization` header value.
144
- *
145
- * @returns the verified claims, or `null` for absent / malformed / expired /
146
- * wrong-issuer / bad-signature. One `null` for every failure on purpose:
147
- * the caller answers 401 either way, and a detailed reason is an oracle.
148
- */
174
+ * Verify an `Authorization` header value.
175
+ *
176
+ * @returns the verified claims, or `null` for absent / malformed / expired /
177
+ * wrong-issuer / bad-signature. One `null` for every failure on purpose:
178
+ * the caller answers 401 either way, and a detailed reason is an oracle.
179
+ */
149
180
  async verify(authorization) {
150
181
  if (!authorization || !authorization.startsWith("Bearer ")) return null;
151
182
  const parts = authorization.slice(7).trim().split(".");
@@ -167,12 +198,10 @@ var AuthVerifier = class {
167
198
  if (!key) return null;
168
199
  let ok = false;
169
200
  try {
170
- ok = await crypto.subtle.verify(
171
- { name: "ECDSA", hash: "SHA-256" },
172
- key,
173
- b64urlToBytes(sig),
174
- new TextEncoder().encode(`${h}.${p}`)
175
- );
201
+ ok = await crypto.subtle.verify({
202
+ name: "ECDSA",
203
+ hash: "SHA-256"
204
+ }, key, b64urlToBytes(sig), new TextEncoder().encode(`${h}.${p}`));
176
205
  } catch {
177
206
  return null;
178
207
  }
@@ -184,9 +213,18 @@ var AuthVerifier = class {
184
213
  };
185
214
  function effectiveAuth(routeAuth, controllerAuth) {
186
215
  const spec = routeAuth !== void 0 ? routeAuth : controllerAuth;
187
- if (spec === false) return { required: false, verifiedEmail: false };
188
- if (spec === true || spec === void 0 || spec === null) return { required: true, verifiedEmail: false };
189
- if (typeof spec !== "object") return { required: true, verifiedEmail: false };
216
+ if (spec === false) return {
217
+ required: false,
218
+ verifiedEmail: false
219
+ };
220
+ if (spec === true || spec === void 0 || spec === null) return {
221
+ required: true,
222
+ verifiedEmail: false
223
+ };
224
+ if (typeof spec !== "object") return {
225
+ required: true,
226
+ verifiedEmail: false
227
+ };
190
228
  const o = spec;
191
229
  const role = typeof o.role === "string" && o.role.trim() !== "" ? o.role.trim() : void 0;
192
230
  return {
@@ -195,23 +233,27 @@ function effectiveAuth(routeAuth, controllerAuth) {
195
233
  verifiedEmail: o.verifiedEmail === true
196
234
  };
197
235
  }
236
+ __name(effectiveAuth, "effectiveAuth");
198
237
 
199
238
  // src/engine/ratelimit.ts
200
239
  var RateLimiter = class {
240
+ static {
241
+ __name(this, "RateLimiter");
242
+ }
243
+ maxKeys;
244
+ buckets = /* @__PURE__ */ new Map();
201
245
  /** Bound on distinct keys held, so an attacker cycling identities cannot
202
- * grow this map without limit. On overflow the oldest windows are dropped —
203
- * forgiving, consistent with the restart behaviour above. */
246
+ * grow this map without limit. On overflow the oldest windows are dropped —
247
+ * forgiving, consistent with the restart behaviour above. */
204
248
  constructor(maxKeys = 1e5) {
205
249
  this.maxKeys = maxKeys;
206
250
  }
207
- maxKeys;
208
- buckets = /* @__PURE__ */ new Map();
209
251
  /**
210
- * Identify the caller: the signed-in user when the route resolved one,
211
- * otherwise the address the edge forwarded. Callers the edge did not
212
- * identify share one bucket — deliberately conservative, since the
213
- * alternative is a limit anyone resets by omitting a header.
214
- */
252
+ * Identify the caller: the signed-in user when the route resolved one,
253
+ * otherwise the address the edge forwarded. Callers the edge did not
254
+ * identify share one bucket — deliberately conservative, since the
255
+ * alternative is a limit anyone resets by omitting a header.
256
+ */
215
257
  static key(routeId, userId, headers) {
216
258
  if (userId) return `${routeId}\0u:${userId}`;
217
259
  const fwd = headers.get("x-forwarded-for");
@@ -219,16 +261,19 @@ var RateLimiter = class {
219
261
  return `${routeId}\0a:${addr || "anonymous"}`;
220
262
  }
221
263
  /**
222
- * @returns `null` when the request may proceed, or the number of seconds to
223
- * wait (never 0 — a caller told to wait 0 comes straight back to the same
224
- * refusal).
225
- */
264
+ * @returns `null` when the request may proceed, or the number of seconds to
265
+ * wait (never 0 — a caller told to wait 0 comes straight back to the same
266
+ * refusal).
267
+ */
226
268
  check(rule, key, now) {
227
269
  if (!rule || !(rule.max > 0) || !(rule.window > 0)) return null;
228
270
  const bucket = this.buckets.get(key);
229
271
  if (!bucket || now >= bucket.resetAt) {
230
272
  if (this.buckets.size >= this.maxKeys) this.evict(now);
231
- this.buckets.set(key, { count: 1, resetAt: now + rule.window * 1e3 });
273
+ this.buckets.set(key, {
274
+ count: 1,
275
+ resetAt: now + rule.window * 1e3
276
+ });
232
277
  return null;
233
278
  }
234
279
  if (bucket.count < rule.max) {
@@ -238,7 +283,7 @@ var RateLimiter = class {
238
283
  return Math.max(1, Math.ceil((bucket.resetAt - now) / 1e3));
239
284
  }
240
285
  /** Drop expired windows; if none are expired, drop the earliest-resetting
241
- * quarter so the map cannot wedge at the ceiling. */
286
+ * quarter so the map cannot wedge at the ceiling. */
242
287
  evict(now) {
243
288
  let dropped = 0;
244
289
  for (const [k, b] of this.buckets) {
@@ -248,7 +293,9 @@ var RateLimiter = class {
248
293
  }
249
294
  }
250
295
  if (dropped > 0) return;
251
- const byReset = [...this.buckets.entries()].sort((a, b) => a[1].resetAt - b[1].resetAt);
296
+ const byReset = [
297
+ ...this.buckets.entries()
298
+ ].sort((a, b) => a[1].resetAt - b[1].resetAt);
252
299
  for (let i = 0; i < Math.ceil(byReset.length / 4); i++) {
253
300
  const victim = byReset[i];
254
301
  if (victim) this.buckets.delete(victim[0]);
@@ -266,7 +313,7 @@ function makeMemoryCache(opts = {}) {
266
313
  const now = opts.now ?? (() => Date.now());
267
314
  const store = /* @__PURE__ */ new Map();
268
315
  const inflight = /* @__PURE__ */ new Map();
269
- const live = (key) => {
316
+ const live = /* @__PURE__ */ __name((key) => {
270
317
  const e = store.get(key);
271
318
  if (!e) return void 0;
272
319
  if (e.expiresAt !== 0 && e.expiresAt <= now()) {
@@ -274,8 +321,8 @@ function makeMemoryCache(opts = {}) {
274
321
  return void 0;
275
322
  }
276
323
  return e;
277
- };
278
- const evict = () => {
324
+ }, "live");
325
+ const evict = /* @__PURE__ */ __name(() => {
279
326
  const t = now();
280
327
  let dropped = 0;
281
328
  for (const [k, e] of store) {
@@ -285,18 +332,21 @@ function makeMemoryCache(opts = {}) {
285
332
  }
286
333
  }
287
334
  if (dropped > 0) return;
288
- const order = [...store.entries()].sort(
289
- (a, b) => (a[1].expiresAt || Infinity) - (b[1].expiresAt || Infinity)
290
- );
335
+ const order = [
336
+ ...store.entries()
337
+ ].sort((a, b) => (a[1].expiresAt || Infinity) - (b[1].expiresAt || Infinity));
291
338
  for (let i = 0; i < Math.ceil(order.length / 4); i++) {
292
339
  const victim = order[i];
293
340
  if (victim) store.delete(victim[0]);
294
341
  }
295
- };
296
- const set = async (key, value, ttl) => {
342
+ }, "evict");
343
+ const set = /* @__PURE__ */ __name(async (key, value, ttl) => {
297
344
  if (store.size >= maxEntries && !store.has(key)) evict();
298
- store.set(key, { value, expiresAt: ttl && ttl > 0 ? now() + ttl * 1e3 : 0 });
299
- };
345
+ store.set(key, {
346
+ value,
347
+ expiresAt: ttl && ttl > 0 ? now() + ttl * 1e3 : 0
348
+ });
349
+ }, "set");
300
350
  return {
301
351
  async get(key) {
302
352
  const e = live(key);
@@ -309,7 +359,10 @@ function makeMemoryCache(opts = {}) {
309
359
  async incr(key) {
310
360
  const e = live(key);
311
361
  const next = (typeof e?.value === "number" ? e.value : 0) + 1;
312
- store.set(key, { value: next, expiresAt: e?.expiresAt ?? 0 });
362
+ store.set(key, {
363
+ value: next,
364
+ expiresAt: e?.expiresAt ?? 0
365
+ });
313
366
  return next;
314
367
  },
315
368
  async getOrSet(key, ttl, fn) {
@@ -331,34 +384,45 @@ function makeMemoryCache(opts = {}) {
331
384
  }
332
385
  };
333
386
  }
387
+ __name(makeMemoryCache, "makeMemoryCache");
334
388
 
335
389
  // src/engine/db.ts
336
390
  function quoteIdent(name) {
337
391
  return `"${name.replace(/"/g, '""')}"`;
338
392
  }
393
+ __name(quoteIdent, "quoteIdent");
339
394
  function quoteTable(key) {
340
395
  const dot = key.indexOf(".");
341
396
  if (dot === -1) return quoteIdent(key);
342
397
  return `${quoteIdent(key.slice(0, dot))}.${quoteIdent(key.slice(dot + 1))}`;
343
398
  }
399
+ __name(quoteTable, "quoteTable");
344
400
  function encodePgArray(value) {
345
- const element = (x) => {
401
+ const element = /* @__PURE__ */ __name((x) => {
346
402
  if (x === null || x === void 0) return "NULL";
347
403
  if (Array.isArray(x)) return encodePgArray(x);
348
404
  return `"${String(x).replace(/(["\\])/g, "\\$1")}"`;
349
- };
405
+ }, "element");
350
406
  return `{${value.map(element).join(",")}}`;
351
407
  }
408
+ __name(encodePgArray, "encodePgArray");
352
409
  var BIND_SQL = "select set_config('role',$1,true), set_config('search_path','public',true), set_config('request.jwt.claims',$2,true)";
353
410
  function createLazyTransaction(sql, role, claimsJson, options = {}) {
354
411
  const { lockTimeout } = options;
355
412
  const bindSql = lockTimeout ? `${BIND_SQL}, set_config('lock_timeout',$3,true)` : BIND_SQL;
356
- const bindParams = lockTimeout ? [role, claimsJson, lockTimeout] : [role, claimsJson];
413
+ const bindParams = lockTimeout ? [
414
+ role,
415
+ claimsJson,
416
+ lockTimeout
417
+ ] : [
418
+ role,
419
+ claimsJson
420
+ ];
357
421
  let opening = null;
358
422
  let release = null;
359
423
  let fail = null;
360
424
  let settled = null;
361
- const ensure = () => {
425
+ const ensure = /* @__PURE__ */ __name(() => {
362
426
  if (opening) return opening;
363
427
  opening = new Promise((resolveTx2, rejectTx) => {
364
428
  const parked = new Promise((res, rej) => {
@@ -375,7 +439,7 @@ function createLazyTransaction(sql, role, claimsJson, options = {}) {
375
439
  });
376
440
  });
377
441
  return opening;
378
- };
442
+ }, "ensure");
379
443
  return {
380
444
  ensure,
381
445
  get opened() {
@@ -393,24 +457,29 @@ function createLazyTransaction(sql, role, claimsJson, options = {}) {
393
457
  }
394
458
  };
395
459
  }
396
- var resolveTx = async (tx) => typeof tx.ensure === "function" ? await tx.ensure() : tx;
460
+ __name(createLazyTransaction, "createLazyTransaction");
461
+ var resolveTx = /* @__PURE__ */ __name(async (tx) => typeof tx.ensure === "function" ? await tx.ensure() : tx, "resolveTx");
397
462
  function asWireValue(value) {
398
463
  if (value instanceof Date) return value.toISOString();
399
464
  if (Array.isArray(value)) return value.map(asWireValue);
400
465
  return value;
401
466
  }
467
+ __name(asWireValue, "asWireValue");
402
468
  function asWireRow(row) {
403
469
  if (row === null || typeof row !== "object") return row;
404
470
  const out = {};
405
471
  for (const [key, value] of Object.entries(row)) out[key] = asWireValue(value);
406
472
  return out;
407
473
  }
474
+ __name(asWireRow, "asWireRow");
408
475
  function asWireRows(rows) {
409
476
  return rows.map((row) => asWireRow(row));
410
477
  }
478
+ __name(asWireRows, "asWireRows");
411
479
  function toVectorLiteral(v) {
412
480
  return `[${v.join(",")}]`;
413
481
  }
482
+ __name(toVectorLiteral, "toVectorLiteral");
414
483
  function vectorColumnsOf(schema, table) {
415
484
  const out = /* @__PURE__ */ new Set();
416
485
  const def = tableDefOf(table, schema);
@@ -422,9 +491,11 @@ function vectorColumnsOf(schema, table) {
422
491
  }
423
492
  return out;
424
493
  }
494
+ __name(vectorColumnsOf, "vectorColumnsOf");
425
495
  function tableDefOf(key, schema = currentSchema) {
426
496
  return schema.tables?.[key] ?? null;
427
497
  }
498
+ __name(tableDefOf, "tableDefOf");
428
499
  function reviveVectors(row, vectorCols) {
429
500
  if (row === null || typeof row !== "object" || vectorCols.size === 0) return row;
430
501
  const out = row;
@@ -434,6 +505,7 @@ function reviveVectors(row, vectorCols) {
434
505
  }
435
506
  return row;
436
507
  }
508
+ __name(reviveVectors, "reviveVectors");
437
509
  function transformsOf(schema, table) {
438
510
  const out = /* @__PURE__ */ new Map();
439
511
  const def = tableDefOf(table, schema);
@@ -448,6 +520,7 @@ function transformsOf(schema, table) {
448
520
  }
449
521
  return out;
450
522
  }
523
+ __name(transformsOf, "transformsOf");
451
524
  function applyFromDb(row, transforms) {
452
525
  if (transforms.size === 0) return row;
453
526
  for (const [col, t] of transforms) {
@@ -458,17 +531,21 @@ function applyFromDb(row, transforms) {
458
531
  }
459
532
  return row;
460
533
  }
534
+ __name(applyFromDb, "applyFromDb");
461
535
  function asTableRow(table, row) {
462
536
  const revived = reviveVectors(asWireRow(row), vectorColumnsOf(currentSchema, table));
463
537
  return applyFromDb(revived, transformsOf(currentSchema, table));
464
538
  }
539
+ __name(asTableRow, "asTableRow");
465
540
  function asTableRows(table, rows) {
466
541
  const vectorCols = vectorColumnsOf(currentSchema, table);
467
542
  const transforms = transformsOf(currentSchema, table);
468
543
  return rows.map((row) => applyFromDb(reviveVectors(asWireRow(row), vectorCols), transforms));
469
544
  }
545
+ __name(asTableRows, "asTableRows");
470
546
  function asBindParams(table, cols, data, caller) {
471
547
  assertUsableWriteValues(caller, table, cols, data);
548
+ assertNoExpressionHandles(caller, table, cols, data);
472
549
  const vectorCols = vectorColumnsOf(currentSchema, table);
473
550
  const transforms = transformsOf(currentSchema, table);
474
551
  return cols.map((c) => {
@@ -479,6 +556,7 @@ function asBindParams(table, cols, data, caller) {
479
556
  return v;
480
557
  });
481
558
  }
559
+ __name(asBindParams, "asBindParams");
482
560
  var SELECTIVITY_EXACT_THRESHOLD = 1e4;
483
561
  var METRIC_OPERATOR = {
484
562
  cosine: "<=>",
@@ -489,27 +567,33 @@ function searchConfigFor(table) {
489
567
  const t = tableDefOf(table);
490
568
  if (!t) return null;
491
569
  const columns = t.columns ?? {};
492
- const defOf = (c) => c !== null && typeof c === "object" && "_def" in c ? c._def : c ?? {};
570
+ const defOf = /* @__PURE__ */ __name((c) => c !== null && typeof c === "object" && "_def" in c ? c._def : c ?? {}, "defOf");
493
571
  const cols = Object.keys(columns);
494
572
  const vectorCols = cols.filter((c) => defOf(columns[c]).type === "vector");
495
- const defOfFull = (c) => c !== null && typeof c === "object" && "_def" in c ? c._def : c ?? {};
573
+ const defOfFull = /* @__PURE__ */ __name((c) => c !== null && typeof c === "object" && "_def" in c ? c._def : c ?? {}, "defOfFull");
496
574
  const pkCols = cols.filter((c) => defOfFull(columns[c]).primaryKey === true);
497
575
  const pk = pkCols.length === 1 ? pkCols[0] : cols.includes("id") ? "id" : null;
498
576
  if (pk === null) {
499
- throw new Error(
500
- `search(${table}): tek-kolon primary key bulunamad\u0131 \u2014 arama s\u0131ralamas\u0131 ve sat\u0131r birle\u015Fimi PK ister (FR-020)`
501
- );
577
+ throw new Error(`search(${table}): tek-kolon primary key bulunamad\u0131 \u2014 arama s\u0131ralamas\u0131 ve sat\u0131r birle\u015Fimi PK ister (FR-020)`);
502
578
  }
503
579
  const search = t.search;
504
- const synonyms = search?.synonyms !== void 0 && Object.keys(search.synonyms).length > 0 ? { synonyms: search.synonyms } : {};
505
- const validity = search?.validity === true ? { validity: true } : {};
580
+ const synonyms = search?.synonyms !== void 0 && Object.keys(search.synonyms).length > 0 ? {
581
+ synonyms: search.synonyms
582
+ } : {};
583
+ const validity = search?.validity === true ? {
584
+ validity: true
585
+ } : {};
506
586
  if (search?.from !== void 0 && search.model !== void 0) {
507
587
  const m = search.model;
508
588
  const embed = {
509
589
  model: m.model,
510
590
  apiKeyName: m.apiKeyName ?? "OPENAI_API_KEY",
511
- ...m.baseURL !== void 0 ? { baseURL: m.baseURL } : {},
512
- ...m.dimensions !== void 0 ? { dimensions: m.dimensions } : {}
591
+ ...m.baseURL !== void 0 ? {
592
+ baseURL: m.baseURL
593
+ } : {},
594
+ ...m.dimensions !== void 0 ? {
595
+ dimensions: m.dimensions
596
+ } : {}
513
597
  };
514
598
  const metric = search.metric ?? "cosine";
515
599
  const ftsOn = search.text !== false;
@@ -520,7 +604,13 @@ function searchConfigFor(table) {
520
604
  cols,
521
605
  colSet: new Set(cols),
522
606
  ftsCols: ftsColsNew,
523
- legs: [{ column: vectorCols[0], metric, embed }],
607
+ legs: [
608
+ {
609
+ column: vectorCols[0],
610
+ metric,
611
+ embed
612
+ }
613
+ ],
524
614
  ...synonyms,
525
615
  ...validity
526
616
  };
@@ -531,13 +621,20 @@ function searchConfigFor(table) {
531
621
  colSet: new Set(cols),
532
622
  ftsCols: ftsColsNew,
533
623
  legs: [],
534
- chunk: { table: `${table}__palbase_chunks`, metric, embed, fts: ftsOn },
624
+ chunk: {
625
+ table: `${table}__palbase_chunks`,
626
+ metric,
627
+ embed,
628
+ fts: ftsOn
629
+ },
535
630
  ...synonyms,
536
631
  ...validity
537
632
  };
538
633
  }
539
634
  const ftsCols = (Array.isArray(search?.text) ? search.text : void 0) ?? [];
540
- const rawLegs = search?.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [search.vector];
635
+ const rawLegs = search?.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [
636
+ search.vector
637
+ ];
541
638
  let legs;
542
639
  if (rawLegs.length > 0) {
543
640
  legs = rawLegs.map((leg) => {
@@ -550,35 +647,57 @@ function searchConfigFor(table) {
550
647
  return {
551
648
  column,
552
649
  metric: l.metric ?? "cosine",
553
- ...model !== void 0 ? { embed: {
554
- model: model.model,
555
- apiKeyName: model.apiKeyName ?? "OPENAI_API_KEY",
556
- ...model.baseURL !== void 0 ? { baseURL: model.baseURL } : {},
557
- ...model.dimensions !== void 0 ? { dimensions: model.dimensions } : {}
558
- } } : {}
650
+ ...model !== void 0 ? {
651
+ embed: {
652
+ model: model.model,
653
+ apiKeyName: model.apiKeyName ?? "OPENAI_API_KEY",
654
+ ...model.baseURL !== void 0 ? {
655
+ baseURL: model.baseURL
656
+ } : {},
657
+ ...model.dimensions !== void 0 ? {
658
+ dimensions: model.dimensions
659
+ } : {}
660
+ }
661
+ } : {}
559
662
  };
560
663
  });
561
664
  } else {
562
- legs = vectorCols.map((column) => ({ column, metric: "cosine" }));
665
+ legs = vectorCols.map((column) => ({
666
+ column,
667
+ metric: "cosine"
668
+ }));
563
669
  }
564
670
  if (ftsCols.length === 0 && legs.length === 0) return null;
565
- return { pk, cols, colSet: new Set(cols), ftsCols, legs, ...synonyms, ...validity };
671
+ return {
672
+ pk,
673
+ cols,
674
+ colSet: new Set(cols),
675
+ ftsCols,
676
+ legs,
677
+ ...synonyms,
678
+ ...validity
679
+ };
566
680
  }
681
+ __name(searchConfigFor, "searchConfigFor");
567
682
  function pickLeg(table, legs, using) {
568
683
  if (legs.length === 0) return null;
569
684
  if (using !== void 0) {
570
685
  const hit = legs.find((l) => l.column === using);
571
686
  if (!hit) {
572
- throw new Error(
573
- `search(${table}): using "${using}" bir vekt\xF6r kolunu adlam\u0131yor \u2014 mevcut: ${legs.map((l) => l.column).join(", ")}`
574
- );
687
+ throw new Error(`search(${table}): using "${using}" bir vekt\xF6r kolunu adlam\u0131yor \u2014 mevcut: ${legs.map((l) => l.column).join(", ")}`);
575
688
  }
576
689
  return hit;
577
690
  }
578
691
  if (legs.length === 1) return legs[0];
579
692
  throw new Error(`search(${table}): birden \xE7ok vekt\xF6r kolu var \u2014 'using' ile se\xE7in (FR-013) \u2014 salt metin ar\u0131yorsan mode:"text" kullan`);
580
693
  }
581
- var HALF_LIFE_UNIT_SECONDS = { s: 1, m: 60, h: 3600, d: 86400 };
694
+ __name(pickLeg, "pickLeg");
695
+ var HALF_LIFE_UNIT_SECONDS = {
696
+ s: 1,
697
+ m: 60,
698
+ h: 3600,
699
+ d: 86400
700
+ };
582
701
  function parseHalfLife(s) {
583
702
  const m = /^(\d+)(s|m|h|d)$/.exec(s);
584
703
  const sec = m === null ? 0 : Number(m[1]) * HALF_LIFE_UNIT_SECONDS[m[2]];
@@ -587,10 +706,20 @@ function parseHalfLife(s) {
587
706
  }
588
707
  return sec;
589
708
  }
590
- var WHERE_OPS = { gt: ">", gte: ">=", lt: "<", lte: "<=", neq: "<>" };
709
+ __name(parseHalfLife, "parseHalfLife");
710
+ var WHERE_OPS = {
711
+ gt: ">",
712
+ gte: ">=",
713
+ lt: "<",
714
+ lte: "<=",
715
+ neq: "<>"
716
+ };
591
717
  function ftsExprOf(ftsCols) {
592
- return [...ftsCols].sort().map((c) => `coalesce(t.${quoteIdent(c)},'')`).join(" || ' ' || ");
718
+ return [
719
+ ...ftsCols
720
+ ].sort().map((c) => `coalesce(t.${quoteIdent(c)},'')`).join(" || ' ' || ");
593
721
  }
722
+ __name(ftsExprOf, "ftsExprOf");
594
723
  function expandSynonyms(query, map) {
595
724
  const lower = {};
596
725
  for (const [word, alts] of Object.entries(map)) lower[word.toLowerCase()] = alts;
@@ -603,9 +732,10 @@ function expandSynonyms(query, map) {
603
732
  }).join("");
604
733
  }).join("");
605
734
  }
606
- var searchLogger = (evt, fields) => {
735
+ __name(expandSynonyms, "expandSynonyms");
736
+ var searchLogger = /* @__PURE__ */ __name((evt, fields) => {
607
737
  console.log(evt, JSON.stringify(fields));
608
- };
738
+ }, "searchLogger");
609
739
  function logNoResults(table, query, mode, n) {
610
740
  if (n === 0) {
611
741
  searchLogger?.("palbase.search.no_results", {
@@ -615,26 +745,29 @@ function logNoResults(table, query, mode, n) {
615
745
  });
616
746
  }
617
747
  }
748
+ __name(logNoResults, "logNoResults");
618
749
  async function fetchFacets(live, table, colSet, facets, where, extraWhere = () => "") {
619
750
  const bind = [];
620
- const add = (v) => {
751
+ const add = /* @__PURE__ */ __name((v) => {
621
752
  bind.push(v);
622
753
  return `$${bind.length}`;
623
- };
754
+ }, "add");
624
755
  const whereSql = compileWhere(table, colSet, where, add) + extraWhere(add);
625
- const parts = facets.map(
626
- (col) => `(SELECT '${col.replace(/'/g, "''")}' AS f, t.${quoteIdent(col)}::text AS v, count(*) AS n FROM ${quoteTable(table)} t WHERE true${whereSql} GROUP BY 2 ORDER BY 3 DESC LIMIT 20)`
627
- );
756
+ const parts = facets.map((col) => `(SELECT '${col.replace(/'/g, "''")}' AS f, t.${quoteIdent(col)}::text AS v, count(*) AS n FROM ${quoteTable(table)} t WHERE true${whereSql} GROUP BY 2 ORDER BY 3 DESC LIMIT 20)`);
628
757
  const rows = await live.unsafe(parts.join(" UNION ALL "), bind);
629
758
  const out = {};
630
759
  for (const col of facets) out[col] = [];
631
760
  for (const r of rows) {
632
761
  if (typeof r.f === "string" && out[r.f] !== void 0) {
633
- out[r.f].push({ value: r.v ?? null, count: Number(r.n) });
762
+ out[r.f].push({
763
+ value: r.v ?? null,
764
+ count: Number(r.n)
765
+ });
634
766
  }
635
767
  }
636
768
  return out;
637
769
  }
770
+ __name(fetchFacets, "fetchFacets");
638
771
  function validitySql(v, add) {
639
772
  if (v === "all") return "";
640
773
  if (v !== void 0) {
@@ -643,26 +776,96 @@ function validitySql(v, add) {
643
776
  }
644
777
  return ` AND t."valid_to" IS NULL`;
645
778
  }
779
+ __name(validitySql, "validitySql");
646
780
  function excludeSql(pk, ids, add) {
647
781
  if (ids.length === 0) return "";
648
782
  if (ids.length === 1) return ` AND t.${quoteIdent(pk)} <> ${add(ids[0])}`;
649
783
  return ` AND t.${quoteIdent(pk)}::text <> ALL(ARRAY[${ids.map((x) => add(x)).join(", ")}])`;
650
784
  }
785
+ __name(excludeSql, "excludeSql");
786
+ function primaryKeyOf(table) {
787
+ const t = tableDefOf(table);
788
+ const columns = t?.columns ?? {};
789
+ const cols = Object.keys(columns);
790
+ if (cols.length === 0) return null;
791
+ const declared = t?.primaryKey;
792
+ if (Array.isArray(declared)) {
793
+ if (declared.length !== 1) return null;
794
+ const only = declared[0];
795
+ return typeof only === "string" && cols.includes(only) ? only : null;
796
+ }
797
+ const pkOf = /* @__PURE__ */ __name((c) => {
798
+ if (c === null || typeof c !== "object") return false;
799
+ const rec = c;
800
+ const def = "_def" in rec ? rec._def : rec;
801
+ return def !== null && typeof def === "object" && def.primaryKey === true;
802
+ }, "pkOf");
803
+ const pks = cols.filter((c) => pkOf(columns[c]));
804
+ return pks.length === 1 ? pks[0] : null;
805
+ }
806
+ __name(primaryKeyOf, "primaryKeyOf");
807
+ function isSinglePkEquality(where, pk) {
808
+ if (pk === null) return false;
809
+ const keys = Object.keys(where);
810
+ if (keys.length !== 1 || keys[0] !== pk) return false;
811
+ const v = where[pk];
812
+ return v !== null && (typeof v !== "object" || Array.isArray(v));
813
+ }
814
+ __name(isSinglePkEquality, "isSinglePkEquality");
815
+ function assertNotAppendOnly(caller, table) {
816
+ const def = tableDefOf(table);
817
+ if (def?.appendOnly !== true) return;
818
+ 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).`);
819
+ }
820
+ __name(assertNotAppendOnly, "assertNotAppendOnly");
651
821
  function schemaColumns(table) {
652
822
  const t = tableDefOf(table);
653
823
  const cols = t?.columns ? Object.keys(t.columns) : [];
654
824
  return cols.length > 0 ? new Set(cols) : null;
655
825
  }
826
+ __name(schemaColumns, "schemaColumns");
827
+ function colRefSql(table, known, ref, caller, alias = "t") {
828
+ const name = ref.$col;
829
+ const shapeOK = /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
830
+ if (known !== null ? !known.has(name) : !shapeOK) {
831
+ throw new Error(`${caller}(${table}): col("${name}") tabloda yok (FR-011)`);
832
+ }
833
+ return `${alias}.${quoteIdent(name)}`;
834
+ }
835
+ __name(colRefSql, "colRefSql");
836
+ function selectClause(table, known, select) {
837
+ if (select !== void 0 && select.length === 0) {
838
+ 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.`);
839
+ }
840
+ if (select === void 0) return "*";
841
+ return select.map((col) => {
842
+ const shapeOK = /^[A-Za-z_][A-Za-z0-9_]*$/.test(col);
843
+ if (known !== null ? !known.has(col) : !shapeOK) {
844
+ throw new Error(`findMany(${table}): select kolonu "${col}" tabloda yok`);
845
+ }
846
+ return `t.${quoteIdent(col)}`;
847
+ }).join(", ");
848
+ }
849
+ __name(selectClause, "selectClause");
656
850
  function orderClause(table, known, orderBy) {
657
851
  if (!orderBy) return "";
658
- const col = orderBy.column;
659
- const shapeOK = /^[A-Za-z_][A-Za-z0-9_]*$/.test(col);
660
- if (known !== null ? !known.has(col) : !shapeOK) {
661
- throw new Error(`findMany(${table}): orderBy kolonu "${col}" tabloda yok`);
662
- }
663
- const dir = orderBy.direction === "desc" ? "DESC" : "ASC";
664
- return ` ORDER BY ${quoteIdent(col)} ${dir}`;
852
+ const items = Array.isArray(orderBy) ? orderBy : [
853
+ orderBy
854
+ ];
855
+ if (items.length === 0) return "";
856
+ const rendered = items.map((o) => {
857
+ const col = o.column;
858
+ const shapeOK = /^[A-Za-z_][A-Za-z0-9_]*$/.test(col);
859
+ if (known !== null ? !known.has(col) : !shapeOK) {
860
+ throw new Error(`findMany(${table}): orderBy kolonu "${col}" tabloda yok`);
861
+ }
862
+ const dir = o.direction === "desc" ? "DESC" : "ASC";
863
+ const nulls = o.nulls === "first" ? " NULLS FIRST" : o.nulls === "last" ? " NULLS LAST" : "";
864
+ return `${quoteIdent(col)} ${dir}${nulls}`;
865
+ });
866
+ return ` ORDER BY ${rendered.join(", ")}`;
665
867
  }
868
+ __name(orderClause, "orderClause");
666
869
  function limitClause(limit) {
667
870
  if (limit === void 0) return "";
668
871
  if (!Number.isInteger(limit) || limit < 0) {
@@ -670,46 +873,131 @@ function limitClause(limit) {
670
873
  }
671
874
  return ` LIMIT ${limit}`;
672
875
  }
876
+ __name(limitClause, "limitClause");
673
877
  function offsetClause(offset, limit) {
674
878
  if (offset === void 0) return "";
675
879
  if (!Number.isInteger(offset) || offset < 0) {
676
- throw new Error(
677
- `findMany: offset bir negatif olmayan tam say\u0131 olmal\u0131 (geldi: ${String(offset)})`
678
- );
880
+ throw new Error(`findMany: offset bir negatif olmayan tam say\u0131 olmal\u0131 (geldi: ${String(offset)})`);
679
881
  }
680
882
  if (limit === void 0) {
681
- throw new Error(
682
- "findMany: offset yaln\u0131z limit ile birlikte verilir \u2014 limitsiz offset bir sayfa de\u011Fil, s\u0131n\u0131rs\u0131z bir kuyru\u011Fun kayd\u0131r\u0131lm\u0131\u015F\u0131d\u0131r"
683
- );
883
+ throw new Error("findMany: offset yaln\u0131z limit ile birlikte verilir \u2014 limitsiz offset bir sayfa de\u011Fil, s\u0131n\u0131rs\u0131z bir kuyru\u011Fun kayd\u0131r\u0131lm\u0131\u015F\u0131d\u0131r");
684
884
  }
685
885
  return ` OFFSET ${offset}`;
686
886
  }
687
- function compileWhere(table, colSet, where, add, caller = "search") {
887
+ __name(offsetClause, "offsetClause");
888
+ function compileWhereBare(table, colSet, where, add, caller = "search", alias = "t", depth = 0) {
688
889
  const parts = [];
689
890
  const transforms = transformsOf(currentSchema, table);
690
- const bindFor = (col) => {
891
+ const bindFor = /* @__PURE__ */ __name((col) => {
691
892
  const t = transforms.get(col);
692
893
  return t?.toDb === void 0 ? add : (v) => add(v === null || v === void 0 ? v : t.toDb(v));
693
- };
894
+ }, "bindFor");
694
895
  assertUsableFilter(caller, table, where);
896
+ if (isSqlFragment(where)) {
897
+ const { text, values } = where.$sql;
898
+ if (text.length !== values.length + 1) {
899
+ throw new Error(`${caller}(${table}): sqlFragment par\xE7alar\u0131 bozuk (${text.length} metin, ${values.length} de\u011Fer) \u2014 bunu \xFCreten tek \u015Fey template tag'i, elle kurulmu\u015F bir nesne de\u011Fil`);
900
+ }
901
+ let out = text[0] ?? "";
902
+ for (let i = 0; i < values.length; i++) out += `${add(values[i])}${text[i + 1] ?? ""}`;
903
+ return `(${out})`;
904
+ }
695
905
  for (const [col, cond] of Object.entries(where)) {
906
+ if (col === "OR" || col === "AND") {
907
+ const branches = cond;
908
+ if (branches.length === 0) {
909
+ parts.push(col === "OR" ? "false" : "true");
910
+ continue;
911
+ }
912
+ const compiled = branches.map((b) => `(${compileWhereBare(table, colSet, b, add, caller, alias, depth)})`);
913
+ parts.push(`(${compiled.join(col === "OR" ? " OR " : " AND ")})`);
914
+ continue;
915
+ }
916
+ if (col === "has") {
917
+ const rels = currentRelations.get(table) ?? [];
918
+ for (const [relName, inner] of Object.entries(cond)) {
919
+ const rel = rels.find((r) => r.name === relName);
920
+ if (rel === void 0) {
921
+ const known = rels.map((r) => r.name).sort();
922
+ throw new Error(`${caller}(${table}): has.${relName} diye bir ili\u015Fki yok${known.length > 0 ? ` (bilinenler: ${known.join(", ")})` : " \u2014 bu tablonun hi\xE7 ili\u015Fkisi yok"}. \u0130li\u015Fkiler yabanc\u0131 anahtarlardan T\xDCRET\u0130L\u0130R; ad\u0131n\u0131 de\u011Fi\u015Ftirmek i\xE7in references(..., { as, reverseAs }).`);
923
+ }
924
+ const ourCol = rel.kind === "many" ? primaryKeyOf(table) : rel.via;
925
+ const theirCol = rel.kind === "many" ? rel.via : primaryKeyOf(rel.to);
926
+ if (ourCol === null || theirCol === null) {
927
+ const missing = ourCol === null ? table : rel.to;
928
+ 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.`);
929
+ }
930
+ const sub = `r${depth + 1}`;
931
+ const innerSql = compileWhereBare(rel.to, schemaColumns(rel.to), inner ?? {}, add, caller, sub, depth + 1);
932
+ parts.push(`${alias}.${quoteIdent(ourCol)} IN (SELECT ${sub}.${quoteIdent(theirCol)} FROM ${quoteTable(rel.to)} ${sub}${innerSql === "" ? "" : ` WHERE ${innerSql}`})`);
933
+ }
934
+ continue;
935
+ }
936
+ if (col === "NOT") {
937
+ parts.push(`NOT (${compileWhereBare(table, colSet, cond, add, caller, alias, depth)})`);
938
+ continue;
939
+ }
696
940
  if (colSet !== null && !colSet.has(col)) {
697
941
  throw new Error(`${caller}(${table}): where kolonu "${col}" tabloda yok (FR-016)`);
698
942
  }
699
- const q = `t.${quoteIdent(col)}`;
943
+ const q = `${alias}.${quoteIdent(col)}`;
700
944
  const bind = bindFor(col);
945
+ if (isColRef(cond)) {
946
+ parts.push(`${q} = ${colRefSql(table, colSet, cond, caller, alias)}`);
947
+ continue;
948
+ }
949
+ if (isNowExpr(cond)) {
950
+ parts.push(`${q} = now()`);
951
+ continue;
952
+ }
701
953
  if (cond !== null && typeof cond === "object" && !Array.isArray(cond)) {
702
954
  for (const [op, v] of Object.entries(cond)) {
955
+ if (op === "contains" || op === "icontains" || op === "startsWith" || op === "endsWith") {
956
+ if (typeof v !== "string") {
957
+ throw new Error(`${caller}(${table}): where.${col}.${op} bir string olmal\u0131`);
958
+ }
959
+ const lit = v.replace(/([\\%_])/g, "\\$1");
960
+ const pattern = op === "startsWith" ? `${lit}%` : op === "endsWith" ? `%${lit}` : `%${lit}%`;
961
+ parts.push(`${q} ${op === "icontains" ? "ILIKE" : "LIKE"} ${bind(pattern)}`);
962
+ continue;
963
+ }
964
+ if (op === "isNull") {
965
+ if (typeof v !== "boolean") {
966
+ throw new Error(`${caller}(${table}): where.${col}.isNull bir boolean olmal\u0131`);
967
+ }
968
+ parts.push(`${q} IS ${v ? "NULL" : "NOT NULL"}`);
969
+ continue;
970
+ }
703
971
  if (op === "in") {
704
972
  if (!Array.isArray(v)) throw new Error(`${caller}(${table}): where.${col}.in bir dizi olmal\u0131`);
973
+ if (v.some(isColRef)) {
974
+ throw new Error(`${caller}(${table}): where.${col}.in col() ile kullan\u0131lamaz \u2014 kolon kar\u015F\u0131la\u015Ft\u0131rmas\u0131 i\xE7in \xE7\u0131plak bi\xE7imi ({ ${col}: col("\u2026") }) ya da gt/gte/lt/lte/neq kullan\u0131n`);
975
+ }
705
976
  if (v.length === 0) {
706
977
  parts.push("false");
707
978
  continue;
708
979
  }
709
980
  parts.push(`${q} IN (${v.map((x) => bind(x)).join(", ")})`);
981
+ } else if (isNowExpr(v)) {
982
+ if (!(op in WHERE_OPS)) {
983
+ throw new Error(`${caller}(${table}): where.${col}.${op} now() ile kullan\u0131lamaz \u2014 e\u015Fitlik i\xE7in \xE7\u0131plak bi\xE7imi kullan\u0131n: { ${col}: now() }`);
984
+ }
985
+ parts.push(`${q} ${WHERE_OPS[op]} now()`);
986
+ } else if (isColumnExpr(v)) {
987
+ throw new Error(`${caller}(${table}): where.${col}.${op} bir YAZMA ifadesi ald\u0131 (increment/decrement) \u2014 kar\u015F\u0131la\u015Ft\u0131rma de\u011Feri de\u011Fil. Sunucu saati i\xE7in now() kullan\u0131n; say\u0131sal bir e\u015Fik i\xE7in de\u011Feri yaz\u0131n.`);
988
+ } else if (isColRef(v)) {
989
+ if (!(op in WHERE_OPS)) {
990
+ throw new Error(`${caller}(${table}): where.${col}.${op} col() ile kullan\u0131lamaz \u2014 e\u015Fitlik i\xE7in \xE7\u0131plak bi\xE7imi kullan\u0131n: { ${col}: col("\u2026") }`);
991
+ }
992
+ parts.push(`${q} ${WHERE_OPS[op]} ${colRefSql(table, colSet, v, caller, alias)}`);
993
+ } else if (looksLikeUnbrandedRef(v) !== null) {
994
+ const kind = looksLikeUnbrandedRef(v);
995
+ const made = kind === "col" ? "col()" : kind === "sql" ? "sqlFragment" : "now()";
996
+ const how = kind === "col" ? 'col("kolon_ad\u0131")' : kind === "sql" ? "sqlFragment`\u2026`" : "now()";
997
+ throw new Error(`${caller}(${table}): where.${col}.${op} bir ${made} TAKL\u0130D\u0130 ald\u0131 \u2014 bu nesne bu s\xFCre\xE7te ${made} ile \xFCretilmemi\u015F. \u0130stek g\xF6vdesinden gelen bir filtre i\u015Faret\xE7i TA\u015EIYAMAZ; ko\u015Fulu kendi kodunuzda ${how} ile kurun.`);
710
998
  } else if (op in WHERE_OPS) {
711
- if (v === null && (op === "neq" || op === "eq")) {
712
- parts.push(`${q} IS ${op === "neq" ? "NOT " : ""}NULL`);
999
+ if (v === null && op === "neq") {
1000
+ parts.push(`${q} IS NOT NULL`);
713
1001
  continue;
714
1002
  }
715
1003
  parts.push(`${q} ${WHERE_OPS[op]} ${bind(v)}`);
@@ -723,8 +1011,14 @@ function compileWhere(table, colSet, where, add, caller = "search") {
723
1011
  parts.push(`${q} = ${bind(cond)}`);
724
1012
  }
725
1013
  }
726
- return parts.length === 0 ? "" : ` AND ${parts.join(" AND ")}`;
1014
+ return parts.join(" AND ");
727
1015
  }
1016
+ __name(compileWhereBare, "compileWhereBare");
1017
+ function compileWhere(table, colSet, where, add, caller = "search") {
1018
+ const bare = compileWhereBare(table, colSet, where, add, caller);
1019
+ return bare === "" ? "" : ` AND ${bare}`;
1020
+ }
1021
+ __name(compileWhere, "compileWhere");
728
1022
  function onConflictTail(cols, conflict, action) {
729
1023
  const target = `ON CONFLICT (${conflict.map(quoteIdent).join(", ")})`;
730
1024
  if (action === "ignore") return `${target} DO NOTHING`;
@@ -732,26 +1026,22 @@ function onConflictTail(cols, conflict, action) {
732
1026
  const sets = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
733
1027
  return sets.length ? `${target} DO UPDATE SET ${sets.join(", ")}` : `${target} DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
734
1028
  }
1029
+ __name(onConflictTail, "onConflictTail");
735
1030
  var cachedVectorSchema = null;
736
1031
  var cachedTrgmSchema = null;
737
1032
  async function trgmSchemaOf(live) {
738
1033
  if (cachedTrgmSchema !== null) return cachedTrgmSchema;
739
- const rows = await live.unsafe(
740
- "select n.nspname from pg_extension e join pg_namespace n on n.oid = e.extnamespace where e.extname = 'pg_trgm'"
741
- );
1034
+ const rows = await live.unsafe("select n.nspname from pg_extension e join pg_namespace n on n.oid = e.extnamespace where e.extname = 'pg_trgm'");
742
1035
  cachedTrgmSchema = rows?.[0]?.nspname ?? "public";
743
1036
  return cachedTrgmSchema;
744
1037
  }
1038
+ __name(trgmSchemaOf, "trgmSchemaOf");
745
1039
  async function vectorSchemaWithGuc(runner) {
746
1040
  if (cachedVectorSchema !== null) {
747
- await runner.unsafe(
748
- "select set_config('hnsw.iterative_scan','relaxed_order',true), set_config('hnsw.max_scan_tuples','200000',true), set_config('hnsw.ef_search','200',true)"
749
- );
1041
+ await runner.unsafe("select set_config('hnsw.iterative_scan','relaxed_order',true), set_config('hnsw.max_scan_tuples','200000',true), set_config('hnsw.ef_search','200',true)");
750
1042
  return cachedVectorSchema;
751
1043
  }
752
- const rows = await runner.unsafe(
753
- "select set_config('hnsw.iterative_scan','relaxed_order',true), set_config('hnsw.max_scan_tuples','200000',true), set_config('hnsw.ef_search','200',true), (select n.nspname from pg_extension e join pg_namespace n on n.oid = e.extnamespace where e.extname = 'vector') as nspname"
754
- );
1044
+ const rows = await runner.unsafe("select set_config('hnsw.iterative_scan','relaxed_order',true), set_config('hnsw.max_scan_tuples','200000',true), set_config('hnsw.ef_search','200',true), (select n.nspname from pg_extension e join pg_namespace n on n.oid = e.extnamespace where e.extname = 'vector') as nspname");
755
1045
  const name = rows?.[0]?.nspname;
756
1046
  if (typeof name !== "string" || name === "") {
757
1047
  throw new Error("pgvector extension kurulu de\u011Fil \u2014 vector aramas\u0131 \xE7al\u0131\u015Famaz (extensions beyan\u0131 deploy'dan ge\xE7ti mi?)");
@@ -759,11 +1049,13 @@ async function vectorSchemaWithGuc(runner) {
759
1049
  cachedVectorSchema = name;
760
1050
  return name;
761
1051
  }
1052
+ __name(vectorSchemaWithGuc, "vectorSchemaWithGuc");
762
1053
  var secretReader = null;
763
1054
  function setSecretReader(fn) {
764
1055
  secretReader = fn;
765
1056
  }
766
- var embedFetch = (url, init) => fetch(url, init);
1057
+ __name(setSecretReader, "setSecretReader");
1058
+ var embedFetch = /* @__PURE__ */ __name((url, init) => fetch(url, init), "embedFetch");
767
1059
  async function embedQuery(embed, text) {
768
1060
  if (secretReader === null) {
769
1061
  throw new Error(`query embed: secret reader ba\u011Flanmam\u0131\u015F \u2014 ${embed.apiKeyName} okunam\u0131yor`);
@@ -778,11 +1070,18 @@ async function embedQuery(embed, text) {
778
1070
  try {
779
1071
  const res = await embedFetch(url, {
780
1072
  method: "POST",
781
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
1073
+ headers: {
1074
+ "Content-Type": "application/json",
1075
+ Authorization: `Bearer ${key}`
1076
+ },
782
1077
  body: JSON.stringify({
783
1078
  model: embed.model,
784
- input: [text],
785
- ...embed.dimensions !== void 0 ? { dimensions: embed.dimensions } : {}
1079
+ input: [
1080
+ text
1081
+ ],
1082
+ ...embed.dimensions !== void 0 ? {
1083
+ dimensions: embed.dimensions
1084
+ } : {}
786
1085
  }),
787
1086
  signal: controller.signal
788
1087
  });
@@ -799,9 +1098,13 @@ async function embedQuery(embed, text) {
799
1098
  clearTimeout(timer);
800
1099
  }
801
1100
  }
1101
+ __name(embedQuery, "embedQuery");
802
1102
  function createOps(tx) {
803
- const at = () => resolveTx(tx);
1103
+ const at = /* @__PURE__ */ __name(() => resolveTx(tx), "at");
804
1104
  const ops = {
1105
+ // Generic dönüş, çağıranın İDDİASIDIR (FR-021). Motor her zaman satır objeleri
1106
+ // üretir; `T` onlara bir ad vermekten ibarettir ve verilmezse `unknown` kalır —
1107
+ // yani "bilmiyorum", ki ham SQL için doğru cevap odur.
805
1108
  async query(sql, params = []) {
806
1109
  const bound = params.map((p) => Array.isArray(p) ? encodePgArray(p) : p);
807
1110
  return asWireRows(await (await at()).unsafe(sql, bound));
@@ -814,27 +1117,141 @@ function createOps(tx) {
814
1117
  const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "insert"));
815
1118
  const inserted = rows[0];
816
1119
  if (!inserted) {
817
- throw new Error(
818
- `insert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`
819
- );
1120
+ throw new Error(`insert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`);
820
1121
  }
821
1122
  return asTableRow(table, inserted);
822
1123
  },
823
1124
  /**
824
- * INSERT the row, or UPDATE it when it collides on `onConflict`.
825
- *
826
- * WHY IT IS AN OPERATION rather than a recipe. "Try the insert, catch the
827
- * unique violation, update instead" does not work here: a request runs in ONE
828
- * Postgres transaction, so the failed insert aborts it and every later
829
- * statement answers `current transaction is aborted`. A tenant measured that
830
- * as 7 of 8 concurrent requests returning 500, gave up on upsert, and had a
831
- * trigger create the row instead a workaround that needs a new trigger for
832
- * every table with a unique row.
833
- *
834
- * The conflict columns are excluded from the SET list: they are what MATCHED,
835
- * so writing them back is at best a no-op and at worst a surprise.
836
- */
837
- async upsert(table, data, opts) {
1125
+ * Bir ADI kilitle satırı değil (FR-054).
1126
+ *
1127
+ * "Aynı anda tek bir fatura kapanışı koşsun" gibi, kilitlenecek satırı
1128
+ * OLMAYAN işler için. Anahtar bir metin; Postgres 64-bit tamsayı istediği
1129
+ * için `hashtextextended` ile hash'leniyor ve metin SQL'e hiç GİRMİYOR.
1130
+ *
1131
+ * YALNIZ transaction kapsamlı aile (`pg_advisory_xact_lock`). Oturum
1132
+ * kapsamlı sürüm bırakıldığı yerde KALIR: bağlantı havuza döner, kilit
1133
+ * üstünde kalır ve bir sonraki isteği — hiç bitmeyecek şekilde — bekletir.
1134
+ * Transaction kapsamlısı commit/rollback'te Postgres tarafından bırakılır,
1135
+ * yani unutulması imkânsız.
1136
+ */
1137
+ async advisoryXactLock(key) {
1138
+ await (await at()).unsafe("select pg_advisory_xact_lock(hashtextextended($1, 0))", [
1139
+ key
1140
+ ]);
1141
+ },
1142
+ /**
1143
+ * Bu satırları KİLİTLE, ve sırayı KATMAN koysun (FR-035).
1144
+ *
1145
+ * D-017: katman sıralamayı değil, **sıralamayı mümkün kılan primitifi**
1146
+ * verir. Uygulama dokunacağı kümeyi önden bildirir; sırayı burası koyar.
1147
+ * Çağıranın verdiği sıraya güvenmek, iki çağıranın aynı iki satıra ters
1148
+ * sırayla girmesi demektir — ve deadlock'un bedeli ucuz değil:
1149
+ * `deadlock_timeout` varsayılanı **1 saniye**, yani kaybeden işlem bir
1150
+ * saniye bekledikten sonra hata alır.
1151
+ *
1152
+ * `FOR NO KEY UPDATE`, `updateMany`'nin CTE'siyle AYNI kilit modu: iki
1153
+ * yazma yolunun tek bir sırası ve tek bir modu olmak zorunda, yoksa biri
1154
+ * diğerini bekletir.
1155
+ */
1156
+ async lockRows(table, ids) {
1157
+ const unique = [
1158
+ ...new Set(ids)
1159
+ ].sort();
1160
+ if (unique.length === 0) {
1161
+ return;
1162
+ }
1163
+ const pk = primaryKeyOf(table);
1164
+ if (pk === null) {
1165
+ throw new Error(`lockRows(${table}): tek-kolon primary key bulunamad\u0131 \u2014 kilit s\u0131ras\u0131 bir anahtar ister (FR-035).`);
1166
+ }
1167
+ const params = [];
1168
+ const add = /* @__PURE__ */ __name((v) => {
1169
+ params.push(v);
1170
+ return `$${params.length}`;
1171
+ }, "add");
1172
+ const list = unique.map((id) => add(id)).join(", ");
1173
+ await (await at()).unsafe(`SELECT t.${quoteIdent(pk)} FROM ${quoteTable(table)} t WHERE t.${quoteIdent(pk)} IN (${list}) ORDER BY t.${quoteIdent(pk)} FOR NO KEY UPDATE`, params);
1174
+ },
1175
+ /**
1176
+ * Bir idempotency anahtarını SAHİPLEN (FR-033).
1177
+ *
1178
+ * `{ inserted: true, row }` → bu çağrı yazdı.
1179
+ * `{ inserted: false, row }` → başkası önce yazdı; dönen satır ONUN satırı.
1180
+ *
1181
+ * ÖLÇÜLDÜ (pg16, spec Research Appendix) — üç dalın üçü de farklı davranıyor:
1182
+ *
1183
+ * plain INSERT ikinci oturum 2121 ms BLOKLANIR, sonra 23505 alır;
1184
+ * birincinin satırı korunur ← istenen
1185
+ * ON CONFLICT DO UPDATE ikincinin verisi birincininkini EZER
1186
+ * ON CONFLICT DO NOTHING `INSERT 0 0` — sessiz; çağıran ne olduğunu bilemez
1187
+ *
1188
+ * Bu yüzden `ON CONFLICT` KULLANILMIYOR. Ödeme yolunda "ikinci çağrı
1189
+ * birincinin sonucunu görsün" idempotency'nin tanımıdır; ezmek ya da
1190
+ * susmak ikisi de yanlış cevap.
1191
+ *
1192
+ * INSERT bir SAVEPOINT içinde koşuyor: başarısız bir INSERT transaction'ı
1193
+ * ABORT eder (25P02) ve savepoint olmadan yakalama işe yaramaz — hata
1194
+ * yakalanır ama sonraki SELECT de "current transaction is aborted" alır.
1195
+ */
1196
+ async claim(table, unique, extra = {}) {
1197
+ const keyCols = Object.keys(unique);
1198
+ if (keyCols.length === 0) {
1199
+ throw new Error(`claim(${table}): benzersiz alan verilmedi. claim, bir anahtar\u0131 sahiplenmektir; anahtar yoksa sahiplenecek bir \u015Fey de yok \u2014 insert(${table}, \u2026) kullan\u0131n.`);
1200
+ }
1201
+ assertUsableWriteValues("claim", table, keyCols, unique);
1202
+ const data = {
1203
+ ...unique,
1204
+ ...extra
1205
+ };
1206
+ const cols = Object.keys(data);
1207
+ const live = await at();
1208
+ try {
1209
+ return await live.savepoint(async (sp) => {
1210
+ const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
1211
+ const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`, asBindParams(table, cols, data, "claim"));
1212
+ if (!rows[0]) {
1213
+ throw new Error(`claim(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si).`);
1214
+ }
1215
+ return {
1216
+ inserted: true,
1217
+ row: asTableRow(table, rows[0])
1218
+ };
1219
+ });
1220
+ } catch (e) {
1221
+ if (!isUniqueViolation(e)) throw e;
1222
+ const params = [];
1223
+ const add = /* @__PURE__ */ __name((v) => {
1224
+ params.push(v);
1225
+ return `$${params.length}`;
1226
+ }, "add");
1227
+ const where = keyCols.map((c) => `t.${quoteIdent(c)} = ${add(unique[c])}`).join(" AND ");
1228
+ const found = await (await at()).unsafe(`SELECT * FROM ${quoteTable(table)} t WHERE ${where} LIMIT 1`, params);
1229
+ if (!found[0]) {
1230
+ const named = constraintOf(e);
1231
+ 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.`);
1232
+ }
1233
+ return {
1234
+ inserted: false,
1235
+ row: asTableRow(table, found[0])
1236
+ };
1237
+ }
1238
+ },
1239
+ /**
1240
+ * INSERT the row, or UPDATE it when it collides on `onConflict`.
1241
+ *
1242
+ * WHY IT IS AN OPERATION rather than a recipe. "Try the insert, catch the
1243
+ * unique violation, update instead" does not work here: a request runs in ONE
1244
+ * Postgres transaction, so the failed insert aborts it and every later
1245
+ * statement answers `current transaction is aborted`. A tenant measured that
1246
+ * as 7 of 8 concurrent requests returning 500, gave up on upsert, and had a
1247
+ * trigger create the row instead — a workaround that needs a new trigger for
1248
+ * every table with a unique row.
1249
+ *
1250
+ * The conflict columns are excluded from the SET list: they are what MATCHED,
1251
+ * so writing them back is at best a no-op and at worst a surprise.
1252
+ */
1253
+ async put(table, data, opts) {
1254
+ assertNotAppendOnly("put", table);
838
1255
  const conflict = opts.onConflict;
839
1256
  if (conflict.length === 0) {
840
1257
  throw new Error(`upsert into ${table}: onConflict en az bir kolon ad\u0131 ister`);
@@ -845,33 +1262,29 @@ function createOps(tx) {
845
1262
  const ph = cols.map((_, i) => `$${i + 1}`).join(", ");
846
1263
  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 *`;
847
1264
  const live = await at();
848
- const bindData = asBindParams(table, cols, data, "upsert");
1265
+ const bindData = asBindParams(table, cols, data, "put");
849
1266
  const first = await live.unsafe(insertSql, bindData);
850
1267
  if (first[0]) return asTableRow(table, first[0]);
851
1268
  const condSql = conflict.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ");
852
1269
  const condBind = conflict.map((c) => data[c]);
853
- const cur = await live.unsafe(
854
- `SELECT * FROM ${quoteTable(table)} WHERE ${condSql} AND "valid_to" IS NULL`,
855
- condBind
856
- );
1270
+ const cur = await live.unsafe(`SELECT * FROM ${quoteTable(table)} WHERE ${condSql} AND "valid_to" IS NULL`, condBind);
857
1271
  const old = cur[0];
858
1272
  if (old === void 0) {
859
1273
  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)`);
860
1274
  }
861
1275
  const pk = Object.keys(old).includes("id") ? "id" : Object.keys(old)[0];
862
- await live.unsafe(
863
- `UPDATE ${quoteTable(table)} SET "valid_to" = now() WHERE ${quoteIdent(pk)} = $1 AND "valid_to" IS NULL`,
864
- [old[pk]]
865
- );
1276
+ await live.unsafe(`UPDATE ${quoteTable(table)} SET "valid_to" = now() WHERE ${quoteIdent(pk)} = $1 AND "valid_to" IS NULL`, [
1277
+ old[pk]
1278
+ ]);
866
1279
  const second = await live.unsafe(insertSql, bindData);
867
1280
  if (!second[0]) {
868
1281
  throw new Error(`upsert into ${table}: e\u015Fzamanl\u0131 supersede yar\u0131\u015F\u0131 \u2014 yeni sat\u0131r a\xE7\u0131lamad\u0131, iste\u011Fi yineleyin (D-020)`);
869
1282
  }
870
1283
  const fresh = second[0];
871
- await live.unsafe(
872
- `UPDATE ${quoteTable(table)} SET "superseded_by" = $2 WHERE ${quoteIdent(pk)} = $1`,
873
- [old[pk], fresh[pk]]
874
- );
1284
+ await live.unsafe(`UPDATE ${quoteTable(table)} SET "superseded_by" = $2 WHERE ${quoteIdent(pk)} = $1`, [
1285
+ old[pk],
1286
+ fresh[pk]
1287
+ ]);
875
1288
  return asTableRow(table, second[0]);
876
1289
  }
877
1290
  const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
@@ -879,50 +1292,54 @@ function createOps(tx) {
879
1292
  const assignments = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
880
1293
  const action = assignments.length ? `DO UPDATE SET ${assignments.join(", ")}` : `DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
881
1294
  const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
882
- const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "upsert"));
1295
+ const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "put"));
883
1296
  const row = rows[0];
884
1297
  if (!row) {
885
- throw new Error(
886
- `upsert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`
887
- );
1298
+ throw new Error(`upsert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`);
888
1299
  }
889
1300
  return asTableRow(table, row);
890
1301
  },
891
1302
  async update(table, id, data) {
1303
+ assertNotAppendOnly("update", table);
892
1304
  const cols = Object.keys(data);
893
1305
  if (cols.length === 0) return ops.findById(table, id);
894
1306
  const assignments = cols.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(", ");
895
1307
  const sql = `UPDATE ${quoteTable(table)} SET ${assignments} WHERE id = $${cols.length + 1} RETURNING *`;
896
- const rows = await (await at()).unsafe(sql, [...asBindParams(table, cols, data, "update"), id]);
1308
+ const rows = await (await at()).unsafe(sql, [
1309
+ ...asBindParams(table, cols, data, "update"),
1310
+ id
1311
+ ]);
897
1312
  return rows[0] ? asTableRow(table, rows[0]) : null;
898
1313
  },
899
1314
  async delete(table, id) {
900
- await (await at()).unsafe(`DELETE FROM ${quoteTable(table)} WHERE id = $1`, [id]);
1315
+ assertNotAppendOnly("delete", table);
1316
+ await (await at()).unsafe(`DELETE FROM ${quoteTable(table)} WHERE id = $1`, [
1317
+ id
1318
+ ]);
901
1319
  },
902
1320
  /**
903
- * Update every row the filter matches, in ONE statement.
904
- *
905
- * The capability was already here and only reachable from inside a
906
- * transaction plan (`tx.tables.x.updateWhere`, since 11.0.0). Outside it the
907
- * only door was `update(id, …)`, so "mark every unapproved row in this
908
- * household" was an N+1 loop or hand-written SQL — and hand-written SQL is
909
- * where the typed surface and RLS both stop helping.
910
- *
911
- * The filter language is `findMany`'s, compiled by the same `compileWhere`:
912
- * one filter language, or the two spellings drift.
913
- *
914
- * AN EMPTY FILTER IS REFUSED. `UPDATE … WHERE true` is a whole-table write,
915
- * and the shape that produces it by accident — a filter object built from
916
- * request input that happened to come back empty — is exactly the shape that
917
- * should not silently succeed. Callers who mean every row say so with a
918
- * predicate that is true for every row.
919
- */
1321
+ * Update every row the filter matches, in ONE statement.
1322
+ *
1323
+ * The capability was already here and only reachable from inside a
1324
+ * transaction plan (`tx.tables.x.updateWhere`, since 11.0.0). Outside it the
1325
+ * only door was `update(id, …)`, so "mark every unapproved row in this
1326
+ * household" was an N+1 loop or hand-written SQL — and hand-written SQL is
1327
+ * where the typed surface and RLS both stop helping.
1328
+ *
1329
+ * The filter language is `findMany`'s, compiled by the same `compileWhere`:
1330
+ * one filter language, or the two spellings drift.
1331
+ *
1332
+ * AN EMPTY FILTER IS REFUSED. `UPDATE … WHERE true` is a whole-table write,
1333
+ * and the shape that produces it by accident — a filter object built from
1334
+ * request input that happened to come back empty — is exactly the shape that
1335
+ * should not silently succeed. Callers who mean every row say so with a
1336
+ * predicate that is true for every row.
1337
+ */
920
1338
  async updateMany(table, where, set) {
1339
+ assertNotAppendOnly("updateMany", table);
921
1340
  const cols = Object.keys(set);
922
1341
  if (cols.length === 0) {
923
- throw new Error(
924
- `updateMany(${table}): nothing to set. An update with no columns is not a no-op worth pretending happened \u2014 pass the columns to write.`
925
- );
1342
+ throw new Error(`updateMany(${table}): nothing to set. An update with no columns is not a no-op worth pretending happened \u2014 pass the columns to write.`);
926
1343
  }
927
1344
  const known = schemaColumns(table);
928
1345
  if (known !== null) {
@@ -933,27 +1350,43 @@ function createOps(tx) {
933
1350
  }
934
1351
  }
935
1352
  const params = [];
936
- const add = (v) => {
1353
+ const add = /* @__PURE__ */ __name((v) => {
937
1354
  params.push(v);
938
1355
  return `$${params.length}`;
939
- };
940
- const assignments = cols.map((c) => `${quoteIdent(c)} = ${add(asBindParams(table, [c], set, "updateMany")[0])}`).join(", ");
1356
+ }, "add");
1357
+ const assignments = cols.map((c) => {
1358
+ const expr = columnExprOf(set[c]);
1359
+ if (expr !== null && expr.fn === "now") {
1360
+ return `${quoteIdent(c)} = now()`;
1361
+ }
1362
+ if (expr !== null) {
1363
+ const operator = expr.fn === "inc" ? "+" : "-";
1364
+ const t = transformsOf(currentSchema, table).get(c);
1365
+ const amount = t?.toDb === void 0 || expr.by === null || expr.by === void 0 ? expr.by : t.toDb(expr.by);
1366
+ return `${quoteIdent(c)} = ${quoteIdent(c)} ${operator} ${add(amount)}`;
1367
+ }
1368
+ return `${quoteIdent(c)} = ${add(asBindParams(table, [
1369
+ c
1370
+ ], set, "updateMany")[0])}`;
1371
+ }).join(", ");
941
1372
  const whereSql = compileWhere(table, known, where, add, "updateMany");
942
1373
  assertHasPredicate(whereSql, "updateMany", table);
943
- const sql = `UPDATE ${quoteTable(table)} AS t SET ${assignments} WHERE true${whereSql} RETURNING *`;
1374
+ const pk = primaryKeyOf(table);
1375
+ const sql = isSinglePkEquality(where, pk) || pk === null ? `UPDATE ${quoteTable(table)} AS t SET ${assignments} WHERE true${whereSql} RETURNING *` : `WITH locked AS (SELECT t.${quoteIdent(pk)} FROM ${quoteTable(table)} t WHERE true${whereSql} ORDER BY t.${quoteIdent(pk)} FOR NO KEY UPDATE) UPDATE ${quoteTable(table)} AS t SET ${assignments} FROM locked WHERE t.${quoteIdent(pk)} = locked.${quoteIdent(pk)} RETURNING t.*`;
944
1376
  return asTableRows(table, await (await at()).unsafe(sql, params));
945
1377
  },
946
1378
  /**
947
- * Delete every row the filter matches, in ONE statement; resolves to how
948
- * many went. Same filter language, same empty-filter refusal as
949
- * {@link updateMany} — and here the accident is worse.
950
- */
1379
+ * Delete every row the filter matches, in ONE statement; resolves to how
1380
+ * many went. Same filter language, same empty-filter refusal as
1381
+ * {@link updateMany} — and here the accident is worse.
1382
+ */
951
1383
  async deleteMany(table, where) {
1384
+ assertNotAppendOnly("deleteMany", table);
952
1385
  const params = [];
953
- const add = (v) => {
1386
+ const add = /* @__PURE__ */ __name((v) => {
954
1387
  params.push(v);
955
1388
  return `$${params.length}`;
956
- };
1389
+ }, "add");
957
1390
  const known = schemaColumns(table);
958
1391
  const whereSql = compileWhere(table, known, where, add, "deleteMany");
959
1392
  assertHasPredicate(whereSql, "deleteMany", table);
@@ -962,20 +1395,20 @@ function createOps(tx) {
962
1395
  return rows.length;
963
1396
  },
964
1397
  /**
965
- * How many rows match — the half of pagination `limit`/`offset` cannot
966
- * supply. Without it a page count is either a guess or "fetch everything and
967
- * read .length", and the second one is the scale risk this surface exists to
968
- * remove.
969
- *
970
- * An empty filter is legitimate HERE: counting a whole table is a read, and
971
- * reads do not destroy anything.
972
- */
1398
+ * How many rows match — the half of pagination `limit`/`offset` cannot
1399
+ * supply. Without it a page count is either a guess or "fetch everything and
1400
+ * read .length", and the second one is the scale risk this surface exists to
1401
+ * remove.
1402
+ *
1403
+ * An empty filter is legitimate HERE: counting a whole table is a read, and
1404
+ * reads do not destroy anything.
1405
+ */
973
1406
  async count(table, where = {}) {
974
1407
  const params = [];
975
- const add = (v) => {
1408
+ const add = /* @__PURE__ */ __name((v) => {
976
1409
  params.push(v);
977
1410
  return `$${params.length}`;
978
- };
1411
+ }, "add");
979
1412
  const known = schemaColumns(table);
980
1413
  const whereSql = compileWhere(table, known, where, add, "count");
981
1414
  const sql = `SELECT count(*) AS n FROM ${quoteTable(table)} t WHERE true${whereSql}`;
@@ -983,33 +1416,33 @@ function createOps(tx) {
983
1416
  return Number(rows[0]?.n ?? 0);
984
1417
  },
985
1418
  async findById(table, id) {
986
- const rows = await (await at()).unsafe(
987
- `SELECT * FROM ${quoteTable(table)} WHERE id = $1`,
988
- [id]
989
- );
1419
+ const rows = await (await at()).unsafe(`SELECT * FROM ${quoteTable(table)} WHERE id = $1`, [
1420
+ id
1421
+ ]);
990
1422
  return rows[0] ? asTableRow(table, rows[0]) : null;
991
1423
  },
992
1424
  async findMany(table, query = {}, opts = {}) {
993
1425
  const params = [];
994
- const add = (v) => {
1426
+ const add = /* @__PURE__ */ __name((v) => {
995
1427
  params.push(v);
996
1428
  return `$${params.length}`;
997
- };
1429
+ }, "add");
998
1430
  const known = schemaColumns(table);
999
1431
  const whereSql = compileWhere(table, known, query, add, "findMany");
1000
1432
  const order = orderClause(table, known, opts.orderBy);
1001
1433
  const limit = limitClause(opts.limit);
1002
1434
  const offset = offsetClause(opts.offset, opts.limit);
1003
- const sql = `SELECT * FROM ${quoteTable(table)} t WHERE true${whereSql}${order}${limit}${offset}`;
1435
+ const cols = selectClause(table, known, opts.select);
1436
+ const sql = `SELECT ${cols} FROM ${quoteTable(table)} t WHERE true${whereSql}${order}${limit}${offset}`;
1004
1437
  return asTableRows(table, await (await at()).unsafe(sql, params));
1005
1438
  },
1006
1439
  /**
1007
- * Tek-SQL hibrit arama (FR-014): iki kol CTE + FULL OUTER JOIN + RRF
1008
- * (1/(50+rank), CLAIM-N4). Operatör şema-nitelikli (C-10, M-1); GUC
1009
- * hnsw.iterative_scan=relaxed_order aynı tx'te set_config ile (CLAIM-N3 —
1010
- * RLS/filtre altında LIMIT-altı dönüş açığını kapatır). Sorgu-anı embed
1011
- * T024'te gelir; o zamana dek vector kolu yalnız params.vector ile koşar.
1012
- */
1440
+ * Tek-SQL hibrit arama (FR-014): iki kol CTE + FULL OUTER JOIN + RRF
1441
+ * (1/(50+rank), CLAIM-N4). Operatör şema-nitelikli (C-10, M-1); GUC
1442
+ * hnsw.iterative_scan=relaxed_order aynı tx'te set_config ile (CLAIM-N3 —
1443
+ * RLS/filtre altında LIMIT-altı dönüş açığını kapatır). Sorgu-anı embed
1444
+ * T024'te gelir; o zamana dek vector kolu yalnız params.vector ile koşar.
1445
+ */
1013
1446
  async search(table, params = {}) {
1014
1447
  const cfg = searchConfigFor(table);
1015
1448
  if (!cfg) {
@@ -1017,18 +1450,12 @@ function createOps(tx) {
1017
1450
  }
1018
1451
  const excl = params.__excludeIds ?? [];
1019
1452
  if (params.facets !== void 0) {
1020
- for (const col of params.facets) {
1021
- if (!cfg.colSet.has(col)) {
1022
- throw new Error(`search(${table}): facets kolonu "${col}" tabloda yok (FR-027)`);
1023
- }
1024
- }
1453
+ throw new Error(`search(${table}): facets art\u0131k search'\xFCn parametresi de\u011Fil \u2014 saya\xE7lar dizi \xFCst\xFCnde ta\u015F\u0131n\u0131nca JSON.stringify onlar\u0131 d\xFC\u015F\xFCr\xFCyordu (FR-058). Ba\u011F\u0131ms\u0131z \xE7a\u011F\u0131r\u0131n: facets({ facets: [...], where })`);
1025
1454
  }
1026
1455
  if (params.validity !== void 0 && cfg.validity !== true) {
1027
- throw new Error(
1028
- `search(${table}): validity parametresi verildi ama tablo validity beyan\u0131 ta\u015F\u0131m\u0131yor (FR-029)`
1029
- );
1456
+ throw new Error(`search(${table}): validity parametresi verildi ama tablo validity beyan\u0131 ta\u015F\u0131m\u0131yor (FR-029)`);
1030
1457
  }
1031
- const validityFor = (adder) => cfg.validity === true ? validitySql(params.validity, adder) : "";
1458
+ const validityFor = /* @__PURE__ */ __name((adder) => cfg.validity === true ? validitySql(params.validity, adder) : "", "validityFor");
1032
1459
  const rawLimit = params.limit ?? 20;
1033
1460
  if (typeof rawLimit !== "number" || !Number.isFinite(rawLimit)) {
1034
1461
  throw new Error(`search(${table}): limit sonlu bir say\u0131 olmal\u0131, ${String(rawLimit)} verildi (FR-013)`);
@@ -1074,15 +1501,13 @@ function createOps(tx) {
1074
1501
  }
1075
1502
  }
1076
1503
  if (!wantTextC && qvC === null) {
1077
- throw new Error(
1078
- `search(${table}): ko\u015Fulabilir kol yok \u2014 metin i\xE7in 'query' (FTS beyan\u0131 gerekir), semantik i\xE7in 'vector' verin (FR-015)`
1079
- );
1504
+ throw new Error(`search(${table}): ko\u015Fulabilir kol yok \u2014 metin i\xE7in 'query' (FTS beyan\u0131 gerekir), semantik i\xE7in 'vector' verin (FR-015)`);
1080
1505
  }
1081
1506
  const bindC = [];
1082
- const addC = (v) => {
1507
+ const addC = /* @__PURE__ */ __name((v) => {
1083
1508
  bindC.push(v);
1084
1509
  return `$${bindC.length}`;
1085
- };
1510
+ }, "addC");
1086
1511
  const userWhereC = compileWhere(table, cfg.colSet, params.where ?? {}, addC);
1087
1512
  const whereC = userWhereC + excludeSql(cfg.pk, excl, addC) + validityFor(addC);
1088
1513
  const parentJoinC = ` JOIN ${quoteTable(table)} t ON t.${quoteIdent(cfg.pk)} = c.${pidQ} WHERE true${whereC} AND `;
@@ -1096,10 +1521,7 @@ function createOps(tx) {
1096
1521
  const op = METRIC_OPERATOR[ck.metric] ?? METRIC_OPERATOR.cosine;
1097
1522
  let exactOrderC = "";
1098
1523
  if (userWhereC !== "") {
1099
- const probeRows = await liveC.unsafe(
1100
- `SELECT count(*)::int AS n FROM (SELECT 1 FROM ${quoteTable(ck.table)} c${parentJoinC}c.embedding IS NOT NULL LIMIT ${SELECTIVITY_EXACT_THRESHOLD + 1}) s`,
1101
- bindC.slice()
1102
- );
1524
+ const probeRows = await liveC.unsafe(`SELECT count(*)::int AS n FROM (SELECT 1 FROM ${quoteTable(ck.table)} c${parentJoinC}c.embedding IS NOT NULL LIMIT ${SELECTIVITY_EXACT_THRESHOLD + 1}) s`, bindC.slice());
1103
1525
  const n = probeRows?.[0]?.n;
1104
1526
  if (typeof n === "number" && n <= SELECTIVITY_EXACT_THRESHOLD) {
1105
1527
  exactOrderC = " + 0.0";
@@ -1119,7 +1541,7 @@ function createOps(tx) {
1119
1541
  const msC = params.minScore === void 0 ? "" : addC(params.minScore);
1120
1542
  const colListC = cfg.cols.map((c) => `t.${quoteIdent(c)}`).join(", ");
1121
1543
  const scoreC = scoreMul === "" ? "g._score" : `(g._score)${scoreMul}`;
1122
- const assembleC = (kwIn, withKwn) => {
1544
+ const assembleC = /* @__PURE__ */ __name((kwIn, withKwn) => {
1123
1545
  const kwnCol = withKwn ? `, (SELECT count(*) FROM kw)::int AS _kwn` : "";
1124
1546
  let fusedSrc;
1125
1547
  if (semC !== "" && kwIn !== "") {
@@ -1131,7 +1553,7 @@ function createOps(tx) {
1131
1553
  }
1132
1554
  const inner = `${fusedSrc}, ranked AS (SELECT f.pid, f.chunk_seq, f.cscore, ROW_NUMBER() OVER (PARTITION BY f.pid ORDER BY f.cscore DESC, f.chunk_seq) AS rn FROM fused f), grouped AS (SELECT r.pid, MAX(r.cscore) AS _score, json_agg(json_build_object('content', c.content, 'score', r.cscore, 'chunkSeq', r.chunk_seq, 'charStart', c.char_start) ORDER BY r.cscore DESC, r.chunk_seq) FILTER (WHERE r.rn <= ${bpr}) AS blocks FROM ranked r JOIN ${quoteTable(ck.table)} c ON c.${pidQ} = r.pid AND c.chunk_seq = r.chunk_seq GROUP BY r.pid) SELECT ${colListC}, ${scoreC}::float8 AS _score, g.blocks AS blocks${kwnCol} FROM grouped g JOIN ${quoteTable(table)} t ON t.${quoteIdent(cfg.pk)} = g.pid`;
1133
1555
  return msC === "" ? `SELECT * FROM (${inner}) q ORDER BY q._score DESC, q.${quoteIdent(cfg.pk)} LIMIT ${limit}` : `SELECT * FROM (${inner}) q WHERE q._score >= ${msC} ORDER BY q._score DESC, q.${quoteIdent(cfg.pk)} LIMIT ${limit}`;
1134
- };
1556
+ }, "assembleC");
1135
1557
  let rowsC = await liveC.unsafe(assembleC(kwC, kwC !== ""), bindC);
1136
1558
  if (wantTextC && kwC !== "" && (rowsC.length === 0 || rowsC[0]._kwn === 0)) {
1137
1559
  const tsch = await trgmSchemaOf(liveC);
@@ -1141,12 +1563,10 @@ function createOps(tx) {
1141
1563
  rowsC = await liveC.unsafe(assembleC(trgmKwC, false), retryBindC);
1142
1564
  }
1143
1565
  for (const r of rowsC) delete r._kwn;
1144
- const outC = asTableRows(table, rowsC).map((r) => ({ ...r, blocks: r.blocks ?? [] }));
1145
- if (params.facets !== void 0 && params.facets.length > 0) {
1146
- Object.assign(outC, {
1147
- _facets: await fetchFacets(liveC, table, cfg.colSet, params.facets, params.where ?? {}, validityFor)
1148
- });
1149
- }
1566
+ const outC = asTableRows(table, rowsC).map((r) => ({
1567
+ ...r,
1568
+ blocks: r.blocks ?? []
1569
+ }));
1150
1570
  logNoResults(table, params.query, params.mode, outC.length);
1151
1571
  return outC;
1152
1572
  }
@@ -1165,15 +1585,13 @@ function createOps(tx) {
1165
1585
  }
1166
1586
  const wantVector = leg !== null && qv !== null;
1167
1587
  if (!wantText && !wantVector) {
1168
- throw new Error(
1169
- `search(${table}): ko\u015Fulabilir kol yok \u2014 metin i\xE7in 'query' (FTS beyan\u0131 gerekir), semantik i\xE7in 'vector' verin (FR-015)`
1170
- );
1588
+ throw new Error(`search(${table}): ko\u015Fulabilir kol yok \u2014 metin i\xE7in 'query' (FTS beyan\u0131 gerekir), semantik i\xE7in 'vector' verin (FR-015)`);
1171
1589
  }
1172
1590
  const bind = [];
1173
- const add = (v) => {
1591
+ const add = /* @__PURE__ */ __name((v) => {
1174
1592
  bind.push(v);
1175
1593
  return `$${bind.length}`;
1176
- };
1594
+ }, "add");
1177
1595
  const userWhere = compileWhere(table, cfg.colSet, params.where ?? {}, add);
1178
1596
  const whereSql = userWhere + excludeSql(cfg.pk, excl, add) + validityFor(add);
1179
1597
  const live = await at();
@@ -1185,10 +1603,7 @@ function createOps(tx) {
1185
1603
  const op = METRIC_OPERATOR[leg.metric] ?? METRIC_OPERATOR.cosine;
1186
1604
  let exactOrder = "";
1187
1605
  if (userWhere !== "") {
1188
- const probeRows = await live.unsafe(
1189
- `SELECT count(*)::int AS n FROM (SELECT 1 FROM ${quoteTable(table)} t WHERE t.${quoteIdent(leg.column)} IS NOT NULL${whereSql} LIMIT ${SELECTIVITY_EXACT_THRESHOLD + 1}) s`,
1190
- bind.slice()
1191
- );
1606
+ const probeRows = await live.unsafe(`SELECT count(*)::int AS n FROM (SELECT 1 FROM ${quoteTable(table)} t WHERE t.${quoteIdent(leg.column)} IS NOT NULL${whereSql} LIMIT ${SELECTIVITY_EXACT_THRESHOLD + 1}) s`, bind.slice());
1192
1607
  const n = probeRows?.[0]?.n;
1193
1608
  if (typeof n === "number" && n <= SELECTIVITY_EXACT_THRESHOLD) {
1194
1609
  exactOrder = " + 0.0";
@@ -1209,7 +1624,7 @@ function createOps(tx) {
1209
1624
  const colList = cfg.cols.map((c) => `t.${quoteIdent(c)}`).join(", ");
1210
1625
  const msP = params.minScore === void 0 ? "" : add(params.minScore);
1211
1626
  const hlCol = params.highlight === true && wantText ? `, ts_headline('simple', ${ftsExprOf(cfg.ftsCols)}, websearch_to_tsquery('simple', ${qpRef})) AS _highlight` : "";
1212
- const assemble = (kwIn, withKwn) => {
1627
+ const assemble = /* @__PURE__ */ __name((kwIn, withKwn) => {
1213
1628
  const kwnCol = withKwn ? `, (SELECT count(*) FROM kw)::int AS _kwn` : "";
1214
1629
  let inner;
1215
1630
  let orderScore;
@@ -1226,7 +1641,7 @@ function createOps(tx) {
1226
1641
  orderScore = "_score";
1227
1642
  }
1228
1643
  return msP === "" ? `${inner} ORDER BY ${orderScore} DESC, t.${quoteIdent(cfg.pk)} LIMIT ${limit}` : `SELECT * FROM (${inner}) q WHERE q._score >= ${msP} ORDER BY q._score DESC, q.${quoteIdent(cfg.pk)} LIMIT ${limit}`;
1229
- };
1644
+ }, "assemble");
1230
1645
  let rows = await live.unsafe(assemble(kwSql, kwSql !== ""), bind);
1231
1646
  if (wantText && kwSql !== "" && (rows.length === 0 || rows[0]._kwn === 0)) {
1232
1647
  const tsch = await trgmSchemaOf(live);
@@ -1237,28 +1652,26 @@ function createOps(tx) {
1237
1652
  rows = await live.unsafe(assemble(trgmKw, false), retryBind);
1238
1653
  }
1239
1654
  for (const r of rows) delete r._kwn;
1240
- const out = asTableRows(table, rows).map((r) => ({ ...r, blocks: [] }));
1241
- if (params.facets !== void 0 && params.facets.length > 0) {
1242
- Object.assign(out, {
1243
- _facets: await fetchFacets(live, table, cfg.colSet, params.facets, params.where ?? {}, validityFor)
1244
- });
1245
- }
1655
+ const out = asTableRows(table, rows).map((r) => ({
1656
+ ...r,
1657
+ blocks: []
1658
+ }));
1246
1659
  logNoResults(table, params.query, params.mode, out.length);
1247
1660
  return out;
1248
1661
  },
1249
1662
  /**
1250
- * similar (T018, FR-022): "bu satıra benzeyenler". Hedef vektör DB'den
1251
- * okunur — satır-modu satırın kendi kolonu, chunk-modu parent chunk'larının
1252
- * şema-nitelikli avg'ı — ve ana arama search'ün vector yoluyla koşar;
1253
- * kaynak satır sonuçtan düşer. İKİ tur BİLİNÇLİ (plan T018): target-CTE'li
1254
- * tek SQL'de "id yok" ile "0 komşu" ayrılamazdı; +1 küçük turla id-yokluğu
1255
- * adlandırılmış hataya çevrilir. Sonuç şekli search ile aynı (FR-015).
1256
- */
1663
+ * similar (T018, FR-022): "bu satıra benzeyenler". Hedef vektör DB'den
1664
+ * okunur — satır-modu satırın kendi kolonu, chunk-modu parent chunk'larının
1665
+ * şema-nitelikli avg'ı — ve ana arama search'ün vector yoluyla koşar;
1666
+ * kaynak satır sonuçtan düşer. İKİ tur BİLİNÇLİ (plan T018): target-CTE'li
1667
+ * tek SQL'de "id yok" ile "0 komşu" ayrılamazdı; +1 küçük turla id-yokluğu
1668
+ * adlandırılmış hataya çevrilir. Sonuç şekli search ile aynı (FR-015).
1669
+ */
1257
1670
  /** D-021 (FR-027 DX): sayaçlar BAĞIMSIZ op'la — search'ün dizi-üstü
1258
- * `_facets` özelliği JSON.stringify'da kaybolur (dizi özelliği), tenant
1259
- * yanıtına koyunca sessizce yok olurdu. Ayrı dönüş ciddi bir sözleşmedir;
1260
- * search'teki alan geriye-uyum için DURUR. where + validity default'u
1261
- * sayaçlara da uygulanır (sayaç, kullanıcının gördüğü kümeyi anlatır). */
1671
+ * `_facets` özelliği JSON.stringify'da kaybolur (dizi özelliği), tenant
1672
+ * yanıtına koyunca sessizce yok olurdu. Ayrı dönüş ciddi bir sözleşmedir;
1673
+ * search'teki alan geriye-uyum için DURUR. where + validity default'u
1674
+ * sayaçlara da uygulanır (sayaç, kullanıcının gördüğü kümeyi anlatır). */
1262
1675
  async facets(table, params) {
1263
1676
  const cfg = searchConfigFor(table);
1264
1677
  if (!cfg) {
@@ -1282,20 +1695,18 @@ function createOps(tx) {
1282
1695
  let v;
1283
1696
  if (cfg.chunk !== void 0) {
1284
1697
  const sch = await vectorSchemaWithGuc(live);
1285
- const rows = await live.unsafe(
1286
- `SELECT ${quoteIdent(sch)}.avg(c.embedding) AS v FROM ${quoteTable(cfg.chunk.table)} c WHERE c.${quoteIdent(`parent_${cfg.pk}`)} = $1 AND c.embedding IS NOT NULL`,
1287
- [id]
1288
- );
1698
+ const rows = await live.unsafe(`SELECT ${quoteIdent(sch)}.avg(c.embedding) AS v FROM ${quoteTable(cfg.chunk.table)} c WHERE c.${quoteIdent(`parent_${cfg.pk}`)} = $1 AND c.embedding IS NOT NULL`, [
1699
+ id
1700
+ ]);
1289
1701
  v = rows?.[0]?.v;
1290
1702
  } else {
1291
1703
  const leg = pickLeg(table, cfg.legs, opts.using);
1292
1704
  if (leg === null) {
1293
1705
  throw new Error(`similar(${table}): tabloda vekt\xF6r kolonu yok \u2014 benzerlik semantik vekt\xF6r ister (FR-022)`);
1294
1706
  }
1295
- const rows = await live.unsafe(
1296
- `SELECT t.${quoteIdent(leg.column)} AS v FROM ${quoteTable(table)} t WHERE t.${quoteIdent(cfg.pk)} = $1`,
1297
- [id]
1298
- );
1707
+ const rows = await live.unsafe(`SELECT t.${quoteIdent(leg.column)} AS v FROM ${quoteTable(table)} t WHERE t.${quoteIdent(cfg.pk)} = $1`, [
1708
+ id
1709
+ ]);
1299
1710
  v = rows?.[0]?.v;
1300
1711
  }
1301
1712
  if (typeof v !== "string") {
@@ -1312,18 +1723,20 @@ function createOps(tx) {
1312
1723
  blocksPerRow: opts.blocksPerRow,
1313
1724
  validity: opts.validity,
1314
1725
  boost: opts.boost,
1315
- __excludeIds: [id]
1726
+ __excludeIds: [
1727
+ id
1728
+ ]
1316
1729
  };
1317
1730
  return ops.search(table, p);
1318
1731
  },
1319
1732
  /**
1320
- * recommend (T018, FR-023): positive/negative id kümelerinden öneri.
1321
- * Hedef vektör DB-İÇİ CTE'lerle türer: pos = avg(embedding of positives),
1322
- * negative varsa hedef = pos.v + (pos.v - neg.v) — pgvector avg agregası
1323
- * ve +/- operatörleri ŞEMA-NİTELİKLİ (M-1: search_path'e güvenilmez).
1324
- * Vektör matematiği istemciye inmez; bulunamayan positive/negative
1325
- * adlandırılmış hatadır ve kaynak id'ler sonuçtan düşer.
1326
- */
1733
+ * recommend (T018, FR-023): positive/negative id kümelerinden öneri.
1734
+ * Hedef vektör DB-İÇİ CTE'lerle türer: pos = avg(embedding of positives),
1735
+ * negative varsa hedef = pos.v + (pos.v - neg.v) — pgvector avg agregası
1736
+ * ve +/- operatörleri ŞEMA-NİTELİKLİ (M-1: search_path'e güvenilmez).
1737
+ * Vektör matematiği istemciye inmez; bulunamayan positive/negative
1738
+ * adlandırılmış hatadır ve kaynak id'ler sonuçtan düşer.
1739
+ */
1327
1740
  async recommend(table, opts) {
1328
1741
  const positive = opts?.positive;
1329
1742
  if (!Array.isArray(positive) || positive.length === 0) {
@@ -1344,14 +1757,14 @@ function createOps(tx) {
1344
1757
  const live = await at();
1345
1758
  const sch = await vectorSchemaWithGuc(live);
1346
1759
  const bind = [];
1347
- const add = (x) => {
1760
+ const add = /* @__PURE__ */ __name((x) => {
1348
1761
  bind.push(x);
1349
1762
  return `$${bind.length}`;
1350
- };
1351
- const avgSel = (ids) => {
1763
+ }, "add");
1764
+ const avgSel = /* @__PURE__ */ __name((ids) => {
1352
1765
  const inList = ids.map((x) => add(x)).join(", ");
1353
1766
  return cfg.chunk !== void 0 ? `SELECT ${quoteIdent(sch)}.avg(c.embedding) AS v FROM ${quoteTable(cfg.chunk.table)} c WHERE c.${quoteIdent(`parent_${cfg.pk}`)} IN (${inList}) AND c.embedding IS NOT NULL` : `SELECT ${quoteIdent(sch)}.avg(t.${quoteIdent(leg.column)}) AS v FROM ${quoteTable(table)} t WHERE t.${quoteIdent(cfg.pk)} IN (${inList}) AND t.${quoteIdent(leg.column)} IS NOT NULL`;
1354
- };
1767
+ }, "avgSel");
1355
1768
  const targetSql = negative.length === 0 ? `WITH pos AS (${avgSel(positive)}) SELECT pos.v AS v, (pos.v IS NULL) AS pos_missing FROM pos` : `WITH pos AS (${avgSel(positive)}), neg AS (${avgSel(negative)}) SELECT (pos.v OPERATOR(${quoteIdent(sch)}.+) (pos.v OPERATOR(${quoteIdent(sch)}.-) neg.v)) AS v, (pos.v IS NULL) AS pos_missing, (neg.v IS NULL) AS neg_missing FROM pos, neg`;
1356
1769
  const rows = await live.unsafe(targetSql, bind);
1357
1770
  const r0 = rows?.[0];
@@ -1375,21 +1788,25 @@ function createOps(tx) {
1375
1788
  blocksPerRow: opts.blocksPerRow,
1376
1789
  validity: opts.validity,
1377
1790
  boost: opts.boost,
1378
- __excludeIds: [...positive, ...negative]
1791
+ __excludeIds: [
1792
+ ...positive,
1793
+ ...negative
1794
+ ]
1379
1795
  };
1380
1796
  return ops.search(table, p);
1381
1797
  },
1382
1798
  /**
1383
- * supersede (T021, FR-029, C-9): validity'li tabloda satırın YENİ
1384
- * versiyonunu TEK savepoint'te yazar — eski satır kapatılır
1385
- * (valid_to = now(), superseded_by = yeni pk), yeni satır eklenir, dönüş
1386
- * yeni satırdır. Yeni pk CLIENT'ta üretilir (row'da verilmemişse
1387
- * randomUUID — declared şemaların uuid().defaultRandom() standardı) ki
1388
- * kapatma UPDATE'i INSERT'ten ÖNCE koşabilsin: 0 satır = zaten superseded
1389
- * ya da yok → INSERT hiç denenmez; INSERT hatası ise savepoint'le
1390
- * kapatmayı da geri sarar — yarım supersede diye bir durum yoktur.
1391
- */
1799
+ * supersede (T021, FR-029, C-9): validity'li tabloda satırın YENİ
1800
+ * versiyonunu TEK savepoint'te yazar — eski satır kapatılır
1801
+ * (valid_to = now(), superseded_by = yeni pk), yeni satır eklenir, dönüş
1802
+ * yeni satırdır. Yeni pk CLIENT'ta üretilir (row'da verilmemişse
1803
+ * randomUUID — declared şemaların uuid().defaultRandom() standardı) ki
1804
+ * kapatma UPDATE'i INSERT'ten ÖNCE koşabilsin: 0 satır = zaten superseded
1805
+ * ya da yok → INSERT hiç denenmez; INSERT hatası ise savepoint'le
1806
+ * kapatmayı da geri sarar — yarım supersede diye bir durum yoktur.
1807
+ */
1392
1808
  async supersede(table, id, row) {
1809
+ assertNotAppendOnly("supersede", table);
1393
1810
  const cfg = searchConfigFor(table);
1394
1811
  if (cfg === null || cfg.validity !== true) {
1395
1812
  throw new Error(`supersede(${table}): tablo validity beyan\u0131 ta\u015F\u0131m\u0131yor (FR-029)`);
@@ -1397,24 +1814,22 @@ function createOps(tx) {
1397
1814
  const live = await at();
1398
1815
  return live.savepoint(async (sp) => {
1399
1816
  const newId = row[cfg.pk] ?? crypto.randomUUID();
1400
- const closed = await sp.unsafe(
1401
- `UPDATE ${quoteTable(table)} SET "valid_to" = now(), "superseded_by" = $1 WHERE ${quoteIdent(cfg.pk)} = $2 AND "valid_to" IS NULL RETURNING ${quoteIdent(cfg.pk)}`,
1402
- [newId, id]
1403
- );
1817
+ const closed = await sp.unsafe(`UPDATE ${quoteTable(table)} SET "valid_to" = now(), "superseded_by" = $1 WHERE ${quoteIdent(cfg.pk)} = $2 AND "valid_to" IS NULL RETURNING ${quoteIdent(cfg.pk)}`, [
1818
+ newId,
1819
+ id
1820
+ ]);
1404
1821
  if (closed.length === 0) {
1405
1822
  throw new Error(`supersede(${table}): id "${String(id)}" zaten superseded ya da yok (FR-029)`);
1406
1823
  }
1407
- const data = { ...row, [cfg.pk]: newId };
1824
+ const data = {
1825
+ ...row,
1826
+ [cfg.pk]: newId
1827
+ };
1408
1828
  const cols = Object.keys(data);
1409
1829
  const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
1410
- const rows = await sp.unsafe(
1411
- `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`,
1412
- asBindParams(table, cols, data, "supersede")
1413
- );
1830
+ const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`, asBindParams(table, cols, data, "supersede"));
1414
1831
  if (!rows[0]) {
1415
- throw new Error(
1416
- `supersede(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si)`
1417
- );
1832
+ throw new Error(`supersede(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si)`);
1418
1833
  }
1419
1834
  return asTableRow(table, rows[0]);
1420
1835
  });
@@ -1425,61 +1840,68 @@ function createOps(tx) {
1425
1840
  return live.savepoint(async (sp) => cb(withTables(createOps(sp), currentSchema)));
1426
1841
  },
1427
1842
  /**
1428
- * Run `fn` against a handle bound to a SAVEPOINT, so a failure inside it
1429
- * rolls back only what that handle wrote and the request can keep writing.
1430
- *
1431
- * WHY THE HANDLE IS AN ARGUMENT. The obvious shape — `attempt(async () => {
1432
- * ... Database.insert(...) ... })`, with no parameter — would have to point
1433
- * the ambient `Database` at the savepoint for the duration, and a request is
1434
- * concurrent with itself: `Promise.all([Database.insert(a),
1435
- * Database.attempt(...)])` would put `a` inside the savepoint and roll it
1436
- * back with it. Silent data loss, and the same interleaving this file already
1437
- * refuses for `asService()`. Passing the handle makes the boundary something
1438
- * you can see in the code that crosses it.
1439
- *
1440
- * Postgres, not us: the savepoint is released on success and rolled back on
1441
- * failure by the driver, so an aborted statement inside `fn` does not poison
1442
- * the surrounding transaction.
1443
- */
1843
+ * Run `fn` against a handle bound to a SAVEPOINT, so a failure inside it
1844
+ * rolls back only what that handle wrote and the request can keep writing.
1845
+ *
1846
+ * WHY THE HANDLE IS AN ARGUMENT. The obvious shape — `attempt(async () => {
1847
+ * ... Database.$insert(...) ... })`, with no parameter — would have to point
1848
+ * the ambient `Database` at the savepoint for the duration, and a request is
1849
+ * concurrent with itself: `Promise.all([Database.$insert(a),
1850
+ * Database.$attempt(...)])` would put `a` inside the savepoint and roll it
1851
+ * back with it. Silent data loss, and the same interleaving this file already
1852
+ * refuses for `asService()`. Passing the handle makes the boundary something
1853
+ * you can see in the code that crosses it.
1854
+ *
1855
+ * Postgres, not us: the savepoint is released on success and rolled back on
1856
+ * failure by the driver, so an aborted statement inside `fn` does not poison
1857
+ * the surrounding transaction.
1858
+ */
1444
1859
  async attempt(fn) {
1445
1860
  const live = await at();
1446
1861
  return live.savepoint(async (sp) => fn(withTables(createOps(sp), currentSchema)));
1447
1862
  },
1448
1863
  /**
1449
- * Execute a whole transaction plan — what `Database.transaction(fn)` builds.
1450
- *
1451
- * WHY IT RUNS HERE. The platform used to carry a complete implementation
1452
- * of this at `/internal-api/db/tx`, for tenant code that ran in an isolate
1453
- * with no connection of its own. Running the plan there means running it on
1454
- * a DIFFERENT connection: a transaction would not see the uncommitted
1455
- * writes of the request that started it, and the two would hold separate
1456
- * RLS bindings of the same identity. In this stack the tenant's code and
1457
- * the connection share a process, so the plan runs on the request's own
1458
- * transaction inside one SAVEPOINT — and that surface was removed on
1459
- * 2026-08-15, once this was the last thing that could have called it.
1460
- *
1461
- * Until 2026-08-15 it ran NOWHERE: `runTxPlan` called `transport.txPlan` and
1462
- * nothing here implemented it, so a live handler answered
1463
- * "transport.txPlan is not a function" while every test that covered
1464
- * transactions passed against a mock that did implement it.
1465
- */
1864
+ * Execute a whole transaction plan — what `Database.$transaction(fn)` builds.
1865
+ *
1866
+ * WHY IT RUNS HERE. The platform used to carry a complete implementation
1867
+ * of this at `/internal-api/db/tx`, for tenant code that ran in an isolate
1868
+ * with no connection of its own. Running the plan there means running it on
1869
+ * a DIFFERENT connection: a transaction would not see the uncommitted
1870
+ * writes of the request that started it, and the two would hold separate
1871
+ * RLS bindings of the same identity. In this stack the tenant's code and
1872
+ * the connection share a process, so the plan runs on the request's own
1873
+ * transaction inside one SAVEPOINT — and that surface was removed on
1874
+ * 2026-08-15, once this was the last thing that could have called it.
1875
+ *
1876
+ * Until 2026-08-15 it ran NOWHERE: `runTxPlan` called `transport.txPlan` and
1877
+ * nothing here implemented it, so a live handler answered
1878
+ * "transport.txPlan is not a function" while every test that covered
1879
+ * transactions passed against a mock that did implement it.
1880
+ */
1466
1881
  async txPlan(plan) {
1467
1882
  const live = await at();
1468
1883
  return live.savepoint(async (sp) => {
1469
1884
  const results = [];
1470
1885
  for (const op of plan.ops) {
1471
1886
  const rows = asTableRows(op.table, await runPlanOp(sp, op, results));
1472
- const result = { rows, rows_affected: rows.length };
1887
+ const result = {
1888
+ rows,
1889
+ rows_affected: rows.length
1890
+ };
1473
1891
  results.push(result);
1474
1892
  assertGuard(op, result);
1475
1893
  }
1476
- return { results };
1894
+ return {
1895
+ results
1896
+ };
1477
1897
  });
1478
1898
  }
1479
1899
  };
1480
1900
  return ops;
1481
1901
  }
1902
+ __name(createOps, "createOps");
1482
1903
  var currentSchema = {};
1904
+ var currentRelations = /* @__PURE__ */ new Map();
1483
1905
  function setSchema(schemas) {
1484
1906
  cachedVectorSchema = null;
1485
1907
  cachedTrgmSchema = null;
@@ -1492,22 +1914,52 @@ function setSchema(schemas) {
1492
1914
  tables[qualifiedTableKey(schemaName, table.name ?? key)] = table;
1493
1915
  }
1494
1916
  }
1495
- currentSchema = { tables };
1917
+ currentSchema = {
1918
+ tables
1919
+ };
1920
+ currentRelations = buildRelations(schemas.map((entry) => {
1921
+ const mod = entry;
1922
+ const def = (mod && "default" in mod ? mod.default : mod) ?? {};
1923
+ const tables2 = {};
1924
+ for (const [key, t] of Object.entries(def.tables ?? {})) {
1925
+ const table = t ?? {};
1926
+ const columns = {};
1927
+ for (const [cname, c] of Object.entries(table.columns ?? {})) {
1928
+ const cd = c ?? {};
1929
+ columns[cname] = "_def" in cd ? cd : {
1930
+ ...cd,
1931
+ _def: cd
1932
+ };
1933
+ }
1934
+ tables2[key] = {
1935
+ ...table,
1936
+ name: table.name ?? key,
1937
+ columns
1938
+ };
1939
+ }
1940
+ return {
1941
+ name: def.name ?? "public",
1942
+ tables: tables2
1943
+ };
1944
+ }));
1496
1945
  }
1946
+ __name(setSchema, "setSchema");
1497
1947
  function withTables(ops, schema = currentSchema) {
1498
- const bind = (key) => ({
1499
- insert: (data) => ops.insert(key, data),
1500
- update: (id, data) => ops.update(key, id, data),
1501
- delete: (id) => ops.delete(key, id),
1502
- findById: (id) => ops.findById(key, id),
1503
- findMany: (query, opts) => ops.findMany(key, query ?? {}, opts),
1504
- upsert: (data, opts) => ops.upsert(key, data, opts)
1505
- });
1948
+ const bind = /* @__PURE__ */ __name((key) => ({
1949
+ insert: /* @__PURE__ */ __name((data) => ops.insert(key, data), "insert"),
1950
+ update: /* @__PURE__ */ __name((id, data) => ops.update(key, id, data), "update"),
1951
+ delete: /* @__PURE__ */ __name((id) => ops.delete(key, id), "delete"),
1952
+ findById: /* @__PURE__ */ __name((id) => ops.findById(key, id), "findById"),
1953
+ findMany: /* @__PURE__ */ __name((query, opts) => ops.findMany(key, query ?? {}, opts), "findMany"),
1954
+ put: /* @__PURE__ */ __name((q) => ops.put(key, q.data, {
1955
+ onConflict: q.onConflict
1956
+ }), "put")
1957
+ }), "bind");
1506
1958
  const tables = {};
1507
1959
  for (const key of Object.keys(schema.tables ?? {})) {
1508
1960
  if (!key.includes(".")) tables[key] = bind(key);
1509
1961
  }
1510
- const schemaOf = (name) => {
1962
+ const schemaOf = /* @__PURE__ */ __name((name) => {
1511
1963
  const out = {};
1512
1964
  const prefix = `${name}.`;
1513
1965
  for (const key of Object.keys(schema.tables ?? {})) {
@@ -1517,25 +1969,31 @@ function withTables(ops, schema = currentSchema) {
1517
1969
  out[key.slice(prefix.length)] = bind(key);
1518
1970
  }
1519
1971
  }
1520
- return { tables: out };
1521
- };
1972
+ return {
1973
+ tables: out
1974
+ };
1975
+ }, "schemaOf");
1522
1976
  const base = /* @__PURE__ */ Object.create(null);
1523
- return Object.assign(base, ops, { tables, schema: schemaOf });
1977
+ return Object.assign(base, ops, {
1978
+ tables,
1979
+ schema: schemaOf
1980
+ });
1524
1981
  }
1982
+ __name(withTables, "withTables");
1525
1983
  var SERVICE_LOCK_TIMEOUT = "5s";
1526
1984
  function isAbortedTransaction(e) {
1527
1985
  const message = String(e?.message ?? "");
1528
1986
  return sqlstateOf(e) === "25P02" || /current transaction is aborted/i.test(message);
1529
1987
  }
1988
+ __name(isAbortedTransaction, "isAbortedTransaction");
1530
1989
  function assertHasPredicate(whereSql, op, table) {
1531
1990
  if (whereSql.trim().length > 0) return;
1532
- throw new Error(
1533
- `${op}(${table}): the filter compiled to no condition, so this would have written EVERY row. An empty filter \u2014 or an operator object with no operators in it, like { col: {} } \u2014 is refused. Name a condition.`
1534
- );
1991
+ throw new Error(`${op}(${table}): the filter compiled to no condition, so this would have written EVERY row. An empty filter \u2014 or an operator object with no operators in it, like { col: {} } \u2014 is refused. Name a condition.`);
1535
1992
  }
1993
+ __name(assertHasPredicate, "assertHasPredicate");
1536
1994
  function sqlstateOf(e) {
1537
1995
  const err = e;
1538
- const isState = (v) => typeof v === "string" && /^[0-9A-Z]{5}$/.test(v);
1996
+ const isState = /* @__PURE__ */ __name((v) => typeof v === "string" && /^[0-9A-Z]{5}$/.test(v), "isState");
1539
1997
  if (isState(err?.code)) return err.code;
1540
1998
  if (isState(err?.errno)) return err.errno;
1541
1999
  if (typeof err?.errno === "number") {
@@ -1544,46 +2002,48 @@ function sqlstateOf(e) {
1544
2002
  }
1545
2003
  return void 0;
1546
2004
  }
2005
+ __name(sqlstateOf, "sqlstateOf");
1547
2006
  function isForeignKeyViolation(e) {
1548
2007
  return sqlstateOf(e) === "23503";
1549
2008
  }
2009
+ __name(isForeignKeyViolation, "isForeignKeyViolation");
1550
2010
  function isUniqueViolation(e) {
1551
- return sqlstateOf(e) === "23505";
2011
+ return sqlstateOf(e) === "23505" || UniqueViolation.is(e);
1552
2012
  }
2013
+ __name(isUniqueViolation, "isUniqueViolation");
1553
2014
  function constraintOf(e) {
1554
2015
  const named = e?.constraint;
1555
2016
  if (typeof named === "string" && named.length > 0) return named;
1556
2017
  const message = String(e?.message ?? "");
1557
2018
  return /violates unique constraint "([^"]+)"/.exec(message)?.[1] ?? "";
1558
2019
  }
2020
+ __name(constraintOf, "constraintOf");
1559
2021
  function isLockTimeout(e) {
1560
2022
  const message = String(e?.message ?? "");
1561
2023
  return sqlstateOf(e) === "55P03" || /lock timeout/i.test(message);
1562
2024
  }
2025
+ __name(isLockTimeout, "isLockTimeout");
1563
2026
  function diagnosingDriver(sql, surface = "service") {
1564
- const original = (e) => String(e?.message ?? e);
1565
- const explain = (e) => {
2027
+ const original = /* @__PURE__ */ __name((e) => String(e?.message ?? e), "original");
2028
+ const explain = /* @__PURE__ */ __name((e) => {
1566
2029
  if (isAbortedTransaction(e)) {
1567
- return new Error(
1568
- `This request cannot write any more: an earlier write in it failed, and a request runs in ONE Postgres transaction, so every statement after the failure is refused. Nothing here is retryable in place. Wrap a write you expect to fail in \`Database.attempt(async (tx) => \u2026)\` \u2014 it takes a SAVEPOINT, so only that write rolls back \u2014 or use \`Database.tables.<t>.upsert(data, { onConflict: [...] })\` when the failure you were bracing for is a duplicate row. (${original(e)})`
1569
- );
2030
+ return new Error(`This request cannot write any more: an earlier write in it failed, and a request runs in ONE Postgres transaction, so every statement after the failure is refused. Nothing here is retryable in place. Wrap a write you expect to fail in \`Database.$attempt(async (tx) => \u2026)\` \u2014 it takes a SAVEPOINT, so only that write rolls back \u2014 or use \`Database.public.<t>.upsert(data, { onConflict: [...] })\` when the failure you were bracing for is a duplicate row. (${original(e)})`);
1570
2031
  }
1571
2032
  if (isUniqueViolation(e)) {
1572
2033
  return markEngineRaised(new UniqueViolation(constraintOf(e)));
1573
2034
  }
2035
+ const state = sqlstateOf(e);
2036
+ if (state === "40001") return markEngineRaised(new SerializationFailure(state));
2037
+ if (state === "40P01") return markEngineRaised(new DeadlockDetected(state));
1574
2038
  if (surface === "service" && isForeignKeyViolation(e)) {
1575
- return new Error(
1576
- `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)})`
1577
- );
2039
+ 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)})`);
1578
2040
  }
1579
2041
  if (isLockTimeout(e)) {
1580
- return new Error(
1581
- `Database.asService() waited too long for a row lock. It runs in its OWN transaction, so a row this request already wrote through Database.* is locked against it until the request commits \u2014 a wait that cannot end. Do that row's work on one surface or the other. (${original(e)})`
1582
- );
2042
+ return new Error(`Database.$asService() waited too long for a row lock. It runs in its OWN transaction, so a row this request already wrote through Database.* is locked against it until the request commits \u2014 a wait that cannot end. Do that row's work on one surface or the other. (${original(e)})`);
1583
2043
  }
1584
2044
  return e;
1585
- };
1586
- const wrapTx = (tx) => ({
2045
+ }, "explain");
2046
+ const wrapTx = /* @__PURE__ */ __name((tx) => ({
1587
2047
  async unsafe(text, params) {
1588
2048
  try {
1589
2049
  return await tx.unsafe(text, params);
@@ -1594,30 +2054,30 @@ function diagnosingDriver(sql, surface = "service") {
1594
2054
  savepoint(cb) {
1595
2055
  return tx.savepoint((sp) => cb(wrapTx(sp)));
1596
2056
  }
1597
- });
2057
+ }), "wrapTx");
1598
2058
  return {
1599
- unsafe: (text, params) => sql.unsafe(text, params),
1600
- begin: (cb) => sql.begin((tx) => cb(wrapTx(tx)))
2059
+ unsafe: /* @__PURE__ */ __name((text, params) => sql.unsafe(text, params), "unsafe"),
2060
+ begin: /* @__PURE__ */ __name((cb) => sql.begin((tx) => cb(wrapTx(tx))), "begin")
1601
2061
  };
1602
2062
  }
2063
+ __name(diagnosingDriver, "diagnosingDriver");
1603
2064
  function createRequestDatabase(sql, identity) {
1604
2065
  const tx = createLazyTransaction(diagnosingDriver(sql, "user"), identity.role, identity.claimsJson);
1605
2066
  let serviceTx = null;
1606
2067
  let serviceClient = null;
1607
- const asService = () => {
2068
+ const asService = /* @__PURE__ */ __name(() => {
1608
2069
  if (serviceClient === null) {
1609
- serviceTx = createLazyTransaction(
1610
- diagnosingDriver(sql, "service"),
1611
- identity.serviceRole,
1612
- identity.claimsJson,
1613
- { lockTimeout: SERVICE_LOCK_TIMEOUT }
1614
- );
2070
+ serviceTx = createLazyTransaction(diagnosingDriver(sql, "service"), identity.serviceRole, identity.claimsJson, {
2071
+ lockTimeout: SERVICE_LOCK_TIMEOUT
2072
+ });
1615
2073
  serviceClient = withTables(createOps(serviceTx));
1616
2074
  }
1617
2075
  return serviceClient;
1618
- };
2076
+ }, "asService");
1619
2077
  return {
1620
- client: Object.assign(withTables(createOps(tx)), { asService }),
2078
+ client: Object.assign(withTables(createOps(tx)), {
2079
+ asService
2080
+ }),
1621
2081
  async commit() {
1622
2082
  await tx.commit();
1623
2083
  await serviceTx?.commit();
@@ -1628,7 +2088,11 @@ function createRequestDatabase(sql, identity) {
1628
2088
  }
1629
2089
  };
1630
2090
  }
1631
- var Args = class {
2091
+ __name(createRequestDatabase, "createRequestDatabase");
2092
+ var Args = class Args2 {
2093
+ static {
2094
+ __name(this, "Args");
2095
+ }
1632
2096
  values = [];
1633
2097
  bind(value) {
1634
2098
  this.values.push(value);
@@ -1636,17 +2100,37 @@ var Args = class {
1636
2100
  }
1637
2101
  };
1638
2102
  function isRef(v) {
1639
- return typeof v === "object" && v !== null && "$ref" in v;
2103
+ return isPlanRef(v);
2104
+ }
2105
+ __name(isRef, "isRef");
2106
+ function refUnresolvedMessage(ref, results) {
2107
+ if (ref.op < 0 || ref.op >= results.length) {
2108
+ return `transaction plan: referans ${ref.op} numaral\u0131 i\u015Fleme bak\u0131yor ama planda ${results.length} i\u015Flem var (0\u2026${results.length - 1}). Referans yaln\u0131z KEND\u0130NDEN \xD6NCEK\u0130 bir i\u015Flemin sat\u0131r\u0131na bakabilir.`;
2109
+ }
2110
+ const row = results[ref.op]?.rows[0];
2111
+ if (!row) {
2112
+ return `transaction plan: ${ref.op} numaral\u0131 i\u015Flem H\u0130\xC7 SATIR d\xF6nd\xFCrmedi, o y\xFCzden "${ref.field}" okunam\u0131yor. Bir referans okunacak sat\u0131r\u0131n var oldu\u011Funu garanti etmez \u2014 \xF6nce beklentiyi bildirin (\`.expectOne(err)\`).`;
2113
+ }
2114
+ return `transaction plan: ${ref.op} numaral\u0131 i\u015Flemin sat\u0131r\u0131nda "${ref.field}" kolonu yok (d\xF6nen kolonlar: ${Object.keys(row).join(", ")}).`;
1640
2115
  }
2116
+ __name(refUnresolvedMessage, "refUnresolvedMessage");
2117
+ function refuseUnbrandedRef(v, where) {
2118
+ if (looksLikeUnbrandedPlanRef(v)) {
2119
+ throw new Error(`${where}: bir plan referans\u0131 TAKL\u0130D\u0130 ald\u0131 ({ $ref: \u2026 }) \u2014 bu nesne bu plan\u0131n builder'\u0131 taraf\u0131ndan \xFCretilmemi\u015F. Referans yaln\u0131z kendi kodunuzda, \xF6nceki bir i\u015Flemin sat\u0131r\u0131ndan okunarak kurulur (\xF6r. insert(...).expectOne(err).id); istek g\xF6vdesinden gelen bir filtre referans TA\u015EIYAMAZ.`);
2120
+ }
2121
+ }
2122
+ __name(refuseUnbrandedRef, "refuseUnbrandedRef");
1641
2123
  function isExpr(v) {
1642
2124
  return typeof v === "object" && v !== null && "$expr" in v;
1643
2125
  }
2126
+ __name(isExpr, "isExpr");
1644
2127
  function renderValue(value, column, args, results, vectorCols, transforms) {
2128
+ refuseUnbrandedRef(value, `transaction plan (${column})`);
1645
2129
  if (isRef(value)) {
1646
2130
  const source = results[value.$ref.op];
1647
2131
  const row = source?.rows[0];
1648
2132
  if (!row || !(value.$ref.field in row)) {
1649
- throw Object.assign(new Error(`op ${value.$ref.op} has no column "${value.$ref.field}" to reference`), {
2133
+ throw Object.assign(new Error(refUnresolvedMessage(value.$ref, results)), {
1650
2134
  error_code: "tx_ref_unresolved"
1651
2135
  });
1652
2136
  }
@@ -1660,16 +2144,50 @@ function renderValue(value, column, args, results, vectorCols, transforms) {
1660
2144
  }
1661
2145
  return bindMaybeVector(args, column, vectorCols, value, transforms);
1662
2146
  }
1663
- function renderWhere(where, args, results) {
1664
- const cols = Object.keys(where ?? {});
1665
- if (cols.length === 0) return "";
1666
- const terms = cols.map((c) => {
1667
- const v = where[c];
1668
- if (v === null) return `${quoteIdent(c)} IS NULL`;
1669
- return `${quoteIdent(c)} = ${renderValue(v, c, args, results)}`;
1670
- });
1671
- return ` WHERE ${terms.join(" AND ")}`;
2147
+ __name(renderValue, "renderValue");
2148
+ function resolveRefsDeep(value, results) {
2149
+ refuseUnbrandedRef(value, "transaction plan filtresi");
2150
+ if (isRef(value)) {
2151
+ const source = results[value.$ref.op];
2152
+ const row = source?.rows[0];
2153
+ if (!row || !(value.$ref.field in row)) {
2154
+ throw Object.assign(new Error(refUnresolvedMessage(value.$ref, results)), {
2155
+ error_code: "tx_ref_unresolved"
2156
+ });
2157
+ }
2158
+ return row[value.$ref.field];
2159
+ }
2160
+ if (Array.isArray(value)) return value.map((v) => resolveRefsDeep(v, results));
2161
+ if (value !== null && typeof value === "object" && !isColRef(value) && !isSqlFragment(value) && !isExpr(value) && !(value instanceof Date)) {
2162
+ const out = {};
2163
+ for (const [k, v] of Object.entries(value)) {
2164
+ out[k] = resolveRefsDeep(v, results);
2165
+ }
2166
+ return out;
2167
+ }
2168
+ return value;
2169
+ }
2170
+ __name(resolveRefsDeep, "resolveRefsDeep");
2171
+ function renderWhere(table, where, args, results) {
2172
+ return planWhere(table, where, args, results).sql;
2173
+ }
2174
+ __name(renderWhere, "renderWhere");
2175
+ function planWhere(table, where, args, results) {
2176
+ if (where === void 0 || Object.keys(where).length === 0) {
2177
+ return {
2178
+ sql: "",
2179
+ resolved: {}
2180
+ };
2181
+ }
2182
+ const resolved = resolveRefsDeep(where, results);
2183
+ const known = schemaColumns(table);
2184
+ const bare = compileWhereBare(table, known, resolved, (v) => args.bind(v), "transaction plan");
2185
+ return {
2186
+ sql: bare === "" ? "" : ` AND ${bare}`,
2187
+ resolved
2188
+ };
1672
2189
  }
2190
+ __name(planWhere, "planWhere");
1673
2191
  function bindMaybeVector(args, column, vectorCols, value, transforms) {
1674
2192
  if (column !== void 0 && vectorCols?.has(column) && Array.isArray(value)) {
1675
2193
  return args.bind(toVectorLiteral(value));
@@ -1680,11 +2198,12 @@ function bindMaybeVector(args, column, vectorCols, value, transforms) {
1680
2198
  }
1681
2199
  return args.bind(value);
1682
2200
  }
2201
+ __name(bindMaybeVector, "bindMaybeVector");
1683
2202
  async function runPlanOp(sp, op, results) {
1684
2203
  const opVectorCols = vectorColumnsOf(currentSchema, op.table);
1685
2204
  const opTransforms = transformsOf(currentSchema, op.table);
1686
2205
  const args = new Args();
1687
- const table = quoteIdent(op.table);
2206
+ const table = quoteTable(op.table);
1688
2207
  let sql;
1689
2208
  switch (op.op) {
1690
2209
  case "insert": {
@@ -1694,6 +2213,7 @@ async function runPlanOp(sp, op, results) {
1694
2213
  break;
1695
2214
  }
1696
2215
  case "upsert": {
2216
+ assertNotAppendOnly("put", op.table);
1697
2217
  const cols = Object.keys(op.values ?? {});
1698
2218
  const conflict = op.onConflict ?? [];
1699
2219
  if (cols.length === 0 || conflict.length === 0) {
@@ -1701,9 +2221,7 @@ async function runPlanOp(sp, op, results) {
1701
2221
  error_code: "tx_bad_upsert"
1702
2222
  });
1703
2223
  }
1704
- const rendered = cols.map(
1705
- (c) => renderValue(op.values[c], c, args, results, opVectorCols, opTransforms)
1706
- );
2224
+ const rendered = cols.map((c) => renderValue(op.values[c], c, args, results, opVectorCols, opTransforms));
1707
2225
  sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) ${onConflictTail(cols, conflict, "update")} RETURNING *`;
1708
2226
  break;
1709
2227
  }
@@ -1711,31 +2229,44 @@ async function runPlanOp(sp, op, results) {
1711
2229
  const rows = op.rows ?? [];
1712
2230
  if (rows.length === 0 || !rows[0]) return [];
1713
2231
  const cols = Object.keys(rows[0]);
1714
- const tuples = rows.map(
1715
- (r) => `(${cols.map((c) => renderValue(r[c], c, args, results, opVectorCols, opTransforms)).join(", ")})`
1716
- );
2232
+ const tuples = rows.map((r) => `(${cols.map((c) => renderValue(r[c], c, args, results, opVectorCols, opTransforms)).join(", ")})`);
1717
2233
  const conflictCols = op.onConflict ?? [];
1718
2234
  const tail = op.action !== void 0 && conflictCols.length > 0 ? ` ${onConflictTail(cols, conflictCols, op.action)}` : "";
1719
2235
  sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail} RETURNING *`;
1720
2236
  break;
1721
2237
  }
1722
2238
  case "update": {
2239
+ assertNotAppendOnly("updateWhere", op.table);
1723
2240
  const cols = Object.keys(op.set ?? {});
1724
2241
  if (cols.length === 0) throw new Error(`update ${op.table}: nothing to set`);
1725
- const assignments = cols.map(
1726
- (c) => `${quoteIdent(c)} = ${renderValue(op.set[c], c, args, results, opVectorCols, opTransforms)}`
1727
- );
1728
- sql = `UPDATE ${table} SET ${assignments.join(", ")}${renderWhere(op.where, args, results)} RETURNING *`;
2242
+ const knownSet = schemaColumns(op.table);
2243
+ for (const c of cols) {
2244
+ if (knownSet !== null && !knownSet.has(c)) {
2245
+ throw new Error(`transaction plan (${op.table}): set kolonu "${c}" tabloda yok`);
2246
+ }
2247
+ }
2248
+ const assignments = cols.map((c) => `${quoteIdent(c)} = ${renderValue(op.set[c], c, args, results, opVectorCols, opTransforms)}`);
2249
+ const planned = planWhere(op.table, op.where, args, results);
2250
+ const planPk = primaryKeyOf(op.table);
2251
+ 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.*`;
1729
2252
  break;
1730
2253
  }
1731
2254
  case "delete": {
1732
- sql = `DELETE FROM ${table}${renderWhere(op.where, args, results)} RETURNING *`;
2255
+ assertNotAppendOnly("deleteWhere", op.table);
2256
+ sql = `DELETE FROM ${table} AS t WHERE true${renderWhere(op.table, op.where, args, results)} RETURNING t.*`;
1733
2257
  break;
1734
2258
  }
1735
2259
  case "select": {
1736
- const limit = op.limit !== void 0 ? ` LIMIT ${Number(op.limit)}` : "";
2260
+ let limit = "";
2261
+ if (op.limit !== void 0) {
2262
+ const n = Number(op.limit);
2263
+ if (!Number.isInteger(n) || n < 0) {
2264
+ throw new Error(`transaction plan (${op.table}): limit bir tam say\u0131 olmal\u0131, "${String(op.limit)}" al\u0131nd\u0131`);
2265
+ }
2266
+ limit = ` LIMIT ${n}`;
2267
+ }
1737
2268
  const lock = op.lock === "update" ? " FOR UPDATE" : "";
1738
- sql = `SELECT * FROM ${table}${renderWhere(op.where, args, results)}${limit}${lock}`;
2269
+ sql = `SELECT t.* FROM ${table} t WHERE true${renderWhere(op.table, op.where, args, results)}${limit}${lock}`;
1739
2270
  break;
1740
2271
  }
1741
2272
  default:
@@ -1743,6 +2274,7 @@ async function runPlanOp(sp, op, results) {
1743
2274
  }
1744
2275
  return await sp.unsafe(sql, args.values);
1745
2276
  }
2277
+ __name(runPlanOp, "runPlanOp");
1746
2278
  function assertGuard(op, result) {
1747
2279
  const guard = op.guard;
1748
2280
  if (!guard) return;
@@ -1754,6 +2286,7 @@ function assertGuard(op, result) {
1754
2286
  slot: guard.slot
1755
2287
  });
1756
2288
  }
2289
+ __name(assertGuard, "assertGuard");
1757
2290
 
1758
2291
  // src/engine/router.ts
1759
2292
  var ROOM_META = /* @__PURE__ */ Symbol.for("palbase.backend.room");
@@ -1761,7 +2294,8 @@ var CONTROLLER_META = /* @__PURE__ */ Symbol.for("palbase.backend.controllerMeta
1761
2294
  function toSegments(path) {
1762
2295
  return path.split("/").filter(Boolean).map((s) => s.startsWith("{") && s.endsWith("}") ? `:${s.slice(1, -1)}` : s);
1763
2296
  }
1764
- function buildRouteTable(controllers) {
2297
+ __name(toSegments, "toSegments");
2298
+ function buildRouteTable(controllers, container) {
1765
2299
  const table = [];
1766
2300
  for (const Ctrl of controllers) {
1767
2301
  if (Ctrl[ROOM_META] !== void 0) continue;
@@ -1771,12 +2305,9 @@ function buildRouteTable(controllers) {
1771
2305
  const routes = getRoutes(Ctrl);
1772
2306
  if (routes.length === 0) {
1773
2307
  const name = Ctrl.name ?? "<anonymous>";
1774
- throw new Error(
1775
- `controller ${name} collected zero routes. Either it declares no @Get/@Post/\u2026 , or its decorator metadata was erased at build time \u2014 check that the bundle was compiled with experimentalDecorators enabled.`
1776
- );
2308
+ throw new Error(`controller ${name} collected zero routes. Either it declares no @Get/@Post/\u2026 , or its decorator metadata was erased at build time \u2014 check that the bundle was compiled with experimentalDecorators enabled.`);
1777
2309
  }
1778
- assertZeroArgConstructor(Ctrl, "controller");
1779
- const instance = new ctor();
2310
+ const instance = container.get(Ctrl);
1780
2311
  for (const r of routes) {
1781
2312
  const full = `${basePath}${r.subpath ?? ""}` || "/";
1782
2313
  table.push({
@@ -1794,6 +2325,7 @@ function buildRouteTable(controllers) {
1794
2325
  }
1795
2326
  return table;
1796
2327
  }
2328
+ __name(buildRouteTable, "buildRouteTable");
1797
2329
  function matchRoute(table, method, pathname) {
1798
2330
  const parts = pathname.split("/").filter(Boolean);
1799
2331
  for (const entry of table) {
@@ -1814,10 +2346,14 @@ function matchRoute(table, method, pathname) {
1814
2346
  break;
1815
2347
  }
1816
2348
  }
1817
- if (ok) return { entry, params };
2349
+ if (ok) return {
2350
+ entry,
2351
+ params
2352
+ };
1818
2353
  }
1819
2354
  return null;
1820
2355
  }
2356
+ __name(matchRoute, "matchRoute");
1821
2357
 
1822
2358
  // src/engine/attest.ts
1823
2359
  var HEADER_DEVICE_ID = "x-palbase-device-id";
@@ -1836,10 +2372,12 @@ async function bodyDigest(body) {
1836
2372
  const buf = await crypto.subtle.digest("SHA-256", bytes);
1837
2373
  return btoa(String.fromCharCode(...new Uint8Array(buf)));
1838
2374
  }
2375
+ __name(bodyDigest, "bodyDigest");
1839
2376
  function readCapable(req) {
1840
2377
  const v = req.headers.get(HEADER_CAPABLE);
1841
2378
  return v === "yes" || v === "no" ? v : "unknown";
1842
2379
  }
2380
+ __name(readCapable, "readCapable");
1843
2381
  function createAttestGate(deps) {
1844
2382
  const doFetch = deps.fetchImpl ?? fetch;
1845
2383
  const ttl = deps.policyTtlMs ?? DEFAULT_POLICY_TTL_MS;
@@ -1850,7 +2388,9 @@ function createAttestGate(deps) {
1850
2388
  if (Date.now() - cachedAt < ttl) return cachedMode;
1851
2389
  try {
1852
2390
  const res = await doFetch(`${deps.moduleBaseUrl}/admin/attest/devices/attest-policy`, {
1853
- headers: { apikey: deps.serviceRoleKey },
2391
+ headers: {
2392
+ apikey: deps.serviceRoleKey
2393
+ },
1854
2394
  signal: AbortSignal.timeout(policyTimeoutMs)
1855
2395
  });
1856
2396
  if (!res.ok) throw new Error(`attest-policy ${res.status}`);
@@ -1861,10 +2401,17 @@ function createAttestGate(deps) {
1861
2401
  }
1862
2402
  return cachedMode;
1863
2403
  }
1864
- return async function gate(req, body, _ctx) {
2404
+ __name(mode, "mode");
2405
+ return /* @__PURE__ */ __name(async function gate(req, body, _ctx) {
1865
2406
  const m = await mode();
1866
2407
  if (m === "off") {
1867
- return { outcome: "pass", reason: "", device: null, observation: null, nextChallenge: null };
2408
+ return {
2409
+ outcome: "pass",
2410
+ reason: "",
2411
+ device: null,
2412
+ observation: null,
2413
+ nextChallenge: null
2414
+ };
1868
2415
  }
1869
2416
  const deviceId = req.headers.get(HEADER_DEVICE_ID) ?? "";
1870
2417
  const payload = req.headers.get(HEADER_PAYLOAD) ?? "";
@@ -1877,14 +2424,23 @@ function createAttestGate(deps) {
1877
2424
  };
1878
2425
  if (m === "monitor") {
1879
2426
  observation.attested = hasEvidence;
1880
- return { outcome: "pass", reason: "", device: null, observation, nextChallenge: null };
2427
+ return {
2428
+ outcome: "pass",
2429
+ reason: "",
2430
+ device: null,
2431
+ observation,
2432
+ nextChallenge: null
2433
+ };
1881
2434
  }
1882
2435
  const url = new URL(req.url);
1883
2436
  let verdict;
1884
2437
  try {
1885
2438
  const res = await doFetch(`${deps.moduleBaseUrl}/admin/attest/devices/assert`, {
1886
2439
  method: "POST",
1887
- headers: { apikey: deps.serviceRoleKey, "content-type": "application/json" },
2440
+ headers: {
2441
+ apikey: deps.serviceRoleKey,
2442
+ "content-type": "application/json"
2443
+ },
1888
2444
  // A HUNG palauth must not become a hung tenant. Without this, a
1889
2445
  // process that accepts the connection and never answers holds every
1890
2446
  // request open until the client gives up — the gate would take the
@@ -1945,8 +2501,9 @@ function createAttestGate(deps) {
1945
2501
  observation,
1946
2502
  nextChallenge: verdict.next_challenge || null
1947
2503
  };
1948
- };
2504
+ }, "gate");
1949
2505
  }
2506
+ __name(createAttestGate, "createAttestGate");
1950
2507
  function createObservationSink(deps) {
1951
2508
  const doFetch = deps.fetchImpl ?? fetch;
1952
2509
  const maxBatch = deps.maxBatch ?? 1e3;
@@ -1958,7 +2515,12 @@ function createObservationSink(deps) {
1958
2515
  }, intervalMs);
1959
2516
  timer.unref?.();
1960
2517
  function bucketsOf(batch) {
1961
- const counts = { capable: 0, incapable: 0, unknown: 0, no_sdk: 0 };
2518
+ const counts = {
2519
+ capable: 0,
2520
+ incapable: 0,
2521
+ unknown: 0,
2522
+ no_sdk: 0
2523
+ };
1962
2524
  for (const o of batch) {
1963
2525
  if (o.attested || o.capable === "yes") counts.capable += 1;
1964
2526
  else if (o.capable === "no") counts.incapable += 1;
@@ -1967,6 +2529,7 @@ function createObservationSink(deps) {
1967
2529
  }
1968
2530
  return counts;
1969
2531
  }
2532
+ __name(bucketsOf, "bucketsOf");
1970
2533
  async function flush() {
1971
2534
  if (buffer.length === 0) return;
1972
2535
  const batch = buffer;
@@ -1975,7 +2538,10 @@ function createObservationSink(deps) {
1975
2538
  try {
1976
2539
  await doFetch(`${deps.moduleBaseUrl}/admin/attest/devices/observations`, {
1977
2540
  method: "POST",
1978
- headers: { apikey: deps.serviceRoleKey, "content-type": "application/json" },
2541
+ headers: {
2542
+ apikey: deps.serviceRoleKey,
2543
+ "content-type": "application/json"
2544
+ },
1979
2545
  body: JSON.stringify(bucketsOf(batch))
1980
2546
  });
1981
2547
  } catch {
@@ -1983,6 +2549,7 @@ function createObservationSink(deps) {
1983
2549
  })();
1984
2550
  await inFlight;
1985
2551
  }
2552
+ __name(flush, "flush");
1986
2553
  return {
1987
2554
  record(o) {
1988
2555
  buffer.push(o);
@@ -1994,6 +2561,7 @@ function createObservationSink(deps) {
1994
2561
  }
1995
2562
  };
1996
2563
  }
2564
+ __name(createObservationSink, "createObservationSink");
1997
2565
 
1998
2566
  // src/engine/upload.ts
1999
2567
  var AUTHORIZE_PATH = "/__palbase/upload/authorize";
@@ -2001,10 +2569,12 @@ var SIGNATURE_HEADER = "x-palbase-upload-signature";
2001
2569
  function renderPath(template, tokens) {
2002
2570
  return template.replaceAll("{userId}", sanitizeSegment(tokens.userId ?? "anonymous")).replaceAll("{uploadId}", sanitizeSegment(tokens.uploadId)).replaceAll("{filename}", sanitizeSegment(tokens.filename ?? "file"));
2003
2571
  }
2572
+ __name(renderPath, "renderPath");
2004
2573
  function sanitizeSegment(raw) {
2005
2574
  const cleaned = raw.replace(/[\x00-\x1F\x7F]/g, "").replace(/[/\\]/g, "-").replace(/\.{2,}/g, ".").replace(/^\.+/, "").trim();
2006
2575
  return cleaned === "" ? "file" : cleaned.slice(0, 200);
2007
2576
  }
2577
+ __name(sanitizeSegment, "sanitizeSegment");
2008
2578
  function grantFor(entry, ctx, bucketLimits) {
2009
2579
  const cfg = entry?.meta?.options?.uploadConfig;
2010
2580
  if (!cfg) return null;
@@ -2020,12 +2590,16 @@ function grantFor(entry, ctx, bucketLimits) {
2020
2590
  ownerUid: ctx.userId ?? null
2021
2591
  };
2022
2592
  }
2593
+ __name(grantFor, "grantFor");
2023
2594
  var CompletionLedger = class {
2024
- constructor(capacity = 1024) {
2025
- this.capacity = capacity;
2595
+ static {
2596
+ __name(this, "CompletionLedger");
2026
2597
  }
2027
2598
  capacity;
2028
2599
  seen = /* @__PURE__ */ new Map();
2600
+ constructor(capacity = 1024) {
2601
+ this.capacity = capacity;
2602
+ }
2029
2603
  recall(uploadId) {
2030
2604
  return this.seen.get(uploadId);
2031
2605
  }
@@ -2050,6 +2624,7 @@ function verifySignature(presented, expected) {
2050
2624
  }
2051
2625
  return diff === 0;
2052
2626
  }
2627
+ __name(verifySignature, "verifySignature");
2053
2628
 
2054
2629
  // src/engine/sse.ts
2055
2630
  var SSE_CONTENT_TYPE = "text/event-stream";
@@ -2058,12 +2633,16 @@ function encodeFrame(value) {
2058
2633
 
2059
2634
  `;
2060
2635
  }
2636
+ __name(encodeFrame, "encodeFrame");
2061
2637
  function encodeErrorFrame(requestId) {
2062
2638
  return `event: error
2063
- data: ${JSON.stringify({ requestId })}
2639
+ data: ${JSON.stringify({
2640
+ requestId
2641
+ })}
2064
2642
 
2065
2643
  `;
2066
2644
  }
2645
+ __name(encodeErrorFrame, "encodeErrorFrame");
2067
2646
  function makeSseWriter(opts) {
2068
2647
  let begun = false;
2069
2648
  let chain = Promise.resolve();
@@ -2085,6 +2664,7 @@ function makeSseWriter(opts) {
2085
2664
  }
2086
2665
  };
2087
2666
  }
2667
+ __name(makeSseWriter, "makeSseWriter");
2088
2668
 
2089
2669
  // src/engine/fence.ts
2090
2670
  var SECRET_ENV = [
@@ -2105,8 +2685,12 @@ function scrubSecrets(env) {
2105
2685
  removed.push(name);
2106
2686
  }
2107
2687
  for (const name of Object.keys(env)) if (name.startsWith("PALBASE_VAR_")) kept.push(name);
2108
- return { removed, kept };
2688
+ return {
2689
+ removed,
2690
+ kept
2691
+ };
2109
2692
  }
2693
+ __name(scrubSecrets, "scrubSecrets");
2110
2694
  function hostAllowed(host, allow) {
2111
2695
  const h = host.toLowerCase();
2112
2696
  for (const raw of allow) {
@@ -2119,40 +2703,49 @@ function hostAllowed(host, allow) {
2119
2703
  }
2120
2704
  return false;
2121
2705
  }
2706
+ __name(hostAllowed, "hostAllowed");
2122
2707
  function installEgressFence(policy) {
2123
2708
  const original = globalThis.fetch.bind(globalThis);
2124
2709
  const declared = policy.allow.length > 0;
2125
2710
  const platform = (policy.alwaysAllow ?? []).map((h) => h.toLowerCase()).filter(Boolean);
2126
2711
  if (!declared && policy.whenUndeclared === "allow") return original;
2127
- const fenced = async (input, init) => {
2712
+ const fenced = /* @__PURE__ */ __name(async (input, init) => {
2128
2713
  const url = typeof input === "string" ? new URL(input) : input instanceof URL ? input : new URL(input.url);
2129
2714
  if (platform.includes(url.hostname.toLowerCase())) return original(input, init);
2130
2715
  if (!declared || !hostAllowed(url.hostname, policy.allow)) {
2131
- throw new Error(
2132
- `egress denied: ${url.hostname} is not in this backend's declared allowlist. Add it to config/egress.ts and deploy.`
2133
- );
2716
+ throw new Error(`egress denied: ${url.hostname} is not in this backend's declared allowlist. Add it to config/egress.ts and deploy.`);
2134
2717
  }
2135
2718
  if (policy.timeoutMs > 0) {
2136
2719
  const controller = new AbortController();
2137
2720
  const timer = setTimeout(() => controller.abort(), policy.timeoutMs);
2138
2721
  try {
2139
- return await original(input, { ...init, signal: init?.signal ?? controller.signal });
2722
+ return await original(input, {
2723
+ ...init,
2724
+ signal: init?.signal ?? controller.signal
2725
+ });
2140
2726
  } finally {
2141
2727
  clearTimeout(timer);
2142
2728
  }
2143
2729
  }
2144
2730
  return original(input, init);
2145
- };
2731
+ }, "fenced");
2146
2732
  globalThis.fetch = fenced;
2147
2733
  return original;
2148
2734
  }
2735
+ __name(installEgressFence, "installEgressFence");
2149
2736
 
2150
2737
  // src/engine/index.ts
2151
2738
  var MAX_BODY_BYTES = 10 * 1024 * 1024;
2152
- var JSON_HEADERS = { "content-type": "application/json" };
2739
+ var JSON_HEADERS = {
2740
+ "content-type": "application/json"
2741
+ };
2153
2742
  function fieldErrors(err) {
2154
- return err.issues.map((i) => ({ field: i.path.join("."), message: i.message }));
2743
+ return err.issues.map((i) => ({
2744
+ field: i.path.join("."),
2745
+ message: i.message
2746
+ }));
2155
2747
  }
2748
+ __name(fieldErrors, "fieldErrors");
2156
2749
  function headersFor(raw, schema) {
2157
2750
  const shape = schema.shape;
2158
2751
  if (typeof shape !== "object" || shape === null) return raw;
@@ -2162,53 +2755,84 @@ function headersFor(raw, schema) {
2162
2755
  if (lower === declared) continue;
2163
2756
  const value = raw[lower];
2164
2757
  if (value === void 0) continue;
2165
- aliased ??= { ...raw };
2758
+ aliased ??= {
2759
+ ...raw
2760
+ };
2166
2761
  aliased[declared] = value;
2167
2762
  }
2168
2763
  return aliased ?? raw;
2169
2764
  }
2765
+ __name(headersFor, "headersFor");
2170
2766
  function envelope(error, description, status, requestId, extra) {
2171
- return new Response(
2172
- JSON.stringify({ error, error_description: description, status, request_id: requestId, ...extra }),
2173
- { status, headers: JSON_HEADERS }
2174
- );
2767
+ return new Response(JSON.stringify({
2768
+ error,
2769
+ error_description: description,
2770
+ status,
2771
+ request_id: requestId,
2772
+ ...extra
2773
+ }), {
2774
+ status,
2775
+ headers: JSON_HEADERS
2776
+ });
2175
2777
  }
2778
+ __name(envelope, "envelope");
2176
2779
  function unavailable(name) {
2177
- throw new Error(
2178
- `${name} is unavailable. Either this backend was started without module clients (set MODULE_BASE_URL and the API keys so the engine can reach the module surface), or ${name} is not one of the modules this backend provides.`
2179
- );
2780
+ throw new Error(`${name} is unavailable. Either this backend was started without module clients (set MODULE_BASE_URL and the API keys so the engine can reach the module surface), or ${name} is not one of the modules this backend provides.`);
2180
2781
  }
2782
+ __name(unavailable, "unavailable");
2181
2783
  function stubModule(name) {
2182
- return new Proxy(
2183
- {},
2184
- {
2185
- get: () => unavailable(name),
2186
- apply: () => unavailable(name)
2187
- }
2188
- );
2784
+ return new Proxy({}, {
2785
+ get: /* @__PURE__ */ __name(() => unavailable(name), "get"),
2786
+ apply: /* @__PURE__ */ __name(() => unavailable(name), "apply")
2787
+ });
2189
2788
  }
2789
+ __name(stubModule, "stubModule");
2190
2790
  async function defaultSqlDriver(config) {
2191
2791
  const g = globalThis;
2192
2792
  if (!g.Bun?.SQL) {
2193
- throw new BootRefused(
2194
- [],
2195
- "boot refused: no SQL driver. Running outside Bun means the driver must be supplied \u2014 pass `sql` to createApp()."
2196
- );
2793
+ throw new BootRefused([], "boot refused: no SQL driver. Running outside Bun means the driver must be supplied \u2014 pass `sql` to createApp().");
2197
2794
  }
2198
- return new g.Bun.SQL({ url: config.databaseUrl, max: config.poolMax });
2795
+ return new g.Bun.SQL({
2796
+ url: config.databaseUrl,
2797
+ max: config.poolMax
2798
+ });
2199
2799
  }
2200
- var AUTHOR_ANSWERED = /* @__PURE__ */ new Set([400, 401, 403, 404, 409, 429]);
2800
+ __name(defaultSqlDriver, "defaultSqlDriver");
2801
+ var AUTHOR_ANSWERED = /* @__PURE__ */ new Set([
2802
+ 400,
2803
+ 401,
2804
+ 403,
2805
+ 404,
2806
+ 409,
2807
+ 429
2808
+ ]);
2201
2809
  async function createApp(opts) {
2202
2810
  const { config, controllers } = opts;
2203
- setSchema(opts.schemas ?? []);
2204
- setSecretReader(opts.secretReader ?? null);
2205
- const routes = buildRouteTable(controllers);
2811
+ const container = buildContainer();
2812
+ let released = false;
2813
+ const liveContainer = {
2814
+ get(t) {
2815
+ if (released) {
2816
+ throw new Error("this app has shut down \u2014 its container no longer resolves. A class resolved here would hold a closed database pool.");
2817
+ }
2818
+ return container.get(t);
2819
+ },
2820
+ owned: container.owned,
2821
+ pressure: container.pressure
2822
+ };
2823
+ assertNoOrphanEntryPoints(controllers, container.owned);
2824
+ const routes = buildRouteTable(controllers, liveContainer);
2206
2825
  if (routes.length === 0) {
2207
2826
  throw new BootRefused([], "boot refused: zero endpoints collected \u2014 nothing would answer.");
2208
2827
  }
2209
2828
  const sql = opts.sql ?? await defaultSqlDriver(config);
2210
2829
  await sql.unsafe("select 1");
2211
- const auth = new AuthVerifier({ jwksUrl: config.authJwksUrl, issuer: config.authIssuer });
2830
+ setSchema(opts.schemas ?? []);
2831
+ setSecretReader(opts.secretReader ?? null);
2832
+ const auth = new AuthVerifier({
2833
+ jwksUrl: config.authJwksUrl,
2834
+ issuer: config.authIssuer
2835
+ });
2212
2836
  const attestGate = createAttestGate({
2213
2837
  moduleBaseUrl: config.moduleBaseUrl,
2214
2838
  serviceRoleKey: config.serviceRoleKey
@@ -2242,6 +2866,7 @@ async function createApp(opts) {
2242
2866
  Secrets: modules.Secrets ?? stubModule("Secrets")
2243
2867
  };
2244
2868
  }
2869
+ __name(buildServices, "buildServices");
2245
2870
  async function runInServiceScope(fn) {
2246
2871
  const db = createRequestDatabase(sql, {
2247
2872
  role: config.dbRole,
@@ -2257,6 +2882,7 @@ async function createApp(opts) {
2257
2882
  throw err;
2258
2883
  }
2259
2884
  }
2885
+ __name(runInServiceScope, "runInServiceScope");
2260
2886
  async function handleAuthorize(req, requestId) {
2261
2887
  if (uploadSecret === "" || !verifySignature(req.headers.get(SIGNATURE_HEADER) ?? "", uploadSecret)) {
2262
2888
  return envelope("unauthorized", "This endpoint is not callable directly", 401, requestId);
@@ -2280,15 +2906,14 @@ async function createApp(opts) {
2280
2906
  filename: body.filename
2281
2907
  });
2282
2908
  if (!grant) {
2283
- return envelope(
2284
- "not_an_upload_route",
2285
- `${body.method ?? "POST"} ${body.path} does not declare @Upload`,
2286
- 400,
2287
- requestId
2288
- );
2909
+ return envelope("not_an_upload_route", `${body.method ?? "POST"} ${body.path} does not declare @Upload`, 400, requestId);
2289
2910
  }
2290
- return new Response(JSON.stringify(grant), { status: 200, headers: JSON_HEADERS });
2911
+ return new Response(JSON.stringify(grant), {
2912
+ status: 200,
2913
+ headers: JSON_HEADERS
2914
+ });
2291
2915
  }
2916
+ __name(handleAuthorize, "handleAuthorize");
2292
2917
  async function handle(req) {
2293
2918
  const requestId = `req_${crypto.randomUUID()}`;
2294
2919
  const url = new URL(req.url);
@@ -2318,31 +2943,31 @@ async function createApp(opts) {
2318
2943
  if (req.method !== "GET" && req.method !== "HEAD") {
2319
2944
  const declared = Number(req.headers.get("content-length") ?? "");
2320
2945
  if (Number.isFinite(declared) && declared > MAX_BODY_BYTES) {
2321
- return new Response(
2322
- JSON.stringify({
2323
- error: "payload_too_large",
2324
- error_description: `Request body exceeds ${MAX_BODY_BYTES} bytes`,
2325
- status: 413,
2326
- request_id: requestId
2327
- }),
2328
- { status: 413, headers: JSON_HEADERS }
2329
- );
2946
+ return new Response(JSON.stringify({
2947
+ error: "payload_too_large",
2948
+ error_description: `Request body exceeds ${MAX_BODY_BYTES} bytes`,
2949
+ status: 413,
2950
+ request_id: requestId
2951
+ }), {
2952
+ status: 413,
2953
+ headers: JSON_HEADERS
2954
+ });
2330
2955
  }
2331
2956
  const buf = await req.arrayBuffer().catch(() => null);
2332
2957
  if (buf && buf.byteLength > MAX_BODY_BYTES) {
2333
- return new Response(
2334
- JSON.stringify({
2335
- error: "payload_too_large",
2336
- error_description: `Request body exceeds ${MAX_BODY_BYTES} bytes`,
2337
- status: 413,
2338
- request_id: requestId
2339
- }),
2340
- { status: 413, headers: JSON_HEADERS }
2341
- );
2958
+ return new Response(JSON.stringify({
2959
+ error: "payload_too_large",
2960
+ error_description: `Request body exceeds ${MAX_BODY_BYTES} bytes`,
2961
+ status: 413,
2962
+ request_id: requestId
2963
+ }), {
2964
+ status: 413,
2965
+ headers: JSON_HEADERS
2966
+ });
2342
2967
  }
2343
2968
  if (buf && buf.byteLength > 0) rawBody = new Uint8Array(buf);
2344
2969
  }
2345
- const readParsedBody = () => {
2970
+ const readParsedBody = /* @__PURE__ */ __name(() => {
2346
2971
  if (bodyRead) return parsedBody;
2347
2972
  bodyRead = true;
2348
2973
  if (!rawBody) {
@@ -2355,49 +2980,54 @@ async function createApp(opts) {
2355
2980
  parsedBody = {};
2356
2981
  }
2357
2982
  return parsedBody;
2358
- };
2359
- const attest = await attestGate(req, rawBody, { userId });
2983
+ }, "readParsedBody");
2984
+ const attest = await attestGate(req, rawBody, {
2985
+ userId
2986
+ });
2360
2987
  if (attest.observation) observations.record(attest.observation);
2361
2988
  if (attest.outcome === "reject") {
2362
- const headers = { ...JSON_HEADERS };
2989
+ const headers = {
2990
+ ...JSON_HEADERS
2991
+ };
2363
2992
  if (attest.nextChallenge) headers["x-palbase-attest-challenge"] = attest.nextChallenge;
2364
- return new Response(
2365
- JSON.stringify({
2366
- error: attest.reason,
2367
- error_description: "This endpoint requires a verified app instance",
2368
- status: 401,
2369
- request_id: requestId
2370
- }),
2371
- { status: 401, headers }
2372
- );
2993
+ return new Response(JSON.stringify({
2994
+ error: attest.reason,
2995
+ error_description: "This endpoint requires a verified app instance",
2996
+ status: 401,
2997
+ request_id: requestId
2998
+ }), {
2999
+ status: 401,
3000
+ headers
3001
+ });
2373
3002
  }
2374
3003
  if (attest.device && claims) attestedDevice = attest.device;
2375
3004
  attestChallengeOut = attest.nextChallenge;
2376
- const retryAfter = limiter.check(
2377
- meta.options?.rateLimit,
2378
- RateLimiter.key(hit.entry.id, userId, req.headers),
2379
- Date.now()
2380
- );
3005
+ const retryAfter = limiter.check(meta.options?.rateLimit, RateLimiter.key(hit.entry.id, userId, req.headers), Date.now());
2381
3006
  if (retryAfter !== null) {
2382
- return new Response(
2383
- JSON.stringify({
2384
- error: "too_many_requests",
2385
- error_description: "Rate limit exceeded for this endpoint",
2386
- status: 429,
2387
- request_id: requestId,
2388
- data: { retryAfter }
2389
- }),
2390
- { status: 429, headers: { ...JSON_HEADERS, "retry-after": String(retryAfter) } }
2391
- );
3007
+ return new Response(JSON.stringify({
3008
+ error: "too_many_requests",
3009
+ error_description: "Rate limit exceeded for this endpoint",
3010
+ status: 429,
3011
+ request_id: requestId,
3012
+ data: {
3013
+ retryAfter
3014
+ }
3015
+ }), {
3016
+ status: 429,
3017
+ headers: {
3018
+ ...JSON_HEADERS,
3019
+ "retry-after": String(retryAfter)
3020
+ }
3021
+ });
2392
3022
  }
2393
3023
  let completionUploadId = null;
2394
3024
  const args = [];
2395
3025
  let sseEnqueue = null;
2396
- let sseSettle = async () => {
2397
- };
3026
+ let sseSettle = /* @__PURE__ */ __name(async () => {
3027
+ }, "sseSettle");
2398
3028
  const sseWriter = makeSseWriter({
2399
- enqueue: (chunk) => sseEnqueue?.(chunk),
2400
- onFirstWrite: () => sseSettle()
3029
+ enqueue: /* @__PURE__ */ __name((chunk) => sseEnqueue?.(chunk), "enqueue"),
3030
+ onFirstWrite: /* @__PURE__ */ __name(() => sseSettle(), "onFirstWrite")
2401
3031
  });
2402
3032
  for (const p of meta.params ?? []) {
2403
3033
  switch (p.kind) {
@@ -2406,7 +3036,9 @@ async function createApp(opts) {
2406
3036
  const r = p.schema.safeParse(parsedBody);
2407
3037
  if (!r.success) {
2408
3038
  return envelope("bad_request", "Request body failed validation", 400, requestId, {
2409
- data: { fields: fieldErrors(r.error) }
3039
+ data: {
3040
+ fields: fieldErrors(r.error)
3041
+ }
2410
3042
  });
2411
3043
  }
2412
3044
  args[p.index] = r.data;
@@ -2416,7 +3048,9 @@ async function createApp(opts) {
2416
3048
  const r = p.schema.safeParse(Object.fromEntries(url.searchParams));
2417
3049
  if (!r.success) {
2418
3050
  return envelope("bad_request", "Query parameters failed validation", 400, requestId, {
2419
- data: { fields: fieldErrors(r.error) }
3051
+ data: {
3052
+ fields: fieldErrors(r.error)
3053
+ }
2420
3054
  });
2421
3055
  }
2422
3056
  args[p.index] = r.data;
@@ -2434,7 +3068,9 @@ async function createApp(opts) {
2434
3068
  const r = p.schema.safeParse(headersFor(raw, p.schema));
2435
3069
  if (!r.success) {
2436
3070
  return envelope("bad_request", "Request headers failed validation", 400, requestId, {
2437
- data: { fields: fieldErrors(r.error) }
3071
+ data: {
3072
+ fields: fieldErrors(r.error)
3073
+ }
2438
3074
  });
2439
3075
  }
2440
3076
  args[p.index] = r.data;
@@ -2457,12 +3093,7 @@ async function createApp(opts) {
2457
3093
  break;
2458
3094
  case "uploadedObject": {
2459
3095
  if (uploadSecret === "" || !verifySignature(req.headers.get(SIGNATURE_HEADER) ?? "", uploadSecret)) {
2460
- return envelope(
2461
- "unauthorized",
2462
- "This endpoint accepts uploads through storage, not directly",
2463
- 401,
2464
- requestId
2465
- );
3096
+ return envelope("unauthorized", "This endpoint accepts uploads through storage, not directly", 401, requestId);
2466
3097
  }
2467
3098
  readParsedBody();
2468
3099
  const envelopeIn = parsedBody;
@@ -2475,7 +3106,9 @@ async function createApp(opts) {
2475
3106
  if (already) {
2476
3107
  return new Response(already.body, {
2477
3108
  status: already.status,
2478
- headers: already.contentType ? { "content-type": already.contentType } : void 0
3109
+ headers: already.contentType ? {
3110
+ "content-type": already.contentType
3111
+ } : void 0
2479
3112
  });
2480
3113
  }
2481
3114
  completionUploadId = uploadId;
@@ -2535,26 +3168,24 @@ async function createApp(opts) {
2535
3168
  sseController = c;
2536
3169
  }
2537
3170
  });
2538
- sseEnqueue = (chunk) => {
3171
+ sseEnqueue = /* @__PURE__ */ __name((chunk) => {
2539
3172
  try {
2540
3173
  sseController.enqueue(sseEncoder.encode(chunk));
2541
3174
  } catch {
2542
3175
  }
2543
- };
3176
+ }, "sseEnqueue");
2544
3177
  let sawFirstFrame;
2545
3178
  const firstFrame = new Promise((r) => sawFirstFrame = r);
2546
- sseSettle = async () => {
3179
+ sseSettle = /* @__PURE__ */ __name(async () => {
2547
3180
  await db.commit();
2548
3181
  sawFirstFrame();
2549
- };
3182
+ }, "sseSettle");
2550
3183
  const guarded = {
2551
3184
  ...services,
2552
3185
  Database: new Proxy(services.Database, {
2553
3186
  get(target, prop, recv) {
2554
3187
  if (sseWriter.started()) {
2555
- throw new Error(
2556
- `sse_db_after_write: ${hit.entry.id} touched the database after its first write(). A streaming response settles its transaction with the first frame \u2014 do the database work before the first write.`
2557
- );
3188
+ throw new Error(`sse_db_after_write: ${hit.entry.id} touched the database after its first write(). A streaming response settles its transaction with the first frame \u2014 do the database work before the first write.`);
2558
3189
  }
2559
3190
  return Reflect.get(target, prop, recv);
2560
3191
  }
@@ -2570,23 +3201,23 @@ async function createApp(opts) {
2570
3201
  }
2571
3202
  const method = hit.entry.instance[meta.fnName];
2572
3203
  if (typeof method !== "function") {
2573
- throw new Error(
2574
- `route ${hit.entry.id} names method ${meta.fnName}, which the controller does not define`
2575
- );
3204
+ throw new Error(`route ${hit.entry.id} names method ${meta.fnName}, which the controller does not define`);
2576
3205
  }
2577
3206
  return method.apply(hit.entry.instance, args);
2578
- })).then(
2579
- () => void 0,
2580
- (err) => {
2581
- thrown = err;
2582
- }
2583
- );
2584
- await Promise.race([firstFrame, ran]);
3207
+ })).then(() => void 0, (err) => {
3208
+ thrown = err;
3209
+ });
3210
+ await Promise.race([
3211
+ firstFrame,
3212
+ ran
3213
+ ]);
2585
3214
  if (!sseWriter.started()) {
2586
3215
  await ran;
2587
3216
  await db.commit();
2588
3217
  if (thrown !== null) throw thrown;
2589
- return new Response(null, { status: 204 });
3218
+ return new Response(null, {
3219
+ status: 204
3220
+ });
2590
3221
  }
2591
3222
  void (async () => {
2592
3223
  await ran;
@@ -2602,7 +3233,10 @@ async function createApp(opts) {
2602
3233
  })();
2603
3234
  return new Response(stream, {
2604
3235
  status: 200,
2605
- headers: { "content-type": SSE_CONTENT_TYPE, "cache-control": "no-cache" }
3236
+ headers: {
3237
+ "content-type": SSE_CONTENT_TYPE,
3238
+ "cache-control": "no-cache"
3239
+ }
2606
3240
  });
2607
3241
  }
2608
3242
  const result = await runWithRuntime(services, () => {
@@ -2614,9 +3248,7 @@ async function createApp(opts) {
2614
3248
  }
2615
3249
  const method = hit.entry.instance[meta.fnName];
2616
3250
  if (typeof method !== "function") {
2617
- throw new Error(
2618
- `route ${hit.entry.id} names method ${meta.fnName}, which the controller does not define`
2619
- );
3251
+ throw new Error(`route ${hit.entry.id} names method ${meta.fnName}, which the controller does not define`);
2620
3252
  }
2621
3253
  return method.apply(hit.entry.instance, args);
2622
3254
  });
@@ -2625,19 +3257,20 @@ async function createApp(opts) {
2625
3257
  const v = meta.returnSchema.safeParse(result);
2626
3258
  if (!v.success) {
2627
3259
  log.error(`[engine] ${hit.entry.id} returned a value its declared type rejects`, v.error.issues);
2628
- return envelope(
2629
- "output_invalid",
2630
- "The handler returned a value its declared return type rejects",
2631
- 500,
2632
- requestId
2633
- );
3260
+ return envelope("output_invalid", "The handler returned a value its declared return type rejects", 500, requestId);
2634
3261
  }
2635
3262
  }
2636
3263
  if (result === void 0 || result === null) {
2637
3264
  if (completionUploadId) {
2638
- completions.remember(completionUploadId, { status: 204, body: null, contentType: null });
3265
+ completions.remember(completionUploadId, {
3266
+ status: 204,
3267
+ body: null,
3268
+ contentType: null
3269
+ });
2639
3270
  }
2640
- return new Response(null, { status: 204 });
3271
+ return new Response(null, {
3272
+ status: 204
3273
+ });
2641
3274
  }
2642
3275
  const payload = JSON.stringify(result);
2643
3276
  if (completionUploadId) {
@@ -2649,7 +3282,10 @@ async function createApp(opts) {
2649
3282
  }
2650
3283
  return new Response(payload, {
2651
3284
  status: 200,
2652
- headers: attestChallengeOut ? { ...JSON_HEADERS, "x-palbase-attest-challenge": attestChallengeOut } : JSON_HEADERS
3285
+ headers: attestChallengeOut ? {
3286
+ ...JSON_HEADERS,
3287
+ "x-palbase-attest-challenge": attestChallengeOut
3288
+ } : JSON_HEADERS
2653
3289
  });
2654
3290
  } catch (err) {
2655
3291
  await db.rollback(err);
@@ -2657,18 +3293,15 @@ async function createApp(opts) {
2657
3293
  if (isEngineRaised(err) || !AUTHOR_ANSWERED.has(err.status)) {
2658
3294
  log.error(`[engine] unhandled ${err.error} in ${hit.entry.id}`, err);
2659
3295
  }
2660
- return envelope(
2661
- err.error,
2662
- err.errorDescription,
2663
- err.status,
2664
- requestId,
2665
- err.data !== void 0 ? { data: err.data } : void 0
2666
- );
3296
+ return envelope(err.error, err.errorDescription, err.status, requestId, err.data !== void 0 ? {
3297
+ data: err.data
3298
+ } : void 0);
2667
3299
  }
2668
3300
  log.error(`[engine] unhandled error in ${hit.entry.id}`, err);
2669
3301
  return envelope("internal_error", "The request could not be completed", 500, requestId);
2670
3302
  }
2671
3303
  }
3304
+ __name(handle, "handle");
2672
3305
  const bootServices = buildServices(null);
2673
3306
  let runShutdownHooks;
2674
3307
  try {
@@ -2681,18 +3314,22 @@ async function createApp(opts) {
2681
3314
  handle,
2682
3315
  routes,
2683
3316
  config,
3317
+ container: liveContainer,
2684
3318
  runInServiceScope,
2685
3319
  async shutdown() {
2686
3320
  await runShutdownHooks();
2687
3321
  await closeDriver(sql);
3322
+ released = true;
2688
3323
  }
2689
3324
  };
2690
3325
  }
3326
+ __name(createApp, "createApp");
2691
3327
  async function closeDriver(sql) {
2692
3328
  const closable = sql;
2693
3329
  await closable.close?.();
2694
3330
  await closable.end?.();
2695
3331
  }
3332
+ __name(closeDriver, "closeDriver");
2696
3333
 
2697
3334
  export {
2698
3335
  BootRefused,
@@ -2713,4 +3350,4 @@ export {
2713
3350
  installEgressFence,
2714
3351
  createApp
2715
3352
  };
2716
- //# sourceMappingURL=chunk-VDF2T4AS.js.map
3353
+ //# sourceMappingURL=chunk-EB3TUX5J.js.map