https://github.com/willfarrell/workbee
A tiny ServiceWorker for secure web applications.
https://github.com/willfarrell/workbee
Last synced: 3 months 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: 2025-04-15T19:15:15.000Z (11 months ago)
- Last Synced: 2025-04-15T20:24:40.715Z (11 months ago)
- Language: JavaScript
- Size: 1.54 MB
- Stars: 2
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# The WorkBee ServiceWorker 🐝
A tiny ServiceWorker for secure web applications.
## Features
- Free to use under MIT license
- Small and modular (up to 1KB minify + brotli)
- Tree-shaking supported
- Zero (0) dependencies
- GDPR Compliant
## TODO
- improve test coverage
- more e2e tests
- documentation
- session management
- Push events
- Range Requests strategy
- precache extract zip
- workbox comparison (~1KB vs ~70KB)
## Getting Started
### Install
```bash
npm install @work-bee/core
```
### Basic Setup
```js
import { compileConfig, installEvent, activateEvent, fetchEvent, 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) => {
installEvent(event, config)
})
addEventListener('activate', (event) => {
activateEvent(event, config)
})
addEventListener('fetch', (event) => {
fetchEvent(event, config)
})
```
## Events
### Install
### Activate
### Fetch
### Push (future)
### BackgroundFetch (future)
```js
import { backgroundFetchSuccessEvent, backgroundFetchFailEvent } from '@work-bee/core'
...
self.addEventListener('backgroundfetchsuccess', (event) => {
backgroundFetchSuccessEvent(event, config)
})
self.addEventListener('backgroundfetchfail', (event) => {
backgroundFetchFailEvent(event, config)
})
```
## 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
```
### 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 } @work-bee/core
import { installEvent, activateEvent, fetchEvent } from '@work-bee/events'
import { strategyCacheFirst, strategyPartition } from '@work-bee/strategies'
const config = compileConfig({
cachePrefix, 'sw-VERSION-',
routes: [
{
methods: ['GET'],
pathPattern: new RegExp('/api/data$'),
cacheName: 'data',
strategy: strategyPartition(compileConfig({
strategy: strategyCacheFirst,
cacheName: 'strategyPartition',
makeRequests: () => []
}),
cacheMaxAgeInSeconds: -1
},
...
]
})
addEventListener('install', (event) => {
installEvent(event, config)
})
addEventListener('activate', async (event) => {
await activateEvent(event, config)
})
addEventListener('fetch', (event) => {
fetchEvent(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 (future)
```js
```
### 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)
})
```
#### [Cache, update and refresh](https://serviceworke.rs/strategy-cache-update-and-refresh.html)
TODO refresh middleware, add to strategyStaleWhileRevalidate?
#### [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)
})
```
### Web Push
### General Usage
### 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',
extract: // TODO
},
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)
```javascript
/* eslint-env: serviceworker */
/*import { strategyLocalDownload } from '@work-bee/core'
const config = {
routes: [
{
pathPattern: new Regexp('/path/to/download/.*$'),
strategy: strategyLocalDownload
}
]
}
addEventListener('install', (event) => {
eventInstall(event, config)
})
addEventListener('activate', (event) => {
eventActivate(event, config)
})
addEventListener('fetch', (event) => {
eventFetch(event, config)
})*/
```
#### [Virtual Server](https://serviceworke.rs/virtual-server.html)
#### [API Analytics](https://serviceworke.rs/api-analytics.html)
TODO 3rd party middleware - ie plausible
#### [Load balancer](https://serviceworke.rs/load-balancer.html)
TODO new middleware.before - group external domains to allow request path replace & ping all to pick best
#### [Cache from ZIP](https://serviceworke.rs/cache-from-zip.html)
TODO new plugin - return empty response, iterate over contents and cachePut
#### [Dependency Injection](https://serviceworke.rs/dependency-injector.html)
TODO need to understand more
#### [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.onlineEvent
}
```
### 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)
}
```