@b9g/platform 0.1.11 → 0.1.13

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/src/runtime.js CHANGED
@@ -1,14 +1,341 @@
1
1
  /// <reference types="./runtime.d.ts" />
2
- import "../chunk-P57PW2II.js";
3
-
4
2
  // src/runtime.ts
5
- import { RequestCookieStore } from "./cookie-store.js";
6
3
  import { AsyncContext } from "@b9g/async-context";
7
- import { CustomBucketStorage } from "@b9g/filesystem";
4
+ import { getLogger, getConsoleSink } from "@logtape/logtape";
5
+ import { CustomDirectoryStorage } from "@b9g/filesystem";
8
6
  import { CustomCacheStorage } from "@b9g/cache";
9
- import { createBucketFactory, createCacheFactory } from "./config.js";
10
- import { getLogger } from "@logtape/logtape";
11
- import { configureLogging } from "./config.js";
7
+ import { handleCacheResponse, PostMessageCache } from "@b9g/cache/postmessage";
8
+ import {
9
+ configure
10
+ } from "@logtape/logtape";
11
+ import { Database } from "@b9g/zen";
12
+ function parseCookieHeader(cookieHeader) {
13
+ const cookies = /* @__PURE__ */ new Map();
14
+ if (!cookieHeader) return cookies;
15
+ const pairs = cookieHeader.split(/;\s*/);
16
+ for (const pair of pairs) {
17
+ const [name, ...valueParts] = pair.split("=");
18
+ if (name) {
19
+ const value = valueParts.join("=");
20
+ cookies.set(name.trim(), decodeURIComponent(value || ""));
21
+ }
22
+ }
23
+ return cookies;
24
+ }
25
+ function serializeCookie(cookie) {
26
+ let header = `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`;
27
+ if (cookie.expires !== void 0 && cookie.expires !== null) {
28
+ const date = new Date(cookie.expires);
29
+ header += `; Expires=${date.toUTCString()}`;
30
+ }
31
+ if (cookie.domain) {
32
+ header += `; Domain=${cookie.domain}`;
33
+ }
34
+ if (cookie.path) {
35
+ header += `; Path=${cookie.path}`;
36
+ } else {
37
+ header += `; Path=/`;
38
+ }
39
+ if (cookie.sameSite) {
40
+ header += `; SameSite=${cookie.sameSite.charAt(0).toUpperCase() + cookie.sameSite.slice(1)}`;
41
+ } else {
42
+ header += `; SameSite=Strict`;
43
+ }
44
+ if (cookie.partitioned) {
45
+ header += `; Partitioned`;
46
+ }
47
+ header += `; Secure`;
48
+ return header;
49
+ }
50
+ function parseSetCookieHeader(setCookieHeader) {
51
+ const parts = setCookieHeader.split(/;\s*/);
52
+ const [nameValue, ...attributes] = parts;
53
+ const [name, ...valueParts] = nameValue.split("=");
54
+ const value = valueParts.join("=");
55
+ const cookie = {
56
+ name: decodeURIComponent(name.trim()),
57
+ value: decodeURIComponent(value || "")
58
+ };
59
+ for (const attr of attributes) {
60
+ const [key, ...attrValueParts] = attr.split("=");
61
+ const attrKey = key.trim().toLowerCase();
62
+ const attrValue = attrValueParts.join("=").trim();
63
+ switch (attrKey) {
64
+ case "expires":
65
+ cookie.expires = new Date(attrValue).getTime();
66
+ break;
67
+ case "max-age":
68
+ cookie.expires = Date.now() + parseInt(attrValue, 10) * 1e3;
69
+ break;
70
+ case "domain":
71
+ cookie.domain = attrValue;
72
+ break;
73
+ case "path":
74
+ cookie.path = attrValue;
75
+ break;
76
+ case "secure":
77
+ cookie.secure = true;
78
+ break;
79
+ case "samesite":
80
+ cookie.sameSite = attrValue.toLowerCase();
81
+ break;
82
+ case "partitioned":
83
+ cookie.partitioned = true;
84
+ break;
85
+ }
86
+ }
87
+ return cookie;
88
+ }
89
+ var RequestCookieStore = class extends EventTarget {
90
+ #cookies;
91
+ #changes;
92
+ // null = deleted
93
+ #request;
94
+ // Event handler for cookie changes (spec compliance)
95
+ // eslint-disable-next-line no-restricted-syntax
96
+ onchange = null;
97
+ constructor(request) {
98
+ super();
99
+ this.#cookies = /* @__PURE__ */ new Map();
100
+ this.#changes = /* @__PURE__ */ new Map();
101
+ this.#request = request || null;
102
+ if (request) {
103
+ const cookieHeader = request.headers.get("cookie");
104
+ if (cookieHeader) {
105
+ const parsed = parseCookieHeader(cookieHeader);
106
+ for (const [name, value] of parsed) {
107
+ this.#cookies.set(name, { name, value });
108
+ }
109
+ }
110
+ }
111
+ }
112
+ async get(nameOrOptions) {
113
+ const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions.name;
114
+ if (!name) {
115
+ throw new TypeError("Cookie name is required");
116
+ }
117
+ if (this.#changes.has(name)) {
118
+ const change = this.#changes.get(name);
119
+ if (change === null || change === void 0) return null;
120
+ return {
121
+ name: change.name,
122
+ value: change.value,
123
+ domain: change.domain ?? void 0,
124
+ path: change.path,
125
+ expires: change.expires ?? void 0,
126
+ sameSite: change.sameSite,
127
+ partitioned: change.partitioned
128
+ };
129
+ }
130
+ return this.#cookies.get(name) || null;
131
+ }
132
+ async getAll(nameOrOptions) {
133
+ const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions?.name;
134
+ const result = [];
135
+ const allNames = /* @__PURE__ */ new Set([
136
+ ...this.#cookies.keys(),
137
+ ...this.#changes.keys()
138
+ ]);
139
+ for (const cookieName of allNames) {
140
+ if (name && cookieName !== name) continue;
141
+ if (this.#changes.has(cookieName) && this.#changes.get(cookieName) === null) {
142
+ continue;
143
+ }
144
+ const cookie = await this.get(cookieName);
145
+ if (cookie) {
146
+ result.push(cookie);
147
+ }
148
+ }
149
+ return result;
150
+ }
151
+ async set(nameOrOptions, value) {
152
+ let cookie;
153
+ if (typeof nameOrOptions === "string") {
154
+ if (value === void 0) {
155
+ throw new TypeError("Cookie value is required");
156
+ }
157
+ cookie = {
158
+ name: nameOrOptions,
159
+ value,
160
+ path: "/",
161
+ sameSite: "strict"
162
+ };
163
+ } else {
164
+ cookie = {
165
+ path: "/",
166
+ sameSite: "strict",
167
+ ...nameOrOptions
168
+ };
169
+ }
170
+ const size = cookie.name.length + cookie.value.length;
171
+ if (size > 4096) {
172
+ throw new TypeError(
173
+ `Cookie name+value too large: ${size} bytes (max 4096)`
174
+ );
175
+ }
176
+ this.#changes.set(cookie.name, cookie);
177
+ }
178
+ async delete(nameOrOptions) {
179
+ const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions.name;
180
+ if (!name) {
181
+ throw new TypeError("Cookie name is required");
182
+ }
183
+ this.#changes.set(name, null);
184
+ }
185
+ /**
186
+ * Get Set-Cookie headers for all changes
187
+ * This should be called when constructing the Response
188
+ */
189
+ getSetCookieHeaders() {
190
+ const headers = [];
191
+ for (const [name, change] of this.#changes) {
192
+ if (change === null) {
193
+ headers.push(
194
+ serializeCookie({
195
+ name,
196
+ value: "",
197
+ expires: 0,
198
+ path: "/"
199
+ })
200
+ );
201
+ } else {
202
+ headers.push(serializeCookie(change));
203
+ }
204
+ }
205
+ return headers;
206
+ }
207
+ hasChanges() {
208
+ return this.#changes.size > 0;
209
+ }
210
+ clearChanges() {
211
+ this.#changes.clear();
212
+ }
213
+ };
214
+ var CustomLoggerStorage = class {
215
+ #factory;
216
+ constructor(factory) {
217
+ this.#factory = factory;
218
+ }
219
+ get(categories) {
220
+ return this.#factory(categories);
221
+ }
222
+ };
223
+ var CustomDatabaseStorage = class {
224
+ #factory;
225
+ #databases;
226
+ #closers;
227
+ #pending;
228
+ constructor(factory) {
229
+ this.#factory = factory;
230
+ this.#databases = /* @__PURE__ */ new Map();
231
+ this.#closers = /* @__PURE__ */ new Map();
232
+ this.#pending = /* @__PURE__ */ new Map();
233
+ }
234
+ async open(name, version, onUpgrade) {
235
+ const existing = this.#databases.get(name);
236
+ if (existing) {
237
+ return existing;
238
+ }
239
+ const pending = this.#pending.get(name);
240
+ if (pending) {
241
+ return pending;
242
+ }
243
+ const promise = (async () => {
244
+ const { db, close } = await this.#factory(name);
245
+ if (onUpgrade) {
246
+ db.addEventListener("upgradeneeded", (e) => {
247
+ const event = e;
248
+ onUpgrade({
249
+ db,
250
+ oldVersion: event.oldVersion,
251
+ newVersion: event.newVersion,
252
+ waitUntil: (p) => event.waitUntil(p)
253
+ });
254
+ });
255
+ }
256
+ try {
257
+ await db.open(version);
258
+ } catch (err) {
259
+ await close();
260
+ throw err;
261
+ }
262
+ this.#databases.set(name, db);
263
+ this.#closers.set(name, close);
264
+ return db;
265
+ })().finally(() => {
266
+ this.#pending.delete(name);
267
+ });
268
+ this.#pending.set(name, promise);
269
+ return promise;
270
+ }
271
+ get(name) {
272
+ const db = this.#databases.get(name);
273
+ if (!db) {
274
+ throw new Error(
275
+ `Database "${name}" has not been opened. Call self.databases.open("${name}", version) in your activate handler first.`
276
+ );
277
+ }
278
+ return db;
279
+ }
280
+ async close(name) {
281
+ const pending = this.#pending.get(name);
282
+ if (pending) {
283
+ try {
284
+ await pending;
285
+ } catch (_err) {
286
+ return;
287
+ }
288
+ }
289
+ const closer = this.#closers.get(name);
290
+ if (closer) {
291
+ await closer();
292
+ this.#databases.delete(name);
293
+ this.#closers.delete(name);
294
+ }
295
+ }
296
+ async closeAll() {
297
+ if (this.#pending.size > 0) {
298
+ await Promise.allSettled(this.#pending.values());
299
+ }
300
+ const promises = Array.from(this.#databases.keys()).map(
301
+ (name) => this.close(name)
302
+ );
303
+ await Promise.allSettled(promises);
304
+ }
305
+ };
306
+ function createDatabaseFactory(configs) {
307
+ return async (name) => {
308
+ const config = configs[name];
309
+ if (!config) {
310
+ throw new Error(
311
+ `Database "${name}" is not configured. Available databases: ${Object.keys(configs).join(", ") || "(none)"}`
312
+ );
313
+ }
314
+ const { impl, url, ...driverOptions } = config;
315
+ if (!impl) {
316
+ throw new Error(
317
+ `Database "${name}" has no impl. Ensure the database module is configured in shovel.json.`
318
+ );
319
+ }
320
+ if (!url) {
321
+ throw new Error(
322
+ `Database "${name}" has no url. Ensure the database URL is configured.`
323
+ );
324
+ }
325
+ const driver = new impl(url, driverOptions);
326
+ const db = new Database(driver);
327
+ return {
328
+ db,
329
+ close: async () => {
330
+ await driver.close();
331
+ }
332
+ };
333
+ };
334
+ }
335
+ var SERVICE_WORKER_EVENTS = ["fetch", "install", "activate"];
336
+ function isServiceWorkerEvent(type) {
337
+ return SERVICE_WORKER_EVENTS.includes(type);
338
+ }
12
339
  if (import.meta.env && !import.meta.env.MODE && import.meta.env.NODE_ENV) {
13
340
  import.meta.env.MODE = import.meta.env.NODE_ENV;
14
341
  }
@@ -19,7 +346,9 @@ var PATCHED_KEYS = [
19
346
  "self",
20
347
  "fetch",
21
348
  "caches",
22
- "buckets",
349
+ "directories",
350
+ "databases",
351
+ "loggers",
23
352
  "registration",
24
353
  "clients",
25
354
  "skipWaiting",
@@ -38,9 +367,9 @@ function promiseWithTimeout(promise, timeoutMs, errorMessage) {
38
367
  )
39
368
  ]);
