Skip to content

Commit 465419b

Browse files
committed
feat(dialect-wasqlite-worker): function to create custom OnMessageCallback in worker
1 parent 6fc13c3 commit 465419b

File tree

8 files changed

+168
-128
lines changed

8 files changed

+168
-128
lines changed

packages/dialect-wasqlite-worker/README.md

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,38 @@ No need to set response header like [official wasm](../dialect-wasm/README.md#of
1010
pnpm add kysely kysely-wasqlite-worker
1111
```
1212

13+
## Usage
14+
15+
```ts
16+
import {
17+
generateDialectOptions,
18+
isIdbSupported,
19+
isModuleWorkerSupport,
20+
isOpfsSupported,
21+
useDefaultWasmURL,
22+
useDefaultWorker,
23+
WaSqliteWorkerDialect
24+
} from 'kysely-wasqlite-worker'
25+
26+
const dialect = new WaSqliteWorkerDialect({
27+
fileName: 'test',
28+
})
29+
```
30+
31+
### Custom Worker
32+
33+
in `worker.ts`
34+
35+
```ts
36+
import { createOnMessageCallback, customFunction } from 'kysely-wasqlite-worker'
37+
38+
onmessage = createOnMessageCallback(
39+
async (sqliteDB: SQLiteDB) => {
40+
customFunction(sqliteDB.sqlite, sqliteDB.db, 'customFunction', (a, b) => a + b)
41+
}
42+
)
43+
```
44+
1345
## Config
1446

1547
```ts
@@ -59,24 +91,6 @@ export interface WaSqliteWorkerDialectConfig {
5991
}
6092
```
6193

62-
## Usage
63-
64-
```ts
65-
import {
66-
generateDialectOptions,
67-
isIdbSupported,
68-
isModuleWorkerSupport,
69-
isOpfsSupported,
70-
useDefaultWasmURL,
71-
useDefaultWorker,
72-
WaSqliteWorkerDialect
73-
} from 'kysely-wasqlite-worker'
74-
75-
const dialect = new WaSqliteWorkerDialect({
76-
fileName: 'test',
77-
})
78-
```
79-
8094
see more in [playground](../../playground/src/modules/wasqliteWorker.ts)
8195

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

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { DatabaseConnection, Driver, QueryResult } from 'kysely'
22
import type { Emitter } from 'zen-mitt'
3-
import type { EventWithError, MainMsg, WaSqliteWorkerDialectConfig, WorkerMsg } from './type'
3+
import type { EventWithError, MainToWorkerMsg, WaSqliteWorkerDialectConfig, WorkerToMainMsg } from './type'
44
import { isModuleWorkerSupport, isOpfsSupported } from '@subframe7536/sqlite-wasm'
55
import { CompiledQuery, SelectQueryNode } from 'kysely'
66
import { mitt } from 'zen-mitt'
@@ -29,7 +29,7 @@ export class WaSqliteWorkerDriver implements Driver {
2929

3030
this.worker = parseWorkerOrURL(this.config.worker || defaultWorker, useOPFS || isModuleWorkerSupport())
3131

32-
this.worker.onmessage = ({ data: [type, ...msg] }: MessageEvent<WorkerMsg>) => {
32+
this.worker.onmessage = ({ data: [type, ...msg] }: MessageEvent<WorkerToMainMsg>) => {
3333
this.mitt?.emit(type, ...msg)
3434
}
3535
this.worker.postMessage([
@@ -38,7 +38,7 @@ export class WaSqliteWorkerDriver implements Driver {
3838
// if use OPFS, wasm should use sync version
3939
parseWorkerOrURL(this.config.url ?? defaultWasmURL, !useOPFS) as string,
4040
useOPFS,
41-
] satisfies MainMsg)
41+
] satisfies MainToWorkerMsg)
4242
await new Promise<void>((resolve, reject) => {
4343
this.mitt?.once(0, (_, err) => err ? reject(err) : resolve())
4444
})
@@ -74,7 +74,7 @@ export class WaSqliteWorkerDriver implements Driver {
7474
if (!this.worker) {
7575
return
7676
}
77-
this.worker.postMessage([2] satisfies MainMsg)
77+
this.worker.postMessage([2] satisfies MainToWorkerMsg)
7878
return new Promise<void>((resolve, reject) => {
7979
this.mitt?.once(2, (_, err) => {
8080
if (err) {
@@ -127,7 +127,7 @@ class WaSqliteWorkerConnection implements DatabaseConnection {
127127
if (!SelectQueryNode.is(query)) {
128128
throw new Error('WaSqlite dialect only supported SELECT queries')
129129
}
130-
this.worker.postMessage([3, sql, parameters] satisfies MainMsg)
130+
this.worker.postMessage([3, sql, parameters] satisfies MainToWorkerMsg)
131131
let resolver: ((value: IteratorResult<{ rows: QueryResult<R>[] }>) => void) | null = null
132132
let rejecter: ((reason: any) => void) | null = null
133133

@@ -169,7 +169,7 @@ class WaSqliteWorkerConnection implements DatabaseConnection {
169169
async executeQuery<R>(compiledQuery: CompiledQuery<unknown>): Promise<QueryResult<R>> {
170170
const { parameters, sql, query } = compiledQuery
171171
const isSelect = SelectQueryNode.is(query)
172-
this.worker.postMessage([1, isSelect, sql, parameters] satisfies MainMsg)
172+
this.worker.postMessage([1, isSelect, sql, parameters] satisfies MainToWorkerMsg)
173173
return new Promise((resolve, reject) => {
174174
if (!this.mitt) {
175175
reject(new Error('kysely instance has been destroyed'))

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ import type { WaSqliteWorkerDialectConfig } from './type'
33
import { SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from 'kysely'
44
import { WaSqliteWorkerDriver } from './driver'
55

6-
export { isIdbSupported, isModuleWorkerSupport, isOpfsSupported } from '@subframe7536/sqlite-wasm'
6+
export { createOnMessageCallback } from './worker/utils'
7+
8+
export {
9+
customFunction,
10+
isIdbSupported,
11+
isModuleWorkerSupport,
12+
isOpfsSupported,
13+
type SQLiteDB,
14+
} from '@subframe7536/sqlite-wasm'
715

816
export class WaSqliteWorkerDialect implements Dialect {
917
/**

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ type InitMsg = [
6767
]
6868

6969
type CloseMsg = [2]
70-
export type MainMsg = InitMsg | RunMsg | CloseMsg | StreamMsg
70+
export type MainToWorkerMsg = InitMsg | RunMsg | CloseMsg | StreamMsg
7171

72-
export type WorkerMsg = {
72+
export type WorkerToMainMsg = {
7373
[K in keyof Events]: [
7474
type: K,
7575
data: Events[K],

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

Lines changed: 0 additions & 55 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createOnMessageCallback } from './utils'
2+
3+
onmessage = createOnMessageCallback()
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import type { SQLiteDB } from '@subframe7536/sqlite-wasm'
2+
import type { QueryResult } from 'kysely'
3+
import type { MainToWorkerMsg, WorkerToMainMsg } from '../type'
4+
import { initSQLite } from '@subframe7536/sqlite-wasm'
5+
6+
let db: SQLiteDB
7+
8+
async function init(
9+
fileName: string,
10+
url: string,
11+
useOPFS: boolean,
12+
afterInit?: (sqliteDB: SQLiteDB) => Promise<void>,
13+
): Promise<void> {
14+
db = await initSQLite(
15+
(
16+
useOPFS
17+
? (await import('@subframe7536/sqlite-wasm/opfs')).useOpfsStorage
18+
: (await import('@subframe7536/sqlite-wasm/idb')).useIdbStorage
19+
)(
20+
fileName,
21+
{ url },
22+
),
23+
)
24+
await afterInit?.(db)
25+
}
26+
async function exec(isSelect: boolean, sql: string, parameters?: readonly unknown[]): Promise<QueryResult<any>> {
27+
const rows = await db.run(sql, parameters as any[])
28+
return isSelect || rows.length
29+
? { rows }
30+
: {
31+
rows,
32+
insertId: BigInt(db.lastInsertRowId()),
33+
numAffectedRows: BigInt(db.changes()),
34+
}
35+
}
36+
async function stream(onData: (data: any) => void, sql: string, parameters?: readonly unknown[]): Promise<void> {
37+
await db.stream(onData, sql, parameters as any[])
38+
}
39+
40+
/**
41+
* Handle worker message, support custom callback on initialization
42+
* @example
43+
* // worker.ts
44+
* import { createOnMessageCallback, customFunction } from 'kysely-wasqlite-worker'
45+
*
46+
* onmessage = createOnMessageCallback(
47+
* async (sqliteDB: SQLiteDB) => {
48+
* customFunction(sqliteDB.sqlite, sqliteDB.db, 'customFunction', (a, b) => a + b)
49+
* }
50+
* )
51+
*/
52+
export function createOnMessageCallback(
53+
afterInit?: (sqliteDB: SQLiteDB) => Promise<void>,
54+
): (event: MessageEvent<MainToWorkerMsg>) => Promise<void> {
55+
return async ({
56+
data: [msg, data1, data2, data3],
57+
}: MessageEvent<MainToWorkerMsg>) => {
58+
const ret: WorkerToMainMsg = [msg, null, null]
59+
try {
60+
switch (msg) {
61+
case 0:
62+
await init(data1, data2, data3, afterInit)
63+
break
64+
case 1:
65+
ret[1] = await exec(data1, data2, data3)
66+
break
67+
case 2:
68+
await db.close()
69+
break
70+
case 3:
71+
await stream(val => postMessage([3, [val], null] satisfies WorkerToMainMsg), data1, data2)
72+
ret[0] = 4
73+
break
74+
}
75+
} catch (error) {
76+
ret[2] = error
77+
}
78+
postMessage(ret)
79+
}
80+
}
Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,42 @@
11
import { copyFile } from 'node:fs/promises'
22
import { defineConfig } from 'tsup'
33

4-
export default defineConfig([
5-
{
6-
entry: [
7-
'src/index.ts',
8-
],
9-
clean: true,
10-
format: ['cjs', 'esm'],
11-
shims: true,
12-
dts: true,
13-
external: ['kysely'],
14-
treeshake: true,
4+
export default defineConfig({
5+
entry: {
6+
index: 'src/index.ts',
7+
worker: 'src/worker/index.ts',
158
},
16-
{
17-
entry: [
18-
'src/worker.ts',
19-
],
20-
clean: false,
21-
format: ['cjs', 'esm'],
22-
dts: true,
23-
treeshake: true,
24-
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-
},
9+
clean: true,
10+
format: ['cjs', 'esm'],
11+
shims: true,
12+
dts: true,
13+
external: ['kysely'],
14+
treeshake: true,
15+
plugins: [
16+
{
17+
name: 'classic worker',
18+
renderChunk(code) {
19+
if (this.format === 'cjs') {
20+
return { code: code.replaceAll('import.meta.url', 'self.location.href') }
21+
}
3222
},
33-
{
34-
name: 'copy',
35-
buildEnd(this) {
36-
if (this.format === 'esm') {
37-
Promise.all([
38-
copyFile(
39-
'./node_modules/@subframe7536/sqlite-wasm/dist/wa-sqlite.wasm',
40-
'./dist/wa-sqlite.wasm',
41-
),
42-
copyFile(
43-
'./node_modules/@subframe7536/sqlite-wasm/dist/wa-sqlite-async.wasm',
44-
'./dist/wa-sqlite-async.wasm',
45-
),
46-
])
47-
}
48-
},
23+
},
24+
{
25+
name: 'copy',
26+
buildEnd(this) {
27+
if (this.format === 'esm') {
28+
Promise.all([
29+
copyFile(
30+
'./node_modules/@subframe7536/sqlite-wasm/dist/wa-sqlite.wasm',
31+
'./dist/wa-sqlite.wasm',
32+
),
33+
copyFile(
34+
'./node_modules/@subframe7536/sqlite-wasm/dist/wa-sqlite-async.wasm',
35+
'./dist/wa-sqlite-async.wasm',
36+
),
37+
])
38+
}
4939
},
50-
],
51-
},
52-
])
40+
},
41+
],
42+
})

0 commit comments

Comments
 (0)