palgres 0.3.1 → 0.3.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.
- package/LICENSE +11 -5
- package/dist/chunks/{FAZVO74A.js → DNJLPMEG.js} +2 -2
- package/dist/chunks/FXAZO7GY.js +2 -0
- package/dist/chunks/{OBLAMOLO.js → OBMTT3YH.js} +2 -2
- package/dist/dsl.js +2 -2
- package/dist/index.js +2 -2
- package/dist/testing.js +2 -2
- package/package.json +2 -2
- package/types/palgres/src/dsl/columns.d.ts +234 -27
- package/types/palgres/src/dsl/foreign-key.d.ts +90 -0
- package/types/palgres/src/dsl/policy.d.ts +290 -1
- package/types/palgres/src/dsl/raw.d.ts +56 -0
- package/types/palgres/src/dsl/schema.d.ts +215 -69
- package/dist/chunks/7255GQJI.js +0 -2
|
@@ -14,25 +14,22 @@ export declare function qualifiedTableKey(schemaName: string, tableName: string)
|
|
|
14
14
|
*/
|
|
15
15
|
export type ColumnMap = Record<string, ColumnBuilder>;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* One index of a table, as `defineTable` stores it: what `index(name)` builds,
|
|
18
|
+
* or a plain object you can write under a table's `indexes` yourself.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* until a policy says who may. Set `rls: false` only for a genuinely public
|
|
25
|
-
* table — it is an explicit opt-out that a reviewer can grep for, not
|
|
26
|
-
* something you get by forgetting.
|
|
27
|
-
* - `policies`: the RLS policies for this table, authored with `policy(name)`.
|
|
28
|
-
* Each entry may be a {@link PolicyBuilder} (the normal `policy(...)` chain)
|
|
29
|
-
* or a raw {@link PolicyDef} object.
|
|
30
|
-
*
|
|
31
|
-
* The `C` type parameter preserves the precise per-column phantom types so the
|
|
32
|
-
* typed `Database.public.*` surface keeps inferring insert/row shapes.
|
|
20
|
+
* `columns` and `expression` are alternatives: either a list of columns, or an
|
|
21
|
+
* SQL expression such as `lower(email)`. `where` makes it a partial index; its
|
|
22
|
+
* condition is written in the same expression language as RLS policies, so
|
|
23
|
+
* there is no separate filter syntax to learn.
|
|
33
24
|
*/
|
|
34
25
|
export interface IndexDef {
|
|
35
26
|
name: string;
|
|
27
|
+
/**
|
|
28
|
+
* Makes this a `CREATE UNIQUE INDEX`. Unlike the table-level `unique`
|
|
29
|
+
* option, which creates a UNIQUE constraint and so cannot have a `WHERE`, a
|
|
30
|
+
* unique index can also be partial: set `where` as well for a partial unique
|
|
31
|
+
* index.
|
|
32
|
+
*/
|
|
36
33
|
unique?: boolean;
|
|
37
34
|
columns?: string[];
|
|
38
35
|
expression?: string;
|
|
@@ -41,18 +38,75 @@ export interface IndexDef {
|
|
|
41
38
|
nulls?: "first" | "last";
|
|
42
39
|
include?: string[];
|
|
43
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Builds one index for a table's `indexes`. Start it with `index(name)`.
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* indexes: (c) => [
|
|
46
|
+
* index("orders_open")
|
|
47
|
+
* .on(c.col("status")) // typed column
|
|
48
|
+
* .where(c.col("deleted_at").isNull()) // partial
|
|
49
|
+
* .include(["total"]), // covering
|
|
50
|
+
* ]
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* The `where` condition uses the same expression language as `policy()`, so
|
|
54
|
+
* policies and indexes share one way of writing a filter.
|
|
55
|
+
*
|
|
56
|
+
* Columns come from the `indexes` callback (`c.col(...)`) rather than from the
|
|
57
|
+
* table value (`orders.status`), because the index is declared inside the
|
|
58
|
+
* table's own definition, where the table value does not exist yet. `policies`
|
|
59
|
+
* works the same way for the same reason.
|
|
60
|
+
*/
|
|
44
61
|
export declare class IndexBuilder {
|
|
45
62
|
readonly _def: IndexDef;
|
|
46
63
|
constructor(name: string);
|
|
64
|
+
/**
|
|
65
|
+
* Makes the index `UNIQUE` (`CREATE UNIQUE INDEX`). Together with `.where()`
|
|
66
|
+
* it gives a partial unique index, the only declarative way to say "NULLs
|
|
67
|
+
* are free, non-NULLs are unique".
|
|
68
|
+
*/
|
|
47
69
|
unique(): this;
|
|
70
|
+
/**
|
|
71
|
+
* The indexed columns, as `c.col(...)` references. Their order is kept and
|
|
72
|
+
* matters: a multi-column index is defined by its column order. For an
|
|
73
|
+
* expression, use `onExpression()` instead.
|
|
74
|
+
*/
|
|
48
75
|
on(...cols: PolicyExprRef[]): this;
|
|
76
|
+
/**
|
|
77
|
+
* Indexes an SQL expression, such as `lower(email)`, instead of columns.
|
|
78
|
+
*
|
|
79
|
+
* The text is used verbatim, as with `policy().using("…")`: it is trusted SQL
|
|
80
|
+
* from your schema file, never user input. It covers what the typed builder
|
|
81
|
+
* cannot express, so you do not have to write a migration by hand.
|
|
82
|
+
*/
|
|
49
83
|
onExpression(expr: string): this;
|
|
84
|
+
/** The partial-index condition, written in the same expression language as RLS policies. */
|
|
50
85
|
where(expr: PolicyExprRef): this;
|
|
86
|
+
/** Sorts the index in descending order. The direction rarely matters for an
|
|
87
|
+
* index read one way; it does when the index has to match a query's `ORDER BY`. */
|
|
51
88
|
desc(): this;
|
|
89
|
+
/** Sorts the index in ascending order, which is the Postgres default. */
|
|
52
90
|
asc(): this;
|
|
91
|
+
/** Where NULLs sort in the index (`NULLS FIRST` or `NULLS LAST`), so it can match an `ORDER BY … NULLS LAST` query. */
|
|
53
92
|
nulls(where: "first" | "last"): this;
|
|
93
|
+
/** Makes a covering index (`INCLUDE`): these columns are stored in the index
|
|
94
|
+
* so a query can be answered by an index-only scan, but they are not searched by. */
|
|
54
95
|
include(cols: string[]): this;
|
|
55
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Starts an index named `name`, for a table's `indexes` callback.
|
|
99
|
+
*
|
|
100
|
+
* Indexes are matched by name across deploys. Change the definition of a
|
|
101
|
+
* live index under the same name and the deploy builds the new one
|
|
102
|
+
* `CONCURRENTLY` next to it, under a name of its own, while the old one keeps
|
|
103
|
+
* serving; at complete the old one goes and the new one takes its name. Give
|
|
104
|
+
* it a new name instead and the old one simply goes at complete. An index
|
|
105
|
+
* renamed with the same definition is only renamed.
|
|
106
|
+
*
|
|
107
|
+
* @param name The index name. It must not be empty.
|
|
108
|
+
* @returns An {@link IndexBuilder}: chain `.on()` or `.onExpression()`, and optionally `.where()`, `.unique()`, `.include()`, `.asc()`, `.desc()`, `.nulls()`.
|
|
109
|
+
*/
|
|
56
110
|
export declare function index(name: string): IndexBuilder;
|
|
57
111
|
/**
|
|
58
112
|
* A table's rows found by meaning and by word — `Database.public.<table>.search("…")`.
|
|
@@ -83,10 +137,63 @@ export interface SearchInput<Col extends string = string> {
|
|
|
83
137
|
*/
|
|
84
138
|
staleness?: 'null' | 'keep';
|
|
85
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* What you pass to `defineTable(name, { columns, rls?, policies?, … })`.
|
|
142
|
+
*
|
|
143
|
+
* - `columns`: the column map (required).
|
|
144
|
+
* - `rls`: enable + FORCE row-level security on this table. **Defaults to
|
|
145
|
+
* `true`**, and is forced on when `policies` is non-empty. A table with RLS
|
|
146
|
+
* and no policies is deny-all, which is the starting state: nothing reads it
|
|
147
|
+
* until a policy says who may. Set `rls: false` only for a genuinely public
|
|
148
|
+
* table — it is an explicit opt-out that a reviewer can grep for, not
|
|
149
|
+
* something you get by forgetting.
|
|
150
|
+
* - `policies`: the RLS policies for this table, authored with `policy(name)`.
|
|
151
|
+
* Each entry may be a {@link PolicyBuilder} (the normal `policy(...)` chain)
|
|
152
|
+
* or a raw {@link PolicyDef} object.
|
|
153
|
+
*
|
|
154
|
+
* The `C` type parameter preserves the precise per-column phantom types so the
|
|
155
|
+
* typed `Database.public.*` surface keeps inferring insert/row shapes.
|
|
156
|
+
*/
|
|
86
157
|
export interface TableInput<C extends ColumnMap = ColumnMap> {
|
|
87
158
|
columns: C;
|
|
88
159
|
rls?: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* The table's RLS policies, written as a function of the table's own columns.
|
|
162
|
+
*
|
|
163
|
+
* It is a callback so that TypeScript knows which table the policies belong
|
|
164
|
+
* to: the column names inferred from `columns` are passed in, and a typo such
|
|
165
|
+
* as `p.col("ownerr")` is a compile error. In a plain array, `policy("x")`
|
|
166
|
+
* could not know its table, and a wrong column name would only come back from
|
|
167
|
+
* Postgres when `CREATE POLICY` runs during the deploy. A wrong column in an
|
|
168
|
+
* RLS policy is an access-control bug, so it is caught at compile time.
|
|
169
|
+
*
|
|
170
|
+
* ```ts
|
|
171
|
+
* policies: (p) => [
|
|
172
|
+
* policy("owner_read").for("select").using(p.col("owner").eq(p.auth.uid())),
|
|
173
|
+
* ]
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
89
176
|
policies?: (p: PolicyExprCtx<Extract<keyof C, string>>) => (PolicyBuilder | PolicyDef)[];
|
|
177
|
+
/**
|
|
178
|
+
* Marks the table as no longer used by this release: the first step of
|
|
179
|
+
* dropping it.
|
|
180
|
+
*
|
|
181
|
+
* This is the table version of a column's `.ignored()`, and it exists for the
|
|
182
|
+
* same reason: while a deploy runs, the release still serving reads the
|
|
183
|
+
* table, so a table that holds rows is not dropped while that release may
|
|
184
|
+
* still use it.
|
|
185
|
+
*
|
|
186
|
+
* A table removed from the schema is dropped at complete when it is empty. A
|
|
187
|
+
* table with rows is refused unless the release already serving marked it
|
|
188
|
+
* ignored: mark it, deploy, then remove it from the schema in a later deploy.
|
|
189
|
+
*
|
|
190
|
+
* Marking produces no DDL, so the deploy that ships it changes nothing in the
|
|
191
|
+
* database.
|
|
192
|
+
*
|
|
193
|
+
* ```ts
|
|
194
|
+
* const cloud_images = defineTable("cloud_images", { ignored: true, columns: { … } });
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
90
197
|
ignored?: boolean;
|
|
91
198
|
/** Composite/named primary key (ordered column names). Omit for single-column inline .primaryKey(). */
|
|
92
199
|
primaryKey?: string[];
|
|
@@ -97,18 +204,43 @@ export interface TableInput<C extends ColumnMap = ColumnMap> {
|
|
|
97
204
|
}[];
|
|
98
205
|
/** Named raw-SQL DDL objects (EXCLUDE, triggers, views) that the typed DSL cannot express. */
|
|
99
206
|
raw?: RawConstraintDef[];
|
|
207
|
+
/**
|
|
208
|
+
* Named first-class CHECK constraints. Matched by NAME; a changed `expr`
|
|
209
|
+
* (compared as written, ignoring leading and trailing spaces) replaces the
|
|
210
|
+
* constraint: the new one is added `NOT VALID`, so new writes are held to it
|
|
211
|
+
* at once, and validated while writes go on. `name` must be a valid
|
|
212
|
+
* identifier.
|
|
213
|
+
*
|
|
214
|
+
* Two forms, stored the same way:
|
|
215
|
+
*
|
|
216
|
+
* - An array: each `expr` is trusted SQL used verbatim, like a policy's
|
|
217
|
+
* `using`.
|
|
218
|
+
* - A callback: `(c) => [check(name, c.col("amount").gt(0))]`. The expression
|
|
219
|
+
* is written with the same expression builder as policies, and column names
|
|
220
|
+
* are checked at compile time. It has to be a callback for that: with a
|
|
221
|
+
* plain array, TypeScript could not tie the expression to this table's
|
|
222
|
+
* `columns`.
|
|
223
|
+
*
|
|
224
|
+
* In the string form, a misspelled column name only comes back from Postgres
|
|
225
|
+
* at `ALTER TABLE … ADD CONSTRAINT`, in the middle of a deploy. The typed form
|
|
226
|
+
* reports the same mistake while you write it.
|
|
227
|
+
*
|
|
228
|
+
* The typed form allows less than a policy does: `existsIn` and `auth.uid()`
|
|
229
|
+
* are subqueries, which Postgres does not allow in a CHECK, so `check()`
|
|
230
|
+
* rejects them.
|
|
231
|
+
*/
|
|
100
232
|
checks?: CheckDef[] | ((c: PolicyExprCtx<Extract<keyof C, string>>) => CheckDef[]);
|
|
101
233
|
/**
|
|
102
234
|
* Plain (non-unique) btree indexes over an ordered column list, emitted as
|
|
103
235
|
* standalone `CREATE INDEX [IF NOT EXISTS] name ON table (col1, col2)`
|
|
104
236
|
* statements (NOT a table clause — a separate migration statement category).
|
|
105
|
-
*
|
|
106
|
-
*
|
|
237
|
+
* Matched by NAME. `name` and each column must be valid identifiers; a
|
|
238
|
+
* deploy refuses one that is not.
|
|
107
239
|
*
|
|
108
|
-
*
|
|
109
|
-
* `.
|
|
110
|
-
*
|
|
111
|
-
*
|
|
240
|
+
* Editing `.where()`, `.onExpression()`, `.include()`, the sort or
|
|
241
|
+
* `.unique()` under a name that is already live rebuilds the index: the new
|
|
242
|
+
* one is built `CONCURRENTLY` next to the old one, which keeps serving until
|
|
243
|
+
* complete, and then takes its name.
|
|
112
244
|
*
|
|
113
245
|
* Scope, via `index(name)`: `.on(...cols)` · `.onExpression(sql)` (expression
|
|
114
246
|
* index) · `.where(pred)` (partial index, typed predicate) · `.unique()`
|
|
@@ -127,6 +259,16 @@ export interface TableInput<C extends ColumnMap = ColumnMap> {
|
|
|
127
259
|
* opclass, or CONCURRENTLY.
|
|
128
260
|
*/
|
|
129
261
|
indexes?: (p: PolicyExprCtx<Extract<keyof C, string>>) => (IndexBuilder | IndexDef)[];
|
|
262
|
+
/**
|
|
263
|
+
* Table-level foreign keys, including multi-column ones, built with
|
|
264
|
+
* `foreignKey(name)`.
|
|
265
|
+
*
|
|
266
|
+
* A column's `.references()` covers one column and one target. It cannot
|
|
267
|
+
* express what multi-tenant integrity needs, such as
|
|
268
|
+
* `FOREIGN KEY (tenant_id, child_id) REFERENCES parent (tenant_id, id)`.
|
|
269
|
+
* The two can be used together and neither drops the other: a key declared
|
|
270
|
+
* here is tracked by its own name, a column's by `<table>_<column>_fkey`.
|
|
271
|
+
*/
|
|
130
272
|
foreignKeys?: (p: PolicyExprCtx<Extract<keyof C, string>>) => (ForeignKeyBuilder | ForeignKeyDef)[];
|
|
131
273
|
}
|
|
132
274
|
/**
|
|
@@ -144,6 +286,7 @@ export interface TableDef<C extends ColumnMap = ColumnMap> {
|
|
|
144
286
|
name: string;
|
|
145
287
|
columns: C;
|
|
146
288
|
rls: boolean;
|
|
289
|
+
/** Set when the table is marked as no longer used by this release; see `TableInput.ignored`. */
|
|
147
290
|
ignored?: boolean;
|
|
148
291
|
policies: PolicyDef[];
|
|
149
292
|
primaryKey?: string[];
|
|
@@ -153,8 +296,13 @@ export interface TableDef<C extends ColumnMap = ColumnMap> {
|
|
|
153
296
|
}[];
|
|
154
297
|
/** Named raw-SQL DDL objects emitted verbatim on deploy. Tracked by name. */
|
|
155
298
|
raw?: RawConstraintDef[];
|
|
299
|
+
/** Named first-class CHECK constraints, matched by name and compared by the
|
|
300
|
+
* expression as written. Both forms of `TableInput.checks` (array and
|
|
301
|
+
* callback) end up here. */
|
|
156
302
|
checks?: CheckDef[];
|
|
303
|
+
/** The table's indexes, with their partial, expression, sort and covering options. */
|
|
157
304
|
indexes?: IndexDef[];
|
|
305
|
+
/** The table's table-level foreign keys, including multi-column ones. */
|
|
158
306
|
foreignKeys?: ForeignKeyDef[];
|
|
159
307
|
/** The table's search, its defaults said — see SearchInput. */
|
|
160
308
|
search?: Search;
|
|
@@ -169,7 +317,7 @@ export interface TableDef<C extends ColumnMap = ColumnMap> {
|
|
|
169
317
|
export interface SchemaDef<T extends Record<string, TableDef> = Record<string, TableDef>> {
|
|
170
318
|
/** The schema's own name — `public`, `billing`, … Declared, never derived. */
|
|
171
319
|
name: string;
|
|
172
|
-
/**
|
|
320
|
+
/** Accepted so older schema files still read; there is no REST surface over tables, so it changes nothing. */
|
|
173
321
|
exposed: boolean;
|
|
174
322
|
tables: T;
|
|
175
323
|
/** Postgres extensions the deploy creates (CREATE EXTENSION IF NOT EXISTS). */
|
|
@@ -179,9 +327,11 @@ export interface SchemaDef<T extends Record<string, TableDef> = Record<string, T
|
|
|
179
327
|
* hand-built `SchemaDef` from before the field still compiles. */
|
|
180
328
|
raw?: RawUnitDef[];
|
|
181
329
|
}
|
|
182
|
-
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
330
|
+
/**
|
|
331
|
+
* The retired dictionary form of `defineSchema`'s input (a `tables` map keyed
|
|
332
|
+
* by table name). `defineSchema` refuses it: pass the schema's name and an
|
|
333
|
+
* array of `defineTable(…)` tables instead.
|
|
334
|
+
*/
|
|
185
335
|
export interface SchemaInput<T extends Record<string, TableInput> = Record<string, TableInput>> {
|
|
186
336
|
tables: T;
|
|
187
337
|
/**
|
|
@@ -197,8 +347,44 @@ type TablesByName<T extends readonly TableHandle[]> = {
|
|
|
197
347
|
[K in T[number] as K[typeof TABLE_META]["name"]]: K[typeof TABLE_META];
|
|
198
348
|
};
|
|
199
349
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
350
|
+
* Where a table's metadata lives.
|
|
351
|
+
*
|
|
352
|
+
* The table VALUE is its columns, so `lists.id` is the column — which means a
|
|
353
|
+
* column called `name` would overwrite the table's own name if metadata sat in
|
|
354
|
+
* a plain field. `columns`, `rls`, `unique` and `indexes` are all plausible
|
|
355
|
+
* column names too. A column cannot collide with a symbol.
|
|
356
|
+
*/
|
|
357
|
+
export declare const TABLE_META: unique symbol;
|
|
358
|
+
/**
|
|
359
|
+
* A value produced by `defineTable`: the columns, plus metadata behind the
|
|
360
|
+
* symbol. `S` is the table's search as declared: with one, the table's type
|
|
361
|
+
* says so, and `search()` is typed for it.
|
|
362
|
+
*/
|
|
363
|
+
export type TableHandle<C extends ColumnMap = ColumnMap, N extends string = string, S = undefined> = C & {
|
|
364
|
+
readonly [TABLE_META]: TableDef<C> & {
|
|
365
|
+
name: N;
|
|
366
|
+
} & ([S] extends [undefined] ? unknown : {
|
|
367
|
+
search: Search;
|
|
368
|
+
});
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Declare one table as a standalone value that KNOWS ITS OWN NAME.
|
|
372
|
+
*
|
|
373
|
+
* The name lives here, not in a dictionary key. A table whose name comes from a
|
|
374
|
+
* key cannot resolve `references(() => other.id)` when it is built — it does not
|
|
375
|
+
* know what to call itself — which forces a two-phase resolution with an
|
|
376
|
+
* empty-name intermediate state. One name source, no empty identity.
|
|
377
|
+
*
|
|
378
|
+
* The returned object spreads its own columns, so a sibling can point at it:
|
|
379
|
+
* `references(() => lists.id)`.
|
|
380
|
+
*/
|
|
381
|
+
export declare function defineTable<const N extends string, C extends ColumnMap, const S extends SearchInput<Extract<keyof C, string>> | undefined = undefined>(name: N, input: TableInput<C> & {
|
|
382
|
+
search?: S;
|
|
383
|
+
}): TableHandle<C, N, S>;
|
|
384
|
+
/**
|
|
385
|
+
* Collects declared tables into one schema: the default export of a schema
|
|
386
|
+
* file. One file per schema — `db/public.ts`, `db/billing.ts` — and the schema
|
|
387
|
+
* says its own name:
|
|
202
388
|
*
|
|
203
389
|
* const todos = defineTable("todos", {
|
|
204
390
|
* columns: {
|
|
@@ -234,49 +420,9 @@ type TablesByName<T extends readonly TableHandle[]> = {
|
|
|
234
420
|
* statement of intent instead of an omission. When `policies` is non-empty,
|
|
235
421
|
* `rls` is forced on (ENABLE + FORCE) regardless of the declared flag — a table
|
|
236
422
|
* with policies must have RLS enabled or the policies would be inert.
|
|
237
|
-
*/
|
|
238
|
-
/**
|
|
239
|
-
* Where a table's metadata lives.
|
|
240
|
-
*
|
|
241
|
-
* The table VALUE is its columns, so `lists.id` is the column — which means a
|
|
242
|
-
* column called `name` would overwrite the table's own name if metadata sat in
|
|
243
|
-
* a plain field. Measured, not imagined: the existing `users` / `rooms` tests
|
|
244
|
-
* (both declare a `name` column) produced an `[object Object]` table key.
|
|
245
|
-
* `columns`, `rls`, `unique` and `indexes` are all plausible column
|
|
246
|
-
* names too. A column cannot collide with a symbol.
|
|
247
|
-
*/
|
|
248
|
-
export declare const TABLE_META: unique symbol;
|
|
249
|
-
/**
|
|
250
|
-
* A value produced by `defineTable`: the columns, plus metadata behind the
|
|
251
|
-
* symbol. `S` is the table's search as declared: with one, the table's type
|
|
252
|
-
* says so, and `search()` is typed for it.
|
|
253
|
-
*/
|
|
254
|
-
export type TableHandle<C extends ColumnMap = ColumnMap, N extends string = string, S = undefined> = C & {
|
|
255
|
-
readonly [TABLE_META]: TableDef<C> & {
|
|
256
|
-
name: N;
|
|
257
|
-
} & ([S] extends [undefined] ? unknown : {
|
|
258
|
-
search: Search;
|
|
259
|
-
});
|
|
260
|
-
};
|
|
261
|
-
/**
|
|
262
|
-
* Declare one table as a standalone value that KNOWS ITS OWN NAME.
|
|
263
|
-
*
|
|
264
|
-
* The name lives here, not in a dictionary key. A table whose name comes from a
|
|
265
|
-
* key cannot resolve `references(() => other.id)` when it is built — it does not
|
|
266
|
-
* know what to call itself — which forces a two-phase resolution with an
|
|
267
|
-
* empty-name intermediate state. One name source, no empty identity.
|
|
268
|
-
*
|
|
269
|
-
* The returned object spreads its own columns, so a sibling can point at it:
|
|
270
|
-
* `references(() => lists.id)`.
|
|
271
|
-
*/
|
|
272
|
-
export declare function defineTable<const N extends string, C extends ColumnMap, const S extends SearchInput<Extract<keyof C, string>> | undefined = undefined>(name: N, input: TableInput<C> & {
|
|
273
|
-
search?: S;
|
|
274
|
-
}): TableHandle<C, N, S>;
|
|
275
|
-
/**
|
|
276
|
-
* Collect declared tables into one schema.
|
|
277
423
|
*
|
|
278
|
-
* Takes an ARRAY, not a dictionary: the tables already carry their
|
|
279
|
-
* second name source is a second thing that can disagree.
|
|
424
|
+
* Takes an ARRAY of tables, not a dictionary: the tables already carry their
|
|
425
|
+
* names, and a second name source is a second thing that can disagree.
|
|
280
426
|
*/
|
|
281
427
|
export declare function defineSchema<const T extends readonly TableHandle[]>(name: string, input: {
|
|
282
428
|
tables: T;
|
package/dist/chunks/7255GQJI.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! palgres 0.3.1 — Palbase SDK License (see LICENSE) */
|
|
2
|
-
import{d as b,e as p,y as c}from"./FAZVO74A.js";var g={uuid:"uuid",text:"text",integer:"integer",bigint:"bigint",numeric:"numeric",boolean:"boolean",timestamp:"timestamp with time zone",date:"date",jsonb:"jsonb"};function v(e){let n=Array.isArray(e)?e:[e],t={tables:[],enums:[],extensions:[],raw:[]},i=new Map;for(let l of n){for(let o of Object.values(l.tables))t.tables.push(E(l.name,o,i));t.extensions.push(...l.extensions);for(let o of l.raw??[])t.raw.push({name:o.name,schema:l.name,sql:o.up,...o.drop!==void 0?{drop:o.drop}:{},...o.before===!0?{before:!0}:{}})}t.enums=[...i.values()];for(let l of n)for(let o of Object.values(l.tables))for(let f of o.raw??[])t.raw.push({name:f.name,table:`${l.name}.${o.name}`,sql:f.up});return t}function x(e){return e._def}function E(e,n,t){let i=[],l=(n.unique??[]).map(r=>({name:r.name,columns:[...r.columns]})),o=[],f=[],s=new Set;for(let[r,y]of Object.entries(n.columns)){let u=x(y),m={name:r,type:D(e,r,n.name,u,t),nullable:u.nullable},h=K(u);if(h!==void 0&&(m.default=h),u.renamedFrom!==void 0&&(m.renamedFrom=u.renamedFrom),u.ignored&&(m.ignored=!0),u.fill!==void 0&&(m.fill=u.fill),u.identity!==void 0&&(m.identity=u.identity==="always"?"always":"by default"),u.generated!==void 0&&(m.generated={...u.generated}),u.user!==void 0&&(m.user={...u.user}),u.primaryKey&&f.push(r),u.unique&&l.push({name:`${n.name}_${r}_key`,columns:[r]}),u.references!==void 0){let[T,C]=w(u.references.table);o.push({name:`${q(e,n.name)}_${r}_fkey`,columns:[r],refSchema:T,refTable:C,refColumns:[u.references.column],onDelete:u.onDeleteAction??"no action",onUpdate:u.onUpdateAction??"no action"}),u.index===!1&&s.add(r)}i.push(m)}for(let r of n.foreignKeys??[]){r.index===!1&&r.columns.length===1&&s.add(r.columns[0]);let[y,u]=w(r.refTable);o.push({name:r.name,columns:[...r.columns],refSchema:y,refTable:u,refColumns:[...r.refColumns],onDelete:r.onDelete??"no action",onUpdate:r.onUpdate??"no action",...r.match==="full"?{match:"full"}:{}})}let a={schema:e,name:n.name,columns:i,primaryKey:n.primaryKey!==void 0?[...n.primaryKey]:f,uniques:l,indexes:(n.indexes??[]).map(N),checks:(n.checks??[]).map(r=>({name:r.name,expr:r.expr})),foreignKeys:o,rls:n.rls,policies:n.policies.map(P)};return n.ignored&&(a.ignored=!0),n.search!==void 0&&(a.search={...n.search,from:[...n.search.from]}),a.uniques=B(a.uniques),S(a,s,new Set((n.raw??[]).map(r=>r.name)),new Set(n.policies.filter(r=>r.indexes===!1).map(r=>r.name))),a}function D(e,n,t,i,l){let o=i.type;if(o==="enum"||o==="enum[]"){let f=i.enumName,s=`${e}.${f}`,a=i.enumValues??[],r=l.get(s);if(r&&r.values.join()!==a.join())throw new Error(`enum ${s} is declared with different values by ${t}.${n}: (${r.values.join(", ")}) and (${a.join(", ")}) \u2014 one type has one list`);return l.set(s,{schema:e,name:f,values:[...a]}),o==="enum[]"?`${p(e,f)}[]`:p(e,f)}return o==="sql"?b(i.sqlType):o==="numeric"&&i.precision!==void 0?`numeric(${i.precision},${i.scale??0})`:o.endsWith("[]")?`${g[o.slice(0,-2)]??o.slice(0,-2)}[]`:g[o]??o}function K(e){if(e.defaultSql!==void 0)return e.defaultSql;if(e.defaultRandom)return"gen_random_uuid()";if(e.defaultNow)return e.type==="date"?"CURRENT_DATE":"now()";let n=e.defaultValue;if(n!==void 0){if(Array.isArray(n)&&n.length===0&&e.type.endsWith("[]"))return"'{}'";if(typeof n=="string")return`'${n.replace(/'/g,"''")}'`;if(typeof n=="boolean")return n?"true":"false";if(typeof n=="number"){if(!Number.isFinite(n))throw new Error(`default ${n} is not a number Postgres can store`);return String(n)}return n===null?"NULL":`'${JSON.stringify(n).replace(/'/g,"''")}'::jsonb`}}function N(e){let n=e.expression!==void 0?[{expr:e.expression}]:(e.columns??[]).map(l=>({column:l}));for(let l of n)e.sort==="desc"&&(l.desc=!0),e.nulls!==void 0&&(l.nulls=e.nulls);let t={name:e.name,keys:n,unique:e.unique===!0},i=e.where===void 0?void 0:c(e.where);return i!==void 0&&(t.where=i),e.include?.length&&(t.include=[...e.include]),t}function P(e){let n={name:e.name,for:e.command,to:e.roles.length===0?["public"]:[...e.roles]},t=c(e.using),i=c(e.withCheck);return t!==void 0&&(n.using=t),i!==void 0&&(n.withCheck=i),e.permissive||(n.restrictive=!0),n}function S(e,n,t,i){let l=new Set,o=s=>s[0]!==void 0&&l.add(s[0]);o(e.primaryKey);for(let s of e.uniques)o(s.columns);for(let s of e.indexes){let a=s.keys[0];a&&"column"in a&&s.where===void 0&&l.add(a.column)}let f=s=>{if(l.has(s)||e.columns.find(r=>r.name===s)?.ignored)return;let a=`${e.name}_${s}_idx`;Buffer.byteLength(a)>63||e.indexes.some(r=>r.name===a)||t.has(a)||(e.indexes.push({name:a,keys:[{column:s}],unique:!1}),l.add(s))};for(let s of e.foreignKeys)s.columns.length===1&&!n.has(s.columns[0])&&f(s.columns[0]);for(let s of e.policies)if(!i.has(s.name))for(let a of A(e,s))f(a)}function A(e,n){let t=[];for(let i of[n.using,n.withCheck])if(i!==void 0)for(let l of i.matchAll(/(?<![.\w"])"((?:[^"]|"")+)"(?!\s*\.)/g)){let o=l[1].replace(/""/g,'"');e.columns.some(f=>f.name===o)&&!t.includes(o)&&t.push(o)}return t}function w(e){let n=e.indexOf(".");return n<0?["public",e]:[e.slice(0,n),e.slice(n+1)]}function q(e,n){return e==="public"?n:`${e}_${n}`}function B(e){let n=new Map;return e.filter(t=>{let i=n.get(t.name);if(i===void 0)return n.set(t.name,t),!0;if(i.columns.join()!==t.columns.join())throw new Error(`palgres: unique ${t.name} is declared over (${i.columns.join(", ")}) and over (${t.columns.join(", ")}): give one of them another name`);return!1})}var _=new Set(["uuid","text","integer","bigint","numeric","boolean","timestamp","date","enum"]),R={number:{fromDb:e=>Number(e),toDb:e=>{if(typeof e=="number"&&!Number.isSafeInteger(e))throw new Error(`asNumber() cannot write ${e}: it is outside the safe integer range and would be stored as a different number. Declare .asDecimal() and hand this column a string.`);return String(e)},tsType:"number"},decimal:{fromDb:e=>e==null?e:String(e),toDb:e=>e==null?e:String(e),tsType:"string"}},d=class e{_def;constructor(n,t){this._def=t??{type:n,nullable:!1,primaryKey:!1}}primaryKey(){return this._def.primaryKey=!0,new e(this._def.type,this._def)}notNull(){return this._def.nullable=!1,new e(this._def.type,this._def)}nullable(){if(this._def.identity!==void 0)throw new Error(".nullable() on an identity column: an identity is always NOT NULL");return this._def.nullable=!0,new e(this._def.type,this._def)}default(n){return this._def.defaultValue=n,new e(this._def.type,this._def)}defaultRandom(){return this._def.defaultRandom=!0,new e(this._def.type,this._def)}defaultNow(){return this._def.defaultNow=!0,new e(this._def.type,this._def)}defaultSql(n){if(typeof n!="string"||n.trim()==="")throw new Error("defaultSql() takes the SQL of the default");return this._def.defaultSql=n,new e(this._def.type,this._def)}dbAssigned(){return this._def.dbAssigned=!0,new e(this._def.type,this._def)}array(){let n=this._def.type;if(!_.has(n))throw new Error(`${n} column: .array() is available on uuid(), text(), integer(), bigint(), numeric(), boolean(), timestamp(), date() and enumType()`);if(this._def.defaultValue!==void 0||this._def.defaultNow||this._def.defaultRandom||this._def.defaultSql!==void 0)throw new Error(`.array() comes before the default \u2014 write ${n}().array().default([])`);if(this._def.codec!==void 0||this._def.transform!==void 0)throw new Error(".array() cannot follow a codec or a transform \u2014 they convert one value, not a list of them");let t=`${n}[]`;return new e(t,{...this._def,type:t})}identity(n){if(this._def.type!=="integer"&&this._def.type!=="bigint"&&!(this._def.type==="sql"&&this._def.sqlType==="smallint"))throw new Error(`${this._def.type} column: .identity() is for integer(), bigint() and sqlType('smallint') columns`);if(this._def.defaultValue!==void 0||this._def.defaultNow||this._def.defaultRandom||this._def.defaultSql!==void 0||this._def.fill!==void 0)throw new Error(".identity() is the column's value \u2014 it cannot also have a default or a fill");let t=n?.always===!1?"byDefault":"always";return new e(this._def.type,{...this._def,nullable:!1,identity:t})}generated(n,t={}){if(typeof n!="string"||n.trim()==="")throw new Error(".generated() takes the SQL expression the column is");if(this._def.defaultValue!==void 0||this._def.defaultNow||this._def.defaultRandom||this._def.defaultSql!==void 0||this._def.fill!==void 0||this._def.identity!==void 0)throw new Error(".generated() is the column's value \u2014 it cannot also have a default, a fill or an identity");return new e(this._def.type,{...this._def,generated:{expr:n,stored:t.stored===!0}})}fillWith(n){return new e(this._def.type,{...this._def,fill:n})}renamedFrom(n){return this._def.renamedFrom=n,this}ignored(){return new e(this._def.type,{...this._def,ignored:!0})}references(n,t){if(typeof n!="function")throw new Error('references(...) takes a callback: write references(() => otherTable.column). The two-string form references("table", "column") is gone \u2014 a string cannot be type-checked and cannot point at a table that does not exist yet.');return this._def.referencesThunk=n,t?.as!==void 0&&(this._def.refAs=t.as),t?.reverseAs!==void 0&&(this._def.reverseAs=t.reverseAs),t?.onDelete!==void 0&&(this._def.onDeleteAction=t.onDelete),t?.onUpdate!==void 0&&(this._def.onUpdateAction=t.onUpdate),t?.index===!1&&(this._def.index=!1),new e(this._def.type,this._def)}selfReferences(n,t){return this._def.selfRefColumn=n,t?.as!==void 0&&(this._def.refAs=t.as),t?.onDelete!==void 0&&(this._def.onDeleteAction=t.onDelete),new e(this._def.type,this._def)}onDelete(n){return this._def.onDeleteAction=n,new e(this._def.type,this._def)}unique(){return this._def.unique=!0,new e(this._def.type,this._def)}asNumber(){return this.withCodec("number","asNumber")}asDecimal(){return this.withCodec("decimal","asDecimal")}withCodec(n,t){if(this._def.type!=="bigint"&&this._def.type!=="numeric")throw new Error(`${this._def.type} column: .${t}() is only available on bigint()/numeric() \u2014 those are the exact-precision types that surface as string.`);if(this._def.transform!==void 0)throw new Error(`.${t}() and .transform() are two ways to say the same thing on one column \u2014 keep .${t}().`);if(this._def.codec!==void 0)throw new Error(`column already declares .as${this._def.codec==="number"?"Number":"Decimal"}(); a column has ONE codec.`);return new e(this._def.type,{...this._def,codec:n})}transform(n){if(this._def.codec!==void 0)throw new Error("column already declares a named codec; .transform() and .as*() are two ways to say the same thing \u2014 keep the codec.");return new e(this._def.type,{...this._def,transform:n})}};function U(){return new d("uuid")}function V(){return new d("text")}function j(){return new d("integer")}function L(){return new d("bigint")}function F(e){if(e===void 0)return new d("numeric");let{precision:n,scale:t=0}=e;if(!Number.isInteger(n)||n<1||n>1e3)throw new Error(`numeric({ precision: ${n} }): precision is a whole number from 1 to 1000`);if(!Number.isInteger(t)||t<0||t>n)throw new Error(`numeric({ precision: ${n}, scale: ${t} }): scale is a whole number from 0 to the precision`);return new d("numeric",{type:"numeric",nullable:!1,primaryKey:!1,precision:n,scale:t})}function I(){return new d("date")}var W={uuid:"uuid()",text:"text()",integer:"integer()",int:"integer()",int4:"integer()",bigint:"bigint()",int8:"bigint()",numeric:"numeric()",decimal:"numeric()",boolean:"boolean()",bool:"boolean()",timestamptz:"timestamp()","timestamp with time zone":"timestamp()",date:"date()",jsonb:"jsonb()"};function M(e){let n=typeof e=="string"?e.trim():"";if(n==="")throw new Error('sqlType() takes the Postgres type, e.g. sqlType("inet")');let t=n.toLowerCase().replace(/\[\]$/,""),i=W[t];if(i!==void 0&&!(n.endsWith("[]")&&!_.has(i.slice(0,-2))))throw new Error(`sqlType(${JSON.stringify(n)}): the DSL has this type \u2014 declare it as ${i}${n.endsWith("[]")?".array()":""}`);return new d("sql",{type:"sql",nullable:!1,primaryKey:!1,sqlType:n})}var Q=new Set(["smallint","real","double precision"]),Y=new Set(["json"]);function H(){return new d("boolean")}function z(){return new d("timestamp")}function J(){return new d("jsonb")}function G(e,n){let t=new d("enum");return t._def.enumName=e,t._def.enumValues=[...n],t}function X(){let e=new d("text");return e._def.nullable=!1,e._def.user={onDelete:"cascade",owns:!0},e}function Z(e){let n=new d("text");return n._def.user={onDelete:e.onDelete,owns:!1},e.as!==void 0&&(n._def.refAs=e.as),n}export{v as a,A as b,R as c,d,U as e,V as f,j as g,L as h,F as i,I as j,M as k,Q as l,Y as m,H as n,z as o,J as p,G as q,X as r,Z as s};
|