@aiao/rxdb-adapter-sqliteai 0.0.26 → 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.
package/README.md CHANGED
@@ -21,6 +21,24 @@ import { createSqliteClient, RxDBAdapterSqliteai, sqliteaiLoad } from '@aiao/rxd
21
21
 
22
22
  适配器共享内核(表名解析、规则编译、基础 Repository)来自 [`@aiao/rxdb-adapter-sqlite-core`](../rxdb-adapter-sqlite-core)。
23
23
 
24
+ ## 备份与恢复
25
+
26
+ `backup()` / `restore()` / `cleanupIncompleteRestore()` 由 [`@aiao/rxdb-adapter-sqlite-core`](../rxdb-adapter-sqlite-core#备份与恢复) 提供,语义与错误码见那里。
27
+
28
+ | 配置 | 备份 / 恢复 |
29
+ | ------------------------------------ | ----------------------------------------------------------------------------------------- |
30
+ | 不开 `opfs` | ✅ 内存库 |
31
+ | `opfs: true` | ✅ OPFS,持久化 `journal_mode` 为 `delete` |
32
+ | `opfs: true, opfsFallback: 'memory'` | ❌ `unsupported_combination`(`opfsFallback`):OPFS 打不开时静默落到内存,恢复目标不确定 |
33
+
34
+ 内置的 vector 与 memory 扩展在每条新连接上建出自己的表(`_sqliteai_vector`、`dbmem_*` 及其 FTS5 影子表,部分带初始行)。
35
+ 这些表连同初始行就是「空库」本来的样子:恢复只把恰好等于新建空库的目标当作空的,写进这些表的用户数据随归档往返。
36
+
37
+ 在 Worker 里跑时,每次连接租用 Worker 的一条独立子端口:恢复用一条连接写库,之后的 `connect()` 在同一个 Worker 上再开一条。
38
+ 同一个 `workerInstance` 同一时间只借给一条连接:第二条并发连接会抛 `Worker transport already has an active SQLite client`。
39
+ 直接调用 `createSqliteClient` 时,先 `await client.disconnect()`,再调用 `@aiao/rxdb-adapter-sqlite-core` 导出的
40
+ `releaseComlinkProxy(client)` 归还子端口,之后才能在同一个 Worker 上重连;适配器已在 `disconnect()` 内自动完成这一步。
41
+
24
42
  ## 文档
25
43
 
26
44
  - 仓库主页:[https://github.com/aiao-io/rxdb](https://github.com/aiao-io/rxdb)
@@ -1,4 +1,4 @@
1
- import { Oo1ClientBase as e, RxDBAdapterSqliteBase as t, assertLoadOptionsTransferable as n, assertOo1Static as r, buildOo1InitOptions as i, defaultWarn as a, isSameOo1LoadConfig as o, releaseComlinkProxy as s, toOo1LoadFingerprint as c, withGlobalOo1LoadLock as l, withPatchedOpfsProxyWorker as u, withSqliteApiConfig as d, wrapWithComlink as f } from "@aiao/rxdb-adapter-sqlite-core";
1
+ import { Oo1ClientBase as e, RxDBAdapterSqliteBase as t, assertLoadOptionsTransferable as n, assertOo1Static as r, buildOo1InitOptions as i, defaultWarn as a, isSameOo1LoadConfig as o, releaseComlinkProxy as s, toOo1LoadFingerprint as c, withGlobalOo1LoadLock as l, withPatchedOpfsProxyWorker as u, withSqliteApiConfig as d, wrapWithComlinkEndpoint as f } from "@aiao/rxdb-adapter-sqlite-core";
2
2
  import p from "@sqliteai/sqlite-wasm";
3
3
  //#region src/sqliteai-load.utils.ts
4
4
  var m, h;
@@ -39,7 +39,7 @@ async function v(e, t) {
39
39
  opfsFallback: t.opfsFallback
40
40
  };
41
41
  n(r, t);
42
- let i = f(new _(), t);
42
+ let i = await f(new _(), t);
43
43
  try {
44
44
  return await i.init(e, r), i;
45
45
  } catch (e) {
@@ -55,6 +55,16 @@ var y = "sqliteai", b = class extends t {
55
55
  constructor(e, t) {
56
56
  super(e, t), this.options = t, this.#e = e.config.dbName;
57
57
  }
58
+ backupStorage() {
59
+ return this.options.opfs ? this.options.opfsFallback === "memory" ? {
60
+ kind: "unsupported",
61
+ field: "opfsFallback",
62
+ actual: "memory"
63
+ } : this.persistentBackupStorage("opfs") : {
64
+ kind: "memory",
65
+ label: "memory"
66
+ };
67
+ }
58
68
  async createClient() {
59
69
  return v(this.#e, this.options);
60
70
  }
@@ -1,5 +1,5 @@
1
1
  import { RxDB } from '@aiao/rxdb';
2
- import { RxDBAdapterSqliteBase, SqliteClientLike } from '@aiao/rxdb-adapter-sqlite-core';
2
+ import { RxDBAdapterSqliteBase, SqliteBackupStorage, SqliteClientLike } from '@aiao/rxdb-adapter-sqlite-core';
3
3
  import { ADAPTER_NAME, SqliteaiOptions } from './sqliteai.interface.js';
4
4
  /**
5
5
  * 基于 `@sqliteai/sqlite-wasm` 的 RxDB 本地存储适配器。
@@ -13,6 +13,11 @@ export declare class RxDBAdapterSqliteai extends RxDBAdapterSqliteBase {
13
13
  readonly options: SqliteaiOptions;
14
14
  readonly name: string;
15
15
  constructor(rxdb: RxDB, options: SqliteaiOptions);
16
+ /**
17
+ * 不开 OPFS 时是内存库;开 OPFS 时是持久化库。
18
+ * `opfsFallback: 'memory'` 会在 OPFS 打不开时静默落到内存,恢复目标不确定,报 `unsupported_combination`。
19
+ */
20
+ protected backupStorage(): SqliteBackupStorage;
16
21
  protected createClient(): Promise<SqliteClientLike>;
17
22
  }
18
23
  declare module '@aiao/rxdb' {
@@ -1 +1 @@
1
- {"version":3,"file":"RxDBAdapterSqliteai.d.ts","sourceRoot":"","sources":["../src/RxDBAdapterSqliteai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAE9F,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE7E;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,qBAAqB;;IAM1D,QAAQ,CAAC,OAAO,EAAE,eAAe;IAJnC,SAAkB,IAAI,EAAE,MAAM,CAAgB;gBAG5C,IAAI,EAAE,IAAI,EACD,OAAO,EAAE,eAAe;cAMV,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAGnE;AAED,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,YAAY;QACpB,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;KACrC;CACF"}
1
+ {"version":3,"file":"RxDBAdapterSqliteai.d.ts","sourceRoot":"","sources":["../src/RxDBAdapterSqliteai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAExH,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE7E;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,qBAAqB;;IAM1D,QAAQ,CAAC,OAAO,EAAE,eAAe;IAJnC,SAAkB,IAAI,EAAE,MAAM,CAAgB;gBAG5C,IAAI,EAAE,IAAI,EACD,OAAO,EAAE,eAAe;IAMnC;;;OAGG;cACgB,aAAa,IAAI,mBAAmB;cAM9B,YAAY,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAGnE;AAED,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,YAAY;QACpB,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;KACrC;CACF"}
@@ -0,0 +1 @@
1
+ "use strict";(function(){let e=(e,...t)=>postMessage({type:e,payload:t});globalThis.SharedArrayBuffer?globalThis.Atomics?!globalThis.FileSystemHandle||!globalThis.FileSystemDirectoryHandle||!globalThis.FileSystemFileHandle||!globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle||!navigator?.storage?.getDirectory?e(`opfs-unavailable`,`Missing required OPFS APIs.`):function(){let t=function(...e){throw Error(e.join(` `))};globalThis.window===globalThis?t(`This code cannot run from the main thread.`,`Load it as a Worker from a separate Worker.`):navigator?.storage?.getDirectory||t(`This API requires navigator.storage.getDirectory.`);let n=Object.create(null);n.verbose=1;let r={0:console.error.bind(console),1:console.warn.bind(console),2:console.log.bind(console)},i=(e,...t)=>{n.verbose>e&&r[e](`OPFS asyncer:`,...t)},a=(...e)=>i(2,...e),o=(...e)=>i(1,...e),s=(...e)=>i(0,...e),c=Object.create(null),l=/* @__PURE__ */ new Set,u=function(e,t){let n=new URL(e,`file://irrelevant`).pathname;return t?n.split(`/`).filter(e=>!!e):n},d=async function(e,t=!1){let r=u(e,!0),i=r.pop(),a=n.rootDir;for(let e of r)e&&(a=await a.getDirectoryHandle(e,{create:!!t}));return[a,i]},f=async e=>{if(e.syncHandle){a(`Closing sync handle for`,e.filenameAbs);let t=e.syncHandle;return delete e.syncHandle,delete e.xLock,l.delete(e.fid),t.close()}},p=async e=>{try{await f(e)}catch(t){o(`closeSyncHandleNoThrow() ignoring:`,t,e)}},m=async()=>{if(l.size)for(let e of l){let t=c[e];await p(t),a(`Auto-unlocked`,e,t.filenameAbs)}},h=async e=>{if(e.releaseImplicitLocks&&l.has(e.fid))return p(e)};class g extends Error{constructor(e,...t){super([...t,`: `+e.name+`:`,e.message].join(` `),{cause:e}),this.name=`GetSyncHandleError`}}g.convertRc=(e,t)=>{if(e instanceof g){if(e.cause.name===`NoModificationAllowedError`||e.cause.name===`DOMException`&&e.cause.message.indexOf(`Access Handles cannot`)===0)return n.sq3Codes.SQLITE_BUSY;if(e.cause.name===`NotFoundError`)return n.sq3Codes.SQLITE_CANTOPEN}else if(e?.name===`NotFoundError`)return n.sq3Codes.SQLITE_CANTOPEN;return t};let _=async(e,t)=>{if(!e.syncHandle){let r=performance.now();a(`Acquiring sync handle for`,e.filenameAbs);let i=n.asyncIdleWaitTime*2,s=1,c=i;for(;;c=i*++s)try{e.syncHandle=await e.fileHandle.createSyncAccessHandle();break}catch(r){if(s===6)throw new g(r,`Error getting sync handle for`,t+`().`,6,`attempts failed.`,e.filenameAbs);o(`Error getting sync handle for`,t+`(). Waiting`,c,`ms and trying again.`,e.filenameAbs,r),Atomics.wait(n.sabOPView,n.opIds.retry,0,c)}a(`Got`,t+`() sync handle for`,e.filenameAbs,`in`,performance.now()-r,`ms`),e.xLock||(l.add(e.fid),a(`Acquired implicit lock for`,t+`()`,e.fid,e.filenameAbs))}return e.syncHandle},v=(e,t)=>{a(e+`() => notify(`,t,`)`),Atomics.store(n.sabOPView,n.opIds.rc,t),Atomics.notify(n.sabOPView,n.opIds.rc)},y=function(e,n){n.readOnly&&t(e+`(): File is read-only: `+n.filenameAbs)},b=!1,x={"opfs-async-shutdown":async()=>{b=!0,v(`opfs-async-shutdown`,0)},mkdir:async e=>{let t=0;try{await d(e+`/filepart`,!0)}catch(e){n.s11n.storeException(2,e),t=n.sq3Codes.SQLITE_IOERR}v(`mkdir`,t)},xAccess:async e=>{let t=0;try{let[t,n]=await d(e);await t.getFileHandle(n)}catch(e){n.s11n.storeException(2,e),t=n.sq3Codes.SQLITE_IOERR}v(`xAccess`,t)},xClose:async function(e){l.delete(e);let t=c[e],r=0;if(t){if(delete c[e],await f(t),t.deleteOnClose)try{await t.dirHandle.removeEntry(t.filenamePart)}catch(e){o(`Ignoring dirHandle.removeEntry() failure of`,t,e)}}else n.s11n.serialize(),r=n.sq3Codes.SQLITE_NOTFOUND;v(`xClose`,r)},xDelete:async function(...e){let t=await x.xDeleteNoWait(...e);v(`xDelete`,t)},xDeleteNoWait:async function(e,t=0,r=!1){let i=0;try{for(;e;){let[n,i]=await d(e,!1);if(!i||(await n.removeEntry(i,{recursive:r}),t!==4660))break;r=!1,e=u(e,!0),e.pop(),e=e.join(`/`)}}catch(e){n.s11n.storeException(2,e),i=n.sq3Codes.SQLITE_IOERR_DELETE}return i},xFileSize:async function(e){let t=c[e],r=0;try{let e=await(await _(t,`xFileSize`)).getSize();n.s11n.serialize(Number(e))}catch(e){n.s11n.storeException(1,e),r=g.convertRc(e,n.sq3Codes.SQLITE_IOERR)}await h(t),v(`xFileSize`,r)},xLock:async function(e,t){let r=c[e],i=0,a=r.xLock;if(r.xLock=t,!r.syncHandle)try{await _(r,`xLock`),l.delete(e)}catch(e){n.s11n.storeException(1,e),i=g.convertRc(e,n.sq3Codes.SQLITE_IOERR_LOCK),r.xLock=a}v(`xLock`,i)},xOpen:async function(e,t,r,i){let a=`xOpen`,o=n.sq3Codes.SQLITE_OPEN_CREATE&r;try{let s,l;try{[s,l]=await d(t,!!o)}catch(e){n.s11n.storeException(1,e),v(a,n.sq3Codes.SQLITE_NOTFOUND);return}if(n.opfsFlags.OPFS_UNLINK_BEFORE_OPEN&i)try{await s.removeEntry(l)}catch{}let u=await s.getFileHandle(l,{create:o}),f=Object.assign(Object.create(null),{fid:e,filenameAbs:t,filenamePart:l,dirHandle:s,fileHandle:u,sabView:n.sabFileBufView,readOnly:!o&&!!(n.sq3Codes.SQLITE_OPEN_READONLY&r),deleteOnClose:!!(n.sq3Codes.SQLITE_OPEN_DELETEONCLOSE&r)});f.releaseImplicitLocks=i&n.opfsFlags.OPFS_UNLOCK_ASAP||n.opfsFlags.defaultUnlockAsap,c[e]=f,v(a,0)}catch(e){s(a,e),n.s11n.storeException(1,e),v(a,n.sq3Codes.SQLITE_IOERR)}},xRead:async function(e,t,r){let i=0,a,o=c[e];try{a=(await _(o,`xRead`)).read(o.sabView.subarray(0,t),{at:Number(r)}),a<t&&(o.sabView.fill(0,a,t),i=n.sq3Codes.SQLITE_IOERR_SHORT_READ)}catch(e){s(`xRead() failed`,e,o),n.s11n.storeException(1,e),i=g.convertRc(e,n.sq3Codes.SQLITE_IOERR_READ)}await h(o),v(`xRead`,i)},xSync:async function(e,t){let r=c[e],i=0;if(!r.readOnly&&r.syncHandle)try{await r.syncHandle.flush()}catch(e){n.s11n.storeException(2,e),i=n.sq3Codes.SQLITE_IOERR_FSYNC}v(`xSync`,i)},xTruncate:async function(e,t){let r=0,i=c[e];try{y(`xTruncate`,i),await(await _(i,`xTruncate`)).truncate(t)}catch(e){s(`xTruncate():`,e,i),n.s11n.storeException(2,e),r=g.convertRc(e,n.sq3Codes.SQLITE_IOERR_TRUNCATE)}await h(i),v(`xTruncate`,r)},xUnlock:async function(e,t){let r=0,i=c[e];if(i.syncHandle&&n.sq3Codes.SQLITE_LOCK_NONE===t)try{await f(i)}catch(e){n.s11n.storeException(1,e),r=n.sq3Codes.SQLITE_IOERR_UNLOCK}v(`xUnlock`,r)},xWrite:async function(e,t,r){let i,a=c[e];try{y(`xWrite`,a),i=t===(await _(a,`xWrite`)).write(a.sabView.subarray(0,t),{at:Number(r)})?0:n.sq3Codes.SQLITE_IOERR_WRITE}catch(e){s(`xWrite():`,e,a),n.s11n.storeException(1,e),i=g.convertRc(e,n.sq3Codes.SQLITE_IOERR_WRITE)}await h(a),v(`xWrite`,i)}},S=()=>{if(n.s11n)return n.s11n;let e=new TextDecoder,r=new TextEncoder(`utf-8`),i=new Uint8Array(n.sabIO,n.sabS11nOffset,n.sabS11nSize),a=new DataView(n.sabIO,n.sabS11nOffset,n.sabS11nSize);n.s11n=Object.create(null);let o=Object.create(null);o.number={id:1,size:8,getter:`getFloat64`,setter:`setFloat64`},o.bigint={id:2,size:8,getter:`getBigInt64`,setter:`setBigInt64`},o.boolean={id:3,size:4,getter:`getInt32`,setter:`setInt32`},o.string={id:4};let s=e=>o[typeof e]||t(`Maintenance required: this value type cannot be serialized.`,e),c=e=>{switch(e){case o.number.id:return o.number;case o.bigint.id:return o.bigint;case o.boolean.id:return o.boolean;case o.string.id:return o.string;default:t(`Invalid type ID:`,e)}};return n.s11n.deserialize=function(t=!1){let r=i[0],o=r?[]:null;if(r){let t=[],s=1,l,u,d;for(l=0;l<r;++l,++s)t.push(c(i[s]));for(l=0;l<r;++l){let r=t[l];r.getter?(d=a[r.getter](s,n.littleEndian),s+=r.size):(u=a.getInt32(s,n.littleEndian),s+=4,d=e.decode(i.slice(s,s+u)),s+=u),o.push(d)}}return t&&(i[0]=0),o},n.s11n.serialize=function(...e){if(e.length){let t=[],o=0,c=1;for(i[0]=e.length&255;o<e.length;++o,++c)t.push(s(e[o])),i[c]=t[o].id;for(o=0;o<e.length;++o){let s=t[o];if(s.setter)a[s.setter](c,e[o],n.littleEndian),c+=s.size;else{let t=r.encode(e[o]);a.setInt32(c,t.byteLength,n.littleEndian),c+=4,i.set(t,c),c+=t.byteLength}}}else i[0]=0},n.s11n.storeException=n.asyncS11nExceptions?(e,t)=>{e<=n.asyncS11nExceptions&&n.s11n.serialize([t.name,`: `,t.message].join(``))}:()=>{},n.s11n},C=async function(){let e=Object.create(null);for(let t of Object.keys(n.opIds)){let r=x[t];if(!r)continue;let i=Object.create(null);e[n.opIds[t]]=i,i.key=t,i.f=r}for(;!b;)try{if(Atomics.wait(n.sabOPView,n.opIds.whichOp,0,n.asyncIdleWaitTime)!==`not-equal`){await m();continue}let r=Atomics.load(n.sabOPView,n.opIds.whichOp);Atomics.store(n.sabOPView,n.opIds.whichOp,0);let i=e[r]??t(`No waitLoop handler for whichOp #`,r),a=n.s11n.deserialize(!0)||[];i.f?await i.f(...a):s(`Missing callback for opId`,r)}catch(e){s(`in waitLoop():`,e)}};navigator.storage.getDirectory().then(function(r){n.rootDir=r,globalThis.onmessage=function({data:r}){switch(r.type){case`opfs-async-init`:{let i=r.args;for(let e in i)n[e]=i[e];n.verbose=i.verbose??1,n.sabOPView=new Int32Array(n.sabOP),n.sabFileBufView=new Uint8Array(n.sabIO,0,n.fileBufferSize),n.sabS11nView=new Uint8Array(n.sabIO,n.sabS11nOffset,n.sabS11nSize),Object.keys(x).forEach(e=>{Number.isFinite(n.opIds[e])||t(`Maintenance required: missing state.opIds[`,e,`]`)}),S(),a(`init state`,n),e(`opfs-async-inited`),C();break}case`opfs-async-restart`:b&&(o(`Restarting after opfs-async-shutdown. Might or might not work.`),b=!1,C())}},e(`opfs-async-loaded`)}).catch(e=>s(`error initializing OPFS asyncer:`,e))}():e(`opfs-unavailable`,`Missing Atomics API.`,`The server must emit the COOP/COEP response headers to enable that.`):e(`opfs-unavailable`,`Missing SharedArrayBuffer API.`,`The server must emit the COOP/COEP response headers to enable that.`)})();