Workbox Workflow Integration Plan

Status: Example implemented with injected policy

examples/static-mpa-workbox-offline demonstrates the current preferred Workbox integration model.

Domstack does not run Workbox injectManifest and does not generate a JavaScript global loaded with importScripts().

Instead, hooks.manifestBuilt computes a Workbox-oriented policy from the finalized Domstack manifest and injects it into the final /service-worker.js bundle with context.defineServiceWorkerConstant().

The authored service worker remains normal user code.

It imports Workbox packages directly and passes the generated precacheManifest field to Workbox APIs.

Goals

Non-goals

Current example architecture

The Workbox example uses:

The manifest settings hook injects a policy constant:

context.defineServiceWorkerConstant('__DOMSTACK_WORKBOX_POLICY__', {
  version: manifest.version,
  offlineFallbackUrl: '/offline/',
  precacheManifest: [
    { url: '/', revision: 'sha256hex...' },
    { url: '/about/', revision: 'sha256hex...' },
    { url: '/global-ABC123.css', revision: null, integrity: 'sha256-...' },
  ],
  runtimeUrls: ['/progressive-cache/'],
  networkOnlyUrls: ['/admin/'],
})

The service worker reads the injected policy inside the manifest-enabled branch:

declare const __DOMSTACK_WORKBOX_POLICY__: StaticMpaWorkboxServiceWorkerPolicy

if (manifestEnabled) {
  const policy = __DOMSTACK_WORKBOX_POLICY__
  precacheAndRoute(policy.precacheManifest)
}

The policy constant must not be read at module top level in watch mode.

Watch mode builds do not define it.

Workbox APIs currently used

The example uses:

Policy shape

Workbox’s native precache input is:

type WorkboxPrecacheEntry = {
  url: string
  revision: string | null
  integrity?: string
}

The example policy includes that native shape plus app-specific route policy:

type StaticMpaWorkboxServiceWorkerPolicy = {
  version: string
  offlineFallbackUrl: string
  precacheManifest: WorkboxPrecacheEntry[]
  runtimeUrls: string[]
  networkOnlyUrls: string[]
}

Only precacheManifest is passed directly to Workbox precaching.

runtimeUrls, networkOnlyUrls, and offlineFallbackUrl are app policy and are mapped explicitly to Workbox routing/strategy APIs.

Why injected policy is preferred

Injected policy has these advantages:

This means Domstack only needs the general manifestBuilt hook and final service-worker build step.

It does not need Workbox-specific source transformation in core.

Watch mode

Workbox precaching is disabled in watch mode.

Watch mode sets DOMSTACK_MANIFEST_ENABLED=false and does not run the manifest/policy injection path.

The service worker branch for watch mode:

The client also unregisters existing workers and clears known caches in watch mode.

Watch builds disable esbuild splitting so /service-worker.js stays self-contained during cleanup.

Client lifecycle

The Workbox example uses workbox-window because it provides cleaner lifecycle events than hand-rolled registration logic.

Current behavior:

Watch-mode cleanup happens before normal registration and does not wait for window.load.

Production registration waits for window.load.

Runtime caching policy

The example only runtime-caches routes selected by manifest vars.

It uses Workbox plugins to keep runtime cache behavior bounded:

The example does not cache arbitrary API/data requests by default.

Potential package helper

A future helper could live outside core or as an optional export:

import { createWorkboxPolicy } from '@domstack/static/workbox'

export default {
  hooks: {
    manifestBuilt: [context => {
      context.defineServiceWorkerConstant(
        '__APP_WORKBOX_POLICY__',
        createWorkboxPolicy(context.manifest, options),
      )
    }],
  },
}

The helper could cover:

This should remain optional.

Domstack core should continue exposing generic manifest hooks rather than hard-coding Workbox behavior.

Deprecated ideas

These ideas were considered but are not the current direction:

They remain possible for external integrations, but the example and current plan prefer injected constants.