@rebasepro/server-postgres 0.19.1 → 0.19.2-canary.g08eed46

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 (96) hide show
  1. package/dist/{BranchService-ByLfM_Tt.js → BranchService-W3DMfcZZ.js} +2 -2
  2. package/dist/{BranchService-ByLfM_Tt.js.map → BranchService-W3DMfcZZ.js.map} +1 -1
  3. package/dist/PostgresBackendDriver.d.ts +81 -7
  4. package/dist/PostgresBootstrapper.d.ts +10 -0
  5. package/dist/{backup-cli-oM9WQVlV.js → backup-cli-DSpQyqcG.js} +2 -2
  6. package/dist/{backup-cli-oM9WQVlV.js.map → backup-cli-DSpQyqcG.js.map} +1 -1
  7. package/dist/{backup-service-Br3tqwsy.js → backup-service-DA7a6SUV.js} +2 -2
  8. package/dist/{backup-service-Br3tqwsy.js.map → backup-service-DA7a6SUV.js.map} +1 -1
  9. package/dist/{cli-errors-DxN4yzdH.js → cli-errors-C3g_kHBw.js} +137 -18
  10. package/dist/cli-errors-C3g_kHBw.js.map +1 -0
  11. package/dist/cli-errors.d.ts +19 -0
  12. package/dist/cli-helpers.d.ts +47 -0
  13. package/dist/cli.js +233 -19
  14. package/dist/cli.js.map +1 -1
  15. package/dist/{collection-index-BHgT_TKd.js → collection-index-DdnsxiJ_.js} +14 -4
  16. package/dist/{collection-index-BHgT_TKd.js.map → collection-index-DdnsxiJ_.js.map} +1 -1
  17. package/dist/collections/validate-relations.d.ts +7 -0
  18. package/dist/column-plan-helpers-CpILzHJS.js +145 -0
  19. package/dist/column-plan-helpers-CpILzHJS.js.map +1 -0
  20. package/dist/{doctor-D7HWmGzs.js → doctor-D5SrGJ5P.js} +175 -61
  21. package/dist/doctor-D5SrGJ5P.js.map +1 -0
  22. package/dist/{ensure-collection-policies-2c2NV8zX.js → ensure-collection-policies-CHO0moXQ.js} +5 -5
  23. package/dist/{ensure-collection-policies-2c2NV8zX.js.map → ensure-collection-policies-CHO0moXQ.js.map} +1 -1
  24. package/dist/{ensure-collection-tables-D4wJzM4y.js → ensure-collection-tables-Bkvehxdm.js} +318 -312
  25. package/dist/ensure-collection-tables-Bkvehxdm.js.map +1 -0
  26. package/dist/{ensure-tables-CIojK-Rx.js → ensure-tables-CYMtuuOd.js} +2 -2
  27. package/dist/{ensure-tables-CIojK-Rx.js.map → ensure-tables-CYMtuuOd.js.map} +1 -1
  28. package/dist/generate-drizzle-schema-logic-Dfyf_MRu.js +228 -0
  29. package/dist/generate-drizzle-schema-logic-Dfyf_MRu.js.map +1 -0
  30. package/dist/{generate-drizzle-schema-CwEgVyjO.js → generate-drizzle-schema-vSK-VdYT.js} +3 -3
  31. package/dist/generate-drizzle-schema-vSK-VdYT.js.map +1 -0
  32. package/dist/generate-postgres-ddl-logic-CR2xcS7e.js +475 -0
  33. package/dist/generate-postgres-ddl-logic-CR2xcS7e.js.map +1 -0
  34. package/dist/{generated-schema-staleness-DQdfJl2A.js → generated-schema-staleness-DRQe2BpC.js} +81 -3
  35. package/dist/{generated-schema-staleness-DQdfJl2A.js.map → generated-schema-staleness-DRQe2BpC.js.map} +1 -1
  36. package/dist/history/HistoryService.d.ts +23 -4
  37. package/dist/index.es.js +1991 -580
  38. package/dist/index.es.js.map +1 -1
  39. package/dist/introspect-db-logic-C6LQdTxj.js.map +1 -1
  40. package/dist/plan-schema-Hgl62S-w.js +1531 -0
  41. package/dist/plan-schema-Hgl62S-w.js.map +1 -0
  42. package/dist/{policy-drift-kNJ6X2Uj.js → policy-drift-B0GDRh7_.js} +2 -2
  43. package/dist/{policy-drift-kNJ6X2Uj.js.map → policy-drift-B0GDRh7_.js.map} +1 -1
  44. package/dist/{rls-bootstrap-sql-DLDnUjtx.js → rls-bootstrap-sql-BlzsOUtz.js} +111 -8
  45. package/dist/{rls-bootstrap-sql-DLDnUjtx.js.map → rls-bootstrap-sql-BlzsOUtz.js.map} +1 -1
  46. package/dist/{rls-enforcement-DeYEjYLt.js → rls-enforcement-BV12vBwQ.js} +2 -1
  47. package/dist/rls-enforcement-BV12vBwQ.js.map +1 -0
  48. package/dist/schema/catalogue-schema.d.ts +97 -0
  49. package/dist/schema/collection-index.d.ts +9 -0
  50. package/dist/schema/column-plan-helpers.d.ts +125 -0
  51. package/dist/schema/config-relations.d.ts +32 -0
  52. package/dist/schema/doctor-cli.js +4 -4
  53. package/dist/schema/doctor.d.ts +9 -0
  54. package/dist/schema/dynamic-tables.d.ts +27 -6
  55. package/dist/schema/ensure-collection-tables.d.ts +31 -268
  56. package/dist/schema/generate-drizzle-schema-logic.d.ts +26 -8
  57. package/dist/schema/generate-drizzle-schema.js +1 -1
  58. package/dist/schema/generate-postgres-ddl-logic.d.ts +84 -168
  59. package/dist/schema/generate-postgres-ddl.js +8 -2
  60. package/dist/schema/generate-postgres-ddl.js.map +1 -1
  61. package/dist/schema/generated-column-conflicts.d.ts +84 -0
  62. package/dist/schema/generated-schema-diff.d.ts +56 -0
  63. package/dist/schema/generated-schema-staleness.d.ts +33 -0
  64. package/dist/schema/introspect-db-logic.d.ts +2 -0
  65. package/dist/schema/plan/diff-plan.d.ts +220 -0
  66. package/dist/schema/plan/plan-schema.d.ts +84 -0
  67. package/dist/schema/plan/render-ddl.d.ts +62 -0
  68. package/dist/schema/plan/render-drizzle.d.ts +31 -0
  69. package/dist/schema/plan/types.d.ts +410 -0
  70. package/dist/schema/plan/updated-at-trigger.d.ts +22 -0
  71. package/dist/schema/relation-names.d.ts +28 -0
  72. package/dist/schema/search-column.d.ts +9 -0
  73. package/dist/security/rls-enforcement.d.ts +17 -0
  74. package/dist/services/FetchService.d.ts +176 -36
  75. package/dist/services/PersistService.d.ts +17 -4
  76. package/dist/services/RelationService.d.ts +11 -3
  77. package/dist/services/collection-helpers.d.ts +16 -2
  78. package/dist/services/dataService.d.ts +29 -2
  79. package/dist/services/field-op-sql.d.ts +16 -0
  80. package/dist/services/field-viewer.d.ts +19 -0
  81. package/dist/services/realtimeService.d.ts +35 -1
  82. package/dist/services/row-pipeline.d.ts +29 -5
  83. package/dist/services/soft-delete.d.ts +73 -0
  84. package/dist/utils/pg-error-utils.d.ts +41 -1
  85. package/dist/{vector-index-DkWxm2de.js → vector-index-CMJKb3C9.js} +3 -14
  86. package/dist/{vector-index-DkWxm2de.js.map → vector-index-CMJKb3C9.js.map} +1 -1
  87. package/package.json +18 -10
  88. package/dist/cli-errors-DxN4yzdH.js.map +0 -1
  89. package/dist/doctor-D7HWmGzs.js.map +0 -1
  90. package/dist/ensure-collection-tables-D4wJzM4y.js.map +0 -1
  91. package/dist/generate-drizzle-schema-CwEgVyjO.js.map +0 -1
  92. package/dist/generate-drizzle-schema-logic-so2Z_lyC.js +0 -528
  93. package/dist/generate-drizzle-schema-logic-so2Z_lyC.js.map +0 -1
  94. package/dist/generate-postgres-ddl-logic-CtnpIBVv.js +0 -1312
  95. package/dist/generate-postgres-ddl-logic-CtnpIBVv.js.map +0 -1
  96. package/dist/rls-enforcement-DeYEjYLt.js.map +0 -1
