@palbase/backend 27.1.0 → 28.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/bin/palbase-backend.cjs +133 -38
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +4 -4
  4. package/dist/{chunk-JVZQCC77.js → chunk-3TUJWHC2.js} +8 -3
  5. package/dist/chunk-3TUJWHC2.js.map +1 -0
  6. package/dist/{chunk-EB3TUX5J.js → chunk-75YROPRZ.js} +102 -38
  7. package/dist/chunk-75YROPRZ.js.map +1 -0
  8. package/dist/{chunk-XABHGMUT.js → chunk-IVZERLTM.js} +2 -2
  9. package/dist/{chunk-BQN723PL.js → chunk-RVP6BTEZ.js} +63 -6
  10. package/dist/chunk-RVP6BTEZ.js.map +1 -0
  11. package/dist/{chunk-OZKSM3JW.js → chunk-SNDXY565.js} +3 -2
  12. package/dist/chunk-SNDXY565.js.map +1 -0
  13. package/dist/db/index.cjs +4 -1
  14. package/dist/db/index.cjs.map +1 -1
  15. package/dist/db/index.d.cts +1 -1
  16. package/dist/db/index.d.ts +1 -1
  17. package/dist/db/index.js +3 -3
  18. package/dist/engine/index.cjs +143 -38
  19. package/dist/engine/index.cjs.map +1 -1
  20. package/dist/engine/index.d.cts +3 -3
  21. package/dist/engine/index.d.ts +3 -3
  22. package/dist/engine/index.js +11 -4
  23. package/dist/{index-H-0qv5d4.d.cts → index-9C3JHxg-.d.cts} +112 -5
  24. package/dist/{index-CAKOgAlP.d.ts → index-BbvOoZFr.d.ts} +112 -5
  25. package/dist/{index-CgE4sVhg.d.cts → index-DtISj9QX.d.cts} +94 -11
  26. package/dist/{index-V7QRh1wg.d.ts → index-dTTLlHIn.d.ts} +94 -11
  27. package/dist/index.cjs +67 -4
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +6 -22
  30. package/dist/index.d.ts +6 -22
  31. package/dist/index.js +10 -4
  32. package/dist/index.js.map +1 -1
  33. package/dist/openapi/index.d.cts +2 -2
  34. package/dist/openapi/index.d.ts +2 -2
  35. package/dist/{registry-4EI8aaFs.d.ts → registry-JQNIX-eA.d.ts} +1 -1
  36. package/dist/{registry-DHsPDY0_.d.cts → registry-qIPM5BQe.d.cts} +1 -1
  37. package/dist/test/index.cjs +73 -6
  38. package/dist/test/index.cjs.map +1 -1
  39. package/dist/test/index.d.cts +1 -1
  40. package/dist/test/index.d.ts +1 -1
  41. package/dist/test/index.js +70 -6
  42. package/dist/test/index.js.map +1 -1
  43. package/docs/README.md +1 -1
  44. package/docs/database.md +69 -1
  45. package/docs/llms-full.txt +70 -2
  46. package/package.json +1 -1
  47. package/template/package.json +1 -1
  48. package/dist/chunk-BQN723PL.js.map +0 -1
  49. package/dist/chunk-EB3TUX5J.js.map +0 -1
  50. package/dist/chunk-JVZQCC77.js.map +0 -1
  51. package/dist/chunk-OZKSM3JW.js.map +0 -1
  52. /package/dist/{chunk-XABHGMUT.js.map → chunk-IVZERLTM.js.map} +0 -0
