@palbase/backend 27.1.0 → 29.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 +284 -50
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +4 -4
- package/dist/{chunk-JVZQCC77.js → chunk-3TUJWHC2.js} +8 -3
- package/dist/chunk-3TUJWHC2.js.map +1 -0
- package/dist/{chunk-EB3TUX5J.js → chunk-DHJ3SYAE.js} +251 -50
- package/dist/chunk-DHJ3SYAE.js.map +1 -0
- package/dist/{chunk-XABHGMUT.js → chunk-ENZ2RFFJ.js} +2 -2
- package/dist/{chunk-BQN723PL.js → chunk-ESSQ3YML.js} +73 -7
- package/dist/chunk-ESSQ3YML.js.map +1 -0
- package/dist/{chunk-OZKSM3JW.js → chunk-SG4UTNOP.js} +7 -3
- package/dist/chunk-SG4UTNOP.js.map +1 -0
- package/dist/db/index.cjs +8 -2
- 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 +294 -50
- 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-V7QRh1wg.d.ts → index-BOe82fsK.d.ts} +256 -19
- package/dist/{index-CAKOgAlP.d.ts → index-BYDUKexK.d.ts} +139 -10
- package/dist/{index-H-0qv5d4.d.cts → index-BZfmdRmh.d.cts} +139 -10
- package/dist/{index-CgE4sVhg.d.cts → index-ChvoVczS.d.cts} +256 -19
- package/dist/index.cjs +81 -6
- 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-DHsPDY0_.d.cts → registry-B2KfKWv1.d.cts} +1 -1
- package/dist/{registry-4EI8aaFs.d.ts → registry-DaqHsOq8.d.ts} +1 -1
- package/dist/test/index.cjs +129 -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 +126 -6
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/database.md +118 -1
- package/docs/llms-full.txt +119 -2
- package/package.json +1 -1
- package/template/package.json +1 -1
- package/dist/chunk-BQN723PL.js.map +0 -1
- package/dist/chunk-EB3TUX5J.js.map +0 -1
- package/dist/chunk-JVZQCC77.js.map +0 -1
- package/dist/chunk-OZKSM3JW.js.map +0 -1
- /package/dist/{chunk-XABHGMUT.js.map → chunk-ENZ2RFFJ.js.map} +0 -0
package/dist/db/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { f as AnyColumn, k as ColumnBuilder, l as ColumnDef, m as ColumnMap, n as ColumnType, E as EXTENSION_DEPENDENCIES, s as EmbeddingModelRef, u as EnvServiceDatabase, w as EnvTypedDatabase, N as InsertShape, V as Materialized, a0 as OnDeleteAction, a1 as PALBASE_EXTENSIONS, aA as PalbaseExtension, bx as PolicyBuilder, by as PolicyCommand, bz as PolicyDef, bC as PolicyMode, bE as RawConstraintDef, bG as Ref, bI as RowShape, S as SchemaDef, bR as TABLE_META, bS as TableDef, bT as TableHandle, bU as TableInput, bW as TxColumnExpr, bX as TxInsertShape, bZ as TxNow, b_ as TxPlan, T as TxPlanBody, b$ as TxPlanError, c0 as TxPlanHandle, c1 as TxPlanOpResult, c2 as TxPlanRejection, b as TxPlanResponse, c3 as TxRefError, c4 as TxRow, c5 as TxRows, c6 as TxSelectOptions, c7 as TxSetShape, c9 as TxTable, ca as TxTables, cb as TxWhere, ch as TypedDB, ci as TypedTable, cj as TypedTx, cs as bigint, ct as boolean, cv as dec, cy as defineSchema, cz as defineTable, cA as enumType, cC as inc, cF as installationRef, cG as integer, cH as isPalbaseExtension, cJ as jsonb, cK as makeTypedDB, cL as now, cP as openai, cQ as ownedByUser, cR as policy, cS as raw, cU as text, cV as timestamp, cW as userRef, cX as uuid, cY as vector } from '../index-
|
|
1
|
+
export { f as AnyColumn, k as ColumnBuilder, l as ColumnDef, m as ColumnMap, n as ColumnType, E as EXTENSION_DEPENDENCIES, s as EmbeddingModelRef, u as EnvServiceDatabase, w as EnvTypedDatabase, N as InsertShape, V as Materialized, a0 as OnDeleteAction, a1 as PALBASE_EXTENSIONS, aA as PalbaseExtension, bx as PolicyBuilder, by as PolicyCommand, bz as PolicyDef, bC as PolicyMode, bE as RawConstraintDef, bG as Ref, bI as RowShape, S as SchemaDef, bR as TABLE_META, bS as TableDef, bT as TableHandle, bU as TableInput, bW as TxColumnExpr, bX as TxInsertShape, bZ as TxNow, b_ as TxPlan, T as TxPlanBody, b$ as TxPlanError, c0 as TxPlanHandle, c1 as TxPlanOpResult, c2 as TxPlanRejection, b as TxPlanResponse, c3 as TxRefError, c4 as TxRow, c5 as TxRows, c6 as TxSelectOptions, c7 as TxSetShape, c9 as TxTable, ca as TxTables, cb as TxWhere, ch as TypedDB, ci as TypedTable, cj as TypedTx, cs as bigint, ct as boolean, cv as dec, cy as defineSchema, cz as defineTable, cA as enumType, cC as inc, cF as installationRef, cG as integer, cH as isPalbaseExtension, cJ as jsonb, cK as makeTypedDB, cL as now, cP as openai, cQ as ownedByUser, cR as policy, cS as raw, cU as text, cV as timestamp, cW as userRef, cX as uuid, cY as vector } from '../index-ChvoVczS.cjs';
|
|
2
2
|
import './env.cjs';
|
|
3
3
|
import '../stack.cjs';
|
|
4
4
|
import 'zod';
|
package/dist/db/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { f as AnyColumn, k as ColumnBuilder, l as ColumnDef, m as ColumnMap, n as ColumnType, E as EXTENSION_DEPENDENCIES, s as EmbeddingModelRef, u as EnvServiceDatabase, w as EnvTypedDatabase, N as InsertShape, V as Materialized, a0 as OnDeleteAction, a1 as PALBASE_EXTENSIONS, aA as PalbaseExtension, bx as PolicyBuilder, by as PolicyCommand, bz as PolicyDef, bC as PolicyMode, bE as RawConstraintDef, bG as Ref, bI as RowShape, S as SchemaDef, bR as TABLE_META, bS as TableDef, bT as TableHandle, bU as TableInput, bW as TxColumnExpr, bX as TxInsertShape, bZ as TxNow, b_ as TxPlan, T as TxPlanBody, b$ as TxPlanError, c0 as TxPlanHandle, c1 as TxPlanOpResult, c2 as TxPlanRejection, b as TxPlanResponse, c3 as TxRefError, c4 as TxRow, c5 as TxRows, c6 as TxSelectOptions, c7 as TxSetShape, c9 as TxTable, ca as TxTables, cb as TxWhere, ch as TypedDB, ci as TypedTable, cj as TypedTx, cs as bigint, ct as boolean, cv as dec, cy as defineSchema, cz as defineTable, cA as enumType, cC as inc, cF as installationRef, cG as integer, cH as isPalbaseExtension, cJ as jsonb, cK as makeTypedDB, cL as now, cP as openai, cQ as ownedByUser, cR as policy, cS as raw, cU as text, cV as timestamp, cW as userRef, cX as uuid, cY as vector } from '../index-
|
|
1
|
+
export { f as AnyColumn, k as ColumnBuilder, l as ColumnDef, m as ColumnMap, n as ColumnType, E as EXTENSION_DEPENDENCIES, s as EmbeddingModelRef, u as EnvServiceDatabase, w as EnvTypedDatabase, N as InsertShape, V as Materialized, a0 as OnDeleteAction, a1 as PALBASE_EXTENSIONS, aA as PalbaseExtension, bx as PolicyBuilder, by as PolicyCommand, bz as PolicyDef, bC as PolicyMode, bE as RawConstraintDef, bG as Ref, bI as RowShape, S as SchemaDef, bR as TABLE_META, bS as TableDef, bT as TableHandle, bU as TableInput, bW as TxColumnExpr, bX as TxInsertShape, bZ as TxNow, b_ as TxPlan, T as TxPlanBody, b$ as TxPlanError, c0 as TxPlanHandle, c1 as TxPlanOpResult, c2 as TxPlanRejection, b as TxPlanResponse, c3 as TxRefError, c4 as TxRow, c5 as TxRows, c6 as TxSelectOptions, c7 as TxSetShape, c9 as TxTable, ca as TxTables, cb as TxWhere, ch as TypedDB, ci as TypedTable, cj as TypedTx, cs as bigint, ct as boolean, cv as dec, cy as defineSchema, cz as defineTable, cA as enumType, cC as inc, cF as installationRef, cG as integer, cH as isPalbaseExtension, cJ as jsonb, cK as makeTypedDB, cL as now, cP as openai, cQ as ownedByUser, cR as policy, cS as raw, cU as text, cV as timestamp, cW as userRef, cX as uuid, cY as vector } from '../index-BOe82fsK.js';
|
|
2
2
|
import './env.js';
|
|
3
3
|
import '../stack.js';
|
|
4
4
|
import 'zod';
|
package/dist/db/index.js
CHANGED
|
@@ -21,17 +21,17 @@ import {
|
|
|
21
21
|
userRef,
|
|
22
22
|
uuid,
|
|
23
23
|
vector
|
|
24
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-ENZ2RFFJ.js";
|
|
25
25
|
import {
|
|
26
26
|
makeTypedDB
|
|
27
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-SG4UTNOP.js";
|
|
28
28
|
import {
|
|
29
29
|
TxPlanError,
|
|
30
30
|
TxRefError,
|
|
31
31
|
dec,
|
|
32
32
|
inc,
|
|
33
33
|
now
|
|
34
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-3TUJWHC2.js";
|
|
35
35
|
import "../chunk-CGNN2PUH.js";
|
|
36
36
|
import "../chunk-VXPNPVAG.js";
|
|
37
37
|
export {
|
package/dist/engine/index.cjs
CHANGED
|
@@ -23,6 +23,8 @@ var engine_exports = {};
|
|
|
23
23
|
__export(engine_exports, {
|
|
24
24
|
AuthVerifier: () => AuthVerifier,
|
|
25
25
|
BootRefused: () => BootRefused,
|
|
26
|
+
DECLARATION_REFUSAL: () => DECLARATION_REFUSAL,
|
|
27
|
+
DeclarationRefused: () => DeclarationRefused,
|
|
26
28
|
RateLimiter: () => RateLimiter,
|
|
27
29
|
buildRouteTable: () => buildRouteTable,
|
|
28
30
|
createApp: () => createApp,
|
|
@@ -32,6 +34,7 @@ __export(engine_exports, {
|
|
|
32
34
|
effectiveAuth: () => effectiveAuth,
|
|
33
35
|
hostAllowed: () => hostAllowed,
|
|
34
36
|
installEgressFence: () => installEgressFence,
|
|
37
|
+
isDeclarationRefused: () => isDeclarationRefused,
|
|
35
38
|
loadConfig: () => loadConfig,
|
|
36
39
|
makeMemoryCache: () => makeMemoryCache,
|
|
37
40
|
matchRoute: () => matchRoute,
|
|
@@ -116,8 +119,11 @@ __name(isColumnExpr, "isColumnExpr");
|
|
|
116
119
|
function assertNoExpressionHandles(caller, table, cols, data) {
|
|
117
120
|
for (const c of cols) {
|
|
118
121
|
const v = data[c];
|
|
122
|
+
if (isNowExpr(v)) {
|
|
123
|
+
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() }).`);
|
|
124
|
+
}
|
|
119
125
|
if (isColumnExpr(v)) {
|
|
120
|
-
throw new Error(`${caller}(${table}): "${c}" bir ifade tutama\u011F\u0131 ald\u0131 (increment()/decrement()
|
|
126
|
+
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.`);
|
|
121
127
|
}
|
|
122
128
|
if (isColRef(v)) {
|
|
123
129
|
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.`);
|
|
@@ -409,7 +415,9 @@ function encodeMap(map, allowColumnExpr) {
|
|
|
409
415
|
const out = {};
|
|
410
416
|
for (const key of Object.keys(map).sort()) {
|
|
411
417
|
const value = map[key];
|
|
412
|
-
if (value === void 0)
|
|
418
|
+
if (value === void 0) {
|
|
419
|
+
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.`);
|
|
420
|
+
}
|
|
413
421
|
out[key] = encodeValue(value, key, allowColumnExpr);
|
|
414
422
|
}
|
|
415
423
|
return out;
|
|
@@ -937,6 +945,11 @@ function makeTableProxy(ops, prefix) {
|
|
|
937
945
|
const name = `${prefix}${prop}`;
|
|
938
946
|
return {
|
|
939
947
|
insert: /* @__PURE__ */ __name((data) => ops().insert(name, data), "insert"),
|
|
948
|
+
// DÖRDÜNCÜ FİİL, aynı gerekçeyle: tip söz veriyor, ops katmanı
|
|
949
|
+
// uyguluyor, ve bir handler'ın gerçekten dokunduğu yer BURASI.
|
|
950
|
+
// `runtime-table-verbs` kapısı bunu adıyla saydı.
|
|
951
|
+
aggregate: /* @__PURE__ */ __name((q) => ops().aggregate(name, q), "aggregate"),
|
|
952
|
+
insertMany: /* @__PURE__ */ __name((rows, opts) => ops().insertMany(name, rows, opts), "insertMany"),
|
|
940
953
|
update: /* @__PURE__ */ __name((q) => ops().update(name, q.where.id, q.set), "update"),
|
|
941
954
|
delete: /* @__PURE__ */ __name((id) => ops().delete(name, id), "delete"),
|
|
942
955
|
findById: /* @__PURE__ */ __name((id) => ops().findById(name, id), "findById"),
|
|
@@ -993,6 +1006,8 @@ function makeTypedSurface(raw) {
|
|
|
993
1006
|
$claim: /* @__PURE__ */ __name((table, unique, extra) => reco.claim(table, unique, extra), "$claim"),
|
|
994
1007
|
$lockRows: /* @__PURE__ */ __name((table, ids) => reco.lockRows(table, ids), "$lockRows"),
|
|
995
1008
|
$advisoryXactLock: /* @__PURE__ */ __name((key) => reco.advisoryXactLock(key), "$advisoryXactLock"),
|
|
1009
|
+
$aggregate: /* @__PURE__ */ __name((table, q) => raw.aggregate(table, q), "$aggregate"),
|
|
1010
|
+
$insertMany: /* @__PURE__ */ __name((table, rows, opts) => raw.insertMany(table, rows, opts), "$insertMany"),
|
|
996
1011
|
$supersede: /* @__PURE__ */ __name((table, id, row) => raw.supersede(table, id, row), "$supersede")
|
|
997
1012
|
};
|
|
998
1013
|
const base = Object.assign(ops, {
|
|
@@ -1182,6 +1197,24 @@ ${detail}`);
|
|
|
1182
1197
|
}
|
|
1183
1198
|
__name(loadConfig, "loadConfig");
|
|
1184
1199
|
|
|
1200
|
+
// src/refusals.ts
|
|
1201
|
+
var DECLARATION_REFUSAL = /* @__PURE__ */ Symbol.for("palbase.backend.declarationRefusal");
|
|
1202
|
+
var DeclarationRefused = class extends Error {
|
|
1203
|
+
static {
|
|
1204
|
+
__name(this, "DeclarationRefused");
|
|
1205
|
+
}
|
|
1206
|
+
/** @see DECLARATION_REFUSAL — realm-safe, unlike `instanceof`. */
|
|
1207
|
+
[DECLARATION_REFUSAL] = true;
|
|
1208
|
+
constructor(message) {
|
|
1209
|
+
super(message);
|
|
1210
|
+
this.name = "DeclarationRefused";
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
function isDeclarationRefused(e) {
|
|
1214
|
+
return typeof e === "object" && e !== null && e[DECLARATION_REFUSAL] === true;
|
|
1215
|
+
}
|
|
1216
|
+
__name(isDeclarationRefused, "isDeclarationRefused");
|
|
1217
|
+
|
|
1185
1218
|
// src/engine/auth.ts
|
|
1186
1219
|
function b64urlToBytes(s) {
|
|
1187
1220
|
const pad = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -1308,9 +1341,11 @@ function effectiveAuth(routeAuth, controllerAuth) {
|
|
|
1308
1341
|
};
|
|
1309
1342
|
const o = spec;
|
|
1310
1343
|
const role = typeof o.role === "string" && o.role.trim() !== "" ? o.role.trim() : void 0;
|
|
1344
|
+
const permission = typeof o.permission === "string" && o.permission.trim() !== "" ? o.permission.trim() : void 0;
|
|
1311
1345
|
return {
|
|
1312
1346
|
required: o.required !== false,
|
|
1313
1347
|
role,
|
|
1348
|
+
permission,
|
|
1314
1349
|
verifiedEmail: o.verifiedEmail === true
|
|
1315
1350
|
};
|
|
1316
1351
|
}
|
|
@@ -1504,12 +1539,12 @@ function buildRelations(schemas) {
|
|
|
1504
1539
|
const held = names.get(name);
|
|
1505
1540
|
if (held !== void 0) {
|
|
1506
1541
|
if (held.def.owns === true && origin.def.owns === true) {
|
|
1507
|
-
throw new
|
|
1542
|
+
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.`);
|
|
1508
1543
|
}
|
|
1509
1544
|
const heldFix = renameCall(held);
|
|
1510
1545
|
const originFix = renameCall(origin);
|
|
1511
1546
|
const remedy = heldFix === originFix ? `rename one of them: ${heldFix}` : `rename one of them \u2014 "${held.table}.${held.column}": ${heldFix}; "${origin.table}.${origin.column}": ${originFix}`;
|
|
1512
|
-
throw new
|
|
1547
|
+
throw new DeclarationRefused(`table "${tableKey}": ${describeOrigin(held)} and ${describeOrigin(origin)} both resolve to the relation name "${name}" \u2014 ${remedy}.`);
|
|
1513
1548
|
}
|
|
1514
1549
|
names.set(name, origin);
|
|
1515
1550
|
out.get(tableKey)?.push(edge);
|
|
@@ -2087,7 +2122,7 @@ function compileWhereBare(table, colSet, where, add, caller = "search", alias =
|
|
|
2087
2122
|
const rel = rels.find((r) => r.name === relName);
|
|
2088
2123
|
if (rel === void 0) {
|
|
2089
2124
|
const known = rels.map((r) => r.name).sort();
|
|
2090
|
-
throw new Error(`${caller}(${table}): has.${relName} diye bir ili\u015Fki yok
|
|
2125
|
+
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.`));
|
|
2091
2126
|
}
|
|
2092
2127
|
const ourCol = rel.kind === "many" ? primaryKeyOf(table) : rel.via;
|
|
2093
2128
|
const theirCol = rel.kind === "many" ? rel.via : primaryKeyOf(rel.to);
|
|
@@ -2267,6 +2302,57 @@ async function embedQuery(embed, text) {
|
|
|
2267
2302
|
}
|
|
2268
2303
|
}
|
|
2269
2304
|
__name(embedQuery, "embedQuery");
|
|
2305
|
+
function buildSetAssignments(table, cols, set, add, caller) {
|
|
2306
|
+
return cols.map((c) => {
|
|
2307
|
+
const expr = columnExprOf(set[c]);
|
|
2308
|
+
if (expr !== null && expr.fn === "now") {
|
|
2309
|
+
return `${quoteIdent(c)} = now()`;
|
|
2310
|
+
}
|
|
2311
|
+
if (expr !== null) {
|
|
2312
|
+
const operator = expr.fn === "inc" ? "+" : "-";
|
|
2313
|
+
const t = transformsOf(currentSchema, table).get(c);
|
|
2314
|
+
const amount = t?.toDb === void 0 || expr.by === null || expr.by === void 0 ? expr.by : t.toDb(expr.by);
|
|
2315
|
+
return `${quoteIdent(c)} = ${quoteIdent(c)} ${operator} ${add(amount)}`;
|
|
2316
|
+
}
|
|
2317
|
+
return `${quoteIdent(c)} = ${add(asBindParams(table, [
|
|
2318
|
+
c
|
|
2319
|
+
], set, caller)[0])}`;
|
|
2320
|
+
}).join(", ");
|
|
2321
|
+
}
|
|
2322
|
+
__name(buildSetAssignments, "buildSetAssignments");
|
|
2323
|
+
function buildValueTuple(table, cols, data, add, caller) {
|
|
2324
|
+
return cols.map((c) => {
|
|
2325
|
+
const expr = columnExprOf(data[c]);
|
|
2326
|
+
if (expr !== null && expr.fn === "now") return "now()";
|
|
2327
|
+
if (expr !== null) {
|
|
2328
|
+
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) } }).`);
|
|
2329
|
+
}
|
|
2330
|
+
return add(asBindParams(table, [
|
|
2331
|
+
c
|
|
2332
|
+
], data, caller)[0]);
|
|
2333
|
+
}).join(", ");
|
|
2334
|
+
}
|
|
2335
|
+
__name(buildValueTuple, "buildValueTuple");
|
|
2336
|
+
function columnTypeOf(table, col) {
|
|
2337
|
+
const def = tableDefOf(table);
|
|
2338
|
+
const c = def?.columns?.[col];
|
|
2339
|
+
if (c === void 0 || c === null) return null;
|
|
2340
|
+
const d = typeof c === "object" && "_def" in c ? c._def : c;
|
|
2341
|
+
return d !== null && typeof d === "object" && typeof d.type === "string" ? d.type : null;
|
|
2342
|
+
}
|
|
2343
|
+
__name(columnTypeOf, "columnTypeOf");
|
|
2344
|
+
var NUMERIC_PG_TYPES = /* @__PURE__ */ new Set([
|
|
2345
|
+
"integer",
|
|
2346
|
+
"bigint",
|
|
2347
|
+
"numeric"
|
|
2348
|
+
]);
|
|
2349
|
+
function aggregateExpr(table, fn, col, alias) {
|
|
2350
|
+
const q = `t.${quoteIdent(col)}`;
|
|
2351
|
+
const t = columnTypeOf(table, col);
|
|
2352
|
+
if (fn === "sum" || fn === "avg") return `${fn}(${q})::text AS ${quoteIdent(alias)}`;
|
|
2353
|
+
return t !== null && NUMERIC_PG_TYPES.has(t) && t !== "integer" ? `${fn}(${q})::text AS ${quoteIdent(alias)}` : `${fn}(${q}) AS ${quoteIdent(alias)}`;
|
|
2354
|
+
}
|
|
2355
|
+
__name(aggregateExpr, "aggregateExpr");
|
|
2270
2356
|
function createOps(tx) {
|
|
2271
2357
|
const at = /* @__PURE__ */ __name(() => resolveTx(tx), "at");
|
|
2272
2358
|
const ops = {
|
|
@@ -2280,9 +2366,11 @@ function createOps(tx) {
|
|
|
2280
2366
|
async insert(table, data) {
|
|
2281
2367
|
const cols = Object.keys(data);
|
|
2282
2368
|
if (cols.length === 0) throw new Error(`insert into ${table}: no columns given`);
|
|
2283
|
-
const
|
|
2284
|
-
const
|
|
2285
|
-
const
|
|
2369
|
+
const params = [];
|
|
2370
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2371
|
+
const tuple = buildValueTuple(table, cols, data, add, "insert");
|
|
2372
|
+
const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`;
|
|
2373
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
2286
2374
|
const inserted = rows[0];
|
|
2287
2375
|
if (!inserted) {
|
|
2288
2376
|
throw new Error(`insert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`);
|
|
@@ -2290,6 +2378,41 @@ function createOps(tx) {
|
|
|
2290
2378
|
return asTableRow(table, inserted);
|
|
2291
2379
|
},
|
|
2292
2380
|
/**
|
|
2381
|
+
* Çok satırı TEK statement'la ekle.
|
|
2382
|
+
*
|
|
2383
|
+
* Motor bunu zaten yapıyordu ve yalnız PLAN yolundan erişilebiliyordu
|
|
2384
|
+
* (`tx.public.x.insertMany`). Dışarıda tek kapı `insert(row)` olduğu için
|
|
2385
|
+
* "şu 200 satırı yaz" ya 200 gidiş-dönüş ya da ham SQL oluyordu — ölçüldü:
|
|
2386
|
+
* bir müşteri projesinde bir seed rutini satırları tek tek siliyor
|
|
2387
|
+
* (`deleteMany` VARKEN) ve tek tek ekliyor.
|
|
2388
|
+
*
|
|
2389
|
+
* SATIRLARIN ANAHTAR KÜMESİ AYNI OLMAK ZORUNDA. Kolon listesi tek bir
|
|
2390
|
+
* statement'ta paylaşıldığı için ilk satırdan türer; farklı anahtar taşıyan
|
|
2391
|
+
* bir satırın fazla kolonu SESSİZCE YAZILMAZDI. Eksik kolon için `null`
|
|
2392
|
+
* yazın ya da varsayılanı istiyorsanız o satırları ayrı çağrıda ekleyin.
|
|
2393
|
+
*/
|
|
2394
|
+
async insertMany(table, rows, opts) {
|
|
2395
|
+
if (rows.length === 0) return [];
|
|
2396
|
+
const first = rows[0];
|
|
2397
|
+
const cols = Object.keys(first);
|
|
2398
|
+
if (cols.length === 0) throw new Error(`insertMany into ${table}: no columns given`);
|
|
2399
|
+
for (let i = 1; i < rows.length; i++) {
|
|
2400
|
+
const keys = Object.keys(rows[i]);
|
|
2401
|
+
const missing = cols.filter((c) => !(c in rows[i]));
|
|
2402
|
+
const extra = keys.filter((k) => !cols.includes(k));
|
|
2403
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
2404
|
+
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.`);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
const params = [];
|
|
2408
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2409
|
+
const tuples = rows.map((r) => `(${buildValueTuple(table, cols, r, add, "insertMany")})`);
|
|
2410
|
+
const tail = opts !== void 0 && opts.onConflict.length > 0 ? ` ${onConflictTail(cols, opts.onConflict, opts.action ?? "ignore")}` : "";
|
|
2411
|
+
const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")}${tail} RETURNING *`;
|
|
2412
|
+
const out = await (await at()).unsafe(sql, params);
|
|
2413
|
+
return out.map((r) => asTableRow(table, r));
|
|
2414
|
+
},
|
|
2415
|
+
/**
|
|
2293
2416
|
* Bir ADI kilitle — satırı değil (FR-054).
|
|
2294
2417
|
*
|
|
2295
2418
|
* "Aynı anda tek bir fatura kapanışı koşsun" gibi, kilitlenecek satırı
|
|
@@ -2375,8 +2498,10 @@ function createOps(tx) {
|
|
|
2375
2498
|
const live = await at();
|
|
2376
2499
|
try {
|
|
2377
2500
|
return await live.savepoint(async (sp) => {
|
|
2378
|
-
const
|
|
2379
|
-
const
|
|
2501
|
+
const params = [];
|
|
2502
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2503
|
+
const tuple = buildValueTuple(table, cols, data, add, "claim");
|
|
2504
|
+
const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`, params);
|
|
2380
2505
|
if (!rows[0]) {
|
|
2381
2506
|
throw new Error(`claim(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si).`);
|
|
2382
2507
|
}
|
|
@@ -2427,10 +2552,11 @@ function createOps(tx) {
|
|
|
2427
2552
|
const cols = Object.keys(data);
|
|
2428
2553
|
if (cols.length === 0) throw new Error(`upsert into ${table}: no columns given`);
|
|
2429
2554
|
if (searchConfigFor(table)?.validity) {
|
|
2430
|
-
const
|
|
2555
|
+
const bindData = [];
|
|
2556
|
+
const addPut = /* @__PURE__ */ __name((v) => `$${bindData.push(v)}`, "addPut");
|
|
2557
|
+
const ph = buildValueTuple(table, cols, data, addPut, "put");
|
|
2431
2558
|
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 *`;
|
|
2432
2559
|
const live = await at();
|
|
2433
|
-
const bindData = asBindParams(table, cols, data, "put");
|
|
2434
2560
|
const first = await live.unsafe(insertSql, bindData);
|
|
2435
2561
|
if (first[0]) return asTableRow(table, first[0]);
|
|
2436
2562
|
const condSql = conflict.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ");
|
|
@@ -2455,12 +2581,14 @@ function createOps(tx) {
|
|
|
2455
2581
|
]);
|
|
2456
2582
|
return asTableRow(table, second[0]);
|
|
2457
2583
|
}
|
|
2458
|
-
const
|
|
2584
|
+
const params = [];
|
|
2585
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2586
|
+
const placeholders = buildValueTuple(table, cols, data, add, "put");
|
|
2459
2587
|
const conflictSet = new Set(conflict);
|
|
2460
2588
|
const assignments = cols.filter((c) => !conflictSet.has(c)).map((c) => `${quoteIdent(c)} = EXCLUDED.${quoteIdent(c)}`);
|
|
2461
2589
|
const action = assignments.length ? `DO UPDATE SET ${assignments.join(", ")}` : `DO UPDATE SET ${quoteIdent(conflict[0])} = EXCLUDED.${quoteIdent(conflict[0])}`;
|
|
2462
2590
|
const sql = `INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) ON CONFLICT (${conflict.map(quoteIdent).join(", ")}) ${action} RETURNING *`;
|
|
2463
|
-
const rows = await (await at()).unsafe(sql,
|
|
2591
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
2464
2592
|
const row = rows[0];
|
|
2465
2593
|
if (!row) {
|
|
2466
2594
|
throw new Error(`upsert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`);
|
|
@@ -2471,12 +2599,14 @@ function createOps(tx) {
|
|
|
2471
2599
|
assertNotAppendOnly("update", table);
|
|
2472
2600
|
const cols = Object.keys(data);
|
|
2473
2601
|
if (cols.length === 0) return ops.findById(table, id);
|
|
2474
|
-
const
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2602
|
+
const params = [];
|
|
2603
|
+
const add = /* @__PURE__ */ __name((v) => {
|
|
2604
|
+
params.push(v);
|
|
2605
|
+
return `$${params.length}`;
|
|
2606
|
+
}, "add");
|
|
2607
|
+
const assignments = buildSetAssignments(table, cols, data, add, "update");
|
|
2608
|
+
const sql = `UPDATE ${quoteTable(table)} SET ${assignments} WHERE id = ${add(id)} RETURNING *`;
|
|
2609
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
2480
2610
|
return rows[0] ? asTableRow(table, rows[0]) : null;
|
|
2481
2611
|
},
|
|
2482
2612
|
async delete(table, id) {
|
|
@@ -2503,7 +2633,7 @@ function createOps(tx) {
|
|
|
2503
2633
|
* should not silently succeed. Callers who mean every row say so with a
|
|
2504
2634
|
* predicate that is true for every row.
|
|
2505
2635
|
*/
|
|
2506
|
-
async updateMany(table, where, set) {
|
|
2636
|
+
async updateMany(table, where, set, opts) {
|
|
2507
2637
|
assertNotAppendOnly("updateMany", table);
|
|
2508
2638
|
const cols = Object.keys(set);
|
|
2509
2639
|
if (cols.length === 0) {
|
|
@@ -2522,26 +2652,19 @@ function createOps(tx) {
|
|
|
2522
2652
|
params.push(v);
|
|
2523
2653
|
return `$${params.length}`;
|
|
2524
2654
|
}, "add");
|
|
2525
|
-
const assignments =
|
|
2526
|
-
const expr = columnExprOf(set[c]);
|
|
2527
|
-
if (expr !== null && expr.fn === "now") {
|
|
2528
|
-
return `${quoteIdent(c)} = now()`;
|
|
2529
|
-
}
|
|
2530
|
-
if (expr !== null) {
|
|
2531
|
-
const operator = expr.fn === "inc" ? "+" : "-";
|
|
2532
|
-
const t = transformsOf(currentSchema, table).get(c);
|
|
2533
|
-
const amount = t?.toDb === void 0 || expr.by === null || expr.by === void 0 ? expr.by : t.toDb(expr.by);
|
|
2534
|
-
return `${quoteIdent(c)} = ${quoteIdent(c)} ${operator} ${add(amount)}`;
|
|
2535
|
-
}
|
|
2536
|
-
return `${quoteIdent(c)} = ${add(asBindParams(table, [
|
|
2537
|
-
c
|
|
2538
|
-
], set, "updateMany")[0])}`;
|
|
2539
|
-
}).join(", ");
|
|
2655
|
+
const assignments = buildSetAssignments(table, cols, set, add, "updateMany");
|
|
2540
2656
|
const whereSql = compileWhere(table, known, where, add, "updateMany");
|
|
2541
2657
|
assertHasPredicate(whereSql, "updateMany", table);
|
|
2542
2658
|
const pk = primaryKeyOf(table);
|
|
2543
|
-
const
|
|
2544
|
-
|
|
2659
|
+
const wantRows = opts?.returning !== false;
|
|
2660
|
+
const tail = wantRows ? "RETURNING *" : "RETURNING 1";
|
|
2661
|
+
const simple = isSinglePkEquality(where, pk) || pk === null;
|
|
2662
|
+
const body = simple ? `UPDATE ${quoteTable(table)} AS t SET ${assignments} WHERE true${whereSql} ${tail}` : `UPDATE ${quoteTable(table)} AS t SET ${assignments} FROM locked WHERE t.${quoteIdent(pk)} = locked.${quoteIdent(pk)} ` + (wantRows ? "RETURNING t.*" : "RETURNING 1");
|
|
2663
|
+
const lockedCte = simple ? "" : `WITH locked AS (SELECT t.${quoteIdent(pk)} FROM ${quoteTable(table)} t WHERE true${whereSql} ORDER BY t.${quoteIdent(pk)} FOR NO KEY UPDATE) `;
|
|
2664
|
+
const sql = wantRows ? `${lockedCte}${body}` : simple ? `WITH upd AS (${body}) SELECT count(*)::int AS n FROM upd` : `${lockedCte.replace(/ $/, "")}, upd AS (${body}) SELECT count(*)::int AS n FROM upd`;
|
|
2665
|
+
const out = await (await at()).unsafe(sql, params);
|
|
2666
|
+
if (!wantRows) return Number(out[0]?.["n"] ?? 0);
|
|
2667
|
+
return asTableRows(table, out);
|
|
2545
2668
|
},
|
|
2546
2669
|
/**
|
|
2547
2670
|
* Delete every row the filter matches, in ONE statement; resolves to how
|
|
@@ -2571,6 +2694,78 @@ function createOps(tx) {
|
|
|
2571
2694
|
* An empty filter is legitimate HERE: counting a whole table is a read, and
|
|
2572
2695
|
* reads do not destroy anything.
|
|
2573
2696
|
*/
|
|
2697
|
+
/**
|
|
2698
|
+
* Toplama — `count`'un yanına `sum`/`avg`/`min`/`max` ve `groupBy`.
|
|
2699
|
+
*
|
|
2700
|
+
* Var olma nedeni: bunlar olmadan "bu hesabın toplamı" ya `$query`'ye ya da
|
|
2701
|
+
* TÜM satırları çekip JS'te toplamaya düşüyor. İkincisi hem N+1 hem de para
|
|
2702
|
+
* için YANLIŞ — `numeric` JS `number`'ına uğradığı anda kesinliğini
|
|
2703
|
+
* kaybeder.
|
|
2704
|
+
*
|
|
2705
|
+
* `groupBy` verilirse SATIRLAR döner, verilmezse TEK sonuç. Boş kümede
|
|
2706
|
+
* Postgres NULL döndürür, 0 değil — ve bu SDK onu `null` olarak geçiriyor:
|
|
2707
|
+
* "hiç satır yoktu" ile "toplam sıfırdı" farklı şeyler.
|
|
2708
|
+
*/
|
|
2709
|
+
async aggregate(table, q = {}) {
|
|
2710
|
+
const known = schemaColumns(table);
|
|
2711
|
+
const check = /* @__PURE__ */ __name((col, where) => {
|
|
2712
|
+
if (known !== null && !known.has(col)) {
|
|
2713
|
+
throw new Error(`aggregate(${table}): ${where} kolonu "${col}" tabloda yok (FR-016)`);
|
|
2714
|
+
}
|
|
2715
|
+
}, "check");
|
|
2716
|
+
const parts = [];
|
|
2717
|
+
const wants = [];
|
|
2718
|
+
for (const fn of [
|
|
2719
|
+
"sum",
|
|
2720
|
+
"avg",
|
|
2721
|
+
"min",
|
|
2722
|
+
"max"
|
|
2723
|
+
]) {
|
|
2724
|
+
for (const col of q[fn] ?? []) {
|
|
2725
|
+
check(col, fn);
|
|
2726
|
+
if (fn === "sum" || fn === "avg") {
|
|
2727
|
+
const t = columnTypeOf(table, col);
|
|
2728
|
+
if (t !== null && !NUMERIC_PG_TYPES.has(t)) {
|
|
2729
|
+
throw new Error(`aggregate(${table}): ${fn}("${col}") say\u0131sal olmayan bir kolonda \u2014 "${col}" bir ${t} kolonu. Toplama yaln\u0131z integer/bigint/numeric kolonlarda anlaml\u0131; ka\xE7 sat\u0131r oldu\u011Funu saymak i\xE7in count: true kullan\u0131n.`);
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
const alias = `${fn}__${col}`;
|
|
2733
|
+
wants.push({
|
|
2734
|
+
fn,
|
|
2735
|
+
col,
|
|
2736
|
+
alias
|
|
2737
|
+
});
|
|
2738
|
+
parts.push(aggregateExpr(table, fn, col, alias));
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
if (q.count === true) parts.push('count(*) AS "_count"');
|
|
2742
|
+
if (parts.length === 0) {
|
|
2743
|
+
throw new Error(`aggregate(${table}): hi\xE7bir toplama istenmedi \u2014 sonu\xE7 bo\u015F bir nesne olurdu. sum/avg/min/max kolonlar\u0131n\u0131 ya da count: true'yu verin.`);
|
|
2744
|
+
}
|
|
2745
|
+
const groups = q.groupBy ?? [];
|
|
2746
|
+
for (const col of groups) check(col, "groupBy");
|
|
2747
|
+
const groupSel = groups.map((c) => `t.${quoteIdent(c)} AS ${quoteIdent(`g__${c}`)}`);
|
|
2748
|
+
const params = [];
|
|
2749
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
2750
|
+
const whereSql = compileWhere(table, known, q.where ?? {}, add, "aggregate");
|
|
2751
|
+
const groupSql = groups.length > 0 ? ` GROUP BY ${groups.map((c) => `t.${quoteIdent(c)}`).join(", ")}` : "";
|
|
2752
|
+
const sql = `SELECT ${[
|
|
2753
|
+
...groupSel,
|
|
2754
|
+
...parts
|
|
2755
|
+
].join(", ")} FROM ${quoteTable(table)} t WHERE true${whereSql}${groupSql}`;
|
|
2756
|
+
const rows = await (await at()).unsafe(sql, params);
|
|
2757
|
+
const shape = /* @__PURE__ */ __name((r) => {
|
|
2758
|
+
const out = {};
|
|
2759
|
+
for (const c of groups) out[c] = r[`g__${c}`];
|
|
2760
|
+
for (const w of wants) {
|
|
2761
|
+
const bucket = out[w.fn] ??= {};
|
|
2762
|
+
bucket[w.col] = r[w.alias] ?? null;
|
|
2763
|
+
}
|
|
2764
|
+
if (q.count === true) out["count"] = Number(r["_count"] ?? 0);
|
|
2765
|
+
return out;
|
|
2766
|
+
}, "shape");
|
|
2767
|
+
return groups.length > 0 ? rows.map(shape) : shape(rows[0] ?? {});
|
|
2768
|
+
},
|
|
2574
2769
|
async count(table, where = {}) {
|
|
2575
2770
|
const params = [];
|
|
2576
2771
|
const add = /* @__PURE__ */ __name((v) => {
|
|
@@ -2994,8 +3189,10 @@ function createOps(tx) {
|
|
|
2994
3189
|
[cfg.pk]: newId
|
|
2995
3190
|
};
|
|
2996
3191
|
const cols = Object.keys(data);
|
|
2997
|
-
const
|
|
2998
|
-
const
|
|
3192
|
+
const params = [];
|
|
3193
|
+
const add = /* @__PURE__ */ __name((v) => `$${params.push(v)}`, "add");
|
|
3194
|
+
const tuple = buildValueTuple(table, cols, data, add, "supersede");
|
|
3195
|
+
const rows = await sp.unsafe(`INSERT INTO ${quoteTable(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${tuple}) RETURNING *`, params);
|
|
2999
3196
|
if (!rows[0]) {
|
|
3000
3197
|
throw new Error(`supersede(${table}): INSERT sat\u0131r d\xF6nd\xFCrmedi \u2014 yazma reddedildi (b\xFCy\xFCk olas\u0131l\u0131kla bir RLS policy'si)`);
|
|
3001
3198
|
}
|
|
@@ -3937,6 +4134,14 @@ var DiError = class extends Error {
|
|
|
3937
4134
|
path;
|
|
3938
4135
|
at;
|
|
3939
4136
|
fixes;
|
|
4137
|
+
/**
|
|
4138
|
+
* EVERY `DiKind` IS A FACT ABOUT THE AUTHOR'S DECLARATIONS — an unresolvable
|
|
4139
|
+
* dependency, a cycle, a class no module owns. Not one of them is something
|
|
4140
|
+
* the environment could change, so restarting re-reads the same bytes and
|
|
4141
|
+
* fails identically. The cure is a new artifact, and the tag is how the
|
|
4142
|
+
* runtime learns that without matching on wording.
|
|
4143
|
+
*/
|
|
4144
|
+
[DECLARATION_REFUSAL] = true;
|
|
3940
4145
|
constructor(kind, path, at, detail, fixes) {
|
|
3941
4146
|
super(`
|
|
3942
4147
|
Kind: ${kind}
|
|
@@ -4403,6 +4608,26 @@ var AUTHOR_ANSWERED = /* @__PURE__ */ new Set([
|
|
|
4403
4608
|
409,
|
|
4404
4609
|
429
|
|
4405
4610
|
]);
|
|
4611
|
+
async function refuseUnlessAuthorized(client, spec, requestId) {
|
|
4612
|
+
if (spec.permission !== void 0) {
|
|
4613
|
+
const [row] = await client.query("select auth.has_permission($1) as ok", [
|
|
4614
|
+
spec.permission
|
|
4615
|
+
]);
|
|
4616
|
+
if (row?.ok !== true) {
|
|
4617
|
+
return envelope("forbidden", `This endpoint requires the "${spec.permission}" permission`, 403, requestId);
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
if (spec.role !== void 0) {
|
|
4621
|
+
const [row] = await client.query("select $1 = any(auth.app_roles()) as ok", [
|
|
4622
|
+
spec.role
|
|
4623
|
+
]);
|
|
4624
|
+
if (row?.ok !== true) {
|
|
4625
|
+
return envelope("forbidden", `This endpoint requires the "${spec.role}" role`, 403, requestId);
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
return null;
|
|
4629
|
+
}
|
|
4630
|
+
__name(refuseUnlessAuthorized, "refuseUnlessAuthorized");
|
|
4406
4631
|
async function createApp(opts) {
|
|
4407
4632
|
const { config, controllers } = opts;
|
|
4408
4633
|
const container = buildContainer();
|
|
@@ -4420,7 +4645,7 @@ async function createApp(opts) {
|
|
|
4420
4645
|
assertNoOrphanEntryPoints(controllers, container.owned);
|
|
4421
4646
|
const routes = buildRouteTable(controllers, liveContainer);
|
|
4422
4647
|
if (routes.length === 0) {
|
|
4423
|
-
throw new
|
|
4648
|
+
throw new DeclarationRefused("boot refused: zero endpoints collected \u2014 nothing would answer.");
|
|
4424
4649
|
}
|
|
4425
4650
|
const sql = opts.sql ?? await defaultSqlDriver(config);
|
|
4426
4651
|
await sql.unsafe("select 1");
|
|
@@ -4494,8 +4719,23 @@ async function createApp(opts) {
|
|
|
4494
4719
|
if (spec.required && !callerClaims) {
|
|
4495
4720
|
return envelope("unauthorized", "A valid access token is required", 401, requestId);
|
|
4496
4721
|
}
|
|
4497
|
-
if (callerClaims
|
|
4498
|
-
|
|
4722
|
+
if (callerClaims) {
|
|
4723
|
+
const uploadDb = createRequestDatabase(sql, {
|
|
4724
|
+
role: config.dbRole,
|
|
4725
|
+
serviceRole: config.dbServiceRole,
|
|
4726
|
+
claimsJson: JSON.stringify(callerClaims)
|
|
4727
|
+
});
|
|
4728
|
+
try {
|
|
4729
|
+
const refusal = await refuseUnlessAuthorized(uploadDb.client, spec, requestId);
|
|
4730
|
+
if (refusal) {
|
|
4731
|
+
await uploadDb.commit();
|
|
4732
|
+
return refusal;
|
|
4733
|
+
}
|
|
4734
|
+
await uploadDb.commit();
|
|
4735
|
+
} catch (err) {
|
|
4736
|
+
await uploadDb.rollback(err);
|
|
4737
|
+
throw err;
|
|
4738
|
+
}
|
|
4499
4739
|
}
|
|
4500
4740
|
const grant = grantFor(target?.entry, {
|
|
4501
4741
|
userId: typeof callerClaims?.sub === "string" ? callerClaims.sub : null,
|
|
@@ -4528,8 +4768,14 @@ async function createApp(opts) {
|
|
|
4528
4768
|
return envelope("unauthorized", "A valid access token is required", 401, requestId);
|
|
4529
4769
|
}
|
|
4530
4770
|
const userId = typeof claims?.sub === "string" ? claims.sub : void 0;
|
|
4531
|
-
|
|
4532
|
-
|
|
4771
|
+
const db = createRequestDatabase(sql, {
|
|
4772
|
+
role: config.dbRole,
|
|
4773
|
+
serviceRole: config.dbServiceRole,
|
|
4774
|
+
claimsJson: JSON.stringify(claims ?? {})
|
|
4775
|
+
});
|
|
4776
|
+
if (claims) {
|
|
4777
|
+
const refusal = await refuseUnlessAuthorized(db.client, spec, requestId);
|
|
4778
|
+
if (refusal) return refusal;
|
|
4533
4779
|
}
|
|
4534
4780
|
if (claims && spec.verifiedEmail && claims.email_verified !== true) {
|
|
4535
4781
|
return envelope("email_not_verified", "A verified email address is required", 403, requestId);
|
|
@@ -4750,11 +4996,6 @@ async function createApp(opts) {
|
|
|
4750
4996
|
args[p.index] = void 0;
|
|
4751
4997
|
}
|
|
4752
4998
|
}
|
|
4753
|
-
const db = createRequestDatabase(sql, {
|
|
4754
|
-
role: config.dbRole,
|
|
4755
|
-
serviceRole: config.dbServiceRole,
|
|
4756
|
-
claimsJson: JSON.stringify(claims ?? {})
|
|
4757
|
-
});
|
|
4758
4999
|
try {
|
|
4759
5000
|
const services = buildServices(db);
|
|
4760
5001
|
if (meta.options?.sseConfig !== void 0) {
|
|
@@ -4931,6 +5172,8 @@ __name(closeDriver, "closeDriver");
|
|
|
4931
5172
|
0 && (module.exports = {
|
|
4932
5173
|
AuthVerifier,
|
|
4933
5174
|
BootRefused,
|
|
5175
|
+
DECLARATION_REFUSAL,
|
|
5176
|
+
DeclarationRefused,
|
|
4934
5177
|
RateLimiter,
|
|
4935
5178
|
buildRouteTable,
|
|
4936
5179
|
createApp,
|
|
@@ -4940,6 +5183,7 @@ __name(closeDriver, "closeDriver");
|
|
|
4940
5183
|
effectiveAuth,
|
|
4941
5184
|
hostAllowed,
|
|
4942
5185
|
installEgressFence,
|
|
5186
|
+
isDeclarationRefused,
|
|
4943
5187
|
loadConfig,
|
|
4944
5188
|
makeMemoryCache,
|
|
4945
5189
|
matchRoute,
|