@sovereignbase/pwa 0.0.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/LICENSE +201 -0
- package/README.md +0 -0
- package/dist/htmlDocument-Cx-dprM5.cjs +225 -0
- package/dist/htmlDocument-Cx-dprM5.cjs.map +1 -0
- package/dist/htmlDocument-DABSNDvf.js +220 -0
- package/dist/htmlDocument-DABSNDvf.js.map +1 -0
- package/dist/index.cjs +258 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +170 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +170 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +257 -0
- package/dist/index.js.map +1 -0
- package/dist/serviceWorker/entrypoint.cjs +128 -0
- package/dist/serviceWorker/entrypoint.cjs.map +1 -0
- package/dist/serviceWorker/entrypoint.d.cts +2 -0
- package/dist/serviceWorker/entrypoint.d.ts +2 -0
- package/dist/serviceWorker/entrypoint.js +129 -0
- package/dist/serviceWorker/entrypoint.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Sovereignbase
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const require_htmlDocument = require("../htmlDocument-Cx-dprM5.cjs");
|
|
18
|
+
//#region src/serviceWorker/entrypoint.ts
|
|
19
|
+
const worker = self;
|
|
20
|
+
const cachePrefix = "@sovereignbase/pwa:";
|
|
21
|
+
const cacheName = `${cachePrefix}${buildId}`;
|
|
22
|
+
const backgroundStartup = Promise.resolve(customWaitUntil?.());
|
|
23
|
+
customInitialize?.();
|
|
24
|
+
const routes = Object.freeze({
|
|
25
|
+
document: buildDocumentResponse,
|
|
26
|
+
static: negotiateCache
|
|
27
|
+
});
|
|
28
|
+
worker.addEventListener("fetch", (event) => {
|
|
29
|
+
event.waitUntil(Promise.all([backgroundStartup, checkForUpdate()]));
|
|
30
|
+
if (event.request.method !== "GET" || bypassesServiceWorker(event.request)) return;
|
|
31
|
+
const route = event.request.mode === "navigate" ? routes.document : routes.static;
|
|
32
|
+
event.respondWith(route(event));
|
|
33
|
+
});
|
|
34
|
+
worker.addEventListener("install", (event) => {
|
|
35
|
+
event.waitUntil(Promise.all([
|
|
36
|
+
caches.open(cacheName).then((cache) => cache.addAll(precache)),
|
|
37
|
+
worker.skipWaiting(),
|
|
38
|
+
backgroundStartup
|
|
39
|
+
]));
|
|
40
|
+
});
|
|
41
|
+
worker.addEventListener("activate", (event) => {
|
|
42
|
+
event.waitUntil(activateServiceWorker());
|
|
43
|
+
});
|
|
44
|
+
worker.addEventListener("message", (event) => {
|
|
45
|
+
if (event.data === "SKIP_WAITING") event.waitUntil(worker.skipWaiting());
|
|
46
|
+
});
|
|
47
|
+
async function buildDocumentResponse(event) {
|
|
48
|
+
const cache = await caches.open(cacheName);
|
|
49
|
+
const cached = await cache.match(event.request);
|
|
50
|
+
const rendered = renderDocument(event.request).then(async (response) => {
|
|
51
|
+
await cache.put(event.request, response.clone());
|
|
52
|
+
return response;
|
|
53
|
+
});
|
|
54
|
+
if (cached === void 0) return rendered;
|
|
55
|
+
event.waitUntil(rendered);
|
|
56
|
+
return cached;
|
|
57
|
+
}
|
|
58
|
+
async function negotiateCache(event) {
|
|
59
|
+
const cached = await caches.match(event.request);
|
|
60
|
+
const updated = fetch(event.request).then(async (response) => {
|
|
61
|
+
if (response.ok) await (await caches.open(cacheName)).put(event.request, response.clone());
|
|
62
|
+
return response;
|
|
63
|
+
});
|
|
64
|
+
if (cached === void 0) return updated;
|
|
65
|
+
event.waitUntil(updated);
|
|
66
|
+
return cached;
|
|
67
|
+
}
|
|
68
|
+
async function renderDocument(request) {
|
|
69
|
+
const language = documentLanguage(request);
|
|
70
|
+
const options = documentOptions[language] ?? documentOptions[defaultLanguage];
|
|
71
|
+
const markup = await require_htmlDocument.documentMarkup({
|
|
72
|
+
...options,
|
|
73
|
+
entrypoint,
|
|
74
|
+
language,
|
|
75
|
+
manifestUrl: `/${language}/manifest.webmanifest`,
|
|
76
|
+
stylesheet
|
|
77
|
+
});
|
|
78
|
+
return new Response(markup, { headers: {
|
|
79
|
+
"content-type": "text/html;charset=UTF-8",
|
|
80
|
+
"x-pwaize-build-id": buildId
|
|
81
|
+
} });
|
|
82
|
+
}
|
|
83
|
+
function documentLanguage(request) {
|
|
84
|
+
const pathLanguage = new URL(request.url).pathname.split("/").filter(Boolean)[0];
|
|
85
|
+
if (pathLanguage in documentOptions) return pathLanguage;
|
|
86
|
+
const accepted = request.headers.get("accept-language") ?? "";
|
|
87
|
+
for (const preference of accepted.split(",")) {
|
|
88
|
+
const language = preference.split(";")[0].trim();
|
|
89
|
+
if (language in documentOptions) return language;
|
|
90
|
+
const baseLanguage = language.split("-")[0];
|
|
91
|
+
if (baseLanguage in documentOptions) return baseLanguage;
|
|
92
|
+
}
|
|
93
|
+
return defaultLanguage;
|
|
94
|
+
}
|
|
95
|
+
function bypassesServiceWorker(request) {
|
|
96
|
+
const url = new URL(request.url);
|
|
97
|
+
for (const rule of bypassRules) {
|
|
98
|
+
const value = rule.absolute ? url.href : `${url.pathname}${url.search}`;
|
|
99
|
+
if (new RegExp(rule.source, rule.flags).test(value)) return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
async function checkForUpdate() {
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch(buildIdUrl, { cache: "no-store" });
|
|
106
|
+
if (!response.ok || (await response.text()).trim() === buildId) return;
|
|
107
|
+
await worker.registration.update();
|
|
108
|
+
} catch {}
|
|
109
|
+
}
|
|
110
|
+
async function activateServiceWorker() {
|
|
111
|
+
await Promise.all([
|
|
112
|
+
deleteOldCaches(),
|
|
113
|
+
worker.clients.claim(),
|
|
114
|
+
backgroundStartup
|
|
115
|
+
]);
|
|
116
|
+
const clients = await worker.clients.matchAll({
|
|
117
|
+
includeUncontrolled: true,
|
|
118
|
+
type: "window"
|
|
119
|
+
});
|
|
120
|
+
await Promise.all(clients.map((client) => client.navigate(client.url)));
|
|
121
|
+
}
|
|
122
|
+
async function deleteOldCaches() {
|
|
123
|
+
const names = await caches.keys();
|
|
124
|
+
await Promise.all(names.filter((name) => name.startsWith(cachePrefix) && name !== cacheName).map((name) => caches.delete(name)));
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=entrypoint.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entrypoint.cjs","names":["documentMarkup"],"sources":["../../src/serviceWorker/entrypoint.ts"],"sourcesContent":["import type { DocumentMarkupOptions } from '../.types/index.js'\nimport { documentMarkup } from '../htmlDocument/index.js'\n\ndeclare const buildId: string\ndeclare const buildIdUrl: string\ndeclare const bypassRules: ReadonlyArray<{\n absolute: boolean\n flags: string\n source: string\n}>\ndeclare const customInitialize: (() => void) | undefined\ndeclare const customWaitUntil: (() => Promise<void>) | undefined\ndeclare const defaultLanguage: DocumentMarkupOptions['language']\ndeclare const documentOptions: Readonly<\n Record<\n string,\n Omit<\n DocumentMarkupOptions,\n 'entrypoint' | 'language' | 'manifestUrl' | 'stylesheet'\n >\n >\n>\ndeclare const entrypoint: string\ndeclare const precache: readonly string[]\ndeclare const stylesheet: string\n\nconst worker = self as unknown as ServiceWorkerGlobalScope\nconst cachePrefix = '@sovereignbase/pwa:'\nconst cacheName = `${cachePrefix}${buildId}`\nconst backgroundStartup = Promise.resolve(customWaitUntil?.())\n\ncustomInitialize?.()\n\nconst routes = Object.freeze({\n document: buildDocumentResponse,\n static: negotiateCache,\n})\n\nworker.addEventListener('fetch', (event) => {\n event.waitUntil(Promise.all([backgroundStartup, checkForUpdate()]))\n\n if (event.request.method !== 'GET' || bypassesServiceWorker(event.request)) {\n return\n }\n\n const route =\n event.request.mode === 'navigate' ? routes.document : routes.static\n event.respondWith(route(event))\n})\n\nworker.addEventListener('install', (event) => {\n event.waitUntil(\n Promise.all([\n caches.open(cacheName).then((cache) => cache.addAll(precache)),\n worker.skipWaiting(),\n backgroundStartup,\n ])\n )\n})\n\nworker.addEventListener('activate', (event) => {\n event.waitUntil(activateServiceWorker())\n})\n\nworker.addEventListener('message', (event) => {\n if (event.data === 'SKIP_WAITING') {\n event.waitUntil(worker.skipWaiting())\n }\n})\n\nasync function buildDocumentResponse(event: FetchEvent): Promise<Response> {\n const cache = await caches.open(cacheName)\n const cached = await cache.match(event.request)\n const rendered = renderDocument(event.request).then(async (response) => {\n await cache.put(event.request, response.clone())\n return response\n })\n\n if (cached === undefined) return rendered\n\n event.waitUntil(rendered)\n return cached\n}\n\nasync function negotiateCache(event: FetchEvent): Promise<Response> {\n const cached = await caches.match(event.request)\n const updated = fetch(event.request).then(async (response) => {\n if (response.ok) {\n const cache = await caches.open(cacheName)\n await cache.put(event.request, response.clone())\n }\n return response\n })\n\n if (cached === undefined) return updated\n\n event.waitUntil(updated)\n return cached\n}\n\nasync function renderDocument(request: Request): Promise<Response> {\n const language = documentLanguage(request)\n const options = documentOptions[language] ?? documentOptions[defaultLanguage]\n const markup = await documentMarkup({\n ...options,\n entrypoint,\n language,\n manifestUrl: `/${language}/manifest.webmanifest`,\n stylesheet,\n })\n\n return new Response(markup, {\n headers: {\n 'content-type': 'text/html;charset=UTF-8',\n 'x-pwaize-build-id': buildId,\n },\n })\n}\n\nfunction documentLanguage(request: Request): DocumentMarkupOptions['language'] {\n const pathLanguage = new URL(request.url).pathname\n .split('/')\n .filter(Boolean)[0]\n if (pathLanguage in documentOptions) {\n return pathLanguage as DocumentMarkupOptions['language']\n }\n\n const accepted = request.headers.get('accept-language') ?? ''\n for (const preference of accepted.split(',')) {\n const language = preference.split(';')[0].trim()\n if (language in documentOptions) {\n return language as DocumentMarkupOptions['language']\n }\n\n const baseLanguage = language.split('-')[0]\n if (baseLanguage in documentOptions) {\n return baseLanguage as DocumentMarkupOptions['language']\n }\n }\n\n return defaultLanguage\n}\n\nfunction bypassesServiceWorker(request: Request): boolean {\n const url = new URL(request.url)\n\n for (const rule of bypassRules) {\n const value = rule.absolute ? url.href : `${url.pathname}${url.search}`\n if (new RegExp(rule.source, rule.flags).test(value)) return true\n }\n\n return false\n}\n\nasync function checkForUpdate(): Promise<void> {\n try {\n const response = await fetch(buildIdUrl, { cache: 'no-store' })\n if (!response.ok || (await response.text()).trim() === buildId) return\n\n await worker.registration.update()\n } catch {\n // Offline version checks must not prevent offline responses.\n }\n}\n\nasync function activateServiceWorker(): Promise<void> {\n await Promise.all([\n deleteOldCaches(),\n worker.clients.claim(),\n backgroundStartup,\n ])\n\n const clients = await worker.clients.matchAll({\n includeUncontrolled: true,\n type: 'window',\n })\n await Promise.all(clients.map((client) => client.navigate(client.url)))\n}\n\nasync function deleteOldCaches(): Promise<void> {\n const names = await caches.keys()\n await Promise.all(\n names\n .filter((name) => name.startsWith(cachePrefix) && name !== cacheName)\n .map((name) => caches.delete(name))\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Sovereignbase
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { t as documentMarkup } from "../htmlDocument-DABSNDvf.js";
|
|
18
|
+
//#region src/serviceWorker/entrypoint.ts
|
|
19
|
+
const worker = self;
|
|
20
|
+
const cachePrefix = "@sovereignbase/pwa:";
|
|
21
|
+
const cacheName = `${cachePrefix}${buildId}`;
|
|
22
|
+
const backgroundStartup = Promise.resolve(customWaitUntil?.());
|
|
23
|
+
customInitialize?.();
|
|
24
|
+
const routes = Object.freeze({
|
|
25
|
+
document: buildDocumentResponse,
|
|
26
|
+
static: negotiateCache
|
|
27
|
+
});
|
|
28
|
+
worker.addEventListener("fetch", (event) => {
|
|
29
|
+
event.waitUntil(Promise.all([backgroundStartup, checkForUpdate()]));
|
|
30
|
+
if (event.request.method !== "GET" || bypassesServiceWorker(event.request)) return;
|
|
31
|
+
const route = event.request.mode === "navigate" ? routes.document : routes.static;
|
|
32
|
+
event.respondWith(route(event));
|
|
33
|
+
});
|
|
34
|
+
worker.addEventListener("install", (event) => {
|
|
35
|
+
event.waitUntil(Promise.all([
|
|
36
|
+
caches.open(cacheName).then((cache) => cache.addAll(precache)),
|
|
37
|
+
worker.skipWaiting(),
|
|
38
|
+
backgroundStartup
|
|
39
|
+
]));
|
|
40
|
+
});
|
|
41
|
+
worker.addEventListener("activate", (event) => {
|
|
42
|
+
event.waitUntil(activateServiceWorker());
|
|
43
|
+
});
|
|
44
|
+
worker.addEventListener("message", (event) => {
|
|
45
|
+
if (event.data === "SKIP_WAITING") event.waitUntil(worker.skipWaiting());
|
|
46
|
+
});
|
|
47
|
+
async function buildDocumentResponse(event) {
|
|
48
|
+
const cache = await caches.open(cacheName);
|
|
49
|
+
const cached = await cache.match(event.request);
|
|
50
|
+
const rendered = renderDocument(event.request).then(async (response) => {
|
|
51
|
+
await cache.put(event.request, response.clone());
|
|
52
|
+
return response;
|
|
53
|
+
});
|
|
54
|
+
if (cached === void 0) return rendered;
|
|
55
|
+
event.waitUntil(rendered);
|
|
56
|
+
return cached;
|
|
57
|
+
}
|
|
58
|
+
async function negotiateCache(event) {
|
|
59
|
+
const cached = await caches.match(event.request);
|
|
60
|
+
const updated = fetch(event.request).then(async (response) => {
|
|
61
|
+
if (response.ok) await (await caches.open(cacheName)).put(event.request, response.clone());
|
|
62
|
+
return response;
|
|
63
|
+
});
|
|
64
|
+
if (cached === void 0) return updated;
|
|
65
|
+
event.waitUntil(updated);
|
|
66
|
+
return cached;
|
|
67
|
+
}
|
|
68
|
+
async function renderDocument(request) {
|
|
69
|
+
const language = documentLanguage(request);
|
|
70
|
+
const options = documentOptions[language] ?? documentOptions[defaultLanguage];
|
|
71
|
+
const markup = await documentMarkup({
|
|
72
|
+
...options,
|
|
73
|
+
entrypoint,
|
|
74
|
+
language,
|
|
75
|
+
manifestUrl: `/${language}/manifest.webmanifest`,
|
|
76
|
+
stylesheet
|
|
77
|
+
});
|
|
78
|
+
return new Response(markup, { headers: {
|
|
79
|
+
"content-type": "text/html;charset=UTF-8",
|
|
80
|
+
"x-pwaize-build-id": buildId
|
|
81
|
+
} });
|
|
82
|
+
}
|
|
83
|
+
function documentLanguage(request) {
|
|
84
|
+
const pathLanguage = new URL(request.url).pathname.split("/").filter(Boolean)[0];
|
|
85
|
+
if (pathLanguage in documentOptions) return pathLanguage;
|
|
86
|
+
const accepted = request.headers.get("accept-language") ?? "";
|
|
87
|
+
for (const preference of accepted.split(",")) {
|
|
88
|
+
const language = preference.split(";")[0].trim();
|
|
89
|
+
if (language in documentOptions) return language;
|
|
90
|
+
const baseLanguage = language.split("-")[0];
|
|
91
|
+
if (baseLanguage in documentOptions) return baseLanguage;
|
|
92
|
+
}
|
|
93
|
+
return defaultLanguage;
|
|
94
|
+
}
|
|
95
|
+
function bypassesServiceWorker(request) {
|
|
96
|
+
const url = new URL(request.url);
|
|
97
|
+
for (const rule of bypassRules) {
|
|
98
|
+
const value = rule.absolute ? url.href : `${url.pathname}${url.search}`;
|
|
99
|
+
if (new RegExp(rule.source, rule.flags).test(value)) return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
async function checkForUpdate() {
|
|
104
|
+
try {
|
|
105
|
+
const response = await fetch(buildIdUrl, { cache: "no-store" });
|
|
106
|
+
if (!response.ok || (await response.text()).trim() === buildId) return;
|
|
107
|
+
await worker.registration.update();
|
|
108
|
+
} catch {}
|
|
109
|
+
}
|
|
110
|
+
async function activateServiceWorker() {
|
|
111
|
+
await Promise.all([
|
|
112
|
+
deleteOldCaches(),
|
|
113
|
+
worker.clients.claim(),
|
|
114
|
+
backgroundStartup
|
|
115
|
+
]);
|
|
116
|
+
const clients = await worker.clients.matchAll({
|
|
117
|
+
includeUncontrolled: true,
|
|
118
|
+
type: "window"
|
|
119
|
+
});
|
|
120
|
+
await Promise.all(clients.map((client) => client.navigate(client.url)));
|
|
121
|
+
}
|
|
122
|
+
async function deleteOldCaches() {
|
|
123
|
+
const names = await caches.keys();
|
|
124
|
+
await Promise.all(names.filter((name) => name.startsWith(cachePrefix) && name !== cacheName).map((name) => caches.delete(name)));
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export {};
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=entrypoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entrypoint.js","names":[],"sources":["../../src/serviceWorker/entrypoint.ts"],"sourcesContent":["import type { DocumentMarkupOptions } from '../.types/index.js'\nimport { documentMarkup } from '../htmlDocument/index.js'\n\ndeclare const buildId: string\ndeclare const buildIdUrl: string\ndeclare const bypassRules: ReadonlyArray<{\n absolute: boolean\n flags: string\n source: string\n}>\ndeclare const customInitialize: (() => void) | undefined\ndeclare const customWaitUntil: (() => Promise<void>) | undefined\ndeclare const defaultLanguage: DocumentMarkupOptions['language']\ndeclare const documentOptions: Readonly<\n Record<\n string,\n Omit<\n DocumentMarkupOptions,\n 'entrypoint' | 'language' | 'manifestUrl' | 'stylesheet'\n >\n >\n>\ndeclare const entrypoint: string\ndeclare const precache: readonly string[]\ndeclare const stylesheet: string\n\nconst worker = self as unknown as ServiceWorkerGlobalScope\nconst cachePrefix = '@sovereignbase/pwa:'\nconst cacheName = `${cachePrefix}${buildId}`\nconst backgroundStartup = Promise.resolve(customWaitUntil?.())\n\ncustomInitialize?.()\n\nconst routes = Object.freeze({\n document: buildDocumentResponse,\n static: negotiateCache,\n})\n\nworker.addEventListener('fetch', (event) => {\n event.waitUntil(Promise.all([backgroundStartup, checkForUpdate()]))\n\n if (event.request.method !== 'GET' || bypassesServiceWorker(event.request)) {\n return\n }\n\n const route =\n event.request.mode === 'navigate' ? routes.document : routes.static\n event.respondWith(route(event))\n})\n\nworker.addEventListener('install', (event) => {\n event.waitUntil(\n Promise.all([\n caches.open(cacheName).then((cache) => cache.addAll(precache)),\n worker.skipWaiting(),\n backgroundStartup,\n ])\n )\n})\n\nworker.addEventListener('activate', (event) => {\n event.waitUntil(activateServiceWorker())\n})\n\nworker.addEventListener('message', (event) => {\n if (event.data === 'SKIP_WAITING') {\n event.waitUntil(worker.skipWaiting())\n }\n})\n\nasync function buildDocumentResponse(event: FetchEvent): Promise<Response> {\n const cache = await caches.open(cacheName)\n const cached = await cache.match(event.request)\n const rendered = renderDocument(event.request).then(async (response) => {\n await cache.put(event.request, response.clone())\n return response\n })\n\n if (cached === undefined) return rendered\n\n event.waitUntil(rendered)\n return cached\n}\n\nasync function negotiateCache(event: FetchEvent): Promise<Response> {\n const cached = await caches.match(event.request)\n const updated = fetch(event.request).then(async (response) => {\n if (response.ok) {\n const cache = await caches.open(cacheName)\n await cache.put(event.request, response.clone())\n }\n return response\n })\n\n if (cached === undefined) return updated\n\n event.waitUntil(updated)\n return cached\n}\n\nasync function renderDocument(request: Request): Promise<Response> {\n const language = documentLanguage(request)\n const options = documentOptions[language] ?? documentOptions[defaultLanguage]\n const markup = await documentMarkup({\n ...options,\n entrypoint,\n language,\n manifestUrl: `/${language}/manifest.webmanifest`,\n stylesheet,\n })\n\n return new Response(markup, {\n headers: {\n 'content-type': 'text/html;charset=UTF-8',\n 'x-pwaize-build-id': buildId,\n },\n })\n}\n\nfunction documentLanguage(request: Request): DocumentMarkupOptions['language'] {\n const pathLanguage = new URL(request.url).pathname\n .split('/')\n .filter(Boolean)[0]\n if (pathLanguage in documentOptions) {\n return pathLanguage as DocumentMarkupOptions['language']\n }\n\n const accepted = request.headers.get('accept-language') ?? ''\n for (const preference of accepted.split(',')) {\n const language = preference.split(';')[0].trim()\n if (language in documentOptions) {\n return language as DocumentMarkupOptions['language']\n }\n\n const baseLanguage = language.split('-')[0]\n if (baseLanguage in documentOptions) {\n return baseLanguage as DocumentMarkupOptions['language']\n }\n }\n\n return defaultLanguage\n}\n\nfunction bypassesServiceWorker(request: Request): boolean {\n const url = new URL(request.url)\n\n for (const rule of bypassRules) {\n const value = rule.absolute ? url.href : `${url.pathname}${url.search}`\n if (new RegExp(rule.source, rule.flags).test(value)) return true\n }\n\n return false\n}\n\nasync function checkForUpdate(): Promise<void> {\n try {\n const response = await fetch(buildIdUrl, { cache: 'no-store' })\n if (!response.ok || (await response.text()).trim() === buildId) return\n\n await worker.registration.update()\n } catch {\n // Offline version checks must not prevent offline responses.\n }\n}\n\nasync function activateServiceWorker(): Promise<void> {\n await Promise.all([\n deleteOldCaches(),\n worker.clients.claim(),\n backgroundStartup,\n ])\n\n const clients = await worker.clients.matchAll({\n includeUncontrolled: true,\n type: 'window',\n })\n await Promise.all(clients.map((client) => client.navigate(client.url)))\n}\n\nasync function deleteOldCaches(): Promise<void> {\n const names = await caches.keys()\n await Promise.all(\n names\n .filter((name) => name.startsWith(cachePrefix) && name !== cacheName)\n .map((name) => caches.delete(name))\n )\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sovereignbase/pwa",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Build offline-first, search-engine-optimized progressive web applications.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"build-tool",
|
|
7
|
+
"offline-first",
|
|
8
|
+
"progressive-web-app",
|
|
9
|
+
"pwa",
|
|
10
|
+
"service-worker",
|
|
11
|
+
"static-site-generation",
|
|
12
|
+
"typescript"
|
|
13
|
+
],
|
|
14
|
+
"license": "Apache-2.0",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js",
|
|
23
|
+
"require": "./dist/index.cjs",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
},
|
|
36
|
+
"sideEffects": false,
|
|
37
|
+
"scripts": {
|
|
38
|
+
"format": "prettier . --write",
|
|
39
|
+
"format:check": "prettier . --check",
|
|
40
|
+
"build": "npm run build:package && npm run build:docs",
|
|
41
|
+
"build:package": "tsdown",
|
|
42
|
+
"build:docs": "typedoc",
|
|
43
|
+
"build:example": "npm run build:package && node test/index.js",
|
|
44
|
+
"typecheck": "tsc --noEmit && tsc --noEmit --project test/tsconfig.json",
|
|
45
|
+
"test": "npm run build && npm run typecheck && npm run test:vitest && npm run test:runtimes && npm run test:e2e",
|
|
46
|
+
"test:vitest": "vitest run --config test/vitest.config.ts --coverage",
|
|
47
|
+
"test:runtimes": "npm run test:runtime:node && npm run test:runtime:bun",
|
|
48
|
+
"test:runtime:node": "node test/runtimes/run.mjs",
|
|
49
|
+
"test:runtime:bun": "bun test/runtimes/run.mjs",
|
|
50
|
+
"test:e2e": "playwright test --config test/playwright.config.ts",
|
|
51
|
+
"prepare": "husky"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@sovereignbase/bytecodec": "^1.7.0",
|
|
55
|
+
"@sovereignbase/utils": "^2.0.0",
|
|
56
|
+
"esbuild": "^0.28.2",
|
|
57
|
+
"fs-extra": "^11.4.0",
|
|
58
|
+
"html-minifier-terser": "^7.2.0",
|
|
59
|
+
"lightningcss": "^1.33.0",
|
|
60
|
+
"terser": "^5.51.2"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@commitlint/cli": "^21.2.2",
|
|
64
|
+
"@commitlint/config-conventional": "^21.2.2",
|
|
65
|
+
"@playwright/test": "^1.62.1",
|
|
66
|
+
"@types/html-minifier-terser": "^7.0.2",
|
|
67
|
+
"@types/node": "^26.4.0",
|
|
68
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
69
|
+
"husky": "^9.1.7",
|
|
70
|
+
"playwright": "^1.62.1",
|
|
71
|
+
"prettier": "^3.9.6",
|
|
72
|
+
"tsdown": "^0.22.14",
|
|
73
|
+
"typedoc": "^0.28.20",
|
|
74
|
+
"typescript": "^5.9.3",
|
|
75
|
+
"vitest": "^4.1.11"
|
|
76
|
+
},
|
|
77
|
+
"repository": {
|
|
78
|
+
"type": "git",
|
|
79
|
+
"url": "git+https://github.com/sovereignbase/pwa.git"
|
|
80
|
+
},
|
|
81
|
+
"bugs": {
|
|
82
|
+
"url": "https://github.com/sovereignbase/pwa/issues"
|
|
83
|
+
},
|
|
84
|
+
"homepage": "https://sovereignbase.dev/pwa"
|
|
85
|
+
}
|