@vouchington/utils 0.7.4 → 0.7.5
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
|
@@ -36,6 +36,10 @@ import { createScopeCatalog } from '@vouchington/utils/scopes'
|
|
|
36
36
|
request/acknowledge round trip with an injected message type and acceptance check, and a named
|
|
37
37
|
Web Lock wrapper. Request actions, expected state transitions, and the bind/clear/reconcile
|
|
38
38
|
sequence stay in the application.
|
|
39
|
+
- `service-worker-activation`: `waitForActiveServiceWorker(registration, options)` resolves when
|
|
40
|
+
the registration has an active worker. An installing or waiting worker is preferred over
|
|
41
|
+
`serviceWorker.ready`. `timeoutMs` bounds the wait; omit it to wait without a deadline. The
|
|
42
|
+
caller supplies the timeout and may inject `serviceWorker` (default `navigator.serviceWorker`).
|
|
39
43
|
- `json-ld`: schema.org JSON-LD builders (organization, website, navigation, collection, thing,
|
|
40
44
|
comparison, item list, breadcrumb, profile) and script-safe serialization. Callers inject the
|
|
41
45
|
absolute-URL resolver and site name; product schemas such as posts stay in the application.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class ServiceWorkerActivationTimeoutError extends Error {
|
|
2
|
+
constructor();
|
|
3
|
+
}
|
|
4
|
+
export interface WaitForActiveServiceWorkerOptions {
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
/** Defaults to navigator.serviceWorker; injectable for tests. */
|
|
7
|
+
serviceWorker?: ServiceWorkerContainer;
|
|
8
|
+
}
|
|
9
|
+
declare global {
|
|
10
|
+
interface ServiceWorker extends EventTarget {
|
|
11
|
+
readonly state: 'parsed' | 'installing' | 'installed' | 'activating' | 'activated' | 'redundant';
|
|
12
|
+
}
|
|
13
|
+
interface ServiceWorkerRegistration {
|
|
14
|
+
readonly active: ServiceWorker | null;
|
|
15
|
+
readonly installing: ServiceWorker | null;
|
|
16
|
+
readonly waiting: ServiceWorker | null;
|
|
17
|
+
}
|
|
18
|
+
interface ServiceWorkerContainer {
|
|
19
|
+
readonly ready: Promise<ServiceWorkerRegistration>;
|
|
20
|
+
}
|
|
21
|
+
interface Navigator {
|
|
22
|
+
readonly serviceWorker: ServiceWorkerContainer;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Resolves when `registration` has an active worker. Pass `timeoutMs` to bound the wait. */
|
|
26
|
+
export declare function waitForActiveServiceWorker(registration: ServiceWorkerRegistration, options?: WaitForActiveServiceWorkerOptions): Promise<ServiceWorkerRegistration>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export class ServiceWorkerActivationTimeoutError extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super('Timed out waiting for the service worker to activate.');
|
|
4
|
+
this.name = 'ServiceWorkerActivationTimeoutError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
/** Resolves when `registration` has an active worker. Pass `timeoutMs` to bound the wait. */
|
|
8
|
+
export function waitForActiveServiceWorker(registration, options = {}) {
|
|
9
|
+
const replacement = registration.installing ?? registration.waiting;
|
|
10
|
+
if (!replacement) {
|
|
11
|
+
if (registration.active)
|
|
12
|
+
return Promise.resolve(registration);
|
|
13
|
+
const container = options.serviceWorker ?? navigator.serviceWorker;
|
|
14
|
+
return waitWithOptionalTimeout(container.ready, options.timeoutMs);
|
|
15
|
+
}
|
|
16
|
+
if (replacement.state === 'activated')
|
|
17
|
+
return Promise.resolve(registration);
|
|
18
|
+
return waitForReplacement(registration, replacement, options.timeoutMs);
|
|
19
|
+
}
|
|
20
|
+
function waitForReplacement(registration, worker, timeoutMs) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const timeout = timeoutMs === undefined
|
|
23
|
+
? undefined
|
|
24
|
+
: setTimeout(() => finish(() => reject(new ServiceWorkerActivationTimeoutError())), timeoutMs);
|
|
25
|
+
function finish(settle) {
|
|
26
|
+
worker.removeEventListener('statechange', onStateChange);
|
|
27
|
+
if (timeout !== undefined)
|
|
28
|
+
clearTimeout(timeout);
|
|
29
|
+
settle();
|
|
30
|
+
}
|
|
31
|
+
function onStateChange() {
|
|
32
|
+
if (worker.state === 'activated')
|
|
33
|
+
finish(() => resolve(registration));
|
|
34
|
+
else if (worker.state === 'redundant') {
|
|
35
|
+
finish(() => reject(new Error('Service worker replacement became redundant.')));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
worker.addEventListener('statechange', onStateChange);
|
|
39
|
+
onStateChange();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function waitWithOptionalTimeout(ready, timeoutMs) {
|
|
43
|
+
if (timeoutMs === undefined)
|
|
44
|
+
return ready;
|
|
45
|
+
let timeout;
|
|
46
|
+
const timedOut = new Promise((_, reject) => {
|
|
47
|
+
timeout = setTimeout(() => reject(new ServiceWorkerActivationTimeoutError()), timeoutMs);
|
|
48
|
+
});
|
|
49
|
+
return Promise.race([ready, timedOut]).finally(() => clearTimeout(timeout));
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vouchington/utils",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Dependency-free runtime utilities for Vouchington-style applications.",
|
|
5
5
|
"homepage": "https://github.com/vouchington/vouchington-platform/tree/main/packages/utils#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -65,6 +65,11 @@
|
|
|
65
65
|
"import": "./dist/web-push-binding.mjs",
|
|
66
66
|
"default": "./dist/web-push-binding.mjs"
|
|
67
67
|
},
|
|
68
|
+
"./service-worker-activation": {
|
|
69
|
+
"types": "./dist/service-worker-activation.d.mts",
|
|
70
|
+
"import": "./dist/service-worker-activation.mjs",
|
|
71
|
+
"default": "./dist/service-worker-activation.mjs"
|
|
72
|
+
},
|
|
68
73
|
"./json-ld": {
|
|
69
74
|
"types": "./dist/json-ld.d.mts",
|
|
70
75
|
"import": "./dist/json-ld.mjs",
|