@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.
- package/dist/{BranchService-ByLfM_Tt.js → BranchService-W3DMfcZZ.js} +2 -2
- package/dist/{BranchService-ByLfM_Tt.js.map → BranchService-W3DMfcZZ.js.map} +1 -1
- package/dist/PostgresBackendDriver.d.ts +81 -7
- package/dist/PostgresBootstrapper.d.ts +10 -0
- package/dist/{backup-cli-oM9WQVlV.js → backup-cli-DSpQyqcG.js} +2 -2
- package/dist/{backup-cli-oM9WQVlV.js.map → backup-cli-DSpQyqcG.js.map} +1 -1
- package/dist/{backup-service-Br3tqwsy.js → backup-service-DA7a6SUV.js} +2 -2
- package/dist/{backup-service-Br3tqwsy.js.map → backup-service-DA7a6SUV.js.map} +1 -1
- package/dist/{cli-errors-DxN4yzdH.js → cli-errors-C3g_kHBw.js} +137 -18
- package/dist/cli-errors-C3g_kHBw.js.map +1 -0
- package/dist/cli-errors.d.ts +19 -0
- package/dist/cli-helpers.d.ts +47 -0
- package/dist/cli.js +233 -19
- package/dist/cli.js.map +1 -1
- package/dist/{collection-index-BHgT_TKd.js → collection-index-DdnsxiJ_.js} +14 -4
- package/dist/{collection-index-BHgT_TKd.js.map → collection-index-DdnsxiJ_.js.map} +1 -1
- package/dist/collections/validate-relations.d.ts +7 -0
- package/dist/column-plan-helpers-CpILzHJS.js +145 -0
- package/dist/column-plan-helpers-CpILzHJS.js.map +1 -0
- package/dist/{doctor-D7HWmGzs.js → doctor-D5SrGJ5P.js} +175 -61
- package/dist/doctor-D5SrGJ5P.js.map +1 -0
- package/dist/{ensure-collection-policies-2c2NV8zX.js → ensure-collection-policies-CHO0moXQ.js} +5 -5
- package/dist/{ensure-collection-policies-2c2NV8zX.js.map → ensure-collection-policies-CHO0moXQ.js.map} +1 -1
- package/dist/{ensure-collection-tables-D4wJzM4y.js → ensure-collection-tables-Bkvehxdm.js} +318 -312
- package/dist/ensure-collection-tables-Bkvehxdm.js.map +1 -0
- package/dist/{ensure-tables-CIojK-Rx.js → ensure-tables-CYMtuuOd.js} +2 -2
- package/dist/{ensure-tables-CIojK-Rx.js.map → ensure-tables-CYMtuuOd.js.map} +1 -1
- package/dist/generate-drizzle-schema-logic-Dfyf_MRu.js +228 -0
- package/dist/generate-drizzle-schema-logic-Dfyf_MRu.js.map +1 -0
- package/dist/{generate-drizzle-schema-CwEgVyjO.js → generate-drizzle-schema-vSK-VdYT.js} +3 -3
- package/dist/generate-drizzle-schema-vSK-VdYT.js.map +1 -0
- package/dist/generate-postgres-ddl-logic-CR2xcS7e.js +475 -0
- package/dist/generate-postgres-ddl-logic-CR2xcS7e.js.map +1 -0
- package/dist/{generated-schema-staleness-DQdfJl2A.js → generated-schema-staleness-DRQe2BpC.js} +81 -3
- package/dist/{generated-schema-staleness-DQdfJl2A.js.map → generated-schema-staleness-DRQe2BpC.js.map} +1 -1
- package/dist/history/HistoryService.d.ts +23 -4
- package/dist/index.es.js +1991 -580
- package/dist/index.es.js.map +1 -1
- package/dist/introspect-db-logic-C6LQdTxj.js.map +1 -1
- package/dist/plan-schema-Hgl62S-w.js +1531 -0
- package/dist/plan-schema-Hgl62S-w.js.map +1 -0
- package/dist/{policy-drift-kNJ6X2Uj.js → policy-drift-B0GDRh7_.js} +2 -2
- package/dist/{policy-drift-kNJ6X2Uj.js.map → policy-drift-B0GDRh7_.js.map} +1 -1
- package/dist/{rls-bootstrap-sql-DLDnUjtx.js → rls-bootstrap-sql-BlzsOUtz.js} +111 -8
- package/dist/{rls-bootstrap-sql-DLDnUjtx.js.map → rls-bootstrap-sql-BlzsOUtz.js.map} +1 -1
- package/dist/{rls-enforcement-DeYEjYLt.js → rls-enforcement-BV12vBwQ.js} +2 -1
- package/dist/rls-enforcement-BV12vBwQ.js.map +1 -0
- package/dist/schema/catalogue-schema.d.ts +97 -0
- package/dist/schema/collection-index.d.ts +9 -0
- package/dist/schema/column-plan-helpers.d.ts +125 -0
- package/dist/schema/config-relations.d.ts +32 -0
- package/dist/schema/doctor-cli.js +4 -4
- package/dist/schema/doctor.d.ts +9 -0
- package/dist/schema/dynamic-tables.d.ts +27 -6
- package/dist/schema/ensure-collection-tables.d.ts +31 -268
- package/dist/schema/generate-drizzle-schema-logic.d.ts +26 -8
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-postgres-ddl-logic.d.ts +84 -168
- package/dist/schema/generate-postgres-ddl.js +8 -2
- package/dist/schema/generate-postgres-ddl.js.map +1 -1
- package/dist/schema/generated-column-conflicts.d.ts +84 -0
- package/dist/schema/generated-schema-diff.d.ts +56 -0
- package/dist/schema/generated-schema-staleness.d.ts +33 -0
- package/dist/schema/introspect-db-logic.d.ts +2 -0
- package/dist/schema/plan/diff-plan.d.ts +220 -0
- package/dist/schema/plan/plan-schema.d.ts +84 -0
- package/dist/schema/plan/render-ddl.d.ts +62 -0
- package/dist/schema/plan/render-drizzle.d.ts +31 -0
- package/dist/schema/plan/types.d.ts +410 -0
- package/dist/schema/plan/updated-at-trigger.d.ts +22 -0
- package/dist/schema/relation-names.d.ts +28 -0
- package/dist/schema/search-column.d.ts +9 -0
- package/dist/security/rls-enforcement.d.ts +17 -0
- package/dist/services/FetchService.d.ts +176 -36
- package/dist/services/PersistService.d.ts +17 -4
- package/dist/services/RelationService.d.ts +11 -3
- package/dist/services/collection-helpers.d.ts +16 -2
- package/dist/services/dataService.d.ts +29 -2
- package/dist/services/field-op-sql.d.ts +16 -0
- package/dist/services/field-viewer.d.ts +19 -0
- package/dist/services/realtimeService.d.ts +35 -1
- package/dist/services/row-pipeline.d.ts +29 -5
- package/dist/services/soft-delete.d.ts +73 -0
- package/dist/utils/pg-error-utils.d.ts +41 -1
- package/dist/{vector-index-DkWxm2de.js → vector-index-CMJKb3C9.js} +3 -14
- package/dist/{vector-index-DkWxm2de.js.map → vector-index-CMJKb3C9.js.map} +1 -1
- package/package.json +18 -10
- package/dist/cli-errors-DxN4yzdH.js.map +0 -1
- package/dist/doctor-D7HWmGzs.js.map +0 -1
- package/dist/ensure-collection-tables-D4wJzM4y.js.map +0 -1
- package/dist/generate-drizzle-schema-CwEgVyjO.js.map +0 -1
- package/dist/generate-drizzle-schema-logic-so2Z_lyC.js +0 -528
- package/dist/generate-drizzle-schema-logic-so2Z_lyC.js.map +0 -1
- package/dist/generate-postgres-ddl-logic-CtnpIBVv.js +0 -1312
- package/dist/generate-postgres-ddl-logic-CtnpIBVv.js.map +0 -1
- 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.
|