@h-ai/reldb 0.1.0-alpha.49 → 0.1.0-alpha.50

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,112 @@
1
+ /**
2
+ * @h-ai/reldb — SQLite worker 线程脚本
3
+ *
4
+ * 纯 JavaScript,供 worker_threads 直接加载(源码与打包运行位置均可)。
5
+ * 持有唯一 better-sqlite3 连接,按 FIFO 串行处理主线程消息,把同步查询的阻塞移出主线程事件循环。
6
+ *
7
+ * 消息协议:
8
+ * 主线程 → worker: { id, op: 'all'|'get'|'run'|'exec'|'batch'|'close', sql?, params?, statements? }
9
+ * worker → 主线程: { id, ok, result } | { id, ok:false, error:{ message } } | { type:'ready'|'init-error' }
10
+ */
11
+
12
+ import { createRequire } from 'node:module'
13
+ import { parentPort, workerData } from 'node:worker_threads'
14
+
15
+ const require = createRequire(import.meta.url)
16
+
17
+ if (!parentPort) {
18
+ throw new Error('reldb sqlite worker must run inside a worker thread')
19
+ }
20
+
21
+ /** 归一化错误为可结构化克隆的对象。 */
22
+ function serializeError(error) {
23
+ return { message: error instanceof Error ? error.message : String(error) }
24
+ }
25
+
26
+ /** 打开数据库连接并按需启用 WAL。 */
27
+ function openDatabase() {
28
+ const Database = require('better-sqlite3')
29
+ const database = new Database(workerData.database, { readonly: workerData.readonly ?? false })
30
+ if (workerData.walMode !== false) {
31
+ database.pragma('journal_mode = WAL')
32
+ }
33
+ return database
34
+ }
35
+
36
+ let db = null
37
+
38
+ try {
39
+ db = openDatabase()
40
+ parentPort.postMessage({ type: 'ready' })
41
+ }
42
+ catch (error) {
43
+ parentPort.postMessage({ type: 'init-error', error: serializeError(error) })
44
+ }
45
+
46
+ parentPort.on('message', (msg) => {
47
+ const { id, op, sql, params, statements } = msg
48
+
49
+ if (op === 'close') {
50
+ try {
51
+ db?.close()
52
+ }
53
+ catch {
54
+ // 忽略关闭异常,仍需回执并退出。
55
+ }
56
+ db = null
57
+ parentPort.postMessage({ id, ok: true, result: undefined })
58
+ parentPort.close()
59
+ return
60
+ }
61
+
62
+ try {
63
+ if (!db)
64
+ throw new Error('sqlite worker database is not open')
65
+
66
+ let result
67
+ switch (op) {
68
+ case 'all': {
69
+ const stmt = db.prepare(sql)
70
+ result = params ? stmt.all(...params) : stmt.all()
71
+ break
72
+ }
73
+ case 'get': {
74
+ const stmt = db.prepare(sql)
75
+ result = (params ? stmt.get(...params) : stmt.get()) ?? null
76
+ break
77
+ }
78
+ case 'run': {
79
+ const stmt = db.prepare(sql)
80
+ const runResult = params ? stmt.run(...params) : stmt.run()
81
+ result = { changes: runResult.changes, lastInsertRowid: runResult.lastInsertRowid }
82
+ break
83
+ }
84
+ case 'exec': {
85
+ db.exec(sql)
86
+ result = undefined
87
+ break
88
+ }
89
+ case 'batch': {
90
+ const database = db
91
+ const tx = database.transaction((items) => {
92
+ for (const item of items) {
93
+ const stmt = database.prepare(item.sql)
94
+ if (item.params)
95
+ stmt.run(...item.params)
96
+ else stmt.run()
97
+ }
98
+ })
99
+ tx(statements)
100
+ result = undefined
101
+ break
102
+ }
103
+ default:
104
+ throw new Error(`unknown sqlite worker op: ${op}`)
105
+ }
106
+
107
+ parentPort.postMessage({ id, ok: true, result })
108
+ }
109
+ catch (error) {
110
+ parentPort.postMessage({ id, ok: false, error: serializeError(error) })
111
+ }
112
+ })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/reldb",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.49",
4
+ "version": "0.1.0-alpha.50",
5
5
  "description": "Hai Framework relational database module for SQLite, PostgreSQL, and MySQL.",
6
6
  "license": "Apache-2.0",
7
7
  "exports": {
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "better-sqlite3": "^12.6.2",
24
24
  "zod": "^4.4.3",
25
- "@h-ai/core": "0.1.0-alpha.49"
25
+ "@h-ai/core": "0.1.0-alpha.50"
26
26
  },
27
27
  "optionalDependencies": {
28
28
  "mysql2": "^3.11.5",