@lls/offline 24.22.0-bfdb7edf → 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 +45 -120
- package/lib/assets/offline-worker.js +41 -213
- package/lib/index.js +49 -135
- 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
|
};
|
|
@@ -293,113 +266,65 @@ var ACCEPTED_EXTENSIONS = {
|
|
|
293
266
|
".m4a": "audio/mp4",
|
|
294
267
|
".webp": "image/webp"
|
|
295
268
|
};
|
|
296
|
-
var putInCache = async (request, response) => {
|
|
297
|
-
const cache = await openCache(CONF_ENV);
|
|
298
|
-
await cache.put(request, response);
|
|
299
|
-
};
|
|
300
|
-
var onlineFirst = async (request, event) => {
|
|
301
|
-
try {
|
|
302
|
-
const responseFromNetwork = await fetch(request);
|
|
303
|
-
const cached = putInCache(request, responseFromNetwork.clone());
|
|
304
|
-
if (request.url.endsWith(".zip")) {
|
|
305
|
-
await cached;
|
|
306
|
-
event.waitUntil(Promise.resolve());
|
|
307
|
-
} else {
|
|
308
|
-
event.waitUntil(cached);
|
|
309
|
-
}
|
|
310
|
-
return responseFromNetwork;
|
|
311
|
-
} catch (_e) {
|
|
312
|
-
const over = /\/\?story=basic/.test(request.url) ? request.url.replace(/\/\?story=.*$/, "") : request;
|
|
313
|
-
const responseFromCache = await caches.match(over);
|
|
314
|
-
if (!responseFromCache) {
|
|
315
|
-
return new Response("FAILED fetch", {
|
|
316
|
-
// @ts-ignore: poor ts typing
|
|
317
|
-
ok: false,
|
|
318
|
-
status: 500,
|
|
319
|
-
type: "cors",
|
|
320
|
-
url: event.request.url
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
return responseFromCache;
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
269
|
var swSelf = self;
|
|
327
270
|
swSelf.addEventListener("fetch", (ev) => {
|
|
328
271
|
const url = ev.request.url;
|
|
329
272
|
const extension = url.match(/\.[^.]+$/)?.[0];
|
|
330
273
|
const mimeType = extension && extension in ACCEPTED_EXTENSIONS ? ACCEPTED_EXTENSIONS[extension] : "";
|
|
331
274
|
const isValidScreenCall = canHandleScreenCall(url);
|
|
332
|
-
if (STORAGE !== "cache" && url.endsWith(".zip")) {
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
275
|
if (!mimeType && !isValidScreenCall) {
|
|
336
|
-
if (!/\.(css|tsx?|jsx?|woff2|fr
|
|
337
|
-
console.debug("no ext",
|
|
276
|
+
if (!/\.(css|tsx?|jsx?|woff2|fr)/.test(url)) {
|
|
277
|
+
console.debug("no ext", url.match(/\.[^.]+$/)?.[0]);
|
|
338
278
|
}
|
|
339
|
-
return
|
|
279
|
+
return;
|
|
340
280
|
}
|
|
341
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
|
+
}
|
|
342
288
|
if (isValidScreenCall) {
|
|
343
289
|
console.debug("screencall!");
|
|
344
290
|
return ev.respondWith(fetchScreenCall(ev, { offlineWorkerSync, knownLinks }));
|
|
345
291
|
}
|
|
346
|
-
|
|
347
|
-
return ev.respondWith(fetchAsset(ev, { entryId, mimeType, offlineWorkerSync }));
|
|
348
|
-
}
|
|
349
|
-
if (/lls|lelivrescolaire/.test(url)) {
|
|
350
|
-
console.info("no link for", url.replace(/https:\/\/.*?\//, ""), `medias/${url.replace(/https:\/\/.*?\//, "")}`);
|
|
351
|
-
}
|
|
352
|
-
return;
|
|
292
|
+
return ev.respondWith(fetchAsset(ev, { entryId, mimeType, offlineWorkerSync }));
|
|
353
293
|
});
|
|
354
294
|
swSelf.addEventListener("install", (_event) => {
|
|
355
295
|
swSelf.skipWaiting();
|
|
356
296
|
});
|
|
357
|
-
swSelf.addEventListener("activate", (
|
|
358
|
-
|
|
297
|
+
swSelf.addEventListener("activate", (_event) => {
|
|
298
|
+
return swSelf.clients.claim();
|
|
359
299
|
});
|
|
360
300
|
var offlineWorker;
|
|
361
301
|
var offlineWorkerSync;
|
|
362
302
|
var initChannel = async (__, { ports }) => {
|
|
363
|
-
console.info(`FETCH WORKER innitChannel v${
|
|
303
|
+
console.info(`FETCH WORKER innitChannel v${13}`);
|
|
364
304
|
offlineWorker = ports[0];
|
|
365
|
-
|
|
305
|
+
offlineWorkerSync = makeSyncWorker(
|
|
366
306
|
offlineWorker,
|
|
367
307
|
["readZipEntry", "loadBook", "slugToFileMeta", "loadPageContent", "searchBooks"],
|
|
368
308
|
"fetchworker"
|
|
369
309
|
);
|
|
370
|
-
offlineWorkerSync
|
|
371
|
-
offlineWorker.onmessage = handler.handle;
|
|
372
|
-
offlineWorkerSync.on("setKnownLinks", setKnownLinks.bind(null, knownLinks));
|
|
310
|
+
offlineWorkerSync.on("setKnownLinks", setKnownLinks);
|
|
373
311
|
offlineWorker.postMessage({ methodName: "sendAppReady" });
|
|
374
312
|
};
|
|
375
313
|
var knownLinks = /* @__PURE__ */ new Map();
|
|
376
|
-
var
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
CONF_ENV = "development";
|
|
382
|
-
if (typeof storage !== "undefined") {
|
|
383
|
-
STORAGE = storage;
|
|
384
|
-
}
|
|
385
|
-
if (typeof confEnv !== "undefined") {
|
|
386
|
-
CONF_ENV = confEnv;
|
|
387
|
-
}
|
|
388
|
-
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
|
+
});
|
|
389
319
|
};
|
|
390
320
|
swSelf.onmessage = async ({
|
|
391
321
|
data: { methodName, arg } = {},
|
|
392
322
|
ports
|
|
393
323
|
}) => {
|
|
394
324
|
const services = {
|
|
395
|
-
initChannel
|
|
396
|
-
configure
|
|
325
|
+
initChannel
|
|
397
326
|
};
|
|
398
327
|
try {
|
|
399
|
-
if (methodName === "configure") {
|
|
400
|
-
await services[methodName](arg);
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
328
|
if (methodName === "initChannel") {
|
|
404
329
|
if (!ports.length) {
|
|
405
330
|
throw new Error("fetchworker::missing port!");
|
|
@@ -1,89 +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
19
|
|
|
88
20
|
// src/worker/wwApi.ts
|
|
89
21
|
var STATE = {
|
|
@@ -802,9 +734,6 @@ var loadZip = async (buf, bookId) => {
|
|
|
802
734
|
const unzipper = new Unzip();
|
|
803
735
|
unzipper.register(UnzipInflate);
|
|
804
736
|
unzipper.onfile = (file) => {
|
|
805
|
-
if (!file.name.includes(".")) {
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
737
|
m.set(file.name, {
|
|
809
738
|
decoded: null,
|
|
810
739
|
zipEntry: {
|
|
@@ -842,26 +771,10 @@ var loadZip = async (buf, bookId) => {
|
|
|
842
771
|
|
|
843
772
|
// src/worker/offline-worker.ts
|
|
844
773
|
var CHECK_UPDATES = true;
|
|
845
|
-
var
|
|
846
|
-
|
|
847
|
-
var CONF_ENV = "development";
|
|
848
|
-
var LOADED_BOOKS = /* @__PURE__ */ new Map();
|
|
849
|
-
var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
774
|
+
var fetchZipFile = async (zipUrl, forceRefresh) => {
|
|
775
|
+
const opfsRoot = await navigator.storage.getDirectory();
|
|
850
776
|
const fileName = zipUrl.split("/").slice(-1)[0];
|
|
851
777
|
const readArrayBuffer = async () => {
|
|
852
|
-
if (STORAGE === "native") {
|
|
853
|
-
const back = await sender.nativeReadFile({ fname: fileName });
|
|
854
|
-
return back;
|
|
855
|
-
}
|
|
856
|
-
if (STORAGE === "cache") {
|
|
857
|
-
const response = await caches.match(zipUrl);
|
|
858
|
-
if (!response) {
|
|
859
|
-
throw new Error(`missing zip ${zipUrl}`);
|
|
860
|
-
}
|
|
861
|
-
const buf = await response.arrayBuffer();
|
|
862
|
-
return buf;
|
|
863
|
-
}
|
|
864
|
-
const opfsRoot = await navigator.storage.getDirectory();
|
|
865
778
|
const handle = await opfsRoot.getFileHandle(fileName);
|
|
866
779
|
const accessHandle = await handle.createSyncAccessHandle();
|
|
867
780
|
const size = accessHandle.getSize();
|
|
@@ -887,8 +800,8 @@ var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
|
887
800
|
}
|
|
888
801
|
return buf;
|
|
889
802
|
} catch (e) {
|
|
890
|
-
if (e
|
|
891
|
-
console.debug(
|
|
803
|
+
if (e.message !== "forceRefresh" && !e.message.includes("refetching")) {
|
|
804
|
+
console.debug("opfs db not found, creating it", e, zipUrl);
|
|
892
805
|
}
|
|
893
806
|
const response = await fetch(zipUrl);
|
|
894
807
|
const contentLength = +response.headers.get("Content-Length");
|
|
@@ -899,7 +812,6 @@ var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
|
899
812
|
while (true) {
|
|
900
813
|
const { done, value: chunk } = await reader.read();
|
|
901
814
|
if (done) {
|
|
902
|
-
self.postMessage({ methodName: "opfsProgress", arg: { total: contentLength, bytes: contentLength } });
|
|
903
815
|
break;
|
|
904
816
|
}
|
|
905
817
|
arrayBuffer.set(chunk, position);
|
|
@@ -914,69 +826,34 @@ var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
|
914
826
|
console.debug(error);
|
|
915
827
|
throw new Error(error);
|
|
916
828
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
const accessHandle = await handle.createSyncAccessHandle();
|
|
923
|
-
await accessHandle.truncate(0);
|
|
924
|
-
await accessHandle.write(arrayBuffer);
|
|
925
|
-
await accessHandle.close();
|
|
926
|
-
} else {
|
|
927
|
-
const cache = await openCache(CONF_ENV);
|
|
928
|
-
const response2 = new Response(arrayBuffer, {
|
|
929
|
-
headers: {
|
|
930
|
-
"Cache-Control": "max-age=600, s-maxage=600, public, proxy-revalidate",
|
|
931
|
-
"Content-Type": "application/zip"
|
|
932
|
-
},
|
|
933
|
-
// @ts-ignore: poor ts typing
|
|
934
|
-
ok: true,
|
|
935
|
-
redirected: false,
|
|
936
|
-
status: 200,
|
|
937
|
-
type: "cors",
|
|
938
|
-
url: zipUrl
|
|
939
|
-
});
|
|
940
|
-
await cache.put(zipUrl, response2);
|
|
941
|
-
}
|
|
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();
|
|
942
834
|
return await readArrayBuffer();
|
|
943
835
|
}
|
|
944
836
|
};
|
|
945
837
|
var slugToFileMeta2 = async ({ slug }) => {
|
|
838
|
+
console.debug("hereshit");
|
|
946
839
|
const json = await readZipEntry({ bookId: "preload", entryName: "preload/hashmap_slugToMetaPage.json", asJson: true });
|
|
840
|
+
console.debug("hereshit2");
|
|
947
841
|
return slugToFileMeta({ slug, json });
|
|
948
842
|
};
|
|
949
843
|
var PRELOAD_AS_BOOK_ID = "preload";
|
|
950
844
|
var isOldZip = (bookId) => typeof bookId === "number";
|
|
951
845
|
var loadBook2 = async ({ id: bookId, forceRefresh }) => {
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
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);
|
|
956
856
|
}
|
|
957
|
-
const p = new Promise(
|
|
958
|
-
tryCatch(async () => {
|
|
959
|
-
const buf = await fetchZipFile(`${ZIP_BASE_URL}/${bookId}.zip`, { forceRefresh });
|
|
960
|
-
try {
|
|
961
|
-
const entryNames = await loadZip(new Uint8Array(buf), bookId);
|
|
962
|
-
fetchWorker.postMessage({ methodName: "setKnownLinks", arg: { entryNames, bookId } });
|
|
963
|
-
if (isOldZip(bookId)) {
|
|
964
|
-
const json = await readZipEntry({
|
|
965
|
-
entryName: `preload/${bookId}.json`,
|
|
966
|
-
bookId: PRELOAD_AS_BOOK_ID,
|
|
967
|
-
asJson: true
|
|
968
|
-
});
|
|
969
|
-
loadBook(json);
|
|
970
|
-
}
|
|
971
|
-
return true;
|
|
972
|
-
} catch (e) {
|
|
973
|
-
console.error("failed", e);
|
|
974
|
-
return false;
|
|
975
|
-
}
|
|
976
|
-
})
|
|
977
|
-
);
|
|
978
|
-
LOADED_BOOKS.set(bookId, p);
|
|
979
|
-
await p;
|
|
980
857
|
};
|
|
981
858
|
var loadPageContent2 = async ({
|
|
982
859
|
bookId: iBookId,
|
|
@@ -998,7 +875,7 @@ var loadPageContent2 = async ({
|
|
|
998
875
|
var fetchWorker;
|
|
999
876
|
var initChannel = (__, { ports }) => {
|
|
1000
877
|
fetchWorker = ports[0];
|
|
1001
|
-
const
|
|
878
|
+
const defaultHandler = makeSyncHandler(fetchWorker, {
|
|
1002
879
|
readZipEntry,
|
|
1003
880
|
loadBook: loadBook2,
|
|
1004
881
|
slugToFileMeta: slugToFileMeta2,
|
|
@@ -1010,69 +887,23 @@ var initChannel = (__, { ports }) => {
|
|
|
1010
887
|
self.postMessage({ methodName: "ready" });
|
|
1011
888
|
return;
|
|
1012
889
|
}
|
|
1013
|
-
|
|
1014
|
-
return handle(dataObj);
|
|
1015
|
-
}
|
|
1016
|
-
console.error("unknown methodName", dataObj.data?.methodName);
|
|
890
|
+
defaultHandler(dataObj);
|
|
1017
891
|
};
|
|
1018
892
|
fetchWorker.onmessage = fn;
|
|
1019
893
|
};
|
|
1020
|
-
var loadBooks = async (
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
entries = await sender.nativeListZips();
|
|
1024
|
-
} else if (STORAGE === "opfs") {
|
|
1025
|
-
const opfsRoot = await navigator.storage.getDirectory();
|
|
1026
|
-
entries = await opfsRoot.values();
|
|
1027
|
-
} else {
|
|
1028
|
-
const cache = await openCache(CONF_ENV);
|
|
1029
|
-
const keys = await cache.keys();
|
|
1030
|
-
const zips = keys.filter((key2) => {
|
|
1031
|
-
return key2.url.match(/lecture-cp\.zip/);
|
|
1032
|
-
});
|
|
1033
|
-
entries = zips.map((zip) => ({ name: String(zip.url.split("/").pop()) }));
|
|
1034
|
-
}
|
|
894
|
+
var loadBooks = async () => {
|
|
895
|
+
const opfsRoot = await navigator.storage.getDirectory();
|
|
896
|
+
const entries = await opfsRoot.values();
|
|
1035
897
|
for await (const entry of entries) {
|
|
1036
|
-
if (/preload/.test(entry.name)) {
|
|
1037
|
-
continue;
|
|
1038
|
-
}
|
|
1039
898
|
if (/\d+\.zip$/.test(entry.name)) {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
await loadBook2({ id: Number.parseInt(bookId, 10) });
|
|
1043
|
-
}
|
|
899
|
+
const bookId = entry.name.match(/\d+/)[0];
|
|
900
|
+
await loadBook2({ id: Number.parseInt(bookId, 10) });
|
|
1044
901
|
} else if (/zip$/.test(entry.name)) {
|
|
1045
902
|
const methodUri = entry.name.replace(".zip", "");
|
|
1046
903
|
await loadBook2({ id: methodUri });
|
|
1047
|
-
} else {
|
|
1048
|
-
console.info(`invalid book entry ${entry.name}`);
|
|
1049
904
|
}
|
|
1050
905
|
}
|
|
1051
906
|
};
|
|
1052
|
-
var configure = ({
|
|
1053
|
-
storage,
|
|
1054
|
-
checkUpdates,
|
|
1055
|
-
zipBaseUrl,
|
|
1056
|
-
confEnv
|
|
1057
|
-
}) => {
|
|
1058
|
-
CHECK_UPDATES = true;
|
|
1059
|
-
STORAGE = "cache";
|
|
1060
|
-
ZIP_BASE_URL = "https://ci.lls.fr/artefacts/content/development";
|
|
1061
|
-
CONF_ENV = "development";
|
|
1062
|
-
if (typeof storage !== "undefined") {
|
|
1063
|
-
STORAGE = storage;
|
|
1064
|
-
}
|
|
1065
|
-
if (typeof checkUpdates !== "undefined") {
|
|
1066
|
-
CHECK_UPDATES = !!checkUpdates;
|
|
1067
|
-
}
|
|
1068
|
-
if (typeof zipBaseUrl !== "undefined") {
|
|
1069
|
-
ZIP_BASE_URL = zipBaseUrl;
|
|
1070
|
-
}
|
|
1071
|
-
if (typeof confEnv !== "undefined") {
|
|
1072
|
-
CONF_ENV = confEnv;
|
|
1073
|
-
}
|
|
1074
|
-
console.info("ow::configure", { STORAGE, CHECK_UPDATES, ZIP_BASE_URL, CONF_ENV });
|
|
1075
|
-
};
|
|
1076
907
|
var loadPreload2 = async () => {
|
|
1077
908
|
const zipUrl = "https://lls-ci-fr.s3.eu-west-3.amazonaws.com/artefacts/content/development/preload.zip";
|
|
1078
909
|
const buf = await fetchZipFile(zipUrl);
|
|
@@ -1085,13 +916,10 @@ var loadPreload2 = async () => {
|
|
|
1085
916
|
console.error("failed", e);
|
|
1086
917
|
}
|
|
1087
918
|
};
|
|
1088
|
-
|
|
919
|
+
self.onmessage = makeSyncHandler(self, {
|
|
1089
920
|
initChannel,
|
|
1090
921
|
loadPreload: loadPreload2,
|
|
1091
922
|
readZipEntry,
|
|
1092
923
|
loadBooks,
|
|
1093
|
-
loadBook: loadBook2
|
|
1094
|
-
configure
|
|
924
|
+
loadBook: loadBook2
|
|
1095
925
|
});
|
|
1096
|
-
var { handler, ...sender } = makeSyncWorker(self, ["nativeWriteFile", "nativeReadFile", "nativeListZips"]);
|
|
1097
|
-
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,93 +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.register("/fetch-worker.js", { type: "module" }).then((reg) => {
|
|
98
|
-
return reg.update().catch((e) => {
|
|
99
|
-
console.debug(e, "likely we are offline");
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
await navigator.serviceWorker.ready.then(async (reg) => {
|
|
103
|
-
console.debug("Service Worker registered:", reg.scope);
|
|
104
|
-
fetchWorker = reg.active;
|
|
105
|
-
}).catch((err) => console.error("Service Worker registration failed:", err));
|
|
106
|
-
if (!fetchWorker) {
|
|
107
|
-
console.debug("failed to get fetch worker");
|
|
108
|
-
}
|
|
109
|
-
return fetchWorker;
|
|
110
|
-
})
|
|
111
|
-
);
|
|
112
61
|
var setWorkersReady;
|
|
113
62
|
var workersReadyPromise = new Promise((resolve) => {
|
|
114
63
|
setWorkersReady = resolve;
|
|
@@ -117,81 +66,46 @@ var setBooksLoaded;
|
|
|
117
66
|
var setBooksLoadedPromise = new Promise((resolve) => {
|
|
118
67
|
setBooksLoaded = resolve;
|
|
119
68
|
});
|
|
120
|
-
var
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
nativeListZips,
|
|
126
|
-
storage,
|
|
127
|
-
zipBaseUrl,
|
|
128
|
-
checkUpdates,
|
|
129
|
-
confEnv
|
|
130
|
-
} = {}) => {
|
|
131
|
-
const fetchWorker = await fetchWorkerPromise;
|
|
132
|
-
const configureOptions = { storage, zipBaseUrl, checkUpdates, confEnv };
|
|
133
|
-
if (nativeReadFile) {
|
|
134
|
-
const wwHandler = makeSyncHandler(offlineWorker, { nativeReadFile, nativeWriteFile, nativeListZips });
|
|
135
|
-
offlineWorker.onmessage = combineHandlers(handler, wwHandler);
|
|
136
|
-
if (storage !== "native") {
|
|
137
|
-
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();
|
|
138
74
|
}
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
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;
|
|
143
84
|
}
|
|
144
85
|
offlineWorkerSync.on("ready", setWorkersReady);
|
|
145
86
|
const channel = new MessageChannel();
|
|
146
87
|
offlineWorkerSync.initChannel({ ports: [channel.port1] });
|
|
147
88
|
fetchWorker.postMessage({ methodName: "initChannel" }, [channel.port2]);
|
|
148
|
-
fetchWorker.postMessage({ methodName: "configure", arg: { storage } });
|
|
149
89
|
offlineWorkerSync.on("opfsProgress", () => {
|
|
150
90
|
});
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
if (!skipLoadBooks) {
|
|
155
|
-
await offlineWorkerSync.loadBooks({ primaryOnly });
|
|
156
|
-
}
|
|
91
|
+
await offlineWorkerSync.loadPreload();
|
|
92
|
+
await offlineWorkerSync.loadBooks();
|
|
157
93
|
setBooksLoaded();
|
|
158
94
|
};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
nativeReadFile,
|
|
162
|
-
nativeWriteFile,
|
|
163
|
-
nativeListZips,
|
|
164
|
-
checkUpdates,
|
|
165
|
-
storage,
|
|
166
|
-
zipBaseUrl,
|
|
167
|
-
confEnv
|
|
168
|
-
} = {}) => {
|
|
95
|
+
fn();
|
|
96
|
+
var useOfflineWorker_default = () => {
|
|
169
97
|
const [ready, setReady] = useState(false);
|
|
170
98
|
useEffect(() => {
|
|
171
|
-
const
|
|
172
|
-
await installWorker({
|
|
173
|
-
primaryOnly,
|
|
174
|
-
nativeReadFile,
|
|
175
|
-
nativeWriteFile,
|
|
176
|
-
nativeListZips,
|
|
177
|
-
storage,
|
|
178
|
-
zipBaseUrl,
|
|
179
|
-
checkUpdates,
|
|
180
|
-
confEnv
|
|
181
|
-
});
|
|
99
|
+
const fn2 = async () => {
|
|
182
100
|
await Promise.all([workersReadyPromise, setBooksLoadedPromise]);
|
|
183
|
-
fetch(window.location.href.replace(window.location.search, ""));
|
|
184
101
|
setReady(true);
|
|
185
102
|
};
|
|
186
|
-
|
|
187
|
-
}, [
|
|
188
|
-
const loadBook = useCallback(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
},
|
|
193
|
-
[]
|
|
194
|
-
);
|
|
103
|
+
fn2();
|
|
104
|
+
}, []);
|
|
105
|
+
const loadBook = useCallback(async ({ id, forceRefresh }, cbk) => {
|
|
106
|
+
offlineWorkerSync.on("opfsProgress", cbk);
|
|
107
|
+
return await offlineWorkerSync.loadBook({ id, forceRefresh });
|
|
108
|
+
}, []);
|
|
195
109
|
return {
|
|
196
110
|
ready,
|
|
197
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
|
}
|