offline-page-kit 0.2.1 → 0.2.2

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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import path3 from "path";
4
+ import path2 from "path";
5
5
 
6
6
  // src/core/utils.ts
7
7
  import fs from "fs";
@@ -82,171 +82,23 @@ function offlineSvgTemplate() {
82
82
  }
83
83
 
84
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
- }
85
+ var js = (v) => JSON.stringify(v);
108
86
  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
- const precacheList = Array.from(
121
- new Set([offlinePage, offlineImage, ...precache ?? []].filter(Boolean))
122
- );
123
- return `/* offline-page-kit service worker */
124
- const CACHE_NAME = ${jsString(cacheName)};
125
- const OFFLINE_PAGE = ${jsString(offlinePage)};
126
- const OFFLINE_IMAGE = ${jsString(offlineImage)};
127
- const PRECACHE = ${jsString(precacheList)};
128
-
129
- const HTML_STRATEGY = ${jsString(htmlStrategy)};
130
- const ASSET_STRATEGY = ${jsString(assetStrategy)};
131
- const IMAGE_STRATEGY = ${jsString(imageStrategy)};
132
-
133
- const ASSET_EXTS = new Set(${jsString(assetExtensions.length ? assetExtensions : DEFAULT_EXTS)});
134
- const API_PREFIXES = ${jsString(apiPrefixes || [])};
135
-
136
- const SAME_ORIGIN_ONLY = true;
137
-
138
- // ---------------------------
139
- // helpers
140
- // ---------------------------
141
- const isSameOrigin = (url) => url.origin === self.location.origin;
142
-
143
- const isApi = (url) => {
144
- if (!API_PREFIXES || API_PREFIXES.length === 0) return false;
145
- return API_PREFIXES.some((p) => {
146
- try {
147
- // allow either absolute prefix or pathname prefix
148
- return url.href.startsWith(p) || url.pathname.startsWith(p);
149
- } catch {
150
- return false;
151
- }
152
- });
153
- };
154
-
155
- const extOf = (pathname) => {
156
- const i = pathname.lastIndexOf(".");
157
- return i === -1 ? "" : pathname.slice(i + 1).toLowerCase();
158
- };
159
-
160
- const isImageExt = (ext) => ["png","jpg","jpeg","webp","gif","svg","ico"].includes(ext);
161
-
162
- // Only cache successful, basic responses (avoid caching errors)
163
- const isCacheableResponse = (res) => res && (res.ok || res.type === "opaque");
164
-
165
- // Cache helpers
166
- async function cacheGet(reqOrUrl) {
167
- const cache = await caches.open(CACHE_NAME);
168
- return cache.match(reqOrUrl);
169
- }
170
-
171
- async function cachePut(req, res) {
172
- try {
173
- if (!isCacheableResponse(res)) return;
174
- const cache = await caches.open(CACHE_NAME);
175
- await cache.put(req, res);
176
- } catch {
177
- // ignore quota / put errors
178
- }
179
- }
87
+ const { cacheName, offlinePage, offlineImage } = options2;
88
+ return `/* offline-page-kit service worker (minimal) */
89
+ const CACHE_NAME = ${js(cacheName)};
90
+ const OFFLINE_PAGE = ${js(offlinePage)};
91
+ const OFFLINE_IMAGE = ${js(offlineImage)};
180
92
 
