@verdant-web/store 3.10.0 → 3.11.1
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/dist/bundle/index.js +6 -6
- package/dist/bundle/index.js.map +4 -4
- package/dist/esm/client/ClientDescriptor.d.ts +1 -1
- package/dist/esm/entities/Entity.d.ts +4 -0
- package/dist/esm/entities/Entity.js +8 -0
- package/dist/esm/entities/Entity.js.map +1 -1
- package/dist/esm/entities/types.d.ts +3 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/sync/PresenceManager.d.ts +6 -0
- package/dist/esm/sync/PresenceManager.js +23 -1
- package/dist/esm/sync/PresenceManager.js.map +1 -1
- package/dist/esm/sync/Sync.d.ts +2 -1
- package/dist/esm/sync/Sync.js +5 -1
- package/dist/esm/sync/Sync.js.map +1 -1
- package/dist/esm/sync/background.d.ts +1 -0
- package/dist/esm/sync/background.js +31 -0
- package/dist/esm/sync/background.js.map +1 -0
- package/dist/esm/sync/cliSync.d.ts +10 -0
- package/dist/esm/sync/cliSync.js +13 -0
- package/dist/esm/sync/cliSync.js.map +1 -0
- package/dist/esm/sync/serviceWorker.d.ts +2 -0
- package/dist/esm/sync/serviceWorker.js +22 -0
- package/dist/esm/sync/serviceWorker.js.map +1 -0
- package/package.json +6 -2
- package/src/client/ClientDescriptor.ts +1 -1
- package/src/entities/Entity.ts +9 -0
- package/src/entities/types.ts +11 -1
- package/src/index.ts +2 -0
- package/src/sync/PresenceManager.ts +28 -1
- package/src/sync/Sync.ts +8 -0
- package/src/sync/background.ts +27 -0
- package/src/sync/cliSync.ts +18 -0
- package/src/sync/serviceWorker.ts +27 -0
|
@@ -172,7 +172,9 @@ export class PresenceManager<
|
|
|
172
172
|
this.emit('peerLeft', message.userId, lastPresence);
|
|
173
173
|
}
|
|
174
174
|
if (peersChanged) {
|
|
175
|
-
this
|
|
175
|
+
// important that this stays sorted to keep stable order, don't want
|
|
176
|
+
// peers swapping around.
|
|
177
|
+
this._peerIds = Array.from(peerIdsSet).sort();
|
|
176
178
|
this.emit('peersChanged', this._peers);
|
|
177
179
|
}
|
|
178
180
|
if (peersChanged || selfChanged) {
|
|
@@ -220,6 +222,17 @@ export class PresenceManager<
|
|
|
220
222
|
this.emit('change');
|
|
221
223
|
};
|
|
222
224
|
|
|
225
|
+
setFieldId = (fieldId: string | undefined, timestamp = Date.now()) => {
|
|
226
|
+
this._updateBatch.update({
|
|
227
|
+
items: [
|
|
228
|
+
{ internal: { lastFieldId: fieldId, lastFieldTimestamp: timestamp } },
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
this.self.internal.lastFieldId = fieldId;
|
|
232
|
+
this.emit('selfChanged', this.self);
|
|
233
|
+
this.emit('change');
|
|
234
|
+
};
|
|
235
|
+
|
|
223
236
|
/**
|
|
224
237
|
* Get all peers that are in the same view as the local user.
|
|
225
238
|
*/
|
|
@@ -236,4 +249,18 @@ export class PresenceManager<
|
|
|
236
249
|
)
|
|
237
250
|
);
|
|
238
251
|
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Get all peers that have interacted with the specified
|
|
255
|
+
* field most recently.
|
|
256
|
+
*/
|
|
257
|
+
getFieldPeers = (fieldId: string, expirationPeriod = 60 * 1000) => {
|
|
258
|
+
return this._peerIds
|
|
259
|
+
.map((id) => this._peers[id])
|
|
260
|
+
.filter(
|
|
261
|
+
(peer) =>
|
|
262
|
+
peer.internal.lastFieldId === fieldId &&
|
|
263
|
+
Date.now() - peer.internal.lastFieldTimestamp! < expirationPeriod,
|
|
264
|
+
);
|
|
265
|
+
};
|
|
239
266
|
}
|
package/src/sync/Sync.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
} from './ServerSyncEndpointProvider.js';
|
|
21
21
|
import { WebSocketSync } from './WebSocketSync.js';
|
|
22
22
|
import { Context } from '../context.js';
|
|
23
|
+
import { attemptToRegisterBackgroundSync } from './background.js';
|
|
23
24
|
|
|
24
25
|
type SyncEvents = {
|
|
25
26
|
onlineChange: (isOnline: boolean) => void;
|
|
@@ -184,6 +185,8 @@ export interface ServerSyncOptions<Profile = any, Presence = any>
|
|
|
184
185
|
* Not sure why you want to do this, but be careful.
|
|
185
186
|
*/
|
|
186
187
|
onOutgoingMessage?: (message: ClientMessage) => void;
|
|
188
|
+
|
|
189
|
+
EXPERIMENTAL_backgroundSync?: boolean;
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
export class ServerSync<Presence = any, Profile = any>
|
|
@@ -225,6 +228,7 @@ export class ServerSync<Presence = any, Profile = any>
|
|
|
225
228
|
defaultProfile,
|
|
226
229
|
useBroadcastChannel,
|
|
227
230
|
onOutgoingMessage,
|
|
231
|
+
EXPERIMENTAL_backgroundSync,
|
|
228
232
|
}: ServerSyncOptions<Profile, Presence>,
|
|
229
233
|
{
|
|
230
234
|
meta,
|
|
@@ -332,6 +336,10 @@ export class ServerSync<Presence = any, Profile = any>
|
|
|
332
336
|
if (autoStart) {
|
|
333
337
|
this.start();
|
|
334
338
|
}
|
|
339
|
+
|
|
340
|
+
if (EXPERIMENTAL_backgroundSync) {
|
|
341
|
+
attemptToRegisterBackgroundSync();
|
|
342
|
+
}
|
|
335
343
|
}
|
|
336
344
|
|
|
337
345
|
get canDoRealtime() {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export async function attemptToRegisterBackgroundSync() {
|
|
2
|
+
try {
|
|
3
|
+
const status = await navigator.permissions.query({
|
|
4
|
+
name: 'periodic-background-sync' as any,
|
|
5
|
+
});
|
|
6
|
+
if (status.state === 'granted') {
|
|
7
|
+
// Periodic background sync can be used.
|
|
8
|
+
const registration = await navigator.serviceWorker.ready;
|
|
9
|
+
if ('periodicSync' in registration) {
|
|
10
|
+
try {
|
|
11
|
+
await (registration.periodicSync as any).register('verdant-sync', {
|
|
12
|
+
// An interval of one day.
|
|
13
|
+
minInterval: 24 * 60 * 60 * 1000,
|
|
14
|
+
});
|
|
15
|
+
} catch (error) {
|
|
16
|
+
// Periodic background sync cannot be used.
|
|
17
|
+
console.warn('Failed to register background sync:', error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
// Periodic background sync cannot be used.
|
|
22
|
+
console.debug('Background sync permission is not granted:', status);
|
|
23
|
+
}
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error('Failed to initiate background sync:', error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function cliSync<Presence extends any = any>(
|
|
2
|
+
libraryId: string,
|
|
3
|
+
{
|
|
4
|
+
port = 3242,
|
|
5
|
+
initialPresence = {} as any,
|
|
6
|
+
}: { port?: number; initialPresence?: Presence } = {},
|
|
7
|
+
) {
|
|
8
|
+
let userId = localStorage.getItem('verdant-userId');
|
|
9
|
+
if (!userId) {
|
|
10
|
+
userId = `user-${Math.random().toString(36).slice(2)}`;
|
|
11
|
+
localStorage.setItem('verdant-userId', userId);
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
defaultProfile: { id: userId },
|
|
15
|
+
initialPresence,
|
|
16
|
+
authEndpoint: `http://localhost:${port}/auth/${libraryId}?userId=${userId}`,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ClientDescriptor } from '../client/ClientDescriptor.js';
|
|
2
|
+
|
|
3
|
+
export async function registerBackgroundSync(clientDesc: ClientDescriptor) {
|
|
4
|
+
self.addEventListener('periodicsync', (event: any) => {
|
|
5
|
+
if (event.tag === 'verdant-sync') {
|
|
6
|
+
// See the "Think before you sync" section for
|
|
7
|
+
// checks you could perform before syncing.
|
|
8
|
+
event.waitUntil(sync(clientDesc));
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function sync(clientDesc: ClientDescriptor) {
|
|
14
|
+
try {
|
|
15
|
+
const client = await clientDesc.open();
|
|
16
|
+
|
|
17
|
+
await client.sync.syncOnce();
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error('Failed to sync:', err);
|
|
20
|
+
if (err instanceof Error) {
|
|
21
|
+
localStorage.setItem(
|
|
22
|
+
'backgroundSyncError',
|
|
23
|
+
`${err.name}: ${err.message}`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|