Skip to content

Configuration Reference

Full TypeScript types: packages/core/src/types.ts.

Both Vite (ViteResourceFallbackOptions) and Webpack (WebpackPluginOptions) plugins use PluginOptions.

PluginOptions

FieldTypeDefaultDescription
rulesFallbackRule[]RequiredFallback rules; compilation sorts by descending base length so longer prefixes match first
defaults{ retry?, circuit? }Default retry/circuit config for all rules
debugboolean | 'auto''auto'true always logs; 'auto' controlled via localStorage.__RF_DEBUG__
sri'strip' | 'keep' | 'strict''strip'Strategy for handling integrity during fallback
enableDevbooleanfalseWhether to activate in dev mode
noncestringCSP nonce appended to every injected <script>, including the automatic inline install(...)
externalRuntimebooleanfalseExternalizes only the runtime IIFE; automatic install(...) stays inline and needs CSP authorization; it does not preserve build-config hooks
externalRuntimePathstring'/__rf/runtime.js'Path for the external runtime script
injectPreconnectbooleantrueInject <link rel="preconnect"> for each fallback domain
htmlInject'head-prepend' | 'head-append''head-prepend'Position in <head> for injection
serviceWorkerboolean | ServiceWorkerOptionsfalseEnable Hybrid SW for non-script subresources and controlled CSS @import
hooksRuntimeHooksFunctions are dropped during serialized injection; for auto-injected setups prefer DOM rf:* events
disableGlobalsstring[]['__RF_DISABLE__']Additional kill-switch global variable names
disableQueryParamstring'__rf'Query param name that disables runtime when set to off
disableCookiestring'__rf_disable'Cookie name that disables runtime when set to 1

FallbackRule

FieldTypeDefaultDescription
basestringRequiredAsset URL prefix (case-sensitive). Used for: prefix-matching failed URLs, stripping the path for candidate swap, and Vite bare filename → CDN URL. May differ from urls: base is the first-load prefix; urls is the fallback chain
urlsstring[]RequiredOrdered candidate URL prefix list (fallback chain). Last one is typically the origin
retryRetryOptionsSee belowOverride retry config for this rule
circuitCircuitOptionsSee belowOverride circuit breaker config for this rule

rule base vs Vite base

Vite's config base and FallbackRule.base share a name: call them Vite base vs rule base in prose. Vite base / Webpack publicPath should equal rules[].base. base and urls may differ — base is the first-load prefix; urls is the fallback chain. RegExp / function matchers are no longer supported.

Current page-side rule and circuit behavior is more specific than the public type suggests:

  • window.__RF__.url(filename) builds the initial URL from the first compiled rule's base; it is not circuit-aware;
  • a recovery session first selects one rule from the initial URL, then walks that rule's ordered urls candidates;
  • the page runtime currently has one circuit registry, initialized from the first compiled rule's circuit options. FallbackRule.circuit remains public, but independent per-rule page circuits are not implemented yet.

RetryOptions

FieldTypeDefaultDescription
maxnumber2Max retries per URL
baseDelaynumber300Initial retry delay (ms)
maxDelaynumber3000Exponential backoff delay cap (ms)
jitterbooleantrueAdd ±25% random jitter to delay

CircuitOptions

FieldTypeDefaultDescription
thresholdnumber5Consecutive failures on the same host before tripping the circuit
cooldownnumber30000Cooldown duration after circuit trip (ms), then retry
shareAcrossTabsbooleantrueShare circuit state across tabs via localStorage
storageTtlnumber120000TTL for circuit entries in localStorage (ms)

The page-side RecoveryCoordinator also shares one in-flight recovery Promise per owner + logical resource key. Calls from the same owner for the same logical resource join the same recovery chain; different owners or different logical keys do not share work. The ownership registry prevents Observer and builder-specific adapters from independently taking over the same logical resource.

Hooks and serialization limits

buildInjectedTags() and plugin-generated window.__RF__.install(...) calls both serialize the config before it reaches the page, and function values are dropped during that step. So:

  • hooks in build config do not survive automatic injection;
  • externalRuntime only changes whether the runtime IIFE is inline or external, not the serialization behavior; the automatic install(...) call remains inline and needs a nonce or equivalent authorization under a strict CSP;
  • DOM rf:* events are the recommended monitoring path for auto-injected setups;
  • use JS hooks only when you manually call window.__RF__.install() in page code and pass live function objects yourself.

ServiceWorkerOptions

Hybrid SW is disabled by default. When enabled, Vite/Webpack plugins generate a resource manifest and emit a SW asset. The SW bundle preloads manifest/config, while the page runtime registers the SW, sends follow-up config updates, and bridges SW postMessage events into existing rf:* events.

ts
resourceFallback({
  rules: [...],
  serviceWorker: {
    scope: '/',
    includeStyleImports: true,
    fallbackOnOpaque: false,
    cache: { enabled: true, cacheOpaque: false },
  },
});
FieldTypeDefaultDescription
enabledbooleantrue for object configSet to false to disable from an object config
pathstringDerived from scope, e.g. //rf-sw.js, /app//app/rf-sw.jsSW file path. Default stays inside scope to avoid requiring Service-Worker-Allowed
scopestring'/'SW control scope
includeStyleImportsbooleantrueLet SW handle CSS @import when request.destination === 'style' and referrer matches a CSS manifest asset
fallbackOnOpaquebooleanfalseEnable a CORS probe for cross-origin no-cors requests; readable non-2xx responses enter fallback, while CORS-unavailable responses downgrade to no-cors and remain opaque
cache.enabledbooleantrueWrite to Cache API after a fallback network response succeeds
cache.cacheOpaquebooleanfalseWhether to cache opaque responses. Disabled by default

Cache policy

Conservative by default: only readable 2xx responses from successful fallback are cached; setting cacheOpaque: true also permits caching opaque responses. Manifest-version cache is read only after network retry/fallback is exhausted; when that cache is returned, the current implementation has already emitted rf:error and does not add rf:success. Old resource-fallback-* caches are cleaned when a new manifest version activates. Manifest version includes resources, fallback rules, and key SW cache policy.

SW circuit breaker isolation

The SW resolver always uses an isolated in-memory circuit breaker. Even if page-side defaults.circuit.shareAcrossTabs is true, the SW does not read or write localStorage. If the SW fetch chain ultimately rejects, it emits rf:error and returns Response.error().

Example configuration

ts
resourceFallback({
  rules: [
    {
      base: 'https://cdn.example.com/',
      urls: ['https://cdn-backup.example.com/', 'https://static.mysite.com/', '/'],
      retry: { max: 2, baseDelay: 300, maxDelay: 3000, jitter: true },
      circuit: { threshold: 3, cooldown: 30000 },
    },
  ],
  defaults: {
    retry: { max: 2 },
    circuit: { threshold: 5, cooldown: 30000 },
  },
  debug: 'auto',
  sri: 'strip',
  nonce: 'my-csp-nonce',
  injectPreconnect: true,
  htmlInject: 'head-prepend',
});