@palbase/backend 24.3.0 → 25.0.1
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.
- package/dist/bin/palbase-backend.cjs +103 -60
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +17 -13
- package/dist/bin/palbase-backend.js.map +1 -1
- package/dist/{chunk-EIXCY4SS.js → chunk-34I4GB7D.js} +82 -49
- package/dist/chunk-34I4GB7D.js.map +1 -0
- package/dist/{chunk-UWSYTUGM.js → chunk-35PNTIRN.js} +48 -1
- package/dist/chunk-35PNTIRN.js.map +1 -0
- package/dist/chunk-HBOJLP2Z.js +840 -0
- package/dist/chunk-HBOJLP2Z.js.map +1 -0
- package/dist/{chunk-7Z6MGMXQ.js → chunk-XJ2RSHEU.js} +11 -5
- package/dist/chunk-XJ2RSHEU.js.map +1 -0
- package/dist/{chunk-ERDL5VAE.js → chunk-YOY5DFQS.js} +2 -2
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +31 -14
- package/dist/db/env.d.ts +31 -14
- package/dist/db/index.cjs +226 -111
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +11 -1
- package/dist/engine/index.cjs +89 -50
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +2 -2
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +3 -3
- package/dist/{index-BTMYod_l.d.ts → index-B4CcpqLb.d.ts} +224 -75
- package/dist/{index-DEneI8Mn.d.ts → index-B8v6hVyU.d.ts} +5 -2
- package/dist/{index-BLAbr9ZH.d.cts → index-DmVyY6N7.d.cts} +224 -75
- package/dist/{index-C-ALG22n.d.cts → index-VsjBQ4Kw.d.cts} +5 -2
- package/dist/index.cjs +580 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -22
- package/dist/index.d.ts +125 -22
- package/dist/index.js +173 -217
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +100 -36
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.js +59 -2
- package/dist/openapi/index.js.map +1 -1
- package/docs/README.md +64 -31
- package/docs/endpoints.md +25 -28
- package/docs/llms-full.txt +430 -153
- package/docs/schema.md +303 -91
- package/docs/services.md +39 -4
- package/package.json +1 -1
- package/template/AGENTS.md +119 -314
- package/template/CLAUDE.md +13 -0
- package/template/controllers/notes.controller.ts +6 -13
- package/template/db/public.ts +38 -0
- package/template/models/notes/create.ts +38 -0
- package/template/package.json +6 -3
- package/template/services/note.service.test.ts +45 -0
- package/template/services/note.service.ts +2 -2
- package/dist/chunk-7Z6MGMXQ.js.map +0 -1
- package/dist/chunk-D5CQES25.js +0 -556
- package/dist/chunk-D5CQES25.js.map +0 -1
- package/dist/chunk-EIXCY4SS.js.map +0 -1
- package/dist/chunk-UWSYTUGM.js.map +0 -1
- package/template/db/schema.ts +0 -35
- /package/dist/{chunk-ERDL5VAE.js.map → chunk-YOY5DFQS.js.map} +0 -0
|
@@ -478,13 +478,13 @@ function makeServiceProxy(key) {
|
|
|
478
478
|
};
|
|
479
479
|
return new Proxy({}, handler);
|
|
480
480
|
}
|
|
481
|
-
function
|
|
482
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1167
|
-
|
|
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
|
-
|
|
1187
|
-
|
|
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 =
|
|
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 ${
|
|
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 =
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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(
|
|
2244
|
+
function setSchema(schemas) {
|
|
2231
2245
|
cachedVectorSchema = null;
|
|
2232
2246
|
cachedTrgmSchema = null;
|
|
2233
|
-
const
|
|
2234
|
-
|
|
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
|
-
|
|
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) {
|
|
@@ -2551,6 +2588,7 @@ function assertZeroArgConstructor(Ctrl, kind) {
|
|
|
2551
2588
|
}
|
|
2552
2589
|
|
|
2553
2590
|
// src/engine/router.ts
|
|
2591
|
+
var ROOM_META = /* @__PURE__ */ Symbol.for("palbase.backend.room");
|
|
2554
2592
|
var CONTROLLER_META = /* @__PURE__ */ Symbol.for("palbase.backend.controllerMeta");
|
|
2555
2593
|
function toSegments(path) {
|
|
2556
2594
|
return path.split("/").filter(Boolean).map((s) => s.startsWith("{") && s.endsWith("}") ? `:${s.slice(1, -1)}` : s);
|
|
@@ -2558,6 +2596,7 @@ function toSegments(path) {
|
|
|
2558
2596
|
function buildRouteTable(controllers) {
|
|
2559
2597
|
const table = [];
|
|
2560
2598
|
for (const Ctrl of controllers) {
|
|
2599
|
+
if (Ctrl[ROOM_META] !== void 0) continue;
|
|
2561
2600
|
const ctor = Ctrl;
|
|
2562
2601
|
const meta = ctor[CONTROLLER_META];
|
|
2563
2602
|
const basePath = meta?.basePath ?? "";
|
|
@@ -2755,7 +2794,7 @@ async function defaultSqlDriver(config) {
|
|
|
2755
2794
|
var AUTHOR_ANSWERED = /* @__PURE__ */ new Set([400, 401, 403, 404, 409, 429]);
|
|
2756
2795
|
async function createApp(opts) {
|
|
2757
2796
|
const { config, controllers } = opts;
|
|
2758
|
-
setSchema(opts.
|
|
2797
|
+
setSchema(opts.schemas ?? []);
|
|
2759
2798
|
setSecretReader(opts.secretReader ?? null);
|
|
2760
2799
|
const routes = buildRouteTable(controllers);
|
|
2761
2800
|
if (routes.length === 0) {
|
|
@@ -3234,16 +3273,20 @@ async function findControllers(root) {
|
|
|
3234
3273
|
await walk(dir);
|
|
3235
3274
|
return out.sort();
|
|
3236
3275
|
}
|
|
3237
|
-
async function
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
} catch {
|
|
3244
|
-
}
|
|
3276
|
+
async function importSchemas(root) {
|
|
3277
|
+
let entries;
|
|
3278
|
+
try {
|
|
3279
|
+
entries = (await (0, import_promises.readdir)((0, import_node_path.join)(root, "db"))).sort();
|
|
3280
|
+
} catch {
|
|
3281
|
+
return [];
|
|
3245
3282
|
}
|
|
3246
|
-
|
|
3283
|
+
const out = [];
|
|
3284
|
+
for (const entry of entries) {
|
|
3285
|
+
if (!entry.endsWith(".ts") && !entry.endsWith(".js")) continue;
|
|
3286
|
+
if (entry.endsWith(".d.ts")) continue;
|
|
3287
|
+
out.push((await import((0, import_node_url.pathToFileURL)((0, import_node_path.join)(root, "db", entry)).href)).default);
|
|
3288
|
+
}
|
|
3289
|
+
return out;
|
|
3247
3290
|
}
|
|
3248
3291
|
async function main() {
|
|
3249
3292
|
const command = process.argv[2] ?? "serve";
|
|
@@ -3309,7 +3352,7 @@ Put them in a .env beside package.json, or export them. A local stack
|
|
|
3309
3352
|
}
|
|
3310
3353
|
throw e;
|
|
3311
3354
|
}
|
|
3312
|
-
const app = await createApp({ config, controllers,
|
|
3355
|
+
const app = await createApp({ config, controllers, schemas: await importSchemas(root) });
|
|
3313
3356
|
if (command === "routes") {
|
|
3314
3357
|
for (const route of app.routes) console.log(route.id);
|
|
3315
3358
|
await app.shutdown();
|