@palbase/backend 24.3.0 → 25.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 (61) hide show
  1. package/dist/bin/palbase-backend.cjs +101 -60
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +17 -13
  4. package/dist/bin/palbase-backend.js.map +1 -1
  5. package/dist/{chunk-EIXCY4SS.js → chunk-43A3KGWL.js} +80 -49
  6. package/dist/chunk-43A3KGWL.js.map +1 -0
  7. package/dist/{chunk-ERDL5VAE.js → chunk-5CMLOAEF.js} +2 -2
  8. package/dist/chunk-OEQBHE2Z.js +825 -0
  9. package/dist/chunk-OEQBHE2Z.js.map +1 -0
  10. package/dist/{chunk-7Z6MGMXQ.js → chunk-XJ2RSHEU.js} +11 -5
  11. package/dist/chunk-XJ2RSHEU.js.map +1 -0
  12. package/dist/{chunk-UWSYTUGM.js → chunk-ZQRWW37O.js} +44 -1
  13. package/dist/chunk-ZQRWW37O.js.map +1 -0
  14. package/dist/db/env.cjs.map +1 -1
  15. package/dist/db/env.d.cts +29 -13
  16. package/dist/db/env.d.ts +29 -13
  17. package/dist/db/index.cjs +212 -111
  18. package/dist/db/index.cjs.map +1 -1
  19. package/dist/db/index.d.cts +1 -1
  20. package/dist/db/index.d.ts +1 -1
  21. package/dist/db/index.js +11 -1
  22. package/dist/engine/index.cjs +87 -50
  23. package/dist/engine/index.cjs.map +1 -1
  24. package/dist/engine/index.d.cts +2 -2
  25. package/dist/engine/index.d.ts +2 -2
  26. package/dist/engine/index.js +3 -3
  27. package/dist/{index-DEneI8Mn.d.ts → index-BF1f0DfA.d.ts} +5 -2
  28. package/dist/{index-C-ALG22n.d.cts → index-CoaDN9dL.d.cts} +5 -2
  29. package/dist/{index-BTMYod_l.d.ts → index-Ct1iiB4N.d.ts} +203 -61
  30. package/dist/{index-BLAbr9ZH.d.cts → index-CwaWRhyc.d.cts} +203 -61
  31. package/dist/index.cjs +550 -297
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +122 -20
  34. package/dist/index.d.ts +122 -20
  35. package/dist/index.js +164 -217
  36. package/dist/index.js.map +1 -1
  37. package/dist/openapi/index.cjs +100 -36
  38. package/dist/openapi/index.cjs.map +1 -1
  39. package/dist/openapi/index.js +59 -2
  40. package/dist/openapi/index.js.map +1 -1
  41. package/docs/README.md +64 -31
  42. package/docs/endpoints.md +25 -28
  43. package/docs/llms-full.txt +399 -153
  44. package/docs/schema.md +272 -91
  45. package/docs/services.md +39 -4
  46. package/package.json +1 -1
  47. package/template/AGENTS.md +119 -314
  48. package/template/CLAUDE.md +13 -0
  49. package/template/controllers/notes.controller.ts +6 -13
  50. package/template/db/public.ts +38 -0
  51. package/template/models/notes/create.ts +38 -0
  52. package/template/package.json +6 -3
  53. package/template/services/note.service.test.ts +45 -0
  54. package/template/services/note.service.ts +2 -2
  55. package/dist/chunk-7Z6MGMXQ.js.map +0 -1
  56. package/dist/chunk-D5CQES25.js +0 -556
  57. package/dist/chunk-D5CQES25.js.map +0 -1
  58. package/dist/chunk-EIXCY4SS.js.map +0 -1
  59. package/dist/chunk-UWSYTUGM.js.map +0 -1
  60. package/template/db/schema.ts +0 -35
  61. /package/dist/{chunk-ERDL5VAE.js.map → chunk-5CMLOAEF.js.map} +0 -0
@@ -478,13 +478,13 @@ function makeServiceProxy(key) {
478
478
  };
479
479
  return new Proxy({}, handler);
480
480
  }
