@interop/was-react 0.1.0
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/LICENSE.md +20 -0
- package/README.md +410 -0
- package/dist/auth/chapi.d.ts +43 -0
- package/dist/auth/chapi.d.ts.map +1 -0
- package/dist/auth/chapi.js +107 -0
- package/dist/auth/chapi.js.map +1 -0
- package/dist/auth/loginFlow.d.ts +93 -0
- package/dist/auth/loginFlow.d.ts.map +1 -0
- package/dist/auth/loginFlow.js +149 -0
- package/dist/auth/loginFlow.js.map +1 -0
- package/dist/auth/loginRequest.d.ts +66 -0
- package/dist/auth/loginRequest.d.ts.map +1 -0
- package/dist/auth/loginRequest.js +83 -0
- package/dist/auth/loginRequest.js.map +1 -0
- package/dist/auth/verifyResponse.d.ts +52 -0
- package/dist/auth/verifyResponse.d.ts.map +1 -0
- package/dist/auth/verifyResponse.js +138 -0
- package/dist/auth/verifyResponse.js.map +1 -0
- package/dist/auth/verifyResponse.test.d.ts +2 -0
- package/dist/auth/verifyResponse.test.d.ts.map +1 -0
- package/dist/auth/verifyResponse.test.js +282 -0
- package/dist/auth/verifyResponse.test.js.map +1 -0
- package/dist/auth/walletRequestTypes.d.ts +143 -0
- package/dist/auth/walletRequestTypes.d.ts.map +1 -0
- package/dist/auth/walletRequestTypes.js +2 -0
- package/dist/auth/walletRequestTypes.js.map +1 -0
- package/dist/config.d.ts +150 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -0
- package/dist/dev/cli.d.ts +16 -0
- package/dist/dev/cli.d.ts.map +1 -0
- package/dist/dev/cli.js +122 -0
- package/dist/dev/cli.js.map +1 -0
- package/dist/dev/cli.test.d.ts +2 -0
- package/dist/dev/cli.test.d.ts.map +1 -0
- package/dist/dev/cli.test.js +39 -0
- package/dist/dev/cli.test.js.map +1 -0
- package/dist/dev/index.d.ts +10 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +10 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/provisionDevGrants.d.ts +73 -0
- package/dist/dev/provisionDevGrants.d.ts.map +1 -0
- package/dist/dev/provisionDevGrants.js +176 -0
- package/dist/dev/provisionDevGrants.js.map +1 -0
- package/dist/grants.d.ts +59 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +82 -0
- package/dist/grants.js.map +1 -0
- package/dist/grants.test.d.ts +2 -0
- package/dist/grants.test.d.ts.map +1 -0
- package/dist/grants.test.js +79 -0
- package/dist/grants.test.js.map +1 -0
- package/dist/identity/agents.d.ts +99 -0
- package/dist/identity/agents.d.ts.map +1 -0
- package/dist/identity/agents.js +132 -0
- package/dist/identity/agents.js.map +1 -0
- package/dist/identity/appSession.d.ts +78 -0
- package/dist/identity/appSession.d.ts.map +1 -0
- package/dist/identity/appSession.js +93 -0
- package/dist/identity/appSession.js.map +1 -0
- package/dist/identity/documentLoader.d.ts +20 -0
- package/dist/identity/documentLoader.d.ts.map +1 -0
- package/dist/identity/documentLoader.js +99 -0
- package/dist/identity/documentLoader.js.map +1 -0
- package/dist/identity/initAppSession.d.ts +27 -0
- package/dist/identity/initAppSession.d.ts.map +1 -0
- package/dist/identity/initAppSession.js +30 -0
- package/dist/identity/initAppSession.js.map +1 -0
- package/dist/identity/seedCredential.d.ts +74 -0
- package/dist/identity/seedCredential.d.ts.map +1 -0
- package/dist/identity/seedCredential.js +165 -0
- package/dist/identity/seedCredential.js.map +1 -0
- package/dist/identity/seedCredential.test.d.ts +2 -0
- package/dist/identity/seedCredential.test.d.ts.map +1 -0
- package/dist/identity/seedCredential.test.js +205 -0
- package/dist/identity/seedCredential.test.js.map +1 -0
- package/dist/identity/seedStore.d.ts +27 -0
- package/dist/identity/seedStore.d.ts.map +1 -0
- package/dist/identity/seedStore.js +74 -0
- package/dist/identity/seedStore.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/mui/ProtectedRoute.d.ts +10 -0
- package/dist/mui/ProtectedRoute.d.ts.map +1 -0
- package/dist/mui/ProtectedRoute.js +54 -0
- package/dist/mui/ProtectedRoute.js.map +1 -0
- package/dist/mui/ReconnectBanner.d.ts +2 -0
- package/dist/mui/ReconnectBanner.d.ts.map +1 -0
- package/dist/mui/ReconnectBanner.js +20 -0
- package/dist/mui/ReconnectBanner.js.map +1 -0
- package/dist/mui/SyncStatusChip.d.ts +2 -0
- package/dist/mui/SyncStatusChip.d.ts.map +1 -0
- package/dist/mui/SyncStatusChip.js +27 -0
- package/dist/mui/SyncStatusChip.js.map +1 -0
- package/dist/mui/index.d.ts +12 -0
- package/dist/mui/index.d.ts.map +1 -0
- package/dist/mui/index.js +12 -0
- package/dist/mui/index.js.map +1 -0
- package/dist/react/WasSessionProvider.d.ts +36 -0
- package/dist/react/WasSessionProvider.d.ts.map +1 -0
- package/dist/react/WasSessionProvider.js +35 -0
- package/dist/react/WasSessionProvider.js.map +1 -0
- package/dist/react/hooks.d.ts +66 -0
- package/dist/react/hooks.d.ts.map +1 -0
- package/dist/react/hooks.js +121 -0
- package/dist/react/hooks.js.map +1 -0
- package/dist/react/index.d.ts +11 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +11 -0
- package/dist/react/index.js.map +1 -0
- package/dist/session/appReadyStore.d.ts +10 -0
- package/dist/session/appReadyStore.d.ts.map +1 -0
- package/dist/session/appReadyStore.js +22 -0
- package/dist/session/appReadyStore.js.map +1 -0
- package/dist/session/authStore.d.ts +71 -0
- package/dist/session/authStore.d.ts.map +1 -0
- package/dist/session/authStore.js +347 -0
- package/dist/session/authStore.js.map +1 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +10 -0
- package/dist/session/index.js.map +1 -0
- package/dist/storage/entityStore.d.ts +51 -0
- package/dist/storage/entityStore.d.ts.map +1 -0
- package/dist/storage/entityStore.js +78 -0
- package/dist/storage/entityStore.js.map +1 -0
- package/dist/storage/localStore.d.ts +144 -0
- package/dist/storage/localStore.d.ts.map +1 -0
- package/dist/storage/localStore.js +289 -0
- package/dist/storage/localStore.js.map +1 -0
- package/dist/storage/rehydrate.d.ts +56 -0
- package/dist/storage/rehydrate.d.ts.map +1 -0
- package/dist/storage/rehydrate.js +87 -0
- package/dist/storage/rehydrate.js.map +1 -0
- package/dist/storage/rehydrate.test.d.ts +2 -0
- package/dist/storage/rehydrate.test.d.ts.map +1 -0
- package/dist/storage/rehydrate.test.js +172 -0
- package/dist/storage/rehydrate.test.js.map +1 -0
- package/dist/storage/storageManager.d.ts +39 -0
- package/dist/storage/storageManager.d.ts.map +1 -0
- package/dist/storage/storageManager.js +68 -0
- package/dist/storage/storageManager.js.map +1 -0
- package/dist/storage/syncController.d.ts +79 -0
- package/dist/storage/syncController.d.ts.map +1 -0
- package/dist/storage/syncController.js +189 -0
- package/dist/storage/syncController.js.map +1 -0
- package/dist/storage/syncStatusStore.d.ts +17 -0
- package/dist/storage/syncStatusStore.d.ts.map +1 -0
- package/dist/storage/syncStatusStore.js +19 -0
- package/dist/storage/syncStatusStore.js.map +1 -0
- package/dist/storage/wasRemoteStore.d.ts +73 -0
- package/dist/storage/wasRemoteStore.d.ts.map +1 -0
- package/dist/storage/wasRemoteStore.js +76 -0
- package/dist/storage/wasRemoteStore.js.map +1 -0
- package/dist/storage/wasSync.d.ts +42 -0
- package/dist/storage/wasSync.d.ts.map +1 -0
- package/dist/storage/wasSync.js +34 -0
- package/dist/storage/wasSync.js.map +1 -0
- package/dist/sync/changesQuery.d.ts +44 -0
- package/dist/sync/changesQuery.d.ts.map +1 -0
- package/dist/sync/changesQuery.js +61 -0
- package/dist/sync/changesQuery.js.map +1 -0
- package/dist/sync/changesQuery.test.d.ts +2 -0
- package/dist/sync/changesQuery.test.d.ts.map +1 -0
- package/dist/sync/changesQuery.test.js +145 -0
- package/dist/sync/changesQuery.test.js.map +1 -0
- package/dist/sync/docCipher.d.ts +68 -0
- package/dist/sync/docCipher.d.ts.map +1 -0
- package/dist/sync/docCipher.js +89 -0
- package/dist/sync/docCipher.js.map +1 -0
- package/dist/sync/feedMasterPort.d.ts +30 -0
- package/dist/sync/feedMasterPort.d.ts.map +1 -0
- package/dist/sync/feedMasterPort.js +58 -0
- package/dist/sync/feedMasterPort.js.map +1 -0
- package/dist/sync/index.d.ts +21 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +21 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/lww.d.ts +29 -0
- package/dist/sync/lww.d.ts.map +1 -0
- package/dist/sync/lww.js +28 -0
- package/dist/sync/lww.js.map +1 -0
- package/dist/sync/lww.test.d.ts +2 -0
- package/dist/sync/lww.test.d.ts.map +1 -0
- package/dist/sync/lww.test.js +28 -0
- package/dist/sync/lww.test.js.map +1 -0
- package/dist/sync/lwwConflictHandler.d.ts +49 -0
- package/dist/sync/lwwConflictHandler.d.ts.map +1 -0
- package/dist/sync/lwwConflictHandler.js +65 -0
- package/dist/sync/lwwConflictHandler.js.map +1 -0
- package/dist/sync/lwwConflictHandler.test.d.ts +2 -0
- package/dist/sync/lwwConflictHandler.test.d.ts.map +1 -0
- package/dist/sync/lwwConflictHandler.test.js +78 -0
- package/dist/sync/lwwConflictHandler.test.js.map +1 -0
- package/dist/sync/pushWrites.d.ts +57 -0
- package/dist/sync/pushWrites.d.ts.map +1 -0
- package/dist/sync/pushWrites.js +159 -0
- package/dist/sync/pushWrites.js.map +1 -0
- package/dist/sync/pushWrites.test.d.ts +2 -0
- package/dist/sync/pushWrites.test.d.ts.map +1 -0
- package/dist/sync/pushWrites.test.js +287 -0
- package/dist/sync/pushWrites.test.js.map +1 -0
- package/dist/sync/syncedDocSchema.d.ts +22 -0
- package/dist/sync/syncedDocSchema.d.ts.map +1 -0
- package/dist/sync/syncedDocSchema.js +26 -0
- package/dist/sync/syncedDocSchema.js.map +1 -0
- package/dist/sync/types.d.ts +194 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +36 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/sync/wasReplication.d.ts +47 -0
- package/dist/sync/wasReplication.d.ts.map +1 -0
- package/dist/sync/wasReplication.js +55 -0
- package/dist/sync/wasReplication.js.map +1 -0
- package/dist/sync/wasSyncPort.d.ts +45 -0
- package/dist/sync/wasSyncPort.d.ts.map +1 -0
- package/dist/sync/wasSyncPort.js +170 -0
- package/dist/sync/wasSyncPort.js.map +1 -0
- package/package.json +142 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
5
|
+
import { setLocalStore, clearLocalStore } from './storageManager.js';
|
|
6
|
+
import { patchFromChange, hydrateAll, clearAllEntityStores, scheduleRehydrate } from './rehydrate.js';
|
|
7
|
+
// A fake LocalStore: `decryptEnvelope` unwraps `{ jwe: payload }` (mirroring the
|
|
8
|
+
// lww handler test's fake cipher); the envelope index calls just record.
|
|
9
|
+
function makeFakeStore({ failDecrypt = false } = {}) {
|
|
10
|
+
const remembered = [];
|
|
11
|
+
const forgotten = [];
|
|
12
|
+
const store = {
|
|
13
|
+
decryptEnvelope: async (_key, envelope) => {
|
|
14
|
+
if (failDecrypt) {
|
|
15
|
+
throw new Error('decrypt failed');
|
|
16
|
+
}
|
|
17
|
+
return envelope.jwe;
|
|
18
|
+
},
|
|
19
|
+
rememberEnvelope: (key, uuid, envelopeId) => {
|
|
20
|
+
remembered.push([key, uuid, envelopeId]);
|
|
21
|
+
},
|
|
22
|
+
forgetEnvelope: (key, uuid) => {
|
|
23
|
+
forgotten.push([key, uuid]);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
return { store, remembered, forgotten };
|
|
27
|
+
}
|
|
28
|
+
// A fake registry entry that records what the mechanism drives.
|
|
29
|
+
function makeRegistry() {
|
|
30
|
+
const calls = {
|
|
31
|
+
hydrate: 0,
|
|
32
|
+
upsert: [],
|
|
33
|
+
drop: [],
|
|
34
|
+
clear: 0
|
|
35
|
+
};
|
|
36
|
+
const registry = {
|
|
37
|
+
notes: {
|
|
38
|
+
hydrate: async () => {
|
|
39
|
+
calls.hydrate += 1;
|
|
40
|
+
},
|
|
41
|
+
upsert: doc => {
|
|
42
|
+
calls.upsert.push(doc);
|
|
43
|
+
},
|
|
44
|
+
drop: uuid => {
|
|
45
|
+
calls.drop.push(uuid);
|
|
46
|
+
},
|
|
47
|
+
clear: () => {
|
|
48
|
+
calls.clear += 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
return { registry, calls };
|
|
53
|
+
}
|
|
54
|
+
function envelope(payload) {
|
|
55
|
+
return { jwe: payload };
|
|
56
|
+
}
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
clearLocalStore();
|
|
59
|
+
});
|
|
60
|
+
describe('rehydrate mechanism', () => {
|
|
61
|
+
it('hydrateAll drives every registry entry', async () => {
|
|
62
|
+
const { registry, calls } = makeRegistry();
|
|
63
|
+
await hydrateAll(registry);
|
|
64
|
+
expect(calls.hydrate).toBe(1);
|
|
65
|
+
});
|
|
66
|
+
it('clearAllEntityStores drives every registry entry', () => {
|
|
67
|
+
const { registry, calls } = makeRegistry();
|
|
68
|
+
clearAllEntityStores(registry);
|
|
69
|
+
expect(calls.clear).toBe(1);
|
|
70
|
+
});
|
|
71
|
+
describe('patchFromChange', () => {
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
const { store } = makeFakeStore();
|
|
74
|
+
setLocalStore(store);
|
|
75
|
+
});
|
|
76
|
+
it('upserts an inserted payload and remembers its envelope', async () => {
|
|
77
|
+
const { store, remembered } = makeFakeStore();
|
|
78
|
+
setLocalStore(store);
|
|
79
|
+
const { registry, calls } = makeRegistry();
|
|
80
|
+
const payload = { id: 'note-1' };
|
|
81
|
+
await patchFromChange(registry, 'notes', {
|
|
82
|
+
operation: 'INSERT',
|
|
83
|
+
documentData: { id: 'env-1', data: envelope(payload) }
|
|
84
|
+
});
|
|
85
|
+
expect(calls.upsert).toEqual([payload]);
|
|
86
|
+
expect(calls.drop).toEqual([]);
|
|
87
|
+
expect(remembered).toEqual([['notes', 'note-1', 'env-1']]);
|
|
88
|
+
});
|
|
89
|
+
it('drops a deleted payload and forgets its envelope', async () => {
|
|
90
|
+
const { store, forgotten } = makeFakeStore();
|
|
91
|
+
setLocalStore(store);
|
|
92
|
+
const { registry, calls } = makeRegistry();
|
|
93
|
+
await patchFromChange(registry, 'notes', {
|
|
94
|
+
operation: 'DELETE',
|
|
95
|
+
documentData: { id: 'env-1', data: envelope({ id: 'note-1' }) }
|
|
96
|
+
});
|
|
97
|
+
expect(calls.drop).toEqual(['note-1']);
|
|
98
|
+
expect(calls.upsert).toEqual([]);
|
|
99
|
+
expect(forgotten).toEqual([['notes', 'note-1']]);
|
|
100
|
+
});
|
|
101
|
+
it('drops on a soft-delete (_deleted) row', async () => {
|
|
102
|
+
const { store } = makeFakeStore();
|
|
103
|
+
setLocalStore(store);
|
|
104
|
+
const { registry, calls } = makeRegistry();
|
|
105
|
+
await patchFromChange(registry, 'notes', {
|
|
106
|
+
operation: 'UPDATE',
|
|
107
|
+
documentData: {
|
|
108
|
+
id: 'env-1',
|
|
109
|
+
data: envelope({ id: 'note-1' }),
|
|
110
|
+
_deleted: true
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
expect(calls.drop).toEqual(['note-1']);
|
|
114
|
+
});
|
|
115
|
+
it('is a no-op for an unregistered collection', async () => {
|
|
116
|
+
const { registry, calls } = makeRegistry();
|
|
117
|
+
await patchFromChange(registry, 'unknown', {
|
|
118
|
+
operation: 'INSERT',
|
|
119
|
+
documentData: { id: 'env-1', data: envelope({ id: 'x' }) }
|
|
120
|
+
});
|
|
121
|
+
expect(calls.upsert).toEqual([]);
|
|
122
|
+
expect(calls.drop).toEqual([]);
|
|
123
|
+
});
|
|
124
|
+
it('schedules a re-hydrate when the envelope is missing', async () => {
|
|
125
|
+
vi.useFakeTimers();
|
|
126
|
+
try {
|
|
127
|
+
const { registry, calls } = makeRegistry();
|
|
128
|
+
await patchFromChange(registry, 'notes', {
|
|
129
|
+
operation: 'INSERT',
|
|
130
|
+
documentData: { id: 'env-1' }
|
|
131
|
+
});
|
|
132
|
+
expect(calls.upsert).toEqual([]);
|
|
133
|
+
await vi.advanceTimersByTimeAsync(60);
|
|
134
|
+
expect(calls.hydrate).toBe(1);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
vi.useRealTimers();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
it('schedules a re-hydrate when decryption fails', async () => {
|
|
141
|
+
vi.useFakeTimers();
|
|
142
|
+
try {
|
|
143
|
+
const { store } = makeFakeStore({ failDecrypt: true });
|
|
144
|
+
setLocalStore(store);
|
|
145
|
+
const { registry, calls } = makeRegistry();
|
|
146
|
+
await patchFromChange(registry, 'notes', {
|
|
147
|
+
operation: 'INSERT',
|
|
148
|
+
documentData: { id: 'env-1', data: envelope({ id: 'note-1' }) }
|
|
149
|
+
});
|
|
150
|
+
expect(calls.upsert).toEqual([]);
|
|
151
|
+
await vi.advanceTimersByTimeAsync(60);
|
|
152
|
+
expect(calls.hydrate).toBe(1);
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
vi.useRealTimers();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
it('scheduleRehydrate is a no-op for an unregistered collection', () => {
|
|
160
|
+
vi.useFakeTimers();
|
|
161
|
+
try {
|
|
162
|
+
const { registry, calls } = makeRegistry();
|
|
163
|
+
scheduleRehydrate(registry, 'unknown');
|
|
164
|
+
vi.advanceTimersByTime(60);
|
|
165
|
+
expect(calls.hydrate).toBe(0);
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
vi.useRealTimers();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
//# sourceMappingURL=rehydrate.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rehydrate.test.js","sourceRoot":"","sources":["../../src/storage/rehydrate.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { LocalStore } from './localStore.js';
|
|
2
|
+
/**
|
|
3
|
+
* Installs the opened store (called once by the app bootstrap).
|
|
4
|
+
*
|
|
5
|
+
* @param store {LocalStore}
|
|
6
|
+
* @returns {void}
|
|
7
|
+
*/
|
|
8
|
+
export declare function setLocalStore(store: LocalStore): void;
|
|
9
|
+
/**
|
|
10
|
+
* The opened store, or throws if the app has not bootstrapped yet.
|
|
11
|
+
*
|
|
12
|
+
* @returns {LocalStore}
|
|
13
|
+
*/
|
|
14
|
+
export declare function requireStore(): LocalStore;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the store has been opened.
|
|
17
|
+
*
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
export declare function hasStore(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Releases the held store reference (logout; the caller closes the db).
|
|
23
|
+
*
|
|
24
|
+
* @returns {void}
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearLocalStore(): void;
|
|
27
|
+
/**
|
|
28
|
+
* A stable per-install device id (the last-write-wins tiebreak stamped into
|
|
29
|
+
* every payload), persisted in localStorage under `<prefix>deviceId`.
|
|
30
|
+
*
|
|
31
|
+
* @param [options] {object}
|
|
32
|
+
* @param [options.storageKeyPrefix] {string} the localStorage key prefix
|
|
33
|
+
* (defaults to {@link DEFAULT_STORAGE_KEY_PREFIX})
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
export declare function getDeviceId({ storageKeyPrefix }?: {
|
|
37
|
+
storageKeyPrefix?: string;
|
|
38
|
+
}): string;
|
|
39
|
+
//# sourceMappingURL=storageManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageManager.d.ts","sourceRoot":"","sources":["../../src/storage/storageManager.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAIjD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAErD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,UAAU,CAKzC;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,OAAO,CAElC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EAC1B,gBAA6C,EAC9C,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CAQ7C"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The storage manager: a thin process-wide holder for the one {@link LocalStore}
|
|
6
|
+
* instance plus the per-install device id. Entity stores reach for the store
|
|
7
|
+
* through {@link requireStore} inside their CRUD actions rather than importing it
|
|
8
|
+
* directly, which keeps this module free of store imports (no cycle) and lets
|
|
9
|
+
* the app own the init/hydrate ordering.
|
|
10
|
+
*/
|
|
11
|
+
import { uuidv7 } from 'uuidv7';
|
|
12
|
+
import { DEFAULT_STORAGE_KEY_PREFIX } from '../config.js';
|
|
13
|
+
let localStore = null;
|
|
14
|
+
/**
|
|
15
|
+
* Installs the opened store (called once by the app bootstrap).
|
|
16
|
+
*
|
|
17
|
+
* @param store {LocalStore}
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
export function setLocalStore(store) {
|
|
21
|
+
localStore = store;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The opened store, or throws if the app has not bootstrapped yet.
|
|
25
|
+
*
|
|
26
|
+
* @returns {LocalStore}
|
|
27
|
+
*/
|
|
28
|
+
export function requireStore() {
|
|
29
|
+
if (!localStore) {
|
|
30
|
+
throw new Error('LocalStore is not initialized; open it first.');
|
|
31
|
+
}
|
|
32
|
+
return localStore;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Whether the store has been opened.
|
|
36
|
+
*
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
export function hasStore() {
|
|
40
|
+
return localStore !== null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Releases the held store reference (logout; the caller closes the db).
|
|
44
|
+
*
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
47
|
+
export function clearLocalStore() {
|
|
48
|
+
localStore = null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A stable per-install device id (the last-write-wins tiebreak stamped into
|
|
52
|
+
* every payload), persisted in localStorage under `<prefix>deviceId`.
|
|
53
|
+
*
|
|
54
|
+
* @param [options] {object}
|
|
55
|
+
* @param [options.storageKeyPrefix] {string} the localStorage key prefix
|
|
56
|
+
* (defaults to {@link DEFAULT_STORAGE_KEY_PREFIX})
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
export function getDeviceId({ storageKeyPrefix = DEFAULT_STORAGE_KEY_PREFIX } = {}) {
|
|
60
|
+
const deviceIdKey = `${storageKeyPrefix}deviceId`;
|
|
61
|
+
let id = localStorage.getItem(deviceIdKey);
|
|
62
|
+
if (!id) {
|
|
63
|
+
id = uuidv7();
|
|
64
|
+
localStorage.setItem(deviceIdKey, id);
|
|
65
|
+
}
|
|
66
|
+
return id;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=storageManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageManager.js","sourceRoot":"","sources":["../../src/storage/storageManager.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAA;AAGzD,IAAI,UAAU,GAAsB,IAAI,CAAA;AAExC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,UAAU,GAAG,KAAK,CAAA;AACpB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAClE,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,UAAU,KAAK,IAAI,CAAA;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,UAAU,GAAG,IAAI,CAAA;AACnB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,gBAAgB,GAAG,0BAA0B,KACZ,EAAE;IACnC,MAAM,WAAW,GAAG,GAAG,gBAAgB,UAAU,CAAA;IACjD,IAAI,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,EAAE,GAAG,MAAM,EAAE,CAAA;QACb,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;IACvC,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { RxChangeEvent } from 'rxdb/plugins/core';
|
|
2
|
+
import { type SyncedDoc } from '../sync/index.js';
|
|
3
|
+
import { type WasCollectionConfig, type WasSyncConfig } from '../config.js';
|
|
4
|
+
import type { LocalStore } from './localStore.js';
|
|
5
|
+
import type { WasRemoteStore } from './wasRemoteStore.js';
|
|
6
|
+
/**
|
|
7
|
+
* Whether a replication error (an RxError wrapping the port's thrown errors)
|
|
8
|
+
* carries an HTTP 401/403 anywhere in its tree -- the storage-access-expired
|
|
9
|
+
* signal.
|
|
10
|
+
*
|
|
11
|
+
* @param err {unknown}
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
*/
|
|
14
|
+
export declare function isAuthError(err: unknown): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* A per-session controller around background replication. Construct via
|
|
17
|
+
* {@link createSyncController}.
|
|
18
|
+
*/
|
|
19
|
+
export declare class SyncController {
|
|
20
|
+
private _collections;
|
|
21
|
+
private _sync;
|
|
22
|
+
private _replications;
|
|
23
|
+
private _onlineHandler?;
|
|
24
|
+
private _pollTimer?;
|
|
25
|
+
private _started;
|
|
26
|
+
constructor({ collections, sync }: {
|
|
27
|
+
collections: WasCollectionConfig[];
|
|
28
|
+
sync?: WasSyncConfig;
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Starts background replication for every entity collection covered by the
|
|
32
|
+
* grant set. Idempotent (a no-op if already running).
|
|
33
|
+
*
|
|
34
|
+
* @param options {object}
|
|
35
|
+
* @param options.remoteStore {WasRemoteStore}
|
|
36
|
+
* @param options.localStore {LocalStore}
|
|
37
|
+
* @param [options.onRemoteChange] {(collectionKey: string, event) => void}
|
|
38
|
+
* fired per RxDB change (pull or conflict-resolved push) for reactive
|
|
39
|
+
* per-doc store patching
|
|
40
|
+
* @param [options.onAuthError] {() => void} fired when a replication error
|
|
41
|
+
* carries a 401/403 (storage access expired/revoked)
|
|
42
|
+
* @returns {Promise<void>}
|
|
43
|
+
*/
|
|
44
|
+
start({ remoteStore, localStore, onRemoteChange, onAuthError }: {
|
|
45
|
+
remoteStore: WasRemoteStore;
|
|
46
|
+
localStore: LocalStore;
|
|
47
|
+
onRemoteChange?: (collectionKey: string, event: RxChangeEvent<SyncedDoc>) => void;
|
|
48
|
+
onAuthError?: () => void;
|
|
49
|
+
}): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Triggers an immediate replication cycle on every running collection, rather
|
|
52
|
+
* than waiting for RxDB's next scheduled tick. Fire-and-forget.
|
|
53
|
+
*
|
|
54
|
+
* @returns {void}
|
|
55
|
+
*/
|
|
56
|
+
reSync(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Stops replication and releases resources (the database is owned by the
|
|
59
|
+
* caller). Idempotent.
|
|
60
|
+
*
|
|
61
|
+
* @returns {Promise<void>}
|
|
62
|
+
*/
|
|
63
|
+
stop(): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Builds a fresh {@link SyncController} bound to the collection registry and
|
|
67
|
+
* sync tuning. Prefer one controller per session (started once, stopped on
|
|
68
|
+
* logout) over a module-level singleton.
|
|
69
|
+
*
|
|
70
|
+
* @param options {object}
|
|
71
|
+
* @param options.collections {WasCollectionConfig[]}
|
|
72
|
+
* @param [options.sync] {WasSyncConfig}
|
|
73
|
+
* @returns {SyncController}
|
|
74
|
+
*/
|
|
75
|
+
export declare function createSyncController({ collections, sync }: {
|
|
76
|
+
collections: WasCollectionConfig[];
|
|
77
|
+
sync?: WasSyncConfig;
|
|
78
|
+
}): SyncController;
|
|
79
|
+
//# sourceMappingURL=syncController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncController.d.ts","sourceRoot":"","sources":["../../src/storage/syncController.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EACnB,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAMzD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAoCjD;AAOD;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,cAAc,CAAC,CAAY;IACnC,OAAO,CAAC,UAAU,CAAC,CAAgC;IACnD,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,EACV,WAAW,EACX,IAAI,EACL,EAAE;QACD,WAAW,EAAE,mBAAmB,EAAE,CAAA;QAClC,IAAI,CAAC,EAAE,aAAa,CAAA;KACrB;IAKD;;;;;;;;;;;;;OAaG;IACG,KAAK,CAAC,EACV,WAAW,EACX,UAAU,EACV,cAAc,EACd,WAAW,EACZ,EAAE;QACD,WAAW,EAAE,cAAc,CAAA;QAC3B,UAAU,EAAE,UAAU,CAAA;QACtB,cAAc,CAAC,EAAE,CACf,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,KAC5B,IAAI,CAAA;QACT,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;KACzB,GAAG,OAAO,CAAC,IAAI,CAAC;IAuEjB;;;;;OAKG;IACH,MAAM,IAAI,IAAI;IAMd;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAuB5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,WAAW,EACX,IAAI,EACL,EAAE;IACD,WAAW,EAAE,mBAAmB,EAAE,CAAA;IAClC,IAAI,CAAC,EAAE,aAAa,CAAA;CACrB,GAAG,cAAc,CAEjB"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { createWasReplication, createWasSyncPort, withFeedMasterRead } from '../sync/index.js';
|
|
2
|
+
import { DEFAULT_SYNC_POLL_MS } from '../config.js';
|
|
3
|
+
import { useSyncStatusStore } from './syncStatusStore.js';
|
|
4
|
+
/**
|
|
5
|
+
* Whether a replication error (an RxError wrapping the port's thrown errors)
|
|
6
|
+
* carries an HTTP 401/403 anywhere in its tree -- the storage-access-expired
|
|
7
|
+
* signal.
|
|
8
|
+
*
|
|
9
|
+
* @param err {unknown}
|
|
10
|
+
* @returns {boolean}
|
|
11
|
+
*/
|
|
12
|
+
export function isAuthError(err) {
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
const queue = [err];
|
|
15
|
+
while (queue.length > 0) {
|
|
16
|
+
const current = queue.shift();
|
|
17
|
+
if (current === null || typeof current !== 'object' || seen.has(current)) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
seen.add(current);
|
|
21
|
+
const candidate = current;
|
|
22
|
+
const statuses = [
|
|
23
|
+
candidate.status,
|
|
24
|
+
candidate.statusCode,
|
|
25
|
+
candidate.response?.status
|
|
26
|
+
];
|
|
27
|
+
if (statuses.includes(401) || statuses.includes(403)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (candidate.cause) {
|
|
31
|
+
queue.push(candidate.cause);
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(candidate.errors)) {
|
|
34
|
+
queue.push(...candidate.errors);
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(candidate.parameters?.errors)) {
|
|
37
|
+
queue.push(...candidate.parameters.errors);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A per-session controller around background replication. Construct via
|
|
44
|
+
* {@link createSyncController}.
|
|
45
|
+
*/
|
|
46
|
+
export class SyncController {
|
|
47
|
+
_collections;
|
|
48
|
+
_sync;
|
|
49
|
+
_replications = [];
|
|
50
|
+
_onlineHandler;
|
|
51
|
+
_pollTimer;
|
|
52
|
+
_started = false;
|
|
53
|
+
constructor({ collections, sync }) {
|
|
54
|
+
this._collections = collections;
|
|
55
|
+
this._sync = sync ?? {};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Starts background replication for every entity collection covered by the
|
|
59
|
+
* grant set. Idempotent (a no-op if already running).
|
|
60
|
+
*
|
|
61
|
+
* @param options {object}
|
|
62
|
+
* @param options.remoteStore {WasRemoteStore}
|
|
63
|
+
* @param options.localStore {LocalStore}
|
|
64
|
+
* @param [options.onRemoteChange] {(collectionKey: string, event) => void}
|
|
65
|
+
* fired per RxDB change (pull or conflict-resolved push) for reactive
|
|
66
|
+
* per-doc store patching
|
|
67
|
+
* @param [options.onAuthError] {() => void} fired when a replication error
|
|
68
|
+
* carries a 401/403 (storage access expired/revoked)
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
*/
|
|
71
|
+
async start({ remoteStore, localStore, onRemoteChange, onAuthError }) {
|
|
72
|
+
if (this._started) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this._started = true;
|
|
76
|
+
const setStatus = useSyncStatusStore.getState().setStatus;
|
|
77
|
+
const batchSize = this._sync.batchSize;
|
|
78
|
+
const retryMs = this._sync.retryMs;
|
|
79
|
+
const pollMs = this._sync.pollMs ?? DEFAULT_SYNC_POLL_MS;
|
|
80
|
+
try {
|
|
81
|
+
for (const { key, id } of this._collections) {
|
|
82
|
+
setStatus(id, 'idle');
|
|
83
|
+
// Wrap the verbatim port so the 412 conflict re-read resolves `version`
|
|
84
|
+
// from the changes-feed body (CORS hides the `GET` ETag cross-origin).
|
|
85
|
+
const wasPort = withFeedMasterRead(createWasSyncPort({
|
|
86
|
+
was: remoteStore.was,
|
|
87
|
+
spaceId: remoteStore.spaceId,
|
|
88
|
+
collectionId: id,
|
|
89
|
+
...(remoteStore.collectionCapability(id) && {
|
|
90
|
+
capability: remoteStore.collectionCapability(id)
|
|
91
|
+
})
|
|
92
|
+
}));
|
|
93
|
+
const state = createWasReplication({
|
|
94
|
+
rxCollection: localStore.rxCollection(key),
|
|
95
|
+
wasPort,
|
|
96
|
+
replicationIdentifier: `was-sync:${remoteStore.serverUrl}:${remoteStore.spaceId}:${id}`,
|
|
97
|
+
...(batchSize !== undefined && { batchSize }),
|
|
98
|
+
...(retryMs !== undefined && { retryTime: retryMs })
|
|
99
|
+
});
|
|
100
|
+
const subscriptions = [
|
|
101
|
+
state.active$.subscribe(active => {
|
|
102
|
+
setStatus(id, active ? 'syncing' : 'synced');
|
|
103
|
+
}),
|
|
104
|
+
state.error$.subscribe(err => {
|
|
105
|
+
console.error(`Sync error for "${id}":`, err);
|
|
106
|
+
setStatus(id, 'error');
|
|
107
|
+
if (onAuthError && isAuthError(err)) {
|
|
108
|
+
onAuthError();
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
];
|
|
112
|
+
if (onRemoteChange) {
|
|
113
|
+
subscriptions.push(localStore
|
|
114
|
+
.rxCollection(key)
|
|
115
|
+
.$.subscribe(event => onRemoteChange(key, event)));
|
|
116
|
+
}
|
|
117
|
+
this._replications.push({ state, subscriptions });
|
|
118
|
+
}
|
|
119
|
+
this._onlineHandler = () => this.reSync();
|
|
120
|
+
window.addEventListener('online', this._onlineHandler);
|
|
121
|
+
// The pull side has no server-side live stream, so an already-open session
|
|
122
|
+
// would otherwise never see another device's edits. A low-frequency
|
|
123
|
+
// periodic reSync polls the changes feed so multi-device edits converge
|
|
124
|
+
// live (the change subscriptions above then patch the stores).
|
|
125
|
+
if (pollMs > 0) {
|
|
126
|
+
this._pollTimer = setInterval(() => this.reSync(), pollMs);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
console.error('Failed to start sync controller:', err);
|
|
131
|
+
await this.stop();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Triggers an immediate replication cycle on every running collection, rather
|
|
136
|
+
* than waiting for RxDB's next scheduled tick. Fire-and-forget.
|
|
137
|
+
*
|
|
138
|
+
* @returns {void}
|
|
139
|
+
*/
|
|
140
|
+
reSync() {
|
|
141
|
+
for (const { state } of this._replications) {
|
|
142
|
+
state.reSync();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Stops replication and releases resources (the database is owned by the
|
|
147
|
+
* caller). Idempotent.
|
|
148
|
+
*
|
|
149
|
+
* @returns {Promise<void>}
|
|
150
|
+
*/
|
|
151
|
+
async stop() {
|
|
152
|
+
if (this._onlineHandler) {
|
|
153
|
+
window.removeEventListener('online', this._onlineHandler);
|
|
154
|
+
this._onlineHandler = undefined;
|
|
155
|
+
}
|
|
156
|
+
if (this._pollTimer) {
|
|
157
|
+
clearInterval(this._pollTimer);
|
|
158
|
+
this._pollTimer = undefined;
|
|
159
|
+
}
|
|
160
|
+
for (const { state, subscriptions } of this._replications) {
|
|
161
|
+
for (const subscription of subscriptions) {
|
|
162
|
+
subscription.unsubscribe();
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
await state.cancel();
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
console.error('Error cancelling replication:', err);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
this._replications = [];
|
|
172
|
+
useSyncStatusStore.getState().reset();
|
|
173
|
+
this._started = false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Builds a fresh {@link SyncController} bound to the collection registry and
|
|
178
|
+
* sync tuning. Prefer one controller per session (started once, stopped on
|
|
179
|
+
* logout) over a module-level singleton.
|
|
180
|
+
*
|
|
181
|
+
* @param options {object}
|
|
182
|
+
* @param options.collections {WasCollectionConfig[]}
|
|
183
|
+
* @param [options.sync] {WasSyncConfig}
|
|
184
|
+
* @returns {SyncController}
|
|
185
|
+
*/
|
|
186
|
+
export function createSyncController({ collections, sync }) {
|
|
187
|
+
return new SyncController({ collections, ...(sync && { sync }) });
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=syncController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncController.js","sourceRoot":"","sources":["../../src/storage/syncController.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single collection's replication status:
|
|
3
|
+
* - `idle` -- configured but no cycle has run yet
|
|
4
|
+
* - `syncing` -- a pull/push cycle is in flight
|
|
5
|
+
* - `synced` -- last cycle completed without error
|
|
6
|
+
* - `error` -- last cycle failed (RxDB is backing off / will retry)
|
|
7
|
+
*/
|
|
8
|
+
export type SyncStatus = 'idle' | 'syncing' | 'synced' | 'error';
|
|
9
|
+
interface SyncStatusState {
|
|
10
|
+
/** Keyed by WAS collection id (e.g. `action-items`). */
|
|
11
|
+
statuses: Record<string, SyncStatus>;
|
|
12
|
+
setStatus: (collectionId: string, status: SyncStatus) => void;
|
|
13
|
+
reset: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare const useSyncStatusStore: import("zustand").UseBoundStore<import("zustand").StoreApi<SyncStatusState>>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=syncStatusStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncStatusStore.d.ts","sourceRoot":"","sources":["../../src/storage/syncStatusStore.ts"],"names":[],"mappings":"AAYA;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEhE,UAAU,eAAe;IACvB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACpC,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC7D,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,eAAO,MAAM,kBAAkB,8EAO5B,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Zustand store holding per-collection replication status, mirroring the WAS
|
|
6
|
+
* per-replica sync-status vocabulary. The sync controller writes to it off the
|
|
7
|
+
* RxDB replication `active$` / `error$` streams; UI (e.g. a header indicator or
|
|
8
|
+
* a settings page) reads from it. In-memory only, like the session -- cleared on
|
|
9
|
+
* logout.
|
|
10
|
+
*/
|
|
11
|
+
import { create } from 'zustand';
|
|
12
|
+
export const useSyncStatusStore = create()(set => ({
|
|
13
|
+
statuses: {},
|
|
14
|
+
setStatus: (collectionId, status) => set(state => ({
|
|
15
|
+
statuses: { ...state.statuses, [collectionId]: status }
|
|
16
|
+
})),
|
|
17
|
+
reset: () => set({ statuses: {} })
|
|
18
|
+
}));
|
|
19
|
+
//# sourceMappingURL=syncStatusStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncStatusStore.js","sourceRoot":"","sources":["../../src/storage/syncStatusStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAkBhC,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,EAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClE,QAAQ,EAAE,EAAE;IACZ,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,CAClC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACZ,QAAQ,EAAE,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE;KACxD,CAAC,CAAC;IACL,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;CACnC,CAAC,CAAC,CAAA"}
|