@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.
Files changed (61) hide show
  1. package/dist/bin/palbase-backend.cjs +101 -60
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +17 -13
  4. package/dist/bin/palbase-backend.js.map +1 -1
  5. package/dist/{chunk-EIXCY4SS.js → chunk-43A3KGWL.js} +80 -49
  6. package/dist/chunk-43A3KGWL.js.map +1 -0
  7. package/dist/{chunk-ERDL5VAE.js → chunk-5CMLOAEF.js} +2 -2
  8. package/dist/chunk-OEQBHE2Z.js +825 -0
  9. package/dist/chunk-OEQBHE2Z.js.map +1 -0
  10. package/dist/{chunk-7Z6MGMXQ.js → chunk-XJ2RSHEU.js} +11 -5
  11. package/dist/chunk-XJ2RSHEU.js.map +1 -0
  12. package/dist/{chunk-UWSYTUGM.js → chunk-ZQRWW37O.js} +44 -1
  13. package/dist/chunk-ZQRWW37O.js.map +1 -0
  14. package/dist/db/env.cjs.map +1 -1
  15. package/dist/db/env.d.cts +29 -13
  16. package/dist/db/env.d.ts +29 -13
  17. package/dist/db/index.cjs +233 -110
  18. package/dist/db/index.cjs.map +1 -1
  19. package/dist/db/index.d.cts +1 -1
  20. package/dist/db/index.d.ts +1 -1
  21. package/dist/db/index.js +11 -1
  22. package/dist/engine/index.cjs +87 -50
  23. package/dist/engine/index.cjs.map +1 -1
  24. package/dist/engine/index.d.cts +2 -2
  25. package/dist/engine/index.d.ts +2 -2
  26. package/dist/engine/index.js +3 -3
  27. package/dist/{index-C0PMn5jl.d.ts → index-BF1f0DfA.d.ts} +5 -2
  28. package/dist/{index-DAwHMppB.d.cts → index-CoaDN9dL.d.cts} +5 -2
  29. package/dist/{index-ByBMibIJ.d.ts → index-Ct1iiB4N.d.ts} +232 -60
  30. package/dist/{index-D4rts8T7.d.cts → index-CwaWRhyc.d.cts} +232 -60
  31. package/dist/index.cjs +572 -296
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +124 -20
  34. package/dist/index.d.ts +124 -20
  35. package/dist/index.js +164 -216
  36. package/dist/index.js.map +1 -1
  37. package/dist/openapi/index.cjs +100 -36
  38. package/dist/openapi/index.cjs.map +1 -1
  39. package/dist/openapi/index.js +59 -2
  40. package/dist/openapi/index.js.map +1 -1
  41. package/docs/README.md +64 -31
  42. package/docs/endpoints.md +25 -28
  43. package/docs/llms-full.txt +465 -148
  44. package/docs/schema.md +338 -86
  45. package/docs/services.md +39 -4
  46. package/package.json +1 -1
  47. package/template/AGENTS.md +119 -314
  48. package/template/CLAUDE.md +13 -0
  49. package/template/controllers/notes.controller.ts +6 -13
  50. package/template/db/public.ts +38 -0
  51. package/template/models/notes/create.ts +38 -0
  52. package/template/package.json +6 -3
  53. package/template/services/note.service.test.ts +45 -0
  54. package/template/services/note.service.ts +2 -2
  55. package/dist/chunk-7Z6MGMXQ.js.map +0 -1
  56. package/dist/chunk-EIXCY4SS.js.map +0 -1
  57. package/dist/chunk-LCL7TUAI.js +0 -534
  58. package/dist/chunk-LCL7TUAI.js.map +0 -1
  59. package/dist/chunk-UWSYTUGM.js.map +0 -1
  60. package/template/db/schema.ts +0 -35
  61. /package/dist/{chunk-ERDL5VAE.js.map → chunk-5CMLOAEF.js.map} +0 -0
