@palbase/backend 25.0.0 → 25.0.2

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 (57) hide show
  1. package/dist/bin/palbase-backend.cjs +2 -0
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +2 -2
  4. package/dist/{chunk-43A3KGWL.js → chunk-34I4GB7D.js} +4 -2
  5. package/dist/{chunk-43A3KGWL.js.map → chunk-34I4GB7D.js.map} +1 -1
  6. package/dist/{chunk-ZQRWW37O.js → chunk-35PNTIRN.js} +5 -1
  7. package/dist/chunk-35PNTIRN.js.map +1 -0
  8. package/dist/{chunk-OEQBHE2Z.js → chunk-7JSEN7UR.js} +20 -4
  9. package/dist/chunk-7JSEN7UR.js.map +1 -0
  10. package/dist/{chunk-5CMLOAEF.js → chunk-YOY5DFQS.js} +2 -2
  11. package/dist/db/env.cjs.map +1 -1
  12. package/dist/db/env.d.cts +2 -1
  13. package/dist/db/env.d.ts +2 -1
  14. package/dist/db/index.cjs +17 -3
  15. package/dist/db/index.cjs.map +1 -1
  16. package/dist/db/index.d.cts +2 -2
  17. package/dist/db/index.d.ts +2 -2
  18. package/dist/db/index.js +1 -1
  19. package/dist/{endpoint-DMwJjEHQ.d.ts → endpoint-CTEHhb7A.d.ts} +19 -8
  20. package/dist/{endpoint-BSGw1pTu.d.cts → endpoint-DYHMo6cC.d.cts} +19 -8
  21. package/dist/engine/index.cjs +2 -0
  22. package/dist/engine/index.cjs.map +1 -1
  23. package/dist/engine/index.d.cts +4 -4
  24. package/dist/engine/index.d.ts +4 -4
  25. package/dist/engine/index.js +2 -2
  26. package/dist/{index-BF1f0DfA.d.ts → index-CUomTA3e.d.ts} +3 -3
  27. package/dist/{index-Ct1iiB4N.d.ts → index-CW21M9Z3.d.ts} +24 -17
  28. package/dist/{index-CoaDN9dL.d.cts → index-ClpDeSos.d.cts} +3 -3
  29. package/dist/{index-CwaWRhyc.d.cts → index-CmBK76nx.d.cts} +24 -17
  30. package/dist/index.cjs +80 -51
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +13 -11
  33. package/dist/index.d.ts +13 -11
  34. package/dist/index.js +62 -51
  35. package/dist/index.js.map +1 -1
  36. package/dist/openapi/index.cjs +1 -1
  37. package/dist/openapi/index.cjs.map +1 -1
  38. package/dist/openapi/index.d.cts +2 -2
  39. package/dist/openapi/index.d.ts +2 -2
  40. package/dist/openapi/index.js +3 -3
  41. package/dist/openapi/index.js.map +1 -1
  42. package/dist/{registry-CgJvDYUW.d.cts → registry-CC0WBQq6.d.cts} +1 -1
  43. package/dist/{registry-B8pld3fj.d.ts → registry-dZZ5JKYg.d.ts} +1 -1
  44. package/dist/test/index.d.cts +1 -1
  45. package/dist/test/index.d.ts +1 -1
  46. package/docs/README.md +9 -8
  47. package/docs/database.md +2 -2
  48. package/docs/endpoints.md +6 -5
  49. package/docs/getting-started.md +1 -1
  50. package/docs/llms-full.txt +74 -38
  51. package/docs/migrations.md +16 -14
  52. package/docs/schema.md +38 -7
  53. package/docs/services.md +2 -1
  54. package/package.json +1 -1
  55. package/dist/chunk-OEQBHE2Z.js.map +0 -1
  56. package/dist/chunk-ZQRWW37O.js.map +0 -1
  57. /package/dist/{chunk-5CMLOAEF.js.map → chunk-YOY5DFQS.js.map} +0 -0
@@ -1,8 +1,8 @@
1
- export { A as App, m as AuthVerifier, B as BootRefused, n as CreateAppOptions, E as EgressPolicy, p as EngineConfig, M as ModuleClients, q as RateLimiter, r as RequestDatabase, s as RouteEntry, t as RuntimeHooks, u as ScrubResult, v as SqlDriver, w as SqlTx, x as buildRouteTable, y as createApp, z as createLazyTransaction, G as createOps, H as createRequestDatabase, I as effectiveAuth, J as hostAllowed, K as installEgressFence, O as loadConfig, P as makeMemoryCache, Q as matchRoute, T as quoteIdent, U as scrubSecrets, V as withTables } from '../index-CoaDN9dL.cjs';
2
- import '../endpoint-BSGw1pTu.cjs';
1
+ export { A as App, m as AuthVerifier, B as BootRefused, n as CreateAppOptions, E as EgressPolicy, p as EngineConfig, M as ModuleClients, q as RateLimiter, r as RequestDatabase, s as RouteEntry, t as RuntimeHooks, u as ScrubResult, v as SqlDriver, w as SqlTx, x as buildRouteTable, y as createApp, z as createLazyTransaction, G as createOps, H as createRequestDatabase, I as effectiveAuth, J as hostAllowed, K as installEgressFence, O as loadConfig, P as makeMemoryCache, Q as matchRoute, T as quoteIdent, U as scrubSecrets, V as withTables } from '../index-ClpDeSos.cjs';
2
+ import '../endpoint-DYHMo6cC.cjs';
3
3
  import '../stack.cjs';
