Files
AFFiNE-Mirror/packages/common/nbstore
keepClamDown cd6593c659 fix(ios): ios local access limit for self-hosted workspace (#15338)
## Summary
- Route iOS nbstore worker token reads through the main-thread
MessagePort and skip Capacitor Auth on `/socket.io` polling so self-host
WebSocket/XHR sync no longer hangs on Connect timeout.
- Harden workspace `flavour:id` routing, DocSyncPeer abort/status
handling, and `resetSync` so local selfhost edits push and Mac browsers
can receive them.
- Soften root-doc readiness waits and session-exchange throttling to
keep mobile selfhost sign-in/sync stable under retries.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

* **New Features**
* Improved workspace switching across local and remote environments,
preserving workspace type and server context.
  * Added support for page navigation with query parameters.
* Added iOS local-network permission messaging for self-hosted
workspaces.
* **Bug Fixes**
* Improved document synchronization, reset handling, prioritized
document refreshes, and retry behavior.
* Prevented authentication headers and refresh attempts for socket
connection requests.
* Improved workspace reopening and routing when multiple workspace types
share an ID.
  * Fixed handling of unlimited data query limits.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DarkSky <darksky2048@gmail.com>
2026-08-24 09:33:58 +08:00
..
2026-06-24 23:55:19 +08:00

Space Storage

Usage

Independent Storage usage

import type { ConnectionStatus } from '@affine/nbstore';
import { IndexedDBDocStorage } from '@affine/nbstore/idb';

const storage = new IndexedDBDocStorage({
  peer: 'local'
  spaceId: 'my-new-workspace',
});

await storage.connect();
storage.connection.onStatusChange((status: ConnectionStatus, error?: Error) => {
  ui.show(status, error);
});

// { docId: string, bin: Uint8Array, timestamp: Date, editor?: string } | null
const doc = await storage.getDoc('my-first-doc');

Use All storages together

import { SpaceStorage } from '@affine/nbstore';
import type { ConnectionStatus } from '@affine/nbstore';
import { IndexedDBDocStorage } from '@affine/nbstore/idb';
import { SqliteBlobStorage } from '@affine/nbstore/sqlite';

const storage = new SpaceStorage([
  new IndexedDBDocStorage({}),
  new SqliteBlobStorage({}),
]);

await storage.connect();
storage.on('connection', ({ storage, status, error }) => {
  ui.show(storage, status, error);
});

await storage.get('doc').pushDocUpdate({
  docId: 'my-first-doc',
  bin: new Uint8Array(),
  editor: 'me',
});
await storage.tryGet('blob')?.get('img');

Put Storage behind Worker

import { SpaceStorageWorkerClient } from '@affine/nbstore/op';
import type { ConnectionStatus } from '@affine/nbstore';
import { IndexedDBDocStorage } from '@affine/nbstore/idb';

const client = new SpaceStorageWorkerClient();
client.addStorage(IndexedDBDocStorage, {
  // options can only be structure-cloneable type
  peer: 'local',
  spaceType: 'workspace',
  spaceId: 'my-new-workspace',
});

await client.connect();
client.ob$('connection', ({ storage, status, error }) => {
  ui.show(storage, status, error);
});

await client.call('pushDocUpdate', {
  docId: 'my-first-doc',
  bin: new Uint8Array(),
  editor: 'me',
});

// call unregistered op will leads to Error
// Error { message: 'Handler for operation [listHistory] is not registered.' }
await client.call('listHistories', { docId: 'my-first-doc' });