@@ -0,0 +1,410 @@
1
+ /**
2
+ * The one description of the database a set of collections asks for.
3
+ *
4
+ * ## Why this shape exists
5
+ *
6
+ * Rebase used to compile a `Property` into a column in three places:
7
+ * `getDrizzleColumn` (`schema.generated.ts`), `getSqlColumnType` plus the
8
+ * `CREATE TABLE` walk around it (`schema.sql`, what `db push` applies) and
9
+ * `planCollectionSchemaEnsure` (what a managed tenant gets at boot, where no
10
+ * `db push` ever runs). Three `switch (prop.type)` statements, three readings
11
+ * of `relations`, of `enum`, of `search`, of `validation.unique`. The audit
12
+ * that produced this module counted twelve open disagreements between them and
13
+ * found that a third of the commits touching those files were
14
+ * re-synchronisations.
15
+ *
16
+ * So a `Property` is read exactly once, by {@link planSchema}, into the types
17
+ * below — and the three emitters became renderers of the result. A
18
+ * disagreement now needs someone to write a second interpreter on purpose.
19
+ *
20
+ * ## The rules the shape enforces
21
+ *
22
+ * - **The type is tagged, never a string.** `{ kind: "varchar", length: 255 }`
23
+ * rather than `"VARCHAR(255)"`. A renderer that has to parse a type back
24
+ * apart is a renderer that will disagree with the one that wrote it, and the
25
+ * Drizzle import list is derived from the tags actually present — which is
26
+ * what makes "a builder used without its import" unrepresentable.
27
+ * - **A default is a value, not a spelling.** See {@link ColumnDefault}.
28
+ * - **Derived names are in the plan, not in the renderer.** Constraint names,
29
+ * index names and enum type names are frozen identifiers (see
30
+ * `contracts/derived-names.txt`); they are computed once, here, so the three
31
+ * files cannot name the same object differently.
32
+ */
33
+ import type { CollectionConfig } from "@rebasepro/types";
34
+ import type { CollectionIndexSpec } from "../collection-index.js";
35
+ import type { SearchColumnSpec } from "../search-column.js";
36
+ import type { VectorColumnSpec, VectorIndexPlan } from "../vector-index.js";
37
+ /**
38
+ * A Postgres column type, tagged.
39
+ *
40
+ * Every member renders to exactly one SQL spelling and exactly one
41
+ * drizzle-orm/pg-core builder, and those two mappings live in the renderers —
42
+ * so adding a type means adding it here and being told, by the compiler, about
43
+ * both. That is the property the three copies of `switch (prop.type)` did not
44
+ * have: `geopoint` had an arm in the DDL generator and none in the Drizzle one,
45
+ * so the database got a column the generated table had no key for and every
46
+ * write to it was discarded with a 201.
47
+ */
48
+ export type PgType = {
49
+ kind: "text";
50
+ } | {
51
+ kind: "varchar";
52
+ length: number;
53
+ } | {
54
+ kind: "char";
55
+ length: number;
56
+ } | {
57
+ kind: "uuid";
58
+ }
59
+ /** A Postgres `enum` type. `name` is the type name, unqualified. */
60
+ | {
61
+ kind: "enum";
62
+ schema: string;
63
+ name: string;
64
+ varName: string;
65
+ labels: string[];
66
+ } | {
67
+ kind: "smallint";
68
+ } | {
69
+ kind: "integer";
70
+ } | {
71
+ kind: "bigint";
72
+ } | {
73
+ kind: "smallserial";
74
+ } | {
75
+ kind: "serial";
76
+ } | {
77
+ kind: "bigserial";
78
+ } | {
79
+ kind: "real";
80
+ } | {
81
+ kind: "doublePrecision";
82
+ }
83
+ /** `precision`/`scale` present only when the property declares them. */
84
+ | {
85
+ kind: "numeric";
86
+ precision?: number;
87
+ scale?: number;
88
+ } | {
89
+ kind: "boolean";
90
+ } | {
91
+ kind: "timestamptz";
92
+ } | {
93
+ kind: "date";
94
+ } | {
95
+ kind: "time";
96
+ } | {
97
+ kind: "json";
98
+ } | {
99
+ kind: "jsonb";
100
+ } | {
101
+ kind: "vector";
102
+ dimensions: number;
103
+ } | {
104
+ kind: "bytea";
105
+ }
106
+ /** The generated full-text column. Only the search planner produces it. */
107
+ | {
108
+ kind: "tsvector";
109
+ } | {
110
+ kind: "array";
111
+ of: PgType;
112
+ };
113
+ /**
114
+ * What a column defaults to, as the thing itself rather than a rendered string.
115
+ *
116
+ * The three emitters each spelled these differently and disagreed on all four
117
+ * id strategies: the DDL generator wrote `DEFAULT ${prop.isId}` verbatim, so
118
+ * the documented ``isId: "sql`gen_id()`"`` reached Postgres with the template
119
+ * wrapper still on it; boot-ensure gave a default to `uuid` and to nothing
120
+ * else, so every other strategy arrived with none and the first insert failed
121
+ * on a NULL primary key.
122
+ *
123
+ * `sql` carries the expression, wrapper already stripped. `identity` is not a
124
+ * DEFAULT at all — it is part of the column type
125
+ * (`INTEGER GENERATED BY DEFAULT AS IDENTITY`), which is where both SQL
126
+ * renderers put it. `literal` carries **both** the JavaScript value and the
127
+ * SQL literal it quotes to: the renderers all emit the SQL form (Drizzle as
128
+ * ``.default(sql`…`)``), so the three files cannot disagree about how a string
129
+ * with an apostrophe or a `Date` is spelled, and the value is kept so the write
130
+ * path and the schema can be checked against each other.
131
+ */
132
+ export type ColumnDefault = {
133
+ kind: "sql";
134
+ expression: string;
135
+ } | {
136
+ kind: "identity";
137
+ } | {
138
+ kind: "literal";
139
+ value: unknown;
140
+ sql: string;
141
+ };
142
+ /** Where a column in a {@link TablePlan} came from. */
143
+ export interface ColumnSource {
144
+ kind:
145
+ /** A declared property. */
146
+ "property"
147
+ /** The `id TEXT PRIMARY KEY` a collection declaring no key gets. */
148
+ | "implicit-id"
149
+ /** A `belongsTo` relation's foreign key column. */
150
+ | "relation"
151
+ /** A `reference` property's foreign key column. */
152
+ | "reference"
153
+ /** A generated search column (`tsvector`, or the fuzzy `text` one). */
154
+ | "search"
155
+ /** An auth-owned column the collection does not declare. */
156
+ | "auth"
157
+ /** A junction endpoint column. */
158
+ | "junction-key";
159
+ /** The property that produced it, when one did. */
160
+ propName?: string;
161
+ /** The collection slug, for error messages. */
162
+ slug?: string;
163
+ }
164
+ /**
165
+ * A foreign key, as both its parts and the statement that creates it.
166
+ *
167
+ * `ALTER TABLE … ADD CONSTRAINT` has no `IF NOT EXISTS`, so a caller applying
168
+ * these has to skip by name — hence the name is a field and not only a
169
+ * substring of the SQL. The name is derived by `toPostgresIdentifier`, which
170
+ * truncates to 63 bytes exactly as Postgres does, because the comparison is
171
+ * against a catalogue that already holds the truncated form.
172
+ */
173
+ export interface ForeignKeyPlan {
174
+ constraintName: string;
175
+ schema: string;
176
+ /** Bare table name, no schema prefix. */
177
+ table: string;
178
+ column: string;
179
+ targetSchema: string;
180
+ targetTable: string;
181
+ targetColumn: string;
182
+ onDelete: string;
183
+ onUpdate?: string;
184
+ sql: string;
185
+ }
186
+ /**
187
+ * One column of one table.
188
+ *
189
+ * `key` and `column` are two names for one thing and this is where they meet:
190
+ * the wire name (the Drizzle object key, what the API serves) and the SQL
191
+ * identifier. They differ whenever a property is camelCase or carries an
192
+ * explicit `columnName` — `authorId` / `author_id` — and every emitter that
193
+ * conflated them produced either a schema that does not compile or an API that
194
+ * serves the wrong field name.
195
+ */
196
+ export interface ColumnPlan {
197
+ /** Wire name: the Drizzle object key and the JSON field. */
198
+ key: string;
199
+ /** SQL identifier. */
200
+ column: string;
201
+ type: PgType;
202
+ nullable: boolean;
203
+ primaryKey: boolean;
204
+ unique: boolean;
205
+ default?: ColumnDefault;
206
+ /** A STORED generated column — today only the search ones. */
207
+ generated?: {
208
+ expression: string;
209
+ stored: true;
210
+ };
211
+ foreignKey?: ForeignKeyPlan;
212
+ /**
213
+ * `autoValue: "on_update"`: the column is stamped with `now()` by a
214
+ * `BEFORE UPDATE` trigger as well as by the driver, so a raw `UPDATE` — a
215
+ * seed, a migration, psql — keeps it honest. See {@link TriggerPlan}.
216
+ */
217
+ touchOnUpdate?: boolean;
218
+ /**
219
+ * True when a *declared property* emits this column and a relation
220
+ * contributes only the constraint — `postId` with `columnName: "post_id"`
221
+ * beside a `belongsTo` on `post_id`. Both used to emit the column and
222
+ * `CREATE TABLE` failed with "column specified more than once".
223
+ */
224
+ columnOwnedByProperty?: boolean;
225
+ /**
226
+ * What this column would have been called before `generateForeignKeyName`
227
+ * learned to singularize — set only when the two differ and the name is the
228
+ * derived default rather than one the author wrote. Carried so boot-ensure
229
+ * can recognise a database provisioned under the old rule and rename rather
230
+ * than add a second, empty column beside it. Never used to name anything.
231
+ */
232
+ legacyColumn?: string;
233
+ /**
234
+ * The verbatim SQL definition this column has when a **SQL** emitter
235
+ * creates it, overriding `type`/`nullable`/`default`/`unique`.
236
+ *
237
+ * Two producers. `auth-users-columns` is the single description of what an
238
+ * auth user table's columns must be — three code paths create
239
+ * `rebase.users` and they disagreed about `email`'s nullability and
240
+ * `roles`'s default until that module existed. And the generated search
241
+ * columns, whose `tsvector GENERATED ALWAYS AS (…) STORED` is one string in
242
+ * `search-column`, shared with the `ADD COLUMN` in `search.sql`.
243
+ *
244
+ * The Drizzle renderer ignores it: drizzle-kit does not create the auth
245
+ * table, and it spells a generated column with `.generatedAlwaysAs()`.
246
+ */
247
+ sqlDefinition?: string;
248
+ source: ColumnSource;
249
+ }
250
+ /** One compiled RLS policy: the clauses, not the SQL and not the rule. */
251
+ export interface PolicyPlan {
252
+ name: string;
253
+ /**
254
+ * Which `SecurityRule` this came from.
255
+ *
256
+ * A rule with `operations: ["update", "delete"]` compiles to two policies,
257
+ * and the pair is still one thing the author wrote: `policies.sql` says
258
+ * "Rebase injected this" once above the run rather than once per statement.
259
+ */
260
+ ruleKey: string;
261
+ operation: string;
262
+ /** `permissive` | `restrictive`, lower-case as the rule spells it. */
263
+ mode: string;
264
+ /** Sorted, `["public"]` when the rule names none. */
265
+ roles: string[];
266
+ /** Compiled `USING` clause, or null when the operation takes none. */
267
+ using: string | null;
268
+ /** Compiled `WITH CHECK` clause, or null when the operation takes none. */
269
+ withCheck: string | null;
270
+ /**
271
+ * True when Rebase added this rule rather than the author. They are
272
+ * permissive, so they OR with the declared rules and widen the ACL beyond
273
+ * what `securityRules` reads like — `policies.sql` says so in a comment.
274
+ */
275
+ injected: boolean;
276
+ }
277
+ /** A `BEFORE UPDATE` trigger stamping `now()` onto an `on_update` column. */
278
+ export interface TriggerPlan {
279
+ schema: string;
280
+ /** Bare table name. */
281
+ table: string;
282
+ column: string;
283
+ /** Frozen derived name — see `contracts/derived-names.txt`. */
284
+ name: string;
285
+ }
286
+ /** A Postgres enum type one or more columns reference. */
287
+ export interface EnumPlan {
288
+ schema: string;
289
+ /** Type name, unqualified: `<table>_<column>`. A frozen derived name. */
290
+ name: string;
291
+ /** `schema.name` — the key `readExistingSchema` returns. */
292
+ qualified: string;
293
+ /**
294
+ * The schema the collection *declared*, before the `public` fallback.
295
+ *
296
+ * `pgEnum` can only declare a type in `public`; anything else has to go
297
+ * through `<name>Schema.enum(...)`, and the choice is the declaration, not
298
+ * the resolved name.
299
+ */
300
+ declaredSchema?: string;
301
+ /** The Drizzle variable the type is declared as. */
302
+ varName: string;
303
+ labels: string[];
304
+ }
305
+ export interface TablePlan {
306
+ schema: string;
307
+ /** Bare table name, no schema prefix. */
308
+ table: string;
309
+ /** `schema.table` — the key `readExistingSchema` returns. */
310
+ qualified: string;
311
+ /** The schema the collection *declared*, before the `public` fallback. */
312
+ declaredSchema?: string;
313
+ /** The Drizzle variable this table is declared as. */
314
+ varName: string;
315
+ kind: "collection" | "junction";
316
+ /** The collection slug, absent for a junction (no collection declares one). */
317
+ slug?: string;
318
+ /**
319
+ * For a junction: the slugs whose `manyToMany` declarations produced it.
320
+ * Named in `policies.sql` so a reader can find the collection to edit.
321
+ */
322
+ declaringSlugs?: string[];
323
+ columns: ColumnPlan[];
324
+ /** Column names, in order. One entry for a collection, two for a junction. */
325
+ primaryKey: string[];
326
+ /** The declared `indexes:` block, with its frozen names. */
327
+ indexes: CollectionIndexSpec[];
328
+ /** The opt-in `search` block's column, helpers and index. */
329
+ search?: SearchColumnSpec;
330
+ /** ANN indexes for the table's `vector` columns, and the ones skipped. */
331
+ vector?: VectorIndexPlan;
332
+ /**
333
+ * The table's `vector` columns, as `vector.sql` adds them.
334
+ *
335
+ * Wider than {@link TablePlan.vector}, which answers "what gets an ANN
336
+ * index" and skips a column too wide to index — one that still needs its
337
+ * column.
338
+ */
339
+ vectorColumns: VectorColumnSpec[];
340
+ /** RLS is enabled on every generated table; these are its policies. */
341
+ policies: PolicyPlan[];
342
+ triggers: TriggerPlan[];
343
+ /** Whether `auth-users-columns` owns part of this table. */
344
+ auth: boolean;
345
+ }
346
+ /**
347
+ * One entry in a generated `relations(...)` block.
348
+ *
349
+ * Both sides of a link must carry the same `relationName` or Drizzle cannot
350
+ * pair them; the rule that derives it is `sharedRelationName`, which both sides
351
+ * compute independently from the table that owns the column.
352
+ */
353
+ export interface RelationPlan {
354
+ /** The Drizzle table variable this entry belongs to. */
355
+ tableVar: string;
356
+ /** The key in the relations object. */
357
+ key: string;
358
+ kind: "one" | "many";
359
+ targetVar: string;
360
+ /**
361
+ * Absent only for a `hasOne` inverse, which is the documented FK-less form:
362
+ * `one(target)` with no config. `one(target, { relationName })` is not a
363
+ * `RelationConfig` (TS2345) and throws at runtime besides.
364
+ */
365
+ relationName?: string;
366
+ /** Property keys on this table. */
367
+ fields?: string[];
368
+ /** Property keys on the target table. */
369
+ references?: string[];
370
+ }
371
+ export interface PlanOptions {
372
+ /**
373
+ * Server extensions the project's databases gave Rebase leave to install —
374
+ * `declaredDatabaseExtensions()`. Absent means none, which is a refusal and
375
+ * the right default for a planner given no configuration at all.
376
+ */
377
+ databaseExtensions?: readonly string[];
378
+ }
379
+ /**
380
+ * Everything a set of collections asks the database for.
381
+ *
382
+ * Ordered as the emitters emit: `tables` is in the order the generators walk
383
+ * (each collection, then any junction its relations imply), because
384
+ * `schema.sql` and `schema.generated.ts` are compared byte-for-byte against
385
+ * their committed copies and a reordering reads as a change.
386
+ */
387
+ export interface SchemaPlan {
388
+ /** `CREATE SCHEMA IF NOT EXISTS` targets, `rebase` first. */
389
+ schemas: string[];
390
+ /**
391
+ * The schemas collections declared, without `rebase`.
392
+ *
393
+ * The SQL file creates `rebase` unconditionally — the RLS helpers live in
394
+ * it, and a schema in Atlas's replayed state but absent from the desired one
395
+ * gets a `DROP SCHEMA … CASCADE` planned against it, which would take the
396
+ * auth tables. The Drizzle file declares only what a collection asked for,
397
+ * because a `pgSchema` variable nothing uses is dead code.
398
+ */
399
+ declaredSchemas: string[];
400
+ enums: EnumPlan[];
401
+ tables: TablePlan[];
402
+ relations: RelationPlan[];
403
+ /** `CREATE EXTENSION IF NOT EXISTS …;` statements, deduplicated. */
404
+ extensions: string[];
405
+ /** `CREATE OR REPLACE FUNCTION …` bodies, deduplicated, in apply order. */
406
+ functions: string[];
407
+ /** The collections this plan was built from, filtered to the SQL ones. */
408
+ collections: CollectionConfig[];
409
+ options: PlanOptions;
410
+ }
@@ -0,0 +1,22 @@
1
+ import type { TriggerPlan } from "./types.js";
2
+ /** The one trigger function, qualified. A frozen derived name. */
3
+ export declare const SET_UPDATED_AT_FN = "rebase.set_updated_at";
4
+ /**
5
+ * `CREATE OR REPLACE FUNCTION`, so replaying it against a database that already
6
+ * has it is a no-op — this is emitted into a file that runs on every push and
7
+ * is appended to migrations that run against databases at any stage of life.
8
+ */
9
+ export declare const setUpdatedAtFunction: () => string;
10
+ /**
11
+ * `DROP TRIGGER IF EXISTS` before the `CREATE`.
12
+ *
13
+ * `CREATE OR REPLACE TRIGGER` exists only on Postgres 14+, and this runs
14
+ * against whatever a self-hosted project points at. Two statements rather than
15
+ * one is also what the boot-time applier needs: it issues DDL one statement at
16
+ * a time over the extended query protocol, which forbids multiple commands in
17
+ * one execute.
18
+ */
19
+ export declare const dropTriggerStatement: (plan: TriggerPlan) => string;
20
+ export declare const createTriggerStatement: (plan: TriggerPlan) => string;
21
+ /** Both statements, in the order they must run. */
22
+ export declare const triggerStatements: (plan: TriggerPlan) => string[];
@@ -0,0 +1,28 @@
1
+ import { CollectionConfig, ResolvedRelation } from "@rebasepro/types";
2
+ /**
3
+ * The `relationName` both sides of a link must agree on.
4
+ *
5
+ * Drizzle pairs an owning `one()` with its inverse `many()` by this string and
6
+ * by nothing else, and the two sides are computed by different callers holding
7
+ * different collections — so the rule has to be derivable from either end. It
8
+ * is: name the link after the table that carries the foreign key and the field
9
+ * key that column is served under.
10
+ *
11
+ * owning (belongsTo) → `{thisTable}_{fieldKey(localKey)}`
12
+ * inverse (hasMany/hasOne) → `{targetTable}_{fieldKey(foreignKeyOnTarget)}`
13
+ *
14
+ * Both spellings of `jobs.company` produce `jobs_companyId`. A many-to-many is
15
+ * named through its junction wiring and a `via` chain is not a Drizzle relation
16
+ * at all, so both keep the local name.
17
+ *
18
+ * The field key rather than the column: the Drizzle object is keyed by the wire
19
+ * name (`fieldKeyForColumn` is the one definition of it), and a name built from
20
+ * the column would differ between a collection that declares `columnName` and
21
+ * one that does not, for the same link.
22
+ *
23
+ * **This is the one definition.** `generate-drizzle-schema-logic.ts` holds a
24
+ * private copy (`computeSharedRelationName`) that this was lifted from verbatim;
25
+ * the generator should import this instead, so the file it writes and the
26
+ * relations the runtime builds from the live catalogue cannot drift apart.
27
+ */
28
+ export declare function sharedRelationName(relation: ResolvedRelation, sourceCollection: CollectionConfig): string;
@@ -140,6 +140,15 @@ export declare const searchHelperFunctions: (spec: SearchColumnSpec) => string[]
140
140
  * with "text search dictionary does not exist". Observed, not theorised.
