@palbase/backend 24.0.1 → 24.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 (44) hide show
  1. package/dist/bin/palbase-backend.cjs +75 -18
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +2 -2
  4. package/dist/{chunk-T5IOSOE5.js → chunk-ERDL5VAE.js} +2 -2
  5. package/dist/{chunk-YSQBC2VL.js → chunk-HPUSV4AZ.js} +71 -20
  6. package/dist/chunk-HPUSV4AZ.js.map +1 -0
  7. package/dist/{chunk-H3JAISUY.js → chunk-UWSYTUGM.js} +11 -1
  8. package/dist/{chunk-H3JAISUY.js.map → chunk-UWSYTUGM.js.map} +1 -1
  9. package/dist/db/index.d.cts +2 -2
  10. package/dist/db/index.d.ts +2 -2
  11. package/dist/engine/index.cjs +75 -18
  12. package/dist/engine/index.cjs.map +1 -1
  13. package/dist/engine/index.d.cts +4 -4
  14. package/dist/engine/index.d.ts +4 -4
  15. package/dist/engine/index.js +2 -2
  16. package/dist/{index-BOS_rFBO.d.ts → index-ByBMibIJ.d.ts} +1 -1
  17. package/dist/{index-dJNhDZ7j.d.ts → index-C0PMn5jl.d.ts} +3 -3
  18. package/dist/{index-MoQ31B6M.d.cts → index-D4rts8T7.d.cts} +1 -1
  19. package/dist/{index-C84bLgeO.d.cts → index-DAwHMppB.d.cts} +3 -3
  20. package/dist/index.cjs +8 -0
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +23 -9
  23. package/dist/index.d.ts +23 -9
  24. package/dist/index.js +5 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/openapi/index.cjs.map +1 -1
  27. package/dist/openapi/index.d.cts +2 -2
  28. package/dist/openapi/index.d.ts +2 -2
  29. package/dist/openapi/index.js +2 -2
  30. package/dist/{registry-CEod_5sz.d.ts → registry-B8pld3fj.d.ts} +1 -1
  31. package/dist/{registry-1X-skBNu.d.cts → registry-CgJvDYUW.d.cts} +1 -1
  32. package/dist/test/index.cjs.map +1 -1
  33. package/dist/test/index.d.cts +1 -1
  34. package/dist/test/index.d.ts +1 -1
  35. package/dist/test/index.js.map +1 -1
  36. package/docs/README.md +5 -1
  37. package/docs/llms-full.txt +5 -1
  38. package/package.json +1 -1
  39. package/template/AGENTS.md +34 -22
  40. package/template/tsconfig.json +6 -0
  41. package/dist/chunk-YSQBC2VL.js.map +0 -1
  42. package/dist/{chunk-T5IOSOE5.js.map → chunk-ERDL5VAE.js.map} +0 -0
  43. package/dist/{endpoint-CcQ1a36a.d.cts → endpoint-BSGw1pTu.d.cts} +16 -16
  44. package/dist/{endpoint-0_DGBajf.d.ts → endpoint-DMwJjEHQ.d.ts} +16 -16
@@ -635,6 +635,14 @@ var Realtime = makeServiceProxy("Realtime");
635
635
 
636
636
  // src/errors.ts
637
637
  var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