4
4
  import 'node:async_hooks';
5
- import '../index-CwaWRhyc.cjs';
5
+ import '../index-CmBK76nx.cjs';
6
6
  import '../db/env.cjs';
7
- import '../registry-CgJvDYUW.cjs';
7
+ import '../registry-CC0WBQq6.cjs';
8
8
  import 'zod';
@@ -1,8 +1,8 @@
1
- export { A as App, m as AuthVerifier, B as BootRefused, n as CreateAppOptions, E as EgressPolicy, p as EngineConfig, M as ModuleClients, q as RateLimiter, r as RequestDatabase, s as RouteEntry, t as RuntimeHooks, u as ScrubResult, v as SqlDriver, w as SqlTx, x as buildRouteTable, y as createApp, z as createLazyTransaction, G as createOps, H as createRequestDatabase, I as effectiveAuth, J as hostAllowed, K as installEgressFence, O as loadConfig, P as makeMemoryCache, Q as matchRoute, T as quoteIdent, U as scrubSecrets, V as withTables } from '../index-BF1f0DfA.js';
2
- import '../endpoint-DMwJjEHQ.js';
1
+ export { A as App, m as AuthVerifier, B as BootRefused, n as CreateAppOptions, E as EgressPolicy, p as EngineConfig, M as ModuleClients, q as RateLimiter, r as RequestDatabase, s as RouteEntry, t as RuntimeHooks, u as ScrubResult, v as SqlDriver, w as SqlTx, x as buildRouteTable, y as createApp, z as createLazyTransaction, G as createOps, H as createRequestDatabase, I as effectiveAuth, J as hostAllowed, K as installEgressFence, O as loadConfig, P as makeMemoryCache, Q as matchRoute, T as quoteIdent, U as scrubSecrets, V as withTables } from '../index-CUomTA3e.js';
2
+ import '../endpoint-CTEHhb7A.js';
3
3
  import '../stack.js';
4
4
  import 'node:async_hooks';
5
- import '../index-Ct1iiB4N.js';
5
+ import '../index-CW21M9Z3.js';
6
6
  import '../db/env.js';
7
- import '../registry-B8pld3fj.js';
7
+ import '../registry-dZZ5JKYg.js';
8
8
  import 'zod';
@@ -16,11 +16,11 @@ import {
16
16
  quoteIdent,
17
17
  scrubSecrets,
18
18
  withTables
19
- } from "../chunk-43A3KGWL.js";
19
+ } from "../chunk-34I4GB7D.js";
20
20
  import "../chunk-XJ2RSHEU.js";
21
21
  import "../chunk-XABBC7JP.js";
22
22
  import "../chunk-P2Q27SGP.js";
23
- import "../chunk-ZQRWW37O.js";
23
+ import "../chunk-35PNTIRN.js";
24
24
  import "../chunk-7D4SUZUM.js";
