@baukit/pwa-web 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -118,67 +118,99 @@ Behavior per strategy:
118
118
  `putCache` receives the strategy that produced the response, which is how the snippet above
119
119
  routes static assets and shell responses into different caches.
120
120
 
121
- ## Build a service worker without a bundler
121
+ ## Use the worker artifact
122
122
 
123
- The package exports ESM for `import` and CommonJS for `require`. Build scripts can load either
124
- format directly:
123
+ `@baukit/pwa-web/worker` resolves to a bundled classic script. It has no imports and assigns the
124
+ public API to `globalThis.BaukitPwa`. A build script can copy it without parsing or rewriting
125
+ package source:
125
126
 
126
127
  ```js
127
- const { decideCacheStrategy } = require('@baukit/pwa-web');
128
+ import { copyFile } from 'node:fs/promises';
129
+ import { fileURLToPath } from 'node:url';
130
+
131
+ const source = fileURLToPath(import.meta.resolve('@baukit/pwa-web/worker'));
132
+ await copyFile(source, 'public/baukit-pwa-worker.js');
128
133
  ```
129
134
 
130
- A service worker copied to `public/` cannot resolve `node_modules`. The following
131
- `scripts/build-sw.mjs` reads the installed ESM files, removes their module syntax, and writes one
132
- classic script. It exposes the package through `globalThis.BaukitPwa`, so a classic worker can use
133
- `importScripts('/sw-strategy.js')`.
135
+ Load the copy before product worker code:
134
136
 
135
137
  ```js
136
- import { readFile, writeFile } from 'node:fs/promises';
137
- import { dirname, join } from 'node:path';
138
- import { fileURLToPath } from 'node:url';
138
+ // public/sw.js
139
+ importScripts('/baukit-pwa-worker.js');
140
+
141
+ const handleFetch = globalThis.BaukitPwa.createFetchHandler({
142
+ // Product cache policy and ports go here.
143
+ });
144
+ ```
145
+
146
+ Register `sw.js` as a normal classic service worker:
139
147
 
