@lls/offline 24.22.0-bdccc0f6 → 24.22.0-debug
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/lib/assets/fetch-worker.js +46 -126
- package/lib/assets/offline-worker.js +55 -252
- package/lib/index.js +49 -138
- package/package.json +6 -12
|
@@ -11,15 +11,16 @@ var makeSyncWorker = (offlineWorker2, methodNames, dbg) => {
|
|
|
11
11
|
}
|
|
12
12
|
} else {
|
|
13
13
|
if (methodName && !methodName.startsWith("async") && !(methodName in cbks)) {
|
|
14
|
-
console.error(`syncWorker:
|
|
14
|
+
console.error(`syncWorker: ${dbg}`, eventId, methodName, error);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
methodName && cbks[methodName]?.(arg);
|
|
18
18
|
};
|
|
19
|
+
offlineWorker2.onmessage = fn;
|
|
19
20
|
const makeFn = (methodName) => {
|
|
20
21
|
return ({ ports, ...arg } = {}) => {
|
|
21
22
|
return new Promise((resolve, reject) => {
|
|
22
|
-
const eventId = `${methodName}:${Date.now()}
|
|
23
|
+
const eventId = `${methodName}:${Date.now()}`;
|
|
23
24
|
dic[eventId] = { resolve, reject };
|
|
24
25
|
offlineWorker2.postMessage({ eventId, methodName, arg }, ports);
|
|
25
26
|
});
|
|
@@ -30,39 +31,25 @@ var makeSyncWorker = (offlineWorker2, methodNames, dbg) => {
|
|
|
30
31
|
cbks[key] = fn2;
|
|
31
32
|
}
|
|
32
33
|
});
|
|
33
|
-
return
|
|
34
|
-
...obj,
|
|
35
|
-
handler: {
|
|
36
|
-
canHandle: (methodName) => {
|
|
37
|
-
return methodName in cbks || methodNames.includes(methodName);
|
|
38
|
-
},
|
|
39
|
-
handle: fn
|
|
40
|
-
}
|
|
41
|
-
};
|
|
34
|
+
return obj;
|
|
42
35
|
};
|
|
43
36
|
var a = (a2) => a2;
|
|
44
37
|
var makeSyncHandler = a(
|
|
45
|
-
(sender, services) => ({
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (typeof methodName === "string" && methodName in services && services[methodName]) {
|
|
52
|
-
const results = await services[methodName](arg, { ports });
|
|
53
|
-
sender.postMessage({ eventId, methodName, results });
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
throw new Error(`invalid methodName ${Object.keys(services)} |${String(methodName)}`);
|
|
57
|
-
} catch (e) {
|
|
58
|
-
console.debug("failed", e);
|
|
59
|
-
sender.postMessage({ eventId, methodName, error: e });
|
|
38
|
+
(sender, services) => async ({ data: { eventId, methodName, arg } = {}, ports }) => {
|
|
39
|
+
try {
|
|
40
|
+
if (typeof methodName === "string" && methodName in services && services[methodName]) {
|
|
41
|
+
const results = await services[methodName](arg, { ports });
|
|
42
|
+
sender.postMessage({ eventId, methodName, results });
|
|
43
|
+
return;
|
|
60
44
|
}
|
|
45
|
+
throw new Error(`invalid methodName ${Object.keys(services)} |${String(methodName)}`);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.debug("failed", e);
|
|
48
|
+
sender.postMessage({ eventId, methodName, error: e });
|
|
61
49
|
}
|
|
62
|
-
}
|
|
50
|
+
}
|
|
63
51
|
);
|
|
64
52
|
var jsonToBuf = (json) => new TextEncoder().encode(JSON.stringify(json));
|
|
65
|
-
var openCache = async (mode) => caches.open(`LLS_OFFLINE_${mode}`);
|
|
66
53
|
|
|
67
54
|
// src/worker/swFetchScreenCall.ts
|
|
68
55
|
var zipSlugName = (str) => str.replace(/[^a-zA-Z0-9-.]/g, "");
|
|
@@ -75,15 +62,6 @@ var cocoreFetchPartialPageScreen = async (_ev, { offlineWorkerSync: offlineWorke
|
|
|
75
62
|
const gqlPage = await offlineWorkerSync2.loadPageContent({ entryId });
|
|
76
63
|
return { data: gqlPage };
|
|
77
64
|
};
|
|
78
|
-
var cocoreFetchBookScreen = async (_ev, { offlineWorkerSync: offlineWorkerSync2, variables, knownLinks: knownLinks2 }) => {
|
|
79
|
-
const key = `gqls/${zipSlugName(variables.uris[0] || "")}.json`;
|
|
80
|
-
const entryId = knownLinks2.get(key);
|
|
81
|
-
if (!entryId) {
|
|
82
|
-
throw new Error(`uri not found ${key}`);
|
|
83
|
-
}
|
|
84
|
-
const gqlBook = await offlineWorkerSync2.loadPageContent({ entryId });
|
|
85
|
-
return { data: gqlBook };
|
|
86
|
-
};
|
|
87
65
|
var cocoreFetchPartialPageSiblingsScreen = async (_ev) => {
|
|
88
66
|
return { data: { viewer: {} } };
|
|
89
67
|
};
|
|
@@ -198,7 +176,6 @@ var coreFetchPagesApi = async (ev, { offlineWorkerSync: offlineWorkerSync2, vari
|
|
|
198
176
|
};
|
|
199
177
|
var handles = {
|
|
200
178
|
coreFetchBookDrawerScreen,
|
|
201
|
-
cocoreFetchBookScreen,
|
|
202
179
|
coreFetchPagesApi,
|
|
203
180
|
cocoreFetchPartialPageScreen,
|
|
204
181
|
cocoreFetchPartialPageSiblingsScreen
|
|
@@ -207,7 +184,7 @@ var canHandleScreenCall = (url) => new RegExp(Object.keys(handles).join("|")).te
|
|
|
207
184
|
var handleScreenCall = async (ev, { offlineWorkerSync: offlineWorkerSync2, knownLinks: knownLinks2 }) => {
|
|
208
185
|
try {
|
|
209
186
|
const variables = (
|
|
210
|
-
// @ts-
|
|
187
|
+
// @ts-ignore TODO: handle variables from body(stream) properly!!
|
|
211
188
|
ev.request.body?.variables || JSON.parse(new URLSearchParams(new URL(ev.request.url).search).get("variables") || "{}")
|
|
212
189
|
);
|
|
213
190
|
console.debug("variables are", variables, "vs", ev.request.body);
|
|
@@ -222,28 +199,23 @@ var handleScreenCall = async (ev, { offlineWorkerSync: offlineWorkerSync2, known
|
|
|
222
199
|
return { status: 500, json: JSON.stringify(e) };
|
|
223
200
|
}
|
|
224
201
|
};
|
|
225
|
-
var setKnownLinks = (knownLinks2, { entryNames, bookId }) => {
|
|
226
|
-
entryNames.forEach((entryName) => {
|
|
227
|
-
const link = entryName.replace(new RegExp(`^\\d+/media/|^${bookId}/medias/`), "").replace(new RegExp(`^${bookId}/gqls/`), "gqls/");
|
|
228
|
-
knownLinks2.set(link, { entryName, bookId });
|
|
229
|
-
});
|
|
230
|
-
};
|
|
231
202
|
|
|
232
203
|
// src/worker/fetch-worker.ts
|
|
233
|
-
var
|
|
234
|
-
var CONF_ENV = "development";
|
|
235
|
-
var fetchAsset = async (_ev, { entryId, mimeType, offlineWorkerSync: offlineWorkerSync2 }) => {
|
|
204
|
+
var fetchAsset = async (ev, { entryId, mimeType, offlineWorkerSync: offlineWorkerSync2 }) => {
|
|
236
205
|
try {
|
|
237
206
|
const buf = await offlineWorkerSync2.readZipEntry(entryId);
|
|
207
|
+
const url = ev.request.url;
|
|
238
208
|
return new Response(buf, {
|
|
239
209
|
headers: {
|
|
240
210
|
"Cache-Control": "max-age=600, s-maxage=600, public, proxy-revalidate",
|
|
241
|
-
"Content-Length": buf.byteLength,
|
|
242
211
|
"Content-Type": mimeType
|
|
243
212
|
},
|
|
244
213
|
// @ts-ignore: poor ts typing
|
|
214
|
+
ok: true,
|
|
215
|
+
redirected: false,
|
|
245
216
|
status: 200,
|
|
246
|
-
|
|
217
|
+
type: "cors",
|
|
218
|
+
url
|
|
247
219
|
});
|
|
248
220
|
} catch (e) {
|
|
249
221
|
console.error("FAILED fetchAsset", e);
|
|
@@ -251,7 +223,8 @@ var fetchAsset = async (_ev, { entryId, mimeType, offlineWorkerSync: offlineWork
|
|
|
251
223
|
// @ts-ignore: poor ts typing
|
|
252
224
|
ok: false,
|
|
253
225
|
status: 500,
|
|
254
|
-
|
|
226
|
+
type: "cors",
|
|
227
|
+
url: ev.request.url
|
|
255
228
|
});
|
|
256
229
|
}
|
|
257
230
|
};
|
|
@@ -291,42 +264,7 @@ var ACCEPTED_EXTENSIONS = {
|
|
|
291
264
|
".gif": "image/gif",
|
|
292
265
|
".webm": "video/webm",
|
|
293
266
|
".m4a": "audio/mp4",
|
|
294
|
-
".webp": "image/webp"
|
|
295
|
-
".riv": "application/octet-stream",
|
|
296
|
-
".pdf": "application/pdf"
|
|
297
|
-
};
|
|
298
|
-
var putInCache = async (request, response) => {
|
|
299
|
-
const cache = await openCache(CONF_ENV);
|
|
300
|
-
await cache.put(request, response);
|
|
301
|
-
};
|
|
302
|
-
var onlineFirst = async (request, event) => {
|
|
303
|
-
try {
|
|
304
|
-
const responseFromNetwork = await fetch(request);
|
|
305
|
-
const cached = putInCache(request, responseFromNetwork.clone());
|
|
306
|
-
if (request.url.endsWith(".zip")) {
|
|
307
|
-
await cached;
|
|
308
|
-
event.waitUntil(Promise.resolve());
|
|
309
|
-
} else {
|
|
310
|
-
event.waitUntil(cached);
|
|
311
|
-
}
|
|
312
|
-
return responseFromNetwork;
|
|
313
|
-
} catch (e) {
|
|
314
|
-
const over = /\/\?story=basic/.test(request.url) ? request.url.replace(/\/\?story=.*$/, "") : request;
|
|
315
|
-
const responseFromCache = await caches.match(over);
|
|
316
|
-
if (!responseFromCache) {
|
|
317
|
-
return new Response(
|
|
318
|
-
`FAILED fetch ${e && typeof e === "object" && "message" in e ? e.message : e?.toString?.()}`,
|
|
319
|
-
{
|
|
320
|
-
// @ts-ignore: poor ts typing
|
|
321
|
-
ok: false,
|
|
322
|
-
status: 500,
|
|
323
|
-
type: "cors",
|
|
324
|
-
url: event.request.url
|
|
325
|
-
}
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
return responseFromCache;
|
|
329
|
-
}
|
|
267
|
+
".webp": "image/webp"
|
|
330
268
|
};
|
|
331
269
|
var swSelf = self;
|
|
332
270
|
swSelf.addEventListener("fetch", (ev) => {
|
|
@@ -334,77 +272,59 @@ swSelf.addEventListener("fetch", (ev) => {
|
|
|
334
272
|
const extension = url.match(/\.[^.]+$/)?.[0];
|
|
335
273
|
const mimeType = extension && extension in ACCEPTED_EXTENSIONS ? ACCEPTED_EXTENSIONS[extension] : "";
|
|
336
274
|
const isValidScreenCall = canHandleScreenCall(url);
|
|
337
|
-
if (STORAGE !== "cache" && url.endsWith(".zip")) {
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
275
|
if (!mimeType && !isValidScreenCall) {
|
|
341
|
-
if (!/\.(css|tsx?|jsx?|woff2|fr
|
|
342
|
-
console.debug("no ext",
|
|
276
|
+
if (!/\.(css|tsx?|jsx?|woff2|fr)/.test(url)) {
|
|
277
|
+
console.debug("no ext", url.match(/\.[^.]+$/)?.[0]);
|
|
343
278
|
}
|
|
344
|
-
return
|
|
279
|
+
return;
|
|
345
280
|
}
|
|
346
281
|
const entryId = knownLinks.get(url.replace(/https:\/\/.*?\//, "")) || knownLinks.get(`medias/${url.replace(/https:\/\/.*?\//, "")}`);
|
|
282
|
+
if (!entryId && !isValidScreenCall) {
|
|
283
|
+
if (/lls|lelivrescolaire/.test(url)) {
|
|
284
|
+
console.info("no link for", url.replace(/https:\/\/.*?\//, ""));
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
347
288
|
if (isValidScreenCall) {
|
|
348
289
|
console.debug("screencall!");
|
|
349
290
|
return ev.respondWith(fetchScreenCall(ev, { offlineWorkerSync, knownLinks }));
|
|
350
291
|
}
|
|
351
|
-
|
|
352
|
-
return ev.respondWith(fetchAsset(ev, { entryId, mimeType, offlineWorkerSync }));
|
|
353
|
-
}
|
|
354
|
-
if (/lls|lelivrescolaire/.test(url)) {
|
|
355
|
-
console.info("no link for", url.replace(/https:\/\/.*?\//, ""), `medias/${url.replace(/https:\/\/.*?\//, "")}`);
|
|
356
|
-
}
|
|
357
|
-
return;
|
|
292
|
+
return ev.respondWith(fetchAsset(ev, { entryId, mimeType, offlineWorkerSync }));
|
|
358
293
|
});
|
|
359
294
|
swSelf.addEventListener("install", (_event) => {
|
|
360
295
|
swSelf.skipWaiting();
|
|
361
296
|
});
|
|
362
|
-
swSelf.addEventListener("activate", (
|
|
363
|
-
|
|
297
|
+
swSelf.addEventListener("activate", (_event) => {
|
|
298
|
+
return swSelf.clients.claim();
|
|
364
299
|
});
|
|
365
300
|
var offlineWorker;
|
|
366
301
|
var offlineWorkerSync;
|
|
367
302
|
var initChannel = async (__, { ports }) => {
|
|
368
|
-
console.info(`FETCH WORKER innitChannel v${
|
|
303
|
+
console.info(`FETCH WORKER innitChannel v${13}`);
|
|
369
304
|
offlineWorker = ports[0];
|
|
370
|
-
|
|
305
|
+
offlineWorkerSync = makeSyncWorker(
|
|
371
306
|
offlineWorker,
|
|
372
307
|
["readZipEntry", "loadBook", "slugToFileMeta", "loadPageContent", "searchBooks"],
|
|
373
308
|
"fetchworker"
|
|
374
309
|
);
|
|
375
|
-
offlineWorkerSync
|
|
376
|
-
offlineWorker.onmessage = handler.handle;
|
|
377
|
-
offlineWorkerSync.on("setKnownLinks", setKnownLinks.bind(null, knownLinks));
|
|
310
|
+
offlineWorkerSync.on("setKnownLinks", setKnownLinks);
|
|
378
311
|
offlineWorker.postMessage({ methodName: "sendAppReady" });
|
|
379
312
|
};
|
|
380
313
|
var knownLinks = /* @__PURE__ */ new Map();
|
|
381
|
-
var
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
CONF_ENV = "development";
|
|
387
|
-
if (typeof storage !== "undefined") {
|
|
388
|
-
STORAGE = storage;
|
|
389
|
-
}
|
|
390
|
-
if (typeof confEnv !== "undefined") {
|
|
391
|
-
CONF_ENV = confEnv;
|
|
392
|
-
}
|
|
393
|
-
console.info("sw::configure", { STORAGE, CONF_ENV });
|
|
314
|
+
var setKnownLinks = ({ entryNames, bookId }) => {
|
|
315
|
+
entryNames.forEach((entryName) => {
|
|
316
|
+
const link = entryName.replace(/^\d+\/media\//, "");
|
|
317
|
+
knownLinks.set(link, { entryName, bookId });
|
|
318
|
+
});
|
|
394
319
|
};
|
|
395
320
|
swSelf.onmessage = async ({
|
|
396
321
|
data: { methodName, arg } = {},
|
|
397
322
|
ports
|
|
398
323
|
}) => {
|
|
399
324
|
const services = {
|
|
400
|
-
initChannel
|
|
401
|
-
configure
|
|
325
|
+
initChannel
|
|
402
326
|
};
|
|
403
327
|
try {
|
|
404
|
-
if (methodName === "configure") {
|
|
405
|
-
await services[methodName](arg);
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
328
|
if (methodName === "initChannel") {
|
|
409
329
|
if (!ports.length) {
|
|
410
330
|
throw new Error("fetchworker::missing port!");
|
|
@@ -1,99 +1,21 @@
|
|
|
1
|
-
// src/utils/error.ts
|
|
2
|
-
var tryCatch = (fn) => {
|
|
3
|
-
return (resolve, reject) => {
|
|
4
|
-
try {
|
|
5
|
-
return fn().then(resolve);
|
|
6
|
-
} catch (e) {
|
|
7
|
-
return reject(e);
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
};
|
|
11
|
-
|
|
12
1
|
// src/utils/worker.ts
|
|
13
|
-
var makeSyncWorker = (offlineWorker, methodNames, dbg) => {
|
|
14
|
-
const dic = {};
|
|
15
|
-
const cbks = {};
|
|
16
|
-
const fn = ({ data: { eventId, methodName, results, error, arg } = {} } = {}) => {
|
|
17
|
-
if (eventId && eventId in dic) {
|
|
18
|
-
if (error) {
|
|
19
|
-
dic[eventId].reject(error);
|
|
20
|
-
} else {
|
|
21
|
-
dic[eventId].resolve(results);
|
|
22
|
-
}
|
|
23
|
-
} else {
|
|
24
|
-
if (methodName && !methodName.startsWith("async") && !(methodName in cbks)) {
|
|
25
|
-
console.error(`syncWorker: unknown methodName cbk ${dbg}`, eventId, methodName, error);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
methodName && cbks[methodName]?.(arg);
|
|
29
|
-
};
|
|
30
|
-
const makeFn = (methodName) => {
|
|
31
|
-
return ({ ports, ...arg } = {}) => {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
const eventId = `${methodName}:${Date.now()}-${Math.random().toFixed(5)}`;
|
|
34
|
-
dic[eventId] = { resolve, reject };
|
|
35
|
-
offlineWorker.postMessage({ eventId, methodName, arg }, ports);
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
const obj = Object.assign(Object.fromEntries(methodNames.map((name) => [name, makeFn(name)])), {
|
|
40
|
-
on: (key2, fn2) => {
|
|
41
|
-
cbks[key2] = fn2;
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
return {
|
|
45
|
-
...obj,
|
|
46
|
-
handler: {
|
|
47
|
-
canHandle: (methodName) => {
|
|
48
|
-
return methodName in cbks || methodNames.includes(methodName);
|
|
49
|
-
},
|
|
50
|
-
handle: fn
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
2
|
var a = (a2) => a2;
|
|
55
3
|
var makeSyncHandler = a(
|
|
56
|
-
(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (typeof methodName === "string" && methodName in services && services[methodName]) {
|
|
63
|
-
const results = await services[methodName](arg, { ports });
|
|
64
|
-
sender2.postMessage({ eventId, methodName, results });
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
throw new Error(`invalid methodName ${Object.keys(services)} |${String(methodName)}`);
|
|
68
|
-
} catch (e) {
|
|
69
|
-
console.debug("failed", e);
|
|
70
|
-
sender2.postMessage({ eventId, methodName, error: e });
|
|
4
|
+
(sender, services) => async ({ data: { eventId, methodName, arg } = {}, ports }) => {
|
|
5
|
+
try {
|
|
6
|
+
if (typeof methodName === "string" && methodName in services && services[methodName]) {
|
|
7
|
+
const results = await services[methodName](arg, { ports });
|
|
8
|
+
sender.postMessage({ eventId, methodName, results });
|
|
9
|
+
return;
|
|
71
10
|
}
|
|
11
|
+
throw new Error(`invalid methodName ${Object.keys(services)} |${String(methodName)}`);
|
|
12
|
+
} catch (e) {
|
|
13
|
+
console.debug("failed", e);
|
|
14
|
+
sender.postMessage({ eventId, methodName, error: e });
|
|
72
15
|
}
|
|
73
|
-
}
|
|
16
|
+
}
|
|
74
17
|
);
|
|
75
18
|
var bufToJson = (buf) => JSON.parse(new TextDecoder("UTF-8").decode(new Uint8Array(buf)));
|
|
76
|
-
var combineHandlers = (...handlers) => {
|
|
77
|
-
return ({ data: { methodName, ...data } = {}, ports }) => {
|
|
78
|
-
for (const h of handlers) {
|
|
79
|
-
if (h.canHandle(String(methodName))) {
|
|
80
|
-
return h.handle({ data: { methodName, ...data }, ports });
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
console.error("no handlers matched", methodName);
|
|
84
|
-
};
|
|
85
|
-
};
|
|
86
|
-
var openCache = async (mode) => caches.open(`LLS_OFFLINE_${mode}`);
|
|
87
|
-
var concatArrayBuffers = (buffers) => {
|
|
88
|
-
const totalLength = buffers.reduce((sum, buf) => sum + buf.byteLength, 0);
|
|
89
|
-
const concatenated = new Uint8Array(totalLength);
|
|
90
|
-
let offset = 0;
|
|
91
|
-
for (const buffer of buffers) {
|
|
92
|
-
concatenated.set(new Uint8Array(buffer), offset);
|
|
93
|
-
offset += buffer.byteLength;
|
|
94
|
-
}
|
|
95
|
-
return concatenated.buffer;
|
|
96
|
-
};
|
|
97
19
|
|
|
98
20
|
// src/worker/wwApi.ts
|
|
99
21
|
var STATE = {
|
|
@@ -775,9 +697,6 @@ async function decryptData(encryptedData, key2, iv2) {
|
|
|
775
697
|
return decryptedData;
|
|
776
698
|
}
|
|
777
699
|
var BOOKS = {};
|
|
778
|
-
var zipNameInMedias = (zipName) => {
|
|
779
|
-
return /^(pages|book)/.test(zipName);
|
|
780
|
-
};
|
|
781
700
|
var readZipEntry = async ({
|
|
782
701
|
bookId,
|
|
783
702
|
entryName,
|
|
@@ -787,20 +706,9 @@ var readZipEntry = async ({
|
|
|
787
706
|
console.trace("no bookId", bookId, "found");
|
|
788
707
|
return null;
|
|
789
708
|
}
|
|
790
|
-
const
|
|
791
|
-
let overridenName = entryName;
|
|
792
|
-
if (manifest && !entryName.includes("gqls/")) {
|
|
793
|
-
const zipName = entryName.replace("medias/", "");
|
|
794
|
-
if (zipNameInMedias(zipName)) {
|
|
795
|
-
const originalName = manifest.medias[zipName];
|
|
796
|
-
overridenName = `medias/${originalName}`;
|
|
797
|
-
} else {
|
|
798
|
-
console.error("any media should exist in manifest", zipName, entryName, bookId);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
const bookEntry = BOOKS[bookId].map.get(overridenName);
|
|
709
|
+
const bookEntry = BOOKS[bookId].get(entryName);
|
|
802
710
|
if (!bookEntry) {
|
|
803
|
-
console.info("no bookEntry", entryName, "found"
|
|
711
|
+
console.info("no bookEntry", entryName, "found");
|
|
804
712
|
return null;
|
|
805
713
|
}
|
|
806
714
|
if (!bookEntry.decoded) {
|
|
@@ -811,14 +719,21 @@ var readZipEntry = async ({
|
|
|
811
719
|
var hasZip = (bookId) => {
|
|
812
720
|
return !!BOOKS[bookId];
|
|
813
721
|
};
|
|
722
|
+
function concatArrayBuffers(buffers) {
|
|
723
|
+
const totalLength = buffers.reduce((sum, buf) => sum + buf.byteLength, 0);
|
|
724
|
+
const concatenated = new Uint8Array(totalLength);
|
|
725
|
+
let offset = 0;
|
|
726
|
+
for (const buffer of buffers) {
|
|
727
|
+
concatenated.set(new Uint8Array(buffer), offset);
|
|
728
|
+
offset += buffer.byteLength;
|
|
729
|
+
}
|
|
730
|
+
return concatenated.buffer;
|
|
731
|
+
}
|
|
814
732
|
var loadZip = async (buf, bookId) => {
|
|
815
733
|
const m = /* @__PURE__ */ new Map();
|
|
816
734
|
const unzipper = new Unzip();
|
|
817
735
|
unzipper.register(UnzipInflate);
|
|
818
736
|
unzipper.onfile = (file) => {
|
|
819
|
-
if (!file.name.includes(".")) {
|
|
820
|
-
return;
|
|
821
|
-
}
|
|
822
737
|
m.set(file.name, {
|
|
823
738
|
decoded: null,
|
|
824
739
|
zipEntry: {
|
|
@@ -850,43 +765,16 @@ var loadZip = async (buf, bookId) => {
|
|
|
850
765
|
await sleep;
|
|
851
766
|
}
|
|
852
767
|
unzipper.push(buf.slice((nChunks - 1) * chunkSize, buf.byteLength), true);
|
|
853
|
-
BOOKS[bookId] =
|
|
854
|
-
|
|
855
|
-
manifest: null
|
|
856
|
-
};
|
|
857
|
-
const manifestEntry = m.get("manifest.json");
|
|
858
|
-
const keys = [...m.keys()];
|
|
859
|
-
if (manifestEntry) {
|
|
860
|
-
manifestEntry.decoded = await decryptData(await manifestEntry.zipEntry.getData(), key, iv);
|
|
861
|
-
BOOKS[bookId].manifest = bufToJson(manifestEntry.decoded);
|
|
862
|
-
const { medias } = BOOKS[bookId].manifest;
|
|
863
|
-
keys.push(...Object.keys(medias).map((entryName) => `medias/${entryName}`));
|
|
864
|
-
}
|
|
865
|
-
return keys;
|
|
768
|
+
BOOKS[bookId] = m;
|
|
769
|
+
return [...m.keys()];
|
|
866
770
|
};
|
|
867
771
|
|
|
868
772
|
// src/worker/offline-worker.ts
|
|
869
773
|
var CHECK_UPDATES = true;
|
|
870
|
-
var
|
|
871
|
-
|
|
872
|
-
var CONF_ENV = "development";
|
|
873
|
-
var LOADED_BOOKS = /* @__PURE__ */ new Map();
|
|
874
|
-
var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
774
|
+
var fetchZipFile = async (zipUrl, forceRefresh) => {
|
|
775
|
+
const opfsRoot = await navigator.storage.getDirectory();
|
|
875
776
|
const fileName = zipUrl.split("/").slice(-1)[0];
|
|
876
777
|
const readArrayBuffer = async () => {
|
|
877
|
-
if (STORAGE === "native") {
|
|
878
|
-
const back = await sender.nativeReadFile({ fname: fileName });
|
|
879
|
-
return back;
|
|
880
|
-
}
|
|
881
|
-
if (STORAGE === "cache") {
|
|
882
|
-
const response = await caches.match(zipUrl);
|
|
883
|
-
if (!response) {
|
|
884
|
-
throw new Error(`missing zip ${zipUrl}`);
|
|
885
|
-
}
|
|
886
|
-
const buf = await response.arrayBuffer();
|
|
887
|
-
return buf;
|
|
888
|
-
}
|
|
889
|
-
const opfsRoot = await navigator.storage.getDirectory();
|
|
890
778
|
const handle = await opfsRoot.getFileHandle(fileName);
|
|
891
779
|
const accessHandle = await handle.createSyncAccessHandle();
|
|
892
780
|
const size = accessHandle.getSize();
|
|
@@ -912,8 +800,8 @@ var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
|
912
800
|
}
|
|
913
801
|
return buf;
|
|
914
802
|
} catch (e) {
|
|
915
|
-
if (e
|
|
916
|
-
console.debug(
|
|
803
|
+
if (e.message !== "forceRefresh" && !e.message.includes("refetching")) {
|
|
804
|
+
console.debug("opfs db not found, creating it", e, zipUrl);
|
|
917
805
|
}
|
|
918
806
|
const response = await fetch(zipUrl);
|
|
919
807
|
const contentLength = +response.headers.get("Content-Length");
|
|
@@ -924,7 +812,6 @@ var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
|
924
812
|
while (true) {
|
|
925
813
|
const { done, value: chunk } = await reader.read();
|
|
926
814
|
if (done) {
|
|
927
|
-
self.postMessage({ methodName: "opfsProgress", arg: { total: contentLength, bytes: contentLength } });
|
|
928
815
|
break;
|
|
929
816
|
}
|
|
930
817
|
arrayBuffer.set(chunk, position);
|
|
@@ -939,69 +826,34 @@ var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
|
939
826
|
console.debug(error);
|
|
940
827
|
throw new Error(error);
|
|
941
828
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
const accessHandle = await handle.createSyncAccessHandle();
|
|
948
|
-
await accessHandle.truncate(0);
|
|
949
|
-
await accessHandle.write(arrayBuffer);
|
|
950
|
-
await accessHandle.close();
|
|
951
|
-
} else {
|
|
952
|
-
const cache = await openCache(CONF_ENV);
|
|
953
|
-
const response2 = new Response(arrayBuffer, {
|
|
954
|
-
headers: {
|
|
955
|
-
"Cache-Control": "max-age=600, s-maxage=600, public, proxy-revalidate",
|
|
956
|
-
"Content-Type": "application/zip"
|
|
957
|
-
},
|
|
958
|
-
// @ts-ignore: poor ts typing
|
|
959
|
-
ok: true,
|
|
960
|
-
redirected: false,
|
|
961
|
-
status: 200,
|
|
962
|
-
type: "cors",
|
|
963
|
-
url: zipUrl
|
|
964
|
-
});
|
|
965
|
-
await cache.put(zipUrl, response2);
|
|
966
|
-
}
|
|
829
|
+
const handle = await opfsRoot.getFileHandle(fileName, { create: true });
|
|
830
|
+
const accessHandle = await handle.createSyncAccessHandle();
|
|
831
|
+
await accessHandle.truncate(0);
|
|
832
|
+
await accessHandle.write(arrayBuffer);
|
|
833
|
+
await accessHandle.close();
|
|
967
834
|
return await readArrayBuffer();
|
|
968
835
|
}
|
|
969
836
|
};
|
|
970
837
|
var slugToFileMeta2 = async ({ slug }) => {
|
|
838
|
+
console.debug("hereshit");
|
|
971
839
|
const json = await readZipEntry({ bookId: "preload", entryName: "preload/hashmap_slugToMetaPage.json", asJson: true });
|
|
840
|
+
console.debug("hereshit2");
|
|
972
841
|
return slugToFileMeta({ slug, json });
|
|
973
842
|
};
|
|
974
843
|
var PRELOAD_AS_BOOK_ID = "preload";
|
|
975
844
|
var isOldZip = (bookId) => typeof bookId === "number";
|
|
976
845
|
var loadBook2 = async ({ id: bookId, forceRefresh }) => {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
846
|
+
const buf = await fetchZipFile(`https://ci.lls.fr/artefacts/content/development/${bookId}.zip`, forceRefresh);
|
|
847
|
+
try {
|
|
848
|
+
const entryNames = await loadZip(new Uint8Array(buf), bookId);
|
|
849
|
+
fetchWorker.postMessage({ methodName: "setKnownLinks", arg: { entryNames, bookId } });
|
|
850
|
+
if (isOldZip(bookId)) {
|
|
851
|
+
const json = await readZipEntry({ entryName: `preload/${bookId}.json`, bookId: PRELOAD_AS_BOOK_ID, asJson: true });
|
|
852
|
+
loadBook(json);
|
|
853
|
+
}
|
|
854
|
+
} catch (e) {
|
|
855
|
+
console.error("failed", e);
|
|
981
856
|
}
|
|
982
|
-
const p = new Promise(
|
|
983
|
-
tryCatch(async () => {
|
|
984
|
-
const buf = await fetchZipFile(`${ZIP_BASE_URL}/${bookId}.zip`, { forceRefresh });
|
|
985
|
-
try {
|
|
986
|
-
const entryNames = await loadZip(new Uint8Array(buf), bookId);
|
|
987
|
-
fetchWorker.postMessage({ methodName: "setKnownLinks", arg: { entryNames, bookId } });
|
|
988
|
-
if (isOldZip(bookId)) {
|
|
989
|
-
const json = await readZipEntry({
|
|
990
|
-
entryName: `preload/${bookId}.json`,
|
|
991
|
-
bookId: PRELOAD_AS_BOOK_ID,
|
|
992
|
-
asJson: true
|
|
993
|
-
});
|
|
994
|
-
loadBook(json);
|
|
995
|
-
}
|
|
996
|
-
return true;
|
|
997
|
-
} catch (e) {
|
|
998
|
-
console.error("failed", e);
|
|
999
|
-
return false;
|
|
1000
|
-
}
|
|
1001
|
-
})
|
|
1002
|
-
);
|
|
1003
|
-
LOADED_BOOKS.set(bookId, p);
|
|
1004
|
-
await p;
|
|
1005
857
|
};
|
|
1006
858
|
var loadPageContent2 = async ({
|
|
1007
859
|
bookId: iBookId,
|
|
@@ -1023,7 +875,7 @@ var loadPageContent2 = async ({
|
|
|
1023
875
|
var fetchWorker;
|
|
1024
876
|
var initChannel = (__, { ports }) => {
|
|
1025
877
|
fetchWorker = ports[0];
|
|
1026
|
-
const
|
|
878
|
+
const defaultHandler = makeSyncHandler(fetchWorker, {
|
|
1027
879
|
readZipEntry,
|
|
1028
880
|
loadBook: loadBook2,
|
|
1029
881
|
slugToFileMeta: slugToFileMeta2,
|
|
@@ -1035,69 +887,23 @@ var initChannel = (__, { ports }) => {
|
|
|
1035
887
|
self.postMessage({ methodName: "ready" });
|
|
1036
888
|
return;
|
|
1037
889
|
}
|
|
1038
|
-
|
|
1039
|
-
return handle(dataObj);
|
|
1040
|
-
}
|
|
1041
|
-
console.error("unknown methodName", dataObj.data?.methodName);
|
|
890
|
+
defaultHandler(dataObj);
|
|
1042
891
|
};
|
|
1043
892
|
fetchWorker.onmessage = fn;
|
|
1044
893
|
};
|
|
1045
|
-
var loadBooks = async (
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
entries = await sender.nativeListZips();
|
|
1049
|
-
} else if (STORAGE === "opfs") {
|
|
1050
|
-
const opfsRoot = await navigator.storage.getDirectory();
|
|
1051
|
-
entries = await opfsRoot.values();
|
|
1052
|
-
} else {
|
|
1053
|
-
const cache = await openCache(CONF_ENV);
|
|
1054
|
-
const keys = await cache.keys();
|
|
1055
|
-
const zips = keys.filter((key2) => {
|
|
1056
|
-
return key2.url.match(/lecture-cp\.zip|story-.*?\.zip/);
|
|
1057
|
-
});
|
|
1058
|
-
entries = zips.map((zip) => ({ name: String(zip.url.split("/").pop()) }));
|
|
1059
|
-
}
|
|
894
|
+
var loadBooks = async () => {
|
|
895
|
+
const opfsRoot = await navigator.storage.getDirectory();
|
|
896
|
+
const entries = await opfsRoot.values();
|
|
1060
897
|
for await (const entry of entries) {
|
|
1061
|
-
if (/preload/.test(entry.name)) {
|
|
1062
|
-
continue;
|
|
1063
|
-
}
|
|
1064
898
|
if (/\d+\.zip$/.test(entry.name)) {
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
await loadBook2({ id: Number.parseInt(bookId, 10) });
|
|
1068
|
-
}
|
|
899
|
+
const bookId = entry.name.match(/\d+/)[0];
|
|
900
|
+
await loadBook2({ id: Number.parseInt(bookId, 10) });
|
|
1069
901
|
} else if (/zip$/.test(entry.name)) {
|
|
1070
902
|
const methodUri = entry.name.replace(".zip", "");
|
|
1071
903
|
await loadBook2({ id: methodUri });
|
|
1072
|
-
} else {
|
|
1073
|
-
console.info(`invalid book entry ${entry.name}`);
|
|
1074
904
|
}
|
|
1075
905
|
}
|
|
1076
906
|
};
|
|
1077
|
-
var configure = ({
|
|
1078
|
-
storage,
|
|
1079
|
-
checkUpdates,
|
|
1080
|
-
zipBaseUrl,
|
|
1081
|
-
confEnv
|
|
1082
|
-
}) => {
|
|
1083
|
-
CHECK_UPDATES = true;
|
|
1084
|
-
STORAGE = "cache";
|
|
1085
|
-
ZIP_BASE_URL = "https://ci.lls.fr/artefacts/content/development";
|
|
1086
|
-
CONF_ENV = "development";
|
|
1087
|
-
if (typeof storage !== "undefined") {
|
|
1088
|
-
STORAGE = storage;
|
|
1089
|
-
}
|
|
1090
|
-
if (typeof checkUpdates !== "undefined") {
|
|
1091
|
-
CHECK_UPDATES = !!checkUpdates;
|
|
1092
|
-
}
|
|
1093
|
-
if (typeof zipBaseUrl !== "undefined") {
|
|
1094
|
-
ZIP_BASE_URL = zipBaseUrl;
|
|
1095
|
-
}
|
|
1096
|
-
if (typeof confEnv !== "undefined") {
|
|
1097
|
-
CONF_ENV = confEnv;
|
|
1098
|
-
}
|
|
1099
|
-
console.info("ow::configure", { STORAGE, CHECK_UPDATES, ZIP_BASE_URL, CONF_ENV });
|
|
1100
|
-
};
|
|
1101
907
|
var loadPreload2 = async () => {
|
|
1102
908
|
const zipUrl = "https://lls-ci-fr.s3.eu-west-3.amazonaws.com/artefacts/content/development/preload.zip";
|
|
1103
909
|
const buf = await fetchZipFile(zipUrl);
|
|
@@ -1110,13 +916,10 @@ var loadPreload2 = async () => {
|
|
|
1110
916
|
console.error("failed", e);
|
|
1111
917
|
}
|
|
1112
918
|
};
|
|
1113
|
-
|
|
919
|
+
self.onmessage = makeSyncHandler(self, {
|
|
1114
920
|
initChannel,
|
|
1115
921
|
loadPreload: loadPreload2,
|
|
1116
922
|
readZipEntry,
|
|
1117
923
|
loadBooks,
|
|
1118
|
-
loadBook: loadBook2
|
|
1119
|
-
configure
|
|
924
|
+
loadBook: loadBook2
|
|
1120
925
|
});
|
|
1121
|
-
var { handler, ...sender } = makeSyncWorker(self, ["nativeWriteFile", "nativeReadFile", "nativeListZips"]);
|
|
1122
|
-
self.onmessage = combineHandlers(mainHandle, handler);
|
package/lib/index.js
CHANGED
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
// src/utils/error.ts
|
|
2
|
-
var tryCatch = (fn) => {
|
|
3
|
-
return (resolve, reject) => {
|
|
4
|
-
try {
|
|
5
|
-
return fn().then(resolve);
|
|
6
|
-
} catch (e) {
|
|
7
|
-
return reject(e);
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
};
|
|
11
|
-
|
|
12
1
|
// src/utils/worker.ts
|
|
13
|
-
var makeSyncWorker = (
|
|
2
|
+
var makeSyncWorker = (offlineWorker, methodNames, dbg) => {
|
|
14
3
|
const dic = {};
|
|
15
4
|
const cbks = {};
|
|
16
|
-
const
|
|
5
|
+
const fn2 = ({ data: { eventId, methodName, results, error, arg } = {} } = {}) => {
|
|
17
6
|
if (eventId && eventId in dic) {
|
|
18
7
|
if (error) {
|
|
19
8
|
dic[eventId].reject(error);
|
|
@@ -22,96 +11,53 @@ var makeSyncWorker = (offlineWorker2, methodNames, dbg) => {
|
|
|
22
11
|
}
|
|
23
12
|
} else {
|
|
24
13
|
if (methodName && !methodName.startsWith("async") && !(methodName in cbks)) {
|
|
25
|
-
console.error(`syncWorker:
|
|
14
|
+
console.error(`syncWorker: ${dbg}`, eventId, methodName, error);
|
|
26
15
|
}
|
|
27
16
|
}
|
|
28
17
|
methodName && cbks[methodName]?.(arg);
|
|
29
18
|
};
|
|
19
|
+
offlineWorker.onmessage = fn2;
|
|
30
20
|
const makeFn = (methodName) => {
|
|
31
21
|
return ({ ports, ...arg } = {}) => {
|
|
32
22
|
return new Promise((resolve, reject) => {
|
|
33
|
-
const eventId = `${methodName}:${Date.now()}
|
|
23
|
+
const eventId = `${methodName}:${Date.now()}`;
|
|
34
24
|
dic[eventId] = { resolve, reject };
|
|
35
|
-
|
|
25
|
+
offlineWorker.postMessage({ eventId, methodName, arg }, ports);
|
|
36
26
|
});
|
|
37
27
|
};
|
|
38
28
|
};
|
|
39
29
|
const obj = Object.assign(Object.fromEntries(methodNames.map((name) => [name, makeFn(name)])), {
|
|
40
|
-
on: (key,
|
|
41
|
-
cbks[key] =
|
|
30
|
+
on: (key, fn3) => {
|
|
31
|
+
cbks[key] = fn3;
|
|
42
32
|
}
|
|
43
33
|
});
|
|
44
|
-
return
|
|
45
|
-
...obj,
|
|
46
|
-
handler: {
|
|
47
|
-
canHandle: (methodName) => {
|
|
48
|
-
return methodName in cbks || methodNames.includes(methodName);
|
|
49
|
-
},
|
|
50
|
-
handle: fn
|
|
51
|
-
}
|
|
52
|
-
};
|
|
34
|
+
return obj;
|
|
53
35
|
};
|
|
54
36
|
var a = (a2) => a2;
|
|
55
37
|
var makeSyncHandler = a(
|
|
56
|
-
(sender, services) => ({
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (typeof methodName === "string" && methodName in services && services[methodName]) {
|
|
63
|
-
const results = await services[methodName](arg, { ports });
|
|
64
|
-
sender.postMessage({ eventId, methodName, results });
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
throw new Error(`invalid methodName ${Object.keys(services)} |${String(methodName)}`);
|
|
68
|
-
} catch (e) {
|
|
69
|
-
console.debug("failed", e);
|
|
70
|
-
sender.postMessage({ eventId, methodName, error: e });
|
|
38
|
+
(sender, services) => async ({ data: { eventId, methodName, arg } = {}, ports }) => {
|
|
39
|
+
try {
|
|
40
|
+
if (typeof methodName === "string" && methodName in services && services[methodName]) {
|
|
41
|
+
const results = await services[methodName](arg, { ports });
|
|
42
|
+
sender.postMessage({ eventId, methodName, results });
|
|
43
|
+
return;
|
|
71
44
|
}
|
|
45
|
+
throw new Error(`invalid methodName ${Object.keys(services)} |${String(methodName)}`);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.debug("failed", e);
|
|
48
|
+
sender.postMessage({ eventId, methodName, error: e });
|
|
72
49
|
}
|
|
73
|
-
}
|
|
50
|
+
}
|
|
74
51
|
);
|
|
75
|
-
var combineHandlers = (...handlers) => {
|
|
76
|
-
return ({ data: { methodName, ...data } = {}, ports }) => {
|
|
77
|
-
for (const h of handlers) {
|
|
78
|
-
if (h.canHandle(String(methodName))) {
|
|
79
|
-
return h.handle({ data: { methodName, ...data }, ports });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
console.error("no handlers matched", methodName);
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
52
|
|
|
86
53
|
// src/hooks/useOfflineWorker.ts
|
|
87
54
|
import { useCallback, useEffect, useState } from "react";
|
|
88
|
-
var
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
["initChannel", "readZipEntry", "
|
|
55
|
+
var offlineWorkerSync = makeSyncWorker(
|
|
56
|
+
// @ts-ignore: TODO
|
|
57
|
+
new Worker("/src/worker/offline-worker.js", { type: "module" }),
|
|
58
|
+
["initChannel", "readZipEntry", "loadPreload", "loadBooks", "loadBook"],
|
|
92
59
|
"app:offlineWorker"
|
|
93
60
|
);
|
|
94
|
-
var fetchWorkerPromise = new Promise(
|
|
95
|
-
tryCatch(async () => {
|
|
96
|
-
let fetchWorker = null;
|
|
97
|
-
await navigator.serviceWorker.getRegistrations().then((registrations) => {
|
|
98
|
-
return Promise.all(registrations.map((reg) => reg.unregister()));
|
|
99
|
-
});
|
|
100
|
-
await navigator.serviceWorker.register("/fetch-worker.js", { type: "module" }).then((reg) => {
|
|
101
|
-
return reg.update().catch((e) => {
|
|
102
|
-
console.debug(e, "likely we are offline");
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
await navigator.serviceWorker.ready.then(async (reg) => {
|
|
106
|
-
console.debug("Service Worker registered:", reg.scope);
|
|
107
|
-
fetchWorker = reg.active;
|
|
108
|
-
}).catch((err) => console.error("Service Worker registration failed:", err));
|
|
109
|
-
if (!fetchWorker) {
|
|
110
|
-
console.debug("failed to get fetch worker");
|
|
111
|
-
}
|
|
112
|
-
return fetchWorker;
|
|
113
|
-
})
|
|
114
|
-
);
|
|
115
61
|
var setWorkersReady;
|
|
116
62
|
var workersReadyPromise = new Promise((resolve) => {
|
|
117
63
|
setWorkersReady = resolve;
|
|
@@ -120,81 +66,46 @@ var setBooksLoaded;
|
|
|
120
66
|
var setBooksLoadedPromise = new Promise((resolve) => {
|
|
121
67
|
setBooksLoaded = resolve;
|
|
122
68
|
});
|
|
123
|
-
var
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
nativeListZips,
|
|
129
|
-
storage,
|
|
130
|
-
zipBaseUrl,
|
|
131
|
-
checkUpdates,
|
|
132
|
-
confEnv
|
|
133
|
-
} = {}) => {
|
|
134
|
-
const fetchWorker = await fetchWorkerPromise;
|
|
135
|
-
const configureOptions = { storage, zipBaseUrl, checkUpdates, confEnv };
|
|
136
|
-
if (nativeReadFile) {
|
|
137
|
-
const wwHandler = makeSyncHandler(offlineWorker, { nativeReadFile, nativeWriteFile, nativeListZips });
|
|
138
|
-
offlineWorker.onmessage = combineHandlers(handler, wwHandler);
|
|
139
|
-
if (storage !== "native") {
|
|
140
|
-
console.warn("nativeReadFile given, ignoring storage", storage);
|
|
69
|
+
var fn = async () => {
|
|
70
|
+
let fetchWorker = null;
|
|
71
|
+
await navigator.serviceWorker.getRegistrations().then(async (registrations) => {
|
|
72
|
+
for (const registration of registrations) {
|
|
73
|
+
await registration.unregister();
|
|
141
74
|
}
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
75
|
+
});
|
|
76
|
+
await navigator.serviceWorker.register("/fetch-worker.js", { type: "module" }).then((reg) => reg.update());
|
|
77
|
+
await navigator.serviceWorker.ready.then(async (reg) => {
|
|
78
|
+
console.debug("Service Worker registered:", reg.scope);
|
|
79
|
+
fetchWorker = reg.active;
|
|
80
|
+
}).catch((err) => console.error("Service Worker registration failed:", err));
|
|
81
|
+
if (!fetchWorker) {
|
|
82
|
+
console.debug("failed to get fetch worker");
|
|
83
|
+
return;
|
|
146
84
|
}
|
|
147
85
|
offlineWorkerSync.on("ready", setWorkersReady);
|
|
148
86
|
const channel = new MessageChannel();
|
|
149
87
|
offlineWorkerSync.initChannel({ ports: [channel.port1] });
|
|
150
88
|
fetchWorker.postMessage({ methodName: "initChannel" }, [channel.port2]);
|
|
151
|
-
fetchWorker.postMessage({ methodName: "configure", arg: { storage } });
|
|
152
89
|
offlineWorkerSync.on("opfsProgress", () => {
|
|
153
90
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
if (!skipLoadBooks) {
|
|
158
|
-
await offlineWorkerSync.loadBooks({ primaryOnly });
|
|
159
|
-
}
|
|
91
|
+
await offlineWorkerSync.loadPreload();
|
|
92
|
+
await offlineWorkerSync.loadBooks();
|
|
160
93
|
setBooksLoaded();
|
|
161
94
|
};
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
nativeReadFile,
|
|
165
|
-
nativeWriteFile,
|
|
166
|
-
nativeListZips,
|
|
167
|
-
checkUpdates,
|
|
168
|
-
storage,
|
|
169
|
-
zipBaseUrl,
|
|
170
|
-
confEnv
|
|
171
|
-
} = {}) => {
|
|
95
|
+
fn();
|
|
96
|
+
var useOfflineWorker_default = () => {
|
|
172
97
|
const [ready, setReady] = useState(false);
|
|
173
98
|
useEffect(() => {
|
|
174
|
-
const
|
|
175
|
-
await installWorker({
|
|
176
|
-
primaryOnly,
|
|
177
|
-
nativeReadFile,
|
|
178
|
-
nativeWriteFile,
|
|
179
|
-
nativeListZips,
|
|
180
|
-
storage,
|
|
181
|
-
zipBaseUrl,
|
|
182
|
-
checkUpdates,
|
|
183
|
-
confEnv
|
|
184
|
-
});
|
|
99
|
+
const fn2 = async () => {
|
|
185
100
|
await Promise.all([workersReadyPromise, setBooksLoadedPromise]);
|
|
186
|
-
fetch(window.location.href.replace(window.location.search, ""));
|
|
187
101
|
setReady(true);
|
|
188
102
|
};
|
|
189
|
-
|
|
190
|
-
}, [
|
|
191
|
-
const loadBook = useCallback(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
},
|
|
196
|
-
[]
|
|
197
|
-
);
|
|
103
|
+
fn2();
|
|
104
|
+
}, []);
|
|
105
|
+
const loadBook = useCallback(async ({ id, forceRefresh }, cbk) => {
|
|
106
|
+
offlineWorkerSync.on("opfsProgress", cbk);
|
|
107
|
+
return await offlineWorkerSync.loadBook({ id, forceRefresh });
|
|
108
|
+
}, []);
|
|
198
109
|
return {
|
|
199
110
|
ready,
|
|
200
111
|
loadBook
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lls/offline",
|
|
3
|
-
"version": "24.22.0-
|
|
3
|
+
"version": "24.22.0-debug",
|
|
4
4
|
"description": "offline",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"module": "./lib/index.js",
|
|
@@ -36,12 +36,10 @@
|
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@biomejs/biome": "1.9.4",
|
|
38
38
|
"@happy-dom/global-registrator": "15.11.0",
|
|
39
|
-
"@ladle/react": "5.0.3",
|
|
40
39
|
"@testing-library/react": "16.3.0",
|
|
41
40
|
"@types/react": "18.3.12",
|
|
42
41
|
"@types/react-dom": "18.3.1",
|
|
43
42
|
"@vitejs/plugin-react": "4.5.1",
|
|
44
|
-
"@vitejs/plugin-react-swc": "3.10.1",
|
|
45
43
|
"esbuild": "0.24.0",
|
|
46
44
|
"happy-dom": "15.11.0",
|
|
47
45
|
"lodash": "4.17.21",
|
|
@@ -50,21 +48,17 @@
|
|
|
50
48
|
"react-dom": "18.3.1",
|
|
51
49
|
"typescript": "5.8.3",
|
|
52
50
|
"vite": "7.0.0",
|
|
53
|
-
"vite-tsconfig-paths": "5.1.4",
|
|
54
51
|
"zod": "3.25.50",
|
|
55
|
-
"@lls/vitescripts": "24.22.0
|
|
52
|
+
"@lls/vitescripts": "24.22.0"
|
|
56
53
|
},
|
|
57
54
|
"dependencies": {
|
|
58
55
|
"fflate": "0.8.2"
|
|
59
56
|
},
|
|
60
57
|
"scripts": {
|
|
61
|
-
"start": "
|
|
58
|
+
"start": "echo no devserver",
|
|
62
59
|
"dts": "pnpm tsc --project tsconfig.production.json",
|
|
63
|
-
"build": "
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"test": "bun test --bail __tests__/unit __tests__/e2e",
|
|
67
|
-
"lint": "pnpm biome check --apply src __tests__ stories",
|
|
68
|
-
"precommit": "pnpm lint"
|
|
60
|
+
"build": "node build",
|
|
61
|
+
"test": "bun test --bail",
|
|
62
|
+
"lint": "pnpm biome check --apply src __tests__"
|
|
69
63
|
}
|
|
70
64
|
}
|