drizzle-orm-libsql-sync 0.0.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 +83 -0
- package/dist/client-xqwnbWL8.d.mts +10 -0
- package/dist/client.d.mts +2 -0
- package/dist/client.mjs +11 -0
- package/dist/driver.d.mts +28 -0
- package/dist/driver.mjs +64 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +4 -0
- package/dist/migrator.d.mts +8 -0
- package/dist/migrator.mjs +17 -0
- package/dist/session-DuHpmegi.d.mts +66 -0
- package/dist/session.d.mts +2 -0
- package/dist/session.mjs +291 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# drizzle-orm-libsql-sync
|
|
2
|
+
|
|
3
|
+
A **synchronous** [Drizzle ORM](https://orm.drizzle.team) driver for
|
|
4
|
+
[`libsql`](https://github.com/tursodatabase/libsql-js), backed directly by the
|
|
5
|
+
libsql `Database` (`prepare`/`exec`) and targeting Drizzle v1's modern relations.
|
|
6
|
+
|
|
7
|
+
## Why this exists
|
|
8
|
+
|
|
9
|
+
Drizzle ships two relevant SQLite drivers, but neither fits a sync + libsql +
|
|
10
|
+
modern-relations use case:
|
|
11
|
+
|
|
12
|
+
- **`drizzle-orm/libsql`** is **async** (`await db.execute(...)`). Great for
|
|
13
|
+
Turso over the network, but heavy for serial Node/Bun build scripts, test
|
|
14
|
+
seeding, and benchmarks where every row insert would otherwise be `await`ed.
|
|
15
|
+
- **`drizzle-orm/better-sqlite3`** is sync, but it does a top-level
|
|
16
|
+
`import Client from "better-sqlite3"` (forcing that dependency) and historically
|
|
17
|
+
targeted Drizzle's legacy relational system.
|
|
18
|
+
|
|
19
|
+
This package gives you the missing combination: a real
|
|
20
|
+
`BaseSQLiteDatabase<"sync", …>` whose `select()/insert()/...` return rows
|
|
21
|
+
directly, bound to a libsql `Database`. libsql is a SQLite superset (more `ALTER`
|
|
22
|
+
statements, encryption-at-rest, extensions), so it's a strong local engine for
|
|
23
|
+
build/CLI/test code.
|
|
24
|
+
|
|
25
|
+
> **Note:** This is for **synchronous, local** work (Node/Bun build scripts,
|
|
26
|
+
> fixtures, benchmarks). For your app talking to Turso over the network, keep
|
|
27
|
+
> using the official async `drizzle-orm/libsql`.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm i drizzle-orm-libsql-sync drizzle-orm libsql
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`drizzle-orm` and `libsql` are peer dependencies.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import Database from "libsql";
|
|
41
|
+
import { drizzle } from "drizzle-orm-libsql-sync";
|
|
42
|
+
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
43
|
+
|
|
44
|
+
const users = sqliteTable("users", {
|
|
45
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
46
|
+
name: text("name").notNull(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Pass a path…
|
|
50
|
+
const db = drizzle("file:local.db", { schema: { users } });
|
|
51
|
+
|
|
52
|
+
// …or an existing libsql client…
|
|
53
|
+
const client = new Database(":memory:");
|
|
54
|
+
const db2 = drizzle(client, { schema: { users } });
|
|
55
|
+
|
|
56
|
+
// …or the config-object form:
|
|
57
|
+
const db3 = drizzle({ client, schema: { users } });
|
|
58
|
+
|
|
59
|
+
// Synchronous — no await:
|
|
60
|
+
db.insert(users).values({ name: "ada" }).run();
|
|
61
|
+
const rows = db.select().from(users).all();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Migrations
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { migrate } from "drizzle-orm-libsql-sync/migrator";
|
|
68
|
+
|
|
69
|
+
migrate(db, { migrationsFolder: "./drizzle" });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Transactions
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
db.transaction((tx) => {
|
|
76
|
+
tx.insert(users).values({ name: "grace" }).run();
|
|
77
|
+
// throw to roll back; nested transactions use SAVEPOINTs
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Database } from "libsql";
|
|
2
|
+
|
|
3
|
+
//#region src/client.d.ts
|
|
4
|
+
type DrizzleSyncSQLiteBindValue = null | string | number | bigint | boolean | Uint8Array;
|
|
5
|
+
/** The libsql `Database` surface the sync session relies on. */
|
|
6
|
+
type DrizzleSyncSQLiteClient = Database;
|
|
7
|
+
declare function isDrizzleSyncSQLiteClient(value: unknown): value is DrizzleSyncSQLiteClient;
|
|
8
|
+
declare function toDrizzleSyncSQLiteClient(value: unknown): DrizzleSyncSQLiteClient;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { toDrizzleSyncSQLiteClient as i, DrizzleSyncSQLiteClient as n, isDrizzleSyncSQLiteClient as r, DrizzleSyncSQLiteBindValue as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { i as toDrizzleSyncSQLiteClient, n as DrizzleSyncSQLiteClient, r as isDrizzleSyncSQLiteClient, t as DrizzleSyncSQLiteBindValue } from "./client-xqwnbWL8.mjs";
|
|
2
|
+
export { DrizzleSyncSQLiteBindValue, DrizzleSyncSQLiteClient, isDrizzleSyncSQLiteClient, toDrizzleSyncSQLiteClient };
|
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/client.ts
|
|
2
|
+
function isDrizzleSyncSQLiteClient(value) {
|
|
3
|
+
if (typeof value !== "object" || value === null) return false;
|
|
4
|
+
return typeof Reflect.get(value, "prepare") === "function" && typeof Reflect.get(value, "exec") === "function";
|
|
5
|
+
}
|
|
6
|
+
function toDrizzleSyncSQLiteClient(value) {
|
|
7
|
+
if (!isDrizzleSyncSQLiteClient(value)) throw new TypeError("Expected a libsql-compatible synchronous SQLite client.");
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { isDrizzleSyncSQLiteClient, toDrizzleSyncSQLiteClient };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { n as LibsqlSyncRunResult } from "./session-DuHpmegi.mjs";
|
|
2
|
+
import { Database } from "libsql";
|
|
3
|
+
import { AnyRelations, DrizzleConfig, EmptyRelations, entityKind } from "drizzle-orm";
|
|
4
|
+
import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core";
|
|
5
|
+
|
|
6
|
+
//#region src/driver.d.ts
|
|
7
|
+
/** Config-object form accepted by the call sites. */
|
|
8
|
+
type LibsqlSyncDrizzleConfig<TSchema extends Record<string, unknown>, TRelations extends AnyRelations> = DrizzleConfig<TSchema, TRelations> & {
|
|
9
|
+
client: Database;
|
|
10
|
+
};
|
|
11
|
+
declare class LibsqlSyncDatabase<TSchema extends Record<string, unknown> = Record<string, never>, TRelations extends AnyRelations = EmptyRelations> extends BaseSQLiteDatabase<"sync", LibsqlSyncRunResult, TSchema, TRelations> {
|
|
12
|
+
static readonly [entityKind]: string;
|
|
13
|
+
$client: Database;
|
|
14
|
+
}
|
|
15
|
+
declare function drizzleImpl<TSchema extends Record<string, unknown> = Record<string, never>, TRelations extends AnyRelations = EmptyRelations>(...params: [string] | [string, DrizzleConfig<TSchema, TRelations>] | [Database] | [Database, DrizzleConfig<TSchema, TRelations>] | [LibsqlSyncDrizzleConfig<TSchema, TRelations>]): LibsqlSyncDatabase<TSchema, TRelations> & {
|
|
16
|
+
$client: Database;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Mirrors `drizzle-orm/better-sqlite3`'s `drizzle.mock` — an in-memory libsql
|
|
20
|
+
* database, handy for type-only fixtures and tests that never touch a real
|
|
21
|
+
* connection.
|
|
22
|
+
*/
|
|
23
|
+
declare function mock<TSchema extends Record<string, unknown> = Record<string, never>, TRelations extends AnyRelations = EmptyRelations>(config?: DrizzleConfig<TSchema, TRelations>): LibsqlSyncDatabase<TSchema, TRelations>;
|
|
24
|
+
declare const drizzle: typeof drizzleImpl & {
|
|
25
|
+
mock: typeof mock;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { LibsqlSyncDatabase, LibsqlSyncDrizzleConfig, drizzle };
|
package/dist/driver.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { toDrizzleSyncSQLiteClient } from "./client.mjs";
|
|
2
|
+
import { LibsqlSyncSession } from "./session.mjs";
|
|
3
|
+
import LibsqlDatabaseCtor from "libsql";
|
|
4
|
+
import { DefaultLogger, entityKind } from "drizzle-orm";
|
|
5
|
+
import * as V1 from "drizzle-orm/_relations";
|
|
6
|
+
import { BaseSQLiteDatabase, SQLiteSyncDialect } from "drizzle-orm/sqlite-core";
|
|
7
|
+
//#region src/driver.ts
|
|
8
|
+
function isAnyObject(value) {
|
|
9
|
+
return typeof value === "object" && value !== null;
|
|
10
|
+
}
|
|
11
|
+
function isRelationsConfig(value) {
|
|
12
|
+
return isAnyObject(value);
|
|
13
|
+
}
|
|
14
|
+
function isExtractedSchemaTables(value) {
|
|
15
|
+
return isAnyObject(value);
|
|
16
|
+
}
|
|
17
|
+
var LibsqlSyncDatabase = class extends BaseSQLiteDatabase {
|
|
18
|
+
static [entityKind] = "LibsqlSyncDatabase";
|
|
19
|
+
};
|
|
20
|
+
function construct(client, drizzleConfig = {}) {
|
|
21
|
+
const sqliteClient = toDrizzleSyncSQLiteClient(client);
|
|
22
|
+
const dialect = new SQLiteSyncDialect();
|
|
23
|
+
let logger;
|
|
24
|
+
if (drizzleConfig.logger === true) logger = new DefaultLogger();
|
|
25
|
+
else if (drizzleConfig.logger !== false) logger = drizzleConfig.logger;
|
|
26
|
+
let schema;
|
|
27
|
+
if (drizzleConfig.schema) {
|
|
28
|
+
const tablesConfig = V1.extractTablesRelationalConfig(drizzleConfig.schema, V1.createTableRelationsHelpers);
|
|
29
|
+
if (!isExtractedSchemaTables(tablesConfig.tables)) throw new TypeError("Invalid relational schema tables config");
|
|
30
|
+
schema = {
|
|
31
|
+
fullSchema: drizzleConfig.schema,
|
|
32
|
+
schema: tablesConfig.tables,
|
|
33
|
+
tableNamesMap: tablesConfig.tableNamesMap
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const relations = drizzleConfig.relations ?? {};
|
|
37
|
+
if (!isRelationsConfig(relations)) throw new TypeError("Invalid drizzle relations config");
|
|
38
|
+
const db = new LibsqlSyncDatabase("sync", dialect, new LibsqlSyncSession(sqliteClient, dialect, relations, schema, { logger }), relations, schema);
|
|
39
|
+
db.$client = sqliteClient;
|
|
40
|
+
return db;
|
|
41
|
+
}
|
|
42
|
+
function isLibsqlSyncDrizzleConfig(value) {
|
|
43
|
+
return isAnyObject(value) && "client" in value && isAnyObject(Reflect.get(value, "client"));
|
|
44
|
+
}
|
|
45
|
+
function drizzleImpl(...params) {
|
|
46
|
+
const [first, second] = params;
|
|
47
|
+
if (typeof first === "string") return construct(new LibsqlDatabaseCtor(first), second);
|
|
48
|
+
if (isLibsqlSyncDrizzleConfig(first)) {
|
|
49
|
+
const { client, ...drizzleConfig } = first;
|
|
50
|
+
return construct(client, drizzleConfig);
|
|
51
|
+
}
|
|
52
|
+
return construct(first, second);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Mirrors `drizzle-orm/better-sqlite3`'s `drizzle.mock` — an in-memory libsql
|
|
56
|
+
* database, handy for type-only fixtures and tests that never touch a real
|
|
57
|
+
* connection.
|
|
58
|
+
*/
|
|
59
|
+
function mock(config) {
|
|
60
|
+
return construct(new LibsqlDatabaseCtor(":memory:"), config);
|
|
61
|
+
}
|
|
62
|
+
const drizzle = Object.assign(drizzleImpl, { mock });
|
|
63
|
+
//#endregion
|
|
64
|
+
export { LibsqlSyncDatabase, drizzle };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { i as toDrizzleSyncSQLiteClient, n as DrizzleSyncSQLiteClient, r as isDrizzleSyncSQLiteClient, t as DrizzleSyncSQLiteBindValue } from "./client-xqwnbWL8.mjs";
|
|
2
|
+
import { a as LibsqlSyncTransaction, i as LibsqlSyncSessionOptions, n as LibsqlSyncRunResult, r as LibsqlSyncSession, t as LibsqlSyncPreparedQuery } from "./session-DuHpmegi.mjs";
|
|
3
|
+
import { LibsqlSyncDatabase, LibsqlSyncDrizzleConfig, drizzle } from "./driver.mjs";
|
|
4
|
+
export { type DrizzleSyncSQLiteBindValue, type DrizzleSyncSQLiteClient, LibsqlSyncDatabase, LibsqlSyncDrizzleConfig, LibsqlSyncPreparedQuery, LibsqlSyncRunResult, LibsqlSyncSession, LibsqlSyncSessionOptions, LibsqlSyncTransaction, drizzle, isDrizzleSyncSQLiteClient, toDrizzleSyncSQLiteClient };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { isDrizzleSyncSQLiteClient, toDrizzleSyncSQLiteClient } from "./client.mjs";
|
|
2
|
+
import { LibsqlSyncPreparedQuery, LibsqlSyncSession, LibsqlSyncTransaction } from "./session.mjs";
|
|
3
|
+
import { LibsqlSyncDatabase, drizzle } from "./driver.mjs";
|
|
4
|
+
export { LibsqlSyncDatabase, LibsqlSyncPreparedQuery, LibsqlSyncSession, LibsqlSyncTransaction, drizzle, isDrizzleSyncSQLiteClient, toDrizzleSyncSQLiteClient };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LibsqlSyncDatabase } from "./driver.mjs";
|
|
2
|
+
import { AnyRelations } from "drizzle-orm";
|
|
3
|
+
import { MigrationConfig } from "drizzle-orm/migrator";
|
|
4
|
+
|
|
5
|
+
//#region src/migrator.d.ts
|
|
6
|
+
declare function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(db: LibsqlSyncDatabase<TSchema, TRelations>, config: MigrationConfig): void;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { migrate };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readMigrationFiles } from "drizzle-orm/migrator";
|
|
2
|
+
//#region src/migrator.ts
|
|
3
|
+
function migrate(db, config) {
|
|
4
|
+
const migrations = readMigrationFiles(config);
|
|
5
|
+
const dialect = Reflect.get(db, "dialect");
|
|
6
|
+
const session = Reflect.get(db, "session");
|
|
7
|
+
if (typeof dialect !== "object" || dialect === null || !("migrate" in dialect)) throw new TypeError("Database dialect does not expose migrate()");
|
|
8
|
+
const migrateFn = Reflect.get(dialect, "migrate");
|
|
9
|
+
if (typeof migrateFn !== "function") throw new TypeError("Database dialect migrate is not a function");
|
|
10
|
+
Reflect.apply(migrateFn, dialect, [
|
|
11
|
+
migrations,
|
|
12
|
+
session,
|
|
13
|
+
config
|
|
14
|
+
]);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { migrate };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { n as DrizzleSyncSQLiteClient } from "./client-xqwnbWL8.mjs";
|
|
2
|
+
import { AnyRelations, DrizzleTypeError, Logger, Query, RelationalQueryMapperConfig, entityKind } from "drizzle-orm";
|
|
3
|
+
import * as V1 from "drizzle-orm/_relations";
|
|
4
|
+
import { PreparedQueryConfig, SQLiteExecuteMethod, SQLitePreparedQuery, SQLiteSession, SQLiteSyncDialect, SQLiteTransaction, SQLiteTransactionConfig, SelectedFieldsOrdered } from "drizzle-orm/sqlite-core";
|
|
5
|
+
import { WithCacheConfig } from "drizzle-orm/cache/core/types";
|
|
6
|
+
|
|
7
|
+
//#region src/session.d.ts
|
|
8
|
+
interface LibsqlSyncSessionOptions {
|
|
9
|
+
logger?: Logger;
|
|
10
|
+
useJitMappers?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface LibsqlSyncRunResult {
|
|
13
|
+
changes: number;
|
|
14
|
+
lastInsertRowid: number | bigint;
|
|
15
|
+
}
|
|
16
|
+
type PreparedQueryConfig$1 = Omit<PreparedQueryConfig, "statement" | "run">;
|
|
17
|
+
declare class LibsqlSyncSession<TFullSchema extends Record<string, unknown>, TRelations extends AnyRelations, TSchema extends V1.TablesRelationalConfig> extends SQLiteSession<"sync", LibsqlSyncRunResult, TFullSchema, TRelations, TSchema> {
|
|
18
|
+
private client;
|
|
19
|
+
private relations;
|
|
20
|
+
private schema;
|
|
21
|
+
private options;
|
|
22
|
+
static readonly [entityKind]: string;
|
|
23
|
+
private logger;
|
|
24
|
+
constructor(client: DrizzleSyncSQLiteClient, dialect: SQLiteSyncDialect, relations: TRelations, schema: V1.RelationalSchemaConfig<TSchema> | undefined, options?: LibsqlSyncSessionOptions);
|
|
25
|
+
prepareQuery<T extends Omit<PreparedQueryConfig$1, "run">>(query: Query, fields: SelectedFieldsOrdered | undefined, executeMethod: SQLiteExecuteMethod, customResultMapper?: (rows: unknown[][], mapColumnValue?: (value: unknown) => unknown) => unknown, queryMetadata?: {
|
|
26
|
+
tables: string[];
|
|
27
|
+
type: "select" | "update" | "delete" | "insert";
|
|
28
|
+
}, _cacheConfig?: WithCacheConfig): LibsqlSyncPreparedQuery<T>;
|
|
29
|
+
prepareRelationalQuery<T extends Omit<PreparedQueryConfig$1, "run">>(query: Query, fields: SelectedFieldsOrdered | undefined, executeMethod: SQLiteExecuteMethod, customResultMapper: (rows: Record<string, unknown>[], mapColumnValue?: (value: unknown) => unknown) => unknown, config: RelationalQueryMapperConfig): LibsqlSyncPreparedQuery<T, true>;
|
|
30
|
+
transaction<T>(transaction: (tx: LibsqlSyncTransaction<TFullSchema, TRelations, TSchema>) => T, config?: SQLiteTransactionConfig): T;
|
|
31
|
+
}
|
|
32
|
+
declare class LibsqlSyncTransaction<TFullSchema extends Record<string, unknown>, TRelations extends AnyRelations, TSchema extends V1.TablesRelationalConfig> extends SQLiteTransaction<"sync", LibsqlSyncRunResult, TFullSchema, TRelations, TSchema> {
|
|
33
|
+
static readonly [entityKind]: string;
|
|
34
|
+
transaction<T>(transaction: (tx: LibsqlSyncTransaction<TFullSchema, TRelations, TSchema>) => T extends Promise<any> ? DrizzleTypeError<"Sync drivers can't use async functions in transactions!"> : T): T;
|
|
35
|
+
}
|
|
36
|
+
declare class LibsqlSyncPreparedQuery<T extends PreparedQueryConfig$1 = PreparedQueryConfig$1, TIsRqbV2 extends boolean = false> extends SQLitePreparedQuery<{
|
|
37
|
+
all: T["all"];
|
|
38
|
+
execute: T["execute"];
|
|
39
|
+
get: T["get"];
|
|
40
|
+
run: LibsqlSyncRunResult;
|
|
41
|
+
type: "sync";
|
|
42
|
+
values: T["values"];
|
|
43
|
+
}> {
|
|
44
|
+
private client;
|
|
45
|
+
private logger;
|
|
46
|
+
private fields;
|
|
47
|
+
private useJitMappers;
|
|
48
|
+
private customResultMapper?;
|
|
49
|
+
private isRqbV2Query?;
|
|
50
|
+
private rqbConfig?;
|
|
51
|
+
static readonly [entityKind]: string;
|
|
52
|
+
private jitRowMapper?;
|
|
53
|
+
private jitRqbMapper?;
|
|
54
|
+
constructor(client: DrizzleSyncSQLiteClient, query: Query, logger: Logger, queryMetadata: {
|
|
55
|
+
tables: string[];
|
|
56
|
+
type: "select" | "update" | "delete" | "insert";
|
|
57
|
+
} | undefined, fields: SelectedFieldsOrdered | undefined, executeMethod: SQLiteExecuteMethod, useJitMappers: boolean | undefined, customResultMapper?: ((rows: TIsRqbV2 extends true ? Record<string, unknown>[] : unknown[][]) => unknown) | undefined, isRqbV2Query?: TIsRqbV2 | undefined, rqbConfig?: RelationalQueryMapperConfig | undefined);
|
|
58
|
+
run(placeholderValues?: Record<string, unknown>): LibsqlSyncRunResult;
|
|
59
|
+
all(placeholderValues?: Record<string, unknown>): T["all"];
|
|
60
|
+
private allRqbV2;
|
|
61
|
+
get(placeholderValues?: Record<string, unknown>): T["get"];
|
|
62
|
+
private getRqbV2;
|
|
63
|
+
values(placeholderValues?: Record<string, unknown>): T["values"];
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { LibsqlSyncTransaction as a, LibsqlSyncSessionOptions as i, LibsqlSyncRunResult as n, LibsqlSyncSession as r, LibsqlSyncPreparedQuery as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as LibsqlSyncTransaction, i as LibsqlSyncSessionOptions, n as LibsqlSyncRunResult, r as LibsqlSyncSession, t as LibsqlSyncPreparedQuery } from "./session-DuHpmegi.mjs";
|
|
2
|
+
export { LibsqlSyncPreparedQuery, LibsqlSyncRunResult, LibsqlSyncSession, LibsqlSyncSessionOptions, LibsqlSyncTransaction };
|
package/dist/session.mjs
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import "./client.mjs";
|
|
2
|
+
import * as drizzleRuntime from "drizzle-orm";
|
|
3
|
+
import { NoopLogger, entityKind, fillPlaceholders, is, makeJitQueryMapper, makeJitRqbMapper, sql } from "drizzle-orm";
|
|
4
|
+
import { SQLitePreparedQuery, SQLiteSession, SQLiteTransaction } from "drizzle-orm/sqlite-core";
|
|
5
|
+
//#region src/session.ts
|
|
6
|
+
function isAnyObject(value) {
|
|
7
|
+
return typeof value === "object" && value !== null;
|
|
8
|
+
}
|
|
9
|
+
function isPlainObject(value) {
|
|
10
|
+
if (typeof value !== "object" || value === null) return false;
|
|
11
|
+
const proto = Object.getPrototypeOf(value);
|
|
12
|
+
return proto === Object.prototype || proto === null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Sync one-shot memoizer for the `drizzle-orm` runtime export lookups below.
|
|
16
|
+
* Avoids a `let cached…` module singleton while keeping the lookup off the hot
|
|
17
|
+
* path.
|
|
18
|
+
*/
|
|
19
|
+
function onetime(fn) {
|
|
20
|
+
let called = false;
|
|
21
|
+
let value;
|
|
22
|
+
return () => {
|
|
23
|
+
if (!called) {
|
|
24
|
+
value = fn();
|
|
25
|
+
called = true;
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function getDrizzleRuntimeFunction(exportName, validate) {
|
|
31
|
+
const value = Reflect.get(drizzleRuntime, exportName);
|
|
32
|
+
if (!validate(value)) throw new TypeError(`drizzle-orm is missing runtime export: ${exportName}`);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
const getMapResultRow = onetime(() => getDrizzleRuntimeFunction("mapResultRow", (value) => typeof value === "function"));
|
|
36
|
+
function isSqliteBindValue(value) {
|
|
37
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || value instanceof Uint8Array;
|
|
38
|
+
}
|
|
39
|
+
function toSqliteBindValues(params) {
|
|
40
|
+
const bindValues = [];
|
|
41
|
+
for (const param of params) {
|
|
42
|
+
if (!isSqliteBindValue(param)) throw new TypeError(`Invalid SQLite bind value: ${JSON.stringify(param)}`);
|
|
43
|
+
bindValues.push(param);
|
|
44
|
+
}
|
|
45
|
+
return bindValues;
|
|
46
|
+
}
|
|
47
|
+
function isRecordBooleanMap(value) {
|
|
48
|
+
if (value === void 0) return true;
|
|
49
|
+
if (!isPlainObject(value)) return false;
|
|
50
|
+
return Object.values(value).every((entry) => typeof entry === "boolean");
|
|
51
|
+
}
|
|
52
|
+
function isSQLiteSyncDialect(value) {
|
|
53
|
+
return isAnyObject(value) && "sqlToQuery" in value;
|
|
54
|
+
}
|
|
55
|
+
function getPreparedQueryInternals(instance) {
|
|
56
|
+
const joinsNotNullableMap = Reflect.get(instance, "joinsNotNullableMap");
|
|
57
|
+
if (!isRecordBooleanMap(joinsNotNullableMap)) throw new TypeError("Prepared query is missing joinsNotNullableMap");
|
|
58
|
+
return { joinsNotNullableMap };
|
|
59
|
+
}
|
|
60
|
+
function getSQLiteSyncDialect(instance) {
|
|
61
|
+
const dialect = Reflect.get(instance, "dialect");
|
|
62
|
+
if (!isSQLiteSyncDialect(dialect)) throw new TypeError("Missing SQLite sync dialect");
|
|
63
|
+
return dialect;
|
|
64
|
+
}
|
|
65
|
+
function isPreparedQueryResult(value) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
function isRqbV2CustomResultMapper(mapper, isRqbV2) {
|
|
69
|
+
return isRqbV2 === true && typeof mapper === "function";
|
|
70
|
+
}
|
|
71
|
+
function isLibsqlSyncSessionFor(value) {
|
|
72
|
+
return is(value, LibsqlSyncSession);
|
|
73
|
+
}
|
|
74
|
+
function getSelectedFields(fields) {
|
|
75
|
+
if (fields === void 0) throw new TypeError("Expected selected fields to be defined");
|
|
76
|
+
return fields;
|
|
77
|
+
}
|
|
78
|
+
function getRelationalQueryMapperConfig(config) {
|
|
79
|
+
if (config === void 0) throw new TypeError("Expected relational query mapper config to be defined");
|
|
80
|
+
return config;
|
|
81
|
+
}
|
|
82
|
+
var LibsqlSyncSession = class extends SQLiteSession {
|
|
83
|
+
client;
|
|
84
|
+
relations;
|
|
85
|
+
schema;
|
|
86
|
+
options;
|
|
87
|
+
static [entityKind] = "LibsqlSyncSession";
|
|
88
|
+
logger;
|
|
89
|
+
constructor(client, dialect, relations, schema, options = {}) {
|
|
90
|
+
super(dialect);
|
|
91
|
+
this.client = client;
|
|
92
|
+
this.relations = relations;
|
|
93
|
+
this.schema = schema;
|
|
94
|
+
this.options = options;
|
|
95
|
+
this.logger = options.logger ?? new NoopLogger();
|
|
96
|
+
}
|
|
97
|
+
prepareQuery(query, fields, executeMethod, customResultMapper, queryMetadata, _cacheConfig) {
|
|
98
|
+
return new LibsqlSyncPreparedQuery(this.client, query, this.logger, queryMetadata, fields, executeMethod, this.options.useJitMappers, customResultMapper);
|
|
99
|
+
}
|
|
100
|
+
prepareRelationalQuery(query, fields, executeMethod, customResultMapper, config) {
|
|
101
|
+
return new LibsqlSyncPreparedQuery(this.client, query, this.logger, void 0, fields, executeMethod, this.options.useJitMappers, customResultMapper, true, config);
|
|
102
|
+
}
|
|
103
|
+
transaction(transaction, config = {}) {
|
|
104
|
+
const tx = new LibsqlSyncTransaction("sync", getSQLiteSyncDialect(this), this, this.relations, this.schema);
|
|
105
|
+
this.run(sql.raw(`begin${config.behavior ? " " + config.behavior : ""}`));
|
|
106
|
+
try {
|
|
107
|
+
const result = transaction(tx);
|
|
108
|
+
this.run(sql`commit`);
|
|
109
|
+
return result;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
this.run(sql`rollback`);
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var LibsqlSyncTransaction = class LibsqlSyncTransaction extends SQLiteTransaction {
|
|
117
|
+
static [entityKind] = "LibsqlSyncTransaction";
|
|
118
|
+
transaction(transaction) {
|
|
119
|
+
const dialect = getSQLiteSyncDialect(this);
|
|
120
|
+
const sessionValue = Reflect.get(this, "session");
|
|
121
|
+
if (!isLibsqlSyncSessionFor(sessionValue)) throw new TypeError("Expected LibsqlSyncSession");
|
|
122
|
+
const savepointName = `sp${this.nestedIndex}`;
|
|
123
|
+
const tx = new LibsqlSyncTransaction("sync", dialect, sessionValue, this.relations, this.schema, this.nestedIndex + 1);
|
|
124
|
+
sessionValue.run(sql.raw(`savepoint ${savepointName}`));
|
|
125
|
+
try {
|
|
126
|
+
const result = transaction(tx);
|
|
127
|
+
sessionValue.run(sql.raw(`release savepoint ${savepointName}`));
|
|
128
|
+
return result;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
sessionValue.run(sql.raw(`rollback to savepoint ${savepointName}`));
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var LibsqlSyncPreparedQuery = class extends SQLitePreparedQuery {
|
|
136
|
+
client;
|
|
137
|
+
logger;
|
|
138
|
+
fields;
|
|
139
|
+
useJitMappers;
|
|
140
|
+
customResultMapper;
|
|
141
|
+
isRqbV2Query;
|
|
142
|
+
rqbConfig;
|
|
143
|
+
static [entityKind] = "LibsqlSyncPreparedQuery";
|
|
144
|
+
jitRowMapper;
|
|
145
|
+
jitRqbMapper;
|
|
146
|
+
constructor(client, query, logger, queryMetadata, fields, executeMethod, useJitMappers, customResultMapper, isRqbV2Query, rqbConfig) {
|
|
147
|
+
super("sync", executeMethod, query, void 0, queryMetadata, void 0);
|
|
148
|
+
this.client = client;
|
|
149
|
+
this.logger = logger;
|
|
150
|
+
this.fields = fields;
|
|
151
|
+
this.useJitMappers = useJitMappers;
|
|
152
|
+
this.customResultMapper = customResultMapper;
|
|
153
|
+
this.isRqbV2Query = isRqbV2Query;
|
|
154
|
+
this.rqbConfig = rqbConfig;
|
|
155
|
+
}
|
|
156
|
+
run(placeholderValues) {
|
|
157
|
+
const params = fillPlaceholders(this.query.params, placeholderValues ?? {});
|
|
158
|
+
this.logger.logQuery(this.query.sql, params);
|
|
159
|
+
const info = this.client.prepare(this.query.sql).run(...toSqliteBindValues(params));
|
|
160
|
+
return {
|
|
161
|
+
changes: info.changes,
|
|
162
|
+
lastInsertRowid: info.lastInsertRowid
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
all(placeholderValues) {
|
|
166
|
+
if (this.isRqbV2Query) return this.allRqbV2(placeholderValues);
|
|
167
|
+
const internals = getPreparedQueryInternals(this);
|
|
168
|
+
const { client, customResultMapper, fields, logger, query } = this;
|
|
169
|
+
const { joinsNotNullableMap } = internals;
|
|
170
|
+
if (!fields && !customResultMapper) {
|
|
171
|
+
const params = fillPlaceholders(query.params, placeholderValues ?? {});
|
|
172
|
+
logger.logQuery(query.sql, params);
|
|
173
|
+
const result = client.prepare(query.sql).all(...toSqliteBindValues(params));
|
|
174
|
+
if (isPreparedQueryResult(result)) return result;
|
|
175
|
+
throw new TypeError("Unexpected prepared query result");
|
|
176
|
+
}
|
|
177
|
+
const rows = this.values(placeholderValues);
|
|
178
|
+
if (!Array.isArray(rows)) throw new TypeError("Expected values() to return row arrays");
|
|
179
|
+
if (customResultMapper) {
|
|
180
|
+
const mapped = customResultMapper(rows);
|
|
181
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
182
|
+
throw new TypeError("Unexpected custom result mapper output");
|
|
183
|
+
}
|
|
184
|
+
const fieldsDefined = getSelectedFields(fields);
|
|
185
|
+
if (this.useJitMappers) {
|
|
186
|
+
const mapper = this.jitRowMapper ?? makeJitQueryMapper(fieldsDefined, joinsNotNullableMap);
|
|
187
|
+
this.jitRowMapper = mapper;
|
|
188
|
+
const mapped = mapper(rows);
|
|
189
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
190
|
+
throw new TypeError("Unexpected JIT mapper output");
|
|
191
|
+
}
|
|
192
|
+
const mapped = rows.map((row) => {
|
|
193
|
+
if (!Array.isArray(row)) throw new TypeError("Expected each row to be an array");
|
|
194
|
+
return getMapResultRow()(fieldsDefined, row, joinsNotNullableMap);
|
|
195
|
+
});
|
|
196
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
197
|
+
throw new TypeError("Unexpected mapped query result");
|
|
198
|
+
}
|
|
199
|
+
allRqbV2(placeholderValues) {
|
|
200
|
+
const { client, customResultMapper, logger, query } = this;
|
|
201
|
+
const params = fillPlaceholders(query.params, placeholderValues ?? {});
|
|
202
|
+
logger.logQuery(query.sql, params);
|
|
203
|
+
const rows = client.prepare(query.sql).all(...toSqliteBindValues(params));
|
|
204
|
+
const rqbConfig = getRelationalQueryMapperConfig(this.rqbConfig);
|
|
205
|
+
if (this.useJitMappers) {
|
|
206
|
+
const mapper = this.jitRqbMapper ?? makeJitRqbMapper(rqbConfig);
|
|
207
|
+
this.jitRqbMapper = mapper;
|
|
208
|
+
const mapped = mapper(rows);
|
|
209
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
210
|
+
throw new TypeError("Unexpected RQB JIT mapper output");
|
|
211
|
+
}
|
|
212
|
+
if (!isRqbV2CustomResultMapper(customResultMapper, this.isRqbV2Query)) throw new TypeError("Expected custom result mapper for RQB v2 query");
|
|
213
|
+
const mapped = customResultMapper(rows);
|
|
214
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
215
|
+
throw new TypeError("Unexpected RQB custom result mapper output");
|
|
216
|
+
}
|
|
217
|
+
get(placeholderValues) {
|
|
218
|
+
if (this.isRqbV2Query) return this.getRqbV2(placeholderValues);
|
|
219
|
+
const internals = getPreparedQueryInternals(this);
|
|
220
|
+
const { client, customResultMapper, fields, logger, query } = this;
|
|
221
|
+
const { joinsNotNullableMap } = internals;
|
|
222
|
+
const params = fillPlaceholders(query.params, placeholderValues ?? {});
|
|
223
|
+
logger.logQuery(query.sql, params);
|
|
224
|
+
if (!fields && !customResultMapper) {
|
|
225
|
+
const row = client.prepare(query.sql).get(...toSqliteBindValues(params)) ?? void 0;
|
|
226
|
+
if (row === void 0 || row === null) return;
|
|
227
|
+
if (isPreparedQueryResult(row)) return row;
|
|
228
|
+
throw new TypeError("Unexpected prepared query row");
|
|
229
|
+
}
|
|
230
|
+
const rows = this.values(placeholderValues);
|
|
231
|
+
if (!Array.isArray(rows)) throw new TypeError("Expected values() to return row arrays");
|
|
232
|
+
const row = rows[0];
|
|
233
|
+
if (!row) return;
|
|
234
|
+
if (customResultMapper) {
|
|
235
|
+
const mapped = customResultMapper(rows);
|
|
236
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
237
|
+
throw new TypeError("Unexpected custom result mapper output");
|
|
238
|
+
}
|
|
239
|
+
const fieldsDefined = getSelectedFields(fields);
|
|
240
|
+
if (this.useJitMappers) {
|
|
241
|
+
const mapper = this.jitRowMapper ?? makeJitQueryMapper(fieldsDefined, joinsNotNullableMap);
|
|
242
|
+
this.jitRowMapper = mapper;
|
|
243
|
+
const mappedRows = mapper([row]);
|
|
244
|
+
if (!Array.isArray(mappedRows)) throw new TypeError("Expected JIT mapper to return an array");
|
|
245
|
+
const mapped = mappedRows[0];
|
|
246
|
+
if (mapped === void 0) return;
|
|
247
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
248
|
+
throw new TypeError("Unexpected JIT mapper row");
|
|
249
|
+
}
|
|
250
|
+
if (!Array.isArray(row)) throw new TypeError("Expected row to be an array");
|
|
251
|
+
const mapped = getMapResultRow()(fieldsDefined, row, joinsNotNullableMap);
|
|
252
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
253
|
+
throw new TypeError("Unexpected mapped query row");
|
|
254
|
+
}
|
|
255
|
+
getRqbV2(placeholderValues) {
|
|
256
|
+
const { client, customResultMapper, logger, query } = this;
|
|
257
|
+
const params = fillPlaceholders(query.params, placeholderValues ?? {});
|
|
258
|
+
logger.logQuery(query.sql, params);
|
|
259
|
+
const rows = client.prepare(query.sql).all(...toSqliteBindValues(params));
|
|
260
|
+
const row = rows[0];
|
|
261
|
+
if (!row) return;
|
|
262
|
+
const rqbConfig = getRelationalQueryMapperConfig(this.rqbConfig);
|
|
263
|
+
if (this.useJitMappers) {
|
|
264
|
+
const mapper = this.jitRqbMapper ?? makeJitRqbMapper(rqbConfig);
|
|
265
|
+
this.jitRqbMapper = mapper;
|
|
266
|
+
const mapped = mapper(rows);
|
|
267
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
268
|
+
throw new TypeError("Unexpected RQB JIT mapper output");
|
|
269
|
+
}
|
|
270
|
+
if (!isRqbV2CustomResultMapper(customResultMapper, this.isRqbV2Query)) throw new TypeError("Expected custom result mapper for RQB v2 query");
|
|
271
|
+
const mapped = customResultMapper([row]);
|
|
272
|
+
if (isPreparedQueryResult(mapped)) return mapped;
|
|
273
|
+
throw new TypeError("Unexpected RQB custom result mapper output");
|
|
274
|
+
}
|
|
275
|
+
values(placeholderValues) {
|
|
276
|
+
const params = fillPlaceholders(this.query.params, placeholderValues ?? {});
|
|
277
|
+
this.logger.logQuery(this.query.sql, params);
|
|
278
|
+
const stmt = this.client.prepare(this.query.sql);
|
|
279
|
+
const toggled = stmt.raw(true);
|
|
280
|
+
let result;
|
|
281
|
+
try {
|
|
282
|
+
result = toggled.all(...toSqliteBindValues(params));
|
|
283
|
+
} finally {
|
|
284
|
+
stmt.raw(false);
|
|
285
|
+
}
|
|
286
|
+
if (isPreparedQueryResult(result)) return result;
|
|
287
|
+
throw new TypeError("Unexpected prepared query values");
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
//#endregion
|
|
291
|
+
export { LibsqlSyncPreparedQuery, LibsqlSyncSession, LibsqlSyncTransaction };
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "drizzle-orm-libsql-sync",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "A synchronous Drizzle ORM driver for libsql, backed by the libsql Database (prepare/exec) and drizzle v1 modern relations.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"drizzle",
|
|
7
|
+
"drizzle-orm",
|
|
8
|
+
"libsql",
|
|
9
|
+
"sqlite",
|
|
10
|
+
"sync",
|
|
11
|
+
"synchronous",
|
|
12
|
+
"turso"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/leonsilicon/drizzle-orm-libsql-sync#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/leonsilicon/drizzle-orm-libsql-sync/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Leon Si <leon@leonsilicon.com>",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/leonsilicon/drizzle-orm-libsql-sync.git"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": "./dist/index.mjs",
|
|
30
|
+
"./client": "./dist/client.mjs",
|
|
31
|
+
"./driver": "./dist/driver.mjs",
|
|
32
|
+
"./migrator": "./dist/migrator.mjs",
|
|
33
|
+
"./session": "./dist/session.mjs",
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "vp pack",
|
|
41
|
+
"dev": "vp pack --watch",
|
|
42
|
+
"test": "vp test",
|
|
43
|
+
"check": "vp check",
|
|
44
|
+
"prepublishOnly": "vp run build",
|
|
45
|
+
"prepare": "vp config"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^25.6.2",
|
|
49
|
+
"@typescript/native-preview": "7.0.0-dev.20260509.2",
|
|
50
|
+
"bumpp": "^11.1.0",
|
|
51
|
+
"drizzle-orm": "1.0.0-rc.4-5d5b77c",
|
|
52
|
+
"libsql": "^0.5.29",
|
|
53
|
+
"typescript": "^6.0.3",
|
|
54
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
|
55
|
+
"vite-plus": "latest"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"drizzle-orm": "^1.0.0-rc.4",
|
|
59
|
+
"libsql": "^0.5.0"
|
|
60
|
+
},
|
|
61
|
+
"overrides": {
|
|
62
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
|
63
|
+
"vitest": "4.1.9"
|
|
64
|
+
},
|
|
65
|
+
"devEngines": {
|
|
66
|
+
"packageManager": {
|
|
67
|
+
"name": "bun",
|
|
68
|
+
"version": "1.3.14",
|
|
69
|
+
"onFail": "download"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|