638
+ var ENGINE_RAISED = /* @__PURE__ */ Symbol.for("palbase.backend.engineRaised");
639
+ function markEngineRaised(e) {
640
+ e[ENGINE_RAISED] = true;
641
+ return e;
642
+ }
643
+ function isEngineRaised(e) {
644
+ return typeof e === "object" && e !== null && e[ENGINE_RAISED] === true;
645
+ }
638
646
  function isHttpError(err) {
639
647
  if (typeof err !== "object" || err === null) return false;
640
648
  const e = err;
@@ -1156,7 +1164,14 @@ function asTableRows(table, rows) {
1156
1164
  const transforms = transformsOf(currentSchema, table);
1157
1165
  return rows.map((row) => applyFromDb(reviveVectors(asWireRow(row), vectorCols), transforms));
1158
1166
  }
1159
- function asBindParams(table, cols, data) {
1167
+ function asBindParams(table, cols, data, caller) {
1168
+ for (const c of cols) {
1169
+ if (data[c] === void 0) {
1170
+ throw new Error(
1171
+ `${caller}(${table}): "${c}" de\u011Feri undefined \u2014 bu bir yazma de\u011Feri de\u011Fil. Kolonu bo\u015Faltmak istiyorsan null yaz; kolonu de\u011Fi\u015Ftirmek istemiyorsan nesneye hi\xE7 koyma (bir eksik istek alan\u0131 sessizce NULL yaz\u0131yordu \u2014 FR-016).`
1172
+ );
1173
+ }
1174
+ }
1160
1175
  const vectorCols = vectorColumnsOf(currentSchema, table);
1161
1176
  const transforms = transformsOf(currentSchema, table);
1162
1177
  return cols.map((c) => {
@@ -1383,23 +1398,49 @@ function compileWhere(table, colSet, where, add, caller = "search") {
1383
1398
  if (colSet !== null && !colSet.has(col)) {
1384
1399
  throw new Error(`${caller}(${table}): where kolonu "${col}" tabloda yok (FR-016)`);
1385
1400
  }
1401
+ if (cond === void 0) {
1402
+ throw new Error(
1403
+ `${caller}(${table}): where.${col} de\u011Feri undefined \u2014 bu bir filtre de\u011Feri de\u011Fil. Ba\u011Flan\u0131nca NULL olur ve '= NULL' hi\xE7bir sat\u0131ra uymaz, yani sorgu sessizce bo\u015F sonu\xE7 d\xF6nerdi. De\u011Fer yoksa anahtar\u0131 filtreye hi\xE7 koymay\u0131n.`
1404
+ );
1405
+ }
1386
1406
  const q = `t.${quoteIdent(col)}`;
1387
1407
  const bind = bindFor(col);
1388
1408
  if (cond !== null && typeof cond === "object" && !Array.isArray(cond)) {
1409
+ if (Object.keys(cond).length === 0) {
1410
+ throw new Error(
1411
+ `${caller}(${table}): where.${col} bo\u015F bir operat\xF6r nesnesi ({}) \u2014 hi\xE7bir ko\u015Ful \xFCretmez, yani bu alan filtreden sessizce D\xDC\u015EERD\u0130. Ko\u015Ful kurulmayacaksa anahtar\u0131 filtreye hi\xE7 koymay\u0131n (D-21).`
1412
+ );
1413
+ }
1389
1414
  for (const [op, v] of Object.entries(cond)) {
1390
1415
  if (op === "in") {
1391
1416
  if (!Array.isArray(v)) throw new Error(`${caller}(${table}): where.${col}.in bir dizi olmal\u0131`);
1417
+ if (v.some((x) => x === void 0)) {
1418
+ throw new Error(
1419
+ `${caller}(${table}): where.${col}.in listesinde undefined var \u2014 sessizce NULL'a ba\u011Flan\u0131r ve o eleman hi\xE7bir sat\u0131rla e\u015Fle\u015Fmez. Listeyi kurarken eleyin.`
1420
+ );
1421
+ }
1392
1422
  if (v.length === 0) {
1393
1423
  parts.push("false");
1394
1424
  continue;
1395
1425
  }
1396
1426
  parts.push(`${q} IN (${v.map((x) => bind(x)).join(", ")})`);
1397
1427
  } else if (op in WHERE_OPS) {
1428
+ if (v === void 0) {
1429
+ throw new Error(
1430
+ `${caller}(${table}): where.${col}.${op} de\u011Feri undefined \u2014 kar\u015F\u0131la\u015Ft\u0131rman\u0131n sa\u011F taraf\u0131 NULL olur ve sonu\xE7 hi\xE7bir sat\u0131ra uymaz. Ko\u015Fulu kurmay\u0131n.`
1431
+ );
1432
+ }
1433
+ if (v === null && (op === "neq" || op === "eq")) {
1434
+ parts.push(`${q} IS ${op === "neq" ? "NOT " : ""}NULL`);
1435
+ continue;
1436
+ }
1398
1437
  parts.push(`${q} ${WHERE_OPS[op]} ${bind(v)}`);
1399
1438
  } else {
1400
1439
  throw new Error(`${caller}(${table}): where.${col} bilinmeyen operat\xF6r "${op}" (gt/gte/lt/lte/neq/in)`);
1401
1440
  }
1402
1441
  }
1442
+ } else if (cond === null) {
1443
+ parts.push(`${q} IS NULL`);
1403
1444
  } else {
1404
1445
  parts.push(`${q} = ${bind(cond)}`);
1405
1446
  }
@@ -1492,7 +1533,7 @@ function createOps(tx) {
1492
1533
  if (cols.length === 0) throw new Error(`insert into ${table}: no columns given`);
1493
1534
  const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
1494
1535
  const sql = `INSERT INTO ${quoteIdent(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`;
1495
- const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data));
1536
+ const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "insert"));
1496
1537
  const inserted = rows[0];
1497
1538
  if (!inserted) {
1498
1539
  throw new Error(
@@ -1526,7 +1567,7 @@ function createOps(tx) {
1526
1567
  const ph = cols.map((_, i) => `$${i + 1}`).join(", ");
1527
1568
  const insertSql = `INSERT INTO ${quoteIdent(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${ph}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) WHERE "valid_to" IS NULL DO NOTHING RETURNING *`;
1528
1569
  const live = await at();
1529
- const bindData = asBindParams(table, cols, data);
1570
+ const bindData = asBindParams(table, cols, data, "upsert");
1530
1571
  const first = await live.unsafe(insertSql, bindData);
1531
1572
  if (first[0]) return asTableRow(table, first[0]);
1532
1573
  const condSql = conflict.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ");
@@ -1560,7 +1601,7 @@ function createOps(tx) {
1560
1601
  const assignments = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
1561
1602
  const action = assignments.length ? `DO UPDATE SET ${assignments.join(", ")}` : `DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
1562
1603
  const sql = `INSERT INTO ${quoteIdent(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
1563
- const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data));
1604
+ const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "upsert"));
1564
1605
  const row = rows[0];
1565
1606
  if (!row) {
1566
1607
  throw new Error(
@@ -1574,7 +1615,7 @@ function createOps(tx) {
1574
1615
  if (cols.length === 0) return ops.findById(table, id);
1575
1616
  const assignments = cols.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(", ");
1576
1617
  const sql = `UPDATE ${quoteIdent(table)} SET ${assignments} WHERE id = $${cols.length + 1} RETURNING *`;
1577
- const rows = await (await at()).unsafe(sql, [...asBindParams(table, cols, data), id]);
1618
+ const rows = await (await at()).unsafe(sql, [...asBindParams(table, cols, data, "update"), id]);
1578
1619
  return rows[0] ? asTableRow(table, rows[0]) : null;
1579
1620
  },
1580
1621
  async delete(table, id) {
@@ -1605,13 +1646,20 @@ function createOps(tx) {
1605
1646
  `updateMany(${table}): nothing to set. An update with no columns is not a no-op worth pretending happened \u2014 pass the columns to write.`
1606
1647
  );
1607
1648
  }
1649
+ const known = schemaColumns(table);
1650
+ if (known !== null) {
1651
+ for (const c of cols) {
1652
+ if (!known.has(c)) {
1653
+ throw new Error(`updateMany(${table}): set kolonu "${c}" tabloda yok (FR-016)`);
1654
+ }
1655
+ }
1656
+ }
1608
1657
  const params = [];
1609
1658
  const add = (v) => {
1610
1659
  params.push(v);
1611
1660
  return `$${params.length}`;
1612
1661
  };
1613
- const assignments = cols.map((c) => `${quoteIdent(c)} = ${add(asBindParams(table, [c], set)[0])}`).join(", ");
1614
- const known = schemaColumns(table);
1662
+ const assignments = cols.map((c) => `${quoteIdent(c)} = ${add(asBindParams(table, [c], set, "updateMany")[0])}`).join(", ");
1615
1663
  const whereSql = compileWhere(table, known, where, add, "updateMany");
1616
1664
  assertHasPredicate(whereSql, "updateMany", table);
1617
1665
  const sql = `UPDATE ${quoteIdent(table)} AS t SET ${assignments} WHERE true${whereSql} RETURNING *`;
@@ -2083,7 +2131,7 @@ function createOps(tx) {
2083
2131
  const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
2084
2132
  const rows = await sp.unsafe(
2085
2133
  `INSERT INTO ${quoteIdent(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`,
2086
- asBindParams(table, cols, data)
2134
+ asBindParams(table, cols, data, "supersede")
2087
2135
  );
2088
2136
  if (!rows[0]) {
2089
2137
  throw new Error(
@@ -2223,7 +2271,7 @@ function diagnosingDriver(sql, surface = "service") {
2223
2271
  );
2224
2272
  }
2225
2273
  if (isUniqueViolation(e)) {
2226
- return new UniqueViolation(constraintOf(e));
2274
+ return markEngineRaised(new UniqueViolation(constraintOf(e)));
2227
2275
  }
2228
2276
  if (surface === "service" && isForeignKeyViolation(e)) {
2229
2277
  return new Error(
@@ -2295,7 +2343,7 @@ function isRef(v) {
2295
2343
  function isExpr(v) {
2296
2344
  return typeof v === "object" && v !== null && "$expr" in v;
2297
2345
  }
2298
- function renderValue(value, column, args, results, vectorCols) {
2346
+ function renderValue(value, column, args, results, vectorCols, transforms) {
2299
2347
  if (isRef(value)) {
2300
2348
  const source = results[value.$ref.op];
2301
2349
  const row = source?.rows[0];
@@ -2304,15 +2352,15 @@ function renderValue(value, column, args, results, vectorCols) {
2304
2352
  error_code: "tx_ref_unresolved"
2305
2353
  });
2306
2354
  }
2307
- return bindMaybeVector(args, column, vectorCols, row[value.$ref.field]);
2355
+ return bindMaybeVector(args, column, vectorCols, row[value.$ref.field], transforms);
2308
2356
  }
2309
2357
  if (isExpr(value)) {
2310
2358
  const fn = value.$expr;
2311
2359
  if (fn.fn === "now") return "now()";
2312
2360
  const operator = fn.fn === "inc" ? "+" : "-";
2313
- return `${quoteIdent(column)} ${operator} ${bindMaybeVector(args, column, vectorCols, fn.by)}`;
2361
+ return `${quoteIdent(column)} ${operator} ${bindMaybeVector(args, column, vectorCols, fn.by, transforms)}`;
2314
2362
  }
2315
- return bindMaybeVector(args, column, vectorCols, value);
2363
+ return bindMaybeVector(args, column, vectorCols, value, transforms);
2316
2364
  }
2317
2365
  function renderWhere(where, args, results) {
2318
2366
  const cols = Object.keys(where ?? {});
@@ -2324,21 +2372,26 @@ function renderWhere(where, args, results) {
2324
2372
  });
2325
2373
  return ` WHERE ${terms.join(" AND ")}`;
2326
2374
  }
2327
- function bindMaybeVector(args, column, vectorCols, value) {
2375
+ function bindMaybeVector(args, column, vectorCols, value, transforms) {
2328
2376
  if (column !== void 0 && vectorCols?.has(column) && Array.isArray(value)) {
2329
2377
  return args.bind(toVectorLiteral(value));
2330
2378
  }
2379
+ const t = column !== void 0 ? transforms?.get(column) : void 0;
2380
+ if (t?.toDb !== void 0 && value !== null && value !== void 0) {
2381
+ return args.bind(t.toDb(value));
2382
+ }
2331
2383
  return args.bind(value);
2332
2384
  }
2333
2385
  async function runPlanOp(sp, op, results) {
2334
2386
  const opVectorCols = vectorColumnsOf(currentSchema, op.table);
2387
+ const opTransforms = transformsOf(currentSchema, op.table);
2335
2388
  const args = new Args();
2336
2389
  const table = quoteIdent(op.table);
2337
2390
  let sql;
2338
2391
  switch (op.op) {
2339
2392
  case "insert": {
2340
2393
  const cols = Object.keys(op.values ?? {});
2341
- const rendered = cols.map((c) => renderValue(op.values[c], c, args, results, opVectorCols));
2394
+ const rendered = cols.map((c) => renderValue(op.values[c], c, args, results, opVectorCols, opTransforms));
2342
2395
  sql = cols.length ? `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) RETURNING *` : `INSERT INTO ${table} DEFAULT VALUES RETURNING *`;
2343
2396
  break;
2344
2397
  }
@@ -2351,7 +2404,7 @@ async function runPlanOp(sp, op, results) {
2351
2404
  });
2352
2405
  }
2353
2406
  const rendered = cols.map(
2354
- (c) => renderValue(op.values[c], c, args, results, opVectorCols)
2407
+ (c) => renderValue(op.values[c], c, args, results, opVectorCols, opTransforms)
2355
2408
  );
2356
2409
  sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) ${onConflictTail(cols, conflict, "update")} RETURNING *`;
2357
2410
  break;
@@ -2361,7 +2414,7 @@ async function runPlanOp(sp, op, results) {
2361
2414
  if (rows.length === 0 || !rows[0]) return [];
2362
2415
  const cols = Object.keys(rows[0]);
2363
2416
  const tuples = rows.map(
2364
- (r) => `(${cols.map((c) => renderValue(r[c], c, args, results, opVectorCols)).join(", ")})`
2417
+ (r) => `(${cols.map((c) => renderValue(r[c], c, args, results, opVectorCols, opTransforms)).join(", ")})`
2365
2418
  );
2366
2419
  const conflictCols = op.onConflict ?? [];
2367
2420
  const tail = op.action !== void 0 && conflictCols.length > 0 ? ` ${onConflictTail(cols, conflictCols, op.action)}` : "";
@@ -2372,7 +2425,7 @@ async function runPlanOp(sp, op, results) {
2372
2425
  const cols = Object.keys(op.set ?? {});
2373
2426
  if (cols.length === 0) throw new Error(`update ${op.table}: nothing to set`);
2374
2427
  const assignments = cols.map(
2375
- (c) => `${quoteIdent(c)} = ${renderValue(op.set[c], c, args, results, opVectorCols)}`
2428
+ (c) => `${quoteIdent(c)} = ${renderValue(op.set[c], c, args, results, opVectorCols, opTransforms)}`
2376
2429
  );
2377
2430
  sql = `UPDATE ${table} SET ${assignments.join(", ")}${renderWhere(op.where, args, results)} RETURNING *`;
2378
2431
  break;
@@ -2674,6 +2727,7 @@ async function defaultSqlDriver(config) {
2674
2727
  }
2675
2728
  return new g.Bun.SQL({ url: config.databaseUrl, max: config.poolMax });
2676
2729
  }
2730
+ var AUTHOR_ANSWERED = /* @__PURE__ */ new Set([400, 401, 403, 404, 409, 429]);
2677
2731
  async function createApp(opts) {
2678
2732
  const { config, controllers } = opts;
2679
2733
  setSchema(opts.schema ?? {});
@@ -3059,6 +3113,9 @@ async function createApp(opts) {
3059
3113
  } catch (err) {
3060
3114
  await db.rollback(err);
3061
3115
  if (isHttpError(err)) {
3116
+ if (isEngineRaised(err) || !AUTHOR_ANSWERED.has(err.status)) {
3117
+ log.error(`[engine] unhandled ${err.error} in ${hit.entry.id}`, err);
3118
+ }
3062
3119
  return envelope(
3063
3120
  err.error,
3064
3121
  err.errorDescription,