@delali/sirannon-db 0.1.3 → 0.1.4

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.
@@ -0,0 +1,8 @@
1
+ import { S as SQLiteDriver } from '../types-BFSsG77t.js';
2
+
3
+ interface BetterSqlite3Options {
4
+ busyTimeout?: number;
5
+ }
6
+ declare function betterSqlite3(driverOptions?: BetterSqlite3Options): SQLiteDriver;
7
+
8
+ export { type BetterSqlite3Options, betterSqlite3 };
@@ -0,0 +1,63 @@
1
+ import { defineDriver } from '../chunk-74UN4DIE.mjs';
2
+ import '../chunk-O7BHI3CF.mjs';
3
+
4
+ // src/drivers/better-sqlite3/index.ts
5
+ function createConnection(db) {
6
+ const conn = {
7
+ async exec(sql) {
8
+ db.exec(sql);
9
+ },
10
+ async prepare(sql) {
11
+ const stmt = db.prepare(sql);
12
+ return {
13
+ async all(...params) {
14
+ return stmt.all(...params);
15
+ },
16
+ async get(...params) {
17
+ return stmt.get(...params);
18
+ },
19
+ async run(...params) {
20
+ const result = stmt.run(...params);
21
+ return {
22
+ changes: result.changes,
23
+ lastInsertRowId: result.lastInsertRowid
24
+ };
25
+ }
26
+ };
27
+ },
28
+ async transaction(fn) {
29
+ await conn.exec("BEGIN");
30
+ try {
31
+ const result = await fn(conn);
32
+ await conn.exec("COMMIT");
33
+ return result;
34
+ } catch (err) {
35
+ try {
36
+ await conn.exec("ROLLBACK");
37
+ } catch {
38
+ }
39
+ throw err;
40
+ }
41
+ },
42
+ async close() {
43
+ db.close();
44
+ }
45
+ };
46
+ return conn;
47
+ }
48
+ function betterSqlite3(driverOptions) {
49
+ return defineDriver({
50
+ capabilities: { multipleConnections: true, extensions: true },
51
+ async open(path, options) {
52
+ const Database = (await import('better-sqlite3')).default;
53
+ const db = new Database(path, { readonly: options?.readonly ?? false });
54
+ if (options?.walMode !== false) db.pragma("journal_mode = WAL");
55
+ db.pragma("synchronous = NORMAL");
56
+ db.pragma("foreign_keys = ON");
57
+ db.pragma(`busy_timeout = ${driverOptions?.busyTimeout ?? 5e3}`);
58
+ return createConnection(db);
59
+ }
60
+ });
61
+ }
62
+
63
+ export { betterSqlite3 };
@@ -0,0 +1,61 @@
1
+ import { defineDriver } from '../chunk-FB2U2Q3Y.mjs';
2
+
3
+ // src/drivers/bun/index.ts
4
+ function bunSqlite(driverOptions) {
5
+ return defineDriver({
6
+ capabilities: { multipleConnections: true, extensions: true },
7
+ async open(path, options) {
8
+ const { Database } = await import('bun:sqlite');
9
+ const db = new Database(path, { readonly: options?.readonly ?? false });
10
+ if (options?.walMode !== false) db.run("PRAGMA journal_mode = WAL");
11
+ db.run("PRAGMA synchronous = NORMAL");
12
+ db.run("PRAGMA foreign_keys = ON");
13
+ db.run(`PRAGMA busy_timeout = ${driverOptions?.busyTimeout ?? 5e3}`);
14
+ const conn = {
15
+ async exec(sql) {
16
+ db.run(sql);
17
+ },
18
+ async prepare(sql) {
19
+ const stmt = db.query(sql);
20
+ return {
21
+ async all(...params) {
22
+ return stmt.all(...params);
23
+ },
24
+ async get(...params) {
25
+ return stmt.get(...params) ?? void 0;
26
+ },
27
+ async run(...params) {
28
+ stmt.run(...params);
29
+ const changesStmt = db.query("SELECT changes() AS changes, last_insert_rowid() AS lastId");
30
+ const info = changesStmt.get();
31
+ return {
32
+ changes: info.changes,
33
+ lastInsertRowId: info.lastId
34
+ };
35
+ }
36
+ };
37
+ },
38
+ async transaction(fn) {
39
+ await conn.exec("BEGIN");
40
+ try {
41
+ const result = await fn(conn);
42
+ await conn.exec("COMMIT");
43
+ return result;
44
+ } catch (err) {
45
+ try {
46
+ await conn.exec("ROLLBACK");
47
+ } catch {
48
+ }
49
+ throw err;
50
+ }
51
+ },
52
+ async close() {
53
+ db.close();
54
+ }
55
+ };
56
+ return conn;
57
+ }
58
+ });
59
+ }
60
+
61
+ export { bunSqlite };
@@ -0,0 +1,55 @@
1
+ import { defineDriver } from '../chunk-FB2U2Q3Y.mjs';
2
+
3
+ // src/drivers/expo/index.ts
4
+ function expoSqlite() {
5
+ return defineDriver({
6
+ capabilities: { multipleConnections: false, extensions: false },
7
+ async open(path, options) {
8
+ const SQLite = await import('expo-sqlite');
9
+ const db = await SQLite.openDatabaseAsync(path, {
10
+ readOnly: options?.readonly
11
+ });
12
+ if (options?.walMode !== false) await db.execAsync("PRAGMA journal_mode = WAL");
13
+ await db.execAsync("PRAGMA synchronous = NORMAL");
14
+ await db.execAsync("PRAGMA foreign_keys = ON");
15
+ const buildConnectionFromHandle = (dbHandle) => ({
16
+ async exec(sql) {
17
+ await dbHandle.execAsync(sql);
18
+ },
19
+ async prepare(sql) {
20
+ return {
21
+ async all(...params) {
22
+ return dbHandle.getAllAsync(sql, params);
23
+ },
24
+ async get(...params) {
25
+ const row = await dbHandle.getFirstAsync(sql, params);
26
+ return row ?? void 0;
27
+ },
28
+ async run(...params) {
29
+ const result = await dbHandle.runAsync(sql, params);
30
+ return {
31
+ changes: result.changes,
32
+ lastInsertRowId: result.lastInsertRowId
33
+ };
34
+ }
35
+ };
36
+ },
37
+ async transaction(fn) {
38
+ let result;
39
+ await dbHandle.withExclusiveTransactionAsync(async (txDb) => {
40
+ const txConn = buildConnectionFromHandle(txDb);
41
+ result = await fn(txConn);
42
+ });
43
+ return result;
44
+ },
45
+ async close() {
46
+ await dbHandle.closeAsync();
47
+ }
48
+ });
49
+ const conn = buildConnectionFromHandle(db);
50
+ return conn;
51
+ }
52
+ });
53
+ }
54
+
55
+ export { expoSqlite };
@@ -0,0 +1,8 @@
1
+ import { S as SQLiteDriver } from '../types-BFSsG77t.js';
2
+
3
+ interface NodeSqliteOptions {
4
+ busyTimeout?: number;
5
+ }
6
+ declare function nodeSqlite(driverOptions?: NodeSqliteOptions): SQLiteDriver;
7
+
8
+ export { type NodeSqliteOptions, nodeSqlite };
@@ -0,0 +1,60 @@
1
+ import { defineDriver } from '../chunk-74UN4DIE.mjs';
2
+ import '../chunk-O7BHI3CF.mjs';
3
+
4
+ // src/drivers/node/index.ts
5
+ function nodeSqlite(driverOptions) {
6
+ return defineDriver({
7
+ capabilities: { multipleConnections: true, extensions: true },
8
+ async open(path, options) {
9
+ const { DatabaseSync } = await import('node:sqlite');
10
+ const db = new DatabaseSync(path, { readOnly: options?.readonly ?? false });
11
+ if (options?.walMode !== false) db.exec("PRAGMA journal_mode = WAL");
12
+ db.exec("PRAGMA synchronous = NORMAL");
13
+ db.exec("PRAGMA foreign_keys = ON");
14
+ db.exec(`PRAGMA busy_timeout = ${driverOptions?.busyTimeout ?? 5e3}`);
15
+ const conn = {
16
+ async exec(sql) {
17
+ db.exec(sql);
18
+ },
19
+ async prepare(sql) {
20
+ const stmt = db.prepare(sql);
21
+ return {
22
+ async all(...params) {
23
+ return stmt.all(...params);
24
+ },
25
+ async get(...params) {
26
+ return stmt.get(...params);
27
+ },
28
+ async run(...params) {
29
+ const result = stmt.run(...params);
30
+ return {
31
+ changes: Number(result.changes),
32
+ lastInsertRowId: result.lastInsertRowid
33
+ };
34
+ }
35
+ };
36
+ },
37
+ async transaction(fn) {
38
+ await conn.exec("BEGIN");
39
+ try {
40
+ const result = await fn(conn);
41
+ await conn.exec("COMMIT");
42
+ return result;
43
+ } catch (err) {
44
+ try {
45
+ await conn.exec("ROLLBACK");
46
+ } catch {
47
+ }
48
+ throw err;
49
+ }
50
+ },
51
+ async close() {
52
+ db.close();
53
+ }
54
+ };
55
+ return conn;
56
+ }
57
+ });
58
+ }
59
+
60
+ export { nodeSqlite };
@@ -0,0 +1,34 @@
1
+ interface RunResult {
2
+ changes: number;
3
+ lastInsertRowId: number | bigint;
4
+ }
5
+ interface SQLiteStatement {
6
+ all<T = unknown>(...params: unknown[]): Promise<T[]>;
7
+ get<T = unknown>(...params: unknown[]): Promise<T | undefined>;
8
+ run(...params: unknown[]): Promise<RunResult>;
9
+ }
10
+ interface SQLiteConnection {
11
+ exec(sql: string): Promise<void>;
12
+ prepare(sql: string): Promise<SQLiteStatement>;
13
+ transaction<T>(fn: (conn: SQLiteConnection) => Promise<T>): Promise<T>;
14
+ close(): Promise<void>;
15
+ }
16
+ interface OpenOptions {
17
+ readonly?: boolean;
18
+ walMode?: boolean;
19
+ }
20
+ interface DriverCapabilities {
21
+ multipleConnections: boolean;
22
+ extensions: boolean;
23
+ }
24
+ interface SQLiteDriver {
25
+ readonly capabilities: DriverCapabilities;
26
+ open(path: string, options?: OpenOptions): Promise<SQLiteConnection>;
27
+ }
28
+
29
+ interface WaSqliteOptions {
30
+ vfs?: 'IDBBatchAtomicVFS' | 'AccessHandlePoolVFS';
31
+ }
32
+ declare function waSqlite(driverOptions?: WaSqliteOptions): SQLiteDriver;
33
+
34
+ export { type WaSqliteOptions, waSqlite };
@@ -0,0 +1,141 @@
1
+ // src/core/errors.ts
2
+ var SirannonError = class extends Error {
3
+ constructor(message, code) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = "SirannonError";
7
+ }
8
+ };
9
+
10
+ // src/core/driver/define.ts
11
+ function defineDriver(config) {
12
+ if (!config.capabilities || typeof config.open !== "function") {
13
+ throw new SirannonError("Driver must define capabilities and open()", "INVALID_DRIVER");
14
+ }
15
+ return Object.freeze({
16
+ capabilities: Object.freeze({ ...config.capabilities }),
17
+ open: config.open
18
+ });
19
+ }
20
+
21
+ // src/drivers/wa-sqlite/index.ts
22
+ function waSqlite(driverOptions) {
23
+ return defineDriver({
24
+ capabilities: { multipleConnections: false, extensions: false },
25
+ async open(path, options) {
26
+ const { default: SQLiteESMFactory } = await import('wa-sqlite/dist/wa-sqlite-async.mjs');
27
+ const { Factory } = await import('wa-sqlite');
28
+ const module = await SQLiteESMFactory();
29
+ const sqlite3 = Factory(module);
30
+ const vfsName = driverOptions?.vfs ?? "IDBBatchAtomicVFS";
31
+ if (vfsName === "AccessHandlePoolVFS") {
32
+ const { AccessHandlePoolVFS } = await import('../vfs-INWQ5DTE.mjs');
33
+ const vfs = new AccessHandlePoolVFS(path);
34
+ await vfs.isReady;
35
+ sqlite3.vfs_register(vfs, true);
36
+ } else {
37
+ const { IDBBatchAtomicVFS } = await import('../vfs-INWQ5DTE.mjs');
38
+ const vfs = new IDBBatchAtomicVFS(path);
39
+ await vfs.isReady;
40
+ sqlite3.vfs_register(vfs, true);
41
+ }
42
+ const db = await sqlite3.open_v2(path);
43
+ if (options?.walMode !== false) {
44
+ try {
45
+ await sqlite3.exec(db, "PRAGMA journal_mode = WAL");
46
+ } catch {
47
+ }
48
+ }
49
+ await sqlite3.exec(db, "PRAGMA synchronous = NORMAL");
50
+ await sqlite3.exec(db, "PRAGMA foreign_keys = ON");
51
+ async function execStatements(sql, params) {
52
+ const columns = [];
53
+ const rows = [];
54
+ let changes = 0;
55
+ let lastRowId = 0;
56
+ for await (const stmt of sqlite3.statements(db, sql)) {
57
+ if (params && params.length > 0) {
58
+ for (let i = 0; i < params.length; i++) {
59
+ sqlite3.bind(stmt, i + 1, params[i]);
60
+ }
61
+ }
62
+ const cols = sqlite3.column_names(stmt);
63
+ if (cols.length > 0 && columns.length === 0) {
64
+ columns.push(...cols);
65
+ }
66
+ while (await sqlite3.step(stmt) === 100) {
67
+ const row = [];
68
+ for (let i = 0; i < cols.length; i++) {
69
+ row.push(sqlite3.column(stmt, i));
70
+ }
71
+ rows.push(row);
72
+ }
73
+ }
74
+ changes = sqlite3.changes(db);
75
+ let rowIdCaptured = false;
76
+ await sqlite3.exec(db, "SELECT last_insert_rowid()", (row) => {
77
+ if (!rowIdCaptured && row[0] !== void 0) {
78
+ lastRowId = row[0];
79
+ rowIdCaptured = true;
80
+ }
81
+ });
82
+ return { columns, rows, changes, lastRowId };
83
+ }
84
+ const conn = {
85
+ async exec(sql) {
86
+ await sqlite3.exec(db, sql);
87
+ },
88
+ async prepare(sql) {
89
+ return {
90
+ async all(...params) {
91
+ const result = await execStatements(sql, params);
92
+ return result.rows.map((row) => {
93
+ const obj = {};
94
+ for (let i = 0; i < result.columns.length; i++) {
95
+ obj[result.columns[i]] = row[i];
96
+ }
97
+ return obj;
98
+ });
99
+ },
100
+ async get(...params) {
101
+ const result = await execStatements(sql, params);
102
+ if (result.rows.length === 0) return void 0;
103
+ const obj = {};
104
+ for (let i = 0; i < result.columns.length; i++) {
105
+ obj[result.columns[i]] = result.rows[0][i];
106
+ }
107
+ return obj;
108
+ },
109
+ async run(...params) {
110
+ const result = await execStatements(sql, params);
111
+ return {
112
+ changes: result.changes,
113
+ lastInsertRowId: result.lastRowId
114
+ };
115
+ }
116
+ };
117
+ },
118
+ async transaction(fn) {
119
+ await conn.exec("BEGIN");
120
+ try {
121
+ const result = await fn(conn);
122
+ await conn.exec("COMMIT");
123
+ return result;
124
+ } catch (err) {
125
+ try {
126
+ await conn.exec("ROLLBACK");
127
+ } catch {
128
+ }
129
+ throw err;
130
+ }
131
+ },
132
+ async close() {
133
+ sqlite3.close(db);
134
+ }
135
+ };
136
+ return conn;
137
+ }
138
+ });
139
+ }
140
+
141
+ export { waSqlite };
@@ -0,0 +1,16 @@
1
+ import { M as Migration } from '../types-DRkJlqex.js';
2
+ import '../types-BFSsG77t.js';
3
+ import '../types-DtDutWRU.js';
4
+
5
+ interface ScannedMigration {
6
+ version: number;
7
+ name: string;
8
+ upPath: string;
9
+ downPath: string | null;
10
+ }
11
+ declare function scanDirectory(dirPath: string): ScannedMigration[];
12
+ declare function readUpMigrations(scanned: ScannedMigration[]): Migration[];
13
+ declare function readDownMigrations(scanned: ScannedMigration[], versions: number[]): Migration[];
14
+ declare function loadMigrations(dirPath: string): Migration[];
15
+
16
+ export { type ScannedMigration, loadMigrations, readDownMigrations, readUpMigrations, scanDirectory };
@@ -0,0 +1,128 @@
1
+ import { MigrationError } from '../chunk-O7BHI3CF.mjs';
2
+ import { statSync, readdirSync, readFileSync } from 'fs';
3
+ import { resolve, join } from 'path';
4
+
5
+ var MIGRATION_FILENAME_PATTERN = /^(\d+)_(\w+)\.(up|down)\.sql$/;
6
+ function hasControlCharacters(s) {
7
+ for (let i = 0; i < s.length; i++) {
8
+ if (s.charCodeAt(i) <= 31) return true;
9
+ }
10
+ return false;
11
+ }
12
+ function scanDirectory(dirPath) {
13
+ if (hasControlCharacters(dirPath)) {
14
+ throw new MigrationError("Migration path contains invalid characters", 0, "MIGRATION_VALIDATION_ERROR");
15
+ }
16
+ const segments = dirPath.split(/[/\\]/);
17
+ if (segments.includes("..")) {
18
+ throw new MigrationError(
19
+ "Migration path must not contain directory traversal segments",
20
+ 0,
21
+ "MIGRATION_VALIDATION_ERROR"
22
+ );
23
+ }
24
+ const resolvedPath = resolve(dirPath);
25
+ let stat;
26
+ try {
27
+ stat = statSync(resolvedPath);
28
+ } catch {
29
+ throw new MigrationError(`Migrations path does not exist: ${resolvedPath}`, 0);
30
+ }
31
+ if (!stat.isDirectory()) {
32
+ throw new MigrationError(`Migrations path is not a directory: ${resolvedPath}`, 0);
33
+ }
34
+ const entries = readdirSync(resolvedPath, { withFileTypes: true });
35
+ const grouped = /* @__PURE__ */ new Map();
36
+ for (const entry of entries) {
37
+ if (!entry.isFile()) continue;
38
+ const match = MIGRATION_FILENAME_PATTERN.exec(entry.name);
39
+ if (!match) continue;
40
+ const version = parseInt(match[1], 10);
41
+ const name = match[2];
42
+ const direction = match[3];
43
+ if (!Number.isFinite(version) || version <= 0 || !Number.isSafeInteger(version)) {
44
+ throw new MigrationError(`Invalid migration version: ${match[1]}`, version, "MIGRATION_VALIDATION_ERROR");
45
+ }
46
+ const existing = grouped.get(version);
47
+ if (existing && existing.name !== name) {
48
+ throw new MigrationError(
49
+ `Duplicate migration version ${version}: '${existing.name}' and '${name}'`,
50
+ version,
51
+ "MIGRATION_DUPLICATE_VERSION"
52
+ );
53
+ }
54
+ const filePath = join(resolvedPath, entry.name);
55
+ if (!existing) {
56
+ grouped.set(version, {
57
+ name,
58
+ [direction === "up" ? "upPath" : "downPath"]: filePath
59
+ });
60
+ } else {
61
+ if (direction === "up") existing.upPath = filePath;
62
+ else existing.downPath = filePath;
63
+ }
64
+ }
65
+ const results = [];
66
+ for (const [version, entry] of grouped) {
67
+ if (!entry.upPath) {
68
+ throw new MigrationError(
69
+ `Migration version ${version} (${entry.name}) is missing an .up.sql file`,
70
+ version,
71
+ "MIGRATION_VALIDATION_ERROR"
72
+ );
73
+ }
74
+ results.push({
75
+ version,
76
+ name: entry.name,
77
+ upPath: entry.upPath,
78
+ downPath: entry.downPath ?? null
79
+ });
80
+ }
81
+ results.sort((a, b) => a.version - b.version);
82
+ return results;
83
+ }
84
+ function readUpMigrations(scanned) {
85
+ return scanned.map((entry) => {
86
+ const sql = readFileSync(entry.upPath, "utf-8").trim();
87
+ if (sql.length === 0) {
88
+ throw new MigrationError(`Migration file is empty: ${entry.upPath}`, entry.version, "MIGRATION_VALIDATION_ERROR");
89
+ }
90
+ return {
91
+ version: entry.version,
92
+ name: entry.name,
93
+ up: sql
94
+ };
95
+ });
96
+ }
97
+ function readDownMigrations(scanned, versions) {
98
+ const versionSet = new Set(versions);
99
+ const filtered = scanned.filter((s) => versionSet.has(s.version));
100
+ return filtered.map((entry) => {
101
+ if (!entry.downPath) {
102
+ throw new MigrationError(
103
+ `Migration version ${entry.version} (${entry.name}) has no .down.sql file`,
104
+ entry.version,
105
+ "MIGRATION_NO_DOWN"
106
+ );
107
+ }
108
+ const downSql = readFileSync(entry.downPath, "utf-8").trim();
109
+ if (downSql.length === 0) {
110
+ throw new MigrationError(
111
+ `Down migration file is empty: ${entry.downPath}`,
112
+ entry.version,
113
+ "MIGRATION_VALIDATION_ERROR"
114
+ );
115
+ }
116
+ return {
117
+ version: entry.version,
118
+ name: entry.name,
119
+ up: "",
120
+ down: downSql
121
+ };
122
+ });
123
+ }
124
+ function loadMigrations(dirPath) {
125
+ return readUpMigrations(scanDirectory(dirPath));
126
+ }
127
+
128
+ export { loadMigrations, readDownMigrations, readUpMigrations, scanDirectory };
@@ -0,0 +1,16 @@
1
+ import { a as SQLiteConnection } from './types-BFSsG77t.js';
2
+ import { e as BackupScheduleOptions } from './types-DtDutWRU.js';
3
+
4
+ declare class BackupManager {
5
+ backup(conn: SQLiteConnection, destPath: string): Promise<void>;
6
+ generateFilename(): string;
7
+ rotate(dir: string, maxFiles: number): void;
8
+ }
9
+
10
+ declare class BackupScheduler {
11
+ private readonly manager;
12
+ constructor(manager?: BackupManager);
13
+ schedule(conn: SQLiteConnection, options: BackupScheduleOptions): () => void;
14
+ }
15
+
16
+ export { BackupManager as B, BackupScheduler as a };