diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..41583e3 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@jsr:registry=https://npm.jsr.io diff --git a/README.md b/README.md index 20df46b..9d5188a 100644 --- a/README.md +++ b/README.md @@ -150,14 +150,14 @@ const PostTags = table({ Currently supported drivers: -| Driver | import | -| ---------------- | ------------------------------ | -| `better-sqlite3` | `'rado/driver/better-sqlite3'` | -| `bun-sqlite` | `'rado/driver/bun-sqlite'` | -| `mysql2` | `'rado/driver/mysql2'` | -| `pg` | `'rado/driver/pg'` | -| `pglite` | `'rado/driver/pglite'` | -| `sql.js` | `'rado/driver/sql.js'` | +| Driver | import | +| ---------------------- | ------------------------------ | +| `better-sqlite3` | `'rado/driver/better-sqlite3'` | +| `bun:sqlite` | `'rado/driver/bun-sqlite'` | +| `mysql2` | `'rado/driver/mysql2'` | +| `pg` | `'rado/driver/pg'` | +| `@electric-sql/pglite` | `'rado/driver/pglite'` | +| `sql.js` | `'rado/driver/sql.js'` | Pass an instance of the database to the `connect` function to get started: diff --git a/bun.lockb b/bun.lockb index 44bba6c..705f508 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 0ce54f0..d88780f 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "cycles": "madge --circular src/index.ts src/sqlite.ts src/mysql.ts src/postgres.ts", "test:bun": "bun test", "test:node": "node --test-force-exit --test-concurrency=1 --import tsx --test \"**/*.test.ts\"", - "test:deno": "deno test --no-check --allow-read" + "test:deno": "deno test --no-check -A --unstable-ffi" }, "sideEffects": false, "exports": { @@ -35,7 +35,7 @@ "@types/glob": "^8.1.0", "@types/pg": "^8.11.8", "@types/sql.js": "^1.4.9", - "better-sqlite3": "^11.2.1", + "better-sqlite3": "^11.3.0", "esbuild": "^0.23.1", "glob": "^11.0.0", "madge": "^8.0.0", @@ -45,7 +45,7 @@ "sql.js": "^1.11.0", "sqlite3": "^5.1.7", "tsx": "^4.19.0", - "typescript": "^5.5.4", + "typescript": "^5.6.2", "@alinea/suite": "^0.4.0" } } diff --git a/src/core/expr/Include.ts b/src/core/expr/Include.ts index 48b221b..5a888d0 100644 --- a/src/core/expr/Include.ts +++ b/src/core/expr/Include.ts @@ -29,8 +29,12 @@ export class Include #mapFromDriverValue = (value: any, specs: DriverSpecs): any => { const {select, first} = getData(this) const parsed = specs.parsesJson ? value : JSON.parse(value) - if (first) - return parsed ? select!.mapRow({values: parsed, index: 0, specs}) : null + if (first) { + const result = parsed + ? select!.mapRow({values: parsed, index: 0, specs}) + : null + return result + } if (!parsed) return [] const rows: Array> = parsed const ctx: MapRowContext = { diff --git a/src/driver.ts b/src/driver.ts new file mode 100644 index 0000000..f709603 --- /dev/null +++ b/src/driver.ts @@ -0,0 +1,6 @@ +export {connect as 'better-sqlite3'} from './driver/better-sqlite3.ts' +export {connect as 'bun:sqlite'} from './driver/bun-sqlite.ts' +export {connect as 'mysql2'} from './driver/mysql2.ts' +export {connect as 'pg'} from './driver/pg.ts' +export {connect as '@electric-sql/pglite'} from './driver/pglite.ts' +export {connect as 'sql.js'} from './driver/sql.js.ts' diff --git a/src/driver/pglite.ts b/src/driver/pglite.ts index 55ae06e..aeb8654 100644 --- a/src/driver/pglite.ts +++ b/src/driver/pglite.ts @@ -1,6 +1,6 @@ import type {PGlite, Transaction} from '@electric-sql/pglite' +import {AsyncDatabase, type TransactionOptions} from '../core/Database.ts' import type {AsyncDriver, AsyncStatement, BatchQuery} from '../core/Driver.ts' -import {AsyncDatabase, type TransactionOptions} from '../index.ts' import {postgresDialect} from '../postgres/dialect.ts' import {postgresDiff} from '../postgres/diff.ts' import {setTransaction} from '../postgres/transactions.ts' diff --git a/test/TestDriver.ts b/test/TestDriver.ts deleted file mode 100644 index 8a30b30..0000000 --- a/test/TestDriver.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {suite} from '@alinea/suite' -import type {Database} from '../src/core/Database.ts' -import {testBasic} from './integration/TestBasic.ts' -import {testCTE} from './integration/TestCTE.ts' -import {testColumns} from './integration/TestColumns.ts' -import {testConstraints} from './integration/TestConstraints.ts' -import {testInclude} from './integration/TestInclude.ts' -import {testJoins} from './integration/TestJoins.ts' -import {testJson} from './integration/TestJson.ts' -import {testMigration} from './integration/TestMigration.ts' -import {testPreparedQuery} from './integration/TestPreparedQuery.ts' -import {testSubquery} from './integration/TestSubquery.ts' -import {testTransactions} from './integration/TestTransactions.ts' - -export async function testDriver( - meta: ImportMeta, - createDb: () => Promise, - supportsDiff = true -) { - const db = await createDb() - suite(meta, test => { - testBasic(db, test) - testColumns(db, test) - testSubquery(db, test) - testPreparedQuery(db, test) - testJoins(db, test) - testJson(db, test) - testTransactions(db, test) - testConstraints(db, test) - testCTE(db, test) - testInclude(db, test) - - if (supportsDiff) testMigration(db, test) - }) -} diff --git a/test/TestRuntime.ts b/test/TestRuntime.ts index 5607df6..c1ced0e 100644 --- a/test/TestRuntime.ts +++ b/test/TestRuntime.ts @@ -1,3 +1,5 @@ export const isBun = 'Bun' in globalThis export const isDeno = 'Deno' in globalThis export const isNode = !isBun && 'process' in globalThis +// @ts-ignore +export const isCi = !isDeno && 'CI' in process.env diff --git a/test/driver.test.ts b/test/driver.test.ts new file mode 100644 index 0000000..db85378 --- /dev/null +++ b/test/driver.test.ts @@ -0,0 +1,108 @@ +import * as driver from '@/driver.ts' +import {type DefineTest, type Describe, suite} from '@alinea/suite' +import {isBun, isCi, isNode} from './TestRuntime.ts' +import {testBasic} from './integration/TestBasic.ts' +import {testCTE} from './integration/TestCTE.ts' +import {testColumns} from './integration/TestColumns.ts' +import {testConstraints} from './integration/TestConstraints.ts' +import {testInclude} from './integration/TestInclude.ts' +import {testJoins} from './integration/TestJoins.ts' +import {testJson} from './integration/TestJson.ts' +import {testMigration} from './integration/TestMigration.ts' +import {testPreparedQuery} from './integration/TestPreparedQuery.ts' +import {testSubquery} from './integration/TestSubquery.ts' +import {testTransactions} from './integration/TestTransactions.ts' + +const init = { + 'better-sqlite3': { + condition: isNode, + supportsDiff: true, + async client() { + const {default: Database} = await import('better-sqlite3') + return new Database(':memory:') + } + }, + 'bun:sqlite': { + condition: isBun, + supportsDiff: true, + async client() { + const {Database} = await import('bun:sqlite') + return new Database(':memory:') + } + }, + mysql2: { + condition: isCi, + supportsDiff: false, + async client() { + const {default: mysql2} = await import('mysql2') + const client = mysql2.createConnection( + 'mysql://root:mysql@0.0.0.0:3306/mysql' + ) + return client + } + }, + '@electric-sql/pglite': { + condition: true, + supportsDiff: true, + async client() { + const {PGlite} = await import('@electric-sql/pglite') + return new PGlite() + } + }, + pg: { + condition: isCi, + supportsDiff: true, + async client() { + const {default: pg} = await import('pg') + const client = new pg.Client({ + connectionString: 'postgres://postgres:postgres@0.0.0.0:5432/postgres' + }) + await client.connect() + return client + } + }, + 'sql.js': { + condition: true, + supportsDiff: true, + async client() { + const {default: init} = await import('sql.js') + const {Database} = await init() + return new Database() + } + } +} + +async function createTests() { + const clients = await Promise.all( + Object.entries(init) + .filter(([name, meta]) => meta.condition) + .map( + async ([name, meta]) => + [name, await meta.client()] as [keyof typeof init, any] + ) + ) + return (test: DefineTest) => { + for (const [name, client] of clients) { + const {supportsDiff} = init[name] + const db = driver[name](client) + const prefixed: Describe = (description, fn) => + test(`${name}: ${description}`, fn) + const withName = Object.assign(prefixed, test) + + testBasic(db, withName) + testColumns(db, withName) + testSubquery(db, withName) + testPreparedQuery(db, withName) + testJoins(db, withName) + testJson(db, withName) + testTransactions(db, withName) + testConstraints(db, withName) + testCTE(db, withName) + testInclude(db, withName) + + if (supportsDiff) testMigration(db, withName) + } + } +} + +suite(import.meta, await createTests()) diff --git a/test/driver/better-sqlite3.test.ts b/test/driver/better-sqlite3.test.ts deleted file mode 100644 index 9722e9c..0000000 --- a/test/driver/better-sqlite3.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {testDriver} from '../TestDriver.ts' -import {isNode} from '../TestRuntime.ts' - -if (isNode) - await testDriver(import.meta, async () => { - const {default: Database} = await import('better-sqlite3') - const {connect} = await import('../../src/driver/better-sqlite3.ts') - return connect(new Database(':memory:')) - }) diff --git a/test/driver/bun-sqlite.test.ts b/test/driver/bun-sqlite.test.ts deleted file mode 100644 index 9939960..0000000 --- a/test/driver/bun-sqlite.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {testDriver} from '../TestDriver.ts' -import {isBun} from '../TestRuntime.ts' - -async function createDb() { - const {Database} = await import('bun:sqlite') - const {connect} = await import('../../src/driver/bun-sqlite.ts') - return connect(new Database(':memory:')) -} - -if (isBun) await testDriver(import.meta, createDb) diff --git a/test/driver/mysql2.test.ts b/test/driver/mysql2.test.ts deleted file mode 100644 index 9d03444..0000000 --- a/test/driver/mysql2.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {connect} from '../../src/driver/mysql2.ts' -import {testDriver} from '../TestDriver.ts' -import {isDeno} from '../TestRuntime.ts' - -if (!isDeno && process.env.CI) - await testDriver( - import.meta, - async () => { - const {default: mysql2} = await import('mysql2') - const client = mysql2.createConnection( - 'mysql://root:mysql@0.0.0.0:3306/mysql' - ) - return connect(client) - }, - false - ) diff --git a/test/driver/pg.test.ts b/test/driver/pg.test.ts deleted file mode 100644 index 8136540..0000000 --- a/test/driver/pg.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {connect} from '../../src/driver/pg.ts' -import {testDriver} from '../TestDriver.ts' -import {isDeno} from '../TestRuntime.ts' - -if (!isDeno && process.env.CI) - await testDriver(import.meta, async () => { - const {default: pg} = await import('pg') - const client = new pg.Client({ - connectionString: 'postgres://postgres:postgres@0.0.0.0:5432/postgres' - }) - await client.connect() - return connect(client) - }) diff --git a/test/driver/pglite.test.ts b/test/driver/pglite.test.ts deleted file mode 100644 index 716ff66..0000000 --- a/test/driver/pglite.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {connect} from '../../src/driver/pglite.ts' -import {testDriver} from '../TestDriver.ts' - -await testDriver(import.meta, async () => { - const {PGlite} = await import('@electric-sql/pglite') - return connect(new PGlite()) -}) diff --git a/test/driver/sql.js.test.ts b/test/driver/sql.js.test.ts deleted file mode 100644 index f342e5f..0000000 --- a/test/driver/sql.js.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {connect} from '../../src/driver/sql.js.ts' -import {testDriver} from '../TestDriver.ts' - -await testDriver(import.meta, async () => { - const {default: init} = await import('sql.js') - const {Database} = await init() - return connect(new Database()) -})