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