https://github.com/willfarrell/workbee
A tiny ServiceWorker for secure web applications.
https://github.com/willfarrell/workbee
Last synced: about 13 hours ago
JSON representation
A tiny ServiceWorker for secure web applications.
- Host: GitHub
- URL: https://github.com/willfarrell/workbee
- Owner: willfarrell
- License: mit
- Created: 2022-10-13T21:40:50.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2026-04-20T11:03:01.000Z (6 days ago)
- Last Synced: 2026-04-20T13:13:43.968Z (6 days ago)
- Language: JavaScript
- Homepage: https://workbee.js.org
- Size: 1.11 MB
- Stars: 5
- Watchers: 1
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Codeowners: .github/CODEOWNERS
- Security: SECURITY.md
Awesome Lists containing this project
README
A tiny ServiceWorker for secure web applications.
You can read the documentation at: https://workbee.js.org
## Features
- Free to use under MIT license
- Small and modular (up to 1KB minify + brotli)
- Tree-shaking supported
- Zero (0) dependencies
- GDPR Compliant
## Browser Support
WorkBee requires [ServiceWorker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) and [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) support.
| Browser | Minimum Version |
|---------|----------------|
| Chrome | 85+ |
| Edge | 85+ |
| Firefox | 80+ |
| Safari | 15.4+ |
| Opera | 71+ |
The `@work-bee/offline` package additionally requires [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) (supported in all browsers listed above).
## Getting Started
### Install
```bash
npm install @work-bee/core
```
### Basic Setup
```js
import { compileConfig, eventInstall, eventActivate, eventFetch, strategyCacheFirst } from '@work-bee/core'
const config = compileConfig({
cachePrefix: '1-',
//precache: ['/path/to/file.ext'],
routes: [
{
methods: ['GET'],
pathPattern: new RegExp('/img/(.+)$'),
cacheName: 'img',
strategy: strategyCacheFirst
},
...
]
})
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
## Events
### Install
Handles pre-caching of resources.
### Activate
Cleans up old caches.
### Fetch
Routes requests through configured strategies and middleware.
### Push (future)
### BackgroundFetch (future)
```js
import { backgroundFetchSuccessEvent, backgroundFetchFailEvent } from '@work-bee/core'
...
self.addEventListener('backgroundfetchsuccess', (event) => {
backgroundFetchSuccessEvent(event)
})
self.addEventListener('backgroundfetchfail', (event) => {
backgroundFetchFailEvent(event)
})
```
## Strategies
### Network Only
```mermaid
sequenceDiagram
participant Page
participant ServiceWorker
participant Cache
participant Network
autonumber
Page->>ServiceWorker: request
ServiceWorker->>Network: fetch
Network->>ServiceWorker: 200: OK
ServiceWorker->>Page: response
```
### Cache Only
```mermaid
sequenceDiagram
participant Page
participant ServiceWorker
participant Cache
participant Network
autonumber
Page->>ServiceWorker: request
ServiceWorker->>Cache: match
Cache->>ServiceWorker: resolve
ServiceWorker->>Page: response
```
### Network First
```mermaid
sequenceDiagram
participant Page
participant ServiceWorker
participant Cache
participant Network
autonumber
Page->>ServiceWorker: request
ServiceWorker->>Network: fetch
Network-->>ServiceWorker: 404: Not Found
ServiceWorker->>Cache: match
Cache->>ServiceWorker: resolve
ServiceWorker->>Page: response
```
### Cache First
```mermaid
sequenceDiagram
participant Page
participant ServiceWorker
participant Cache
participant Network
autonumber
Page->>ServiceWorker: request
ServiceWorker->>Cache: Cache: Not Found
ServiceWorker->>Network: fetch
Network->>ServiceWorker: 200: OK
ServiceWorker->>Page: response
```
### Ignore
Always returns a synthetic `504 Gateway Timeout` response without hitting cache or network. Useful for cheaply short-circuiting a route you do not want the ServiceWorker to handle.
```javascript
import { strategyIgnore } from '@work-bee/core'
const config = {
strategy: strategyIgnore
}
```
### Cache First Ignore
Tries the cache; if the entry is missing or expired, returns the same synthetic `504` as `strategyIgnore` instead of hitting the network. Use when you want to serve only primed cache and fail fast otherwise.
```javascript
import { strategyCacheFirstIgnore } from '@work-bee/core'
const config = {
strategy: strategyCacheFirstIgnore
}
```
### Static
Returns a pre-built `Response` (cloned on each call) for every request. Passing an `Error` instead makes the strategy reject — the failure flows through the normal error-path middleware.
```javascript
import { strategyStatic } from '@work-bee/core'
const offlineResponse = new Response('offline', {
status: 503,
headers: { 'Content-Type': 'text/plain' }
})
const config = {
strategy: strategyStatic(offlineResponse)
}
```
### StaleWhileRevalidate
```mermaid
sequenceDiagram
participant Page
participant ServiceWorker
participant Cache
participant Network
autonumber
Page->>ServiceWorker: request
par
ServiceWorker->>strategyCacheFirst: fetch
strategyCacheFirst->>ServiceWorker: 200: OK
ServiceWorker->>Page: response
and Revalidate
ServiceWorker->>strategy: fetch
strategy->>ServiceWorker: 200: OK
ServiceWorker->>Cache: put
end
```
### Partition
```mermaid
sequenceDiagram
participant Page
participant ServiceWorker
participant Cache
participant Network
autonumber
Page->>ServiceWorker: request
par routes[0]
ServiceWorker->>strategy: fetch
strategy->>ServiceWorker: 200: OK
and routes[...]
ServiceWorker->>strategy: fetch
strategy->>ServiceWorker: 200: OK
end
ServiceWorker->>Page: response
```
### HTML Partitioning
Breaks a page into parts that can each have their own strategy. ie ``, ``, ``, and `` where only the `` may need to be requested when multiple pages are being viewed (`` in this case should bootstrap the `` using js).
```mermaid
sequenceDiagram
participant Page
participant ServiceWorker
participant Cache
participant Network
autonumber
Page->>ServiceWorker: request
par header.html
ServiceWorker->>Cache: match
Cache->>ServiceWorker: resolve
and main.html
ServiceWorker->>Network: fetch
Network->>ServiceWorker: 200: OK
and footer.html
ServiceWorker->>Cache: match
Cache->>ServiceWorker: resolve
end
ServiceWorker->>Page: response
```
### Request Partitioning
```javascript
import { compileConfig, eventInstall, eventActivate, eventFetch, strategyCacheFirst, strategyPartition } from '@work-bee/core'
const config = compileConfig({
cachePrefix: 'sw-VERSION-',
routes: [
{
methods: ['GET'],
pathPattern: new RegExp('/api/data$'),
cacheName: 'data',
strategy: strategyPartition(compileConfig({
strategy: strategyCacheFirst,
cacheName: 'strategyPartition',
makeRequests: () => []
})),
cacheControlMaxAge: -1
},
...
]
})
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
```mermaid
sequenceDiagram
participant Page
participant ServiceWorker
participant Cache
participant Network
autonumber
Page->>ServiceWorker: request
par ?year=2000
ServiceWorker->>Cache: match
Cache->>ServiceWorker: resolve
and ?year=2001
ServiceWorker->>Cache: no-match
ServiceWorker->>Network: fetch
Network->>ServiceWorker: 200: OK
end
ServiceWorker->>Page: response
```
## Middleware
### SaveData
Choose strategy based on if Save-Data is enabled.
### Session Management
See [@work-bee/session](https://workbee.js.org/docs/packages/session) for authentication token management, session expiry, and inactivity detection.
### Offline Request Enqueue
## Examples
https://serviceworke.rs
https://github.com/mdn/serviceworker-cookbook
### Caching strategies
#### [Network or cache](https://serviceworke.rs/strategy-network-or-cache.html)
```javascript
/* eslint-env: serviceworker */
import { strategyNetworkFirst } from '@work-bee/core'
const config = {
strategy: strategyNetworkFirst
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
#### [Cache only](https://serviceworke.rs/strategy-cache-only.html)
```javascript
/* eslint-env: serviceworker */
import { strategyCacheOnly } from '@work-bee/core'
const config = {
strategy: strategyCacheOnly
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
#### [Cache and update](https://serviceworke.rs/strategy-cache-and-update.html)
```javascript
/* eslint-env: serviceworker */
import { strategyStaleWhileRevalidate } from '@work-bee/core'
const config = {
strategy: strategyStaleWhileRevalidate
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
#### Stale if error
Tries the network, caches successful responses gated by `Cache-Control` max-age, and on a thrown error or 5xx response falls back to any cached copy (even expired). Throws the original error only when both network and cache fail.
```javascript
/* eslint-env: serviceworker */
import { strategyStaleIfError } from '@work-bee/core'
const config = {
strategy: strategyStaleIfError
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
#### [Cache, update and refresh](https://serviceworke.rs/strategy-cache-update-and-refresh.html)
Not yet implemented.
#### [Embedded fallback](https://serviceworke.rs/strategy-embedded-fallback.html)
```javascript
/* eslint-env: serviceworker */
import { strategyNetworkOnly, strategyCacheOnly } from '@work-bee/core'
import fallbackMiddleware from '@work-bee/fallback'
const fallback = fallbackMiddleware({ path: '/path/to/fallback' })
const config = {
precache: {
routes: [
{
path: '/path/to/fallback'
}
],
strategy: strategyCacheOnly
},
strategy: strategyNetworkOnly,
after: [fallback.after]
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
### Offline
#### [Offline fallback](https://serviceworke.rs/offline-fallback.html)
```javascript
/* eslint-env: serviceworker */
import { strategyNetworkOnly, strategyCacheOnly } from '@work-bee/core'
import fallbackMiddleware from '@work-bee/fallback'
const fallback = fallbackMiddleware({
path: '/path/to/offline',
statusCodes: [503, 504] // or Error
})
const config = {
precache: {
routes: [
{
path: '/path/to/offline'
}
],
strategy: strategyCacheOnly
},
strategy: strategyNetworkOnly,
after: [fallback.after]
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
#### [Offline Status](https://serviceworke.rs/offline-status.html)
```javascript
/* eslint-env: serviceworker */
import { strategyCacheFirst, strategyCacheOnly } from '@work-bee/core'
const config = {
precache: {
routes: [
{
path: '/path/to/required'
},
...
],
eventType: 'precache'
},
strategy: strategyCacheFirst
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
#### [JSON Cache](https://serviceworke.rs/json-cache.html)
```javascript
/* eslint-env: serviceworker */
import { strategyCacheFirst, strategyCacheOnly } from '@work-bee/core'
const config = {
precache: {
routes: '/path/to/precache.json',
},
strategy: strategyCacheFirst
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
### Beyond Offline
#### [Local Download](https://serviceworke.rs/local-download.html)
Not yet implemented.
#### [Virtual Server](https://serviceworke.rs/virtual-server.html)
#### [API Analytics](https://serviceworke.rs/api-analytics.html)
Not yet implemented.
#### [Load balancer](https://serviceworke.rs/load-balancer.html)
Not yet implemented.
#### [Cache from ZIP](https://serviceworke.rs/cache-from-zip.html)
Not yet implemented.
#### [Dependency Injection](https://serviceworke.rs/dependency-injector.html)
Not yet implemented.
#### [Request Deferrer](https://serviceworke.rs/request-deferrer.html)
```javascript
/* eslint-env: serviceworker */
import { strategyCacheFirst } from '@work-bee/core'
import offlineMiddleware from '@work-bee/offline'
const offline = offlineMiddleware({ pollDelay: 0 })
const config = {
strategy: strategyCacheFirst,
middlewares: [offline]
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
addEventListener('message', (event) => {
const { data } = event
event.waitUntil(messageEvents[data.type](data))
})
const messageEvents = {
online: offline.postMessageEvent
}
```
### Performance
#### [Cache then Network](https://serviceworke.rs/cache-then-network.html)
```javascript
/* eslint-env: serviceworker */
import { strategyCacheFirst } from '@work-bee/core'
const config = {
strategy: strategyCacheFirst
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
```
#### [Render Store](https://serviceworke.rs/render-store.html)
```javascript
/* eslint-env: serviceworker */
import {
compileConfig,
eventInstall,
eventActivate,
eventFetch,
cacheOverrideEvent
} from '@work-bee/core'
const config = compileConfig({
strategy: strategyNetworkFirst
})
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})
addEventListener('message', (event) => {
const { data } = event
/* data = {
type: 'cache',
request: new Request('/path/to/template', {method:'GET'}),
response: new Response('')
}*/
event.waitUntil(messageEvents[data.type](data))
})
const messageEvents = {
cache: cacheOverrideEvent(config)
}
```
## License
Licensed under [MIT License](LICENSE). Copyright (c) 2026 [will Farrell](https://github.com/willfarrell) and the [Workbee contributors](https://github.com/willfarrell/workbee/graphs/contributors).