tempest-db-js 0.1.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/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/chunk-F36ZSQAN.js +1360 -0
- package/dist/chunk-F36ZSQAN.js.map +1 -0
- package/dist/index.cjs +1411 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1084 -0
- package/dist/index.d.ts +1084 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/index.cjs +1038 -0
- package/dist/migrations/index.cjs.map +1 -0
- package/dist/migrations/index.d.cts +391 -0
- package/dist/migrations/index.d.ts +391 -0
- package/dist/migrations/index.js +925 -0
- package/dist/migrations/index.js.map +1 -0
- package/package.json +74 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1084 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tempest-db-js — Phase 2 feasibility spike: the typed query builder.
|
|
3
|
+
*
|
|
4
|
+
* The builder is PURE AST + phantom types. It does not touch a database — that is
|
|
5
|
+
* Phase 4 (`session.execute`). This file proves that:
|
|
6
|
+
* - `select(User)` infers the full row type,
|
|
7
|
+
* - `select(User, ["id", "name"])` infers a `Pick` projection,
|
|
8
|
+
* - `.where(...)` / `.orderBy(...)` reject keys that are not columns,
|
|
9
|
+
* all at compile time.
|
|
10
|
+
*
|
|
11
|
+
* This is a SPIKE, not the final API.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Sort direction for ORDER BY. */
|
|
15
|
+
type SortDirection = "asc" | "desc";
|
|
16
|
+
/** One ORDER BY term. */
|
|
17
|
+
interface OrderTerm {
|
|
18
|
+
readonly column: string;
|
|
19
|
+
readonly direction: SortDirection;
|
|
20
|
+
}
|
|
21
|
+
/** Serializable AST for a SELECT. Dialects (Phase 4) compile this to SQL. */
|
|
22
|
+
interface SelectNode {
|
|
23
|
+
readonly kind: "select";
|
|
24
|
+
readonly table: string;
|
|
25
|
+
/** Projected columns, or "*" for the whole row. */
|
|
26
|
+
readonly columns: readonly string[] | "*";
|
|
27
|
+
readonly where: CondNode | undefined;
|
|
28
|
+
readonly orderBy: readonly OrderTerm[];
|
|
29
|
+
readonly limit: number | undefined;
|
|
30
|
+
readonly offset: number | undefined;
|
|
31
|
+
}
|
|
32
|
+
/** Operators valid on every column type. */
|
|
33
|
+
interface BaseOperators<T> {
|
|
34
|
+
/** Equal to. */
|
|
35
|
+
eq?: T;
|
|
36
|
+
/** Not equal to. */
|
|
37
|
+
ne?: T;
|
|
38
|
+
/** One of the given values (`IN`). */
|
|
39
|
+
in?: readonly T[];
|
|
40
|
+
/** None of the given values (`NOT IN`). */
|
|
41
|
+
notIn?: readonly T[];
|
|
42
|
+
/** `IS NULL` (true) / `IS NOT NULL` (false). */
|
|
43
|
+
isNull?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** Extra operators for ordered types (numbers, bigint, dates). */
|
|
46
|
+
interface OrderedOperators<T> extends BaseOperators<T> {
|
|
47
|
+
/** Greater than. */
|
|
48
|
+
gt?: T;
|
|
49
|
+
/** Greater than or equal. */
|
|
50
|
+
gte?: T;
|
|
51
|
+
/** Less than. */
|
|
52
|
+
lt?: T;
|
|
53
|
+
/** Less than or equal. */
|
|
54
|
+
lte?: T;
|
|
55
|
+
/** Inclusive range `BETWEEN lo AND hi`. */
|
|
56
|
+
between?: readonly [T, T];
|
|
57
|
+
}
|
|
58
|
+
/** Extra operators for string-like types. */
|
|
59
|
+
interface StringOperators<T> extends BaseOperators<T> {
|
|
60
|
+
/** `LIKE` pattern (case-sensitive). */
|
|
61
|
+
like?: string;
|
|
62
|
+
/** `ILIKE` pattern (case-insensitive). */
|
|
63
|
+
ilike?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The operator object allowed for a column of (non-null) type `T`:
|
|
67
|
+
* - `string` → equality, `in`, `like`/`ilike`
|
|
68
|
+
* - `number` / `bigint` / `Date` → equality, `in`, ordered comparisons, `between`
|
|
69
|
+
* - `boolean` → equality, `isNull`
|
|
70
|
+
* - anything else (json/blob) → equality and `in` only
|
|
71
|
+
*/
|
|
72
|
+
type OperatorsFor<T> = [T] extends [string] ? StringOperators<T> : [T] extends [number] ? OrderedOperators<T> : [T] extends [bigint] ? OrderedOperators<T> : [T] extends [Date] ? OrderedOperators<T> : [T] extends [boolean] ? BaseOperators<T> : BaseOperators<T>;
|
|
73
|
+
/**
|
|
74
|
+
* `where` shape: each key must be a real column; each value accepts either a
|
|
75
|
+
* bare value (shorthand for `eq`) or an operator object restricted to operators
|
|
76
|
+
* valid for that column's type. A `like` on a `number` column, or `gt` on a
|
|
77
|
+
* `string`, is a compile error.
|
|
78
|
+
*/
|
|
79
|
+
type WhereInput<Row = Record<string, unknown>> = {
|
|
80
|
+
[K in keyof Row]?: Row[K] | OperatorsFor<NonNullable<Row[K]>>;
|
|
81
|
+
};
|
|
82
|
+
/** The full set of operator keys, for the dialect compiler to recognize. */
|
|
83
|
+
declare const OPERATORS: readonly ["eq", "ne", "gt", "gte", "lt", "lte", "like", "ilike", "in", "notIn", "between", "isNull"];
|
|
84
|
+
/** One supported operator name. */
|
|
85
|
+
type Operator = (typeof OPERATORS)[number];
|
|
86
|
+
/**
|
|
87
|
+
* Immutable, chainable SELECT builder.
|
|
88
|
+
*
|
|
89
|
+
* @typeParam Full - the complete row type (constrains where/orderBy keys).
|
|
90
|
+
* @typeParam Proj - the projected result type returned on execution.
|
|
91
|
+
*/
|
|
92
|
+
declare class SelectBuilder<Full, Proj = Full> {
|
|
93
|
+
readonly node: SelectNode;
|
|
94
|
+
/** The source model, used to coerce rows on execution. */
|
|
95
|
+
readonly source: ModelClass;
|
|
96
|
+
/** Phantom: the result element type, read only by the type system. */
|
|
97
|
+
readonly __row: Proj;
|
|
98
|
+
constructor(node: SelectNode,
|
|
99
|
+
/** The source model, used to coerce rows on execution. */
|
|
100
|
+
source: ModelClass);
|
|
101
|
+
private with;
|
|
102
|
+
/** Add a WHERE filter: the object form (keys typed) or an `and`/`or`/`not`. */
|
|
103
|
+
where(input: WhereInput<Full> | Condition): SelectBuilder<Full, Proj>;
|
|
104
|
+
/** Order by a column of `Full`. */
|
|
105
|
+
orderBy(column: keyof Full & string, direction?: SortDirection): SelectBuilder<Full, Proj>;
|
|
106
|
+
/** Limit the number of rows. */
|
|
107
|
+
limit(n: number): SelectBuilder<Full, Proj>;
|
|
108
|
+
/** Skip the first `n` rows. */
|
|
109
|
+
offset(n: number): SelectBuilder<Full, Proj>;
|
|
110
|
+
}
|
|
111
|
+
/** Build a SELECT over every column of the model. */
|
|
112
|
+
declare function select<C extends ModelClass>(model: C): SelectBuilder<InferModel<C>, InferModel<C>>;
|
|
113
|
+
/** Build a SELECT projecting only the given columns. */
|
|
114
|
+
declare function select<C extends ModelClass, K extends keyof InferModel<C> & string>(model: C, columns: readonly K[]): SelectBuilder<InferModel<C>, Pick<InferModel<C>, K>>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* tempest-db-js — logical combinators for `where` (and/or/not).
|
|
118
|
+
*
|
|
119
|
+
* A `where` value is either the object form (an implicit AND of its fields) or a
|
|
120
|
+
* `Condition` built with `and`/`or`/`not`. Both normalize to a `CondNode` tree
|
|
121
|
+
* that every builder stores and the dialect compiles recursively — so select,
|
|
122
|
+
* update, delete and join all share one condition language.
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/** Field-map condition (implicit AND of its entries). */
|
|
126
|
+
interface CondFields {
|
|
127
|
+
readonly kind: "fields";
|
|
128
|
+
readonly fields: Record<string, unknown>;
|
|
129
|
+
}
|
|
130
|
+
/** Logical condition nodes. */
|
|
131
|
+
type CondNode = CondFields | {
|
|
132
|
+
readonly kind: "and";
|
|
133
|
+
readonly parts: readonly CondNode[];
|
|
134
|
+
} | {
|
|
135
|
+
readonly kind: "or";
|
|
136
|
+
readonly parts: readonly CondNode[];
|
|
137
|
+
} | {
|
|
138
|
+
readonly kind: "not";
|
|
139
|
+
readonly part: CondNode;
|
|
140
|
+
};
|
|
141
|
+
declare const CONDITION: unique symbol;
|
|
142
|
+
/** A composed condition produced by `and`/`or`/`not`. */
|
|
143
|
+
interface Condition {
|
|
144
|
+
readonly [CONDITION]: true;
|
|
145
|
+
readonly node: CondNode;
|
|
146
|
+
}
|
|
147
|
+
/** Runtime guard: is this value a composed `Condition`? */
|
|
148
|
+
declare function isCondition(value: unknown): value is Condition;
|
|
149
|
+
/** Normalize a where argument (object form or `Condition`) to a `CondNode`. */
|
|
150
|
+
declare function toCondNode(input: Condition | Record<string, unknown>): CondNode;
|
|
151
|
+
/**
|
|
152
|
+
* A `where` argument: the object form (keys typed against `Row`) or a `Condition`.
|
|
153
|
+
*
|
|
154
|
+
* @typeParam Row - the row type whose columns the field keys are checked against.
|
|
155
|
+
* Defaults to a permissive shape; pass it explicitly (e.g. `or<UserRow>(...)`)
|
|
156
|
+
* for full key + operator checking inside combinators.
|
|
157
|
+
*/
|
|
158
|
+
type WhereArg<Row = Record<string, unknown>> = WhereInput<Row> | Condition;
|
|
159
|
+
/** Combine conditions with `AND`. */
|
|
160
|
+
declare function and<Row = Record<string, unknown>>(...inputs: WhereArg<NoInfer<Row>>[]): Condition;
|
|
161
|
+
/** Combine conditions with `OR`. */
|
|
162
|
+
declare function or<Row = Record<string, unknown>>(...inputs: WhereArg<NoInfer<Row>>[]): Condition;
|
|
163
|
+
/** Negate a condition with `NOT`. */
|
|
164
|
+
declare function not<Row = Record<string, unknown>>(input: WhereArg<NoInfer<Row>>): Condition;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* tempest-db-js — Phase 2: typed INSERT / UPDATE / DELETE builders.
|
|
168
|
+
*
|
|
169
|
+
* Like `select`, these are PURE AST + phantom types — no database access. They
|
|
170
|
+
* are executed in Phase 4 via `session.execute`.
|
|
171
|
+
*
|
|
172
|
+
* Safety rule: UPDATE and DELETE start in an *unguarded* type state. A builder
|
|
173
|
+
* only becomes executable once it has a `.where(...)` clause or an explicit
|
|
174
|
+
* `.unguarded()` opt-in. Phase 4's `session.execute` will accept only guarded
|
|
175
|
+
* builders, making an accidental full-table write a compile error.
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
/** Columns to return from a mutation, or "*" for the whole row. */
|
|
179
|
+
type Returning = readonly string[] | "*" | null;
|
|
180
|
+
/** Serializable AST for an INSERT. */
|
|
181
|
+
interface InsertNode {
|
|
182
|
+
readonly kind: "insert";
|
|
183
|
+
readonly table: string;
|
|
184
|
+
readonly values: readonly Record<string, unknown>[];
|
|
185
|
+
readonly returning: Returning;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* INSERT builder.
|
|
189
|
+
*
|
|
190
|
+
* @typeParam Full - the complete row type (for `returning`).
|
|
191
|
+
* @typeParam Ins - the insert shape (defaults/PK optional).
|
|
192
|
+
* @typeParam Ret - execution result: `number` (rows affected) until `returning`.
|
|
193
|
+
*/
|
|
194
|
+
declare class InsertBuilder<Full, Ins, Ret = number> {
|
|
195
|
+
readonly node: InsertNode;
|
|
196
|
+
/** The source model, used to coerce returned rows on execution. */
|
|
197
|
+
readonly source: ModelClass;
|
|
198
|
+
readonly __row: Ret;
|
|
199
|
+
constructor(node: InsertNode,
|
|
200
|
+
/** The source model, used to coerce returned rows on execution. */
|
|
201
|
+
source: ModelClass);
|
|
202
|
+
private with;
|
|
203
|
+
/** Provide one row or many rows to insert, typed by the insert shape. */
|
|
204
|
+
values(rows: Ins | readonly Ins[]): InsertBuilder<Full, Ins, Ret>;
|
|
205
|
+
/** Return the full inserted row(s). */
|
|
206
|
+
returning(): InsertBuilder<Full, Ins, Full>;
|
|
207
|
+
/** Return only the given columns of the inserted row(s). */
|
|
208
|
+
returning<K extends keyof Full & string>(columns: readonly K[]): InsertBuilder<Full, Ins, Pick<Full, K>>;
|
|
209
|
+
}
|
|
210
|
+
/** Build an INSERT into the model's table. */
|
|
211
|
+
declare function insert<C extends ModelClass>(model: C): InsertBuilder<InferModel<C>, InferInsert<C>>;
|
|
212
|
+
/** Serializable AST for an UPDATE. */
|
|
213
|
+
interface UpdateNode {
|
|
214
|
+
readonly kind: "update";
|
|
215
|
+
readonly table: string;
|
|
216
|
+
readonly set: Record<string, unknown>;
|
|
217
|
+
readonly where: CondNode | undefined;
|
|
218
|
+
/** True once a where-clause or explicit opt-in makes the write safe. */
|
|
219
|
+
readonly guarded: boolean;
|
|
220
|
+
readonly returning: Returning;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* UPDATE builder.
|
|
224
|
+
*
|
|
225
|
+
* @typeParam Full - the complete row type.
|
|
226
|
+
* @typeParam Guarded - `true` once safe to execute (has where or opted out).
|
|
227
|
+
* @typeParam Ret - execution result type.
|
|
228
|
+
*/
|
|
229
|
+
declare class UpdateBuilder<Full, Guarded extends boolean, Ret = number> {
|
|
230
|
+
readonly node: UpdateNode;
|
|
231
|
+
/** The source model, used to coerce returned rows on execution. */
|
|
232
|
+
readonly source: ModelClass;
|
|
233
|
+
readonly __row: Ret;
|
|
234
|
+
readonly __guarded: Guarded;
|
|
235
|
+
constructor(node: UpdateNode,
|
|
236
|
+
/** The source model, used to coerce returned rows on execution. */
|
|
237
|
+
source: ModelClass);
|
|
238
|
+
private with;
|
|
239
|
+
/** The columns to write. Partial — only the given columns change. */
|
|
240
|
+
set(values: Partial<Full>): UpdateBuilder<Full, Guarded, Ret>;
|
|
241
|
+
/** Restrict the rows to update. Marks the builder safe to execute. */
|
|
242
|
+
where(input: WhereInput<Full> | Condition): UpdateBuilder<Full, true, Ret>;
|
|
243
|
+
/** Explicit opt-in to update EVERY row. Use deliberately. */
|
|
244
|
+
unguarded(): UpdateBuilder<Full, true, Ret>;
|
|
245
|
+
/** Return the full updated row(s). */
|
|
246
|
+
returning(): UpdateBuilder<Full, Guarded, Full>;
|
|
247
|
+
/** Return only the given columns of the updated row(s). */
|
|
248
|
+
returning<K extends keyof Full & string>(columns: readonly K[]): UpdateBuilder<Full, Guarded, Pick<Full, K>>;
|
|
249
|
+
}
|
|
250
|
+
/** Build an UPDATE on the model's table. Starts unguarded. */
|
|
251
|
+
declare function update<C extends ModelClass>(model: C): UpdateBuilder<InferModel<C>, false>;
|
|
252
|
+
/** Serializable AST for a DELETE. */
|
|
253
|
+
interface DeleteNode {
|
|
254
|
+
readonly kind: "delete";
|
|
255
|
+
readonly table: string;
|
|
256
|
+
readonly where: CondNode | undefined;
|
|
257
|
+
readonly guarded: boolean;
|
|
258
|
+
readonly returning: Returning;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* DELETE builder. Starts unguarded — same safety rule as UPDATE.
|
|
262
|
+
*
|
|
263
|
+
* @typeParam Full - the complete row type.
|
|
264
|
+
* @typeParam Guarded - `true` once safe to execute.
|
|
265
|
+
* @typeParam Ret - execution result type.
|
|
266
|
+
*/
|
|
267
|
+
declare class DeleteBuilder<Full, Guarded extends boolean, Ret = number> {
|
|
268
|
+
readonly node: DeleteNode;
|
|
269
|
+
/** The source model, used to coerce returned rows on execution. */
|
|
270
|
+
readonly source: ModelClass;
|
|
271
|
+
readonly __row: Ret;
|
|
272
|
+
readonly __guarded: Guarded;
|
|
273
|
+
constructor(node: DeleteNode,
|
|
274
|
+
/** The source model, used to coerce returned rows on execution. */
|
|
275
|
+
source: ModelClass);
|
|
276
|
+
private with;
|
|
277
|
+
/** Restrict the rows to delete. Marks the builder safe to execute. */
|
|
278
|
+
where(input: WhereInput<Full> | Condition): DeleteBuilder<Full, true, Ret>;
|
|
279
|
+
/** Explicit opt-in to delete EVERY row. Use deliberately. */
|
|
280
|
+
unguarded(): DeleteBuilder<Full, true, Ret>;
|
|
281
|
+
/** Return the full deleted row(s). */
|
|
282
|
+
returning(): DeleteBuilder<Full, Guarded, Full>;
|
|
283
|
+
/** Return only the given columns of the deleted row(s). */
|
|
284
|
+
returning<K extends keyof Full & string>(columns: readonly K[]): DeleteBuilder<Full, Guarded, Pick<Full, K>>;
|
|
285
|
+
}
|
|
286
|
+
/** Build a DELETE on the model's table. Starts unguarded. */
|
|
287
|
+
declare function del<C extends ModelClass>(model: C): DeleteBuilder<InferModel<C>, false>;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* tempest-db-js — database URL parsing & dialect detection.
|
|
291
|
+
*
|
|
292
|
+
* Mirrors SQLAlchemy's `make_url`: a single connection string identifies the
|
|
293
|
+
* dialect (and optional driver), so switching databases is just swapping the
|
|
294
|
+
* URL — `sqlite://./app.db` ↔ `postgresql://user:pass@host/db`.
|
|
295
|
+
*
|
|
296
|
+
* The driver suffix (`postgresql+pg`, `sqlite+better-sqlite3`, or SQLAlchemy's
|
|
297
|
+
* async flavors like `postgresql+asyncpg`) is parsed out and ignored for dialect
|
|
298
|
+
* detection, so URLs copied from a Python service still work here.
|
|
299
|
+
*/
|
|
300
|
+
/** A database dialect tempest-db-js can target. */
|
|
301
|
+
type Dialect = "sqlite" | "postgresql";
|
|
302
|
+
/** A parsed database URL, dialect-neutral. */
|
|
303
|
+
interface ParsedDatabaseUrl {
|
|
304
|
+
/** The detected dialect. */
|
|
305
|
+
readonly dialect: Dialect;
|
|
306
|
+
/** Driver after the `+` in the scheme (e.g. `better-sqlite3`), or `null`. */
|
|
307
|
+
readonly driver: string | null;
|
|
308
|
+
/** Host (PostgreSQL), or `null` for SQLite. */
|
|
309
|
+
readonly host: string | null;
|
|
310
|
+
/** Port, or `null`. */
|
|
311
|
+
readonly port: number | null;
|
|
312
|
+
/** Username, or `null`. */
|
|
313
|
+
readonly user: string | null;
|
|
314
|
+
/** Password, or `null`. */
|
|
315
|
+
readonly password: string | null;
|
|
316
|
+
/** Database name (PostgreSQL) or file path (SQLite). `:memory:` for in-memory. */
|
|
317
|
+
readonly database: string | null;
|
|
318
|
+
/** Extra query-string options (`?sslmode=require`). */
|
|
319
|
+
readonly options: Readonly<Record<string, string>>;
|
|
320
|
+
/** The original URL, untouched. */
|
|
321
|
+
readonly raw: string;
|
|
322
|
+
}
|
|
323
|
+
/** Raised when a URL cannot be parsed or its dialect is unsupported. */
|
|
324
|
+
declare class InvalidDatabaseUrl extends Error {
|
|
325
|
+
constructor(url: string, reason: string);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Parse a database URL into its dialect and connection parts.
|
|
329
|
+
*
|
|
330
|
+
* @param url A connection string, e.g. `"postgresql://app:app@localhost/app"`
|
|
331
|
+
* or `"sqlite:///app.db"`. An async driver suffix (`+asyncpg`, `+aiosqlite`)
|
|
332
|
+
* is accepted and ignored for dialect detection.
|
|
333
|
+
* @returns The parsed, dialect-neutral connection descriptor.
|
|
334
|
+
* @throws InvalidDatabaseUrl When the URL has no scheme or an unknown dialect.
|
|
335
|
+
*/
|
|
336
|
+
declare function parseDatabaseUrl(url: string): ParsedDatabaseUrl;
|
|
337
|
+
/** Detect just the dialect of a URL, ignoring the rest. */
|
|
338
|
+
declare function detectDialect(url: string): Dialect;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* tempest-db-js — row (de)serialization, à la Python's `model_dump` / `model_validate`.
|
|
342
|
+
*
|
|
343
|
+
* Rows in tempest-db-js are plain inferred objects. This module converts between three
|
|
344
|
+
* representations, coercing each field by its column type:
|
|
345
|
+
*
|
|
346
|
+
* - **Row** — native JS values (`Date`, `bigint`, `Uint8Array`, parsed JSON).
|
|
347
|
+
* - **Dict** — a plain object of native values, restricted to known columns.
|
|
348
|
+
* - **JSON** — a JSON-safe object (`Date` → ISO string, `bigint` → string,
|
|
349
|
+
* `Uint8Array` → base64), ready for `JSON.stringify`.
|
|
350
|
+
*
|
|
351
|
+
* `fromDict` rebuilds a validated Row from arbitrary input (e.g. an API payload),
|
|
352
|
+
* coercing strings back to `Date`/`bigint`/`Uint8Array` and validating that
|
|
353
|
+
* required columns are present.
|
|
354
|
+
*/
|
|
355
|
+
|
|
356
|
+
/** Raised when `fromDict` input fails validation against the model. */
|
|
357
|
+
declare class ValidationError extends Error {
|
|
358
|
+
readonly table: string;
|
|
359
|
+
readonly issues: readonly string[];
|
|
360
|
+
constructor(table: string, issues: readonly string[]);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Convert a row to a plain dict of native values, restricted to known columns.
|
|
364
|
+
* Strips any non-column properties; keeps `Date`/`bigint`/`Uint8Array` as-is.
|
|
365
|
+
*
|
|
366
|
+
* @param model The model class.
|
|
367
|
+
* @param row The row object.
|
|
368
|
+
* @returns A plain object with one entry per column.
|
|
369
|
+
*/
|
|
370
|
+
declare function toDict<C extends ModelClass>(model: C, row: InferModel<C>): Record<string, unknown>;
|
|
371
|
+
/**
|
|
372
|
+
* Convert a row to a JSON-safe object: `Date` → ISO string, `bigint` → string,
|
|
373
|
+
* `Uint8Array` → base64. Ready to hand to `JSON.stringify`.
|
|
374
|
+
*
|
|
375
|
+
* @param model The model class.
|
|
376
|
+
* @param row The row object.
|
|
377
|
+
* @returns A JSON-safe object with one entry per column.
|
|
378
|
+
*/
|
|
379
|
+
declare function toJSON<C extends ModelClass>(model: C, row: InferModel<C>): Record<string, unknown>;
|
|
380
|
+
/** Convenience: `toJSON` then `JSON.stringify`. */
|
|
381
|
+
declare function stringify<C extends ModelClass>(model: C, row: InferModel<C>): string;
|
|
382
|
+
/**
|
|
383
|
+
* Build a validated row from an arbitrary dict/JSON object (e.g. an API body).
|
|
384
|
+
*
|
|
385
|
+
* Each known column is coerced from the input to its native type (strings back
|
|
386
|
+
* to `Date`/`bigint`/`Uint8Array`, JSON strings parsed). A column that is
|
|
387
|
+
* `notNull`, has no default, and is missing/null in the input is a validation
|
|
388
|
+
* error. Unknown keys in the input are ignored.
|
|
389
|
+
*
|
|
390
|
+
* @param model The model class.
|
|
391
|
+
* @param data The input object (parsed JSON or a plain dict).
|
|
392
|
+
* @returns A typed row.
|
|
393
|
+
* @throws ValidationError When a required column is absent.
|
|
394
|
+
*/
|
|
395
|
+
declare function fromDict<C extends ModelClass>(model: C, data: Record<string, unknown>): InferModel<C>;
|
|
396
|
+
/** Parse a JSON string then build a validated row. */
|
|
397
|
+
declare function parse<C extends ModelClass>(model: C, json: string): InferModel<C>;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* tempest-db-js — Phase 5: typed joins with composite result types.
|
|
401
|
+
*
|
|
402
|
+
* `join(User, "user").innerJoin(Order, "order", { "user.id": "order.userId" })`
|
|
403
|
+
* yields rows shaped `{ user: UserRow; order: OrderRow }`. A `leftJoin` makes the
|
|
404
|
+
* joined side nullable (`OrderRow | null`), matching SQL outer-join semantics.
|
|
405
|
+
*
|
|
406
|
+
* Columns are aliased in SQL (`"user"."id" AS "user.id"`) so a flat driver row is
|
|
407
|
+
* split back into one nested object per source, each coerced by its model.
|
|
408
|
+
*/
|
|
409
|
+
|
|
410
|
+
/** One joined table. */
|
|
411
|
+
interface JoinClause {
|
|
412
|
+
readonly kind: "inner" | "left";
|
|
413
|
+
readonly table: string;
|
|
414
|
+
readonly alias: string;
|
|
415
|
+
/** Equality pairs of qualified columns: `["user.id", "order.userId"]`. */
|
|
416
|
+
readonly on: readonly (readonly [string, string])[];
|
|
417
|
+
}
|
|
418
|
+
/** A selected column, qualified by source alias. */
|
|
419
|
+
interface JoinSelection {
|
|
420
|
+
readonly alias: string;
|
|
421
|
+
readonly column: string;
|
|
422
|
+
}
|
|
423
|
+
/** `where` filter for a join: keys are `alias.column` refs (object form). */
|
|
424
|
+
type JoinWhereInput = Record<string, unknown>;
|
|
425
|
+
/** Serializable AST for a multi-table SELECT. */
|
|
426
|
+
interface JoinNode {
|
|
427
|
+
readonly kind: "join_select";
|
|
428
|
+
readonly base: {
|
|
429
|
+
readonly table: string;
|
|
430
|
+
readonly alias: string;
|
|
431
|
+
};
|
|
432
|
+
readonly joins: readonly JoinClause[];
|
|
433
|
+
readonly selections: readonly JoinSelection[];
|
|
434
|
+
readonly where: CondNode | undefined;
|
|
435
|
+
readonly orderBy: readonly {
|
|
436
|
+
readonly ref: string;
|
|
437
|
+
readonly direction: SortDirection;
|
|
438
|
+
}[];
|
|
439
|
+
readonly limit: number | undefined;
|
|
440
|
+
readonly offset: number | undefined;
|
|
441
|
+
}
|
|
442
|
+
/** A map of source alias → its (possibly nullable) row type. */
|
|
443
|
+
type Sources = Record<string, object | null>;
|
|
444
|
+
/** Every `alias.column` reference across the current sources. */
|
|
445
|
+
type ColRef<S extends Sources> = {
|
|
446
|
+
[A in keyof S]: `${A & string}.${keyof NonNullable<S[A]> & string}`;
|
|
447
|
+
}[keyof S];
|
|
448
|
+
/** Valid `alias.column` references for a newly joined model. */
|
|
449
|
+
type RightRef<A extends string, C extends ModelClass> = `${A}.${keyof InferModel<C> & string}`;
|
|
450
|
+
/** The `on` condition: map existing-source refs to new-table refs (equality). */
|
|
451
|
+
type JoinOn<S extends Sources, A extends string, C extends ModelClass> = Partial<Record<ColRef<S>, RightRef<A, C>>>;
|
|
452
|
+
/**
|
|
453
|
+
* Immutable, chainable multi-table SELECT builder.
|
|
454
|
+
*
|
|
455
|
+
* @typeParam S - the accumulated sources (alias → row type; nullable for left joins).
|
|
456
|
+
*/
|
|
457
|
+
declare class JoinBuilder<S extends Sources> {
|
|
458
|
+
readonly node: JoinNode;
|
|
459
|
+
/** Source models keyed by alias, for SQL expansion and row coercion. */
|
|
460
|
+
readonly sources: Readonly<Record<string, ModelClass>>;
|
|
461
|
+
/** Phantom: the composite result row type. */
|
|
462
|
+
readonly __row: {
|
|
463
|
+
[A in keyof S]: S[A];
|
|
464
|
+
};
|
|
465
|
+
constructor(node: JoinNode,
|
|
466
|
+
/** Source models keyed by alias, for SQL expansion and row coercion. */
|
|
467
|
+
sources: Readonly<Record<string, ModelClass>>);
|
|
468
|
+
private add;
|
|
469
|
+
private clause;
|
|
470
|
+
/** Inner join another model under `alias`. */
|
|
471
|
+
innerJoin<C extends ModelClass, A extends string>(model: C, alias: A, on: JoinOn<S, A, C>): JoinBuilder<S & {
|
|
472
|
+
[K in A]: InferModel<C>;
|
|
473
|
+
}>;
|
|
474
|
+
/** Left (outer) join another model under `alias` — its side becomes nullable. */
|
|
475
|
+
leftJoin<C extends ModelClass, A extends string>(model: C, alias: A, on: JoinOn<S, A, C>): JoinBuilder<S & {
|
|
476
|
+
[K in A]: InferModel<C> | null;
|
|
477
|
+
}>;
|
|
478
|
+
/** Filter by `alias.column` references (object form) or an `and`/`or`/`not`. */
|
|
479
|
+
where(input: Partial<Record<ColRef<S>, unknown>> | Condition): JoinBuilder<S>;
|
|
480
|
+
/** Order by an `alias.column` reference. */
|
|
481
|
+
orderBy(ref: ColRef<S>, direction?: SortDirection): JoinBuilder<S>;
|
|
482
|
+
limit(n: number): JoinBuilder<S>;
|
|
483
|
+
offset(n: number): JoinBuilder<S>;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Start a multi-table query from a base model under `alias`.
|
|
487
|
+
*
|
|
488
|
+
* @param model The base model.
|
|
489
|
+
* @param alias The alias to key the base table's rows under in the result.
|
|
490
|
+
* @returns A `JoinBuilder` with the base source registered.
|
|
491
|
+
*/
|
|
492
|
+
declare function join<C extends ModelClass, A extends string>(model: C, alias: A): JoinBuilder<{
|
|
493
|
+
[K in A]: InferModel<C>;
|
|
494
|
+
}>;
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* tempest-db-js — Phase 4a: dialect SQL compilation.
|
|
498
|
+
*
|
|
499
|
+
* Turns the dialect-neutral AST (`SelectNode`, `InsertNode`, `UpdateNode`,
|
|
500
|
+
* `DeleteNode` from Phases 1-2) into `{ sql, params }`. This is the ONLY place
|
|
501
|
+
* SQL is produced — always parameterized (`?` for SQLite, `$1` for PostgreSQL),
|
|
502
|
+
* never string interpolation, so it is injection-safe by construction.
|
|
503
|
+
*
|
|
504
|
+
* It does NOT touch a database — execution is Phase 4b (`session.execute`).
|
|
505
|
+
*/
|
|
506
|
+
|
|
507
|
+
/** A compiled, parameterized statement ready to hand to a driver. */
|
|
508
|
+
interface CompiledQuery {
|
|
509
|
+
readonly sql: string;
|
|
510
|
+
readonly params: readonly unknown[];
|
|
511
|
+
}
|
|
512
|
+
/** Any compilable AST node. */
|
|
513
|
+
type QueryNode = SelectNode | InsertNode | UpdateNode | DeleteNode | JoinNode;
|
|
514
|
+
/**
|
|
515
|
+
* Base SQL compiler shared by every dialect. Subclasses customize only what
|
|
516
|
+
* actually differs between databases (placeholder syntax, `ILIKE` support).
|
|
517
|
+
*/
|
|
518
|
+
declare abstract class BaseDialect {
|
|
519
|
+
abstract readonly name: "sqlite" | "postgresql";
|
|
520
|
+
/** Render the n-th (1-based) placeholder. */
|
|
521
|
+
protected abstract placeholder(index: number): string;
|
|
522
|
+
/** Render a case-insensitive LIKE for the active dialect. */
|
|
523
|
+
protected abstract ilike(column: string, param: string): string;
|
|
524
|
+
/** Quote an identifier (column/table) for the active dialect. */
|
|
525
|
+
protected quoteId(name: string): string;
|
|
526
|
+
/** Compile any node to `{ sql, params }`. */
|
|
527
|
+
compile(node: QueryNode): CompiledQuery;
|
|
528
|
+
/** Render a qualified `alias.column` ref as `"alias"."column"`. */
|
|
529
|
+
private qualify;
|
|
530
|
+
private compileSelect;
|
|
531
|
+
private compileInsert;
|
|
532
|
+
private compileUpdate;
|
|
533
|
+
private compileDelete;
|
|
534
|
+
private compileJoin;
|
|
535
|
+
private compileReturning;
|
|
536
|
+
/**
|
|
537
|
+
* Compile a condition tree (fields / and / or / not) to SQL. `idFor` renders a
|
|
538
|
+
* key to a quoted identifier — `quoteId` for single-table, `qualify` for joins —
|
|
539
|
+
* so select/update/delete/join all share this one compiler.
|
|
540
|
+
*/
|
|
541
|
+
private compileCondition;
|
|
542
|
+
private compileOperator;
|
|
543
|
+
private compileIn;
|
|
544
|
+
}
|
|
545
|
+
/** SQLite dialect: `?` placeholders; `ILIKE` falls back to `LIKE` (ASCII-insensitive). */
|
|
546
|
+
declare class SqliteDialect extends BaseDialect {
|
|
547
|
+
readonly name: "sqlite";
|
|
548
|
+
protected placeholder(): string;
|
|
549
|
+
protected ilike(column: string, param: string): string;
|
|
550
|
+
}
|
|
551
|
+
/** PostgreSQL dialect: `$1` placeholders; native `ILIKE`. */
|
|
552
|
+
declare class PostgresDialect extends BaseDialect {
|
|
553
|
+
readonly name: "postgresql";
|
|
554
|
+
protected placeholder(index: number): string;
|
|
555
|
+
protected ilike(column: string, param: string): string;
|
|
556
|
+
}
|
|
557
|
+
/** Get a dialect instance by name. */
|
|
558
|
+
declare function getDialect(name: "sqlite" | "postgresql"): BaseDialect;
|
|
559
|
+
|
|
560
|
+
/** The outcome of running one statement. */
|
|
561
|
+
interface DriverResult {
|
|
562
|
+
/** Returned rows (SELECT or `RETURNING`); empty otherwise. */
|
|
563
|
+
readonly rows: Record<string, unknown>[];
|
|
564
|
+
/** Rows affected by an INSERT/UPDATE/DELETE. */
|
|
565
|
+
readonly changes: number;
|
|
566
|
+
}
|
|
567
|
+
/** A synchronous driver (SQLite). */
|
|
568
|
+
interface SyncDriver {
|
|
569
|
+
execute(sql: string, params: readonly unknown[]): DriverResult;
|
|
570
|
+
/** Lazily iterate rows (for `.stream()`), if the driver supports it. */
|
|
571
|
+
iterate?(sql: string, params: readonly unknown[]): IterableIterator<Record<string, unknown>>;
|
|
572
|
+
close(): void;
|
|
573
|
+
}
|
|
574
|
+
/** An asynchronous driver (PostgreSQL, or an async-wrapped SQLite). */
|
|
575
|
+
interface AsyncDriver {
|
|
576
|
+
execute(sql: string, params: readonly unknown[]): Promise<DriverResult>;
|
|
577
|
+
/** Lazily iterate rows (for `.stream()`), if the driver supports it. */
|
|
578
|
+
iterate?(sql: string, params: readonly unknown[]): AsyncIterableIterator<Record<string, unknown>>;
|
|
579
|
+
close(): Promise<void>;
|
|
580
|
+
}
|
|
581
|
+
/** SQLite driver backed by Node's built-in `node:sqlite` (zero install). */
|
|
582
|
+
declare class NodeSqliteDriver implements SyncDriver {
|
|
583
|
+
private readonly db;
|
|
584
|
+
constructor(database: any);
|
|
585
|
+
/** Open a `node:sqlite` database at the given path (or `:memory:`). */
|
|
586
|
+
static open(path: string): NodeSqliteDriver;
|
|
587
|
+
execute(sql: string, params: readonly unknown[]): DriverResult;
|
|
588
|
+
iterate(sql: string, params: readonly unknown[]): IterableIterator<Record<string, unknown>>;
|
|
589
|
+
close(): void;
|
|
590
|
+
}
|
|
591
|
+
type AnySelect = SelectBuilder<any, any>;
|
|
592
|
+
type AnyInsert = InsertBuilder<any, any, any>;
|
|
593
|
+
type GuardedUpdate = UpdateBuilder<any, true, any>;
|
|
594
|
+
type GuardedDelete = DeleteBuilder<any, true, any>;
|
|
595
|
+
type AnyJoin = JoinBuilder<any>;
|
|
596
|
+
/**
|
|
597
|
+
* A builder that is safe to execute. UPDATE/DELETE are accepted only once
|
|
598
|
+
* guarded (after `.where()` or `.unguarded()`) — an unguarded full-table write
|
|
599
|
+
* is a compile error at the execution boundary.
|
|
600
|
+
*/
|
|
601
|
+
type Executable = AnySelect | AnyInsert | GuardedUpdate | GuardedDelete | AnyJoin;
|
|
602
|
+
/** The element type a builder yields on execution. */
|
|
603
|
+
type RowOf<B> = B extends {
|
|
604
|
+
readonly __row: infer R;
|
|
605
|
+
} ? R : never;
|
|
606
|
+
/** Raised by `.one()` when the row count is not exactly one. */
|
|
607
|
+
declare class NoResultError extends Error {
|
|
608
|
+
constructor(message: string);
|
|
609
|
+
}
|
|
610
|
+
/** Synchronous result view over already-fetched rows. */
|
|
611
|
+
declare class SyncResult<Row> {
|
|
612
|
+
private readonly rows;
|
|
613
|
+
private readonly changes;
|
|
614
|
+
constructor(rows: Row[], changes: number);
|
|
615
|
+
all(): Row[];
|
|
616
|
+
first(): Row | null;
|
|
617
|
+
one(): Row;
|
|
618
|
+
oneOrNull(): Row | null;
|
|
619
|
+
scalar(): unknown;
|
|
620
|
+
scalars(): unknown[];
|
|
621
|
+
rowsAffected(): number;
|
|
622
|
+
}
|
|
623
|
+
/** Asynchronous result view (terminals return Promises). */
|
|
624
|
+
declare class AsyncResult<Row> {
|
|
625
|
+
private readonly inner;
|
|
626
|
+
constructor(inner: Promise<SyncResult<Row>>);
|
|
627
|
+
all(): Promise<Row[]>;
|
|
628
|
+
first(): Promise<Row | null>;
|
|
629
|
+
one(): Promise<Row>;
|
|
630
|
+
oneOrNull(): Promise<Row | null>;
|
|
631
|
+
scalar(): Promise<unknown>;
|
|
632
|
+
scalars(): Promise<unknown[]>;
|
|
633
|
+
rowsAffected(): Promise<number>;
|
|
634
|
+
}
|
|
635
|
+
/** A synchronous unit of work (SQLite). */
|
|
636
|
+
declare class SyncSession {
|
|
637
|
+
private readonly driver;
|
|
638
|
+
private readonly dialect;
|
|
639
|
+
constructor(driver: SyncDriver, dialect: BaseDialect);
|
|
640
|
+
/** Compile, run, and coerce a builder into a result. */
|
|
641
|
+
execute<B extends Executable>(builder: B): SyncResult<RowOf<B>>;
|
|
642
|
+
/** Run `fn` inside a transaction: commit on success, rollback on throw. */
|
|
643
|
+
transaction<T>(fn: (tx: SyncSession) => T): T;
|
|
644
|
+
/** Run `fn` inside a SAVEPOINT (nested transaction). */
|
|
645
|
+
beginNested<T>(fn: (sp: SyncSession) => T): T;
|
|
646
|
+
/**
|
|
647
|
+
* Lazily iterate result rows without materializing them all. Falls back to a
|
|
648
|
+
* buffered fetch when the driver has no native iteration.
|
|
649
|
+
*/
|
|
650
|
+
stream<B extends Executable>(builder: B): IterableIterator<RowOf<B>>;
|
|
651
|
+
close(): void;
|
|
652
|
+
}
|
|
653
|
+
/** An asynchronous unit of work. */
|
|
654
|
+
declare class AsyncSession {
|
|
655
|
+
private readonly driver;
|
|
656
|
+
private readonly dialect;
|
|
657
|
+
constructor(driver: AsyncDriver, dialect: BaseDialect);
|
|
658
|
+
execute<B extends Executable>(builder: B): AsyncResult<RowOf<B>>;
|
|
659
|
+
/** Lazily iterate result rows. Uses driver streaming when available. */
|
|
660
|
+
stream<B extends Executable>(builder: B): AsyncIterableIterator<RowOf<B>>;
|
|
661
|
+
transaction<T>(fn: (tx: AsyncSession) => Promise<T>): Promise<T>;
|
|
662
|
+
close(): Promise<void>;
|
|
663
|
+
}
|
|
664
|
+
/** Connection-pool tuning (PostgreSQL; ignored by SQLite). */
|
|
665
|
+
interface PoolOptions {
|
|
666
|
+
/** Max connections in the pool. */
|
|
667
|
+
readonly size?: number;
|
|
668
|
+
/** Close a connection after it sits idle this long (ms). */
|
|
669
|
+
readonly idleTimeoutMs?: number;
|
|
670
|
+
/** Give up acquiring a connection after this long (ms). */
|
|
671
|
+
readonly connectTimeoutMs?: number;
|
|
672
|
+
}
|
|
673
|
+
/** Options shared by both engine flavors. */
|
|
674
|
+
interface EngineOptions {
|
|
675
|
+
/** Override the driver detected from the URL (e.g. `"better-sqlite3"`). */
|
|
676
|
+
readonly driver?: string;
|
|
677
|
+
/** Connection-pool tuning (PostgreSQL only). */
|
|
678
|
+
readonly pool?: PoolOptions;
|
|
679
|
+
}
|
|
680
|
+
/** A synchronous engine (SQLite only). */
|
|
681
|
+
declare class SyncEngine {
|
|
682
|
+
private readonly driver;
|
|
683
|
+
readonly dialect: Dialect;
|
|
684
|
+
constructor(driver: SyncDriver);
|
|
685
|
+
session(): SyncSession;
|
|
686
|
+
transaction<T>(fn: (tx: SyncSession) => T): T;
|
|
687
|
+
close(): void;
|
|
688
|
+
}
|
|
689
|
+
/** An asynchronous engine. */
|
|
690
|
+
declare class AsyncEngine {
|
|
691
|
+
private readonly driver;
|
|
692
|
+
readonly dialect: Dialect;
|
|
693
|
+
constructor(driver: AsyncDriver, dialect: Dialect);
|
|
694
|
+
session(): AsyncSession;
|
|
695
|
+
transaction<T>(fn: (tx: AsyncSession) => Promise<T>): Promise<T>;
|
|
696
|
+
close(): Promise<void>;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Create a **synchronous** engine from a database URL. SQLite only — PostgreSQL
|
|
700
|
+
* has no sane synchronous driver in Node, so a Postgres URL throws, pointing at
|
|
701
|
+
* the async `createEngine`.
|
|
702
|
+
*
|
|
703
|
+
* @param url A SQLite URL, e.g. `"sqlite:///app.db"` or `"sqlite://:memory:"`.
|
|
704
|
+
* @param options Engine options.
|
|
705
|
+
* @returns A `SyncEngine`.
|
|
706
|
+
*/
|
|
707
|
+
declare function createSyncEngine(url: string, options?: EngineOptions): SyncEngine;
|
|
708
|
+
/**
|
|
709
|
+
* Create an **asynchronous** engine from a database URL (the default). Works for
|
|
710
|
+
* both SQLite (sync driver wrapped as async) and PostgreSQL (postgres.js,
|
|
711
|
+
* lazy-loaded).
|
|
712
|
+
*
|
|
713
|
+
* @param url A database URL, e.g. `"postgresql://app@localhost/app"` or
|
|
714
|
+
* `"sqlite:///app.db"`.
|
|
715
|
+
* @param options Engine options.
|
|
716
|
+
* @returns An `AsyncEngine`.
|
|
717
|
+
*/
|
|
718
|
+
declare function createEngine(url: string, options?: EngineOptions): AsyncEngine;
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* tempest-db-js — Phase 7: typed repository + pagination.
|
|
722
|
+
*
|
|
723
|
+
* `BaseRepository<Model>` mirrors the `tempest-fastapi-sdk` repository: a thin,
|
|
724
|
+
* fully-typed CRUD + pagination layer over a model and an async session. The
|
|
725
|
+
* 404-convention is honored — `getById` throws when absent, collection methods
|
|
726
|
+
* return `[]` (never a "not found" error for an empty list).
|
|
727
|
+
*/
|
|
728
|
+
|
|
729
|
+
/** Pagination request — 1-indexed page. */
|
|
730
|
+
interface PaginationFilter<Row> {
|
|
731
|
+
readonly page?: number;
|
|
732
|
+
readonly pageSize?: number;
|
|
733
|
+
readonly orderBy?: keyof Row & string;
|
|
734
|
+
readonly ascending?: boolean;
|
|
735
|
+
readonly filters?: WhereInput<Row>;
|
|
736
|
+
}
|
|
737
|
+
/** A page of results plus metadata (mirrors `BasePaginationSchema`). */
|
|
738
|
+
interface PaginationResult<Row> {
|
|
739
|
+
readonly items: Row[];
|
|
740
|
+
readonly total: number;
|
|
741
|
+
readonly page: number;
|
|
742
|
+
readonly pageSize: number;
|
|
743
|
+
readonly pages: number;
|
|
744
|
+
}
|
|
745
|
+
/** Raised by single-record lookups (`getById`) when nothing matches (404). */
|
|
746
|
+
declare class RecordNotFound extends Error {
|
|
747
|
+
constructor(table: string, id: unknown);
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* A fully-typed CRUD + pagination repository over a model and an async session.
|
|
751
|
+
*
|
|
752
|
+
* @typeParam C - the model class.
|
|
753
|
+
*/
|
|
754
|
+
declare class BaseRepository<C extends ModelClass> {
|
|
755
|
+
protected readonly model: C;
|
|
756
|
+
protected readonly session: AsyncSession;
|
|
757
|
+
private readonly pk;
|
|
758
|
+
constructor(model: C, session: AsyncSession);
|
|
759
|
+
/** All rows matching `filters` (or everything). Empty list when none match. */
|
|
760
|
+
list(filters?: WhereInput<InferModel<C>>): Promise<InferModel<C>[]>;
|
|
761
|
+
/** The first row matching `filters`, or `null`. */
|
|
762
|
+
first(filters?: WhereInput<InferModel<C>>): Promise<InferModel<C> | null>;
|
|
763
|
+
/** A single row by primary key, or `null`. */
|
|
764
|
+
getByIdOrNull(id: unknown): Promise<InferModel<C> | null>;
|
|
765
|
+
/** A single row by primary key; throws `RecordNotFound` when absent. */
|
|
766
|
+
getById(id: unknown): Promise<InferModel<C>>;
|
|
767
|
+
/** Whether any row matches `filters`. */
|
|
768
|
+
exists(filters: WhereInput<InferModel<C>>): Promise<boolean>;
|
|
769
|
+
/** How many rows match `filters` (or the whole table). */
|
|
770
|
+
count(filters?: WhereInput<InferModel<C>>): Promise<number>;
|
|
771
|
+
/** Insert one row, returning the created row. */
|
|
772
|
+
create(data: InferInsert<C>): Promise<InferModel<C>>;
|
|
773
|
+
/** Insert many rows, returning the created rows. */
|
|
774
|
+
createMany(data: readonly InferInsert<C>[]): Promise<InferModel<C>[]>;
|
|
775
|
+
/** Update rows matching `filters`; returns the number of rows affected. */
|
|
776
|
+
update(filters: WhereInput<InferModel<C>>, set: Partial<InferModel<C>>): Promise<number>;
|
|
777
|
+
/** Delete rows matching `filters`; returns the number of rows affected. */
|
|
778
|
+
delete(filters: WhereInput<InferModel<C>>): Promise<number>;
|
|
779
|
+
/**
|
|
780
|
+
* A page of rows plus metadata. `total` counts all matching rows.
|
|
781
|
+
*
|
|
782
|
+
* @param filter Page, size, ordering and filters.
|
|
783
|
+
* @returns The page and pagination metadata.
|
|
784
|
+
*/
|
|
785
|
+
paginate(filter?: PaginationFilter<InferModel<C>>): Promise<PaginationResult<InferModel<C>>>;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* tempest-db-js — typed relations (hasMany / belongsTo) with eager loading.
|
|
790
|
+
*
|
|
791
|
+
* Relations are plain descriptors that reference another model plus the local /
|
|
792
|
+
* foreign key. `loadRelations` fetches the related rows in **one query per
|
|
793
|
+
* relation** (no N+1), groups them, and attaches them to the base rows — with
|
|
794
|
+
* the result type widened so each relation key is typed (`Row[]` for hasMany,
|
|
795
|
+
* `Row | null` for belongsTo).
|
|
796
|
+
*/
|
|
797
|
+
|
|
798
|
+
/** A one-to-many relation: each base row owns many target rows. */
|
|
799
|
+
interface HasMany<C extends ModelClass> {
|
|
800
|
+
readonly kind: "hasMany";
|
|
801
|
+
readonly target: () => C;
|
|
802
|
+
/** Column on the base row (usually its primary key). */
|
|
803
|
+
readonly localKey: string;
|
|
804
|
+
/** Column on the target row pointing back to the base row. */
|
|
805
|
+
readonly foreignKey: string;
|
|
806
|
+
}
|
|
807
|
+
/** A many-to-one relation: each base row points to one target row. */
|
|
808
|
+
interface BelongsTo<C extends ModelClass> {
|
|
809
|
+
readonly kind: "belongsTo";
|
|
810
|
+
readonly target: () => C;
|
|
811
|
+
/** Column on the base row holding the foreign key. */
|
|
812
|
+
readonly localKey: string;
|
|
813
|
+
/** Column on the target row (usually its primary key). */
|
|
814
|
+
readonly foreignKey: string;
|
|
815
|
+
}
|
|
816
|
+
type Relation = HasMany<any> | BelongsTo<any>;
|
|
817
|
+
/** Declare a one-to-many relation. */
|
|
818
|
+
declare function hasMany<C extends ModelClass>(target: () => C, keys: {
|
|
819
|
+
localKey: string;
|
|
820
|
+
foreignKey: string;
|
|
821
|
+
}): HasMany<C>;
|
|
822
|
+
/** Declare a many-to-one relation. */
|
|
823
|
+
declare function belongsTo<C extends ModelClass>(target: () => C, keys: {
|
|
824
|
+
localKey: string;
|
|
825
|
+
foreignKey: string;
|
|
826
|
+
}): BelongsTo<C>;
|
|
827
|
+
/** The value a relation contributes to a loaded row. */
|
|
828
|
+
type RelationValue<R> = R extends HasMany<infer C> ? InferModel<C>[] : R extends BelongsTo<infer C> ? InferModel<C> | null : never;
|
|
829
|
+
/** A base row augmented with its loaded relations. */
|
|
830
|
+
type WithRelations<Row, Spec extends Record<string, Relation>> = Row & {
|
|
831
|
+
[K in keyof Spec]: RelationValue<Spec[K]>;
|
|
832
|
+
};
|
|
833
|
+
/**
|
|
834
|
+
* Eager-load relations onto a set of base rows. One query per relation.
|
|
835
|
+
*
|
|
836
|
+
* @param session The async session to query through.
|
|
837
|
+
* @param rows The already-loaded base rows.
|
|
838
|
+
* @param spec A map of relation name → relation descriptor.
|
|
839
|
+
* @returns The base rows, each augmented with its relation values.
|
|
840
|
+
*/
|
|
841
|
+
declare function loadRelations<Row extends Record<string, unknown>, Spec extends Record<string, Relation>>(session: AsyncSession, rows: Row[], spec: Spec): Promise<WithRelations<Row, Spec>[]>;
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* tempest-db-js — feasibility spike for Phase 1.
|
|
845
|
+
*
|
|
846
|
+
* Proves the central design claim: a class-based, SQLAlchemy-style model whose
|
|
847
|
+
* fields are runtime column-builders can drive full static row-type inference in
|
|
848
|
+
* TypeScript, despite TS erasing types at runtime.
|
|
849
|
+
*
|
|
850
|
+
* This is a SPIKE, not the final API. It validates the type machinery only.
|
|
851
|
+
*/
|
|
852
|
+
/** Phantom marker carrying the static TS type a column maps to. */
|
|
853
|
+
declare const TYPE: unique symbol;
|
|
854
|
+
/** Column flags that influence the inferred row/insert shape. */
|
|
855
|
+
interface ColumnFlags {
|
|
856
|
+
readonly primaryKey: boolean;
|
|
857
|
+
readonly notNull: boolean;
|
|
858
|
+
readonly hasDefault: boolean;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* The canonical, dialect-neutral kind of a column type. Mirrors SQLAlchemy's
|
|
862
|
+
* generic types (e.g. `String` → varchar, `Text` → text). Dialect renderers
|
|
863
|
+
* (Phase 4/6) map each kind + meta to concrete SQL per database.
|
|
864
|
+
*/
|
|
865
|
+
type ColumnTypeKind = "smallint" | "integer" | "bigint" | "numeric" | "real" | "double" | "varchar" | "text" | "char" | "boolean" | "date" | "time" | "datetime" | "timestamp" | "blob" | "json" | "uuid" | "enum";
|
|
866
|
+
/** Parameters that refine a column type and feed the migration IR / DDL. */
|
|
867
|
+
interface ColumnTypeMeta {
|
|
868
|
+
/** Max length for `varchar`/`char`. */
|
|
869
|
+
readonly length?: number | undefined;
|
|
870
|
+
/** Total digits for `numeric`. */
|
|
871
|
+
readonly precision?: number | undefined;
|
|
872
|
+
/** Digits after the decimal point for `numeric`. */
|
|
873
|
+
readonly scale?: number | undefined;
|
|
874
|
+
/** `WITH TIME ZONE` for `timestamp`/`time`. */
|
|
875
|
+
readonly withTimezone?: boolean | undefined;
|
|
876
|
+
/** Allowed values for `enum`. */
|
|
877
|
+
readonly values?: readonly string[] | undefined;
|
|
878
|
+
/** Render as `JSONB` (PostgreSQL) instead of `JSON`. */
|
|
879
|
+
readonly jsonb?: boolean | undefined;
|
|
880
|
+
}
|
|
881
|
+
/** A structured, dialect-neutral column type descriptor. */
|
|
882
|
+
interface ColumnType {
|
|
883
|
+
readonly kind: ColumnTypeKind;
|
|
884
|
+
readonly meta: ColumnTypeMeta;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* A portable default expression. The token is dialect-neutral; the renderer
|
|
888
|
+
* (Phase 4/6) maps it to the right SQL per database — e.g. `"now"` becomes
|
|
889
|
+
* `CURRENT_TIMESTAMP` on SQLite and `now()` on PostgreSQL. Use `{ raw }` as an
|
|
890
|
+
* escape hatch for a verbatim SQL fragment.
|
|
891
|
+
*/
|
|
892
|
+
type PortableExpression = "now" | "current_date" | "current_time" | "uuidv4" | {
|
|
893
|
+
readonly raw: string;
|
|
894
|
+
};
|
|
895
|
+
/**
|
|
896
|
+
* A column default. Either a constant literal value or a server-side expression
|
|
897
|
+
* evaluated by the database (mirrors SQLAlchemy's `default` vs `server_default`).
|
|
898
|
+
* Feeds the migration IR (`DefaultIR`).
|
|
899
|
+
*/
|
|
900
|
+
type DefaultValue = {
|
|
901
|
+
readonly kind: "literal";
|
|
902
|
+
readonly value: unknown;
|
|
903
|
+
} | {
|
|
904
|
+
readonly kind: "expression";
|
|
905
|
+
readonly expression: PortableExpression;
|
|
906
|
+
};
|
|
907
|
+
/** Portable server-side default expressions, à la SQLAlchemy's `func`. */
|
|
908
|
+
declare const sql: {
|
|
909
|
+
/** Current timestamp at insert (`CURRENT_TIMESTAMP` / `now()`). */
|
|
910
|
+
readonly now: () => DefaultValue;
|
|
911
|
+
/** Current date. */
|
|
912
|
+
readonly currentDate: () => DefaultValue;
|
|
913
|
+
/** Current time. */
|
|
914
|
+
readonly currentTime: () => DefaultValue;
|
|
915
|
+
/** A freshly generated UUID v4 (`gen_random_uuid()` / portable fallback). */
|
|
916
|
+
readonly uuidv4: () => DefaultValue;
|
|
917
|
+
/** Escape hatch: a verbatim SQL expression rendered as-is. */
|
|
918
|
+
readonly raw: (expression: string) => DefaultValue;
|
|
919
|
+
};
|
|
920
|
+
/**
|
|
921
|
+
* A typed column builder. Holds runtime metadata (structured `type`, `flags`,
|
|
922
|
+
* `default`, `onUpdate`) and a phantom static type `T` used purely for inference.
|
|
923
|
+
*/
|
|
924
|
+
declare class Column<T, F extends ColumnFlags = ColumnFlags> {
|
|
925
|
+
readonly type: ColumnType;
|
|
926
|
+
readonly flags: F;
|
|
927
|
+
/** The default applied on insert, or `null` for none. */
|
|
928
|
+
readonly defaultValue: DefaultValue | null;
|
|
929
|
+
/** The value re-applied on update (e.g. `updated_at`), or `null`. */
|
|
930
|
+
readonly onUpdateValue: DefaultValue | null;
|
|
931
|
+
/** Phantom: never read at runtime, only inspected by the type system. */
|
|
932
|
+
readonly [TYPE]: T;
|
|
933
|
+
constructor(type: ColumnType, flags: F,
|
|
934
|
+
/** The default applied on insert, or `null` for none. */
|
|
935
|
+
defaultValue?: DefaultValue | null,
|
|
936
|
+
/** The value re-applied on update (e.g. `updated_at`), or `null`. */
|
|
937
|
+
onUpdateValue?: DefaultValue | null);
|
|
938
|
+
primaryKey(): Column<T, F & {
|
|
939
|
+
primaryKey: true;
|
|
940
|
+
hasDefault: true;
|
|
941
|
+
}>;
|
|
942
|
+
notNull(): Column<T, F & {
|
|
943
|
+
notNull: true;
|
|
944
|
+
}>;
|
|
945
|
+
/**
|
|
946
|
+
* Set the insert-time default: a constant value of type `T`, or a portable
|
|
947
|
+
* server-side expression from {@link sql} (e.g. `sql.now()`, `sql.uuidv4()`).
|
|
948
|
+
*/
|
|
949
|
+
default(value: T | DefaultValue): Column<T, F & {
|
|
950
|
+
hasDefault: true;
|
|
951
|
+
}>;
|
|
952
|
+
/**
|
|
953
|
+
* Re-apply a value whenever the row is updated (e.g. an `updated_at` column
|
|
954
|
+
* with `sql.now()`). Mirrors SQLAlchemy's `onupdate`.
|
|
955
|
+
*/
|
|
956
|
+
onUpdate(value: T | DefaultValue): Column<T, F>;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Column factory mirroring SQLAlchemy's typed column constructors. Each entry
|
|
960
|
+
* pairs a distinct SQL type with the TypeScript type it maps to.
|
|
961
|
+
*
|
|
962
|
+
* Notable mappings:
|
|
963
|
+
* - `varchar(n)` (`VARCHAR(n)`) is distinct from `text` (`TEXT`).
|
|
964
|
+
* - `bigInteger` maps to `bigint` (not `number`) to keep 64-bit precision.
|
|
965
|
+
* - `numeric`/`decimal` map to `string` — JavaScript has no exact decimal, and
|
|
966
|
+
* stringifying preserves precision instead of losing it to a float.
|
|
967
|
+
* - `json<T>()` carries the parsed value type; `jsonb` is the PostgreSQL variant.
|
|
968
|
+
* - `enum(...)` infers a string-literal union from its values.
|
|
969
|
+
*/
|
|
970
|
+
declare const column: {
|
|
971
|
+
/** `SMALLINT` → `number`. */
|
|
972
|
+
readonly smallInteger: () => Column<number, ColumnFlags>;
|
|
973
|
+
/** `INTEGER` → `number`. */
|
|
974
|
+
readonly integer: () => Column<number, ColumnFlags>;
|
|
975
|
+
/** `BIGINT` → `bigint` (64-bit precision preserved). */
|
|
976
|
+
readonly bigInteger: () => Column<bigint, ColumnFlags>;
|
|
977
|
+
/** `NUMERIC(precision, scale)` → `string` (exact decimal, no float loss). */
|
|
978
|
+
readonly numeric: (precision?: number, scale?: number) => Column<string, ColumnFlags>;
|
|
979
|
+
/** Alias of {@link column.numeric}. */
|
|
980
|
+
readonly decimal: (precision?: number, scale?: number) => Column<string, ColumnFlags>;
|
|
981
|
+
/** `REAL` → `number`. */
|
|
982
|
+
readonly real: () => Column<number, ColumnFlags>;
|
|
983
|
+
/** `DOUBLE PRECISION` → `number`. */
|
|
984
|
+
readonly double: () => Column<number, ColumnFlags>;
|
|
985
|
+
/** `VARCHAR(length)` → `string`. Distinct from {@link column.text}. */
|
|
986
|
+
readonly varchar: (length: number) => Column<string, ColumnFlags>;
|
|
987
|
+
/** Alias of {@link column.varchar} (SQLAlchemy's `String`). */
|
|
988
|
+
readonly string: (length: number) => Column<string, ColumnFlags>;
|
|
989
|
+
/** `CHAR(length)` → `string` (fixed-width). */
|
|
990
|
+
readonly char: (length: number) => Column<string, ColumnFlags>;
|
|
991
|
+
/** `TEXT` → `string` (unbounded). Distinct from {@link column.varchar}. */
|
|
992
|
+
readonly text: () => Column<string, ColumnFlags>;
|
|
993
|
+
/** `BOOLEAN` → `boolean`. */
|
|
994
|
+
readonly boolean: () => Column<boolean, ColumnFlags>;
|
|
995
|
+
/** `DATE` → `Date`. */
|
|
996
|
+
readonly date: () => Column<Date, ColumnFlags>;
|
|
997
|
+
/** `TIME` → `string`. Pass `{ timezone: true }` for `WITH TIME ZONE`. */
|
|
998
|
+
readonly time: (options?: {
|
|
999
|
+
timezone?: boolean;
|
|
1000
|
+
}) => Column<string, ColumnFlags>;
|
|
1001
|
+
/**
|
|
1002
|
+
* `DATETIME`/`TIMESTAMP` → `Date` (SQLAlchemy's generic `DateTime`). Pass
|
|
1003
|
+
* `{ timezone: true }` for `WITH TIME ZONE`. Pair with `.default(sql.now())`
|
|
1004
|
+
* and `.onUpdate(sql.now())` for managed `created_at`/`updated_at` columns.
|
|
1005
|
+
*/
|
|
1006
|
+
readonly datetime: (options?: {
|
|
1007
|
+
timezone?: boolean;
|
|
1008
|
+
}) => Column<Date, ColumnFlags>;
|
|
1009
|
+
/** `TIMESTAMP` → `Date` (SQL-specific). Pass `{ timezone: true }`. */
|
|
1010
|
+
readonly timestamp: (options?: {
|
|
1011
|
+
timezone?: boolean;
|
|
1012
|
+
}) => Column<Date, ColumnFlags>;
|
|
1013
|
+
/** `BLOB`/`BYTEA` → `Uint8Array`. */
|
|
1014
|
+
readonly blob: () => Column<Uint8Array, ColumnFlags>;
|
|
1015
|
+
/** `JSON` → the given parsed value type `T` (defaults to `unknown`). */
|
|
1016
|
+
readonly json: <T = unknown>() => Column<T, ColumnFlags>;
|
|
1017
|
+
/** `JSONB` (PostgreSQL) → the given parsed value type `T`. */
|
|
1018
|
+
readonly jsonb: <T = unknown>() => Column<T, ColumnFlags>;
|
|
1019
|
+
/** `UUID` → `string`. */
|
|
1020
|
+
readonly uuid: () => Column<string, ColumnFlags>;
|
|
1021
|
+
/** `ENUM(...values)` → a string-literal union of the given values. */
|
|
1022
|
+
readonly enum: <const E extends string>(...values: E[]) => Column<E, ColumnFlags>;
|
|
1023
|
+
};
|
|
1024
|
+
/** Base class every model extends, SQLAlchemy-declarative style. */
|
|
1025
|
+
declare abstract class Model {
|
|
1026
|
+
static tablename: string;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Reflect a model class into its column map at runtime, keyed by column name.
|
|
1030
|
+
*
|
|
1031
|
+
* Instantiates the class once and collects every field that is a `Column`. Used
|
|
1032
|
+
* by the serialization layer and (Phase 6) the migration schema reflector.
|
|
1033
|
+
*
|
|
1034
|
+
* @param model The model class (subclass of `Model`).
|
|
1035
|
+
* @returns A record of column name → `Column` instance.
|
|
1036
|
+
*/
|
|
1037
|
+
declare function columnsOf(model: ModelClass): Record<string, Column<unknown>>;
|
|
1038
|
+
/** Pull the static type out of a Column. */
|
|
1039
|
+
type ColType<C> = C extends Column<infer T, infer _F> ? T : never;
|
|
1040
|
+
/** Keys of the model instance whose values are Columns. */
|
|
1041
|
+
type ColumnKeys<M> = {
|
|
1042
|
+
[K in keyof M]: M[K] extends Column<unknown, ColumnFlags> ? K : never;
|
|
1043
|
+
}[keyof M];
|
|
1044
|
+
/** Constructor type for a Model subclass. */
|
|
1045
|
+
type ModelClass = (new () => Model) & {
|
|
1046
|
+
tablename: string;
|
|
1047
|
+
};
|
|
1048
|
+
/** Flatten an intersection into a single object literal for clean inference. */
|
|
1049
|
+
type Simplify<T> = {
|
|
1050
|
+
[K in keyof T]: T[K];
|
|
1051
|
+
} & {};
|
|
1052
|
+
/** The nullability-aware value a column contributes to a row. */
|
|
1053
|
+
type ColValue<Col> = Col extends Column<infer T, infer F> ? F extends {
|
|
1054
|
+
notNull: true;
|
|
1055
|
+
} | {
|
|
1056
|
+
primaryKey: true;
|
|
1057
|
+
} ? T : T | null : never;
|
|
1058
|
+
/** True when a column has a default (or is a PK) — i.e. optional on insert. */
|
|
1059
|
+
type HasDefault<Col> = Col extends Column<unknown, infer F> ? F extends {
|
|
1060
|
+
hasDefault: true;
|
|
1061
|
+
} ? true : false : false;
|
|
1062
|
+
/** Keys of the model whose columns are optional on insert. */
|
|
1063
|
+
type OptionalInsertKeys<I> = {
|
|
1064
|
+
[K in ColumnKeys<I>]: HasDefault<I[K]> extends true ? K : never;
|
|
1065
|
+
}[ColumnKeys<I>];
|
|
1066
|
+
/**
|
|
1067
|
+
* Infer the SELECT row shape from a model class: every column field becomes its
|
|
1068
|
+
* mapped static type. Columns marked notNull/primaryKey are non-nullable; others
|
|
1069
|
+
* are `T | null` (SQL semantics — an unconstrained column can be NULL).
|
|
1070
|
+
*/
|
|
1071
|
+
type InferModel<C extends ModelClass> = {
|
|
1072
|
+
[K in ColumnKeys<InstanceType<C>>]: ColValue<InstanceType<C>[K]>;
|
|
1073
|
+
};
|
|
1074
|
+
/**
|
|
1075
|
+
* Infer the INSERT shape: columns with a default (or PK) are optional; the rest
|
|
1076
|
+
* are required. Nullability is preserved on both sides.
|
|
1077
|
+
*/
|
|
1078
|
+
type InferInsert<C extends ModelClass> = Simplify<{
|
|
1079
|
+
[K in OptionalInsertKeys<InstanceType<C>>]?: ColValue<InstanceType<C>[K]>;
|
|
1080
|
+
} & {
|
|
1081
|
+
[K in Exclude<ColumnKeys<InstanceType<C>>, OptionalInsertKeys<InstanceType<C>>>]: ColValue<InstanceType<C>[K]>;
|
|
1082
|
+
}>;
|
|
1083
|
+
|
|
1084
|
+
export { type AsyncDriver, AsyncEngine, AsyncResult, AsyncSession, BaseDialect, BaseRepository, type BelongsTo, type ColRef, type ColType, Column, type ColumnFlags, type ColumnType, type ColumnTypeKind, type ColumnTypeMeta, type CompiledQuery, type CondNode, type Condition, type DefaultValue, DeleteBuilder, type DeleteNode, type Dialect, type DriverResult, type EngineOptions, type Executable, type HasMany, type InferInsert, type InferModel, InsertBuilder, type InsertNode, InvalidDatabaseUrl, JoinBuilder, type JoinClause, type JoinNode, type JoinOn, type JoinSelection, type JoinWhereInput, Model, type ModelClass, NoResultError, NodeSqliteDriver, OPERATORS, type Operator, type OperatorsFor, type OrderTerm, type PaginationFilter, type PaginationResult, type ParsedDatabaseUrl, type PoolOptions, type PortableExpression, PostgresDialect, type QueryNode, RecordNotFound, type Relation, type RelationValue, type Returning, type RowOf, SelectBuilder, type SelectNode, type SortDirection, type Sources, SqliteDialect, type SyncDriver, SyncEngine, SyncResult, SyncSession, UpdateBuilder, type UpdateNode, ValidationError, type WhereArg, type WhereInput, type WithRelations, and, belongsTo, column, columnsOf, createEngine, createSyncEngine, del, detectDialect, fromDict, getDialect, hasMany, insert, isCondition, join, loadRelations, not, or, parse, parseDatabaseUrl, select, sql, stringify, toCondNode, toDict, toJSON, update };
|