@b9g/platform 0.1.4 → 0.1.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 +94 -28
- package/package.json +44 -86
- package/src/config.d.ts +105 -0
- package/src/config.js +463 -0
- package/src/cookie-store.d.ts +80 -0
- package/src/cookie-store.js +231 -0
- package/src/index.d.ts +189 -9
- package/src/index.js +242 -49
- package/src/runtime.d.ts +426 -0
- package/src/runtime.js +997 -0
- package/src/single-threaded.d.ts +57 -0
- package/src/single-threaded.js +107 -0
- package/src/worker-pool.d.ts +22 -32
- package/src/worker-pool.js +214 -100
- package/src/adapter-registry.d.ts +0 -53
- package/src/adapter-registry.js +0 -71
- package/src/base-platform.d.ts +0 -49
- package/src/base-platform.js +0 -114
- package/src/detection.d.ts +0 -36
- package/src/detection.js +0 -126
- package/src/directory-storage.d.ts +0 -41
- package/src/directory-storage.js +0 -53
- package/src/filesystem.d.ts +0 -16
- package/src/filesystem.js +0 -20
- package/src/registry.d.ts +0 -31
- package/src/registry.js +0 -74
- package/src/service-worker.d.ts +0 -175
- package/src/service-worker.js +0 -245
- package/src/types.d.ts +0 -246
- package/src/types.js +0 -36
- package/src/utils.d.ts +0 -33
- package/src/utils.js +0 -139
- package/src/worker-web.js +0 -119
package/src/runtime.js
ADDED
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
/// <reference types="./runtime.d.ts" />
|
|
2
|
+
// src/runtime.ts
|
|
3
|
+
import { RequestCookieStore } from "./cookie-store.js";
|
|
4
|
+
import { AsyncContext } from "@b9g/async-context";
|
|
5
|
+
import { CustomBucketStorage } from "@b9g/filesystem";
|
|
6
|
+
import { CustomCacheStorage } from "@b9g/cache";
|
|
7
|
+
import { createBucketFactory, createCacheFactory } from "./config.js";
|
|
8
|
+
import { getLogger } from "@logtape/logtape";
|
|
9
|
+
if (import.meta.env && !import.meta.env.MODE && import.meta.env.NODE_ENV) {
|
|
10
|
+
import.meta.env.MODE = import.meta.env.NODE_ENV;
|
|
11
|
+
}
|
|
12
|
+
var cookieStoreStorage = new AsyncContext.Variable();
|
|
13
|
+
function promiseWithTimeout(promise, timeoutMs, errorMessage) {
|
|
14
|
+
return Promise.race([
|
|
15
|
+
promise,
|
|
16
|
+
new Promise(
|
|
17
|
+
(_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs)
|
|
18
|
+
)
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
var kEndDispatchPhase = Symbol.for("shovel.endDispatchPhase");
|
|
22
|
+
var kCanExtend = Symbol.for("shovel.canExtend");
|
|
23
|
+
var ExtendableEvent = class extends Event {
|
|
24
|
+
#promises;
|
|
25
|
+
#dispatchPhase;
|
|
26
|
+
#pendingCount;
|
|
27
|
+
constructor(type, eventInitDict) {
|
|
28
|
+
super(type, eventInitDict);
|
|
29
|
+
this.#promises = [];
|
|
30
|
+
this.#dispatchPhase = true;
|
|
31
|
+
this.#pendingCount = 0;
|
|
32
|
+
}
|
|
33
|
+
waitUntil(promise) {
|
|
34
|
+
if (!this.#dispatchPhase && this.#pendingCount === 0) {
|
|
35
|
+
throw new DOMException(
|
|
36
|
+
"waitUntil() must be called synchronously during event dispatch, or while there are pending promises from respondWith()/waitUntil()",
|
|
37
|
+
"InvalidStateError"
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
this.#pendingCount++;
|
|
41
|
+
const trackedPromise = promise.finally(() => {
|
|
42
|
+
this.#pendingCount--;
|
|
43
|
+
});
|
|
44
|
+
trackedPromise.catch(() => {
|
|
45
|
+
});
|
|
46
|
+
this.#promises.push(trackedPromise);
|
|
47
|
+
}
|
|
48
|
+
getPromises() {
|
|
49
|
+
return [...this.#promises];
|
|
50
|
+
}
|
|
51
|
+
/** @internal Called after synchronous dispatch completes */
|
|
52
|
+
[kEndDispatchPhase]() {
|
|
53
|
+
this.#dispatchPhase = false;
|
|
54
|
+
}
|
|
55
|
+
/** @internal Check if extensions are still allowed */
|
|
56
|
+
[kCanExtend]() {
|
|
57
|
+
return this.#dispatchPhase || this.#pendingCount > 0;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var FetchEvent = class extends ExtendableEvent {
|
|
61
|
+
request;
|
|
62
|
+
cookieStore;
|
|
63
|
+
#responsePromise;
|
|
64
|
+
#responded;
|
|
65
|
+
constructor(request, eventInitDict) {
|
|
66
|
+
super("fetch", eventInitDict);
|
|
67
|
+
this.request = request;
|
|
68
|
+
this.cookieStore = new RequestCookieStore(request);
|
|
69
|
+
this.#responsePromise = null;
|
|
70
|
+
this.#responded = false;
|
|
71
|
+
}
|
|
72
|
+
respondWith(response) {
|
|
73
|
+
if (this.#responded) {
|
|
74
|
+
throw new Error("respondWith() already called");
|
|
75
|
+
}
|
|
76
|
+
if (!this[kCanExtend]()) {
|
|
77
|
+
throw new DOMException(
|
|
78
|
+
"respondWith() must be called synchronously during event dispatch",
|
|
79
|
+
"InvalidStateError"
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
this.#responded = true;
|
|
83
|
+
this.#responsePromise = Promise.resolve(response);
|
|
84
|
+
this.waitUntil(this.#responsePromise);
|
|
85
|
+
}
|
|
86
|
+
getResponse() {
|
|
87
|
+
return this.#responsePromise;
|
|
88
|
+
}
|
|
89
|
+
hasResponded() {
|
|
90
|
+
return this.#responded;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
var InstallEvent = class extends ExtendableEvent {
|
|
94
|
+
constructor(eventInitDict) {
|
|
95
|
+
super("install", eventInitDict);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var ActivateEvent = class extends ExtendableEvent {
|
|
99
|
+
constructor(eventInitDict) {
|
|
100
|
+
super("activate", eventInitDict);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var ShovelClient = class {
|
|
104
|
+
frameType;
|
|
105
|
+
id;
|
|
106
|
+
type;
|
|
107
|
+
url;
|
|
108
|
+
constructor(options) {
|
|
109
|
+
this.frameType = "none";
|
|
110
|
+
this.id = options.id;
|
|
111
|
+
this.url = options.url;
|
|
112
|
+
this.type = options.type || "worker";
|
|
113
|
+
}
|
|
114
|
+
// Implementation
|
|
115
|
+
postMessage(_message, _transferOrOptions) {
|
|
116
|
+
logger.warn(
|
|
117
|
+
"[ServiceWorker] Client.postMessage() not supported in server context"
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var ShovelWindowClient = class extends ShovelClient {
|
|
122
|
+
focused;
|
|
123
|
+
visibilityState;
|
|
124
|
+
constructor(options) {
|
|
125
|
+
super({ ...options, type: "window" });
|
|
126
|
+
this.focused = options.focused || false;
|
|
127
|
+
this.visibilityState = options.visibilityState || "hidden";
|
|
128
|
+
}
|
|
129
|
+
async focus() {
|
|
130
|
+
logger.warn(
|
|
131
|
+
"[ServiceWorker] WindowClient.focus() not supported in server context"
|
|
132
|
+
);
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
async navigate(_url) {
|
|
136
|
+
logger.warn(
|
|
137
|
+
"[ServiceWorker] WindowClient.navigate() not supported in server context"
|
|
138
|
+
);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
var ShovelClients = class {
|
|
143
|
+
async claim() {
|
|
144
|
+
}
|
|
145
|
+
async get(_id) {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
async matchAll(_options) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
async openWindow(_url) {
|
|
152
|
+
logger.warn(
|
|
153
|
+
"[ServiceWorker] Clients.openWindow() not supported in server context"
|
|
154
|
+
);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
var ExtendableMessageEvent = class extends ExtendableEvent {
|
|
159
|
+
data;
|
|
160
|
+
origin;
|
|
161
|
+
lastEventId;
|
|
162
|
+
source;
|
|
163
|
+
ports;
|
|
164
|
+
constructor(type, eventInitDict) {
|
|
165
|
+
super(type, eventInitDict);
|
|
166
|
+
this.data = eventInitDict?.data ?? null;
|
|
167
|
+
this.origin = eventInitDict?.origin ?? "";
|
|
168
|
+
this.lastEventId = eventInitDict?.lastEventId ?? "";
|
|
169
|
+
this.source = eventInitDict?.source ?? null;
|
|
170
|
+
this.ports = Object.freeze([...eventInitDict?.ports ?? []]);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
var ShovelServiceWorker = class extends EventTarget {
|
|
174
|
+
scriptURL;
|
|
175
|
+
state;
|
|
176
|
+
// Event handlers required by Web API
|
|
177
|
+
onstatechange;
|
|
178
|
+
onerror;
|
|
179
|
+
constructor(scriptURL, state = "parsed") {
|
|
180
|
+
super();
|
|
181
|
+
this.scriptURL = scriptURL;
|
|
182
|
+
this.state = state;
|
|
183
|
+
this.onstatechange = null;
|
|
184
|
+
this.onerror = null;
|
|
185
|
+
}
|
|
186
|
+
// Implementation
|
|
187
|
+
postMessage(_message, _transferOrOptions) {
|
|
188
|
+
logger.warn(
|
|
189
|
+
"[ServiceWorker] ServiceWorker.postMessage() not implemented in server context"
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
// Internal method to update state and dispatch statechange event
|
|
193
|
+
_setState(newState) {
|
|
194
|
+
if (this.state !== newState) {
|
|
195
|
+
this.state = newState;
|
|
196
|
+
this.dispatchEvent(new Event("statechange"));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Events: statechange, error
|
|
200
|
+
};
|
|
201
|
+
var ShovelNavigationPreloadManager = class {
|
|
202
|
+
async disable() {
|
|
203
|
+
}
|
|
204
|
+
async enable() {
|
|
205
|
+
}
|
|
206
|
+
async getState() {
|
|
207
|
+
return { enabled: false, headerValue: "" };
|
|
208
|
+
}
|
|
209
|
+
async setHeaderValue(_value) {
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var ShovelServiceWorkerRegistration = class extends EventTarget {
|
|
213
|
+
scope;
|
|
214
|
+
updateViaCache;
|
|
215
|
+
navigationPreload;
|
|
216
|
+
// ServiceWorker instances representing different lifecycle states
|
|
217
|
+
_serviceWorker;
|
|
218
|
+
// Web API properties (not supported in server context, but required by interface)
|
|
219
|
+
cookies;
|
|
220
|
+
pushManager;
|
|
221
|
+
onupdatefound;
|
|
222
|
+
constructor(scope2 = "/", scriptURL = "/") {
|
|
223
|
+
super();
|
|
224
|
+
this.scope = scope2;
|
|
225
|
+
this.updateViaCache = "imports";
|
|
226
|
+
this.navigationPreload = new ShovelNavigationPreloadManager();
|
|
227
|
+
this._serviceWorker = new ShovelServiceWorker(scriptURL, "parsed");
|
|
228
|
+
this.cookies = null;
|
|
229
|
+
this.pushManager = null;
|
|
230
|
+
this.onupdatefound = null;
|
|
231
|
+
}
|
|
232
|
+
// Standard ServiceWorkerRegistration properties
|
|
233
|
+
get active() {
|
|
234
|
+
return this._serviceWorker.state === "activated" ? this._serviceWorker : null;
|
|
235
|
+
}
|
|
236
|
+
get installing() {
|
|
237
|
+
return this._serviceWorker.state === "installing" ? this._serviceWorker : null;
|
|
238
|
+
}
|
|
239
|
+
get waiting() {
|
|
240
|
+
return this._serviceWorker.state === "installed" ? this._serviceWorker : null;
|
|
241
|
+
}
|
|
242
|
+
// Standard ServiceWorkerRegistration methods
|
|
243
|
+
async getNotifications(_options) {
|
|
244
|
+
return [];
|
|
245
|
+
}
|
|
246
|
+
async showNotification(_title, _options) {
|
|
247
|
+
logger.warn(
|
|
248
|
+
"[ServiceWorker] Notifications not supported in server context"
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
async sync() {
|
|
252
|
+
}
|
|
253
|
+
async unregister() {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
async update() {
|
|
257
|
+
return this;
|
|
258
|
+
}
|
|
259
|
+
// Shovel runtime extensions (non-standard but needed for platforms)
|
|
260
|
+
/**
|
|
261
|
+
* Install the ServiceWorker (Shovel extension)
|
|
262
|
+
*/
|
|
263
|
+
async install() {
|
|
264
|
+
if (this._serviceWorker.state !== "parsed")
|
|
265
|
+
return;
|
|
266
|
+
this._serviceWorker._setState("installing");
|
|
267
|
+
return new Promise((resolve, reject) => {
|
|
268
|
+
const event = new InstallEvent();
|
|
269
|
+
process.nextTick(() => {
|
|
270
|
+
try {
|
|
271
|
+
this.dispatchEvent(event);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
process.nextTick(() => {
|
|
274
|
+
throw error;
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
const promises = event.getPromises();
|
|
278
|
+
if (promises.length === 0) {
|
|
279
|
+
this._serviceWorker._setState("installed");
|
|
280
|
+
resolve();
|
|
281
|
+
} else {
|
|
282
|
+
promiseWithTimeout(
|
|
283
|
+
Promise.all(promises),
|
|
284
|
+
3e4,
|
|
285
|
+
"ServiceWorker install event timed out after 30s - waitUntil promises did not resolve"
|
|
286
|
+
).then(() => {
|
|
287
|
+
this._serviceWorker._setState("installed");
|
|
288
|
+
resolve();
|
|
289
|
+
}).catch(reject);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Activate the ServiceWorker (Shovel extension)
|
|
296
|
+
*/
|
|
297
|
+
async activate() {
|
|
298
|
+
if (this._serviceWorker.state !== "installed") {
|
|
299
|
+
throw new Error("ServiceWorker must be installed before activation");
|
|
300
|
+
}
|
|
301
|
+
this._serviceWorker._setState("activating");
|
|
302
|
+
return new Promise((resolve, reject) => {
|
|
303
|
+
const event = new ActivateEvent();
|
|
304
|
+
process.nextTick(() => {
|
|
305
|
+
try {
|
|
306
|
+
this.dispatchEvent(event);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
process.nextTick(() => {
|
|
309
|
+
throw error;
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
const promises = event.getPromises();
|
|
313
|
+
if (promises.length === 0) {
|
|
314
|
+
this._serviceWorker._setState("activated");
|
|
315
|
+
resolve();
|
|
316
|
+
} else {
|
|
317
|
+
promiseWithTimeout(
|
|
318
|
+
Promise.all(promises),
|
|
319
|
+
3e4,
|
|
320
|
+
"ServiceWorker activate event timed out after 30s - waitUntil promises did not resolve"
|
|
321
|
+
).then(() => {
|
|
322
|
+
this._serviceWorker._setState("activated");
|
|
323
|
+
resolve();
|
|
324
|
+
}).catch(reject);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Handle a fetch request (Shovel extension)
|
|
331
|
+
*/
|
|
332
|
+
async handleRequest(request) {
|
|
333
|
+
if (this._serviceWorker.state !== "activated") {
|
|
334
|
+
throw new Error("ServiceWorker not activated");
|
|
335
|
+
}
|
|
336
|
+
const event = new FetchEvent(request);
|
|
337
|
+
return cookieStoreStorage.run(event.cookieStore, async () => {
|
|
338
|
+
this.dispatchEvent(event);
|
|
339
|
+
event[kEndDispatchPhase]();
|
|
340
|
+
if (!event.hasResponded()) {
|
|
341
|
+
throw new Error(
|
|
342
|
+
"No response provided for fetch event. respondWith() must be called synchronously during event dispatch."
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
const response = await event.getResponse();
|
|
346
|
+
const promises = event.getPromises();
|
|
347
|
+
if (promises.length > 0) {
|
|
348
|
+
Promise.allSettled(promises).catch(logger.error);
|
|
349
|
+
}
|
|
350
|
+
if (event.cookieStore.hasChanges()) {
|
|
351
|
+
const setCookieHeaders = event.cookieStore.getSetCookieHeaders();
|
|
352
|
+
const headers = new Headers(response.headers);
|
|
353
|
+
for (const setCookie of setCookieHeaders) {
|
|
354
|
+
headers.append("Set-Cookie", setCookie);
|
|
355
|
+
}
|
|
356
|
+
return new Response(response.body, {
|
|
357
|
+
status: response.status,
|
|
358
|
+
statusText: response.statusText,
|
|
359
|
+
headers
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return response;
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Check if ready to handle requests (Shovel extension)
|
|
367
|
+
*/
|
|
368
|
+
get ready() {
|
|
369
|
+
return this._serviceWorker.state === "activated";
|
|
370
|
+
}
|
|
371
|
+
// Events: updatefound (standard), plus Shovel lifecycle events
|
|
372
|
+
};
|
|
373
|
+
var ShovelServiceWorkerContainer = class extends EventTarget {
|
|
374
|
+
#registrations;
|
|
375
|
+
controller;
|
|
376
|
+
ready;
|
|
377
|
+
// Event handlers required by Web API
|
|
378
|
+
oncontrollerchange;
|
|
379
|
+
onmessage;
|
|
380
|
+
onmessageerror;
|
|
381
|
+
constructor() {
|
|
382
|
+
super();
|
|
383
|
+
this.#registrations = /* @__PURE__ */ new Map();
|
|
384
|
+
this.controller = null;
|
|
385
|
+
this.oncontrollerchange = null;
|
|
386
|
+
this.onmessage = null;
|
|
387
|
+
this.onmessageerror = null;
|
|
388
|
+
const defaultRegistration = new ShovelServiceWorkerRegistration("/", "/");
|
|
389
|
+
this.#registrations.set("/", defaultRegistration);
|
|
390
|
+
this.ready = Promise.resolve(defaultRegistration);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Get registration for a specific scope
|
|
394
|
+
*/
|
|
395
|
+
async getRegistration(scope2 = "/") {
|
|
396
|
+
return this.#registrations.get(scope2);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get all registrations
|
|
400
|
+
*/
|
|
401
|
+
async getRegistrations() {
|
|
402
|
+
return Array.from(this.#registrations.values());
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Register a new ServiceWorker for a specific scope
|
|
406
|
+
*/
|
|
407
|
+
async register(scriptURL, options) {
|
|
408
|
+
const url = typeof scriptURL === "string" ? scriptURL : scriptURL.toString();
|
|
409
|
+
const scope2 = this.#normalizeScope(options?.scope || "/");
|
|
410
|
+
let registration2 = this.#registrations.get(scope2);
|
|
411
|
+
if (registration2) {
|
|
412
|
+
registration2._serviceWorker.scriptURL = url;
|
|
413
|
+
registration2._serviceWorker._setState("parsed");
|
|
414
|
+
} else {
|
|
415
|
+
registration2 = new ShovelServiceWorkerRegistration(scope2, url);
|
|
416
|
+
this.#registrations.set(scope2, registration2);
|
|
417
|
+
this.dispatchEvent(new Event("updatefound"));
|
|
418
|
+
}
|
|
419
|
+
return registration2;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Unregister a ServiceWorker registration
|
|
423
|
+
*/
|
|
424
|
+
async unregister(scope2) {
|
|
425
|
+
const registration2 = this.#registrations.get(scope2);
|
|
426
|
+
if (registration2) {
|
|
427
|
+
await registration2.unregister();
|
|
428
|
+
this.#registrations.delete(scope2);
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Route a request to the appropriate registration based on scope matching
|
|
435
|
+
*/
|
|
436
|
+
async handleRequest(request) {
|
|
437
|
+
const url = new URL(request.url);
|
|
438
|
+
const pathname = url.pathname;
|
|
439
|
+
const matchingScope = this.#findMatchingScope(pathname);
|
|
440
|
+
if (matchingScope) {
|
|
441
|
+
const registration2 = this.#registrations.get(matchingScope);
|
|
442
|
+
if (registration2 && registration2.ready) {
|
|
443
|
+
return await registration2.handleRequest(request);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Install and activate all registrations
|
|
450
|
+
*/
|
|
451
|
+
async installAll() {
|
|
452
|
+
const installations = Array.from(this.#registrations.values()).map(
|
|
453
|
+
async (registration2) => {
|
|
454
|
+
await registration2.install();
|
|
455
|
+
await registration2.activate();
|
|
456
|
+
}
|
|
457
|
+
);
|
|
458
|
+
await promiseWithTimeout(
|
|
459
|
+
Promise.all(installations),
|
|
460
|
+
65e3,
|
|
461
|
+
"ServiceWorker installAll timed out after 65s - some registrations failed to install/activate"
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Get list of all scopes
|
|
466
|
+
*/
|
|
467
|
+
getScopes() {
|
|
468
|
+
return Array.from(this.#registrations.keys());
|
|
469
|
+
}
|
|
470
|
+
startMessages() {
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Normalize scope to ensure it starts and ends correctly
|
|
474
|
+
*/
|
|
475
|
+
#normalizeScope(scope2) {
|
|
476
|
+
if (!scope2.startsWith("/")) {
|
|
477
|
+
scope2 = "/" + scope2;
|
|
478
|
+
}
|
|
479
|
+
if (scope2 !== "/" && !scope2.endsWith("/")) {
|
|
480
|
+
scope2 = scope2 + "/";
|
|
481
|
+
}
|
|
482
|
+
return scope2;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Find the most specific scope that matches a pathname
|
|
486
|
+
*/
|
|
487
|
+
#findMatchingScope(pathname) {
|
|
488
|
+
const scopes = Array.from(this.#registrations.keys());
|
|
489
|
+
scopes.sort((a, b) => b.length - a.length);
|
|
490
|
+
for (const scope2 of scopes) {
|
|
491
|
+
if (pathname.startsWith(scope2 === "/" ? "/" : scope2)) {
|
|
492
|
+
return scope2;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
// Events: controllerchange, message, messageerror, updatefound
|
|
498
|
+
};
|
|
499
|
+
var Notification = class extends EventTarget {
|
|
500
|
+
actions;
|
|
501
|
+
badge;
|
|
502
|
+
body;
|
|
503
|
+
data;
|
|
504
|
+
dir;
|
|
505
|
+
icon;
|
|
506
|
+
image;
|
|
507
|
+
lang;
|
|
508
|
+
renotify;
|
|
509
|
+
requireInteraction;
|
|
510
|
+
silent;
|
|
511
|
+
tag;
|
|
512
|
+
timestamp;
|
|
513
|
+
title;
|
|
514
|
+
vibrate;
|
|
515
|
+
// Event handlers required by Web API
|
|
516
|
+
onclick;
|
|
517
|
+
onclose;
|
|
518
|
+
onerror;
|
|
519
|
+
onshow;
|
|
520
|
+
static permission;
|
|
521
|
+
constructor(title, options = {}) {
|
|
522
|
+
super();
|
|
523
|
+
this.title = title;
|
|
524
|
+
this.actions = Object.freeze([...options.actions || []]);
|
|
525
|
+
this.badge = options.badge || "";
|
|
526
|
+
this.body = options.body || "";
|
|
527
|
+
this.data = options.data;
|
|
528
|
+
this.dir = options.dir || "auto";
|
|
529
|
+
this.icon = options.icon || "";
|
|
530
|
+
this.image = options.image || "";
|
|
531
|
+
this.lang = options.lang || "";
|
|
532
|
+
this.renotify = options.renotify || false;
|
|
533
|
+
this.requireInteraction = options.requireInteraction || false;
|
|
534
|
+
this.silent = options.silent || false;
|
|
535
|
+
this.tag = options.tag || "";
|
|
536
|
+
this.timestamp = options.timestamp || Date.now();
|
|
537
|
+
this.vibrate = Object.freeze([...options.vibrate || []]);
|
|
538
|
+
this.onclick = null;
|
|
539
|
+
this.onclose = null;
|
|
540
|
+
this.onerror = null;
|
|
541
|
+
this.onshow = null;
|
|
542
|
+
}
|
|
543
|
+
close() {
|
|
544
|
+
logger.warn(
|
|
545
|
+
"[ServiceWorker] Notification.close() not supported in server context"
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
static async requestPermission() {
|
|
549
|
+
return "denied";
|
|
550
|
+
}
|
|
551
|
+
// Events: click, close, error, show
|
|
552
|
+
};
|
|
553
|
+
Notification.permission = "denied";
|
|
554
|
+
var NotificationEvent = class extends ExtendableEvent {
|
|
555
|
+
action;
|
|
556
|
+
notification;
|
|
557
|
+
reply;
|
|
558
|
+
constructor(type, eventInitDict) {
|
|
559
|
+
super(type, eventInitDict);
|
|
560
|
+
this.action = eventInitDict.action ?? "";
|
|
561
|
+
this.notification = eventInitDict.notification;
|
|
562
|
+
this.reply = eventInitDict.reply ?? null;
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
var PushEvent = class extends ExtendableEvent {
|
|
566
|
+
data;
|
|
567
|
+
constructor(type, eventInitDict) {
|
|
568
|
+
super(type, eventInitDict);
|
|
569
|
+
this.data = eventInitDict?.data ?? null;
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
var ShovelPushMessageData = class {
|
|
573
|
+
constructor(_data) {
|
|
574
|
+
this._data = _data;
|
|
575
|
+
}
|
|
576
|
+
arrayBuffer() {
|
|
577
|
+
if (this._data instanceof ArrayBuffer) {
|
|
578
|
+
return this._data;
|
|
579
|
+
}
|
|
580
|
+
return new TextEncoder().encode(this._data).buffer;
|
|
581
|
+
}
|
|
582
|
+
blob() {
|
|
583
|
+
return new Blob([this.arrayBuffer()]);
|
|
584
|
+
}
|
|
585
|
+
bytes() {
|
|
586
|
+
return new Uint8Array(this.arrayBuffer());
|
|
587
|
+
}
|
|
588
|
+
json() {
|
|
589
|
+
return JSON.parse(this.text());
|
|
590
|
+
}
|
|
591
|
+
text() {
|
|
592
|
+
if (typeof this._data === "string") {
|
|
593
|
+
return this._data;
|
|
594
|
+
}
|
|
595
|
+
return new TextDecoder().decode(this._data);
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
var SyncEvent = class extends ExtendableEvent {
|
|
599
|
+
tag;
|
|
600
|
+
lastChance;
|
|
601
|
+
constructor(type, eventInitDict) {
|
|
602
|
+
super(type, eventInitDict);
|
|
603
|
+
this.tag = eventInitDict.tag;
|
|
604
|
+
this.lastChance = eventInitDict.lastChance ?? false;
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
var WorkerGlobalScope = class {
|
|
608
|
+
};
|
|
609
|
+
var DedicatedWorkerGlobalScope = class extends WorkerGlobalScope {
|
|
610
|
+
};
|
|
611
|
+
var ShovelGlobalScope = class {
|
|
612
|
+
// Self-reference (standard in ServiceWorkerGlobalScope)
|
|
613
|
+
// Type assertion: we provide a compatible subset of WorkerGlobalScope
|
|
614
|
+
self;
|
|
615
|
+
// ServiceWorker standard properties
|
|
616
|
+
// Our custom ServiceWorkerRegistration provides core functionality compatible with the Web API
|
|
617
|
+
registration;
|
|
618
|
+
// Storage APIs
|
|
619
|
+
caches;
|
|
620
|
+
buckets;
|
|
621
|
+
// Clients API
|
|
622
|
+
// Our custom Clients implementation provides core functionality compatible with the Web API
|
|
623
|
+
clients;
|
|
624
|
+
// Shovel-specific development features
|
|
625
|
+
#isDevelopment;
|
|
626
|
+
// Web API required properties
|
|
627
|
+
// Note: Using RequestCookieStore but typing as any for flexibility with global CookieStore type
|
|
628
|
+
// cookieStore is retrieved from AsyncContext for per-request isolation
|
|
629
|
+
get cookieStore() {
|
|
630
|
+
return cookieStoreStorage.get();
|
|
631
|
+
}
|
|
632
|
+
serviceWorker;
|
|
633
|
+
// WorkerGlobalScope required properties (stubs for server context)
|
|
634
|
+
location;
|
|
635
|
+
navigator;
|
|
636
|
+
fonts;
|
|
637
|
+
indexedDB;
|
|
638
|
+
isSecureContext;
|
|
639
|
+
crossOriginIsolated;
|
|
640
|
+
origin;
|
|
641
|
+
performance;
|
|
642
|
+
crypto;
|
|
643
|
+
// WorkerGlobalScope methods (stubs for server context)
|
|
644
|
+
importScripts(..._urls) {
|
|
645
|
+
logger.warn(
|
|
646
|
+
"[ServiceWorker] importScripts() not supported in server context"
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
atob(data) {
|
|
650
|
+
return globalThis.atob(data);
|
|
651
|
+
}
|
|
652
|
+
btoa(data) {
|
|
653
|
+
return globalThis.btoa(data);
|
|
654
|
+
}
|
|
655
|
+
clearInterval(id) {
|
|
656
|
+
globalThis.clearInterval(id);
|
|
657
|
+
}
|
|
658
|
+
clearTimeout(id) {
|
|
659
|
+
globalThis.clearTimeout(id);
|
|
660
|
+
}
|
|
661
|
+
createImageBitmap(..._args) {
|
|
662
|
+
throw new Error(
|
|
663
|
+
"[ServiceWorker] createImageBitmap() not supported in server context"
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
fetch(input, init) {
|
|
667
|
+
return globalThis.fetch(input, init);
|
|
668
|
+
}
|
|
669
|
+
queueMicrotask(callback) {
|
|
670
|
+
globalThis.queueMicrotask(callback);
|
|
671
|
+
}
|
|
672
|
+
reportError(e) {
|
|
673
|
+
logger.error("[ServiceWorker] reportError:", e);
|
|
674
|
+
}
|
|
675
|
+
setInterval(handler, timeout, ...args) {
|
|
676
|
+
return globalThis.setInterval(handler, timeout, ...args);
|
|
677
|
+
}
|
|
678
|
+
setTimeout(handler, timeout, ...args) {
|
|
679
|
+
return globalThis.setTimeout(handler, timeout, ...args);
|
|
680
|
+
}
|
|
681
|
+
structuredClone(value, options) {
|
|
682
|
+
return globalThis.structuredClone(value, options);
|
|
683
|
+
}
|
|
684
|
+
// Event handlers required by ServiceWorkerGlobalScope
|
|
685
|
+
// Use Web API types (not our custom implementations) for event handler signatures
|
|
686
|
+
onactivate;
|
|
687
|
+
oncookiechange;
|
|
688
|
+
onfetch;
|
|
689
|
+
oninstall;
|
|
690
|
+
onmessage;
|
|
691
|
+
onmessageerror;
|
|
692
|
+
onnotificationclick;
|
|
693
|
+
onnotificationclose;
|
|
694
|
+
onpush;
|
|
695
|
+
onpushsubscriptionchange;
|
|
696
|
+
onsync;
|
|
697
|
+
// WorkerGlobalScope event handlers (inherited by ServiceWorkerGlobalScope)
|
|
698
|
+
onerror;
|
|
699
|
+
onlanguagechange;
|
|
700
|
+
onoffline;
|
|
701
|
+
ononline;
|
|
702
|
+
onrejectionhandled;
|
|
703
|
+
onunhandledrejection;
|
|
704
|
+
constructor(options) {
|
|
705
|
+
this.self = this;
|
|
706
|
+
this.registration = options.registration;
|
|
707
|
+
this.caches = options.caches;
|
|
708
|
+
this.buckets = options.buckets;
|
|
709
|
+
this.#isDevelopment = options.isDevelopment ?? false;
|
|
710
|
+
this.clients = this.#createClientsAPI();
|
|
711
|
+
this.serviceWorker = null;
|
|
712
|
+
this.location = {};
|
|
713
|
+
this.navigator = {};
|
|
714
|
+
this.fonts = {};
|
|
715
|
+
this.indexedDB = {};
|
|
716
|
+
this.isSecureContext = true;
|
|
717
|
+
this.crossOriginIsolated = false;
|
|
718
|
+
this.origin = "";
|
|
719
|
+
this.performance = {};
|
|
720
|
+
this.crypto = {};
|
|
721
|
+
this.onactivate = null;
|
|
722
|
+
this.oncookiechange = null;
|
|
723
|
+
this.onfetch = null;
|
|
724
|
+
this.oninstall = null;
|
|
725
|
+
this.onmessage = null;
|
|
726
|
+
this.onmessageerror = null;
|
|
727
|
+
this.onnotificationclick = null;
|
|
728
|
+
this.onnotificationclose = null;
|
|
729
|
+
this.onpush = null;
|
|
730
|
+
this.onpushsubscriptionchange = null;
|
|
731
|
+
this.onsync = null;
|
|
732
|
+
this.onerror = null;
|
|
733
|
+
this.onlanguagechange = null;
|
|
734
|
+
this.onoffline = null;
|
|
735
|
+
this.ononline = null;
|
|
736
|
+
this.onrejectionhandled = null;
|
|
737
|
+
this.onunhandledrejection = null;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Standard ServiceWorker skipWaiting() implementation
|
|
741
|
+
* Allows the ServiceWorker to activate immediately
|
|
742
|
+
*/
|
|
743
|
+
async skipWaiting() {
|
|
744
|
+
logger.info("[ServiceWorker] skipWaiting() called");
|
|
745
|
+
if (!this.#isDevelopment) {
|
|
746
|
+
logger.info(
|
|
747
|
+
"[ServiceWorker] skipWaiting() - production graceful restart not implemented"
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Event target delegation to registration
|
|
753
|
+
*/
|
|
754
|
+
addEventListener(type, listener, options) {
|
|
755
|
+
if (listener) {
|
|
756
|
+
this.registration.addEventListener(type, listener, options);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
removeEventListener(type, listener, options) {
|
|
760
|
+
if (listener) {
|
|
761
|
+
this.registration.removeEventListener(type, listener, options);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
dispatchEvent(event) {
|
|
765
|
+
return this.registration.dispatchEvent(event);
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Create Clients API implementation
|
|
769
|
+
* Note: HTTP requests are stateless, so most client operations are no-ops
|
|
770
|
+
*/
|
|
771
|
+
#createClientsAPI() {
|
|
772
|
+
return new ShovelClients();
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Install this scope as the global scope
|
|
776
|
+
* Sets up globalThis with all ServiceWorker globals
|
|
777
|
+
*/
|
|
778
|
+
install() {
|
|
779
|
+
globalThis.WorkerGlobalScope = WorkerGlobalScope;
|
|
780
|
+
globalThis.DedicatedWorkerGlobalScope = DedicatedWorkerGlobalScope;
|
|
781
|
+
globalThis.self = this;
|
|
782
|
+
const isWorker = "onmessage" in globalThis;
|
|
783
|
+
if (isWorker && typeof postMessage === "function") {
|
|
784
|
+
this.postMessage = postMessage.bind(globalThis);
|
|
785
|
+
}
|
|
786
|
+
globalThis.addEventListener = this.addEventListener.bind(this);
|
|
787
|
+
globalThis.removeEventListener = this.removeEventListener.bind(this);
|
|
788
|
+
globalThis.dispatchEvent = this.dispatchEvent.bind(this);
|
|
789
|
+
if (this.caches) {
|
|
790
|
+
globalThis.caches = this.caches;
|
|
791
|
+
}
|
|
792
|
+
if (this.buckets) {
|
|
793
|
+
globalThis.buckets = this.buckets;
|
|
794
|
+
}
|
|
795
|
+
globalThis.registration = this.registration;
|
|
796
|
+
globalThis.skipWaiting = this.skipWaiting.bind(this);
|
|
797
|
+
globalThis.clients = this.clients;
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
var logger = getLogger(["worker"]);
|
|
801
|
+
async function initializeWorker() {
|
|
802
|
+
const messagePort = globalThis;
|
|
803
|
+
const sendMessage2 = (message, transfer) => {
|
|
804
|
+
if (transfer && transfer.length > 0) {
|
|
805
|
+
postMessage(message, transfer);
|
|
806
|
+
} else {
|
|
807
|
+
postMessage(message);
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
onmessage = function(event) {
|
|
811
|
+
void handleMessage(event.data);
|
|
812
|
+
};
|
|
813
|
+
return { messagePort, sendMessage: sendMessage2 };
|
|
814
|
+
}
|
|
815
|
+
var registration = null;
|
|
816
|
+
var scope = null;
|
|
817
|
+
var _workerSelf = null;
|
|
818
|
+
var currentApp = null;
|
|
819
|
+
var serviceWorkerReady = false;
|
|
820
|
+
var loadedVersion = null;
|
|
821
|
+
var caches;
|
|
822
|
+
var buckets;
|
|
823
|
+
async function handleFetchEvent(request) {
|
|
824
|
+
if (!currentApp || !serviceWorkerReady) {
|
|
825
|
+
throw new Error("ServiceWorker not ready");
|
|
826
|
+
}
|
|
827
|
+
if (!registration) {
|
|
828
|
+
throw new Error("ServiceWorker runtime not initialized");
|
|
829
|
+
}
|
|
830
|
+
try {
|
|
831
|
+
const response = await registration.handleRequest(request);
|
|
832
|
+
return response;
|
|
833
|
+
} catch (error) {
|
|
834
|
+
logger.error("[Worker] ServiceWorker request failed", { error });
|
|
835
|
+
const response = new Response("ServiceWorker request failed", {
|
|
836
|
+
status: 500
|
|
837
|
+
});
|
|
838
|
+
return response;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
async function loadServiceWorker(version, entrypoint) {
|
|
842
|
+
try {
|
|
843
|
+
logger.debug("loadServiceWorker called", {
|
|
844
|
+
version,
|
|
845
|
+
loadedVersion
|
|
846
|
+
});
|
|
847
|
+
if (!entrypoint) {
|
|
848
|
+
throw new Error(
|
|
849
|
+
"ServiceWorker entrypoint must be provided via loadServiceWorker() call"
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
logger.info("[Worker] Loading from", { entrypoint });
|
|
853
|
+
if (loadedVersion !== null && loadedVersion !== version) {
|
|
854
|
+
logger.info(
|
|
855
|
+
`[Worker] Hot reload detected: ${loadedVersion} -> ${version}`
|
|
856
|
+
);
|
|
857
|
+
logger.info("[Worker] Creating completely fresh ServiceWorker context");
|
|
858
|
+
registration = new ShovelServiceWorkerRegistration();
|
|
859
|
+
if (!caches || !buckets) {
|
|
860
|
+
throw new Error("Runtime not initialized - missing caches or buckets");
|
|
861
|
+
}
|
|
862
|
+
scope = new ShovelGlobalScope({
|
|
863
|
+
registration,
|
|
864
|
+
caches,
|
|
865
|
+
buckets
|
|
866
|
+
});
|
|
867
|
+
scope.install();
|
|
868
|
+
_workerSelf = scope;
|
|
869
|
+
currentApp = null;
|
|
870
|
+
serviceWorkerReady = false;
|
|
871
|
+
}
|
|
872
|
+
if (loadedVersion === version) {
|
|
873
|
+
logger.info("[Worker] ServiceWorker already loaded for version", {
|
|
874
|
+
version
|
|
875
|
+
});
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const appModule = await import(`${entrypoint}?v=${version}`);
|
|
879
|
+
loadedVersion = version;
|
|
880
|
+
currentApp = appModule;
|
|
881
|
+
if (!registration) {
|
|
882
|
+
throw new Error("ServiceWorker runtime not initialized");
|
|
883
|
+
}
|
|
884
|
+
await registration.install();
|
|
885
|
+
await registration.activate();
|
|
886
|
+
serviceWorkerReady = true;
|
|
887
|
+
logger.info(
|
|
888
|
+
`[Worker] ServiceWorker loaded and activated (v${version}) from ${entrypoint}`
|
|
889
|
+
);
|
|
890
|
+
} catch (error) {
|
|
891
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
892
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
893
|
+
logger.error("[Worker] Failed to load ServiceWorker", {
|
|
894
|
+
error: errorMessage,
|
|
895
|
+
stack: errorStack,
|
|
896
|
+
entrypoint
|
|
897
|
+
});
|
|
898
|
+
serviceWorkerReady = false;
|
|
899
|
+
throw error;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
var workerId = Math.random().toString(36).substring(2, 8);
|
|
903
|
+
var sendMessage;
|
|
904
|
+
async function initializeRuntime(config, baseDir) {
|
|
905
|
+
try {
|
|
906
|
+
logger.info(`[Worker-${workerId}] Initializing runtime with config`, {
|
|
907
|
+
config,
|
|
908
|
+
baseDir
|
|
909
|
+
});
|
|
910
|
+
logger.info(`[Worker-${workerId}] Creating cache storage`);
|
|
911
|
+
caches = new CustomCacheStorage(createCacheFactory({ config }));
|
|
912
|
+
logger.info(`[Worker-${workerId}] Creating bucket storage`);
|
|
913
|
+
buckets = new CustomBucketStorage(createBucketFactory({ baseDir, config }));
|
|
914
|
+
logger.info(`[Worker-${workerId}] Creating and installing scope`);
|
|
915
|
+
registration = new ShovelServiceWorkerRegistration();
|
|
916
|
+
scope = new ShovelGlobalScope({ registration, caches, buckets });
|
|
917
|
+
scope.install();
|
|
918
|
+
_workerSelf = scope;
|
|
919
|
+
logger.info(`[Worker-${workerId}] Runtime initialized successfully`);
|
|
920
|
+
} catch (error) {
|
|
921
|
+
logger.error(`[Worker-${workerId}] Failed to initialize runtime`, { error });
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
async function handleMessage(message) {
|
|
926
|
+
try {
|
|
927
|
+
logger.info(`[Worker-${workerId}] Received message`, { type: message.type });
|
|
928
|
+
if (message.type === "init") {
|
|
929
|
+
const initMsg = message;
|
|
930
|
+
await initializeRuntime(initMsg.config, initMsg.baseDir);
|
|
931
|
+
logger.info(`[Worker-${workerId}] Sending initialized message`);
|
|
932
|
+
sendMessage({ type: "initialized" });
|
|
933
|
+
} else if (message.type === "load") {
|
|
934
|
+
const loadMsg = message;
|
|
935
|
+
await loadServiceWorker(loadMsg.version, loadMsg.entrypoint);
|
|
936
|
+
sendMessage({ type: "ready", version: loadMsg.version });
|
|
937
|
+
} else if (message.type === "request") {
|
|
938
|
+
const reqMsg = message;
|
|
939
|
+
const request = new Request(reqMsg.request.url, {
|
|
940
|
+
method: reqMsg.request.method,
|
|
941
|
+
headers: reqMsg.request.headers,
|
|
942
|
+
body: reqMsg.request.body
|
|
943
|
+
});
|
|
944
|
+
const response = await handleFetchEvent(request);
|
|
945
|
+
const body = await response.arrayBuffer();
|
|
946
|
+
const responseMsg = {
|
|
947
|
+
type: "response",
|
|
948
|
+
response: {
|
|
949
|
+
status: response.status,
|
|
950
|
+
statusText: response.statusText,
|
|
951
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
952
|
+
body
|
|
953
|
+
},
|
|
954
|
+
requestID: reqMsg.requestID
|
|
955
|
+
};
|
|
956
|
+
sendMessage(responseMsg, [body]);
|
|
957
|
+
}
|
|
958
|
+
} catch (error) {
|
|
959
|
+
const errorMsg = {
|
|
960
|
+
type: "error",
|
|
961
|
+
error: error instanceof Error ? error.message : String(error),
|
|
962
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
963
|
+
requestID: message.requestID
|
|
964
|
+
};
|
|
965
|
+
sendMessage(errorMsg);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (typeof onmessage !== "undefined") {
|
|
969
|
+
initializeWorker().then(({ messagePort: _messagePort, sendMessage: send }) => {
|
|
970
|
+
sendMessage = send;
|
|
971
|
+
sendMessage({ type: "worker-ready" });
|
|
972
|
+
}).catch((error) => {
|
|
973
|
+
logger.error("[Worker] Failed to initialize:", error);
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
export {
|
|
977
|
+
ActivateEvent,
|
|
978
|
+
DedicatedWorkerGlobalScope,
|
|
979
|
+
ExtendableEvent,
|
|
980
|
+
ExtendableMessageEvent,
|
|
981
|
+
FetchEvent,
|
|
982
|
+
InstallEvent,
|
|
983
|
+
Notification,
|
|
984
|
+
NotificationEvent,
|
|
985
|
+
PushEvent,
|
|
986
|
+
ShovelClient,
|
|
987
|
+
ShovelClients,
|
|
988
|
+
ShovelGlobalScope,
|
|
989
|
+
ShovelNavigationPreloadManager,
|
|
990
|
+
ShovelPushMessageData,
|
|
991
|
+
ShovelServiceWorker,
|
|
992
|
+
ShovelServiceWorkerContainer,
|
|
993
|
+
ShovelServiceWorkerRegistration,
|
|
994
|
+
ShovelWindowClient,
|
|
995
|
+
SyncEvent,
|
|
996
|
+
WorkerGlobalScope
|
|
997
|
+
};
|