ts-service-worker 1.3.1 → 1.3.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/README.md +72 -69
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Automate the build of a
|
|
1
|
+
Automate the build of a strongly-typed service worker that manages file caching. The idea is simple: Given a declarative **Plan** and options, this package outputs a functional browser-ready Javascript service worker file that manages all resource caching.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- provide declarative support for the most common caching strategies
|
|
3
|
+
In particular:
|
|
4
|
+
- provides declarative support for the most common caching strategies
|
|
6
5
|
- supports easy integration of multiple caching strategies
|
|
7
6
|
- produces a `service-worker.js` that requires no additional transcompilation
|
|
8
7
|
- avoids nesting and promise chains
|
|
@@ -11,17 +10,14 @@ Automate the build of a complex, strongly-typed service worker.
|
|
|
11
10
|
- provides built-in debug logging
|
|
12
11
|
- provides clear and simple cache purging rules
|
|
13
12
|
|
|
14
|
-
Copyright (c) 2023-
|
|
13
|
+
Copyright (c) 2023-2025 Andrew J. Peterson, dba NDP Software All Rights Reserved.
|
|
15
14
|
|
|
16
15
|
#### NOTICE
|
|
17
|
-
This is in testing... It works on two
|
|
18
|
-
|
|
19
|
-
## Overview
|
|
20
|
-
The idea is simple. You provide TS-Service-Worker with a **Plan** (and options), and it outputs a functional Javascript file that can be sent to the browser. The **Plan** is a series of caching strategies to be applied, along with the paths they should be applied to.
|
|
16
|
+
This is in testing... It works on two production projects. I'd love people to try it out and provide feedback.
|
|
21
17
|
|
|
22
18
|
## Examples
|
|
23
19
|
|
|
24
|
-
The very simplest service worker might provide an offline backup of the whole site:
|
|
20
|
+
The very simplest service worker might provide an offline backup of the whole site. This uses one strategy:
|
|
25
21
|
```ts
|
|
26
22
|
import {requestHandler} from "ts-service-worker";
|
|
27
23
|
|
|
@@ -30,10 +26,11 @@ export const plan: Plan = [
|
|
|
30
26
|
]
|
|
31
27
|
app.get('/service-worker.js', requestHandler(plan))
|
|
32
28
|
```
|
|
33
|
-
This plan says, "intercept all the requests, and serve them from the network if available; otherwise, serve from the cache."
|
|
29
|
+
This plan says, "intercept all the requests, and serve them from the network if available; otherwise, serve from the cache."
|
|
30
|
+
Although this is just one example, it demonstrates the declarative configuration. In real life (and despite service worker documentation!), a simple caching strategy like this is seldom-- if ever-- sufficient.
|
|
34
31
|
|
|
35
32
|
Some of the deficiencies of this strategy:
|
|
36
|
-
- it only caches paths that have been visited
|
|
33
|
+
- it only caches paths that have been visited already
|
|
37
34
|
- provides no way to update cached files
|
|
38
35
|
- caches everything fetched, even unimportant resources
|
|
39
36
|
|
|
@@ -41,12 +38,15 @@ A real caching plan requires more complexity and nuance. Here is part of a sligh
|
|
|
41
38
|
```ts
|
|
42
39
|
export const plan: Plan =
|
|
43
40
|
[
|
|
44
|
-
{
|
|
45
|
-
{
|
|
46
|
-
{
|
|
41
|
+
{paths: ['/open-search.xml'], strategy: 'networkOnly'},
|
|
42
|
+
{paths: ['/my-data.json'], strategy: 'staleWhileRevalidate'},
|
|
43
|
+
{paths: [/.*\.html/], strategy: 'staticOfflineBackup',
|
|
44
|
+
backup: '/networkDown.html' }
|
|
47
45
|
]
|
|
48
46
|
```
|
|
49
|
-
Here, we see multiple strategies
|
|
47
|
+
Here, we see how multiple strategies work in consort. Each request is evaluated (sequentially) against the paths, and the first matching strategy is applied. If no strategy matches, the resource is fetched as normal.
|
|
48
|
+
|
|
49
|
+
Therefore, in this example:
|
|
50
50
|
- The path `open-search.xml` will never be cached,
|
|
51
51
|
- `/my-data.json` will be served from the cache and re-fetched in the background, available for a later access.
|
|
52
52
|
- Finally, the file `/networkDown.html` will be cached, and served as a replacement for other HTML pages when fetching fails.
|
|
@@ -56,9 +56,10 @@ Another example: a common strategy is to pre-cache some resources when the servi
|
|
|
56
56
|
{
|
|
57
57
|
strategy: 'cache-on-install',
|
|
58
58
|
files: {
|
|
59
|
-
dir:
|
|
60
|
-
glob:
|
|
61
|
-
prefix: '/img'
|
|
59
|
+
dir: 'src/assets/images', // Root of search
|
|
60
|
+
glob: '*.png', // match
|
|
61
|
+
prefix: '/img' // pre-pended to matched relative path
|
|
62
|
+
}
|
|
62
63
|
}
|
|
63
64
|
```
|
|
64
65
|
These are just a few examples of a caching plan. Each application is different and requires careful consideration and crafting. **TS-Service-Worker** makes developing a custom caching service worker easy and error-free.
|
|
@@ -71,8 +72,7 @@ There are several ways to use this:
|
|
|
71
72
|
|
|
72
73
|
### Within Express
|
|
73
74
|
|
|
74
|
-
This package exports a request handler called
|
|
75
|
-
This request handler builder is a function that accepts a **plan** and **options** and returns a function that can be used as a route handler. Here's an example of how to use it in an Express app:
|
|
75
|
+
This package exports a request handler called requestHandler. So using it directly within Express (or equivalent) is convenient and provides good Typescript type hints. This request handler builder is a function that accepts a Plan and options and returns a function that can be used as a route handler. Here's an example of how to use it in an Express app:
|
|
76
76
|
|
|
77
77
|
```ts
|
|
78
78
|
import { requestHandler } from 'ts-service-worker'
|
|
@@ -82,7 +82,7 @@ app.get('/service-worker.js', requestHandler([
|
|
|
82
82
|
// ...
|
|
83
83
|
]))
|
|
84
84
|
```
|
|
85
|
-
Note: you may want to manage your own caching of
|
|
85
|
+
Note: you may want to manage your own caching of the service worker file itself on the server.
|
|
86
86
|
|
|
87
87
|
### As a script
|
|
88
88
|
|
|
@@ -139,27 +139,26 @@ The CLI will generate the Javascript file from the specifications in a JSON file
|
|
|
139
139
|
|
|
140
140
|
1. Create a `sw-plan.json` file with `plan` and `options`. See below for available properties.
|
|
141
141
|
2. Run `> npx ts-service-worker sw-plan.json >dist/service-worker.js`
|
|
142
|
-
3. Serve the resultant file (`dist/service-worker.js`) as your service worker, and reference it in your HTML head.
|
|
143
|
-
4.
|
|
142
|
+
3. Serve the resultant file (`dist/service-worker.js`) as your service worker, and reference it in your HTML `<head>` section.
|
|
143
|
+
4. Edit the JSON file and regenerate as needed.
|
|
144
144
|
|
|
145
145
|
### Other
|
|
146
|
-
There are other ways to incorporate this into your build cycle. The library exposes a
|
|
146
|
+
There are other ways to incorporate this into your build cycle. The library exposes a function `generateServiceWorker()` which accepts a plan and options and generates the necessary Javascript (text). Let me know how you do it!
|
|
147
147
|
|
|
148
148
|
Note that the output of this tool needs _no_ post-processing, so DON'T DO IT. If the code output isn't compatible with a browser,
|
|
149
149
|
let me know and I'll fix it right away.
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
## Definitions
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
* `debug` – outputs log messages as files are fetched. This is too noisy for production, but may be useful to figure out what is going on with your service worker.
|
|
153
|
+
- A **Strategy** is a ruleset to fetch and cache resources based on matching URLs.
|
|
154
|
+
- A **Plan** is a set of strategies used together to manage the caching of resources for a web app.
|
|
155
|
+
- A **Path expression** is one or more strings or regular expressions that describe a set of resources.
|
|
156
|
+
- **Options** are non-strategy settings for a Plan, governing how the service worker itself is managed.
|
|
158
157
|
|
|
159
158
|
## Paths
|
|
160
159
|
|
|
161
160
|
Paths expressions, used for most strategies, will match URLs requested from your website. The path property can be:
|
|
162
|
-
- **a string URL path**. This is interpreted as the meaning the full path to the resource.
|
|
161
|
+
- **a string URL path**. This is interpreted as the meaning the full path to the resource. This is usually a path from the root of the domain, but could be a full URL if desired.
|
|
163
162
|
- **a RegExp**, matching the full URL. This is useful for matching a set of URLs, such as all the images in a directory.
|
|
164
163
|
- **an array** of strings or RegExps, following the rules above
|
|
165
164
|
- **the imported symbol `OriginAndBelow`**, which matches everything under the service worker's domain.
|
|
@@ -169,47 +168,45 @@ Paths expressions, used for most strategies, will match URLs requested from your
|
|
|
169
168
|
|
|
170
169
|
These are the currently implemented strategies.
|
|
171
170
|
|
|
172
|
-
### cacheFirst
|
|
173
|
-
|
|
174
|
-
This strategy is ideal for CSS, images, fonts, JS, templates… basically anything you'd consider static to that "version" of your site. Per [https://web.dev/learn/pwa/serving/#cache-first](https://web.dev/learn/pwa/serving/#cache-first):
|
|
175
|
-
>> Using this strategy, the service worker looks for the matching request in the cache and returns the corresponding Response if it's cached. Otherwise it retrieves the response from the network (~~optionally,~~ updating the cache for future calls). If there is neither a cache response nor a network response, the request will error. Since serving assets without going to the network tends to be faster, this strategy prioritizes performance over freshness.
|
|
171
|
+
### `cacheFirst`
|
|
176
172
|
|
|
177
|
-
|
|
173
|
+
This strategy is ideal for CSS, images, fonts, JS, templates… basically anything you'd consider static to that "version" of your site.
|
|
174
|
+
"Using this strategy, the service worker looks for the matching request in the cache and returns the corresponding Response if it's cached. Otherwise it retrieves the response from the network (updating the cache for future calls). If there is neither a cache response nor a network response, the request will error." ([https://web.dev/learn/pwa/serving/#cache-first](https://web.dev/learn/pwa/serving/#cache-first))
|
|
178
175
|
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
And the gist of it:
|
|
177
|
+
> Since serving assets without going to the network tends to be faster, this strategy prioritizes performance over freshness.
|
|
181
178
|
|
|
182
|
-
|
|
179
|
+
There is no way to remove files from the browser cache individually. For this reason, only use this cache for _immutable resources_ (certain images and fingerprinted assets). If these do need to be updated, you will need to [rebuild the cache (see below)](#clearing-the-cache).
|
|
183
180
|
|
|
184
|
-
###
|
|
181
|
+
### `networkFirst`
|
|
182
|
+
This provides the user with a backup in case the network isn't available. It prioritizes updated content instead of performance. It won't speed up your site, but will allow it to keep working if the network is down, of flakey.
|
|
185
183
|
|
|
186
|
-
This
|
|
184
|
+
"This strategy ... checks if the request can be fulfilled from the network and, if it can't, tries to retrieve it from the cache. If there is neither a network response nor a cache response, the request will error. Getting the response from the network is usually slower than getting it from the cache." ([https://web.dev/learn/pwa/serving/#network-first](https://web.dev/learn/pwa/serving/#network-first))
|
|
187
185
|
|
|
188
|
-
> The stale while revalidate strategy returns a cached response immediately, then checks the network for an update, replacing the cached response if one is found. This strategy always makes a network request, because even if a cached resource is found, it will try to update what was in the cache with what was received from the network, to use the updated version in the next request. This strategy, therefore, provides a way for you to benefit from the quick serving of the cache first strategy and update the cache in the background.
|
|
189
186
|
|
|
190
|
-
|
|
187
|
+
### `staleWhileRevalidate`
|
|
191
188
|
|
|
192
|
-
|
|
189
|
+
This is useful for resources that could be slow to fetch and whose freshness is not critical.
|
|
190
|
+
"The stale while revalidate strategy returns a cached response immediately, then checks the network for an update, replacing the cached response if one is found. This strategy always makes a network request, because even if a cached resource is found, it will try to update what was in the cache with what was received from the network, to use the updated version in the next request. This strategy, therefore, provides a way for you to benefit from the quick serving of the cache first strategy and update the cache in the background.
|
|
193
191
|
|
|
194
|
-
|
|
192
|
+
"With this strategy, the assets are updated in the background. This means your users won't get the new version of the assets until the next time that path is requested. At that time the strategy will again check if a new version exists and the cycle repeats." ([https://web.dev/learn/pwa/serving/#stale-while-revalidate](https://web.dev/learn/pwa/serving/#stale-while-revalidate))
|
|
195
193
|
|
|
196
|
-
|
|
194
|
+
### `networkOnly`
|
|
195
|
+
"The network only strategy is similar to how browsers behave without a service worker or the Cache Storage API. Requests will only return a resource if it can be fetched from the network. This is often useful for resources like online-only API requests." ([https://web.dev/learn/pwa/serving/#network-only](https://web.dev/learn/pwa/serving/#network-only))
|
|
197
196
|
|
|
198
|
-
### staticOfflineBackup
|
|
197
|
+
### `staticOfflineBackup`
|
|
199
198
|
|
|
200
199
|
Use this strategy to provide a backup for a resource to be used only when a site is offline. This may be a "sorry" page, or could provide some reasonable alternative. This is commonly used for placeholder images or other content.
|
|
201
200
|
|
|
202
|
-
The backup resource is loaded
|
|
203
|
-
|
|
204
|
-
Note: this currently doesn't support inline SVGs that you see in some examples, but requires using a separate file.
|
|
201
|
+
The backup resource is loaded _when the service worker is installed_. If it is stale, you will need to invalidate the whole cache by bumping the minor version.
|
|
205
202
|
|
|
206
203
|
Note: If this backup URL is also a regular resource on the site, you can configure its behavior separately from `staticOfflineBackup`. It does **not** serve the "backup" URL from cache by default. If it's a normal app in the URL and you want it cached, also include a strategy like `networkFirst` in combination.
|
|
207
204
|
|
|
208
|
-
### cache-on-install
|
|
205
|
+
### `cache-on-install`
|
|
209
206
|
|
|
210
|
-
Cache specific
|
|
207
|
+
Cache specific resources when the service worker installs.
|
|
211
208
|
|
|
212
|
-
|
|
209
|
+
Because it needs to know the specific paths, _path matching wildcards and regular expression will not work._ For example, from within a running service worker, you can't find all files that end in `.png`. Therefore, you must use FULL resource paths. To facilitate this, this strategy can **scan directories of your local disk for resources.** When you have a whole directory of files to pre-cache, this will be the easy to maintain: use glob patterns to identify these as shown below. Here's one example:
|
|
213
210
|
|
|
214
211
|
```ts
|
|
215
212
|
plan: Plan = [
|
|
@@ -217,45 +214,51 @@ plan: Plan = [
|
|
|
217
214
|
strategy: 'cache on install',
|
|
218
215
|
files: {
|
|
219
216
|
glob: '**/*.png',
|
|
220
|
-
dir: 'src/images', // where to start searching
|
|
221
|
-
prefix: '/assets' // prefix applied to any matches
|
|
217
|
+
dir: 'src/images', // where to start searching on your disk
|
|
218
|
+
prefix: '/assets' // prefix applied to any matches in URLs
|
|
222
219
|
},
|
|
223
220
|
}
|
|
224
221
|
]
|
|
225
222
|
```
|
|
226
223
|
The globs are evaluated _when the `service-worker.js` file is created_, so updating resources will require rebuilding the service worker file.
|
|
227
224
|
|
|
228
|
-
##
|
|
225
|
+
## Options
|
|
226
|
+
|
|
227
|
+
The following may be provided as `options`, and they affect the generation of the Javascript code:
|
|
229
228
|
|
|
230
|
-
|
|
229
|
+
* `version` – semantic version of the worker. Major or minor version change implies (and forces) a reset of the cache on the browser. It has no other meaning.
|
|
230
|
+
* `skipWaiting` – executes the normal skip waiting logic, and allows your service worker to come into play without all the browser windows being closed.
|
|
231
|
+
* `debug` – outputs log messages as files are fetched. This is too noisy for production, but may be useful to figure out what is going on with your service worker.
|
|
231
232
|
|
|
232
|
-
#### Clearing the Cache
|
|
233
|
+
#### `Version` and Clearing the Cache
|
|
233
234
|
There are two components that relevant here:
|
|
234
235
|
|
|
235
236
|
- the service worker
|
|
236
237
|
- the cached files (or "responses")
|
|
237
238
|
|
|
238
|
-
When building your service worker, provide a `version` string in the options, following semvar conventions.
|
|
239
|
+
When building your service worker, provide a `version` string in the options, following semvar conventions.
|
|
239
240
|
|
|
240
|
-
|
|
241
|
+
There are two rules that apply to this version:
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
1. If you change this version at all, the service worker itself will be re-installed (because it has changed). This re-installation in itself, however, will _not_ affect the browser cache of responses.
|
|
244
|
+
|
|
245
|
+
2. If the _major or minor version_ changes, the cache (of responses) will be deleted and a complete new cache created. _With this library, the way to reset the cache in users' browsers is to bump the major or minor version number._ (To test locally you can use the browser's developer tools.)
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
That being said, you're not in complete control. Browsers will manage the actual response cache, removing items if it becomes too large, or perhaps stale. From my testing, though, I have not seen this happen.
|
|
245
248
|
|
|
246
249
|
#### Skip Waiting
|
|
247
250
|
|
|
248
|
-
Service workers themselves will re-install when they have changed-- based on the browsers' rules but typically a checksum of the `service-worker.js` code itself. This can be confusing at first, though, as new service workers doesn't come into play until it is activated. The default behavior for service workers is to wait until all the windows associated with the service worker
|
|
251
|
+
Service workers themselves will re-install when they have changed-- based on the browsers' rules but typically a checksum of the `service-worker.js` code itself. This can be confusing at first, though, as new service workers doesn't come into play until it is activated. The default behavior for service workers is to wait until all the windows associated with the service worker are closed, and only then, activate the new service worker.
|
|
249
252
|
|
|
250
|
-
If this isn't what you want, you can accelerate this by passing `skipWaiting: true` in the options. This will activate the service worker as soon as it is downloaded-- but still will not be available as the page first loads. Usually this means it will be ready on the next reload.
|
|
253
|
+
If this isn't what you want, you can accelerate this by passing `skipWaiting: true` in the options. This will activate the service worker as soon as it is downloaded-- but it still will not be available as the page first loads. Usually this means it will be ready on the next reload.
|
|
251
254
|
|
|
252
255
|
## Why TS-Service-Worker?
|
|
253
256
|
|
|
254
257
|
Service workers are a powerful tool for developers to improve the user experience, providing for fine-grained caching, offline usage and progressive web apps (PWAs). It replaced the coarse-grained _AppCache_, which proved quite hard to use (and was retired).
|
|
255
258
|
|
|
256
|
-
Unfortunately, the service worker API is also hard to use
|
|
259
|
+
Unfortunately, the service worker API is also hard to use, as it is quite low-level. It requires a series of nested promise chains, and in any real world situation, quickly grows complicated. In the documentation contains good examples describing single specific strategies. While these are readable, they are minimally useful because real-world usage requires integrating several of these strategies at the same time. This can be error-prone and hard to follow. And as anyone who has developed service workers knows, they are tedious to test because of the complex lifecycle. Examples provide no guidance, or examples, where multiple strategies are applied.
|
|
257
260
|
|
|
258
|
-
|
|
261
|
+
Another big hurdle for some developers is that since service workers run in the browser, they must be written in a browser-friendly version Javascript (not Typescript). This likely involves transcompilation, which will requiring additional complexity in the build process to produce an additional artifact. It can be quite challenging in a Typescript and JS-bundling project (most projects). The service-worker will require a different build path than the rest of your code because it must be distributed as a separate resource.
|
|
259
262
|
|
|
260
263
|
There are [other libraries](#other-libraries) designed to help, which may be useful to you. **TS-Service-Worker** aims to solve specific challenges.
|
|
261
264
|
|
|
@@ -275,7 +278,7 @@ Published using `npm publish`.
|
|
|
275
278
|
- [ ] Other matching mechanisms, like mime-type.
|
|
276
279
|
- [ ] https://web.dev/learn/pwa/serving/#cache-only
|
|
277
280
|
- [ ] filter/only strategy: to process only certain types, eg. `{ strategy: 'filter', paths: ' /https?:.*/ }`. This is weird semantically, so I'm still thinking.
|
|
278
|
-
- [
|
|
281
|
+
- [x] https://web.dev/learn/pwa/workbox/#offline-fallback
|
|
279
282
|
- [ ] https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#recovering_failed_requests
|
|
280
283
|
- [ ] offline SVG: https://github.com/veiss-com/sw-tools#offline
|
|
281
284
|
- [ ] https://github.com/veiss-com/sw-tools#limiting-the-cache-size
|
|
@@ -300,6 +303,6 @@ These are the basics of how service workers function:
|
|
|
300
303
|
|
|
301
304
|
## License
|
|
302
305
|
|
|
303
|
-
Copyright (c) 2023-
|
|
306
|
+
Copyright (c) 2023-2025 Andrew J. Peterson, dba NDP Software All Rights Reserved.
|
|
304
307
|
|
|
305
308
|
Available for licensing at reasonable rate. Please contact [NDP Software](mailto:andy@ndpsoftware.com?subject=ts-service-worker).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-service-worker",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "Generate caching service-worker.js using declarative Typescript. Copyright (c) 2023 NDP Software. All Rights Reserved.",
|
|
3
|
+
"version": "1.3.2",
|
|
4
|
+
"description": "Generate caching service-worker.js using declarative Typescript. Copyright (c) 2023-2025 NDP Software. All Rights Reserved.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Andrew Peterson, NDP Software",
|
|
7
7
|
"email": "andy@ndpsoftware.com"
|