@@ -0,0 +1,825 @@
1
+ import {
2
+ TxPlanBuilder,
3
+ runTxPlan
4
+ } from "./chunk-P2Q27SGP.js";
5
+
6
+ // src/db/schema-json.ts
7
+ function defOf(column) {
8
+ return "_def" in column ? column._def : column;
9
+ }
10
+ function columnToJSON(column) {
11
+ const def = defOf(column);
12
+ const out = {
13
+ type: def.type,
14
+ nullable: def.nullable,
15
+ primaryKey: def.primaryKey
16
+ };
17
+ if (def.defaultValue !== void 0) out.defaultValue = def.defaultValue;
18
+ if (def.defaultRandom === true) out.defaultRandom = true;
19
+ if (def.defaultNow === true) out.defaultNow = true;
20
+ if (def.renamedFrom !== void 0) out.renamedFrom = def.renamedFrom;
21
+ if (def.ignored === true) out.ignored = true;
22
+ if (def.references !== void 0) {
23
+ out.references = { table: def.references.table, column: def.references.column };
24
+ }
25
+ if (def.onDeleteAction !== void 0) out.onDeleteAction = def.onDeleteAction;
26
+ if (def.enumName !== void 0) out.enumName = def.enumName;
27
+ if (def.enumValues !== void 0) out.enumValues = [...def.enumValues];
28
+ if (def.unique === true) out.unique = true;
29
+ if (def.dimensions !== void 0) out.dimensions = def.dimensions;
30
+ return out;
31
+ }
32
+ function policyToJSON(policy2) {
33
+ return {
34
+ name: policy2.name,
35
+ command: policy2.command ?? "all",
36
+ roles: policy2.roles ? [...policy2.roles] : [],
37
+ // null rather than omitted: a policy with no USING clause is a different
38
+ // thing from one whose clause the emitter forgot, and Go reads the
39
+ // difference.
40
+ using: policy2.using ?? null,
41
+ withCheck: policy2.withCheck ?? null,
42
+ permissive: policy2.permissive !== false
43
+ };
44
+ }
45
+ function commonSearchFields(search, out) {
46
+ if (search.synonyms !== void 0 && Object.keys(search.synonyms).length > 0) {
47
+ out.synonyms = Object.fromEntries(
48
+ Object.entries(search.synonyms).map(([word, alts]) => [word, [...alts]])
49
+ );
50
+ }
51
+ if (search.validity === true) out.validity = true;
52
+ }
53
+ function searchToJSON(search, vectorColumn) {
54
+ if (search.from !== void 0 && search.model !== void 0) {
55
+ const out2 = {};
56
+ const textCols = search.text === false ? void 0 : Array.isArray(search.text) && search.text.length > 0 ? search.text : search.from;
57
+ if (textCols !== void 0) out2.text = { columns: [...textCols] };
58
+ const v = { metric: search.metric ?? "cosine" };
59
+ if (vectorColumn !== void 0) v.column = vectorColumn;
60
+ v.embed = {
61
+ provider: search.model.provider,
62
+ model: search.model.model,
63
+ from: [...search.from],
64
+ ...search.model.apiKeyName !== void 0 ? { apiKeyName: search.model.apiKeyName } : {},
65
+ ...search.model.dimensions !== void 0 ? { dimensions: search.model.dimensions } : {},
66
+ ...search.model.baseURL !== void 0 ? { baseURL: search.model.baseURL } : {}
67
+ };
68
+ if (search.staleness !== void 0) v.staleness = search.staleness;
69
+ if (vectorColumn === void 0) {
70
+ v.mode = "chunks";
71
+ if (search.chunks !== void 0) {
72
+ const c = {};
73
+ if (search.chunks.size !== void 0 && search.chunks.size > 0) c.sizeChars = search.chunks.size;
74
+ if (search.chunks.overlap !== void 0 && search.chunks.overlap > 0) c.overlapChars = search.chunks.overlap;
75
+ if (Object.keys(c).length > 0) v.chunks = c;
76
+ }
77
+ }
78
+ out2.vector = [v];
79
+ commonSearchFields(search, out2);
80
+ return out2;
81
+ }
82
+ const out = {};
83
+ if (Array.isArray(search.text) && search.text.length > 0) out.text = { columns: [...search.text] };
84
+ const legs = search.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [search.vector];
85
+ if (legs.length > 0) {
86
+ out.vector = legs.map((leg) => {
87
+ const v = { metric: leg.metric ?? "cosine" };
88
+ if (leg.column !== void 0) v.column = leg.column;
89
+ if (leg.staleness !== void 0) v.staleness = leg.staleness;
90
+ if (leg.model !== void 0) {
91
+ v.embed = {
92
+ provider: leg.model.provider,
93
+ model: leg.model.model,
94
+ from: [...leg.from ?? []],
95
+ ...leg.model.apiKeyName !== void 0 ? { apiKeyName: leg.model.apiKeyName } : {},
96
+ ...leg.model.dimensions !== void 0 ? { dimensions: leg.model.dimensions } : {},
97
+ ...leg.model.baseURL !== void 0 ? { baseURL: leg.model.baseURL } : {}
98
+ };
99
+ }
100
+ return v;
101
+ });
102
+ }
103
+ commonSearchFields(search, out);
104
+ return out;
105
+ }
106
+ function memoryToJSON(m) {
107
+ return {
108
+ from: [...m.from],
109
+ into: m.into,
110
+ ...m.subject !== void 0 ? { subject: m.subject } : {},
111
+ extract: { provider: m.extract.provider, model: m.extract.model }
112
+ };
113
+ }
114
+ function tableToJSON(table, schemaName) {
115
+ const columns = {};
116
+ for (const [name, column] of Object.entries(table.columns)) {
117
+ columns[name] = columnToJSON(column);
118
+ }
119
+ const out = {
120
+ name: table.name,
121
+ schema: schemaName,
122
+ columns,
123
+ // Read, not re-derived. `defineSchema` already resolves the fail-closed
124
+ // default (RLS on unless the author wrote `rls: false`, and forced on by any
125
+ // policy), and a second copy of a SECURITY default is exactly the thing that
126
+ // drifts — the direction it drifted last time was "expose everything", and
127
+ // the live proof was one user reading another's rows.
128
+ rls: table.rls,
129
+ policies: (table.policies ?? []).map(policyToJSON)
130
+ };
131
+ if (table.primaryKey !== void 0 && table.primaryKey.length > 0) {
132
+ out.primaryKey = [...table.primaryKey];
133
+ }
134
+ if (table.unique !== void 0 && table.unique.length > 0) {
135
+ out.uniqueConstraints = table.unique.map((u) => ({ name: u.name, columns: [...u.columns] }));
136
+ }
137
+ if (table.raw !== void 0 && table.raw.length > 0) {
138
+ out.rawConstraints = table.raw.map((r) => ({
139
+ name: r.name,
140
+ up: r.up,
141
+ down: r.down ?? null
142
+ }));
143
+ }
144
+ if (table.checks !== void 0 && table.checks.length > 0) {
145
+ out.checks = table.checks.map((c) => ({ name: c.name, expr: c.expr }));
146
+ }
147
+ if (table.indexes !== void 0 && table.indexes.length > 0) {
148
+ out.indexes = table.indexes.map((i) => ({ name: i.name, columns: [...i.columns] }));
149
+ }
150
+ if (table.search !== void 0) {
151
+ const vectorColumn = Object.entries(columns).find(([, c]) => c.dimensions !== void 0)?.[0];
152
+ const sj = searchToJSON(table.search, vectorColumn);
153
+ if (sj.text !== void 0 || sj.vector !== void 0) out.search = sj;
154
+ }
155
+ if (table.memory !== void 0) {
156
+ out.memory = memoryToJSON(table.memory);
157
+ }
158
+ return out;
159
+ }
160
+ function qualifiedTableKey(schemaName, tableName) {
161
+ return schemaName === "public" ? tableName : `${schemaName}.${tableName}`;
162
+ }
163
+ function toSchemaJSON(schemas) {
164
+ const tables = {};
165
+ const extensions = [];
166
+ const meta = [];
167
+ const seen = /* @__PURE__ */ new Set();
168
+ for (const schema of schemas) {
169
+ if (seen.has(schema.name)) {
170
+ throw new Error(`two schemas declare the name "${schema.name}" \u2014 schema names must be unique`);
171
+ }
172
+ seen.add(schema.name);
173
+ for (const table of Object.values(schema.tables)) {
174
+ const json = tableToJSON(table, schema.name);
175
+ tables[qualifiedTableKey(schema.name, json.name)] = json;
176
+ }
177
+ extensions.push(...schema.extensions ?? []);
178
+ meta.push({ name: schema.name, exposed: schema.exposed });
179
+ }
180
+ return { tables, extensions: [...new Set(extensions)], schemas: meta };
181
+ }
182
+
183
+ // src/db/policy.ts
184
+ function quote(name) {
185
+ return `"${name.replace(/"/g, '""')}"`;
186
+ }
187
+ var PolicyBuilder = class {
188
+ _def;
189
+ constructor(name) {
190
+ this._def = {
191
+ name,
192
+ command: "all",
193
+ roles: ["authenticated"],
194
+ using: null,
195
+ withCheck: null,
196
+ permissive: true
197
+ };
198
+ }
199
+ /** Restrict the policy to a single SQL command (default `"all"`). */
200
+ for(command) {
201
+ this._def.command = command;
202
+ return this;
203
+ }
204
+ /**
205
+ * Set the DB roles the policy applies to (the `TO` clause), replacing any
206
+ * previously-set roles. Call with no arguments to target PUBLIC (all roles).
207
+ *
208
+ * @example
209
+ * policy("p").to("authenticated")
210
+ * policy("p").to("authenticated", "service_role")
211
+ * policy("p").to() // PUBLIC
212
+ */
213
+ to(...roles) {
214
+ this._def.roles = roles;
215
+ return this;
216
+ }
217
+ /** Set the `USING (...)` row-visibility expression (raw SQL). */
218
+ using(sqlExpr) {
219
+ this._def.using = sqlExpr;
220
+ return this;
221
+ }
222
+ /**
223
+ * "Rows of THIS table whose owner the caller is a member of" — the membership
224
+ * pattern, written so it cannot recurse.
225
+ *
226
+ * THE TRAP IT EXISTS FOR. Written by hand, membership policies point at each
227
+ * other: `channels` is visible to members, so its policy reads
228
+ * `channel_members`; `channel_members` is visible to members, so its policy
229
+ * reads `channels`. Postgres refuses the pair at query time with `infinite
230
+ * recursion detected in policy for relation ...`, and the error names the
231
+ * relation but not the cycle. The way out is asymmetry — the MEMBERSHIP table
232
+ * is protected by `user_id = auth.uid()` and nothing else, and every other
233
+ * table subqueries INTO it. That shape was in the platform's own schema and
234
+ * written down nowhere; a customer recovered it by reading that schema.
235
+ *
236
+ * `(select auth.uid())` rather than a bare call: the scalar subquery is
237
+ * evaluated ONCE per statement instead of per row.
238
+ *
239
+ * @example
240
+ * // channels: visible to members. The membership table gets the simple one.
241
+ * policy("member_read").for("select").to("authenticated")
242
+ * .memberOf("channel_members", "channel_id")
243
+ * // → id IN (SELECT "channel_id" FROM "channel_members"
244
+ * // WHERE "user_id" = (select auth.uid()))
245
+ */
246
+ memberOf(membershipTable, foreignKey, options = {}) {
247
+ if (this._def.using !== null) {
248
+ throw new Error(
249
+ `policy(${this._def.name}).memberOf(): this policy already has a using() expression. Write one or the other \u2014 memberOf IS the using expression.`
250
+ );
251
+ }
252
+ const column = options.column ?? "id";
253
+ const userColumn = options.userColumn ?? "user_id";
254
+ this._def.using = `${quote(column)} IN (SELECT ${quote(foreignKey)} FROM ${quote(membershipTable)} WHERE ${quote(userColumn)} = (select auth.uid()))`;
255
+ return this;
256
+ }
257
+ /** Set the `WITH CHECK (...)` write-validation expression (raw SQL). */
258
+ withCheck(sqlExpr) {
259
+ this._def.withCheck = sqlExpr;
260
+ return this;
261
+ }
262
+ /** Set the policy mode: `"permissive"` (default, OR-combined) or
263
+ * `"restrictive"` (AND-combined). */
264
+ as(mode) {
265
+ this._def.permissive = mode === "permissive";
266
+ return this;
267
+ }
268
+ };
269
+ function policy(name) {
270
+ return new PolicyBuilder(name);
271
+ }
272
+
273
+ // src/db/schema.ts
274
+ function toPolicyDef(p) {
275
+ return p instanceof PolicyBuilder ? p._def : p;
276
+ }
277
+ var TABLE_META = /* @__PURE__ */ Symbol.for("palbase.table.meta");
278
+ function defineTable(name, input) {
279
+ if (name.trim() === "") {
280
+ throw new Error("defineTable(name, \u2026): name must not be empty");
281
+ }
282
+ const policies = (input.policies ?? []).map(toPolicyDef);
283
+ const rls = policies.length > 0 || input.rls !== false;
284
+ const def = {
285
+ name,
286
+ columns: input.columns,
287
+ rls,
288
+ policies
289
+ };
290
+ if (input.primaryKey !== void 0) def.primaryKey = input.primaryKey;
291
+ if (input.unique !== void 0) def.unique = input.unique;
292
+ if (input.raw !== void 0 && input.raw.length > 0) def.raw = input.raw.slice();
293
+ if (input.checks !== void 0 && input.checks.length > 0) def.checks = input.checks.slice();
294
+ if (input.indexes !== void 0 && input.indexes.length > 0) def.indexes = input.indexes.slice();
295
+ if (input.search !== void 0) {
296
+ const s = input.search;
297
+ if (s.from !== void 0) {
298
+ if (s.vector !== void 0) {
299
+ throw new Error(
300
+ `table ${name}: search beyan\u0131nda tek bi\xE7im kullan\u0131n \u2014 'from' (yeni) ile 'vector' (eski) birlikte olamaz`
301
+ );
302
+ }
303
+ if (s.from.length === 0) {
304
+ throw new Error(`table ${name}: search.from bo\u015F olamaz`);
305
+ }
306
+ if (s.model === void 0) {
307
+ throw new Error(
308
+ `table ${name}: search.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin (BYO i\xE7in eski 'vector' bi\xE7imini kullan\u0131n)`
309
+ );
310
+ }
311
+ if (Array.isArray(s.text) && s.text.length === 0) {
312
+ throw new Error(`table ${name}: search.text bo\u015F dizi olamaz \u2014 FTS istemiyorsan text: false yaz\u0131n`);
313
+ }
314
+ } else {
315
+ if (typeof s.text === "boolean") {
316
+ throw new Error(`table ${name}: text:${String(s.text)} yaln\u0131z yeni bi\xE7imde ('from' ile) ge\xE7erli`);
317
+ }
318
+ if (s.text === void 0 && s.vector === void 0) {
319
+ throw new Error(
320
+ `table ${name}: search beyan\u0131 bo\u015F \u2014 en az bir kol (text ya da vector) verin, yoksa alan\u0131 hi\xE7 yazmay\u0131n`
321
+ );
322
+ }
323
+ if (s.text !== void 0 && s.text.length === 0) {
324
+ throw new Error(`table ${name}: search.text bo\u015F olamaz \u2014 FTS kolu istemiyorsan alan\u0131 hi\xE7 yazma`);
325
+ }
326
+ const legs = s.vector === void 0 ? [] : Array.isArray(s.vector) ? s.vector : [s.vector];
327
+ for (const leg of legs) {
328
+ if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
329
+ throw new Error(`table ${name}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
330
+ }
331
+ if (leg.model === void 0 && leg.from !== void 0) {
332
+ throw new Error(`table ${name}: search.vector.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin`);
333
+ }
334
+ }
335
+ }
336
+ def.search = s;
337
+ }
338
+ if (input.memory !== void 0) {
339
+ if (input.memory.from.length === 0) {
340
+ throw new Error(`table ${name}: memory.from bo\u015F olamaz`);
341
+ }
342
+ def.memory = input.memory;
343
+ }
344
+ for (const b of Object.values(input.columns)) {
345
+ b._def.ownerTable = def;
346
+ }
347
+ const handle = { ...input.columns };
348
+ Object.defineProperty(handle, TABLE_META, { value: def, enumerable: false });
349
+ return handle;
350
+ }
351
+ var declaringSchemaOf = /* @__PURE__ */ new WeakMap();
352
+ function resolveReferences(tables, schemaName) {
353
+ for (const table of Object.values(tables)) {
354
+ for (const [colName, builder] of Object.entries(table.columns)) {
355
+ const def = builder._def;
356
+ if (def.selfRefColumn !== void 0) {
357
+ if (!(def.selfRefColumn in table.columns)) {
358
+ throw new Error(
359
+ `table "${table.name}" column "${colName}": selfReferences("${def.selfRefColumn}") \u2014 no such column on this table`
360
+ );
361
+ }
362
+ def.references = {
363
+ table: qualifiedTableKey(schemaName, table.name),
364
+ column: def.selfRefColumn
365
+ };
366
+ continue;
367
+ }
368
+ if (def.referencesThunk === void 0) continue;
369
+ const target = def.referencesThunk();
370
+ const owner = target?._def?.ownerTable;
371
+ if (owner === void 0) {
372
+ throw new Error(
373
+ `table "${table.name}" column "${colName}": references(() => \u2026) must point at a column of a table declared with defineTable(...)`
374
+ );
375
+ }
376
+ const targetColumn = Object.entries(owner.columns).find(
377
+ ([, b]) => b._def === target._def
378
+ )?.[0];
379
+ if (targetColumn === void 0) {
380
+ throw new Error(
381
+ `table "${table.name}" column "${colName}": the referenced column was not found on table "${owner.name}"`
382
+ );
383
+ }
384
+ const targetSchema = declaringSchemaOf.get(owner);
385
+ if (targetSchema === void 0) {
386
+ throw new Error(
387
+ `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).`
388
+ );
389
+ }
390
+ def.references = {
391
+ table: qualifiedTableKey(targetSchema, owner.name),
392
+ column: targetColumn
393
+ };
394
+ }
395
+ }
396
+ }
397
+ function defineSchema(name, input) {
398
+ if (typeof name !== "string") {
399
+ throw new Error(
400
+ `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.`
401
+ );
402
+ }
403
+ if (name.trim() === "") {
404
+ throw new Error("defineSchema(name, \u2026): name must not be empty");
405
+ }
406
+ const tables = {};
407
+ for (const handle of input.tables) {
408
+ const table = handle[TABLE_META];
409
+ if (tables[table.name] !== void 0) {
410
+ throw new Error(
411
+ `defineSchema("${name}"): two tables declare the name "${table.name}" \u2014 table names must be unique within a schema`
412
+ );
413
+ }
414
+ tables[table.name] = table;
415
+ declaringSchemaOf.set(table, name);
416
+ }
417
+ resolveReferences(tables, name);
418
+ for (const [name2, def] of Object.entries(tables)) {
419
+ const m = def.memory;
420
+ if (m === void 0) continue;
421
+ const target = Object.values(tables).find((t) => t.name === m.into);
422
+ if (target === void 0) {
423
+ throw new Error(`table ${name2}: memory.into "${m.into}" \u015Femada declared de\u011Fil`);
424
+ }
425
+ const subject = m.subject ?? "owner";
426
+ if (!(subject in def.columns)) {
427
+ throw new Error(`table ${name2}: memory.subject "${subject}" kolonu kaynak tabloda yok`);
428
+ }
429
+ if (!(subject in target.columns)) {
430
+ throw new Error(`table ${name2}: memory.subject "${subject}" kolonu hedef "${m.into}" tablosunda yok`);
431
+ }
432
+ const factCol = target.columns["fact"];
433
+ const factDef = factCol !== void 0 && "_def" in factCol ? factCol._def : factCol;
434
+ if (factDef === void 0 || factDef.type !== "text") {
435
+ throw new Error(`table ${name2}: memory.into "${m.into}" tablosunda "fact" (text) kolonu zorunlu`);
436
+ }
437
+ for (const c of m.from) {
438
+ if (!(c in def.columns)) {
439
+ throw new Error(`table ${name2}: memory.from kolonu "${c}" kaynak tabloda yok`);
440
+ }
441
+ }
442
+ }
443
+ const extensions = [...new Set(input.extensions ?? [])];
444
+ return {
445
+ name,
446
+ tables,
447
+ extensions,
448
+ exposed: input.exposed ?? name === "public"
449
+ };
450
+ }
451
+
452
+ // src/db/extensions.ts
453
+ var PALBASE_EXTENSIONS = [
454
+ // Search & text
455
+ "vector",
456
+ // pgvector: AI embeddings + vector similarity search (semantic search / RAG).
457
+ // NB: the Postgres extension is named "vector", not "pgvector" — declare "vector".
458
+ "pg_trgm",
459
+ // trigram fuzzy / typo-tolerant text search
460
+ "unaccent",
461
+ // accent-insensitive text search
462
+ "citext",
463
+ // case-insensitive text type
464
+ // Geospatial / location
465
+ "cube",
466
+ // multi-dimensional cubes (dependency of earthdistance)
467
+ "earthdistance",
468
+ // great-circle distance (needs cube)
469
+ // Data types & structures
470
+ "hstore",
471
+ // key/value pairs in a single column
472
+ "ltree",
473
+ // hierarchical tree-structured labels
474
+ // Indexing & constraints
475
+ "btree_gist",
476
+ // GiST operator classes for scalar types — needed for EXCLUDE
477
+ // constraints that mix "=" with a range/&& overlap (e.g. no-double-booking).
478
+ // Scheduling
479
+ // Crypto / ids (also installed by default; listable for explicitness)
480
+ "pgcrypto",
481
+ // cryptographic functions (hashing, encryption)
482
+ "uuid-ossp"
483
+ // UUID generation functions
484
+ ];
485
+ var EXTENSION_DEPENDENCIES = {
486
+ earthdistance: ["cube"]
487
+ };
488
+ function isPalbaseExtension(name) {
489
+ return PALBASE_EXTENSIONS.includes(name);
490
+ }
491
+
492
+ // src/db/columns.ts
493
+ function refuseOnVector(def, modifier) {
494
+ if (def.type === "vector") {
495
+ throw new Error(`vector column: .${modifier}() is not supported (FR-002 \u2014 allowed: nullable()/notNull())`);
496
+ }
497
+ }
498
+ var ColumnBuilder = class _ColumnBuilder {
499
+ _def;
500
+ constructor(type, existingDef) {
501
+ this._def = existingDef ?? {
502
+ type,
503
+ nullable: false,
504
+ primaryKey: false
505
+ };
506
+ }
507
+ /** Mark this column as the primary key. */
508
+ primaryKey() {
509
+ refuseOnVector(this._def, "primaryKey");
510
+ this._def.primaryKey = true;
511
+ return new _ColumnBuilder(this._def.type, this._def);
512
+ }
513
+ /** Mark this column as NOT NULL (default). */
514
+ notNull() {
515
+ this._def.nullable = false;
516
+ return new _ColumnBuilder(this._def.type, this._def);
517
+ }
518
+ /** Allow NULL values. */
519
+ nullable() {
520
+ this._def.nullable = true;
521
+ return new _ColumnBuilder(this._def.type, this._def);
522
+ }
523
+ /** Set a default value. */
524
+ default(value) {
525
+ refuseOnVector(this._def, "default");
526
+ this._def.defaultValue = value;
527
+ return new _ColumnBuilder(this._def.type, this._def);
528
+ }
529
+ /** UUID: generate a random default (gen_random_uuid()). */
530
+ defaultRandom() {
531
+ refuseOnVector(this._def, "defaultRandom");
532
+ this._def.defaultRandom = true;
533
+ return new _ColumnBuilder(this._def.type, this._def);
534
+ }
535
+ /** Timestamp: default to now(). */
536
+ defaultNow() {
537
+ refuseOnVector(this._def, "defaultNow");
538
+ this._def.defaultNow = true;
539
+ return new _ColumnBuilder(this._def.type, this._def);
540
+ }
541
+ /**
542
+ * The DATABASE assigns this column's value — a trigger, a rule, an identity.
543
+ *
544
+ * The column becomes optional on INSERT (the author has nothing to send) while
545
+ * the DDL stays free of a DEFAULT this schema would not honour. It is NOT
546
+ * `default()`: that declares a value the schema promises to write.
547
+ *
548
+ * Naming: deliberately not `generated()`. Postgres has GENERATED columns and
549
+ * they are a different thing; borrowing the word would send a reader — or a
550
+ * model writing a schema — to the wrong feature.
551
+ */
552
+ dbAssigned() {
553
+ this._def.dbAssigned = true;
554
+ return new _ColumnBuilder(this._def.type, this._def);
555
+ }
556
+ /** Add a foreign key reference. */
557
+ /**
558
+ * Declares that this column used to be called `previous`.
559
+ *
560
+ * A schema diff sees one name gone and another present; it cannot know whether
561
+ * you renamed a column or dropped one and added another, and the two are very
562
+ * different — the second loses every value. Saying so here turns the plan into
563
+ * `ALTER TABLE … RENAME COLUMN` instead.
564
+ *
565
+ * Once the rename has been applied the annotation is inert (the old name is no
566
+ * longer there to rename), so it can be deleted at your leisure.
567
+ */
568
+ renamedFrom(previous) {
569
+ this._def.renamedFrom = previous;
570
+ return this;
571
+ }
572
+ /**
573
+ * See {@link ColumnDef.ignored}.
574
+ *
575
+ * COPIES the def rather than mutating it. The constructor takes an existing
576
+ * def BY REFERENCE, so every builder derived from another shares one object —
577
+ * `const a = slug.unique()` leaves `a._def === slug._def`. An in-place
578
+ * `ignored = true` therefore marks every column sharing that def, including
579
+ * one another table actively reads, and the gate would let THAT column be
580
+ * dropped. Measured before this copy existed.
581
+ *
582
+ * The aliasing is older than this method and other fields leak through it too.
583
+ * The reason this one cannot wait: every other leak produces a VISIBLE schema
584
+ * difference — the plan shows it, the DDL shows it. This one is invisible by
585
+ * design (no DDL, no diff, no plan line), so its only effect is to disarm a
586
+ * safety gate in silence.
587
+ */
588
+ ignored() {
589
+ return new _ColumnBuilder(this._def.type, {
590
+ ...this._def,
591
+ ignored: true
592
+ });
593
+ }
594
+ /**
595
+ * Foreign key onto another table's column.
596
+ *
597
+ * The target is a THUNK, not a direct reference. In a cycle (`x → y`, `y → x`)
598
+ * the second table does not exist yet when the first is built; a direct
599
+ * reference makes TypeScript chase its own tail (TS7022 — measured, and making
600
+ * the return type independent of the target does NOT help). The thunk is
601
+ * invoked in `defineSchema`, where every binding exists and every table
602
+ * already knows its name.
603
+ *
604
+ * In a cycle, ONE side needs an explicit return type:
605
+ * `references((): AnyColumn => y.id)`. One side is enough — measured.
606
+ * For a self-reference use `selfReferences(column)`: no thunk, no annotation.
607
+ */
608
+ references(target, opts) {
609
+ refuseOnVector(this._def, "references");
610
+ if (typeof target !== "function") {
611
+ throw new Error(
612
+ `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.`
613
+ );
614
+ }
615
+ this._def.referencesThunk = target;
616
+ if (opts?.as !== void 0) this._def.refAs = opts.as;
617
+ if (opts?.onDelete !== void 0) this._def.onDeleteAction = opts.onDelete;
618
+ return new _ColumnBuilder(this._def.type, this._def);
619
+ }
620
+ /**
621
+ * Foreign key onto THIS table (`parent_id → id`) — category trees, comment
622
+ * replies, org charts.
623
+ *
624
+ * No thunk and no type annotation: the target table is the one being declared,
625
+ * so there is nothing to defer and nothing for TypeScript to chase in a circle.
626
+ * Drizzle forces an explicit `(): AnyPgColumn =>` here because its reference
627
+ * always goes through a callback; measured, we do not need one.
628
+ */
629
+ selfReferences(column, opts) {
630
+ refuseOnVector(this._def, "selfReferences");
631
+ this._def.selfRefColumn = column;
632
+ if (opts?.as !== void 0) this._def.refAs = opts.as;
633
+ if (opts?.onDelete !== void 0) this._def.onDeleteAction = opts.onDelete;
634
+ return new _ColumnBuilder(this._def.type, this._def);
635
+ }
636
+ /** Set the ON DELETE action for a foreign key reference. */
637
+ onDelete(action) {
638
+ this._def.onDeleteAction = action;
639
+ return new _ColumnBuilder(this._def.type, this._def);
640
+ }
641
+ /** Add a single-column UNIQUE constraint. */
642
+ unique() {
643
+ refuseOnVector(this._def, "unique");
644
+ this._def.unique = true;
645
+ return new _ColumnBuilder(this._def.type, this._def);
646
+ }
647
+ /**
648
+ * Declare how this column's value is projected in and out of the process.
649
+ *
650
+ * The DDL does not move: `numeric` stays `numeric`, and the driver still hands
651
+ * back what Postgres sent. What changes is the type the row surface exposes —
652
+ * it becomes `Target`:
653
+ *
654
+ * amount: numeric().transform<number>({ fromDb: Number, toDb: String })
655
+ *
656
+ * `numeric` surfacing as `string` is CORRECT (a JS number cannot hold
657
+ * arbitrary precision), and that is exactly why this exists: application code
658
+ * that does arithmetic on the column otherwise rewrites the same
659
+ * `Number(row.amount)` / `String(x)` pair in every controller that touches it,
660
+ * and each rewrite is a place the two directions can drift apart.
661
+ *
662
+ * A transform is a PROJECTION, never a constraint: it lives only in this
663
+ * process, so it can neither validate nor migrate what is stored.
664
+ */
665
+ transform(fns) {
666
+ this._def.transform = fns;
667
+ return new _ColumnBuilder(this._def.type, this._def);
668
+ }
669
+ };
670
+ function uuid() {
671
+ return new ColumnBuilder("uuid");
672
+ }
673
+ function text() {
674
+ return new ColumnBuilder("text");
675
+ }
676
+ function integer() {
677
+ return new ColumnBuilder("integer");
678
+ }
679
+ function bigint() {
680
+ return new ColumnBuilder("bigint");
681
+ }
682
+ function numeric() {
683
+ return new ColumnBuilder("numeric");
684
+ }
685
+ function boolean() {
686
+ return new ColumnBuilder("boolean");
687
+ }
688
+ function timestamp() {
689
+ return new ColumnBuilder("timestamp");
690
+ }
691
+ function jsonb() {
692
+ return new ColumnBuilder("jsonb");
693
+ }
694
+ function enumType(name, values) {
695
+ const builder = new ColumnBuilder("enum");
696
+ builder._def.enumName = name;
697
+ builder._def.enumValues = [...values];
698
+ return builder;
699
+ }
700
+ function vector(dimensions) {
701
+ if (!Number.isInteger(dimensions) || dimensions < 1 || dimensions > 2e3) {
702
+ throw new Error(`vector(): dimensions must be an integer in [1, 2000], got ${String(dimensions)}`);
703
+ }
704
+ const b = new ColumnBuilder("vector");
705
+ b._def.dimensions = dimensions;
706
+ return b;
707
+ }
708
+ function ownedByUser() {
709
+ const b = new ColumnBuilder("text");
710
+ b._def.nullable = false;
711
+ b._def.references = { table: "auth.users", column: "id" };
712
+ b._def.onDeleteAction = "cascade";
713
+ b._def.owns = true;
714
+ return b;
715
+ }
716
+ function userRef(opts) {
717
+ const b = new ColumnBuilder("text");
718
+ b._def.references = { table: "auth.users", column: "id" };
719
+ b._def.onDeleteAction = opts.onDelete;
720
+ if (opts.as !== void 0) b._def.refAs = opts.as;
721
+ return b;
722
+ }
723
+ function installationRef(opts) {
724
+ const b = new ColumnBuilder("text");
725
+ b._def.references = { table: "auth.installations", column: "id" };
726
+ b._def.onDeleteAction = opts.onDelete;
727
+ if (opts.as !== void 0) b._def.refAs = opts.as;
728
+ return b;
729
+ }
730
+
731
+ // src/db/raw.ts
732
+ function raw(name, up, opts) {
733
+ return { name, up, ...opts?.down != null ? { down: opts.down } : {} };
734
+ }
735
+
736
+ // src/db/embedding.ts
737
+ var openai = {
738
+ embedding(model, opts) {
739
+ return {
740
+ provider: "openai",
741
+ model,
742
+ apiKeyName: opts?.apiKeyName ?? "OPENAI_API_KEY",
743
+ ...opts?.dimensions !== void 0 ? { dimensions: opts.dimensions } : {},
744
+ ...opts?.baseURL !== void 0 ? { baseURL: opts.baseURL } : {}
745
+ };
746
+ },
747
+ chat(model) {
748
+ return { provider: "openai", model };
749
+ }
750
+ };
751
+
752
+ // src/db/typed-db.ts
753
+ function makeTypedTable(name, raw2) {
754
+ return {
755
+ insert: (data) => raw2.insert(name, data),
756
+ upsert: (data, opts) => raw2.upsert(name, data, opts),
757
+ update: (id, data) => raw2.update(name, id, data),
758
+ delete: (id) => raw2.delete(name, id),
759
+ findById: (id) => raw2.findById(name, id),
760
+ findMany: (query, opts) => raw2.findMany(name, query, opts),
761
+ updateMany: (where, set) => raw2.updateMany(
762
+ name,
763
+ where,
764
+ set
765
+ ),
766
+ deleteMany: (where) => raw2.deleteMany(name, where),
767
+ count: (where) => raw2.count(name, where)
768
+ };
769
+ }
770
+ function makeTypedDB(schema, raw2) {
771
+ const tables = {};
772
+ for (const key of Object.keys(schema.tables)) {
773
+ const tableDef = schema.tables[key];
774
+ if (tableDef !== void 0) {
775
+ tables[key] = makeTypedTable(tableDef.name, raw2);
776
+ }
777
+ }
778
+ const result = {
779
+ tables,
780
+ transaction(fn) {
781
+ const builder = new TxPlanBuilder();
782
+ const planTables = {};
783
+ for (const key of Object.keys(schema.tables)) {
784
+ const tableDef = schema.tables[key];
785
+ if (tableDef !== void 0) planTables[key] = builder.table(tableDef.name);
786
+ }
787
+ return runTxPlan(
788
+ raw2,
789
+ planTables,
790
+ builder,
791
+ fn
792
+ );
793
+ }
794
+ };
795
+ return result;
796
+ }
797
+
798
+ export {
799
+ toSchemaJSON,
800
+ PolicyBuilder,
801
+ policy,
802
+ TABLE_META,
803
+ defineTable,
804
+ defineSchema,
805
+ PALBASE_EXTENSIONS,
806
+ EXTENSION_DEPENDENCIES,
807
+ isPalbaseExtension,
808
+ uuid,
809
+ text,
810
+ integer,
811
+ bigint,
812
+ numeric,
813
+ boolean,
814
+ timestamp,
815
+ jsonb,
816
+ enumType,
817
+ vector,
818
+ ownedByUser,
819
+ userRef,
820
+ installationRef,
821
+ raw,
822
+ openai,
823
+ makeTypedDB
824
+ };
825
+ //# sourceMappingURL=chunk-OEQBHE2Z.js.map