@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.
@@ -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: unknown methodName cbk ${dbg}`, eventId, methodName, error);
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()}-${Math.random().toFixed(5)}`;
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
- canHandle: (methodName) => {
47
- return methodName in services;
48
- },
49
- handle: async ({ data: { eventId, methodName, arg } = {}, ports }) => {
50
- try {
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-expect-error TODO: handle variables from body(stream) properly!!
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 STORAGE = "cache";
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
- statusText: "OK"
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
- statusText: e?.toString?.() || "fail fetch"
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|manifest)(\??|$)|@(vite|react|id)/.test(url)) {
337
- console.debug("no ext", extension || url);
276
+ if (!/\.(css|tsx?|jsx?|woff2|fr)/.test(url)) {
277
+ console.debug("no ext", url.match(/\.[^.]+$/)?.[0]);
338
278
  }
339
- return ev.respondWith(onlineFirst(ev.request, ev));
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
- if (entryId) {
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", (event) => {
358
- event.waitUntil(swSelf.clients.claim());
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${/* @__PURE__ */ new Date()}`);
303
+ console.info(`FETCH WORKER innitChannel v${13}`);
364
304
  offlineWorker = ports[0];
365
- const { handler, ...sender } = makeSyncWorker(
305
+ offlineWorkerSync = makeSyncWorker(
366
306
  offlineWorker,
367
307
  ["readZipEntry", "loadBook", "slugToFileMeta", "loadPageContent", "searchBooks"],
368
308
  "fetchworker"
369
309
  );
370
- offlineWorkerSync = sender;
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 configure = ({
377
- storage,
378
- confEnv
379
- } = {}) => {
380
- STORAGE = "cache";
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
- (sender2, services) => ({
57
- canHandle: (methodName) => {
58
- return methodName in services;
59
- },
60
- handle: async ({ data: { eventId, methodName, arg } = {}, ports }) => {
61
- try {
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 STORAGE = "cache";
846
- var ZIP_BASE_URL = "https://ci.lls.fr/artefacts/content/development";
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 instanceof Error && e.message !== "forceRefresh" && !e.message.includes("refetching")) {
891
- console.debug(`${STORAGE} zip not found, creating it`, e, zipUrl);
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
- if (STORAGE === "native") {
918
- await sender.nativeWriteFile({ fname: fileName, buffer: arrayBuffer });
919
- } else if (STORAGE === "opfs") {
920
- const opfsRoot = await navigator.storage.getDirectory();
921
- const handle = await opfsRoot.getFileHandle(fileName, { create: true });
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
- if (LOADED_BOOKS.has(bookId)) {
953
- await LOADED_BOOKS.get(bookId);
954
- self.postMessage({ methodName: "opfsProgress", arg: { total: 1, bytes: 1 } });
955
- return;
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 { canHandle, handle } = makeSyncHandler(fetchWorker, {
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
- if (canHandle(dataObj.data?.methodName || "")) {
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 ({ primaryOnly = false } = {}) => {
1021
- let entries = [];
1022
- if (STORAGE === "native") {
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
- if (!primaryOnly) {
1041
- const bookId = entry.name.match(/\d+/)[0];
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
- var mainHandle = makeSyncHandler(self, {
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 = (offlineWorker2, methodNames, dbg) => {
2
+ var makeSyncWorker = (offlineWorker, methodNames, dbg) => {
14
3
  const dic = {};
15
4
  const cbks = {};
16
- const fn = ({ data: { eventId, methodName, results, error, arg } = {} } = {}) => {
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: unknown methodName cbk ${dbg}`, eventId, methodName, error);
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()}-${Math.random().toFixed(5)}`;
23
+ const eventId = `${methodName}:${Date.now()}`;
34
24
  dic[eventId] = { resolve, reject };
35
- offlineWorker2.postMessage({ eventId, methodName, arg }, ports);
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, fn2) => {
41
- cbks[key] = fn2;
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
- canHandle: (methodName) => {
58
- return methodName in services;
59
- },
60
- handle: async ({ data: { eventId, methodName, arg } = {}, ports }) => {
61
- try {
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 offlineWorker = new Worker("/src/worker/offline-worker.js", { type: "module" });
89
- var { handler, ...offlineWorkerSync } = makeSyncWorker(
90
- offlineWorker,
91
- ["initChannel", "readZipEntry", "configure", "loadPreload", "loadBooks", "loadBook"],
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 installWorker = async ({
121
- skipLoadBooks = false,
122
- primaryOnly = false,
123
- nativeReadFile,
124
- nativeWriteFile,
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
- offlineWorkerSync.configure({ ...configureOptions, storage: "native" });
140
- } else {
141
- offlineWorker.onmessage = handler.handle;
142
- offlineWorkerSync.configure(configureOptions);
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
- if (!primaryOnly) {
152
- await offlineWorkerSync.loadPreload();
153
- }
154
- if (!skipLoadBooks) {
155
- await offlineWorkerSync.loadBooks({ primaryOnly });
156
- }
91
+ await offlineWorkerSync.loadPreload();
92
+ await offlineWorkerSync.loadBooks();
157
93
  setBooksLoaded();
158
94
  };
159
- var useOfflineWorker_default = ({
160
- primaryOnly = false,
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 fn = async () => {
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
- fn();
187
- }, [primaryOnly, nativeReadFile, nativeWriteFile, nativeListZips, storage, zipBaseUrl, checkUpdates, confEnv]);
188
- const loadBook = useCallback(
189
- async ({ id, forceRefresh }, cbk) => {
190
- offlineWorkerSync.on("opfsProgress", cbk);
191
- return await offlineWorkerSync.loadBook({ id, forceRefresh });
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-bfdb7edf",
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-bfdb7edf"
52
+ "@lls/vitescripts": "24.22.0"
56
53
  },
57
54
  "dependencies": {
58
55
  "fflate": "0.8.2"
59
56
  },
60
57
  "scripts": {
61
- "start": "pnpm ladle serve",
58
+ "start": "echo no devserver",
62
59
  "dts": "pnpm tsc --project tsconfig.production.json",
63
- "build": "NODE_ENV=production node build.js",
64
- "deploy-ladle": "PUBLISH=1 pnpm ladle build",
65
- "deploy-ladle-pr": "pnpm ladle build && s5cmd sync --delete --acl public-read --destination-region eu-west-1 'dist/*' s3://build.lelivrescolaire.fr/storybook/viewer-pr-$PR_NUMBER/",
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
  }