141
141
  */
142
142
  export declare const searchExtensionStatements: (spec: SearchColumnSpec) => string[];
143
+ /**
144
+ * Everything after the column name — the type and the generation expression.
145
+ *
146
+ * Split out because four emitters need it and only two of them have a place to
147
+ * put the name: `CREATE TABLE` and `ADD COLUMN` write `"col" <this>`, while the
148
+ * schema plan carries it as the column's SQL definition and the boot-time
149
+ * rebuild statement interpolates it on its own.
150
+ */
151
+ export declare const searchColumnTypeSql: (expression: string, kind: "tsvector" | "text") => string;
143
152
  /** The column definition as it appears inside `CREATE TABLE`. */
144
153
  export declare const searchColumnDefinition: (spec: SearchColumnSpec) => string;
145
154
  /** The fuzzy column definition, when the spec asks for one. */
@@ -97,6 +97,23 @@ export interface AuthContext {
97
97
  * rather than having every one of its users reclassified as guests.
98
98
  */
99
99
  isAnonymous?: boolean;
100
+ /**
101
+ * The **custom** claims on the caller's token, as `rebase.jwt()` reports
102
+ * them.
103
+ *
104
+ * This is how a policy reaches a fact about the session that is neither the
105
+ * uid nor the roles — which, before this, it could not: `app.jwt` was built
106
+ * from three fields the driver already had, so `rebase.jwt()`, documented
107
+ * as "full JWT claims as JSONB", returned a three-key object that never had
108
+ * a claim in it.
109
+ *
110
+ * Multi-tenancy's `claim` form is the first caller — `policy.authClaim(…)`
111
+ * compiles to `rebase.jwt() ->> '<name>'`. Identity claims are stripped out
112
+ * before they reach here (`AccessTokenPayload.claims`) and the identity
113
+ * fields are written *over* them below, so nothing a hook put on a token
114
+ * can assert a uid, roles or guest status.
115
+ */
116
+ claims?: Record<string, unknown>;
100
117
  }
101
118
  /**
102
119
  * Warn when the connection role shares its name with an existing schema.