40
369
  }
41
- var kEndDispatchPhase = Symbol.for("shovel.endDispatchPhase");
42
- var kCanExtend = Symbol.for("shovel.canExtend");
43
- var ExtendableEvent = class extends Event {
370
+ var kEndDispatchPhase = /* @__PURE__ */ Symbol.for("shovel.endDispatchPhase");
371
+ var kCanExtend = /* @__PURE__ */ Symbol.for("shovel.canExtend");
372
+ var ShovelExtendableEvent = class extends Event {
44
373
  #promises;
45
374
  #dispatchPhase;
46
375
  #pendingCount;
@@ -77,17 +406,33 @@ var ExtendableEvent = class extends Event {
77
406
  return this.#dispatchPhase || this.#pendingCount > 0;
78
407
  }
79
408
  };
80
- var FetchEvent = class extends ExtendableEvent {
409
+ var ShovelFetchEvent = class extends ShovelExtendableEvent {
81
410
  request;
82
411
  cookieStore;
412
+ clientId;
413
+ handled;
414
+ preloadResponse;
415
+ resultingClientId;
83
416
  #responsePromise;
84
417
  #responded;
85
- constructor(request, eventInitDict) {
86
- super("fetch", eventInitDict);
418
+ #platformWaitUntil;
419
+ constructor(request, options) {
420
+ super("fetch", options);
87
421
  this.request = request;
88
422
  this.cookieStore = new RequestCookieStore(request);
423
+ this.clientId = "";
424
+ this.handled = Promise.resolve(void 0);
425
+ this.preloadResponse = Promise.resolve(void 0);
426
+ this.resultingClientId = "";
89
427
  this.#responsePromise = null;
90
428
  this.#responded = false;
429
+ this.#platformWaitUntil = options?.platformWaitUntil;
430
+ }
431
+ waitUntil(promise) {
432
+ if (this.#platformWaitUntil) {
433
+ this.#platformWaitUntil(promise);
434
+ }
435
+ super.waitUntil(promise);
91
436
  }
92
437
  respondWith(response) {
93
438
  if (this.#responded) {
@@ -109,13 +454,17 @@ var FetchEvent = class extends ExtendableEvent {
109
454
  hasResponded() {
110
455
  return this.#responded;
111
456
  }
457
+ /** The URL of the request (convenience property) */
458
+ get url() {
459
+ return this.request.url;
460
+ }
112
461
  };
113
- var InstallEvent = class extends ExtendableEvent {
462
+ var ShovelInstallEvent = class extends ShovelExtendableEvent {
114
463
  constructor(eventInitDict) {
115
464
  super("install", eventInitDict);
116
465
  }
117
466
  };
118
- var ActivateEvent = class extends ExtendableEvent {
467
+ var ShovelActivateEvent = class extends ShovelExtendableEvent {
119
468
  constructor(eventInitDict) {
120
469
  super("activate", eventInitDict);
121
470
  }
@@ -133,9 +482,7 @@ var ShovelClient = class {
133
482
  }
134
483
  // Implementation
135
484
  postMessage(_message, _transferOrOptions) {
136
- logger.warn(
137
- "[ServiceWorker] Client.postMessage() not supported in server context"
138
- );
485
+ self.loggers.open("platform").warn("Client.postMessage() not supported in server context");
139
486
  }
140
487
  };
141
488
  var ShovelWindowClient = class extends ShovelClient {
@@ -147,15 +494,11 @@ var ShovelWindowClient = class extends ShovelClient {
147
494
  this.visibilityState = options.visibilityState || "hidden";
148
495
  }
149
496
  async focus() {
150
- logger.warn(
151
- "[ServiceWorker] WindowClient.focus() not supported in server context"
152
- );
497
+ self.loggers.open("platform").warn("WindowClient.focus() not supported in server context");
153
498
  return this;
154
499
  }
155
500
  async navigate(_url) {
156
- logger.warn(
157
- "[ServiceWorker] WindowClient.navigate() not supported in server context"
158
- );
501
+ self.loggers.open("platform").warn("WindowClient.navigate() not supported in server context");
159
502
  return null;
160
503
  }
161
504
  };
@@ -169,13 +512,11 @@ var ShovelClients = class {
169
512
  return [];
170
513
  }
171
514
  async openWindow(_url) {
172
- logger.warn(
173
- "[ServiceWorker] Clients.openWindow() not supported in server context"
174
- );
515
+ self.loggers.open("platform").warn("Clients.openWindow() not supported in server context");
175
516
  return null;
176
517
  }
177
518
  };
178
- var ExtendableMessageEvent = class extends ExtendableEvent {
519
+ var ExtendableMessageEvent = class extends ShovelExtendableEvent {
179
520
  data;
180
521
  origin;
181
522
  lastEventId;
@@ -205,9 +546,7 @@ var ShovelServiceWorker = class extends EventTarget {
205
546
  }
206
547
  // Implementation
207
548
  postMessage(_message, _transferOrOptions) {
208
- logger.warn(
209
- "[ServiceWorker] ServiceWorker.postMessage() not implemented in server context"
210
- );
549
+ self.loggers.open("platform").warn("ServiceWorker.postMessage() not implemented in server context");
211
550
  }
212
551
  // Internal method to update state and dispatch statechange event
213
552
  _setState(newState) {
@@ -239,9 +578,9 @@ var ShovelServiceWorkerRegistration = class extends EventTarget {
239
578
  cookies;
240
579
  pushManager;
241
580
  onupdatefound;
242
- constructor(scope2 = "/", scriptURL = "/") {
581
+ constructor(scope = "/", scriptURL = "/") {
243
582
  super();
244
- this.scope = scope2;
583
+ this.scope = scope;
245
584
  this.updateViaCache = "imports";
246
585
  this.navigationPreload = new ShovelNavigationPreloadManager();
247
586
  this._serviceWorker = new ShovelServiceWorker(scriptURL, "parsed");
@@ -264,9 +603,7 @@ var ShovelServiceWorkerRegistration = class extends EventTarget {
264
603
  return [];
265
604
  }
266
605
  async showNotification(_title, _options) {
267
- logger.warn(
268
- "[ServiceWorker] Notifications not supported in server context"
269
- );
606
+ self.loggers.open("platform").warn("Notifications not supported in server context");
270
607
  }
271
608
  async sync() {
272
609
  }
@@ -281,11 +618,10 @@ var ShovelServiceWorkerRegistration = class extends EventTarget {
281
618
  * Install the ServiceWorker (Shovel extension)
282
619
  */
283
620
  async install() {
284
- if (this._serviceWorker.state !== "parsed")
285
- return;
621
+ if (this._serviceWorker.state !== "parsed") return;
286
622
  this._serviceWorker._setState("installing");
287
623
  return new Promise((resolve, reject) => {
288
- const event = new InstallEvent();
624
+ const event = new ShovelInstallEvent();
289
625
  process.nextTick(() => {
290
626
  try {
291
627
  this.dispatchEvent(event);
@@ -320,7 +656,7 @@ var ShovelServiceWorkerRegistration = class extends EventTarget {
320
656
  }
321
657
  this._serviceWorker._setState("activating");
322
658
  return new Promise((resolve, reject) => {
323
- const event = new ActivateEvent();
659
+ const event = new ShovelActivateEvent();
324
660
  process.nextTick(() => {
325
661
  try {
326
662
  this.dispatchEvent(event);
@@ -348,12 +684,16 @@ var ShovelServiceWorkerRegistration = class extends EventTarget {
348
684
  }
349
685
  /**
350
686
  * Handle a fetch request (Shovel extension)
687
+ *
688
+ * Platforms create a ShovelFetchEvent (or subclass) with platform-specific
689
+ * properties and hooks, then pass it to this method for dispatching.
690
+ *
691
+ * @param event - The fetch event to handle (created by platform adapter)
351
692
  */
352
- async handleRequest(request) {
693
+ async handleRequest(event) {
353
694
  if (this._serviceWorker.state !== "activated") {
354
695
  throw new Error("ServiceWorker not activated");
355
696
  }
356
- const event = new FetchEvent(request);
357
697
  return cookieStoreStorage.run(event.cookieStore, async () => {
358
698
  this.dispatchEvent(event);
359
699
  event[kEndDispatchPhase]();
@@ -363,10 +703,6 @@ var ShovelServiceWorkerRegistration = class extends EventTarget {
363
703
  );
364
704
  }
365
705
  const response = await event.getResponse();
366
- const promises = event.getPromises();
367
- if (promises.length > 0) {
368
- Promise.allSettled(promises).catch(logger.error);
369
- }
370
706
  if (event.cookieStore.hasChanges()) {
371
707
  const setCookieHeaders = event.cookieStore.getSetCookieHeaders();
372
708
  const headers = new Headers(response.headers);
@@ -412,8 +748,8 @@ var ShovelServiceWorkerContainer = class extends EventTarget {
412
748
  /**
413
749
  * Get registration for a specific scope
414
750
  */
415
- async getRegistration(scope2 = "/") {
416
- return this.#registrations.get(scope2);
751
+ async getRegistration(scope = "/") {
752
+ return this.#registrations.get(scope);
417
753
  }
418
754
  /**
419
755
  * Get all registrations
@@ -426,26 +762,26 @@ var ShovelServiceWorkerContainer = class extends EventTarget {
426
762
  */
427
763
  async register(scriptURL, options) {
428
764
  const url = typeof scriptURL === "string" ? scriptURL : scriptURL.toString();
429
- const scope2 = this.#normalizeScope(options?.scope || "/");
430
- let registration2 = this.#registrations.get(scope2);
431
- if (registration2) {
432
- registration2._serviceWorker.scriptURL = url;
433
- registration2._serviceWorker._setState("parsed");
765
+ const scope = this.#normalizeScope(options?.scope || "/");
766
+ let registration = this.#registrations.get(scope);
767
+ if (registration) {
768
+ registration._serviceWorker.scriptURL = url;
769
+ registration._serviceWorker._setState("parsed");
434
770
  } else {
435
- registration2 = new ShovelServiceWorkerRegistration(scope2, url);
436
- this.#registrations.set(scope2, registration2);
771
+ registration = new ShovelServiceWorkerRegistration(scope, url);
772
+ this.#registrations.set(scope, registration);
437
773
  this.dispatchEvent(new Event("updatefound"));
438
774
  }
439
- return registration2;
775
+ return registration;
440
776
  }
441
777
  /**
442
778
  * Unregister a ServiceWorker registration
443
779
  */
444
- async unregister(scope2) {
445
- const registration2 = this.#registrations.get(scope2);
446
- if (registration2) {
447
- await registration2.unregister();
448
- this.#registrations.delete(scope2);
780
+ async unregister(scope) {
781
+ const registration = this.#registrations.get(scope);
782
+ if (registration) {
783
+ await registration.unregister();
784
+ this.#registrations.delete(scope);
449
785
  return true;
450
786
  }
451
787
  return false;
@@ -458,9 +794,10 @@ var ShovelServiceWorkerContainer = class extends EventTarget {
458
794
  const pathname = url.pathname;
459
795
  const matchingScope = this.#findMatchingScope(pathname);
460
796
  if (matchingScope) {
461
- const registration2 = this.#registrations.get(matchingScope);
462
- if (registration2 && registration2.ready) {
463
- return await registration2.handleRequest(request);
797
+ const registration = this.#registrations.get(matchingScope);
798
+ if (registration && registration.ready) {
799
+ const event = new ShovelFetchEvent(request);
800
+ return await registration.handleRequest(event);
464
801
  }
465
802
  }
466
803
  return null;
@@ -470,9 +807,9 @@ var ShovelServiceWorkerContainer = class extends EventTarget {
470
807
  */
471
808
  async installAll() {
472
809
  const installations = Array.from(this.#registrations.values()).map(
473
- async (registration2) => {
474
- await registration2.install();
475
- await registration2.activate();
810
+ async (registration) => {
811
+ await registration.install();
812
+ await registration.activate();
476
813
  }
477
814
  );
478
815
  await promiseWithTimeout(
@@ -492,14 +829,14 @@ var ShovelServiceWorkerContainer = class extends EventTarget {
492
829
  /**
493
830
  * Normalize scope to ensure it starts and ends correctly
494
831
  */
495
- #normalizeScope(scope2) {
496
- if (!scope2.startsWith("/")) {
497
- scope2 = "/" + scope2;
832
+ #normalizeScope(scope) {
833
+ if (!scope.startsWith("/")) {
834
+ scope = "/" + scope;
498
835
  }
499
- if (scope2 !== "/" && !scope2.endsWith("/")) {
500
- scope2 = scope2 + "/";
836
+ if (scope !== "/" && !scope.endsWith("/")) {
837
+ scope = scope + "/";
501
838
  }
502
- return scope2;
839
+ return scope;
503
840
  }
504
841
  /**
505
842
  * Find the most specific scope that matches a pathname
@@ -507,9 +844,9 @@ var ShovelServiceWorkerContainer = class extends EventTarget {
507
844
  #findMatchingScope(pathname) {
508
845
  const scopes = Array.from(this.#registrations.keys());
509
846
  scopes.sort((a, b) => b.length - a.length);
510
- for (const scope2 of scopes) {
511
- if (pathname.startsWith(scope2 === "/" ? "/" : scope2)) {
512
- return scope2;
847
+ for (const scope of scopes) {
848
+ if (pathname.startsWith(scope === "/" ? "/" : scope)) {
849
+ return scope;
513
850
  }
514
851
  }
515
852
  return null;
@@ -561,9 +898,7 @@ var Notification = class extends EventTarget {
561
898
  this.onshow = null;
562
899
  }
563
900
  close() {
564
- logger.warn(
565
- "[ServiceWorker] Notification.close() not supported in server context"
566
- );
901
+ self.loggers.open("platform").warn("Notification.close() not supported in server context");
567
902
  }
568
903
  static async requestPermission() {
569
904
  return "denied";
@@ -571,7 +906,7 @@ var Notification = class extends EventTarget {
571
906
  // Events: click, close, error, show
572
907
  };
573
908
  Notification.permission = "denied";
574
- var NotificationEvent = class extends ExtendableEvent {
909
+ var NotificationEvent = class extends ShovelExtendableEvent {
575
910
  action;
576
911
  notification;
577
912
  reply;
@@ -582,7 +917,7 @@ var NotificationEvent = class extends ExtendableEvent {
582
917
  this.reply = eventInitDict.reply ?? null;
583
918
  }
584
919
  };
585
- var PushEvent = class extends ExtendableEvent {
920
+ var PushEvent = class extends ShovelExtendableEvent {
586
921
  data;
587
922
  constructor(type, eventInitDict) {
588
923
  super(type, eventInitDict);
@@ -615,7 +950,7 @@ var ShovelPushMessageData = class {
615
950
  return new TextDecoder().decode(this._data);
616
951
  }
617
952
  };
618
- var SyncEvent = class extends ExtendableEvent {
953
+ var SyncEvent = class extends ShovelExtendableEvent {
619
954
  tag;
620
955
  lastChance;
621
956
  constructor(type, eventInitDict) {
@@ -637,7 +972,9 @@ var ServiceWorkerGlobals = class {
637
972
  registration;
638
973
  // Storage APIs
639
974
  caches;
640
- buckets;
975
+ directories;
976
+ databases;
977
+ loggers;
641
978
  // Clients API
642
979
  // Our custom Clients implementation provides core functionality compatible with the Web API
643
980
  clients;
@@ -664,9 +1001,7 @@ var ServiceWorkerGlobals = class {
664
1001
  crypto;
665
1002
  // WorkerGlobalScope methods (stubs for server context)
666
1003
  importScripts(..._urls) {
667
- logger.warn(
668
- "[ServiceWorker] importScripts() not supported in server context"
669
- );
1004
+ self.loggers.open("platform").warn("importScripts() not supported in server context");
670
1005
  }
671
1006
  atob(data) {
672
1007
  return globalThis.atob(data);
@@ -700,14 +1035,15 @@ var ServiceWorkerGlobals = class {
700
1035
  }
701
1036
  const request = new Request(new URL(urlString, "http://localhost"), init);
702
1037
  return fetchDepthStorage.run(currentDepth + 1, () => {
703
- return this.registration.handleRequest(request);
1038
+ const event = new ShovelFetchEvent(request);
1039
+ return this.registration.handleRequest(event);
704
1040
  });
705
1041
  }
706
1042
  queueMicrotask(callback) {
707
1043
  globalThis.queueMicrotask(callback);
708
1044
  }
709
1045
  reportError(e) {
710
- logger.error("[ServiceWorker] reportError:", e);
1046
+ getLogger(["shovel", "platform"]).error`reportError: ${e}`;
711
1047
  }
712
1048
  setInterval(handler, timeout, ...args) {
713
1049
  return globalThis.setInterval(handler, timeout, ...args);
@@ -747,7 +1083,9 @@ var ServiceWorkerGlobals = class {
747
1083
  this.self = globalThis;
748
1084
  this.registration = options.registration;
749
1085
  this.caches = options.caches;
750
- this.buckets = options.buckets;
1086
+ this.directories = options.directories;
1087
+ this.databases = options.databases;
1088
+ this.loggers = options.loggers;
751
1089
  this.#isDevelopment = options.isDevelopment ?? false;
752
1090
  this.clients = this.#createClientsAPI();
753
1091
  this.serviceWorker = null;
@@ -783,28 +1121,48 @@ var ServiceWorkerGlobals = class {
783
1121
  * Allows the ServiceWorker to activate immediately
784
1122
  */
785
1123
  async skipWaiting() {
786
- logger.info("[ServiceWorker] skipWaiting() called");
1124
+ getLogger(["shovel", "platform"]).info("skipWaiting() called");
787
1125
  if (!this.#isDevelopment) {
788
- logger.info(
789
- "[ServiceWorker] skipWaiting() - production graceful restart not implemented"
1126
+ getLogger(["shovel", "platform"]).info(
1127
+ "skipWaiting() - production graceful restart not implemented"
790
1128
  );
791
1129
  }
792
1130
  }
793
1131
  /**
794
- * Event target delegation to registration
1132
+ * Event target delegation - ServiceWorker events go to registration,
1133
+ * other events (like "message" for worker threads) go to native handler
795
1134
  */
796
1135
  addEventListener(type, listener, options) {
797
- if (listener) {
1136
+ if (!listener) return;
1137
+ if (isServiceWorkerEvent(type)) {
798
1138
  this.registration.addEventListener(type, listener, options);
1139
+ } else {
1140
+ const original = this.#originals.addEventListener;
1141
+ if (original) {
1142
+ original.call(globalThis, type, listener, options);
1143
+ }
799
1144
  }
800
1145
  }
801
1146
  removeEventListener(type, listener, options) {
802
- if (listener) {
1147
+ if (!listener) return;
1148
+ if (isServiceWorkerEvent(type)) {
803
1149
  this.registration.removeEventListener(type, listener, options);
1150
+ } else {
1151
+ const original = this.#originals.removeEventListener;
1152
+ if (original) {
1153
+ original.call(globalThis, type, listener, options);
1154
+ }
804
1155
  }
805
1156
  }
806
1157
  dispatchEvent(event) {
807
- return this.registration.dispatchEvent(event);
1158
+ if (isServiceWorkerEvent(event.type)) {
1159
+ return this.registration.dispatchEvent(event);
1160
+ }
1161
+ const original = this.#originals.dispatchEvent;
1162
+ if (original) {
1163
+ return original.call(globalThis, event);
1164
+ }
1165
+ return false;
808
1166
  }
809
1167
  /**
810
1168
  * Create Clients API implementation
@@ -828,7 +1186,11 @@ var ServiceWorkerGlobals = class {
828
1186
  g.removeEventListener = this.removeEventListener.bind(this);
829
1187
  g.dispatchEvent = this.dispatchEvent.bind(this);
830
1188
  g.caches = this.caches;
831
- g.buckets = this.buckets;
1189
+ g.directories = this.directories;
1190
+ if (this.databases) {
1191
+ g.databases = this.databases;
1192
+ }
1193
+ g.loggers = this.loggers;
832
1194
  g.registration = this.registration;
833
1195
  g.skipWaiting = this.skipWaiting.bind(this);
834
1196
  g.clients = this.clients;
@@ -854,150 +1216,112 @@ var ServiceWorkerGlobals = class {
854
1216
  }
855
1217
  }
856
1218
  };
857
- var logger = getLogger(["worker"]);
858
- async function initializeWorker() {
859
- const messagePort = globalThis;
860
- const sendMessage2 = (message, transfer) => {
861
- if (transfer && transfer.length > 0) {
862
- postMessage(message, transfer);
1219
+ function isClass(fn) {
1220
+ return typeof fn === "function" && fn.prototype !== void 0;
1221
+ }
1222
+ function createDirectoryFactory(configs) {
1223
+ return async (name) => {
1224
+ const config = configs[name];
1225
+ if (!config) {
1226
+ throw new Error(
1227
+ `Directory "${name}" is not configured. Available directories: ${Object.keys(configs).join(", ") || "(none)"}`
1228
+ );
1229
+ }
1230
+ const { impl, ...dirOptions } = config;
1231
+ if (!impl) {
1232
+ throw new Error(
1233
+ `Directory "${name}" has no impl. Ensure the directory module is configured.`
1234
+ );
1235
+ }
1236
+ if (isClass(impl)) {
1237
+ return new impl(name, dirOptions);
863
1238
  } else {
864
- postMessage(message);
1239
+ return impl(name, dirOptions);
865
1240
  }
866
1241
  };
867
- onmessage = function(event) {
868
- void handleMessage(event.data);
869
- };
870
- return { messagePort, sendMessage: sendMessage2 };
871
- }
872
- var registration = null;
873
- var scope = null;
874
- var _workerSelf = null;
875
- var currentApp = null;
876
- var serviceWorkerReady = false;
877
- var loadedEntrypoint = null;
878
- var caches;
879
- var buckets;
880
- async function handleFetchEvent(request) {
881
- if (!currentApp || !serviceWorkerReady) {
882
- throw new Error("ServiceWorker not ready");
883
- }
884
- if (!registration) {
885
- throw new Error("ServiceWorker runtime not initialized");
886
- }
887
- try {
888
- const response = await registration.handleRequest(request);
889
- return response;
890
- } catch (error) {
891
- logger.error("[Worker] ServiceWorker request failed: {error}", { error });
892
- console.error("[Worker] ServiceWorker request failed:", error);
893
- const response = new Response("ServiceWorker request failed", {
894
- status: 500
895
- });
896
- return response;
897
- }
898
1242
  }
899
- async function loadServiceWorker(entrypoint) {
900
- try {
901
- logger.debug("loadServiceWorker called", {
902
- entrypoint,
903
- loadedEntrypoint
904
- });
905
- logger.info("[Worker] Loading from", { entrypoint });
906
- if (loadedEntrypoint !== null && loadedEntrypoint !== entrypoint) {
907
- logger.info(
908
- `[Worker] Hot reload detected: ${loadedEntrypoint} -> ${entrypoint}`
1243
+ function createCacheFactory(options) {
1244
+ const { configs, usePostMessage = false } = options;
1245
+ return async (name) => {
1246
+ if (usePostMessage) {
1247
+ return new PostMessageCache(name);
1248
+ }
1249
+ const config = configs[name];
1250
+ if (!config) {
1251
+ throw new Error(
1252
+ `Cache "${name}" is not configured. Available caches: ${Object.keys(configs).join(", ") || "(none)"}`
909
1253
  );
910
- logger.info("[Worker] Creating completely fresh ServiceWorker context");
911
- registration = new ShovelServiceWorkerRegistration();
912
- if (!caches || !buckets) {
913
- throw new Error("Runtime not initialized - missing caches or buckets");
914
- }
915
- scope = new ServiceWorkerGlobals({
916
- registration,
917
- caches,
918
- buckets
919
- });
920
- scope.install();
921
- _workerSelf = scope;
922
- currentApp = null;
923
- serviceWorkerReady = false;
924
1254
  }
925
- if (loadedEntrypoint === entrypoint) {
926
- logger.info("[Worker] ServiceWorker already loaded for entrypoint", {
927
- entrypoint
928
- });
929
- return;
1255
+ const { impl, ...cacheOptions } = config;
1256
+ if (!impl) {
1257
+ throw new Error(
1258
+ `Cache "${name}" has no impl. Ensure the cache module is configured.`
1259
+ );
930
1260
  }
931
- const appModule = await import(entrypoint);
932
- loadedEntrypoint = entrypoint;
933
- currentApp = appModule;
934
- if (!registration) {
935
- throw new Error("ServiceWorker runtime not initialized");
1261
+ if (isClass(impl)) {
1262
+ return new impl(name, cacheOptions);
1263
+ } else {
1264
+ return impl(name, cacheOptions);
936
1265
  }
937
- await registration.install();
938
- await registration.activate();
939
- serviceWorkerReady = true;
940
- logger.info(
941
- `[Worker] ServiceWorker loaded and activated from ${entrypoint}`
942
- );
943
- } catch (error) {
944
- const errorMessage = error instanceof Error ? error.message : String(error);
945
- const errorStack = error instanceof Error ? error.stack : void 0;
946
- logger.error("[Worker] Failed to load ServiceWorker", {
947
- error: errorMessage,
948
- stack: errorStack,
949
- entrypoint
950
- });
951
- serviceWorkerReady = false;
952
- throw error;
953
- }
1266
+ };
954
1267
  }
955
- var workerId = Math.random().toString(36).substring(2, 8);
956
- var sendMessage;
957
- async function initializeRuntime(config, baseDir) {
958
- try {
959
- if (config?.logging) {
960
- await configureLogging(config.logging);
1268
+ async function initWorkerRuntime(options) {
1269
+ const { config } = options;
1270
+ const runtimeLogger = getLogger(["shovel", "platform"]);
1271
+ if (config?.logging) {
1272
+ await configureLogging(config.logging);
1273
+ }
1274
+ runtimeLogger.debug("Initializing worker runtime");
1275
+ const caches = new CustomCacheStorage(
1276
+ createCacheFactory({
1277
+ configs: config?.caches ?? {},
1278
+ usePostMessage: true
1279
+ })
1280
+ );
1281
+ const directories = new CustomDirectoryStorage(
1282
+ createDirectoryFactory(config?.directories ?? {})
1283
+ );
1284
+ let databases;
1285
+ if (config?.databases && Object.keys(config.databases).length > 0) {
1286
+ const factory = createDatabaseFactory(config.databases);
1287
+ databases = new CustomDatabaseStorage(factory);
1288
+ }
1289
+ const loggers = new CustomLoggerStorage(
1290
+ (categories) => getLogger(categories)
1291
+ );
1292
+ const registration = new ShovelServiceWorkerRegistration();
1293
+ const scope = new ServiceWorkerGlobals({
1294
+ registration,
1295
+ caches,
1296
+ directories,
1297
+ databases,
1298
+ loggers
1299
+ });
1300
+ scope.install();
1301
+ runtimeLogger.debug("Worker runtime initialized");
1302
+ return { registration, scope, caches, directories, databases, loggers };
1303
+ }
1304
+ function startWorkerMessageLoop(options) {
1305
+ const registration = options instanceof ShovelServiceWorkerRegistration ? options : options.registration;
1306
+ const databases = options instanceof ShovelServiceWorkerRegistration ? void 0 : options.databases;
1307
+ const messageLogger = getLogger(["shovel", "platform"]);
1308
+ const workerId = Math.random().toString(36).substring(2, 8);
1309
+ function sendMessage(message, transfer) {
1310
+ if (transfer && transfer.length > 0) {
1311
+ postMessage(message, transfer);
1312
+ } else {
1313
+ postMessage(message);
961
1314
  }
962
- logger.info(`[Worker-${workerId}] Initializing runtime with config`, {
963
- config,
964
- baseDir
965
- });
966
- logger.info(`[Worker-${workerId}] Creating cache storage`);
967
- caches = new CustomCacheStorage(createCacheFactory({ config }));
968
- logger.info(`[Worker-${workerId}] Creating bucket storage`);
969
- buckets = new CustomBucketStorage(createBucketFactory({ baseDir, config }));
970
- logger.info(`[Worker-${workerId}] Creating and installing scope`);
971
- registration = new ShovelServiceWorkerRegistration();
972
- scope = new ServiceWorkerGlobals({ registration, caches, buckets });
973
- scope.install();
974
- _workerSelf = scope;
975
- logger.info(`[Worker-${workerId}] Runtime initialized successfully`);
976
- } catch (error) {
977
- logger.error(`[Worker-${workerId}] Failed to initialize runtime`, { error });
978
- throw error;
979
1315
  }
980
- }
981
- async function handleMessage(message) {
982
- try {
983
- logger.info(`[Worker-${workerId}] Received message`, { type: message.type });
984
- if (message.type === "init") {
985
- const initMsg = message;
986
- await initializeRuntime(initMsg.config, initMsg.baseDir);
987
- logger.info(`[Worker-${workerId}] Sending initialized message`);
988
- sendMessage({ type: "initialized" });
989
- } else if (message.type === "load") {
990
- const loadMsg = message;
991
- await loadServiceWorker(loadMsg.entrypoint);
992
- sendMessage({ type: "ready", entrypoint: loadMsg.entrypoint });
993
- } else if (message.type === "request") {
994
- const reqMsg = message;
995
- const request = new Request(reqMsg.request.url, {
996
- method: reqMsg.request.method,
997
- headers: reqMsg.request.headers,
998
- body: reqMsg.request.body
1316
+ async function handleFetchRequest(message) {
1317
+ try {
1318
+ const request = new Request(message.request.url, {
1319
+ method: message.request.method,
1320
+ headers: message.request.headers,
1321
+ body: message.request.body
999
1322
  });
1000
- const response = await handleFetchEvent(request);
1323
+ const event = new ShovelFetchEvent(request);
1324
+ const response = await registration.handleRequest(event);
1001
1325
  const body = await response.arrayBuffer();
1002
1326
  const headers = Object.fromEntries(response.headers.entries());
1003
1327
  if (!headers["Content-Type"] && !headers["content-type"]) {
@@ -1011,41 +1335,151 @@ async function handleMessage(message) {
1011
1335
  headers,
1012
1336
  body
1013
1337
  },
1014
- requestID: reqMsg.requestID
1338
+ requestID: message.requestID
1015
1339
  };
1016
1340
  sendMessage(responseMsg, [body]);
1341
+ } catch (error) {
1342
+ messageLogger.error(`[Worker-${workerId}] Request failed: {error}`, {
1343
+ error
1344
+ });
1345
+ const errorMsg = {
1346
+ type: "error",
1347
+ error: error instanceof Error ? error.message : String(error),
1348
+ stack: error instanceof Error ? error.stack : void 0,
1349
+ requestID: message.requestID
1350
+ };
1351
+ sendMessage(errorMsg);
1017
1352
  }
1018
- } catch (error) {
1019
- const errorMsg = {
1020
- type: "error",
1021
- error: error instanceof Error ? error.message : String(error),
1022
- stack: error instanceof Error ? error.stack : void 0,
1023
- requestID: message.requestID
1024
- };
1025
- sendMessage(errorMsg);
1026
1353
  }
1354
+ function handleMessage(event) {
1355
+ const message = event.data;
1356
+ if (message?.type === "cache:response" || message?.type === "cache:error") {
1357
+ messageLogger.debug(`[Worker-${workerId}] Forwarding cache message`, {
1358
+ type: message.type,
1359
+ requestID: message.requestID
1360
+ });
1361
+ handleCacheResponse(message);
1362
+ return;
1363
+ }
1364
+ if (message?.type === "request") {
1365
+ handleFetchRequest(message).catch((error) => {
1366
+ messageLogger.error(`[Worker-${workerId}] Unhandled error: {error}`, {
1367
+ error
1368
+ });
1369
+ });
1370
+ return;
1371
+ }
1372
+ if (message?.type === "shutdown") {
1373
+ messageLogger.debug(`[Worker-${workerId}] Received shutdown signal`);
1374
+ (async () => {
1375
+ try {
1376
+ if (databases) {
1377
+ await databases.closeAll();
1378
+ messageLogger.debug(`[Worker-${workerId}] Databases closed`);
1379
+ }
1380
+ sendMessage({ type: "shutdown-complete" });
1381
+ messageLogger.debug(`[Worker-${workerId}] Shutdown complete`);
1382
+ } catch (error) {
1383
+ messageLogger.error(`[Worker-${workerId}] Shutdown error: {error}`, {
1384
+ error
1385
+ });
1386
+ sendMessage({ type: "shutdown-complete" });
1387
+ }
1388
+ })();
1389
+ return;
1390
+ }
1391
+ if (message?.type) {
1392
+ messageLogger.debug(`[Worker-${workerId}] Unknown message type`, {
1393
+ type: message.type
1394
+ });
1395
+ }
1396
+ }
1397
+ self.addEventListener("message", handleMessage);
1398
+ sendMessage({ type: "ready" });
1399
+ messageLogger.debug(`[Worker-${workerId}] Message loop started`);
1027
1400
  }
1028
- if (typeof onmessage !== "undefined") {
1029
- initializeWorker().then(({ messagePort: _messagePort, sendMessage: send }) => {
1030
- sendMessage = send;
1031
- sendMessage({ type: "worker-ready" });
1032
- }).catch((error) => {
1033
- logger.error("[Worker] Failed to initialize:", error);
1401
+ var SHOVEL_DEFAULT_LOGGERS = [
1402
+ { category: ["shovel"], level: "info", sinks: ["console"] },
1403
+ { category: ["logtape", "meta"], level: "warning", sinks: ["console"] }
1404
+ ];
1405
+ async function createSink(config) {
1406
+ const {
1407
+ impl,
1408
+ path,
1409
+ // Extract path for file-based sinks
1410
+ ...sinkOptions
1411
+ } = config;
1412
+ if (!impl) {
1413
+ throw new Error(
1414
+ `Sink has no impl. Ensure the sink module is configured in shovel.json.`
1415
+ );
1416
+ }
1417
+ if (path !== void 0) {
1418
+ return impl(path, sinkOptions);
1419
+ } else if (Object.keys(sinkOptions).length > 0) {
1420
+ return impl(sinkOptions);
1421
+ } else {
1422
+ return impl();
1423
+ }
1424
+ }
1425
+ function normalizeCategory(category) {
1426
+ return typeof category === "string" ? [category] : category;
1427
+ }
1428
+ async function configureLogging(loggingConfig) {
1429
+ const userSinks = loggingConfig.sinks || {};
1430
+ const userLoggers = loggingConfig.loggers || [];
1431
+ const sinks = {
1432
+ console: getConsoleSink()
1433
+ };
1434
+ for (const [name, config] of Object.entries(userSinks)) {
1435
+ sinks[name] = await createSink(config);
1436
+ }
1437
+ const userCategoryKeys = new Set(
1438
+ userLoggers.map((l) => JSON.stringify(normalizeCategory(l.category)))
1439
+ );
1440
+ const mergedLoggers = [
1441
+ // Shovel defaults (unless overridden by user)
1442
+ ...SHOVEL_DEFAULT_LOGGERS.filter(
1443
+ (l) => !userCategoryKeys.has(JSON.stringify(normalizeCategory(l.category)))
1444
+ ),
1445
+ // User loggers
1446
+ ...userLoggers
1447
+ ];
1448
+ const loggers = mergedLoggers.map((loggerConfig) => {
1449
+ const result = {
1450
+ category: normalizeCategory(loggerConfig.category)
1451
+ };
1452
+ if (loggerConfig.level) {
1453
+ result.lowestLevel = loggerConfig.level;
1454
+ }
1455
+ result.sinks = loggerConfig.sinks ?? ["console"];
1456
+ if (loggerConfig.parentSinks) {
1457
+ result.parentSinks = loggerConfig.parentSinks;
1458
+ }
1459
+ return result;
1460
+ });
1461
+ await configure({
1462
+ reset: true,
1463
+ sinks,
1464
+ loggers
1034
1465
  });
1035
1466
  }
1036
1467
  export {
1037
- ActivateEvent,
1468
+ CustomDatabaseStorage,
1469
+ CustomLoggerStorage,
1038
1470
  DedicatedWorkerGlobalScope,
1039
- ExtendableEvent,
1040
1471
  ExtendableMessageEvent,
1041
- FetchEvent,
1042
- InstallEvent,
1043
1472
  Notification,
1044
1473
  NotificationEvent,
1045
1474
  PushEvent,
1475
+ RequestCookieStore,
1046
1476
  ServiceWorkerGlobals,
1477
+ ShovelActivateEvent,
1047
1478
  ShovelClient,
1048
1479
  ShovelClients,
1480
+ ShovelExtendableEvent,
1481
+ ShovelFetchEvent,
1482
+ ShovelInstallEvent,
1049
1483
  ShovelNavigationPreloadManager,
1050
1484
  ShovelPushMessageData,
1051
1485
  ShovelServiceWorker,
@@ -1053,5 +1487,14 @@ export {
1053
1487
  ShovelServiceWorkerRegistration,
1054
1488
  ShovelWindowClient,
1055
1489
  SyncEvent,
1056
- WorkerGlobalScope
1490
+ WorkerGlobalScope,
1491
+ configureLogging,
1492
+ createCacheFactory,
1493
+ createDatabaseFactory,
1494
+ createDirectoryFactory,
1495
+ initWorkerRuntime,
1496
+ parseCookieHeader,
1497
+ parseSetCookieHeader,
1498
+ serializeCookie,
1499
+ startWorkerMessageLoop
1057
1500
  };