@palbase/backend 24.1.0 → 24.1.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 +52 -27
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +2 -1
- package/dist/bin/palbase-backend.js.map +1 -1
- package/dist/{chunk-HPUSV4AZ.js → chunk-2A62DDVO.js} +7 -28
- package/dist/chunk-2A62DDVO.js.map +1 -0
- package/dist/chunk-XABBC7JP.js +55 -0
- package/dist/chunk-XABBC7JP.js.map +1 -0
- package/dist/engine/index.cjs +52 -27
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.js +2 -1
- package/dist/test/index.cjs +58 -0
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +12 -0
- package/dist/test/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HPUSV4AZ.js.map +0 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/db/input-guards.ts
|
|
2
|
+
var KNOWN_OPS = /* @__PURE__ */ new Set(["gt", "gte", "lt", "lte", "neq", "eq", "in"]);
|
|
3
|
+
function assertUsableFilter(caller, table, where) {
|
|
4
|
+
if (!where) return;
|
|
5
|
+
for (const [col, cond] of Object.entries(where)) {
|
|
6
|
+
if (cond === void 0) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
`${caller}(${table}): where.${col} de\u011Feri undefined \u2014 bu bir filtre de\u011Feri de\u011Fil. Ba\u011Flan\u0131nca NULL olur ve '= NULL' hi\xE7bir sat\u0131ra uymaz, yani sorgu sessizce bo\u015F sonu\xE7 d\xF6nerdi. De\u011Fer yoksa anahtar\u0131 filtreye hi\xE7 koymay\u0131n.`
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
if (cond === null || typeof cond !== "object" || Array.isArray(cond)) continue;
|
|
12
|
+
const entries = Object.entries(cond);
|
|
13
|
+
if (entries.length === 0) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`${caller}(${table}): where.${col} bo\u015F bir operat\xF6r nesnesi ({}) \u2014 hi\xE7bir ko\u015Ful \xFCretmez, yani bu alan filtreden sessizce D\xDC\u015EERD\u0130. Ko\u015Ful kurulmayacaksa anahtar\u0131 filtreye hi\xE7 koymay\u0131n (D-21).`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
for (const [op, v] of entries) {
|
|
19
|
+
if (op === "in") {
|
|
20
|
+
if (!Array.isArray(v)) throw new Error(`${caller}(${table}): where.${col}.in bir dizi olmal\u0131`);
|
|
21
|
+
if (v.some((x) => x === void 0)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`${caller}(${table}): where.${col}.in listesinde undefined var \u2014 sessizce NULL'a ba\u011Flan\u0131r ve o eleman hi\xE7bir sat\u0131rla e\u015Fle\u015Fmez. Listeyi kurarken eleyin.`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (!KNOWN_OPS.has(op)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`${caller}(${table}): where.${col} bilinmeyen operat\xF6r "${op}" (gt/gte/lt/lte/neq/in)`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (v === void 0) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`${caller}(${table}): where.${col}.${op} de\u011Feri undefined \u2014 kar\u015F\u0131la\u015Ft\u0131rman\u0131n sa\u011F taraf\u0131 NULL olur ve sonu\xE7 hi\xE7bir sat\u0131ra uymaz. Ko\u015Fulu kurmay\u0131n.`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function assertUsableWriteValues(caller, table, cols, data) {
|
|
42
|
+
for (const c of cols) {
|
|
43
|
+
if (data[c] === void 0) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`${caller}(${table}): "${c}" de\u011Feri undefined \u2014 bu bir yazma de\u011Feri de\u011Fil. Kolonu bo\u015Faltmak istiyorsan null yaz; kolonu de\u011Fi\u015Ftirmek istemiyorsan nesneye hi\xE7 koyma (bir eksik istek alan\u0131 sessizce NULL yaz\u0131yordu \u2014 FR-016).`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
assertUsableFilter,
|
|
53
|
+
assertUsableWriteValues
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=chunk-XABBC7JP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/db/input-guards.ts"],"sourcesContent":["/**\n * The refusals a Database call gets BEFORE any SQL exists — written once, so the\n * engine and the test double cannot disagree about them.\n *\n * WHY THIS FILE EXISTS. `fakeDatabase()` is a second implementation of the same\n * surface (`__tests__/helpers/mock-db.ts`), and it never touched `compileWhere`\n * or `asBindParams`. Measured against the published 24.1.0: all four of the\n * calls that release had just started refusing went through the fake SILENTLY —\n * `update{title:undefined}`, `insert{title:undefined}`, `findMany{done:{}}`,\n * `deleteMany{owner,created_at:{}}`.\n *\n * The scaffold tells authors to test the service layer against exactly that\n * fake. So a test went green on a call production would throw on, and the\n * author found out in production instead — the same \"the surface does not match\n * the engine\" shape these refusals exist to end, arriving through the door the\n * SDK hands people for testing.\n *\n * These are pure and SQL-free on purpose: an in-memory store can run them as\n * easily as the driver path can.\n */\n\n/** The comparison operators a filter value may carry. Kept here because the\n * guard has to tell an operator object from a plain value. */\nconst KNOWN_OPS = new Set([\"gt\", \"gte\", \"lt\", \"lte\", \"neq\", \"eq\", \"in\"]);\n\n/**\n * Refuse a filter that would compile to something other than what it reads like.\n *\n * Three shapes, each measured in production before it was closed:\n *\n * `{ col: undefined }` binds NULL; `= NULL` matches no row, so the query\n * answered \"no records\" and said nothing.\n * `{ col: {} }` produces no term at all — every row on the read\n * path, a dropped condition on the write path.\n * `{ col: { gte: undefined } }` and an `undefined` inside `in`: the same NULL,\n * one level down.\n */\nexport function assertUsableFilter(\n caller: string,\n table: string,\n where: Record<string, unknown> | undefined,\n): void {\n if (!where) return;\n for (const [col, cond] of Object.entries(where)) {\n if (cond === undefined) {\n throw new Error(\n `${caller}(${table}): where.${col} değeri undefined — bu bir filtre değeri değil. ` +\n `Bağlanınca NULL olur ve '= NULL' hiçbir satıra uymaz, yani sorgu sessizce ` +\n `boş sonuç dönerdi. Değer yoksa anahtarı filtreye hiç koymayın.`,\n );\n }\n if (cond === null || typeof cond !== \"object\" || Array.isArray(cond)) continue;\n\n const entries = Object.entries(cond as Record<string, unknown>);\n if (entries.length === 0) {\n throw new Error(\n `${caller}(${table}): where.${col} boş bir operatör nesnesi ({}) — hiçbir koşul ` +\n `üretmez, yani bu alan filtreden sessizce DÜŞERDİ. Koşul kurulmayacaksa ` +\n `anahtarı filtreye hiç koymayın (D-21).`,\n );\n }\n for (const [op, v] of entries) {\n if (op === \"in\") {\n if (!Array.isArray(v)) throw new Error(`${caller}(${table}): where.${col}.in bir dizi olmalı`);\n if (v.some((x) => x === undefined)) {\n throw new Error(\n `${caller}(${table}): where.${col}.in listesinde undefined var — sessizce NULL'a ` +\n `bağlanır ve o eleman hiçbir satırla eşleşmez. Listeyi kurarken eleyin.`,\n );\n }\n continue;\n }\n if (!KNOWN_OPS.has(op)) {\n throw new Error(\n `${caller}(${table}): where.${col} bilinmeyen operatör \"${op}\" (gt/gte/lt/lte/neq/in)`,\n );\n }\n if (v === undefined) {\n throw new Error(\n `${caller}(${table}): where.${col}.${op} değeri undefined — karşılaştırmanın ` +\n `sağ tarafı NULL olur ve sonuç hiçbir satıra uymaz. Koşulu kurmayın.`,\n );\n }\n }\n }\n}\n\n/**\n * Refuse a write whose value never arrived.\n *\n * `{ title: req.body.title }` with no `title` in the body bound NULL and\n * answered 200 — the column was ERASED. `null` is untouched, and the difference\n * is the whole point: null is an author SAYING \"empty this column\"; undefined is\n * nobody saying anything.\n */\nexport function assertUsableWriteValues(\n caller: string,\n table: string,\n cols: readonly string[],\n data: Record<string, unknown>,\n): void {\n for (const c of cols) {\n if (data[c] === undefined) {\n throw new Error(\n `${caller}(${table}): \"${c}\" değeri undefined — bu bir yazma değeri değil. ` +\n `Kolonu boşaltmak istiyorsan null yaz; kolonu değiştirmek istemiyorsan nesneye hiç koyma ` +\n `(bir eksik istek alanı sessizce NULL yazıyordu — FR-016).`,\n );\n }\n }\n}\n"],"mappings":";AAuBA,IAAM,YAAY,oBAAI,IAAI,CAAC,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM,IAAI,CAAC;AAchE,SAAS,mBACd,QACA,OACA,OACM;AACN,MAAI,CAAC,MAAO;AACZ,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC/C,QAAI,SAAS,QAAW;AACtB,YAAM,IAAI;AAAA,QACR,GAAG,MAAM,IAAI,KAAK,YAAY,GAAG;AAAA,MAGnC;AAAA,IACF;AACA,QAAI,SAAS,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,IAAI,EAAG;AAEtE,UAAM,UAAU,OAAO,QAAQ,IAA+B;AAC9D,QAAI,QAAQ,WAAW,GAAG;AACxB,YAAM,IAAI;AAAA,QACR,GAAG,MAAM,IAAI,KAAK,YAAY,GAAG;AAAA,MAGnC;AAAA,IACF;AACA,eAAW,CAAC,IAAI,CAAC,KAAK,SAAS;AAC7B,UAAI,OAAO,MAAM;AACf,YAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,OAAM,IAAI,MAAM,GAAG,MAAM,IAAI,KAAK,YAAY,GAAG,0BAAqB;AAC7F,YAAI,EAAE,KAAK,CAAC,MAAM,MAAM,MAAS,GAAG;AAClC,gBAAM,IAAI;AAAA,YACR,GAAG,MAAM,IAAI,KAAK,YAAY,GAAG;AAAA,UAEnC;AAAA,QACF;AACA;AAAA,MACF;AACA,UAAI,CAAC,UAAU,IAAI,EAAE,GAAG;AACtB,cAAM,IAAI;AAAA,UACR,GAAG,MAAM,IAAI,KAAK,YAAY,GAAG,4BAAyB,EAAE;AAAA,QAC9D;AAAA,MACF;AACA,UAAI,MAAM,QAAW;AACnB,cAAM,IAAI;AAAA,UACR,GAAG,MAAM,IAAI,KAAK,YAAY,GAAG,IAAI,EAAE;AAAA,QAEzC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAUO,SAAS,wBACd,QACA,OACA,MACA,MACM;AACN,aAAW,KAAK,MAAM;AACpB,QAAI,KAAK,CAAC,MAAM,QAAW;AACzB,YAAM,IAAI;AAAA,QACR,GAAG,MAAM,IAAI,KAAK,OAAO,CAAC;AAAA,MAG5B;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/engine/index.cjs
CHANGED
|
@@ -1071,6 +1071,56 @@ function makeMemoryCache(opts = {}) {
|
|
|
1071
1071
|
};
|
|
1072
1072
|
}
|
|
1073
1073
|
|
|
1074
|
+
// src/db/input-guards.ts
|
|
1075
|
+
var KNOWN_OPS = /* @__PURE__ */ new Set(["gt", "gte", "lt", "lte", "neq", "eq", "in"]);
|
|
1076
|
+
function assertUsableFilter(caller, table, where) {
|
|
1077
|
+
if (!where) return;
|
|
1078
|
+
for (const [col, cond] of Object.entries(where)) {
|
|
1079
|
+
if (cond === void 0) {
|
|
1080
|
+
throw new Error(
|
|
1081
|
+
`${caller}(${table}): where.${col} de\u011Feri undefined \u2014 bu bir filtre de\u011Feri de\u011Fil. Ba\u011Flan\u0131nca NULL olur ve '= NULL' hi\xE7bir sat\u0131ra uymaz, yani sorgu sessizce bo\u015F sonu\xE7 d\xF6nerdi. De\u011Fer yoksa anahtar\u0131 filtreye hi\xE7 koymay\u0131n.`
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
if (cond === null || typeof cond !== "object" || Array.isArray(cond)) continue;
|
|
1085
|
+
const entries = Object.entries(cond);
|
|
1086
|
+
if (entries.length === 0) {
|
|
1087
|
+
throw new Error(
|
|
1088
|
+
`${caller}(${table}): where.${col} bo\u015F bir operat\xF6r nesnesi ({}) \u2014 hi\xE7bir ko\u015Ful \xFCretmez, yani bu alan filtreden sessizce D\xDC\u015EERD\u0130. Ko\u015Ful kurulmayacaksa anahtar\u0131 filtreye hi\xE7 koymay\u0131n (D-21).`
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
for (const [op, v] of entries) {
|
|
1092
|
+
if (op === "in") {
|
|
1093
|
+
if (!Array.isArray(v)) throw new Error(`${caller}(${table}): where.${col}.in bir dizi olmal\u0131`);
|
|
1094
|
+
if (v.some((x) => x === void 0)) {
|
|
1095
|
+
throw new Error(
|
|
1096
|
+
`${caller}(${table}): where.${col}.in listesinde undefined var \u2014 sessizce NULL'a ba\u011Flan\u0131r ve o eleman hi\xE7bir sat\u0131rla e\u015Fle\u015Fmez. Listeyi kurarken eleyin.`
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
if (!KNOWN_OPS.has(op)) {
|
|
1102
|
+
throw new Error(
|
|
1103
|
+
`${caller}(${table}): where.${col} bilinmeyen operat\xF6r "${op}" (gt/gte/lt/lte/neq/in)`
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
if (v === void 0) {
|
|
1107
|
+
throw new Error(
|
|
1108
|
+
`${caller}(${table}): where.${col}.${op} de\u011Feri undefined \u2014 kar\u015F\u0131la\u015Ft\u0131rman\u0131n sa\u011F taraf\u0131 NULL olur ve sonu\xE7 hi\xE7bir sat\u0131ra uymaz. Ko\u015Fulu kurmay\u0131n.`
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
function assertUsableWriteValues(caller, table, cols, data) {
|
|
1115
|
+
for (const c of cols) {
|
|
1116
|
+
if (data[c] === void 0) {
|
|
1117
|
+
throw new Error(
|
|
1118
|
+
`${caller}(${table}): "${c}" de\u011Feri undefined \u2014 bu bir yazma de\u011Feri de\u011Fil. Kolonu bo\u015Faltmak istiyorsan null yaz; kolonu de\u011Fi\u015Ftirmek istemiyorsan nesneye hi\xE7 koyma (bir eksik istek alan\u0131 sessizce NULL yaz\u0131yordu \u2014 FR-016).`
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1074
1124
|
// src/engine/db.ts
|
|
1075
1125
|
function quoteIdent(name) {
|
|
1076
1126
|
return `"${name.replace(/"/g, '""')}"`;
|
|
@@ -1199,13 +1249,7 @@ function asTableRows(table, rows) {
|
|
|
1199
1249
|
return rows.map((row) => applyFromDb(reviveVectors(asWireRow(row), vectorCols), transforms));
|
|
1200
1250
|
}
|
|
1201
1251
|
function asBindParams(table, cols, data, caller) {
|
|
1202
|
-
|
|
1203
|
-
if (data[c] === void 0) {
|
|
1204
|
-
throw new Error(
|
|
1205
|
-
`${caller}(${table}): "${c}" de\u011Feri undefined \u2014 bu bir yazma de\u011Feri de\u011Fil. Kolonu bo\u015Faltmak istiyorsan null yaz; kolonu de\u011Fi\u015Ftirmek istemiyorsan nesneye hi\xE7 koyma (bir eksik istek alan\u0131 sessizce NULL yaz\u0131yordu \u2014 FR-016).`
|
|
1206
|
-
);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1252
|
+
assertUsableWriteValues(caller, table, cols, data);
|
|
1209
1253
|
const vectorCols = vectorColumnsOf(currentSchema, table);
|
|
1210
1254
|
const transforms = transformsOf(currentSchema, table);
|
|
1211
1255
|
return cols.map((c) => {
|
|
@@ -1428,42 +1472,23 @@ function compileWhere(table, colSet, where, add, caller = "search") {
|
|
|
1428
1472
|
const t = transforms.get(col);
|
|
1429
1473
|
return t?.toDb === void 0 ? add : (v) => add(v === null || v === void 0 ? v : t.toDb(v));
|
|
1430
1474
|
};
|
|
1475
|
+
assertUsableFilter(caller, table, where);
|
|
1431
1476
|
for (const [col, cond] of Object.entries(where)) {
|
|
1432
1477
|
if (colSet !== null && !colSet.has(col)) {
|
|
1433
1478
|
throw new Error(`${caller}(${table}): where kolonu "${col}" tabloda yok (FR-016)`);
|
|
1434
1479
|
}
|
|
1435
|
-
if (cond === void 0) {
|
|
1436
|
-
throw new Error(
|
|
1437
|
-
`${caller}(${table}): where.${col} de\u011Feri undefined \u2014 bu bir filtre de\u011Feri de\u011Fil. Ba\u011Flan\u0131nca NULL olur ve '= NULL' hi\xE7bir sat\u0131ra uymaz, yani sorgu sessizce bo\u015F sonu\xE7 d\xF6nerdi. De\u011Fer yoksa anahtar\u0131 filtreye hi\xE7 koymay\u0131n.`
|
|
1438
|
-
);
|
|
1439
|
-
}
|
|
1440
1480
|
const q = `t.${quoteIdent(col)}`;
|
|
1441
1481
|
const bind = bindFor(col);
|
|
1442
1482
|
if (cond !== null && typeof cond === "object" && !Array.isArray(cond)) {
|
|
1443
|
-
if (Object.keys(cond).length === 0) {
|
|
1444
|
-
throw new Error(
|
|
1445
|
-
`${caller}(${table}): where.${col} bo\u015F bir operat\xF6r nesnesi ({}) \u2014 hi\xE7bir ko\u015Ful \xFCretmez, yani bu alan filtreden sessizce D\xDC\u015EERD\u0130. Ko\u015Ful kurulmayacaksa anahtar\u0131 filtreye hi\xE7 koymay\u0131n (D-21).`
|
|
1446
|
-
);
|
|
1447
|
-
}
|
|
1448
1483
|
for (const [op, v] of Object.entries(cond)) {
|
|
1449
1484
|
if (op === "in") {
|
|
1450
1485
|
if (!Array.isArray(v)) throw new Error(`${caller}(${table}): where.${col}.in bir dizi olmal\u0131`);
|
|
1451
|
-
if (v.some((x) => x === void 0)) {
|
|
1452
|
-
throw new Error(
|
|
1453
|
-
`${caller}(${table}): where.${col}.in listesinde undefined var \u2014 sessizce NULL'a ba\u011Flan\u0131r ve o eleman hi\xE7bir sat\u0131rla e\u015Fle\u015Fmez. Listeyi kurarken eleyin.`
|
|
1454
|
-
);
|
|
1455
|
-
}
|
|
1456
1486
|
if (v.length === 0) {
|
|
1457
1487
|
parts.push("false");
|
|
1458
1488
|
continue;
|
|
1459
1489
|
}
|
|
1460
1490
|
parts.push(`${q} IN (${v.map((x) => bind(x)).join(", ")})`);
|
|
1461
1491
|
} else if (op in WHERE_OPS) {
|
|
1462
|
-
if (v === void 0) {
|
|
1463
|
-
throw new Error(
|
|
1464
|
-
`${caller}(${table}): where.${col}.${op} de\u011Feri undefined \u2014 kar\u015F\u0131la\u015Ft\u0131rman\u0131n sa\u011F taraf\u0131 NULL olur ve sonu\xE7 hi\xE7bir sat\u0131ra uymaz. Ko\u015Fulu kurmay\u0131n.`
|
|
1465
|
-
);
|
|
1466
|
-
}
|
|
1467
1492
|
if (v === null && (op === "neq" || op === "eq")) {
|
|
1468
1493
|
parts.push(`${q} IS ${op === "neq" ? "NOT " : ""}NULL`);
|
|
1469
1494
|
continue;
|