@alxia/react-router 0.9.0 → 0.11.0
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/README.md +301 -13
- package/dist/chunks/index-59vhfj6n.js +10 -0
- package/dist/chunks/index-59vhfj6n.js.map +10 -0
- package/dist/chunks/index-5gqcpyyg.js +34 -0
- package/dist/chunks/index-5gqcpyyg.js.map +10 -0
- package/dist/chunks/{index-zqa30gr7.js → index-9xv2a8et.js} +7 -38
- package/dist/chunks/index-9xv2a8et.js.map +11 -0
- package/dist/chunks/index-mppz0806.js +119 -0
- package/dist/chunks/index-mppz0806.js.map +11 -0
- package/dist/chunks/index-rfr02vg4.js +40 -0
- package/dist/chunks/index-rfr02vg4.js.map +10 -0
- package/dist/chunks/index-zdbqzcjp.js +97 -0
- package/dist/chunks/index-zdbqzcjp.js.map +12 -0
- package/dist/errors/api-error.d.ts +86 -0
- package/dist/errors/api-error.d.ts.map +1 -0
- package/dist/errors/api-errors.d.ts +96 -0
- package/dist/errors/api-errors.d.ts.map +1 -0
- package/dist/errors/call-failure.d.ts +45 -0
- package/dist/errors/call-failure.d.ts.map +1 -0
- package/dist/errors/catalogue-check.d.ts +24 -0
- package/dist/errors/catalogue-check.d.ts.map +1 -0
- package/dist/errors/catalogue-read.d.ts +33 -0
- package/dist/errors/catalogue-read.d.ts.map +1 -0
- package/dist/errors/catalogue.d.ts +59 -0
- package/dist/errors/catalogue.d.ts.map +1 -0
- package/dist/errors/code-resolver.d.ts +44 -0
- package/dist/errors/code-resolver.d.ts.map +1 -0
- package/dist/errors/describe-refusal.d.ts +6 -0
- package/dist/errors/describe-refusal.d.ts.map +1 -0
- package/dist/errors/describe.d.ts +3 -0
- package/dist/errors/describe.d.ts.map +1 -0
- package/dist/errors/fetcher-result.d.ts +21 -0
- package/dist/errors/fetcher-result.d.ts.map +1 -0
- package/dist/errors/form-errors.d.ts +8 -0
- package/dist/errors/form-errors.d.ts.map +1 -0
- package/dist/errors/index.d.ts +15 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +602 -0
- package/dist/errors/index.js.map +29 -0
- package/dist/errors/json-actions.d.ts +38 -0
- package/dist/errors/json-actions.d.ts.map +1 -0
- package/dist/errors/log.d.ts +8 -0
- package/dist/errors/log.d.ts.map +1 -0
- package/dist/errors/message-values.d.ts +11 -0
- package/dist/errors/message-values.d.ts.map +1 -0
- package/dist/errors/messages.d.ts +63 -0
- package/dist/errors/messages.d.ts.map +1 -0
- package/dist/errors/page-path.d.ts +9 -0
- package/dist/errors/page-path.d.ts.map +1 -0
- package/dist/errors/provider.d.ts +20 -0
- package/dist/errors/provider.d.ts.map +1 -0
- package/dist/errors/request-id.d.ts +5 -0
- package/dist/errors/request-id.d.ts.map +1 -0
- package/dist/errors/route-error.d.ts +31 -0
- package/dist/errors/route-error.d.ts.map +1 -0
- package/dist/errors/standard-schema.d.ts +34 -0
- package/dist/errors/standard-schema.d.ts.map +1 -0
- package/dist/errors/submit-form.d.ts +16 -0
- package/dist/errors/submit-form.d.ts.map +1 -0
- package/dist/errors/use-json-fetcher.d.ts +18 -0
- package/dist/errors/use-json-fetcher.d.ts.map +1 -0
- package/dist/pwa/build/build-worker.d.ts +41 -0
- package/dist/pwa/build/build-worker.d.ts.map +1 -0
- package/dist/pwa/build/index.d.ts +9 -0
- package/dist/pwa/build/index.d.ts.map +1 -0
- package/dist/pwa/build/index.js +9 -0
- package/dist/pwa/build/index.js.map +9 -0
- package/dist/pwa/build/precache.d.ts +17 -0
- package/dist/pwa/build/precache.d.ts.map +1 -0
- package/dist/pwa/index.js +34 -134
- package/dist/pwa/index.js.map +4 -8
- package/dist/pwa/react/index.js +1 -1
- package/dist/pwa/worker/index.d.ts +9 -0
- package/dist/pwa/worker/index.d.ts.map +1 -0
- package/dist/pwa/worker/index.js +144 -0
- package/dist/pwa/worker/index.js.map +12 -0
- package/dist/pwa/worker/injected.d.ts +15 -0
- package/dist/pwa/worker/injected.d.ts.map +1 -0
- package/dist/pwa/worker/plan.d.ts +65 -0
- package/dist/pwa/worker/plan.d.ts.map +1 -0
- package/dist/pwa/worker/scope.d.ts +18 -0
- package/dist/pwa/worker/scope.d.ts.map +1 -0
- package/dist/pwa/worker/start-worker.d.ts +17 -0
- package/dist/pwa/worker/start-worker.d.ts.map +1 -0
- package/dist/vite/index.d.ts +10 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +49 -3
- package/dist/vite/index.js.map +6 -5
- package/dist/vite/runtime.d.ts +1 -1
- package/dist/vite/runtime.d.ts.map +1 -1
- package/dist/vite/worker.d.ts +16 -0
- package/dist/vite/worker.d.ts.map +1 -0
- package/docs/README.md +3 -2
- package/docs/guide/errors.md +838 -0
- package/docs/guide/how-it-works.md +7 -1
- package/docs/guide/pwa.md +372 -211
- package/docs/roadmap.md +38 -5
- package/docs/troubleshooting.md +689 -3
- package/package.json +56 -4
- package/dist/chunks/index-zqa30gr7.js.map +0 -12
package/dist/pwa/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
MANIFEST_PATH2,
|
|
3
|
+
MANIFEST_HEADERS2,
|
|
4
|
+
manifestHref2,
|
|
5
|
+
manifestLanguage2,
|
|
6
|
+
MINIMAL_2023_ICONS2,
|
|
7
|
+
webAppManifest2,
|
|
8
|
+
cachingFor2,
|
|
9
|
+
isStorable2
|
|
10
|
+
} from "../chunks/index-zdbqzcjp.js";
|
|
11
|
+
import {
|
|
6
12
|
watchForUpdates2,
|
|
7
13
|
createUpdater2,
|
|
8
14
|
afterLoad2,
|
|
@@ -10,77 +16,18 @@ import {
|
|
|
10
16
|
UPDATE_LATER_KEY2,
|
|
11
17
|
createUpdateNotice2,
|
|
12
18
|
sessionStore2
|
|
13
|
-
} from "../chunks/index-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
function manifestLanguage(url, supported, fallback) {
|
|
27
|
-
const requested = url.searchParams.get("lang");
|
|
28
|
-
return supported.find((language) => language === requested) ?? fallback;
|
|
29
|
-
}
|
|
30
|
-
var MINIMAL_2023_ICONS = Object.freeze([
|
|
31
|
-
{ src: "/pwa-64x64.png", sizes: "64x64", type: "image/png" },
|
|
32
|
-
{ src: "/pwa-192x192.png", sizes: "192x192", type: "image/png" },
|
|
33
|
-
{ src: "/pwa-512x512.png", sizes: "512x512", type: "image/png" },
|
|
34
|
-
{
|
|
35
|
-
src: "/maskable-icon-512x512.png",
|
|
36
|
-
sizes: "512x512",
|
|
37
|
-
type: "image/png",
|
|
38
|
-
purpose: "maskable"
|
|
39
|
-
}
|
|
40
|
-
].map((icon) => Object.freeze(icon)));
|
|
41
|
-
function webAppManifest(options) {
|
|
42
|
-
return {
|
|
43
|
-
id: options.id ?? "/",
|
|
44
|
-
name: options.name,
|
|
45
|
-
short_name: options.shortName ?? options.name,
|
|
46
|
-
description: options.description,
|
|
47
|
-
lang: options.lang,
|
|
48
|
-
dir: options.dir ?? "ltr",
|
|
49
|
-
start_url: options.startUrl ?? "/",
|
|
50
|
-
scope: options.scope ?? "/",
|
|
51
|
-
display: options.display ?? "standalone",
|
|
52
|
-
theme_color: options.themeColor,
|
|
53
|
-
background_color: options.backgroundColor,
|
|
54
|
-
...options.categories && { categories: [...options.categories] },
|
|
55
|
-
icons: (options.icons ?? MINIMAL_2023_ICONS).map((icon) => ({ ...icon }))
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// src/pwa/model/caching.ts
|
|
60
|
-
var STATIC_EXTENSION = /\.(?:js|mjs|css|woff2?|ttf|otf|svg|png|jpe?g|webp|avif|gif|ico)$/i;
|
|
61
|
-
var underAssets = (path) => path.startsWith("/assets/");
|
|
62
|
-
function cachingFor(request, origin, options = {}) {
|
|
63
|
-
if (request.method !== "GET")
|
|
64
|
-
return "none";
|
|
65
|
-
if (request.mode === "navigate")
|
|
66
|
-
return "navigation";
|
|
67
|
-
const url = new URL(request.url);
|
|
68
|
-
if (url.origin !== origin)
|
|
69
|
-
return "none";
|
|
70
|
-
if (request.headers.has("authorization"))
|
|
71
|
-
return "none";
|
|
72
|
-
if (url.search)
|
|
73
|
-
return "none";
|
|
74
|
-
const path = url.pathname;
|
|
75
|
-
if (path.endsWith(".data") || path === "/__manifest" || path === (options.serviceWorkerUrl ?? options.contract?.serviceWorkerUrl ?? SERVICE_WORKER_URL2) || path === (options.manifestPath ?? MANIFEST_PATH) || options.never?.(path)) {
|
|
76
|
-
return "none";
|
|
77
|
-
}
|
|
78
|
-
if (request.destination === "document" || request.destination === "iframe")
|
|
79
|
-
return "none";
|
|
80
|
-
if (!STATIC_EXTENSION.test(path))
|
|
81
|
-
return "none";
|
|
82
|
-
return (options.immutable ?? underAssets)(path) ? "immutable" : "static";
|
|
83
|
-
}
|
|
19
|
+
} from "../chunks/index-9xv2a8et.js";
|
|
20
|
+
import {
|
|
21
|
+
parseBrowserManifest2,
|
|
22
|
+
precacheList2,
|
|
23
|
+
assetPrecache2
|
|
24
|
+
} from "../chunks/index-5gqcpyyg.js";
|
|
25
|
+
import {
|
|
26
|
+
SERVICE_WORKER_URL2,
|
|
27
|
+
OFFLINE_URL2,
|
|
28
|
+
SKIP_WAITING2,
|
|
29
|
+
defineWorkerContract2
|
|
30
|
+
} from "../chunks/index-rfr02vg4.js";
|
|
84
31
|
// src/pwa/model/clear-app-caches.ts
|
|
85
32
|
async function clearAppCaches(contract) {
|
|
86
33
|
if (typeof caches === "undefined")
|
|
@@ -88,78 +35,31 @@ async function clearAppCaches(contract) {
|
|
|
88
35
|
const names = contract.clearableCaches(await caches.keys());
|
|
89
36
|
await Promise.all(names.map((name) => caches.delete(name)));
|
|
90
37
|
}
|
|
91
|
-
// src/pwa/model/precache-list.ts
|
|
92
|
-
var HASHED_ASSET = /^\/assets\/[^/]+-[\w-]{8}\.(?:js|css)$/;
|
|
93
|
-
function parseBrowserManifest(source) {
|
|
94
|
-
const start = source.indexOf("{");
|
|
95
|
-
const end = source.lastIndexOf("}");
|
|
96
|
-
if (start < 0 || end < start) {
|
|
97
|
-
throw new Error("parseBrowserManifest(): not React Router's browser manifest (build/client/assets/manifest-*.js).");
|
|
98
|
-
}
|
|
99
|
-
return JSON.parse(source.slice(start, end + 1));
|
|
100
|
-
}
|
|
101
|
-
function precacheList(manifest) {
|
|
102
|
-
const urls = new Set([manifest.url]);
|
|
103
|
-
for (const part of [manifest.entry, ...Object.values(manifest.routes)]) {
|
|
104
|
-
for (const url of [
|
|
105
|
-
...part.module ? [part.module] : [],
|
|
106
|
-
...part.imports ?? [],
|
|
107
|
-
...part.css ?? []
|
|
108
|
-
]) {
|
|
109
|
-
urls.add(url);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return [...urls].filter((url) => HASHED_ASSET.test(url)).map((url) => ({ url, revision: null }));
|
|
113
|
-
}
|
|
114
|
-
function assetPrecache(assetFiles, include, base = "/assets/") {
|
|
115
|
-
return assetFiles.filter((file) => {
|
|
116
|
-
include.lastIndex = 0;
|
|
117
|
-
return include.test(file);
|
|
118
|
-
}).sort().map((file) => ({ url: `${base}${file}`, revision: null }));
|
|
119
|
-
}
|
|
120
|
-
// src/pwa/model/storable.ts
|
|
121
|
-
var STATIC_CONTENT_TYPE = /^(?:(?:text|application)\/(?:javascript|x-javascript|ecmascript)|text\/css|font\/[\w.+-]+|application\/(?:font-woff2?|x-font-[\w.+-]+|vnd\.ms-fontobject)|image\/[\w.+-]+)$/i;
|
|
122
|
-
function isStorable(response) {
|
|
123
|
-
if (response.status !== 200)
|
|
124
|
-
return false;
|
|
125
|
-
if (response.type !== "basic" && response.type !== "default")
|
|
126
|
-
return false;
|
|
127
|
-
const contentType = (response.headers.get("content-type") ?? "").split(";")[0]?.trim();
|
|
128
|
-
if (!contentType || !STATIC_CONTENT_TYPE.test(contentType))
|
|
129
|
-
return false;
|
|
130
|
-
const cacheControl = response.headers.get("cache-control") ?? "";
|
|
131
|
-
if (!/\bpublic\b/i.test(cacheControl))
|
|
132
|
-
return false;
|
|
133
|
-
if (/\b(?:no-store|private)\b/i.test(cacheControl))
|
|
134
|
-
return false;
|
|
135
|
-
const vary = response.headers.get("vary") ?? "";
|
|
136
|
-
return !/(?:^|,)\s*(?:\*|cookie|authorization)\s*(?:,|$)/i.test(vary);
|
|
137
|
-
}
|
|
138
38
|
export {
|
|
139
|
-
MANIFEST_HEADERS,
|
|
140
|
-
MANIFEST_PATH,
|
|
141
|
-
MINIMAL_2023_ICONS,
|
|
39
|
+
MANIFEST_HEADERS2 as MANIFEST_HEADERS,
|
|
40
|
+
MANIFEST_PATH2 as MANIFEST_PATH,
|
|
41
|
+
MINIMAL_2023_ICONS2 as MINIMAL_2023_ICONS,
|
|
142
42
|
OFFLINE_URL2 as OFFLINE_URL,
|
|
143
43
|
SERVICE_WORKER_URL2 as SERVICE_WORKER_URL,
|
|
144
44
|
SKIP_WAITING2 as SKIP_WAITING,
|
|
145
45
|
UPDATE_LATER_KEY2 as UPDATE_LATER_KEY,
|
|
146
46
|
afterLoad2 as afterLoad,
|
|
147
|
-
assetPrecache,
|
|
148
|
-
cachingFor,
|
|
47
|
+
assetPrecache2 as assetPrecache,
|
|
48
|
+
cachingFor2 as cachingFor,
|
|
149
49
|
clearAppCaches,
|
|
150
50
|
createUpdateNotice2 as createUpdateNotice,
|
|
151
51
|
createUpdater2 as createUpdater,
|
|
152
52
|
defineWorkerContract2 as defineWorkerContract,
|
|
153
|
-
isStorable,
|
|
154
|
-
manifestHref,
|
|
155
|
-
manifestLanguage,
|
|
156
|
-
parseBrowserManifest,
|
|
157
|
-
precacheList,
|
|
53
|
+
isStorable2 as isStorable,
|
|
54
|
+
manifestHref2 as manifestHref,
|
|
55
|
+
manifestLanguage2 as manifestLanguage,
|
|
56
|
+
parseBrowserManifest2 as parseBrowserManifest,
|
|
57
|
+
precacheList2 as precacheList,
|
|
158
58
|
registerServiceWorker2 as registerServiceWorker,
|
|
159
59
|
sessionStore2 as sessionStore,
|
|
160
60
|
watchForUpdates2 as watchForUpdates,
|
|
161
|
-
webAppManifest
|
|
61
|
+
webAppManifest2 as webAppManifest
|
|
162
62
|
};
|
|
163
63
|
|
|
164
|
-
//# debugId=
|
|
64
|
+
//# debugId=7B8375F7BF1AB78E64756E2164756E21
|
|
165
65
|
//# sourceMappingURL=index.js.map
|
package/dist/pwa/index.js.map
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/pwa/model/
|
|
3
|
+
"sources": ["../src/pwa/model/clear-app-caches.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"
|
|
6
|
-
"import { MANIFEST_PATH } from './manifest';\nimport { SERVICE_WORKER_URL, type WorkerContract } from './worker-contract';\n\n/**\n * Which requests the service worker caches, and how. Pure, so that what is\n * never cached is tested, not hoped for.\n *\n * - `navigation`: a page load. Always from the network, never stored; the\n * offline page answers only when the network fails.\n * - `immutable`: a hashed bundle: cache-first.\n * - `static`: another same-origin static file (icon, sprite, logo):\n * stale-while-revalidate.\n * - `none`: left to the browser, untouched: every non-GET, any other\n * origin, React Router's `.data` and `/__manifest`, the worker and the\n * manifest themselves, the app's own `never` paths, and anything carrying\n * credentials.\n */\nexport type Caching = 'navigation' | 'immutable' | 'static' | 'none';\n\n/** What a `Request` exposes that the decision reads. */\nexport interface RequestFacts {\n\treadonly method: string;\n\treadonly url: string;\n\treadonly mode: string;\n\treadonly destination: string;\n\treadonly headers: Pick<Headers, 'has'>;\n}\n\nexport interface CachingOptions {\n\t/**\n\t * The worker's own path, never cached. Wins over `contract`;\n\t * `SERVICE_WORKER_URL` when neither is given.\n\t */\n\tserviceWorkerUrl?: string;\n\t/** The app's contract, whose `serviceWorkerUrl` is never cached. */\n\tcontract?: Pick<WorkerContract, 'serviceWorkerUrl'>;\n\t/** The web app manifest's path, never cached. `MANIFEST_PATH` by default. */\n\tmanifestPath?: string;\n\t/** Paths of the app answered fresh every time, such as `(p) => p.startsWith('/api/')`. */\n\tnever?: (path: string) => boolean;\n\t/**\n\t * Which static paths are immutable (cache-first) rather than static.\n\t * `(path) => path.startsWith('/assets/')` by default, as `serveClient`\n\t * serves them.\n\t */\n\timmutable?: (path: string) => boolean;\n}\n\nconst STATIC_EXTENSION =\n\t/\\.(?:js|mjs|css|woff2?|ttf|otf|svg|png|jpe?g|webp|avif|gif|ico)$/i;\n\nconst underAssets = (path: string) => path.startsWith('/assets/');\n\n/** How the worker answers `request`, for an app served at `origin`. */\nexport function cachingFor(\n\trequest: RequestFacts,\n\torigin: string,\n\toptions: CachingOptions = {},\n): Caching {\n\tif (request.method !== 'GET') return 'none';\n\tif (request.mode === 'navigate') return 'navigation';\n\tconst url = new URL(request.url);\n\tif (url.origin !== origin) return 'none';\n\tif (request.headers.has('authorization')) return 'none';\n\tif (url.search) return 'none';\n\tconst path = url.pathname;\n\tif (\n\t\tpath.endsWith('.data') || // React Router single fetch: loader data\n\t\tpath === '/__manifest' || // React Router route discovery\n\t\tpath ===\n\t\t\t(options.serviceWorkerUrl ??\n\t\t\t\toptions.contract?.serviceWorkerUrl ??\n\t\t\t\tSERVICE_WORKER_URL) ||\n\t\tpath === (options.manifestPath ?? MANIFEST_PATH) ||\n\t\toptions.never?.(path)\n\t) {\n\t\treturn 'none';\n\t}\n\tif (request.destination === 'document' || request.destination === 'iframe')\n\t\treturn 'none';\n\tif (!STATIC_EXTENSION.test(path)) return 'none';\n\treturn (options.immutable ?? underAssets)(path) ? 'immutable' : 'static';\n}\n",
|
|
7
|
-
"import type { WorkerContract } from './worker-contract';\n\n/**\n * Deletes every cache the service worker filled, but the offline page.\n * For sign-out: what the caches hold is static and public by design, and\n * clearing them leaves nothing behind anyway. Safe where Cache Storage does\n * not exist (server, old browsers).\n */\nexport async function clearAppCaches(\n\tcontract: Pick<WorkerContract, 'clearableCaches'>,\n): Promise<void> {\n\tif (typeof caches === 'undefined') return;\n\tconst names = contract.clearableCaches(await caches.keys());\n\tawait Promise.all(names.map((name) => caches.delete(name)));\n}\n",
|
|
8
|
-
"/**\n * What the service worker precaches, read from a build: every script and\n * stylesheet React Router's manifest names (the client entry and each\n * route's module, imports and CSS), and the manifest itself, so a client\n * navigation offline to a route never visited still finds its code. Only\n * hashed `/assets/` files: a new build names new files, so each precaches\n * under its own URL, revision `null`. Chunks only a component loads on\n * demand stay out: a cache-first route stores them the first time they load.\n *\n * Dependency-free: a build script runs it at build time.\n */\n\n/** A route or the entry, as `window.__reactRouterManifest` lists it. */\ntype ManifestModule = { module?: string; imports?: string[]; css?: string[] };\n\n/** The parts of React Router's browser manifest the list reads. */\nexport type BrowserManifest = {\n\turl: string;\n\tentry: ManifestModule;\n\troutes: Record<string, ManifestModule>;\n};\n\n/** A Workbox precache entry: a hashed URL needs no revision. */\nexport type PrecacheEntry = { url: string; revision: string | null };\n\n/** A Vite-hashed file under `/assets/`: `name-<8 hash chars>.js|css`. */\nconst HASHED_ASSET = /^\\/assets\\/[^/]+-[\\w-]{8}\\.(?:js|css)$/;\n\n/** Reads `window.__reactRouterManifest={…};` (build/client/assets/manifest-*.js). */\nexport function parseBrowserManifest(source: string): BrowserManifest {\n\tconst start = source.indexOf('{');\n\tconst end = source.lastIndexOf('}');\n\tif (start < 0 || end < start) {\n\t\tthrow new Error(\n\t\t\t\"parseBrowserManifest(): not React Router's browser manifest (build/client/assets/manifest-*.js).\",\n\t\t);\n\t}\n\treturn JSON.parse(source.slice(start, end + 1)) as BrowserManifest;\n}\n\n/** The hashed scripts and stylesheets every route needs, the manifest's own file first. */\nexport function precacheList(manifest: BrowserManifest): PrecacheEntry[] {\n\tconst urls = new Set<string>([manifest.url]);\n\tfor (const part of [manifest.entry, ...Object.values(manifest.routes)]) {\n\t\tfor (const url of [\n\t\t\t...(part.module ? [part.module] : []),\n\t\t\t...(part.imports ?? []),\n\t\t\t...(part.css ?? []),\n\t\t]) {\n\t\t\turls.add(url);\n\t\t}\n\t}\n\treturn [...urls]\n\t\t.filter((url) => HASHED_ASSET.test(url))\n\t\t.map((url) => ({ url, revision: null }));\n}\n\n/**\n * The precache entries of the files among `assetFiles` (the names in\n * build/client/assets) that `include` matches, sorted, under `base`: fonts\n * an app keeps offline, say. The regex is the app's.\n */\nexport function assetPrecache(\n\tassetFiles: readonly string[],\n\tinclude: RegExp,\n\tbase = '/assets/',\n): PrecacheEntry[] {\n\treturn assetFiles\n\t\t.filter((file) => {\n\t\t\tinclude.lastIndex = 0; // a /g or /y regex remembers its last match\n\t\t\treturn include.test(file);\n\t\t})\n\t\t.sort()\n\t\t.map((file) => ({ url: `${base}${file}`, revision: null }));\n}\n",
|
|
9
|
-
"/**\n * The content types of the static extensions: scripts, styles, fonts and\n * images (SVG and icons included). A page, JSON or anything else served\n * under a static-looking path is refused all the same.\n */\nconst STATIC_CONTENT_TYPE =\n\t/^(?:(?:text|application)\\/(?:javascript|x-javascript|ecmascript)|text\\/css|font\\/[\\w.+-]+|application\\/(?:font-woff2?|x-font-[\\w.+-]+|vnd\\.ms-fontobject)|image\\/[\\w.+-]+)$/i;\n\n/**\n * Whether a response may be stored: a full, same-origin 200 of a static\n * content type, that the server marked `public` (alxia does on every\n * static file it serves; a route answering an image, a user's photo, does\n * not), not private nor no-store, and that does not vary on who asks.\n */\nexport function isStorable(response: Response): boolean {\n\tif (response.status !== 200) return false;\n\tif (response.type !== 'basic' && response.type !== 'default') return false;\n\tconst contentType = (response.headers.get('content-type') ?? '')\n\t\t.split(';')[0]\n\t\t?.trim();\n\tif (!contentType || !STATIC_CONTENT_TYPE.test(contentType)) return false;\n\tconst cacheControl = response.headers.get('cache-control') ?? '';\n\tif (!/\\bpublic\\b/i.test(cacheControl)) return false;\n\tif (/\\b(?:no-store|private)\\b/i.test(cacheControl)) return false;\n\tconst vary = response.headers.get('vary') ?? '';\n\treturn !/(?:^|,)\\s*(?:\\*|cookie|authorization)\\s*(?:,|$)/i.test(vary);\n}\n"
|
|
5
|
+
"import type { WorkerContract } from './worker-contract';\n\n/**\n * Deletes every cache the service worker filled, but the offline page.\n * For sign-out: what the caches hold is static and public by design, and\n * clearing them leaves nothing behind anyway. Safe where Cache Storage does\n * not exist (server, old browsers).\n */\nexport async function clearAppCaches(\n\tcontract: Pick<WorkerContract, 'clearableCaches'>,\n): Promise<void> {\n\tif (typeof caches === 'undefined') return;\n\tconst names = contract.clearableCaches(await caches.keys());\n\tawait Promise.all(names.map((name) => caches.delete(name)));\n}\n"
|
|
10
6
|
],
|
|
11
|
-
"mappings": "
|
|
12
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,eAAsB,cAAc,CACnC,UACgB;AAAA,EAChB,IAAI,OAAO,WAAW;AAAA,IAAa;AAAA,EACnC,MAAM,QAAQ,SAAS,gBAAgB,MAAM,OAAO,KAAK,CAAC;AAAA,EAC1D,MAAM,QAAQ,IAAI,MAAM,IAAI,CAAC,SAAS,OAAO,OAAO,IAAI,CAAC,CAAC;AAAA;",
|
|
8
|
+
"debugId": "7B8375F7BF1AB78E64756E2164756E21",
|
|
13
9
|
"names": []
|
|
14
10
|
}
|
package/dist/pwa/react/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@alxia/react-router/pwa/worker`: the service worker on Workbox. Import it
|
|
3
|
+
* from a service worker bundle alone, never from a page. The six `workbox-*`
|
|
4
|
+
* packages (`core`, `expiration`, `navigation-preload`, `precaching`,
|
|
5
|
+
* `routing`, `strategies`, optional peers) are its only package imports.
|
|
6
|
+
*/
|
|
7
|
+
export type { ExpirationOptions, WorkerOptions } from './plan';
|
|
8
|
+
export { startWorker } from './start-worker';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pwa/worker/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cachingFor2,
|
|
3
|
+
isStorable2
|
|
4
|
+
} from "../../chunks/index-zdbqzcjp.js";
|
|
5
|
+
import {
|
|
6
|
+
injectedPrecache
|
|
7
|
+
} from "../../chunks/index-59vhfj6n.js";
|
|
8
|
+
import {
|
|
9
|
+
SKIP_WAITING2
|
|
10
|
+
} from "../../chunks/index-rfr02vg4.js";
|
|
11
|
+
|
|
12
|
+
// src/pwa/worker/start-worker.ts
|
|
13
|
+
import {
|
|
14
|
+
clientsClaim,
|
|
15
|
+
setCacheNameDetails
|
|
16
|
+
} from "workbox-core";
|
|
17
|
+
import { ExpirationPlugin } from "workbox-expiration";
|
|
18
|
+
import * as navigationPreload from "workbox-navigation-preload";
|
|
19
|
+
import { matchPrecache, precacheAndRoute } from "workbox-precaching";
|
|
20
|
+
import { registerRoute } from "workbox-routing";
|
|
21
|
+
import {
|
|
22
|
+
CacheFirst,
|
|
23
|
+
NetworkOnly,
|
|
24
|
+
StaleWhileRevalidate
|
|
25
|
+
} from "workbox-strategies";
|
|
26
|
+
|
|
27
|
+
// src/pwa/worker/plan.ts
|
|
28
|
+
var THIRTY_DAYS = 30 * 24 * 60 * 60;
|
|
29
|
+
function workerPlan(options, injected) {
|
|
30
|
+
const { contract } = options;
|
|
31
|
+
if (!contract) {
|
|
32
|
+
throw new TypeError("startWorker(): contract is required: pass defineWorkerContract({ cachePrefix }) from @alxia/react-router/pwa.");
|
|
33
|
+
}
|
|
34
|
+
const precache = options.precache ?? injected;
|
|
35
|
+
if (!precache) {
|
|
36
|
+
throw new Error("startWorker(): no precache list. Build this worker with alxia() from @alxia/react-router/vite (app/service-worker.ts) or buildWorker() from @alxia/react-router/pwa/build, which write it in.");
|
|
37
|
+
}
|
|
38
|
+
const offlineUrl = contract.offlineUrl;
|
|
39
|
+
const hasOffline = precache.some((entry) => entry.url.split("?")[0] === offlineUrl);
|
|
40
|
+
return {
|
|
41
|
+
cacheNameDetails: {
|
|
42
|
+
prefix: contract.cachePrefix,
|
|
43
|
+
precache: "precache",
|
|
44
|
+
suffix: contract.cacheVersion
|
|
45
|
+
},
|
|
46
|
+
precache,
|
|
47
|
+
routes: [
|
|
48
|
+
{
|
|
49
|
+
caching: "navigation",
|
|
50
|
+
strategy: "network-only",
|
|
51
|
+
fallback: hasOffline ? offlineUrl : undefined
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
caching: "immutable",
|
|
55
|
+
strategy: "cache-first",
|
|
56
|
+
cacheName: contract.cacheNames.assets,
|
|
57
|
+
expiration: { maxEntries: 200, ...options.assets }
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
caching: "static",
|
|
61
|
+
strategy: "stale-while-revalidate",
|
|
62
|
+
cacheName: contract.cacheNames.static,
|
|
63
|
+
expiration: {
|
|
64
|
+
maxEntries: 100,
|
|
65
|
+
maxAgeSeconds: THIRTY_DAYS,
|
|
66
|
+
...options.static
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
navigationPreload: options.navigationPreload ?? true
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function matcher(caching, origin, options) {
|
|
74
|
+
return ({ request }) => cachingFor2(request, origin(), {
|
|
75
|
+
...options.caching,
|
|
76
|
+
contract: options.contract
|
|
77
|
+
}) === caching;
|
|
78
|
+
}
|
|
79
|
+
var storableOnly = {
|
|
80
|
+
async cacheWillUpdate({
|
|
81
|
+
response
|
|
82
|
+
}) {
|
|
83
|
+
return isStorable2(response) ? response : null;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
function isSkipWaiting(data) {
|
|
87
|
+
return data?.type === SKIP_WAITING2;
|
|
88
|
+
}
|
|
89
|
+
async function deleteObsoleteCaches(contract, storage) {
|
|
90
|
+
const names = await storage.keys();
|
|
91
|
+
await Promise.all(contract.obsoleteCaches(names).map((name) => storage.delete(name)));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/pwa/worker/scope.ts
|
|
95
|
+
var workerScope = () => globalThis;
|
|
96
|
+
|
|
97
|
+
// src/pwa/worker/start-worker.ts
|
|
98
|
+
function startWorker(options) {
|
|
99
|
+
const plan = workerPlan(options, injectedPrecache());
|
|
100
|
+
const scope = workerScope();
|
|
101
|
+
const { contract } = options;
|
|
102
|
+
setCacheNameDetails(plan.cacheNameDetails);
|
|
103
|
+
precacheAndRoute([...plan.precache], {
|
|
104
|
+
cleanURLs: false,
|
|
105
|
+
directoryIndex: ""
|
|
106
|
+
});
|
|
107
|
+
if (plan.navigationPreload)
|
|
108
|
+
navigationPreload.enable();
|
|
109
|
+
for (const route of plan.routes) {
|
|
110
|
+
const match = matcher(route.caching, () => scope.location.origin, options);
|
|
111
|
+
if (route.strategy === "network-only") {
|
|
112
|
+
const { fallback } = route;
|
|
113
|
+
registerRoute(match, new NetworkOnly({
|
|
114
|
+
plugins: fallback ? [{ handlerDidError: async () => matchPrecache(fallback) }] : []
|
|
115
|
+
}));
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const Strategy = route.strategy === "cache-first" ? CacheFirst : StaleWhileRevalidate;
|
|
119
|
+
registerRoute(match, new Strategy({
|
|
120
|
+
cacheName: route.cacheName,
|
|
121
|
+
plugins: [
|
|
122
|
+
storableOnly,
|
|
123
|
+
new ExpirationPlugin({
|
|
124
|
+
...route.expiration,
|
|
125
|
+
purgeOnQuotaError: true
|
|
126
|
+
})
|
|
127
|
+
]
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
clientsClaim();
|
|
131
|
+
scope.addEventListener("activate", (event) => {
|
|
132
|
+
event.waitUntil(deleteObsoleteCaches(contract, caches));
|
|
133
|
+
});
|
|
134
|
+
scope.addEventListener("message", (event) => {
|
|
135
|
+
if (isSkipWaiting(event.data))
|
|
136
|
+
scope.skipWaiting();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
startWorker
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
//# debugId=52EB15D1D020B66D64756E2164756E21
|
|
144
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/pwa/worker/start-worker.ts", "../src/pwa/worker/plan.ts", "../src/pwa/worker/scope.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import {\n\tclientsClaim,\n\tsetCacheNameDetails,\n\ttype WorkboxPlugin,\n} from 'workbox-core';\nimport { ExpirationPlugin } from 'workbox-expiration';\nimport * as navigationPreload from 'workbox-navigation-preload';\nimport { matchPrecache, precacheAndRoute } from 'workbox-precaching';\nimport { registerRoute } from 'workbox-routing';\nimport {\n\tCacheFirst,\n\tNetworkOnly,\n\tStaleWhileRevalidate,\n} from 'workbox-strategies';\nimport { injectedPrecache } from './injected';\nimport {\n\tdeleteObsoleteCaches,\n\tisSkipWaiting,\n\tmatcher,\n\tstorableOnly,\n\ttype WorkerOptions,\n\tworkerPlan,\n} from './plan';\nimport { workerScope } from './scope';\n\n/**\n * Starts the service worker on Workbox. Call it once, from the worker's entry.\n *\n * - The precache route is registered first: a precached URL is answered from\n * the precache before any route below. Exact URLs only (`cleanURLs: false`,\n * no directory index), so a page load still goes to the network.\n * - A navigation is never stored: with navigation preload the browser starts\n * the request alongside the worker, and `NetworkOnly` answers with it. The\n * offline page answers only when the network fails, never for an HTTP error.\n * - Hashed bundles are cache-first, other static files stale-while-revalidate;\n * both store only what `isStorable` accepts.\n * - A first install controls the open pages at once; an update waits until\n * the page posts `SKIP_WAITING`.\n */\nexport function startWorker(options: WorkerOptions): void {\n\tconst plan = workerPlan(options, injectedPrecache());\n\tconst scope = workerScope();\n\tconst { contract } = options;\n\tsetCacheNameDetails(plan.cacheNameDetails);\n\tprecacheAndRoute([...plan.precache], {\n\t\tcleanURLs: false,\n\t\tdirectoryIndex: '',\n\t});\n\tif (plan.navigationPreload) navigationPreload.enable();\n\n\tfor (const route of plan.routes) {\n\t\tconst match = matcher(route.caching, () => scope.location.origin, options);\n\t\tif (route.strategy === 'network-only') {\n\t\t\tconst { fallback } = route;\n\t\t\tregisterRoute(\n\t\t\t\tmatch,\n\t\t\t\tnew NetworkOnly({\n\t\t\t\t\tplugins: fallback\n\t\t\t\t\t\t? [{ handlerDidError: async () => matchPrecache(fallback) }]\n\t\t\t\t\t\t: [],\n\t\t\t\t}),\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst Strategy =\n\t\t\troute.strategy === 'cache-first' ? CacheFirst : StaleWhileRevalidate;\n\t\tregisterRoute(\n\t\t\tmatch,\n\t\t\tnew Strategy({\n\t\t\t\tcacheName: route.cacheName,\n\t\t\t\tplugins: [\n\t\t\t\t\tstorableOnly,\n\t\t\t\t\tnew ExpirationPlugin({\n\t\t\t\t\t\t...route.expiration,\n\t\t\t\t\t\tpurgeOnQuotaError: true,\n\t\t\t\t\t}) as WorkboxPlugin, // ExpirationPlugin's optional hooks are not exact-optional under exactOptionalPropertyTypes; this states it is a plugin.\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\tclientsClaim();\n\tscope.addEventListener('activate', (event) => {\n\t\tevent.waitUntil(deleteObsoleteCaches(contract, caches));\n\t});\n\tscope.addEventListener('message', (event) => {\n\t\tif (isSkipWaiting(event.data)) void scope.skipWaiting();\n\t});\n}\n",
|
|
6
|
+
"import {\n\ttype Caching,\n\ttype CachingOptions,\n\tcachingFor,\n\ttype RequestFacts,\n} from '../model/caching';\nimport type { PrecacheEntry } from '../model/precache-list';\nimport { isStorable } from '../model/storable';\nimport { SKIP_WAITING, type WorkerContract } from '../model/worker-contract';\n\n/** How many files, and for how long, a runtime cache keeps. */\nexport interface ExpirationOptions {\n\tmaxEntries?: number;\n\tmaxAgeSeconds?: number;\n}\n\nexport interface WorkerOptions {\n\t/** The app's contract: cache names, offline page, the worker's URL. */\n\tcontract: WorkerContract;\n\t/** What `cachingFor` reads besides the contract: `never`, `immutable`, `manifestPath`, `serviceWorkerUrl`. */\n\tcaching?: Omit<CachingOptions, 'contract'>;\n\t/** The hashed bundles' cache. `{ maxEntries: 200 }` by default. */\n\tassets?: ExpirationOptions;\n\t/** The other static files' cache. `{ maxEntries: 100, maxAgeSeconds: 30 days }` by default. */\n\tstatic?: ExpirationOptions;\n\t/** Start the worker alongside a page load. `true` by default. */\n\tnavigationPreload?: boolean;\n\t/** The precache list. By default the one the build wrote into the worker. */\n\tprecache?: readonly PrecacheEntry[];\n}\n\nexport type WorkerRoute =\n\t| {\n\t\t\tcaching: 'navigation';\n\t\t\tstrategy: 'network-only';\n\t\t\tfallback: string | undefined;\n\t }\n\t| {\n\t\t\tcaching: 'immutable';\n\t\t\tstrategy: 'cache-first';\n\t\t\tcacheName: string;\n\t\t\texpiration: ExpirationOptions & { maxEntries: number };\n\t }\n\t| {\n\t\t\tcaching: 'static';\n\t\t\tstrategy: 'stale-while-revalidate';\n\t\t\tcacheName: string;\n\t\t\texpiration: ExpirationOptions;\n\t };\n\nexport interface WorkerPlan {\n\tcacheNameDetails: { prefix: string; precache: 'precache'; suffix: string };\n\tprecache: readonly PrecacheEntry[];\n\troutes: readonly WorkerRoute[];\n\tnavigationPreload: boolean;\n}\n\nconst THIRTY_DAYS = 30 * 24 * 60 * 60;\n\n/** What the worker does, decided without Workbox so that it is tested as data. */\nexport function workerPlan(\n\toptions: WorkerOptions,\n\tinjected: readonly PrecacheEntry[] | undefined,\n): WorkerPlan {\n\tconst { contract } = options;\n\tif (!contract) {\n\t\tthrow new TypeError(\n\t\t\t'startWorker(): contract is required: pass defineWorkerContract({ cachePrefix }) from @alxia/react-router/pwa.',\n\t\t);\n\t}\n\tconst precache = options.precache ?? injected;\n\tif (!precache) {\n\t\tthrow new Error(\n\t\t\t'startWorker(): no precache list. Build this worker with alxia() from @alxia/react-router/vite (app/service-worker.ts) or buildWorker() from @alxia/react-router/pwa/build, which write it in.',\n\t\t);\n\t}\n\tconst offlineUrl = contract.offlineUrl;\n\tconst hasOffline = precache.some(\n\t\t(entry) => entry.url.split('?')[0] === offlineUrl,\n\t);\n\treturn {\n\t\tcacheNameDetails: {\n\t\t\tprefix: contract.cachePrefix,\n\t\t\tprecache: 'precache',\n\t\t\tsuffix: contract.cacheVersion,\n\t\t},\n\t\tprecache,\n\t\troutes: [\n\t\t\t{\n\t\t\t\tcaching: 'navigation',\n\t\t\t\tstrategy: 'network-only',\n\t\t\t\tfallback: hasOffline ? offlineUrl : undefined,\n\t\t\t},\n\t\t\t{\n\t\t\t\tcaching: 'immutable',\n\t\t\t\tstrategy: 'cache-first',\n\t\t\t\tcacheName: contract.cacheNames.assets,\n\t\t\t\texpiration: { maxEntries: 200, ...options.assets },\n\t\t\t},\n\t\t\t{\n\t\t\t\tcaching: 'static',\n\t\t\t\tstrategy: 'stale-while-revalidate',\n\t\t\t\tcacheName: contract.cacheNames.static,\n\t\t\t\texpiration: {\n\t\t\t\t\tmaxEntries: 100,\n\t\t\t\t\tmaxAgeSeconds: THIRTY_DAYS,\n\t\t\t\t\t...options.static,\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\tnavigationPreload: options.navigationPreload ?? true,\n\t};\n}\n\n/** Whether a request is answered the way `caching` says. */\nexport function matcher(\n\tcaching: Caching,\n\torigin: () => string,\n\toptions: WorkerOptions,\n): (o: { request: RequestFacts }) => boolean {\n\treturn ({ request }) =>\n\t\tcachingFor(request, origin(), {\n\t\t\t...options.caching,\n\t\t\tcontract: options.contract,\n\t\t}) === caching;\n}\n\n/** Stores only what `isStorable` accepts: a public, same-origin 200. */\nexport const storableOnly = {\n\tasync cacheWillUpdate({\n\t\tresponse,\n\t}: {\n\t\tresponse: Response;\n\t}): Promise<Response | null> {\n\t\treturn isStorable(response) ? response : null;\n\t},\n};\n\nexport function isSkipWaiting(data: unknown): boolean {\n\treturn (data as { type?: unknown } | null | undefined)?.type === SKIP_WAITING;\n}\n\n/** Deletes the caches an earlier version of the contract owned. */\nexport async function deleteObsoleteCaches(\n\tcontract: Pick<WorkerContract, 'obsoleteCaches'>,\n\tstorage: Pick<CacheStorage, 'keys' | 'delete'>,\n): Promise<void> {\n\tconst names = await storage.keys();\n\tawait Promise.all(\n\t\tcontract.obsoleteCaches(names).map((name) => storage.delete(name)),\n\t);\n}\n",
|
|
7
|
+
"/**\n * The part of the service worker's global scope that `startWorker` reads,\n * declared here so no WebWorker-only type reaches the package's declarations\n * (the DOM lib has no `ServiceWorkerGlobalScope`).\n */\nexport interface WorkerScope {\n\treadonly location: { readonly origin: string };\n\tskipWaiting(): Promise<void>;\n\taddEventListener(\n\t\ttype: 'activate' | 'message',\n\t\tlistener: (event: {\n\t\t\tdata?: unknown;\n\t\t\twaitUntil(promise: Promise<unknown>): void;\n\t\t}) => void,\n\t): void;\n}\n\n/** The worker's scope. Called only inside `startWorker`, never at module load. */\nexport const workerScope = (): WorkerScope =>\n\tglobalThis as unknown as WorkerScope;\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "52EB15D1D020B66D64756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** The define key the build sets: the worker's precache list, as JSON. */
|
|
2
|
+
export declare const PRECACHE_DEFINE = "globalThis.__ALXIA_PRECACHE__";
|
|
3
|
+
/** One file the worker precaches: its URL and a revision (`null` when hashed in the name). */
|
|
4
|
+
type Entry = {
|
|
5
|
+
readonly url: string;
|
|
6
|
+
readonly revision: string | null;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The precache list the build wrote into this worker, or `undefined` when the
|
|
10
|
+
* worker did not go through the build. Read through `globalThis`, so a missing
|
|
11
|
+
* define is `undefined`, never a `ReferenceError`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function injectedPrecache(): readonly Entry[] | undefined;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=injected.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injected.d.ts","sourceRoot":"","sources":["../../../src/pwa/worker/injected.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,eAAO,MAAM,eAAe,kCAAkC,CAAC;AAE/D,8FAA8F;AAC9F,KAAK,KAAK,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAExE;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,SAAS,KAAK,EAAE,GAAG,SAAS,CAG/D"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type Caching, type CachingOptions, type RequestFacts } from '../model/caching';
|
|
2
|
+
import type { PrecacheEntry } from '../model/precache-list';
|
|
3
|
+
import { type WorkerContract } from '../model/worker-contract';
|
|
4
|
+
/** How many files, and for how long, a runtime cache keeps. */
|
|
5
|
+
export interface ExpirationOptions {
|
|
6
|
+
maxEntries?: number;
|
|
7
|
+
maxAgeSeconds?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface WorkerOptions {
|
|
10
|
+
/** The app's contract: cache names, offline page, the worker's URL. */
|
|
11
|
+
contract: WorkerContract;
|
|
12
|
+
/** What `cachingFor` reads besides the contract: `never`, `immutable`, `manifestPath`, `serviceWorkerUrl`. */
|
|
13
|
+
caching?: Omit<CachingOptions, 'contract'>;
|
|
14
|
+
/** The hashed bundles' cache. `{ maxEntries: 200 }` by default. */
|
|
15
|
+
assets?: ExpirationOptions;
|
|
16
|
+
/** The other static files' cache. `{ maxEntries: 100, maxAgeSeconds: 30 days }` by default. */
|
|
17
|
+
static?: ExpirationOptions;
|
|
18
|
+
/** Start the worker alongside a page load. `true` by default. */
|
|
19
|
+
navigationPreload?: boolean;
|
|
20
|
+
/** The precache list. By default the one the build wrote into the worker. */
|
|
21
|
+
precache?: readonly PrecacheEntry[];
|
|
22
|
+
}
|
|
23
|
+
export type WorkerRoute = {
|
|
24
|
+
caching: 'navigation';
|
|
25
|
+
strategy: 'network-only';
|
|
26
|
+
fallback: string | undefined;
|
|
27
|
+
} | {
|
|
28
|
+
caching: 'immutable';
|
|
29
|
+
strategy: 'cache-first';
|
|
30
|
+
cacheName: string;
|
|
31
|
+
expiration: ExpirationOptions & {
|
|
32
|
+
maxEntries: number;
|
|
33
|
+
};
|
|
34
|
+
} | {
|
|
35
|
+
caching: 'static';
|
|
36
|
+
strategy: 'stale-while-revalidate';
|
|
37
|
+
cacheName: string;
|
|
38
|
+
expiration: ExpirationOptions;
|
|
39
|
+
};
|
|
40
|
+
export interface WorkerPlan {
|
|
41
|
+
cacheNameDetails: {
|
|
42
|
+
prefix: string;
|
|
43
|
+
precache: 'precache';
|
|
44
|
+
suffix: string;
|
|
45
|
+
};
|
|
46
|
+
precache: readonly PrecacheEntry[];
|
|
47
|
+
routes: readonly WorkerRoute[];
|
|
48
|
+
navigationPreload: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** What the worker does, decided without Workbox so that it is tested as data. */
|
|
51
|
+
export declare function workerPlan(options: WorkerOptions, injected: readonly PrecacheEntry[] | undefined): WorkerPlan;
|
|
52
|
+
/** Whether a request is answered the way `caching` says. */
|
|
53
|
+
export declare function matcher(caching: Caching, origin: () => string, options: WorkerOptions): (o: {
|
|
54
|
+
request: RequestFacts;
|
|
55
|
+
}) => boolean;
|
|
56
|
+
/** Stores only what `isStorable` accepts: a public, same-origin 200. */
|
|
57
|
+
export declare const storableOnly: {
|
|
58
|
+
cacheWillUpdate({ response, }: {
|
|
59
|
+
response: Response;
|
|
60
|
+
}): Promise<Response | null>;
|
|
61
|
+
};
|
|
62
|
+
export declare function isSkipWaiting(data: unknown): boolean;
|
|
63
|
+
/** Deletes the caches an earlier version of the contract owned. */
|
|
64
|
+
export declare function deleteObsoleteCaches(contract: Pick<WorkerContract, 'obsoleteCaches'>, storage: Pick<CacheStorage, 'keys' | 'delete'>): Promise<void>;
|
|
65
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/pwa/worker/plan.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,cAAc,EAEnB,KAAK,YAAY,EACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE7E,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC7B,uEAAuE;IACvE,QAAQ,EAAE,cAAc,CAAC;IACzB,8GAA8G;IAC9G,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAC3C,mEAAmE;IACnE,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,+FAA+F;IAC/F,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,WAAW,GACpB;IACA,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,GACD;IACA,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,aAAa,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,iBAAiB,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD,GACD;IACA,OAAO,EAAE,QAAQ,CAAC;IAClB,QAAQ,EAAE,wBAAwB,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,iBAAiB,CAAC;CAC7B,CAAC;AAEL,MAAM,WAAW,UAAU;IAC1B,gBAAgB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;CAC3B;AAID,kFAAkF;AAClF,wBAAgB,UAAU,CACzB,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,GAC5C,UAAU,CAiDZ;AAED,4DAA4D;AAC5D,wBAAgB,OAAO,CACtB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,MAAM,EACpB,OAAO,EAAE,aAAa,GACpB,CAAC,CAAC,EAAE;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,KAAK,OAAO,CAM3C;AAED,wEAAwE;AACxE,eAAO,MAAM,YAAY;mCAGrB;QACF,QAAQ,EAAE,QAAQ,CAAC;KACnB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;CAG5B,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAEpD;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CACzC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,EAChD,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAKf"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The part of the service worker's global scope that `startWorker` reads,
|
|
3
|
+
* declared here so no WebWorker-only type reaches the package's declarations
|
|
4
|
+
* (the DOM lib has no `ServiceWorkerGlobalScope`).
|
|
5
|
+
*/
|
|
6
|
+
export interface WorkerScope {
|
|
7
|
+
readonly location: {
|
|
8
|
+
readonly origin: string;
|
|
9
|
+
};
|
|
10
|
+
skipWaiting(): Promise<void>;
|
|
11
|
+
addEventListener(type: 'activate' | 'message', listener: (event: {
|
|
12
|
+
data?: unknown;
|
|
13
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
14
|
+
}) => void): void;
|
|
15
|
+
}
|
|
16
|
+
/** The worker's scope. Called only inside `startWorker`, never at module load. */
|
|
17
|
+
export declare const workerScope: () => WorkerScope;
|
|
18
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../src/pwa/worker/scope.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,gBAAgB,CACf,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,QAAQ,EAAE,CAAC,KAAK,EAAE;QACjB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;KAC3C,KAAK,IAAI,GACR,IAAI,CAAC;CACR;AAED,kFAAkF;AAClF,eAAO,MAAM,WAAW,QAAO,WACM,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type WorkerOptions } from './plan';
|
|
2
|
+
/**
|
|
3
|
+
* Starts the service worker on Workbox. Call it once, from the worker's entry.
|
|
4
|
+
*
|
|
5
|
+
* - The precache route is registered first: a precached URL is answered from
|
|
6
|
+
* the precache before any route below. Exact URLs only (`cleanURLs: false`,
|
|
7
|
+
* no directory index), so a page load still goes to the network.
|
|
8
|
+
* - A navigation is never stored: with navigation preload the browser starts
|
|
9
|
+
* the request alongside the worker, and `NetworkOnly` answers with it. The
|
|
10
|
+
* offline page answers only when the network fails, never for an HTTP error.
|
|
11
|
+
* - Hashed bundles are cache-first, other static files stale-while-revalidate;
|
|
12
|
+
* both store only what `isStorable` accepts.
|
|
13
|
+
* - A first install controls the open pages at once; an update waits until
|
|
14
|
+
* the page posts `SKIP_WAITING`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function startWorker(options: WorkerOptions): void;
|
|
17
|
+
//# sourceMappingURL=start-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-worker.d.ts","sourceRoot":"","sources":["../../../src/pwa/worker/start-worker.ts"],"names":[],"mappings":"AAeA,OAAO,EAKN,KAAK,aAAa,EAElB,MAAM,QAAQ,CAAC;AAGhB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAiDxD"}
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
+
import { type AlxiaWorkerOptions } from './worker';
|
|
3
|
+
export type { AlxiaWorkerOptions } from './worker';
|
|
2
4
|
export interface AlxiaOptions {
|
|
3
5
|
/**
|
|
4
6
|
* The server file, relative to Vite's root: a module whose default export
|
|
@@ -7,6 +9,14 @@ export interface AlxiaOptions {
|
|
|
7
9
|
* otherwise `createServer()` with no options.
|
|
8
10
|
*/
|
|
9
11
|
readonly entry?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The service worker, bundled into `build/client/service-worker.js` after
|
|
14
|
+
* `react-router build`, with its precache list: a file relative to Vite's
|
|
15
|
+
* root, or options (`file`, `contract`, `offline`, `precacheAssets`,
|
|
16
|
+
* `minify`). By default `app/service-worker.ts` when it exists, and
|
|
17
|
+
* nothing without it; `false` builds none. Needs Bun.
|
|
18
|
+
*/
|
|
19
|
+
readonly worker?: false | string | AlxiaWorkerOptions;
|
|
10
20
|
}
|
|
11
21
|
/**
|
|
12
22
|
* The Vite plugin that makes alxia the server of a React Router app,
|
package/dist/vite/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAkB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAkB,MAAM,MAAM,CAAC;AAenD,OAAO,EAAE,KAAK,kBAAkB,EAAkB,MAAM,UAAU,CAAC;AAEnE,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,YAAY;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,kBAAkB,CAAC;CACtD;AAwBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,MAAM,CAyExD"}
|