@aiao/rxdb-adapter-electron 0.0.0-stage → 0.0.28

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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +394 -2
  3. package/dist/RxDBAdapterElectron.d.ts +61 -0
  4. package/dist/RxDBAdapterElectron.d.ts.map +1 -0
  5. package/dist/electron-adapter.interface.d.ts +23 -0
  6. package/dist/electron-adapter.interface.d.ts.map +1 -0
  7. package/dist/electron-file-host.d.ts +52 -0
  8. package/dist/electron-file-host.d.ts.map +1 -0
  9. package/dist/electron-sqlite-host.d.ts +65 -0
  10. package/dist/electron-sqlite-host.d.ts.map +1 -0
  11. package/dist/host.d.ts +19 -0
  12. package/dist/host.d.ts.map +1 -0
  13. package/dist/host.js +768 -0
  14. package/dist/index.d.ts +22 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +36 -0
  17. package/dist/node-sqlite-engine.d.ts +97 -0
  18. package/dist/node-sqlite-engine.d.ts.map +1 -0
  19. package/dist/pglite/RxDBAdapterElectronPGlite.d.ts +96 -0
  20. package/dist/pglite/RxDBAdapterElectronPGlite.d.ts.map +1 -0
  21. package/dist/pglite/desktop-pglite-client.d.ts +144 -0
  22. package/dist/pglite/desktop-pglite-client.d.ts.map +1 -0
  23. package/dist/pglite/desktop-pglite-results.d.ts +14 -0
  24. package/dist/pglite/desktop-pglite-results.d.ts.map +1 -0
  25. package/dist/pglite/electron-pglite-backup-error.d.ts +32 -0
  26. package/dist/pglite/electron-pglite-backup-error.d.ts.map +1 -0
  27. package/dist/pglite/pglite-adapter.interface.d.ts +68 -0
  28. package/dist/pglite/pglite-adapter.interface.d.ts.map +1 -0
  29. package/dist/pglite/restore-electron-pglite-database.d.ts +68 -0
  30. package/dist/pglite/restore-electron-pglite-database.d.ts.map +1 -0
  31. package/dist/pglite-host/electron-pglite-host.d.ts +129 -0
  32. package/dist/pglite-host/electron-pglite-host.d.ts.map +1 -0
  33. package/dist/pglite-host/pglite-host-data-dir.d.ts +75 -0
  34. package/dist/pglite-host/pglite-host-data-dir.d.ts.map +1 -0
  35. package/dist/pglite-host/pglite-host-lock.d.ts +48 -0
  36. package/dist/pglite-host/pglite-host-lock.d.ts.map +1 -0
  37. package/dist/pglite-host/pglite-host-restore.d.ts +38 -0
  38. package/dist/pglite-host/pglite-host-restore.d.ts.map +1 -0
  39. package/dist/pglite-host/pglite-host-runtime.d.ts +90 -0
  40. package/dist/pglite-host/pglite-host-runtime.d.ts.map +1 -0
  41. package/dist/pglite-host.d.ts +17 -0
  42. package/dist/pglite-host.d.ts.map +1 -0
  43. package/dist/pglite-host.js +738 -0
  44. package/dist/pglite.d.ts +22 -0
  45. package/dist/pglite.d.ts.map +1 -0
  46. package/dist/pglite.js +493 -0
  47. package/dist/sqlite-script.d.ts +26 -0
  48. package/dist/sqlite-script.d.ts.map +1 -0
  49. package/package.json +94 -3
  50. package/src/RxDBAdapterElectron.ts +105 -0
  51. package/src/electron-adapter.interface.ts +25 -0
  52. package/src/electron-file-host.ts +758 -0
  53. package/src/electron-sqlite-host.ts +301 -0
  54. package/src/host.ts +64 -0
  55. package/src/index.ts +71 -0
  56. package/src/node-sqlite-engine.ts +631 -0
  57. package/src/pglite/RxDBAdapterElectronPGlite.ts +152 -0
  58. package/src/pglite/desktop-pglite-client.ts +614 -0
  59. package/src/pglite/desktop-pglite-results.ts +24 -0
  60. package/src/pglite/electron-pglite-backup-error.ts +128 -0
  61. package/src/pglite/pglite-adapter.interface.ts +78 -0
  62. package/src/pglite/restore-electron-pglite-database.ts +429 -0
  63. package/src/pglite-host/electron-pglite-host.ts +829 -0
  64. package/src/pglite-host/pglite-host-data-dir.ts +224 -0
  65. package/src/pglite-host/pglite-host-lock.ts +154 -0
  66. package/src/pglite-host/pglite-host-restore.ts +460 -0
  67. package/src/pglite-host/pglite-host-runtime.ts +166 -0
  68. package/src/pglite-host.ts +50 -0
  69. package/src/pglite.ts +51 -0
  70. package/src/sqlite-script.ts +134 -0
