Stagepass

Modules: inject, cookies and maintenance

Optional helpers on top of the loader: sp.inject, sp.cookies and sp.maintenance.

loader 2.3.0modules 2.2.0macOS

Load the modules

Modules need the loader. Add the loader first, then the modules you use, or all.min.js for all three.

Head Code
<script src="https://cdn.jsdelivr.net/npm/@stagepass/loader@2/dist/loader.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@stagepass/modules@2/dist/inject.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@stagepass/modules@2/dist/cookies.min.js"></script>
Terminal
$ npm install @stagepass/loader@2 @stagepass/modules@2

Modules depend on the Loader at runtime (they use sp.vars / stagepass.vars). The loader script must be loaded before any module script. Without the loader, inject and cookies still attach their APIs and warn, and maintenance does nothing. For npm users, @stagepass/modules lists @stagepass/loader as a peer dependency: install the loader when you install modules.

Available Modules

Injector Module (inject.min.js)

  • Programmatic asset injection API
  • Smart source resolution (local vs production)
  • Flexible positioning and deduplication
  • Include as a separate script tag after loader.min.js

Cookies Module (cookies.min.js)

  • Lightweight cookie utility helpers with optional consent categories (no consent manager)
  • Read, write, delete, existence checks, and list helpers
  • Available as sp.cookies.* and sp.cookie.*
  • Include as a separate script tag after loader.min.js

Maintenance Module (maintenance.min.js)

  • Opt in overlay for visitors while the site is under maintenance
  • Developers with a session still see the real page
  • Available as sp.maintenance.*
  • Include as a separate script tag after loader.min.js

Injector: Programmatic Injection API

When the Injector module is loaded, use sp.inject() (preferred). stagepass.inject() and window.stagepass.inject() remain available as aliases:

JavaScript
// Single injection
await sp.inject({
  src: 'https://cdn.example.com/lib.js',
  stagepass: true,        // Enable local swapping when active
  localPath: 'lib.js',     // Optional: local file path (defaults to filename from src)
  id: 'my-library',        // Optional: for deduplication
  position: 'head',        // 'head' | 'body-start' | 'body-end' | { target: '#id', action: 'before' | 'after' }
  type: 'script',          // Optional: 'script' | 'style' (auto-detected from extension)
  version: '1.4.0',        // Optional: in production, append ?v=<version> for cache busting
  async: false,            // Optional: default false (keeps script order)
  defer: true,             // Optional: default true (unless async is true)
  attributes: {            // Optional: additional HTML attributes
    crossorigin: 'anonymous'
  }
});

// Batch injection (array)
await sp.inject([
  { src: 'https://cdn.example.com/lib1.js', stagepass: true, localPath: 'lib1.js', version: '1.4.0' },
  { src: 'https://cdn.example.com/lib2.css', stagepass: true, localPath: 'lib2.css', type: 'style', version: '1.4.0' }
]);

// Batch injection ({ scripts, styles })
await sp.inject({
  scripts: [{ src: 'https://cdn.example.com/lib1.js', stagepass: true, path: 'lib1.js', version: '1.4.0' }],
  styles: [{ src: 'https://cdn.example.com/lib2.css', stagepass: true, localPath: 'lib2.css', version: '1.4.0' }]
});

path is an alias for localPath. Deprecated cacheBust no longer appends production timestamps; use version instead.

Integrity, nonce and allowed hosts

Optional options for stricter sites. Reserved attributes cannot bypass the host allowlist.

JavaScript
await sp.inject({
  src: 'https://cdn.example.com/lib.js',
  integrity: 'sha384-abc',            // passed through as SRI hash
  crossorigin: 'anonymous',           // passed through as CORS mode
  nonce: 'csp-nonce',                 // CSP nonce
  allowedHosts: ['cdn.example.com']   // other hosts are skipped with a warning
});

Cookies: Utility API

When the Cookies module is loaded, use sp.cookies (singular alias sp.cookie):

JavaScript
sp.cookies.bake('token', 'abc123', { days: 7, path: '/' });
sp.cookies.eat('token'); // 'abc123'
sp.cookies.has('token'); // true
sp.cookies.list(); // { token: 'abc123', ... }
sp.cookies.throw('token', { path: '/' });

