@fougere/adapter-sql 0.13.0-alpha.0 → 0.13.1-alpha.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/dist/driver/Driver.d.ts +12 -6
- package/dist/driver/Driver.d.ts.map +1 -1
- package/dist/driver/Driver.js +52 -23
- package/dist/driver/Driver.js.map +1 -1
- package/dist/sqlite/SqliteSource.d.ts +2 -2
- package/dist/sqlite/SqliteSource.d.ts.map +1 -1
- package/dist/sqlite/SqliteSource.js +23 -3
- package/dist/sqlite/SqliteSource.js.map +1 -1
- package/package.json +5 -5
- package/src/driver/Driver.ts +58 -29
- package/src/sqlite/SqliteSource.ts +27 -4
package/dist/driver/Driver.d.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { type Dialect as KyselyDialect } from 'kysely';
|
|
2
2
|
import type { Engine } from '../fields/Engine.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The packages each engine opens with. The APP installs them, never this package, so a
|
|
5
|
+
* project that opens no SQL installs no driver — and nothing native.
|
|
6
6
|
*
|
|
7
7
|
* Documented: [sources](https://fougere.dev/docs/infra/sources).
|
|
8
8
|
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
9
|
+
export declare const PACKAGES: Record<Engine, string[]>;
|
|
10
|
+
/** The Kysely dialect an engine builds from a url, over its packages. SQLite opens a path. */
|
|
11
|
+
export type Driver = (url: string, modules: any[]) => KyselyDialect;
|
|
13
12
|
export declare const DRIVERS: Record<Exclude<Engine, 'sqlite'>, Driver>;
|
|
13
|
+
/**
|
|
14
|
+
* The packages of `engine`, resolved from the app — loaded at the moment it opens, not before.
|
|
15
|
+
* What is not installed and what is installed but will not load are two refusals, and each
|
|
16
|
+
* names what to do.
|
|
17
|
+
*/
|
|
18
|
+
export declare function modulesOf(engine: Engine, from?: string): any[];
|
|
19
|
+
export declare function messageOf(error: unknown): string;
|
|
14
20
|
/** The dialect `url` names, over the driver the app installed beside it. */
|
|
15
21
|
export declare function dialectFor(engine: Exclude<Engine, 'sqlite'>, url: string, from?: string): KyselyDialect;
|
|
16
22
|
/** `mssql://user:password@host:1433/database` — tedious takes an object and reads no url. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Driver.d.ts","sourceRoot":"","sources":["../../src/driver/Driver.ts"],"names":[],"mappings":"AAEA,OAAO,EAA+C,KAAK,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAC;AACpG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,
|
|
1
|
+
{"version":3,"file":"Driver.d.ts","sourceRoot":"","sources":["../../src/driver/Driver.ts"],"names":[],"mappings":"AAEA,OAAO,EAA+C,KAAK,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAC;AACpG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAK7C,CAAC;AAEF,8FAA8F;AAC9F,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,aAAa,CAAC;AAEpE,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAO7D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,SAAgB,GAAG,GAAG,EAAE,CAqBrE;AAgBD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,SAAgB,GAAG,aAAa,CAE9G;AAED,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM;;;;;YAM1B,QAAQ;YAAuC,QAAQ;;;;;;;;EAIrE"}
|
package/dist/driver/Driver.js
CHANGED
|
@@ -1,37 +1,66 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { MssqlDialect, MysqlDialect, PostgresDialect } from 'kysely';
|
|
4
|
+
/**
|
|
5
|
+
* The packages each engine opens with. The APP installs them, never this package, so a
|
|
6
|
+
* project that opens no SQL installs no driver — and nothing native.
|
|
7
|
+
*
|
|
8
|
+
* Documented: [sources](https://fougere.dev/docs/infra/sources).
|
|
9
|
+
*/
|
|
10
|
+
export const PACKAGES = {
|
|
11
|
+
sqlite: ['better-sqlite3'],
|
|
12
|
+
pg: ['pg'],
|
|
13
|
+
mysql: ['mysql2'],
|
|
14
|
+
mssql: ['tedious', 'tarn'],
|
|
15
|
+
};
|
|
4
16
|
export const DRIVERS = {
|
|
5
|
-
pg: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
dialect: (url, [mysql]) => new MysqlDialect({ pool: mysql.createPool(url) }),
|
|
12
|
-
},
|
|
13
|
-
mssql: {
|
|
14
|
-
packages: ['tedious', 'tarn'],
|
|
15
|
-
dialect: (url, [tedious, tarn]) => new MssqlDialect({
|
|
16
|
-
tarn: { ...tarn, options: { min: 0, max: 10 } },
|
|
17
|
-
tedious: { ...tedious, connectionFactory: () => new tedious.Connection(tediousConfig(url)) },
|
|
18
|
-
}),
|
|
19
|
-
},
|
|
17
|
+
pg: (url, [pg]) => new PostgresDialect({ pool: new pg.Pool({ connectionString: url }) }),
|
|
18
|
+
mysql: (url, [mysql]) => new MysqlDialect({ pool: mysql.createPool(url) }),
|
|
19
|
+
mssql: (url, [tedious, tarn]) => new MssqlDialect({
|
|
20
|
+
tarn: { ...tarn, options: { min: 0, max: 10 } },
|
|
21
|
+
tedious: { ...tedious, connectionFactory: () => new tedious.Connection(tediousConfig(url)) },
|
|
22
|
+
}),
|
|
20
23
|
};
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
/**
|
|
25
|
+
* The packages of `engine`, resolved from the app — loaded at the moment it opens, not before.
|
|
26
|
+
* What is not installed and what is installed but will not load are two refusals, and each
|
|
27
|
+
* names what to do.
|
|
28
|
+
*/
|
|
29
|
+
export function modulesOf(engine, from = process.cwd()) {
|
|
24
30
|
const require = createRequire(join(from, 'package.json'));
|
|
25
|
-
const
|
|
31
|
+
const packages = PACKAGES[engine];
|
|
32
|
+
const missing = packages.filter((name) => !resolves(require, name));
|
|
33
|
+
if (missing.length) {
|
|
34
|
+
throw new Error(`The ${engine} engine opens through ${quoted(packages)}, and ${quoted(missing)} is not installed beside the app (${from}).\n`
|
|
35
|
+
+ `Install it there: pnpm add ${missing.join(' ')}`);
|
|
36
|
+
}
|
|
37
|
+
return packages.map((name) => {
|
|
26
38
|
try {
|
|
27
39
|
return require(name);
|
|
28
40
|
}
|
|
29
|
-
catch {
|
|
30
|
-
throw new Error(`
|
|
31
|
-
+ `install it beside the app: pnpm add ${driver.packages.join(' ')}`);
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new Error(`The ${engine} engine opens through '${name}', which is installed beside the app (${from}) but does not load: ${messageOf(error)}`, { cause: error });
|
|
32
43
|
}
|
|
33
44
|
});
|
|
34
|
-
|
|
45
|
+
}
|
|
46
|
+
function resolves(require, name) {
|
|
47
|
+
try {
|
|
48
|
+
require.resolve(name);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function quoted(names) {
|
|
56
|
+
return names.map((name) => `'${name}'`).join(' and ');
|
|
57
|
+
}
|
|
58
|
+
export function messageOf(error) {
|
|
59
|
+
return error instanceof Error ? error.message.split('\n')[0] : String(error);
|
|
60
|
+
}
|
|
61
|
+
/** The dialect `url` names, over the driver the app installed beside it. */
|
|
62
|
+
export function dialectFor(engine, url, from = process.cwd()) {
|
|
63
|
+
return DRIVERS[engine](url, modulesOf(engine, from));
|
|
35
64
|
}
|
|
36
65
|
/** `mssql://user:password@host:1433/database` — tedious takes an object and reads no url. */
|
|
37
66
|
export function tediousConfig(url) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Driver.js","sourceRoot":"","sources":["../../src/driver/Driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAiC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"Driver.js","sourceRoot":"","sources":["../../src/driver/Driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type BetterSqlite from 'better-sqlite3';
|
|
2
2
|
import { type SqlSource } from '../source/SqlSource.js';
|
|
3
3
|
import type { SqliteSourceOptions } from './SqliteSourceOptions.js';
|
|
4
4
|
export interface SqliteSource extends SqlSource {
|
|
5
5
|
/** The raw handle, for pragmas and synchronous exec. */
|
|
6
|
-
sqlite:
|
|
6
|
+
sqlite: BetterSqlite.Database;
|
|
7
7
|
}
|
|
8
8
|
export declare function createSqliteSource(opts?: SqliteSourceOptions): SqliteSource;
|
|
9
9
|
//# sourceMappingURL=SqliteSource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteSource.d.ts","sourceRoot":"","sources":["../../src/sqlite/SqliteSource.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"SqliteSource.d.ts","sourceRoot":"","sources":["../../src/sqlite/SqliteSource.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,YAAY,MAAM,gBAAgB,CAAC;AAI/C,OAAO,EAA4C,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAMlG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,wDAAwD;IACxD,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,mBAAwB,GAAG,YAAY,CA6B/E"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { mkdirSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { Kysely, SqliteDialect } from 'kysely';
|
|
4
|
-
import Database from 'better-sqlite3';
|
|
5
4
|
import { createStorageFactory } from '../crud/SqlStorage.js';
|
|
6
5
|
import { logQueries } from '../query/QuerySink.js';
|
|
7
6
|
import { drift, driftReport } from '../drift.js';
|
|
8
7
|
import { createKyselySource, sqlEnforces, sqlSink } from '../source/SqlSource.js';
|
|
9
|
-
import { DRIVERS, dialectFor } from '../driver/Driver.js';
|
|
8
|
+
import { DRIVERS, dialectFor, messageOf, modulesOf } from '../driver/Driver.js';
|
|
10
9
|
import { ENGINES } from '../fields/Engine.js';
|
|
11
10
|
import { desiredTables, migrate } from '../diff/Change.js';
|
|
12
11
|
import { toTableName } from '../table/TableDef.js';
|
|
@@ -16,7 +15,7 @@ export function createSqliteSource(opts = {}) {
|
|
|
16
15
|
// A file-backed DB needs its directory — SQLite won't create it.
|
|
17
16
|
if (path !== ':memory:')
|
|
18
17
|
mkdirSync(dirname(path), { recursive: true });
|
|
19
|
-
const sqlite =
|
|
18
|
+
const sqlite = open(path);
|
|
20
19
|
sqlite.pragma('journal_mode = WAL');
|
|
21
20
|
sqlite.pragma('foreign_keys = ON');
|
|
22
21
|
const db = new Kysely({ dialect: new SqliteDialect({ database: sqlite }), log: logQueries(opts.name ?? path) });
|
|
@@ -40,6 +39,27 @@ export function createSqliteSource(opts = {}) {
|
|
|
40
39
|
transacted: (fn) => db.transaction().execute((trx) => fn(createStorageFactory(trx, opts.storageFactoryOptions, 'sqlite'))),
|
|
41
40
|
};
|
|
42
41
|
}
|
|
42
|
+
function open(path) {
|
|
43
|
+
const [Database] = modulesOf('sqlite');
|
|
44
|
+
try {
|
|
45
|
+
return new Database(path);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
throw new Error(unopened(error, path), { cause: error });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function unopened(error, path) {
|
|
52
|
+
const code = error.code;
|
|
53
|
+
const rebuild = 'Build it from its sources: cd node_modules/better-sqlite3 && npx node-gyp rebuild';
|
|
54
|
+
if (code === 'MODULE_NOT_FOUND' && messageOf(error).includes('better_sqlite3.node')) {
|
|
55
|
+
return `'better-sqlite3' ships no native binary for ${process.platform}-${process.arch} — its prebuilt ones cover `
|
|
56
|
+
+ `linux, macOS and Windows on x64 and arm64. ${rebuild}`;
|
|
57
|
+
}
|
|
58
|
+
if (code === 'ERR_DLOPEN_FAILED') {
|
|
59
|
+
return `The native binary of 'better-sqlite3' does not load under Node ${process.version}: ${messageOf(error)}. ${rebuild}`;
|
|
60
|
+
}
|
|
61
|
+
return `SQLite could not open '${path}': ${messageOf(error)}`;
|
|
62
|
+
}
|
|
43
63
|
/** `source. */
|
|
44
64
|
Sources.register('sql', (conf) => {
|
|
45
65
|
const dialect = conf.dialect;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteSource.js","sourceRoot":"","sources":["../../src/sqlite/SqliteSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"SqliteSource.js","sourceRoot":"","sources":["../../src/sqlite/SqliteSource.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/adapter-sql",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.1-alpha.0",
|
|
4
4
|
"description": "Entity → SQL tables through Kysely (SQLite, PostgreSQL, MySQL, SQL Server).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -37,16 +37,16 @@
|
|
|
37
37
|
"src"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@fougere/core": "0.13.
|
|
41
|
-
"@fougere/schema": "0.13.
|
|
42
|
-
"better-sqlite3": "^13.0.3",
|
|
40
|
+
"@fougere/core": "0.13.1-alpha.0",
|
|
41
|
+
"@fougere/schema": "0.13.1-alpha.0",
|
|
43
42
|
"dequal": "^2.0.3"
|
|
44
43
|
},
|
|
45
44
|
"peerDependencies": {
|
|
46
45
|
"kysely": ">=0.28.17"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
|
-
"@fougere/core": "0.13.
|
|
48
|
+
"@fougere/core": "0.13.1-alpha.0",
|
|
49
|
+
"better-sqlite3": "^13.0.3",
|
|
50
50
|
"@types/better-sqlite3": "^7.6.13",
|
|
51
51
|
"@types/node": "^22.0.0",
|
|
52
52
|
"kysely": "^0.28.17"
|
package/src/driver/Driver.ts
CHANGED
|
@@ -4,50 +4,79 @@ import { MssqlDialect, MysqlDialect, PostgresDialect, type Dialect as KyselyDial
|
|
|
4
4
|
import type { Engine } from '../fields/Engine.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* The packages each engine opens with. The APP installs them, never this package, so a
|
|
8
|
+
* project that opens no SQL installs no driver — and nothing native.
|
|
9
9
|
*
|
|
10
10
|
* Documented: [sources](https://fougere.dev/docs/infra/sources).
|
|
11
11
|
*/
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export const PACKAGES: Record<Engine, string[]> = {
|
|
13
|
+
sqlite: ['better-sqlite3'],
|
|
14
|
+
pg: ['pg'],
|
|
15
|
+
mysql: ['mysql2'],
|
|
16
|
+
mssql: ['tedious', 'tarn'],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** The Kysely dialect an engine builds from a url, over its packages. SQLite opens a path. */
|
|
20
|
+
export type Driver = (url: string, modules: any[]) => KyselyDialect;
|
|
16
21
|
|
|
17
22
|
export const DRIVERS: Record<Exclude<Engine, 'sqlite'>, Driver> = {
|
|
18
|
-
pg: {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
dialect: (url, [mysql]) => new MysqlDialect({ pool: mysql.createPool(url) }),
|
|
25
|
-
},
|
|
26
|
-
mssql: {
|
|
27
|
-
packages: ['tedious', 'tarn'],
|
|
28
|
-
dialect: (url, [tedious, tarn]) => new MssqlDialect({
|
|
29
|
-
tarn: { ...tarn, options: { min: 0, max: 10 } },
|
|
30
|
-
tedious: { ...tedious, connectionFactory: () => new tedious.Connection(tediousConfig(url)) },
|
|
31
|
-
}),
|
|
32
|
-
},
|
|
23
|
+
pg: (url, [pg]) => new PostgresDialect({ pool: new pg.Pool({ connectionString: url }) }),
|
|
24
|
+
mysql: (url, [mysql]) => new MysqlDialect({ pool: mysql.createPool(url) }),
|
|
25
|
+
mssql: (url, [tedious, tarn]) => new MssqlDialect({
|
|
26
|
+
tarn: { ...tarn, options: { min: 0, max: 10 } },
|
|
27
|
+
tedious: { ...tedious, connectionFactory: () => new tedious.Connection(tediousConfig(url)) },
|
|
28
|
+
}),
|
|
33
29
|
};
|
|
34
30
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The packages of `engine`, resolved from the app — loaded at the moment it opens, not before.
|
|
33
|
+
* What is not installed and what is installed but will not load are two refusals, and each
|
|
34
|
+
* names what to do.
|
|
35
|
+
*/
|
|
36
|
+
export function modulesOf(engine: Engine, from = process.cwd()): any[] {
|
|
38
37
|
const require = createRequire(join(from, 'package.json'));
|
|
39
|
-
const
|
|
38
|
+
const packages = PACKAGES[engine];
|
|
39
|
+
const missing = packages.filter((name) => !resolves(require, name));
|
|
40
|
+
if (missing.length) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`The ${engine} engine opens through ${quoted(packages)}, and ${quoted(missing)} is not installed beside the app (${from}).\n`
|
|
43
|
+
+ `Install it there: pnpm add ${missing.join(' ')}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return packages.map((name) => {
|
|
40
48
|
try {
|
|
41
49
|
return require(name);
|
|
42
|
-
} catch {
|
|
50
|
+
} catch (error) {
|
|
43
51
|
throw new Error(
|
|
44
|
-
`
|
|
45
|
-
|
|
52
|
+
`The ${engine} engine opens through '${name}', which is installed beside the app (${from}) but does not load: ${messageOf(error)}`,
|
|
53
|
+
{ cause: error },
|
|
46
54
|
);
|
|
47
55
|
}
|
|
48
56
|
});
|
|
57
|
+
}
|
|
49
58
|
|
|
50
|
-
|
|
59
|
+
function resolves(require: NodeJS.Require, name: string): boolean {
|
|
60
|
+
try {
|
|
61
|
+
require.resolve(name);
|
|
62
|
+
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function quoted(names: string[]): string {
|
|
70
|
+
return names.map((name) => `'${name}'`).join(' and ');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function messageOf(error: unknown): string {
|
|
74
|
+
return error instanceof Error ? error.message.split('\n')[0] : String(error);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The dialect `url` names, over the driver the app installed beside it. */
|
|
78
|
+
export function dialectFor(engine: Exclude<Engine, 'sqlite'>, url: string, from = process.cwd()): KyselyDialect {
|
|
79
|
+
return DRIVERS[engine](url, modulesOf(engine, from));
|
|
51
80
|
}
|
|
52
81
|
|
|
53
82
|
/** `mssql://user:password@host:1433/database` — tedious takes an object and reads no url. */
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { mkdirSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { Kysely, SqliteDialect } from 'kysely';
|
|
4
|
-
import
|
|
4
|
+
import type BetterSqlite from 'better-sqlite3';
|
|
5
5
|
import { createStorageFactory } from '../crud/SqlStorage.js';
|
|
6
6
|
import { logQueries } from '../query/QuerySink.js';
|
|
7
7
|
import { drift, driftReport } from '../drift.js';
|
|
8
8
|
import { createKyselySource, sqlEnforces, sqlSink, type SqlSource } from '../source/SqlSource.js';
|
|
9
|
-
import { DRIVERS, dialectFor } from '../driver/Driver.js';
|
|
9
|
+
import { DRIVERS, dialectFor, messageOf, modulesOf } from '../driver/Driver.js';
|
|
10
10
|
import { ENGINES } from '../fields/Engine.js';
|
|
11
11
|
import { desiredTables, migrate } from '../diff/Change.js';
|
|
12
12
|
import { toTableName } from '../table/TableDef.js';
|
|
@@ -15,14 +15,14 @@ import type { SqliteSourceOptions } from './SqliteSourceOptions.js';
|
|
|
15
15
|
|
|
16
16
|
export interface SqliteSource extends SqlSource {
|
|
17
17
|
/** The raw handle, for pragmas and synchronous exec. */
|
|
18
|
-
sqlite:
|
|
18
|
+
sqlite: BetterSqlite.Database;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function createSqliteSource(opts: SqliteSourceOptions = {}): SqliteSource {
|
|
22
22
|
const path = opts.path ?? 'fougere.db';
|
|
23
23
|
// A file-backed DB needs its directory — SQLite won't create it.
|
|
24
24
|
if (path !== ':memory:') mkdirSync(dirname(path), { recursive: true });
|
|
25
|
-
const sqlite =
|
|
25
|
+
const sqlite = open(path);
|
|
26
26
|
sqlite.pragma('journal_mode = WAL');
|
|
27
27
|
sqlite.pragma('foreign_keys = ON');
|
|
28
28
|
const db = new Kysely<any>({ dialect: new SqliteDialect({ database: sqlite }), log: logQueries(opts.name ?? path) });
|
|
@@ -49,6 +49,29 @@ export function createSqliteSource(opts: SqliteSourceOptions = {}): SqliteSource
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function open(path: string): BetterSqlite.Database {
|
|
53
|
+
const [Database] = modulesOf('sqlite') as [typeof BetterSqlite];
|
|
54
|
+
try {
|
|
55
|
+
return new Database(path);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new Error(unopened(error, path), { cause: error });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function unopened(error: unknown, path: string): string {
|
|
62
|
+
const code = (error as { code?: string }).code;
|
|
63
|
+
const rebuild = 'Build it from its sources: cd node_modules/better-sqlite3 && npx node-gyp rebuild';
|
|
64
|
+
if (code === 'MODULE_NOT_FOUND' && messageOf(error).includes('better_sqlite3.node')) {
|
|
65
|
+
return `'better-sqlite3' ships no native binary for ${process.platform}-${process.arch} — its prebuilt ones cover `
|
|
66
|
+
+ `linux, macOS and Windows on x64 and arm64. ${rebuild}`;
|
|
67
|
+
}
|
|
68
|
+
if (code === 'ERR_DLOPEN_FAILED') {
|
|
69
|
+
return `The native binary of 'better-sqlite3' does not load under Node ${process.version}: ${messageOf(error)}. ${rebuild}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return `SQLite could not open '${path}': ${messageOf(error)}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
52
75
|
/** `source. */
|
|
53
76
|
Sources.register('sql', (conf: SourceConfig): Source => {
|
|
54
77
|
const dialect = conf.dialect as string | undefined;
|