@palbase/backend 27.0.0 → 28.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 +165 -39
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +4 -4
- package/dist/{chunk-GYK6QYS4.js → chunk-3TUJWHC2.js} +28 -3
- package/dist/chunk-3TUJWHC2.js.map +1 -0
- package/dist/{chunk-OO7R25AI.js → chunk-75YROPRZ.js} +118 -39
- package/dist/chunk-75YROPRZ.js.map +1 -0
- package/dist/{chunk-TS4U7NBD.js → chunk-IVZERLTM.js} +2 -2
- package/dist/{chunk-I3C4PFIW.js → chunk-RVP6BTEZ.js} +63 -6
- package/dist/chunk-RVP6BTEZ.js.map +1 -0
- package/dist/{chunk-DRZFQRJI.js → chunk-SNDXY565.js} +3 -2
- package/dist/chunk-SNDXY565.js.map +1 -0
- package/dist/db/index.cjs +30 -1
- 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 +3 -3
- package/dist/engine/index.cjs +175 -39
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +3 -3
- package/dist/engine/index.d.ts +3 -3
- package/dist/engine/index.js +11 -4
- package/dist/{index-VtToZmUm.d.cts → index-9C3JHxg-.d.cts} +112 -5
- package/dist/{index-NuzRCuxe.d.ts → index-BbvOoZFr.d.ts} +112 -5
- package/dist/{index-Bve7BBTL.d.cts → index-DtISj9QX.d.cts} +125 -22
- package/dist/{index-BrvvxSpn.d.ts → index-dTTLlHIn.d.ts} +125 -22
- package/dist/index.cjs +93 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -22
- package/dist/index.d.ts +6 -22
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.d.cts +2 -2
- package/dist/openapi/index.d.ts +2 -2
- package/dist/{registry-B0eyOF9x.d.ts → registry-JQNIX-eA.d.ts} +1 -1
- package/dist/{registry-Bk9_rbNd.d.cts → registry-qIPM5BQe.d.cts} +1 -1
- package/dist/test/index.cjs +100 -6
- 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 +88 -6
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/database.md +103 -1
- package/docs/llms-full.txt +104 -2
- package/package.json +1 -1
- package/template/package.json +1 -1
- package/dist/chunk-DRZFQRJI.js.map +0 -1
- package/dist/chunk-GYK6QYS4.js.map +0 -1
- package/dist/chunk-I3C4PFIW.js.map +0 -1
- package/dist/chunk-OO7R25AI.js.map +0 -1
- /package/dist/{chunk-TS4U7NBD.js.map → chunk-IVZERLTM.js.map} +0 -0
|
@@ -29,9 +29,11 @@ __name(brandOf, "brandOf");
|
|
|
29
29
|
function looksLikeUnbrandedRef(v) {
|
|
30
30
|
if (typeof v !== "object" || v === null) return null;
|
|
31
31
|
if (brandOf(v) !== void 0) return null;
|
|
32
|
+
if (isColumnExpr(v)) return null;
|
|
32
33
|
const o = v;
|
|
33
34
|
if (typeof o.$col === "string") return "col";
|
|
34
35
|
if (o.$sql !== void 0 && typeof o.$sql === "object" && o.$sql !== null) return "sql";
|
|
36
|
+
if (o.$expr !== void 0 && typeof o.$expr === "object" && o.$expr !== null) return "expr";
|
|
35
37
|
return null;
|
|
36
38
|
}
|
|
37
39
|
__name(looksLikeUnbrandedRef, "looksLikeUnbrandedRef");
|
|
@@ -58,6 +60,16 @@ function isSqlFragment(v) {
|
|
|
58
60
|
}
|
|
59
61
|
__name(isSqlFragment, "isSqlFragment");
|
|
60
62
|
var TX_EXPR = /* @__PURE__ */ Symbol.for("palbase.tx.expr");
|
|
63
|
+
function isNowExpr(v) {
|
|
64
|
+
if (!isColumnExpr(v)) return false;
|
|
65
|
+
try {
|
|
66
|
+
const e = v[TX_EXPR];
|
|
67
|
+
return typeof e === "object" && e !== null && e.fn === "now";
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
__name(isNowExpr, "isNowExpr");
|
|
61
73
|
function isColumnExpr(v) {
|
|
62
74
|
if (typeof v !== "object" && typeof v !== "function") return false;
|
|
63
75
|
if (v === null) return false;
|
|
@@ -71,8 +83,11 @@ __name(isColumnExpr, "isColumnExpr");
|
|
|
71
83
|
function assertNoExpressionHandles(caller, table, cols, data) {
|
|
72
84
|
for (const c of cols) {
|
|
73
85
|
const v = data[c];
|
|
86
|
+
if (isNowExpr(v)) {
|
|
87
|
+
throw new Error(`${caller}(${table}): "${c}" now() ald\u0131 \u2014 bu yolda de\u011Fer beklenir. Sat\u0131r EKLEN\u0130RKEN sunucu saatini yazman\u0131n yolu kolonu defaultNow() ile bildirmek (varsay\u0131lan kolonun yan\u0131nda durur, her \xE7a\u011Fr\u0131da tekrarlanmaz); var olan bir sat\u0131r\u0131 damgalamak i\xE7in updateMany({ where, set: { ${c}: now() } }) ya da $transaction i\xE7inde tx.public.${table}.updateWhere(where, { ${c}: now() }).`);
|
|
88
|
+
}
|
|
74
89
|
if (isColumnExpr(v)) {
|
|
75
|
-
throw new Error(`${caller}(${table}): "${c}" bir ifade tutama\u011F\u0131 ald\u0131 (increment()/decrement()
|
|
90
|
+
throw new Error(`${caller}(${table}): "${c}" bir ifade tutama\u011F\u0131 ald\u0131 (increment()/decrement()). Bu yolda de\u011Fer beklenir. Saya\xE7 art\u0131\u015F\u0131 i\xE7in updateMany({ where, set: { ${c}: increment(n) } }) ya da $transaction i\xE7inde tx.public.${table}.updateWhere(where, { ${c}: increment(n) }) kullan\u0131n.`);
|
|
76
91
|
}
|
|
77
92
|
if (isColRef(v)) {
|
|
78
93
|
throw new Error(`${caller}(${table}): "${c}" bir col() ald\u0131. Kolon referans\u0131 yaln\u0131z F\u0130LTREDE durabilir; bir kolonun de\u011Ferini ba\u015Fka bir kolona yazmak i\xE7in $query kullan\u0131n.`);
|
|
@@ -140,6 +155,7 @@ function assertUsableFilter(caller, table, where) {
|
|
|
140
155
|
}
|
|
141
156
|
if (cond === null || typeof cond !== "object" || Array.isArray(cond)) continue;
|
|
142
157
|
if (isColRef(cond)) continue;
|
|
158
|
+
if (isNowExpr(cond)) continue;
|
|
143
159
|
const entries = Object.entries(cond);
|
|
144
160
|
if (entries.length === 0) {
|
|
145
161
|
throw new Error(`${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).`);
|
|
@@ -281,6 +297,11 @@ function encodeValue(value, column, allowColumnExpr) {
|
|
|
281
297
|
field: ref.field
|
|
282
298
|
}
|
|
283
299
|
}, "ref");
|
|
300
|
+
if (isNowExpr(value)) return {
|
|
301
|
+
$expr: {
|
|
302
|
+
fn: "now"
|
|
303
|
+
}
|
|
304
|
+
};
|
|
284
305
|
const expr = exprOf(value);
|
|
285
306
|
if (expr) {
|
|
286
307
|
if (expr.fn !== "now" && !allowColumnExpr) {
|
|
@@ -358,7 +379,9 @@ function encodeMap(map, allowColumnExpr) {
|
|
|
358
379
|
const out = {};
|
|
359
380
|
for (const key of Object.keys(map).sort()) {
|
|
360
381
|
const value = map[key];
|
|
361
|
-
if (value === void 0)
|
|
382
|
+
if (value === void 0) {
|
|
383
|
+
throw new TxPlanError(`${key} de\u011Feri undefined \u2014 bu bir yazma de\u011Feri de\u011Fil. Anahtar duruyor ama de\u011Feri yok, yani kolon sessizce YAZILMAZDI (istek g\xF6vdesinden gelen bir alan\u0131n eksik olmas\u0131 bu \u015Fekilde g\xF6r\xFCn\xFCr). Kolon varsay\u0131lan\u0131n\u0131 istiyorsan\u0131z anahtar\u0131 hi\xE7 koymay\u0131n; NULL yazmak istiyorsan\u0131z a\xE7\u0131k\xE7a null yaz\u0131n.`);
|
|
384
|
+
}
|
|
362
385
|
out[key] = encodeValue(value, key, allowColumnExpr);
|
|
363
386
|
}
|
|
364
387
|
return out;
|
|
@@ -886,6 +909,10 @@ function makeTableProxy(ops, prefix) {
|
|
|
886
909
|
const name = `${prefix}${prop}`;
|
|
887
910
|
return {
|
|
888
911
|
insert: /* @__PURE__ */ __name((data) => ops().insert(name, data), "insert"),
|
|
912
|
+
// DÖRDÜNCÜ FİİL, aynı gerekçeyle: tip söz veriyor, ops katmanı
|
|
913
|
+
// uyguluyor, ve bir handler'ın gerçekten dokunduğu yer BURASI.
|
|
914
|
+
// `runtime-table-verbs` kapısı bunu adıyla saydı.
|
|
915
|
+
insertMany: /* @__PURE__ */ __name((rows, opts) => ops().insertMany(name, rows, opts), "insertMany"),
|
|
889
916
|
update: /* @__PURE__ */ __name((q) => ops().update(name, q.where.id, q.set), "update"),
|
|
890
917
|
delete: /* @__PURE__ */ __name((id) => ops().delete(name, id), "delete"),
|
|
891
918
|
findById: /* @__PURE__ */ __name((id) => ops().findById(name, id), "findById"),
|
|
@@ -942,6 +969,7 @@ function makeTypedSurface(raw) {
|
|
|
942
969
|
$claim: /* @__PURE__ */ __name((table, unique, extra) => reco.claim(table, unique, extra), "$claim"),
|
|
943
970
|
$lockRows: /* @__PURE__ */ __name((table, ids) => reco.lockRows(table, ids), "$lockRows"),
|
|
944
971
|
$advisoryXactLock: /* @__PURE__ */ __name((key) => reco.advisoryXactLock(key), "$advisoryXactLock"),
|
|
972
|
+
$insertMany: /* @__PURE__ */ __name((table, rows, opts) => raw.insertMany(table, rows, opts), "$insertMany"),
|
|
945
973
|
$supersede: /* @__PURE__ */ __name((table, id, row) => raw.supersede(table, id, row), "$supersede")
|
|
946
974
|
};
|
|
947
975
|
const base = Object.assign(ops, {
|
|
@@ -1131,6 +1159,20 @@ ${detail}`);
|
|
|
1131
1159
|
}
|
|
1132
1160
|
__name(loadConfig, "loadConfig");
|
|
1133
1161
|
|
|
1162
|
+
// src/refusals.ts
|
|
1163
|
+
var DECLARATION_REFUSAL = /* @__PURE__ */ Symbol.for("palbase.backend.declarationRefusal");
|
|
1164
|
+
var DeclarationRefused = class extends Error {
|
|
1165
|
+
static {
|
|
1166
|
+
__name(this, "DeclarationRefused");
|
|
1167
|
+
}
|
|
1168
|
+
/** @see DECLARATION_REFUSAL — realm-safe, unlike `instanceof`. */
|
|
1169
|
+
[DECLARATION_REFUSAL] = true;
|
|
1170
|
+
constructor(message) {
|
|
1171
|
+
super(message);
|
|
1172
|
+
this.name = "DeclarationRefused";
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1134
1176
|
// src/engine/auth.ts
|
|
1135
1177
|
function b64urlToBytes(s) {
|
|
1136
1178
|
const pad = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -1453,12 +1495,12 @@ function buildRelations(schemas) {
|
|
|
1453
1495
|
const held = names.get(name);
|
|
1454
1496
|
if (held !== void 0) {
|
|
1455
1497
|
if (held.def.owns === true && origin.def.owns === true) {
|
|
1456
|
-
throw new
|
|
1498
|
+
throw new DeclarationRefused(`table "${tableKey}" declares TWO owner columns (${held.column}, ${origin.column}) \u2014 a table has at most ONE ownedByUser(). If the other column only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead.`);
|
|
1457
1499
|
}
|
|
1458
1500
|
const heldFix = renameCall(held);
|
|
1459
1501
|
const originFix = renameCall(origin);
|
|
1460
1502
|
const remedy = heldFix === originFix ? `rename one of them: ${heldFix}` : `rename one of them \u2014 "${held.table}.${held.column}": ${heldFix}; "${origin.table}.${origin.column}": ${originFix}`;
|
|
1461
|
-
throw new
|
|
1503
|
+
throw new DeclarationRefused(`table "${tableKey}": ${describeOrigin(held)} and ${describeOrigin(origin)} both resolve to the relation name "${name}" \u2014 ${remedy}.`);
|
|
1462
1504
|
}
|
|
1463
1505
|
names.set(name, origin);
|
|
1464
1506
|
out.get(tableKey)?.push(edge);
|
|
@@ -2036,7 +2078,7 @@ function compileWhereBare(table, colSet, where, add, caller = "search", alias =
|
|
|
2036
2078
|
const rel = rels.find((r) => r.name === relName);
|
|
2037
2079
|
if (rel === void 0) {
|
|
2038
2080
|
const known = rels.map((r) => r.name).sort();
|
|
2039
|
-
throw new Error(`${caller}(${table}): has.${relName} diye bir ili\u015Fki yok
|
|
2081
|
+
throw new Error(`${caller}(${table}): has.${relName} diye bir ili\u015Fki yok` + (known.length > 0 ? ` (bilinenler: ${known.join(", ")}). \u0130li\u015Fkiler yabanc\u0131 anahtarlardan T\xDCRET\u0130L\u0130R; ad\u0131n\u0131 de\u011Fi\u015Ftirmek i\xE7in references(..., { as, reverseAs }).` : `. Bu tablo H\u0130\xC7 yabanc\u0131 anahtar bildirmiyor, ve ili\u015Fkiler onlardan T\xDCRET\u0130L\u0130R. Var olan bir veritaban\u0131ndan introspect edilmi\u015F \u015Femalarda bu s\u0131k olur: kolon \`text()\`/\`uuid()\` olarak durur, \`references(() => <tablo>.<kolon>)\` yoktur. Bildirdi\u011Finiz anda ili\u015Fki iki tarafta da adland\u0131r\u0131l\u0131r \u2014 ama bu CANLI veritaban\u0131na giden bir k\u0131s\u0131tt\u0131r ve mevcut yetim sat\u0131rlar onu d\xFC\u015F\xFCr\xFCr; \xF6nce onlar\u0131 say\u0131n.`));
|
|
2040
2082
|
}
|
|
2041
2083
|
const ourCol = rel.kind === "many" ? primaryKeyOf(table) : rel.via;
|
|
2042
2084
|
const theirCol = rel.kind === "many" ? rel.via : primaryKeyOf(rel.to);
|
|
@@ -2063,6 +2105,10 @@ function compileWhereBare(table, colSet, where, add, caller = "search", alias =
|
|
|
2063
2105
|
parts.push(`${q} = ${colRefSql(table, colSet, cond, caller, alias)}`);
|
|
2064
2106
|
continue;
|
|
2065
2107
|
}
|
|
2108
|
+
if (isNowExpr(cond)) {
|
|
2109
|
+
parts.push(`${q} = now()`);
|
|
2110
|
+
continue;
|
|
2111
|
+
}
|
|
2066
2112
|
if (cond !== null && typeof cond === "object" && !Array.isArray(cond)) {
|
|
2067
2113
|
for (const [op, v] of Object.entries(cond)) {
|
|
2068
2114
|
if (op === "contains" || op === "icontains" || op === "startsWith" || op === "endsWith") {
|
|
@@ -2091,6 +2137,13 @@ function compileWhereBare(table, colSet, where, add, caller = "search", alias =
|
|
|
2091
2137
|
continue;
|
|
2092
2138
|
}
|
|
2093
2139
|
parts.push(`${q} IN (${v.map((x) => bind(x)).join(", ")})`);
|
|
2140
|
+
} else if (isNowExpr(v)) {
|
|
2141
|
+
if (!(op in WHERE_OPS)) {
|
|
2142
|
+
throw new Error(`${caller}(${table}): where.${col}.${op} now() ile kullan\u0131lamaz \u2014 e\u015Fitlik i\xE7in \xE7\u0131plak bi\xE7imi kullan\u0131n: { ${col}: now() }`);
|
|
2143
|
+
}
|
|
2144
|
+
parts.push(`${q} ${WHERE_OPS[op]} now()`);
|
|
2145
|
+
} else if (isColumnExpr(v)) {
|
|
2146
|
+
throw new Error(`${caller}(${table}): where.${col}.${op} bir YAZMA ifadesi ald\u0131 (increment/decrement) \u2014 kar\u015F\u0131la\u015Ft\u0131rma de\u011Feri de\u011Fil. Sunucu saati i\xE7in now() kullan\u0131n; say\u0131sal bir e\u015Fik i\xE7in de\u011Feri yaz\u0131n.`);
|
|
2094
2147
|
} else if (isColRef(v)) {
|
|
2095
2148
|
if (!(op in WHERE_OPS)) {
|
|
2096
2149
|
throw new Error(`${caller}(${table}): where.${col}.${op} col() ile kullan\u0131lamaz \u2014 e\u015Fitlik i\xE7in \xE7\u0131plak bi\xE7imi kullan\u0131n: { ${col}: col("\u2026") }`);
|
|
@@ -2098,7 +2151,9 @@ function compileWhereBare(table, colSet, where, add, caller = "search", alias =
|
|
|
2098
2151
|
parts.push(`${q} ${WHERE_OPS[op]} ${colRefSql(table, colSet, v, caller, alias)}`);
|
|
2099
2152
|
} else if (looksLikeUnbrandedRef(v) !== null) {
|
|
2100
2153
|
const kind = looksLikeUnbrandedRef(v);
|
|
2101
|
-
|
|
2154
|
+
const made = kind === "col" ? "col()" : kind === "sql" ? "sqlFragment" : "now()";
|
|
2155
|
+
const how = kind === "col" ? 'col("kolon_ad\u0131")' : kind === "sql" ? "sqlFragment`\u2026`" : "now()";
|
|
2156
|
+
throw new Error(`${caller}(${table}): where.${col}.${op} bir ${made} TAKL\u0130D\u0130 ald\u0131 \u2014 bu nesne bu s\xFCre\xE7te ${made} ile \xFCretilmemi\u015F. \u0130stek g\xF6vdesinden gelen bir filtre i\u015Faret\xE7i TA\u015EIYAMAZ; ko\u015Fulu kendi kodunuzda ${how} ile kurun.`);
|
|
2102
2157
|
} else if (op in WHERE_OPS) {
|
|
2103
2158
|
if (v === null && op === "neq") {
|
|
2104
2159
|
parts.push(`${q} IS NOT NULL`);
|
|
@@ -2203,6 +2258,37 @@ async function embedQuery(embed, text) {
|
|
|
2203
2258
|
}
|
|
2204
2259
|
}
|
|
2205
2260
|
__name(embedQuery, "embedQuery");
|
|
2261
|
+
function buildSetAssignments(table, cols, set, add, caller) {
|
|
2262
|
+
return cols.map((c) => {
|
|
2263
|
+
const expr = columnExprOf(set[c]);
|
|
2264
|
+
if (expr !== null && expr.fn === "now") {
|
|
2265
|
+
return `${quoteIdent(c)} = now()`;
|
|
2266
|
+
}
|
|
2267
|
+
if (expr !== null) {
|
|
2268
|
+
const operator = expr.fn === "inc" ? "+" : "-";
|
|
2269
|
+
const t = transformsOf(currentSchema, table).get(c);
|
|
2270
|
+
const amount = t?.toDb === void 0 || expr.by === null || expr.by === void 0 ? expr.by : t.toDb(expr.by);
|
|
2271
|
+
return `${quoteIdent(c)} = ${quoteIdent(c)} ${operator} ${add(amount)}`;
|
|
2272
|
+
}
|
|
2273
|
+
return `${quoteIdent(c)} = ${add(asBindParams(table, [
|
|
2274
|
+
c
|
|
2275
|
+
], set, caller)[0])}`;
|
|
2276
|
+
}).join(", ");
|
|
2277
|
+
}
|
|
2278
|
+
__name(buildSetAssignments, "buildSetAssignments");
|
|
2279
|
+
function buildValueTuple(table, cols, data, add, caller) {
|
|
2280
|
+
return cols.map((c) => {
|
|
2281
|
+
const expr = columnExprOf(data[c]);
|
|
2282
|
+
if (expr !== null && expr.fn === "now") return "now()";
|
|
2283
|
+
if (expr !== null) {
|
|
2284
|
+
throw new Error(`${caller}(${table}): "${c}" ${expr.fn === "inc" ? "increment()" : "decrement()"} ald\u0131 \u2014 sat\u0131r HEN\xDCZ YOK, yani "kolonun \u015Fu anki de\u011Feri" diye bir \u015Fey yok. Ba\u015Flang\u0131\xE7 de\u011Ferini yaz\u0131n; art\u0131rmak i\xE7in sat\u0131r olu\u015Ftuktan sonra updateMany({ where, set: { ${c}: ${expr.fn === "inc" ? "increment" : "decrement"}(n) } }).`);
|
|
2285
|
+
}
|
|
2286
|
+
return add(asBindParams(table, [
|
|
2287
|
+
c
|
|
2288
|
+
], data, caller)[0]);
|
|
2289
|
+
}).join(", ");
|
|
2290
|
+
}
|
|
2291
|
+
__name(buildValueTuple, "buildValueTuple");
|
|
2206
2292
|
function createOps(tx) {
|
|
2207
2293
|
const at = /* @__PURE__ */ __name(() => resolveTx(tx), "at");
|
|
2208
2294
|
const ops = {
|
|
@@ -2216,9 +2302,11 @@ function createOps(tx) {
|
|
|
2216
2302
|
async insert(table, data) {
|
|
2217
2303
|
const cols = Object.keys(data);
|
|
2218
2304
|
if (cols.length === 0) throw new Error(`insert into ${table}: no columns given`);
|
|
2219
|
-
const
|
|
2220
|
-
const
|
|
2221
|
-
const
|
|
2305
|
+
const params = [];
|
|
2306
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2307
|
+
const tuple = buildValueTuple(table, cols, data, add, "insert");
|
|
2308
|
+
const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`;
|
|
2309
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
2222
2310
|
const inserted = rows[0];
|
|
2223
2311
|
if (!inserted) {
|
|
2224
2312
|
throw new Error(`insert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`);
|
|
@@ -2226,6 +2314,41 @@ function createOps(tx) {
|
|
|
2226
2314
|
return asTableRow(table, inserted);
|
|
2227
2315
|
},
|
|
2228
2316
|
/**
|
|
2317
|
+
* Çok satırı TEK statement'la ekle.
|
|
2318
|
+
*
|
|
2319
|
+
* Motor bunu zaten yapıyordu ve yalnız PLAN yolundan erişilebiliyordu
|
|
2320
|
+
* (`tx.public.x.insertMany`). Dışarıda tek kapı `insert(row)` olduğu için
|
|
2321
|
+
* "şu 200 satırı yaz" ya 200 gidiş-dönüş ya da ham SQL oluyordu — ölçüldü:
|
|
2322
|
+
* bir müşteri projesinde bir seed rutini satırları tek tek siliyor
|
|
2323
|
+
* (`deleteMany` VARKEN) ve tek tek ekliyor.
|
|
2324
|
+
*
|
|
2325
|
+
* SATIRLARIN ANAHTAR KÜMESİ AYNI OLMAK ZORUNDA. Kolon listesi tek bir
|
|
2326
|
+
* statement'ta paylaşıldığı için ilk satırdan türer; farklı anahtar taşıyan
|
|
2327
|
+
* bir satırın fazla kolonu SESSİZCE YAZILMAZDI. Eksik kolon için `null`
|
|
2328
|
+
* yazın ya da varsayılanı istiyorsanız o satırları ayrı çağrıda ekleyin.
|
|
2329
|
+
*/
|
|
2330
|
+
async insertMany(table, rows, opts) {
|
|
2331
|
+
if (rows.length === 0) return [];
|
|
2332
|
+
const first = rows[0];
|
|
2333
|
+
const cols = Object.keys(first);
|
|
2334
|
+
if (cols.length === 0) throw new Error(`insertMany into ${table}: no columns given`);
|
|
2335
|
+
for (let i = 1; i < rows.length; i++) {
|
|
2336
|
+
const keys = Object.keys(rows[i]);
|
|
2337
|
+
const missing = cols.filter((c) => !(c in rows[i]));
|
|
2338
|
+
const extra = keys.filter((k) => !cols.includes(k));
|
|
2339
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
2340
|
+
throw new Error(`insertMany(${table}): ${i}. sat\u0131r\u0131n kolonlar\u0131 ilk sat\u0131rla ayn\u0131 de\u011Fil` + (missing.length > 0 ? ` (eksik: ${missing.join(", ")})` : "") + (extra.length > 0 ? ` (fazla: ${extra.join(", ")})` : "") + `. Tek statement kolon listesini PAYLA\u015EIR, yani fazla kolon sessizce yaz\u0131lmaz ve eksik kolon yanl\u0131\u015F s\xFCtuna kayar. Eksikler i\xE7in null yaz\u0131n, ya da farkl\u0131 \u015Fekilli sat\u0131rlar\u0131 ayr\u0131 \xE7a\u011Fr\u0131larda ekleyin.`);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
const params = [];
|
|
2344
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2345
|
+
const tuples = rows.map((r) => `(${buildValueTuple(table, cols, r, add, "insertMany")})`);
|
|
2346
|
+
const tail = opts !== void 0 && opts.onConflict.length > 0 ? ` ${onConflictTail(cols, opts.onConflict, opts.action ?? "ignore")}` : "";
|
|
2347
|
+
const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail} RETURNING *`;
|
|
2348
|
+
const out = await (await at()).unsafe(sql, params);
|
|
2349
|
+
return out.map((r) => asTableRow(table, r));
|
|
2350
|
+
},
|
|
2351
|
+
/**
|
|
2229
2352
|
* Bir ADI kilitle — satırı değil (FR-054).
|
|
2230
2353
|
*
|
|
2231
2354
|
* "Aynı anda tek bir fatura kapanışı koşsun" gibi, kilitlenecek satırı
|
|
@@ -2311,8 +2434,10 @@ function createOps(tx) {
|
|
|
2311
2434
|
const live = await at();
|
|
2312
2435
|
try {
|
|
2313
2436
|
return await live.savepoint(async (sp) => {
|
|
2314
|
-
const
|
|
2315
|
-
const
|
|
2437
|
+
const params = [];
|
|
2438
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2439
|
+
const tuple = buildValueTuple(table, cols, data, add, "claim");
|
|
2440
|
+
const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`, params);
|
|
2316
2441
|
if (!rows[0]) {
|
|
2317
2442
|
throw new Error(`claim(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si).`);
|
|
2318
2443
|
}
|
|
@@ -2363,10 +2488,11 @@ function createOps(tx) {
|
|
|
2363
2488
|
const cols = Object.keys(data);
|
|
2364
2489
|
if (cols.length === 0) throw new Error(`upsert into ${table}: no columns given`);
|
|
2365
2490
|
if (searchConfigFor(table)?.validity) {
|
|
2366
|
-
const
|
|
2491
|
+
const bindData = [];
|
|
2492
|
+
const addPut = /* @__PURE__ */ __name((v) => `$${bindData.push(v)}`, "addPut");
|
|
2493
|
+
const ph = buildValueTuple(table, cols, data, addPut, "put");
|
|
2367
2494
|
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 *`;
|
|
2368
2495
|
const live = await at();
|
|
2369
|
-
const bindData = asBindParams(table, cols, data, "put");
|
|
2370
2496
|
const first = await live.unsafe(insertSql, bindData);
|
|
2371
2497
|
if (first[0]) return asTableRow(table, first[0]);
|
|
2372
2498
|
const condSql = conflict.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ");
|
|
@@ -2391,12 +2517,14 @@ function createOps(tx) {
|
|
|
2391
2517
|
]);
|
|
2392
2518
|
return asTableRow(table, second[0]);
|
|
2393
2519
|
}
|
|
2394
|
-
const
|
|
2520
|
+
const params = [];
|
|
2521
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2522
|
+
const placeholders = buildValueTuple(table, cols, data, add, "put");
|
|
2395
2523
|
const conflictSet = new Set(conflict);
|
|
2396
2524
|
const assignments = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
|
|
2397
2525
|
const action = assignments.length ? `DO UPDATE SET ${assignments.join(", ")}` : `DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
|
|
2398
2526
|
const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
|
|
2399
|
-
const rows = await (await at()).unsafe(sql,
|
|
2527
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
2400
2528
|
const row = rows[0];
|
|
2401
2529
|
if (!row) {
|
|
2402
2530
|
throw new Error(`upsert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`);
|
|
@@ -2407,12 +2535,14 @@ function createOps(tx) {
|
|
|
2407
2535
|
assertNotAppendOnly("update", table);
|
|
2408
2536
|
const cols = Object.keys(data);
|
|
2409
2537
|
if (cols.length === 0) return ops.findById(table, id);
|
|
2410
|
-
const
|
|
2411
|
-
const
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2538
|
+
const params = [];
|
|
2539
|
+
const add = /* @__PURE__ */ __name((v) => {
|
|
2540
|
+
params.push(v);
|
|
2541
|
+
return `$${params.length}`;
|
|
2542
|
+
}, "add");
|
|
2543
|
+
const assignments = buildSetAssignments(table, cols, data, add, "update");
|
|
2544
|
+
const sql = `UPDATE ${quoteTable(table)} SET ${assignments} WHERE id = ${add(id)} RETURNING *`;
|
|
2545
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
2416
2546
|
return rows[0] ? asTableRow(table, rows[0]) : null;
|
|
2417
2547
|
},
|
|
2418
2548
|
async delete(table, id) {
|
|
@@ -2458,21 +2588,7 @@ function createOps(tx) {
|
|
|
2458
2588
|
params.push(v);
|
|
2459
2589
|
return `$${params.length}`;
|
|
2460
2590
|
}, "add");
|
|
2461
|
-
const assignments =
|
|
2462
|
-
const expr = columnExprOf(set[c]);
|
|
2463
|
-
if (expr !== null && expr.fn === "now") {
|
|
2464
|
-
return `${quoteIdent(c)} = now()`;
|
|
2465
|
-
}
|
|
2466
|
-
if (expr !== null) {
|
|
2467
|
-
const operator = expr.fn === "inc" ? "+" : "-";
|
|
2468
|
-
const t = transformsOf(currentSchema, table).get(c);
|
|
2469
|
-
const amount = t?.toDb === void 0 || expr.by === null || expr.by === void 0 ? expr.by : t.toDb(expr.by);
|
|
2470
|
-
return `${quoteIdent(c)} = ${quoteIdent(c)} ${operator} ${add(amount)}`;
|
|
2471
|
-
}
|
|
2472
|
-
return `${quoteIdent(c)} = ${add(asBindParams(table, [
|
|
2473
|
-
c
|
|
2474
|
-
], set, "updateMany")[0])}`;
|
|
2475
|
-
}).join(", ");
|
|
2591
|
+
const assignments = buildSetAssignments(table, cols, set, add, "updateMany");
|
|
2476
2592
|
const whereSql = compileWhere(table, known, where, add, "updateMany");
|
|
2477
2593
|
assertHasPredicate(whereSql, "updateMany", table);
|
|
2478
2594
|
const pk = primaryKeyOf(table);
|
|
@@ -2930,8 +3046,10 @@ function createOps(tx) {
|
|
|
2930
3046
|
[cfg.pk]: newId
|
|
2931
3047
|
};
|
|
2932
3048
|
const cols = Object.keys(data);
|
|
2933
|
-
const
|
|
2934
|
-
const
|
|
3049
|
+
const params = [];
|
|
3050
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
3051
|
+
const tuple = buildValueTuple(table, cols, data, add, "supersede");
|
|
3052
|
+
const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`, params);
|
|
2935
3053
|
if (!rows[0]) {
|
|
2936
3054
|
throw new Error(`supersede(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si)`);
|
|
2937
3055
|
}
|
|
@@ -3887,6 +4005,14 @@ var DiError = class extends Error {
|
|
|
3887
4005
|
path;
|
|
3888
4006
|
at;
|
|
3889
4007
|
fixes;
|
|
4008
|
+
/**
|
|
4009
|
+
* EVERY `DiKind` IS A FACT ABOUT THE AUTHOR'S DECLARATIONS — an unresolvable
|
|
4010
|
+
* dependency, a cycle, a class no module owns. Not one of them is something
|
|
4011
|
+
* the environment could change, so restarting re-reads the same bytes and
|
|
4012
|
+
* fails identically. The cure is a new artifact, and the tag is how the
|
|
4013
|
+
* runtime learns that without matching on wording.
|
|
4014
|
+
*/
|
|
4015
|
+
[DECLARATION_REFUSAL] = true;
|
|
3890
4016
|
constructor(kind, path, at, detail, fixes) {
|
|
3891
4017
|
super(`
|
|
3892
4018
|
Kind: ${kind}
|
|
@@ -4302,7 +4428,7 @@ async function createApp(opts) {
|
|
|
4302
4428
|
assertNoOrphanEntryPoints(controllers, container.owned);
|
|
4303
4429
|
const routes = buildRouteTable(controllers, liveContainer);
|
|
4304
4430
|
if (routes.length === 0) {
|
|
4305
|
-
throw new
|
|
4431
|
+
throw new DeclarationRefused("boot refused: zero endpoints collected \u2014 nothing would answer.");
|
|
4306
4432
|
}
|
|
4307
4433
|
const sql = opts.sql ?? await defaultSqlDriver(config);
|
|
4308
4434
|
await sql.unsafe("select 1");
|