API reference:

  • sp.cookies.bake(name, value, options?) Creates/updates a cookie.
  • sp.cookies.eat(name, options?) Reads a cookie value. Returns null when not found.
  • sp.cookies.throw(name, options?) Deletes a cookie (writes Max-Age=0 + expired date).
  • sp.cookies.has(name) Boolean existence check.
  • sp.cookies.list(options?) Returns all visible cookies as an object map.
  • sp.cookies.throwAll({ category? }) Deletes the cookies this helper knows, optionally only one category.
  • sp.cookies.jar() Returns a copy of the cookies this helper knows.
  • sp.cookies.crumbs(prefix) Returns the known cookie names that start with prefix.

bake options:

  • days?: number - Expiration in days.
  • maxAge?: number - Expiration in seconds (Max-Age).
  • path?: string - Cookie path (default: /).
  • domain?: string - Cookie domain.
  • secure?: boolean - Defaults to true on HTTPS pages.
  • sameSite?: 'Lax' | 'Strict' | 'None' - Default: Lax.
  • encode?: boolean - URL-encode key/value (default: true).
  • json?: boolean - Store value as JSON string.
  • category?: string - Remembers the category. The write waits in a queue while consent rejects it.

eat / list options:

  • decode?: boolean - URL-decode values (default: true).
  • json?: boolean - Attempt JSON.parse for values.

throw options:

  • path?: string (default: /)
  • domain?: string
  • secure?: boolean
  • sameSite?: 'Lax' | 'Strict' | 'None'

JSON example:

JavaScript
sp.cookies.bake('profile', { id: 7, role: 'editor' }, { json: true, days: 30 });
sp.cookies.eat('profile', { json: true }); // { id: 7, role: 'editor' }

sp.consent is a small hook for your own consent banner. By default every category is allowed. Writes in a rejected category wait in a queue until you call sp.consent.update().

JavaScript
sp.cookies.bake('analytics_id', 'x', { category: 'analytics', days: 30 });
sp.cookies.throwAll({ category: 'analytics' });
sp.cookies.jar();                    // copy of known cookies
sp.cookies.crumbs('analytics_');     // matching known names

sp.consent.set((category) => category !== 'marketing');
sp.cookies.bake('ad', '1', { category: 'marketing' }); // queued
sp.consent.set(() => true);
sp.consent.update();                 // flushes the queue

Notes and limitations:

  • JavaScript cannot read or delete HttpOnly cookies.
  • To reliably delete a cookie, use the same path/domain you used when writing it.
  • If sameSite: 'None' is used, the module enforces Secure=true (browser requirement).
  • This module provides cookie storage helpers only (bake/eat/throw). It does not implement consent management. sp.consent only gates writes, it is not a consent manager or a legal compliance solution.
  • Cookie attribute values are validated against cookie injection.

Maintenance overlay

A full page overlay for visitors while the site is under maintenance. It is off unless you set data-stagepass-maintenance="true" on its script tag or enable it in code.

Head Code
<script
  src="https://cdn.jsdelivr.net/npm/@stagepass/modules@2/dist/maintenance.min.js"
  data-stagepass-maintenance="true"
  data-stagepass-maintenance-title="We will be back soon"
  data-stagepass-maintenance-text="Please check again shortly."
></script>
JavaScript
sp.maintenance.enable({ title: 'We will be back soon', text: 'Please check again shortly.' });
sp.maintenance.isActive(); // true
sp.maintenance.disable();
  • Visitors without a session see the overlay. Title and text are set as plain text.
  • With a session or ?stagepass=debug you see the real page and a small badge.
  • It follows prefers-color-scheme and loads no external resources.

TypeScript types

Add one entry per module your site loads, so a module that is not there is a type error:

tsconfig.json
{ "compilerOptions": { "types": ["@stagepass/loader", "@stagepass/modules/cookies"] } }
EntryAdds
@stagepass/modules/injectsp.inject
@stagepass/modules/cookiessp.cookies, sp.cookie, sp.consent
@stagepass/modules/maintenancesp.maintenance
@stagepass/modulesAll of the above

Module members are optional, because a page may not load the file. Guard your calls, for example if (sp.cookies) { … }. stagepass new and stagepass upgrade set these entries for you. See Declared modules.

Injector details

  • sp.inject() returns a promise and accepts one item, an array, or { scripts: [...], styles: [...] }.
  • position defaults to body-end.
  • localPath also works as path. Without it, the file name from src is used.
  • version appends ?v=<value> in production. cacheBust is deprecated and no longer adds a timestamp.
  • Scripts keep insertion order unless you pass async: true.