uql-orm 0.21.0 → 0.23.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 +5 -4
- package/dist/browser/uql-browser.min.js.map +3 -3
- package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
- package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
- package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
- package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
- package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
- package/dist/cockroachdb/cockroachDialect.js +33 -18
- package/dist/d1/d1Querier.d.ts +4 -3
- package/dist/d1/d1Querier.js +0 -3
- package/dist/d1/d1SqliteDialect.d.ts +7 -0
- package/dist/d1/d1SqliteDialect.js +9 -0
- package/dist/dialect/abstractDialect.d.ts +8 -3
- package/dist/dialect/abstractDialect.js +9 -4
- package/dist/dialect/abstractSqlDialect.d.ts +49 -8
- package/dist/dialect/abstractSqlDialect.js +95 -47
- package/dist/dialect/index.d.ts +1 -0
- package/dist/dialect/index.js +1 -0
- package/dist/dialect/indexSqlDialect.d.ts +57 -0
- package/dist/dialect/indexSqlDialect.js +101 -0
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +26 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +57 -1
- package/dist/dialect/pgLikeSqlDialect.d.ts +37 -22
- package/dist/dialect/pgLikeSqlDialect.js +97 -68
- package/dist/dialect/vectorCast.d.ts +21 -0
- package/dist/dialect/vectorCast.js +28 -0
- package/dist/dialect/vectorSqlDialect.d.ts +10 -3
- package/dist/dialect/vectorSqlDialect.js +19 -8
- 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 -8
- package/dist/entity/metadata/definition.js +56 -100
- package/dist/http/contract.js +1 -2
- package/dist/http/handler.js +5 -26
- package/dist/http/query.js +1 -1
- package/dist/libsql/libsqlDialect.d.ts +12 -2
- package/dist/libsql/libsqlDialect.js +14 -2
- package/dist/libsql/libsqlQuerier.d.ts +9 -22
- package/dist/libsql/libsqlQuerier.js +9 -85
- package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
- package/dist/libsql/libsqlQuerierPool.js +17 -15
- package/dist/maria/mariaDialect.d.ts +26 -4
- package/dist/maria/mariaDialect.js +54 -15
- package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
- package/dist/migrate/acquireQuerierForMigrations.js +28 -0
- package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
- package/dist/migrate/builder/migrationBuilder.js +58 -76
- package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
- package/dist/migrate/builder/splitSqlStatements.js +0 -2
- package/dist/migrate/builder/tableBuilder.d.ts +12 -2
- package/dist/migrate/builder/tableBuilder.js +71 -119
- package/dist/migrate/builder/types.d.ts +30 -41
- package/dist/migrate/cli-config.js +24 -8
- package/dist/migrate/cli.d.ts +3 -2
- package/dist/migrate/cli.js +26 -49
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
- package/dist/migrate/codegen/entityCodeGenerator.js +8 -67
- package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
- package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
- package/dist/migrate/codegen/index.d.ts +0 -3
- package/dist/migrate/codegen/index.js +0 -6
- package/dist/migrate/drift/driftDetector.d.ts +10 -1
- package/dist/migrate/drift/driftDetector.js +22 -5
- package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
- package/dist/migrate/generator/indexNodeToSchema.js +14 -0
- package/dist/migrate/generator/mongoCommand.d.ts +57 -0
- package/dist/migrate/generator/mongoCommand.js +26 -0
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
- package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
- package/dist/migrate/index.d.ts +0 -1
- package/dist/migrate/index.js +0 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +30 -11
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +61 -57
- package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
- package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
- package/dist/migrate/introspection/mongoIntrospector.js +9 -15
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
- package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
- package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
- package/dist/migrate/introspection/postgresIntrospector.js +4 -10
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
- package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
- package/dist/migrate/migrator.d.ts +23 -3
- package/dist/migrate/migrator.js +101 -150
- package/dist/migrate/schemaGenerator.d.ts +26 -37
- package/dist/migrate/schemaGenerator.js +69 -174
- package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
- package/dist/migrate/schemaGeneratorAsync.js +3 -3
- package/dist/migrate/storage/databaseStorage.d.ts +2 -0
- package/dist/migrate/storage/databaseStorage.js +12 -27
- package/dist/mongo/mongoDialect.d.ts +2 -0
- package/dist/mongo/mongoDialect.js +14 -3
- package/dist/mongo/mongodbQuerier.js +171 -229
- package/dist/mysql/mysqlDialect.d.ts +8 -0
- package/dist/mysql/mysqlDialect.js +13 -0
- 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 +3 -0
- package/dist/querier/abstractQuerierPool.js +5 -3
- package/dist/querier/abstractSqlQuerier.d.ts +1 -3
- package/dist/querier/abstractSqlQuerier.js +48 -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/canonicalType.d.ts +14 -2
- package/dist/schema/canonicalType.js +41 -31
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/schemaASTBuilder.d.ts +9 -2
- package/dist/schema/schemaASTBuilder.js +58 -49
- package/dist/schema/types.d.ts +10 -58
- package/dist/sqlite/abstractSqliteQuerier.d.ts +46 -0
- package/dist/sqlite/abstractSqliteQuerier.js +44 -0
- package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
- package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
- package/dist/sqlite/hranaQuerier.d.ts +54 -0
- package/dist/sqlite/hranaQuerier.js +68 -0
- package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
- package/dist/sqlite/hranaQuerierPool.js +25 -0
- package/dist/sqlite/index.d.ts +3 -1
- package/dist/sqlite/index.js +3 -1
- 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 +18 -5
- package/dist/sqlite/sqliteDialect.js +23 -36
- package/dist/sqlite/sqliteQuerier.d.ts +16 -9
- package/dist/sqlite/sqliteQuerier.js +4 -27
- package/dist/sqlite/sqliteQuerierPool.d.ts +11 -14
- package/dist/sqlite/sqliteQuerierPool.js +16 -24
- package/dist/turso/index.d.ts +3 -0
- package/dist/turso/index.js +3 -0
- package/dist/turso/local.d.ts +3 -0
- package/dist/turso/local.js +3 -0
- package/dist/turso/tursoDialect.d.ts +15 -0
- package/dist/turso/tursoDialect.js +18 -0
- package/dist/turso/tursoLocalQuerier.d.ts +25 -0
- package/dist/turso/tursoLocalQuerier.js +20 -0
- package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
- package/dist/turso/tursoLocalQuerierPool.js +39 -0
- package/dist/turso/tursoQuerier.d.ts +10 -0
- package/dist/turso/tursoQuerier.js +10 -0
- package/dist/turso/tursoQuerierPool.d.ts +39 -0
- package/dist/turso/tursoQuerierPool.js +38 -0
- package/dist/type/config.d.ts +0 -6
- package/dist/type/dialect.d.ts +12 -4
- package/dist/type/dialect.js +7 -1
- package/dist/type/entity.d.ts +154 -26
- package/dist/type/migration.d.ts +32 -23
- package/dist/type/querier.d.ts +6 -0
- package/dist/type/vector.d.ts +5 -2
- package/dist/util/hook.util.js +1 -1
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/dist/util/indexColumn.util.d.ts +6 -0
- package/dist/util/indexColumn.util.js +26 -0
- package/package.json +21 -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 -29
- 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/entityMerger.d.ts +0 -114
- package/dist/migrate/codegen/entityMerger.js +0 -293
- package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
- package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
- package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
- package/dist/migrate/codegen/smartRelationDetector.js +0 -134
- package/dist/migrate/generator/index.d.ts +0 -2
- package/dist/migrate/generator/index.js +0 -2
- package/dist/migrate/sync/index.d.ts +0 -6
- package/dist/migrate/sync/index.js +0 -6
- package/dist/migrate/sync/schemaSync.d.ts +0 -131
- package/dist/migrate/sync/schemaSync.js +0 -259
- 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
- package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
- package/dist/sqlite/betterSqlite3Dialect.js +0 -9
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
/**
|
|
3
|
+
* The querier `@Transactional()` opened for the current async flow.
|
|
4
|
+
*
|
|
5
|
+
* Separate from the `UqlContext` storage in `context/context.ts` on purpose: that one is remapped to a
|
|
6
|
+
* synchronous browser shim which cannot propagate across `await`, and a querier handle must. Nothing in
|
|
7
|
+
* a browser bundle opens a transaction, so this stays server-only and `verify-dist` keeps it out of the
|
|
8
|
+
* browser-facing graph.
|
|
9
|
+
*/
|
|
10
|
+
const querierStorage = new AsyncLocalStorage();
|
|
11
|
+
/** Runs `callback` with `querier` as the ambient one, for the whole async flow beneath it. */
|
|
12
|
+
export function withQuerierContext(querier, callback) {
|
|
13
|
+
return querierStorage.run(querier, callback);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The querier of the enclosing `@Transactional()` method.
|
|
17
|
+
*
|
|
18
|
+
* This is what replaced `@InjectQuerier()`: the standard decorator spec has no parameter decorators, so
|
|
19
|
+
* the querier can no longer be injected into an argument and is read from the ambient flow instead.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* class UserService {
|
|
24
|
+
* @Transactional()
|
|
25
|
+
* async register(data: Partial<User>) {
|
|
26
|
+
* await currentQuerier().insertOne(User, data);
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function currentQuerier() {
|
|
32
|
+
const querier = querierStorage.getStore();
|
|
33
|
+
if (!querier) {
|
|
34
|
+
throw new TypeError('currentQuerier() found no active querier. Call it inside a @Transactional() method, or take a querier ' +
|
|
35
|
+
'from the pool yourself with `await using querier = await pool.getQuerier()`.');
|
|
36
|
+
}
|
|
37
|
+
return querier;
|
|
38
|
+
}
|
|
39
|
+
/** The ambient querier, or `undefined` outside a transaction. For callers that can work without one. */
|
|
40
|
+
export function currentQuerierIfAny() {
|
|
41
|
+
return querierStorage.getStore();
|
|
42
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LoggerWrapper } from '../util/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* A driver error enriched with context by {@link enrichError}. `query` is always attached; `values`
|
|
4
|
+
* only when the querier's logger is configured to surface them (see {@link enrichError}) - they can
|
|
5
|
+
* carry sensitive data (PII, tokens, etc.) and would otherwise leak into whatever error-tracking
|
|
6
|
+
* pipeline (Sentry, console.error, ...) serializes the error, without the developer opting in.
|
|
7
|
+
*/
|
|
8
|
+
export interface QueryError extends Error {
|
|
9
|
+
query?: string;
|
|
10
|
+
values?: unknown[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Tags `err` with the query it failed on (as {@link QueryError}) and hands it back for the caller to
|
|
14
|
+
* throw. `values` is only attached when `logger?.willLogValues()` is true, i.e. the app already has
|
|
15
|
+
* query values surfacing somewhere (query-level or slow-query logging), so attaching them here does not
|
|
16
|
+
* introduce a new leak surface. Shared by every query call site (timed queries, streams, transaction
|
|
17
|
+
* statements) so the logic lives in one place.
|
|
18
|
+
*
|
|
19
|
+
* Returns rather than throws so `throw enrichError(...)` reads as the control flow it is, which also
|
|
20
|
+
* means callers need no `never` annotation and no unreachable-code suppression.
|
|
21
|
+
*/
|
|
22
|
+
export declare function enrichError(err: unknown, logger: LoggerWrapper | undefined, query: string, values?: unknown[]): unknown;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tags `err` with the query it failed on (as {@link QueryError}) and hands it back for the caller to
|
|
3
|
+
* throw. `values` is only attached when `logger?.willLogValues()` is true, i.e. the app already has
|
|
4
|
+
* query values surfacing somewhere (query-level or slow-query logging), so attaching them here does not
|
|
5
|
+
* introduce a new leak surface. Shared by every query call site (timed queries, streams, transaction
|
|
6
|
+
* statements) so the logic lives in one place.
|
|
7
|
+
*
|
|
8
|
+
* Returns rather than throws so `throw enrichError(...)` reads as the control flow it is, which also
|
|
9
|
+
* means callers need no `never` annotation and no unreachable-code suppression.
|
|
10
|
+
*/
|
|
11
|
+
export function enrichError(err, logger, query, values) {
|
|
12
|
+
if (err instanceof Error) {
|
|
13
|
+
const queryError = err;
|
|
14
|
+
queryError.query ??= query;
|
|
15
|
+
if (values !== undefined && logger?.willLogValues()) {
|
|
16
|
+
queryError.values ??= values;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return err;
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IsolationLevel, QuerierPool } from '../type/index.js';
|
|
2
|
+
export type TransactionalOptions = {
|
|
3
|
+
/** `required` opens a transaction when none is active; `supported` joins one but never starts one. */
|
|
4
|
+
readonly propagation?: 'supported' | 'required';
|
|
5
|
+
readonly pool?: QuerierPool;
|
|
6
|
+
readonly isolationLevel?: IsolationLevel;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Wraps the method in a transaction and publishes its querier for {@link currentQuerier} to pick up.
|
|
10
|
+
*
|
|
11
|
+
* @remarks Replaces the `@InjectQuerier()` parameter that used to receive the querier. The standard
|
|
12
|
+
* decorator spec has no parameter decorators, and the separate TC39 proposal for them is still Stage 1,
|
|
13
|
+
* so the querier travels through async-local storage instead. A nested call joins the transaction
|
|
14
|
+
* already in flight rather than opening a second one.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* class UserService {
|
|
19
|
+
* @Transactional()
|
|
20
|
+
* async register(data: Partial<User>) {
|
|
21
|
+
* await currentQuerier().insertOne(User, data);
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function Transactional({ propagation, pool, isolationLevel }?: TransactionalOptions): <This, Args extends unknown[], R>(original: (this: This, ...args: Args) => Promise<R>, context: ClassMethodDecoratorContext<This>) => (this: This, ...args: Args) => Promise<R>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getQuerierPool } from '../options.js';
|
|
2
|
+
import { currentQuerierIfAny, withQuerierContext } from './querierContext.js';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps the method in a transaction and publishes its querier for {@link currentQuerier} to pick up.
|
|
5
|
+
*
|
|
6
|
+
* @remarks Replaces the `@InjectQuerier()` parameter that used to receive the querier. The standard
|
|
7
|
+
* decorator spec has no parameter decorators, and the separate TC39 proposal for them is still Stage 1,
|
|
8
|
+
* so the querier travels through async-local storage instead. A nested call joins the transaction
|
|
9
|
+
* already in flight rather than opening a second one.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* class UserService {
|
|
14
|
+
* @Transactional()
|
|
15
|
+
* async register(data: Partial<User>) {
|
|
16
|
+
* await currentQuerier().insertOne(User, data);
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function Transactional({ propagation = 'required', pool, isolationLevel } = {}) {
|
|
22
|
+
return (original, context) => {
|
|
23
|
+
// Checked at decoration time rather than on the first call: a synchronous method cannot be wrapped in
|
|
24
|
+
// a transaction, and finding that out at startup beats finding out mid-request.
|
|
25
|
+
if (original.constructor.name !== 'AsyncFunction') {
|
|
26
|
+
throw new TypeError(`@Transactional() needs an async method, but '${String(context.name)}' is not one.`);
|
|
27
|
+
}
|
|
28
|
+
return async function (...args) {
|
|
29
|
+
// Already inside a transactional flow: join it and let the outermost call own commit and release.
|
|
30
|
+
if (currentQuerierIfAny()) {
|
|
31
|
+
return original.apply(this, args);
|
|
32
|
+
}
|
|
33
|
+
// `withQuerier` releases; `transaction` commits or rolls back. `supported` joins a transaction but
|
|
34
|
+
// never starts one, so it takes only the first half.
|
|
35
|
+
return (pool ?? getQuerierPool()).withQuerier((querier) => {
|
|
36
|
+
const run = () => original.apply(this, args);
|
|
37
|
+
return withQuerierContext(querier, () => propagation === 'supported'
|
|
38
|
+
? run()
|
|
39
|
+
: querier.transaction(run, isolationLevel ? { isolationLevel } : undefined));
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -7,15 +7,27 @@
|
|
|
7
7
|
* - TypeScript types (for entity generation)
|
|
8
8
|
*/
|
|
9
9
|
import type { AbstractDialect } from '../dialect/abstractDialect.js';
|
|
10
|
+
import type { VectorCast } from '../dialect/vectorCast.js';
|
|
10
11
|
import type { ColumnType, FieldOptions } from '../type/entity.js';
|
|
11
12
|
import type { CanonicalType, TypeCategory } from './types.js';
|
|
12
|
-
/**
|
|
13
|
-
export declare function isVectorCategory(category: TypeCategory):
|
|
13
|
+
/** Whether a category is one of the vector types, narrowing it to the cast pgvector names use. */
|
|
14
|
+
export declare function isVectorCategory(category: TypeCategory | undefined): category is VectorCast;
|
|
14
15
|
/**
|
|
15
16
|
* Parse a SQL type string into a canonical type.
|
|
16
17
|
* Handles complex types like VARCHAR(255), DECIMAL(10,2), etc.
|
|
17
18
|
*/
|
|
18
19
|
export declare function sqlToCanonical(sqlType: string): CanonicalType;
|
|
20
|
+
/**
|
|
21
|
+
* The canonical type for a column an engine reported, merging the metadata columns it reports beside
|
|
22
|
+
* the type name (`character_maximum_length` and friends) over whatever the name itself carried. This is
|
|
23
|
+
* the one place a SQL type string is parsed: everything downstream compares and renders canonical
|
|
24
|
+
* types, so no consumer has to know that `TINYINT(1)` means boolean on MySQL.
|
|
25
|
+
*/
|
|
26
|
+
export declare function canonicalColumnType(sqlType: string, reported?: {
|
|
27
|
+
length?: number;
|
|
28
|
+
precision?: number;
|
|
29
|
+
scale?: number;
|
|
30
|
+
}): CanonicalType;
|
|
19
31
|
/**
|
|
20
32
|
* Convert a canonical type to a SQL type string for a specific dialect instance.
|
|
21
33
|
*/
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// ============================================================================
|
|
10
10
|
// Vector Category Helpers
|
|
11
11
|
// ============================================================================
|
|
12
|
-
/**
|
|
12
|
+
/** Whether a category is one of the vector types, narrowing it to the cast pgvector names use. */
|
|
13
13
|
export function isVectorCategory(category) {
|
|
14
14
|
return category === 'vector' || category === 'halfvec' || category === 'sparsevec';
|
|
15
15
|
}
|
|
@@ -86,7 +86,15 @@ const SQL_TO_CANONICAL = {
|
|
|
86
86
|
halfvec: { category: 'halfvec' },
|
|
87
87
|
sparsevec: { category: 'sparsevec' },
|
|
88
88
|
};
|
|
89
|
-
|
|
89
|
+
/**
|
|
90
|
+
* pgvector is the only engine with three vector column types, so every other dialect maps all three
|
|
91
|
+
* canonical categories onto the single type it does have (see {@link MULTI_VECTOR_TYPE_DIALECTS}, the
|
|
92
|
+
* dialect-side half of the same fact).
|
|
93
|
+
*/
|
|
94
|
+
function withVectorType(scalars, vector) {
|
|
95
|
+
return { ...scalars, vector, halfvec: vector, sparsevec: vector };
|
|
96
|
+
}
|
|
97
|
+
const PG_SCALAR_MAP = {
|
|
90
98
|
integer: 'INTEGER',
|
|
91
99
|
float: 'REAL',
|
|
92
100
|
decimal: 'NUMERIC',
|
|
@@ -98,22 +106,18 @@ const PG_TYPE_MAP = {
|
|
|
98
106
|
json: 'JSONB',
|
|
99
107
|
uuid: 'UUID',
|
|
100
108
|
blob: 'BYTEA',
|
|
109
|
+
};
|
|
110
|
+
const PG_TYPE_MAP = {
|
|
111
|
+
...PG_SCALAR_MAP,
|
|
101
112
|
vector: 'VECTOR',
|
|
102
113
|
halfvec: 'HALFVEC',
|
|
103
114
|
sparsevec: 'SPARSEVEC',
|
|
104
115
|
};
|
|
105
|
-
// CockroachDB's VECTOR type is native (no extension), but it has no HALFVEC/SPARSEVEC types at
|
|
106
|
-
// all - verified live: `CREATE TABLE t (v HALFVEC(3))` is a syntax error, not just unsupported at
|
|
107
|
-
// runtime. Falls back to VECTOR, same as MariaDB (see its own halfvec/sparsevec below).
|
|
108
|
-
const CRDB_TYPE_MAP = {
|
|
109
|
-
...PG_TYPE_MAP,
|
|
110
|
-
halfvec: 'VECTOR',
|
|
111
|
-
sparsevec: 'VECTOR',
|
|
112
|
-
};
|
|
113
116
|
const CANONICAL_TO_SQL = {
|
|
114
117
|
postgres: PG_TYPE_MAP,
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
// CockroachDB's VECTOR is native, no extension needed.
|
|
119
|
+
cockroachdb: withVectorType(PG_SCALAR_MAP, 'VECTOR'),
|
|
120
|
+
mysql: withVectorType({
|
|
117
121
|
integer: 'INT',
|
|
118
122
|
float: 'FLOAT',
|
|
119
123
|
decimal: 'DECIMAL',
|
|
@@ -125,11 +129,11 @@ const CANONICAL_TO_SQL = {
|
|
|
125
129
|
json: 'JSON',
|
|
126
130
|
uuid: 'CHAR(36)',
|
|
127
131
|
blob: 'BLOB',
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
sqlite: {
|
|
132
|
+
},
|
|
133
|
+
// MySQL 9.x does have a `VECTOR` type, but no distance function outside HeatWave and no vector
|
|
134
|
+
// index, so JSON keeps the column queryable with the JSON operators and needs no conversion.
|
|
135
|
+
'JSON'),
|
|
136
|
+
sqlite: withVectorType({
|
|
133
137
|
integer: 'INTEGER',
|
|
134
138
|
float: 'REAL',
|
|
135
139
|
decimal: 'REAL',
|
|
@@ -141,11 +145,8 @@ const CANONICAL_TO_SQL = {
|
|
|
141
145
|
json: 'TEXT',
|
|
142
146
|
uuid: 'TEXT',
|
|
143
147
|
blob: 'BLOB',
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
sparsevec: 'TEXT',
|
|
147
|
-
},
|
|
148
|
-
mariadb: {
|
|
148
|
+
}, 'TEXT'),
|
|
149
|
+
mariadb: withVectorType({
|
|
149
150
|
integer: 'INT',
|
|
150
151
|
float: 'FLOAT',
|
|
151
152
|
decimal: 'DECIMAL',
|
|
@@ -157,12 +158,9 @@ const CANONICAL_TO_SQL = {
|
|
|
157
158
|
json: 'JSON',
|
|
158
159
|
uuid: 'CHAR(36)',
|
|
159
160
|
blob: 'BLOB',
|
|
160
|
-
|
|
161
|
-
halfvec: 'VECTOR', // MariaDB only supports VECTOR; no native halfvec/sparsevec
|
|
162
|
-
sparsevec: 'VECTOR',
|
|
163
|
-
},
|
|
161
|
+
}, 'VECTOR'),
|
|
164
162
|
// MongoDB uses BSON types, not SQL types. These are placeholders for compatibility.
|
|
165
|
-
mongodb: {
|
|
163
|
+
mongodb: withVectorType({
|
|
166
164
|
integer: 'int',
|
|
167
165
|
float: 'double',
|
|
168
166
|
decimal: 'decimal128',
|
|
@@ -174,10 +172,7 @@ const CANONICAL_TO_SQL = {
|
|
|
174
172
|
json: 'object',
|
|
175
173
|
uuid: 'binData',
|
|
176
174
|
blob: 'binData',
|
|
177
|
-
|
|
178
|
-
halfvec: 'array',
|
|
179
|
-
sparsevec: 'array',
|
|
180
|
-
},
|
|
175
|
+
}, 'array'),
|
|
181
176
|
};
|
|
182
177
|
/**
|
|
183
178
|
* Size variant modifiers for SQL types.
|
|
@@ -318,6 +313,21 @@ export function sqlToCanonical(sqlType) {
|
|
|
318
313
|
}
|
|
319
314
|
return result;
|
|
320
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* The canonical type for a column an engine reported, merging the metadata columns it reports beside
|
|
318
|
+
* the type name (`character_maximum_length` and friends) over whatever the name itself carried. This is
|
|
319
|
+
* the one place a SQL type string is parsed: everything downstream compares and renders canonical
|
|
320
|
+
* types, so no consumer has to know that `TINYINT(1)` means boolean on MySQL.
|
|
321
|
+
*/
|
|
322
|
+
export function canonicalColumnType(sqlType, reported = {}) {
|
|
323
|
+
const base = sqlToCanonical(sqlType);
|
|
324
|
+
return {
|
|
325
|
+
...base,
|
|
326
|
+
length: reported.length ?? base.length,
|
|
327
|
+
precision: reported.precision ?? base.precision,
|
|
328
|
+
scale: reported.scale ?? base.scale,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
321
331
|
/**
|
|
322
332
|
* Convert a canonical type to a SQL type string for a specific dialect instance.
|
|
323
333
|
*/
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -22,4 +22,4 @@ export type { BuildFromEntitiesOptions } from './schemaASTBuilder.js';
|
|
|
22
22
|
export { SchemaASTBuilder } from './schemaASTBuilder.js';
|
|
23
23
|
export type { DiffOptions } from './schemaASTDiffer.js';
|
|
24
24
|
export { diffSchemas, SchemaASTDiffer } from './schemaASTDiffer.js';
|
|
25
|
-
export type { CanonicalType, ColumnDiff, ColumnNode,
|
|
25
|
+
export type { CanonicalType, ColumnDiff, ColumnNode, Drift, DriftReport, DriftSeverity, DriftStatus, DriftType, ForeignKeyAction, IndexDiff, IndexNode, IndexSource, IndexSyncStatus, IndexType, RelationshipDiff, RelationshipNode, RelationshipSource, RelationshipType, SchemaAST as ISchemaAST, SchemaDiffResult, SizeVariant, TableDiff, TableNode, TypeCategory, ValidationError, ValidationErrorType, } from './types.js';
|
|
@@ -54,7 +54,7 @@ export declare class SchemaASTBuilder {
|
|
|
54
54
|
* foreign key column whose type doesn't match the UUID primary key it
|
|
55
55
|
* references, which Postgres (and most databases) reject outright.
|
|
56
56
|
*
|
|
57
|
-
* `field.
|
|
57
|
+
* `field.typeFromReference` (set by `defineField`, see entity/metadata/definition.ts)
|
|
58
58
|
* is what distinguishes "no type was given" from "the decorator explicitly set
|
|
59
59
|
* a type" - including explicit constructor overrides like `type: BigInt`, which
|
|
60
60
|
* a value-based check (e.g. `typeof field.type === 'string'`) would miss since
|
|
@@ -71,7 +71,14 @@ export declare class SchemaASTBuilder {
|
|
|
71
71
|
*/
|
|
72
72
|
private addRelationshipsFromEntity;
|
|
73
73
|
/**
|
|
74
|
-
* Add indexes from field options
|
|
74
|
+
* Add indexes from field options (`@Field({ index })`) and from `@Index([...])`, which have nothing
|
|
75
|
+
* in common beyond their target table.
|
|
75
76
|
*/
|
|
76
77
|
private addIndexesFromEntity;
|
|
78
|
+
/**
|
|
79
|
+
* One `@Index([...])`. Its entries keep the authored form (expression, prefix length, order) with
|
|
80
|
+
* names resolved, so the generator renders exactly what was declared; `columns` is the resolvable
|
|
81
|
+
* subset, which is what diffing and introspection compare.
|
|
82
|
+
*/
|
|
83
|
+
private addCompositeIndex;
|
|
77
84
|
}
|
|
@@ -74,7 +74,7 @@ export class SchemaASTBuilder {
|
|
|
74
74
|
* foreign key column whose type doesn't match the UUID primary key it
|
|
75
75
|
* references, which Postgres (and most databases) reject outright.
|
|
76
76
|
*
|
|
77
|
-
* `field.
|
|
77
|
+
* `field.typeFromReference` (set by `defineField`, see entity/metadata/definition.ts)
|
|
78
78
|
* is what distinguishes "no type was given" from "the decorator explicitly set
|
|
79
79
|
* a type" - including explicit constructor overrides like `type: BigInt`, which
|
|
80
80
|
* a value-based check (e.g. `typeof field.type === 'string'`) would miss since
|
|
@@ -82,7 +82,7 @@ export class SchemaASTBuilder {
|
|
|
82
82
|
* `columnType` remains the unambiguous, always-respected explicit override.
|
|
83
83
|
*/
|
|
84
84
|
resolveColumnCanonicalType(field, seen = new Set()) {
|
|
85
|
-
const hasExplicitType = !!field.columnType || !field.
|
|
85
|
+
const hasExplicitType = !!field.columnType || !field.typeFromReference;
|
|
86
86
|
if (!hasExplicitType && field.references && !seen.has(field.references)) {
|
|
87
87
|
seen.add(field.references);
|
|
88
88
|
const referencedMeta = getMeta(field.references());
|
|
@@ -160,8 +160,12 @@ export class SchemaASTBuilder {
|
|
|
160
160
|
const relatedTable = this.ast.getTable(relatedTableName);
|
|
161
161
|
if (!relatedTable)
|
|
162
162
|
continue;
|
|
163
|
-
// Only
|
|
164
|
-
|
|
163
|
+
// Only the owning side gets the FK. `mappedBy` marks the inverse side of a one-to-one, whose
|
|
164
|
+
// `references` describe how to join back (its own primary key against the owner's FK column) -
|
|
165
|
+
// reading those as a foreign key emitted a reversed constraint (`User(id) REFERENCES
|
|
166
|
+
// user_profile(creatorId)`), which SQLite rejects outright as a foreign key mismatch.
|
|
167
|
+
const ownsForeignKey = relation.cardinality === 'm1' || (relation.cardinality === '11' && !!relation.references && !relation.mappedBy);
|
|
168
|
+
if (ownsForeignKey) {
|
|
165
169
|
const references = relation.references ?? [{ local: `${key}Id`, foreign: relatedMeta.id }];
|
|
166
170
|
const localPropName = references[0].local;
|
|
167
171
|
const foreignPropName = references[0].foreign;
|
|
@@ -192,67 +196,72 @@ export class SchemaASTBuilder {
|
|
|
192
196
|
}
|
|
193
197
|
}
|
|
194
198
|
/**
|
|
195
|
-
* Add indexes from field options
|
|
199
|
+
* Add indexes from field options (`@Field({ index })`) and from `@Index([...])`, which have nothing
|
|
200
|
+
* in common beyond their target table.
|
|
196
201
|
*/
|
|
197
202
|
addIndexesFromEntity(entity, meta, resolveTableName, resolveColumnName) {
|
|
198
203
|
const tableName = resolveTableName(entity, meta);
|
|
199
204
|
const table = this.ast.getTable(tableName);
|
|
200
205
|
if (!table)
|
|
201
206
|
return;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
for (const key of Object.keys(indexFields)) {
|
|
205
|
-
const field = indexFields[key];
|
|
207
|
+
for (const key of Object.keys(meta.fields)) {
|
|
208
|
+
const field = meta.fields[key];
|
|
206
209
|
if (!field?.index)
|
|
207
210
|
continue;
|
|
208
|
-
const
|
|
209
|
-
const column = table.columns.get(columnName);
|
|
211
|
+
const column = table.columns.get(resolveColumnName(key, field));
|
|
210
212
|
if (!column)
|
|
211
213
|
continue;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
name: indexName,
|
|
214
|
+
this.ast.addIndex({
|
|
215
|
+
name: typeof field.index === 'string' ? field.index : `idx_${tableName}_${column.name}`,
|
|
215
216
|
table,
|
|
216
217
|
columns: [column],
|
|
217
218
|
unique: field.unique ?? false,
|
|
218
219
|
source: 'entity',
|
|
219
220
|
syncStatus: 'entity_only',
|
|
220
|
-
};
|
|
221
|
-
this.ast.addIndex(indexNode);
|
|
221
|
+
});
|
|
222
222
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
for (const idxMeta of meta.indexes) {
|
|
226
|
-
const columns = [];
|
|
227
|
-
for (const propName of idxMeta.columns) {
|
|
228
|
-
const field = meta.fields[propName];
|
|
229
|
-
if (!field)
|
|
230
|
-
continue;
|
|
231
|
-
const colName = resolveColumnName(propName, field);
|
|
232
|
-
const column = table.columns.get(colName);
|
|
233
|
-
if (column) {
|
|
234
|
-
columns.push(column);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
if (columns.length > 0) {
|
|
238
|
-
const indexName = idxMeta.name ?? `idx_${tableName}_${columns.map((c) => c.name).join('_')}`;
|
|
239
|
-
const indexNode = {
|
|
240
|
-
name: indexName,
|
|
241
|
-
table,
|
|
242
|
-
columns,
|
|
243
|
-
unique: idxMeta.unique ?? false,
|
|
244
|
-
type: idxMeta.type,
|
|
245
|
-
where: idxMeta.where,
|
|
246
|
-
distance: idxMeta.distance,
|
|
247
|
-
m: idxMeta.m,
|
|
248
|
-
efConstruction: idxMeta.efConstruction,
|
|
249
|
-
lists: idxMeta.lists,
|
|
250
|
-
source: 'entity',
|
|
251
|
-
syncStatus: 'entity_only',
|
|
252
|
-
};
|
|
253
|
-
this.ast.addIndex(indexNode);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
223
|
+
for (const idxMeta of meta.indexes ?? []) {
|
|
224
|
+
this.addCompositeIndex(table, meta, idxMeta, resolveColumnName);
|
|
256
225
|
}
|
|
257
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* One `@Index([...])`. Its entries keep the authored form (expression, prefix length, order) with
|
|
229
|
+
* names resolved, so the generator renders exactly what was declared; `columns` is the resolvable
|
|
230
|
+
* subset, which is what diffing and introspection compare.
|
|
231
|
+
*/
|
|
232
|
+
addCompositeIndex(table, meta, idxMeta, resolveColumnName) {
|
|
233
|
+
// An entry survives if it is an expression (nothing to resolve) or names a column that exists;
|
|
234
|
+
// an index left with none is dropped, the same as one naming only unknown columns always was.
|
|
235
|
+
const entries = idxMeta.columns
|
|
236
|
+
.map((entry) => {
|
|
237
|
+
if (entry.expression)
|
|
238
|
+
return entry;
|
|
239
|
+
const field = meta.fields[entry.column];
|
|
240
|
+
const column = field && resolveColumnName(entry.column, field);
|
|
241
|
+
return column && table.columns.has(column) ? { ...entry, column } : undefined;
|
|
242
|
+
})
|
|
243
|
+
.filter((entry) => entry !== undefined);
|
|
244
|
+
if (!entries.length)
|
|
245
|
+
return;
|
|
246
|
+
const columns = entries
|
|
247
|
+
.map((entry) => (entry.expression ? undefined : table.columns.get(entry.column)))
|
|
248
|
+
.filter((column) => column !== undefined);
|
|
249
|
+
const named = columns.length ? columns.map((column) => column.name) : entries.map((_, at) => `expr${at}`);
|
|
250
|
+
this.ast.addIndex({
|
|
251
|
+
name: idxMeta.name ?? `idx_${table.name}_${named.join('_')}`,
|
|
252
|
+
table,
|
|
253
|
+
columns,
|
|
254
|
+
entries,
|
|
255
|
+
include: idxMeta.include,
|
|
256
|
+
unique: idxMeta.unique ?? false,
|
|
257
|
+
type: idxMeta.type,
|
|
258
|
+
where: idxMeta.where,
|
|
259
|
+
distance: idxMeta.distance,
|
|
260
|
+
m: idxMeta.m,
|
|
261
|
+
efConstruction: idxMeta.efConstruction,
|
|
262
|
+
lists: idxMeta.lists,
|
|
263
|
+
source: 'entity',
|
|
264
|
+
syncStatus: 'entity_only',
|
|
265
|
+
});
|
|
266
|
+
}
|
|
258
267
|
}
|
package/dist/schema/types.d.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* A unified graph representation of database schema with relationships as first-class citizens.
|
|
5
5
|
* Enables reliable diffing, smart relation detection, and dialect-agnostic schema operations.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { IndexColumnSchema } from '../type/entity.js';
|
|
8
|
+
import type { IndexSchema } from '../type/migration.js';
|
|
8
9
|
/**
|
|
9
10
|
* Type categories universal across SQL dialects.
|
|
10
11
|
* These represent logical/semantic types, not specific SQL types.
|
|
@@ -148,24 +149,21 @@ export interface RelationshipNode {
|
|
|
148
149
|
* Index node in the schema graph.
|
|
149
150
|
* Represents a database index on one or more columns.
|
|
150
151
|
*/
|
|
151
|
-
export type IndexNode = {
|
|
152
|
-
/** Index name */
|
|
153
|
-
readonly name: string;
|
|
152
|
+
export type IndexNode = Omit<IndexSchema, 'columns'> & {
|
|
154
153
|
/** Reference to the table this index belongs to */
|
|
155
154
|
readonly table: TableNode;
|
|
156
|
-
/** Columns included in the index (order matters) */
|
|
155
|
+
/** Columns included in the index (order matters). Empty for an index over expressions only. */
|
|
157
156
|
readonly columns: ColumnNode[];
|
|
158
|
-
/**
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
readonly where?: string;
|
|
157
|
+
/**
|
|
158
|
+
* The index as authored, with names resolved: expressions, prefix lengths and per-column order that
|
|
159
|
+
* a `ColumnNode` cannot represent. The generator renders these; `columns` is what diffing compares.
|
|
160
|
+
*/
|
|
161
|
+
readonly entries?: readonly IndexColumnSchema[];
|
|
164
162
|
/** Where this index was defined */
|
|
165
163
|
readonly source?: IndexSource;
|
|
166
164
|
/** Current sync status */
|
|
167
165
|
readonly syncStatus?: IndexSyncStatus;
|
|
168
|
-
}
|
|
166
|
+
};
|
|
169
167
|
/**
|
|
170
168
|
* Root of the schema graph.
|
|
171
169
|
* Contains all tables, relationships, and provides graph operations.
|
|
@@ -258,27 +256,6 @@ export interface ValidationError {
|
|
|
258
256
|
readonly relationship?: RelationshipNode;
|
|
259
257
|
readonly tables?: TableNode[];
|
|
260
258
|
}
|
|
261
|
-
/**
|
|
262
|
-
* A detected/inferred relationship with confidence score.
|
|
263
|
-
*/
|
|
264
|
-
export interface DetectedRelation {
|
|
265
|
-
readonly type: RelationshipType;
|
|
266
|
-
readonly from: {
|
|
267
|
-
readonly table: TableNode;
|
|
268
|
-
readonly columns: ColumnNode[];
|
|
269
|
-
};
|
|
270
|
-
readonly to: {
|
|
271
|
-
readonly table: TableNode;
|
|
272
|
-
readonly columns: ColumnNode[];
|
|
273
|
-
};
|
|
274
|
-
readonly through?: TableNode;
|
|
275
|
-
/** Confidence level (0-1) */
|
|
276
|
-
readonly confidence: number;
|
|
277
|
-
/** How this relation was detected */
|
|
278
|
-
readonly source: RelationshipSource;
|
|
279
|
-
/** Suggested constraint name */
|
|
280
|
-
readonly suggestedName?: string;
|
|
281
|
-
}
|
|
282
259
|
/**
|
|
283
260
|
* Severity level for schema drift issues.
|
|
284
261
|
*/
|
|
@@ -320,28 +297,3 @@ export interface DriftReport {
|
|
|
320
297
|
readonly info: number;
|
|
321
298
|
};
|
|
322
299
|
}
|
|
323
|
-
/**
|
|
324
|
-
* Direction for schema synchronization.
|
|
325
|
-
*/
|
|
326
|
-
export type SyncDirection = 'bidirectional' | 'entity-to-db' | 'db-to-entity';
|
|
327
|
-
/**
|
|
328
|
-
* Strategy for resolving conflicts in bidirectional sync.
|
|
329
|
-
*/
|
|
330
|
-
export type ConflictResolution = 'prompt' | 'entity-wins' | 'db-wins' | 'skip';
|
|
331
|
-
/**
|
|
332
|
-
* Options for sync operations.
|
|
333
|
-
*/
|
|
334
|
-
export interface SyncOptions {
|
|
335
|
-
/** Direction of synchronization */
|
|
336
|
-
readonly direction?: SyncDirection;
|
|
337
|
-
/** How to resolve conflicts (for bidirectional) */
|
|
338
|
-
readonly conflictResolution?: ConflictResolution;
|
|
339
|
-
/** Prevent destructive changes (DROP, type narrowing) */
|
|
340
|
-
readonly safe?: boolean;
|
|
341
|
-
/** Include indexes in sync */
|
|
342
|
-
readonly includeIndexes?: boolean;
|
|
343
|
-
/** Include foreign keys in sync */
|
|
344
|
-
readonly includeForeignKeys?: boolean;
|
|
345
|
-
/** Dry run - report changes without applying */
|
|
346
|
-
readonly dryRun?: boolean;
|
|
347
|
-
}
|
|
@@ -1,3 +1,49 @@
|
|
|
1
1
|
import { AbstractSqlQuerier } from '../querier/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Values every SQLite driver accepts as a bound parameter.
|
|
4
|
+
*
|
|
5
|
+
* @remarks No `boolean`: SQLite has no boolean storage class, and both `better-sqlite3` ("SQLite3 can
|
|
6
|
+
* only bind numbers, strings, bigints, buffers, and null") and `node:sqlite` reject one outright. The
|
|
7
|
+
* dialect already binds booleans as integers (`booleanLiteral: 'integer'`), so nothing reaches a
|
|
8
|
+
* driver as one; leaving `boolean` here only invited a runtime throw that is now a compile error.
|
|
9
|
+
*/
|
|
10
|
+
export type SqliteBindValue = null | string | number | bigint | Uint8Array;
|
|
11
|
+
/** Header a SQLite driver returns for a statement without a `RETURNING` clause. */
|
|
12
|
+
export type SqliteRunResult = {
|
|
13
|
+
changes: number;
|
|
14
|
+
lastInsertRowid: number | bigint;
|
|
15
|
+
};
|
|
16
|
+
/** Bound parameters reach a driver as `unknown[]` from the compiler; every driver types them narrowly. */
|
|
17
|
+
export declare function toSqliteBindValues(values?: unknown[]): SqliteBindValue[];
|
|
18
|
+
/**
|
|
19
|
+
* A prepared statement from a driver with better-sqlite3 semantics. `better-sqlite3` and `bun:sqlite`
|
|
20
|
+
* answer synchronously, `@tursodatabase/database` with promises, and {@link PreparedSqliteQuerier}
|
|
21
|
+
* awaits either.
|
|
22
|
+
*/
|
|
23
|
+
export type SqlitePreparedStatement = {
|
|
24
|
+
/** True for any statement returning rows, including one with a `RETURNING` clause. */
|
|
25
|
+
readonly reader: boolean;
|
|
26
|
+
all(...values: SqliteBindValue[]): unknown[] | Promise<unknown[]>;
|
|
27
|
+
run(...values: SqliteBindValue[]): SqliteRunResult | Promise<SqliteRunResult>;
|
|
28
|
+
iterate(...values: SqliteBindValue[]): Iterable<unknown> | AsyncIterable<unknown>;
|
|
29
|
+
};
|
|
2
30
|
export declare abstract class AbstractSqliteQuerier extends AbstractSqlQuerier {
|
|
31
|
+
/**
|
|
32
|
+
* SQLite drivers hold a single shared handle rather than a connection from a pool, so releasing
|
|
33
|
+
* a querier returns nothing; it only asserts the unit of work was finished. Drivers owning a
|
|
34
|
+
* closable per-querier connection override this.
|
|
35
|
+
*/
|
|
36
|
+
internalRelease(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Querier for the SQLite drivers that expose prepared statements: `better-sqlite3`, `bun:sqlite`
|
|
40
|
+
* (through `adaptBunSqlite`) and the embedded Turso engine. They differ only in whether preparing and
|
|
41
|
+
* stepping are synchronous, which `await` and `for await` absorb, so the read/write/stream logic -
|
|
42
|
+
* including the `reader` rule below, whose loss silently drops inserted ids - is written once.
|
|
43
|
+
*/
|
|
44
|
+
export declare abstract class PreparedSqliteQuerier extends AbstractSqliteQuerier {
|
|
45
|
+
protected abstract prepare(query: string): SqlitePreparedStatement | Promise<SqlitePreparedStatement>;
|
|
46
|
+
internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
|
|
47
|
+
internalStream<T>(query: string, values?: unknown[]): AsyncGenerator<Awaited<T>, void, unknown>;
|
|
48
|
+
internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
|
|
3
49
|
}
|