@palbase/backend 24.3.0 → 25.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/palbase-backend.cjs +103 -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-34I4GB7D.js} +82 -49
- package/dist/chunk-34I4GB7D.js.map +1 -0
- package/dist/{chunk-UWSYTUGM.js → chunk-35PNTIRN.js} +48 -1
- package/dist/chunk-35PNTIRN.js.map +1 -0
- package/dist/chunk-HBOJLP2Z.js +840 -0
- package/dist/chunk-HBOJLP2Z.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-ERDL5VAE.js → chunk-YOY5DFQS.js} +2 -2
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +31 -14
- package/dist/db/env.d.ts +31 -14
- package/dist/db/index.cjs +226 -111
- 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 +89 -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-BTMYod_l.d.ts → index-B4CcpqLb.d.ts} +224 -75
- package/dist/{index-DEneI8Mn.d.ts → index-B8v6hVyU.d.ts} +5 -2
- package/dist/{index-BLAbr9ZH.d.cts → index-DmVyY6N7.d.cts} +224 -75
- package/dist/{index-C-ALG22n.d.cts → index-VsjBQ4Kw.d.cts} +5 -2
- package/dist/index.cjs +580 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -22
- package/dist/index.d.ts +125 -22
- package/dist/index.js +173 -217
- 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 +430 -153
- package/docs/schema.md +303 -91
- 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-D5CQES25.js +0 -556
- package/dist/chunk-D5CQES25.js.map +0 -1
- package/dist/chunk-EIXCY4SS.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-YOY5DFQS.js.map} +0 -0
package/dist/db/env.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ export { BucketTypes, Buckets } from '../stack.js';
|
|
|
6
6
|
*
|
|
7
7
|
* `Database.tables.<name>` is typed against the `Tables` interface declared
|
|
8
8
|
* here. By default `Tables` is EMPTY; the generated `palbase-env.d.ts`
|
|
9
|
-
* (emitted from `db
|
|
9
|
+
* (emitted from the project's `db/*.ts` schema files by {@link makeEnvDts})
|
|
10
|
+
* augments it with one
|
|
10
11
|
* member per table:
|
|
11
12
|
*
|
|
12
13
|
* // palbase-env.d.ts (generated — do not edit)
|
|
@@ -27,28 +28,37 @@ export { BucketTypes, Buckets } from '../stack.js';
|
|
|
27
28
|
* type Todo = Tables["todos"]["row"];
|
|
28
29
|
*/
|
|
29
30
|
/**
|
|
30
|
-
* One typed table's shapes and its
|
|
31
|
+
* One typed table's shapes and its relations:
|
|
31
32
|
*
|
|
32
33
|
* - `row` — the full row, every column present.
|
|
33
34
|
* - `insert` — the write payload, required vs optional columns.
|
|
34
|
-
* - `
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* - `children` — tables that foreign-key THIS table, mapped to the FK column on
|
|
39
|
-
* the child (`{ todos: "list_id" }`). Lets a nested seed attach children
|
|
40
|
-
* without the author ever writing the FK. Self-FKs and cycle back-edges are
|
|
41
|
-
* omitted by the generator so recursive types over `children` terminate.
|
|
35
|
+
* - `relations` — every foreign key this table takes part in, named, in both
|
|
36
|
+
* directions. `{ list: { to: "lists", kind: "one", via: "list_id" } }` on the
|
|
37
|
+
* child; `{ todos: { to: "todos", kind: "many", via: "list_id" } }` on the
|
|
38
|
+
* parent. Ownership declared with `ownedByUser()` appears as `owner`.
|
|
42
39
|
*
|
|
43
40
|
* The generated `palbase-env.d.ts` fills `row`/`insert` with flat object types —
|
|
44
41
|
* no `ColumnBuilder` phantom types ever appear in the generated output — and
|
|
45
|
-
* `
|
|
42
|
+
* `relations` with string literals.
|
|
46
43
|
*/
|
|
47
44
|
interface TableTypes {
|
|
48
45
|
row: Record<string, unknown>;
|
|
49
46
|
insert: Record<string, unknown>;
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The table's relations, derived from its declared foreign keys.
|
|
49
|
+
*
|
|
50
|
+
* Replaces the old `owner` / `children` pair, which nothing ever read: `owner`
|
|
51
|
+
* was whichever column happened to reference `auth.users` FIRST in declaration
|
|
52
|
+
* order, and `children` silently kept only ONE foreign key per parent.
|
|
53
|
+
*
|
|
54
|
+
* `to` is the target's table key — bare for `public`, schema-qualified
|
|
55
|
+
* otherwise, the same convention `RefJSON.table` uses.
|
|
56
|
+
*/
|
|
57
|
+
relations: Record<string, {
|
|
58
|
+
to: string;
|
|
59
|
+
kind: "one" | "many";
|
|
60
|
+
via: string;
|
|
61
|
+
}>;
|
|
52
62
|
}
|
|
53
63
|
/**
|
|
54
64
|
* The project's tables, keyed by table name. EMPTY by default; the generated
|
|
@@ -57,5 +67,12 @@ interface TableTypes {
|
|
|
57
67
|
*/
|
|
58
68
|
interface Tables {
|
|
59
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Tables in schemas other than `public`, reached with
|
|
72
|
+
* `Database.schema("<name>").tables.*`. Declaring a schema does not put it on
|
|
73
|
+
* the internet — see `exposed`.
|
|
74
|
+
*/
|
|
75
|
+
interface Schemas {
|
|
76
|
+
}
|
|
60
77
|
|
|
61
|
-
export type { TableTypes, Tables };
|
|
78
|
+
export type { Schemas, TableTypes, Tables };
|
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,186 @@ 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 owned = {};
|
|
161
|
+
for (const [colName, b] of Object.entries(input.columns)) {
|
|
162
|
+
const builder = b;
|
|
163
|
+
const clone = Object.create(Object.getPrototypeOf(builder));
|
|
164
|
+
Object.defineProperty(clone, "_def", { value: { ...builder._def }, enumerable: true, writable: true });
|
|
165
|
+
owned[colName] = clone;
|
|
166
|
+
}
|
|
167
|
+
const def = {
|
|
168
|
+
name,
|
|
169
|
+
columns: owned,
|
|
170
|
+
rls,
|
|
171
|
+
policies
|
|
172
|
+
};
|
|
173
|
+
if (input.primaryKey !== void 0) def.primaryKey = input.primaryKey;
|
|
174
|
+
if (input.unique !== void 0) def.unique = input.unique;
|
|
175
|
+
if (input.raw !== void 0 && input.raw.length > 0) def.raw = input.raw.slice();
|
|
176
|
+
if (input.checks !== void 0 && input.checks.length > 0) def.checks = input.checks.slice();
|
|
177
|
+
if (input.indexes !== void 0 && input.indexes.length > 0) def.indexes = input.indexes.slice();
|
|
178
|
+
if (input.search !== void 0) {
|
|
179
|
+
const s = input.search;
|
|
180
|
+
if (s.from !== void 0) {
|
|
181
|
+
if (s.vector !== void 0) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`table ${name}: search beyan\u0131nda tek bi\xE7im kullan\u0131n \u2014 'from' (yeni) ile 'vector' (eski) birlikte olamaz`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
if (s.from.length === 0) {
|
|
187
|
+
throw new Error(`table ${name}: search.from bo\u015F olamaz`);
|
|
188
|
+
}
|
|
189
|
+
if (s.model === void 0) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`table ${name}: search.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin (BYO i\xE7in eski 'vector' bi\xE7imini kullan\u0131n)`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
if (Array.isArray(s.text) && s.text.length === 0) {
|
|
195
|
+
throw new Error(`table ${name}: search.text bo\u015F dizi olamaz \u2014 FTS istemiyorsan text: false yaz\u0131n`);
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
if (typeof s.text === "boolean") {
|
|
199
|
+
throw new Error(`table ${name}: text:${String(s.text)} yaln\u0131z yeni bi\xE7imde ('from' ile) ge\xE7erli`);
|
|
200
|
+
}
|
|
201
|
+
if (s.text === void 0 && s.vector === void 0) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`table ${name}: search beyan\u0131 bo\u015F \u2014 en az bir kol (text ya da vector) verin, yoksa alan\u0131 hi\xE7 yazmay\u0131n`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
if (s.text !== void 0 && s.text.length === 0) {
|
|
207
|
+
throw new Error(`table ${name}: search.text bo\u015F olamaz \u2014 FTS kolu istemiyorsan alan\u0131 hi\xE7 yazma`);
|
|
208
|
+
}
|
|
209
|
+
const legs = s.vector === void 0 ? [] : Array.isArray(s.vector) ? s.vector : [s.vector];
|
|
210
|
+
for (const leg of legs) {
|
|
211
|
+
if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
|
|
212
|
+
throw new Error(`table ${name}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
|
|
179
213
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
throw new Error(`table ${String(name)}: text:${String(s.text)} yaln\u0131z yeni bi\xE7imde ('from' ile) ge\xE7erli`);
|
|
214
|
+
if (leg.model === void 0 && leg.from !== void 0) {
|
|
215
|
+
throw new Error(`table ${name}: search.vector.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin`);
|
|
183
216
|
}
|
|
184
|
-
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
def.search = s;
|
|
220
|
+
}
|
|
221
|
+
if (input.memory !== void 0) {
|
|
222
|
+
if (input.memory.from.length === 0) {
|
|
223
|
+
throw new Error(`table ${name}: memory.from bo\u015F olamaz`);
|
|
224
|
+
}
|
|
225
|
+
def.memory = input.memory;
|
|
226
|
+
}
|
|
227
|
+
for (const b of Object.values(owned)) {
|
|
228
|
+
b._def.ownerTable = def;
|
|
229
|
+
}
|
|
230
|
+
const handle = { ...owned };
|
|
231
|
+
Object.defineProperty(handle, TABLE_META, { value: def, enumerable: false });
|
|
232
|
+
return handle;
|
|
233
|
+
}
|
|
234
|
+
var declaringSchemaOf = /* @__PURE__ */ new WeakMap();
|
|
235
|
+
function resolveReferences(tables, schemaName) {
|
|
236
|
+
for (const table of Object.values(tables)) {
|
|
237
|
+
for (const [colName, builder] of Object.entries(table.columns)) {
|
|
238
|
+
const def = builder._def;
|
|
239
|
+
if (def.selfRefColumn !== void 0) {
|
|
240
|
+
if (!(def.selfRefColumn in table.columns)) {
|
|
185
241
|
throw new Error(
|
|
186
|
-
`table ${
|
|
242
|
+
`table "${table.name}" column "${colName}": selfReferences("${def.selfRefColumn}") \u2014 no such column on this table`
|
|
187
243
|
);
|
|
188
244
|
}
|
|
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
|
-
}
|
|
245
|
+
def.references = {
|
|
246
|
+
table: qualifiedTableKey(schemaName, table.name),
|
|
247
|
+
column: def.selfRefColumn
|
|
248
|
+
};
|
|
249
|
+
continue;
|
|
201
250
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
throw new Error(
|
|
251
|
+
if (def.referencesThunk === void 0) continue;
|
|
252
|
+
const target = def.referencesThunk();
|
|
253
|
+
const owner = target?._def?.ownerTable;
|
|
254
|
+
if (owner === void 0) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`table "${table.name}" column "${colName}": references(() => \u2026) must point at a column of a table declared with defineTable(...)`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
const targetColumn = Object.entries(owner.columns).find(
|
|
260
|
+
([, b]) => b._def === target._def
|
|
261
|
+
)?.[0];
|
|
262
|
+
if (targetColumn === void 0) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`table "${table.name}" column "${colName}": the referenced column was not found on table "${owner.name}"`
|
|
265
|
+
);
|
|
207
266
|
}
|
|
208
|
-
|
|
267
|
+
const targetSchema = declaringSchemaOf.get(owner);
|
|
268
|
+
if (targetSchema === void 0) {
|
|
269
|
+
throw new Error(
|
|
270
|
+
`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).`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
def.references = {
|
|
274
|
+
table: qualifiedTableKey(targetSchema, owner.name),
|
|
275
|
+
column: targetColumn
|
|
276
|
+
};
|
|
209
277
|
}
|
|
210
|
-
tables[name] = tableDef;
|
|
211
278
|
}
|
|
212
|
-
|
|
279
|
+
}
|
|
280
|
+
function defineSchema(name, input) {
|
|
281
|
+
if (typeof name !== "string") {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`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.`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (name.trim() === "") {
|
|
287
|
+
throw new Error("defineSchema(name, \u2026): name must not be empty");
|
|
288
|
+
}
|
|
289
|
+
const tables = {};
|
|
290
|
+
for (const handle of input.tables) {
|
|
291
|
+
const table = handle[TABLE_META];
|
|
292
|
+
if (tables[table.name] !== void 0) {
|
|
293
|
+
throw new Error(
|
|
294
|
+
`defineSchema("${name}"): two tables declare the name "${table.name}" \u2014 table names must be unique within a schema`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
tables[table.name] = table;
|
|
298
|
+
declaringSchemaOf.set(table, name);
|
|
299
|
+
}
|
|
300
|
+
resolveReferences(tables, name);
|
|
301
|
+
for (const [name2, def] of Object.entries(tables)) {
|
|
213
302
|
const m = def.memory;
|
|
214
303
|
if (m === void 0) continue;
|
|
215
304
|
const target = Object.values(tables).find((t) => t.name === m.into);
|
|
216
305
|
if (target === void 0) {
|
|
217
|
-
throw new Error(`table ${
|
|
306
|
+
throw new Error(`table ${name2}: memory.into "${m.into}" \u015Femada declared de\u011Fil`);
|
|
218
307
|
}
|
|
219
308
|
const subject = m.subject ?? "owner";
|
|
220
309
|
if (!(subject in def.columns)) {
|
|
221
|
-
throw new Error(`table ${
|
|
310
|
+
throw new Error(`table ${name2}: memory.subject "${subject}" kolonu kaynak tabloda yok`);
|
|
222
311
|
}
|
|
223
312
|
if (!(subject in target.columns)) {
|
|
224
|
-
throw new Error(`table ${
|
|
313
|
+
throw new Error(`table ${name2}: memory.subject "${subject}" kolonu hedef "${m.into}" tablosunda yok`);
|
|
225
314
|
}
|
|
226
315
|
const factCol = target.columns["fact"];
|
|
227
316
|
const factDef = factCol !== void 0 && "_def" in factCol ? factCol._def : factCol;
|
|
228
317
|
if (factDef === void 0 || factDef.type !== "text") {
|
|
229
|
-
throw new Error(`table ${
|
|
318
|
+
throw new Error(`table ${name2}: memory.into "${m.into}" tablosunda "fact" (text) kolonu zorunlu`);
|
|
230
319
|
}
|
|
231
320
|
for (const c of m.from) {
|
|
232
321
|
if (!(c in def.columns)) {
|
|
233
|
-
throw new Error(`table ${
|
|
322
|
+
throw new Error(`table ${name2}: memory.from kolonu "${c}" kaynak tabloda yok`);
|
|
234
323
|
}
|
|
235
324
|
}
|
|
236
325
|
}
|
|
237
326
|
const extensions = [...new Set(input.extensions ?? [])];
|
|
238
|
-
return {
|
|
327
|
+
return {
|
|
328
|
+
name,
|
|
329
|
+
tables,
|
|
330
|
+
extensions,
|
|
331
|
+
exposed: input.exposed ?? name === "public"
|
|
332
|
+
};
|
|
239
333
|
}
|
|
240
334
|
|
|
241
335
|
// src/db/extensions.ts
|
|
@@ -380,59 +474,53 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
380
474
|
ignored: true
|
|
381
475
|
});
|
|
382
476
|
}
|
|
383
|
-
references(table, column) {
|
|
384
|
-
refuseOnVector(this._def, "references");
|
|
385
|
-
this._def.references = { table, column };
|
|
386
|
-
return new _ColumnBuilder(this._def.type, this._def);
|
|
387
|
-
}
|
|
388
477
|
/**
|
|
389
|
-
*
|
|
390
|
-
* (`REFERENCES auth.users(id)`), so a column like `user_id` gets true
|
|
391
|
-
* database cascade/integrity instead of app-layer-only. Sugar for
|
|
392
|
-
* `.references("auth.users", "id")`.
|
|
478
|
+
* Foreign key onto another table's column.
|
|
393
479
|
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
* the
|
|
480
|
+
* The target is a THUNK, not a direct reference. In a cycle (`x → y`, `y → x`)
|
|
481
|
+
* the second table does not exist yet when the first is built; a direct
|
|
482
|
+
* reference makes TypeScript chase its own tail (TS7022 — measured, and making
|
|
483
|
+
* the return type independent of the target does NOT help). The thunk is
|
|
484
|
+
* invoked in `defineSchema`, where every binding exists and every table
|
|
485
|
+
* already knows its name.
|
|
398
486
|
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
* `
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
487
|
+
* In a cycle, ONE side needs an explicit return type:
|
|
488
|
+
* `references((): AnyColumn => y.id)`. One side is enough — measured.
|
|
489
|
+
* For a self-reference use `selfReferences(column)`: no thunk, no annotation.
|
|
490
|
+
*
|
|
491
|
+
* `as` names the FORWARD relation (`author_id` → `author` by default);
|
|
492
|
+
* `reverseAs` names the REVERSE one on the parent, whose default is this
|
|
493
|
+
* table's own name (`users.posts`). Two foreign keys from one table onto one
|
|
494
|
+
* parent therefore need a `reverseAs` on at least one of them — the reverse
|
|
495
|
+
* names would otherwise both be this table's name.
|
|
407
496
|
*/
|
|
408
|
-
|
|
409
|
-
refuseOnVector(this._def, "
|
|
410
|
-
|
|
411
|
-
|
|
497
|
+
references(target, opts) {
|
|
498
|
+
refuseOnVector(this._def, "references");
|
|
499
|
+
if (typeof target !== "function") {
|
|
500
|
+
throw new Error(
|
|
501
|
+
`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.`
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
this._def.referencesThunk = target;
|
|
505
|
+
if (opts?.as !== void 0) this._def.refAs = opts.as;
|
|
506
|
+
if (opts?.reverseAs !== void 0) this._def.reverseAs = opts.reverseAs;
|
|
507
|
+
if (opts?.onDelete !== void 0) this._def.onDeleteAction = opts.onDelete;
|
|
412
508
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
413
509
|
}
|
|
414
510
|
/**
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* root (`ins_...`). Sugar for `.references("auth.installations", "id")`.
|
|
418
|
-
*
|
|
419
|
-
* An installation is an APP INSTALL, not a user: this FK is NOT user ownership.
|
|
420
|
-
* A user-owned row STILL needs its own `.referencesAuthUser(...)` FK so account
|
|
421
|
-
* erasure removes it — an installation reference alone does not tie a row to a
|
|
422
|
-
* user's deletion. Use this only for install-scoped state (device prefs, push
|
|
423
|
-
* routing, …), alongside a separate auth-user FK where the row is user-owned.
|
|
511
|
+
* Foreign key onto THIS table (`parent_id → id`) — category trees, comment
|
|
512
|
+
* replies, org charts.
|
|
424
513
|
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
* blocked by a lingering FK. The server (validateAuthAnchorFK) is the real
|
|
430
|
-
* boundary; this signature is the compile-time DX mirror.
|
|
514
|
+
* No thunk and no type annotation: the target table is the one being declared,
|
|
515
|
+
* so there is nothing to defer and nothing for TypeScript to chase in a circle.
|
|
516
|
+
* Drizzle forces an explicit `(): AnyPgColumn =>` here because its reference
|
|
517
|
+
* always goes through a callback; measured, we do not need one.
|
|
431
518
|
*/
|
|
432
|
-
|
|
433
|
-
refuseOnVector(this._def, "
|
|
434
|
-
this._def.
|
|
435
|
-
this._def.
|
|
519
|
+
selfReferences(column, opts) {
|
|
520
|
+
refuseOnVector(this._def, "selfReferences");
|
|
521
|
+
this._def.selfRefColumn = column;
|
|
522
|
+
if (opts?.as !== void 0) this._def.refAs = opts.as;
|
|
523
|
+
if (opts?.onDelete !== void 0) this._def.onDeleteAction = opts.onDelete;
|
|
436
524
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
437
525
|
}
|
|
438
526
|
/** Set the ON DELETE action for a foreign key reference. */
|
|
@@ -504,6 +592,28 @@ function vector(dimensions) {
|
|
|
504
592
|
b._def.dimensions = dimensions;
|
|
505
593
|
return b;
|
|
506
594
|
}
|
|
595
|
+
function ownedByUser() {
|
|
596
|
+
const b = new ColumnBuilder("text");
|
|
597
|
+
b._def.nullable = false;
|
|
598
|
+
b._def.references = { table: "auth.users", column: "id" };
|
|
599
|
+
b._def.onDeleteAction = "cascade";
|
|
600
|
+
b._def.owns = true;
|
|
601
|
+
return b;
|
|
602
|
+
}
|
|
603
|
+
function userRef(opts) {
|
|
604
|
+
const b = new ColumnBuilder("text");
|
|
605
|
+
b._def.references = { table: "auth.users", column: "id" };
|
|
606
|
+
b._def.onDeleteAction = opts.onDelete;
|
|
607
|
+
if (opts.as !== void 0) b._def.refAs = opts.as;
|
|
608
|
+
return b;
|
|
609
|
+
}
|
|
610
|
+
function installationRef(opts) {
|
|
611
|
+
const b = new ColumnBuilder("text");
|
|
612
|
+
b._def.references = { table: "auth.installations", column: "id" };
|
|
613
|
+
b._def.onDeleteAction = opts.onDelete;
|
|
614
|
+
if (opts.as !== void 0) b._def.refAs = opts.as;
|
|
615
|
+
return b;
|
|
616
|
+
}
|
|
507
617
|
|
|
508
618
|
// src/db/raw.ts
|
|
509
619
|
function raw(name, up, opts) {
|
|
@@ -1013,24 +1123,29 @@ function makeTypedDB(schema, raw2) {
|
|
|
1013
1123
|
EXTENSION_DEPENDENCIES,
|
|
1014
1124
|
PALBASE_EXTENSIONS,
|
|
1015
1125
|
PolicyBuilder,
|
|
1126
|
+
TABLE_META,
|
|
1016
1127
|
TxPlanError,
|
|
1017
1128
|
TxRefError,
|
|
1018
1129
|
bigint,
|
|
1019
1130
|
boolean,
|
|
1020
1131
|
dec,
|
|
1021
1132
|
defineSchema,
|
|
1133
|
+
defineTable,
|
|
1022
1134
|
enumType,
|
|
1023
1135
|
inc,
|
|
1136
|
+
installationRef,
|
|
1024
1137
|
integer,
|
|
1025
1138
|
isPalbaseExtension,
|
|
1026
1139
|
jsonb,
|
|
1027
1140
|
makeTypedDB,
|
|
1028
1141
|
now,
|
|
1029
1142
|
openai,
|
|
1143
|
+
ownedByUser,
|
|
1030
1144
|
policy,
|
|
1031
1145
|
raw,
|
|
1032
1146
|
text,
|
|
1033
1147
|
timestamp,
|
|
1148
|
+
userRef,
|
|
1034
1149
|
uuid,
|
|
1035
1150
|
vector
|
|
1036
1151
|
});
|