tempest-db-js 0.1.0 → 0.3.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/README.md +70 -2
- package/dist/bin.cjs +1191 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +27 -0
- package/dist/bin.d.ts +27 -0
- package/dist/bin.js +111 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-OP7FRDI5.js +1225 -0
- package/dist/chunk-OP7FRDI5.js.map +1 -0
- package/dist/{chunk-F36ZSQAN.js → chunk-Q32CBI2A.js} +584 -53
- package/dist/chunk-Q32CBI2A.js.map +1 -0
- package/dist/index.cjs +591 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +320 -15
- package/dist/index.d.ts +320 -15
- package/dist/index.js +1 -1
- package/dist/migrations/index.cjs +338 -53
- package/dist/migrations/index.cjs.map +1 -1
- package/dist/migrations/index.d.cts +116 -5
- package/dist/migrations/index.d.ts +116 -5
- package/dist/migrations/index.js +2 -923
- package/dist/migrations/index.js.map +1 -1
- package/package.json +13 -5
- package/dist/chunk-F36ZSQAN.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -18,12 +18,26 @@ interface OrderTerm {
|
|
|
18
18
|
readonly column: string;
|
|
19
19
|
readonly direction: SortDirection;
|
|
20
20
|
}
|
|
21
|
+
/** One aggregate expression in a grouped SELECT (`COUNT(*) AS "n"`). */
|
|
22
|
+
interface AggregateTerm {
|
|
23
|
+
readonly fn: "count" | "sum" | "avg" | "min" | "max";
|
|
24
|
+
/** The column to aggregate, or `"*"` (only valid for `count`). */
|
|
25
|
+
readonly column: string | "*";
|
|
26
|
+
/** The result alias. */
|
|
27
|
+
readonly alias: string;
|
|
28
|
+
}
|
|
21
29
|
/** Serializable AST for a SELECT. Dialects (Phase 4) compile this to SQL. */
|
|
22
30
|
interface SelectNode {
|
|
23
31
|
readonly kind: "select";
|
|
24
32
|
readonly table: string;
|
|
25
33
|
/** Projected columns, or "*" for the whole row. */
|
|
26
34
|
readonly columns: readonly string[] | "*";
|
|
35
|
+
/** Emit `SELECT DISTINCT` when true. */
|
|
36
|
+
readonly distinct: boolean;
|
|
37
|
+
/** Aggregate expressions; when non-empty, this is a grouped/aggregate query. */
|
|
38
|
+
readonly aggregates: readonly AggregateTerm[];
|
|
39
|
+
/** `GROUP BY` columns. */
|
|
40
|
+
readonly groupBy: readonly string[];
|
|
27
41
|
readonly where: CondNode | undefined;
|
|
28
42
|
readonly orderBy: readonly OrderTerm[];
|
|
29
43
|
readonly limit: number | undefined;
|
|
@@ -83,6 +97,29 @@ type WhereInput<Row = Record<string, unknown>> = {
|
|
|
83
97
|
declare const OPERATORS: readonly ["eq", "ne", "gt", "gte", "lt", "lte", "like", "ilike", "in", "notIn", "between", "isNull"];
|
|
84
98
|
/** One supported operator name. */
|
|
85
99
|
type Operator = (typeof OPERATORS)[number];
|
|
100
|
+
/** An aggregate expression carrying its result type `T` as a phantom. */
|
|
101
|
+
declare class Agg<T> {
|
|
102
|
+
readonly fn: AggregateTerm["fn"];
|
|
103
|
+
readonly column: string | "*";
|
|
104
|
+
readonly __t: T;
|
|
105
|
+
constructor(fn: AggregateTerm["fn"], column: string | "*");
|
|
106
|
+
}
|
|
107
|
+
/** `COUNT(*)` — the number of rows in the group (never null). */
|
|
108
|
+
declare function count(): Agg<number>;
|
|
109
|
+
/** `SUM(column)` — null when the group has no non-null values. */
|
|
110
|
+
declare function sum(column: string): Agg<number | null>;
|
|
111
|
+
/** `AVG(column)` — null when the group has no non-null values. */
|
|
112
|
+
declare function avg(column: string): Agg<number | null>;
|
|
113
|
+
/** `MIN(column)` — numeric columns; null on an empty group. */
|
|
114
|
+
declare function min(column: string): Agg<number | null>;
|
|
115
|
+
/** `MAX(column)` — numeric columns; null on an empty group. */
|
|
116
|
+
declare function max(column: string): Agg<number | null>;
|
|
117
|
+
/** Extract the phantom result type of an aggregate expression. */
|
|
118
|
+
type AggResult<A> = A extends Agg<infer T> ? T : never;
|
|
119
|
+
/** Flatten an intersection into a single object literal. */
|
|
120
|
+
type SimplifyProj<T> = {
|
|
121
|
+
[K in keyof T]: T[K];
|
|
122
|
+
} & {};
|
|
86
123
|
/**
|
|
87
124
|
* Immutable, chainable SELECT builder.
|
|
88
125
|
*
|
|
@@ -101,6 +138,27 @@ declare class SelectBuilder<Full, Proj = Full> {
|
|
|
101
138
|
private with;
|
|
102
139
|
/** Add a WHERE filter: the object form (keys typed) or an `and`/`or`/`not`. */
|
|
103
140
|
where(input: WhereInput<Full> | Condition): SelectBuilder<Full, Proj>;
|
|
141
|
+
/** Emit `SELECT DISTINCT` — drop duplicate rows. */
|
|
142
|
+
distinct(): SelectBuilder<Full, Proj>;
|
|
143
|
+
/**
|
|
144
|
+
* Group by columns and compute aggregates. The result row is the grouped
|
|
145
|
+
* columns (typed from the model) plus one field per aggregate alias.
|
|
146
|
+
*
|
|
147
|
+
* @param groupBy The columns to group by (checked against the model). Pass `[]`
|
|
148
|
+
* for a whole-table aggregate.
|
|
149
|
+
* @param spec A map of result alias → aggregate expression ({@link count},
|
|
150
|
+
* {@link sum}, {@link avg}, {@link min}, {@link max}).
|
|
151
|
+
* @returns A builder whose row is `Pick<Full, K> & { [alias]: aggResult }`.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* select(Order).aggregate(["status"], { n: count(), total: sum("amount") });
|
|
156
|
+
* // rows: { status: string; n: number; total: number | null }[]
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
aggregate<K extends keyof Full & string, S extends Record<string, Agg<unknown>>>(groupBy: readonly K[], spec: S): SelectBuilder<Full, SimplifyProj<Pick<Full, K> & {
|
|
160
|
+
[A in keyof S]: AggResult<S[A]>;
|
|
161
|
+
}>>;
|
|
104
162
|
/** Order by a column of `Full`. */
|
|
105
163
|
orderBy(column: keyof Full & string, direction?: SortDirection): SelectBuilder<Full, Proj>;
|
|
106
164
|
/** Limit the number of rows. */
|
|
@@ -177,12 +235,23 @@ declare function not<Row = Record<string, unknown>>(input: WhereArg<NoInfer<Row>
|
|
|
177
235
|
|
|
178
236
|
/** Columns to return from a mutation, or "*" for the whole row. */
|
|
179
237
|
type Returning = readonly string[] | "*" | null;
|
|
238
|
+
/**
|
|
239
|
+
* Conflict-resolution clause for an INSERT (`ON CONFLICT`). `target` is the
|
|
240
|
+
* conflicting column(s) (a unique/PK constraint); `update` is `"nothing"` for
|
|
241
|
+
* `DO NOTHING`, or the columns to overwrite for `DO UPDATE SET`.
|
|
242
|
+
*/
|
|
243
|
+
interface OnConflict {
|
|
244
|
+
readonly target: readonly string[];
|
|
245
|
+
readonly update: Record<string, unknown> | "nothing";
|
|
246
|
+
}
|
|
180
247
|
/** Serializable AST for an INSERT. */
|
|
181
248
|
interface InsertNode {
|
|
182
249
|
readonly kind: "insert";
|
|
183
250
|
readonly table: string;
|
|
184
251
|
readonly values: readonly Record<string, unknown>[];
|
|
185
252
|
readonly returning: Returning;
|
|
253
|
+
/** Conflict handling (`ON CONFLICT ...`), or `undefined` for none. */
|
|
254
|
+
readonly onConflict?: OnConflict;
|
|
186
255
|
}
|
|
187
256
|
/**
|
|
188
257
|
* INSERT builder.
|
|
@@ -202,6 +271,19 @@ declare class InsertBuilder<Full, Ins, Ret = number> {
|
|
|
202
271
|
private with;
|
|
203
272
|
/** Provide one row or many rows to insert, typed by the insert shape. */
|
|
204
273
|
values(rows: Ins | readonly Ins[]): InsertBuilder<Full, Ins, Ret>;
|
|
274
|
+
/**
|
|
275
|
+
* On a unique/PK conflict on `target`, do nothing (skip the row).
|
|
276
|
+
*
|
|
277
|
+
* @param target The conflicting column(s) — a unique or primary key.
|
|
278
|
+
*/
|
|
279
|
+
onConflictDoNothing(target: readonly (keyof Full & string)[]): InsertBuilder<Full, Ins, Ret>;
|
|
280
|
+
/**
|
|
281
|
+
* On a unique/PK conflict on `target`, overwrite the given columns (upsert).
|
|
282
|
+
*
|
|
283
|
+
* @param target The conflicting column(s) — a unique or primary key.
|
|
284
|
+
* @param set The columns to update with new values.
|
|
285
|
+
*/
|
|
286
|
+
onConflictDoUpdate(target: readonly (keyof Full & string)[], set: Partial<Full>): InsertBuilder<Full, Ins, Ret>;
|
|
205
287
|
/** Return the full inserted row(s). */
|
|
206
288
|
returning(): InsertBuilder<Full, Ins, Full>;
|
|
207
289
|
/** Return only the given columns of the inserted row(s). */
|
|
@@ -298,14 +380,14 @@ declare function del<C extends ModelClass>(model: C): DeleteBuilder<InferModel<C
|
|
|
298
380
|
* detection, so URLs copied from a Python service still work here.
|
|
299
381
|
*/
|
|
300
382
|
/** A database dialect tempest-db-js can target. */
|
|
301
|
-
type Dialect = "sqlite" | "postgresql";
|
|
383
|
+
type Dialect = "sqlite" | "postgresql" | "mysql";
|
|
302
384
|
/** A parsed database URL, dialect-neutral. */
|
|
303
385
|
interface ParsedDatabaseUrl {
|
|
304
386
|
/** The detected dialect. */
|
|
305
387
|
readonly dialect: Dialect;
|
|
306
388
|
/** Driver after the `+` in the scheme (e.g. `better-sqlite3`), or `null`. */
|
|
307
389
|
readonly driver: string | null;
|
|
308
|
-
/** Host (PostgreSQL), or `null` for SQLite. */
|
|
390
|
+
/** Host (PostgreSQL/MySQL), or `null` for SQLite. */
|
|
309
391
|
readonly host: string | null;
|
|
310
392
|
/** Port, or `null`. */
|
|
311
393
|
readonly port: number | null;
|
|
@@ -420,8 +502,20 @@ interface JoinSelection {
|
|
|
420
502
|
readonly alias: string;
|
|
421
503
|
readonly column: string;
|
|
422
504
|
}
|
|
423
|
-
/**
|
|
424
|
-
type
|
|
505
|
+
/** Collapse a union of object types into their intersection. */
|
|
506
|
+
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
507
|
+
/**
|
|
508
|
+
* `where` filter for a join. Keys are `alias.column` refs; each value accepts a
|
|
509
|
+
* bare value (shorthand for `eq`) or an operator object restricted to the
|
|
510
|
+
* operators valid for that column's type — exactly like the single-table
|
|
511
|
+
* {@link WhereInput}, but qualified per source. `like` on a numeric join column,
|
|
512
|
+
* or `gt` on a string one, is a compile error.
|
|
513
|
+
*/
|
|
514
|
+
type JoinWhereInput<S extends Sources> = Partial<UnionToIntersection<{
|
|
515
|
+
[A in keyof S]: {
|
|
516
|
+
[C in keyof NonNullable<S[A]> & string as `${A & string}.${C}`]: NonNullable<S[A]>[C] | OperatorsFor<NonNullable<NonNullable<S[A]>[C]>>;
|
|
517
|
+
};
|
|
518
|
+
}[keyof S]>>;
|
|
425
519
|
/** Serializable AST for a multi-table SELECT. */
|
|
426
520
|
interface JoinNode {
|
|
427
521
|
readonly kind: "join_select";
|
|
@@ -476,7 +570,7 @@ declare class JoinBuilder<S extends Sources> {
|
|
|
476
570
|
[K in A]: InferModel<C> | null;
|
|
477
571
|
}>;
|
|
478
572
|
/** Filter by `alias.column` references (object form) or an `and`/`or`/`not`. */
|
|
479
|
-
where(input:
|
|
573
|
+
where(input: JoinWhereInput<S> | Condition): JoinBuilder<S>;
|
|
480
574
|
/** Order by an `alias.column` reference. */
|
|
481
575
|
orderBy(ref: ColRef<S>, direction?: SortDirection): JoinBuilder<S>;
|
|
482
576
|
limit(n: number): JoinBuilder<S>;
|
|
@@ -516,12 +610,27 @@ type QueryNode = SelectNode | InsertNode | UpdateNode | DeleteNode | JoinNode;
|
|
|
516
610
|
* actually differs between databases (placeholder syntax, `ILIKE` support).
|
|
517
611
|
*/
|
|
518
612
|
declare abstract class BaseDialect {
|
|
519
|
-
abstract readonly name:
|
|
613
|
+
abstract readonly name: Dialect;
|
|
614
|
+
/**
|
|
615
|
+
* INSERT SQL templates keyed by structure (dialect|table|columns|rowCount|
|
|
616
|
+
* returning). Shared across dialect instances — the key namespaces by dialect
|
|
617
|
+
* name, and the placeholder text is dialect-specific but structure-determined.
|
|
618
|
+
*/
|
|
619
|
+
private static readonly insertTemplates;
|
|
620
|
+
/** Quoted-identifier cache (see {@link quoteId}). Shared across dialects. */
|
|
621
|
+
private static readonly quotedIds;
|
|
520
622
|
/** Render the n-th (1-based) placeholder. */
|
|
521
623
|
protected abstract placeholder(index: number): string;
|
|
522
624
|
/** Render a case-insensitive LIKE for the active dialect. */
|
|
523
625
|
protected abstract ilike(column: string, param: string): string;
|
|
524
|
-
/**
|
|
626
|
+
/**
|
|
627
|
+
* Quote an identifier (column/table) for the active dialect.
|
|
628
|
+
*
|
|
629
|
+
* Memoized: identifiers form a small, stable set (column/table names), but this
|
|
630
|
+
* runs for every identifier on every compile. Caching the quoted form removes a
|
|
631
|
+
* regex-replace + string allocation from the hot path. The standard double-quote
|
|
632
|
+
* form is identical across both dialects, so one shared cache is correct.
|
|
633
|
+
*/
|
|
525
634
|
protected quoteId(name: string): string;
|
|
526
635
|
/** Compile any node to `{ sql, params }`. */
|
|
527
636
|
compile(node: QueryNode): CompiledQuery;
|
|
@@ -529,10 +638,28 @@ declare abstract class BaseDialect {
|
|
|
529
638
|
private qualify;
|
|
530
639
|
private compileSelect;
|
|
531
640
|
private compileInsert;
|
|
641
|
+
/**
|
|
642
|
+
* The INSERT SQL template for a given structure, cached across calls.
|
|
643
|
+
*
|
|
644
|
+
* The text depends only on (dialect, table, columns, row count, returning,
|
|
645
|
+
* conflict shape) — never on the bound values — and placeholder positions are
|
|
646
|
+
* deterministic from the counts (a fresh statement always starts binding at 1).
|
|
647
|
+
* So a per-row insert loop compiles the string once and reuses it every row.
|
|
648
|
+
*/
|
|
649
|
+
private insertTemplate;
|
|
650
|
+
/**
|
|
651
|
+
* Render the conflict-handling clause. Standard SQL (SQLite/PostgreSQL) uses
|
|
652
|
+
* `ON CONFLICT (...) DO NOTHING | DO UPDATE SET ...`; MySQL overrides this.
|
|
653
|
+
*
|
|
654
|
+
* @param onConflict The conflict clause from the node.
|
|
655
|
+
* @param conflictCols The columns to overwrite on `DO UPDATE` (empty for nothing).
|
|
656
|
+
* @param nextPlaceholder Yields the next positional placeholder (advances the count).
|
|
657
|
+
*/
|
|
658
|
+
protected renderConflict(onConflict: NonNullable<InsertNode["onConflict"]>, conflictCols: readonly string[], nextPlaceholder: () => string): string;
|
|
532
659
|
private compileUpdate;
|
|
533
660
|
private compileDelete;
|
|
534
661
|
private compileJoin;
|
|
535
|
-
|
|
662
|
+
protected compileReturning(returning: readonly string[] | "*" | null): string;
|
|
536
663
|
/**
|
|
537
664
|
* Compile a condition tree (fields / and / or / not) to SQL. `idFor` renders a
|
|
538
665
|
* key to a quoted identifier — `quoteId` for single-table, `qualify` for joins —
|
|
@@ -554,8 +681,21 @@ declare class PostgresDialect extends BaseDialect {
|
|
|
554
681
|
protected placeholder(index: number): string;
|
|
555
682
|
protected ilike(column: string, param: string): string;
|
|
556
683
|
}
|
|
684
|
+
/**
|
|
685
|
+
* MySQL dialect: `?` placeholders, backtick identifiers, `ON DUPLICATE KEY
|
|
686
|
+
* UPDATE` for upsert, and case-insensitive `LIKE` (default collation). MySQL has
|
|
687
|
+
* no `RETURNING`, so requesting it throws.
|
|
688
|
+
*/
|
|
689
|
+
declare class MysqlDialect extends BaseDialect {
|
|
690
|
+
readonly name: "mysql";
|
|
691
|
+
protected placeholder(): string;
|
|
692
|
+
protected ilike(column: string, param: string): string;
|
|
693
|
+
protected quoteId(name: string): string;
|
|
694
|
+
protected renderConflict(onConflict: NonNullable<InsertNode["onConflict"]>, conflictCols: readonly string[], nextPlaceholder: () => string): string;
|
|
695
|
+
protected compileReturning(returning: readonly string[] | "*" | null): string;
|
|
696
|
+
}
|
|
557
697
|
/** Get a dialect instance by name. */
|
|
558
|
-
declare function getDialect(name:
|
|
698
|
+
declare function getDialect(name: Dialect): BaseDialect;
|
|
559
699
|
|
|
560
700
|
/** The outcome of running one statement. */
|
|
561
701
|
interface DriverResult {
|
|
@@ -576,14 +716,35 @@ interface AsyncDriver {
|
|
|
576
716
|
execute(sql: string, params: readonly unknown[]): Promise<DriverResult>;
|
|
577
717
|
/** Lazily iterate rows (for `.stream()`), if the driver supports it. */
|
|
578
718
|
iterate?(sql: string, params: readonly unknown[]): AsyncIterableIterator<Record<string, unknown>>;
|
|
719
|
+
/**
|
|
720
|
+
* Reserve a single pinned connection for the duration of a transaction.
|
|
721
|
+
* Pooled drivers (PostgreSQL) MUST implement this so `BEGIN`/`COMMIT` and the
|
|
722
|
+
* statements between them all run on the same connection. Single-connection
|
|
723
|
+
* drivers (SQLite) may omit it — `transaction` then runs on the shared handle.
|
|
724
|
+
*/
|
|
725
|
+
reserve?(): Promise<ReservedAsyncDriver>;
|
|
579
726
|
close(): Promise<void>;
|
|
580
727
|
}
|
|
728
|
+
/** An {@link AsyncDriver} pinned to one connection, used inside a transaction. */
|
|
729
|
+
interface ReservedAsyncDriver extends AsyncDriver {
|
|
730
|
+
/** Return the pinned connection to the pool. */
|
|
731
|
+
release(): Promise<void>;
|
|
732
|
+
}
|
|
581
733
|
/** SQLite driver backed by Node's built-in `node:sqlite` (zero install). */
|
|
582
734
|
declare class NodeSqliteDriver implements SyncDriver {
|
|
583
735
|
private readonly db;
|
|
736
|
+
/**
|
|
737
|
+
* Prepared-statement cache keyed by SQL text. tempest-db-js always
|
|
738
|
+
* parameterizes, so a query shape maps to one stable SQL string — reusing the
|
|
739
|
+
* compiled statement avoids re-`prepare()` on every call (the dominant cost of
|
|
740
|
+
* per-row inserts and point lookups).
|
|
741
|
+
*/
|
|
742
|
+
private readonly statements;
|
|
584
743
|
constructor(database: any);
|
|
585
744
|
/** Open a `node:sqlite` database at the given path (or `:memory:`). */
|
|
586
745
|
static open(path: string): NodeSqliteDriver;
|
|
746
|
+
/** Return the cached prepared statement for `sql`, preparing it on first use. */
|
|
747
|
+
private prepare;
|
|
587
748
|
execute(sql: string, params: readonly unknown[]): DriverResult;
|
|
588
749
|
iterate(sql: string, params: readonly unknown[]): IterableIterator<Record<string, unknown>>;
|
|
589
750
|
close(): void;
|
|
@@ -607,6 +768,34 @@ type RowOf<B> = B extends {
|
|
|
607
768
|
declare class NoResultError extends Error {
|
|
608
769
|
constructor(message: string);
|
|
609
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* Raised when the driver rejects a statement. Wraps the original driver error
|
|
773
|
+
* and attaches the offending SQL and its bound parameters, so a failure points
|
|
774
|
+
* at the exact query instead of an opaque driver message.
|
|
775
|
+
*/
|
|
776
|
+
declare class QueryExecutionError extends Error {
|
|
777
|
+
/** The original error thrown by the driver. */
|
|
778
|
+
readonly cause: unknown;
|
|
779
|
+
/** The SQL that failed. */
|
|
780
|
+
readonly sql: string;
|
|
781
|
+
/** The bound parameters, in order. */
|
|
782
|
+
readonly params: readonly unknown[];
|
|
783
|
+
constructor(
|
|
784
|
+
/** The original error thrown by the driver. */
|
|
785
|
+
cause: unknown,
|
|
786
|
+
/** The SQL that failed. */
|
|
787
|
+
sql: string,
|
|
788
|
+
/** The bound parameters, in order. */
|
|
789
|
+
params: readonly unknown[]);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* A hook invoked for every statement a session runs (query logging / tracing).
|
|
793
|
+
* Errors thrown by the logger are ignored so logging never breaks execution.
|
|
794
|
+
*/
|
|
795
|
+
type QueryLogger = (event: {
|
|
796
|
+
readonly sql: string;
|
|
797
|
+
readonly params: readonly unknown[];
|
|
798
|
+
}) => void;
|
|
610
799
|
/** Synchronous result view over already-fetched rows. */
|
|
611
800
|
declare class SyncResult<Row> {
|
|
612
801
|
private readonly rows;
|
|
@@ -636,7 +825,13 @@ declare class AsyncResult<Row> {
|
|
|
636
825
|
declare class SyncSession {
|
|
637
826
|
private readonly driver;
|
|
638
827
|
private readonly dialect;
|
|
639
|
-
|
|
828
|
+
/** Optional per-statement logger (query tracing). */
|
|
829
|
+
private readonly logger?;
|
|
830
|
+
constructor(driver: SyncDriver, dialect: BaseDialect,
|
|
831
|
+
/** Optional per-statement logger (query tracing). */
|
|
832
|
+
logger?: QueryLogger | undefined);
|
|
833
|
+
/** Log, run, and error-wrap one raw statement. */
|
|
834
|
+
private exec;
|
|
640
835
|
/** Compile, run, and coerce a builder into a result. */
|
|
641
836
|
execute<B extends Executable>(builder: B): SyncResult<RowOf<B>>;
|
|
642
837
|
/** Run `fn` inside a transaction: commit on success, rollback on throw. */
|
|
@@ -649,17 +844,27 @@ declare class SyncSession {
|
|
|
649
844
|
*/
|
|
650
845
|
stream<B extends Executable>(builder: B): IterableIterator<RowOf<B>>;
|
|
651
846
|
close(): void;
|
|
847
|
+
/** `using session = ...` closes the driver when the scope exits. */
|
|
848
|
+
[Symbol.dispose](): void;
|
|
652
849
|
}
|
|
653
850
|
/** An asynchronous unit of work. */
|
|
654
851
|
declare class AsyncSession {
|
|
655
852
|
private readonly driver;
|
|
656
853
|
private readonly dialect;
|
|
657
|
-
|
|
854
|
+
/** Optional per-statement logger (query tracing). */
|
|
855
|
+
private readonly logger?;
|
|
856
|
+
constructor(driver: AsyncDriver, dialect: BaseDialect,
|
|
857
|
+
/** Optional per-statement logger (query tracing). */
|
|
858
|
+
logger?: QueryLogger | undefined);
|
|
859
|
+
/** Log, run, and error-wrap one raw statement. */
|
|
860
|
+
private exec;
|
|
658
861
|
execute<B extends Executable>(builder: B): AsyncResult<RowOf<B>>;
|
|
659
862
|
/** Lazily iterate result rows. Uses driver streaming when available. */
|
|
660
863
|
stream<B extends Executable>(builder: B): AsyncIterableIterator<RowOf<B>>;
|
|
661
864
|
transaction<T>(fn: (tx: AsyncSession) => Promise<T>): Promise<T>;
|
|
662
865
|
close(): Promise<void>;
|
|
866
|
+
/** `await using session = ...` closes the driver when the scope exits. */
|
|
867
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
663
868
|
}
|
|
664
869
|
/** Connection-pool tuning (PostgreSQL; ignored by SQLite). */
|
|
665
870
|
interface PoolOptions {
|
|
@@ -676,24 +881,35 @@ interface EngineOptions {
|
|
|
676
881
|
readonly driver?: string;
|
|
677
882
|
/** Connection-pool tuning (PostgreSQL only). */
|
|
678
883
|
readonly pool?: PoolOptions;
|
|
884
|
+
/**
|
|
885
|
+
* Called for every statement a session runs — SQL + bound params. Use for
|
|
886
|
+
* query logging/tracing. Thrown errors are swallowed so it never breaks a query.
|
|
887
|
+
*/
|
|
888
|
+
readonly onQuery?: QueryLogger;
|
|
679
889
|
}
|
|
680
890
|
/** A synchronous engine (SQLite only). */
|
|
681
891
|
declare class SyncEngine {
|
|
682
892
|
private readonly driver;
|
|
893
|
+
private readonly logger?;
|
|
683
894
|
readonly dialect: Dialect;
|
|
684
|
-
constructor(driver: SyncDriver);
|
|
895
|
+
constructor(driver: SyncDriver, logger?: QueryLogger | undefined);
|
|
685
896
|
session(): SyncSession;
|
|
686
897
|
transaction<T>(fn: (tx: SyncSession) => T): T;
|
|
687
898
|
close(): void;
|
|
899
|
+
/** `using engine = createSyncEngine(...)` closes the pool when the scope exits. */
|
|
900
|
+
[Symbol.dispose](): void;
|
|
688
901
|
}
|
|
689
902
|
/** An asynchronous engine. */
|
|
690
903
|
declare class AsyncEngine {
|
|
691
904
|
private readonly driver;
|
|
692
905
|
readonly dialect: Dialect;
|
|
693
|
-
|
|
906
|
+
private readonly logger?;
|
|
907
|
+
constructor(driver: AsyncDriver, dialect: Dialect, logger?: QueryLogger | undefined);
|
|
694
908
|
session(): AsyncSession;
|
|
695
909
|
transaction<T>(fn: (tx: AsyncSession) => Promise<T>): Promise<T>;
|
|
696
910
|
close(): Promise<void>;
|
|
911
|
+
/** `await using engine = createEngine(...)` closes the pool when the scope exits. */
|
|
912
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
697
913
|
}
|
|
698
914
|
/**
|
|
699
915
|
* Create a **synchronous** engine from a database URL. SQLite only — PostgreSQL
|
|
@@ -785,6 +1001,91 @@ declare class BaseRepository<C extends ModelClass> {
|
|
|
785
1001
|
paginate(filter?: PaginationFilter<InferModel<C>>): Promise<PaginationResult<InferModel<C>>>;
|
|
786
1002
|
}
|
|
787
1003
|
|
|
1004
|
+
/**
|
|
1005
|
+
* tempest-db-js — opt-in active-record layer.
|
|
1006
|
+
*
|
|
1007
|
+
* The library's default return shape is a plain inferred object (a locked design
|
|
1008
|
+
* decision — see the roadmap). This module adds an **opt-in** wrapper for code
|
|
1009
|
+
* that prefers instance methods: `ActiveRecord` holds the plain row on `.data`
|
|
1010
|
+
* and exposes `save` / `update` / `delete` / `reload` over an async session. It
|
|
1011
|
+
* never replaces the default plain-object return — you reach for it explicitly.
|
|
1012
|
+
*/
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* An opt-in active-record wrapper around a single row.
|
|
1016
|
+
*
|
|
1017
|
+
* The current field values live on {@link data} (a plain, typed row object).
|
|
1018
|
+
* Mutating methods persist through the bound session and refresh `data`.
|
|
1019
|
+
*
|
|
1020
|
+
* @typeParam C - the model class.
|
|
1021
|
+
*/
|
|
1022
|
+
declare class ActiveRecord<C extends ModelClass> {
|
|
1023
|
+
private readonly model;
|
|
1024
|
+
private readonly session;
|
|
1025
|
+
/** The current field values (a plain, typed row). */
|
|
1026
|
+
data: InferModel<C>;
|
|
1027
|
+
private readonly pk;
|
|
1028
|
+
constructor(model: C, session: AsyncSession,
|
|
1029
|
+
/** The current field values (a plain, typed row). */
|
|
1030
|
+
data: InferModel<C>);
|
|
1031
|
+
/** The primary-key value of the wrapped row. */
|
|
1032
|
+
private pkValue;
|
|
1033
|
+
private pkFilter;
|
|
1034
|
+
/**
|
|
1035
|
+
* Persist the current `data` — insert if new, otherwise overwrite the existing
|
|
1036
|
+
* row (upsert on the primary key). Refreshes `data` from the returned row.
|
|
1037
|
+
*
|
|
1038
|
+
* @returns This wrapper, for chaining.
|
|
1039
|
+
*/
|
|
1040
|
+
save(): Promise<this>;
|
|
1041
|
+
/**
|
|
1042
|
+
* Update the given columns for this row and merge them into `data`.
|
|
1043
|
+
*
|
|
1044
|
+
* @param patch The columns to change.
|
|
1045
|
+
* @returns This wrapper, for chaining.
|
|
1046
|
+
*/
|
|
1047
|
+
update(patch: Partial<InferModel<C>>): Promise<this>;
|
|
1048
|
+
/**
|
|
1049
|
+
* Delete this row.
|
|
1050
|
+
*
|
|
1051
|
+
* @returns The number of rows affected (0 or 1).
|
|
1052
|
+
*/
|
|
1053
|
+
delete(): Promise<number>;
|
|
1054
|
+
/**
|
|
1055
|
+
* Re-fetch this row by primary key and refresh `data`.
|
|
1056
|
+
*
|
|
1057
|
+
* @returns This wrapper, for chaining.
|
|
1058
|
+
* @throws When the row no longer exists.
|
|
1059
|
+
*/
|
|
1060
|
+
reload(): Promise<this>;
|
|
1061
|
+
}
|
|
1062
|
+
/** A small factory binding a model + session for producing {@link ActiveRecord}s. */
|
|
1063
|
+
interface ActiveRecordManager<C extends ModelClass> {
|
|
1064
|
+
/** Wrap an existing row (already loaded) as an active record. */
|
|
1065
|
+
wrap(row: InferModel<C>): ActiveRecord<C>;
|
|
1066
|
+
/** Build an unsaved active record from insert data; call `.save()` to persist. */
|
|
1067
|
+
create(data: InferInsert<C>): ActiveRecord<C>;
|
|
1068
|
+
/** Fetch a row by primary key and wrap it, or `null` if absent. */
|
|
1069
|
+
get(id: unknown): Promise<ActiveRecord<C> | null>;
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Create an {@link ActiveRecordManager} for a model over a session.
|
|
1073
|
+
*
|
|
1074
|
+
* @param model The model class.
|
|
1075
|
+
* @param session The async session to persist through.
|
|
1076
|
+
* @returns A manager that wraps/fetches rows as active records.
|
|
1077
|
+
*
|
|
1078
|
+
* @example
|
|
1079
|
+
* ```ts
|
|
1080
|
+
* const users = activeRecord(User, engine.session());
|
|
1081
|
+
* const u = users.create({ name: "Ana", age: 30 });
|
|
1082
|
+
* await u.save();
|
|
1083
|
+
* await u.update({ age: 31 });
|
|
1084
|
+
* await u.delete();
|
|
1085
|
+
* ```
|
|
1086
|
+
*/
|
|
1087
|
+
declare function activeRecord<C extends ModelClass>(model: C, session: AsyncSession): ActiveRecordManager<C>;
|
|
1088
|
+
|
|
788
1089
|
/**
|
|
789
1090
|
* tempest-db-js — typed relations (hasMany / belongsTo) with eager loading.
|
|
790
1091
|
*
|
|
@@ -1031,8 +1332,12 @@ declare abstract class Model {
|
|
|
1031
1332
|
* Instantiates the class once and collects every field that is a `Column`. Used
|
|
1032
1333
|
* by the serialization layer and (Phase 6) the migration schema reflector.
|
|
1033
1334
|
*
|
|
1335
|
+
* The result is **memoized per class** — a model's columns never change at
|
|
1336
|
+
* runtime, and this is called once per row on hot read paths (coercion, joins),
|
|
1337
|
+
* so re-instantiating the class every time would dominate large result sets.
|
|
1338
|
+
*
|
|
1034
1339
|
* @param model The model class (subclass of `Model`).
|
|
1035
|
-
* @returns A record of column name → `Column` instance.
|
|
1340
|
+
* @returns A record of column name → `Column` instance (do not mutate).
|
|
1036
1341
|
*/
|
|
1037
1342
|
declare function columnsOf(model: ModelClass): Record<string, Column<unknown>>;
|
|
1038
1343
|
/** Pull the static type out of a Column. */
|
|
@@ -1081,4 +1386,4 @@ type InferInsert<C extends ModelClass> = Simplify<{
|
|
|
1081
1386
|
[K in Exclude<ColumnKeys<InstanceType<C>>, OptionalInsertKeys<InstanceType<C>>>]: ColValue<InstanceType<C>[K]>;
|
|
1082
1387
|
}>;
|
|
1083
1388
|
|
|
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 };
|
|
1389
|
+
export { ActiveRecord, type ActiveRecordManager, Agg, type AggregateTerm, 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, MysqlDialect, NoResultError, NodeSqliteDriver, OPERATORS, type OnConflict, type Operator, type OperatorsFor, type OrderTerm, type PaginationFilter, type PaginationResult, type ParsedDatabaseUrl, type PoolOptions, type PortableExpression, PostgresDialect, QueryExecutionError, type QueryLogger, type QueryNode, RecordNotFound, type Relation, type RelationValue, type ReservedAsyncDriver, 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, activeRecord, and, avg, belongsTo, column, columnsOf, count, createEngine, createSyncEngine, del, detectDialect, fromDict, getDialect, hasMany, insert, isCondition, join, loadRelations, max, min, not, or, parse, parseDatabaseUrl, select, sql, stringify, sum, toCondNode, toDict, toJSON, update };
|