@@ -83,8 +83,11 @@ __name(isColumnExpr, "isColumnExpr");
83
83
  function assertNoExpressionHandles(caller, table, cols, data) {
84
84
  for (const c of cols) {
85
85
  const v = data[c];
86
+ if (isNowExpr(v)) {
87
+ throw new Error(`${caller}(${table}): "${c}" now() ald\u0131 \u2014 bu yolda de\u011Fer beklenir. Sat\u0131r EKLEN\u0130RKEN sunucu saatini yazman\u0131n yolu kolonu defaultNow() ile bildirmek (varsay\u0131lan kolonun yan\u0131nda durur, her \xE7a\u011Fr\u0131da tekrarlanmaz); var olan bir sat\u0131r\u0131 damgalamak i\xE7in updateMany({ where, set: { ${c}: now() } }) ya da $transaction i\xE7inde tx.public.${table}.updateWhere(where, { ${c}: now() }).`);
88
+ }
86
89
  if (isColumnExpr(v)) {
87
- throw new Error(`${caller}(${table}): "${c}" bir ifade tutama\u011F\u0131 ald\u0131 (increment()/decrement()/now()). Bu yolda de\u011Fer beklenir. Saya\xE7 art\u0131\u015F\u0131 i\xE7in updateMany(where, { ${c}: increment(n) }) ya da $transaction i\xE7inde tx.tables.${table}.updateWhere(where, { ${c}: increment(n) }) kullan\u0131n.`);
90
+ throw new Error(`${caller}(${table}): "${c}" bir ifade tutama\u011F\u0131 ald\u0131 (increment()/decrement()). Bu yolda de\u011Fer beklenir. Saya\xE7 art\u0131\u015F\u0131 i\xE7in updateMany({ where, set: { ${c}: increment(n) } }) ya da $transaction i\xE7inde tx.public.${table}.updateWhere(where, { ${c}: increment(n) }) kullan\u0131n.`);
88
91
  }
89
92
  if (isColRef(v)) {
90
93
  throw new Error(`${caller}(${table}): "${c}" bir col() ald\u0131. Kolon referans\u0131 yaln\u0131z F\u0130LTREDE durabilir; bir kolonun de\u011Ferini ba\u015Fka bir kolona yazmak i\xE7in $query kullan\u0131n.`);
@@ -376,7 +379,9 @@ function encodeMap(map, allowColumnExpr) {
376
379
  const out = {};
377
380
  for (const key of Object.keys(map).sort()) {
378
381
  const value = map[key];
379
- if (value === void 0) continue;
382
+ if (value === void 0) {
383
+ throw new TxPlanError(`${key} de\u011Feri undefined \u2014 bu bir yazma de\u011Feri de\u011Fil. Anahtar duruyor ama de\u011Feri yok, yani kolon sessizce YAZILMAZDI (istek g\xF6vdesinden gelen bir alan\u0131n eksik olmas\u0131 bu \u015Fekilde g\xF6r\xFCn\xFCr). Kolon varsay\u0131lan\u0131n\u0131 istiyorsan\u0131z anahtar\u0131 hi\xE7 koymay\u0131n; NULL yazmak istiyorsan\u0131z a\xE7\u0131k\xE7a null yaz\u0131n.`);
384
+ }
380
385
  out[key] = encodeValue(value, key, allowColumnExpr);
381
386
  }
382
387
  return out;
@@ -904,6 +909,10 @@ function makeTableProxy(ops, prefix) {
904
909
  const name = `${prefix}${prop}`;
905
910
  return {
906
911
  insert: /* @__PURE__ */ __name((data) => ops().insert(name, data), "insert"),
912
+ // DÖRDÜNCÜ FİİL, aynı gerekçeyle: tip söz veriyor, ops katmanı
913
+ // uyguluyor, ve bir handler'ın gerçekten dokunduğu yer BURASI.
914
+ // `runtime-table-verbs` kapısı bunu adıyla saydı.
915
+ insertMany: /* @__PURE__ */ __name((rows, opts) => ops().insertMany(name, rows, opts), "insertMany"),
907
916
  update: /* @__PURE__ */ __name((q) => ops().update(name, q.where.id, q.set), "update"),
908
917
  delete: /* @__PURE__ */ __name((id) => ops().delete(name, id), "delete"),
909
918
  findById: /* @__PURE__ */ __name((id) => ops().findById(name, id), "findById"),
@@ -960,6 +969,7 @@ function makeTypedSurface(raw) {
960
969
  $claim: /* @__PURE__ */ __name((table, unique, extra) => reco.claim(table, unique, extra), "$claim"),
961
970
  $lockRows: /* @__PURE__ */ __name((table, ids) => reco.lockRows(table, ids), "$lockRows"),
962
971
  $advisoryXactLock: /* @__PURE__ */ __name((key) => reco.advisoryXactLock(key), "$advisoryXactLock"),
972
+ $insertMany: /* @__PURE__ */ __name((table, rows, opts) => raw.insertMany(table, rows, opts), "$insertMany"),
963
973
  $supersede: /* @__PURE__ */ __name((table, id, row) => raw.supersede(table, id, row), "$supersede")
964
974
  };
965
975
  const base = Object.assign(ops, {
@@ -1149,6 +1159,20 @@ ${detail}`);
1149
1159
  }
1150
1160
  __name(loadConfig, "loadConfig");
1151
1161
 
1162
+ // src/refusals.ts
1163
+ var DECLARATION_REFUSAL = /* @__PURE__ */ Symbol.for("palbase.backend.declarationRefusal");
1164
+ var DeclarationRefused = class extends Error {
1165
+ static {
1166
+ __name(this, "DeclarationRefused");
1167
+ }
1168
+ /** @see DECLARATION_REFUSAL — realm-safe, unlike `instanceof`. */
1169
+ [DECLARATION_REFUSAL] = true;
1170
+ constructor(message) {
1171
+ super(message);
1172
+ this.name = "DeclarationRefused";
1173
+ }
1174
+ };
1175
+
1152
1176
  // src/engine/auth.ts
1153
1177
  function b64urlToBytes(s) {
1154
1178
  const pad = s.replace(/-/g, "+").replace(/_/g, "/");
@@ -1471,12 +1495,12 @@ function buildRelations(schemas) {
1471
1495
  const held = names.get(name);
1472
1496
  if (held !== void 0) {
1473
1497
  if (held.def.owns === true && origin.def.owns === true) {
1474
- throw new Error(`table "${tableKey}" declares TWO owner columns (${held.column}, ${origin.column}) \u2014 a table has at most ONE ownedByUser(). If the other column only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead.`);
1498
+ throw new DeclarationRefused(`table "${tableKey}" declares TWO owner columns (${held.column}, ${origin.column}) \u2014 a table has at most ONE ownedByUser(). If the other column only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead.`);
1475
1499
  }
1476
1500
  const heldFix = renameCall(held);
1477
1501
  const originFix = renameCall(origin);
1478
1502
  const remedy = heldFix === originFix ? `rename one of them: ${heldFix}` : `rename one of them \u2014 "${held.table}.${held.column}": ${heldFix}; "${origin.table}.${origin.column}": ${originFix}`;
1479
- throw new Error(`table "${tableKey}": ${describeOrigin(held)} and ${describeOrigin(origin)} both resolve to the relation name "${name}" \u2014 ${remedy}.`);
1503
+ throw new DeclarationRefused(`table "${tableKey}": ${describeOrigin(held)} and ${describeOrigin(origin)} both resolve to the relation name "${name}" \u2014 ${remedy}.`);
1480
1504
  }
1481
1505
  names.set(name, origin);
1482
1506
  out.get(tableKey)?.push(edge);
@@ -2054,7 +2078,7 @@ function compileWhereBare(table, colSet, where, add, caller = "search", alias =
2054
2078
  const rel = rels.find((r) => r.name === relName);
2055
2079
  if (rel === void 0) {
2056
2080
  const known = rels.map((r) => r.name).sort();
2057
- 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 }).`);
2081
+ throw new Error(`${caller}(${table}): has.${relName} diye bir ili\u015Fki yok` + (known.length > 0 ? ` (bilinenler: ${known.join(", ")}). \u0130li\u015Fkiler yabanc\u0131 anahtarlardan T\xDCRET\u0130L\u0130R; ad\u0131n\u0131 de\u011Fi\u015Ftirmek i\xE7in references(..., { as, reverseAs }).` : `. Bu tablo H\u0130\xC7 yabanc\u0131 anahtar bildirmiyor, ve ili\u015Fkiler onlardan T\xDCRET\u0130L\u0130R. Var olan bir veritaban\u0131ndan introspect edilmi\u015F \u015Femalarda bu s\u0131k olur: kolon \`text()\`/\`uuid()\` olarak durur, \`references(() => <tablo>.<kolon>)\` yoktur. Bildirdi\u011Finiz anda ili\u015Fki iki tarafta da adland\u0131r\u0131l\u0131r \u2014 ama bu CANLI veritaban\u0131na giden bir k\u0131s\u0131tt\u0131r ve mevcut yetim sat\u0131rlar onu d\xFC\u015F\xFCr\xFCr; \xF6nce onlar\u0131 say\u0131n.`));
2058
2082
  }
2059
2083
  const ourCol = rel.kind === "many" ? primaryKeyOf(table) : rel.via;
2060
2084
  const theirCol = rel.kind === "many" ? rel.via : primaryKeyOf(rel.to);
@@ -2234,6 +2258,37 @@ async function embedQuery(embed, text) {
2234
2258
  }
2235
2259
  }
2236
2260
  __name(embedQuery, "embedQuery");
2261
+ function buildSetAssignments(table, cols, set, add, caller) {
2262
+ return cols.map((c) => {
2263
+ const expr = columnExprOf(set[c]);
2264
+ if (expr !== null && expr.fn === "now") {
2265
+ return `${quoteIdent(c)} = now()`;
2266
+ }
2267
+ if (expr !== null) {
2268
+ const operator = expr.fn === "inc" ? "+" : "-";
2269
+ const t = transformsOf(currentSchema, table).get(c);
2270
+ const amount = t?.toDb === void 0 || expr.by === null || expr.by === void 0 ? expr.by : t.toDb(expr.by);
2271
+ return `${quoteIdent(c)} = ${quoteIdent(c)} ${operator} ${add(amount)}`;
2272
+ }
2273
+ return `${quoteIdent(c)} = ${add(asBindParams(table, [
2274
+ c
2275
+ ], set, caller)[0])}`;
2276
+ }).join(", ");
2277
+ }
2278
+ __name(buildSetAssignments, "buildSetAssignments");
2279
+ function buildValueTuple(table, cols, data, add, caller) {
2280
+ return cols.map((c) => {
2281
+ const expr = columnExprOf(data[c]);
2282
+ if (expr !== null && expr.fn === "now") return "now()";
2283
+ if (expr !== null) {
2284
+ throw new Error(`${caller}(${table}): "${c}" ${expr.fn === "inc" ? "increment()" : "decrement()"} ald\u0131 \u2014 sat\u0131r HEN\xDCZ YOK, yani "kolonun \u015Fu anki de\u011Feri" diye bir \u015Fey yok. Ba\u015Flang\u0131\xE7 de\u011Ferini yaz\u0131n; art\u0131rmak i\xE7in sat\u0131r olu\u015Ftuktan sonra updateMany({ where, set: { ${c}: ${expr.fn === "inc" ? "increment" : "decrement"}(n) } }).`);
2285
+ }
2286
+ return add(asBindParams(table, [
2287
+ c
2288
+ ], data, caller)[0]);
2289
+ }).join(", ");
2290
+ }
2291
+ __name(buildValueTuple, "buildValueTuple");
2237
2292
  function createOps(tx) {
2238
2293
  const at = /* @__PURE__ */ __name(() => resolveTx(tx), "at");
2239
2294
  const ops = {
@@ -2247,9 +2302,11 @@ function createOps(tx) {
2247
2302
  async insert(table, data) {
2248
2303
  const cols = Object.keys(data);
2249
2304
  if (cols.length === 0) throw new Error(`insert into ${table}: no columns given`);
2250
- const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
2251
- const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`;
2252
- const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "insert"));
2305
+ const params = [];
2306
+ const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
2307
+ const tuple = buildValueTuple(table, cols, data, add, "insert");
2308
+ const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`;
2309
+ const rows = await (await at()).unsafe(sql, params);
2253
2310
  const inserted = rows[0];
2254
2311
  if (!inserted) {
2255
2312
  throw new Error(`insert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`);
@@ -2257,6 +2314,41 @@ function createOps(tx) {
2257
2314
  return asTableRow(table, inserted);
2258
2315
  },
2259
2316
  /**
2317
+ * Çok satırı TEK statement'la ekle.
2318
+ *
2319
+ * Motor bunu zaten yapıyordu ve yalnız PLAN yolundan erişilebiliyordu
2320
+ * (`tx.public.x.insertMany`). Dışarıda tek kapı `insert(row)` olduğu için
2321
+ * "şu 200 satırı yaz" ya 200 gidiş-dönüş ya da ham SQL oluyordu — ölçüldü:
2322
+ * bir müşteri projesinde bir seed rutini satırları tek tek siliyor
2323
+ * (`deleteMany` VARKEN) ve tek tek ekliyor.
2324
+ *
2325
+ * SATIRLARIN ANAHTAR KÜMESİ AYNI OLMAK ZORUNDA. Kolon listesi tek bir
2326
+ * statement'ta paylaşıldığı için ilk satırdan türer; farklı anahtar taşıyan
2327
+ * bir satırın fazla kolonu SESSİZCE YAZILMAZDI. Eksik kolon için `null`
2328
+ * yazın ya da varsayılanı istiyorsanız o satırları ayrı çağrıda ekleyin.
2329
+ */
2330
+ async insertMany(table, rows, opts) {
2331
+ if (rows.length === 0) return [];
2332
+ const first = rows[0];
2333
+ const cols = Object.keys(first);
2334
+ if (cols.length === 0) throw new Error(`insertMany into ${table}: no columns given`);
2335
+ for (let i = 1; i < rows.length; i++) {
2336
+ const keys = Object.keys(rows[i]);
2337
+ const missing = cols.filter((c) => !(c in rows[i]));
2338
+ const extra = keys.filter((k) => !cols.includes(k));
2339
+ if (missing.length > 0 || extra.length > 0) {
2340
+ throw new Error(`insertMany(${table}): ${i}. sat\u0131r\u0131n kolonlar\u0131 ilk sat\u0131rla ayn\u0131 de\u011Fil` + (missing.length > 0 ? ` (eksik: ${missing.join(", ")})` : "") + (extra.length > 0 ? ` (fazla: ${extra.join(", ")})` : "") + `. Tek statement kolon listesini PAYLA\u015EIR, yani fazla kolon sessizce yaz\u0131lmaz ve eksik kolon yanl\u0131\u015F s\xFCtuna kayar. Eksikler i\xE7in null yaz\u0131n, ya da farkl\u0131 \u015Fekilli sat\u0131rlar\u0131 ayr\u0131 \xE7a\u011Fr\u0131larda ekleyin.`);
2341
+ }
2342
+ }
2343
+ const params = [];
2344
+ const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
2345
+ const tuples = rows.map((r) => `(${buildValueTuple(table, cols, r, add, "insertMany")})`);
2346
+ const tail = opts !== void 0 && opts.onConflict.length > 0 ? ` ${onConflictTail(cols, opts.onConflict, opts.action ?? "ignore")}` : "";
2347
+ const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail} RETURNING *`;
2348
+ const out = await (await at()).unsafe(sql, params);
2349
+ return out.map((r) => asTableRow(table, r));
2350
+ },
2351
+ /**
2260
2352
  * Bir ADI kilitle — satırı değil (FR-054).
2261
2353
  *
2262
2354
  * "Aynı anda tek bir fatura kapanışı koşsun" gibi, kilitlenecek satırı
@@ -2342,8 +2434,10 @@ function createOps(tx) {
2342
2434
  const live = await at();
2343
2435
  try {
2344
2436
  return await live.savepoint(async (sp) => {
2345
- const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
2346
- const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`, asBindParams(table, cols, data, "claim"));
2437
+ const params = [];
2438
+ const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
2439
+ const tuple = buildValueTuple(table, cols, data, add, "claim");
2440
+ const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`, params);
2347
2441
  if (!rows[0]) {
2348
2442
  throw new Error(`claim(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si).`);
2349
2443
  }
@@ -2394,10 +2488,11 @@ function createOps(tx) {
2394
2488
  const cols = Object.keys(data);
2395
2489
  if (cols.length === 0) throw new Error(`upsert into ${table}: no columns given`);
2396
2490
  if (searchConfigFor(table)?.validity) {
2397
- const ph = cols.map((_, i) => `$${i + 1}`).join(", ");
2491
+ const bindData = [];
2492
+ const addPut = /* @__PURE__ */ __name((v) => `$${bindData.push(v)}`, "addPut");
2493
+ const ph = buildValueTuple(table, cols, data, addPut, "put");
2398
2494
  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 *`;
2399
2495
  const live = await at();
2400
- const bindData = asBindParams(table, cols, data, "put");
2401
2496
  const first = await live.unsafe(insertSql, bindData);
2402
2497
  if (first[0]) return asTableRow(table, first[0]);
2403
2498
  const condSql = conflict.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ");
@@ -2422,12 +2517,14 @@ function createOps(tx) {
2422
2517
  ]);
2423
2518
  return asTableRow(table, second[0]);
2424
2519
  }
2425
- const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
2520
+ const params = [];
2521
+ const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
2522
+ const placeholders = buildValueTuple(table, cols, data, add, "put");
2426
2523
  const conflictSet = new Set(conflict);
2427
2524
  const assignments = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
2428
2525
  const action = assignments.length ? `DO UPDATE SET ${assignments.join(", ")}` : `DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
2429
2526
  const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
2430
- const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "put"));
2527
+ const rows = await (await at()).unsafe(sql, params);
2431
2528
  const row = rows[0];
2432
2529
  if (!row) {
2433
2530
  throw new Error(`upsert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`);
@@ -2438,12 +2535,14 @@ function createOps(tx) {
2438
2535
  assertNotAppendOnly("update", table);
2439
2536
  const cols = Object.keys(data);
2440
2537
  if (cols.length === 0) return ops.findById(table, id);
2441
- const assignments = cols.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(", ");
2442
- const sql = `UPDATE ${quoteTable(table)} SET ${assignments} WHERE id = $${cols.length + 1} RETURNING *`;
2443
- const rows = await (await at()).unsafe(sql, [
2444
- ...asBindParams(table, cols, data, "update"),
2445
- id
2446
- ]);
2538
+ const params = [];
2539
+ const add = /* @__PURE__ */ __name((v) => {
2540
+ params.push(v);
2541
+ return `$${params.length}`;
2542
+ }, "add");
2543
+ const assignments = buildSetAssignments(table, cols, data, add, "update");
2544
+ const sql = `UPDATE ${quoteTable(table)} SET ${assignments} WHERE id = ${add(id)} RETURNING *`;
2545
+ const rows = await (await at()).unsafe(sql, params);
2447
2546
  return rows[0] ? asTableRow(table, rows[0]) : null;
2448
2547
  },
2449
2548
  async delete(table, id) {
@@ -2489,21 +2588,7 @@ function createOps(tx) {
2489
2588
  params.push(v);
2490
2589
  return `$${params.length}`;
2491
2590
  }, "add");
2492
- const assignments = cols.map((c) => {
2493
- const expr = columnExprOf(set[c]);
2494
- if (expr !== null && expr.fn === "now") {
2495
- return `${quoteIdent(c)} = now()`;
2496
- }
2497
- if (expr !== null) {
2498
- const operator = expr.fn === "inc" ? "+" : "-";
2499
- const t = transformsOf(currentSchema, table).get(c);
2500
- const amount = t?.toDb === void 0 || expr.by === null || expr.by === void 0 ? expr.by : t.toDb(expr.by);
2501
- return `${quoteIdent(c)} = ${quoteIdent(c)} ${operator} ${add(amount)}`;
2502
- }
2503
- return `${quoteIdent(c)} = ${add(asBindParams(table, [
2504
- c
2505
- ], set, "updateMany")[0])}`;
2506
- }).join(", ");
2591
+ const assignments = buildSetAssignments(table, cols, set, add, "updateMany");
2507
2592
  const whereSql = compileWhere(table, known, where, add, "updateMany");
2508
2593
  assertHasPredicate(whereSql, "updateMany", table);
2509
2594
  const pk = primaryKeyOf(table);
@@ -2961,8 +3046,10 @@ function createOps(tx) {
2961
3046
  [cfg.pk]: newId
2962
3047
  };
2963
3048
  const cols = Object.keys(data);
2964
- const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
2965
- const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`, asBindParams(table, cols, data, "supersede"));
3049
+ const params = [];
3050
+ const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
3051
+ const tuple = buildValueTuple(table, cols, data, add, "supersede");
3052
+ const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`, params);
2966
3053
  if (!rows[0]) {
2967
3054
  throw new Error(`supersede(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si)`);
2968
3055
  }
@@ -3918,6 +4005,14 @@ var DiError = class extends Error {
3918
4005
  path;
3919
4006
  at;
3920
4007
  fixes;
4008
+ /**
4009
+ * EVERY `DiKind` IS A FACT ABOUT THE AUTHOR'S DECLARATIONS — an unresolvable
4010
+ * dependency, a cycle, a class no module owns. Not one of them is something
4011
+ * the environment could change, so restarting re-reads the same bytes and
4012
+ * fails identically. The cure is a new artifact, and the tag is how the
4013
+ * runtime learns that without matching on wording.
4014
+ */
4015
+ [DECLARATION_REFUSAL] = true;
3921
4016
  constructor(kind, path, at, detail, fixes) {
3922
4017
  super(`
3923
4018
  Kind: ${kind}
@@ -4333,7 +4428,7 @@ async function createApp(opts) {
4333
4428
  assertNoOrphanEntryPoints(controllers, container.owned);
4334
4429
  const routes = buildRouteTable(controllers, liveContainer);
4335
4430
  if (routes.length === 0) {
4336
- throw new BootRefused([], "boot refused: zero endpoints collected \u2014 nothing would answer.");
4431
+ throw new DeclarationRefused("boot refused: zero endpoints collected \u2014 nothing would answer.");
4337
4432
  }
4338
4433
  const sql = opts.sql ?? await defaultSqlDriver(config);
4339
4434
  await sql.unsafe("select 1");