@palbase/backend 23.1.0 → 24.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.
- package/dist/bin/palbase-backend.cjs +389 -44
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +4 -5
- package/dist/bin/palbase-backend.js.map +1 -1
- package/dist/{chunk-OHALWEOG.js → chunk-7Z6MGMXQ.js} +57 -2
- package/dist/chunk-7Z6MGMXQ.js.map +1 -0
- package/dist/{chunk-RCLNBJCM.js → chunk-H3JAISUY.js} +136 -1
- package/dist/chunk-H3JAISUY.js.map +1 -0
- package/dist/{chunk-M5MCBWJI.js → chunk-NXDH6VQJ.js} +264 -28
- package/dist/chunk-NXDH6VQJ.js.map +1 -0
- package/dist/{chunk-NS5V43YQ.js → chunk-P2Q27SGP.js} +19 -3
- package/dist/chunk-P2Q27SGP.js.map +1 -0
- package/dist/{chunk-R3KN6RHD.js → chunk-T5IOSOE5.js} +7 -2
- package/dist/chunk-T5IOSOE5.js.map +1 -0
- package/dist/{chunk-PY7YJDCT.js → chunk-ZUGY7RGS.js} +32 -3
- package/dist/chunk-ZUGY7RGS.js.map +1 -0
- package/dist/db/index.cjs +48 -3
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +2 -2
- package/dist/{endpoint-CVWXh6oG.d.ts → endpoint-0_DGBajf.d.ts} +100 -3
- package/dist/{endpoint-c9h5jriX.d.cts → endpoint-CcQ1a36a.d.cts} +100 -3
- package/dist/engine/index.cjs +378 -31
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +4 -4
- package/dist/engine/index.d.ts +4 -4
- package/dist/engine/index.js +4 -4
- package/dist/{index-CwAJ7HEe.d.ts → index-CJiJU9ux.d.ts} +125 -21
- package/dist/{index-By8Dle5U.d.cts → index-D-4-PNuQ.d.cts} +125 -21
- package/dist/{index-BZrJXnVh.d.ts → index-D17r-MKb.d.ts} +134 -9
- package/dist/{index-CxeQSfJP.d.cts → index-DRFxf07H.d.cts} +134 -9
- package/dist/index.cjs +166 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -12
- package/dist/index.d.ts +54 -12
- package/dist/index.js +28 -11
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +16 -1
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +6 -4
- package/dist/openapi/index.d.ts +6 -4
- package/dist/openapi/index.js +6 -7
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-CqPK2Qby.d.cts → registry-1X-skBNu.d.cts} +1 -1
- package/dist/{registry-B3niOVYp.d.ts → registry-CEod_5sz.d.ts} +1 -1
- package/dist/test/index.cjs +27 -0
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +27 -0
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +4 -4
- package/docs/database.md +115 -11
- package/docs/getting-started.md +5 -4
- package/docs/llms-full.txt +385 -89
- package/docs/migrations.md +81 -59
- package/docs/schema.md +82 -2
- package/docs/services.md +98 -9
- package/package.json +2 -2
- package/template/AGENTS.md +121 -41
- package/template/controllers/notes.controller.ts +64 -0
- package/template/package.json +1 -1
- package/template/services/note.service.ts +74 -0
- package/template/tsconfig.json +11 -1
- package/dist/chunk-HQRJDARQ.js +0 -90
- package/dist/chunk-HQRJDARQ.js.map +0 -1
- package/dist/chunk-M5MCBWJI.js.map +0 -1
- package/dist/chunk-NS5V43YQ.js.map +0 -1
- package/dist/chunk-OHALWEOG.js.map +0 -1
- package/dist/chunk-PY7YJDCT.js.map +0 -1
- package/dist/chunk-R3KN6RHD.js.map +0 -1
- package/dist/chunk-RCLNBJCM.js.map +0 -1
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__requestALS,
|
|
3
|
+
__runStartHooks,
|
|
3
4
|
__runWithRuntime
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-7Z6MGMXQ.js";
|
|
5
6
|
import {
|
|
7
|
+
UniqueViolation,
|
|
8
|
+
assertZeroArgConstructor,
|
|
6
9
|
getRoutes,
|
|
7
|
-
isHttpError
|
|
8
|
-
|
|
10
|
+
isHttpError,
|
|
11
|
+
resolveEffectiveAuth
|
|
12
|
+
} from "./chunk-H3JAISUY.js";
|
|
9
13
|
|
|
10
14
|
// src/engine/config.ts
|
|
11
15
|
var BootRefused = class extends Error {
|
|
@@ -413,18 +417,48 @@ function reviveVectors(row, vectorCols) {
|
|
|
413
417
|
}
|
|
414
418
|
return row;
|
|
415
419
|
}
|
|
420
|
+
function transformsOf(schema, table) {
|
|
421
|
+
const out = /* @__PURE__ */ new Map();
|
|
422
|
+
for (const [key, def] of Object.entries(schema.tables ?? {})) {
|
|
423
|
+
if ((def.name ?? key) !== table) continue;
|
|
424
|
+
for (const [col, c] of Object.entries(def.columns ?? {})) {
|
|
425
|
+
const d = c !== null && typeof c === "object" && "_def" in c ? c._def : c;
|
|
426
|
+
const t = d?.transform;
|
|
427
|
+
if (t !== null && typeof t === "object") {
|
|
428
|
+
out.set(col, t);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return out;
|
|
433
|
+
}
|
|
434
|
+
function applyFromDb(row, transforms) {
|
|
435
|
+
if (transforms.size === 0) return row;
|
|
436
|
+
for (const [col, t] of transforms) {
|
|
437
|
+
if (t.fromDb === void 0) continue;
|
|
438
|
+
const v = row[col];
|
|
439
|
+
if (v === null || v === void 0) continue;
|
|
440
|
+
row[col] = t.fromDb(v);
|
|
441
|
+
}
|
|
442
|
+
return row;
|
|
443
|
+
}
|
|
416
444
|
function asTableRow(table, row) {
|
|
417
|
-
|
|
445
|
+
const revived = reviveVectors(asWireRow(row), vectorColumnsOf(currentSchema, table));
|
|
446
|
+
return applyFromDb(revived, transformsOf(currentSchema, table));
|
|
418
447
|
}
|
|
419
448
|
function asTableRows(table, rows) {
|
|
420
449
|
const vectorCols = vectorColumnsOf(currentSchema, table);
|
|
421
|
-
|
|
450
|
+
const transforms = transformsOf(currentSchema, table);
|
|
451
|
+
return rows.map((row) => applyFromDb(reviveVectors(asWireRow(row), vectorCols), transforms));
|
|
422
452
|
}
|
|
423
453
|
function asBindParams(table, cols, data) {
|
|
424
454
|
const vectorCols = vectorColumnsOf(currentSchema, table);
|
|
455
|
+
const transforms = transformsOf(currentSchema, table);
|
|
425
456
|
return cols.map((c) => {
|
|
426
457
|
const v = data[c];
|
|
427
|
-
|
|
458
|
+
if (Array.isArray(v) && vectorCols.has(c)) return toVectorLiteral(v);
|
|
459
|
+
const t = transforms.get(c);
|
|
460
|
+
if (t?.toDb !== void 0 && v !== null && v !== void 0) return t.toDb(v);
|
|
461
|
+
return v;
|
|
428
462
|
});
|
|
429
463
|
}
|
|
430
464
|
var SELECTIVITY_EXACT_THRESHOLD = 1e4;
|
|
@@ -618,6 +652,20 @@ function limitClause(limit) {
|
|
|
618
652
|
}
|
|
619
653
|
return ` LIMIT ${limit}`;
|
|
620
654
|
}
|
|
655
|
+
function offsetClause(offset, limit) {
|
|
656
|
+
if (offset === void 0) return "";
|
|
657
|
+
if (!Number.isInteger(offset) || offset < 0) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
`findMany: offset bir negatif olmayan tam say\u0131 olmal\u0131 (geldi: ${String(offset)})`
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
if (limit === void 0) {
|
|
663
|
+
throw new Error(
|
|
664
|
+
"findMany: offset yaln\u0131z limit ile birlikte verilir \u2014 limitsiz offset bir sayfa de\u011Fil, s\u0131n\u0131rs\u0131z bir kuyru\u011Fun kayd\u0131r\u0131lm\u0131\u015F\u0131d\u0131r"
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
return ` OFFSET ${offset}`;
|
|
668
|
+
}
|
|
621
669
|
function compileWhere(table, colSet, where, add, caller = "search") {
|
|
622
670
|
const parts = [];
|
|
623
671
|
for (const [col, cond] of Object.entries(where)) {
|
|
@@ -646,6 +694,13 @@ function compileWhere(table, colSet, where, add, caller = "search") {
|
|
|
646
694
|
}
|
|
647
695
|
return parts.length === 0 ? "" : ` AND ${parts.join(" AND ")}`;
|
|
648
696
|
}
|
|
697
|
+
function onConflictTail(cols, conflict, action) {
|
|
698
|
+
const target = `ON CONFLICT (${conflict.map(quoteIdent).join(", ")})`;
|
|
699
|
+
if (action === "ignore") return `${target} DO NOTHING`;
|
|
700
|
+
const conflictSet = new Set(conflict);
|
|
701
|
+
const sets = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
|
|
702
|
+
return sets.length ? `${target} DO UPDATE SET ${sets.join(", ")}` : `${target} DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
|
|
703
|
+
}
|
|
649
704
|
var cachedVectorSchema = null;
|
|
650
705
|
var cachedTrgmSchema = null;
|
|
651
706
|
async function trgmSchemaOf(live) {
|
|
@@ -755,6 +810,39 @@ function createOps(tx) {
|
|
|
755
810
|
}
|
|
756
811
|
const cols = Object.keys(data);
|
|
757
812
|
if (cols.length === 0) throw new Error(`upsert into ${table}: no columns given`);
|
|
813
|
+
if (searchConfigFor(table)?.validity) {
|
|
814
|
+
const ph = cols.map((_, i) => `$${i + 1}`).join(", ");
|
|
815
|
+
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 *`;
|
|
816
|
+
const live = await at();
|
|
817
|
+
const bindData = asBindParams(table, cols, data);
|
|
818
|
+
const first = await live.unsafe(insertSql, bindData);
|
|
819
|
+
if (first[0]) return asTableRow(table, first[0]);
|
|
820
|
+
const condSql = conflict.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ");
|
|
821
|
+
const condBind = conflict.map((c) => data[c]);
|
|
822
|
+
const cur = await live.unsafe(
|
|
823
|
+
`SELECT * FROM ${quoteIdent(table)} WHERE ${condSql} AND "valid_to" IS NULL`,
|
|
824
|
+
condBind
|
|
825
|
+
);
|
|
826
|
+
const old = cur[0];
|
|
827
|
+
if (old === void 0) {
|
|
828
|
+
throw new Error(`upsert into ${table}: e\u015Fzamanl\u0131 supersede yar\u0131\u015F\u0131 \u2014 ge\xE7erli sat\u0131r bulunamad\u0131, iste\u011Fi yineleyin (D-020)`);
|
|
829
|
+
}
|
|
830
|
+
const pk = Object.keys(old).includes("id") ? "id" : Object.keys(old)[0];
|
|
831
|
+
await live.unsafe(
|
|
832
|
+
`UPDATE ${quoteIdent(table)} SET "valid_to" = now() WHERE ${quoteIdent(pk)} = $1 AND "valid_to" IS NULL`,
|
|
833
|
+
[old[pk]]
|
|
834
|
+
);
|
|
835
|
+
const second = await live.unsafe(insertSql, bindData);
|
|
836
|
+
if (!second[0]) {
|
|
837
|
+
throw new Error(`upsert into ${table}: e\u015Fzamanl\u0131 supersede yar\u0131\u015F\u0131 \u2014 yeni sat\u0131r a\xE7\u0131lamad\u0131, iste\u011Fi yineleyin (D-020)`);
|
|
838
|
+
}
|
|
839
|
+
const fresh = second[0];
|
|
840
|
+
await live.unsafe(
|
|
841
|
+
`UPDATE ${quoteIdent(table)} SET "superseded_by" = $2 WHERE ${quoteIdent(pk)} = $1`,
|
|
842
|
+
[old[pk], fresh[pk]]
|
|
843
|
+
);
|
|
844
|
+
return asTableRow(table, second[0]);
|
|
845
|
+
}
|
|
758
846
|
const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
|
|
759
847
|
const conflictSet = new Set(conflict);
|
|
760
848
|
const assignments = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
|
|
@@ -780,6 +868,86 @@ function createOps(tx) {
|
|
|
780
868
|
async delete(table, id) {
|
|
781
869
|
await (await at()).unsafe(`DELETE FROM ${quoteIdent(table)} WHERE id = $1`, [id]);
|
|
782
870
|
},
|
|
871
|
+
/**
|
|
872
|
+
* Update every row the filter matches, in ONE statement.
|
|
873
|
+
*
|
|
874
|
+
* The capability was already here and only reachable from inside a
|
|
875
|
+
* transaction plan (`tx.tables.x.updateWhere`, since 11.0.0). Outside it the
|
|
876
|
+
* only door was `update(id, …)`, so "mark every unapproved row in this
|
|
877
|
+
* household" was an N+1 loop or hand-written SQL — and hand-written SQL is
|
|
878
|
+
* where the typed surface and RLS both stop helping.
|
|
879
|
+
*
|
|
880
|
+
* The filter language is `findMany`'s, compiled by the same `compileWhere`:
|
|
881
|
+
* one filter language, or the two spellings drift.
|
|
882
|
+
*
|
|
883
|
+
* AN EMPTY FILTER IS REFUSED. `UPDATE … WHERE true` is a whole-table write,
|
|
884
|
+
* and the shape that produces it by accident — a filter object built from
|
|
885
|
+
* request input that happened to come back empty — is exactly the shape that
|
|
886
|
+
* should not silently succeed. Callers who mean every row say so with a
|
|
887
|
+
* predicate that is true for every row.
|
|
888
|
+
*/
|
|
889
|
+
async updateMany(table, where, set) {
|
|
890
|
+
const cols = Object.keys(set);
|
|
891
|
+
if (cols.length === 0) return [];
|
|
892
|
+
if (Object.keys(where).length === 0) {
|
|
893
|
+
throw new Error(
|
|
894
|
+
`updateMany(${table}): bo\u015F filtre T\xDCM sat\u0131rlar\u0131 g\xFCncellerdi \u2014 bir filtre verin, ya da her sat\u0131ra uyan bir ko\u015Ful yaz\u0131n`
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
const params = [];
|
|
898
|
+
const add = (v) => {
|
|
899
|
+
params.push(v);
|
|
900
|
+
return `$${params.length}`;
|
|
901
|
+
};
|
|
902
|
+
const assignments = cols.map((c) => `${quoteIdent(c)} = ${add(asBindParams(table, [c], set)[0])}`).join(", ");
|
|
903
|
+
const known = schemaColumns(table);
|
|
904
|
+
const whereSql = compileWhere(table, known, where, add, "updateMany");
|
|
905
|
+
const sql = `UPDATE ${quoteIdent(table)} AS t SET ${assignments} WHERE true${whereSql} RETURNING *`;
|
|
906
|
+
return asTableRows(table, await (await at()).unsafe(sql, params));
|
|
907
|
+
},
|
|
908
|
+
/**
|
|
909
|
+
* Delete every row the filter matches, in ONE statement; resolves to how
|
|
910
|
+
* many went. Same filter language, same empty-filter refusal as
|
|
911
|
+
* {@link updateMany} — and here the accident is worse.
|
|
912
|
+
*/
|
|
913
|
+
async deleteMany(table, where) {
|
|
914
|
+
if (Object.keys(where).length === 0) {
|
|
915
|
+
throw new Error(
|
|
916
|
+
`deleteMany(${table}): bo\u015F filtre T\xDCM sat\u0131rlar\u0131 silerdi \u2014 bir filtre verin, ya da her sat\u0131ra uyan bir ko\u015Ful yaz\u0131n`
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
const params = [];
|
|
920
|
+
const add = (v) => {
|
|
921
|
+
params.push(v);
|
|
922
|
+
return `$${params.length}`;
|
|
923
|
+
};
|
|
924
|
+
const known = schemaColumns(table);
|
|
925
|
+
const whereSql = compileWhere(table, known, where, add, "deleteMany");
|
|
926
|
+
const sql = `DELETE FROM ${quoteIdent(table)} AS t WHERE true${whereSql} RETURNING 1`;
|
|
927
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
928
|
+
return rows.length;
|
|
929
|
+
},
|
|
930
|
+
/**
|
|
931
|
+
* How many rows match — the half of pagination `limit`/`offset` cannot
|
|
932
|
+
* supply. Without it a page count is either a guess or "fetch everything and
|
|
933
|
+
* read .length", and the second one is the scale risk this surface exists to
|
|
934
|
+
* remove.
|
|
935
|
+
*
|
|
936
|
+
* An empty filter is legitimate HERE: counting a whole table is a read, and
|
|
937
|
+
* reads do not destroy anything.
|
|
938
|
+
*/
|
|
939
|
+
async count(table, where = {}) {
|
|
940
|
+
const params = [];
|
|
941
|
+
const add = (v) => {
|
|
942
|
+
params.push(v);
|
|
943
|
+
return `$${params.length}`;
|
|
944
|
+
};
|
|
945
|
+
const known = schemaColumns(table);
|
|
946
|
+
const whereSql = compileWhere(table, known, where, add, "count");
|
|
947
|
+
const sql = `SELECT count(*) AS n FROM ${quoteIdent(table)} t WHERE true${whereSql}`;
|
|
948
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
949
|
+
return Number(rows[0]?.n ?? 0);
|
|
950
|
+
},
|
|
783
951
|
async findById(table, id) {
|
|
784
952
|
const rows = await (await at()).unsafe(
|
|
785
953
|
`SELECT * FROM ${quoteIdent(table)} WHERE id = $1`,
|
|
@@ -797,7 +965,8 @@ function createOps(tx) {
|
|
|
797
965
|
const whereSql = compileWhere(table, known, query, add, "findMany");
|
|
798
966
|
const order = orderClause(table, known, opts.orderBy);
|
|
799
967
|
const limit = limitClause(opts.limit);
|
|
800
|
-
const
|
|
968
|
+
const offset = offsetClause(opts.offset, opts.limit);
|
|
969
|
+
const sql = `SELECT * FROM ${quoteIdent(table)} t WHERE true${whereSql}${order}${limit}${offset}`;
|
|
801
970
|
return asTableRows(table, await (await at()).unsafe(sql, params));
|
|
802
971
|
},
|
|
803
972
|
/**
|
|
@@ -880,7 +1049,9 @@ function createOps(tx) {
|
|
|
880
1049
|
bindC.push(v);
|
|
881
1050
|
return `$${bindC.length}`;
|
|
882
1051
|
};
|
|
883
|
-
const
|
|
1052
|
+
const userWhereC = compileWhere(table, cfg.colSet, params.where ?? {}, addC);
|
|
1053
|
+
const whereC = userWhereC + excludeSql(cfg.pk, excl, addC) + validityFor(addC);
|
|
1054
|
+
const parentJoinC = ` JOIN ${quoteIdent(table)} t ON t.${quoteIdent(cfg.pk)} = c.${pidQ} WHERE true${whereC} AND `;
|
|
884
1055
|
const liveC = await at();
|
|
885
1056
|
const K2 = 50;
|
|
886
1057
|
const cpool = Math.max(limit * 9, 90);
|
|
@@ -889,8 +1060,19 @@ function createOps(tx) {
|
|
|
889
1060
|
if (qvC !== null) {
|
|
890
1061
|
const sch = await vectorSchemaWithGuc(liveC);
|
|
891
1062
|
const op = METRIC_OPERATOR[ck.metric] ?? METRIC_OPERATOR.cosine;
|
|
1063
|
+
let exactOrderC = "";
|
|
1064
|
+
if (userWhereC !== "") {
|
|
1065
|
+
const probeRows = await liveC.unsafe(
|
|
1066
|
+
`SELECT count(*)::int AS n FROM (SELECT 1 FROM ${quoteIdent(ck.table)} c${parentJoinC}c.embedding IS NOT NULL LIMIT ${SELECTIVITY_EXACT_THRESHOLD + 1}) s`,
|
|
1067
|
+
bindC.slice()
|
|
1068
|
+
);
|
|
1069
|
+
const n = probeRows?.[0]?.n;
|
|
1070
|
+
if (typeof n === "number" && n <= SELECTIVITY_EXACT_THRESHOLD) {
|
|
1071
|
+
exactOrderC = " + 0.0";
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
892
1074
|
const vp = addC(toVectorLiteral(qvC));
|
|
893
|
-
semC = `SELECT c.${pidQ} AS pid, c.chunk_seq, ROW_NUMBER() OVER (ORDER BY (c.embedding OPERATOR(${quoteIdent(sch)}.${op}) ${vp}::${quoteIdent(sch)}.vector)) AS r FROM ${quoteIdent(ck.table)} c
|
|
1075
|
+
semC = `SELECT c.${pidQ} AS pid, c.chunk_seq, ROW_NUMBER() OVER (ORDER BY (c.embedding OPERATOR(${quoteIdent(sch)}.${op}) ${vp}::${quoteIdent(sch)}.vector)${exactOrderC}) AS r FROM ${quoteIdent(ck.table)} c${parentJoinC}c.embedding IS NOT NULL ORDER BY r LIMIT ${cpool}`;
|
|
894
1076
|
}
|
|
895
1077
|
let qpC = "";
|
|
896
1078
|
let qpCIdx = -1;
|
|
@@ -898,7 +1080,7 @@ function createOps(tx) {
|
|
|
898
1080
|
const qTextC = cfg.synonyms !== void 0 ? expandSynonyms(params.query, cfg.synonyms) : params.query;
|
|
899
1081
|
qpC = addC(qTextC);
|
|
900
1082
|
qpCIdx = bindC.length - 1;
|
|
901
|
-
kwC = `SELECT c.${pidQ} AS pid, c.chunk_seq, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(c.palbase_fts, websearch_to_tsquery('simple', ${qpC})) DESC) AS r FROM ${quoteIdent(ck.table)} c
|
|
1083
|
+
kwC = `SELECT c.${pidQ} AS pid, c.chunk_seq, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(c.palbase_fts, websearch_to_tsquery('simple', ${qpC})) DESC) AS r FROM ${quoteIdent(ck.table)} c${parentJoinC}c.palbase_fts @@ websearch_to_tsquery('simple', ${qpC}) ORDER BY r LIMIT ${cpool}`;
|
|
902
1084
|
}
|
|
903
1085
|
const msC = params.minScore === void 0 ? "" : addC(params.minScore);
|
|
904
1086
|
const colListC = cfg.cols.map((c) => `t.${quoteIdent(c)}`).join(", ");
|
|
@@ -913,13 +1095,13 @@ function createOps(tx) {
|
|
|
913
1095
|
} else {
|
|
914
1096
|
fusedSrc = `WITH kw AS (${kwIn}), fused AS (SELECT kw.pid, kw.chunk_seq, (1.0/(${K2} + kw.r))::float8 AS cscore FROM kw)`;
|
|
915
1097
|
}
|
|
916
|
-
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
|
|
1098
|
+
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`;
|
|
917
1099
|
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}`;
|
|
918
1100
|
};
|
|
919
1101
|
let rowsC = await liveC.unsafe(assembleC(kwC, kwC !== ""), bindC);
|
|
920
1102
|
if (wantTextC && kwC !== "" && (rowsC.length === 0 || rowsC[0]._kwn === 0)) {
|
|
921
1103
|
const tsch = await trgmSchemaOf(liveC);
|
|
922
|
-
const trgmKwC = `SELECT c.${pidQ} AS pid, c.chunk_seq, ROW_NUMBER() OVER (ORDER BY ${quoteIdent(tsch)}.word_similarity(${qpC}, coalesce(c.content,'')) DESC) AS r FROM ${quoteIdent(ck.table)} c
|
|
1104
|
+
const trgmKwC = `SELECT c.${pidQ} AS pid, c.chunk_seq, ROW_NUMBER() OVER (ORDER BY ${quoteIdent(tsch)}.word_similarity(${qpC}, coalesce(c.content,'')) DESC) AS r FROM ${quoteIdent(ck.table)} c${parentJoinC}${quoteIdent(tsch)}.word_similarity(${qpC}, coalesce(c.content,'')) > 0.3 ORDER BY r LIMIT ${cpool}`;
|
|
923
1105
|
const retryBindC = bindC.slice();
|
|
924
1106
|
if (qpCIdx >= 0) retryBindC[qpCIdx] = params.query;
|
|
925
1107
|
rowsC = await liveC.unsafe(assembleC(trgmKwC, false), retryBindC);
|
|
@@ -1015,7 +1197,7 @@ function createOps(tx) {
|
|
|
1015
1197
|
if (wantText && kwSql !== "" && (rows.length === 0 || rows[0]._kwn === 0)) {
|
|
1016
1198
|
const tsch = await trgmSchemaOf(live);
|
|
1017
1199
|
const expr = ftsExprOf(cfg.ftsCols);
|
|
1018
|
-
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 ${
|
|
1200
|
+
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}`;
|
|
1019
1201
|
const retryBind = bind.slice();
|
|
1020
1202
|
if (qpIdx >= 0) retryBind[qpIdx] = params.query;
|
|
1021
1203
|
rows = await live.unsafe(assemble(trgmKw, false), retryBind);
|
|
@@ -1038,6 +1220,25 @@ function createOps(tx) {
|
|
|
1038
1220
|
* tek SQL'de "id yok" ile "0 komşu" ayrılamazdı; +1 küçük turla id-yokluğu
|
|
1039
1221
|
* adlandırılmış hataya çevrilir. Sonuç şekli search ile aynı (FR-015).
|
|
1040
1222
|
*/
|
|
1223
|
+
/** D-021 (FR-027 DX): sayaçlar BAĞIMSIZ op'la — search'ün dizi-üstü
|
|
1224
|
+
* `_facets` özelliği JSON.stringify'da kaybolur (dizi özelliği), tenant
|
|
1225
|
+
* yanıtına koyunca sessizce yok olurdu. Ayrı dönüş ciddi bir sözleşmedir;
|
|
1226
|
+
* search'teki alan geriye-uyum için DURUR. where + validity default'u
|
|
1227
|
+
* sayaçlara da uygulanır (sayaç, kullanıcının gördüğü kümeyi anlatır). */
|
|
1228
|
+
async facets(table, params) {
|
|
1229
|
+
const cfg = searchConfigFor(table);
|
|
1230
|
+
if (!cfg) {
|
|
1231
|
+
throw new Error(`facets(${table}): tablo aranabilir de\u011Fil \u2014 search beyan\u0131 yok (FR-027)`);
|
|
1232
|
+
}
|
|
1233
|
+
for (const col of params.facets) {
|
|
1234
|
+
if (!cfg.colSet.has(col)) {
|
|
1235
|
+
throw new Error(`facets(${table}): facets kolonu "${col}" tabloda yok (FR-027)`);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
const live = await at();
|
|
1239
|
+
const extra = cfg.validity ? (add) => validitySql(params.validity, add) : () => "";
|
|
1240
|
+
return fetchFacets(live, table, cfg.colSet, params.facets, params.where ?? {}, extra);
|
|
1241
|
+
},
|
|
1041
1242
|
async similar(table, id, opts = {}) {
|
|
1042
1243
|
const cfg = searchConfigFor(table);
|
|
1043
1244
|
if (!cfg) {
|
|
@@ -1269,17 +1470,35 @@ function withTables(ops, schema = currentSchema) {
|
|
|
1269
1470
|
}
|
|
1270
1471
|
var SERVICE_LOCK_TIMEOUT = "5s";
|
|
1271
1472
|
function isAbortedTransaction(e) {
|
|
1272
|
-
const code = e?.code;
|
|
1273
1473
|
const message = String(e?.message ?? "");
|
|
1274
|
-
return
|
|
1474
|
+
return sqlstateOf(e) === "25P02" || /current transaction is aborted/i.test(message);
|
|
1475
|
+
}
|
|
1476
|
+
function sqlstateOf(e) {
|
|
1477
|
+
const err = e;
|
|
1478
|
+
const isState = (v) => typeof v === "string" && /^[0-9A-Z]{5}$/.test(v);
|
|
1479
|
+
if (isState(err?.code)) return err.code;
|
|
1480
|
+
if (isState(err?.errno)) return err.errno;
|
|
1481
|
+
if (typeof err?.errno === "number") {
|
|
1482
|
+
const asText = String(err.errno);
|
|
1483
|
+
if (isState(asText)) return asText;
|
|
1484
|
+
}
|
|
1485
|
+
return void 0;
|
|
1275
1486
|
}
|
|
1276
1487
|
function isForeignKeyViolation(e) {
|
|
1277
|
-
return e
|
|
1488
|
+
return sqlstateOf(e) === "23503";
|
|
1489
|
+
}
|
|
1490
|
+
function isUniqueViolation(e) {
|
|
1491
|
+
return sqlstateOf(e) === "23505";
|
|
1492
|
+
}
|
|
1493
|
+
function constraintOf(e) {
|
|
1494
|
+
const named = e?.constraint;
|
|
1495
|
+
if (typeof named === "string" && named.length > 0) return named;
|
|
1496
|
+
const message = String(e?.message ?? "");
|
|
1497
|
+
return /violates unique constraint "([^"]+)"/.exec(message)?.[1] ?? "";
|
|
1278
1498
|
}
|
|
1279
1499
|
function isLockTimeout(e) {
|
|
1280
|
-
const code = e?.code;
|
|
1281
1500
|
const message = String(e?.message ?? "");
|
|
1282
|
-
return
|
|
1501
|
+
return sqlstateOf(e) === "55P03" || /lock timeout/i.test(message);
|
|
1283
1502
|
}
|
|
1284
1503
|
function diagnosingDriver(sql, surface = "service") {
|
|
1285
1504
|
const original = (e) => String(e?.message ?? e);
|
|
@@ -1289,6 +1508,9 @@ function diagnosingDriver(sql, surface = "service") {
|
|
|
1289
1508
|
`This request cannot write any more: an earlier write in it failed, and a request runs in ONE Postgres transaction, so every statement after the failure is refused. Nothing here is retryable in place. Wrap a write you expect to fail in \`Database.attempt(async (tx) => \u2026)\` \u2014 it takes a SAVEPOINT, so only that write rolls back \u2014 or use \`Database.tables.<t>.upsert(data, { onConflict: [...] })\` when the failure you were bracing for is a duplicate row. (${original(e)})`
|
|
1290
1509
|
);
|
|
1291
1510
|
}
|
|
1511
|
+
if (isUniqueViolation(e)) {
|
|
1512
|
+
return new UniqueViolation(constraintOf(e));
|
|
1513
|
+
}
|
|
1292
1514
|
if (surface === "service" && isForeignKeyViolation(e)) {
|
|
1293
1515
|
return new Error(
|
|
1294
1516
|
`Database.asService() hit a foreign key with nothing to point at. It runs in its OWN transaction on its OWN connection, so a row this request wrote through \`Database.*\` is not visible to it until the request commits \u2014 and the request cannot commit until the handler returns. If the row it needs is one this request just created, do both writes on ONE surface. (${original(e)})`
|
|
@@ -1417,10 +1639,7 @@ async function runPlanOp(sp, op, results) {
|
|
|
1417
1639
|
const rendered = cols.map(
|
|
1418
1640
|
(c) => renderValue(op.values[c], c, args, results, opVectorCols)
|
|
1419
1641
|
);
|
|
1420
|
-
|
|
1421
|
-
const sets = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
|
|
1422
|
-
const action = sets.length ? `DO UPDATE SET ${sets.join(", ")}` : `DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
|
|
1423
|
-
sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
|
|
1642
|
+
sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) ${onConflictTail(cols, conflict, "update")} RETURNING *`;
|
|
1424
1643
|
break;
|
|
1425
1644
|
}
|
|
1426
1645
|
case "insertMany": {
|
|
@@ -1430,7 +1649,9 @@ async function runPlanOp(sp, op, results) {
|
|
|
1430
1649
|
const tuples = rows.map(
|
|
1431
1650
|
(r) => `(${cols.map((c) => renderValue(r[c], c, args, results, opVectorCols)).join(", ")})`
|
|
1432
1651
|
);
|
|
1433
|
-
|
|
1652
|
+
const conflictCols = op.onConflict ?? [];
|
|
1653
|
+
const tail = op.action !== void 0 && conflictCols.length > 0 ? ` ${onConflictTail(cols, conflictCols, op.action)}` : "";
|
|
1654
|
+
sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail} RETURNING *`;
|
|
1434
1655
|
break;
|
|
1435
1656
|
}
|
|
1436
1657
|
case "update": {
|
|
@@ -1487,6 +1708,7 @@ function buildRouteTable(controllers) {
|
|
|
1487
1708
|
`controller ${name} collected zero routes. Either it declares no @Get/@Post/\u2026 , or its decorator metadata was erased at build time \u2014 check that the bundle was compiled with experimentalDecorators enabled.`
|
|
1488
1709
|
);
|
|
1489
1710
|
}
|
|
1711
|
+
assertZeroArgConstructor(Ctrl, "controller");
|
|
1490
1712
|
const instance = new ctor();
|
|
1491
1713
|
for (const r of routes) {
|
|
1492
1714
|
const full = `${basePath}${r.subpath ?? ""}` || "/";
|
|
@@ -1496,7 +1718,10 @@ function buildRouteTable(controllers) {
|
|
|
1496
1718
|
meta: r,
|
|
1497
1719
|
instance,
|
|
1498
1720
|
id: `${r.method} ${full}`,
|
|
1499
|
-
|
|
1721
|
+
// The route's OWN spec is deliberately not folded in here: this field
|
|
1722
|
+
// is what applies when the route is silent, and `effectiveAuth` puts
|
|
1723
|
+
// the route level back on top per request.
|
|
1724
|
+
controllerAuth: resolveEffectiveAuth(void 0, meta?.defaultAuth)
|
|
1500
1725
|
});
|
|
1501
1726
|
}
|
|
1502
1727
|
}
|
|
@@ -2125,18 +2350,29 @@ async function createApp(opts) {
|
|
|
2125
2350
|
return envelope("internal_error", "The request could not be completed", 500, requestId);
|
|
2126
2351
|
}
|
|
2127
2352
|
}
|
|
2353
|
+
let runShutdownHooks;
|
|
2354
|
+
try {
|
|
2355
|
+
runShutdownHooks = await __runStartHooks();
|
|
2356
|
+
} catch (err) {
|
|
2357
|
+
await closeDriver(sql);
|
|
2358
|
+
throw err;
|
|
2359
|
+
}
|
|
2128
2360
|
return {
|
|
2129
2361
|
handle,
|
|
2130
2362
|
routes,
|
|
2131
2363
|
config,
|
|
2132
2364
|
runInServiceScope,
|
|
2133
2365
|
async shutdown() {
|
|
2134
|
-
|
|
2135
|
-
await
|
|
2136
|
-
await closable.end?.();
|
|
2366
|
+
await runShutdownHooks();
|
|
2367
|
+
await closeDriver(sql);
|
|
2137
2368
|
}
|
|
2138
2369
|
};
|
|
2139
2370
|
}
|
|
2371
|
+
async function closeDriver(sql) {
|
|
2372
|
+
const closable = sql;
|
|
2373
|
+
await closable.close?.();
|
|
2374
|
+
await closable.end?.();
|
|
2375
|
+
}
|
|
2140
2376
|
|
|
2141
2377
|
export {
|
|
2142
2378
|
BootRefused,
|
|
@@ -2157,4 +2393,4 @@ export {
|
|
|
2157
2393
|
installEgressFence,
|
|
2158
2394
|
createApp
|
|
2159
2395
|
};
|
|
2160
|
-
//# sourceMappingURL=chunk-
|
|
2396
|
+
//# sourceMappingURL=chunk-NXDH6VQJ.js.map
|