181
- async function cleanOldCaches() {
182
- const keys = await caches.keys();
183
- await Promise.all(keys.map((k) => (k === CACHE_NAME ? null : caches.delete(k))));
184
- }
185
-
186
- // ---------------------------
187
- // strategies
188
- // ---------------------------
189
- async function networkFirst(req, fallbackUrl) {
190
- try {
191
- const res = await fetch(req);
192
- await cachePut(req, res.clone());
193
- return res;
194
- } catch {
195
- const cached = await cacheGet(req);
196
- if (cached) return cached;
197
- if (fallbackUrl) {
198
- const fb = await cacheGet(fallbackUrl);
199
- if (fb) return fb;
200
- }
201
- return new Response("Offline", { status: 503, headers: { "content-type": "text/plain" } });
202
- }
203
- }
204
-
205
- async function cacheFirst(req, fallbackUrl) {
206
- const cached = await cacheGet(req);
207
- if (cached) return cached;
208
-
209
- try {
210
- const res = await fetch(req);
211
- await cachePut(req, res.clone());
212
- return res;
213
- } catch {
214
- if (fallbackUrl) {
215
- const fb = await cacheGet(fallbackUrl);
216
- if (fb) return fb;
217
- }
218
- return new Response("Offline", { status: 503, headers: { "content-type": "text/plain" } });
219
- }
220
- }
221
-
222
- async function staleWhileRevalidate(req) {
223
- const cached = await cacheGet(req);
224
-
225
- const fetchPromise = fetch(req)
226
- .then(async (res) => {
227
- await cachePut(req, res.clone());
228
- return res;
229
- })
230
- .catch(() => null);
231
-
232
- return cached || (await fetchPromise) || new Response("Offline", { status: 503 });
233
- }
234
-
235
- function pickStrategy(name) {
236
- if (name === "cacheFirst") return cacheFirst;
237
- if (name === "staleWhileRevalidate") return staleWhileRevalidate;
238
- return networkFirst;
239
- }
240
-
241
- // ---------------------------
242
- // lifecycle
243
- // ---------------------------
244
93
  self.addEventListener("install", (event) => {
245
94
  event.waitUntil((async () => {
246
95
  const cache = await caches.open(CACHE_NAME);
247
96
 
248
- // \u2705 fail-safe install (never redundant from a single bad file)
249
- await Promise.allSettled(PRECACHE.map((u) => cache.add(u)));
97
+ // \u2705 Do not let one 404 kill the install
98
+ await Promise.allSettled([
99
+ cache.add(OFFLINE_PAGE),
100
+ cache.add(OFFLINE_IMAGE),
101
+ ]);
250
102
 
251
103
  await self.skipWaiting();
252
104
  })());
@@ -254,130 +106,32 @@ self.addEventListener("install", (event) => {
254
106
 
255
107
  self.addEventListener("activate", (event) => {
256
108
  event.waitUntil((async () => {
257
- await cleanOldCaches();
258
109
  await self.clients.claim();
259
110
  })());
260
111
  });
261
112
 
262
- // Allow app to trigger update instantly:
263
- // navigator.serviceWorker.controller?.postMessage({ type: "SKIP_WAITING" })
264
- self.addEventListener("message", (event) => {
265
- if (event?.data?.type === "SKIP_WAITING") {
266
- self.skipWaiting();
267
- }
268
- });
269
-
270
- // ---------------------------
271
- // fetch routing
272
- // ---------------------------
273
113
  self.addEventListener("fetch", (event) => {
274
114
  const req = event.request;
275
115
 
276
- // Don't touch non-GET requests
277
- if (req.method && req.method !== "GET") return;
278
-
279
- const url = new URL(req.url);
280
-
281
- // Safe default: same-origin only
282
- if (SAME_ORIGIN_ONLY && !isSameOrigin(url)) return;
283
-
284
- const accept = req.headers.get("accept") || "";
285
- const ext = extOf(url.pathname);
286
-
287
- // 1) HTML / navigations -> offline page fallback
288
- const isHtml = req.mode === "navigate" || accept.includes("text/html");
289
- if (isHtml) {
290
- const fn = pickStrategy(HTML_STRATEGY);
291
- event.respondWith(fn(req, OFFLINE_PAGE));
292
- return;
293
- }
294
-
295
- // 2) API calls (network-first by default)
296
- if (isApi(url)) {
297
- event.respondWith(networkFirst(req));
298
- return;
299
- }
300
-
301
- // 3) Images (cache-first default) with OFFLINE_IMAGE fallback
302
- if (isImageExt(ext)) {
303
- const fn = pickStrategy(IMAGE_STRATEGY);
304
- event.respondWith(fn(req, OFFLINE_IMAGE));
305
- return;
306
- }
307
-
308
- // 4) Static assets (js/css/fonts/json/etc.)
309
- if (ASSET_EXTS.has(ext)) {
310
- const fn = pickStrategy(ASSET_STRATEGY);
311
- if (fn === staleWhileRevalidate) {
312
- event.respondWith(staleWhileRevalidate(req));
313
- } else {
314
- event.respondWith(fn(req));
315
- }
316
- return;
317
- }
318
-
319
- // Everything else: do nothing (network as normal)
320
- });
321
- `;
322
- }
323
-
324
- // src/core/precacheScan.ts
325
- import fs2 from "fs";
326
- import path2 from "path";
327
- function normalizeExts(exts) {
328
- return new Set(exts.map((e) => e.replace(/^\./, "").toLowerCase()));
329
- }
330
- function toUrlPath(outDirAbs, fileAbs) {
331
- const rel = path2.relative(outDirAbs, fileAbs).split(path2.sep).join("/");
332
- return "/" + rel.replace(/^\/+/, "");
333
- }
334
- function startsWithAny(urlPath, prefixes) {
335
- return prefixes.some((p) => urlPath.startsWith(p));
336
- }
337
- function scanPrecacheFiles(opts) {
338
- const exts = normalizeExts(opts.extensions);
339
- const results = [];
340
- const walk = (dirAbs) => {
341
- if (results.length >= opts.maxFiles) return;
342
- const entries = fs2.readdirSync(dirAbs, { withFileTypes: true });
343
- for (const ent of entries) {
344
- if (results.length >= opts.maxFiles) break;
345
- const abs = path2.join(dirAbs, ent.name);
346
- if (ent.isDirectory()) {
347
- walk(abs);
348
- continue;
349
- }
350
- if (!ent.isFile()) continue;
116
+ // \u2705 Offline fallback only for page navigations
117
+ if (req.mode === "navigate") {
118
+ event.respondWith((async () => {
351
119
  try {
352
- const st = fs2.statSync(abs);
353
- if (st.size > opts.maxFileSizeKB * 1024) continue;
120
+ return await fetch(req);
354
121
  } catch {
355
- continue;
122
+ const cache = await caches.open(CACHE_NAME);
123
+ return (await cache.match(OFFLINE_PAGE)) || new Response("Offline", { status: 503 });
356
124
  }
357
- const urlPath = toUrlPath(opts.outDir, abs);
358
- if (startsWithAny(urlPath, opts.ignorePrefixes)) continue;
359
- const dot = urlPath.lastIndexOf(".");
360
- const ext = dot === -1 ? "" : urlPath.slice(dot + 1).toLowerCase();
361
- if (!exts.has(ext)) continue;
362
- results.push(urlPath);
363
- }
364
- };
365
- walk(opts.outDir);
366
- return results;
125
+ })());
126
+ }
127
+ });
128
+ `;
367
129
  }
368
130
 
369
131
  // src/cli.ts
370
132
  function normalizePublicPath(p) {
371
- if (!p) return "/";
372
- return p.startsWith("/") ? p : "/" + p;
373
- }
374
- function normalizeIgnorePrefix(p) {
375
- return normalizePublicPath(p.trim());
376
- }
377
- function safeNumber(v, fallback) {
378
- if (!v) return fallback;
379
- const n = Number(v);
380
- return Number.isFinite(n) ? n : fallback;
133
+ if (!p.startsWith("/")) return "/" + p;
134
+ return p;
381
135
  }
382
136
  function withDefaults(o) {
383
137
  return {
@@ -391,69 +145,28 @@ function withDefaults(o) {
391
145
  assetStrategy: o.assetStrategy ?? "staleWhileRevalidate",
392
146
  imageStrategy: o.imageStrategy ?? "cacheFirst",
393
147
  assetExtensions: o.assetExtensions ?? [],
394
- apiPrefixes: o.apiPrefixes ?? [],
395
- autoPrecache: o.autoPrecache ?? true,
396
- precacheExtensions: o.precacheExtensions ?? [
397
- "ico",
398
- "png",
399
- "jpg",
400
- "jpeg",
401
- "webp",
402
- "svg",
403
- "gif",
404
- "css",
405
- "js",
406
- "woff2",
407
- "woff",
408
- "ttf",
409
- "eot",
410
- "json",
411
- "webmanifest",
412
- "txt",
413
- "xml"
414
- ],
415
- precacheIgnore: o.precacheIgnore ?? [
416
- "/sw.js",
417
- "/sw.js.map",
418
- "/_next/",
419
- "/static/"
420
- ],
421
- precacheMaxFiles: o.precacheMaxFiles ?? 200,
422
- precacheMaxFileSizeKB: o.precacheMaxFileSizeKB ?? 512
148
+ apiPrefixes: o.apiPrefixes ?? []
423
149
  };
424
150
  }
425
- function parseCommand(argv) {
426
- const first = argv[0];
427
- if (first === "init" || first === "build") {
428
- return { cmd: first, rest: argv.slice(1) };
429
- }
430
- return { cmd: "init", rest: argv };
431
- }
432
- var rawArgv = process.argv.slice(2);
433
- var { cmd, rest } = parseCommand(rawArgv);
434
- var args = parseArgs(rest);
435
- var outDir = path3.resolve(process.cwd(), args.get("outDir") || "public");
151
+ var args = parseArgs(process.argv.slice(2));
152
+ var cmd = process.argv.slice(2).find((a) => !a.startsWith("--")) || "init";
153
+ var outDir = path2.resolve(process.cwd(), args.get("outDir") || "public");
436
154
  var options = withDefaults({
437
155
  outDir,
438
156
  swFileName: args.get("swFileName") || "sw.js",
439
157
  offlinePage: args.get("offlinePage") || "/offline.html",
440
158
  offlineImage: args.get("offlineImage") || "/offline.svg",
441
159
  cacheName: args.get("cacheName") || "offline-page-kit",
442
- precache: splitList(args.get("precache")).map(normalizePublicPath),
160
+ precache: splitList(args.get("precache")),
443
161
  htmlStrategy: args.get("htmlStrategy") || "networkFirst",
444
162
  assetStrategy: args.get("assetStrategy") || "staleWhileRevalidate",
445
163
  imageStrategy: args.get("imageStrategy") || "cacheFirst",
446
164
  assetExtensions: splitList(args.get("assetExtensions")),
447
- apiPrefixes: splitList(args.get("apiPrefixes")),
448
- autoPrecache: args.get("autoPrecache") !== "false",
449
- precacheExtensions: splitList(args.get("precacheExtensions")),
450
- precacheIgnore: splitList(args.get("precacheIgnore")).map(normalizeIgnorePrefix),
451
- precacheMaxFiles: safeNumber(args.get("precacheMaxFiles"), 200),
452
- precacheMaxFileSizeKB: safeNumber(args.get("precacheMaxFileSizeKB"), 512)
165
+ apiPrefixes: splitList(args.get("apiPrefixes"))
453
166
  });
454
- var swOut = path3.join(outDir, options.swFileName);
455
- var offlineHtmlOut = path3.join(outDir, options.offlinePage.replace(/^\//, ""));
456
- var offlineSvgOut = path3.join(outDir, options.offlineImage.replace(/^\//, ""));
167
+ var swOut = path2.join(outDir, options.swFileName);
168
+ var offlineHtmlOut = path2.join(outDir, options.offlinePage.replace(/^\//, ""));
169
+ var offlineSvgOut = path2.join(outDir, options.offlineImage.replace(/^\//, ""));
457
170
  if (cmd === "init" || cmd === "build") {
458
171
  if (cmd === "build" || !exists(offlineHtmlOut)) {
459
172
  writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());
@@ -461,46 +174,12 @@ if (cmd === "init" || cmd === "build") {
461
174
  if (cmd === "build" || !exists(offlineSvgOut)) {
462
175
  writeFileSafe(offlineSvgOut, offlineSvgTemplate());
463
176
  }
464
- let scanned = [];
465
- if (options.autoPrecache) {
466
- const swUrlPath = normalizePublicPath(options.swFileName);
467
- const swMapUrlPath = swUrlPath + ".map";
468
- const ignorePrefixes = Array.from(/* @__PURE__ */ new Set([
469
- swUrlPath,
470
- swMapUrlPath,
471
- ...options.precacheIgnore.map(normalizeIgnorePrefix)
472
- ]));
473
- scanned = scanPrecacheFiles({
474
- outDir,
475
- // absolute /public
476
- extensions: options.precacheExtensions.length ? options.precacheExtensions : ["ico", "png", "css", "js", "woff2", "json", "webmanifest", "svg"],
477
- ignorePrefixes,
478
- maxFiles: options.precacheMaxFiles,
479
- maxFileSizeKB: options.precacheMaxFileSizeKB
480
- }).map(normalizePublicPath);
481
- }
482
- const allPrecache = Array.from(/* @__PURE__ */ new Set([
483
- normalizePublicPath(options.offlinePage),
484
- normalizePublicPath(options.offlineImage),
485
- ...(options.precache || []).map(normalizePublicPath),
486
- ...scanned.map(normalizePublicPath)
487
- ]));
488
- const sw = buildServiceWorkerJS({
489
- ...options,
490
- precache: allPrecache
491
- });
177
+ const sw = buildServiceWorkerJS(options);
492
178
  writeFileSafe(swOut, sw);
493
- const manualCount = (options.precache || []).length;
494
- const scannedCount = scanned.length;
495
179
  console.log(`[offline-page-kit] Generated:
496
180
  - ${swOut}
497
181
  - ${offlineHtmlOut}
498
182
  - ${offlineSvgOut}
499
-
500
- Precache:
501
- - manual: ${manualCount}
502
- - scanned: ${scannedCount}
503
- - total: ${allPrecache.length}
504
183
  `);
505
184
  } else {
506
185
  console.log(`[offline-page-kit] Unknown command: ${cmd}
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/core/utils.ts","../src/core/offlineHtml.ts","../src/core/offlineSvg.ts","../src/core/swTemplate.ts","../src/core/precacheScan.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\nimport { scanPrecacheFiles } from \"./core/precacheScan\";\r\n\r\ntype Cmd = \"init\" | \"build\";\r\n\r\nfunction normalizePublicPath(p: string) {\r\n if (!p) return \"/\";\r\n return p.startsWith(\"/\") ? p : \"/\" + p;\r\n}\r\n\r\nfunction normalizeIgnorePrefix(p: string) {\r\n // ignore prefixes are URL-style and should start with \"/\"\r\n return normalizePublicPath(p.trim());\r\n}\r\n\r\nfunction safeNumber(v: string | undefined, fallback: number) {\r\n if (!v) return fallback;\r\n const n = Number(v);\r\n return Number.isFinite(n) ? n : fallback;\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\r\n offlinePage: normalizePublicPath(o.offlinePage ?? \"/offline.html\"),\r\n offlineImage: normalizePublicPath(o.offlineImage ?? \"/offline.svg\"),\r\n\r\n cacheName: o.cacheName ?? \"offline-page-kit\",\r\n precache: o.precache ?? [],\r\n\r\n htmlStrategy: o.htmlStrategy ?? \"networkFirst\",\r\n assetStrategy: o.assetStrategy ?? \"staleWhileRevalidate\",\r\n imageStrategy: o.imageStrategy ?? \"cacheFirst\",\r\n\r\n assetExtensions: o.assetExtensions ?? [],\r\n apiPrefixes: o.apiPrefixes ?? [],\r\n\r\n autoPrecache: o.autoPrecache ?? true,\r\n precacheExtensions: o.precacheExtensions ?? [\r\n \"ico\", \"png\", \"jpg\", \"jpeg\", \"webp\", \"svg\", \"gif\",\r\n \"css\", \"js\", \"woff2\", \"woff\", \"ttf\", \"eot\",\r\n \"json\", \"webmanifest\", \"txt\", \"xml\"\r\n ],\r\n precacheIgnore: o.precacheIgnore ?? [\r\n \"/sw.js\",\r\n \"/sw.js.map\",\r\n \"/_next/\",\r\n \"/static/\"\r\n ],\r\n precacheMaxFiles: o.precacheMaxFiles ?? 200,\r\n precacheMaxFileSizeKB: o.precacheMaxFileSizeKB ?? 512,\r\n };\r\n}\r\n\r\n/**\r\n * Command parsing\r\n * Supports:\r\n * offline-page-kit init --outDir public\r\n * offline-page-kit --outDir public (defaults to init)\r\n */\r\nfunction parseCommand(argv: string[]): { cmd: Cmd; rest: string[] } {\r\n const first = argv[0];\r\n if (first === \"init\" || first === \"build\") {\r\n return { cmd: first, rest: argv.slice(1) };\r\n }\r\n return { cmd: \"init\", rest: argv };\r\n}\r\n\r\nconst rawArgv = process.argv.slice(2);\r\nconst { cmd, rest } = parseCommand(rawArgv);\r\nconst args = parseArgs(rest);\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\")).map(normalizePublicPath),\r\n\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\r\n assetExtensions: splitList(args.get(\"assetExtensions\")),\r\n apiPrefixes: splitList(args.get(\"apiPrefixes\")),\r\n\r\n autoPrecache: args.get(\"autoPrecache\") !== \"false\",\r\n precacheExtensions: splitList(args.get(\"precacheExtensions\")),\r\n precacheIgnore: splitList(args.get(\"precacheIgnore\")).map(normalizeIgnorePrefix),\r\n\r\n precacheMaxFiles: safeNumber(args.get(\"precacheMaxFiles\"), 200),\r\n precacheMaxFileSizeKB: safeNumber(args.get(\"precacheMaxFileSizeKB\"), 512),\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 // 1) Generate offline page + offline svg first (so scanner can find them)\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 // 2) Auto-scan (after offline files exist)\r\n let scanned: string[] = [];\r\n if (options.autoPrecache) {\r\n // ✅ always ignore the actual sw filename (even if user customizes it)\r\n const swUrlPath = normalizePublicPath(options.swFileName);\r\n const swMapUrlPath = swUrlPath + \".map\";\r\n\r\n const ignorePrefixes = Array.from(new Set([\r\n swUrlPath,\r\n swMapUrlPath,\r\n ...options.precacheIgnore.map(normalizeIgnorePrefix),\r\n ]));\r\n\r\n scanned = scanPrecacheFiles({\r\n outDir, // absolute /public\r\n extensions: options.precacheExtensions.length\r\n ? options.precacheExtensions\r\n : [\"ico\", \"png\", \"css\", \"js\", \"woff2\", \"json\", \"webmanifest\", \"svg\"],\r\n ignorePrefixes,\r\n maxFiles: options.precacheMaxFiles,\r\n maxFileSizeKB: options.precacheMaxFileSizeKB,\r\n }).map(normalizePublicPath);\r\n }\r\n\r\n // 3) Merge + de-duplicate (and normalize)\r\n const allPrecache = Array.from(new Set([\r\n normalizePublicPath(options.offlinePage),\r\n normalizePublicPath(options.offlineImage),\r\n ...(options.precache || []).map(normalizePublicPath),\r\n ...scanned.map(normalizePublicPath),\r\n ]));\r\n\r\n // 4) Build SW using merged list\r\n const sw = buildServiceWorkerJS({\r\n ...options,\r\n precache: allPrecache,\r\n });\r\n\r\n writeFileSafe(swOut, sw);\r\n\r\n const manualCount = (options.precache || []).length;\r\n const scannedCount = scanned.length;\r\n\r\n console.log(`[offline-page-kit] Generated:\r\n- ${swOut}\r\n- ${offlineHtmlOut}\r\n- ${offlineSvgOut}\r\n\r\nPrecache:\r\n- manual: ${manualCount}\r\n- scanned: ${scannedCount}\r\n- total: ${allPrecache.length}\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 } 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 // precache already normalized in CLI; still keep it safe\r\n const precacheList = Array.from(\r\n new Set([offlinePage, offlineImage, ...(precache ?? [])].filter(Boolean))\r\n );\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(precacheList)};\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\nconst SAME_ORIGIN_ONLY = true;\r\n\r\n// ---------------------------\r\n// helpers\r\n// ---------------------------\r\nconst isSameOrigin = (url) => url.origin === self.location.origin;\r\n\r\nconst isApi = (url) => {\r\n if (!API_PREFIXES || API_PREFIXES.length === 0) return false;\r\n return API_PREFIXES.some((p) => {\r\n try {\r\n // allow either absolute prefix or pathname prefix\r\n return url.href.startsWith(p) || url.pathname.startsWith(p);\r\n } catch {\r\n return false;\r\n }\r\n });\r\n};\r\n\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\nconst isImageExt = (ext) => [\"png\",\"jpg\",\"jpeg\",\"webp\",\"gif\",\"svg\",\"ico\"].includes(ext);\r\n\r\n// Only cache successful, basic responses (avoid caching errors)\r\nconst isCacheableResponse = (res) => res && (res.ok || res.type === \"opaque\");\r\n\r\n// Cache helpers\r\nasync function cacheGet(reqOrUrl) {\r\n const cache = await caches.open(CACHE_NAME);\r\n return cache.match(reqOrUrl);\r\n}\r\n\r\nasync function cachePut(req, res) {\r\n try {\r\n if (!isCacheableResponse(res)) return;\r\n const cache = await caches.open(CACHE_NAME);\r\n await cache.put(req, res);\r\n } catch {\r\n // ignore quota / put errors\r\n }\r\n}\r\n\r\nasync function cleanOldCaches() {\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// ---------------------------\r\n// strategies\r\n// ---------------------------\r\nasync function networkFirst(req, fallbackUrl) {\r\n try {\r\n const res = await fetch(req);\r\n await 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) {\r\n const fb = await cacheGet(fallbackUrl);\r\n if (fb) return fb;\r\n }\r\n return new Response(\"Offline\", { status: 503, headers: { \"content-type\": \"text/plain\" } });\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\r\n try {\r\n const res = await fetch(req);\r\n await cachePut(req, res.clone());\r\n return res;\r\n } catch {\r\n if (fallbackUrl) {\r\n const fb = await cacheGet(fallbackUrl);\r\n if (fb) return fb;\r\n }\r\n return new Response(\"Offline\", { status: 503, headers: { \"content-type\": \"text/plain\" } });\r\n }\r\n}\r\n\r\nasync function staleWhileRevalidate(req) {\r\n const cached = await cacheGet(req);\r\n\r\n const fetchPromise = fetch(req)\r\n .then(async (res) => {\r\n await cachePut(req, res.clone());\r\n return res;\r\n })\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\n// ---------------------------\r\n// lifecycle\r\n// ---------------------------\r\nself.addEventListener(\"install\", (event) => {\r\n event.waitUntil((async () => {\r\n const cache = await caches.open(CACHE_NAME);\r\n\r\n // ✅ fail-safe install (never redundant from a single bad file)\r\n await Promise.allSettled(PRECACHE.map((u) => cache.add(u)));\r\n\r\n await self.skipWaiting();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"activate\", (event) => {\r\n event.waitUntil((async () => {\r\n await cleanOldCaches();\r\n await self.clients.claim();\r\n })());\r\n});\r\n\r\n// Allow app to trigger update instantly:\r\n// navigator.serviceWorker.controller?.postMessage({ type: \"SKIP_WAITING\" })\r\nself.addEventListener(\"message\", (event) => {\r\n if (event?.data?.type === \"SKIP_WAITING\") {\r\n self.skipWaiting();\r\n }\r\n});\r\n\r\n// ---------------------------\r\n// fetch routing\r\n// ---------------------------\r\nself.addEventListener(\"fetch\", (event) => {\r\n const req = event.request;\r\n\r\n // Don't touch non-GET requests\r\n if (req.method && req.method !== \"GET\") return;\r\n\r\n const url = new URL(req.url);\r\n\r\n // Safe default: same-origin only\r\n if (SAME_ORIGIN_ONLY && !isSameOrigin(url)) return;\r\n\r\n const accept = req.headers.get(\"accept\") || \"\";\r\n const ext = extOf(url.pathname);\r\n\r\n // 1) HTML / navigations -> offline page fallback\r\n const isHtml = req.mode === \"navigate\" || accept.includes(\"text/html\");\r\n if (isHtml) {\r\n const fn = pickStrategy(HTML_STRATEGY);\r\n event.respondWith(fn(req, OFFLINE_PAGE));\r\n return;\r\n }\r\n\r\n // 2) API calls (network-first by default)\r\n if (isApi(url)) {\r\n event.respondWith(networkFirst(req));\r\n return;\r\n }\r\n\r\n // 3) Images (cache-first default) with OFFLINE_IMAGE fallback\r\n if (isImageExt(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 // 4) Static assets (js/css/fonts/json/etc.)\r\n if (ASSET_EXTS.has(ext)) {\r\n const fn = pickStrategy(ASSET_STRATEGY);\r\n if (fn === staleWhileRevalidate) {\r\n event.respondWith(staleWhileRevalidate(req));\r\n } else {\r\n event.respondWith(fn(req));\r\n }\r\n return;\r\n }\r\n\r\n // Everything else: do nothing (network as normal)\r\n});\r\n`;\r\n}","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\ntype ScanOptions = {\r\n outDir: string; // absolute path to public\r\n extensions: string[];\r\n ignorePrefixes: string[]; // url-style (\"/_next\", \"/sw.js\")\r\n maxFiles: number;\r\n maxFileSizeKB: number;\r\n};\r\n\r\nfunction normalizeExts(exts: string[]) {\r\n return new Set(exts.map(e => e.replace(/^\\./, \"\").toLowerCase()));\r\n}\r\n\r\nfunction toUrlPath(outDirAbs: string, fileAbs: string) {\r\n // convert /abs/public/assets/a.css -> /assets/a.css\r\n const rel = path.relative(outDirAbs, fileAbs).split(path.sep).join(\"/\");\r\n return \"/\" + rel.replace(/^\\/+/, \"\");\r\n}\r\n\r\nfunction startsWithAny(urlPath: string, prefixes: string[]) {\r\n return prefixes.some(p => urlPath.startsWith(p));\r\n}\r\n\r\nexport function scanPrecacheFiles(opts: ScanOptions): string[] {\r\n const exts = normalizeExts(opts.extensions);\r\n const results: string[] = [];\r\n\r\n const walk = (dirAbs: string) => {\r\n if (results.length >= opts.maxFiles) return;\r\n\r\n const entries = fs.readdirSync(dirAbs, { withFileTypes: true });\r\n for (const ent of entries) {\r\n if (results.length >= opts.maxFiles) break;\r\n\r\n const abs = path.join(dirAbs, ent.name);\r\n\r\n if (ent.isDirectory()) {\r\n walk(abs);\r\n continue;\r\n }\r\n\r\n if (!ent.isFile()) continue;\r\n\r\n // file size limit\r\n try {\r\n const st = fs.statSync(abs);\r\n if (st.size > opts.maxFileSizeKB * 1024) continue;\r\n } catch {\r\n continue;\r\n }\r\n\r\n const urlPath = toUrlPath(opts.outDir, abs);\r\n\r\n // ignore list\r\n if (startsWithAny(urlPath, opts.ignorePrefixes)) continue;\r\n\r\n // extension check\r\n const dot = urlPath.lastIndexOf(\".\");\r\n const ext = dot === -1 ? \"\" : urlPath.slice(dot + 1).toLowerCase();\r\n if (!exts.has(ext)) continue;\r\n\r\n results.push(urlPath);\r\n }\r\n };\r\n\r\n walk(opts.outDir);\r\n return results;\r\n}"],"mappings":"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","names":["path","options","fs","path","path"]}
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 } from \"../types\";\r\n\r\nconst js = (v: unknown) => JSON.stringify(v);\r\n\r\nexport function buildServiceWorkerJS(options: Required<OfflineKitBuildOptions>) {\r\n const { cacheName, offlinePage, offlineImage } = options;\r\n\r\n return `/* offline-page-kit service worker (minimal) */\r\nconst CACHE_NAME = ${js(cacheName)};\r\nconst OFFLINE_PAGE = ${js(offlinePage)};\r\nconst OFFLINE_IMAGE = ${js(offlineImage)};\r\n\r\nself.addEventListener(\"install\", (event) => {\r\n event.waitUntil((async () => {\r\n const cache = await caches.open(CACHE_NAME);\r\n\r\n // ✅ Do not let one 404 kill the install\r\n await Promise.allSettled([\r\n cache.add(OFFLINE_PAGE),\r\n cache.add(OFFLINE_IMAGE),\r\n ]);\r\n\r\n await self.skipWaiting();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"activate\", (event) => {\r\n event.waitUntil((async () => {\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\r\n // ✅ Offline fallback only for page navigations\r\n if (req.mode === \"navigate\") {\r\n event.respondWith((async () => {\r\n try {\r\n return await fetch(req);\r\n } catch {\r\n const cache = await caches.open(CACHE_NAME);\r\n return (await cache.match(OFFLINE_PAGE)) || new Response(\"Offline\", { status: 503 });\r\n }\r\n })());\r\n }\r\n});\r\n`;\r\n}"],"mappings":"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","names":["path","options","path"]}
package/dist/index.d.cts CHANGED
@@ -14,14 +14,9 @@ type OfflineKitBuildOptions = {
14
14
  offlineImage?: string;
15
15
  cacheName?: string;
16
16
  precache?: string[];
17
- autoPrecache?: boolean;
18
- precacheExtensions?: string[];
19
- precacheIgnore?: string[];
20
- precacheMaxFiles?: number;
21
- precacheMaxFileSizeKB?: number;
22
- htmlStrategy?: "networkFirst" | "cacheFirst" | "staleWhileRevalidate";
23
- assetStrategy?: "networkFirst" | "cacheFirst" | "staleWhileRevalidate";
24
- imageStrategy?: "networkFirst" | "cacheFirst" | "staleWhileRevalidate";
17
+ htmlStrategy?: Strategy;
18
+ assetStrategy?: Strategy;
19
+ imageStrategy?: Strategy;
25
20
  assetExtensions?: string[];
26
21
  apiPrefixes?: string[];
27
22
  };
package/dist/index.d.ts CHANGED
@@ -14,14 +14,9 @@ type OfflineKitBuildOptions = {
14
14
  offlineImage?: string;
15
15
  cacheName?: string;
16
16
  precache?: string[];
17
- autoPrecache?: boolean;
18
- precacheExtensions?: string[];
19
- precacheIgnore?: string[];
20
- precacheMaxFiles?: number;
21
- precacheMaxFileSizeKB?: number;
22
- htmlStrategy?: "networkFirst" | "cacheFirst" | "staleWhileRevalidate";
23
- assetStrategy?: "networkFirst" | "cacheFirst" | "staleWhileRevalidate";
24
- imageStrategy?: "networkFirst" | "cacheFirst" | "staleWhileRevalidate";
17
+ htmlStrategy?: Strategy;
18
+ assetStrategy?: Strategy;
19
+ imageStrategy?: Strategy;
25
20
  assetExtensions?: string[];
26
21
  apiPrefixes?: string[];
27
22
  };
package/package.json CHANGED
@@ -1,8 +1,25 @@
1
1
  {
2
2
  "name": "offline-page-kit",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Framework-agnostic offline page + service worker generator (TypeScript)",
5
5
  "license": "MIT",
6
+ "author": {
7
+ "name": "MD Kuhel Ahmed",
8
+ "email": "kuhelahmed84@gmail.com",
9
+ "url": "https://devcrack.dev"
10
+ },
11
+ "homepage": "https://devcrack.dev",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/YOUR_GITHUB_USERNAME/offline-page-kit.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/YOUR_GITHUB_USERNAME/offline-page-kit/issues"
18
+ },
19
+ "funding": {
20
+ "type": "individual",
21
+ "url": "https://www.youtube.com/@dev-crack"
22
+ },
6
23
  "type": "module",
7
24
  "main": "./dist/index.cjs",
8
25
  "module": "./dist/index.js",
@@ -14,15 +31,34 @@
14
31
  "require": "./dist/index.cjs"
15
32
  }
16
33
  },
17
- "files": [
18
- "dist"
19
- ],
20
34
  "bin": {
21
35
  "offline-page-kit": "./dist/cli.cjs"
22
36
  },
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "LICENSE"
41
+ ],
42
+ "sideEffects": false,
43
+ "keywords": [
44
+ "offline",
45
+ "offline-page",
46
+ "service-worker",
47
+ "pwa",
48
+ "service worker",
49
+ "generator",
50
+ "typescript",
51
+ "nextjs",
52
+ "vite",
53
+ "framework-agnostic"
54
+ ],
23
55
  "scripts": {
24
- "test": "echo \"Error: no test specified\" && exit 1"
56
+ "build": "tsup",
57
+ "dev": "tsup --watch",
58
+ "prepublishOnly": "npm run build"
25
59
  },
26
60
  "devDependencies": {
61
+ "tsup": "^8.0.0",
62
+ "typescript": "^5.0.0"
27
63
  }
28
64
  }