Merge branch 'dev'
This commit is contained in:
commit
69bd4a476b
|
|
@ -38,10 +38,11 @@ export class BooksLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('books', 'catalog');
|
const cachedIds = await this.#store.listByModule('books', 'catalog');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<BooksCatalogDoc>(docId, booksCatalogSchema, binary);
|
this.#documents.open<BooksCatalogDoc>(docId, booksCatalogSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[BooksClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[BooksClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,11 @@ export class CalLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('cal', 'events');
|
const cachedIds = await this.#store.listByModule('cal', 'events');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<CalendarDoc>(docId, calendarSchema, binary);
|
this.#documents.open<CalendarDoc>(docId, calendarSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[CalClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[CalClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,20 @@ export class CartLocalFirstClient {
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const catalogIds = await this.#store.listByModule('cart', 'catalog');
|
const [catalogIds, orderIds] = await Promise.all([
|
||||||
for (const docId of catalogIds) {
|
this.#store.listByModule('cart', 'catalog'),
|
||||||
const binary = await this.#store.load(docId);
|
this.#store.listByModule('cart', 'orders'),
|
||||||
if (binary) this.#documents.open<CatalogDoc>(docId, catalogSchema, binary);
|
]);
|
||||||
}
|
const allIds = [...catalogIds, ...orderIds];
|
||||||
const orderIds = await this.#store.listByModule('cart', 'orders');
|
const cached = await this.#store.loadMany(allIds);
|
||||||
for (const docId of orderIds) {
|
for (const [docId, binary] of cached) {
|
||||||
const binary = await this.#store.load(docId);
|
if (catalogIds.includes(docId)) {
|
||||||
if (binary) this.#documents.open<OrderDoc>(docId, orderSchema, binary);
|
this.#documents.open<CatalogDoc>(docId, catalogSchema, binary);
|
||||||
|
} else {
|
||||||
|
this.#documents.open<OrderDoc>(docId, orderSchema, binary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(allIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[CartClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[CartClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,11 @@ export class FilesLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('files', 'cards');
|
const cachedIds = await this.#store.listByModule('files', 'cards');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<FilesDoc>(docId, filesSchema, binary);
|
this.#documents.open<FilesDoc>(docId, filesSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[FilesClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[FilesClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,11 @@ export class FundsLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('funds', 'flows');
|
const cachedIds = await this.#store.listByModule('funds', 'flows');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<FundsDoc>(docId, fundsSchema, binary);
|
this.#documents.open<FundsDoc>(docId, fundsSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[FundsClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[FundsClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,11 @@ export class InboxLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('inbox', 'mailboxes');
|
const cachedIds = await this.#store.listByModule('inbox', 'mailboxes');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<MailboxDoc>(docId, mailboxSchema, binary);
|
this.#documents.open<MailboxDoc>(docId, mailboxSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[InboxClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[InboxClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -54,15 +54,16 @@ export class NotesLocalFirstClient {
|
||||||
// Open IndexedDB store
|
// Open IndexedDB store
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
|
|
||||||
// Load any cached notebook docs from IndexedDB
|
// Load all cached notebook docs from IndexedDB in parallel
|
||||||
const cachedIds = await this.#store.listByModule('notes', 'notebooks');
|
const cachedIds = await this.#store.listByModule('notes', 'notebooks');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) {
|
this.#documents.open<NotebookDoc>(docId, notebookSchema, binary);
|
||||||
this.#documents.open<NotebookDoc>(docId, notebookSchema, binary);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preload sync states in parallel before connecting
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
|
|
||||||
// Connect to sync server
|
// Connect to sync server
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,11 @@ export class SplatLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('splat', 'scenes');
|
const cachedIds = await this.#store.listByModule('splat', 'scenes');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<SplatScenesDoc>(docId, splatScenesSchema, binary);
|
this.#documents.open<SplatScenesDoc>(docId, splatScenesSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[SplatClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[SplatClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,11 @@ export class TripsLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('trips', 'trips');
|
const cachedIds = await this.#store.listByModule('trips', 'trips');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<TripDoc>(docId, tripSchema, binary);
|
this.#documents.open<TripDoc>(docId, tripSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[TripsClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[TripsClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,11 @@ export class VoteLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('vote', 'proposals');
|
const cachedIds = await this.#store.listByModule('vote', 'proposals');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<ProposalDoc>(docId, proposalSchema, binary);
|
this.#documents.open<ProposalDoc>(docId, proposalSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[VoteClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[VoteClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,11 @@ export class WorkLocalFirstClient {
|
||||||
if (this.#initialized) return;
|
if (this.#initialized) return;
|
||||||
await this.#store.open();
|
await this.#store.open();
|
||||||
const cachedIds = await this.#store.listByModule('work', 'boards');
|
const cachedIds = await this.#store.listByModule('work', 'boards');
|
||||||
for (const docId of cachedIds) {
|
const cached = await this.#store.loadMany(cachedIds);
|
||||||
const binary = await this.#store.load(docId);
|
for (const [docId, binary] of cached) {
|
||||||
if (binary) this.#documents.open<BoardDoc>(docId, boardSchema, binary);
|
this.#documents.open<BoardDoc>(docId, boardSchema, binary);
|
||||||
}
|
}
|
||||||
|
await this.#sync.preloadSyncStates(cachedIds);
|
||||||
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
const wsUrl = `${proto}//${location.host}/ws/${this.#space}`;
|
||||||
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[WorkClient] Working offline'); }
|
try { await this.#sync.connect(wsUrl, this.#space); } catch { console.warn('[WorkClient] Working offline'); }
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ const encoder = new TextEncoder();
|
||||||
*/
|
*/
|
||||||
export class DocCrypto {
|
export class DocCrypto {
|
||||||
#masterKeyMaterial: CryptoKey | null = null;
|
#masterKeyMaterial: CryptoKey | null = null;
|
||||||
|
#spaceKeyCache = new Map<string, CryptoKey>();
|
||||||
|
#docKeyCache = new Map<string, CryptoKey>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize from a master key. Accepts either:
|
* Initialize from a master key. Accepts either:
|
||||||
|
|
@ -94,6 +96,9 @@ export class DocCrypto {
|
||||||
async deriveSpaceKey(spaceId: string): Promise<CryptoKey> {
|
async deriveSpaceKey(spaceId: string): Promise<CryptoKey> {
|
||||||
this.#assertInit();
|
this.#assertInit();
|
||||||
|
|
||||||
|
const cached = this.#spaceKeyCache.get(spaceId);
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
// Derive 256 bits of key material for the space
|
// Derive 256 bits of key material for the space
|
||||||
const bits = await crypto.subtle.deriveBits(
|
const bits = await crypto.subtle.deriveBits(
|
||||||
{
|
{
|
||||||
|
|
@ -107,13 +112,15 @@ export class DocCrypto {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Re-import as HKDF for further derivation (space → doc)
|
// Re-import as HKDF for further derivation (space → doc)
|
||||||
return crypto.subtle.importKey(
|
const key = await crypto.subtle.importKey(
|
||||||
'raw',
|
'raw',
|
||||||
bits,
|
bits,
|
||||||
{ name: 'HKDF' },
|
{ name: 'HKDF' },
|
||||||
false,
|
false,
|
||||||
['deriveKey', 'deriveBits']
|
['deriveKey', 'deriveBits']
|
||||||
);
|
);
|
||||||
|
this.#spaceKeyCache.set(spaceId, key);
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -121,7 +128,10 @@ export class DocCrypto {
|
||||||
* info = "rspace:{spaceId}:{docId}"
|
* info = "rspace:{spaceId}:{docId}"
|
||||||
*/
|
*/
|
||||||
async deriveDocKey(spaceKey: CryptoKey, docId: string): Promise<CryptoKey> {
|
async deriveDocKey(spaceKey: CryptoKey, docId: string): Promise<CryptoKey> {
|
||||||
return crypto.subtle.deriveKey(
|
const cached = this.#docKeyCache.get(docId);
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
const key = await crypto.subtle.deriveKey(
|
||||||
{
|
{
|
||||||
name: 'HKDF',
|
name: 'HKDF',
|
||||||
hash: 'SHA-256',
|
hash: 'SHA-256',
|
||||||
|
|
@ -133,6 +143,8 @@ export class DocCrypto {
|
||||||
false, // non-extractable
|
false, // non-extractable
|
||||||
['encrypt', 'decrypt']
|
['encrypt', 'decrypt']
|
||||||
);
|
);
|
||||||
|
this.#docKeyCache.set(docId, key);
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,6 +212,8 @@ export class DocCrypto {
|
||||||
*/
|
*/
|
||||||
clear(): void {
|
clear(): void {
|
||||||
this.#masterKeyMaterial = null;
|
this.#masterKeyMaterial = null;
|
||||||
|
this.#spaceKeyCache.clear();
|
||||||
|
this.#docKeyCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
#assertInit(): void {
|
#assertInit(): void {
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,20 @@ export class EncryptedDocStore {
|
||||||
return stored.data;
|
return stored.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load multiple documents in parallel. Returns a Map of docId → decrypted bytes.
|
||||||
|
*/
|
||||||
|
async loadMany(docIds: DocumentId[]): Promise<Map<DocumentId, Uint8Array>> {
|
||||||
|
const results = new Map<DocumentId, Uint8Array>();
|
||||||
|
if (!this.#db || docIds.length === 0) return results;
|
||||||
|
|
||||||
|
const entries = await Promise.all(docIds.map(id => this.load(id).then(data => [id, data] as const)));
|
||||||
|
for (const [id, data] of entries) {
|
||||||
|
if (data) results.set(id, data);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load raw stored bytes for a document (without decrypting).
|
* Load raw stored bytes for a document (without decrypting).
|
||||||
* Used by the backup manager to upload already-encrypted blobs.
|
* Used by the backup manager to upload already-encrypted blobs.
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,31 @@ export class DocSyncManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preload sync states for multiple documents in parallel.
|
||||||
|
* Call before subscribe() to avoid sequential IDB reads during subscription.
|
||||||
|
*/
|
||||||
|
async preloadSyncStates(docIds: DocumentId[]): Promise<void> {
|
||||||
|
if (!this.#store) return;
|
||||||
|
|
||||||
|
const toLoad = docIds.filter(id => !this.#syncStates.has(id));
|
||||||
|
if (toLoad.length === 0) return;
|
||||||
|
|
||||||
|
const entries = await Promise.all(
|
||||||
|
toLoad.map(id =>
|
||||||
|
this.#store!.loadSyncState(id, this.#peerId).then(saved => [id, saved] as const)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const [id, saved] of entries) {
|
||||||
|
if (saved) {
|
||||||
|
this.#syncStates.set(id, Automerge.decodeSyncState(saved));
|
||||||
|
} else {
|
||||||
|
this.#syncStates.set(id, Automerge.initSyncState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to sync for one or more documents.
|
* Subscribe to sync for one or more documents.
|
||||||
*/
|
*/
|
||||||
|
|
@ -213,16 +238,17 @@ export class DocSyncManager {
|
||||||
this.#subscribedDocs.add(id);
|
this.#subscribedDocs.add(id);
|
||||||
newIds.push(id);
|
newIds.push(id);
|
||||||
|
|
||||||
// Initialize sync state from store if available
|
// Initialize sync state from store if available (skip if preloaded)
|
||||||
if (this.#store && !this.#syncStates.has(id)) {
|
|
||||||
const saved = await this.#store.loadSyncState(id, this.#peerId);
|
|
||||||
if (saved) {
|
|
||||||
this.#syncStates.set(id, Automerge.decodeSyncState(saved));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.#syncStates.has(id)) {
|
if (!this.#syncStates.has(id)) {
|
||||||
this.#syncStates.set(id, Automerge.initSyncState());
|
if (this.#store) {
|
||||||
|
const saved = await this.#store.loadSyncState(id, this.#peerId);
|
||||||
|
if (saved) {
|
||||||
|
this.#syncStates.set(id, Automerge.decodeSyncState(saved));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.#syncStates.has(id)) {
|
||||||
|
this.#syncStates.set(id, Automerge.initSyncState());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue