Skip to content

Commit d0f0556

Browse files
committed
feat(dialect-wasqlite-worker): fallback module worker to classic worker
1 parent 3060810 commit d0f0556

File tree

12 files changed

+180
-68
lines changed

12 files changed

+180
-68
lines changed

packages/dialect-wasqlite-worker/README.md

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,66 @@ pnpm add kysely kysely-wasqlite-worker
1212

1313
```ts
1414
export interface WaSqliteWorkerDialectConfig {
15-
dbName: string
1615
/**
17-
* the URL of wa-sqlite WASM
18-
* @example
19-
* ```ts
20-
* // vite
21-
* import url from 'kysely-wasqlite-worker/dist/wa-sqlite-async.wasm?url'
22-
* ```
16+
* db file name
17+
*/
18+
fileName: string
19+
/**
20+
* prefer to store data in OPFS
2321
*/
24-
url?: string
22+
preferOPFS?: boolean
2523
/**
26-
* worker for executing sql
24+
* wasqlite worker
25+
*
26+
* built-in: {@link useDefaultWorker}
27+
* @param supportModuleWorker if support { type: 'module' } in worker options
2728
* @example
28-
* ```ts
29-
* // vite
30-
* import Worker from 'kysely-wasqlite-worker/dist/worker?worker'
31-
* ```
29+
* import { useDefaultWorker } from 'kysely-wasqlite-worker'
30+
* @example
31+
* (support) => support
32+
* ? new Worker(
33+
* new URL('kysely-wasqlite-worker/worker-module', import.meta.url),
34+
* { type: 'module', credentials: 'same-origin' }
35+
* )
36+
* : new Worker(
37+
* new URL('kysely-wasqlite-worker/worker-classic', import.meta.url),
38+
* { type: 'classic', name: 'test' }
39+
* )
3240
*/
33-
worker?: Worker
41+
worker: Worker | ((supportModuleWorker: boolean) => Worker)
3442
/**
35-
* prefer to store in OPFS, fallback to IndexedDB
43+
* wasm URL
44+
*
45+
* built-in: {@link useDefaultWasmURL}
46+
* @param useAsyncWasm if need to use wa-sqlite-async.wasm
47+
* @example
48+
* import { useDefaultWasmURL } from 'kysely-wasqlite-worker'
49+
* @example
50+
* (useAsync) => useAsync
51+
* ? 'https://cdn.jsdelivr.net/gh/rhashimoto/wa-sqlite@v0.9.9/dist/wa-sqlite-async.wasm'
52+
* : new URL('kysely-wasqlite-worker/wasm-sync', import.meta.url).href
3653
*/
37-
preferOPFS?: boolean
38-
onCreateConnection?: (connection: DatabaseConnection) => Promise<void>
54+
url: string | ((useAsyncWasm: boolean) => string)
55+
onCreateConnection?: (connection: DatabaseConnection) => Promisable<void>
3956
}
4057
```
4158

4259
## usage
4360

44-
see in [playground](../../playground/src/modules/wasqliteWorker.ts)
61+
```ts
62+
import {
63+
WaSqliteWorkerDialect,
64+
generateDialectOptions,
65+
useDefaultWasmURL,
66+
useDefaultWorker
67+
} from 'kysely-wasqlite-worker'
68+
69+
const dialect = new WaSqliteWorkerDialect({
70+
fileName: 'test',
71+
})
72+
```
73+
74+
see more in [playground](../../playground/src/modules/wasqliteWorker.ts)
4575

4676
if throw error when using `Vite` to build, add worker config
4777

packages/dialect-wasqlite-worker/package.json

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,24 @@
2222
".": {
2323
"import": "./dist/index.mjs",
2424
"require": "./dist/index.js"
25-
}
25+
},
26+
"./worker-classic": "./dist/worker.js",
27+
"./worker-module": "./dist/worker.mjs",
28+
"./wasm-sync": "./dist/wa-sqlite.wasm",
29+
"./wasm-async": "./dist/wa-sqlite-async.wasm"
2630
},
2731
"main": "./dist/index.js",
2832
"module": "./dist/index.mjs",
33+
"typesVersions": {
34+
"*": {
35+
"./worker-classic": [
36+
"./dist/worker.d.ts"
37+
],
38+
"./worker-module": [
39+
"./dist/worker.d.mts"
40+
]
41+
}
42+
},
2943
"files": [
3044
"dist"
3145
],
@@ -43,6 +57,6 @@
4357
"zen-mitt": "^0.3.0"
4458
},
4559
"devDependencies": {
46-
"@subframe7536/sqlite-wasm": "^0.1.4"
60+
"@subframe7536/sqlite-wasm": "^0.1.9"
4761
}
4862
}