481
- function makeTablesAccessor(ops) {
482
- const tablesProxy = new Proxy(
481
+ function makeTableProxy(ops, prefix) {
482
+ return new Proxy(
483
483
  {},
484
484
  {
485
485
  get(_t, prop) {
486
486
  if (typeof prop !== "string") return void 0;
487
- const name = prop;
487
+ const name = `${prefix}${prop}`;
488
488
  return {
489
489
  insert: (data) => ops().insert(name, data),
490
490
  update: (id, data) => ops().update(name, id, data),
@@ -501,7 +501,12 @@ function makeTablesAccessor(ops) {
501
501
  }
502
502
  }
503
503
  );
504
- return tablesProxy;
504
+ }
505
+ function makeTablesAccessor(ops) {
506
+ return makeTableProxy(ops, "");
507
+ }
508
+ function makeSchemaAccessor(ops, schema) {
509
+ return { tables: makeTableProxy(ops, `${String(schema)}.`) };
505
510
  }
506
511
  var rawDatabase = makeServiceProxy("Database");
507
512
  function makeTypedSurface(raw) {
@@ -528,6 +533,7 @@ function makeTypedSurface(raw) {
528
533
  // as one on the request's, and each is bound to its own connection.
529
534
  attempt: (fn) => raw.attempt(fn),
530
535
  tables: makeTablesAccessor(() => reco),
536
+ schema: (name) => makeSchemaAccessor(() => reco, name),
531
537
  transaction(fn) {
532
538
  const builder = new TxPlanBuilder();
533
539
  return runTxPlan(raw, makeTxTablesAccessor(builder), builder, fn);
@@ -1091,6 +1097,11 @@ function assertUsableWriteValues(caller, table, cols, data) {
1091
1097
  function quoteIdent(name) {
1092
1098
  return `"${name.replace(/"/g, '""')}"`;
1093
1099
  }
1100
+ function quoteTable(key) {
1101
+ const dot = key.indexOf(".");
1102
+ if (dot === -1) return quoteIdent(key);
1103
+ return `${quoteIdent(key.slice(0, dot))}.${quoteIdent(key.slice(dot + 1))}`;
1104
+ }
1094
1105
  function encodePgArray(value) {
1095
1106
  const element = (x) => {
1096
1107
  if (x === null || x === void 0) return "NULL";
@@ -1163,8 +1174,8 @@ function toVectorLiteral(v) {
1163
1174
  }
1164
1175
  function vectorColumnsOf(schema, table) {
1165
1176
  const out = /* @__PURE__ */ new Set();
1166
- for (const [key, def] of Object.entries(schema.tables ?? {})) {
1167
- if ((def.name ?? key) !== table) continue;
1177
+ const def = tableDefOf(table, schema);
1178
+ if (def) {
1168
1179
  for (const [col, c] of Object.entries(def.columns ?? {})) {
1169
1180
  const d = c !== null && typeof c === "object" && "_def" in c ? c._def : c;
1170
1181
  if (d !== null && typeof d === "object" && d.type === "vector") out.add(col);
@@ -1172,6 +1183,9 @@ function vectorColumnsOf(schema, table) {
1172
1183
  }
1173
1184
  return out;
1174
1185
  }
1186
+ function tableDefOf(key, schema = currentSchema) {
1187
+ return schema.tables?.[key] ?? null;
1188
+ }
1175
1189
  function reviveVectors(row, vectorCols) {
1176
1190
  if (row === null || typeof row !== "object" || vectorCols.size === 0) return row;
1177
1191
  const out = row;
@@ -1183,8 +1197,8 @@ function reviveVectors(row, vectorCols) {
1183
1197
  }
1184
1198
  function transformsOf(schema, table) {
1185
1199
  const out = /* @__PURE__ */ new Map();
1186
- for (const [key, def] of Object.entries(schema.tables ?? {})) {
1187
- if ((def.name ?? key) !== table) continue;
1200
+ const def = tableDefOf(table, schema);
1201
+ if (def) {
1188
1202
  for (const [col, c] of Object.entries(def.columns ?? {})) {
1189
1203
  const d = c !== null && typeof c === "object" && "_def" in c ? c._def : c;
1190
1204
  const t = d?.transform;
@@ -1233,7 +1247,7 @@ var METRIC_OPERATOR = {
1233
1247
  inner_product: "<#>"
1234
1248
  };
1235
1249
  function searchConfigFor(table) {
1236
- const t = currentSchema.tables?.[table];
1250
+ const t = tableDefOf(table);
1237
1251
  if (!t) return null;
1238
1252
  const columns = t.columns ?? {};
1239
1253
  const defOf = (c) => c !== null && typeof c === "object" && "_def" in c ? c._def : c ?? {};
@@ -1370,7 +1384,7 @@ async function fetchFacets(live, table, colSet, facets, where, extraWhere = () =
1370
1384
  };
1371
1385
  const whereSql = compileWhere(table, colSet, where, add) + extraWhere(add);
1372
1386
  const parts = facets.map(
1373
- (col) => `(SELECT '${col.replace(/'/g, "''")}' AS f, t.${quoteIdent(col)}::text AS v, count(*) AS n FROM ${quoteIdent(table)} t WHERE true${whereSql} GROUP BY 2 ORDER BY 3 DESC LIMIT 20)`
1387
+ (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)`
1374
1388
  );
1375
1389
  const rows = await live.unsafe(parts.join(" UNION ALL "), bind);
1376
1390
  const out = {};
@@ -1396,7 +1410,7 @@ function excludeSql(pk, ids, add) {
1396
1410
  return ` AND t.${quoteIdent(pk)}::text <> ALL(ARRAY[${ids.map((x) => add(x)).join(", ")}])`;
1397
1411
  }
1398
1412
  function schemaColumns(table) {
1399
- const t = Object.values(currentSchema.tables ?? {}).find((x) => x?.name === table);
1413
+ const t = tableDefOf(table);
1400
1414
  const cols = t?.columns ? Object.keys(t.columns) : [];
1401
1415
  return cols.length > 0 ? new Set(cols) : null;
1402
1416
  }
@@ -1557,7 +1571,7 @@ function createOps(tx) {
1557
1571
  const cols = Object.keys(data);
1558
1572
  if (cols.length === 0) throw new Error(`insert into ${table}: no columns given`);
1559
1573
  const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
1560
- const sql = `INSERT INTO ${quoteIdent(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`;
1574
+ const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`;
1561
1575
  const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "insert"));
1562
1576
  const inserted = rows[0];
1563
1577
  if (!inserted) {
@@ -1590,7 +1604,7 @@ function createOps(tx) {
1590
1604
  if (cols.length === 0) throw new Error(`upsert into ${table}: no columns given`);
1591
1605
  if (searchConfigFor(table)?.validity) {
1592
1606
  const ph = cols.map((_, i) => `$${i + 1}`).join(", ");
1593
- 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 *`;
1607
+ 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 *`;
1594
1608
  const live = await at();
1595
1609
  const bindData = asBindParams(table, cols, data, "upsert");
1596
1610
  const first = await live.unsafe(insertSql, bindData);
@@ -1598,7 +1612,7 @@ function createOps(tx) {
1598
1612
  const condSql = conflict.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ");
1599
1613
  const condBind = conflict.map((c) => data[c]);
1600
1614
  const cur = await live.unsafe(
1601
- `SELECT * FROM ${quoteIdent(table)} WHERE ${condSql} AND "valid_to" IS NULL`,
1615
+ `SELECT * FROM ${quoteTable(table)} WHERE ${condSql} AND "valid_to" IS NULL`,
1602
1616
  condBind
1603
1617
  );
1604
1618
  const old = cur[0];
@@ -1607,7 +1621,7 @@ function createOps(tx) {
1607
1621
  }
1608
1622
  const pk = Object.keys(old).includes("id") ? "id" : Object.keys(old)[0];
1609
1623
  await live.unsafe(
1610
- `UPDATE ${quoteIdent(table)} SET "valid_to" = now() WHERE ${quoteIdent(pk)} = $1 AND "valid_to" IS NULL`,
1624
+ `UPDATE ${quoteTable(table)} SET "valid_to" = now() WHERE ${quoteIdent(pk)} = $1 AND "valid_to" IS NULL`,
1611
1625
  [old[pk]]
1612
1626
  );
1613
1627
  const second = await live.unsafe(insertSql, bindData);
@@ -1616,7 +1630,7 @@ function createOps(tx) {
1616
1630
  }
1617
1631
  const fresh = second[0];
1618
1632
  await live.unsafe(
1619
- `UPDATE ${quoteIdent(table)} SET "superseded_by" = $2 WHERE ${quoteIdent(pk)} = $1`,
1633
+ `UPDATE ${quoteTable(table)} SET "superseded_by" = $2 WHERE ${quoteIdent(pk)} = $1`,
1620
1634
  [old[pk], fresh[pk]]
1621
1635
  );
1622
1636
  return asTableRow(table, second[0]);
@@ -1625,7 +1639,7 @@ function createOps(tx) {
1625
1639
  const conflictSet = new Set(conflict);
1626
1640
  const assignments = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
1627
1641
  const action = assignments.length ? `DO UPDATE SET ${assignments.join(", ")}` : `DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
1628
- const sql = `INSERT INTO ${quoteIdent(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
1642
+ const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
1629
1643
  const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data, "upsert"));
1630
1644
  const row = rows[0];
1631
1645
  if (!row) {
@@ -1639,12 +1653,12 @@ function createOps(tx) {
1639
1653
  const cols = Object.keys(data);
1640
1654
  if (cols.length === 0) return ops.findById(table, id);
1641
1655
  const assignments = cols.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(", ");
1642
- const sql = `UPDATE ${quoteIdent(table)} SET ${assignments} WHERE id = $${cols.length + 1} RETURNING *`;
1656
+ const sql = `UPDATE ${quoteTable(table)} SET ${assignments} WHERE id = $${cols.length + 1} RETURNING *`;
1643
1657
  const rows = await (await at()).unsafe(sql, [...asBindParams(table, cols, data, "update"), id]);
1644
1658
  return rows[0] ? asTableRow(table, rows[0]) : null;
1645
1659
  },
1646
1660
  async delete(table, id) {
1647
- await (await at()).unsafe(`DELETE FROM ${quoteIdent(table)} WHERE id = $1`, [id]);
1661
+ await (await at()).unsafe(`DELETE FROM ${quoteTable(table)} WHERE id = $1`, [id]);
1648
1662
  },
1649
1663
  /**
1650
1664
  * Update every row the filter matches, in ONE statement.
@@ -1687,7 +1701,7 @@ function createOps(tx) {
1687
1701
  const assignments = cols.map((c) => `${quoteIdent(c)} = ${add(asBindParams(table, [c], set, "updateMany")[0])}`).join(", ");
1688
1702
  const whereSql = compileWhere(table, known, where, add, "updateMany");
1689
1703
  assertHasPredicate(whereSql, "updateMany", table);
1690
- const sql = `UPDATE ${quoteIdent(table)} AS t SET ${assignments} WHERE true${whereSql} RETURNING *`;
1704
+ const sql = `UPDATE ${quoteTable(table)} AS t SET ${assignments} WHERE true${whereSql} RETURNING *`;
1691
1705
  return asTableRows(table, await (await at()).unsafe(sql, params));
1692
1706
  },
1693
1707
  /**
@@ -1704,7 +1718,7 @@ function createOps(tx) {
1704
1718
  const known = schemaColumns(table);
1705
1719
  const whereSql = compileWhere(table, known, where, add, "deleteMany");
1706
1720
  assertHasPredicate(whereSql, "deleteMany", table);
1707
- const sql = `DELETE FROM ${quoteIdent(table)} AS t WHERE true${whereSql} RETURNING 1`;
1721
+ const sql = `DELETE FROM ${quoteTable(table)} AS t WHERE true${whereSql} RETURNING 1`;
1708
1722
  const rows = await (await at()).unsafe(sql, params);
1709
1723
  return rows.length;
1710
1724
  },
@@ -1725,13 +1739,13 @@ function createOps(tx) {
1725
1739
  };
1726
1740
  const known = schemaColumns(table);
1727
1741
  const whereSql = compileWhere(table, known, where, add, "count");
1728
- const sql = `SELECT count(*) AS n FROM ${quoteIdent(table)} t WHERE true${whereSql}`;
1742
+ const sql = `SELECT count(*) AS n FROM ${quoteTable(table)} t WHERE true${whereSql}`;
1729
1743
  const rows = await (await at()).unsafe(sql, params);
1730
1744
  return Number(rows[0]?.n ?? 0);
1731
1745
  },
1732
1746
  async findById(table, id) {
1733
1747
  const rows = await (await at()).unsafe(
1734
- `SELECT * FROM ${quoteIdent(table)} WHERE id = $1`,
1748
+ `SELECT * FROM ${quoteTable(table)} WHERE id = $1`,
1735
1749
  [id]
1736
1750
  );
1737
1751
  return rows[0] ? asTableRow(table, rows[0]) : null;
@@ -1747,7 +1761,7 @@ function createOps(tx) {
1747
1761
  const order = orderClause(table, known, opts.orderBy);
1748
1762
  const limit = limitClause(opts.limit);
1749
1763
  const offset = offsetClause(opts.offset, opts.limit);
1750
- const sql = `SELECT * FROM ${quoteIdent(table)} t WHERE true${whereSql}${order}${limit}${offset}`;
1764
+ const sql = `SELECT * FROM ${quoteTable(table)} t WHERE true${whereSql}${order}${limit}${offset}`;
1751
1765
  return asTableRows(table, await (await at()).unsafe(sql, params));
1752
1766
  },
1753
1767
  /**
@@ -1832,7 +1846,7 @@ function createOps(tx) {
1832
1846
  };
1833
1847
  const userWhereC = compileWhere(table, cfg.colSet, params.where ?? {}, addC);
1834
1848
  const whereC = userWhereC + excludeSql(cfg.pk, excl, addC) + validityFor(addC);
1835
- const parentJoinC = ` JOIN ${quoteIdent(table)} t ON t.${quoteIdent(cfg.pk)} = c.${pidQ} WHERE true${whereC} AND `;
1849
+ const parentJoinC = ` JOIN ${quoteTable(table)} t ON t.${quoteIdent(cfg.pk)} = c.${pidQ} WHERE true${whereC} AND `;
1836
1850
  const liveC = await at();
1837
1851
  const K2 = 50;
1838
1852
  const cpool = Math.max(limit * 9, 90);
@@ -1876,7 +1890,7 @@ function createOps(tx) {
1876
1890
  } else {
1877
1891
  fusedSrc = `WITH kw AS (${kwIn}), fused AS (SELECT kw.pid, kw.chunk_seq, (1.0/(${K2} + kw.r))::float8 AS cscore FROM kw)`;
1878
1892
  }
1879
- 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 ${quoteIdent(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 ${quoteIdent(table)} t ON t.${quoteIdent(cfg.pk)} = g.pid`;
1893
+ 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 ${quoteIdent(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`;
1880
1894
  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}`;
1881
1895
  };
1882
1896
  let rowsC = await liveC.unsafe(assembleC(kwC, kwC !== ""), bindC);
@@ -1933,7 +1947,7 @@ function createOps(tx) {
1933
1947
  let exactOrder = "";
1934
1948
  if (userWhere !== "") {
1935
1949
  const probeRows = await live.unsafe(
1936
- `SELECT count(*)::int AS n FROM (SELECT 1 FROM ${quoteIdent(table)} t WHERE t.${quoteIdent(leg.column)} IS NOT NULL${whereSql} LIMIT ${SELECTIVITY_EXACT_THRESHOLD + 1}) s`,
1950
+ `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`,
1937
1951
  bind.slice()
1938
1952
  );
1939
1953
  const n = probeRows?.[0]?.n;
@@ -1942,7 +1956,7 @@ function createOps(tx) {
1942
1956
  }
1943
1957
  }
1944
1958
  const vp = add(toVectorLiteral(qv));
1945
- semSql = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY (t.${quoteIdent(leg.column)} OPERATOR(${quoteIdent(sch)}.${op}) ${vp}::${quoteIdent(sch)}.vector)${exactOrder}) AS r FROM ${quoteIdent(table)} t WHERE t.${quoteIdent(leg.column)} IS NOT NULL${whereSql} ORDER BY r LIMIT ${pool}`;
1959
+ semSql = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY (t.${quoteIdent(leg.column)} OPERATOR(${quoteIdent(sch)}.${op}) ${vp}::${quoteIdent(sch)}.vector)${exactOrder}) AS r FROM ${quoteTable(table)} t WHERE t.${quoteIdent(leg.column)} IS NOT NULL${whereSql} ORDER BY r LIMIT ${pool}`;
1946
1960
  }
1947
1961
  let qpRef = "";
1948
1962
  let qpIdx = -1;
@@ -1951,7 +1965,7 @@ function createOps(tx) {
1951
1965
  const qp = add(qText);
1952
1966
  qpIdx = bind.length - 1;
1953
1967
  qpRef = qp;
1954
- kwSql = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(t.palbase_fts, websearch_to_tsquery('simple', ${qp})) DESC) AS r FROM ${quoteIdent(table)} t WHERE t.palbase_fts @@ websearch_to_tsquery('simple', ${qp})${whereSql} ORDER BY r LIMIT ${pool}`;
1968
+ kwSql = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(t.palbase_fts, websearch_to_tsquery('simple', ${qp})) DESC) AS r FROM ${quoteTable(table)} t WHERE t.palbase_fts @@ websearch_to_tsquery('simple', ${qp})${whereSql} ORDER BY r LIMIT ${pool}`;
1955
1969
  }
1956
1970
  const colList = cfg.cols.map((c) => `t.${quoteIdent(c)}`).join(", ");
1957
1971
  const msP = params.minScore === void 0 ? "" : add(params.minScore);
@@ -1962,14 +1976,14 @@ function createOps(tx) {
1962
1976
  let orderScore;
1963
1977
  if (semSql !== "" && kwIn !== "") {
1964
1978
  const score = scoreMul === "" ? "fused._score" : `(fused._score)${scoreMul}`;
1965
- inner = `WITH sem AS (${semSql}), kw AS (${kwIn}), fused AS (SELECT COALESCE(sem.id, kw.id) AS id, (COALESCE(1.0/(${K} + sem.r), 0) + COALESCE(1.0/(${K} + kw.r), 0))::float8 AS _score FROM sem FULL OUTER JOIN kw ON sem.id = kw.id) SELECT ${colList}, ${score} AS _score${hlCol}${kwnCol} FROM fused JOIN ${quoteIdent(table)} t ON t.${quoteIdent(cfg.pk)} = fused.id`;
1979
+ inner = `WITH sem AS (${semSql}), kw AS (${kwIn}), fused AS (SELECT COALESCE(sem.id, kw.id) AS id, (COALESCE(1.0/(${K} + sem.r), 0) + COALESCE(1.0/(${K} + kw.r), 0))::float8 AS _score FROM sem FULL OUTER JOIN kw ON sem.id = kw.id) SELECT ${colList}, ${score} AS _score${hlCol}${kwnCol} FROM fused JOIN ${quoteTable(table)} t ON t.${quoteIdent(cfg.pk)} = fused.id`;
1966
1980
  orderScore = scoreMul === "" ? "fused._score" : "_score";
1967
1981
  } else {
1968
1982
  const single = semSql !== "" ? `sem AS (${semSql})` : `kw AS (${kwIn})`;
1969
1983
  const alias = semSql !== "" ? "sem" : "kw";
1970
1984
  const base = `(1.0/(${K} + ${alias}.r))::float8`;
1971
1985
  const score = scoreMul === "" ? base : `(${base})${scoreMul}`;
1972
- inner = `WITH ${single} SELECT ${colList}, ${score} AS _score${hlCol}${semSql !== "" ? "" : kwnCol} FROM ${alias} JOIN ${quoteIdent(table)} t ON t.${quoteIdent(cfg.pk)} = ${alias}.id`;
1986
+ inner = `WITH ${single} SELECT ${colList}, ${score} AS _score${hlCol}${semSql !== "" ? "" : kwnCol} FROM ${alias} JOIN ${quoteTable(table)} t ON t.${quoteIdent(cfg.pk)} = ${alias}.id`;
1973
1987
  orderScore = "_score";
1974
1988
  }
1975
1989
  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}`;
@@ -1978,7 +1992,7 @@ function createOps(tx) {
1978
1992
  if (wantText && kwSql !== "" && (rows.length === 0 || rows[0]._kwn === 0)) {
1979
1993
  const tsch = await trgmSchemaOf(live);
1980
1994
  const expr = ftsExprOf(cfg.ftsCols);
1981
- const trgmKw = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY ${quoteIdent(tsch)}.word_similarity(${qpRef}, ${expr}) DESC) AS r FROM ${quoteIdent(table)} t WHERE ${quoteIdent(tsch)}.word_similarity(${qpRef}, ${expr}) > 0.3${whereSql} ORDER BY r LIMIT ${pool}`;
1995
+ const trgmKw = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY ${quoteIdent(tsch)}.word_similarity(${qpRef}, ${expr}) DESC) AS r FROM ${quoteTable(table)} t WHERE ${quoteIdent(tsch)}.word_similarity(${qpRef}, ${expr}) > 0.3${whereSql} ORDER BY r LIMIT ${pool}`;
1982
1996
  const retryBind = bind.slice();
1983
1997
  if (qpIdx >= 0) retryBind[qpIdx] = params.query;
1984
1998
  rows = await live.unsafe(assemble(trgmKw, false), retryBind);
@@ -2040,7 +2054,7 @@ function createOps(tx) {
2040
2054
  throw new Error(`similar(${table}): tabloda vekt\xF6r kolonu yok \u2014 benzerlik semantik vekt\xF6r ister (FR-022)`);
2041
2055
  }
2042
2056
  const rows = await live.unsafe(
2043
- `SELECT t.${quoteIdent(leg.column)} AS v FROM ${quoteIdent(table)} t WHERE t.${quoteIdent(cfg.pk)} = $1`,
2057
+ `SELECT t.${quoteIdent(leg.column)} AS v FROM ${quoteTable(table)} t WHERE t.${quoteIdent(cfg.pk)} = $1`,
2044
2058
  [id]
2045
2059
  );
2046
2060
  v = rows?.[0]?.v;
@@ -2097,7 +2111,7 @@ function createOps(tx) {
2097
2111
  };
2098
2112
  const avgSel = (ids) => {
2099
2113
  const inList = ids.map((x) => add(x)).join(", ");
2100
- return cfg.chunk !== void 0 ? `SELECT ${quoteIdent(sch)}.avg(c.embedding) AS v FROM ${quoteIdent(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 ${quoteIdent(table)} t WHERE t.${quoteIdent(cfg.pk)} IN (${inList}) AND t.${quoteIdent(leg.column)} IS NOT NULL`;
2114
+ return cfg.chunk !== void 0 ? `SELECT ${quoteIdent(sch)}.avg(c.embedding) AS v FROM ${quoteIdent(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`;
2101
2115
  };
2102
2116
  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`;
2103
2117
  const rows = await live.unsafe(targetSql, bind);
@@ -2145,7 +2159,7 @@ function createOps(tx) {
2145
2159
  return live.savepoint(async (sp) => {
2146
2160
  const newId = row[cfg.pk] ?? crypto.randomUUID();
2147
2161
  const closed = await sp.unsafe(
2148
- `UPDATE ${quoteIdent(table)} SET "valid_to" = now(), "superseded_by" = $1 WHERE ${quoteIdent(cfg.pk)} = $2 AND "valid_to" IS NULL RETURNING ${quoteIdent(cfg.pk)}`,
2162
+ `UPDATE ${quoteTable(table)} SET "valid_to" = now(), "superseded_by" = $1 WHERE ${quoteIdent(cfg.pk)} = $2 AND "valid_to" IS NULL RETURNING ${quoteIdent(cfg.pk)}`,
2149
2163
  [newId, id]
2150
2164
  );
2151
2165
  if (closed.length === 0) {
@@ -2155,7 +2169,7 @@ function createOps(tx) {
2155
2169
  const cols = Object.keys(data);
2156
2170
  const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
2157
2171
  const rows = await sp.unsafe(
2158
- `INSERT INTO ${quoteIdent(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`,
2172
+ `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`,
2159
2173
  asBindParams(table, cols, data, "supersede")
2160
2174
  );
2161
2175
  if (!rows[0]) {
@@ -2227,27 +2241,50 @@ function createOps(tx) {
2227
2241
  return ops;
2228
2242
  }
2229
2243
  var currentSchema = {};
2230
- function setSchema(schema) {
2244
+ function setSchema(schemas) {
2231
2245
  cachedVectorSchema = null;
2232
2246
  cachedTrgmSchema = null;
2233
- const s = schema;
2234
- currentSchema = (s && "default" in s ? s.default : s) ?? {};
2247
+ const tables = {};
2248
+ for (const entry of schemas) {
2249
+ const mod = entry;
2250
+ const def = (mod && "default" in mod ? mod.default : mod) ?? {};
2251
+ const schemaName = def.name ?? "public";
2252
+ for (const [key, table] of Object.entries(def.tables ?? {})) {
2253
+ tables[qualifiedTableKey(schemaName, table.name ?? key)] = table;
2254
+ }
2255
+ }
2256
+ currentSchema = { tables };
2257
+ }
2258
+ function qualifiedTableKey(schema, table) {
2259
+ return schema === "" || schema === "public" ? table : `${schema}.${table}`;
2235
2260
  }
2236
2261
  function withTables(ops, schema = currentSchema) {
2262
+ const bind = (key) => ({
2263
+ insert: (data) => ops.insert(key, data),
2264
+ update: (id, data) => ops.update(key, id, data),
2265
+ delete: (id) => ops.delete(key, id),
2266
+ findById: (id) => ops.findById(key, id),
2267
+ findMany: (query, opts) => ops.findMany(key, query ?? {}, opts),
2268
+ upsert: (data, opts) => ops.upsert(key, data, opts)
2269
+ });
2237
2270
  const tables = {};
2238
2271
  for (const key of Object.keys(schema.tables ?? {})) {
2239
- const name = schema.tables?.[key]?.name ?? key;
2240
- tables[key] = {
2241
- insert: (data) => ops.insert(name, data),
2242
- update: (id, data) => ops.update(name, id, data),
2243
- delete: (id) => ops.delete(name, id),
2244
- findById: (id) => ops.findById(name, id),
2245
- findMany: (query, opts) => ops.findMany(name, query ?? {}, opts),
2246
- upsert: (data, opts) => ops.upsert(name, data, opts)
2247
- };
2272
+ if (!key.includes(".")) tables[key] = bind(key);
2248
2273
  }
2274
+ const schemaOf = (name) => {
2275
+ const out = {};
2276
+ const prefix = `${name}.`;
2277
+ for (const key of Object.keys(schema.tables ?? {})) {
2278
+ if (name === "public") {
2279
+ if (!key.includes(".")) out[key] = bind(key);
2280
+ } else if (key.startsWith(prefix)) {
2281
+ out[key.slice(prefix.length)] = bind(key);
2282
+ }
2283
+ }
2284
+ return { tables: out };
2285
+ };
2249
2286
  const base = /* @__PURE__ */ Object.create(null);
2250
- return Object.assign(base, ops, { tables });
2287
+ return Object.assign(base, ops, { tables, schema: schemaOf });
2251
2288
  }
2252
2289
  var SERVICE_LOCK_TIMEOUT = "5s";
2253
2290
  function isAbortedTransaction(e) {
@@ -2755,7 +2792,7 @@ async function defaultSqlDriver(config) {
2755
2792
  var AUTHOR_ANSWERED = /* @__PURE__ */ new Set([400, 401, 403, 404, 409, 429]);
2756
2793
  async function createApp(opts) {
2757
2794
  const { config, controllers } = opts;
2758
- setSchema(opts.schema ?? {});
2795
+ setSchema(opts.schemas ?? []);
2759
2796
  setSecretReader(opts.secretReader ?? null);
2760
2797
  const routes = buildRouteTable(controllers);
2761
2798
  if (routes.length === 0) {
@@ -3234,16 +3271,20 @@ async function findControllers(root) {
3234
3271
  await walk(dir);
3235
3272
  return out.sort();
3236
3273
  }
3237
- async function importSchema(root) {
3238
- for (const candidate of ["db/schema.ts", "db/schema.js"]) {
3239
- const path = (0, import_node_path.join)(root, candidate);
3240
- try {
3241
- await (0, import_promises.stat)(path);
3242
- return (await import((0, import_node_url.pathToFileURL)(path).href)).default;
3243
- } catch {
3244
- }
3274
+ async function importSchemas(root) {
3275
+ let entries;
3276
+ try {
3277
+ entries = (await (0, import_promises.readdir)((0, import_node_path.join)(root, "db"))).sort();
3278
+ } catch {
3279
+ return [];
3245
3280
  }
3246
- return void 0;
3281
+ const out = [];
3282
+ for (const entry of entries) {
3283
+ if (!entry.endsWith(".ts") && !entry.endsWith(".js")) continue;
3284
+ if (entry.endsWith(".d.ts")) continue;
3285
+ out.push((await import((0, import_node_url.pathToFileURL)((0, import_node_path.join)(root, "db", entry)).href)).default);
3286
+ }
3287
+ return out;
3247
3288
  }
3248
3289
  async function main() {
3249
3290
  const command = process.argv[2] ?? "serve";
@@ -3309,7 +3350,7 @@ Put them in a .env beside package.json, or export them. A local stack
3309
3350
  }
3310
3351
  throw e;
3311
3352
  }
3312
- const app = await createApp({ config, controllers, schema: await importSchema(root) });
3353
+ const app = await createApp({ config, controllers, schemas: await importSchemas(root) });
3313
3354
  if (command === "routes") {
3314
3355
  for (const route of app.routes) console.log(route.id);
3315
3356
  await app.shutdown();