@toapi/worker 1.2.1 → 1.2.3
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/dist/console-fallback.d.ts +7 -0
- package/dist/console-fallback.d.ts.map +1 -0
- package/dist/console-fallback.js +5 -0
- package/dist/handle-toapi-request.d.ts.map +1 -1
- package/dist/handle-toapi-request.js +14 -19
- package/dist/revalidation-stream.d.ts +4 -1
- package/dist/revalidation-stream.d.ts.map +1 -1
- package/dist/revalidation-stream.js +15 -16
- package/dist/setup.d.ts +13 -7
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +11 -9
- package/package.json +2 -2
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Logger } from "@toapi/common";
|
|
2
|
+
export declare const consoleFallback: (logger?: Logger) => {
|
|
3
|
+
error: (error: unknown) => import("@toapi/common").MaybePromise<void>;
|
|
4
|
+
warn: (message: string) => import("@toapi/common").MaybePromise<void>;
|
|
5
|
+
info: (message: string) => import("@toapi/common").MaybePromise<void>;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=console-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console-fallback.d.ts","sourceRoot":"","sources":["../src/console-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM;;;;CAK1B,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const consoleFallback = (logger) => ({
|
|
2
|
+
error: logger?.error ?? ((error) => console.error("Toapi Worker:", error)),
|
|
3
|
+
warn: logger?.warn ?? ((message) => console.warn("Toapi Worker:", message)),
|
|
4
|
+
info: logger?.info ?? ((message) => console.info("Toapi Worker:", message)),
|
|
5
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-toapi-request.d.ts","sourceRoot":"","sources":["../src/handle-toapi-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"handle-toapi-request.d.ts","sourceRoot":"","sources":["../src/handle-toapi-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAO5C,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,qBA8B9B;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,2BAAqB,CAAC"}
|
|
@@ -2,9 +2,9 @@ import { isMutation } from "@toapi/common";
|
|
|
2
2
|
import { getCachedEntry, getMetadata } from "./cache";
|
|
3
3
|
import { mutateAndInvalidate } from "./mutate-and-invalidate";
|
|
4
4
|
import { serveFromNetwork } from "./serve-from-network";
|
|
5
|
+
import { consoleFallback } from "./console-fallback";
|
|
5
6
|
export async function handleToapiRequest(req, options) {
|
|
6
|
-
const
|
|
7
|
-
((err) => console.error("Toapi Worker fetch failed", err));
|
|
7
|
+
const logger = consoleFallback(options?.logger);
|
|
8
8
|
if (isMutation(req)) {
|
|
9
9
|
return mutateAndInvalidate(req);
|
|
10
10
|
}
|
|
@@ -15,26 +15,21 @@ export async function handleToapiRequest(req, options) {
|
|
|
15
15
|
return serveFromNetwork(req);
|
|
16
16
|
}
|
|
17
17
|
const meta = await getMetadata(req.url);
|
|
18
|
-
if (meta
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
if (meta && (!meta.expiresAt || meta.expiresAt > Date.now())) {
|
|
19
|
+
return cachedResponse;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
// cached response is expired
|
|
23
|
+
try {
|
|
24
|
+
// try to serve from network
|
|
25
|
+
return await serveFromNetwork(req);
|
|
22
26
|
}
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return await serveFromNetwork(req);
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
// probably network not available, serve old response
|
|
31
|
-
errorLog(error);
|
|
32
|
-
return cachedResponse;
|
|
33
|
-
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
// probably network not available, serve old response
|
|
29
|
+
logger.error(error);
|
|
30
|
+
return cachedResponse;
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
|
-
// no expiration header, serve cached response
|
|
37
|
-
return cachedResponse;
|
|
38
33
|
}
|
|
39
34
|
}
|
|
40
35
|
/**
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { type Logger } from "@toapi/common";
|
|
1
2
|
interface Options {
|
|
2
3
|
url: string;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
logger?: Logger;
|
|
3
6
|
}
|
|
4
|
-
export declare function listenForInvalidations({ url }: Options): Promise<void>;
|
|
7
|
+
export declare function listenForInvalidations({ url, timeout, logger: customLogger, }: Options): Promise<void>;
|
|
5
8
|
export {};
|
|
6
9
|
//# sourceMappingURL=revalidation-stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"revalidation-stream.d.ts","sourceRoot":"","sources":["../src/revalidation-stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"revalidation-stream.d.ts","sourceRoot":"","sources":["../src/revalidation-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AAMvB,UAAU,OAAO;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,GAAG,EACH,OAAc,EACd,MAAM,EAAE,YAAY,GACrB,EAAE,OAAO,iBAqFT"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { INVALIDATION_POST_EVENT, TAGS_CONTENT_TYPE, } from "@toapi/common";
|
|
2
2
|
import { deleteCache, expireAll, invalidateTags } from "./cache";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { consoleFallback } from "./console-fallback";
|
|
4
|
+
export async function listenForInvalidations({ url, timeout = 5000, logger: customLogger, }) {
|
|
5
|
+
const logger = consoleFallback(customLogger);
|
|
6
|
+
logger.info("Listening for invalidations...");
|
|
5
7
|
let res = null;
|
|
6
8
|
const MAX_ATTEMPTS = 1000;
|
|
7
9
|
for (let retry = 0; retry < MAX_ATTEMPTS; retry++) {
|
|
@@ -10,32 +12,32 @@ export async function listenForInvalidations({ url }) {
|
|
|
10
12
|
break;
|
|
11
13
|
}
|
|
12
14
|
catch (error) {
|
|
13
|
-
|
|
15
|
+
logger.warn(`Failed attempt #${retry + 1} to open invalidation stream\n${String(error)}`);
|
|
14
16
|
}
|
|
15
17
|
await new Promise((resolve) => setTimeout(resolve, 500 * Math.pow(2, retry)));
|
|
16
18
|
}
|
|
17
19
|
if (!res) {
|
|
18
|
-
|
|
20
|
+
logger.error(new Error(`Failed to open invalidation stream after ${MAX_ATTEMPTS} attempts, giving up.`));
|
|
19
21
|
return;
|
|
20
22
|
}
|
|
21
23
|
const contentType = res.headers.get("Content-Type");
|
|
22
24
|
if (!res.ok || contentType !== TAGS_CONTENT_TYPE || !res.body) {
|
|
23
|
-
|
|
25
|
+
logger.error(new Error(`Failed to open invalidation stream: Received ${res.status} ${res.statusText} with content type ${contentType}. Cleaning up and unregistering service worker.`));
|
|
24
26
|
await deleteCache();
|
|
25
27
|
await self.registration.unregister();
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
28
|
-
|
|
30
|
+
logger.info("Invalidation Stream Connection Established");
|
|
29
31
|
try {
|
|
30
32
|
const tags = await expireAll();
|
|
31
33
|
const clients = await self.clients.matchAll();
|
|
32
34
|
for (const client of clients) {
|
|
33
35
|
client.postMessage({ type: INVALIDATION_POST_EVENT, tags });
|
|
34
36
|
}
|
|
35
|
-
|
|
37
|
+
logger.info("Marked all cached entries as expired");
|
|
36
38
|
}
|
|
37
39
|
catch {
|
|
38
|
-
|
|
40
|
+
logger.warn("Failed to expire existing cache entries");
|
|
39
41
|
}
|
|
40
42
|
try {
|
|
41
43
|
let buffer = "";
|
|
@@ -50,7 +52,7 @@ export async function listenForInvalidations({ url }) {
|
|
|
50
52
|
if (!rawTags)
|
|
51
53
|
continue;
|
|
52
54
|
const tags = rawTags.split(" ");
|
|
53
|
-
|
|
55
|
+
logger.info(`Remote-Invalidating tags: ${tags}`);
|
|
54
56
|
await invalidateTags(tags);
|
|
55
57
|
for (const client of clients) {
|
|
56
58
|
client.postMessage({ type: INVALIDATION_POST_EVENT, tags });
|
|
@@ -59,12 +61,9 @@ export async function listenForInvalidations({ url }) {
|
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
catch (error) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}, 5000);
|
|
67
|
-
}
|
|
68
|
-
console.error("TApi: Failed to read invalidation stream", error);
|
|
64
|
+
logger.warn(`Invalidation stream failure, retrying in ${Math.round(timeout / 1000)}s`);
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
listenForInvalidations({ url, timeout: Math.round(timeout * 1.5) });
|
|
67
|
+
}, timeout);
|
|
69
68
|
}
|
|
70
69
|
}
|
package/dist/setup.d.ts
CHANGED
|
@@ -17,24 +17,30 @@ export interface SetupToapiWorkerOptions {
|
|
|
17
17
|
invalidationsUrl?: string;
|
|
18
18
|
/**
|
|
19
19
|
* Grace period in seconds past a cache entry's `expiresAt` before it is
|
|
20
|
-
* dropped
|
|
20
|
+
* dropped by the cleanup pass that runs on every worker startup. See
|
|
21
|
+
* {@link CleanupOptions}.
|
|
21
22
|
*
|
|
22
23
|
* @default 60 * 60 * 24 * 7 // 7 days
|
|
23
24
|
*/
|
|
24
25
|
maximumStaleAge?: number;
|
|
25
26
|
/**
|
|
26
|
-
* Optional logger.
|
|
27
|
-
*
|
|
28
|
-
* `
|
|
27
|
+
* Optional logger. `error` reports failed network refetches and a fatal
|
|
28
|
+
* failure of the invalidation stream, `warn` reports invalidation-stream
|
|
29
|
+
* retries, and `info` reports stream progress. Each method that is omitted
|
|
30
|
+
* falls back to the matching `console` method.
|
|
29
31
|
*/
|
|
30
32
|
logger?: Logger;
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Wire up the whole Toapi service worker in a single call. This registers the
|
|
34
|
-
* `
|
|
35
|
-
* equivalent to calling {@link cleanup}, {@link handleToapiRequest}, and
|
|
36
|
+
* `fetch` listener, runs {@link cleanup}, and opens the revalidation stream,
|
|
37
|
+
* and is equivalent to calling {@link cleanup}, {@link handleToapiRequest}, and
|
|
36
38
|
* {@link listenForInvalidations} by hand.
|
|
37
39
|
*
|
|
40
|
+
* Cleanup runs at worker startup rather than from the `activate` event, which
|
|
41
|
+
* only fires when a new worker takes over — an installed worker that is never
|
|
42
|
+
* updated would otherwise never clean up.
|
|
43
|
+
*
|
|
38
44
|
* ```ts
|
|
39
45
|
* // service-worker.ts
|
|
40
46
|
* import { setupToapiWorker } from "@toapi/worker";
|
|
@@ -49,5 +55,5 @@ export interface SetupToapiWorkerOptions {
|
|
|
49
55
|
* `vite-plugin-pwa`'s precaching) — requests it doesn't handle fall through to
|
|
50
56
|
* them.
|
|
51
57
|
*/
|
|
52
|
-
export declare function setupToapiWorker({ basePath, invalidationsUrl, maximumStaleAge, logger, }?: SetupToapiWorkerOptions): void;
|
|
58
|
+
export declare function setupToapiWorker({ basePath, invalidationsUrl, maximumStaleAge, logger: customLogger, }?: SetupToapiWorkerOptions): void;
|
|
53
59
|
//# sourceMappingURL=setup.d.ts.map
|
package/dist/setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAW5C,MAAM,WAAW,uBAAuB;IACtC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAiB,EACjB,gBAAsD,EACtD,eAAkC,EAClC,MAAM,EAAE,YAAY,GACrB,GAAE,uBAA4B,QAkB9B"}
|
package/dist/setup.js
CHANGED
|
@@ -2,13 +2,18 @@ import { INVALIDATIONS_ROUTE } from "@toapi/common";
|
|
|
2
2
|
import { cleanup } from "./cleanup";
|
|
3
3
|
import { handleToapiRequest } from "./handle-toapi-request";
|
|
4
4
|
import { listenForInvalidations } from "./revalidation-stream";
|
|
5
|
+
import { consoleFallback } from "./console-fallback";
|
|
5
6
|
const ONE_WEEK_SECONDS = 60 * 60 * 24 * 7;
|
|
6
7
|
/**
|
|
7
8
|
* Wire up the whole Toapi service worker in a single call. This registers the
|
|
8
|
-
* `
|
|
9
|
-
* equivalent to calling {@link cleanup}, {@link handleToapiRequest}, and
|
|
9
|
+
* `fetch` listener, runs {@link cleanup}, and opens the revalidation stream,
|
|
10
|
+
* and is equivalent to calling {@link cleanup}, {@link handleToapiRequest}, and
|
|
10
11
|
* {@link listenForInvalidations} by hand.
|
|
11
12
|
*
|
|
13
|
+
* Cleanup runs at worker startup rather than from the `activate` event, which
|
|
14
|
+
* only fires when a new worker takes over — an installed worker that is never
|
|
15
|
+
* updated would otherwise never clean up.
|
|
16
|
+
*
|
|
12
17
|
* ```ts
|
|
13
18
|
* // service-worker.ts
|
|
14
19
|
* import { setupToapiWorker } from "@toapi/worker";
|
|
@@ -23,13 +28,9 @@ const ONE_WEEK_SECONDS = 60 * 60 * 24 * 7;
|
|
|
23
28
|
* `vite-plugin-pwa`'s precaching) — requests it doesn't handle fall through to
|
|
24
29
|
* them.
|
|
25
30
|
*/
|
|
26
|
-
export function setupToapiWorker({ basePath = "/api", invalidationsUrl = `${basePath}${INVALIDATIONS_ROUTE}`, maximumStaleAge = ONE_WEEK_SECONDS, logger, } = {}) {
|
|
31
|
+
export function setupToapiWorker({ basePath = "/api", invalidationsUrl = `${basePath}${INVALIDATIONS_ROUTE}`, maximumStaleAge = ONE_WEEK_SECONDS, logger: customLogger, } = {}) {
|
|
32
|
+
const logger = consoleFallback(customLogger);
|
|
27
33
|
const controlPrefix = `${basePath}/__tapi`;
|
|
28
|
-
const errorLog = logger?.error ??
|
|
29
|
-
((err) => console.error("Toapi Worker: invalidation stream failed", err));
|
|
30
|
-
self.addEventListener("activate", (event) => {
|
|
31
|
-
event.waitUntil(cleanup({ maximumStaleAge }));
|
|
32
|
-
});
|
|
33
34
|
self.addEventListener("fetch", (event) => {
|
|
34
35
|
const url = new URL(event.request.url);
|
|
35
36
|
// only handle same-origin API requests, and never the control endpoints
|
|
@@ -40,5 +41,6 @@ export function setupToapiWorker({ basePath = "/api", invalidationsUrl = `${base
|
|
|
40
41
|
event.respondWith(handleToapiRequest(event.request, { logger }));
|
|
41
42
|
}
|
|
42
43
|
});
|
|
43
|
-
listenForInvalidations({ url: invalidationsUrl }).catch(
|
|
44
|
+
listenForInvalidations({ url: invalidationsUrl, logger }).catch(logger.error);
|
|
45
|
+
cleanup({ maximumStaleAge }).catch(logger.error);
|
|
44
46
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toapi/worker",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Michel Smola",
|
|
6
6
|
"email": "michel.smola@farbenmeer.de"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@toapi/common": "^1.2.
|
|
23
|
+
"@toapi/common": "^1.2.3"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^25.0.3",
|