packages/dialect-wasqlite-worker/src/driver.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import type { DatabaseConnection, Driver, QueryResult } from 'kysely'
22
import { CompiledQuery, SelectQueryNode } from 'kysely'
33
import type { Emitter } from 'zen-mitt'
44
import { mitt } from 'zen-mitt'
5+
import { isModuleWorkerSupport, isOpfsSupported } from '@subframe7536/sqlite-wasm'
56
import type { EventWithError, MainMsg, WorkerMsg } from './type'
7+
import { defaultWasmURL, defaultWorker, parseObject } from './utils'
68
import type { WaSqliteWorkerDialectConfig } from '.'
79

810
export class WaSqliteWorkerDriver implements Driver {
@@ -16,17 +18,18 @@ export class WaSqliteWorkerDriver implements Driver {
1618
}
1719

1820
async init(): Promise<void> {
19-
this.worker = this.config.worker
20-
?? new Worker(new URL('./worker', import.meta.url), { type: 'module' })
21+
const useOPFS = (this.config.preferOPFS ?? true) ? await isOpfsSupported() : false
22+
this.worker = parseObject(this.config.worker || defaultWorker, useOPFS || isModuleWorkerSupport())
2123
this.mitt = mitt<EventWithError>()
2224
this.worker.onmessage = ({ data: { type, ...msg } }: MessageEvent<WorkerMsg>) => {
2325
this.mitt?.emit(type, msg)
2426
}
2527
this.worker.postMessage({
2628
type: 'init',
2729
fileName: this.config.fileName,
28-
url: this.config.url,
29-
preferOPFS: this.config.preferOPFS,
30+
// if use OPFS, wasm should use sync version
31+
url: parseObject(this.config.url || defaultWasmURL, !useOPFS),
32+
useOPFS,
3033
} satisfies MainMsg)
3134
await new Promise<void>((resolve, reject) => {
3235
this.mitt?.once('init', ({ err }) => {

packages/dialect-wasqlite-worker/src/index.ts

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,66 @@ import { SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from 'kysely'
33
import { WaSqliteWorkerDriver } from './driver'
44
import type { Promisable } from './type'
55

6-
export { isIdbSupported as isSupported } from '@subframe7536/sqlite-wasm'
76
export interface WaSqliteWorkerDialectConfig {
87
/**
98
* db file name
109
*/
1110
fileName: string
1211
/**
13-
* the URL of `wa-sqlite` WASM
14-
* @example
15-
* ```ts
16-
* // vite
17-
* import url from 'kysely-wasqlite-worker/dist/wa-sqlite-async.wasm?url'
18-
* ```
12+
* prefer to store data in OPFS
13+
* @default true
1914
*/
20-
url?: string
15+
preferOPFS?: boolean
2116
/**
22-
* worker for executing sql
17+
* wasqlite worker
18+
*
19+
* built-in: {@link useDefaultWorker}
20+
* @param supportModuleWorker if support `{ type: 'module' }` in worker options
2321
* @example
24-
* ```ts
25-
* // vite
26-
* import Worker from 'kysely-wasqlite-worker/dist/worker?worker'
27-
* ```
22+
* import { useDefaultWorker } from 'kysely-wasqlite-worker'
23+
* @example
24+
* (supportModuleWorker) => supportModuleWorker
25+
* ? new Worker(
26+
* new URL('kysely-wasqlite-worker/worker-module', import.meta.url),
27+
* { type: 'module', credentials: 'same-origin' }
28+
* )
29+
* : new Worker(
30+
* new URL('kysely-wasqlite-worker/worker-classic', import.meta.url),
31+
* { type: 'classic', name: 'test' }
32+
* )
2833
*/
29-
worker?: Worker
34+
worker?: Worker | ((supportModuleWorker: boolean) => Worker)
3035
/**
31-
* prefer to use OPFS, fallback to IndexedDB
36+
* wasm URL
37+
*
38+
* built-in: {@link useDefaultWasmURL}
39+
* @param useAsyncWasm if need to use wa-sqlite-async.wasm
40+
* @example
41+
* import { useDefaultWasmURL } from 'kysely-wasqlite-worker'
42+
* @example
43+
* (useAsyncWasm) => useAsyncWasm
44+
* ? 'https://cdn.jsdelivr.net/gh/rhashimoto/wa-sqlite@v0.9.9/dist/wa-sqlite-async.wasm'
45+
* : new URL('kysely-wasqlite-worker/wasm-sync', import.meta.url).href
3246
*/
33-
preferOPFS?: boolean
47+
url?: string | ((useAsyncWasm: boolean) => string)
3448
onCreateConnection?: (connection: DatabaseConnection) => Promisable<void>
3549
}
50+
export { isIdbSupported, isOpfsSupported, isModuleWorkerSupport } from '@subframe7536/sqlite-wasm'
51+
3652
export class WaSqliteWorkerDialect implements Dialect {
3753
readonly #config: WaSqliteWorkerDialectConfig
3854

3955
/**
40-
* dialect for [wa-sqlite](https://github.com/rhashimoto/wa-sqlite)
56+
* dialect for [`wa-sqlite`](https://github.com/rhashimoto/wa-sqlite),
57+
* execute sql in `Web Worker`,
58+
* store data in [OPFS](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system) or IndexedDB
59+
*
60+
* @example
61+
* import { WaSqliteWorkerDialect } from 'kysely-wasqlite-worker'
4162
*
42-
* execute sql in `Web Worker`, store data in IndexedDB
63+
* const dialect = new WaSqliteWorkerDialect({
64+
* fileName: 'test',
65+
* })
4366
*/
4467
constructor(config: WaSqliteWorkerDialectConfig) {
4568
this.#config = config

packages/dialect-wasqlite-worker/src/type.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ export type RunMsg = {
1111

1212
export type InitMsg = {
1313
type: 'init'
14-
url?: string
14+
url: string
1515
fileName: string
16-
preferOPFS?: boolean
16+
useOPFS: boolean
1717
}
1818

1919
export type MainMsg =
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export function parseObject<T, P extends T | ((is: boolean) => T)>(obj: P, is: boolean): T {
2+
return typeof obj === 'function' ? obj(is) : is
3+
}
4+
5+
/**
6+
* auto load target worker
7+
*
8+
* **only basic worker options**
9+
*/
10+
export function defaultWorker(support: boolean) {
11+
return support
12+
? new Worker(new URL('worker.mjs', import.meta.url), { type: 'module' })
13+
: new Worker(new URL('worker.js', import.meta.url))
14+
}
15+
16+
/**
17+
* auto load target wasm
18+
*/
19+
export function defaultWasmURL(useAsyncWasm: boolean) {
20+
return useAsyncWasm
21+
? new URL('wa-sqlite-async.wasm', import.meta.url).href
22+
: new URL('wa-sqlite.wasm', import.meta.url).href
23+
}

packages/dialect-wasqlite-worker/src/worker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import type { SQLiteDB } from '@subframe7536/sqlite-wasm'
2-
import { initSQLite, isOpfsSupported } from '@subframe7536/sqlite-wasm'
2+
import { initSQLite } from '@subframe7536/sqlite-wasm'
33
import type { QueryResult } from 'kysely'
44
import type { InitMsg, MainMsg, RunMsg, WorkerMsg } from './type'
55

66
let db: SQLiteDB
77

8-
async function init({ fileName, preferOPFS, url }: InitMsg) {
8+
async function init({ fileName, useOPFS, url }: InitMsg) {
99
db = await initSQLite(
1010
(
11-
await isOpfsSupported() && preferOPFS
11+
useOPFS
1212
? (await import('@subframe7536/sqlite-wasm/opfs')).useOpfsStorage
1313
: (await import('@subframe7536/sqlite-wasm/idb')).useIdbStorage
1414
)(

packages/dialect-wasqlite-worker/tsup.config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,15 @@ export default defineConfig([
2121
format: ['cjs', 'esm'],
2222
dts: true,
2323
treeshake: true,
24-
minify: true,
2524
plugins: [
25+
{
26+
name: 'classic worker',
27+
renderChunk(code) {
28+
if (this.format === 'cjs') {
29+
return { code: code.replaceAll('import.meta.url', 'self.location.href') }
30+
}
31+
},
32+
},
2633
{
2734
name: 'copy',
2835
buildEnd(this) {

playground/src/App.vue

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ async function deleteDatabase() {
3333
} catch { }
3434
}
3535
async function clear() {
36-
// console.clear()
36+
console.clear()
3737
await deleteFile('sqljs')
3838
await deleteFile('sqljsWorker')
3939
await deleteDatabase()
@@ -45,9 +45,8 @@ async function clear() {
4545
await root.removeEntry('test.db-journal')
4646
} catch { }
4747
try {
48-
await root.removeEntry('wa-sqlite-worker-test',{recursive:true})
48+
await root.removeEntry('wa-sqlite-worker-test', { recursive:true })
4949
} catch { }
50-
console.log('clear all')
5150
}
5251
</script>
5352

playground/src/modules/utils.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,9 @@ export async function testDB(dialect: Dialect) {
4141
})
4242
}
4343

44-
return db.execute(db => db.selectFrom('test').selectAll())
44+
return db.execute(db => db.selectFrom('test').selectAll()).then(async (data) => {
45+
await db.destroy()
46+
console.log(data)
47+
return data
48+
})
4549
}

0 commit comments

Comments
 (0)