@palbase/backend 24.2.0 → 25.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 +101 -60
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +17 -13
- package/dist/bin/palbase-backend.js.map +1 -1
- package/dist/{chunk-EIXCY4SS.js → chunk-43A3KGWL.js} +80 -49
- package/dist/chunk-43A3KGWL.js.map +1 -0
- package/dist/{chunk-ERDL5VAE.js → chunk-5CMLOAEF.js} +2 -2
- package/dist/chunk-OEQBHE2Z.js +825 -0
- package/dist/chunk-OEQBHE2Z.js.map +1 -0
- package/dist/{chunk-7Z6MGMXQ.js → chunk-XJ2RSHEU.js} +11 -5
- package/dist/chunk-XJ2RSHEU.js.map +1 -0
- package/dist/{chunk-UWSYTUGM.js → chunk-ZQRWW37O.js} +44 -1
- package/dist/chunk-ZQRWW37O.js.map +1 -0
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +29 -13
- package/dist/db/env.d.ts +29 -13
- package/dist/db/index.cjs +233 -110
- 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 +11 -1
- package/dist/engine/index.cjs +87 -50
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +2 -2
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +3 -3
- package/dist/{index-C0PMn5jl.d.ts → index-BF1f0DfA.d.ts} +5 -2
- package/dist/{index-DAwHMppB.d.cts → index-CoaDN9dL.d.cts} +5 -2
- package/dist/{index-ByBMibIJ.d.ts → index-Ct1iiB4N.d.ts} +232 -60
- package/dist/{index-D4rts8T7.d.cts → index-CwaWRhyc.d.cts} +232 -60
- package/dist/index.cjs +572 -296
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -20
- package/dist/index.d.ts +124 -20
- package/dist/index.js +164 -216
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +100 -36
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.js +59 -2
- package/dist/openapi/index.js.map +1 -1
- package/docs/README.md +64 -31
- package/docs/endpoints.md +25 -28
- package/docs/llms-full.txt +465 -148
- package/docs/schema.md +338 -86
- package/docs/services.md +39 -4
- package/package.json +1 -1
- package/template/AGENTS.md +119 -314
- package/template/CLAUDE.md +13 -0
- package/template/controllers/notes.controller.ts +6 -13
- package/template/db/public.ts +38 -0
- package/template/models/notes/create.ts +38 -0
- package/template/package.json +6 -3
- package/template/services/note.service.test.ts +45 -0
- package/template/services/note.service.ts +2 -2
- package/dist/chunk-7Z6MGMXQ.js.map +0 -1
- package/dist/chunk-EIXCY4SS.js.map +0 -1
- package/dist/chunk-LCL7TUAI.js +0 -534
- package/dist/chunk-LCL7TUAI.js.map +0 -1
- package/dist/chunk-UWSYTUGM.js.map +0 -1
- package/template/db/schema.ts +0 -35
- /package/dist/{chunk-ERDL5VAE.js.map → chunk-5CMLOAEF.js.map} +0 -0
package/dist/db/index.cjs
CHANGED
|
@@ -23,29 +23,39 @@ __export(db_exports, {
|
|
|
23
23
|
EXTENSION_DEPENDENCIES: () => EXTENSION_DEPENDENCIES,
|
|
24
24
|
PALBASE_EXTENSIONS: () => PALBASE_EXTENSIONS,
|
|
25
25
|
PolicyBuilder: () => PolicyBuilder,
|
|
26
|
+
TABLE_META: () => TABLE_META,
|
|
26
27
|
TxPlanError: () => TxPlanError,
|
|
27
28
|
TxRefError: () => TxRefError,
|
|
28
29
|
bigint: () => bigint,
|
|
29
30
|
boolean: () => boolean,
|
|
30
31
|
dec: () => dec,
|
|
31
32
|
defineSchema: () => defineSchema,
|
|
33
|
+
defineTable: () => defineTable,
|
|
32
34
|
enumType: () => enumType,
|
|
33
35
|
inc: () => inc,
|
|
36
|
+
installationRef: () => installationRef,
|
|
34
37
|
integer: () => integer,
|
|
35
38
|
isPalbaseExtension: () => isPalbaseExtension,
|
|
36
39
|
jsonb: () => jsonb,
|
|
37
40
|
makeTypedDB: () => makeTypedDB,
|
|
38
41
|
now: () => now,
|
|
39
42
|
openai: () => openai,
|
|
43
|
+
ownedByUser: () => ownedByUser,
|
|
40
44
|
policy: () => policy,
|
|
41
45
|
raw: () => raw,
|
|
42
46
|
text: () => text,
|
|
43
47
|
timestamp: () => timestamp,
|
|
48
|
+
userRef: () => userRef,
|
|
44
49
|
uuid: () => uuid,
|
|
45
50
|
vector: () => vector
|
|
46
51
|
});
|
|
47
52
|
module.exports = __toCommonJS(db_exports);
|
|
48
53
|
|
|
54
|
+
// src/db/schema-json.ts
|
|
55
|
+
function qualifiedTableKey(schemaName, tableName) {
|
|
56
|
+
return schemaName === "public" ? tableName : `${schemaName}.${tableName}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
// src/db/policy.ts
|
|
50
60
|
function quote(name) {
|
|
51
61
|
return `"${name.replace(/"/g, '""')}"`;
|
|
@@ -140,102 +150,179 @@ function policy(name) {
|
|
|
140
150
|
function toPolicyDef(p) {
|
|
141
151
|
return p instanceof PolicyBuilder ? p._def : p;
|
|
142
152
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
153
|
+
var TABLE_META = /* @__PURE__ */ Symbol.for("palbase.table.meta");
|
|
154
|
+
function defineTable(name, input) {
|
|
155
|
+
if (name.trim() === "") {
|
|
156
|
+
throw new Error("defineTable(name, \u2026): name must not be empty");
|
|
157
|
+
}
|
|
158
|
+
const policies = (input.policies ?? []).map(toPolicyDef);
|
|
159
|
+
const rls = policies.length > 0 || input.rls !== false;
|
|
160
|
+
const def = {
|
|
161
|
+
name,
|
|
162
|
+
columns: input.columns,
|
|
163
|
+
rls,
|
|
164
|
+
policies
|
|
165
|
+
};
|
|
166
|
+
if (input.primaryKey !== void 0) def.primaryKey = input.primaryKey;
|
|
167
|
+
if (input.unique !== void 0) def.unique = input.unique;
|
|
168
|
+
if (input.raw !== void 0 && input.raw.length > 0) def.raw = input.raw.slice();
|
|
169
|
+
if (input.checks !== void 0 && input.checks.length > 0) def.checks = input.checks.slice();
|
|
170
|
+
if (input.indexes !== void 0 && input.indexes.length > 0) def.indexes = input.indexes.slice();
|
|
171
|
+
if (input.search !== void 0) {
|
|
172
|
+
const s = input.search;
|
|
173
|
+
if (s.from !== void 0) {
|
|
174
|
+
if (s.vector !== void 0) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`table ${name}: search beyan\u0131nda tek bi\xE7im kullan\u0131n \u2014 'from' (yeni) ile 'vector' (eski) birlikte olamaz`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
if (s.from.length === 0) {
|
|
180
|
+
throw new Error(`table ${name}: search.from bo\u015F olamaz`);
|
|
181
|
+
}
|
|
182
|
+
if (s.model === void 0) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`table ${name}: search.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin (BYO i\xE7in eski 'vector' bi\xE7imini kullan\u0131n)`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (Array.isArray(s.text) && s.text.length === 0) {
|
|
188
|
+
throw new Error(`table ${name}: search.text bo\u015F dizi olamaz \u2014 FTS istemiyorsan text: false yaz\u0131n`);
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
if (typeof s.text === "boolean") {
|
|
192
|
+
throw new Error(`table ${name}: text:${String(s.text)} yaln\u0131z yeni bi\xE7imde ('from' ile) ge\xE7erli`);
|
|
193
|
+
}
|
|
194
|
+
if (s.text === void 0 && s.vector === void 0) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`table ${name}: search beyan\u0131 bo\u015F \u2014 en az bir kol (text ya da vector) verin, yoksa alan\u0131 hi\xE7 yazmay\u0131n`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
if (s.text !== void 0 && s.text.length === 0) {
|
|
200
|
+
throw new Error(`table ${name}: search.text bo\u015F olamaz \u2014 FTS kolu istemiyorsan alan\u0131 hi\xE7 yazma`);
|
|
201
|
+
}
|
|
202
|
+
const legs = s.vector === void 0 ? [] : Array.isArray(s.vector) ? s.vector : [s.vector];
|
|
203
|
+
for (const leg of legs) {
|
|
204
|
+
if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
|
|
205
|
+
throw new Error(`table ${name}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
|
|
179
206
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
throw new Error(`table ${String(name)}: text:${String(s.text)} yaln\u0131z yeni bi\xE7imde ('from' ile) ge\xE7erli`);
|
|
207
|
+
if (leg.model === void 0 && leg.from !== void 0) {
|
|
208
|
+
throw new Error(`table ${name}: search.vector.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin`);
|
|
183
209
|
}
|
|
184
|
-
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
def.search = s;
|
|
213
|
+
}
|
|
214
|
+
if (input.memory !== void 0) {
|
|
215
|
+
if (input.memory.from.length === 0) {
|
|
216
|
+
throw new Error(`table ${name}: memory.from bo\u015F olamaz`);
|
|
217
|
+
}
|
|
218
|
+
def.memory = input.memory;
|
|
219
|
+
}
|
|
220
|
+
for (const b of Object.values(input.columns)) {
|
|
221
|
+
b._def.ownerTable = def;
|
|
222
|
+
}
|
|
223
|
+
const handle = { ...input.columns };
|
|
224
|
+
Object.defineProperty(handle, TABLE_META, { value: def, enumerable: false });
|
|
225
|
+
return handle;
|
|
226
|
+
}
|
|
227
|
+
var declaringSchemaOf = /* @__PURE__ */ new WeakMap();
|
|
228
|
+
function resolveReferences(tables, schemaName) {
|
|
229
|
+
for (const table of Object.values(tables)) {
|
|
230
|
+
for (const [colName, builder] of Object.entries(table.columns)) {
|
|
231
|
+
const def = builder._def;
|
|
232
|
+
if (def.selfRefColumn !== void 0) {
|
|
233
|
+
if (!(def.selfRefColumn in table.columns)) {
|
|
185
234
|
throw new Error(
|
|
186
|
-
`table ${
|
|
235
|
+
`table "${table.name}" column "${colName}": selfReferences("${def.selfRefColumn}") \u2014 no such column on this table`
|
|
187
236
|
);
|
|
188
237
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
|
|
195
|
-
throw new Error(`table ${String(name)}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
|
|
196
|
-
}
|
|
197
|
-
if (leg.model === void 0 && leg.from !== void 0) {
|
|
198
|
-
throw new Error(`table ${String(name)}: search.vector.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
238
|
+
def.references = {
|
|
239
|
+
table: qualifiedTableKey(schemaName, table.name),
|
|
240
|
+
column: def.selfRefColumn
|
|
241
|
+
};
|
|
242
|
+
continue;
|
|
201
243
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
throw new Error(
|
|
244
|
+
if (def.referencesThunk === void 0) continue;
|
|
245
|
+
const target = def.referencesThunk();
|
|
246
|
+
const owner = target?._def?.ownerTable;
|
|
247
|
+
if (owner === void 0) {
|
|
248
|
+
throw new Error(
|
|
249
|
+
`table "${table.name}" column "${colName}": references(() => \u2026) must point at a column of a table declared with defineTable(...)`
|
|
250
|
+
);
|
|
207
251
|
}
|
|
208
|
-
|
|
252
|
+
const targetColumn = Object.entries(owner.columns).find(
|
|
253
|
+
([, b]) => b._def === target._def
|
|
254
|
+
)?.[0];
|
|
255
|
+
if (targetColumn === void 0) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
`table "${table.name}" column "${colName}": the referenced column was not found on table "${owner.name}"`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
const targetSchema = declaringSchemaOf.get(owner);
|
|
261
|
+
if (targetSchema === void 0) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
`table "${table.name}" column "${colName}": references(() => \u2026) points at table "${owner.name}", which no defineSchema(...) has claimed yet \u2014 a foreign key 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).`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
def.references = {
|
|
267
|
+
table: qualifiedTableKey(targetSchema, owner.name),
|
|
268
|
+
column: targetColumn
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function defineSchema(name, input) {
|
|
274
|
+
if (typeof name !== "string") {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`defineSchema(...) takes the schema NAME first: defineSchema("public", { tables: [todos, lists] }). The one-argument form defineSchema({ tables: { \u2026 } }) is gone \u2014 a schema that does not say its own name cannot be told apart from another schema's table of the same name, which is what a cross-schema foreign key has to do. Migrate db/public.ts (one file per schema: db/public.ts, db/billing.ts \u2026): name the schema, and lift each table key onto the table itself with defineTable("todos", { columns: { \u2026 } }), then list the tables in the array.`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
if (name.trim() === "") {
|
|
280
|
+
throw new Error("defineSchema(name, \u2026): name must not be empty");
|
|
281
|
+
}
|
|
282
|
+
const tables = {};
|
|
283
|
+
for (const handle of input.tables) {
|
|
284
|
+
const table = handle[TABLE_META];
|
|
285
|
+
if (tables[table.name] !== void 0) {
|
|
286
|
+
throw new Error(
|
|
287
|
+
`defineSchema("${name}"): two tables declare the name "${table.name}" \u2014 table names must be unique within a schema`
|
|
288
|
+
);
|
|
209
289
|
}
|
|
210
|
-
tables[name] =
|
|
290
|
+
tables[table.name] = table;
|
|
291
|
+
declaringSchemaOf.set(table, name);
|
|
211
292
|
}
|
|
212
|
-
|
|
293
|
+
resolveReferences(tables, name);
|
|
294
|
+
for (const [name2, def] of Object.entries(tables)) {
|
|
213
295
|
const m = def.memory;
|
|
214
296
|
if (m === void 0) continue;
|
|
215
297
|
const target = Object.values(tables).find((t) => t.name === m.into);
|
|
216
298
|
if (target === void 0) {
|
|
217
|
-
throw new Error(`table ${
|
|
299
|
+
throw new Error(`table ${name2}: memory.into "${m.into}" \u015Femada declared de\u011Fil`);
|
|
218
300
|
}
|
|
219
301
|
const subject = m.subject ?? "owner";
|
|
220
302
|
if (!(subject in def.columns)) {
|
|
221
|
-
throw new Error(`table ${
|
|
303
|
+
throw new Error(`table ${name2}: memory.subject "${subject}" kolonu kaynak tabloda yok`);
|
|
222
304
|
}
|
|
223
305
|
if (!(subject in target.columns)) {
|
|
224
|
-
throw new Error(`table ${
|
|
306
|
+
throw new Error(`table ${name2}: memory.subject "${subject}" kolonu hedef "${m.into}" tablosunda yok`);
|
|
225
307
|
}
|
|
226
308
|
const factCol = target.columns["fact"];
|
|
227
309
|
const factDef = factCol !== void 0 && "_def" in factCol ? factCol._def : factCol;
|
|
228
310
|
if (factDef === void 0 || factDef.type !== "text") {
|
|
229
|
-
throw new Error(`table ${
|
|
311
|
+
throw new Error(`table ${name2}: memory.into "${m.into}" tablosunda "fact" (text) kolonu zorunlu`);
|
|
230
312
|
}
|
|
231
313
|
for (const c of m.from) {
|
|
232
314
|
if (!(c in def.columns)) {
|
|
233
|
-
throw new Error(`table ${
|
|
315
|
+
throw new Error(`table ${name2}: memory.from kolonu "${c}" kaynak tabloda yok`);
|
|
234
316
|
}
|
|
235
317
|
}
|
|
236
318
|
}
|
|
237
319
|
const extensions = [...new Set(input.extensions ?? [])];
|
|
238
|
-
return {
|
|
320
|
+
return {
|
|
321
|
+
name,
|
|
322
|
+
tables,
|
|
323
|
+
extensions,
|
|
324
|
+
exposed: input.exposed ?? name === "public"
|
|
325
|
+
};
|
|
239
326
|
}
|
|
240
327
|
|
|
241
328
|
// src/db/extensions.ts
|
|
@@ -358,59 +445,68 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
358
445
|
this._def.renamedFrom = previous;
|
|
359
446
|
return this;
|
|
360
447
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
448
|
+
/**
|
|
449
|
+
* See {@link ColumnDef.ignored}.
|
|
450
|
+
*
|
|
451
|
+
* COPIES the def rather than mutating it. The constructor takes an existing
|
|
452
|
+
* def BY REFERENCE, so every builder derived from another shares one object —
|
|
453
|
+
* `const a = slug.unique()` leaves `a._def === slug._def`. An in-place
|
|
454
|
+
* `ignored = true` therefore marks every column sharing that def, including
|
|
455
|
+
* one another table actively reads, and the gate would let THAT column be
|
|
456
|
+
* dropped. Measured before this copy existed.
|
|
457
|
+
*
|
|
458
|
+
* The aliasing is older than this method and other fields leak through it too.
|
|
459
|
+
* The reason this one cannot wait: every other leak produces a VISIBLE schema
|
|
460
|
+
* difference — the plan shows it, the DDL shows it. This one is invisible by
|
|
461
|
+
* design (no DDL, no diff, no plan line), so its only effect is to disarm a
|
|
462
|
+
* safety gate in silence.
|
|
463
|
+
*/
|
|
464
|
+
ignored() {
|
|
465
|
+
return new _ColumnBuilder(this._def.type, {
|
|
466
|
+
...this._def,
|
|
467
|
+
ignored: true
|
|
468
|
+
});
|
|
365
469
|
}
|
|
366
470
|
/**
|
|
367
|
-
*
|
|
368
|
-
* (`REFERENCES auth.users(id)`), so a column like `user_id` gets true
|
|
369
|
-
* database cascade/integrity instead of app-layer-only. Sugar for
|
|
370
|
-
* `.references("auth.users", "id")`.
|
|
471
|
+
* Foreign key onto another table's column.
|
|
371
472
|
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* the
|
|
473
|
+
* The target is a THUNK, not a direct reference. In a cycle (`x → y`, `y → x`)
|
|
474
|
+
* the second table does not exist yet when the first is built; a direct
|
|
475
|
+
* reference makes TypeScript chase its own tail (TS7022 — measured, and making
|
|
476
|
+
* the return type independent of the target does NOT help). The thunk is
|
|
477
|
+
* invoked in `defineSchema`, where every binding exists and every table
|
|
478
|
+
* already knows its name.
|
|
376
479
|
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* `text().notNull().referencesAuthUser("cascade")`, or
|
|
381
|
-
* `text().nullable().referencesAuthUser("set null")`. The server
|
|
382
|
-
* (validateAuthUserFK) enforces this — and the remaining rules the type can't
|
|
383
|
-
* express (referencing column is text, `set null` needs a nullable column) —
|
|
384
|
-
* as the real boundary; this signature is the compile-time DX mirror.
|
|
480
|
+
* In a cycle, ONE side needs an explicit return type:
|
|
481
|
+
* `references((): AnyColumn => y.id)`. One side is enough — measured.
|
|
482
|
+
* For a self-reference use `selfReferences(column)`: no thunk, no annotation.
|
|
385
483
|
*/
|
|
386
|
-
|
|
387
|
-
refuseOnVector(this._def, "
|
|
388
|
-
|
|
389
|
-
|
|
484
|
+
references(target, opts) {
|
|
485
|
+
refuseOnVector(this._def, "references");
|
|
486
|
+
if (typeof target !== "function") {
|
|
487
|
+
throw new Error(
|
|
488
|
+
`references(...) takes a callback: write references(() => otherTable.column). The two-string form references("table", "column") is gone \u2014 a string cannot be type-checked and cannot point at a table that does not exist yet.`
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
this._def.referencesThunk = target;
|
|
492
|
+
if (opts?.as !== void 0) this._def.refAs = opts.as;
|
|
493
|
+
if (opts?.onDelete !== void 0) this._def.onDeleteAction = opts.onDelete;
|
|
390
494
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
391
495
|
}
|
|
392
496
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* root (`ins_...`). Sugar for `.references("auth.installations", "id")`.
|
|
396
|
-
*
|
|
397
|
-
* An installation is an APP INSTALL, not a user: this FK is NOT user ownership.
|
|
398
|
-
* A user-owned row STILL needs its own `.referencesAuthUser(...)` FK so account
|
|
399
|
-
* erasure removes it — an installation reference alone does not tie a row to a
|
|
400
|
-
* user's deletion. Use this only for install-scoped state (device prefs, push
|
|
401
|
-
* routing, …), alongside a separate auth-user FK where the row is user-owned.
|
|
497
|
+
* Foreign key onto THIS table (`parent_id → id`) — category trees, comment
|
|
498
|
+
* replies, org charts.
|
|
402
499
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
* blocked by a lingering FK. The server (validateAuthAnchorFK) is the real
|
|
408
|
-
* boundary; this signature is the compile-time DX mirror.
|
|
500
|
+
* No thunk and no type annotation: the target table is the one being declared,
|
|
501
|
+
* so there is nothing to defer and nothing for TypeScript to chase in a circle.
|
|
502
|
+
* Drizzle forces an explicit `(): AnyPgColumn =>` here because its reference
|
|
503
|
+
* always goes through a callback; measured, we do not need one.
|
|
409
504
|
*/
|
|
410
|
-
|
|
411
|
-
refuseOnVector(this._def, "
|
|
412
|
-
this._def.
|
|
413
|
-
this._def.
|
|
505
|
+
selfReferences(column, opts) {
|
|
506
|
+
refuseOnVector(this._def, "selfReferences");
|
|
507
|
+
this._def.selfRefColumn = column;
|
|
508
|
+
if (opts?.as !== void 0) this._def.refAs = opts.as;
|
|
509
|
+
if (opts?.onDelete !== void 0) this._def.onDeleteAction = opts.onDelete;
|
|
414
510
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
415
511
|
}
|
|
416
512
|
/** Set the ON DELETE action for a foreign key reference. */
|
|
@@ -482,6 +578,28 @@ function vector(dimensions) {
|
|
|
482
578
|
b._def.dimensions = dimensions;
|
|
483
579
|
return b;
|
|
484
580
|
}
|
|
581
|
+
function ownedByUser() {
|
|
582
|
+
const b = new ColumnBuilder("text");
|
|
583
|
+
b._def.nullable = false;
|
|
584
|
+
b._def.references = { table: "auth.users", column: "id" };
|
|
585
|
+
b._def.onDeleteAction = "cascade";
|
|
586
|
+
b._def.owns = true;
|
|
587
|
+
return b;
|
|
588
|
+
}
|
|
589
|
+
function userRef(opts) {
|
|
590
|
+
const b = new ColumnBuilder("text");
|
|
591
|
+
b._def.references = { table: "auth.users", column: "id" };
|
|
592
|
+
b._def.onDeleteAction = opts.onDelete;
|
|
593
|
+
if (opts.as !== void 0) b._def.refAs = opts.as;
|
|
594
|
+
return b;
|
|
595
|
+
}
|
|
596
|
+
function installationRef(opts) {
|
|
597
|
+
const b = new ColumnBuilder("text");
|
|
598
|
+
b._def.references = { table: "auth.installations", column: "id" };
|
|
599
|
+
b._def.onDeleteAction = opts.onDelete;
|
|
600
|
+
if (opts.as !== void 0) b._def.refAs = opts.as;
|
|
601
|
+
return b;
|
|
602
|
+
}
|
|
485
603
|
|
|
486
604
|
// src/db/raw.ts
|
|
487
605
|
function raw(name, up, opts) {
|
|
@@ -991,24 +1109,29 @@ function makeTypedDB(schema, raw2) {
|
|
|
991
1109
|
EXTENSION_DEPENDENCIES,
|
|
992
1110
|
PALBASE_EXTENSIONS,
|
|
993
1111
|
PolicyBuilder,
|
|
1112
|
+
TABLE_META,
|
|
994
1113
|
TxPlanError,
|
|
995
1114
|
TxRefError,
|
|
996
1115
|
bigint,
|
|
997
1116
|
boolean,
|
|
998
1117
|
dec,
|
|
999
1118
|
defineSchema,
|
|
1119
|
+
defineTable,
|
|
1000
1120
|
enumType,
|
|
1001
1121
|
inc,
|
|
1122
|
+
installationRef,
|
|
1002
1123
|
integer,
|
|
1003
1124
|
isPalbaseExtension,
|
|
1004
1125
|
jsonb,
|
|
1005
1126
|
makeTypedDB,
|
|
1006
1127
|
now,
|
|
1007
1128
|
openai,
|
|
1129
|
+
ownedByUser,
|
|
1008
1130
|
policy,
|
|
1009
1131
|
raw,
|
|
1010
1132
|
text,
|
|
1011
1133
|
timestamp,
|
|
1134
|
+
userRef,
|
|
1012
1135
|
uuid,
|
|
1013
1136
|
vector
|
|
1014
1137
|
});
|