Files
tech-log-frontend/src/adapters/web-push/service-worker-scope-host.ts
T

58 lines
2.1 KiB
TypeScript

import type { ServiceWorkerEventHost } from "./service-worker-runtime.ts";
type NotificationOptions = Parameters<
ServiceWorkerEventHost["registration"]["showNotification"]
>[1];
type MatchAllOptions = Parameters<
ServiceWorkerEventHost["clients"]["matchAll"]
>[0];
/**
* Adapts a native Service Worker global scope onto the structural
* {@link ServiceWorkerEventHost} facade.
*
* The native `showNotification` call lives here because `src/adapters/web-push`
* is the owner of the notification API. The single physical worker entry
* (§17.1) composes the runtime but never touches the native API itself.
*/
export type NativeWorkerScope = Readonly<{
location: Readonly<{ origin: string }>;
registration: Readonly<{
showNotification(title: string, options: unknown): Promise<void>;
}>;
clients: Readonly<{
matchAll(options: unknown): Promise<readonly unknown[]>;
openWindow(url: string): Promise<unknown>;
}>;
// Deliberately loose: a native scope declares a richly overloaded listener
// signature, and this facade only needs to forward the registration.
addEventListener(type: string, listener: never, options?: never): void;
removeEventListener(type: string, listener: never, options?: never): void;
}>;
export function createServiceWorkerScopeHost(
scope: NativeWorkerScope,
): ServiceWorkerEventHost {
return Object.freeze({
origin: scope.location.origin,
registration: Object.freeze({
showNotification: (title: string, options: NotificationOptions) =>
scope.registration.showNotification(title, {
body: options.body,
data: options.data,
requireInteraction: options.requireInteraction,
tag: options.tag,
}),
}),
clients: Object.freeze({
matchAll: (options: MatchAllOptions) => scope.clients.matchAll(options),
openWindow: (url: string) => scope.clients.openWindow(url),
}),
addEventListener: (type, listener) =>
scope.addEventListener(type, listener as never),
removeEventListener: (type, listener) =>
scope.removeEventListener(type, listener as never),
});
}