@lls/offline 24.22.0-658f61a6 → 24.22.0-a4074e64
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 +24 -43
- package/lib/assets/offline-worker.js +21 -54
- package/lib/index.js +6 -12
- package/package.json +2 -2
|
@@ -19,7 +19,7 @@ var makeSyncWorker = (offlineWorker2, methodNames, dbg) => {
|
|
|
19
19
|
const makeFn = (methodName) => {
|
|
20
20
|
return ({ ports, ...arg } = {}) => {
|
|
21
21
|
return new Promise((resolve, reject) => {
|
|
22
|
-
const eventId = `${methodName}:${Date.now()}
|
|
22
|
+
const eventId = `${methodName}:${Date.now()}`;
|
|
23
23
|
dic[eventId] = { resolve, reject };
|
|
24
24
|
offlineWorker2.postMessage({ eventId, methodName, arg }, ports);
|
|
25
25
|
});
|
|
@@ -62,7 +62,7 @@ var makeSyncHandler = a(
|
|
|
62
62
|
})
|
|
63
63
|
);
|
|
64
64
|
var jsonToBuf = (json) => new TextEncoder().encode(JSON.stringify(json));
|
|
65
|
-
var openCache = async (
|
|
65
|
+
var openCache = async () => caches.open(`LLS_OFFLINE_${process.env.CONF_ENV}`);
|
|
66
66
|
|
|
67
67
|
// src/worker/swFetchScreenCall.ts
|
|
68
68
|
var zipSlugName = (str) => str.replace(/[^a-zA-Z0-9-.]/g, "");
|
|
@@ -75,15 +75,6 @@ var cocoreFetchPartialPageScreen = async (_ev, { offlineWorkerSync: offlineWorke
|
|
|
75
75
|
const gqlPage = await offlineWorkerSync2.loadPageContent({ entryId });
|
|
76
76
|
return { data: gqlPage };
|
|
77
77
|
};
|
|
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
78
|
var cocoreFetchPartialPageSiblingsScreen = async (_ev) => {
|
|
88
79
|
return { data: { viewer: {} } };
|
|
89
80
|
};
|
|
@@ -198,7 +189,6 @@ var coreFetchPagesApi = async (ev, { offlineWorkerSync: offlineWorkerSync2, vari
|
|
|
198
189
|
};
|
|
199
190
|
var handles = {
|
|
200
191
|
coreFetchBookDrawerScreen,
|
|
201
|
-
cocoreFetchBookScreen,
|
|
202
192
|
coreFetchPagesApi,
|
|
203
193
|
cocoreFetchPartialPageScreen,
|
|
204
194
|
cocoreFetchPartialPageSiblingsScreen
|
|
@@ -230,20 +220,21 @@ var setKnownLinks = (knownLinks2, { entryNames, bookId }) => {
|
|
|
230
220
|
};
|
|
231
221
|
|
|
232
222
|
// src/worker/fetch-worker.ts
|
|
233
|
-
var
|
|
234
|
-
var CONF_ENV = "development";
|
|
235
|
-
var fetchAsset = async (_ev, { entryId, mimeType, offlineWorkerSync: offlineWorkerSync2 }) => {
|
|
223
|
+
var fetchAsset = async (ev, { entryId, mimeType, offlineWorkerSync: offlineWorkerSync2 }) => {
|
|
236
224
|
try {
|
|
237
225
|
const buf = await offlineWorkerSync2.readZipEntry(entryId);
|
|
226
|
+
const url = ev.request.url;
|
|
238
227
|
return new Response(buf, {
|
|
239
228
|
headers: {
|
|
240
229
|
"Cache-Control": "max-age=600, s-maxage=600, public, proxy-revalidate",
|
|
241
|
-
"Content-Length": buf.byteLength,
|
|
242
230
|
"Content-Type": mimeType
|
|
243
231
|
},
|
|
244
232
|
// @ts-ignore: poor ts typing
|
|
233
|
+
ok: true,
|
|
234
|
+
redirected: false,
|
|
245
235
|
status: 200,
|
|
246
|
-
|
|
236
|
+
type: "cors",
|
|
237
|
+
url
|
|
247
238
|
});
|
|
248
239
|
} catch (e) {
|
|
249
240
|
console.error("FAILED fetchAsset", e);
|
|
@@ -251,7 +242,8 @@ var fetchAsset = async (_ev, { entryId, mimeType, offlineWorkerSync: offlineWork
|
|
|
251
242
|
// @ts-ignore: poor ts typing
|
|
252
243
|
ok: false,
|
|
253
244
|
status: 500,
|
|
254
|
-
|
|
245
|
+
type: "cors",
|
|
246
|
+
url: ev.request.url
|
|
255
247
|
});
|
|
256
248
|
}
|
|
257
249
|
};
|
|
@@ -291,12 +283,10 @@ var ACCEPTED_EXTENSIONS = {
|
|
|
291
283
|
".gif": "image/gif",
|
|
292
284
|
".webm": "video/webm",
|
|
293
285
|
".m4a": "audio/mp4",
|
|
294
|
-
".webp": "image/webp"
|
|
295
|
-
".riv": "application/octet-stream",
|
|
296
|
-
".pdf": "application/pdf"
|
|
286
|
+
".webp": "image/webp"
|
|
297
287
|
};
|
|
298
288
|
var putInCache = async (request, response) => {
|
|
299
|
-
const cache = await openCache(
|
|
289
|
+
const cache = await openCache();
|
|
300
290
|
await cache.put(request, response);
|
|
301
291
|
};
|
|
302
292
|
var onlineFirst = async (request, event) => {
|
|
@@ -310,24 +300,22 @@ var onlineFirst = async (request, event) => {
|
|
|
310
300
|
event.waitUntil(cached);
|
|
311
301
|
}
|
|
312
302
|
return responseFromNetwork;
|
|
313
|
-
} catch (
|
|
303
|
+
} catch (_e) {
|
|
314
304
|
const over = /\/\?story=basic/.test(request.url) ? request.url.replace(/\/\?story=.*$/, "") : request;
|
|
315
305
|
const responseFromCache = await caches.match(over);
|
|
316
306
|
if (!responseFromCache) {
|
|
317
|
-
return new Response(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
url: event.request.url
|
|
325
|
-
}
|
|
326
|
-
);
|
|
307
|
+
return new Response("FAILED fetch", {
|
|
308
|
+
// @ts-ignore: poor ts typing
|
|
309
|
+
ok: false,
|
|
310
|
+
status: 500,
|
|
311
|
+
type: "cors",
|
|
312
|
+
url: event.request.url
|
|
313
|
+
});
|
|
327
314
|
}
|
|
328
315
|
return responseFromCache;
|
|
329
316
|
}
|
|
330
317
|
};
|
|
318
|
+
var STORAGE = "cache";
|
|
331
319
|
var swSelf = self;
|
|
332
320
|
swSelf.addEventListener("fetch", (ev) => {
|
|
333
321
|
const url = ev.request.url;
|
|
@@ -374,23 +362,16 @@ var initChannel = async (__, { ports }) => {
|
|
|
374
362
|
);
|
|
375
363
|
offlineWorkerSync = sender;
|
|
376
364
|
offlineWorker.onmessage = handler.handle;
|
|
377
|
-
offlineWorkerSync.on("setKnownLinks", setKnownLinks.bind(null,
|
|
365
|
+
offlineWorkerSync.on("setKnownLinks", setKnownLinks.bind(null, setKnownLinks));
|
|
378
366
|
offlineWorker.postMessage({ methodName: "sendAppReady" });
|
|
379
367
|
};
|
|
380
368
|
var knownLinks = /* @__PURE__ */ new Map();
|
|
381
|
-
var configure = ({
|
|
382
|
-
storage,
|
|
383
|
-
confEnv
|
|
384
|
-
} = {}) => {
|
|
369
|
+
var configure = ({ storage } = {}) => {
|
|
385
370
|
STORAGE = "cache";
|
|
386
|
-
CONF_ENV = "development";
|
|
387
371
|
if (typeof storage !== "undefined") {
|
|
388
372
|
STORAGE = storage;
|
|
389
373
|
}
|
|
390
|
-
|
|
391
|
-
CONF_ENV = confEnv;
|
|
392
|
-
}
|
|
393
|
-
console.info("sw::configure", { STORAGE, CONF_ENV });
|
|
374
|
+
console.info("sw::configure", { STORAGE });
|
|
394
375
|
};
|
|
395
376
|
swSelf.onmessage = async ({
|
|
396
377
|
data: { methodName, arg } = {},
|
|
@@ -30,7 +30,7 @@ var makeSyncWorker = (offlineWorker, methodNames, dbg) => {
|
|
|
30
30
|
const makeFn = (methodName) => {
|
|
31
31
|
return ({ ports, ...arg } = {}) => {
|
|
32
32
|
return new Promise((resolve, reject) => {
|
|
33
|
-
const eventId = `${methodName}:${Date.now()}
|
|
33
|
+
const eventId = `${methodName}:${Date.now()}`;
|
|
34
34
|
dic[eventId] = { resolve, reject };
|
|
35
35
|
offlineWorker.postMessage({ eventId, methodName, arg }, ports);
|
|
36
36
|
});
|
|
@@ -83,17 +83,7 @@ var combineHandlers = (...handlers) => {
|
|
|
83
83
|
console.error("no handlers matched", methodName);
|
|
84
84
|
};
|
|
85
85
|
};
|
|
86
|
-
var openCache = async (
|
|
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
|
-
};
|
|
86
|
+
var openCache = async () => caches.open(`LLS_OFFLINE_${process.env.CONF_ENV}`);
|
|
97
87
|
|
|
98
88
|
// src/worker/wwApi.ts
|
|
99
89
|
var STATE = {
|
|
@@ -775,9 +765,6 @@ async function decryptData(encryptedData, key2, iv2) {
|
|
|
775
765
|
return decryptedData;
|
|
776
766
|
}
|
|
777
767
|
var BOOKS = {};
|
|
778
|
-
var zipNameInMedias = (zipName) => {
|
|
779
|
-
return /^(pages|book)/.test(zipName);
|
|
780
|
-
};
|
|
781
768
|
var readZipEntry = async ({
|
|
782
769
|
bookId,
|
|
783
770
|
entryName,
|
|
@@ -787,20 +774,9 @@ var readZipEntry = async ({
|
|
|
787
774
|
console.trace("no bookId", bookId, "found");
|
|
788
775
|
return null;
|
|
789
776
|
}
|
|
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);
|
|
777
|
+
const bookEntry = BOOKS[bookId].get(entryName);
|
|
802
778
|
if (!bookEntry) {
|
|
803
|
-
console.info("no bookEntry", entryName, "found"
|
|
779
|
+
console.info("no bookEntry", entryName, "found");
|
|
804
780
|
return null;
|
|
805
781
|
}
|
|
806
782
|
if (!bookEntry.decoded) {
|
|
@@ -811,6 +787,16 @@ var readZipEntry = async ({
|
|
|
811
787
|
var hasZip = (bookId) => {
|
|
812
788
|
return !!BOOKS[bookId];
|
|
813
789
|
};
|
|
790
|
+
function concatArrayBuffers(buffers) {
|
|
791
|
+
const totalLength = buffers.reduce((sum, buf) => sum + buf.byteLength, 0);
|
|
792
|
+
const concatenated = new Uint8Array(totalLength);
|
|
793
|
+
let offset = 0;
|
|
794
|
+
for (const buffer of buffers) {
|
|
795
|
+
concatenated.set(new Uint8Array(buffer), offset);
|
|
796
|
+
offset += buffer.byteLength;
|
|
797
|
+
}
|
|
798
|
+
return concatenated.buffer;
|
|
799
|
+
}
|
|
814
800
|
var loadZip = async (buf, bookId) => {
|
|
815
801
|
const m = /* @__PURE__ */ new Map();
|
|
816
802
|
const unzipper = new Unzip();
|
|
@@ -850,26 +836,14 @@ var loadZip = async (buf, bookId) => {
|
|
|
850
836
|
await sleep;
|
|
851
837
|
}
|
|
852
838
|
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;
|
|
839
|
+
BOOKS[bookId] = m;
|
|
840
|
+
return [...m.keys()];
|
|
866
841
|
};
|
|
867
842
|
|
|
868
843
|
// src/worker/offline-worker.ts
|
|
869
844
|
var CHECK_UPDATES = true;
|
|
870
845
|
var STORAGE = "cache";
|
|
871
846
|
var ZIP_BASE_URL = "https://ci.lls.fr/artefacts/content/development";
|
|
872
|
-
var CONF_ENV = "development";
|
|
873
847
|
var LOADED_BOOKS = /* @__PURE__ */ new Map();
|
|
874
848
|
var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
875
849
|
const fileName = zipUrl.split("/").slice(-1)[0];
|
|
@@ -949,7 +923,7 @@ var fetchZipFile = async (zipUrl, { forceRefresh } = {}) => {
|
|
|
949
923
|
await accessHandle.write(arrayBuffer);
|
|
950
924
|
await accessHandle.close();
|
|
951
925
|
} else {
|
|
952
|
-
const cache = await openCache(
|
|
926
|
+
const cache = await openCache();
|
|
953
927
|
const response2 = new Response(arrayBuffer, {
|
|
954
928
|
headers: {
|
|
955
929
|
"Cache-Control": "max-age=600, s-maxage=600, public, proxy-revalidate",
|
|
@@ -1050,11 +1024,9 @@ var loadBooks = async ({ primaryOnly = false } = {}) => {
|
|
|
1050
1024
|
const opfsRoot = await navigator.storage.getDirectory();
|
|
1051
1025
|
entries = await opfsRoot.values();
|
|
1052
1026
|
} else {
|
|
1053
|
-
const cache = await openCache(
|
|
1027
|
+
const cache = await openCache();
|
|
1054
1028
|
const keys = await cache.keys();
|
|
1055
|
-
const zips = keys.filter((key2) =>
|
|
1056
|
-
return key2.url.match(/lecture-cp\.zip|story-.*?\.zip/);
|
|
1057
|
-
});
|
|
1029
|
+
const zips = keys.filter((key2) => key2.url.endsWith(".zip"));
|
|
1058
1030
|
entries = zips.map((zip) => ({ name: String(zip.url.split("/").pop()) }));
|
|
1059
1031
|
}
|
|
1060
1032
|
for await (const entry of entries) {
|
|
@@ -1077,13 +1049,11 @@ var loadBooks = async ({ primaryOnly = false } = {}) => {
|
|
|
1077
1049
|
var configure = ({
|
|
1078
1050
|
storage,
|
|
1079
1051
|
checkUpdates,
|
|
1080
|
-
zipBaseUrl
|
|
1081
|
-
confEnv
|
|
1052
|
+
zipBaseUrl
|
|
1082
1053
|
}) => {
|
|
1083
1054
|
CHECK_UPDATES = true;
|
|
1084
1055
|
STORAGE = "cache";
|
|
1085
1056
|
ZIP_BASE_URL = "https://ci.lls.fr/artefacts/content/development";
|
|
1086
|
-
CONF_ENV = "development";
|
|
1087
1057
|
if (typeof storage !== "undefined") {
|
|
1088
1058
|
STORAGE = storage;
|
|
1089
1059
|
}
|
|
@@ -1093,10 +1063,7 @@ var configure = ({
|
|
|
1093
1063
|
if (typeof zipBaseUrl !== "undefined") {
|
|
1094
1064
|
ZIP_BASE_URL = zipBaseUrl;
|
|
1095
1065
|
}
|
|
1096
|
-
|
|
1097
|
-
CONF_ENV = confEnv;
|
|
1098
|
-
}
|
|
1099
|
-
console.info("ow::configure", { STORAGE, CHECK_UPDATES, ZIP_BASE_URL, CONF_ENV });
|
|
1066
|
+
console.info("ow::configure", { STORAGE, CHECK_UPDATES, ZIP_BASE_URL });
|
|
1100
1067
|
};
|
|
1101
1068
|
var loadPreload2 = async () => {
|
|
1102
1069
|
const zipUrl = "https://lls-ci-fr.s3.eu-west-3.amazonaws.com/artefacts/content/development/preload.zip";
|
package/lib/index.js
CHANGED
|
@@ -30,7 +30,7 @@ var makeSyncWorker = (offlineWorker2, methodNames, dbg) => {
|
|
|
30
30
|
const makeFn = (methodName) => {
|
|
31
31
|
return ({ ports, ...arg } = {}) => {
|
|
32
32
|
return new Promise((resolve, reject) => {
|
|
33
|
-
const eventId = `${methodName}:${Date.now()}
|
|
33
|
+
const eventId = `${methodName}:${Date.now()}`;
|
|
34
34
|
dic[eventId] = { resolve, reject };
|
|
35
35
|
offlineWorker2.postMessage({ eventId, methodName, arg }, ports);
|
|
36
36
|
});
|
|
@@ -94,9 +94,6 @@ var { handler, ...offlineWorkerSync } = makeSyncWorker(
|
|
|
94
94
|
var fetchWorkerPromise = new Promise(
|
|
95
95
|
tryCatch(async () => {
|
|
96
96
|
let fetchWorker = null;
|
|
97
|
-
await navigator.serviceWorker.getRegistrations().then((registrations) => {
|
|
98
|
-
return Promise.all(registrations.map((reg) => reg.unregister()));
|
|
99
|
-
});
|
|
100
97
|
await navigator.serviceWorker.register("/fetch-worker.js", { type: "module" }).then((reg) => {
|
|
101
98
|
return reg.update().catch((e) => {
|
|
102
99
|
console.debug(e, "likely we are offline");
|
|
@@ -128,11 +125,10 @@ var installWorker = async ({
|
|
|
128
125
|
nativeListZips,
|
|
129
126
|
storage,
|
|
130
127
|
zipBaseUrl,
|
|
131
|
-
checkUpdates
|
|
132
|
-
confEnv
|
|
128
|
+
checkUpdates
|
|
133
129
|
} = {}) => {
|
|
134
130
|
const fetchWorker = await fetchWorkerPromise;
|
|
135
|
-
const configureOptions = { storage, zipBaseUrl, checkUpdates
|
|
131
|
+
const configureOptions = { storage, zipBaseUrl, checkUpdates };
|
|
136
132
|
if (nativeReadFile) {
|
|
137
133
|
const wwHandler = makeSyncHandler(offlineWorker, { nativeReadFile, nativeWriteFile, nativeListZips });
|
|
138
134
|
offlineWorker.onmessage = combineHandlers(handler, wwHandler);
|
|
@@ -166,8 +162,7 @@ var useOfflineWorker_default = ({
|
|
|
166
162
|
nativeListZips,
|
|
167
163
|
checkUpdates,
|
|
168
164
|
storage,
|
|
169
|
-
zipBaseUrl
|
|
170
|
-
confEnv
|
|
165
|
+
zipBaseUrl
|
|
171
166
|
} = {}) => {
|
|
172
167
|
const [ready, setReady] = useState(false);
|
|
173
168
|
useEffect(() => {
|
|
@@ -179,15 +174,14 @@ var useOfflineWorker_default = ({
|
|
|
179
174
|
nativeListZips,
|
|
180
175
|
storage,
|
|
181
176
|
zipBaseUrl,
|
|
182
|
-
checkUpdates
|
|
183
|
-
confEnv
|
|
177
|
+
checkUpdates
|
|
184
178
|
});
|
|
185
179
|
await Promise.all([workersReadyPromise, setBooksLoadedPromise]);
|
|
186
180
|
fetch(window.location.href.replace(window.location.search, ""));
|
|
187
181
|
setReady(true);
|
|
188
182
|
};
|
|
189
183
|
fn();
|
|
190
|
-
}, [primaryOnly, nativeReadFile, nativeWriteFile, nativeListZips, storage, zipBaseUrl, checkUpdates
|
|
184
|
+
}, [primaryOnly, nativeReadFile, nativeWriteFile, nativeListZips, storage, zipBaseUrl, checkUpdates]);
|
|
191
185
|
const loadBook = useCallback(
|
|
192
186
|
async ({ id, forceRefresh }, cbk) => {
|
|
193
187
|
offlineWorkerSync.on("opfsProgress", cbk);
|
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-a4074e64",
|
|
4
4
|
"description": "offline",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"module": "./lib/index.js",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"vite": "7.0.0",
|
|
53
53
|
"vite-tsconfig-paths": "5.1.4",
|
|
54
54
|
"zod": "3.25.50",
|
|
55
|
-
"@lls/vitescripts": "24.22.0-
|
|
55
|
+
"@lls/vitescripts": "24.22.0-a4074e64"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"fflate": "0.8.2"
|