@@ -0,0 +1,129 @@
1
+ import { DesktopPgliteNotifyMessage, DesktopPgliteResponse } from '@aiao/rxdb-adapter-sqlite-core/desktop-host';
2
+ import { ElectronPgliteProbeRuntime, ElectronPgliteRuntime } from './pglite-host-runtime.js';
3
+ export type { ElectronPgliteProbeRuntime, ElectronPgliteRuntime, ElectronPgliteRuntimeResult, ElectronPgliteTransaction } from './pglite-host-runtime.js';
4
+ /**
5
+ * host 代 renderer 订阅的 NOTIFY 频道。
6
+ *
7
+ * @remarks
8
+ * 与 `@aiao/rxdb-adapter-pglite` 里 `PGliteClient` 监听的三张系统表一一对应。
9
+ * PostgreSQL 只把 `LISTEN` 过的频道推给连接,因此这份清单漏一项就等于对应的响应式
10
+ * 查询在桌面下永远不刷新——而浏览器下一切正常,排查时几乎不会怀疑到订阅清单上。
11
+ */
12
+ export declare const DESKTOP_PGLITE_WATCH_CHANNELS: readonly string[];
13
+ /**
14
+ * 备份与恢复所需的宿主配置(US-217)。
15
+ *
16
+ * @remarks
17
+ * 不传时 host 只提供连接:`pg.engine`、`pg.backup.*`、`pg.restore.*` 一律报 `unsupported_operation`,
18
+ * `pg.open` 也不取目录锁,与引入备份之前的行为逐项一致。传了之后每个打开的数据目录旁边都有一把
19
+ * 跨进程的目录锁:同一目录上的恢复与连接互斥,另一个进程里的 host 也不例外(AC#12)。
20
+ */
21
+ export interface ElectronPgliteBackupOptions {
22
+ /**
23
+ * 逻辑数据目录名到物理路径。
24
+ *
25
+ * @remarks
26
+ * **必须**就是 `createRuntime` 为同一个名字打开的目录:备份从这里读文件,恢复往这里写文件,
27
+ * 目录锁与恢复标记也建在它旁边。两者不一致时备份报 `host_internal_error`,而不是悄悄读出别的目录。
28
+ * 传进来的名字已过白名单校验,不含任何路径分隔符。
29
+ */
30
+ readonly resolveDataDirectory: (dataDirectoryName: string) => string;
31
+ /**
32
+ * 创建一个不落盘的探针运行时,用来回答 `pg.engine`。
33
+ *
34
+ * @remarks
35
+ * 例如 `new PGlite()`(内存实例)。必须与 `createRuntime` 用同一份 PGlite、带同一组扩展,
36
+ * 否则回答的引擎版本对真实数据目录不成立。探针用完即关,结果在 host 生命周期内缓存;
37
+ * 起不来时不缓存,下一次 `pg.engine` 重试。
38
+ */
39
+ readonly createProbeRuntime: () => Promise<ElectronPgliteProbeRuntime>;
40
+ /**
41
+ * `createRuntime` 为每个实例装载的扩展名。
42
+ *
43
+ * @remarks
44
+ * 只列影响数据兼容性的扩展,不含 `live`(它不改变数据目录);没有扩展时传空数组。
45
+ * 归档据此声明所需扩展,恢复端不满足时在写入目标之前拒绝(AC#5)。
46
+ */
47
+ readonly extensions: readonly string[];
48
+ }
49
+ /** {@link createElectronPgliteHost} 的入参。 */
50
+ export interface ElectronPgliteHostOptions {
51
+ /**
52
+ * 按逻辑数据目录名创建一个 PGlite 运行时。
53
+ *
54
+ * @remarks
55
+ * **必填,没有默认实现**:默认值只能是「在某处建一个 PGlite」,而那个「某处」只有宿主
56
+ * 应用知道(Electron 下通常在 `app.getPath('userData')` 之下)。给一个兜底目录意味着
57
+ * 数据会静默落在谁也没打算用的位置,而症状是「重启后数据没了」。
58
+ *
59
+ * 传进来的名字已过白名单校验,不含任何路径分隔符,因此 `join(root, name)` 不会越出 `root`。
60
+ * 同一个名字同一时刻至多只有一个活着的运行时(AC#7);恢复校验同样经它打开目标目录。
61
+ */
62
+ readonly createRuntime: (dataDirectoryName: string) => Promise<ElectronPgliteRuntime>;
63
+ /** 把裸 NOTIFY 送达对应会话的 renderer,例如 `webContents.send`。 */
64
+ readonly postNotify: (message: DesktopPgliteNotifyMessage) => void;
65
+ /**
66
+ * NOTIFY 送达失败时的上报口。
67
+ *
68
+ * @remarks
69
+ * 与 SQLite host 同理:窗口在通知投递途中被销毁是常规竞态,此时写入早已落库,
70
+ * 把送达失败当成写失败回给调用方只会诱发一次重复写入。不传则丢弃。
71
+ */
72
+ readonly onDeliveryError?: (error: unknown) => void;
73
+ /** 备份与恢复支持;不传则不提供(见 {@link ElectronPgliteBackupOptions})。 */
74
+ readonly backup?: ElectronPgliteBackupOptions;
75
+ }
76
+ /** 桌面 PGlite host 实例。 */
77
+ export interface ElectronPgliteHost {
78
+ /**
79
+ * 处理一条来自 renderer 的请求。
80
+ *
81
+ * @remarks
82
+ * **永不 reject**:失败以 `kind: 'error'` 的应答返回,理由与 SQLite host 一致
83
+ * (`ipcRenderer.invoke` 在 reject 时会把错误压平成字符串,错误码随之丢失)。
84
+ *
85
+ * @param request - 未经校验的请求负载
86
+ * @param ownerId - 发起方的 `webContents.id`;会话与事务都按它归属
87
+ * @returns 协议应答
88
+ */
89
+ handle(request: unknown, ownerId: number): Promise<DesktopPgliteResponse>;
90
+ /** 当前打开的会话数。 */
91
+ readonly openSessionCount: number;
92
+ /** 当前活着的 PGlite 实例数;同一个数据目录上的多个会话只算一个,恢复校验用的私有实例不计入。 */
93
+ readonly openInstanceCount: number;
94
+ /** 当前挂起的事务数;正常静止时应为 0。 */
95
+ readonly openTransactionCount: number;
96
+ /** 当前进行中的备份快照数;快照同样占着连接,但不计入 `openTransactionCount`。 */
97
+ readonly openBackupCount: number;
98
+ /** 当前进行中的恢复数。 */
99
+ readonly openRestoreCount: number;
100
+ /**
101
+ * 回收某个窗口名下的全部会话、事务、快照与恢复。
102
+ *
103
+ * @remarks
104
+ * 这是本方案的**前提而非收尾**:挂起的 callback 独占 PGlite 的连接锁,渲染进程崩在
105
+ * 事务中间而没人回收的话,之后任何查询都会永远排队——表征是「数据库不响应」,
106
+ * 与那次崩溃毫无关联线索。调用方必须把它挂到 `render-process-gone` 与 `destroyed`
107
+ * 两个事件上(AC#3)。被打断的恢复保留恢复标记,之后的连接据此被拒绝(AC#11)。
108
+ *
109
+ * @param ownerId - 已崩溃或已销毁的 `webContents.id`
110
+ * @returns 本次回滚掉的事务条数
111
+ */
112
+ releaseOwner(ownerId: number): Promise<number>;
113
+ /** 关闭全部会话与实例、打断全部恢复,通常在应用退出前调用。 */
114
+ closeAll(): Promise<void>;
115
+ }
116
+ /**
117
+ * 创建一个桌面 PGlite host。
118
+ *
119
+ * @remarks
120
+ * 与 SQLite host 相反,同一个数据目录上的多个会话**共享一个** PGlite 实例(AC#7):
121
+ * PGlite 是嵌入式单写者,同一份数据目录被两个实例同时打开会直接损坏它。同一 host 内跨窗口的
122
+ * 并发因此由这一条连接的排队来串行化;启用备份时每个实例还在数据目录旁持有一把跨进程的
123
+ * 目录锁,恢复据此确认目标没有被任何进程连着(AC#12)。
124
+ *
125
+ * @param options - host 配置
126
+ * @returns host 实例
127
+ */
128
+ export declare function createElectronPgliteHost(options: ElectronPgliteHostOptions): ElectronPgliteHost;
129
+ //# sourceMappingURL=electron-pglite-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"electron-pglite-host.d.ts","sourceRoot":"","sources":["../../src/pglite-host/electron-pglite-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EASL,KAAK,0BAA0B,EAE/B,KAAK,qBAAqB,EAC3B,MAAM,6CAA6C,CAAC;AASrD,OAAO,EAIL,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAE3B,MAAM,0BAA0B,CAAC;AAElC,YAAY,EACV,0BAA0B,EAC1B,qBAAqB,EACrB,2BAA2B,EAC3B,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAYlC;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,EAAE,SAAS,MAAM,EAIzD,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;OAOG;IACH,QAAQ,CAAC,oBAAoB,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,MAAM,CAAC;IACrE;;;;;;;OAOG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACvE;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,4CAA4C;AAC5C,MAAM,WAAW,yBAAyB;IACxC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACtF,wDAAwD;IACxD,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACnE;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,CAAC,EAAE,2BAA2B,CAAC;CAC/C;AAED,yBAAyB;AACzB,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC1E,gBAAgB;IAChB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,wDAAwD;IACxD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,0BAA0B;IAC1B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,wDAAwD;IACxD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,iBAAiB;IACjB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,mCAAmC;IACnC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAoED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CAsjB/F"}
@@ -0,0 +1,75 @@
1
+ import { RxDBAdapterDesktopError, DesktopPgliteDataDirItem } from '@aiao/rxdb-adapter-sqlite-core/desktop-host';
2
+ /**
3
+ * 取 Node 文件系统错误上的 errno 名(`ENOENT` 等)。
4
+ *
5
+ * @param error - 原始错误
6
+ * @returns errno 名;不是 errno 形状的错误时为 `undefined`
7
+ */
8
+ export declare const errnoOf: (error: unknown) => string | undefined;
9
+ /**
10
+ * 把文件系统错误归一成桌面错误。
11
+ *
12
+ * @remarks
13
+ * 已经是桌面错误的原样透传:遍历中途抛出的「文件变短」之类的判定比外层的笼统描述更准。
14
+ * 磁盘写满必须单独成码,renderer 据此报 `storage_full`,调用方才知道腾出空间后重试即可。
15
+ *
16
+ * @param error - 原始错误
17
+ * @param detail - 面向开发者的描述
18
+ * @returns 桌面错误
19
+ */
20
+ export declare const toHostIoError: (error: unknown, detail: string) => RxDBAdapterDesktopError;
21
+ /**
22
+ * 按确定顺序逐块读出一个 PGlite 数据目录。
23
+ *
24
+ * @remarks
25
+ * 调用方必须已经占住该目录上唯一的运行时连接并做完 `CHECKPOINT`:遍历期间没有语句能改动文件,
26
+ * 目录树因此就是一个一致快照。同一时刻只有一块数据在途,与库大小无关;消费方不取下一项,
27
+ * 就不会读下一块。提前结束(`return()`)时当前文件句柄在 `finally` 里关闭。
28
+ *
29
+ * @param directory - 数据目录的物理路径
30
+ * @returns 数据目录各项;每个数据块独占自己的 `ArrayBuffer`
31
+ * @throws {@link RxDBAdapterDesktopError} `disk_full` / `permission_denied` / `file_not_found` / `host_internal_error`
32
+ */
33
+ export declare function walkPgliteDataDirectory(directory: string): AsyncGenerator<DesktopPgliteDataDirItem>;
34
+ /**
35
+ * 判定恢复目标是否可写:不存在,或是一个空目录。
36
+ *
37
+ * @remarks
38
+ * 同名的普通文件算「不空」而不是 I/O 故障:它同样意味着那里已经有别的东西,恢复不能覆盖。
39
+ *
40
+ * @param directory - 数据目录的物理路径
41
+ * @returns 可以在此恢复时为 `true`
42
+ * @throws {@link RxDBAdapterDesktopError} 目录读不出来时
43
+ */
44
+ export declare const isEmptyOrMissing: (directory: string) => Promise<boolean>;
45
+ /**
46
+ * 数据目录里是否有 `PG_VERSION`。
47
+ *
48
+ * @remarks
49
+ * PGlite 打开一个没有 `PG_VERSION` 的目录时会**就地 initdb**。恢复校验前必须先确认它在,
50
+ * 否则一份缺了它的归档会被悄悄换成一个全新的空库,然后以「系统表水位不符」之类不相干的
51
+ * 原因失败,甚至通过校验。
52
+ *
53
+ * @param directory - 数据目录的物理路径
54
+ * @returns 存在且是普通文件时为 `true`
55
+ */
56
+ export declare const hasPgVersion: (directory: string) => Promise<boolean>;
57
+ /**
58
+ * 把整棵数据目录(连同它在父目录里的目录项)逐一落盘。
59
+ *
60
+ * @remarks
61
+ * 恢复标记只能在这一步完成之后删除:先删标记再掉电,重启后看到的是一个「没有标记、
62
+ * 却缺了几页」的库,与完整恢复无从区分(AC#11)。
63
+ *
64
+ * @param directory - 数据目录的物理路径
65
+ * @throws {@link RxDBAdapterDesktopError} 落盘失败时
66
+ */
67
+ export declare const syncPgliteDataDirectory: (directory: string) => Promise<void>;
68
+ /**
69
+ * 删除一棵数据目录;不存在时视为已删除。
70
+ *
71
+ * @param directory - 数据目录的物理路径
72
+ * @throws {@link RxDBAdapterDesktopError} 删除失败时
73
+ */
74
+ export declare const removePgliteDataDirectory: (directory: string) => Promise<void>;
75
+ //# sourceMappingURL=pglite-host-data-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pglite-host-data-dir.d.ts","sourceRoot":"","sources":["../../src/pglite-host/pglite-host-data-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAGL,uBAAuB,EACvB,KAAK,wBAAwB,EAE9B,MAAM,6CAA6C,CAAC;AAcrD;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAGjD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,EAAE,QAAQ,MAAM,KAAG,uBAI9D,CAAC;AA2CF;;;;;;;;;;;GAWG;AACH,wBAAuB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAAC,wBAAwB,CAAC,CAM1G;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,OAAO,CASzE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,OAAO,CAOrE,CAAC;AAgCF;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,IAAI,CAO7E,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,IAAI,CAO/E,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * PGlite 数据目录的跨进程独占锁与恢复标记(US-217)。
3
+ *
4
+ * @module pglite-host/pglite-host-lock
5
+ */
6
+ /** 一个已持有的数据目录锁。 */
7
+ export interface PgliteDirectoryLock {
8
+ /** 目录上是否留有未完成恢复的标记。 */
9
+ hasMarker(): boolean;
10
+ /** 持久化恢复标记;返回时标记已经落盘。 */
11
+ markRestoring(): void;
12
+ /** 删除恢复标记;返回时删除已经落盘。 */
13
+ clearRestoring(): void;
14
+ /** 释放锁;重复调用无副作用。 */
15
+ release(): void;
16
+ }
17
+ /**
18
+ * 数据目录对应的锁文件路径:与数据目录同级的隐藏文件。
19
+ *
20
+ * @remarks
21
+ * 库名不能以 `.` 开头({@link assertValidDesktopDatabaseName}),锁文件名因此不会与任何库名撞车;
22
+ * 放在数据目录外面,恢复删除整个目录时锁与标记都还在。
23
+ *
24
+ * @param directory - 数据目录的物理路径
25
+ * @returns 锁文件的物理路径
26
+ */
27
+ export declare const pgliteLockPathOf: (directory: string) => string;
28
+ /**
29
+ * 取得一个 PGlite 数据目录的独占使用权。
30
+ *
31
+ * @remarks
32
+ * Node 上的 PGlite(NODEFS)自己没有任何跨进程锁:两个进程打开同一个数据目录会各自写坏对方。
33
+ * 这里借 SQLite 的文件锁(POSIX `fcntl` / Windows `LockFileEx`)实现独占:
34
+ *
35
+ * - 锁由操作系统持有,进程被强杀时随之释放,不会像 pid 文件那样留下需要人工判断的陈旧锁,
36
+ * 也不怕 pid 被复用(AC#11);
37
+ * - 同一进程内的第二条连接同样撞锁,同一 host 里的重复打开也被挡住;
38
+ * - 恢复标记与锁在同一个文件里,写入在拿锁之后、提交即落盘,任何改动目标的动作都排在它后面。
39
+ *
40
+ * 锁文件本身从不删除:删掉一个仍被别的进程锁着的文件,后来者会在新文件上拿到「独占」,锁就裂成两把。
41
+ *
42
+ * @param directory - 数据目录的物理路径;不要求存在,其父目录不存在时会被创建
43
+ * @returns 已持有的锁
44
+ * @throws {@link RxDBAdapterDesktopError} `database_busy`:别的连接(其他进程或同一进程内)正持有该目录;
45
+ * `permission_denied` / `disk_full` / `open_failed`:锁文件无法创建或写入
46
+ */
47
+ export declare const acquirePgliteDirectoryLock: (directory: string) => PgliteDirectoryLock;
48
+ //# sourceMappingURL=pglite-host-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pglite-host-lock.d.ts","sourceRoot":"","sources":["../../src/pglite-host/pglite-host-lock.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,mBAAmB;AACnB,MAAM,WAAW,mBAAmB;IAClC,uBAAuB;IACvB,SAAS,IAAI,OAAO,CAAC;IACrB,yBAAyB;IACzB,aAAa,IAAI,IAAI,CAAC;IACtB,wBAAwB;IACxB,cAAc,IAAI,IAAI,CAAC;IACvB,oBAAoB;IACpB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,KAAG,MACU,CAAC;AA0DhE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,0BAA0B,GAAI,WAAW,MAAM,KAAG,mBAyC9D,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { DesktopPgliteResponse, DesktopPgliteRestoreQueryRequest, DesktopPgliteRestoreStepRequest, DesktopPgliteRestoreTargetRequest, DesktopPgliteRestoreWriteRequest } from '@aiao/rxdb-adapter-sqlite-core/desktop-host';
2
+ import { ElectronPgliteRuntime } from './pglite-host-runtime.js';
3
+ /** 恢复协调器处理的全部请求。 */
4
+ export type DesktopPgliteRestoreRequest = DesktopPgliteRestoreTargetRequest | DesktopPgliteRestoreStepRequest | DesktopPgliteRestoreWriteRequest | DesktopPgliteRestoreQueryRequest;
5
+ /** {@link createPgliteRestoreCoordinator} 的依赖。 */
6
+ export interface PgliteRestoreCoordinatorOptions {
7
+ /** 起一个运行时;恢复校验经它打开目标目录,与普通连接用同一份 PGlite 与扩展。 */
8
+ readonly createRuntime: (dataDirectoryName: string) => Promise<ElectronPgliteRuntime>;
9
+ /** 逻辑名到物理路径;必须就是 `createRuntime` 打开的目录。 */
10
+ readonly resolveDataDirectory: (dataDirectoryName: string) => string;
11
+ /** 本 host 是否正连着该目录;正在关闭的实例要等它关完再回答。 */
12
+ readonly isInUse: (dataDirectoryName: string) => Promise<boolean>;
13
+ }
14
+ /** 一个 host 内全部进行中的恢复。 */
15
+ export interface PgliteRestoreCoordinator {
16
+ /** 进行中的恢复数。 */
17
+ readonly openCount: number;
18
+ /** 该名字是否正被本 host 的恢复占着(含正在取锁的);`pg.open` 据此报 `restore_in_progress`。 */
19
+ isReserved(dataDirectoryName: string): boolean;
20
+ /**
21
+ * 处理一条恢复请求。
22
+ *
23
+ * @throws {@link RxDBAdapterDesktopError} 失败以桌面错误抛出,由 host 统一转成错误应答
24
+ */
25
+ handle(request: DesktopPgliteRestoreRequest, ownerId: number): Promise<DesktopPgliteResponse>;
26
+ /** 打断某个窗口名下的全部恢复:关资源、交还独占,**保留**恢复标记(AC#11)。 */
27
+ releaseOwner(ownerId: number): Promise<void>;
28
+ /** 打断全部恢复,语义同 {@link PgliteRestoreCoordinator.releaseOwner}。 */
29
+ closeAll(): Promise<void>;
30
+ }
31
+ /**
32
+ * 创建一个 host 的恢复协调器。
33
+ *
34
+ * @param options - 运行时工厂、目录解析与本 host 的占用查询
35
+ * @returns 恢复协调器
36
+ */
37
+ export declare const createPgliteRestoreCoordinator: (options: PgliteRestoreCoordinatorOptions) => PgliteRestoreCoordinator;
38
+ //# sourceMappingURL=pglite-host-restore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pglite-host-restore.d.ts","sourceRoot":"","sources":["../../src/pglite-host/pglite-host-restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAKL,KAAK,qBAAqB,EAC1B,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACpC,KAAK,iCAAiC,EACtC,KAAK,gCAAgC,EACtC,MAAM,6CAA6C,CAAC;AAarD,OAAO,EAA8B,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAElG,oBAAoB;AACpB,MAAM,MAAM,2BAA2B,GACnC,iCAAiC,GACjC,+BAA+B,GAC/B,gCAAgC,GAChC,gCAAgC,CAAC;AAKrC,kDAAkD;AAClD,MAAM,WAAW,+BAA+B;IAC9C,gDAAgD;IAChD,QAAQ,CAAC,aAAa,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACtF,2CAA2C;IAC3C,QAAQ,CAAC,oBAAoB,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,MAAM,CAAC;IACrE,uCAAuC;IACvC,QAAQ,CAAC,OAAO,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAED,yBAAyB;AACzB,MAAM,WAAW,wBAAwB;IACvC,eAAe;IACf,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,UAAU,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/C;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,2BAA2B,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC9F,gDAAgD;IAChD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,gEAAgE;IAChE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAsFD;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,+BAA+B,KAAG,wBAsSzF,CAAC"}
@@ -0,0 +1,90 @@
1
+ import { DesktopPgliteQueryResult } from '@aiao/rxdb-adapter-sqlite-core/desktop-host';
2
+ /** 一条语句的结果;`ElectronPgliteRuntime` 与 PGlite 的 `Results` 在此结构相容。 */
3
+ export interface ElectronPgliteRuntimeResult {
4
+ readonly rows: Record<string, unknown>[];
5
+ readonly fields: {
6
+ name: string;
7
+ dataTypeID: number;
8
+ }[];
9
+ readonly affectedRows?: number;
10
+ }
11
+ /** 事务句柄;对应 PGlite 的 `Transaction`,只声明 host 真正用到的两个操作。 */
12
+ export interface ElectronPgliteTransaction {
13
+ query(sql: string, params?: unknown[]): Promise<ElectronPgliteRuntimeResult>;
14
+ exec(sql: string): Promise<ElectronPgliteRuntimeResult[]>;
15
+ }
16
+ /**
17
+ * host 需要的 PGlite 能力子集。
18
+ *
19
+ * @remarks
20
+ * 刻意不写成 `PGliteInterface`:宿主应用极可能传进来的不是裸 `PGlite`,而是跑在 worker
21
+ * 里的 `PGliteWorker`——PGlite 的 WASM 在主进程 JS 线程上是**同步**执行的,一条重查询会
22
+ * 把整个窗口的 IPC 一起卡住(故事「冻结带来的三条实现约束」第 3 条)。声明成结构类型后,
23
+ * 两者都能直接传入,而这份清单也顺便说清了 host 只需要这五个操作。
24
+ */
25
+ export interface ElectronPgliteRuntime {
26
+ query(sql: string, params?: unknown[]): Promise<ElectronPgliteRuntimeResult>;
27
+ exec(sql: string): Promise<ElectronPgliteRuntimeResult[]>;
28
+ transaction<T>(callback: (tx: ElectronPgliteTransaction) => Promise<T>): Promise<T>;
29
+ listen(channel: string, callback: (payload: string) => void): Promise<unknown>;
30
+ close(): Promise<void>;
31
+ }
32
+ /**
33
+ * 回答 `pg.engine` 的探针运行时:只需要查询与关闭(US-217)。
34
+ *
35
+ * @remarks
36
+ * 裸 `new PGlite()`(内存实例)即满足;它必须与 `createRuntime` 起的实例是同一份 PGlite、
37
+ * 带同一组扩展,否则回答的引擎版本对真实数据目录不成立。
38
+ */
39
+ export type ElectronPgliteProbeRuntime = Pick<ElectronPgliteRuntime, 'query' | 'close'>;
40
+ /**
41
+ * 把 PGlite 的结果收窄成能过结构化克隆的形状。
42
+ *
43
+ * @remarks
44
+ * `fields` 上除了 `name` / `dataTypeID` 还挂着 PGlite 自己的解析器,整个丢给
45
+ * `ipcRenderer.invoke` 会以 DataCloneError 失败——而报错点在 IPC 层,看上去与 SQL 毫无关系。
46
+ * `rows` 里的 bigint / Uint8Array / Date 都是结构化克隆原生支持的,原样带走(AC#1)。
47
+ *
48
+ * @param result - 运行时返回的结果
49
+ * @returns 可以跨进程传递的结果
50
+ */
51
+ export declare const toWireResult: (result: ElectronPgliteRuntimeResult) => DesktopPgliteQueryResult;
52
+ /**
53
+ * 跑一条语句,把引擎抛出的错误归一成 `statement_failed`。
54
+ *
55
+ * @remarks
56
+ * 不归一的话,一条写错的 SQL 会以 `host_internal_error` 回到 renderer——那个码的含义是
57
+ * 「host 有缺陷」,会把调用方的排查引向完全错误的方向。
58
+ *
59
+ * @param run - 执行语句
60
+ * @returns 语句结果
61
+ * @throws {@link RxDBAdapterDesktopError} 语句失败时为 `statement_failed`;已经是桌面错误的原样透传
62
+ */
63
+ export declare const runStatement: <T>(run: () => Promise<T>) => Promise<T>;
64
+ /** 一条挂起中的事务:`transaction(cb)` 的 callback 停在 host 持有的 promise 上。 */
65
+ export interface SuspendedTransaction {
66
+ readonly tx: ElectronPgliteTransaction;
67
+ /** 结掉 callback:`resolve` 提交,`reject` 让 callback 抛出从而回滚。 */
68
+ readonly settle: {
69
+ readonly resolve: () => void;
70
+ readonly reject: (error: Error) => void;
71
+ };
72
+ /** `transaction(...)` 本身;等它落地才算真的提交/回滚完。 */
73
+ readonly finished: Promise<unknown>;
74
+ }
75
+ /**
76
+ * 在运行时上开一条事务并把它挂起,直到调用方结掉 `settle`。
77
+ *
78
+ * @remarks
79
+ * PGlite 只提供 callback 形态的事务(`transaction(cb)`,返回即 COMMIT、抛出即 ROLLBACK),
80
+ * 而 `cb` 跨不了 IPC。这里进入 `cb` 后立刻 `await` 一个由 host 持有的 promise,事务因此在
81
+ * PostgreSQL 侧一直开着、连接一直被占住。`pg.begin` 用它把多次 IPC 串成一条真事务,
82
+ * `pg.backup.begin` 用它在读快照期间挡住一切写入(AC#16)。
83
+ *
84
+ * @param runtime - 运行时
85
+ * @param timeout - 等待连接空闲的上限(毫秒)
86
+ * @returns 已挂起的事务
87
+ * @throws {@link RxDBAdapterDesktopError} `transaction_unavailable`:到期时连接仍被别的事务占着
88
+ */
89
+ export declare const suspendTransaction: (runtime: ElectronPgliteRuntime, timeout: number) => Promise<SuspendedTransaction>;
90
+ //# sourceMappingURL=pglite-host-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pglite-host-runtime.d.ts","sourceRoot":"","sources":["../../src/pglite-host/pglite-host-runtime.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAA2B,KAAK,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAErH,mEAAmE;AACnE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,yDAAyD;AACzD,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAC7E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,2BAA2B,EAAE,CAAC,CAAC;CAC3D;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAC7E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,2BAA2B,EAAE,CAAC,CAAC;IAC1D,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,yBAAyB,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,qBAAqB,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC;AAExF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,2BAA2B,KAAG,wBAIjE,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,GAAU,CAAC,EAAE,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAStE,CAAC;AAEF,mEAAmE;AACnE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,EAAE,yBAAyB,CAAC;IACvC,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;KAAE,CAAC;IAC3F,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC;AAqBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,qBAAqB,EAC9B,SAAS,MAAM,KACd,OAAO,CAAC,oBAAoB,CAkC9B,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `@aiao/rxdb-adapter-electron` 的 **PGlite 特权侧**入口。
3
+ *
4
+ * @remarks
5
+ * 与 `@aiao/rxdb-adapter-electron/host` 分开而不是并进去,理由是依赖:那个入口只要
6
+ * `node:sqlite`(Node 内建,装了就有),本入口却要 `@electric-sql/pglite` 这个可选 peer。
7
+ * 合并的话,任何一个只用 SQLite 的宿主应用都会在加载 host 时撞上「找不到
8
+ * `@electric-sql/pglite`」——而它压根没打算用 PGlite。
9
+ *
10
+ * 本入口只能在有 Node 运行时的地方加载(Electron 主进程,或它自己拥有的 worker)。
11
+ * renderer 侧请用 `@aiao/rxdb-adapter-electron/pglite`。
12
+ *
13
+ * @module @aiao/rxdb-adapter-electron/pglite-host
14
+ */
15
+ export { DESKTOP_PGLITE_WATCH_CHANNELS, createElectronPgliteHost, type ElectronPgliteBackupOptions, type ElectronPgliteHost, type ElectronPgliteHostOptions, type ElectronPgliteProbeRuntime, type ElectronPgliteRuntime, type ElectronPgliteRuntimeResult, type ElectronPgliteTransaction } from './pglite-host/electron-pglite-host.js';
16
+ export { DESKTOP_PGLITE_DEFAULT_BEGIN_TIMEOUT_MS, DESKTOP_PGLITE_EXCLUDED_FILES, DESKTOP_PGLITE_MAX_BEGIN_TIMEOUT_MS, DESKTOP_PGLITE_MAX_DATA_CHUNK_BYTES, DESKTOP_PGLITE_PROTOCOL_VERSION, RxDBAdapterDesktopError, assertValidDesktopDatabaseName, isDesktopPgliteRequestKind, parseDesktopPgliteRequest, type DesktopPgliteBackupItem, type DesktopPgliteDataDirItem, type DesktopPgliteEngineResult, type DesktopPgliteNotifyMessage, type DesktopPgliteQueryResult, type DesktopPgliteRequest, type DesktopPgliteResponse, type RxDBAdapterDesktopErrorCode } from '@aiao/rxdb-adapter-sqlite-core/desktop-host';
17
+ //# sourceMappingURL=pglite-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pglite-host.d.ts","sourceRoot":"","sources":["../src/pglite-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC/B,MAAM,uCAAuC,CAAC;AAI/C,OAAO,EACL,uCAAuC,EACvC,6BAA6B,EAC7B,mCAAmC,EACnC,mCAAmC,EACnC,+BAA+B,EAC/B,uBAAuB,EAGvB,8BAA8B,EAC9B,0BAA0B,EAC1B,yBAAyB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EACjC,MAAM,6CAA6C,CAAC"}