uql-orm 0.22.0 → 0.24.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 +4 -4
- package/dist/browser/uql-browser.min.js.map +3 -3
- package/dist/dialect/abstractSqlDialect.d.ts +14 -1
- package/dist/dialect/abstractSqlDialect.js +21 -14
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +25 -0
- package/dist/dialect/pgLikeSqlDialect.d.ts +4 -7
- package/dist/dialect/pgLikeSqlDialect.js +7 -12
- package/dist/entity/decorator/bag.d.ts +35 -0
- package/dist/entity/decorator/bag.js +54 -0
- package/dist/entity/decorator/entity.d.ts +28 -2
- package/dist/entity/decorator/entity.js +40 -2
- package/dist/entity/decorator/members.d.ts +51 -0
- package/dist/entity/decorator/members.js +51 -0
- package/dist/entity/index.d.ts +3 -1
- package/dist/entity/index.js +3 -1
- package/dist/entity/metadata/definition.d.ts +23 -9
- package/dist/entity/metadata/definition.js +55 -100
- package/dist/http/contract.js +1 -2
- package/dist/http/handler.js +5 -26
- package/dist/http/query.js +1 -1
- package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
- package/dist/migrate/acquireQuerierForMigrations.js +28 -0
- package/dist/migrate/cli-config.js +24 -8
- package/dist/migrate/cli.js +5 -1
- package/dist/migrate/codegen/entityCodeGenerator.js +3 -4
- package/dist/migrate/codegen/index.d.ts +0 -1
- package/dist/migrate/codegen/index.js +0 -2
- package/dist/migrate/drift/driftDetector.d.ts +10 -1
- package/dist/migrate/drift/driftDetector.js +22 -5
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +5 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +17 -27
- package/dist/migrate/introspection/mongoIntrospector.js +7 -13
- package/dist/migrate/migrator.js +44 -81
- package/dist/migrate/storage/databaseStorage.d.ts +2 -0
- package/dist/migrate/storage/databaseStorage.js +8 -23
- package/dist/mongo/mongodbQuerier.js +171 -229
- package/dist/nestjs/uqlContextInterceptor.js +58 -22
- package/dist/nestjs/uqlModule.d.ts +2 -11
- package/dist/nestjs/uqlModule.js +93 -44
- package/dist/postgres/postgresDialect.js +2 -2
- package/dist/querier/abstractQuerier.d.ts +28 -0
- package/dist/querier/abstractQuerier.js +47 -22
- package/dist/querier/abstractQuerierPool.d.ts +21 -1
- package/dist/querier/abstractQuerierPool.js +113 -3
- package/dist/querier/abstractSqlQuerier.d.ts +1 -3
- package/dist/querier/abstractSqlQuerier.js +51 -96
- package/dist/querier/index.d.ts +3 -1
- package/dist/querier/index.js +3 -1
- package/dist/querier/querierContext.browser.d.ts +12 -0
- package/dist/querier/querierContext.browser.js +18 -0
- package/dist/querier/querierContext.d.ts +22 -0
- package/dist/querier/querierContext.js +42 -0
- package/dist/querier/queryError.d.ts +22 -0
- package/dist/querier/queryError.js +20 -0
- package/dist/querier/transactional.d.ts +26 -0
- package/dist/querier/transactional.js +43 -0
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/schemaASTBuilder.d.ts +1 -1
- package/dist/schema/schemaASTBuilder.js +2 -2
- package/dist/schema/types.d.ts +0 -21
- package/dist/sqlite/abstractSqliteQuerier.d.ts +9 -2
- package/dist/sqlite/hranaQuerier.js +20 -42
- package/dist/sqlite/index.d.ts +1 -0
- package/dist/sqlite/index.js +1 -0
- package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
- package/dist/sqlite/localSqliteQuerierPool.js +34 -0
- package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
- package/dist/sqlite/nodeSqliteAdapter.js +28 -0
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
- package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
- package/dist/sqlite/sqliteDialect.d.ts +1 -2
- package/dist/sqlite/sqliteDialect.js +0 -13
- package/dist/sqlite/sqliteQuerierPool.d.ts +10 -23
- package/dist/sqlite/sqliteQuerierPool.js +9 -28
- package/dist/type/entity.d.ts +95 -28
- package/dist/type/querier.d.ts +8 -35
- package/dist/type/querierPool.d.ts +13 -12
- package/dist/type/universalQuerier.d.ts +23 -7
- package/dist/util/hook.util.js +1 -1
- package/package.json +7 -16
- package/dist/entity/decorator/field.d.ts +0 -2
- package/dist/entity/decorator/field.js +0 -7
- package/dist/entity/decorator/filter.d.ts +0 -7
- package/dist/entity/decorator/filter.js +0 -11
- package/dist/entity/decorator/hook.d.ts +0 -7
- package/dist/entity/decorator/hook.js +0 -14
- package/dist/entity/decorator/id.d.ts +0 -2
- package/dist/entity/decorator/id.js +0 -7
- package/dist/entity/decorator/index-decorator.d.ts +0 -22
- package/dist/entity/decorator/index-decorator.js +0 -26
- package/dist/entity/decorator/index.d.ts +0 -8
- package/dist/entity/decorator/index.js +0 -8
- package/dist/entity/decorator/relation.d.ts +0 -8
- package/dist/entity/decorator/relation.js +0 -19
- package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
- package/dist/migrate/codegen/smartRelationDetector.js +0 -134
- package/dist/querier/decorator/index.d.ts +0 -4
- package/dist/querier/decorator/index.js +0 -4
- package/dist/querier/decorator/injectQuerier.d.ts +0 -3
- package/dist/querier/decorator/injectQuerier.js +0 -33
- package/dist/querier/decorator/log.d.ts +0 -32
- package/dist/querier/decorator/log.js +0 -52
- package/dist/querier/decorator/serialized.d.ts +0 -6
- package/dist/querier/decorator/serialized.js +0 -13
- package/dist/querier/decorator/transactional.d.ts +0 -6
- package/dist/querier/decorator/transactional.js +0 -48
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AbstractSqlQuerierPool } from '../querier/index.js';
|
|
2
|
+
import { SqliteDialect } from './sqliteDialect.js';
|
|
3
|
+
import { SqliteQuerier } from './sqliteQuerier.js';
|
|
4
|
+
/**
|
|
5
|
+
* Pool for a SQLite database opened in this process, whichever driver provides it.
|
|
6
|
+
*
|
|
7
|
+
* The handle is shared - SQLite gives one connection per file - but each acquisition gets its own
|
|
8
|
+
* querier, so transaction state stays per unit of work. Subclasses supply only {@link createDb}: the
|
|
9
|
+
* lifecycle, and loading the extensions on the way up, are the same for `better-sqlite3`, `bun:sqlite`
|
|
10
|
+
* and `node:sqlite`, and were written out once per pool before.
|
|
11
|
+
*/
|
|
12
|
+
export class AbstractLocalSqliteQuerierPool extends AbstractSqlQuerierPool {
|
|
13
|
+
opts;
|
|
14
|
+
db;
|
|
15
|
+
constructor(opts, extra) {
|
|
16
|
+
super(new SqliteDialect({ namingStrategy: extra?.namingStrategy }), extra);
|
|
17
|
+
this.opts = opts;
|
|
18
|
+
}
|
|
19
|
+
async getQuerier() {
|
|
20
|
+
this.db ??= await this.openDb();
|
|
21
|
+
return new SqliteQuerier(this.db, this.dialect, this.extra);
|
|
22
|
+
}
|
|
23
|
+
async openDb() {
|
|
24
|
+
const db = await this.createDb();
|
|
25
|
+
for (const extension of this.opts?.extensions ?? []) {
|
|
26
|
+
db.loadExtension(extension);
|
|
27
|
+
}
|
|
28
|
+
return db;
|
|
29
|
+
}
|
|
30
|
+
async end() {
|
|
31
|
+
await this.db?.close();
|
|
32
|
+
this.db = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SqliteBindValue } from './abstractSqliteQuerier.js';
|
|
2
|
+
import type { SqliteDatabase } from './sqliteQuerier.js';
|
|
3
|
+
/**
|
|
4
|
+
* A `node:sqlite` statement: better-sqlite3-shaped, except it describes columns instead of reporting
|
|
5
|
+
* `reader`, and types `changes` as possibly `bigint` where better-sqlite3 always answers a `number`.
|
|
6
|
+
*/
|
|
7
|
+
type NodeSqliteStatement = {
|
|
8
|
+
columns(): readonly unknown[];
|
|
9
|
+
all(...values: SqliteBindValue[]): unknown[];
|
|
10
|
+
run(...values: SqliteBindValue[]): {
|
|
11
|
+
changes: number | bigint;
|
|
12
|
+
lastInsertRowid: number | bigint;
|
|
13
|
+
};
|
|
14
|
+
iterate(...values: SqliteBindValue[]): Iterable<unknown>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Structural subset of `node:sqlite`'s `DatabaseSync`, declared locally for the same reason the rest
|
|
18
|
+
* of this folder declares its driver shapes: nothing here depends on `@types/node` being in scope.
|
|
19
|
+
*/
|
|
20
|
+
export type NodeSqliteDatabase = {
|
|
21
|
+
prepare(sql: string): NodeSqliteStatement;
|
|
22
|
+
exec(sql: string): void;
|
|
23
|
+
loadExtension(path: string): void;
|
|
24
|
+
close(): void;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Presents a `node:sqlite` handle as a {@link SqliteDatabase}.
|
|
28
|
+
*
|
|
29
|
+
* @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
|
|
30
|
+
* would take the `run()` path, which discards returned rows, and inserts would report no ids.
|
|
31
|
+
* `columns()` is non-empty for exactly the statements better-sqlite3 marks as readers, including
|
|
32
|
+
* `INSERT ... RETURNING` and `DELETE ... RETURNING`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function adaptNodeSqlite(db: NodeSqliteDatabase): SqliteDatabase;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presents a `node:sqlite` handle as a {@link SqliteDatabase}.
|
|
3
|
+
*
|
|
4
|
+
* @remarks Its statements expose no `reader`, so without deriving one every `RETURNING` statement
|
|
5
|
+
* would take the `run()` path, which discards returned rows, and inserts would report no ids.
|
|
6
|
+
* `columns()` is non-empty for exactly the statements better-sqlite3 marks as readers, including
|
|
7
|
+
* `INSERT ... RETURNING` and `DELETE ... RETURNING`.
|
|
8
|
+
*/
|
|
9
|
+
export function adaptNodeSqlite(db) {
|
|
10
|
+
return {
|
|
11
|
+
prepare: (sql) => {
|
|
12
|
+
const stmt = db.prepare(sql);
|
|
13
|
+
return {
|
|
14
|
+
reader: stmt.columns().length > 0,
|
|
15
|
+
all: (...values) => stmt.all(...values),
|
|
16
|
+
// `changes` is narrowed to `number` to match every other driver; a row count cannot exceed
|
|
17
|
+
// the safe-integer range, so nothing is lost. `lastInsertRowid` keeps its `bigint` arm.
|
|
18
|
+
run: (...values) => {
|
|
19
|
+
const { changes, lastInsertRowid } = stmt.run(...values);
|
|
20
|
+
return { changes: Number(changes), lastInsertRowid };
|
|
21
|
+
},
|
|
22
|
+
iterate: (...values) => stmt.iterate(...values),
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
loadExtension: (path) => db.loadExtension(path),
|
|
26
|
+
close: () => db.close(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ExtraOptions } from '../type/index.js';
|
|
2
|
+
import { AbstractLocalSqliteQuerierPool, type LocalSqlitePoolOptions } from './localSqliteQuerierPool.js';
|
|
3
|
+
import type { SqliteDatabase } from './sqliteQuerier.js';
|
|
4
|
+
/**
|
|
5
|
+
* The `DatabaseSync` options worth surfacing, plus the loadable extensions to install. Declared here
|
|
6
|
+
* rather than imported from `node:sqlite` so this module needs no ambient Node types; unknown keys
|
|
7
|
+
* are ignored by the driver, so the list only has to cover what callers actually set.
|
|
8
|
+
*/
|
|
9
|
+
export type NodeSqlitePoolOptions = LocalSqlitePoolOptions & {
|
|
10
|
+
readonly readOnly?: boolean;
|
|
11
|
+
readonly enableForeignKeyConstraints?: boolean;
|
|
12
|
+
/** Milliseconds a locked database is retried before `SQLITE_BUSY`. */
|
|
13
|
+
readonly timeout?: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Pool backed by Node's built-in `node:sqlite`, so SQLite works with **no dependency at all** rather
|
|
17
|
+
* than requiring the `better-sqlite3` native build. Use {@link Sqlite3QuerierPool} instead when you
|
|
18
|
+
* want `better-sqlite3`, or are on Bun.
|
|
19
|
+
*
|
|
20
|
+
* @remarks `node:sqlite` needs no CLI flag from Node 22.13, and is still a release candidate
|
|
21
|
+
* (stability 1.2) as of Node 26, so `better-sqlite3` via {@link Sqlite3QuerierPool} remains the
|
|
22
|
+
* faster option for read-heavy work.
|
|
23
|
+
*/
|
|
24
|
+
export declare class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool<NodeSqlitePoolOptions> {
|
|
25
|
+
readonly filename: string;
|
|
26
|
+
constructor(filename?: string, opts?: NodeSqlitePoolOptions, extra?: ExtraOptions);
|
|
27
|
+
protected createDb(): Promise<SqliteDatabase>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AbstractLocalSqliteQuerierPool } from './localSqliteQuerierPool.js';
|
|
2
|
+
import { adaptNodeSqlite } from './nodeSqliteAdapter.js';
|
|
3
|
+
/**
|
|
4
|
+
* Pool backed by Node's built-in `node:sqlite`, so SQLite works with **no dependency at all** rather
|
|
5
|
+
* than requiring the `better-sqlite3` native build. Use {@link Sqlite3QuerierPool} instead when you
|
|
6
|
+
* want `better-sqlite3`, or are on Bun.
|
|
7
|
+
*
|
|
8
|
+
* @remarks `node:sqlite` needs no CLI flag from Node 22.13, and is still a release candidate
|
|
9
|
+
* (stability 1.2) as of Node 26, so `better-sqlite3` via {@link Sqlite3QuerierPool} remains the
|
|
10
|
+
* faster option for read-heavy work.
|
|
11
|
+
*/
|
|
12
|
+
export class NodeSqliteQuerierPool extends AbstractLocalSqliteQuerierPool {
|
|
13
|
+
filename;
|
|
14
|
+
constructor(filename = ':memory:', opts, extra) {
|
|
15
|
+
super(opts, extra);
|
|
16
|
+
this.filename = filename;
|
|
17
|
+
}
|
|
18
|
+
async createDb() {
|
|
19
|
+
const { DatabaseSync } = await import('node:sqlite');
|
|
20
|
+
const { extensions, ...driverOpts } = this.opts ?? {};
|
|
21
|
+
const nodeDb = new DatabaseSync(this.filename, {
|
|
22
|
+
...driverOpts,
|
|
23
|
+
// `node:sqlite` refuses `loadExtension` unless the database was opened with this on.
|
|
24
|
+
...(extensions?.length ? { allowExtension: true } : undefined),
|
|
25
|
+
});
|
|
26
|
+
nodeDb.exec('PRAGMA journal_mode = WAL');
|
|
27
|
+
return adaptNodeSqlite(nodeDb);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbstractSqlDialect } from '../dialect/abstractSqlDialect.js';
|
|
2
|
-
import type { DialectFeatures, EntityMeta, FieldOptions,
|
|
2
|
+
import type { DialectFeatures, EntityMeta, FieldOptions, QueryContext, QuerySizeComparisonOps, QueryTextSearchOptions, Type, VectorDistance } from '../type/index.js';
|
|
3
3
|
export declare class SqliteDialect extends AbstractSqlDialect {
|
|
4
4
|
/** Default {@link DialectFeatures} for SQLite and SQLite-derived dialects. */
|
|
5
5
|
protected readonly featureDefaults: DialectFeatures;
|
|
@@ -52,7 +52,6 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
52
52
|
protected jsonElemRef(alias: string, field?: string, asJson?: boolean): string;
|
|
53
53
|
protected getJsonPathScalarExpr(escapedColumn: string, jsonPathStr: string): string;
|
|
54
54
|
protected numericCast(expr: string): string;
|
|
55
|
-
upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E | E[]): void;
|
|
56
55
|
protected jsonCast(operand: string): string;
|
|
57
56
|
/**
|
|
58
57
|
* `json_replace` leaves an absent key (and a NULL column) untouched. Elements are read back
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AbstractSqlDialect } from '../dialect/abstractSqlDialect.js';
|
|
2
2
|
import { JSON_ELEM_ALIAS_PREFIX, JSON_PULL_ALIAS, jsonAssignCall, jsonElemExists, jsonPath, jsonRemoveCall, jsonSetTarget, } from '../dialect/jsonSql.js';
|
|
3
|
-
import { getMeta } from '../entity/index.js';
|
|
4
3
|
export class SqliteDialect extends AbstractSqlDialect {
|
|
5
4
|
/** Default {@link DialectFeatures} for SQLite and SQLite-derived dialects. */
|
|
6
5
|
featureDefaults = {
|
|
@@ -109,18 +108,6 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
109
108
|
numericCast(expr) {
|
|
110
109
|
return `CAST(${expr} AS REAL)`;
|
|
111
110
|
}
|
|
112
|
-
upsert(ctx, entity, conflictPaths, payload) {
|
|
113
|
-
const meta = getMeta(entity);
|
|
114
|
-
const updateCtx = this.createContext();
|
|
115
|
-
const update = this.getUpsertUpdateAssignments(updateCtx, meta, conflictPaths, payload, (name) => `EXCLUDED.${name}`);
|
|
116
|
-
const keysStr = this.getUpsertConflictPathsStr(meta, conflictPaths);
|
|
117
|
-
const onConflict = update ? `DO UPDATE SET ${update}` : 'DO NOTHING';
|
|
118
|
-
// Use the base (non-RETURNING) insert here: the appended RETURNING below would otherwise
|
|
119
|
-
// be doubled by `this.insert`'s own.
|
|
120
|
-
this.appendInsertValues(ctx, entity, payload);
|
|
121
|
-
ctx.append(` ON CONFLICT (${keysStr}) ${onConflict} ${this.returningId(entity)}`);
|
|
122
|
-
ctx.pushValue(...updateCtx.values);
|
|
123
|
-
}
|
|
124
111
|
jsonCast(operand) {
|
|
125
112
|
return `json(${operand})`;
|
|
126
113
|
}
|
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
import type { Options } from 'better-sqlite3';
|
|
2
|
-
import { AbstractSqlQuerierPool } from '../querier/index.js';
|
|
3
2
|
import type { ExtraOptions } from '../type/index.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { AbstractLocalSqliteQuerierPool, type LocalSqlitePoolOptions } from './localSqliteQuerierPool.js';
|
|
4
|
+
import type { SqliteDatabase } from './sqliteQuerier.js';
|
|
6
5
|
/** Driver options, plus the loadable extensions to install on the connection. */
|
|
7
|
-
export type Sqlite3PoolOptions = Options &
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
extensions?: readonly string[];
|
|
14
|
-
};
|
|
15
|
-
export declare class Sqlite3QuerierPool extends AbstractSqlQuerierPool<SqliteQuerier, SqliteDialect> {
|
|
6
|
+
export type Sqlite3PoolOptions = Options & LocalSqlitePoolOptions;
|
|
7
|
+
/**
|
|
8
|
+
* Pool for `better-sqlite3`, or `bun:sqlite` when running under Bun - the same file, through whichever
|
|
9
|
+
* driver the runtime provides.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool<Sqlite3PoolOptions> {
|
|
16
12
|
readonly filename: string | Buffer;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
constructor(filename?: string | Buffer, opts?: Sqlite3PoolOptions | undefined, extra?: ExtraOptions);
|
|
20
|
-
/**
|
|
21
|
-
* The database handle is shared (single connection), but each acquisition gets its own querier
|
|
22
|
-
* so transaction state stays per unit of work.
|
|
23
|
-
*/
|
|
24
|
-
getQuerier(): Promise<SqliteQuerier>;
|
|
25
|
-
private openDb;
|
|
26
|
-
private openDriverDb;
|
|
27
|
-
end(): Promise<void>;
|
|
13
|
+
constructor(filename?: string | Buffer, opts?: Sqlite3PoolOptions, extra?: ExtraOptions);
|
|
14
|
+
protected createDb(): Promise<SqliteDatabase>;
|
|
28
15
|
}
|
|
@@ -1,34 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { AbstractLocalSqliteQuerierPool } from './localSqliteQuerierPool.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pool for `better-sqlite3`, or `bun:sqlite` when running under Bun - the same file, through whichever
|
|
4
|
+
* driver the runtime provides.
|
|
5
|
+
*/
|
|
6
|
+
export class Sqlite3QuerierPool extends AbstractLocalSqliteQuerierPool {
|
|
5
7
|
filename;
|
|
6
|
-
opts;
|
|
7
|
-
db;
|
|
8
8
|
constructor(filename = ':memory:', opts, extra) {
|
|
9
|
-
super(
|
|
9
|
+
super(opts, extra);
|
|
10
10
|
this.filename = filename;
|
|
11
|
-
this.opts = opts;
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
* The database handle is shared (single connection), but each acquisition gets its own querier
|
|
15
|
-
* so transaction state stays per unit of work.
|
|
16
|
-
*/
|
|
17
|
-
async getQuerier() {
|
|
18
|
-
this.db ??= await this.openDb();
|
|
19
|
-
return new SqliteQuerier(this.db, this.dialect, this.extra);
|
|
20
|
-
}
|
|
21
|
-
async openDb() {
|
|
12
|
+
async createDb() {
|
|
22
13
|
// `bun:sqlite` rejects option keys it does not know, and rejects an options object carrying no
|
|
23
14
|
// open flags, so `extensions` is stripped out and what remains of it collapses back to nothing.
|
|
24
15
|
const { extensions, ...driverOpts } = this.opts ?? {};
|
|
25
|
-
const
|
|
26
|
-
for (const extension of extensions ?? []) {
|
|
27
|
-
db.loadExtension(extension);
|
|
28
|
-
}
|
|
29
|
-
return db;
|
|
30
|
-
}
|
|
31
|
-
async openDriverDb(opts) {
|
|
16
|
+
const opts = Object.keys(driverOpts).length > 0 ? driverOpts : undefined;
|
|
32
17
|
if (typeof Bun !== 'undefined') {
|
|
33
18
|
const { Database: BunDatabase } = await import('bun:sqlite');
|
|
34
19
|
const { adaptBunSqlite } = await import('./bunSqliteAdapter.bun.js');
|
|
@@ -41,8 +26,4 @@ export class Sqlite3QuerierPool extends AbstractSqlQuerierPool {
|
|
|
41
26
|
db.pragma('journal_mode = WAL');
|
|
42
27
|
return db;
|
|
43
28
|
}
|
|
44
|
-
async end() {
|
|
45
|
-
await this.db?.close();
|
|
46
|
-
this.db = undefined;
|
|
47
|
-
}
|
|
48
29
|
}
|
package/dist/type/entity.d.ts
CHANGED
|
@@ -175,14 +175,36 @@ export type JsonColumnType = 'json' | 'jsonb';
|
|
|
175
175
|
* SQL binary/blob column types
|
|
176
176
|
*/
|
|
177
177
|
export type BlobColumnType = 'blob' | 'bytea';
|
|
178
|
+
/**
|
|
179
|
+
* SQL boolean column types
|
|
180
|
+
*/
|
|
181
|
+
export type BooleanColumnType = 'bool' | 'boolean';
|
|
182
|
+
/**
|
|
183
|
+
* SQL vector column types
|
|
184
|
+
*/
|
|
185
|
+
export type VectorColumnType = 'vector' | 'halfvec' | 'sparsevec';
|
|
178
186
|
/**
|
|
179
187
|
* SQL column types supported by uql migrations
|
|
180
188
|
*/
|
|
181
|
-
export type ColumnType = NumericColumnType | StringColumnType | DateColumnType | JsonColumnType | BlobColumnType |
|
|
189
|
+
export type ColumnType = NumericColumnType | StringColumnType | DateColumnType | JsonColumnType | BlobColumnType | BooleanColumnType | VectorColumnType;
|
|
182
190
|
/**
|
|
183
191
|
* Logical types for a field
|
|
184
192
|
*/
|
|
185
193
|
export type FieldType = StringConstructor | NumberConstructor | BooleanConstructor | DateConstructor | BigIntConstructor | ColumnType;
|
|
194
|
+
/**
|
|
195
|
+
* The {@link FieldType} values legal for a field declared as `V`.
|
|
196
|
+
*
|
|
197
|
+
* This is what makes an explicit `type` an improvement over the reflected one it replaces: the
|
|
198
|
+
* annotation is checked against the property's real TypeScript type, so `@Field({ type: String })` on
|
|
199
|
+
* a `number` no longer compiles into a silent TEXT column. `unknown` shapes fall through to the full
|
|
200
|
+
* {@link FieldType}, keeping genuinely untyped fields usable.
|
|
201
|
+
*
|
|
202
|
+
* JSON is matched on the `__json` brand rather than structurally, because {@link Json} intersects its
|
|
203
|
+
* payload (`Json<string>` really does extend `string`) and would otherwise land on the string arm.
|
|
204
|
+
* Both `Json<T>` and `Json<T>[]` have to be recognised, and the array check has to precede the scalar
|
|
205
|
+
* arms so a `number[]` vector is not read as a `number`.
|
|
206
|
+
*/
|
|
207
|
+
export type TypeFor<V, T = NonNullable<V>> = IsJson<T> extends true ? JsonColumnType : IsJson<NonNullable<Unpacked<T>>> extends true ? JsonColumnType : T extends readonly number[] ? VectorColumnType : T extends string ? StringConstructor | StringColumnType : T extends number ? NumberConstructor | NumericColumnType : T extends bigint ? BigIntConstructor | NumericColumnType : T extends boolean ? BooleanConstructor | BooleanColumnType : T extends Date ? DateConstructor | DateColumnType : T extends Uint8Array ? BlobColumnType : FieldType;
|
|
186
208
|
/**
|
|
187
209
|
* Configurable options for a field
|
|
188
210
|
*/
|
|
@@ -191,11 +213,13 @@ export type FieldOptions = {
|
|
|
191
213
|
readonly isId?: true;
|
|
192
214
|
readonly type?: FieldType;
|
|
193
215
|
/**
|
|
194
|
-
* Set by `defineField` when `
|
|
195
|
-
*
|
|
216
|
+
* Set by `defineField` when the field gave `references` but no `type`, so schema generation resolves
|
|
217
|
+
* the column from the referenced primary key rather than from whatever ended up in `type`. That is
|
|
218
|
+
* what keeps a `uuid` primary key from becoming TEXT on every foreign key pointing at it.
|
|
219
|
+
* Internal bookkeeping - do not set this from a decorator.
|
|
196
220
|
* @internal
|
|
197
221
|
*/
|
|
198
|
-
readonly
|
|
222
|
+
readonly typeFromReference?: boolean;
|
|
199
223
|
/**
|
|
200
224
|
* Dimensions for vector fields. Used in schema generation.
|
|
201
225
|
* @example `@Field({ type: 'vector', dimensions: 1536 })`
|
|
@@ -270,6 +294,55 @@ export type FieldOptions = {
|
|
|
270
294
|
readonly comment?: string;
|
|
271
295
|
};
|
|
272
296
|
export type OnFieldCallback = Scalar | QueryRaw | (() => Scalar | QueryRaw);
|
|
297
|
+
/**
|
|
298
|
+
* The TypeScript types a field may be declared as, given the `type` it registers: the inverse of
|
|
299
|
+
* {@link TypeFor}.
|
|
300
|
+
*
|
|
301
|
+
* Both directions are needed because they are consumed at opposite ends. `defineEntity` keys its bulk
|
|
302
|
+
* `fields` by property name, so the property's type is already known and {@link TypeFor} narrows the
|
|
303
|
+
* `type` allowed. A decorator has it the other way round: `@Field({ type: String })` is checked before
|
|
304
|
+
* the class exists, so the only way to reach the property is to state what `type: String` implies and
|
|
305
|
+
* let the decorator's context position compare it against the real field. Neither can be derived from
|
|
306
|
+
* the other by inference, so `entityOptions.type-test.ts` asserts they agree instead.
|
|
307
|
+
*/
|
|
308
|
+
export type TsTypeOf<T> = T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BigIntConstructor ? bigint : T extends BooleanConstructor ? boolean : T extends DateConstructor ? Date : T extends StringColumnType ? string : T extends NumericColumnType ? number | bigint : T extends BooleanColumnType ? boolean : T extends DateColumnType ? Date : T extends JsonColumnType ? Json<unknown> | readonly Json<unknown>[] : T extends BlobColumnType ? Uint8Array : T extends VectorColumnType ? readonly number[] : unknown;
|
|
309
|
+
/**
|
|
310
|
+
* {@link FieldOptions} for a field declared as `V`, with `type` required and checked by
|
|
311
|
+
* {@link TypeFor}.
|
|
312
|
+
*
|
|
313
|
+
* The second arm is load-bearing rather than a convenience: a foreign-key column may omit `type` so
|
|
314
|
+
* that schema generation resolves it from the referenced primary key instead, picking up that key's
|
|
315
|
+
* `columnType`, length and chained references. Forcing `type: Number` onto
|
|
316
|
+
* `@Field({ references: () => Company })` would silently downgrade a `uuid` key to TEXT on every
|
|
317
|
+
* column pointing at it.
|
|
318
|
+
*/
|
|
319
|
+
export type FieldOptionsFor<V> = (FieldOptions & {
|
|
320
|
+
readonly type: TypeFor<V>;
|
|
321
|
+
}) | (FieldOptions & {
|
|
322
|
+
readonly references: EntityGetter;
|
|
323
|
+
readonly type?: TypeFor<V>;
|
|
324
|
+
});
|
|
325
|
+
/**
|
|
326
|
+
* The entity a relation field points at: `Company` for both `company?: Company` and
|
|
327
|
+
* `companies?: Company[]`.
|
|
328
|
+
*/
|
|
329
|
+
export type RelationTarget<V> = NonNullable<Unpacked<NonNullable<V>>>;
|
|
330
|
+
/**
|
|
331
|
+
* {@link RelationOptions} for a relation field declared as `V`, with `entity` required and pinned to
|
|
332
|
+
* `V`'s own type, and the cardinality restricted to the ones that field shape can hold. Together those
|
|
333
|
+
* reject `@ManyToOne({ entity: () => Other })` on a `Company` field, and any to-many cardinality on a
|
|
334
|
+
* field that is not an array.
|
|
335
|
+
*/
|
|
336
|
+
export type RelationOptionsFor<V> = Omit<RelationOptions<RelationTarget<V>>, 'entity' | 'cardinality'> & {
|
|
337
|
+
readonly entity: EntityGetter<RelationTarget<V>>;
|
|
338
|
+
readonly cardinality: NonNullable<V> extends readonly unknown[] ? '1m' | 'mm' : '11' | 'm1';
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* The method names of an entity, so hook registrations name a method that exists.
|
|
342
|
+
*/
|
|
343
|
+
export type MethodKey<E> = {
|
|
344
|
+
readonly [K in keyof E]-?: NonNullable<E[K]> extends (...args: never[]) => unknown ? K : never;
|
|
345
|
+
}[Key<E>];
|
|
273
346
|
export type EntityGetter<E = any> = () => Type<E>;
|
|
274
347
|
export type CascadeType = 'persist' | 'delete';
|
|
275
348
|
export type RelationOptions<E = any> = {
|
|
@@ -299,20 +372,6 @@ export type RelationOneToOneOptions<E> = RelationOptionsOwner<E> | RelationOptio
|
|
|
299
372
|
export type RelationOneToManyOptions<E> = RelationOptionsInverseSide<E> | RelationOptionsThroughOwner<E>;
|
|
300
373
|
export type RelationManyToOneOptions<E> = RelationOptionsOwner<E>;
|
|
301
374
|
export type RelationManyToManyOptions<E> = RelationOptionsThroughOwner<E> | RelationOptionsInverseSide<E>;
|
|
302
|
-
/**
|
|
303
|
-
* Wrapper type for relation type definitions in entities.
|
|
304
|
-
* Used to circumvent ESM modules circular dependency issue caused by reflection metadata saving the type of the property.
|
|
305
|
-
*
|
|
306
|
-
* Usage example:
|
|
307
|
-
* @Entity()
|
|
308
|
-
* export default class User {
|
|
309
|
-
*
|
|
310
|
-
* @OneToOne(() => Profile, profile => profile.user)
|
|
311
|
-
* profile: Relation<Profile>;
|
|
312
|
-
*
|
|
313
|
-
* }
|
|
314
|
-
*/
|
|
315
|
-
export type Relation<T> = T;
|
|
316
375
|
/**
|
|
317
376
|
* Lifecycle hook event names.
|
|
318
377
|
*/
|
|
@@ -354,8 +413,12 @@ export type IndexTypeOptions = {
|
|
|
354
413
|
* @Index([{ column: 'body', length: 64 }]) // MySQL needs a prefix on TEXT
|
|
355
414
|
* @Index(['data'], { type: 'gin' }) // JSONB containment
|
|
356
415
|
* ```
|
|
416
|
+
*
|
|
417
|
+
* `C` is the entity's `FieldKey` on the `@Index`/`defineEntity` paths, where the decorated class says
|
|
418
|
+
* which columns exist. It defaults to `string` for the migration builder's `table.index(...)`, which
|
|
419
|
+
* names raw table columns with no entity in scope.
|
|
357
420
|
*/
|
|
358
|
-
export type IndexColumnInput = string | QueryRaw | IndexColumnOptions
|
|
421
|
+
export type IndexColumnInput<C extends string = string> = C | QueryRaw | IndexColumnOptions<C>;
|
|
359
422
|
/**
|
|
360
423
|
* What an index entry can carry besides the thing being indexed. Shared with the normalized
|
|
361
424
|
* `IndexColumnSchema`, so the authored and internal shapes cannot drift apart.
|
|
@@ -373,9 +436,9 @@ export type IndexColumnModifiers = {
|
|
|
373
436
|
/** Operator class, e.g. `jsonb_path_ops` for a smaller GIN index. Postgres only. */
|
|
374
437
|
readonly opsClass?: string;
|
|
375
438
|
};
|
|
376
|
-
export type IndexColumnOptions = IndexColumnModifiers & {
|
|
439
|
+
export type IndexColumnOptions<C extends string = string> = IndexColumnModifiers & {
|
|
377
440
|
/** The column to index, or `raw(...)` for an expression. */
|
|
378
|
-
readonly column:
|
|
441
|
+
readonly column: C | QueryRaw;
|
|
379
442
|
};
|
|
380
443
|
/**
|
|
381
444
|
* One index entry, normalized: {@link IndexColumnInput}'s three authored shapes all reduce to this
|
|
@@ -439,11 +502,15 @@ export type EntityOptions<E = unknown> = {
|
|
|
439
502
|
/** Named, default-on `$where` filters (soft-delete is auto-registered from `@Field({ softDelete })`). */
|
|
440
503
|
readonly filters?: Record<string, FilterOptions<E>>;
|
|
441
504
|
/** Scalar fields; use `isId: true` on exactly one field for the primary key. */
|
|
442
|
-
readonly fields?:
|
|
443
|
-
|
|
444
|
-
|
|
505
|
+
readonly fields?: {
|
|
506
|
+
readonly [K in FieldKey<E>]?: FieldOptionsFor<E[K]>;
|
|
507
|
+
};
|
|
508
|
+
readonly relations?: {
|
|
509
|
+
readonly [K in RelationKey<E>]?: RelationOptionsFor<E[K]>;
|
|
510
|
+
};
|
|
511
|
+
readonly indexes?: readonly EntityIndexInput<FieldKey<E>>[];
|
|
445
512
|
/** Map hook events to method names on the entity class. */
|
|
446
|
-
readonly hooks?: Partial<Record<HookEvent, readonly
|
|
513
|
+
readonly hooks?: Partial<Record<HookEvent, readonly MethodKey<E>[]>>;
|
|
447
514
|
};
|
|
448
515
|
/**
|
|
449
516
|
* Everything an index carries beyond its columns, shared by `@Index`, `defineEntity` and the
|
|
@@ -452,9 +519,9 @@ export type EntityOptions<E = unknown> = {
|
|
|
452
519
|
*/
|
|
453
520
|
export type IndexOptions = DistributiveOmit<EntityIndexMeta, 'columns'>;
|
|
454
521
|
/**
|
|
455
|
-
* An index as authored, before
|
|
522
|
+
* An index as authored, before `defineIndex` normalizes its columns.
|
|
456
523
|
*/
|
|
457
|
-
export type EntityIndexInput = IndexOptions & {
|
|
458
|
-
readonly columns: readonly IndexColumnInput[];
|
|
524
|
+
export type EntityIndexInput<C extends string = string> = IndexOptions & {
|
|
525
|
+
readonly columns: readonly IndexColumnInput<C>[];
|
|
459
526
|
};
|
|
460
527
|
export {};
|
package/dist/type/querier.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { Db } from 'mongodb';
|
|
2
2
|
import type { AbstractSqlDialect } from '../dialect/index.js';
|
|
3
3
|
import type { SqlDialectName } from './dialect.js';
|
|
4
|
-
import type { HookEvent
|
|
4
|
+
import type { HookEvent } from './entity.js';
|
|
5
5
|
import type { LoggingOptions } from './logger.js';
|
|
6
6
|
import type { NamingStrategy } from './namingStrategy.js';
|
|
7
|
-
import type { Query,
|
|
8
|
-
import type { QueryAggMap, QueryAggregate, QueryAggregateResult, QueryGroupMap } from './queryAggregate.js';
|
|
7
|
+
import type { Query, QueryOne, QueryOptions, QuerySearch, QueryUpdateResult } from './query.js';
|
|
9
8
|
import type { UniversalQuerier } from './universalQuerier.js';
|
|
10
9
|
import type { Type } from './utility.js';
|
|
11
10
|
/**
|
|
@@ -56,34 +55,6 @@ export interface Querier extends UniversalQuerier {
|
|
|
56
55
|
count<E extends object>(q: QuerySearch<E> & {
|
|
57
56
|
$entity: Type<E>;
|
|
58
57
|
}, opts?: QueryOptions): Promise<number>;
|
|
59
|
-
/**
|
|
60
|
-
* Insert a single record and return its ID (provided, `onInsert`-generated, or
|
|
61
|
-
* database-generated - see {@link Querier.insertMany} for the exact semantics).
|
|
62
|
-
* Returns `undefined` when the ID cannot be determined (e.g. MySQL/SQLite non-auto-increment
|
|
63
|
-
* keys in batches without explicit IDs).
|
|
64
|
-
*/
|
|
65
|
-
insertOne<E extends object>(entity: Type<E>, payload: E): Promise<IdValue<E> | undefined>;
|
|
66
|
-
/**
|
|
67
|
-
* Insert multiple records in a single statement (auto-chunked when the batch exceeds the
|
|
68
|
-
* dialect's bind-parameter limit) and return their IDs in payload order.
|
|
69
|
-
*
|
|
70
|
-
* Provided IDs and client-generated ones (`@Id({ onInsert })`) are always returned as-is.
|
|
71
|
-
* Database-generated IDs are exact on `'returning'` dialects (Postgres, MariaDB, MongoDB);
|
|
72
|
-
* on MySQL/SQLite they are inferred from the driver header, which is only reliable for
|
|
73
|
-
* auto-increment keys in batches without explicit IDs - otherwise those entries are
|
|
74
|
-
* `undefined` rather than potentially wrong values.
|
|
75
|
-
*/
|
|
76
|
-
insertMany<E extends object>(entity: Type<E>, payload: E[]): Promise<IdValue<E>[]>;
|
|
77
|
-
updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: QueryOptions): Promise<number>;
|
|
78
|
-
/**
|
|
79
|
-
* Restore soft-deleted records (sets the soft-delete field back to `null`). Throws if the
|
|
80
|
-
* entity has no soft-delete field.
|
|
81
|
-
*/
|
|
82
|
-
restoreOneById<E extends object>(entity: Type<E>, id: IdValue<E>): Promise<number>;
|
|
83
|
-
restoreMany<E extends object>(entity: Type<E>, q: QuerySearch<E>): Promise<number>;
|
|
84
|
-
upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E): Promise<QueryUpdateResult>;
|
|
85
|
-
upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E[]): Promise<QueryUpdateResult>;
|
|
86
|
-
saveMany<E extends object>(entity: Type<E>, payload: E[]): Promise<IdValue<E>[]>;
|
|
87
58
|
/**
|
|
88
59
|
* Delete many records (soft-deletes when the entity has a soft-delete field, else removes them).
|
|
89
60
|
* Supports both entity-as-argument and entity-as-field patterns.
|
|
@@ -92,10 +63,6 @@ export interface Querier extends UniversalQuerier {
|
|
|
92
63
|
deleteMany<E extends object>(q: QuerySearch<E> & {
|
|
93
64
|
$entity: Type<E>;
|
|
94
65
|
}, opts?: QueryOptions): Promise<number>;
|
|
95
|
-
/**
|
|
96
|
-
* Run an aggregate query (GROUP BY with aggregate functions).
|
|
97
|
-
*/
|
|
98
|
-
aggregate<E extends object, const G extends QueryGroupMap<E>, const A extends QueryAggMap<E>>(entity: Type<E>, q: QueryAggregate<E, G, A>, opts?: QueryOptions): Promise<QueryAggregateResult<E, G, A>[]>;
|
|
99
66
|
/**
|
|
100
67
|
* whether this querier is in a transaction or not.
|
|
101
68
|
*/
|
|
@@ -120,6 +87,12 @@ export interface Querier extends UniversalQuerier {
|
|
|
120
87
|
* release the querier to the pool.
|
|
121
88
|
*/
|
|
122
89
|
release(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Releases the querier when an `await using` binding goes out of scope, so a unit of work cannot
|
|
92
|
+
* leak a connection on an early return or a throw.
|
|
93
|
+
* @example `await using querier = await pool.getQuerier();`
|
|
94
|
+
*/
|
|
95
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
123
96
|
}
|
|
124
97
|
export interface SqlQuerier extends Querier {
|
|
125
98
|
/**
|
|
@@ -15,24 +15,25 @@ export interface PoolRunOptions {
|
|
|
15
15
|
/**
|
|
16
16
|
* Querier pool. Read the dialect id via `pool.dialect.dialectName` (see {@link AbstractDialect.dialectName}); queriers expose the same on `querier.dialect`.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
22
|
-
* connection and serialize. Single-connection backends (better-sqlite3, Bun sqlite, D1) stay
|
|
23
|
-
* correct but always serialize on their one connection.
|
|
18
|
+
* A pool is a {@link UniversalQuerier} too, so a function that runs queries takes that type and the
|
|
19
|
+
* caller passes its own querier or the pool. `pool.op(...)` is exactly
|
|
20
|
+
* `pool.withQuerier((querier) => querier.op(...))`, so two pool calls are two units of work; when they
|
|
21
|
+
* must commit together, that is `transaction`.
|
|
24
22
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
23
|
+
* Acquiring per call is also what makes `Promise.all([pool.findMany(A, {}), pool.count(B, {})])` run on
|
|
24
|
+
* separate connections, while the same calls inside one `withQuerier`/`transaction` share a pinned
|
|
25
|
+
* connection and serialize. Single-connection backends (better-sqlite3, Bun sqlite, D1) stay correct
|
|
26
|
+
* but always serialize.
|
|
28
27
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* An enclosing `withContext` scopes pool calls (`security` filters apply), which is why they take no
|
|
29
|
+
* per-call `context` option (unlike `withQuerier`/`transaction`).
|
|
30
|
+
*
|
|
31
|
+
* Pool calls take the entity-as-argument form only; the `{ $entity }` form needs a querier.
|
|
31
32
|
*
|
|
32
33
|
* @typeParam Q - Querier implementation returned from the pool.
|
|
33
34
|
* @typeParam D - Concrete dialect class held by the pool.
|
|
34
35
|
*/
|
|
35
|
-
export interface QuerierPool<Q extends Querier = Querier, D extends AbstractDialect = AbstractDialect> extends
|
|
36
|
+
export interface QuerierPool<Q extends Querier = Querier, D extends AbstractDialect = AbstractDialect> extends UniversalQuerier {
|
|
36
37
|
/**
|
|
37
38
|
* Database dialect instance (single source of truth for dialect id and SQL/NoSQL behavior).
|
|
38
39
|
*/
|