@inkandswitch/patchwork-bootloader 0.2.6 → 0.2.8
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/CHANGELOG.md +18 -0
- package/dist/automerge-worker.d.ts +1 -0
- package/dist/automerge-worker.js +505 -0
- package/dist/externals.js +0 -1
- package/dist/service-worker.js +99 -273
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +89 -106
- package/dist/site.d.ts +3 -7
- package/dist/site.js +26 -55
- package/dist/sync-config.d.ts +8 -0
- package/dist/sync-config.js +13 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.js +7 -1
- package/dist/vite/service-worker-plugin.js +25 -9
- package/package.json +19 -19
- package/src/automerge-worker.ts +647 -0
- package/src/externals.ts +0 -1
- package/src/service-worker.ts +124 -349
- package/src/setup.ts +105 -118
- package/src/site.ts +31 -66
- package/src/sync-config.ts +23 -0
- package/src/types.ts +98 -0
- package/src/vite/service-worker-plugin.ts +26 -11
- package/tsconfig.json +1 -1
- package/dist/sw-logger.d.ts +0 -105
- package/dist/sw-logger.js +0 -366
- package/src/sw-logger.ts +0 -463
package/dist/service-worker.js
CHANGED
|
@@ -1,61 +1,28 @@
|
|
|
1
1
|
/// <reference types="service-worker-types" />
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
import { initializeWasm, hasHeads } from "@automerge/automerge/slim";
|
|
10
|
-
// eslint-disable-next-line
|
|
11
|
-
// @ts-ignore — initSync is a wasm-bindgen runtime helper not in the .d.ts
|
|
12
|
-
import { initSync as initSubductionSync } from "@automerge/automerge-subduction/slim";
|
|
13
|
-
import { WebCryptoSigner } from "@automerge/automerge-subduction/slim";
|
|
14
|
-
import { Repo, isValidAutomergeUrl, parseAutomergeUrl, stringifyAutomergeUrl, } from "@automerge/automerge-repo/slim";
|
|
15
|
-
import { resolvePath } from "@inkandswitch/patchwork-filesystem";
|
|
16
|
-
// Small adapters — bundled directly into the SW
|
|
17
|
-
import { IndexedDBStorageAdapter } from "@automerge/automerge-repo-storage-indexeddb";
|
|
18
|
-
import { MessageChannelNetworkAdapter } from "@automerge/automerge-repo-network-messagechannel";
|
|
19
|
-
import { initializeAutomergeRepoKeyhiveRust, initKeyhiveWasm, } from "@automerge/automerge-repo-keyhive";
|
|
20
|
-
// TEMPORARY: enable debug npm module in SW context (no localStorage available)
|
|
2
|
+
// The service worker holds no automerge repo — that lives in the automerge
|
|
3
|
+
// SharedWorker (automerge-worker.ts). This worker manages the cache. When a
|
|
4
|
+
// special URL misses the cache it broadcasts a handoff request; the
|
|
5
|
+
// automerge worker resolves it, puts the response in our cache, and replies
|
|
6
|
+
// "cached" (or "response" for errors and other things that shouldn't be
|
|
7
|
+
// cached).
|
|
8
|
+
import { HANDOFF_CHANNEL, } from "./types.js";
|
|
21
9
|
let cachename = "default";
|
|
22
10
|
let debugging = false;
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// Initialized eagerly so it's available for the entire SW lifetime.
|
|
28
|
-
// Access from the SW inspector console via self.printLogs(), self.tailLogs(),
|
|
29
|
-
// self.exportLogs(), self.clearLogs().
|
|
30
|
-
const slog = SwLogger.open().then((logger) => {
|
|
31
|
-
self.slog = logger;
|
|
32
|
-
self.printLogs = async (n = 200) => {
|
|
33
|
-
const entries = await logger.tail(n);
|
|
34
|
-
for (const e of entries) {
|
|
35
|
-
const prefix = `[${e.ts}] [${e.level}]`;
|
|
36
|
-
if (e.data !== undefined) {
|
|
37
|
-
console.log(prefix, e.msg, e.data);
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
console.log(prefix, e.msg);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
console.log(`--- ${entries.length} entries ---`);
|
|
44
|
-
};
|
|
45
|
-
self.tailLogs = (n = 200) => logger.tail(n);
|
|
46
|
-
self.exportLogs = () => logger.exportAll();
|
|
47
|
-
self.clearLogs = () => logger.clear();
|
|
48
|
-
logger.info("sw-logger initialized");
|
|
49
|
-
return logger;
|
|
50
|
-
});
|
|
51
|
-
const siteName = typeof __SITE_NAME__ !== "undefined" ? __SITE_NAME__ : "tiny-patchwork";
|
|
52
|
-
const cacheableStatuses = [200, 203, 204, 206];
|
|
11
|
+
const cacheableStatuses = [200, 203, 204];
|
|
12
|
+
// The automerge worker times its own resolution out after 30s and replies
|
|
13
|
+
// with an error, so this only fires when nobody is listening at all.
|
|
14
|
+
const HANDOFF_TIMEOUT_MS = 35_000;
|
|
53
15
|
function log(...args) {
|
|
54
16
|
if (!debugging)
|
|
55
17
|
return;
|
|
56
18
|
console.log.call(console, `%cpatchwork:serviceworker%c\n`, `color: #00ffcc; font-weight: bold`, "color: inherit", ...args);
|
|
57
19
|
}
|
|
58
|
-
self.addEventListener("install", () =>
|
|
20
|
+
self.addEventListener("install", (event) => {
|
|
21
|
+
// waitUntil keeps the worker alive until skipWaiting resolves, so a freshly
|
|
22
|
+
// installed SW reliably jumps the "waiting" queue instead of stalling until
|
|
23
|
+
// every old tab closes.
|
|
24
|
+
event.waitUntil(self.skipWaiting());
|
|
25
|
+
});
|
|
59
26
|
async function clearOldCaches() {
|
|
60
27
|
const cacheWhitelist = [cachename];
|
|
61
28
|
const cacheNames = await caches.keys();
|
|
@@ -66,166 +33,17 @@ async function clearOldCaches() {
|
|
|
66
33
|
});
|
|
67
34
|
await Promise.all(deletePromises);
|
|
68
35
|
}
|
|
69
|
-
self.addEventListener("activate",
|
|
70
|
-
|
|
71
|
-
|
|
36
|
+
self.addEventListener("activate", (event) => {
|
|
37
|
+
// Without waitUntil the activate event settles immediately and clients.claim()
|
|
38
|
+
// runs detached — the new worker can be killed before it takes control, so
|
|
39
|
+
// existing tabs keep talking to the old SW. Extend the event instead.
|
|
40
|
+
event.waitUntil((async () => {
|
|
41
|
+
await clearOldCaches();
|
|
42
|
+
await self.clients.claim();
|
|
43
|
+
})());
|
|
72
44
|
});
|
|
73
|
-
let repoHivePromise = null;
|
|
74
|
-
const useKeyhive = typeof __KEYHIVE__ !== "undefined" && __KEYHIVE__;
|
|
75
|
-
function getRepoHive() {
|
|
76
|
-
if (!repoHivePromise) {
|
|
77
|
-
repoHivePromise = (async () => {
|
|
78
|
-
const logger = await slog;
|
|
79
|
-
logger.info("getRepo: starting");
|
|
80
|
-
logger.info("fetching wasm modules");
|
|
81
|
-
const [amWasmBuf, sdnWasmBuf] = await Promise.all([
|
|
82
|
-
fetch("/automerge.wasm?sw").then((r) => r.arrayBuffer()),
|
|
83
|
-
fetch("/subduction.wasm").then((r) => r.arrayBuffer()),
|
|
84
|
-
]);
|
|
85
|
-
initSubductionSync(new Uint8Array(sdnWasmBuf));
|
|
86
|
-
await initializeWasm(new Uint8Array(amWasmBuf));
|
|
87
|
-
logger.info("wasm initialized");
|
|
88
|
-
if (!useKeyhive) {
|
|
89
|
-
const signer = await WebCryptoSigner.setup();
|
|
90
|
-
const repo = new Repo({
|
|
91
|
-
storage: new IndexedDBStorageAdapter(),
|
|
92
|
-
signer,
|
|
93
|
-
peerId: ("service-worker-" +
|
|
94
|
-
Math.random().toString(36).slice(2)),
|
|
95
|
-
async sharePolicy(peerId) {
|
|
96
|
-
return peerId.includes("storage-server");
|
|
97
|
-
},
|
|
98
|
-
enableRemoteHeadsGossiping: true,
|
|
99
|
-
subductionWebsocketEndpoints: SUBDUCTION_ENDPOINTS,
|
|
100
|
-
});
|
|
101
|
-
self.repo = repo;
|
|
102
|
-
logger.info("repo constructed (no keyhive), waiting for network subsystem");
|
|
103
|
-
repo.networkSubsystem.whenReady().then(() => {
|
|
104
|
-
logger.info("repo network subsystem ready");
|
|
105
|
-
});
|
|
106
|
-
return { repo };
|
|
107
|
-
}
|
|
108
|
-
initKeyhiveWasm();
|
|
109
|
-
const keyhiveStorage = new IndexedDBStorageAdapter(`${siteName}-keyhive`);
|
|
110
|
-
// Keyhive bootstrap needs to run before Repo creation but
|
|
111
|
-
// the adapter needs the subduction instance from the Repo.
|
|
112
|
-
// A deferred promise breaks the cycle.
|
|
113
|
-
let resolveRepoSubduction;
|
|
114
|
-
const repoSubductionPromise = new Promise((resolve) => {
|
|
115
|
-
resolveRepoSubduction = resolve;
|
|
116
|
-
});
|
|
117
|
-
// We use the Rust variant of Keyhive initialization to talk
|
|
118
|
-
// to the Rust keyhive-enabled subduction sync server.
|
|
119
|
-
const hive = await initializeAutomergeRepoKeyhiveRust({
|
|
120
|
-
storage: keyhiveStorage,
|
|
121
|
-
peerIdSuffix: `${siteName}-worker` + Math.random().toString(36).slice(2),
|
|
122
|
-
subduction: repoSubductionPromise,
|
|
123
|
-
automaticArchiveIngestion: true,
|
|
124
|
-
cachingMode: "periodic",
|
|
125
|
-
});
|
|
126
|
-
const signer = await hive.constructSubductionSigner();
|
|
127
|
-
const repo = new Repo({
|
|
128
|
-
storage: new IndexedDBStorageAdapter(),
|
|
129
|
-
signer,
|
|
130
|
-
subductionWebsocketEndpoints: SUBDUCTION_ENDPOINTS,
|
|
131
|
-
peerId: hive.peerId,
|
|
132
|
-
enableRemoteHeadsGossiping: true,
|
|
133
|
-
idFactory: hive.idFactory,
|
|
134
|
-
//network: [new WebSocketClientAdapter("wss://sync3.automerge.org")],
|
|
135
|
-
});
|
|
136
|
-
repo.subduction.then(resolveRepoSubduction);
|
|
137
|
-
hive.linkRepo(repo);
|
|
138
|
-
self.repo = repo;
|
|
139
|
-
self.hive = hive;
|
|
140
|
-
logger.info("repo constructed, waiting for network subsystem");
|
|
141
|
-
// Don't block getRepoHive() on whenReady() — the network subsystem starts
|
|
142
|
-
// with only the subduction adapter, and the MessageChannel adapter is
|
|
143
|
-
// added later via connectPort (which awaits getRepoHive). Blocking here
|
|
144
|
-
// would deadlock that path and starve the fetch handler.
|
|
145
|
-
repo.networkSubsystem.whenReady().then(() => {
|
|
146
|
-
logger.info("repo network subsystem ready");
|
|
147
|
-
});
|
|
148
|
-
hive.networkAdapter.whenReady().then(() => {
|
|
149
|
-
hive.networkAdapter.syncKeyhive();
|
|
150
|
-
});
|
|
151
|
-
return { hive, repo };
|
|
152
|
-
})();
|
|
153
|
-
// If construction fails (e.g. wasm fetch errors out because the SW was
|
|
154
|
-
// terminated mid-flight), don't permanently cache the rejection — clear
|
|
155
|
-
// the slot so the next caller can retry from scratch.
|
|
156
|
-
repoHivePromise.catch(() => {
|
|
157
|
-
repoHivePromise = null;
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
return repoHivePromise;
|
|
161
|
-
}
|
|
162
|
-
// Connect client MessagePorts to the repo for sync
|
|
163
|
-
async function connectPort(port) {
|
|
164
|
-
const { hive, repo } = await getRepoHive();
|
|
165
|
-
const networkAdapter = new MessageChannelNetworkAdapter(port, { useWeakRef: true });
|
|
166
|
-
if (!hive) {
|
|
167
|
-
repo.networkSubsystem.addNetworkAdapter(networkAdapter);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
const onlyShareWithHardcodedServerPeerId = false;
|
|
171
|
-
const periodicallyRequestKeyhiveSync = false;
|
|
172
|
-
const keyhiveNetworkAdapter = hive.createKeyhiveNetworkAdapter(networkAdapter, onlyShareWithHardcodedServerPeerId, periodicallyRequestKeyhiveSync, 2000);
|
|
173
|
-
keyhiveNetworkAdapter.on("message", async (msg) => {
|
|
174
|
-
if ((msg.type === "sync" || msg.type === "request") && msg.documentId) {
|
|
175
|
-
const handle = repo.handles[msg.documentId];
|
|
176
|
-
if (!handle || handle.state === "unavailable") {
|
|
177
|
-
const url = `automerge:${msg.documentId}`;
|
|
178
|
-
repo.findWithProgress(url);
|
|
179
|
-
repo.shareConfigChanged();
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
keyhiveNetworkAdapter.on("ingest-remote", () => {
|
|
184
|
-
hive.networkAdapter.syncKeyhive?.();
|
|
185
|
-
repo.shareConfigChanged();
|
|
186
|
-
});
|
|
187
|
-
repo.networkSubsystem.addNetworkAdapter(keyhiveNetworkAdapter);
|
|
188
|
-
}
|
|
189
45
|
self.addEventListener("message", async (event) => {
|
|
190
|
-
if (event.data.type == "
|
|
191
|
-
// Keepalive — Chromium idles out service workers after ~30s of inactivity.
|
|
192
|
-
// Reply via the provided port if any; the message event itself also resets
|
|
193
|
-
// the idle timer.
|
|
194
|
-
const [pongPort] = event.ports;
|
|
195
|
-
log("ping");
|
|
196
|
-
if (pongPort) {
|
|
197
|
-
pongPort.postMessage({ type: "pong", workerInstanceId });
|
|
198
|
-
log("pong");
|
|
199
|
-
pongPort.close();
|
|
200
|
-
}
|
|
201
|
-
else if (event.source) {
|
|
202
|
-
event.source.postMessage({
|
|
203
|
-
type: "pong",
|
|
204
|
-
workerInstanceId,
|
|
205
|
-
});
|
|
206
|
-
log("pong");
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
else if (event.data.type == "port") {
|
|
210
|
-
log("received messagechannel");
|
|
211
|
-
const [port] = event.ports;
|
|
212
|
-
const source = event.source;
|
|
213
|
-
const id = event.data.id;
|
|
214
|
-
// event.waitUntil keeps the SW alive until the work completes. Without
|
|
215
|
-
// it, the browser can terminate the SW the moment this synchronous block
|
|
216
|
-
// returns, killing the in-flight wasm fetch.
|
|
217
|
-
event.waitUntil(connectPort(port).then(() => source?.postMessage({ type: "port-ready", id, workerInstanceId }), (err) => {
|
|
218
|
-
console.error("connectPort failed", err);
|
|
219
|
-
// Tell the client we failed so it doesn't hang forever.
|
|
220
|
-
source?.postMessage({
|
|
221
|
-
type: "port-failed",
|
|
222
|
-
id,
|
|
223
|
-
error: String(err),
|
|
224
|
-
workerInstanceId,
|
|
225
|
-
});
|
|
226
|
-
}));
|
|
227
|
-
}
|
|
228
|
-
else if (event.data.type == "cachename") {
|
|
46
|
+
if (event.data.type == "cachename") {
|
|
229
47
|
const nextCachename = event.data.cachename;
|
|
230
48
|
if (cachename == nextCachename) {
|
|
231
49
|
return;
|
|
@@ -239,49 +57,62 @@ self.addEventListener("message", async (event) => {
|
|
|
239
57
|
log("serviceworker debugging enabled");
|
|
240
58
|
}
|
|
241
59
|
});
|
|
242
|
-
// ──
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
if (
|
|
248
|
-
|
|
60
|
+
// ── Handoff to the automerge worker ────────────────────────────────────
|
|
61
|
+
const handoffChannel = new BroadcastChannel(HANDOFF_CHANNEL);
|
|
62
|
+
const pendingHandoffs = new Map();
|
|
63
|
+
handoffChannel.addEventListener("message", (event) => {
|
|
64
|
+
const data = event.data;
|
|
65
|
+
if (data?.type === "cached" || data?.type === "response") {
|
|
66
|
+
const pending = pendingHandoffs.get(data.id);
|
|
67
|
+
if (!pending) {
|
|
68
|
+
return log(`no pending handoff for id ${data.id}`);
|
|
69
|
+
}
|
|
70
|
+
pending.resolvers.resolve(data);
|
|
249
71
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const latestHeads = folder.heads();
|
|
258
|
-
const url = stringifyAutomergeUrl({ documentId, heads: latestHeads });
|
|
259
|
-
let location = `/${encodeURIComponent(url)}`;
|
|
260
|
-
if (path.length)
|
|
261
|
-
location += `/${path.join("/")}`;
|
|
262
|
-
return Response.redirect(location, 307);
|
|
72
|
+
else if (data?.type === "online") {
|
|
73
|
+
// The automerge worker (re)started — re-broadcast anything still in
|
|
74
|
+
// flight so requests that raced its boot aren't stranded.
|
|
75
|
+
for (const { message } of pendingHandoffs.values()) {
|
|
76
|
+
log(`re-broadcasting handoff ${message.id} to the fresh worker`);
|
|
77
|
+
handoffChannel.postMessage(message);
|
|
78
|
+
}
|
|
263
79
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
80
|
+
});
|
|
81
|
+
function handoff(request, handoffURL) {
|
|
82
|
+
const id = crypto.randomUUID();
|
|
83
|
+
const resolvers = Promise.withResolvers();
|
|
84
|
+
const message = {
|
|
85
|
+
id,
|
|
86
|
+
type: "request",
|
|
87
|
+
cachename,
|
|
88
|
+
request: {
|
|
89
|
+
url: request.url,
|
|
90
|
+
handoffURL: handoffURL.href,
|
|
91
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
92
|
+
method: request.method,
|
|
93
|
+
destination: request.destination,
|
|
94
|
+
referrer: request.referrer,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
pendingHandoffs.set(id, { message, resolvers });
|
|
98
|
+
log(`broadcasting handoff request for cache ${cachename}`, message);
|
|
99
|
+
handoffChannel.postMessage(message);
|
|
100
|
+
const timeout = setTimeout(() => {
|
|
101
|
+
resolvers.reject(new Error(`no reply from the automerge worker after ${HANDOFF_TIMEOUT_MS}ms`));
|
|
102
|
+
}, HANDOFF_TIMEOUT_MS);
|
|
103
|
+
return resolvers.promise.finally(() => {
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
pendingHandoffs.delete(id);
|
|
269
106
|
});
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const rootHandle = baseHandle.view(heads);
|
|
274
|
-
const resolved = await resolvePath(repo, rootHandle, path.map(decodeURIComponent));
|
|
275
|
-
if (!resolved) {
|
|
276
|
-
throw new Error(`couldn't resolve ${path.join("/")} in folder at ${maybeAutomergeUrl}`);
|
|
277
|
-
}
|
|
278
|
-
const body = resolved.content instanceof Uint8Array
|
|
279
|
-
? new Uint8Array(resolved.content)
|
|
280
|
-
: resolved.content;
|
|
281
|
-
const headers = new Headers({ "content-type": resolved.type });
|
|
107
|
+
}
|
|
108
|
+
function withSpecialHeaders(response) {
|
|
109
|
+
const headers = new Headers(response.headers);
|
|
282
110
|
headers.set("cross-origin-embedder-policy", "credentialless");
|
|
283
111
|
headers.set("cross-origin-resource-policy", "cross-origin");
|
|
284
|
-
return new Response(body
|
|
112
|
+
return new Response(response.body ?? null, {
|
|
113
|
+
status: response.status ?? 200,
|
|
114
|
+
headers,
|
|
115
|
+
});
|
|
285
116
|
}
|
|
286
117
|
// ── Fetch handler ──────────────────────────────────────────────────────
|
|
287
118
|
self.addEventListener("fetch", (fetchEvent) => {
|
|
@@ -290,13 +121,13 @@ self.addEventListener("fetch", (fetchEvent) => {
|
|
|
290
121
|
if (request.method !== "GET")
|
|
291
122
|
return fetchEvent.respondWith(fetch(request));
|
|
292
123
|
const url = new URL(fetchEvent.request.url);
|
|
293
|
-
let
|
|
124
|
+
let handoffURL;
|
|
294
125
|
if (url.hostname == self.location.hostname &&
|
|
295
126
|
url.port == self.location.port &&
|
|
296
127
|
url.protocol == self.location.protocol) {
|
|
297
128
|
try {
|
|
298
|
-
|
|
299
|
-
log(`received special request ${
|
|
129
|
+
handoffURL = new URL(decodeURIComponent(url.pathname.slice(1)));
|
|
130
|
+
log(`received special request ${handoffURL}`);
|
|
300
131
|
}
|
|
301
132
|
catch { }
|
|
302
133
|
}
|
|
@@ -304,29 +135,28 @@ self.addEventListener("fetch", (fetchEvent) => {
|
|
|
304
135
|
const cache = await caches.open(cachename);
|
|
305
136
|
const match = await cache.match(request);
|
|
306
137
|
try {
|
|
307
|
-
if (
|
|
138
|
+
if (handoffURL) {
|
|
308
139
|
if (match) {
|
|
309
|
-
log(`serving ${
|
|
310
|
-
|
|
311
|
-
headers.set("cross-origin-embedder-policy", "credentialless");
|
|
312
|
-
headers.set("cross-origin-resource-policy", "cross-origin");
|
|
313
|
-
return new Response(match.body, {
|
|
314
|
-
status: match.status,
|
|
315
|
-
headers,
|
|
316
|
-
});
|
|
140
|
+
log(`serving ${handoffURL} from cache ${cachename}`);
|
|
141
|
+
return withSpecialHeaders(match);
|
|
317
142
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (
|
|
323
|
-
|
|
143
|
+
log(`handing ${handoffURL} off to the automerge worker`);
|
|
144
|
+
const replyPromise = handoff(request, handoffURL);
|
|
145
|
+
fetchEvent.waitUntil(replyPromise.catch(() => { }));
|
|
146
|
+
const reply = await replyPromise;
|
|
147
|
+
if (reply.type === "response") {
|
|
148
|
+
// errors, redirects and other things that shouldn't be cached
|
|
149
|
+
log(`serving handed-off response for ${handoffURL}`, reply);
|
|
150
|
+
return withSpecialHeaders(reply.response);
|
|
324
151
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
152
|
+
// reply.type === "cached": the automerge worker has put the
|
|
153
|
+
// response in our cache
|
|
154
|
+
const cached = await cache.match(request);
|
|
155
|
+
if (!cached) {
|
|
156
|
+
return new Response(`the automerge worker reported ${handoffURL} cached, but it has no match in ${cachename}`, { status: 500 });
|
|
328
157
|
}
|
|
329
|
-
|
|
158
|
+
log(`serving ${handoffURL} from cache ${cachename} after handoff`);
|
|
159
|
+
return withSpecialHeaders(cached);
|
|
330
160
|
}
|
|
331
161
|
else {
|
|
332
162
|
const response = await fetch(request).catch(() => null);
|
|
@@ -349,11 +179,7 @@ self.addEventListener("fetch", (fetchEvent) => {
|
|
|
349
179
|
const message = error instanceof Error
|
|
350
180
|
? `${error.message}\n\n${error.stack}`
|
|
351
181
|
: String(error);
|
|
352
|
-
|
|
353
|
-
logger.error(`service worker error resolving ${request.url}${specialURL ? ` (for: ${specialURL})` : ""}`, {
|
|
354
|
-
message: error instanceof Error ? error.message : String(error),
|
|
355
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
356
|
-
});
|
|
182
|
+
console.error(`service worker error resolving ${request.url}${handoffURL ? ` (for: ${handoffURL})` : ""}`, error);
|
|
357
183
|
if (match)
|
|
358
184
|
return match;
|
|
359
185
|
return new Response(message, {
|
package/dist/setup.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { SetupServiceWorkerOptions, SetupServiceWorkerResult } from "./types.js";
|
|
2
2
|
export declare function bumpServiceWorkerCache(sw?: ServiceWorker | null): void;
|
|
3
|
+
export declare function connectClassicSync(server?: string): Promise<void>;
|
|
3
4
|
export default function setupServiceWorker(options?: SetupServiceWorkerOptions): Promise<SetupServiceWorkerResult>;
|