@arkade-os/sdk 0.4.0-next.3 → 0.4.0-next.4
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.
|
@@ -4,15 +4,111 @@ exports.__resetServiceWorkerManager = void 0;
|
|
|
4
4
|
exports.setupServiceWorkerOnce = setupServiceWorkerOnce;
|
|
5
5
|
exports.getActiveServiceWorker = getActiveServiceWorker;
|
|
6
6
|
const registrations = new Map();
|
|
7
|
+
let handshakes = new WeakSet();
|
|
7
8
|
function ensureServiceWorkerSupport() {
|
|
8
9
|
if (!("serviceWorker" in navigator)) {
|
|
9
10
|
throw new Error("Service workers are not supported in this browser");
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
|
-
function
|
|
13
|
+
function debugLog(debug, ...args) {
|
|
14
|
+
if (debug) {
|
|
15
|
+
// eslint-disable-next-line no-console
|
|
16
|
+
console.debug(...args);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function normalizeOptions(pathOrOptions) {
|
|
20
|
+
if (typeof pathOrOptions === "string") {
|
|
21
|
+
return {
|
|
22
|
+
path: pathOrOptions,
|
|
23
|
+
updateViaCache: "none",
|
|
24
|
+
autoReload: true,
|
|
25
|
+
debug: false,
|
|
26
|
+
activationTimeoutMs: 10000,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
path: pathOrOptions.path,
|
|
31
|
+
updateViaCache: pathOrOptions.updateViaCache ?? "none",
|
|
32
|
+
autoReload: pathOrOptions.autoReload ?? true,
|
|
33
|
+
onNeedRefresh: pathOrOptions.onNeedRefresh,
|
|
34
|
+
onUpdated: pathOrOptions.onUpdated,
|
|
35
|
+
debug: pathOrOptions.debug ?? false,
|
|
36
|
+
activationTimeoutMs: pathOrOptions.activationTimeoutMs ?? 10000,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function sendSkipWaiting(worker, debug) {
|
|
40
|
+
if (!worker)
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
worker.postMessage({ type: "SKIP_WAITING" });
|
|
44
|
+
debugLog(debug, "Sent SKIP_WAITING to waiting service worker");
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.warn("Failed to post SKIP_WAITING to service worker", error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function attachUpdateHandlers(registration, options) {
|
|
51
|
+
// Guard: only the first caller per registration attaches handlers.
|
|
52
|
+
// Subsequent calls with different options are silently ignored.
|
|
53
|
+
if (handshakes.has(registration))
|
|
54
|
+
return;
|
|
55
|
+
handshakes.add(registration);
|
|
56
|
+
const { autoReload, onNeedRefresh, onUpdated, activationTimeoutMs, debug } = options;
|
|
57
|
+
let reloadTriggered = false;
|
|
58
|
+
const maybeReload = () => {
|
|
59
|
+
if (reloadTriggered)
|
|
60
|
+
return;
|
|
61
|
+
reloadTriggered = true;
|
|
62
|
+
debugLog(debug, "Service worker controller change detected");
|
|
63
|
+
onUpdated?.();
|
|
64
|
+
if (autoReload &&
|
|
65
|
+
typeof window !== "undefined" &&
|
|
66
|
+
typeof window.location?.reload === "function") {
|
|
67
|
+
window.location.reload();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const handleWaiting = (worker) => {
|
|
71
|
+
if (!worker)
|
|
72
|
+
return;
|
|
73
|
+
onNeedRefresh?.();
|
|
74
|
+
sendSkipWaiting(worker, debug);
|
|
75
|
+
if (activationTimeoutMs > 0 && typeof window !== "undefined") {
|
|
76
|
+
window.setTimeout(() => {
|
|
77
|
+
if (registration.waiting) {
|
|
78
|
+
debugLog(debug, "Waiting worker still pending; re-sending SKIP_WAITING");
|
|
79
|
+
sendSkipWaiting(registration.waiting, debug);
|
|
80
|
+
registration
|
|
81
|
+
.update()
|
|
82
|
+
.catch(() => debugLog(debug, "Service worker update retry failed (timeout path)"));
|
|
83
|
+
}
|
|
84
|
+
}, activationTimeoutMs);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
// Handle an already waiting worker at startup.
|
|
88
|
+
if (registration.waiting) {
|
|
89
|
+
handleWaiting(registration.waiting);
|
|
90
|
+
}
|
|
91
|
+
// Listen for newly installed workers becoming waiting.
|
|
92
|
+
registration.addEventListener("updatefound", () => {
|
|
93
|
+
const installing = registration.installing;
|
|
94
|
+
if (!installing)
|
|
95
|
+
return;
|
|
96
|
+
installing.addEventListener("statechange", () => {
|
|
97
|
+
if (installing.state === "installed") {
|
|
98
|
+
handleWaiting(registration.waiting);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// Reload (or callback) once the new controller takes over.
|
|
103
|
+
navigator.serviceWorker.addEventListener("controllerchange", maybeReload, {
|
|
104
|
+
once: true,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function registerOnce(options) {
|
|
108
|
+
const { path, updateViaCache } = options;
|
|
13
109
|
if (!registrations.has(path)) {
|
|
14
110
|
const registrationPromise = navigator.serviceWorker
|
|
15
|
-
.register(path)
|
|
111
|
+
.register(path, { updateViaCache })
|
|
16
112
|
.then(async (registration) => {
|
|
17
113
|
try {
|
|
18
114
|
await registration.update();
|
|
@@ -29,18 +125,23 @@ function registerOnce(path) {
|
|
|
29
125
|
});
|
|
30
126
|
registrations.set(path, registrationPromise);
|
|
31
127
|
}
|
|
32
|
-
return registrations.get(path)
|
|
128
|
+
return registrations.get(path).then((registration) => {
|
|
129
|
+
attachUpdateHandlers(registration, options);
|
|
130
|
+
return registration;
|
|
131
|
+
});
|
|
33
132
|
}
|
|
34
133
|
/**
|
|
35
|
-
* Registers a service worker for the given path only once
|
|
36
|
-
*
|
|
134
|
+
* Registers a service worker for the given path only once, attaches an
|
|
135
|
+
* update/activation handshake (SKIP_WAITING + controllerchange reload), and
|
|
136
|
+
* caches the registration promise for subsequent calls.
|
|
37
137
|
*
|
|
38
|
-
* @param
|
|
138
|
+
* @param pathOrOptions - Service worker script path or a configuration object.
|
|
39
139
|
* @throws if service workers are not supported or registration fails.
|
|
40
140
|
*/
|
|
41
|
-
async function setupServiceWorkerOnce(
|
|
141
|
+
async function setupServiceWorkerOnce(pathOrOptions) {
|
|
42
142
|
ensureServiceWorkerSupport();
|
|
43
|
-
|
|
143
|
+
const options = normalizeOptions(pathOrOptions);
|
|
144
|
+
return registerOnce(options);
|
|
44
145
|
}
|
|
45
146
|
/**
|
|
46
147
|
* Returns an active service worker instance, optionally ensuring a specific
|
|
@@ -51,9 +152,8 @@ async function setupServiceWorkerOnce(path) {
|
|
|
51
152
|
*/
|
|
52
153
|
async function getActiveServiceWorker(path) {
|
|
53
154
|
ensureServiceWorkerSupport();
|
|
54
|
-
// Avoid mixing registrations when a specific script path is provided.
|
|
55
155
|
const registration = path
|
|
56
|
-
? await registerOnce(path)
|
|
156
|
+
? await registerOnce(normalizeOptions(path))
|
|
57
157
|
: await navigator.serviceWorker.ready;
|
|
58
158
|
let serviceWorker = registration.active ||
|
|
59
159
|
registration.waiting ||
|
|
@@ -78,5 +178,6 @@ async function getActiveServiceWorker(path) {
|
|
|
78
178
|
*/
|
|
79
179
|
const __resetServiceWorkerManager = () => {
|
|
80
180
|
registrations.clear();
|
|
181
|
+
handshakes = new WeakSet();
|
|
81
182
|
};
|
|
82
183
|
exports.__resetServiceWorkerManager = __resetServiceWorkerManager;
|
|
@@ -1,13 +1,109 @@
|
|
|
1
1
|
const registrations = new Map();
|
|
2
|
+
let handshakes = new WeakSet();
|
|
2
3
|
function ensureServiceWorkerSupport() {
|
|
3
4
|
if (!("serviceWorker" in navigator)) {
|
|
4
5
|
throw new Error("Service workers are not supported in this browser");
|
|
5
6
|
}
|
|
6
7
|
}
|
|
7
|
-
function
|
|
8
|
+
function debugLog(debug, ...args) {
|
|
9
|
+
if (debug) {
|
|
10
|
+
// eslint-disable-next-line no-console
|
|
11
|
+
console.debug(...args);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function normalizeOptions(pathOrOptions) {
|
|
15
|
+
if (typeof pathOrOptions === "string") {
|
|
16
|
+
return {
|
|
17
|
+
path: pathOrOptions,
|
|
18
|
+
updateViaCache: "none",
|
|
19
|
+
autoReload: true,
|
|
20
|
+
debug: false,
|
|
21
|
+
activationTimeoutMs: 10000,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
path: pathOrOptions.path,
|
|
26
|
+
updateViaCache: pathOrOptions.updateViaCache ?? "none",
|
|
27
|
+
autoReload: pathOrOptions.autoReload ?? true,
|
|
28
|
+
onNeedRefresh: pathOrOptions.onNeedRefresh,
|
|
29
|
+
onUpdated: pathOrOptions.onUpdated,
|
|
30
|
+
debug: pathOrOptions.debug ?? false,
|
|
31
|
+
activationTimeoutMs: pathOrOptions.activationTimeoutMs ?? 10000,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function sendSkipWaiting(worker, debug) {
|
|
35
|
+
if (!worker)
|
|
36
|
+
return;
|
|
37
|
+
try {
|
|
38
|
+
worker.postMessage({ type: "SKIP_WAITING" });
|
|
39
|
+
debugLog(debug, "Sent SKIP_WAITING to waiting service worker");
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.warn("Failed to post SKIP_WAITING to service worker", error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function attachUpdateHandlers(registration, options) {
|
|
46
|
+
// Guard: only the first caller per registration attaches handlers.
|
|
47
|
+
// Subsequent calls with different options are silently ignored.
|
|
48
|
+
if (handshakes.has(registration))
|
|
49
|
+
return;
|
|
50
|
+
handshakes.add(registration);
|
|
51
|
+
const { autoReload, onNeedRefresh, onUpdated, activationTimeoutMs, debug } = options;
|
|
52
|
+
let reloadTriggered = false;
|
|
53
|
+
const maybeReload = () => {
|
|
54
|
+
if (reloadTriggered)
|
|
55
|
+
return;
|
|
56
|
+
reloadTriggered = true;
|
|
57
|
+
debugLog(debug, "Service worker controller change detected");
|
|
58
|
+
onUpdated?.();
|
|
59
|
+
if (autoReload &&
|
|
60
|
+
typeof window !== "undefined" &&
|
|
61
|
+
typeof window.location?.reload === "function") {
|
|
62
|
+
window.location.reload();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const handleWaiting = (worker) => {
|
|
66
|
+
if (!worker)
|
|
67
|
+
return;
|
|
68
|
+
onNeedRefresh?.();
|
|
69
|
+
sendSkipWaiting(worker, debug);
|
|
70
|
+
if (activationTimeoutMs > 0 && typeof window !== "undefined") {
|
|
71
|
+
window.setTimeout(() => {
|
|
72
|
+
if (registration.waiting) {
|
|
73
|
+
debugLog(debug, "Waiting worker still pending; re-sending SKIP_WAITING");
|
|
74
|
+
sendSkipWaiting(registration.waiting, debug);
|
|
75
|
+
registration
|
|
76
|
+
.update()
|
|
77
|
+
.catch(() => debugLog(debug, "Service worker update retry failed (timeout path)"));
|
|
78
|
+
}
|
|
79
|
+
}, activationTimeoutMs);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
// Handle an already waiting worker at startup.
|
|
83
|
+
if (registration.waiting) {
|
|
84
|
+
handleWaiting(registration.waiting);
|
|
85
|
+
}
|
|
86
|
+
// Listen for newly installed workers becoming waiting.
|
|
87
|
+
registration.addEventListener("updatefound", () => {
|
|
88
|
+
const installing = registration.installing;
|
|
89
|
+
if (!installing)
|
|
90
|
+
return;
|
|
91
|
+
installing.addEventListener("statechange", () => {
|
|
92
|
+
if (installing.state === "installed") {
|
|
93
|
+
handleWaiting(registration.waiting);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
// Reload (or callback) once the new controller takes over.
|
|
98
|
+
navigator.serviceWorker.addEventListener("controllerchange", maybeReload, {
|
|
99
|
+
once: true,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function registerOnce(options) {
|
|
103
|
+
const { path, updateViaCache } = options;
|
|
8
104
|
if (!registrations.has(path)) {
|
|
9
105
|
const registrationPromise = navigator.serviceWorker
|
|
10
|
-
.register(path)
|
|
106
|
+
.register(path, { updateViaCache })
|
|
11
107
|
.then(async (registration) => {
|
|
12
108
|
try {
|
|
13
109
|
await registration.update();
|
|
@@ -24,18 +120,23 @@ function registerOnce(path) {
|
|
|
24
120
|
});
|
|
25
121
|
registrations.set(path, registrationPromise);
|
|
26
122
|
}
|
|
27
|
-
return registrations.get(path)
|
|
123
|
+
return registrations.get(path).then((registration) => {
|
|
124
|
+
attachUpdateHandlers(registration, options);
|
|
125
|
+
return registration;
|
|
126
|
+
});
|
|
28
127
|
}
|
|
29
128
|
/**
|
|
30
|
-
* Registers a service worker for the given path only once
|
|
31
|
-
*
|
|
129
|
+
* Registers a service worker for the given path only once, attaches an
|
|
130
|
+
* update/activation handshake (SKIP_WAITING + controllerchange reload), and
|
|
131
|
+
* caches the registration promise for subsequent calls.
|
|
32
132
|
*
|
|
33
|
-
* @param
|
|
133
|
+
* @param pathOrOptions - Service worker script path or a configuration object.
|
|
34
134
|
* @throws if service workers are not supported or registration fails.
|
|
35
135
|
*/
|
|
36
|
-
export async function setupServiceWorkerOnce(
|
|
136
|
+
export async function setupServiceWorkerOnce(pathOrOptions) {
|
|
37
137
|
ensureServiceWorkerSupport();
|
|
38
|
-
|
|
138
|
+
const options = normalizeOptions(pathOrOptions);
|
|
139
|
+
return registerOnce(options);
|
|
39
140
|
}
|
|
40
141
|
/**
|
|
41
142
|
* Returns an active service worker instance, optionally ensuring a specific
|
|
@@ -46,9 +147,8 @@ export async function setupServiceWorkerOnce(path) {
|
|
|
46
147
|
*/
|
|
47
148
|
export async function getActiveServiceWorker(path) {
|
|
48
149
|
ensureServiceWorkerSupport();
|
|
49
|
-
// Avoid mixing registrations when a specific script path is provided.
|
|
50
150
|
const registration = path
|
|
51
|
-
? await registerOnce(path)
|
|
151
|
+
? await registerOnce(normalizeOptions(path))
|
|
52
152
|
: await navigator.serviceWorker.ready;
|
|
53
153
|
let serviceWorker = registration.active ||
|
|
54
154
|
registration.waiting ||
|
|
@@ -73,4 +173,5 @@ export async function getActiveServiceWorker(path) {
|
|
|
73
173
|
*/
|
|
74
174
|
export const __resetServiceWorkerManager = () => {
|
|
75
175
|
registrations.clear();
|
|
176
|
+
handshakes = new WeakSet();
|
|
76
177
|
};
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
type SetupServiceWorkerOptions = {
|
|
2
|
+
path: string;
|
|
3
|
+
updateViaCache?: ServiceWorkerUpdateViaCache;
|
|
4
|
+
autoReload?: boolean;
|
|
5
|
+
onNeedRefresh?: () => void;
|
|
6
|
+
onUpdated?: () => void;
|
|
7
|
+
debug?: boolean;
|
|
8
|
+
activationTimeoutMs?: number;
|
|
9
|
+
};
|
|
1
10
|
/**
|
|
2
|
-
* Registers a service worker for the given path only once
|
|
3
|
-
*
|
|
11
|
+
* Registers a service worker for the given path only once, attaches an
|
|
12
|
+
* update/activation handshake (SKIP_WAITING + controllerchange reload), and
|
|
13
|
+
* caches the registration promise for subsequent calls.
|
|
4
14
|
*
|
|
5
|
-
* @param
|
|
15
|
+
* @param pathOrOptions - Service worker script path or a configuration object.
|
|
6
16
|
* @throws if service workers are not supported or registration fails.
|
|
7
17
|
*/
|
|
8
|
-
export declare function setupServiceWorkerOnce(
|
|
18
|
+
export declare function setupServiceWorkerOnce(pathOrOptions: string | SetupServiceWorkerOptions): Promise<ServiceWorkerRegistration>;
|
|
9
19
|
/**
|
|
10
20
|
* Returns an active service worker instance, optionally ensuring a specific
|
|
11
21
|
* script path is registered before resolving.
|
|
@@ -19,3 +29,4 @@ export declare function getActiveServiceWorker(path?: string): Promise<ServiceWo
|
|
|
19
29
|
* Intended for tests to reset state between runs.
|
|
20
30
|
*/
|
|
21
31
|
export declare const __resetServiceWorkerManager: () => void;
|
|
32
|
+
export {};
|