offline-page-kit 0.1.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/dist/cli.cjs ADDED
@@ -0,0 +1,334 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var import_node_path2 = __toESM(require("path"), 1);
28
+
29
+ // src/core/utils.ts
30
+ var import_node_fs = __toESM(require("fs"), 1);
31
+ var import_node_path = __toESM(require("path"), 1);
32
+ function ensureDir(p) {
33
+ import_node_fs.default.mkdirSync(p, { recursive: true });
34
+ }
35
+ function writeFileSafe(filePath, content) {
36
+ ensureDir(import_node_path.default.dirname(filePath));
37
+ import_node_fs.default.writeFileSync(filePath, content, "utf8");
38
+ }
39
+ function exists(filePath) {
40
+ try {
41
+ import_node_fs.default.accessSync(filePath);
42
+ return true;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+ function parseArgs(argv) {
48
+ const m = /* @__PURE__ */ new Map();
49
+ for (let i = 0; i < argv.length; i++) {
50
+ const a = argv[i];
51
+ if (!a.startsWith("--")) continue;
52
+ const key = a.slice(2);
53
+ const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
54
+ m.set(key, value);
55
+ }
56
+ return m;
57
+ }
58
+ function splitList(v) {
59
+ return (v || "").split(",").map((s) => s.trim()).filter(Boolean);
60
+ }
61
+
62
+ // src/core/offlineHtml.ts
63
+ function offlineHtmlTemplate(title = "You're Offline") {
64
+ return `<!doctype html>
65
+ <html lang="en">
66
+ <head>
67
+ <meta charset="UTF-8"/>
68
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
69
+ <title>${title}</title>
70
+ <style>
71
+ body{margin:0;height:100vh;display:grid;place-items:center;font-family:system-ui,-apple-system,Segoe UI,Roboto;background:#0b0f19;color:#fff}
72
+ .box{max-width:560px;padding:28px 26px;border-radius:18px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04)}
73
+ h1{margin:0 0 10px;font-size:28px}
74
+ p{margin:0 0 18px;opacity:.85;line-height:1.5}
75
+ .row{display:flex;gap:10px;flex-wrap:wrap}
76
+ button,a{appearance:none;border:0;border-radius:12px;padding:10px 14px;cursor:pointer;text-decoration:none}
77
+ button{background:#fff;color:#111}
78
+ a{background:rgba(255,255,255,.1);color:#fff}
79
+ </style>
80
+ </head>
81
+ <body>
82
+ <div class="box">
83
+ <h1>\u26A1 You\u2019re Offline</h1>
84
+ <p>No internet connection detected. You can retry, or go back to the homepage (if cached).</p>
85
+ <div class="row">
86
+ <button onclick="location.reload()">Retry</button>
87
+ <a href="/">Go Home</a>
88
+ </div>
89
+ </div>
90
+ </body>
91
+ </html>`;
92
+ }
93
+
94
+ // src/core/offlineSvg.ts
95
+ function offlineSvgTemplate() {
96
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
97
+ <rect width="1200" height="630" fill="#0b0f19"/>
98
+ <text x="80" y="220" fill="#ffffff" font-size="64" font-family="system-ui, -apple-system, Segoe UI, Roboto">You\u2019re Offline</text>
99
+ <text x="80" y="300" fill="#cbd5e1" font-size="28" font-family="system-ui, -apple-system, Segoe UI, Roboto">Please check your connection and try again.</text>
100
+ <circle cx="1040" cy="220" r="90" fill="rgba(255,255,255,0.08)"/>
101
+ <path d="M980 220c40-40 120-40 160 0" stroke="#fff" stroke-width="10" fill="none" opacity="0.6"/>
102
+ <path d="M1010 250c25-25 75-25 100 0" stroke="#fff" stroke-width="10" fill="none" opacity="0.6"/>
103
+ <circle cx="1060" cy="290" r="10" fill="#fff" opacity="0.7"/>
104
+ </svg>`;
105
+ }
106
+
107
+ // src/core/swTemplate.ts
108
+ var DEFAULT_EXTS = [
109
+ "js",
110
+ "css",
111
+ "map",
112
+ "ico",
113
+ "png",
114
+ "jpg",
115
+ "jpeg",
116
+ "webp",
117
+ "svg",
118
+ "gif",
119
+ "woff2",
120
+ "woff",
121
+ "ttf",
122
+ "eot",
123
+ "json",
124
+ "txt",
125
+ "xml",
126
+ "webmanifest"
127
+ ];
128
+ function jsString(v) {
129
+ return JSON.stringify(v);
130
+ }
131
+ function buildServiceWorkerJS(options2) {
132
+ const {
133
+ cacheName,
134
+ offlinePage,
135
+ offlineImage,
136
+ precache,
137
+ htmlStrategy,
138
+ assetStrategy,
139
+ imageStrategy,
140
+ assetExtensions,
141
+ apiPrefixes
142
+ } = options2;
143
+ return `/* offline-page-kit service worker */
144
+ const CACHE_NAME = ${jsString(cacheName)};
145
+ const OFFLINE_PAGE = ${jsString(offlinePage)};
146
+ const OFFLINE_IMAGE = ${jsString(offlineImage)};
147
+ const PRECACHE = ${jsString([offlinePage, offlineImage, ...precache].filter(Boolean))};
148
+
149
+ const HTML_STRATEGY = ${jsString(htmlStrategy)};
150
+ const ASSET_STRATEGY = ${jsString(assetStrategy)};
151
+ const IMAGE_STRATEGY = ${jsString(imageStrategy)};
152
+
153
+ const ASSET_EXTS = new Set(${jsString(assetExtensions.length ? assetExtensions : DEFAULT_EXTS)});
154
+ const API_PREFIXES = ${jsString(apiPrefixes)};
155
+
156
+ // helpers
157
+ const isApi = (url) => API_PREFIXES.some(p => url.href.startsWith(p) || url.pathname.startsWith(p));
158
+ const extOf = (pathname) => {
159
+ const i = pathname.lastIndexOf(".");
160
+ return i === -1 ? "" : pathname.slice(i+1).toLowerCase();
161
+ };
162
+
163
+ async function cacheGet(req) {
164
+ const cache = await caches.open(CACHE_NAME);
165
+ return cache.match(req);
166
+ }
167
+ async function cachePut(req, res) {
168
+ const cache = await caches.open(CACHE_NAME);
169
+ await cache.put(req, res);
170
+ }
171
+ async function cacheDeleteOld() {
172
+ const keys = await caches.keys();
173
+ await Promise.all(keys.map(k => (k === CACHE_NAME ? null : caches.delete(k))));
174
+ }
175
+
176
+ // strategies
177
+ async function networkFirst(req, fallbackUrl) {
178
+ try {
179
+ const res = await fetch(req);
180
+ if (res && res.ok) cachePut(req, res.clone());
181
+ return res;
182
+ } catch {
183
+ const cached = await cacheGet(req);
184
+ if (cached) return cached;
185
+ if (fallbackUrl) return cacheGet(fallbackUrl);
186
+ return new Response("Offline", { status: 503 });
187
+ }
188
+ }
189
+
190
+ async function cacheFirst(req, fallbackUrl) {
191
+ const cached = await cacheGet(req);
192
+ if (cached) return cached;
193
+ try {
194
+ const res = await fetch(req);
195
+ if (res && res.ok) cachePut(req, res.clone());
196
+ return res;
197
+ } catch {
198
+ if (fallbackUrl) return cacheGet(fallbackUrl);
199
+ return new Response("Offline", { status: 503 });
200
+ }
201
+ }
202
+
203
+ async function staleWhileRevalidate(req) {
204
+ const cached = await cacheGet(req);
205
+ const fetchPromise = fetch(req).then(res => {
206
+ if (res && res.ok) cachePut(req, res.clone());
207
+ return res;
208
+ }).catch(() => null);
209
+
210
+ return cached || (await fetchPromise) || new Response("Offline", { status: 503 });
211
+ }
212
+
213
+ function pickStrategy(name) {
214
+ if (name === "cacheFirst") return cacheFirst;
215
+ if (name === "staleWhileRevalidate") return staleWhileRevalidate;
216
+ return networkFirst;
217
+ }
218
+
219
+ self.addEventListener("install", (event) => {
220
+ event.waitUntil((async () => {
221
+ const cache = await caches.open(CACHE_NAME);
222
+ await cache.addAll(PRECACHE);
223
+ self.skipWaiting();
224
+ })());
225
+ });
226
+
227
+ self.addEventListener("activate", (event) => {
228
+ event.waitUntil((async () => {
229
+ await cacheDeleteOld();
230
+ await self.clients.claim();
231
+ })());
232
+ });
233
+
234
+ self.addEventListener("fetch", (event) => {
235
+ const req = event.request;
236
+ const url = new URL(req.url);
237
+
238
+ // Only handle same-origin requests (safe default)
239
+ if (url.origin !== self.location.origin) return;
240
+
241
+ // HTML navigations (SPA, MPA, Next, etc.)
242
+ if (req.mode === "navigate" || (req.headers.get("accept") || "").includes("text/html")) {
243
+ const fn = pickStrategy(HTML_STRATEGY);
244
+ event.respondWith(fn(req, OFFLINE_PAGE));
245
+ return;
246
+ }
247
+
248
+ // API calls (optional: you can also skip caching)
249
+ if (isApi(url)) {
250
+ // network-first is usually best for APIs
251
+ event.respondWith(networkFirst(req));
252
+ return;
253
+ }
254
+
255
+ // Images
256
+ const ext = extOf(url.pathname);
257
+ if (["png","jpg","jpeg","webp","gif","svg","ico"].includes(ext)) {
258
+ const fn = pickStrategy(IMAGE_STRATEGY);
259
+ event.respondWith(fn(req, OFFLINE_IMAGE));
260
+ return;
261
+ }
262
+
263
+ // Static assets (js/css/fonts/etc.)
264
+ if (ASSET_EXTS.has(ext)) {
265
+ const fn = pickStrategy(ASSET_STRATEGY);
266
+ // staleWhileRevalidate doesn't use fallback
267
+ event.respondWith(fn === staleWhileRevalidate ? staleWhileRevalidate(req) : fn(req));
268
+ return;
269
+ }
270
+ });
271
+ `;
272
+ }
273
+
274
+ // src/cli.ts
275
+ function normalizePublicPath(p) {
276
+ if (!p.startsWith("/")) return "/" + p;
277
+ return p;
278
+ }
279
+ function withDefaults(o) {
280
+ return {
281
+ outDir: o.outDir ?? "public",
282
+ swFileName: o.swFileName ?? "sw.js",
283
+ offlinePage: normalizePublicPath(o.offlinePage ?? "/offline.html"),
284
+ offlineImage: normalizePublicPath(o.offlineImage ?? "/offline.svg"),
285
+ cacheName: o.cacheName ?? "offline-page-kit",
286
+ precache: o.precache ?? [],
287
+ htmlStrategy: o.htmlStrategy ?? "networkFirst",
288
+ assetStrategy: o.assetStrategy ?? "staleWhileRevalidate",
289
+ imageStrategy: o.imageStrategy ?? "cacheFirst",
290
+ assetExtensions: o.assetExtensions ?? [],
291
+ apiPrefixes: o.apiPrefixes ?? []
292
+ };
293
+ }
294
+ var args = parseArgs(process.argv.slice(2));
295
+ var cmd = process.argv.slice(2).find((a) => !a.startsWith("--")) || "init";
296
+ var outDir = import_node_path2.default.resolve(process.cwd(), args.get("outDir") || "public");
297
+ var options = withDefaults({
298
+ outDir,
299
+ swFileName: args.get("swFileName") || "sw.js",
300
+ offlinePage: args.get("offlinePage") || "/offline.html",
301
+ offlineImage: args.get("offlineImage") || "/offline.svg",
302
+ cacheName: args.get("cacheName") || "offline-page-kit",
303
+ precache: splitList(args.get("precache")),
304
+ htmlStrategy: args.get("htmlStrategy") || "networkFirst",
305
+ assetStrategy: args.get("assetStrategy") || "staleWhileRevalidate",
306
+ imageStrategy: args.get("imageStrategy") || "cacheFirst",
307
+ assetExtensions: splitList(args.get("assetExtensions")),
308
+ apiPrefixes: splitList(args.get("apiPrefixes"))
309
+ });
310
+ var swOut = import_node_path2.default.join(outDir, options.swFileName);
311
+ var offlineHtmlOut = import_node_path2.default.join(outDir, options.offlinePage.replace(/^\//, ""));
312
+ var offlineSvgOut = import_node_path2.default.join(outDir, options.offlineImage.replace(/^\//, ""));
313
+ if (cmd === "init" || cmd === "build") {
314
+ if (cmd === "build" || !exists(offlineHtmlOut)) {
315
+ writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());
316
+ }
317
+ if (cmd === "build" || !exists(offlineSvgOut)) {
318
+ writeFileSafe(offlineSvgOut, offlineSvgTemplate());
319
+ }
320
+ const sw = buildServiceWorkerJS(options);
321
+ writeFileSafe(swOut, sw);
322
+ console.log(`[offline-page-kit] Generated:
323
+ - ${swOut}
324
+ - ${offlineHtmlOut}
325
+ - ${offlineSvgOut}
326
+ `);
327
+ } else {
328
+ console.log(`[offline-page-kit] Unknown command: ${cmd}
329
+ Use:
330
+ offline-page-kit init --outDir public
331
+ offline-page-kit build --outDir public
332
+ `);
333
+ }
334
+ //# sourceMappingURL=cli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/core/utils.ts","../src/core/offlineHtml.ts","../src/core/offlineSvg.ts","../src/core/swTemplate.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport path from \"node:path\";\r\nimport { parseArgs, splitList, writeFileSafe, exists } from \"./core/utils\";\r\nimport { offlineHtmlTemplate } from \"./core/offlineHtml\";\r\nimport { offlineSvgTemplate } from \"./core/offlineSvg\";\r\nimport { buildServiceWorkerJS } from \"./core/swTemplate\";\r\nimport type { OfflineKitBuildOptions } from \"./types\";\r\n\r\nfunction normalizePublicPath(p: string) {\r\n if (!p.startsWith(\"/\")) return \"/\" + p;\r\n return p;\r\n}\r\n\r\nfunction withDefaults(o: OfflineKitBuildOptions): Required<OfflineKitBuildOptions> {\r\n return {\r\n outDir: o.outDir ?? \"public\",\r\n swFileName: o.swFileName ?? \"sw.js\",\r\n offlinePage: normalizePublicPath(o.offlinePage ?? \"/offline.html\"),\r\n offlineImage: normalizePublicPath(o.offlineImage ?? \"/offline.svg\"),\r\n cacheName: o.cacheName ?? \"offline-page-kit\",\r\n precache: o.precache ?? [],\r\n htmlStrategy: o.htmlStrategy ?? \"networkFirst\",\r\n assetStrategy: o.assetStrategy ?? \"staleWhileRevalidate\",\r\n imageStrategy: o.imageStrategy ?? \"cacheFirst\",\r\n assetExtensions: o.assetExtensions ?? [],\r\n apiPrefixes: o.apiPrefixes ?? [],\r\n };\r\n}\r\n\r\nconst args = parseArgs(process.argv.slice(2));\r\nconst cmd = process.argv.slice(2).find(a => !a.startsWith(\"--\")) || \"init\";\r\n\r\nconst outDir = path.resolve(process.cwd(), args.get(\"outDir\") || \"public\");\r\n\r\nconst options = withDefaults({\r\n outDir,\r\n swFileName: args.get(\"swFileName\") || \"sw.js\",\r\n offlinePage: args.get(\"offlinePage\") || \"/offline.html\",\r\n offlineImage: args.get(\"offlineImage\") || \"/offline.svg\",\r\n cacheName: args.get(\"cacheName\") || \"offline-page-kit\",\r\n precache: splitList(args.get(\"precache\")),\r\n htmlStrategy: (args.get(\"htmlStrategy\") as any) || \"networkFirst\",\r\n assetStrategy: (args.get(\"assetStrategy\") as any) || \"staleWhileRevalidate\",\r\n imageStrategy: (args.get(\"imageStrategy\") as any) || \"cacheFirst\",\r\n assetExtensions: splitList(args.get(\"assetExtensions\")),\r\n apiPrefixes: splitList(args.get(\"apiPrefixes\")),\r\n} as OfflineKitBuildOptions);\r\n\r\nconst swOut = path.join(outDir, options.swFileName);\r\nconst offlineHtmlOut = path.join(outDir, options.offlinePage.replace(/^\\//, \"\"));\r\nconst offlineSvgOut = path.join(outDir, options.offlineImage.replace(/^\\//, \"\"));\r\n\r\nif (cmd === \"init\" || cmd === \"build\") {\r\n // generate offline page if missing OR force (when build)\r\n if (cmd === \"build\" || !exists(offlineHtmlOut)) {\r\n writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());\r\n }\r\n if (cmd === \"build\" || !exists(offlineSvgOut)) {\r\n writeFileSafe(offlineSvgOut, offlineSvgTemplate());\r\n }\r\n\r\n const sw = buildServiceWorkerJS(options);\r\n writeFileSafe(swOut, sw);\r\n\r\n console.log(`[offline-page-kit] Generated:\r\n- ${swOut}\r\n- ${offlineHtmlOut}\r\n- ${offlineSvgOut}\r\n`);\r\n} else {\r\n console.log(`[offline-page-kit] Unknown command: ${cmd}\r\nUse:\r\n offline-page-kit init --outDir public\r\n offline-page-kit build --outDir public\r\n`);\r\n}","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport function ensureDir(p: string) {\r\n fs.mkdirSync(p, { recursive: true });\r\n}\r\n\r\nexport function writeFileSafe(filePath: string, content: string) {\r\n ensureDir(path.dirname(filePath));\r\n fs.writeFileSync(filePath, content, \"utf8\");\r\n}\r\n\r\nexport function exists(filePath: string) {\r\n try { fs.accessSync(filePath); return true; } catch { return false; }\r\n}\r\n\r\nexport function parseArgs(argv: string[]) {\r\n const m = new Map<string, string>();\r\n for (let i = 0; i < argv.length; i++) {\r\n const a = argv[i];\r\n if (!a.startsWith(\"--\")) continue;\r\n const key = a.slice(2);\r\n const value = argv[i + 1] && !argv[i + 1].startsWith(\"--\") ? argv[++i] : \"true\";\r\n m.set(key, value);\r\n }\r\n return m;\r\n}\r\n\r\nexport function splitList(v: string | undefined) {\r\n return (v || \"\")\r\n .split(\",\")\r\n .map(s => s.trim())\r\n .filter(Boolean);\r\n}","export function offlineHtmlTemplate(title = \"You're Offline\") {\r\n return `<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\"/>\r\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\r\n <title>${title}</title>\r\n <style>\r\n body{margin:0;height:100vh;display:grid;place-items:center;font-family:system-ui,-apple-system,Segoe UI,Roboto;background:#0b0f19;color:#fff}\r\n .box{max-width:560px;padding:28px 26px;border-radius:18px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04)}\r\n h1{margin:0 0 10px;font-size:28px}\r\n p{margin:0 0 18px;opacity:.85;line-height:1.5}\r\n .row{display:flex;gap:10px;flex-wrap:wrap}\r\n button,a{appearance:none;border:0;border-radius:12px;padding:10px 14px;cursor:pointer;text-decoration:none}\r\n button{background:#fff;color:#111}\r\n a{background:rgba(255,255,255,.1);color:#fff}\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"box\">\r\n <h1>⚡ You’re Offline</h1>\r\n <p>No internet connection detected. You can retry, or go back to the homepage (if cached).</p>\r\n <div class=\"row\">\r\n <button onclick=\"location.reload()\">Retry</button>\r\n <a href=\"/\">Go Home</a>\r\n </div>\r\n </div>\r\n</body>\r\n</html>`;\r\n}","export function offlineSvgTemplate() {\r\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\" viewBox=\"0 0 1200 630\">\r\n <rect width=\"1200\" height=\"630\" fill=\"#0b0f19\"/>\r\n <text x=\"80\" y=\"220\" fill=\"#ffffff\" font-size=\"64\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">You’re Offline</text>\r\n <text x=\"80\" y=\"300\" fill=\"#cbd5e1\" font-size=\"28\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">Please check your connection and try again.</text>\r\n <circle cx=\"1040\" cy=\"220\" r=\"90\" fill=\"rgba(255,255,255,0.08)\"/>\r\n <path d=\"M980 220c40-40 120-40 160 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <path d=\"M1010 250c25-25 75-25 100 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <circle cx=\"1060\" cy=\"290\" r=\"10\" fill=\"#fff\" opacity=\"0.7\"/>\r\n</svg>`;\r\n}","import type { OfflineKitBuildOptions, Strategy } from \"../types\";\r\n\r\nconst DEFAULT_EXTS = [\r\n \"js\", \"css\", \"map\", \"ico\", \"png\", \"jpg\", \"jpeg\", \"webp\", \"svg\", \"gif\",\r\n \"woff2\", \"woff\", \"ttf\", \"eot\", \"json\", \"txt\", \"xml\", \"webmanifest\"\r\n];\r\n\r\nfunction jsString(v: unknown) {\r\n return JSON.stringify(v);\r\n}\r\n\r\nexport function buildServiceWorkerJS(options: Required<OfflineKitBuildOptions>) {\r\n const {\r\n cacheName,\r\n offlinePage,\r\n offlineImage,\r\n precache,\r\n htmlStrategy,\r\n assetStrategy,\r\n imageStrategy,\r\n assetExtensions,\r\n apiPrefixes,\r\n } = options;\r\n\r\n return `/* offline-page-kit service worker */\r\nconst CACHE_NAME = ${jsString(cacheName)};\r\nconst OFFLINE_PAGE = ${jsString(offlinePage)};\r\nconst OFFLINE_IMAGE = ${jsString(offlineImage)};\r\nconst PRECACHE = ${jsString([offlinePage, offlineImage, ...precache].filter(Boolean))};\r\n\r\nconst HTML_STRATEGY = ${jsString(htmlStrategy)};\r\nconst ASSET_STRATEGY = ${jsString(assetStrategy)};\r\nconst IMAGE_STRATEGY = ${jsString(imageStrategy)};\r\n\r\nconst ASSET_EXTS = new Set(${jsString(assetExtensions.length ? assetExtensions : DEFAULT_EXTS)});\r\nconst API_PREFIXES = ${jsString(apiPrefixes)};\r\n\r\n// helpers\r\nconst isApi = (url) => API_PREFIXES.some(p => url.href.startsWith(p) || url.pathname.startsWith(p));\r\nconst extOf = (pathname) => {\r\n const i = pathname.lastIndexOf(\".\");\r\n return i === -1 ? \"\" : pathname.slice(i+1).toLowerCase();\r\n};\r\n\r\nasync function cacheGet(req) {\r\n const cache = await caches.open(CACHE_NAME);\r\n return cache.match(req);\r\n}\r\nasync function cachePut(req, res) {\r\n const cache = await caches.open(CACHE_NAME);\r\n await cache.put(req, res);\r\n}\r\nasync function cacheDeleteOld() {\r\n const keys = await caches.keys();\r\n await Promise.all(keys.map(k => (k === CACHE_NAME ? null : caches.delete(k))));\r\n}\r\n\r\n// strategies\r\nasync function networkFirst(req, fallbackUrl) {\r\n try {\r\n const res = await fetch(req);\r\n if (res && res.ok) cachePut(req, res.clone());\r\n return res;\r\n } catch {\r\n const cached = await cacheGet(req);\r\n if (cached) return cached;\r\n if (fallbackUrl) return cacheGet(fallbackUrl);\r\n return new Response(\"Offline\", { status: 503 });\r\n }\r\n}\r\n\r\nasync function cacheFirst(req, fallbackUrl) {\r\n const cached = await cacheGet(req);\r\n if (cached) return cached;\r\n try {\r\n const res = await fetch(req);\r\n if (res && res.ok) cachePut(req, res.clone());\r\n return res;\r\n } catch {\r\n if (fallbackUrl) return cacheGet(fallbackUrl);\r\n return new Response(\"Offline\", { status: 503 });\r\n }\r\n}\r\n\r\nasync function staleWhileRevalidate(req) {\r\n const cached = await cacheGet(req);\r\n const fetchPromise = fetch(req).then(res => {\r\n if (res && res.ok) cachePut(req, res.clone());\r\n return res;\r\n }).catch(() => null);\r\n\r\n return cached || (await fetchPromise) || new Response(\"Offline\", { status: 503 });\r\n}\r\n\r\nfunction pickStrategy(name) {\r\n if (name === \"cacheFirst\") return cacheFirst;\r\n if (name === \"staleWhileRevalidate\") return staleWhileRevalidate;\r\n return networkFirst;\r\n}\r\n\r\nself.addEventListener(\"install\", (event) => {\r\n event.waitUntil((async () => {\r\n const cache = await caches.open(CACHE_NAME);\r\n await cache.addAll(PRECACHE);\r\n self.skipWaiting();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"activate\", (event) => {\r\n event.waitUntil((async () => {\r\n await cacheDeleteOld();\r\n await self.clients.claim();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"fetch\", (event) => {\r\n const req = event.request;\r\n const url = new URL(req.url);\r\n\r\n // Only handle same-origin requests (safe default)\r\n if (url.origin !== self.location.origin) return;\r\n\r\n // HTML navigations (SPA, MPA, Next, etc.)\r\n if (req.mode === \"navigate\" || (req.headers.get(\"accept\") || \"\").includes(\"text/html\")) {\r\n const fn = pickStrategy(HTML_STRATEGY);\r\n event.respondWith(fn(req, OFFLINE_PAGE));\r\n return;\r\n }\r\n\r\n // API calls (optional: you can also skip caching)\r\n if (isApi(url)) {\r\n // network-first is usually best for APIs\r\n event.respondWith(networkFirst(req));\r\n return;\r\n }\r\n\r\n // Images\r\n const ext = extOf(url.pathname);\r\n if ([\"png\",\"jpg\",\"jpeg\",\"webp\",\"gif\",\"svg\",\"ico\"].includes(ext)) {\r\n const fn = pickStrategy(IMAGE_STRATEGY);\r\n event.respondWith(fn(req, OFFLINE_IMAGE));\r\n return;\r\n }\r\n\r\n // Static assets (js/css/fonts/etc.)\r\n if (ASSET_EXTS.has(ext)) {\r\n const fn = pickStrategy(ASSET_STRATEGY);\r\n // staleWhileRevalidate doesn't use fallback\r\n event.respondWith(fn === staleWhileRevalidate ? staleWhileRevalidate(req) : fn(req));\r\n return;\r\n }\r\n});\r\n`;\r\n}"],"mappings":"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","names":["import_node_path","fs","path","options","path"]}
package/dist/cli.d.cts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import path2 from "path";
5
+
6
+ // src/core/utils.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+ function ensureDir(p) {
10
+ fs.mkdirSync(p, { recursive: true });
11
+ }
12
+ function writeFileSafe(filePath, content) {
13
+ ensureDir(path.dirname(filePath));
14
+ fs.writeFileSync(filePath, content, "utf8");
15
+ }
16
+ function exists(filePath) {
17
+ try {
18
+ fs.accessSync(filePath);
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+ function parseArgs(argv) {
25
+ const m = /* @__PURE__ */ new Map();
26
+ for (let i = 0; i < argv.length; i++) {
27
+ const a = argv[i];
28
+ if (!a.startsWith("--")) continue;
29
+ const key = a.slice(2);
30
+ const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
31
+ m.set(key, value);
32
+ }
33
+ return m;
34
+ }
35
+ function splitList(v) {
36
+ return (v || "").split(",").map((s) => s.trim()).filter(Boolean);
37
+ }
38
+
39
+ // src/core/offlineHtml.ts
40
+ function offlineHtmlTemplate(title = "You're Offline") {
41
+ return `<!doctype html>
42
+ <html lang="en">
43
+ <head>
44
+ <meta charset="UTF-8"/>
45
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
46
+ <title>${title}</title>
47
+ <style>
48
+ body{margin:0;height:100vh;display:grid;place-items:center;font-family:system-ui,-apple-system,Segoe UI,Roboto;background:#0b0f19;color:#fff}
49
+ .box{max-width:560px;padding:28px 26px;border-radius:18px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04)}
50
+ h1{margin:0 0 10px;font-size:28px}
51
+ p{margin:0 0 18px;opacity:.85;line-height:1.5}
52
+ .row{display:flex;gap:10px;flex-wrap:wrap}
53
+ button,a{appearance:none;border:0;border-radius:12px;padding:10px 14px;cursor:pointer;text-decoration:none}
54
+ button{background:#fff;color:#111}
55
+ a{background:rgba(255,255,255,.1);color:#fff}
56
+ </style>
57
+ </head>
58
+ <body>
59
+ <div class="box">
60
+ <h1>\u26A1 You\u2019re Offline</h1>
61
+ <p>No internet connection detected. You can retry, or go back to the homepage (if cached).</p>
62
+ <div class="row">
63
+ <button onclick="location.reload()">Retry</button>
64
+ <a href="/">Go Home</a>
65
+ </div>
66
+ </div>
67
+ </body>
68
+ </html>`;
69
+ }
70
+
71
+ // src/core/offlineSvg.ts
72
+ function offlineSvgTemplate() {
73
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
74
+ <rect width="1200" height="630" fill="#0b0f19"/>
75
+ <text x="80" y="220" fill="#ffffff" font-size="64" font-family="system-ui, -apple-system, Segoe UI, Roboto">You\u2019re Offline</text>
76
+ <text x="80" y="300" fill="#cbd5e1" font-size="28" font-family="system-ui, -apple-system, Segoe UI, Roboto">Please check your connection and try again.</text>
77
+ <circle cx="1040" cy="220" r="90" fill="rgba(255,255,255,0.08)"/>
78
+ <path d="M980 220c40-40 120-40 160 0" stroke="#fff" stroke-width="10" fill="none" opacity="0.6"/>
79
+ <path d="M1010 250c25-25 75-25 100 0" stroke="#fff" stroke-width="10" fill="none" opacity="0.6"/>
80
+ <circle cx="1060" cy="290" r="10" fill="#fff" opacity="0.7"/>
81
+ </svg>`;
82
+ }
83
+
84
+ // src/core/swTemplate.ts
85
+ var DEFAULT_EXTS = [
86
+ "js",
87
+ "css",
88
+ "map",
89
+ "ico",
90
+ "png",
91
+ "jpg",
92
+ "jpeg",
93
+ "webp",
94
+ "svg",
95
+ "gif",
96
+ "woff2",
97
+ "woff",
98
+ "ttf",
99
+ "eot",
100
+ "json",
101
+ "txt",
102
+ "xml",
103
+ "webmanifest"
104
+ ];
105
+ function jsString(v) {
106
+ return JSON.stringify(v);
107
+ }
108
+ function buildServiceWorkerJS(options2) {
109
+ const {
110
+ cacheName,
111
+ offlinePage,
112
+ offlineImage,
113
+ precache,
114
+ htmlStrategy,
115
+ assetStrategy,
116
+ imageStrategy,
117
+ assetExtensions,
118
+ apiPrefixes
119
+ } = options2;
120
+ return `/* offline-page-kit service worker */
121
+ const CACHE_NAME = ${jsString(cacheName)};
122
+ const OFFLINE_PAGE = ${jsString(offlinePage)};
123
+ const OFFLINE_IMAGE = ${jsString(offlineImage)};
124
+ const PRECACHE = ${jsString([offlinePage, offlineImage, ...precache].filter(Boolean))};
125
+
126
+ const HTML_STRATEGY = ${jsString(htmlStrategy)};
127
+ const ASSET_STRATEGY = ${jsString(assetStrategy)};
128
+ const IMAGE_STRATEGY = ${jsString(imageStrategy)};
129
+
130
+ const ASSET_EXTS = new Set(${jsString(assetExtensions.length ? assetExtensions : DEFAULT_EXTS)});
131
+ const API_PREFIXES = ${jsString(apiPrefixes)};
132
+
133
+ // helpers
134
+ const isApi = (url) => API_PREFIXES.some(p => url.href.startsWith(p) || url.pathname.startsWith(p));
135
+ const extOf = (pathname) => {
136
+ const i = pathname.lastIndexOf(".");
137
+ return i === -1 ? "" : pathname.slice(i+1).toLowerCase();
138
+ };
139
+
140
+ async function cacheGet(req) {
141
+ const cache = await caches.open(CACHE_NAME);
142
+ return cache.match(req);
143
+ }
144
+ async function cachePut(req, res) {
145
+ const cache = await caches.open(CACHE_NAME);
146
+ await cache.put(req, res);
147
+ }
148
+ async function cacheDeleteOld() {
149
+ const keys = await caches.keys();
150
+ await Promise.all(keys.map(k => (k === CACHE_NAME ? null : caches.delete(k))));
151
+ }
152
+
153
+ // strategies
154
+ async function networkFirst(req, fallbackUrl) {
155
+ try {
156
+ const res = await fetch(req);
157
+ if (res && res.ok) cachePut(req, res.clone());
158
+ return res;
159
+ } catch {
160
+ const cached = await cacheGet(req);
161
+ if (cached) return cached;
162
+ if (fallbackUrl) return cacheGet(fallbackUrl);
163
+ return new Response("Offline", { status: 503 });
164
+ }
165
+ }
166
+
167
+ async function cacheFirst(req, fallbackUrl) {
168
+ const cached = await cacheGet(req);
169
+ if (cached) return cached;
170
+ try {
171
+ const res = await fetch(req);
172
+ if (res && res.ok) cachePut(req, res.clone());
173
+ return res;
174
+ } catch {
175
+ if (fallbackUrl) return cacheGet(fallbackUrl);
176
+ return new Response("Offline", { status: 503 });
177
+ }
178
+ }
179
+
180
+ async function staleWhileRevalidate(req) {
181
+ const cached = await cacheGet(req);
182
+ const fetchPromise = fetch(req).then(res => {
183
+ if (res && res.ok) cachePut(req, res.clone());
184
+ return res;
185
+ }).catch(() => null);
186
+
187
+ return cached || (await fetchPromise) || new Response("Offline", { status: 503 });
188
+ }
189
+
190
+ function pickStrategy(name) {
191
+ if (name === "cacheFirst") return cacheFirst;
192
+ if (name === "staleWhileRevalidate") return staleWhileRevalidate;
193
+ return networkFirst;
194
+ }
195
+
196
+ self.addEventListener("install", (event) => {
197
+ event.waitUntil((async () => {
198
+ const cache = await caches.open(CACHE_NAME);
199
+ await cache.addAll(PRECACHE);
200
+ self.skipWaiting();
201
+ })());
202
+ });
203
+
204
+ self.addEventListener("activate", (event) => {
205
+ event.waitUntil((async () => {
206
+ await cacheDeleteOld();
207
+ await self.clients.claim();
208
+ })());
209
+ });
210
+
211
+ self.addEventListener("fetch", (event) => {
212
+ const req = event.request;
213
+ const url = new URL(req.url);
214
+
215
+ // Only handle same-origin requests (safe default)
216
+ if (url.origin !== self.location.origin) return;
217
+
218
+ // HTML navigations (SPA, MPA, Next, etc.)
219
+ if (req.mode === "navigate" || (req.headers.get("accept") || "").includes("text/html")) {
220
+ const fn = pickStrategy(HTML_STRATEGY);
221
+ event.respondWith(fn(req, OFFLINE_PAGE));
222
+ return;
223
+ }
224
+
225
+ // API calls (optional: you can also skip caching)
226
+ if (isApi(url)) {
227
+ // network-first is usually best for APIs
228
+ event.respondWith(networkFirst(req));
229
+ return;
230
+ }
231
+
232
+ // Images
233
+ const ext = extOf(url.pathname);
234
+ if (["png","jpg","jpeg","webp","gif","svg","ico"].includes(ext)) {
235
+ const fn = pickStrategy(IMAGE_STRATEGY);
236
+ event.respondWith(fn(req, OFFLINE_IMAGE));
237
+ return;
238
+ }
239
+
240
+ // Static assets (js/css/fonts/etc.)
241
+ if (ASSET_EXTS.has(ext)) {
242
+ const fn = pickStrategy(ASSET_STRATEGY);
243
+ // staleWhileRevalidate doesn't use fallback
244
+ event.respondWith(fn === staleWhileRevalidate ? staleWhileRevalidate(req) : fn(req));
245
+ return;
246
+ }
247
+ });
248
+ `;
249
+ }
250
+
251
+ // src/cli.ts
252
+ function normalizePublicPath(p) {
253
+ if (!p.startsWith("/")) return "/" + p;
254
+ return p;
255
+ }
256
+ function withDefaults(o) {
257
+ return {
258
+ outDir: o.outDir ?? "public",
259
+ swFileName: o.swFileName ?? "sw.js",
260
+ offlinePage: normalizePublicPath(o.offlinePage ?? "/offline.html"),
261
+ offlineImage: normalizePublicPath(o.offlineImage ?? "/offline.svg"),
262
+ cacheName: o.cacheName ?? "offline-page-kit",
263
+ precache: o.precache ?? [],
264
+ htmlStrategy: o.htmlStrategy ?? "networkFirst",
265
+ assetStrategy: o.assetStrategy ?? "staleWhileRevalidate",
266
+ imageStrategy: o.imageStrategy ?? "cacheFirst",
267
+ assetExtensions: o.assetExtensions ?? [],
268
+ apiPrefixes: o.apiPrefixes ?? []
269
+ };
270
+ }
271
+ var args = parseArgs(process.argv.slice(2));
272
+ var cmd = process.argv.slice(2).find((a) => !a.startsWith("--")) || "init";
273
+ var outDir = path2.resolve(process.cwd(), args.get("outDir") || "public");
274
+ var options = withDefaults({
275
+ outDir,
276
+ swFileName: args.get("swFileName") || "sw.js",
277
+ offlinePage: args.get("offlinePage") || "/offline.html",
278
+ offlineImage: args.get("offlineImage") || "/offline.svg",
279
+ cacheName: args.get("cacheName") || "offline-page-kit",
280
+ precache: splitList(args.get("precache")),
281
+ htmlStrategy: args.get("htmlStrategy") || "networkFirst",
282
+ assetStrategy: args.get("assetStrategy") || "staleWhileRevalidate",
283
+ imageStrategy: args.get("imageStrategy") || "cacheFirst",
284
+ assetExtensions: splitList(args.get("assetExtensions")),
285
+ apiPrefixes: splitList(args.get("apiPrefixes"))
286
+ });
287
+ var swOut = path2.join(outDir, options.swFileName);
288
+ var offlineHtmlOut = path2.join(outDir, options.offlinePage.replace(/^\//, ""));
289
+ var offlineSvgOut = path2.join(outDir, options.offlineImage.replace(/^\//, ""));
290
+ if (cmd === "init" || cmd === "build") {
291
+ if (cmd === "build" || !exists(offlineHtmlOut)) {
292
+ writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());
293
+ }
294
+ if (cmd === "build" || !exists(offlineSvgOut)) {
295
+ writeFileSafe(offlineSvgOut, offlineSvgTemplate());
296
+ }
297
+ const sw = buildServiceWorkerJS(options);
298
+ writeFileSafe(swOut, sw);
299
+ console.log(`[offline-page-kit] Generated:
300
+ - ${swOut}
301
+ - ${offlineHtmlOut}
302
+ - ${offlineSvgOut}
303
+ `);
304
+ } else {
305
+ console.log(`[offline-page-kit] Unknown command: ${cmd}
306
+ Use:
307
+ offline-page-kit init --outDir public
308
+ offline-page-kit build --outDir public
309
+ `);
310
+ }
311
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/core/utils.ts","../src/core/offlineHtml.ts","../src/core/offlineSvg.ts","../src/core/swTemplate.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport path from \"node:path\";\r\nimport { parseArgs, splitList, writeFileSafe, exists } from \"./core/utils\";\r\nimport { offlineHtmlTemplate } from \"./core/offlineHtml\";\r\nimport { offlineSvgTemplate } from \"./core/offlineSvg\";\r\nimport { buildServiceWorkerJS } from \"./core/swTemplate\";\r\nimport type { OfflineKitBuildOptions } from \"./types\";\r\n\r\nfunction normalizePublicPath(p: string) {\r\n if (!p.startsWith(\"/\")) return \"/\" + p;\r\n return p;\r\n}\r\n\r\nfunction withDefaults(o: OfflineKitBuildOptions): Required<OfflineKitBuildOptions> {\r\n return {\r\n outDir: o.outDir ?? \"public\",\r\n swFileName: o.swFileName ?? \"sw.js\",\r\n offlinePage: normalizePublicPath(o.offlinePage ?? \"/offline.html\"),\r\n offlineImage: normalizePublicPath(o.offlineImage ?? \"/offline.svg\"),\r\n cacheName: o.cacheName ?? \"offline-page-kit\",\r\n precache: o.precache ?? [],\r\n htmlStrategy: o.htmlStrategy ?? \"networkFirst\",\r\n assetStrategy: o.assetStrategy ?? \"staleWhileRevalidate\",\r\n imageStrategy: o.imageStrategy ?? \"cacheFirst\",\r\n assetExtensions: o.assetExtensions ?? [],\r\n apiPrefixes: o.apiPrefixes ?? [],\r\n };\r\n}\r\n\r\nconst args = parseArgs(process.argv.slice(2));\r\nconst cmd = process.argv.slice(2).find(a => !a.startsWith(\"--\")) || \"init\";\r\n\r\nconst outDir = path.resolve(process.cwd(), args.get(\"outDir\") || \"public\");\r\n\r\nconst options = withDefaults({\r\n outDir,\r\n swFileName: args.get(\"swFileName\") || \"sw.js\",\r\n offlinePage: args.get(\"offlinePage\") || \"/offline.html\",\r\n offlineImage: args.get(\"offlineImage\") || \"/offline.svg\",\r\n cacheName: args.get(\"cacheName\") || \"offline-page-kit\",\r\n precache: splitList(args.get(\"precache\")),\r\n htmlStrategy: (args.get(\"htmlStrategy\") as any) || \"networkFirst\",\r\n assetStrategy: (args.get(\"assetStrategy\") as any) || \"staleWhileRevalidate\",\r\n imageStrategy: (args.get(\"imageStrategy\") as any) || \"cacheFirst\",\r\n assetExtensions: splitList(args.get(\"assetExtensions\")),\r\n apiPrefixes: splitList(args.get(\"apiPrefixes\")),\r\n} as OfflineKitBuildOptions);\r\n\r\nconst swOut = path.join(outDir, options.swFileName);\r\nconst offlineHtmlOut = path.join(outDir, options.offlinePage.replace(/^\\//, \"\"));\r\nconst offlineSvgOut = path.join(outDir, options.offlineImage.replace(/^\\//, \"\"));\r\n\r\nif (cmd === \"init\" || cmd === \"build\") {\r\n // generate offline page if missing OR force (when build)\r\n if (cmd === \"build\" || !exists(offlineHtmlOut)) {\r\n writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());\r\n }\r\n if (cmd === \"build\" || !exists(offlineSvgOut)) {\r\n writeFileSafe(offlineSvgOut, offlineSvgTemplate());\r\n }\r\n\r\n const sw = buildServiceWorkerJS(options);\r\n writeFileSafe(swOut, sw);\r\n\r\n console.log(`[offline-page-kit] Generated:\r\n- ${swOut}\r\n- ${offlineHtmlOut}\r\n- ${offlineSvgOut}\r\n`);\r\n} else {\r\n console.log(`[offline-page-kit] Unknown command: ${cmd}\r\nUse:\r\n offline-page-kit init --outDir public\r\n offline-page-kit build --outDir public\r\n`);\r\n}","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport function ensureDir(p: string) {\r\n fs.mkdirSync(p, { recursive: true });\r\n}\r\n\r\nexport function writeFileSafe(filePath: string, content: string) {\r\n ensureDir(path.dirname(filePath));\r\n fs.writeFileSync(filePath, content, \"utf8\");\r\n}\r\n\r\nexport function exists(filePath: string) {\r\n try { fs.accessSync(filePath); return true; } catch { return false; }\r\n}\r\n\r\nexport function parseArgs(argv: string[]) {\r\n const m = new Map<string, string>();\r\n for (let i = 0; i < argv.length; i++) {\r\n const a = argv[i];\r\n if (!a.startsWith(\"--\")) continue;\r\n const key = a.slice(2);\r\n const value = argv[i + 1] && !argv[i + 1].startsWith(\"--\") ? argv[++i] : \"true\";\r\n m.set(key, value);\r\n }\r\n return m;\r\n}\r\n\r\nexport function splitList(v: string | undefined) {\r\n return (v || \"\")\r\n .split(\",\")\r\n .map(s => s.trim())\r\n .filter(Boolean);\r\n}","export function offlineHtmlTemplate(title = \"You're Offline\") {\r\n return `<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\"/>\r\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\r\n <title>${title}</title>\r\n <style>\r\n body{margin:0;height:100vh;display:grid;place-items:center;font-family:system-ui,-apple-system,Segoe UI,Roboto;background:#0b0f19;color:#fff}\r\n .box{max-width:560px;padding:28px 26px;border-radius:18px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04)}\r\n h1{margin:0 0 10px;font-size:28px}\r\n p{margin:0 0 18px;opacity:.85;line-height:1.5}\r\n .row{display:flex;gap:10px;flex-wrap:wrap}\r\n button,a{appearance:none;border:0;border-radius:12px;padding:10px 14px;cursor:pointer;text-decoration:none}\r\n button{background:#fff;color:#111}\r\n a{background:rgba(255,255,255,.1);color:#fff}\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"box\">\r\n <h1>⚡ You’re Offline</h1>\r\n <p>No internet connection detected. You can retry, or go back to the homepage (if cached).</p>\r\n <div class=\"row\">\r\n <button onclick=\"location.reload()\">Retry</button>\r\n <a href=\"/\">Go Home</a>\r\n </div>\r\n </div>\r\n</body>\r\n</html>`;\r\n}","export function offlineSvgTemplate() {\r\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\" viewBox=\"0 0 1200 630\">\r\n <rect width=\"1200\" height=\"630\" fill=\"#0b0f19\"/>\r\n <text x=\"80\" y=\"220\" fill=\"#ffffff\" font-size=\"64\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">You’re Offline</text>\r\n <text x=\"80\" y=\"300\" fill=\"#cbd5e1\" font-size=\"28\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">Please check your connection and try again.</text>\r\n <circle cx=\"1040\" cy=\"220\" r=\"90\" fill=\"rgba(255,255,255,0.08)\"/>\r\n <path d=\"M980 220c40-40 120-40 160 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <path d=\"M1010 250c25-25 75-25 100 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <circle cx=\"1060\" cy=\"290\" r=\"10\" fill=\"#fff\" opacity=\"0.7\"/>\r\n</svg>`;\r\n}","import type { OfflineKitBuildOptions, Strategy } from \"../types\";\r\n\r\nconst DEFAULT_EXTS = [\r\n \"js\", \"css\", \"map\", \"ico\", \"png\", \"jpg\", \"jpeg\", \"webp\", \"svg\", \"gif\",\r\n \"woff2\", \"woff\", \"ttf\", \"eot\", \"json\", \"txt\", \"xml\", \"webmanifest\"\r\n];\r\n\r\nfunction jsString(v: unknown) {\r\n return JSON.stringify(v);\r\n}\r\n\r\nexport function buildServiceWorkerJS(options: Required<OfflineKitBuildOptions>) {\r\n const {\r\n cacheName,\r\n offlinePage,\r\n offlineImage,\r\n precache,\r\n htmlStrategy,\r\n assetStrategy,\r\n imageStrategy,\r\n assetExtensions,\r\n apiPrefixes,\r\n } = options;\r\n\r\n return `/* offline-page-kit service worker */\r\nconst CACHE_NAME = ${jsString(cacheName)};\r\nconst OFFLINE_PAGE = ${jsString(offlinePage)};\r\nconst OFFLINE_IMAGE = ${jsString(offlineImage)};\r\nconst PRECACHE = ${jsString([offlinePage, offlineImage, ...precache].filter(Boolean))};\r\n\r\nconst HTML_STRATEGY = ${jsString(htmlStrategy)};\r\nconst ASSET_STRATEGY = ${jsString(assetStrategy)};\r\nconst IMAGE_STRATEGY = ${jsString(imageStrategy)};\r\n\r\nconst ASSET_EXTS = new Set(${jsString(assetExtensions.length ? assetExtensions : DEFAULT_EXTS)});\r\nconst API_PREFIXES = ${jsString(apiPrefixes)};\r\n\r\n// helpers\r\nconst isApi = (url) => API_PREFIXES.some(p => url.href.startsWith(p) || url.pathname.startsWith(p));\r\nconst extOf = (pathname) => {\r\n const i = pathname.lastIndexOf(\".\");\r\n return i === -1 ? \"\" : pathname.slice(i+1).toLowerCase();\r\n};\r\n\r\nasync function cacheGet(req) {\r\n const cache = await caches.open(CACHE_NAME);\r\n return cache.match(req);\r\n}\r\nasync function cachePut(req, res) {\r\n const cache = await caches.open(CACHE_NAME);\r\n await cache.put(req, res);\r\n}\r\nasync function cacheDeleteOld() {\r\n const keys = await caches.keys();\r\n await Promise.all(keys.map(k => (k === CACHE_NAME ? null : caches.delete(k))));\r\n}\r\n\r\n// strategies\r\nasync function networkFirst(req, fallbackUrl) {\r\n try {\r\n const res = await fetch(req);\r\n if (res && res.ok) cachePut(req, res.clone());\r\n return res;\r\n } catch {\r\n const cached = await cacheGet(req);\r\n if (cached) return cached;\r\n if (fallbackUrl) return cacheGet(fallbackUrl);\r\n return new Response(\"Offline\", { status: 503 });\r\n }\r\n}\r\n\r\nasync function cacheFirst(req, fallbackUrl) {\r\n const cached = await cacheGet(req);\r\n if (cached) return cached;\r\n try {\r\n const res = await fetch(req);\r\n if (res && res.ok) cachePut(req, res.clone());\r\n return res;\r\n } catch {\r\n if (fallbackUrl) return cacheGet(fallbackUrl);\r\n return new Response(\"Offline\", { status: 503 });\r\n }\r\n}\r\n\r\nasync function staleWhileRevalidate(req) {\r\n const cached = await cacheGet(req);\r\n const fetchPromise = fetch(req).then(res => {\r\n if (res && res.ok) cachePut(req, res.clone());\r\n return res;\r\n }).catch(() => null);\r\n\r\n return cached || (await fetchPromise) || new Response(\"Offline\", { status: 503 });\r\n}\r\n\r\nfunction pickStrategy(name) {\r\n if (name === \"cacheFirst\") return cacheFirst;\r\n if (name === \"staleWhileRevalidate\") return staleWhileRevalidate;\r\n return networkFirst;\r\n}\r\n\r\nself.addEventListener(\"install\", (event) => {\r\n event.waitUntil((async () => {\r\n const cache = await caches.open(CACHE_NAME);\r\n await cache.addAll(PRECACHE);\r\n self.skipWaiting();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"activate\", (event) => {\r\n event.waitUntil((async () => {\r\n await cacheDeleteOld();\r\n await self.clients.claim();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"fetch\", (event) => {\r\n const req = event.request;\r\n const url = new URL(req.url);\r\n\r\n // Only handle same-origin requests (safe default)\r\n if (url.origin !== self.location.origin) return;\r\n\r\n // HTML navigations (SPA, MPA, Next, etc.)\r\n if (req.mode === \"navigate\" || (req.headers.get(\"accept\") || \"\").includes(\"text/html\")) {\r\n const fn = pickStrategy(HTML_STRATEGY);\r\n event.respondWith(fn(req, OFFLINE_PAGE));\r\n return;\r\n }\r\n\r\n // API calls (optional: you can also skip caching)\r\n if (isApi(url)) {\r\n // network-first is usually best for APIs\r\n event.respondWith(networkFirst(req));\r\n return;\r\n }\r\n\r\n // Images\r\n const ext = extOf(url.pathname);\r\n if ([\"png\",\"jpg\",\"jpeg\",\"webp\",\"gif\",\"svg\",\"ico\"].includes(ext)) {\r\n const fn = pickStrategy(IMAGE_STRATEGY);\r\n event.respondWith(fn(req, OFFLINE_IMAGE));\r\n return;\r\n }\r\n\r\n // Static assets (js/css/fonts/etc.)\r\n if (ASSET_EXTS.has(ext)) {\r\n const fn = pickStrategy(ASSET_STRATEGY);\r\n // staleWhileRevalidate doesn't use fallback\r\n event.respondWith(fn === staleWhileRevalidate ? staleWhileRevalidate(req) : fn(req));\r\n return;\r\n }\r\n});\r\n`;\r\n}"],"mappings":"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","names":["path","options","path"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ registerOfflineKit: () => registerOfflineKit
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/register.ts
28
+ function registerOfflineKit(opts = {}) {
29
+ const {
30
+ swUrl = "/sw.js",
31
+ scope = "/",
32
+ debug = false,
33
+ onReady,
34
+ onUpdate,
35
+ onError
36
+ } = opts;
37
+ if (typeof window === "undefined") return;
38
+ if (!("serviceWorker" in navigator)) {
39
+ debug && console.warn("[offline-kit] Service Worker not supported");
40
+ return;
41
+ }
42
+ window.addEventListener("load", async () => {
43
+ try {
44
+ const reg = await navigator.serviceWorker.register(swUrl, { scope });
45
+ debug && console.log("[offline-kit] registered:", { swUrl, scope });
46
+ onReady?.(reg);
47
+ reg.addEventListener("updatefound", () => {
48
+ const installing = reg.installing;
49
+ if (!installing) return;
50
+ installing.addEventListener("statechange", () => {
51
+ if (installing.state !== "installed") return;
52
+ if (navigator.serviceWorker.controller) {
53
+ debug && console.log("[offline-kit] update available");
54
+ onUpdate?.(reg);
55
+ } else {
56
+ debug && console.log("[offline-kit] first install complete");
57
+ }
58
+ });
59
+ });
60
+ } catch (err) {
61
+ debug && console.error("[offline-kit] register failed:", err);
62
+ onError?.(err);
63
+ }
64
+ });
65
+ }
66
+ // Annotate the CommonJS export names for ESM import in node:
67
+ 0 && (module.exports = {
68
+ registerOfflineKit
69
+ });
70
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/register.ts"],"sourcesContent":["export { registerOfflineKit } from \"./register\";\r\nexport type { OfflineKitRegisterOptions, OfflineKitBuildOptions, Strategy } from \"./types\";","\r\nimport type { OfflineKitRegisterOptions } from \"./types\";\r\n\r\nexport function registerOfflineKit(opts: OfflineKitRegisterOptions = {}) {\r\n const {\r\n swUrl = \"/sw.js\",\r\n scope = \"/\",\r\n debug = false,\r\n onReady,\r\n onUpdate,\r\n onError,\r\n } = opts;\r\n\r\n if (typeof window === \"undefined\") return;\r\n if (!(\"serviceWorker\" in navigator)) {\r\n debug && console.warn(\"[offline-kit] Service Worker not supported\");\r\n return;\r\n }\r\n\r\n window.addEventListener(\"load\", async () => {\r\n try {\r\n const reg = await navigator.serviceWorker.register(swUrl, { scope });\r\n debug && console.log(\"[offline-kit] registered:\", { swUrl, scope });\r\n\r\n onReady?.(reg);\r\n\r\n reg.addEventListener(\"updatefound\", () => {\r\n const installing = reg.installing;\r\n if (!installing) return;\r\n\r\n installing.addEventListener(\"statechange\", () => {\r\n if (installing.state !== \"installed\") return;\r\n\r\n if (navigator.serviceWorker.controller) {\r\n debug && console.log(\"[offline-kit] update available\");\r\n onUpdate?.(reg);\r\n } else {\r\n debug && console.log(\"[offline-kit] first install complete\");\r\n }\r\n });\r\n });\r\n } catch (err) {\r\n debug && console.error(\"[offline-kit] register failed:\", err);\r\n onError?.(err);\r\n }\r\n });\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,SAAS,mBAAmB,OAAkC,CAAC,GAAG;AACrE,QAAM;AAAA,IACF,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACJ,IAAI;AAEJ,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,EAAE,mBAAmB,YAAY;AACjC,aAAS,QAAQ,KAAK,4CAA4C;AAClE;AAAA,EACJ;AAEA,SAAO,iBAAiB,QAAQ,YAAY;AACxC,QAAI;AACA,YAAM,MAAM,MAAM,UAAU,cAAc,SAAS,OAAO,EAAE,MAAM,CAAC;AACnE,eAAS,QAAQ,IAAI,6BAA6B,EAAE,OAAO,MAAM,CAAC;AAElE,gBAAU,GAAG;AAEb,UAAI,iBAAiB,eAAe,MAAM;AACtC,cAAM,aAAa,IAAI;AACvB,YAAI,CAAC,WAAY;AAEjB,mBAAW,iBAAiB,eAAe,MAAM;AAC7C,cAAI,WAAW,UAAU,YAAa;AAEtC,cAAI,UAAU,cAAc,YAAY;AACpC,qBAAS,QAAQ,IAAI,gCAAgC;AACrD,uBAAW,GAAG;AAAA,UAClB,OAAO;AACH,qBAAS,QAAQ,IAAI,sCAAsC;AAAA,UAC/D;AAAA,QACJ,CAAC;AAAA,MACL,CAAC;AAAA,IACL,SAAS,KAAK;AACV,eAAS,QAAQ,MAAM,kCAAkC,GAAG;AAC5D,gBAAU,GAAG;AAAA,IACjB;AAAA,EACJ,CAAC;AACL;","names":[]}
@@ -0,0 +1,26 @@
1
+ type Strategy = "networkFirst" | "cacheFirst" | "staleWhileRevalidate";
2
+ type OfflineKitRegisterOptions = {
3
+ swUrl?: string;
4
+ scope?: string;
5
+ debug?: boolean;
6
+ onReady?: (reg: ServiceWorkerRegistration) => void;
7
+ onUpdate?: (reg: ServiceWorkerRegistration) => void;
8
+ onError?: (err: unknown) => void;
9
+ };
10
+ type OfflineKitBuildOptions = {
11
+ outDir?: string;
12
+ swFileName?: string;
13
+ offlinePage?: string;
14
+ offlineImage?: string;
15
+ cacheName?: string;
16
+ precache?: string[];
17
+ htmlStrategy?: Strategy;
18
+ assetStrategy?: Strategy;
19
+ imageStrategy?: Strategy;
20
+ assetExtensions?: string[];
21
+ apiPrefixes?: string[];
22
+ };
23
+
24
+ declare function registerOfflineKit(opts?: OfflineKitRegisterOptions): void;
25
+
26
+ export { type OfflineKitBuildOptions, type OfflineKitRegisterOptions, type Strategy, registerOfflineKit };
@@ -0,0 +1,26 @@
1
+ type Strategy = "networkFirst" | "cacheFirst" | "staleWhileRevalidate";
2
+ type OfflineKitRegisterOptions = {
3
+ swUrl?: string;
4
+ scope?: string;
5
+ debug?: boolean;
6
+ onReady?: (reg: ServiceWorkerRegistration) => void;
7
+ onUpdate?: (reg: ServiceWorkerRegistration) => void;
8
+ onError?: (err: unknown) => void;
9
+ };
10
+ type OfflineKitBuildOptions = {
11
+ outDir?: string;
12
+ swFileName?: string;
13
+ offlinePage?: string;
14
+ offlineImage?: string;
15
+ cacheName?: string;
16
+ precache?: string[];
17
+ htmlStrategy?: Strategy;
18
+ assetStrategy?: Strategy;
19
+ imageStrategy?: Strategy;
20
+ assetExtensions?: string[];
21
+ apiPrefixes?: string[];
22
+ };
23
+
24
+ declare function registerOfflineKit(opts?: OfflineKitRegisterOptions): void;
25
+
26
+ export { type OfflineKitBuildOptions, type OfflineKitRegisterOptions, type Strategy, registerOfflineKit };
package/dist/index.js ADDED
@@ -0,0 +1,43 @@
1
+ // src/register.ts
2
+ function registerOfflineKit(opts = {}) {
3
+ const {
4
+ swUrl = "/sw.js",
5
+ scope = "/",
6
+ debug = false,
7
+ onReady,
8
+ onUpdate,
9
+ onError
10
+ } = opts;
11
+ if (typeof window === "undefined") return;
12
+ if (!("serviceWorker" in navigator)) {
13
+ debug && console.warn("[offline-kit] Service Worker not supported");
14
+ return;
15
+ }
16
+ window.addEventListener("load", async () => {
17
+ try {
18
+ const reg = await navigator.serviceWorker.register(swUrl, { scope });
19
+ debug && console.log("[offline-kit] registered:", { swUrl, scope });
20
+ onReady?.(reg);
21
+ reg.addEventListener("updatefound", () => {
22
+ const installing = reg.installing;
23
+ if (!installing) return;
24
+ installing.addEventListener("statechange", () => {
25
+ if (installing.state !== "installed") return;
26
+ if (navigator.serviceWorker.controller) {
27
+ debug && console.log("[offline-kit] update available");
28
+ onUpdate?.(reg);
29
+ } else {
30
+ debug && console.log("[offline-kit] first install complete");
31
+ }
32
+ });
33
+ });
34
+ } catch (err) {
35
+ debug && console.error("[offline-kit] register failed:", err);
36
+ onError?.(err);
37
+ }
38
+ });
39
+ }
40
+ export {
41
+ registerOfflineKit
42
+ };
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/register.ts"],"sourcesContent":["\r\nimport type { OfflineKitRegisterOptions } from \"./types\";\r\n\r\nexport function registerOfflineKit(opts: OfflineKitRegisterOptions = {}) {\r\n const {\r\n swUrl = \"/sw.js\",\r\n scope = \"/\",\r\n debug = false,\r\n onReady,\r\n onUpdate,\r\n onError,\r\n } = opts;\r\n\r\n if (typeof window === \"undefined\") return;\r\n if (!(\"serviceWorker\" in navigator)) {\r\n debug && console.warn(\"[offline-kit] Service Worker not supported\");\r\n return;\r\n }\r\n\r\n window.addEventListener(\"load\", async () => {\r\n try {\r\n const reg = await navigator.serviceWorker.register(swUrl, { scope });\r\n debug && console.log(\"[offline-kit] registered:\", { swUrl, scope });\r\n\r\n onReady?.(reg);\r\n\r\n reg.addEventListener(\"updatefound\", () => {\r\n const installing = reg.installing;\r\n if (!installing) return;\r\n\r\n installing.addEventListener(\"statechange\", () => {\r\n if (installing.state !== \"installed\") return;\r\n\r\n if (navigator.serviceWorker.controller) {\r\n debug && console.log(\"[offline-kit] update available\");\r\n onUpdate?.(reg);\r\n } else {\r\n debug && console.log(\"[offline-kit] first install complete\");\r\n }\r\n });\r\n });\r\n } catch (err) {\r\n debug && console.error(\"[offline-kit] register failed:\", err);\r\n onError?.(err);\r\n }\r\n });\r\n}"],"mappings":";AAGO,SAAS,mBAAmB,OAAkC,CAAC,GAAG;AACrE,QAAM;AAAA,IACF,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACJ,IAAI;AAEJ,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,EAAE,mBAAmB,YAAY;AACjC,aAAS,QAAQ,KAAK,4CAA4C;AAClE;AAAA,EACJ;AAEA,SAAO,iBAAiB,QAAQ,YAAY;AACxC,QAAI;AACA,YAAM,MAAM,MAAM,UAAU,cAAc,SAAS,OAAO,EAAE,MAAM,CAAC;AACnE,eAAS,QAAQ,IAAI,6BAA6B,EAAE,OAAO,MAAM,CAAC;AAElE,gBAAU,GAAG;AAEb,UAAI,iBAAiB,eAAe,MAAM;AACtC,cAAM,aAAa,IAAI;AACvB,YAAI,CAAC,WAAY;AAEjB,mBAAW,iBAAiB,eAAe,MAAM;AAC7C,cAAI,WAAW,UAAU,YAAa;AAEtC,cAAI,UAAU,cAAc,YAAY;AACpC,qBAAS,QAAQ,IAAI,gCAAgC;AACrD,uBAAW,GAAG;AAAA,UAClB,OAAO;AACH,qBAAS,QAAQ,IAAI,sCAAsC;AAAA,UAC/D;AAAA,QACJ,CAAC;AAAA,MACL,CAAC;AAAA,IACL,SAAS,KAAK;AACV,eAAS,QAAQ,MAAM,kCAAkC,GAAG;AAC5D,gBAAU,GAAG;AAAA,IACjB;AAAA,EACJ,CAAC;AACL;","names":[]}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "offline-page-kit",
3
+ "version": "0.1.0",
4
+ "description": "Framework-agnostic offline page + service worker generator (TypeScript)",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "bin": {
21
+ "offline-page-kit": "./dist/cli.cjs"
22
+ },
23
+ "scripts": {
24
+ "test": "echo \"Error: no test specified\" && exit 1"
25
+ },
26
+ "devDependencies": {
27
+ }
28
+ }