tempest-db-js 0.1.0 → 0.2.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 +1132 -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-F36ZSQAN.js → chunk-AGDD7K3F.js} +461 -47
- package/dist/chunk-AGDD7K3F.js.map +1 -0
- package/dist/chunk-QMW4NKMH.js +1060 -0
- package/dist/chunk-QMW4NKMH.js.map +1 -0
- package/dist/index.cjs +467 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +293 -10
- package/dist/index.d.ts +293 -10
- package/dist/index.js +1 -1
- package/dist/migrations/index.cjs +143 -24
- package/dist/migrations/index.cjs.map +1 -1
- package/dist/migrations/index.d.cts +75 -1
- package/dist/migrations/index.d.ts +75 -1
- 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). */
|
|
@@ -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>;
|
|
@@ -517,11 +611,26 @@ type QueryNode = SelectNode | InsertNode | UpdateNode | DeleteNode | JoinNode;
|
|
|
517
611
|
*/
|
|
518
612
|
declare abstract class BaseDialect {
|
|
519
613
|
abstract readonly name: "sqlite" | "postgresql";
|
|
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,6 +638,15 @@ 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;
|
|
532
650
|
private compileUpdate;
|
|
533
651
|
private compileDelete;
|
|
534
652
|
private compileJoin;
|
|
@@ -576,14 +694,35 @@ interface AsyncDriver {
|
|
|
576
694
|
execute(sql: string, params: readonly unknown[]): Promise<DriverResult>;
|
|
577
695
|
/** Lazily iterate rows (for `.stream()`), if the driver supports it. */
|
|
578
696
|
iterate?(sql: string, params: readonly unknown[]): AsyncIterableIterator<Record<string, unknown>>;
|
|
697
|
+
/**
|
|
698
|
+
* Reserve a single pinned connection for the duration of a transaction.
|
|
699
|
+
* Pooled drivers (PostgreSQL) MUST implement this so `BEGIN`/`COMMIT` and the
|
|
700
|
+
* statements between them all run on the same connection. Single-connection
|
|
701
|
+
* drivers (SQLite) may omit it — `transaction` then runs on the shared handle.
|
|
702
|
+
*/
|
|
703
|
+
reserve?(): Promise<ReservedAsyncDriver>;
|
|
579
704
|
close(): Promise<void>;
|
|
580
705
|
}
|
|
706
|
+
/** An {@link AsyncDriver} pinned to one connection, used inside a transaction. */
|
|
707
|
+
interface ReservedAsyncDriver extends AsyncDriver {
|
|
708
|
+
/** Return the pinned connection to the pool. */
|
|
709
|
+
release(): Promise<void>;
|
|
710
|
+
}
|
|
581
711
|
/** SQLite driver backed by Node's built-in `node:sqlite` (zero install). */
|
|
582
712
|
declare class NodeSqliteDriver implements SyncDriver {
|
|
583
713
|
private readonly db;
|
|
714
|
+
/**
|
|
715
|
+
* Prepared-statement cache keyed by SQL text. tempest-db-js always
|
|
716
|
+
* parameterizes, so a query shape maps to one stable SQL string — reusing the
|
|
717
|
+
* compiled statement avoids re-`prepare()` on every call (the dominant cost of
|
|
718
|
+
* per-row inserts and point lookups).
|
|
719
|
+
*/
|
|
720
|
+
private readonly statements;
|
|
584
721
|
constructor(database: any);
|
|
585
722
|
/** Open a `node:sqlite` database at the given path (or `:memory:`). */
|
|
586
723
|
static open(path: string): NodeSqliteDriver;
|
|
724
|
+
/** Return the cached prepared statement for `sql`, preparing it on first use. */
|
|
725
|
+
private prepare;
|
|
587
726
|
execute(sql: string, params: readonly unknown[]): DriverResult;
|
|
588
727
|
iterate(sql: string, params: readonly unknown[]): IterableIterator<Record<string, unknown>>;
|
|
589
728
|
close(): void;
|
|
@@ -607,6 +746,34 @@ type RowOf<B> = B extends {
|
|
|
607
746
|
declare class NoResultError extends Error {
|
|
608
747
|
constructor(message: string);
|
|
609
748
|
}
|
|
749
|
+
/**
|
|
750
|
+
* Raised when the driver rejects a statement. Wraps the original driver error
|
|
751
|
+
* and attaches the offending SQL and its bound parameters, so a failure points
|
|
752
|
+
* at the exact query instead of an opaque driver message.
|
|
753
|
+
*/
|
|
754
|
+
declare class QueryExecutionError extends Error {
|
|
755
|
+
/** The original error thrown by the driver. */
|
|
756
|
+
readonly cause: unknown;
|
|
757
|
+
/** The SQL that failed. */
|
|
758
|
+
readonly sql: string;
|
|
759
|
+
/** The bound parameters, in order. */
|
|
760
|
+
readonly params: readonly unknown[];
|
|
761
|
+
constructor(
|
|
762
|
+
/** The original error thrown by the driver. */
|
|
763
|
+
cause: unknown,
|
|
764
|
+
/** The SQL that failed. */
|
|
765
|
+
sql: string,
|
|
766
|
+
/** The bound parameters, in order. */
|
|
767
|
+
params: readonly unknown[]);
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* A hook invoked for every statement a session runs (query logging / tracing).
|
|
771
|
+
* Errors thrown by the logger are ignored so logging never breaks execution.
|
|
772
|
+
*/
|
|
773
|
+
type QueryLogger = (event: {
|
|
774
|
+
readonly sql: string;
|
|
775
|
+
readonly params: readonly unknown[];
|
|
776
|
+
}) => void;
|
|
610
777
|
/** Synchronous result view over already-fetched rows. */
|
|
611
778
|
declare class SyncResult<Row> {
|
|
612
779
|
private readonly rows;
|
|
@@ -636,7 +803,13 @@ declare class AsyncResult<Row> {
|
|
|
636
803
|
declare class SyncSession {
|
|
637
804
|
private readonly driver;
|
|
638
805
|
private readonly dialect;
|
|
639
|
-
|
|
806
|
+
/** Optional per-statement logger (query tracing). */
|
|
807
|
+
private readonly logger?;
|
|
808
|
+
constructor(driver: SyncDriver, dialect: BaseDialect,
|
|
809
|
+
/** Optional per-statement logger (query tracing). */
|
|
810
|
+
logger?: QueryLogger | undefined);
|
|
811
|
+
/** Log, run, and error-wrap one raw statement. */
|
|
812
|
+
private exec;
|
|
640
813
|
/** Compile, run, and coerce a builder into a result. */
|
|
641
814
|
execute<B extends Executable>(builder: B): SyncResult<RowOf<B>>;
|
|
642
815
|
/** Run `fn` inside a transaction: commit on success, rollback on throw. */
|
|
@@ -649,17 +822,27 @@ declare class SyncSession {
|
|
|
649
822
|
*/
|
|
650
823
|
stream<B extends Executable>(builder: B): IterableIterator<RowOf<B>>;
|
|
651
824
|
close(): void;
|
|
825
|
+
/** `using session = ...` closes the driver when the scope exits. */
|
|
826
|
+
[Symbol.dispose](): void;
|
|
652
827
|
}
|
|
653
828
|
/** An asynchronous unit of work. */
|
|
654
829
|
declare class AsyncSession {
|
|
655
830
|
private readonly driver;
|
|
656
831
|
private readonly dialect;
|
|
657
|
-
|
|
832
|
+
/** Optional per-statement logger (query tracing). */
|
|
833
|
+
private readonly logger?;
|
|
834
|
+
constructor(driver: AsyncDriver, dialect: BaseDialect,
|
|
835
|
+
/** Optional per-statement logger (query tracing). */
|
|
836
|
+
logger?: QueryLogger | undefined);
|
|
837
|
+
/** Log, run, and error-wrap one raw statement. */
|
|
838
|
+
private exec;
|
|
658
839
|
execute<B extends Executable>(builder: B): AsyncResult<RowOf<B>>;
|
|
659
840
|
/** Lazily iterate result rows. Uses driver streaming when available. */
|
|
660
841
|
stream<B extends Executable>(builder: B): AsyncIterableIterator<RowOf<B>>;
|
|
661
842
|
transaction<T>(fn: (tx: AsyncSession) => Promise<T>): Promise<T>;
|
|
662
843
|
close(): Promise<void>;
|
|
844
|
+
/** `await using session = ...` closes the driver when the scope exits. */
|
|
845
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
663
846
|
}
|
|
664
847
|
/** Connection-pool tuning (PostgreSQL; ignored by SQLite). */
|
|
665
848
|
interface PoolOptions {
|
|
@@ -676,24 +859,35 @@ interface EngineOptions {
|
|
|
676
859
|
readonly driver?: string;
|
|
677
860
|
/** Connection-pool tuning (PostgreSQL only). */
|
|
678
861
|
readonly pool?: PoolOptions;
|
|
862
|
+
/**
|
|
863
|
+
* Called for every statement a session runs — SQL + bound params. Use for
|
|
864
|
+
* query logging/tracing. Thrown errors are swallowed so it never breaks a query.
|
|
865
|
+
*/
|
|
866
|
+
readonly onQuery?: QueryLogger;
|
|
679
867
|
}
|
|
680
868
|
/** A synchronous engine (SQLite only). */
|
|
681
869
|
declare class SyncEngine {
|
|
682
870
|
private readonly driver;
|
|
871
|
+
private readonly logger?;
|
|
683
872
|
readonly dialect: Dialect;
|
|
684
|
-
constructor(driver: SyncDriver);
|
|
873
|
+
constructor(driver: SyncDriver, logger?: QueryLogger | undefined);
|
|
685
874
|
session(): SyncSession;
|
|
686
875
|
transaction<T>(fn: (tx: SyncSession) => T): T;
|
|
687
876
|
close(): void;
|
|
877
|
+
/** `using engine = createSyncEngine(...)` closes the pool when the scope exits. */
|
|
878
|
+
[Symbol.dispose](): void;
|
|
688
879
|
}
|
|
689
880
|
/** An asynchronous engine. */
|
|
690
881
|
declare class AsyncEngine {
|
|
691
882
|
private readonly driver;
|
|
692
883
|
readonly dialect: Dialect;
|
|
693
|
-
|
|
884
|
+
private readonly logger?;
|
|
885
|
+
constructor(driver: AsyncDriver, dialect: Dialect, logger?: QueryLogger | undefined);
|
|
694
886
|
session(): AsyncSession;
|
|
695
887
|
transaction<T>(fn: (tx: AsyncSession) => Promise<T>): Promise<T>;
|
|
696
888
|
close(): Promise<void>;
|
|
889
|
+
/** `await using engine = createEngine(...)` closes the pool when the scope exits. */
|
|
890
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
697
891
|
}
|
|
698
892
|
/**
|
|
699
893
|
* Create a **synchronous** engine from a database URL. SQLite only — PostgreSQL
|
|
@@ -785,6 +979,91 @@ declare class BaseRepository<C extends ModelClass> {
|
|
|
785
979
|
paginate(filter?: PaginationFilter<InferModel<C>>): Promise<PaginationResult<InferModel<C>>>;
|
|
786
980
|
}
|
|
787
981
|
|
|
982
|
+
/**
|
|
983
|
+
* tempest-db-js — opt-in active-record layer.
|
|
984
|
+
*
|
|
985
|
+
* The library's default return shape is a plain inferred object (a locked design
|
|
986
|
+
* decision — see the roadmap). This module adds an **opt-in** wrapper for code
|
|
987
|
+
* that prefers instance methods: `ActiveRecord` holds the plain row on `.data`
|
|
988
|
+
* and exposes `save` / `update` / `delete` / `reload` over an async session. It
|
|
989
|
+
* never replaces the default plain-object return — you reach for it explicitly.
|
|
990
|
+
*/
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* An opt-in active-record wrapper around a single row.
|
|
994
|
+
*
|
|
995
|
+
* The current field values live on {@link data} (a plain, typed row object).
|
|
996
|
+
* Mutating methods persist through the bound session and refresh `data`.
|
|
997
|
+
*
|
|
998
|
+
* @typeParam C - the model class.
|
|
999
|
+
*/
|
|
1000
|
+
declare class ActiveRecord<C extends ModelClass> {
|
|
1001
|
+
private readonly model;
|
|
1002
|
+
private readonly session;
|
|
1003
|
+
/** The current field values (a plain, typed row). */
|
|
1004
|
+
data: InferModel<C>;
|
|
1005
|
+
private readonly pk;
|
|
1006
|
+
constructor(model: C, session: AsyncSession,
|
|
1007
|
+
/** The current field values (a plain, typed row). */
|
|
1008
|
+
data: InferModel<C>);
|
|
1009
|
+
/** The primary-key value of the wrapped row. */
|
|
1010
|
+
private pkValue;
|
|
1011
|
+
private pkFilter;
|
|
1012
|
+
/**
|
|
1013
|
+
* Persist the current `data` — insert if new, otherwise overwrite the existing
|
|
1014
|
+
* row (upsert on the primary key). Refreshes `data` from the returned row.
|
|
1015
|
+
*
|
|
1016
|
+
* @returns This wrapper, for chaining.
|
|
1017
|
+
*/
|
|
1018
|
+
save(): Promise<this>;
|
|
1019
|
+
/**
|
|
1020
|
+
* Update the given columns for this row and merge them into `data`.
|
|
1021
|
+
*
|
|
1022
|
+
* @param patch The columns to change.
|
|
1023
|
+
* @returns This wrapper, for chaining.
|
|
1024
|
+
*/
|
|
1025
|
+
update(patch: Partial<InferModel<C>>): Promise<this>;
|
|
1026
|
+
/**
|
|
1027
|
+
* Delete this row.
|
|
1028
|
+
*
|
|
1029
|
+
* @returns The number of rows affected (0 or 1).
|
|
1030
|
+
*/
|
|
1031
|
+
delete(): Promise<number>;
|
|
1032
|
+
/**
|
|
1033
|
+
* Re-fetch this row by primary key and refresh `data`.
|
|
1034
|
+
*
|
|
1035
|
+
* @returns This wrapper, for chaining.
|
|
1036
|
+
* @throws When the row no longer exists.
|
|
1037
|
+
*/
|
|
1038
|
+
reload(): Promise<this>;
|
|
1039
|
+
}
|
|
1040
|
+
/** A small factory binding a model + session for producing {@link ActiveRecord}s. */
|
|
1041
|
+
interface ActiveRecordManager<C extends ModelClass> {
|
|
1042
|
+
/** Wrap an existing row (already loaded) as an active record. */
|
|
1043
|
+
wrap(row: InferModel<C>): ActiveRecord<C>;
|
|
1044
|
+
/** Build an unsaved active record from insert data; call `.save()` to persist. */
|
|
1045
|
+
create(data: InferInsert<C>): ActiveRecord<C>;
|
|
1046
|
+
/** Fetch a row by primary key and wrap it, or `null` if absent. */
|
|
1047
|
+
get(id: unknown): Promise<ActiveRecord<C> | null>;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Create an {@link ActiveRecordManager} for a model over a session.
|
|
1051
|
+
*
|
|
1052
|
+
* @param model The model class.
|
|
1053
|
+
* @param session The async session to persist through.
|
|
1054
|
+
* @returns A manager that wraps/fetches rows as active records.
|
|
1055
|
+
*
|
|
1056
|
+
* @example
|
|
1057
|
+
* ```ts
|
|
1058
|
+
* const users = activeRecord(User, engine.session());
|
|
1059
|
+
* const u = users.create({ name: "Ana", age: 30 });
|
|
1060
|
+
* await u.save();
|
|
1061
|
+
* await u.update({ age: 31 });
|
|
1062
|
+
* await u.delete();
|
|
1063
|
+
* ```
|
|
1064
|
+
*/
|
|
1065
|
+
declare function activeRecord<C extends ModelClass>(model: C, session: AsyncSession): ActiveRecordManager<C>;
|
|
1066
|
+
|
|
788
1067
|
/**
|
|
789
1068
|
* tempest-db-js — typed relations (hasMany / belongsTo) with eager loading.
|
|
790
1069
|
*
|
|
@@ -1031,8 +1310,12 @@ declare abstract class Model {
|
|
|
1031
1310
|
* Instantiates the class once and collects every field that is a `Column`. Used
|
|
1032
1311
|
* by the serialization layer and (Phase 6) the migration schema reflector.
|
|
1033
1312
|
*
|
|
1313
|
+
* The result is **memoized per class** — a model's columns never change at
|
|
1314
|
+
* runtime, and this is called once per row on hot read paths (coercion, joins),
|
|
1315
|
+
* so re-instantiating the class every time would dominate large result sets.
|
|
1316
|
+
*
|
|
1034
1317
|
* @param model The model class (subclass of `Model`).
|
|
1035
|
-
* @returns A record of column name → `Column` instance.
|
|
1318
|
+
* @returns A record of column name → `Column` instance (do not mutate).
|
|
1036
1319
|
*/
|
|
1037
1320
|
declare function columnsOf(model: ModelClass): Record<string, Column<unknown>>;
|
|
1038
1321
|
/** Pull the static type out of a Column. */
|
|
@@ -1081,4 +1364,4 @@ type InferInsert<C extends ModelClass> = Simplify<{
|
|
|
1081
1364
|
[K in Exclude<ColumnKeys<InstanceType<C>>, OptionalInsertKeys<InstanceType<C>>>]: ColValue<InstanceType<C>[K]>;
|
|
1082
1365
|
}>;
|
|
1083
1366
|
|
|
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 };
|
|
1367
|
+
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, 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 };
|