Creator Notify v1.0.0

Service Worker

The Service Worker is what turns a push message into a visible notification, even when your page is closed. It must be served from your own origin — no CDN shortcut exists, and none is claimed here.

Hosting requirements

RequirementWhy
Same origin as the pageA worker script served from another origin cannot control your pages; the browser refuses to register it.
HTTPS (or http://localhost)Service Workers require a secure context.
Path covers the scopeBy default a worker at /js/sw.js can only control /js/*. To control the whole site, serve it from the root or send Service-Worker-Allowed: /.
JavaScript MIME typetext/javascript or application/javascript.
No aggressive cachingA stale worker can pin an old payload format; serve with a short Cache-Control.

Installing it

# copy both files to your web root
cp sdk/service-worker.js /var/www/example.com/service-worker.js
cp sdk/creator-notify.js /var/www/example.com/creator-notify.js

The SDK registers it from serviceWorkerPath (default /service-worker.js):

const notify = new CreatorNotify({
  websiteId: 'pub_8d1f0c...',
  apiBaseUrl: 'https://YOUR-API-HOST',
  serviceWorkerPath: '/service-worker.js',
  serviceWorkerScope: '/'
});

If you must keep the file elsewhere, send the header from your web server:

# nginx
location = /assets/service-worker.js {
  add_header Service-Worker-Allowed /;
  add_header Cache-Control "public, max-age=300";
  types { } default_type application/javascript;
}

# Apache (.htaccess)
<Files "service-worker.js">
  Header set Service-Worker-Allowed "/"
  Header set Cache-Control "public, max-age=300"
</Files>

Creator Notify also serves a copy

GET /sdk/service-worker.js returns the same file with Service-Worker-Allowed: /. Use it to test quickly, but for production copy the file to your own domain — registration is origin-scoped.

What the worker does

push

  • Parses the JSON payload. If it is not JSON, the raw text becomes the notification body. If there is no payload at all, a neutral fallback is shown.
  • Copies title, body, icon, badge, image, tag and up to two actions.
  • Validates every URL against an allow-list of origins (the site's own origin by default, extended through the SDK's configuration message).
  • Retries with a minimal option set if the platform rejects an option it does not support (for example image on some Android builds).

notificationclick

  • Closes the notification and reads the validated destination from notification.data.url.
  • Finds a window client on the same origin: the exact URL is navigated and focused; otherwise the first client is focused.
  • Falls back to clients.openWindow(url) when no window is open.
  • Action buttons may carry their own destination through data.actions[].url.

pushsubscriptionchange

Browsers may rotate a subscription without asking. The worker cannot re-subscribe without the VAPID key and a user gesture, so it notifies the page. The SDK surfaces this as a SUBSCRIPTION_CHANGED error; call subscribe() again from a click to restore delivery.

Controlling click destinations

{
  "title": "Order shipped",
  "body": "Tracking number 1Z999",
  "url": "https://example.com/orders/1234",
  "data": { "orderId": 1234 }
}

The worker only opens URLs on an allowed origin. Relative values are resolved against the site origin before validation. Anything that is not http: or https: is discarded, so javascript: and data: destinations cannot be injected.

Browser support and differences

BrowserBackground pushNotes
Chrome / Edge (desktop, Android)Yes, via FCMRequires a user gesture for the permission prompt. image is supported on Android, not on desktop.
Firefox (desktop, Android)Yes, via Mozilla autopushUses its own push service; endpoints differ. Some actions are unsupported.
Safari 16.4+ (macOS, iOS)Yes, via APNsOn iOS the site must be added to the Home Screen as a web app. Notification options are a subset.
Older Safari / IENoisSupported() returns false; hide the button.
Private / incognito windowsVariesSubscriptions often die with the session — expect churn.
Browsers in "quiet" notification modeDelivered but not surfacedThe push service accepted the message; this is not a Creator Notify failure.

No cross-browser guarantee

Push behaviour — payload size limits, action support, image rendering, notification grouping, badge colours — differs per platform and OS version. Test on the browsers you actually care about. The payload limits enforced by Creator Notify (MAX_PUSH_PAYLOAD_BYTES, default 3500) are chosen to be accepted by all current push services, but rendering remains the browser's decision.

Permissions

  • The prompt can only appear after a user gesture on Chrome; Firefox and Safari allow it but punish unsolicited prompts with quieter UI or blocking.
  • A denied permission is sticky per origin — the user must change it in browser settings.
  • getPermission() returns 'denied' immediately; do not re-prompt in a loop.
  • The SDK never calls requestPermission() at load time.

Debugging

# Chrome: chrome://serviceworker-internals  — unregister, push, inspect
# Firefox: about:debugging#/runtime/this-firefox → Service Workers
# Safari:  Develop → Service Workers, and the Web Inspector console

// From the page console, after registration:
const reg = await navigator.serviceWorker.getRegistration('/');
await reg.pushManager.getSubscription();

A push that the push service accepted but never rendered is usually one of: permission revoked, the site muted at OS level, a browser-level "quiet" mode, or a payload the platform refused. See Troubleshooting.