@palbase/backend 38.0.15 → 39.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 +46 -1
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +5 -5
- package/dist/{chunk-LFUNRWUE.js → chunk-4J6I4KVQ.js} +67 -3
- package/dist/{chunk-LFUNRWUE.js.map → chunk-4J6I4KVQ.js.map} +1 -1
- package/dist/{chunk-7WAGQ3VR.js → chunk-AR2I4M7I.js} +497 -17
- package/dist/chunk-AR2I4M7I.js.map +1 -0
- package/dist/{chunk-YANGWAIM.js → chunk-H7EKL6HC.js} +2 -2
- package/dist/{chunk-EHKEMHB4.js → chunk-JJSR4BUX.js} +49 -5
- package/dist/chunk-JJSR4BUX.js.map +1 -0
- package/dist/{chunk-CS6NQ6PO.js → chunk-JPTFYHP3.js} +2 -2
- package/dist/{chunk-CS6NQ6PO.js.map → chunk-JPTFYHP3.js.map} +1 -1
- package/dist/{chunk-X7UR3VXA.js → chunk-XZWOMPD3.js} +3 -2
- package/dist/chunk-XZWOMPD3.js.map +1 -0
- package/dist/{chunk-PDD55QWN.js → chunk-ZNQDL466.js} +2 -2
- package/dist/db/index.cjs +378 -14
- 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 +46 -1
- 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 +5 -5
- package/dist/{index-CI7S0Wqv.d.cts → index-B0mjUnxy.d.cts} +25 -2
- package/dist/{index-CUy50OLU.d.ts → index-eN4KzGa5.d.ts} +555 -29
- package/dist/{index-DRx880KY.d.cts → index-pK2At5Yc.d.cts} +555 -29
- package/dist/{index-Ogi30dbt.d.ts → index-yDno9Ju9.d.ts} +25 -2
- package/dist/index.cjs +628 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +183 -8
- package/dist/index.d.ts +183 -8
- package/dist/index.js +74 -6
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +2 -2
- package/dist/openapi/index.d.ts +2 -2
- package/dist/openapi/index.js +2 -2
- package/dist/{registry-C7UCkQf0.d.cts → registry-9dNeVN5d.d.cts} +1 -1
- package/dist/{registry-DJcvbomD.d.ts → registry-B8ddZiMY.d.ts} +1 -1
- package/dist/test/index.cjs +19 -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 +20 -2
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/database.md +79 -0
- package/docs/llms-full.txt +462 -33
- package/docs/schema.md +382 -32
- package/package.json +1 -1
- package/template/package.json +1 -1
- package/dist/chunk-7WAGQ3VR.js.map +0 -1
- package/dist/chunk-EHKEMHB4.js.map +0 -1
- package/dist/chunk-X7UR3VXA.js.map +0 -1
- /package/dist/{chunk-YANGWAIM.js.map → chunk-H7EKL6HC.js.map} +0 -0
- /package/dist/{chunk-PDD55QWN.js.map → chunk-ZNQDL466.js.map} +0 -0
|
@@ -4,17 +4,312 @@ import {
|
|
|
4
4
|
qualifiedTableKey,
|
|
5
5
|
retryBudget,
|
|
6
6
|
runTxPlan
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-4J6I4KVQ.js";
|
|
8
8
|
import {
|
|
9
9
|
isRetryable
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-JPTFYHP3.js";
|
|
11
11
|
import {
|
|
12
12
|
__name
|
|
13
13
|
} from "./chunk-VXPNPVAG.js";
|
|
14
14
|
|
|
15
|
+
// src/db/foreign-key.ts
|
|
16
|
+
var TABLE_META = /* @__PURE__ */ Symbol.for("palbase.table.meta");
|
|
17
|
+
var REF_TARGET = /* @__PURE__ */ Symbol.for("palbase.ref.target");
|
|
18
|
+
var ForeignKeyBuilder = class {
|
|
19
|
+
static {
|
|
20
|
+
__name(this, "ForeignKeyBuilder");
|
|
21
|
+
}
|
|
22
|
+
_def;
|
|
23
|
+
constructor(name) {
|
|
24
|
+
if (name.trim() === "") {
|
|
25
|
+
throw new Error("foreignKey(ad): foreign key'in ad\u0131 bo\u015F olamaz \u2014 Palbase k\u0131s\u0131tlar\u0131 ADA g\xF6re uzla\u015Ft\u0131r\u0131r.");
|
|
26
|
+
}
|
|
27
|
+
this._def = {
|
|
28
|
+
name,
|
|
29
|
+
columns: [],
|
|
30
|
+
refTable: "",
|
|
31
|
+
refColumns: []
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Yerel sütunlar. SIRA KORUNUR — hedef sütunlarla birebir eşleşir. */
|
|
35
|
+
on(...cols) {
|
|
36
|
+
this._def.columns = cols.map((c) => {
|
|
37
|
+
const e = c.expr;
|
|
38
|
+
if (e.kind !== "col") {
|
|
39
|
+
throw new Error(`foreignKey(${this._def.name}).on(): yaln\u0131z kolon referans\u0131 al\u0131r \u2014 ifade bir FK'n\u0131n par\xE7as\u0131 olamaz.`);
|
|
40
|
+
}
|
|
41
|
+
return e.name;
|
|
42
|
+
});
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Hedef tablo ve sütunları. Sütun sayısı `.on()`'unkiyle eşit OLMALI ve bu
|
|
47
|
+
* burada, bildirim yazılırken kontrol edilir — bozuk bir FK wire'a hiç çıkmaz.
|
|
48
|
+
*/
|
|
49
|
+
references(table, ...cols) {
|
|
50
|
+
if (cols.length === 0) {
|
|
51
|
+
throw new Error(`foreignKey(${this._def.name}).references(): en az bir hedef s\xFCtun gerekir.`);
|
|
52
|
+
}
|
|
53
|
+
this._def.refTable = table[TABLE_META].name;
|
|
54
|
+
this._def.refColumns = [
|
|
55
|
+
...cols
|
|
56
|
+
];
|
|
57
|
+
Object.defineProperty(this._def, REF_TARGET, {
|
|
58
|
+
value: table[TABLE_META],
|
|
59
|
+
enumerable: false,
|
|
60
|
+
configurable: true
|
|
61
|
+
});
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
onUpdate(action) {
|
|
65
|
+
this._def.onUpdate = action;
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
onDelete(action) {
|
|
69
|
+
this._def.onDelete = action;
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `MATCH FULL`: referans veren sütunların HEPSİ NULL olmadıkça kısıt
|
|
74
|
+
* değerlendirilir. Varsayılan `MATCH SIMPLE`'da tek bir NULL kısıtı atlatır —
|
|
75
|
+
* `freeze` bunu bilerek kullanır, sıradan bir FK'da genelde istenmez.
|
|
76
|
+
*/
|
|
77
|
+
match(m) {
|
|
78
|
+
this._def.match = m;
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function foreignKey(name) {
|
|
83
|
+
return new ForeignKeyBuilder(name);
|
|
84
|
+
}
|
|
85
|
+
__name(foreignKey, "foreignKey");
|
|
86
|
+
function toForeignKeyDef(b) {
|
|
87
|
+
const def = b instanceof ForeignKeyBuilder ? b._def : b;
|
|
88
|
+
if (def.columns.length === 0) {
|
|
89
|
+
throw new Error(`foreignKey(${def.name}): en az bir yerel s\xFCtun gerekir \u2014 .on(...) \xE7a\u011F\u0131r\u0131n.`);
|
|
90
|
+
}
|
|
91
|
+
if (def.refColumns.length === 0 || def.refTable === "") {
|
|
92
|
+
throw new Error(`foreignKey(${def.name}): hedef tablo ve s\xFCtunlar gerekir \u2014 .references(...) \xE7a\u011F\u0131r\u0131n.`);
|
|
93
|
+
}
|
|
94
|
+
if (def.columns.length !== def.refColumns.length) {
|
|
95
|
+
throw new Error(`foreignKey(${def.name}): ${def.columns.length} yerel s\xFCtun (${def.columns.join(", ")}) ama ${def.refColumns.length} hedef s\xFCtun (${def.refColumns.join(", ")}) \u2014 iki liste e\u015Fit uzunlukta olmal\u0131.`);
|
|
96
|
+
}
|
|
97
|
+
return def;
|
|
98
|
+
}
|
|
99
|
+
__name(toForeignKeyDef, "toForeignKeyDef");
|
|
100
|
+
var FreezeBuilder = class {
|
|
101
|
+
static {
|
|
102
|
+
__name(this, "FreezeBuilder");
|
|
103
|
+
}
|
|
104
|
+
_def;
|
|
105
|
+
constructor(name) {
|
|
106
|
+
if (name.trim() === "") {
|
|
107
|
+
throw new Error("freeze(ad): freeze k\u0131s\u0131t\u0131n\u0131n ad\u0131 bo\u015F olamaz \u2014 Palbase k\u0131s\u0131tlar\u0131 ADA g\xF6re uzla\u015Ft\u0131r\u0131r.");
|
|
108
|
+
}
|
|
109
|
+
this._def = {
|
|
110
|
+
name,
|
|
111
|
+
columns: [],
|
|
112
|
+
refTable: "",
|
|
113
|
+
refColumns: []
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* DONMA KOŞULU — çocuk satırının kendi kolonları üzerinde bir yüklem.
|
|
118
|
+
*
|
|
119
|
+
* Generated sütunlara `CASE WHEN <koşul> THEN <sütun> END` olarak iner; yani
|
|
120
|
+
* koşul yanlışken sütunlar NULL olur ve FK o satır için hiç değerlendirilmez.
|
|
121
|
+
*/
|
|
122
|
+
when(pred) {
|
|
123
|
+
this._def.when = pred.expr;
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
/** Çocuğun DONAN sütunları. SIRA KORUNUR — hedef sütunlarla birebir eşleşir. */
|
|
127
|
+
columns(...cols) {
|
|
128
|
+
if (cols.length === 0) {
|
|
129
|
+
throw new Error(`freeze(${this._def.name}).columns(): en az bir donan s\xFCtun gerekir \u2014 hi\xE7birini vermeyen bir freeze hi\xE7bir \u015Feyi dondurmaz.`);
|
|
130
|
+
}
|
|
131
|
+
this._def.columns = [
|
|
132
|
+
...cols
|
|
133
|
+
];
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Ebeveyn tablo ve donan demetin ORADAKİ karşılığı. Zincirin SON halkası:
|
|
138
|
+
* bildirim burada tamamlanır ve tutarlılığı burada doğrulanır, yani
|
|
139
|
+
* `.references()`'ı unutmuş bir freeze `FreezeDef` üretmediği için tabloya
|
|
140
|
+
* hiç giremez.
|
|
141
|
+
*/
|
|
142
|
+
references(table, ...cols) {
|
|
143
|
+
if (cols.length === 0) {
|
|
144
|
+
throw new Error(`freeze(${this._def.name}).references(): en az bir hedef s\xFCtun gerekir.`);
|
|
145
|
+
}
|
|
146
|
+
this._def.refTable = table[TABLE_META].name;
|
|
147
|
+
this._def.refColumns = [
|
|
148
|
+
...cols
|
|
149
|
+
];
|
|
150
|
+
Object.defineProperty(this._def, REF_TARGET, {
|
|
151
|
+
value: table[TABLE_META],
|
|
152
|
+
enumerable: false,
|
|
153
|
+
configurable: true
|
|
154
|
+
});
|
|
155
|
+
return toFreezeDef(this);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
function freeze(name) {
|
|
159
|
+
return new FreezeBuilder(name);
|
|
160
|
+
}
|
|
161
|
+
__name(freeze, "freeze");
|
|
162
|
+
function toFreezeDef(b) {
|
|
163
|
+
const def = b instanceof FreezeBuilder ? b._def : b;
|
|
164
|
+
if (def.columns.length === 0) {
|
|
165
|
+
throw new Error(`freeze(${def.name}): en az bir donan s\xFCtun gerekir \u2014 .columns(...) \xE7a\u011F\u0131r\u0131n.`);
|
|
166
|
+
}
|
|
167
|
+
if (def.refColumns.length === 0 || def.refTable === "") {
|
|
168
|
+
throw new Error(`freeze(${def.name}): hedef tablo ve s\xFCtunlar gerekir \u2014 .references(...) \xE7a\u011F\u0131r\u0131n.`);
|
|
169
|
+
}
|
|
170
|
+
if (def.columns.length !== def.refColumns.length) {
|
|
171
|
+
throw new Error(`freeze(${def.name}): ${def.columns.length} donan s\xFCtun (${def.columns.join(", ")}) ama ${def.refColumns.length} hedef s\xFCtun (${def.refColumns.join(", ")}) \u2014 iki liste e\u015Fit uzunlukta olmal\u0131.`);
|
|
172
|
+
}
|
|
173
|
+
if (def.when === void 0) {
|
|
174
|
+
throw new Error(`freeze(${def.name}): donma ko\u015Fulu gerekir \u2014 .when(...) \xE7a\u011F\u0131r\u0131n. Ko\u015Fulsuz bir freeze s\u0131radan bir FK'd\u0131r; onun i\xE7in foreignKey(...) var.`);
|
|
175
|
+
}
|
|
176
|
+
const out = {
|
|
177
|
+
name: def.name,
|
|
178
|
+
when: def.when,
|
|
179
|
+
columns: [
|
|
180
|
+
...def.columns
|
|
181
|
+
],
|
|
182
|
+
refTable: def.refTable,
|
|
183
|
+
refColumns: [
|
|
184
|
+
...def.refColumns
|
|
185
|
+
]
|
|
186
|
+
};
|
|
187
|
+
const target = def[REF_TARGET];
|
|
188
|
+
if (target !== void 0) {
|
|
189
|
+
Object.defineProperty(out, REF_TARGET, {
|
|
190
|
+
value: target,
|
|
191
|
+
enumerable: false,
|
|
192
|
+
configurable: true
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
__name(toFreezeDef, "toFreezeDef");
|
|
198
|
+
|
|
15
199
|
// src/db/policy.ts
|
|
16
200
|
import { z } from "zod";
|
|
17
201
|
|
|
202
|
+
// src/db/guard.ts
|
|
203
|
+
var GuardBuilder = class {
|
|
204
|
+
static {
|
|
205
|
+
__name(this, "GuardBuilder");
|
|
206
|
+
}
|
|
207
|
+
_def;
|
|
208
|
+
constructor(name) {
|
|
209
|
+
if (name.trim() === "") {
|
|
210
|
+
throw new Error("guard(ad): guard'\u0131n ad\u0131 bo\u015F olamaz \u2014 Postgres ayn\u0131 olaydaki trigger'lar\u0131 ADA G\xD6RE alfabetik ko\u015Fuyor ve ba\u015Fka s\u0131ra tutamac\u0131 yok.");
|
|
211
|
+
}
|
|
212
|
+
this._def = {
|
|
213
|
+
name
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/** Hangi yazma fiilinde bakılacak. */
|
|
217
|
+
on(event) {
|
|
218
|
+
this._def.event = event;
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Satırın KENDİ kolonları üzerinde koşul — trigger'ın `WHEN` yan tümcesi.
|
|
223
|
+
*
|
|
224
|
+
* Emitter karşılaştırmaları `IS DISTINCT FROM` ile yazar (`<>` NULL'da yanlış
|
|
225
|
+
* cevap verir) ve geçen sütunlardan bir `UPDATE OF (…)` listesi türetir
|
|
226
|
+
* (FR-018).
|
|
227
|
+
*/
|
|
228
|
+
when(pred) {
|
|
229
|
+
this._def.when = pred.expr;
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* ÇAPRAZ SATIR koşulu — gövdedeki `EXISTS` bloğuna iner, `WHEN`'e DEĞİL:
|
|
234
|
+
* Postgres trigger `WHEN`'inde alt sorgu kabul etmiyor (FR-019).
|
|
235
|
+
*
|
|
236
|
+
* Yüklem içinde takma adsız kalan kolon guard'ın KENDİ satırıdır (`NEW`/`OLD`);
|
|
237
|
+
* `r.col(...)` hedef tablonun kolonlarını verir ve ayrı nitelenir. Bu yüzden
|
|
238
|
+
* düğümün `outer` alanı BOŞ kalır: bir guard'ın "dış satır"ı bir tablo takma
|
|
239
|
+
* adı değildir.
|
|
240
|
+
*/
|
|
241
|
+
exists(table, predicate) {
|
|
242
|
+
this._def.exists = exprCtx().existsIn(table, predicate).expr;
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* REDDİN KENDİSİ ve zincirin son halkası: mesaj olmadan bir guard tamamlanmaz.
|
|
247
|
+
*
|
|
248
|
+
* `detail`/`hint` Postgres'in aynı adlı alanlarına, `column` ise `COLUMN`
|
|
249
|
+
* alanına gider — istemci hangi alanın reddedildiğini mesajı parse etmeden
|
|
250
|
+
* okur.
|
|
251
|
+
*/
|
|
252
|
+
refuse(message, opts) {
|
|
253
|
+
this._def.message = message;
|
|
254
|
+
if (opts?.detail !== void 0) this._def.detail = opts.detail;
|
|
255
|
+
if (opts?.hint !== void 0) this._def.hint = opts.hint;
|
|
256
|
+
if (opts?.column !== void 0) this._def.column = opts.column;
|
|
257
|
+
return toGuardDef(this);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
function guard(name) {
|
|
261
|
+
return new GuardBuilder(name);
|
|
262
|
+
}
|
|
263
|
+
__name(guard, "guard");
|
|
264
|
+
function toGuardDef(b) {
|
|
265
|
+
const def = b instanceof GuardBuilder ? b._def : b;
|
|
266
|
+
if (def.event === void 0) {
|
|
267
|
+
throw new Error(`guard(${def.name}): hangi olayda bakaca\u011F\u0131 gerekir \u2014 .on("insert"|"update"|"delete") \xE7a\u011F\u0131r\u0131n.`);
|
|
268
|
+
}
|
|
269
|
+
if (def.when === void 0 && def.exists === void 0) {
|
|
270
|
+
throw new Error(`guard(${def.name}): bir ko\u015Ful gerekir \u2014 .when(...) ya da .exists(...) \xE7a\u011F\u0131r\u0131n. Ko\u015Fulsuz bir guard o olaydaki HER yazmay\u0131 reddeder.`);
|
|
271
|
+
}
|
|
272
|
+
if (def.message === void 0 || def.message.trim() === "") {
|
|
273
|
+
throw new Error(`guard(${def.name}): bo\u015F mesaj kabul edilmez \u2014 reddin mesaj\u0131 guard'\u0131n kullan\u0131c\u0131ya ula\u015Fan TEK \xE7\u0131kt\u0131s\u0131.`);
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
name: def.name,
|
|
277
|
+
event: def.event,
|
|
278
|
+
...def.when !== void 0 ? {
|
|
279
|
+
when: def.when
|
|
280
|
+
} : {},
|
|
281
|
+
...def.exists !== void 0 ? {
|
|
282
|
+
exists: def.exists
|
|
283
|
+
} : {},
|
|
284
|
+
message: def.message,
|
|
285
|
+
...def.detail !== void 0 ? {
|
|
286
|
+
detail: def.detail
|
|
287
|
+
} : {},
|
|
288
|
+
...def.hint !== void 0 ? {
|
|
289
|
+
hint: def.hint
|
|
290
|
+
} : {},
|
|
291
|
+
...def.column !== void 0 ? {
|
|
292
|
+
column: def.column
|
|
293
|
+
} : {}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
__name(toGuardDef, "toGuardDef");
|
|
297
|
+
|
|
298
|
+
// src/db/backfill.ts
|
|
299
|
+
function backfill(name, sql) {
|
|
300
|
+
if (name.trim() === "") {
|
|
301
|
+
throw new Error("backfill(ad, sql): backfill'in ad\u0131 bo\u015F olamaz \u2014 'bir kez ko\u015Ftu' kayd\u0131 ADA g\xF6re tutuluyor.");
|
|
302
|
+
}
|
|
303
|
+
if (sql.trim() === "") {
|
|
304
|
+
throw new Error(`backfill(${name}): SQL bo\u015F olamaz \u2014 bo\u015F bir g\xF6vde ad\u0131 kayda ge\xE7irip KALICI olarak yakar; sonradan yaz\u0131lan ger\xE7ek SQL "zaten ko\u015Ftu" diye hi\xE7 \xE7al\u0131\u015Fmaz.`);
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
name,
|
|
308
|
+
sql
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
__name(backfill, "backfill");
|
|
312
|
+
|
|
18
313
|
// src/db/schema.ts
|
|
19
314
|
var IndexBuilder = class {
|
|
20
315
|
static {
|
|
@@ -29,6 +324,15 @@ var IndexBuilder = class {
|
|
|
29
324
|
name
|
|
30
325
|
};
|
|
31
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* `CREATE UNIQUE INDEX` (FR-007). `.where()` ile birlikte PARTIAL UNIQUE
|
|
329
|
+
* index verir (FR-007b) — "NULL'lar serbest, non-NULL'lar tekil" kalıbının
|
|
330
|
+
* tek bildirimsel yolu.
|
|
331
|
+
*/
|
|
332
|
+
unique() {
|
|
333
|
+
this._def.unique = true;
|
|
334
|
+
return this;
|
|
335
|
+
}
|
|
32
336
|
/** Index'in kolonları. SIRA KORUNUR — çok kolonlu bir index'in anlamı sırasıdır. */
|
|
33
337
|
on(...cols) {
|
|
34
338
|
this._def.columns = cols.map((c) => {
|
|
@@ -86,7 +390,7 @@ function toPolicyDef(p) {
|
|
|
86
390
|
return p instanceof PolicyBuilder ? p._def : p;
|
|
87
391
|
}
|
|
88
392
|
__name(toPolicyDef, "toPolicyDef");
|
|
89
|
-
var
|
|
393
|
+
var TABLE_META2 = /* @__PURE__ */ Symbol.for("palbase.table.meta");
|
|
90
394
|
function defineTable(name, input) {
|
|
91
395
|
if (name.trim() === "") {
|
|
92
396
|
throw new Error("defineTable(name, \u2026): name must not be empty");
|
|
@@ -119,9 +423,46 @@ function defineTable(name, input) {
|
|
|
119
423
|
if (input.primaryKey !== void 0) def.primaryKey = input.primaryKey;
|
|
120
424
|
if (input.unique !== void 0) def.unique = input.unique;
|
|
121
425
|
if (input.raw !== void 0 && input.raw.length > 0) def.raw = input.raw.slice();
|
|
122
|
-
|
|
426
|
+
const declaredChecks = typeof input.checks === "function" ? input.checks(exprCtx(Object.keys(input.columns), 0, name)) : input.checks?.slice() ?? [];
|
|
427
|
+
if (declaredChecks.length > 0) def.checks = declaredChecks;
|
|
123
428
|
const declaredIndexes = (input.indexes?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map((i) => i instanceof IndexBuilder ? i._def : i);
|
|
124
429
|
if (declaredIndexes.length > 0) def.indexes = declaredIndexes;
|
|
430
|
+
const declaredFKs = (input.foreignKeys?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map(toForeignKeyDef);
|
|
431
|
+
if (declaredFKs.length > 0) def.foreignKeys = declaredFKs;
|
|
432
|
+
const declaredFreezes = (input.freeze?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map(toFreezeDef);
|
|
433
|
+
if (declaredFreezes.length > 0) def.freeze = declaredFreezes;
|
|
434
|
+
for (const fz of declaredFreezes) {
|
|
435
|
+
for (const c of fz.columns) {
|
|
436
|
+
if (!(c in input.columns)) {
|
|
437
|
+
throw new Error(`table "${name}": freeze("${fz.name}").columns("${c}") \u2014 no such column on this table. Declared columns: ${Object.keys(input.columns).join(", ")}`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const declaredGuards = (input.guards?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map(toGuardDef);
|
|
442
|
+
if (declaredGuards.length > 0) def.guards = declaredGuards;
|
|
443
|
+
for (const g of declaredGuards) {
|
|
444
|
+
if (g.column !== void 0 && !(g.column in input.columns)) {
|
|
445
|
+
throw new Error(`table "${name}": guard("${g.name}").refuse(\u2026, { column: "${g.column}" }) \u2014 no such column on this table. Declared columns: ${Object.keys(input.columns).join(", ")}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (input.backfills !== void 0 && input.backfills.length > 0) {
|
|
449
|
+
const seen = /* @__PURE__ */ new Set();
|
|
450
|
+
for (const b of input.backfills) {
|
|
451
|
+
if (seen.has(b.name)) {
|
|
452
|
+
throw new Error(`table ${name}: backfill ad\u0131 iki kez bildirildi: ${b.name} \u2014 kay\u0131t ADA g\xF6re tutuluyor, ikincisi hi\xE7 ko\u015Fmazd\u0131.`);
|
|
453
|
+
}
|
|
454
|
+
seen.add(b.name);
|
|
455
|
+
}
|
|
456
|
+
def.backfills = input.backfills.map((b) => ({
|
|
457
|
+
name: b.name,
|
|
458
|
+
sql: b.sql
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
461
|
+
if (input.dropConstraints !== void 0 && input.dropConstraints.length > 0) {
|
|
462
|
+
def.dropConstraints = [
|
|
463
|
+
...input.dropConstraints
|
|
464
|
+
];
|
|
465
|
+
}
|
|
125
466
|
if (input.appendOnly === true) def.appendOnly = true;
|
|
126
467
|
if (input.ignored === true) def.ignored = true;
|
|
127
468
|
if (input.search !== void 0) {
|
|
@@ -175,7 +516,7 @@ function defineTable(name, input) {
|
|
|
175
516
|
const handle = {
|
|
176
517
|
...owned
|
|
177
518
|
};
|
|
178
|
-
Object.defineProperty(handle,
|
|
519
|
+
Object.defineProperty(handle, TABLE_META2, {
|
|
179
520
|
value: def,
|
|
180
521
|
enumerable: false
|
|
181
522
|
});
|
|
@@ -184,6 +525,9 @@ function defineTable(name, input) {
|
|
|
184
525
|
__name(defineTable, "defineTable");
|
|
185
526
|
var declaringSchemaOf = /* @__PURE__ */ new WeakMap();
|
|
186
527
|
function resolveReferences(tables, schemaName) {
|
|
528
|
+
for (const table of Object.values(tables)) {
|
|
529
|
+
qualifyRefTargets(table, `table "${table.name}"`);
|
|
530
|
+
}
|
|
187
531
|
for (const table of Object.values(tables)) {
|
|
188
532
|
for (const [colName, builder] of Object.entries(table.columns)) {
|
|
189
533
|
const def = builder._def;
|
|
@@ -228,7 +572,7 @@ function defineSchema(name, input) {
|
|
|
228
572
|
}
|
|
229
573
|
const tables = {};
|
|
230
574
|
for (const handle of input.tables) {
|
|
231
|
-
const table = handle[
|
|
575
|
+
const table = handle[TABLE_META2];
|
|
232
576
|
if (tables[table.name] !== void 0) {
|
|
233
577
|
throw new Error(`defineSchema("${name}"): two tables declare the name "${table.name}" \u2014 table names must be unique within a schema`);
|
|
234
578
|
}
|
|
@@ -272,6 +616,48 @@ function defineSchema(name, input) {
|
|
|
272
616
|
};
|
|
273
617
|
}
|
|
274
618
|
__name(defineSchema, "defineSchema");
|
|
619
|
+
function qualifyRefTargets(table, where) {
|
|
620
|
+
const qualify = /* @__PURE__ */ __name((holder, what) => {
|
|
621
|
+
const target = holder[REF_TARGET];
|
|
622
|
+
if (target === void 0) return;
|
|
623
|
+
const targetSchema = declaringSchemaOf.get(target);
|
|
624
|
+
if (targetSchema === void 0) {
|
|
625
|
+
throw new Error(`${where}: ${what} points at table "${target.name}", which no defineSchema(...) has claimed yet \u2014 a cross-table reference cannot be written without its target's schema. Declare that table's schema first (one file per schema: db/public.ts, db/billing.ts \u2026, imported by this one).`);
|
|
626
|
+
}
|
|
627
|
+
const qualified = qualifiedTableKey(targetSchema, target.name);
|
|
628
|
+
if (holder.refTable !== void 0) holder.refTable = qualified;
|
|
629
|
+
if (holder.table !== void 0) holder.table = qualified;
|
|
630
|
+
}, "qualify");
|
|
631
|
+
for (const fk of table.foreignKeys ?? []) qualify(fk, `foreignKey("${fk.name}")`);
|
|
632
|
+
for (const fz of table.freeze ?? []) qualify(fz, `freeze("${fz.name}")`);
|
|
633
|
+
for (const g of table.guards ?? []) {
|
|
634
|
+
if (g.exists !== void 0) {
|
|
635
|
+
qualifyExistsNodes(g.exists, `guard("${g.name}").exists(...)`, where);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
__name(qualifyRefTargets, "qualifyRefTargets");
|
|
640
|
+
function qualifyExistsNodes(node, what, where) {
|
|
641
|
+
if (node === null || typeof node !== "object") return;
|
|
642
|
+
if (node.kind === "exists") {
|
|
643
|
+
const target = node[REF_TARGET];
|
|
644
|
+
if (target !== void 0) {
|
|
645
|
+
const targetSchema = declaringSchemaOf.get(target);
|
|
646
|
+
if (targetSchema === void 0) {
|
|
647
|
+
throw new Error(`${where}: ${what} points at table "${target.name}", which no defineSchema(...) has claimed yet \u2014 a guard body is parsed at RUN TIME under a pinned search_path, so an unqualified name never resolves.`);
|
|
648
|
+
}
|
|
649
|
+
node.table = qualifiedTableKey(targetSchema, target.name);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
for (const v of Object.values(node)) {
|
|
653
|
+
if (Array.isArray(v)) {
|
|
654
|
+
for (const e of v) qualifyExistsNodes(e, what, where);
|
|
655
|
+
} else if (v !== null && typeof v === "object") {
|
|
656
|
+
qualifyExistsNodes(v, what, where);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
__name(qualifyExistsNodes, "qualifyExistsNodes");
|
|
275
661
|
|
|
276
662
|
// src/db/policy.ts
|
|
277
663
|
var PolicyExprRef = class _PolicyExprRef {
|
|
@@ -385,15 +771,21 @@ function exprCtx(_columns, depth = 0, ownTable = "") {
|
|
|
385
771
|
expr: part.expr
|
|
386
772
|
}), "not"),
|
|
387
773
|
existsIn: /* @__PURE__ */ __name((table, predicate) => {
|
|
388
|
-
const meta = table[
|
|
774
|
+
const meta = table[TABLE_META2];
|
|
389
775
|
const inner = exprCtx(Object.keys(meta.columns), depth + 1, ownTable);
|
|
390
|
-
|
|
776
|
+
const node = {
|
|
391
777
|
kind: "exists",
|
|
392
778
|
table: meta.name,
|
|
393
779
|
alias: `pb_e${depth + 1}`,
|
|
394
780
|
outer: ownTable,
|
|
395
781
|
predicate: predicate(inner).expr
|
|
782
|
+
};
|
|
783
|
+
Object.defineProperty(node, REF_TARGET, {
|
|
784
|
+
value: meta,
|
|
785
|
+
enumerable: false,
|
|
786
|
+
configurable: true
|
|
396
787
|
});
|
|
788
|
+
return new PolicyExprRef(node);
|
|
397
789
|
}, "existsIn")
|
|
398
790
|
};
|
|
399
791
|
}
|
|
@@ -481,13 +873,13 @@ var PolicyBuilder = class {
|
|
|
481
873
|
* // → id IN (SELECT "channel_id" FROM "channel_members"
|
|
482
874
|
* // WHERE "user_id" = (select auth.uid()))
|
|
483
875
|
*/
|
|
484
|
-
memberOf(membershipTable,
|
|
876
|
+
memberOf(membershipTable, foreignKey2, options = {}) {
|
|
485
877
|
if (this._def.using !== null) {
|
|
486
878
|
throw new Error(`policy(${this._def.name}).memberOf(): this policy already has a using() expression. Write one or the other \u2014 memberOf IS the using expression.`);
|
|
487
879
|
}
|
|
488
880
|
const column = options.column ?? "id";
|
|
489
881
|
const userColumn = options.userColumn ?? "user_id";
|
|
490
|
-
this._def.using = `${quote(column)} IN (SELECT ${quote(
|
|
882
|
+
this._def.using = `${quote(column)} IN (SELECT ${quote(foreignKey2)} FROM ${quote(membershipTable)} WHERE ${quote(userColumn)} = (select auth.uid()))`;
|
|
491
883
|
return this;
|
|
492
884
|
}
|
|
493
885
|
/** `WITH CHECK (...)` yazma doğrulaması — `using` ile aynı iki biçim. */
|
|
@@ -506,6 +898,92 @@ function policy(name) {
|
|
|
506
898
|
return new PolicyBuilder(name);
|
|
507
899
|
}
|
|
508
900
|
__name(policy, "policy");
|
|
901
|
+
function assertCheckNode(node, name) {
|
|
902
|
+
switch (node.kind) {
|
|
903
|
+
case "exists":
|
|
904
|
+
throw new Error(`check(${name}): \`existsIn(...)\` is a subquery and PostgreSQL forbids subqueries in a CHECK constraint \u2014 express a cross-table rule as a composite FOREIGN KEY (foreignKeys), or as a guard.`);
|
|
905
|
+
case "authUid":
|
|
906
|
+
throw new Error(`check(${name}): \`auth.uid()\` renders as a subquery \`(SELECT auth.uid())\` and PostgreSQL forbids subqueries in a CHECK constraint \u2014 a caller-dependent rule belongs in an RLS policy, not a CHECK.`);
|
|
907
|
+
case "binop":
|
|
908
|
+
assertCheckNode(node.left, name);
|
|
909
|
+
assertCheckNode(node.right, name);
|
|
910
|
+
return;
|
|
911
|
+
case "and":
|
|
912
|
+
case "or":
|
|
913
|
+
for (const part of node.parts) assertCheckNode(part, name);
|
|
914
|
+
return;
|
|
915
|
+
case "not":
|
|
916
|
+
case "isNull":
|
|
917
|
+
assertCheckNode(node.expr, name);
|
|
918
|
+
return;
|
|
919
|
+
default:
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
__name(assertCheckNode, "assertCheckNode");
|
|
924
|
+
var CHECK_BIN_OPS = {
|
|
925
|
+
eq: "=",
|
|
926
|
+
neq: "<>",
|
|
927
|
+
gt: ">",
|
|
928
|
+
gte: ">=",
|
|
929
|
+
lt: "<",
|
|
930
|
+
lte: "<="
|
|
931
|
+
};
|
|
932
|
+
function literalSQL(v) {
|
|
933
|
+
if (v === null) return "NULL";
|
|
934
|
+
if (typeof v === "boolean") return v ? "true" : "false";
|
|
935
|
+
if (typeof v === "number") {
|
|
936
|
+
if (!Number.isFinite(v)) {
|
|
937
|
+
throw new Error(`check(): ${v} bir SQL say\u0131 literali de\u011Fil.`);
|
|
938
|
+
}
|
|
939
|
+
return String(v);
|
|
940
|
+
}
|
|
941
|
+
return `'${v.replace(/'/g, "''")}'`;
|
|
942
|
+
}
|
|
943
|
+
__name(literalSQL, "literalSQL");
|
|
944
|
+
function checkNodeSQL(node, name) {
|
|
945
|
+
switch (node.kind) {
|
|
946
|
+
case "col":
|
|
947
|
+
return quote(node.name);
|
|
948
|
+
case "lit":
|
|
949
|
+
return literalSQL(node.value);
|
|
950
|
+
case "binop": {
|
|
951
|
+
const op = CHECK_BIN_OPS[node.op];
|
|
952
|
+
if (op === void 0) {
|
|
953
|
+
throw new Error(`check(${name}): \`${node.op}\` bir CHECK ifadesinde desteklenmiyor.`);
|
|
954
|
+
}
|
|
955
|
+
return `(${checkNodeSQL(node.left, name)} ${op} ${checkNodeSQL(node.right, name)})`;
|
|
956
|
+
}
|
|
957
|
+
case "and":
|
|
958
|
+
case "or": {
|
|
959
|
+
if (node.parts.length === 0) {
|
|
960
|
+
return node.kind === "and" ? "(true)" : "(false)";
|
|
961
|
+
}
|
|
962
|
+
const joiner = node.kind === "and" ? " AND " : " OR ";
|
|
963
|
+
return `(${node.parts.map((p) => checkNodeSQL(p, name)).join(joiner)})`;
|
|
964
|
+
}
|
|
965
|
+
case "not":
|
|
966
|
+
return `(NOT ${checkNodeSQL(node.expr, name)})`;
|
|
967
|
+
case "isNull":
|
|
968
|
+
return `(${checkNodeSQL(node.expr, name)} IS ${node.negated ? "NOT NULL" : "NULL"})`;
|
|
969
|
+
default: {
|
|
970
|
+
const kind = node.kind;
|
|
971
|
+
throw new Error(`check(${name}): \`${kind}\` d\xFC\u011F\xFCm\xFC bir CHECK ifadesinde tan\u0131nm\u0131yor.`);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
__name(checkNodeSQL, "checkNodeSQL");
|
|
976
|
+
function check(name, expr) {
|
|
977
|
+
if (name.trim() === "") {
|
|
978
|
+
throw new Error("check(): k\u0131s\u0131t\u0131n ad\u0131 bo\u015F olamaz \u2014 CHECK'ler ADA g\xF6re uzla\u015Ft\u0131r\u0131l\u0131yor.");
|
|
979
|
+
}
|
|
980
|
+
assertCheckNode(expr.expr, name);
|
|
981
|
+
return {
|
|
982
|
+
name,
|
|
983
|
+
expr: checkNodeSQL(expr.expr, name)
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
__name(check, "check");
|
|
509
987
|
function can(table) {
|
|
510
988
|
if (table === void 0 || table === null) {
|
|
511
989
|
throw new Error("can(<table>): received " + String(table) + " \u2014 the schema declares no such table (check the name against db/*.ts; can(publicSchema.tables.<name>))");
|
|
@@ -582,13 +1060,10 @@ function isPalbaseExtension(name) {
|
|
|
582
1060
|
__name(isPalbaseExtension, "isPalbaseExtension");
|
|
583
1061
|
|
|
584
1062
|
// src/db/raw.ts
|
|
585
|
-
function raw(name, up
|
|
1063
|
+
function raw(name, up) {
|
|
586
1064
|
return {
|
|
587
1065
|
name,
|
|
588
|
-
up
|
|
589
|
-
...opts?.down != null ? {
|
|
590
|
-
down: opts.down
|
|
591
|
-
} : {}
|
|
1066
|
+
up
|
|
592
1067
|
};
|
|
593
1068
|
}
|
|
594
1069
|
__name(raw, "raw");
|
|
@@ -717,14 +1192,19 @@ function sqlFragment(strings, ...values) {
|
|
|
717
1192
|
__name(sqlFragment, "sqlFragment");
|
|
718
1193
|
|
|
719
1194
|
export {
|
|
1195
|
+
foreignKey,
|
|
1196
|
+
freeze,
|
|
720
1197
|
PolicyExprRef,
|
|
721
1198
|
exprCtx,
|
|
722
1199
|
PolicyBuilder,
|
|
723
1200
|
policy,
|
|
1201
|
+
check,
|
|
724
1202
|
can,
|
|
1203
|
+
guard,
|
|
1204
|
+
backfill,
|
|
725
1205
|
IndexBuilder,
|
|
726
1206
|
index,
|
|
727
|
-
TABLE_META,
|
|
1207
|
+
TABLE_META2 as TABLE_META,
|
|
728
1208
|
defineTable,
|
|
729
1209
|
defineSchema,
|
|
730
1210
|
PALBASE_EXTENSIONS,
|
|
@@ -737,4 +1217,4 @@ export {
|
|
|
737
1217
|
withRetry,
|
|
738
1218
|
sqlFragment
|
|
739
1219
|
};
|
|
740
|
-
//# sourceMappingURL=chunk-
|
|
1220
|
+
//# sourceMappingURL=chunk-AR2I4M7I.js.map
|