140
- const outputPath = process.argv[2] ?? 'public/sw-strategy.js';
141
- const entryPath = fileURLToPath(import.meta.resolve('@baukit/pwa-web'));
142
- const distDir = dirname(entryPath);
143
-
144
- async function readDistFile(name) {
145
- const source = await readFile(join(distDir, name), 'utf8');
146
- return source
147
- .replace(/import\s*\{[^}]+\}\s*from\s*['"]\.\/cache-strategy\.js['"];\s*/s, '')
148
- .replace(/^export /gm, '')
149
- .replace(/^\/\/# sourceMappingURL=.*$/gm, '')
150
- .trim();
148
+ ```js
149
+ if ('serviceWorker' in navigator) {
150
+ await navigator.serviceWorker.register('/sw.js');
151
151
  }
152
+ ```
152
153
 
153
- const source = `// Generated by scripts/build-sw.mjs. Do not edit.
154
- ${await readDistFile('cache-strategy.js')}
155
- ${await readDistFile('fetch-handler.js')}
156
- globalThis.BaukitPwa = Object.freeze({
157
- CACHE_STRATEGIES,
158
- createCacheStrategyDecider,
159
- createFetchHandler,
160
- decideCacheStrategy,
161
- });
162
- `;
154
+ The IIFE format is deliberate. Vite copies its `public/` directory into `dist/`, and Expo web does
155
+ the same during static export. Both paths can copy this artifact byte for byte. Neither path needs
156
+ bare module imports in a browser, module-worker registration, or package-specific bundler setup.
157
+ The packed npm tarball test loads the artifact with no `window`, `document`, or `self` global.
158
+
159
+ ### Migrate a source-rewriting build
163
160
 
164
- await writeFile(outputPath, source);
161
+ Replace code that reads `dist/cache-strategy.js`, removes imports or exports, and concatenates files
162
+ with one copy from `@baukit/pwa-web/worker`. Keep the product-owned `sw.js` and its cache policy.
163
+ Imports from `@baukit/pwa-web` and CommonJS `require('@baukit/pwa-web')` remain supported.
164
+
165
+ ## Cache cleanup
166
+
167
+ Products decide which exact cache names belong to an old version or identity. `cleanupCaches`
168
+ provides the list-and-delete operation and returns counts without returning or logging cache names.
169
+ This activate handler removes old application cache versions while preserving the current version
170
+ and caches owned by other code:
171
+
172
+ ```js
173
+ const CURRENT_APP_CACHE = 'notes-app-v4';
174
+
175
+ self.addEventListener('activate', (event) => {
176
+ event.waitUntil(
177
+ globalThis.BaukitPwa.cleanupCaches({
178
+ ports: {
179
+ listCacheNames: () => caches.keys(),
180
+ deleteCache: (name) => caches.delete(name),
181
+ },
182
+ shouldDelete: (name) => name.startsWith('notes-app-') && name !== CURRENT_APP_CACHE,
183
+ }),
184
+ );
185
+ });
165
186
  ```
166
187
 
167
- Add generation and drift checks to the product's `package.json`:
188
+ Private caches need a product-owned, opaque partition key. On an identity change, delete the exact
189
+ previous partition before exposing the new identity's data. On logout, delete every private cache
190
+ for the outgoing partition. Do not put access tokens, email addresses, or raw identity-provider
191
+ subjects in cache names.
168
192
 
169
- ```json
170
- {
171
- "scripts": {
172
- "build:sw": "node scripts/build-sw.mjs public/sw-strategy.js",
173
- "build:sw:check": "sh -c 'tmp=$(mktemp); trap \"rm -f $tmp\" EXIT; node scripts/build-sw.mjs \"$tmp\" && diff -u public/sw-strategy.js \"$tmp\"'"
174
- }
175
- }
193
+ ```ts
194
+ import { cleanupCaches } from '@baukit/pwa-web';
195
+
196
+ await cleanupCaches({
197
+ ports: {
198
+ listCacheNames: () => caches.keys(),
199
+ deleteCache: (name) => caches.delete(name),
200
+ },
201
+ shouldDelete: (name) => name === `notes-private-${outgoingPartitionKey}`,
202
+ });
176
203
  ```
177
204
 
178
- Run `build:sw` after updating `@baukit/pwa-web`. Commit the generated file and run
179
- `build:sw:check` in CI. The diff fails when the installed package and committed worker differ.
205
+ `cleanupCaches` selects all names before starting deletes, then runs the deletes together. It counts
206
+ a cache as deleted only when `CacheStorage.delete` resolves to `true`. A list, predicate, or delete
207
+ failure rejects the operation. Some deletes may already have completed when another delete rejects,
208
+ so retry cleanup before completing logout or an identity switch.
180
209
 
181
210
  ## What this package does not do
182
211
 
183
- It does not register the service worker, precache an app shell, version caches, prompt for
184
- updates, or supply an offline page. Those depend on the product's build output and copy.
212
+ It does not register the service worker, precache an app shell, choose cache names, define routes,
213
+ create a web manifest or icons, prompt for updates, choose an offline route, or derive identity
214
+ partition keys. Those depend on the product's build output, privacy model, and copy. Baukit's CLI
215
+ still requires the web capability for PWA use. Publishing this artifact does not add PWA generation
216
+ to Expo web projects.
@@ -0,0 +1,14 @@
1
+ export interface CacheCleanupPorts {
2
+ readonly listCacheNames: () => Promise<readonly string[]>;
3
+ readonly deleteCache: (name: string) => Promise<boolean>;
4
+ }
5
+ export interface CacheCleanupOptions {
6
+ readonly ports: CacheCleanupPorts;
7
+ readonly shouldDelete: (name: string) => boolean;
8
+ }
9
+ export interface CacheCleanupResult {
10
+ readonly matchedCount: number;
11
+ readonly deletedCount: number;
12
+ }
13
+ export declare function cleanupCaches(options: CacheCleanupOptions): Promise<CacheCleanupResult>;
14
+ //# sourceMappingURL=cache-cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-cleanup.d.ts","sourceRoot":"","sources":["../src/cache-cleanup.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAC1D,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAClD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAS7F"}
@@ -0,0 +1,10 @@
1
+ export async function cleanupCaches(options) {
2
+ const names = await options.ports.listCacheNames();
3
+ const selected = names.filter(options.shouldDelete);
4
+ const deleted = await Promise.all(selected.map((name) => options.ports.deleteCache(name)));
5
+ return {
6
+ matchedCount: selected.length,
7
+ deletedCount: deleted.filter(Boolean).length,
8
+ };
9
+ }
10
+ //# sourceMappingURL=cache-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-cleanup.js","sourceRoot":"","sources":["../src/cache-cleanup.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA4B;IAC9D,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;IACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE3F,OAAO;QACL,YAAY,EAAE,QAAQ,CAAC,MAAM;QAC7B,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM;KAC7C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cleanupCaches = cleanupCaches;
4
+ async function cleanupCaches(options) {
5
+ const names = await options.ports.listCacheNames();
6
+ const selected = names.filter(options.shouldDelete);
7
+ const deleted = await Promise.all(selected.map((name) => options.ports.deleteCache(name)));
8
+ return {
9
+ matchedCount: selected.length,
10
+ deletedCount: deleted.filter(Boolean).length,
11
+ };
12
+ }
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createFetchHandler = exports.decideCacheStrategy = exports.createCacheStrategyDecider = exports.CACHE_STRATEGIES = void 0;
3
+ exports.createFetchHandler = exports.decideCacheStrategy = exports.createCacheStrategyDecider = exports.CACHE_STRATEGIES = exports.cleanupCaches = void 0;
4
+ var cache_cleanup_js_1 = require("./cache-cleanup.js");
5
+ Object.defineProperty(exports, "cleanupCaches", { enumerable: true, get: function () { return cache_cleanup_js_1.cleanupCaches; } });
4
6
  var cache_strategy_js_1 = require("./cache-strategy.js");
5
7
  Object.defineProperty(exports, "CACHE_STRATEGIES", { enumerable: true, get: function () { return cache_strategy_js_1.CACHE_STRATEGIES; } });
6
8
  Object.defineProperty(exports, "createCacheStrategyDecider", { enumerable: true, get: function () { return cache_strategy_js_1.createCacheStrategyDecider; } });
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./index.js"), exports);
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { cleanupCaches, type CacheCleanupOptions, type CacheCleanupPorts, type CacheCleanupResult, } from './cache-cleanup.js';
1
2
  export { CACHE_STRATEGIES, createCacheStrategyDecider, decideCacheStrategy, type CacheStrategy, type CacheStrategyOptions, type CacheStrategyRequest, } from './cache-strategy.js';
2
3
  export { createFetchHandler, type FetchHandlerOptions, type FetchHandlerPorts, type FetchLikeRequest, } from './fetch-handler.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export { cleanupCaches, } from './cache-cleanup.js';
1
2
  export { CACHE_STRATEGIES, createCacheStrategyDecider, decideCacheStrategy, } from './cache-strategy.js';
2
3
  export { createFetchHandler, } from './fetch-handler.js';
3
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,mBAAmB,GAIpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,GAInB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,GAId,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,mBAAmB,GAIpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,GAInB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './index.js';
2
+ //# sourceMappingURL=worker-entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-entry.d.ts","sourceRoot":"","sources":["../src/worker-entry.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './index.js';
2
+ //# sourceMappingURL=worker-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-entry.js","sourceRoot":"","sources":["../src/worker-entry.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
package/dist/worker.js ADDED
@@ -0,0 +1,155 @@
1
+ this.globalThis = this.globalThis || {};
2
+ this.globalThis.BaukitPwa = (function(exports) {
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ //#region src/cache-cleanup.ts
5
+ async function cleanupCaches(options) {
6
+ const selected = (await options.ports.listCacheNames()).filter(options.shouldDelete);
7
+ const deleted = await Promise.all(selected.map((name) => options.ports.deleteCache(name)));
8
+ return {
9
+ matchedCount: selected.length,
10
+ deletedCount: deleted.filter(Boolean).length
11
+ };
12
+ }
13
+ //#endregion
14
+ //#region src/cache-strategy.ts
15
+ const CACHE_STRATEGIES = {
16
+ cacheFirst: "cache-first",
17
+ networkFirst: "network-first",
18
+ networkOnly: "network-only",
19
+ staleWhileRevalidate: "stale-while-revalidate"
20
+ };
21
+ const DEFAULT_API_PATH_PREFIXES = ["/api"];
22
+ const DEFAULT_STATIC_DESTINATIONS = [
23
+ "audio",
24
+ "font",
25
+ "image",
26
+ "script",
27
+ "style",
28
+ "video",
29
+ "worker"
30
+ ];
31
+ const DEFAULT_STATIC_EXTENSIONS = [
32
+ "avif",
33
+ "css",
34
+ "gif",
35
+ "ico",
36
+ "jpeg",
37
+ "jpg",
38
+ "js",
39
+ "json",
40
+ "mjs",
41
+ "mp3",
42
+ "mp4",
43
+ "ogg",
44
+ "otf",
45
+ "png",
46
+ "svg",
47
+ "ttf",
48
+ "webmanifest",
49
+ "webp",
50
+ "woff",
51
+ "woff2"
52
+ ];
53
+ function matchesPathPrefix(pathname, prefix) {
54
+ if (prefix.length === 0) return false;
55
+ const normalized = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
56
+ return pathname === normalized || pathname.startsWith(`${normalized}/`);
57
+ }
58
+ function matchesAnyPathPrefix(pathname, prefixes) {
59
+ return prefixes.some((prefix) => matchesPathPrefix(pathname, prefix));
60
+ }
61
+ function hasStaticExtension(pathname, extensions) {
62
+ const lastSegment = pathname.slice(pathname.lastIndexOf("/") + 1);
63
+ const dotIndex = lastSegment.lastIndexOf(".");
64
+ if (dotIndex <= 0) return false;
65
+ const extension = lastSegment.slice(dotIndex + 1).toLowerCase();
66
+ return extensions.includes(extension);
67
+ }
68
+ function startsWithAny(pathname, prefixes) {
69
+ return prefixes.some((prefix) => prefix.length > 0 && pathname.startsWith(prefix));
70
+ }
71
+ function decideCacheStrategyDecision(request, options) {
72
+ const { appOrigin, apiPathPrefixes = DEFAULT_API_PATH_PREFIXES, navigationFallback, neverCachedPathPrefixes = [], staticPathPrefixes = [], staticDestinations = DEFAULT_STATIC_DESTINATIONS, staticExtensions = DEFAULT_STATIC_EXTENSIONS, apiStrategy = CACHE_STRATEGIES.networkFirst, staticStrategy = CACHE_STRATEGIES.cacheFirst, defaultStrategy = CACHE_STRATEGIES.networkFirst } = options;
73
+ const parsed = new URL(request.url, appOrigin);
74
+ const { pathname } = parsed;
75
+ const sameOrigin = parsed.origin === appOrigin;
76
+ if (matchesAnyPathPrefix(pathname, neverCachedPathPrefixes)) return { strategy: CACHE_STRATEGIES.networkOnly };
77
+ if ((request.method ?? "GET").toUpperCase() !== "GET") return { strategy: CACHE_STRATEGIES.networkOnly };
78
+ if (matchesAnyPathPrefix(pathname, apiPathPrefixes)) return { strategy: apiStrategy };
79
+ if (request.mode === "navigate") {
80
+ if (navigationFallback !== void 0 && sameOrigin) return {
81
+ strategy: defaultStrategy,
82
+ navigationFallback
83
+ };
84
+ return { strategy: defaultStrategy };
85
+ }
86
+ const staticAsset = request.destination !== void 0 && staticDestinations.includes(request.destination) || hasStaticExtension(pathname, staticExtensions) || startsWithAny(pathname, staticPathPrefixes);
87
+ return { strategy: sameOrigin && staticAsset ? staticStrategy : defaultStrategy };
88
+ }
89
+ function decideCacheStrategy(request, options) {
90
+ return decideCacheStrategyDecision(request, options).strategy;
91
+ }
92
+ function createCacheStrategyDecider(options) {
93
+ return (request) => decideCacheStrategy(request, options);
94
+ }
95
+ //#endregion
96
+ //#region src/fetch-handler.ts
97
+ async function cacheFirst(request, ports) {
98
+ const cached = await ports.matchCache(request);
99
+ if (cached !== void 0) return cached;
100
+ const response = await ports.fetch(request);
101
+ if (ports.isCacheable(response)) await ports.putCache(CACHE_STRATEGIES.cacheFirst, request, ports.cloneResponse(response));
102
+ return response;
103
+ }
104
+ async function networkFirst(request, ports) {
105
+ try {
106
+ const response = await ports.fetch(request);
107
+ if (ports.isCacheable(response)) await ports.putCache(CACHE_STRATEGIES.networkFirst, request, ports.cloneResponse(response));
108
+ return response;
109
+ } catch (error) {
110
+ const cached = await ports.matchCache(request);
111
+ if (cached !== void 0) return cached;
112
+ throw error;
113
+ }
114
+ }
115
+ async function staleWhileRevalidate(request, ports, onRevalidateError) {
116
+ const cached = await ports.matchCache(request);
117
+ const revalidate = (async () => {
118
+ const response = await ports.fetch(request);
119
+ if (ports.isCacheable(response)) await ports.putCache(CACHE_STRATEGIES.staleWhileRevalidate, request, ports.cloneResponse(response));
120
+ return response;
121
+ })();
122
+ if (cached === void 0) return revalidate;
123
+ revalidate.catch((error) => {
124
+ onRevalidateError?.(error);
125
+ });
126
+ return cached;
127
+ }
128
+ function createFetchHandler(options) {
129
+ const { ports, onRevalidateError, ...strategyOptions } = options;
130
+ return async (request) => {
131
+ const { strategy, navigationFallback } = decideCacheStrategyDecision(request, strategyOptions);
132
+ try {
133
+ switch (strategy) {
134
+ case CACHE_STRATEGIES.networkOnly: return await ports.fetch(request, { cache: "no-store" });
135
+ case CACHE_STRATEGIES.cacheFirst: return await cacheFirst(request, ports);
136
+ case CACHE_STRATEGIES.staleWhileRevalidate: return await staleWhileRevalidate(request, ports, onRevalidateError);
137
+ case CACHE_STRATEGIES.networkFirst: return await networkFirst(request, ports);
138
+ }
139
+ } catch (error) {
140
+ if (navigationFallback !== void 0) {
141
+ const fallback = await ports.matchCache(navigationFallback);
142
+ if (fallback !== void 0) return fallback;
143
+ }
144
+ throw error;
145
+ }
146
+ };
147
+ }
148
+ //#endregion
149
+ exports.CACHE_STRATEGIES = CACHE_STRATEGIES;
150
+ exports.cleanupCaches = cleanupCaches;
151
+ exports.createCacheStrategyDecider = createCacheStrategyDecider;
152
+ exports.createFetchHandler = createFetchHandler;
153
+ exports.decideCacheStrategy = decideCacheStrategy;
154
+ return exports;
155
+ })({});
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@baukit/pwa-web",
3
- "version": "0.2.1",
4
- "description": "Pure-function caching-strategy decisions for PWA service workers.",
3
+ "version": "0.4.0",
4
+ "description": "Cache strategy, cleanup, and a classic worker artifact for web PWAs.",
5
5
  "license": "MIT",
6
6
  "author": "Patrick Koss",
7
7
  "homepage": "https://github.com/PatrickKoss/baukit#readme",
@@ -21,27 +21,33 @@
21
21
  "node": ">=24.0.0"
22
22
  },
23
23
  "types": "./dist/index.d.ts",
24
+ "sideEffects": [
25
+ "./dist/worker.js"
26
+ ],
24
27
  "exports": {
25
28
  ".": {
26
29
  "types": "./dist/index.d.ts",
27
30
  "import": "./dist/index.js",
28
31
  "require": "./dist/cjs/index.js"
29
- }
32
+ },
33
+ "./worker": "./dist/worker.js"
30
34
  },
31
35
  "files": [
32
36
  "dist",
33
37
  "README.md",
34
38
  "LICENSE"
35
39
  ],
36
- "scripts": {
37
- "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node --input-type=module -e \"import { writeFileSync } from 'node:fs'; writeFileSync('dist/cjs/package.json', '{\\\"type\\\":\\\"commonjs\\\"}\\\\n')\"",
38
- "prepare": "pnpm run build",
39
- "test": "vitest run --config ../../vitest.config.ts",
40
- "lint": "eslint src --max-warnings=0",
41
- "format:check": "prettier --check src package.json tsconfig.json tsconfig.cjs.json"
42
- },
43
40
  "devDependencies": {
41
+ "rolldown": "1.2.3",
44
42
  "typescript": "6.0.3",
45
43
  "vitest": "4.1.10"
44
+ },
45
+ "scripts": {
46
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node scripts/build-worker.mjs && node --input-type=module -e \"import { writeFileSync } from 'node:fs'; writeFileSync('dist/cjs/package.json', '{\\\"type\\\":\\\"commonjs\\\"}\\\\n')\"",
47
+ "test": "pnpm run build && vitest run --config ../../vitest.config.ts && pnpm run test:artifact && pnpm run test:package",
48
+ "test:artifact": "node scripts/test-artifact.mjs",
49
+ "test:package": "node scripts/test-package.mjs",
50
+ "lint": "eslint src --max-warnings=0",
51
+ "format:check": "prettier --check src scripts package.json tsconfig.json tsconfig.cjs.json"
46
52
  }
47
- }
53
+ }