25
25
  export {
26
26
  AuthVerifier,
@@ -1,8 +1,8 @@
1
1
  import { Buckets, BucketTypes } from './stack.js';
2
2
  import { AsyncLocalStorage } from 'node:async_hooks';
3
- import { C as CacheClient, P as PalbaseDocsClient, a as PalbaseFlagsClient, L as Logger, b as PalbaseNotificationsClient, c as PalbaseRealtimeClient, D as DBClient, S as SecretsService, d as PalbaseStorageClient, e as PalbaseBucketClient, f as DBOps, T as TxPlanBody, g as TxPlanResponse, A as AuthSpec } from './endpoint-DMwJjEHQ.js';
4
- import { E as EnvTypedDatabase } from './index-Ct1iiB4N.js';
5
- import { R as RouteMeta } from './registry-B8pld3fj.js';
3
+ import { C as CacheClient, P as PalbaseDocsClient, a as PalbaseFlagsClient, L as Logger, b as PalbaseNotificationsClient, c as PalbaseRealtimeClient, D as DBClient, S as SecretsService, d as PalbaseStorageClient, e as PalbaseBucketClient, f as DBOps, T as TxPlanBody, g as TxPlanResponse, A as AuthSpec } from './endpoint-CTEHhb7A.js';
4
+ import { E as EnvTypedDatabase } from './index-CW21M9Z3.js';
5
+ import { R as RouteMeta } from './registry-dZZ5JKYg.js';
6
6
 
7
7
  /**
8
8
  * runtime.ts — request-scoped service singletons.
@@ -1,5 +1,5 @@
1
1
  import { Tables, TableTypes, Schemas } from './db/env.js';
2
- import { D as DBClient, b8 as TxPlanHandle, bh as TxTable, M as Materialized } from './endpoint-DMwJjEHQ.js';
2
+ import { D as DBClient, b8 as TxPlanHandle, bh as TxTable, M as Materialized } from './endpoint-CTEHhb7A.js';
3
3
 
4
4
  /** On delete action for foreign key references. */
5
5
  type OnDeleteAction = 'cascade' | 'set null' | 'restrict' | 'no action';
@@ -32,8 +32,20 @@ interface ColumnDef {
32
32
  selfRefColumn?: string;
33
33
  /** This column OWNS the row: erasure, RLS and the `owner` relation read it. */
34
34
  owns?: true;
35
- /** Explicit relation name, when the derived one would be ambiguous. */
35
+ /** Explicit name for the FORWARD relation (child parent), when the one
36
+ * derived from the column would be ambiguous or unclear. */
36
37
  refAs?: string;
38
+ /**
39
+ * Explicit name for the REVERSE relation (parent → children).
40
+ *
41
+ * Separate from {@link refAs} because the two directions are different facts:
42
+ * the forward name describes the parent this row points at (`author`), the
43
+ * reverse one describes the rows hanging off the parent (`posts`). One option
44
+ * naming both made an ordinary schema undeclarable — `posts.author_id` and
45
+ * `comments.author_id` both named `{ as: "author" }` collided on `users`, and
46
+ * the refusal asked for the `{ as }` they had both already written.
47
+ */
48
+ reverseAs?: string;
37
49
  /** The table this column belongs to; set by `defineTable`. */
38
50
  ownerTable?: {
39
51
  name: string;
@@ -197,9 +209,16 @@ declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends boolean
197
209
  * In a cycle, ONE side needs an explicit return type:
198
210
  * `references((): AnyColumn => y.id)`. One side is enough — measured.
199
211
  * For a self-reference use `selfReferences(column)`: no thunk, no annotation.
212
+ *
213
+ * `as` names the FORWARD relation (`author_id` → `author` by default);
214
+ * `reverseAs` names the REVERSE one on the parent, whose default is this
215
+ * table's own name (`users.posts`). Two foreign keys from one table onto one
216
+ * parent therefore need a `reverseAs` on at least one of them — the reverse
217
+ * names would otherwise both be this table's name.
200
218
  */
201
219
  references(target: () => AnyColumn, opts?: {
202
220
  as?: string;
221
+ reverseAs?: string;
203
222
  onDelete?: OnDeleteAction;
204
223
  }): ColumnBuilder<K, N, D, E, P, T>;
205
224
  /**
@@ -489,7 +508,8 @@ declare const EXTENSION_DEPENDENCIES: Partial<Record<PalbaseExtension, PalbaseEx
489
508
  declare function isPalbaseExtension(name: string): name is PalbaseExtension;
490
509
 
491
510
  /**
492
- * A named raw-SQL DDL object declared in db/schema.ts for anything the typed DSL
511
+ * A named raw-SQL DDL object declared in a schema file (`db/public.ts`,
512
+ * `db/billing.ts`, …) for anything the typed DSL
493
513
  * cannot express (EXCLUDE, CHECK, partial/expression indexes, triggers, views).
494
514
  * The deploy emits `up` verbatim on the privileged DDL connection — same trust
495
515
  * posture as policy().using(). Tracked by NAME (not by diffing the body), so a
@@ -714,19 +734,6 @@ interface SchemaDef<T extends Record<string, TableDef> = Record<string, TableDef
714
734
  /** Postgres extensions to install on deploy. Normalized to `[]` when absent. */
715
735
  extensions: PalbaseExtension[];
716
736
  }
717
- /** The author-facing input to `defineSchema` — a `tables` map whose keys are
718
- * the table names and whose values are `{ columns, rls?, policies? }`, plus an
719
- * optional `extensions` allowlist. */
720
- interface SchemaInput<T extends Record<string, TableInput> = Record<string, TableInput>> {
721
- tables: T;
722
- /**
723
- * Postgres extensions to enable for this project, e.g. `["vector"]`.
724
- * Config-as-code: installed by the deploy (CREATE EXTENSION … SCHEMA
725
- * extensions) with the privileged deploy connection. The type is an
726
- * allowlist union, so unsupported names fail typecheck.
727
- */
728
- extensions?: PalbaseExtension[];
729
- }
730
737
  /** Map the author's `{ tables: { <name>: { columns } } }` input to the
731
738
  * `{ tables: { <name>: TableDef<columns> } }` runtime/type shape, threading the
732
739
  * per-table column map `T[K]["columns"]` so column-level inference survives. */
@@ -1212,4 +1219,4 @@ interface EnvTypedDatabase extends Omit<DBClient, "txPlan" | "asService"> {
1212
1219
  asService(): EnvServiceDatabase;
1213
1220
  }
1214
1221
 
1215
- export { type AnyColumn as A, defineSchema as B, ColumnBuilder as C, defineTable as D, type EnvTypedDatabase as E, enumType as F, installationRef as G, integer as H, type InsertShape as I, isPalbaseExtension as J, jsonb as K, makeTypedDB as L, numeric as M, openai as N, type OnDeleteAction as O, PALBASE_EXTENSIONS as P, ownedByUser as Q, type RawConstraintDef as R, type SchemaDef as S, TABLE_META as T, policy as U, raw as V, text as W, timestamp as X, userRef as Y, uuid as Z, vector as _, type ColumnDef as a, type ColumnMap as b, type ColumnType as c, EXTENSION_DEPENDENCIES as d, type EmbeddingModelRef as e, type EnvSchemas as f, type EnvServiceDatabase as g, type EnvTables as h, type EnvTypedTable as i, type PalbaseExtension as j, PolicyBuilder as k, type PolicyCommand as l, type PolicyDef as m, type PolicyMode as n, type RowShape as o, type SchemaInput as p, type TableDef as q, type TableHandle as r, type TableInput as s, type TxPlan as t, type TxTables as u, type TypedDB as v, type TypedTable as w, type TypedTx as x, bigint as y, boolean as z };
1222
+ export { type AnyColumn as A, defineTable as B, ColumnBuilder as C, enumType as D, type EnvTypedDatabase as E, installationRef as F, integer as G, isPalbaseExtension as H, type InsertShape as I, jsonb as J, makeTypedDB as K, numeric as L, openai as M, ownedByUser as N, type OnDeleteAction as O, PALBASE_EXTENSIONS as P, policy as Q, type RawConstraintDef as R, type SchemaDef as S, TABLE_META as T, raw as U, text as V, timestamp as W, userRef as X, uuid as Y, vector as Z, type ColumnDef as a, type ColumnMap as b, type ColumnType as c, EXTENSION_DEPENDENCIES as d, type EmbeddingModelRef as e, type EnvSchemas as f, type EnvServiceDatabase as g, type EnvTables as h, type EnvTypedTable as i, type PalbaseExtension as j, PolicyBuilder as k, type PolicyCommand as l, type PolicyDef as m, type PolicyMode as n, type RowShape as o, type TableDef as p, type TableHandle as q, type TableInput as r, type TxPlan as s, type TxTables as t, type TypedDB as u, type TypedTable as v, type TypedTx as w, bigint as x, boolean as y, defineSchema as z };
@@ -1,8 +1,8 @@
1
1
  import { Buckets, BucketTypes } from './stack.cjs';
2
2
  import { AsyncLocalStorage } from 'node:async_hooks';
3
- import { C as CacheClient, P as PalbaseDocsClient, a as PalbaseFlagsClient, L as Logger, b as PalbaseNotificationsClient, c as PalbaseRealtimeClient, D as DBClient, S as SecretsService, d as PalbaseStorageClient, e as PalbaseBucketClient, f as DBOps, T as TxPlanBody, g as TxPlanResponse, A as AuthSpec } from './endpoint-BSGw1pTu.cjs';
4
- import { E as EnvTypedDatabase } from './index-CwaWRhyc.cjs';
5
- import { R as RouteMeta } from './registry-CgJvDYUW.cjs';
3
+ import { C as CacheClient, P as PalbaseDocsClient, a as PalbaseFlagsClient, L as Logger, b as PalbaseNotificationsClient, c as PalbaseRealtimeClient, D as DBClient, S as SecretsService, d as PalbaseStorageClient, e as PalbaseBucketClient, f as DBOps, T as TxPlanBody, g as TxPlanResponse, A as AuthSpec } from './endpoint-DYHMo6cC.cjs';
4
+ import { E as EnvTypedDatabase } from './index-CmBK76nx.cjs';
5
+ import { R as RouteMeta } from './registry-CC0WBQq6.cjs';
6
6
 
7
7
  /**
8
8
  * runtime.ts — request-scoped service singletons.
@@ -1,5 +1,5 @@
1
1
  import { Tables, TableTypes, Schemas } from './db/env.cjs';
2
- import { D as DBClient, b8 as TxPlanHandle, bh as TxTable, M as Materialized } from './endpoint-BSGw1pTu.cjs';
2
+ import { D as DBClient, b8 as TxPlanHandle, bh as TxTable, M as Materialized } from './endpoint-DYHMo6cC.cjs';
3
3
 
4
4
  /** On delete action for foreign key references. */
5
5
  type OnDeleteAction = 'cascade' | 'set null' | 'restrict' | 'no action';
@@ -32,8 +32,20 @@ interface ColumnDef {
32
32
  selfRefColumn?: string;
33
33
  /** This column OWNS the row: erasure, RLS and the `owner` relation read it. */
34
34
  owns?: true;
35
- /** Explicit relation name, when the derived one would be ambiguous. */
35
+ /** Explicit name for the FORWARD relation (child parent), when the one
36
+ * derived from the column would be ambiguous or unclear. */
36
37
  refAs?: string;
38
+ /**
39
+ * Explicit name for the REVERSE relation (parent → children).
40
+ *
41
+ * Separate from {@link refAs} because the two directions are different facts:
42
+ * the forward name describes the parent this row points at (`author`), the
43
+ * reverse one describes the rows hanging off the parent (`posts`). One option
44
+ * naming both made an ordinary schema undeclarable — `posts.author_id` and
45
+ * `comments.author_id` both named `{ as: "author" }` collided on `users`, and
46
+ * the refusal asked for the `{ as }` they had both already written.
47
+ */
48
+ reverseAs?: string;
37
49
  /** The table this column belongs to; set by `defineTable`. */
38
50
  ownerTable?: {
39
51
  name: string;
@@ -197,9 +209,16 @@ declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends boolean
197
209
  * In a cycle, ONE side needs an explicit return type:
198
210
  * `references((): AnyColumn => y.id)`. One side is enough — measured.
199
211
  * For a self-reference use `selfReferences(column)`: no thunk, no annotation.
212
+ *
213
+ * `as` names the FORWARD relation (`author_id` → `author` by default);
214
+ * `reverseAs` names the REVERSE one on the parent, whose default is this
215
+ * table's own name (`users.posts`). Two foreign keys from one table onto one
216
+ * parent therefore need a `reverseAs` on at least one of them — the reverse
217
+ * names would otherwise both be this table's name.
200
218
  */
201
219
  references(target: () => AnyColumn, opts?: {
202
220
  as?: string;
221
+ reverseAs?: string;
203
222
  onDelete?: OnDeleteAction;
204
223
  }): ColumnBuilder<K, N, D, E, P, T>;
205
224
  /**
@@ -489,7 +508,8 @@ declare const EXTENSION_DEPENDENCIES: Partial<Record<PalbaseExtension, PalbaseEx
489
508
  declare function isPalbaseExtension(name: string): name is PalbaseExtension;
490
509
 
491
510
  /**
492
- * A named raw-SQL DDL object declared in db/schema.ts for anything the typed DSL
511
+ * A named raw-SQL DDL object declared in a schema file (`db/public.ts`,
512
+ * `db/billing.ts`, …) for anything the typed DSL
493
513
  * cannot express (EXCLUDE, CHECK, partial/expression indexes, triggers, views).
494
514
  * The deploy emits `up` verbatim on the privileged DDL connection — same trust
495
515
  * posture as policy().using(). Tracked by NAME (not by diffing the body), so a
@@ -714,19 +734,6 @@ interface SchemaDef<T extends Record<string, TableDef> = Record<string, TableDef
714
734
  /** Postgres extensions to install on deploy. Normalized to `[]` when absent. */
715
735
  extensions: PalbaseExtension[];
716
736
  }
717
- /** The author-facing input to `defineSchema` — a `tables` map whose keys are
718
- * the table names and whose values are `{ columns, rls?, policies? }`, plus an
719
- * optional `extensions` allowlist. */
720
- interface SchemaInput<T extends Record<string, TableInput> = Record<string, TableInput>> {
721
- tables: T;
722
- /**
723
- * Postgres extensions to enable for this project, e.g. `["vector"]`.
724
- * Config-as-code: installed by the deploy (CREATE EXTENSION … SCHEMA
725
- * extensions) with the privileged deploy connection. The type is an
726
- * allowlist union, so unsupported names fail typecheck.
727
- */
728
- extensions?: PalbaseExtension[];
729
- }
730
737
  /** Map the author's `{ tables: { <name>: { columns } } }` input to the
731
738
  * `{ tables: { <name>: TableDef<columns> } }` runtime/type shape, threading the
732
739
  * per-table column map `T[K]["columns"]` so column-level inference survives. */
@@ -1212,4 +1219,4 @@ interface EnvTypedDatabase extends Omit<DBClient, "txPlan" | "asService"> {
1212
1219
  asService(): EnvServiceDatabase;
1213
1220
  }
1214
1221
 
1215
- export { type AnyColumn as A, defineSchema as B, ColumnBuilder as C, defineTable as D, type EnvTypedDatabase as E, enumType as F, installationRef as G, integer as H, type InsertShape as I, isPalbaseExtension as J, jsonb as K, makeTypedDB as L, numeric as M, openai as N, type OnDeleteAction as O, PALBASE_EXTENSIONS as P, ownedByUser as Q, type RawConstraintDef as R, type SchemaDef as S, TABLE_META as T, policy as U, raw as V, text as W, timestamp as X, userRef as Y, uuid as Z, vector as _, type ColumnDef as a, type ColumnMap as b, type ColumnType as c, EXTENSION_DEPENDENCIES as d, type EmbeddingModelRef as e, type EnvSchemas as f, type EnvServiceDatabase as g, type EnvTables as h, type EnvTypedTable as i, type PalbaseExtension as j, PolicyBuilder as k, type PolicyCommand as l, type PolicyDef as m, type PolicyMode as n, type RowShape as o, type SchemaInput as p, type TableDef as q, type TableHandle as r, type TableInput as s, type TxPlan as t, type TxTables as u, type TypedDB as v, type TypedTable as w, type TypedTx as x, bigint as y, boolean as z };
1222
+ export { type AnyColumn as A, defineTable as B, ColumnBuilder as C, enumType as D, type EnvTypedDatabase as E, installationRef as F, integer as G, isPalbaseExtension as H, type InsertShape as I, jsonb as J, makeTypedDB as K, numeric as L, openai as M, ownedByUser as N, type OnDeleteAction as O, PALBASE_EXTENSIONS as P, policy as Q, type RawConstraintDef as R, type SchemaDef as S, TABLE_META as T, raw as U, text as V, timestamp as W, userRef as X, uuid as Y, vector as Z, type ColumnDef as a, type ColumnMap as b, type ColumnType as c, EXTENSION_DEPENDENCIES as d, type EmbeddingModelRef as e, type EnvSchemas as f, type EnvServiceDatabase as g, type EnvTables as h, type EnvTypedTable as i, type PalbaseExtension as j, PolicyBuilder as k, type PolicyCommand as l, type PolicyDef as m, type PolicyMode as n, type RowShape as o, type TableDef as p, type TableHandle as q, type TableInput as r, type TxPlan as s, type TxTables as t, type TypedDB as u, type TypedTable as v, type TypedTx as w, bigint as x, boolean as y, defineSchema as z };
package/dist/index.cjs CHANGED
@@ -1703,6 +1703,7 @@ function columnToJSON(column) {
1703
1703
  if (def.defaultNow === true) out.defaultNow = true;
1704
1704
  if (def.renamedFrom !== void 0) out.renamedFrom = def.renamedFrom;
1705
1705
  if (def.ignored === true) out.ignored = true;
1706
+ if (def.owns === true) out.owns = true;
1706
1707
  if (def.references !== void 0) {
1707
1708
  out.references = { table: def.references.table, column: def.references.column };
1708
1709
  }
@@ -1965,9 +1966,16 @@ function defineTable(name, input) {
1965
1966
  }
1966
1967
  const policies = (input.policies ?? []).map(toPolicyDef);
1967
1968
  const rls = policies.length > 0 || input.rls !== false;
1969
+ const owned = {};
1970
+ for (const [colName, b] of Object.entries(input.columns)) {
1971
+ const builder = b;
1972
+ const clone = Object.create(Object.getPrototypeOf(builder));
1973
+ Object.defineProperty(clone, "_def", { value: { ...builder._def }, enumerable: true, writable: true });
1974
+ owned[colName] = clone;
1975
+ }
1968
1976
  const def = {
1969
1977
  name,
1970
- columns: input.columns,
1978
+ columns: owned,
1971
1979
  rls,
1972
1980
  policies
1973
1981
  };
@@ -2025,10 +2033,10 @@ function defineTable(name, input) {
2025
2033
  }
2026
2034
  def.memory = input.memory;
2027
2035
  }
2028
- for (const b of Object.values(input.columns)) {
2036
+ for (const b of Object.values(owned)) {
2029
2037
  b._def.ownerTable = def;
2030
2038
  }
2031
- const handle = { ...input.columns };
2039
+ const handle = { ...owned };
2032
2040
  Object.defineProperty(handle, TABLE_META, { value: def, enumerable: false });
2033
2041
  return handle;
2034
2042
  }
@@ -2345,6 +2353,12 @@ var ColumnBuilder = class _ColumnBuilder {
2345
2353
  * In a cycle, ONE side needs an explicit return type:
2346
2354
  * `references((): AnyColumn => y.id)`. One side is enough — measured.
2347
2355
  * For a self-reference use `selfReferences(column)`: no thunk, no annotation.
2356
+ *
2357
+ * `as` names the FORWARD relation (`author_id` → `author` by default);
2358
+ * `reverseAs` names the REVERSE one on the parent, whose default is this
2359
+ * table's own name (`users.posts`). Two foreign keys from one table onto one
2360
+ * parent therefore need a `reverseAs` on at least one of them — the reverse
2361
+ * names would otherwise both be this table's name.
2348
2362
  */
2349
2363
  references(target, opts) {
2350
2364
  refuseOnVector(this._def, "references");
@@ -2355,6 +2369,7 @@ var ColumnBuilder = class _ColumnBuilder {
2355
2369
  }
2356
2370
  this._def.referencesThunk = target;
2357
2371
  if (opts?.as !== void 0) this._def.refAs = opts.as;
2372
+ if (opts?.reverseAs !== void 0) this._def.reverseAs = opts.reverseAs;
2358
2373
  if (opts?.onDelete !== void 0) this._def.onDeleteAction = opts.onDelete;
2359
2374
  return new _ColumnBuilder(this._def.type, this._def);
2360
2375
  }
@@ -2581,64 +2596,73 @@ function optionalOnInsert(def) {
2581
2596
  function forwardName(column) {
2582
2597
  return column.endsWith("_id") ? column.slice(0, -3) : column;
2583
2598
  }
2584
- function qualify(schemaName, tableName) {
2585
- return schemaName === "public" ? tableName : `${schemaName}.${tableName}`;
2599
+ function describeOrigin(o) {
2600
+ return o.direction === "forward" ? `the foreign key "${o.table}.${o.column}"` : `the reverse of "${o.table}.${o.column}"`;
2601
+ }
2602
+ function renameCall(o) {
2603
+ if (o.direction === "reverse") return `references(() => \u2026, { reverseAs: "\u2026" })`;
2604
+ if (o.def.owns === true) {
2605
+ return `ownedByUser() takes no { as } \u2014 if "${o.column}" only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead`;
2606
+ }
2607
+ const target = o.def.references?.table;
2608
+ if (target === "auth.users") return `userRef({ onDelete: \u2026, as: "\u2026" })`;
2609
+ if (target === "auth.installations") return `installationRef({ onDelete: \u2026, as: "\u2026" })`;
2610
+ if (o.def.selfRefColumn !== void 0) return `selfReferences("\u2026", { as: "\u2026" })`;
2611
+ return `references(() => \u2026, { as: "\u2026" })`;
2586
2612
  }
2587
2613
  function buildRelations(schemas) {
2588
2614
  const out = /* @__PURE__ */ new Map();
2589
- const keyOf = /* @__PURE__ */ new Map();
2615
+ const taken = /* @__PURE__ */ new Map();
2590
2616
  for (const schema of schemas) {
2591
2617
  for (const t of Object.values(schema.tables)) {
2592
- const key = qualify(schema.name, t.name);
2618
+ const key = qualifiedTableKey(schema.name, t.name);
2593
2619
  out.set(key, []);
2594
- keyOf.set(t.name, key);
2620
+ taken.set(key, /* @__PURE__ */ new Map());
2595
2621
  }
2596
2622
  }
2623
+ const claim = (tableKey, name, origin, edge) => {
2624
+ const names = taken.get(tableKey);
2625
+ if (names === void 0) return;
2626
+ const held = names.get(name);
2627
+ if (held !== void 0) {
2628
+ if (held.def.owns === true && origin.def.owns === true) {
2629
+ throw new Error(
2630
+ `table "${tableKey}" declares TWO owner columns (${held.column}, ${origin.column}) \u2014 a table has at most ONE ownedByUser(). If the other column only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead.`
2631
+ );
2632
+ }
2633
+ const heldFix = renameCall(held);
2634
+ const originFix = renameCall(origin);
2635
+ const remedy = heldFix === originFix ? `rename one of them: ${heldFix}` : `rename one of them \u2014 "${held.table}.${held.column}": ${heldFix}; "${origin.table}.${origin.column}": ${originFix}`;
2636
+ throw new Error(
2637
+ `table "${tableKey}": ${describeOrigin(held)} and ${describeOrigin(origin)} both resolve to the relation name "${name}" \u2014 ${remedy}.`
2638
+ );
2639
+ }
2640
+ names.set(name, origin);
2641
+ out.get(tableKey)?.push(edge);
2642
+ };
2597
2643
  for (const schema of schemas) {
2598
2644
  for (const table of Object.values(schema.tables)) {
2599
- const childKey = qualify(schema.name, table.name);
2600
- const bucket = out.get(childKey);
2601
- if (bucket === void 0) continue;
2602
- const byTarget = /* @__PURE__ */ new Map();
2603
- for (const [col, builder] of Object.entries(table.columns)) {
2604
- const ref = builder._def.references;
2605
- if (ref === void 0) continue;
2606
- const list = byTarget.get(ref.table) ?? [];
2607
- list.push(col);
2608
- byTarget.set(ref.table, list);
2609
- }
2610
- for (const [target, cols] of byTarget) {
2611
- if (cols.length < 2) continue;
2612
- const unnamed = cols.filter((c) => {
2613
- const d = table.columns[c]?._def;
2614
- return d?.refAs === void 0 && d?.owns !== true;
2615
- });
2616
- if (unnamed.length > 0) {
2617
- throw new Error(
2618
- `table "${table.name}" declares ${cols.length} foreign keys onto "${target}" (${cols.join(", ")}) \u2014 name each one: references(() => \u2026, { as: "\u2026" })`
2619
- );
2620
- }
2621
- }
2645
+ const childKey = qualifiedTableKey(schema.name, table.name);
2646
+ if (!out.has(childKey)) continue;
2622
2647
  for (const [col, builder] of Object.entries(table.columns)) {
2623
2648
  const def = builder._def;
2624
2649
  if (def.references === void 0) continue;
2650
+ const targetKey = def.references.table;
2625
2651
  const name = def.owns === true ? "owner" : def.refAs ?? forwardName(col);
2626
- if (bucket.some((r) => r.name === name)) {
2627
- throw new Error(
2628
- name === "owner" ? `table "${table.name}": two columns claim ownership of the row \u2014 a table has at most ONE ownedByUser(). If the other column only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead.` : `table "${table.name}": two relations resolve to the name "${name}" \u2014 give one an explicit { as: "\u2026" }`
2629
- );
2630
- }
2631
- const targetKey = keyOf.get(def.references.table) ?? def.references.table;
2632
- bucket.push({ name, to: targetKey, kind: "one", via: col });
2633
- const parent = out.get(targetKey);
2634
- if (parent === void 0 || targetKey === childKey) continue;
2635
- const reverse = def.refAs ?? table.name;
2636
- if (parent.some((r) => r.name === reverse)) {
2637
- throw new Error(
2638
- `table "${def.references.table}": two reverse relations resolve to the name "${reverse}" \u2014 give one an explicit { as: "\u2026" }`
2639
- );
2640
- }
2641
- parent.push({ name: reverse, to: childKey, kind: "many", via: col });
2652
+ claim(
2653
+ childKey,
2654
+ name,
2655
+ { table: table.name, column: col, direction: "forward", def },
2656
+ { name, to: targetKey, kind: "one", via: col }
2657
+ );
2658
+ if (!out.has(targetKey) || targetKey === childKey) continue;
2659
+ const reverse = def.reverseAs ?? table.name;
2660
+ claim(
2661
+ targetKey,
2662
+ reverse,
2663
+ { table: table.name, column: col, direction: "reverse", def },
2664
+ { name: reverse, to: childKey, kind: "many", via: col }
2665
+ );
2642
2666
  }
2643
2667
  }
2644
2668
  }
@@ -2675,7 +2699,6 @@ function tableBlock(table, relations, indent) {
2675
2699
  }
2676
2700
  function makeEnvDts(schemas) {
2677
2701
  const relations = buildRelations(schemas);
2678
- const qualify2 = (schemaName, table) => schemaName === "public" ? table : `${schemaName}.${table}`;
2679
2702
  const publicSchema = schemas.find((s) => s.name === "public");
2680
2703
  const others = schemas.filter((s) => s.name !== "public");
2681
2704
  const publicBlocks = Object.keys(publicSchema?.tables ?? {}).map(
@@ -2686,7 +2709,7 @@ ${publicBlocks.join("\n")}
2686
2709
  ` : "";
2687
2710
  const schemaBlocks = others.map((schema) => {
2688
2711
  const inner = Object.keys(schema.tables).map(
2689
- (name) => tableBlock(schema.tables[name], relations.get(qualify2(schema.name, name)) ?? [], " ")
2712
+ (name) => tableBlock(schema.tables[name], relations.get(qualifiedTableKey(schema.name, name)) ?? [], " ")
2690
2713
  );
2691
2714
  return ` ${schema.name}: {
2692
2715
  ${inner.join("\n")}
@@ -2880,6 +2903,10 @@ function recordRoom(ctor, meta) {
2880
2903
  const carrier = carrierOf(ctor);
2881
2904
  const buffer = ownRoomBuffer(carrier);
2882
2905
  carrier[ROOM] = { ...meta, hooks: buffer.hooks, messages: buffer.messages };
2906
+ const g = globalThis;
2907
+ const REGISTRY2 = /* @__PURE__ */ Symbol.for("palbase.backend.allControllers");
2908
+ const all = g[REGISTRY2] ??= [];
2909
+ if (!all.includes(ctor)) all.push(ctor);
2883
2910
  }
2884
2911
 
2885
2912
  // src/decorators/controller.ts
@@ -3286,8 +3313,10 @@ function Req() {
3286
3313
  }
3287
3314
 
3288
3315
  // src/middleware.ts
3289
- function defineMiddleware(fn) {
3290
- return fn;
3316
+ function defineMiddleware(_fn) {
3317
+ throw new Error(
3318
+ "defineMiddleware() is not wired to anything: no bundler reads a `middleware/` directory and the engine has no middleware pipeline, so a handler defined here deploys and never runs. Put cross-cutting work in a service the controllers call, and use route options for auth (`@Controller(path, { auth })`) and rate limits (`@Get(path, { rateLimit })`)."
3319
+ );
3291
3320
  }
3292
3321
 
3293
3322
  // src/errors.ts