@palbase/backend 2.0.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-4J3F32SH.js → chunk-B7EUJP5W.js} +38 -9
- package/dist/chunk-B7EUJP5W.js.map +1 -0
- package/dist/{chunk-L36JLUPO.js → chunk-PHAFZGHN.js} +43 -46
- package/dist/chunk-PHAFZGHN.js.map +1 -0
- package/dist/db/env.cjs +19 -0
- package/dist/db/env.cjs.map +1 -0
- package/dist/db/env.d.cts +45 -0
- package/dist/db/env.d.ts +45 -0
- package/dist/db/env.js +1 -0
- package/dist/db/env.js.map +1 -0
- package/dist/db/index.cjs +28 -231
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +4 -20
- package/dist/db/index.d.ts +4 -20
- package/dist/db/index.js +3 -233
- package/dist/db/index.js.map +1 -1
- package/dist/{endpoint-Djk5L6G2.d.ts → endpoint-DJ98tQd6.d.cts} +30 -68
- package/dist/{endpoint-BlcY2xNA.d.cts → endpoint-DJ98tQd6.d.ts} +30 -68
- package/dist/index-CXUs9iTQ.d.ts +294 -0
- package/dist/index-CZAwpQE1.d.cts +294 -0
- package/dist/index.cjs +229 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +398 -154
- package/dist/index.d.ts +398 -154
- package/dist/index.js +147 -12
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +41 -1
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -2
- package/dist/test/index.d.ts +1 -2
- package/dist/test/index.js +1 -1
- package/docs/README.md +31 -10
- package/docs/background.md +19 -13
- package/docs/database.md +30 -17
- package/docs/endpoints.md +42 -24
- package/docs/errors.md +3 -4
- package/docs/events.md +25 -17
- package/docs/getting-started.md +25 -9
- package/docs/llms-full.txt +489 -164
- package/docs/llms.txt +3 -1
- package/docs/migrations.md +98 -0
- package/docs/resources.md +94 -0
- package/docs/routing.md +59 -26
- package/docs/schema.md +48 -38
- package/docs/services.md +5 -6
- package/package.json +11 -1
- package/dist/chunk-4J3F32SH.js.map +0 -1
- package/dist/chunk-L36JLUPO.js.map +0 -1
- package/dist/schema-BqfEhIC0.d.cts +0 -133
- package/dist/schema-BqfEhIC0.d.ts +0 -133
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { Tables, TableTypes } from './db/env.js';
|
|
2
|
+
import { D as DBClient } from './endpoint-DJ98tQd6.js';
|
|
3
|
+
|
|
4
|
+
/** On delete action for foreign key references. */
|
|
5
|
+
type OnDeleteAction = 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
6
|
+
/** Column type identifiers. */
|
|
7
|
+
type ColumnType = 'uuid' | 'text' | 'integer' | 'boolean' | 'timestamp' | 'jsonb' | 'enum';
|
|
8
|
+
/** Base column definition shared by all column types. */
|
|
9
|
+
interface ColumnDef {
|
|
10
|
+
type: ColumnType;
|
|
11
|
+
nullable: boolean;
|
|
12
|
+
primaryKey: boolean;
|
|
13
|
+
defaultValue?: unknown;
|
|
14
|
+
defaultRandom?: boolean;
|
|
15
|
+
defaultNow?: boolean;
|
|
16
|
+
references?: {
|
|
17
|
+
table: string;
|
|
18
|
+
column: string;
|
|
19
|
+
};
|
|
20
|
+
onDeleteAction?: OnDeleteAction;
|
|
21
|
+
enumName?: string;
|
|
22
|
+
enumValues?: string[];
|
|
23
|
+
}
|
|
24
|
+
declare const __colKind: unique symbol;
|
|
25
|
+
declare const __colNullable: unique symbol;
|
|
26
|
+
declare const __colHasDefault: unique symbol;
|
|
27
|
+
declare const __colEnumValues: unique symbol;
|
|
28
|
+
/**
|
|
29
|
+
* Fluent column builder with phantom type params:
|
|
30
|
+
* K — ColumnType literal (e.g. "text", "integer")
|
|
31
|
+
* N — boolean: true when nullable() has been called last (false = NOT NULL)
|
|
32
|
+
* D — boolean: true when a default has been set
|
|
33
|
+
* E — enum value union (never for non-enum columns)
|
|
34
|
+
*
|
|
35
|
+
* All four params have defaults so bare `ColumnBuilder` (no args) still
|
|
36
|
+
* satisfies `Record<string, ColumnBuilder>` in schema.ts without modification.
|
|
37
|
+
*
|
|
38
|
+
* The four `declare readonly` brand fields carry the phantom types into the
|
|
39
|
+
* structural shape so that conditional types like ColValue<C> can discriminate
|
|
40
|
+
* on K without requiring runtime values on those fields.
|
|
41
|
+
*/
|
|
42
|
+
declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends boolean = boolean, D extends boolean = boolean, E = unknown> {
|
|
43
|
+
readonly [__colKind]: K;
|
|
44
|
+
readonly [__colNullable]: N;
|
|
45
|
+
readonly [__colHasDefault]: D;
|
|
46
|
+
readonly [__colEnumValues]: E;
|
|
47
|
+
readonly _def: ColumnDef;
|
|
48
|
+
constructor(type: K, existingDef?: ColumnDef);
|
|
49
|
+
/** Mark this column as the primary key. */
|
|
50
|
+
primaryKey(): ColumnBuilder<K, N, D, E>;
|
|
51
|
+
/** Mark this column as NOT NULL (default). */
|
|
52
|
+
notNull(): ColumnBuilder<K, false, D, E>;
|
|
53
|
+
/** Allow NULL values. */
|
|
54
|
+
nullable(): ColumnBuilder<K, true, D, E>;
|
|
55
|
+
/** Set a default value. */
|
|
56
|
+
default(value: unknown): ColumnBuilder<K, N, true, E>;
|
|
57
|
+
/** UUID: generate a random default (gen_random_uuid()). */
|
|
58
|
+
defaultRandom(): ColumnBuilder<K, N, true, E>;
|
|
59
|
+
/** Timestamp: default to now(). */
|
|
60
|
+
defaultNow(): ColumnBuilder<K, N, true, E>;
|
|
61
|
+
/** Add a foreign key reference. */
|
|
62
|
+
references(table: string, column: string): ColumnBuilder<K, N, D, E>;
|
|
63
|
+
/** Set the ON DELETE action for a foreign key reference. */
|
|
64
|
+
onDelete(action: OnDeleteAction): ColumnBuilder<K, N, D, E>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Extracts the TypeScript value type for a column, respecting nullability.
|
|
68
|
+
* - "uuid" | "text" | "timestamp" → string (or string | null when N = true)
|
|
69
|
+
* - "integer" → number
|
|
70
|
+
* - "boolean" → boolean
|
|
71
|
+
* - "jsonb" → unknown (opaque JSON)
|
|
72
|
+
* - "enum" → E (the union of literal values)
|
|
73
|
+
*/
|
|
74
|
+
type ColValue<C> = C extends ColumnBuilder<'uuid' | 'text' | 'timestamp', infer N, infer _D, infer _E> ? N extends true ? string | null : string : C extends ColumnBuilder<'integer', infer N, infer _D, infer _E> ? N extends true ? number | null : number : C extends ColumnBuilder<'boolean', infer N, infer _D, infer _E> ? N extends true ? boolean | null : boolean : C extends ColumnBuilder<'jsonb', infer _N, infer _D, infer _E> ? unknown : C extends ColumnBuilder<'enum', infer N, infer _D, infer E> ? N extends true ? E | null : E : never;
|
|
75
|
+
/**
|
|
76
|
+
* True when a column is optional on INSERT:
|
|
77
|
+
* - nullable columns (N = true) — the DB allows NULL so the field may be omitted
|
|
78
|
+
* - columns with a default (D = true) — the DB fills in the value when absent
|
|
79
|
+
*/
|
|
80
|
+
type ColIsOptionalOnInsert<C> = C extends ColumnBuilder<infer _K, true, infer _D, infer _E> ? true : C extends ColumnBuilder<infer _K, infer _N, true, infer _E> ? true : false;
|
|
81
|
+
/** Create a UUID column. */
|
|
82
|
+
declare function uuid(): ColumnBuilder<'uuid', false, false, never>;
|
|
83
|
+
/** Create a TEXT column. */
|
|
84
|
+
declare function text(): ColumnBuilder<'text', false, false, never>;
|
|
85
|
+
/** Create an INTEGER column. */
|
|
86
|
+
declare function integer(): ColumnBuilder<'integer', false, false, never>;
|
|
87
|
+
/** Create a BOOLEAN column. */
|
|
88
|
+
declare function boolean(): ColumnBuilder<'boolean', false, false, never>;
|
|
89
|
+
/** Create a TIMESTAMP column. */
|
|
90
|
+
declare function timestamp(): ColumnBuilder<'timestamp', false, false, never>;
|
|
91
|
+
/** Create a JSONB column. */
|
|
92
|
+
declare function jsonb(): ColumnBuilder<'jsonb', false, false, never>;
|
|
93
|
+
/**
|
|
94
|
+
* Create an ENUM column.
|
|
95
|
+
* @param name The PostgreSQL enum type name (used in DDL).
|
|
96
|
+
* @param values A readonly tuple of valid string values — kept `const` so the
|
|
97
|
+
* union `V[number]` is as narrow as possible.
|
|
98
|
+
*/
|
|
99
|
+
declare function enumType<const V extends readonly string[]>(name: string, values: V): ColumnBuilder<'enum', false, false, V[number]>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A map of column builders keyed by column name — the value you write under
|
|
103
|
+
* each key of `defineSchema({ tables: { <name>: <columns> } })`.
|
|
104
|
+
*
|
|
105
|
+
* The default `Record<string, ColumnBuilder>` keeps bare references compiling
|
|
106
|
+
* without a type argument.
|
|
107
|
+
*/
|
|
108
|
+
type ColumnMap = Record<string, ColumnBuilder>;
|
|
109
|
+
/**
|
|
110
|
+
* A table definition with its name and columns.
|
|
111
|
+
*
|
|
112
|
+
* The runtime value shape — `{ name, columns }` — is the contract the Go
|
|
113
|
+
* runtime's `schema_extract.js` reads (it keys tables by `tableDef.name`).
|
|
114
|
+
* `defineSchema` derives `name` from the object key, so authors never repeat
|
|
115
|
+
* the table name.
|
|
116
|
+
*
|
|
117
|
+
* The `C` type parameter preserves the precise per-column phantom types so
|
|
118
|
+
* that downstream mapped types (InsertShape, RowShape) can discriminate on
|
|
119
|
+
* them.
|
|
120
|
+
*/
|
|
121
|
+
interface TableDef<C extends ColumnMap = ColumnMap> {
|
|
122
|
+
name: string;
|
|
123
|
+
columns: C;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A schema definition containing multiple tables, keyed by table name.
|
|
127
|
+
*
|
|
128
|
+
* The `T` type parameter preserves the exact `TableDef<...>` type for each
|
|
129
|
+
* table so that `SchemaDef["tables"]["rooms"]` resolves to the precise
|
|
130
|
+
* `TableDef<{ id: ColumnBuilder<'uuid', false, true, never>; ... }>`.
|
|
131
|
+
*/
|
|
132
|
+
interface SchemaDef<T extends Record<string, TableDef> = Record<string, TableDef>> {
|
|
133
|
+
tables: T;
|
|
134
|
+
}
|
|
135
|
+
/** The author-facing input to `defineSchema` — a `tables` map whose keys are
|
|
136
|
+
* the table names and whose values are the column maps. */
|
|
137
|
+
interface SchemaInput<T extends Record<string, ColumnMap> = Record<string, ColumnMap>> {
|
|
138
|
+
tables: T;
|
|
139
|
+
}
|
|
140
|
+
/** Map the author's `{ tables: { <name>: <columns> } }` input to the
|
|
141
|
+
* `{ tables: { <name>: TableDef<columns> } }` runtime/type shape. */
|
|
142
|
+
type TablesFromInput<T extends Record<string, ColumnMap>> = {
|
|
143
|
+
[K in keyof T]: TableDef<T[K]>;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Define a schema. The table NAME comes from the object key — there is one
|
|
147
|
+
* canonical form:
|
|
148
|
+
*
|
|
149
|
+
* export default defineSchema({
|
|
150
|
+
* tables: {
|
|
151
|
+
* todos: {
|
|
152
|
+
* id: uuid().primaryKey().defaultRandom(),
|
|
153
|
+
* title: text().notNull(),
|
|
154
|
+
* },
|
|
155
|
+
* },
|
|
156
|
+
* });
|
|
157
|
+
*
|
|
158
|
+
* The returned value is `{ tables: { todos: { name: "todos", columns: {...} } } }`
|
|
159
|
+
* — the exact shape the runtime schema extractor parses. Per-column phantom
|
|
160
|
+
* types are preserved so `Database.tables.todos.insert({...})` is typed.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* import { defineSchema, uuid, text, timestamp } from "@palbase/backend";
|
|
164
|
+
*
|
|
165
|
+
* export default defineSchema({
|
|
166
|
+
* tables: {
|
|
167
|
+
* todos: {
|
|
168
|
+
* id: uuid().primaryKey().defaultRandom(),
|
|
169
|
+
* title: text().notNull(),
|
|
170
|
+
* done: boolean().default(false),
|
|
171
|
+
* created_at: timestamp().defaultNow(),
|
|
172
|
+
* },
|
|
173
|
+
* },
|
|
174
|
+
* });
|
|
175
|
+
*/
|
|
176
|
+
declare function defineSchema<T extends Record<string, ColumnMap>>(input: SchemaInput<T>): SchemaDef<TablesFromInput<T>>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* typed-db.ts — Task 2: TypedDB schema-derived insert/row shapes.
|
|
180
|
+
*
|
|
181
|
+
* Derives INSERT and full-row TypeScript types from a `defineSchema()` result
|
|
182
|
+
* and wraps the untyped runtime `DBClient` with a typed facade.
|
|
183
|
+
*
|
|
184
|
+
* No value-any. No `as unknown as X`. The two narrow `as` casts in
|
|
185
|
+
* `makeTypedTable` are safe because:
|
|
186
|
+
* - `data as Record<string, unknown>`: InsertShape<T> maps string keys to
|
|
187
|
+
* typed values; all value types are subsets of `unknown`, so the cast is
|
|
188
|
+
* structurally sound.
|
|
189
|
+
* - `result as RowShape<T>`: The runtime DBClient returns `Record<string,
|
|
190
|
+
* unknown>` which is the erased form of the typed row; we're narrowing back
|
|
191
|
+
* to the precise shape that the schema declared.
|
|
192
|
+
* Both casts are narrowing only (not widening) and correctness is guaranteed
|
|
193
|
+
* by the schema the caller provides.
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
/** Keys of C whose columns are required on INSERT (not nullable, no default). */
|
|
197
|
+
type RequiredKeys<C> = {
|
|
198
|
+
[K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? never : K;
|
|
199
|
+
}[keyof C];
|
|
200
|
+
/** Keys of C whose columns are optional on INSERT (nullable or has a default). */
|
|
201
|
+
type OptionalKeys<C> = {
|
|
202
|
+
[K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? K : never;
|
|
203
|
+
}[keyof C];
|
|
204
|
+
/**
|
|
205
|
+
* The TypeScript type for an INSERT payload for table `T`.
|
|
206
|
+
* - Required: columns that are NOT NULL and have no DB-level default.
|
|
207
|
+
* - Optional: columns that are nullable or carry a default.
|
|
208
|
+
*
|
|
209
|
+
* When all columns are optional, `RequiredKeys<C>` resolves to `never` and
|
|
210
|
+
* the first part becomes `{}`, which is a neutral element for `&`.
|
|
211
|
+
*/
|
|
212
|
+
type InsertShape<T extends TableDef> = {
|
|
213
|
+
[K in RequiredKeys<T["columns"]>]: ColValue<T["columns"][K]>;
|
|
214
|
+
} & {
|
|
215
|
+
[K in OptionalKeys<T["columns"]>]?: ColValue<T["columns"][K]>;
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* The TypeScript type for a full row returned by the DB for table `T`.
|
|
219
|
+
* Every column is present; nullable columns resolve to `T | null`.
|
|
220
|
+
*/
|
|
221
|
+
type RowShape<T extends TableDef> = {
|
|
222
|
+
[K in keyof T["columns"]]: ColValue<T["columns"][K]>;
|
|
223
|
+
};
|
|
224
|
+
/** A typed table accessor that mirrors the runtime DBClient surface. */
|
|
225
|
+
interface TypedTable<T extends TableDef> {
|
|
226
|
+
insert(data: InsertShape<T>): Promise<RowShape<T>>;
|
|
227
|
+
update(id: string, data: Partial<InsertShape<T>>): Promise<RowShape<T>>;
|
|
228
|
+
delete(id: string): Promise<void>;
|
|
229
|
+
findById(id: string): Promise<RowShape<T> | null>;
|
|
230
|
+
findMany(query?: Partial<RowShape<T>>): Promise<RowShape<T>[]>;
|
|
231
|
+
}
|
|
232
|
+
/** A typed DB facade covering all tables declared in schema `S`. */
|
|
233
|
+
interface TypedDB<S extends SchemaDef> {
|
|
234
|
+
tables: {
|
|
235
|
+
[K in keyof S["tables"]]: TypedTable<S["tables"][K]>;
|
|
236
|
+
};
|
|
237
|
+
transaction<T>(fn: (tx: TypedTx<S>) => Promise<T>): Promise<T>;
|
|
238
|
+
}
|
|
239
|
+
/** Transaction-scoped typed facade: same typed tables, no nested transaction. */
|
|
240
|
+
interface TypedTx<S extends SchemaDef> {
|
|
241
|
+
tables: {
|
|
242
|
+
[K in keyof S["tables"]]: TypedTable<S["tables"][K]>;
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Wraps a raw `DBClient` with the type-safe `TypedDB<S>` facade derived from
|
|
247
|
+
* the provided schema. No behavior change — all calls delegate to `raw` with
|
|
248
|
+
* the table name as a plain string.
|
|
249
|
+
*
|
|
250
|
+
* `buildTables` is the reusable factory that wraps any op-bearing client
|
|
251
|
+
* (`TxClient` — the surface shared by `DBClient` and the transaction-scoped
|
|
252
|
+
* client) into the typed tables map. It is used both for the top-level db
|
|
253
|
+
* (wrapping `raw`) and inside `transaction`, where it wraps the raw `TxClient`
|
|
254
|
+
* the runtime yields so the callback sees the same typed `.tables` API.
|
|
255
|
+
*
|
|
256
|
+
* `transaction` delegates straight to `raw.transaction`; the two narrow
|
|
257
|
+
* `as TypedTx<S>` / `as TypedDB<S>` casts are single structural narrowings
|
|
258
|
+
* from the dynamically-built tables object to the precise mapped type (TS
|
|
259
|
+
* cannot infer through `Object.keys` iteration) — see module-level doc comment.
|
|
260
|
+
*/
|
|
261
|
+
declare function makeTypedDB<S extends SchemaDef>(schema: S, raw: DBClient): TypedDB<S>;
|
|
262
|
+
/** A typed table accessor derived from one env `Tables` entry's flat shapes. */
|
|
263
|
+
interface EnvTypedTable<T extends TableTypes> {
|
|
264
|
+
insert(data: T["insert"]): Promise<T["row"]>;
|
|
265
|
+
update(id: string, data: Partial<T["insert"]>): Promise<T["row"]>;
|
|
266
|
+
delete(id: string): Promise<void>;
|
|
267
|
+
findById(id: string): Promise<T["row"] | null>;
|
|
268
|
+
findMany(query?: Partial<T["row"]>): Promise<T["row"][]>;
|
|
269
|
+
}
|
|
270
|
+
/** The `tables` map exposed on `Database`/`tx`, keyed by the env `Tables`
|
|
271
|
+
* interface. When no schema is declared `Tables` is empty, so `tables` is an
|
|
272
|
+
* empty object — accessing `.tables.foo` is then a compile error (no member). */
|
|
273
|
+
type EnvTables = {
|
|
274
|
+
[K in keyof Tables]: EnvTypedTable<Tables[K]>;
|
|
275
|
+
};
|
|
276
|
+
/** Transaction-scoped typed facade for the env-augmented surface: same typed
|
|
277
|
+
* tables, no nested transaction. */
|
|
278
|
+
interface EnvTypedTx {
|
|
279
|
+
tables: EnvTables;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The typed-by-default Database surface: the raw string-keyed `DBClient` ops
|
|
283
|
+
* PLUS a `tables` map typed against the project's generated `palbase-env.d.ts`
|
|
284
|
+
* and a `transaction` whose callback receives the typed tables.
|
|
285
|
+
*
|
|
286
|
+
* `transaction` is declared here (overriding `DBClient["transaction"]`) so the
|
|
287
|
+
* `tx` the callback receives carries the typed `.tables` API.
|
|
288
|
+
*/
|
|
289
|
+
interface EnvTypedDatabase extends Omit<DBClient, "transaction"> {
|
|
290
|
+
tables: EnvTables;
|
|
291
|
+
transaction<T>(fn: (tx: EnvTypedTx) => Promise<T>): Promise<T>;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export { ColumnBuilder as C, type EnvTypedDatabase as E, type InsertShape as I, type OnDeleteAction as O, type RowShape as R, type SchemaDef as S, type TableDef as T, type ColumnDef as a, type ColumnMap as b, type ColumnType as c, type EnvTables as d, type EnvTypedTable as e, type EnvTypedTx as f, type SchemaInput as g, type TypedDB as h, type TypedTable as i, type TypedTx as j, boolean as k, defineSchema as l, enumType as m, integer as n, jsonb as o, makeTypedDB as p, timestamp as q, text as t, uuid as u };
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { Tables, TableTypes } from './db/env.cjs';
|
|
2
|
+
import { D as DBClient } from './endpoint-DJ98tQd6.cjs';
|
|
3
|
+
|
|
4
|
+
/** On delete action for foreign key references. */
|
|
5
|
+
type OnDeleteAction = 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
6
|
+
/** Column type identifiers. */
|
|
7
|
+
type ColumnType = 'uuid' | 'text' | 'integer' | 'boolean' | 'timestamp' | 'jsonb' | 'enum';
|
|
8
|
+
/** Base column definition shared by all column types. */
|
|
9
|
+
interface ColumnDef {
|
|
10
|
+
type: ColumnType;
|
|
11
|
+
nullable: boolean;
|
|
12
|
+
primaryKey: boolean;
|
|
13
|
+
defaultValue?: unknown;
|
|
14
|
+
defaultRandom?: boolean;
|
|
15
|
+
defaultNow?: boolean;
|
|
16
|
+
references?: {
|
|
17
|
+
table: string;
|
|
18
|
+
column: string;
|
|
19
|
+
};
|
|
20
|
+
onDeleteAction?: OnDeleteAction;
|
|
21
|
+
enumName?: string;
|
|
22
|
+
enumValues?: string[];
|
|
23
|
+
}
|
|
24
|
+
declare const __colKind: unique symbol;
|
|
25
|
+
declare const __colNullable: unique symbol;
|
|
26
|
+
declare const __colHasDefault: unique symbol;
|
|
27
|
+
declare const __colEnumValues: unique symbol;
|
|
28
|
+
/**
|
|
29
|
+
* Fluent column builder with phantom type params:
|
|
30
|
+
* K — ColumnType literal (e.g. "text", "integer")
|
|
31
|
+
* N — boolean: true when nullable() has been called last (false = NOT NULL)
|
|
32
|
+
* D — boolean: true when a default has been set
|
|
33
|
+
* E — enum value union (never for non-enum columns)
|
|
34
|
+
*
|
|
35
|
+
* All four params have defaults so bare `ColumnBuilder` (no args) still
|
|
36
|
+
* satisfies `Record<string, ColumnBuilder>` in schema.ts without modification.
|
|
37
|
+
*
|
|
38
|
+
* The four `declare readonly` brand fields carry the phantom types into the
|
|
39
|
+
* structural shape so that conditional types like ColValue<C> can discriminate
|
|
40
|
+
* on K without requiring runtime values on those fields.
|
|
41
|
+
*/
|
|
42
|
+
declare class ColumnBuilder<K extends ColumnType = ColumnType, N extends boolean = boolean, D extends boolean = boolean, E = unknown> {
|
|
43
|
+
readonly [__colKind]: K;
|
|
44
|
+
readonly [__colNullable]: N;
|
|
45
|
+
readonly [__colHasDefault]: D;
|
|
46
|
+
readonly [__colEnumValues]: E;
|
|
47
|
+
readonly _def: ColumnDef;
|
|
48
|
+
constructor(type: K, existingDef?: ColumnDef);
|
|
49
|
+
/** Mark this column as the primary key. */
|
|
50
|
+
primaryKey(): ColumnBuilder<K, N, D, E>;
|
|
51
|
+
/** Mark this column as NOT NULL (default). */
|
|
52
|
+
notNull(): ColumnBuilder<K, false, D, E>;
|
|
53
|
+
/** Allow NULL values. */
|
|
54
|
+
nullable(): ColumnBuilder<K, true, D, E>;
|
|
55
|
+
/** Set a default value. */
|
|
56
|
+
default(value: unknown): ColumnBuilder<K, N, true, E>;
|
|
57
|
+
/** UUID: generate a random default (gen_random_uuid()). */
|
|
58
|
+
defaultRandom(): ColumnBuilder<K, N, true, E>;
|
|
59
|
+
/** Timestamp: default to now(). */
|
|
60
|
+
defaultNow(): ColumnBuilder<K, N, true, E>;
|
|
61
|
+
/** Add a foreign key reference. */
|
|
62
|
+
references(table: string, column: string): ColumnBuilder<K, N, D, E>;
|
|
63
|
+
/** Set the ON DELETE action for a foreign key reference. */
|
|
64
|
+
onDelete(action: OnDeleteAction): ColumnBuilder<K, N, D, E>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Extracts the TypeScript value type for a column, respecting nullability.
|
|
68
|
+
* - "uuid" | "text" | "timestamp" → string (or string | null when N = true)
|
|
69
|
+
* - "integer" → number
|
|
70
|
+
* - "boolean" → boolean
|
|
71
|
+
* - "jsonb" → unknown (opaque JSON)
|
|
72
|
+
* - "enum" → E (the union of literal values)
|
|
73
|
+
*/
|
|
74
|
+
type ColValue<C> = C extends ColumnBuilder<'uuid' | 'text' | 'timestamp', infer N, infer _D, infer _E> ? N extends true ? string | null : string : C extends ColumnBuilder<'integer', infer N, infer _D, infer _E> ? N extends true ? number | null : number : C extends ColumnBuilder<'boolean', infer N, infer _D, infer _E> ? N extends true ? boolean | null : boolean : C extends ColumnBuilder<'jsonb', infer _N, infer _D, infer _E> ? unknown : C extends ColumnBuilder<'enum', infer N, infer _D, infer E> ? N extends true ? E | null : E : never;
|
|
75
|
+
/**
|
|
76
|
+
* True when a column is optional on INSERT:
|
|
77
|
+
* - nullable columns (N = true) — the DB allows NULL so the field may be omitted
|
|
78
|
+
* - columns with a default (D = true) — the DB fills in the value when absent
|
|
79
|
+
*/
|
|
80
|
+
type ColIsOptionalOnInsert<C> = C extends ColumnBuilder<infer _K, true, infer _D, infer _E> ? true : C extends ColumnBuilder<infer _K, infer _N, true, infer _E> ? true : false;
|
|
81
|
+
/** Create a UUID column. */
|
|
82
|
+
declare function uuid(): ColumnBuilder<'uuid', false, false, never>;
|
|
83
|
+
/** Create a TEXT column. */
|
|
84
|
+
declare function text(): ColumnBuilder<'text', false, false, never>;
|
|
85
|
+
/** Create an INTEGER column. */
|
|
86
|
+
declare function integer(): ColumnBuilder<'integer', false, false, never>;
|
|
87
|
+
/** Create a BOOLEAN column. */
|
|
88
|
+
declare function boolean(): ColumnBuilder<'boolean', false, false, never>;
|
|
89
|
+
/** Create a TIMESTAMP column. */
|
|
90
|
+
declare function timestamp(): ColumnBuilder<'timestamp', false, false, never>;
|
|
91
|
+
/** Create a JSONB column. */
|
|
92
|
+
declare function jsonb(): ColumnBuilder<'jsonb', false, false, never>;
|
|
93
|
+
/**
|
|
94
|
+
* Create an ENUM column.
|
|
95
|
+
* @param name The PostgreSQL enum type name (used in DDL).
|
|
96
|
+
* @param values A readonly tuple of valid string values — kept `const` so the
|
|
97
|
+
* union `V[number]` is as narrow as possible.
|
|
98
|
+
*/
|
|
99
|
+
declare function enumType<const V extends readonly string[]>(name: string, values: V): ColumnBuilder<'enum', false, false, V[number]>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A map of column builders keyed by column name — the value you write under
|
|
103
|
+
* each key of `defineSchema({ tables: { <name>: <columns> } })`.
|
|
104
|
+
*
|
|
105
|
+
* The default `Record<string, ColumnBuilder>` keeps bare references compiling
|
|
106
|
+
* without a type argument.
|
|
107
|
+
*/
|
|
108
|
+
type ColumnMap = Record<string, ColumnBuilder>;
|
|
109
|
+
/**
|
|
110
|
+
* A table definition with its name and columns.
|
|
111
|
+
*
|
|
112
|
+
* The runtime value shape — `{ name, columns }` — is the contract the Go
|
|
113
|
+
* runtime's `schema_extract.js` reads (it keys tables by `tableDef.name`).
|
|
114
|
+
* `defineSchema` derives `name` from the object key, so authors never repeat
|
|
115
|
+
* the table name.
|
|
116
|
+
*
|
|
117
|
+
* The `C` type parameter preserves the precise per-column phantom types so
|
|
118
|
+
* that downstream mapped types (InsertShape, RowShape) can discriminate on
|
|
119
|
+
* them.
|
|
120
|
+
*/
|
|
121
|
+
interface TableDef<C extends ColumnMap = ColumnMap> {
|
|
122
|
+
name: string;
|
|
123
|
+
columns: C;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A schema definition containing multiple tables, keyed by table name.
|
|
127
|
+
*
|
|
128
|
+
* The `T` type parameter preserves the exact `TableDef<...>` type for each
|
|
129
|
+
* table so that `SchemaDef["tables"]["rooms"]` resolves to the precise
|
|
130
|
+
* `TableDef<{ id: ColumnBuilder<'uuid', false, true, never>; ... }>`.
|
|
131
|
+
*/
|
|
132
|
+
interface SchemaDef<T extends Record<string, TableDef> = Record<string, TableDef>> {
|
|
133
|
+
tables: T;
|
|
134
|
+
}
|
|
135
|
+
/** The author-facing input to `defineSchema` — a `tables` map whose keys are
|
|
136
|
+
* the table names and whose values are the column maps. */
|
|
137
|
+
interface SchemaInput<T extends Record<string, ColumnMap> = Record<string, ColumnMap>> {
|
|
138
|
+
tables: T;
|
|
139
|
+
}
|
|
140
|
+
/** Map the author's `{ tables: { <name>: <columns> } }` input to the
|
|
141
|
+
* `{ tables: { <name>: TableDef<columns> } }` runtime/type shape. */
|
|
142
|
+
type TablesFromInput<T extends Record<string, ColumnMap>> = {
|
|
143
|
+
[K in keyof T]: TableDef<T[K]>;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Define a schema. The table NAME comes from the object key — there is one
|
|
147
|
+
* canonical form:
|
|
148
|
+
*
|
|
149
|
+
* export default defineSchema({
|
|
150
|
+
* tables: {
|
|
151
|
+
* todos: {
|
|
152
|
+
* id: uuid().primaryKey().defaultRandom(),
|
|
153
|
+
* title: text().notNull(),
|
|
154
|
+
* },
|
|
155
|
+
* },
|
|
156
|
+
* });
|
|
157
|
+
*
|
|
158
|
+
* The returned value is `{ tables: { todos: { name: "todos", columns: {...} } } }`
|
|
159
|
+
* — the exact shape the runtime schema extractor parses. Per-column phantom
|
|
160
|
+
* types are preserved so `Database.tables.todos.insert({...})` is typed.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* import { defineSchema, uuid, text, timestamp } from "@palbase/backend";
|
|
164
|
+
*
|
|
165
|
+
* export default defineSchema({
|
|
166
|
+
* tables: {
|
|
167
|
+
* todos: {
|
|
168
|
+
* id: uuid().primaryKey().defaultRandom(),
|
|
169
|
+
* title: text().notNull(),
|
|
170
|
+
* done: boolean().default(false),
|
|
171
|
+
* created_at: timestamp().defaultNow(),
|
|
172
|
+
* },
|
|
173
|
+
* },
|
|
174
|
+
* });
|
|
175
|
+
*/
|
|
176
|
+
declare function defineSchema<T extends Record<string, ColumnMap>>(input: SchemaInput<T>): SchemaDef<TablesFromInput<T>>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* typed-db.ts — Task 2: TypedDB schema-derived insert/row shapes.
|
|
180
|
+
*
|
|
181
|
+
* Derives INSERT and full-row TypeScript types from a `defineSchema()` result
|
|
182
|
+
* and wraps the untyped runtime `DBClient` with a typed facade.
|
|
183
|
+
*
|
|
184
|
+
* No value-any. No `as unknown as X`. The two narrow `as` casts in
|
|
185
|
+
* `makeTypedTable` are safe because:
|
|
186
|
+
* - `data as Record<string, unknown>`: InsertShape<T> maps string keys to
|
|
187
|
+
* typed values; all value types are subsets of `unknown`, so the cast is
|
|
188
|
+
* structurally sound.
|
|
189
|
+
* - `result as RowShape<T>`: The runtime DBClient returns `Record<string,
|
|
190
|
+
* unknown>` which is the erased form of the typed row; we're narrowing back
|
|
191
|
+
* to the precise shape that the schema declared.
|
|
192
|
+
* Both casts are narrowing only (not widening) and correctness is guaranteed
|
|
193
|
+
* by the schema the caller provides.
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
/** Keys of C whose columns are required on INSERT (not nullable, no default). */
|
|
197
|
+
type RequiredKeys<C> = {
|
|
198
|
+
[K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? never : K;
|
|
199
|
+
}[keyof C];
|
|
200
|
+
/** Keys of C whose columns are optional on INSERT (nullable or has a default). */
|
|
201
|
+
type OptionalKeys<C> = {
|
|
202
|
+
[K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? K : never;
|
|
203
|
+
}[keyof C];
|
|
204
|
+
/**
|
|
205
|
+
* The TypeScript type for an INSERT payload for table `T`.
|
|
206
|
+
* - Required: columns that are NOT NULL and have no DB-level default.
|
|
207
|
+
* - Optional: columns that are nullable or carry a default.
|
|
208
|
+
*
|
|
209
|
+
* When all columns are optional, `RequiredKeys<C>` resolves to `never` and
|
|
210
|
+
* the first part becomes `{}`, which is a neutral element for `&`.
|
|
211
|
+
*/
|
|
212
|
+
type InsertShape<T extends TableDef> = {
|
|
213
|
+
[K in RequiredKeys<T["columns"]>]: ColValue<T["columns"][K]>;
|
|
214
|
+
} & {
|
|
215
|
+
[K in OptionalKeys<T["columns"]>]?: ColValue<T["columns"][K]>;
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* The TypeScript type for a full row returned by the DB for table `T`.
|
|
219
|
+
* Every column is present; nullable columns resolve to `T | null`.
|
|
220
|
+
*/
|
|
221
|
+
type RowShape<T extends TableDef> = {
|
|
222
|
+
[K in keyof T["columns"]]: ColValue<T["columns"][K]>;
|
|
223
|
+
};
|
|
224
|
+
/** A typed table accessor that mirrors the runtime DBClient surface. */
|
|
225
|
+
interface TypedTable<T extends TableDef> {
|
|
226
|
+
insert(data: InsertShape<T>): Promise<RowShape<T>>;
|
|
227
|
+
update(id: string, data: Partial<InsertShape<T>>): Promise<RowShape<T>>;
|
|
228
|
+
delete(id: string): Promise<void>;
|
|
229
|
+
findById(id: string): Promise<RowShape<T> | null>;
|
|
230
|
+
findMany(query?: Partial<RowShape<T>>): Promise<RowShape<T>[]>;
|
|
231
|
+
}
|
|
232
|
+
/** A typed DB facade covering all tables declared in schema `S`. */
|
|
233
|
+
interface TypedDB<S extends SchemaDef> {
|
|
234
|
+
tables: {
|
|
235
|
+
[K in keyof S["tables"]]: TypedTable<S["tables"][K]>;
|
|
236
|
+
};
|
|
237
|
+
transaction<T>(fn: (tx: TypedTx<S>) => Promise<T>): Promise<T>;
|
|
238
|
+
}
|
|
239
|
+
/** Transaction-scoped typed facade: same typed tables, no nested transaction. */
|
|
240
|
+
interface TypedTx<S extends SchemaDef> {
|
|
241
|
+
tables: {
|
|
242
|
+
[K in keyof S["tables"]]: TypedTable<S["tables"][K]>;
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Wraps a raw `DBClient` with the type-safe `TypedDB<S>` facade derived from
|
|
247
|
+
* the provided schema. No behavior change — all calls delegate to `raw` with
|
|
248
|
+
* the table name as a plain string.
|
|
249
|
+
*
|
|
250
|
+
* `buildTables` is the reusable factory that wraps any op-bearing client
|
|
251
|
+
* (`TxClient` — the surface shared by `DBClient` and the transaction-scoped
|
|
252
|
+
* client) into the typed tables map. It is used both for the top-level db
|
|
253
|
+
* (wrapping `raw`) and inside `transaction`, where it wraps the raw `TxClient`
|
|
254
|
+
* the runtime yields so the callback sees the same typed `.tables` API.
|
|
255
|
+
*
|
|
256
|
+
* `transaction` delegates straight to `raw.transaction`; the two narrow
|
|
257
|
+
* `as TypedTx<S>` / `as TypedDB<S>` casts are single structural narrowings
|
|
258
|
+
* from the dynamically-built tables object to the precise mapped type (TS
|
|
259
|
+
* cannot infer through `Object.keys` iteration) — see module-level doc comment.
|
|
260
|
+
*/
|
|
261
|
+
declare function makeTypedDB<S extends SchemaDef>(schema: S, raw: DBClient): TypedDB<S>;
|
|
262
|
+
/** A typed table accessor derived from one env `Tables` entry's flat shapes. */
|
|
263
|
+
interface EnvTypedTable<T extends TableTypes> {
|
|
264
|
+
insert(data: T["insert"]): Promise<T["row"]>;
|
|
265
|
+
update(id: string, data: Partial<T["insert"]>): Promise<T["row"]>;
|
|
266
|
+
delete(id: string): Promise<void>;
|
|
267
|
+
findById(id: string): Promise<T["row"] | null>;
|
|
268
|
+
findMany(query?: Partial<T["row"]>): Promise<T["row"][]>;
|
|
269
|
+
}
|
|
270
|
+
/** The `tables` map exposed on `Database`/`tx`, keyed by the env `Tables`
|
|
271
|
+
* interface. When no schema is declared `Tables` is empty, so `tables` is an
|
|
272
|
+
* empty object — accessing `.tables.foo` is then a compile error (no member). */
|
|
273
|
+
type EnvTables = {
|
|
274
|
+
[K in keyof Tables]: EnvTypedTable<Tables[K]>;
|
|
275
|
+
};
|
|
276
|
+
/** Transaction-scoped typed facade for the env-augmented surface: same typed
|
|
277
|
+
* tables, no nested transaction. */
|
|
278
|
+
interface EnvTypedTx {
|
|
279
|
+
tables: EnvTables;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The typed-by-default Database surface: the raw string-keyed `DBClient` ops
|
|
283
|
+
* PLUS a `tables` map typed against the project's generated `palbase-env.d.ts`
|
|
284
|
+
* and a `transaction` whose callback receives the typed tables.
|
|
285
|
+
*
|
|
286
|
+
* `transaction` is declared here (overriding `DBClient["transaction"]`) so the
|
|
287
|
+
* `tx` the callback receives carries the typed `.tables` API.
|
|
288
|
+
*/
|
|
289
|
+
interface EnvTypedDatabase extends Omit<DBClient, "transaction"> {
|
|
290
|
+
tables: EnvTables;
|
|
291
|
+
transaction<T>(fn: (tx: EnvTypedTx) => Promise<T>): Promise<T>;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export { ColumnBuilder as C, type EnvTypedDatabase as E, type InsertShape as I, type OnDeleteAction as O, type RowShape as R, type SchemaDef as S, type TableDef as T, type ColumnDef as a, type ColumnMap as b, type ColumnType as c, type EnvTables as d, type EnvTypedTable as e, type EnvTypedTx as f, type SchemaInput as g, type TypedDB as h, type TypedTable as i, type TypedTx as j, boolean as k, defineSchema as l, enumType as m, integer as n, jsonb as o, makeTypedDB as p, timestamp as q, text as t, uuid as u };
|