tempest-react-sdk 0.68.0 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(){return globalThis}function t(
|
|
1
|
+
function e(){return globalThis}var t=[`body`,`image`,`tag`,`requireInteraction`,`vibrate`,`silent`,`renotify`,`timestamp`,`dir`,`lang`];function n(e,n,r){let i={icon:e.icon??n,badge:e.badge??r};for(let n of t)e[n]!==void 0&&(i[n]=e[n]);let a={url:e.url??`/`,...e.data??{}};if(e.actions){let t={};i.actions=e.actions.map(({url:e,...n})=>(e!==void 0&&(t[n.action]=e),n)),Object.keys(t).length>0&&(a.actionUrls=t)}return i.data=a,i}function r(t={}){let r=e(),{defaultTitle:i=`Notificação`,defaultIcon:a,defaultBadge:o,transform:s}=t;r.addEventListener(`push`,e=>{if(!e.data)return;let t;try{t=e.data.json()}catch{t={title:i,body:e.data.text()}}let c=s?s(t):t;if(!c)return;let l=c.title??i,u=n(c,a,o);e.waitUntil(r.registration.showNotification(l,u))})}function i(e,t){if(typeof e==`string`)return e;if(!e||typeof e!=`object`)return`/`;let n=e;if(t&&n.actionUrls&&typeof n.actionUrls==`object`){let e=n.actionUrls[t];if(typeof e==`string`)return e}return typeof n.url==`string`?n.url:`/`}function a(e,t){try{return new URL(t,e).href===new URL(e).href}catch{return!1}}function o(t={}){let n=e(),r=t.resolveUrl??i;n.addEventListener(`notificationclick`,e=>{e.notification.close();let t=r(e.notification.data,e.action);e.waitUntil((async()=>{let e=await n.clients.matchAll({type:`window`,includeUncontrolled:!0});for(let n of e)if(a(n.url,t))return n.focus();return n.clients.openWindow(t)})())})}function s(){let t=e();t.addEventListener(`message`,e=>{e.data?.type===`SKIP_WAITING`&&t.skipWaiting()})}exports.installNotificationClickHandler=o,exports.installPushHandler=r,exports.installSkipWaitingListener=s;
|
|
2
2
|
//# sourceMappingURL=create-push-handler.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-push-handler.cjs","names":[],"sources":["../../src/sw/create-push-handler.ts"],"sourcesContent":["/**\n * Service-worker context helpers for handling `push` and `notificationclick`\n * events. Import these inside your own `sw.ts` — they expect to run in the\n * service-worker global scope, not in the main thread.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installPushHandler, installNotificationClickHandler } from \"tempest-react-sdk\";\n *\n * installPushHandler({ defaultIcon: \"/icons/Logo.png\" });\n * installNotificationClickHandler();\n */\n\ninterface SwGlobal {\n registration: {\n showNotification(title: string, options?: NotificationOptions): Promise<void>;\n };\n clients: {\n matchAll(options: { type: \"window\"; includeUncontrolled?: boolean }): Promise<\n {\n url: string;\n focused: boolean;\n focus(): Promise<unknown>;\n navigate(url: string): Promise<unknown>;\n }[]\n >;\n openWindow(url: string): Promise<unknown>;\n };\n addEventListener(\n type: \"push\",\n listener: (event: {\n data: { json(): unknown; text(): string } | null;\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n addEventListener(\n type: \"notificationclick\",\n listener: (event: {\n notification: { close(): void; data?: unknown };\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n skipWaiting(): Promise<void>;\n}\n\nfunction getSwScope(): SwGlobal {\n return globalThis as unknown as SwGlobal;\n}\n\nexport interface PushPayload {\n title?: string;\n body?: string;\n icon?: string;\n badge?: string;\n image?: string;\n tag?: string;\n url?: string;\n /** Arbitrary extra data forwarded to `event.notification.data`. */\n data?: Record<string, unknown>;\n}\n\nexport interface InstallPushHandlerOptions {\n /** Title used when the payload omits one. */\n defaultTitle?: string;\n /** Icon used when the payload omits one. */\n defaultIcon?: string;\n /** Badge image (mobile). */\n defaultBadge?: string;\n /**\n * Transform the raw payload before showing the notification. Return `null`\n * to suppress the notification entirely (e.g. silent pings).\n */\n transform?: (payload: PushPayload) => PushPayload | null;\n}\n\n/**\n * Install a `push` event listener that parses the payload as JSON (with a\n * plain-text fallback) and shows a notification.\n */\nexport function installPushHandler(options: InstallPushHandlerOptions = {}): void {\n const sw = getSwScope();\n const { defaultTitle = \"Notificação\", defaultIcon, defaultBadge, transform } = options;\n\n sw.addEventListener(\"push\", (event) => {\n if (!event.data) return;\n\n let raw: PushPayload;\n try {\n raw = event.data.json() as PushPayload;\n } catch {\n raw = { title: defaultTitle, body: event.data.text() };\n }\n\n const payload = transform ? transform(raw) : raw;\n if (!payload) return;\n\n const title = payload.title ?? defaultTitle;\n const notification: NotificationOptions & { image?: string } = {\n body: payload.body,\n icon: payload.icon ?? defaultIcon,\n badge: payload.badge ?? defaultBadge,\n image: payload.image,\n tag: payload.tag,\n data: { url: payload.url ?? \"/\", ...(payload.data ?? {}) },\n };\n\n event.waitUntil(sw.registration.showNotification(title, notification));\n });\n}\n\nexport interface InstallNotificationClickHandlerOptions {\n /** Resolve the destination URL from the notification data. Default: `data.url`. */\n resolveUrl?: (data: unknown) => string;\n}\n\n/**\n * Install a `notificationclick` handler that focuses an existing client when\n * possible and falls back to opening a new window.\n */\nexport function installNotificationClickHandler(\n options: InstallNotificationClickHandlerOptions = {},\n): void {\n const sw = getSwScope();\n const resolveUrl =\n options.resolveUrl ??\n ((data: unknown) => {\n if (typeof data === \"string\") return data;\n if (data && typeof data === \"object\" && \"url\" in data) {\n const url = (data as Record<string, unknown>).url;\n return typeof url === \"string\" ? url : \"/\";\n }\n return \"/\";\n });\n\n sw.addEventListener(\"notificationclick\", (event) => {\n event.notification.close();\n const target = resolveUrl(event.notification.data);\n\n event.waitUntil(\n (async () => {\n const clients = await sw.clients.matchAll({\n type: \"window\",\n includeUncontrolled: true,\n });\n for (const client of clients) {\n if (client.url.includes(target)) {\n return client.focus();\n }\n }\n return sw.clients.openWindow(target);\n })(),\n );\n });\n}\n\n/**\n * Install a `message` listener that activates a waiting worker when the host\n * app sends `{ type: \"SKIP_WAITING\" }`.\n */\nexport function installSkipWaitingListener(): void {\n const sw = getSwScope() as SwGlobal & {\n addEventListener(\n type: \"message\",\n listener: (event: { data?: { type?: string } }) => void,\n ): void;\n };\n sw.addEventListener(\"message\", (event) => {\n if (event.data?.type === \"SKIP_WAITING\") {\n void sw.skipWaiting();\n }\n });\n}\n"],"mappings":"AA6CA,SAAS,GAAuB,CAC5B,OAAO,UACX,CAgCA,SAAgB,EAAmB,EAAqC,CAAC,EAAS,CAC9E,IAAM,EAAK,EAAW,EAChB,CAAE,eAAe,cAAe,cAAa,eAAc,aAAc,EAE/E,EAAG,iBAAiB,OAAS,GAAU,CACnC,GAAI,CAAC,EAAM,KAAM,OAEjB,IAAI,EACJ,GAAI,CACA,EAAM,EAAM,KAAK,KAAK,CAC1B,MAAQ,CACJ,EAAM,CAAE,MAAO,EAAc,KAAM,EAAM,KAAK,KAAK,CAAE,CACzD,CAEA,IAAM,EAAU,EAAY,EAAU,CAAG,EAAI,EAC7C,GAAI,CAAC,EAAS,OAEd,IAAM,EAAQ,EAAQ,OAAS,EACzB,EAAyD,CAC3D,KAAM,EAAQ,KACd,KAAM,EAAQ,MAAQ,EACtB,MAAO,EAAQ,OAAS,EACxB,MAAO,EAAQ,MACf,IAAK,EAAQ,IACb,KAAM,CAAE,IAAK,EAAQ,KAAO,IAAK,GAAI,EAAQ,MAAQ,CAAC,CAAG,CAC7D,EAEA,EAAM,UAAU,EAAG,aAAa,iBAAiB,EAAO,CAAY,CAAC,CACzE,CAAC,CACL,CAWA,SAAgB,EACZ,EAAkD,CAAC,EAC/C,CACJ,IAAM,EAAK,EAAW,EAChB,EACF,EAAQ,aACN,GAAkB,CAChB,GAAI,OAAO,GAAS,SAAU,OAAO,EACrC,GAAI,GAAQ,OAAO,GAAS,UAAY,QAAS,EAAM,CACnD,IAAM,EAAO,EAAiC,IAC9C,OAAO,OAAO,GAAQ,SAAW,EAAM,GAC3C,CACA,MAAO,GACX,GAEJ,EAAG,iBAAiB,oBAAsB,GAAU,CAChD,EAAM,aAAa,MAAM,EACzB,IAAM,EAAS,EAAW,EAAM,aAAa,IAAI,EAEjD,EAAM,WACD,SAAY,CACT,IAAM,EAAU,MAAM,EAAG,QAAQ,SAAS,CACtC,KAAM,SACN,oBAAqB,EACzB,CAAC,EACD,IAAK,IAAM,KAAU,EACjB,GAAI,EAAO,IAAI,SAAS,CAAM,EAC1B,OAAO,EAAO,MAAM,EAG5B,OAAO,EAAG,QAAQ,WAAW,CAAM,CACvC,EAAA,CAAG,CACP,CACJ,CAAC,CACL,CAMA,SAAgB,GAAmC,CAC/C,IAAM,EAAK,EAAW,EAMtB,EAAG,iBAAiB,UAAY,GAAU,CAClC,EAAM,MAAM,OAAS,gBACrB,EAAQ,YAAY,CAE5B,CAAC,CACL"}
|
|
1
|
+
{"version":3,"file":"create-push-handler.cjs","names":[],"sources":["../../src/sw/create-push-handler.ts"],"sourcesContent":["/**\n * Service-worker context helpers for handling `push` and `notificationclick`\n * events. Import these inside your own `sw.ts` — they expect to run in the\n * service-worker global scope, not in the main thread.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installPushHandler, installNotificationClickHandler } from \"tempest-react-sdk\";\n *\n * installPushHandler({ defaultIcon: \"/icons/Logo.png\" });\n * installNotificationClickHandler();\n */\n\ninterface SwGlobal {\n registration: {\n showNotification(title: string, options?: NotificationOptions): Promise<void>;\n };\n clients: {\n matchAll(options: { type: \"window\"; includeUncontrolled?: boolean }): Promise<\n {\n url: string;\n focused: boolean;\n focus(): Promise<unknown>;\n navigate(url: string): Promise<unknown>;\n }[]\n >;\n openWindow(url: string): Promise<unknown>;\n };\n addEventListener(\n type: \"push\",\n listener: (event: {\n data: { json(): unknown; text(): string } | null;\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n addEventListener(\n type: \"notificationclick\",\n listener: (event: {\n notification: { close(): void; data?: unknown };\n action?: string;\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n skipWaiting(): Promise<void>;\n}\n\nfunction getSwScope(): SwGlobal {\n return globalThis as unknown as SwGlobal;\n}\n\n/**\n * A notification button as the push payload describes it.\n *\n * `action`, `title` and `icon` are forwarded to the browser as a\n * `NotificationAction`. `url` is not part of that type — the browser would drop\n * it — so {@link installPushHandler} moves it into `data.actionUrls`, where\n * {@link installNotificationClickHandler} reads it back when that button is\n * clicked.\n */\nexport interface PushNotificationAction {\n /** Identifier delivered as `event.action` when the button is clicked. */\n action: string;\n /** Button label. */\n title: string;\n /** Button icon URL. */\n icon?: string;\n /** URL opened when this button is clicked. Falls back to the top-level `url`. */\n url?: string;\n}\n\nexport interface PushPayload {\n title?: string;\n body?: string;\n icon?: string;\n badge?: string;\n image?: string;\n tag?: string;\n /** URL opened when the notification body is clicked. Default: `\"/\"`. */\n url?: string;\n /** Buttons rendered on the notification (browsers show up to `Notification.maxActions`). */\n actions?: PushNotificationAction[];\n /** Keep the notification on screen until the user interacts with it. */\n requireInteraction?: boolean;\n /** Vibration pattern in milliseconds (mobile). */\n vibrate?: number | number[];\n /** Show the notification without sound or vibration. */\n silent?: boolean;\n /** Alert again when a notification with the same `tag` replaces an older one. */\n renotify?: boolean;\n /** Event time in epoch milliseconds, shown by some platforms. */\n timestamp?: number;\n /** Text direction of the title and body. */\n dir?: NotificationDirection;\n /** BCP 47 language tag of the title and body. */\n lang?: string;\n /** Arbitrary extra data forwarded to `event.notification.data`. */\n data?: Record<string, unknown>;\n}\n\n/**\n * `NotificationOptions` plus the members TypeScript's DOM lib leaves out\n * because not every engine implements them. Chromium honours all of them;\n * engines without support ignore the unknown keys.\n */\ntype ExtendedNotificationOptions = NotificationOptions & {\n image?: string;\n actions?: Omit<PushNotificationAction, \"url\">[];\n vibrate?: number | number[];\n renotify?: boolean;\n timestamp?: number;\n};\n\n/**\n * Payload keys copied verbatim onto the notification options when present.\n */\nconst FORWARDED_OPTIONS = [\n \"body\",\n \"image\",\n \"tag\",\n \"requireInteraction\",\n \"vibrate\",\n \"silent\",\n \"renotify\",\n \"timestamp\",\n \"dir\",\n \"lang\",\n] as const satisfies readonly (keyof PushPayload & keyof ExtendedNotificationOptions)[];\n\n/**\n * Build the `showNotification` options from a push payload.\n *\n * Keys the payload omits stay absent instead of being set to `undefined`, so\n * the browser applies its own defaults. Per-action `url`s are collected into\n * `data.actionUrls` (only when at least one action carries one) and stripped\n * from the actions handed to the browser.\n *\n * @param payload - The parsed (and transformed) push payload.\n * @param defaultIcon - Icon used when the payload omits one.\n * @param defaultBadge - Badge used when the payload omits one.\n * @returns The options object for `registration.showNotification`.\n */\nfunction buildNotificationOptions(\n payload: PushPayload,\n defaultIcon: string | undefined,\n defaultBadge: string | undefined,\n): ExtendedNotificationOptions {\n const options: Record<string, unknown> = {\n icon: payload.icon ?? defaultIcon,\n badge: payload.badge ?? defaultBadge,\n };\n for (const key of FORWARDED_OPTIONS) {\n if (payload[key] !== undefined) options[key] = payload[key];\n }\n\n const data: Record<string, unknown> = { url: payload.url ?? \"/\", ...(payload.data ?? {}) };\n if (payload.actions) {\n const actionUrls: Record<string, string> = {};\n options.actions = payload.actions.map(({ url, ...action }) => {\n if (url !== undefined) actionUrls[action.action] = url;\n return action;\n });\n if (Object.keys(actionUrls).length > 0) data.actionUrls = actionUrls;\n }\n options.data = data;\n return options as ExtendedNotificationOptions;\n}\n\nexport interface InstallPushHandlerOptions {\n /** Title used when the payload omits one. */\n defaultTitle?: string;\n /** Icon used when the payload omits one. */\n defaultIcon?: string;\n /** Badge image (mobile). */\n defaultBadge?: string;\n /**\n * Transform the raw payload before showing the notification. Return `null`\n * to suppress the notification entirely (e.g. silent pings).\n */\n transform?: (payload: PushPayload) => PushPayload | null;\n}\n\n/**\n * Install a `push` event listener that parses the payload as JSON (with a\n * plain-text fallback) and shows a notification.\n *\n * Every `NotificationOptions` member the payload carries is forwarded —\n * `actions`, `requireInteraction`, `vibrate`, `silent`, `renotify`,\n * `timestamp`, `dir` and `lang` alongside `body`, `icon`, `badge`, `image` and\n * `tag`. `data` receives the top-level `url` (default `\"/\"`), the payload's\n * own `data`, and `actionUrls` mapping each action id to its `url`.\n */\nexport function installPushHandler(options: InstallPushHandlerOptions = {}): void {\n const sw = getSwScope();\n const { defaultTitle = \"Notificação\", defaultIcon, defaultBadge, transform } = options;\n\n sw.addEventListener(\"push\", (event) => {\n if (!event.data) return;\n\n let raw: PushPayload;\n try {\n raw = event.data.json() as PushPayload;\n } catch {\n raw = { title: defaultTitle, body: event.data.text() };\n }\n\n const payload = transform ? transform(raw) : raw;\n if (!payload) return;\n\n const title = payload.title ?? defaultTitle;\n const notification = buildNotificationOptions(payload, defaultIcon, defaultBadge);\n\n event.waitUntil(sw.registration.showNotification(title, notification));\n });\n}\n\nexport interface InstallNotificationClickHandlerOptions {\n /**\n * Resolve the destination URL from the notification data and the clicked\n * action id (`\"\"` or `undefined` for a click on the body). Default: the\n * action's entry in `data.actionUrls`, else `data.url`, else `\"/\"`.\n */\n resolveUrl?: (data: unknown, action: string | undefined) => string;\n}\n\n/**\n * Default URL resolution for a notification click.\n *\n * @param data - `event.notification.data`, as {@link installPushHandler} wrote it.\n * @param action - `event.action`; empty or `undefined` for a click on the body.\n * @returns The action's URL when it has one, else `data.url`, else `\"/\"`.\n */\nfunction defaultResolveUrl(data: unknown, action: string | undefined): string {\n if (typeof data === \"string\") return data;\n if (!data || typeof data !== \"object\") return \"/\";\n const record = data as Record<string, unknown>;\n if (action && record.actionUrls && typeof record.actionUrls === \"object\") {\n const actionUrl = (record.actionUrls as Record<string, unknown>)[action];\n if (typeof actionUrl === \"string\") return actionUrl;\n }\n return typeof record.url === \"string\" ? record.url : \"/\";\n}\n\n/**\n * Whether an open window client is already showing `target`.\n *\n * `target` is resolved against the client's own URL, so a relative path\n * compares on the client's origin, and the comparison is on the full href.\n * A substring test would let `/events/1` match a client on `/events/10`, and\n * `/` match every client.\n *\n * @param clientUrl - The window client's absolute URL.\n * @param target - The URL the click resolved to, absolute or relative.\n * @returns `true` when both point at the same document.\n */\nfunction clientShowsTarget(clientUrl: string, target: string): boolean {\n try {\n return new URL(target, clientUrl).href === new URL(clientUrl).href;\n } catch {\n return false;\n }\n}\n\n/**\n * Install a `notificationclick` handler that focuses an existing client when\n * possible and falls back to opening a new window.\n *\n * A click on an action button opens that action's URL (see\n * {@link PushNotificationAction.url}); a click on the body, or on an action\n * without one, opens the top-level `url`.\n */\nexport function installNotificationClickHandler(\n options: InstallNotificationClickHandlerOptions = {},\n): void {\n const sw = getSwScope();\n const resolveUrl = options.resolveUrl ?? defaultResolveUrl;\n\n sw.addEventListener(\"notificationclick\", (event) => {\n event.notification.close();\n const target = resolveUrl(event.notification.data, event.action);\n\n event.waitUntil(\n (async () => {\n const clients = await sw.clients.matchAll({\n type: \"window\",\n includeUncontrolled: true,\n });\n for (const client of clients) {\n if (clientShowsTarget(client.url, target)) {\n return client.focus();\n }\n }\n return sw.clients.openWindow(target);\n })(),\n );\n });\n}\n\n/**\n * Install a `message` listener that activates a waiting worker when the host\n * app sends `{ type: \"SKIP_WAITING\" }`.\n */\nexport function installSkipWaitingListener(): void {\n const sw = getSwScope() as SwGlobal & {\n addEventListener(\n type: \"message\",\n listener: (event: { data?: { type?: string } }) => void,\n ): void;\n };\n sw.addEventListener(\"message\", (event) => {\n if (event.data?.type === \"SKIP_WAITING\") {\n void sw.skipWaiting();\n }\n });\n}\n"],"mappings":"AA8CA,SAAS,GAAuB,CAC5B,OAAO,UACX,CAmEA,IAAM,EAAoB,CACtB,OACA,QACA,MACA,qBACA,UACA,SACA,WACA,YACA,MACA,MACJ,EAeA,SAAS,EACL,EACA,EACA,EAC2B,CAC3B,IAAM,EAAmC,CACrC,KAAM,EAAQ,MAAQ,EACtB,MAAO,EAAQ,OAAS,CAC5B,EACA,IAAK,IAAM,KAAO,EACV,EAAQ,KAAS,IAAA,KAAW,EAAQ,GAAO,EAAQ,IAG3D,IAAM,EAAgC,CAAE,IAAK,EAAQ,KAAO,IAAK,GAAI,EAAQ,MAAQ,CAAC,CAAG,EACzF,GAAI,EAAQ,QAAS,CACjB,IAAM,EAAqC,CAAC,EAC5C,EAAQ,QAAU,EAAQ,QAAQ,KAAK,CAAE,MAAK,GAAG,MACzC,IAAQ,IAAA,KAAW,EAAW,EAAO,QAAU,GAC5C,EACV,EACG,OAAO,KAAK,CAAU,CAAC,CAAC,OAAS,IAAG,EAAK,WAAa,EAC9D,CAEA,MADA,GAAQ,KAAO,EACR,CACX,CA0BA,SAAgB,EAAmB,EAAqC,CAAC,EAAS,CAC9E,IAAM,EAAK,EAAW,EAChB,CAAE,eAAe,cAAe,cAAa,eAAc,aAAc,EAE/E,EAAG,iBAAiB,OAAS,GAAU,CACnC,GAAI,CAAC,EAAM,KAAM,OAEjB,IAAI,EACJ,GAAI,CACA,EAAM,EAAM,KAAK,KAAK,CAC1B,MAAQ,CACJ,EAAM,CAAE,MAAO,EAAc,KAAM,EAAM,KAAK,KAAK,CAAE,CACzD,CAEA,IAAM,EAAU,EAAY,EAAU,CAAG,EAAI,EAC7C,GAAI,CAAC,EAAS,OAEd,IAAM,EAAQ,EAAQ,OAAS,EACzB,EAAe,EAAyB,EAAS,EAAa,CAAY,EAEhF,EAAM,UAAU,EAAG,aAAa,iBAAiB,EAAO,CAAY,CAAC,CACzE,CAAC,CACL,CAkBA,SAAS,EAAkB,EAAe,EAAoC,CAC1E,GAAI,OAAO,GAAS,SAAU,OAAO,EACrC,GAAI,CAAC,GAAQ,OAAO,GAAS,SAAU,MAAO,IAC9C,IAAM,EAAS,EACf,GAAI,GAAU,EAAO,YAAc,OAAO,EAAO,YAAe,SAAU,CACtE,IAAM,EAAa,EAAO,WAAuC,GACjE,GAAI,OAAO,GAAc,SAAU,OAAO,CAC9C,CACA,OAAO,OAAO,EAAO,KAAQ,SAAW,EAAO,IAAM,GACzD,CAcA,SAAS,EAAkB,EAAmB,EAAyB,CACnE,GAAI,CACA,OAAO,IAAI,IAAI,EAAQ,CAAS,CAAC,CAAC,OAAS,IAAI,IAAI,CAAS,CAAC,CAAC,IAClE,MAAQ,CACJ,MAAO,EACX,CACJ,CAUA,SAAgB,EACZ,EAAkD,CAAC,EAC/C,CACJ,IAAM,EAAK,EAAW,EAChB,EAAa,EAAQ,YAAc,EAEzC,EAAG,iBAAiB,oBAAsB,GAAU,CAChD,EAAM,aAAa,MAAM,EACzB,IAAM,EAAS,EAAW,EAAM,aAAa,KAAM,EAAM,MAAM,EAE/D,EAAM,WACD,SAAY,CACT,IAAM,EAAU,MAAM,EAAG,QAAQ,SAAS,CACtC,KAAM,SACN,oBAAqB,EACzB,CAAC,EACD,IAAK,IAAM,KAAU,EACjB,GAAI,EAAkB,EAAO,IAAK,CAAM,EACpC,OAAO,EAAO,MAAM,EAG5B,OAAO,EAAG,QAAQ,WAAW,CAAM,CACvC,EAAA,CAAG,CACP,CACJ,CAAC,CACL,CAMA,SAAgB,GAAmC,CAC/C,IAAM,EAAK,EAAW,EAMtB,EAAG,iBAAiB,UAAY,GAAU,CAClC,EAAM,MAAM,OAAS,gBACrB,EAAQ,YAAY,CAE5B,CAAC,CACL"}
|
|
@@ -2,64 +2,92 @@
|
|
|
2
2
|
function e() {
|
|
3
3
|
return globalThis;
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
var t = [
|
|
6
|
+
"body",
|
|
7
|
+
"image",
|
|
8
|
+
"tag",
|
|
9
|
+
"requireInteraction",
|
|
10
|
+
"vibrate",
|
|
11
|
+
"silent",
|
|
12
|
+
"renotify",
|
|
13
|
+
"timestamp",
|
|
14
|
+
"dir",
|
|
15
|
+
"lang"
|
|
16
|
+
];
|
|
17
|
+
function n(e, n, r) {
|
|
18
|
+
let i = {
|
|
19
|
+
icon: e.icon ?? n,
|
|
20
|
+
badge: e.badge ?? r
|
|
21
|
+
};
|
|
22
|
+
for (let n of t) e[n] !== void 0 && (i[n] = e[n]);
|
|
23
|
+
let a = {
|
|
24
|
+
url: e.url ?? "/",
|
|
25
|
+
...e.data ?? {}
|
|
26
|
+
};
|
|
27
|
+
if (e.actions) {
|
|
28
|
+
let t = {};
|
|
29
|
+
i.actions = e.actions.map(({ url: e, ...n }) => (e !== void 0 && (t[n.action] = e), n)), Object.keys(t).length > 0 && (a.actionUrls = t);
|
|
30
|
+
}
|
|
31
|
+
return i.data = a, i;
|
|
32
|
+
}
|
|
33
|
+
function r(t = {}) {
|
|
34
|
+
let r = e(), { defaultTitle: i = "Notificação", defaultIcon: a, defaultBadge: o, transform: s } = t;
|
|
35
|
+
r.addEventListener("push", (e) => {
|
|
8
36
|
if (!e.data) return;
|
|
9
37
|
let t;
|
|
10
38
|
try {
|
|
11
39
|
t = e.data.json();
|
|
12
40
|
} catch {
|
|
13
41
|
t = {
|
|
14
|
-
title:
|
|
42
|
+
title: i,
|
|
15
43
|
body: e.data.text()
|
|
16
44
|
};
|
|
17
45
|
}
|
|
18
|
-
let
|
|
19
|
-
if (!
|
|
20
|
-
let
|
|
21
|
-
|
|
22
|
-
icon: s.icon ?? i,
|
|
23
|
-
badge: s.badge ?? a,
|
|
24
|
-
image: s.image,
|
|
25
|
-
tag: s.tag,
|
|
26
|
-
data: {
|
|
27
|
-
url: s.url ?? "/",
|
|
28
|
-
...s.data ?? {}
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
e.waitUntil(n.registration.showNotification(c, l));
|
|
46
|
+
let c = s ? s(t) : t;
|
|
47
|
+
if (!c) return;
|
|
48
|
+
let l = c.title ?? i, u = n(c, a, o);
|
|
49
|
+
e.waitUntil(r.registration.showNotification(l, u));
|
|
32
50
|
});
|
|
33
51
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
52
|
+
function i(e, t) {
|
|
53
|
+
if (typeof e == "string") return e;
|
|
54
|
+
if (!e || typeof e != "object") return "/";
|
|
55
|
+
let n = e;
|
|
56
|
+
if (t && n.actionUrls && typeof n.actionUrls == "object") {
|
|
57
|
+
let e = n.actionUrls[t];
|
|
36
58
|
if (typeof e == "string") return e;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
59
|
+
}
|
|
60
|
+
return typeof n.url == "string" ? n.url : "/";
|
|
61
|
+
}
|
|
62
|
+
function a(e, t) {
|
|
63
|
+
try {
|
|
64
|
+
return new URL(t, e).href === new URL(e).href;
|
|
65
|
+
} catch {
|
|
66
|
+
return !1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function o(t = {}) {
|
|
70
|
+
let n = e(), r = t.resolveUrl ?? i;
|
|
43
71
|
n.addEventListener("notificationclick", (e) => {
|
|
44
72
|
e.notification.close();
|
|
45
|
-
let t = r(e.notification.data);
|
|
73
|
+
let t = r(e.notification.data, e.action);
|
|
46
74
|
e.waitUntil((async () => {
|
|
47
75
|
let e = await n.clients.matchAll({
|
|
48
76
|
type: "window",
|
|
49
77
|
includeUncontrolled: !0
|
|
50
78
|
});
|
|
51
|
-
for (let n of e) if (n.url
|
|
79
|
+
for (let n of e) if (a(n.url, t)) return n.focus();
|
|
52
80
|
return n.clients.openWindow(t);
|
|
53
81
|
})());
|
|
54
82
|
});
|
|
55
83
|
}
|
|
56
|
-
function
|
|
84
|
+
function s() {
|
|
57
85
|
let t = e();
|
|
58
86
|
t.addEventListener("message", (e) => {
|
|
59
87
|
e.data?.type === "SKIP_WAITING" && t.skipWaiting();
|
|
60
88
|
});
|
|
61
89
|
}
|
|
62
90
|
//#endregion
|
|
63
|
-
export {
|
|
91
|
+
export { o as installNotificationClickHandler, r as installPushHandler, s as installSkipWaitingListener };
|
|
64
92
|
|
|
65
93
|
//# sourceMappingURL=create-push-handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-push-handler.js","names":[],"sources":["../../src/sw/create-push-handler.ts"],"sourcesContent":["/**\n * Service-worker context helpers for handling `push` and `notificationclick`\n * events. Import these inside your own `sw.ts` — they expect to run in the\n * service-worker global scope, not in the main thread.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installPushHandler, installNotificationClickHandler } from \"tempest-react-sdk\";\n *\n * installPushHandler({ defaultIcon: \"/icons/Logo.png\" });\n * installNotificationClickHandler();\n */\n\ninterface SwGlobal {\n registration: {\n showNotification(title: string, options?: NotificationOptions): Promise<void>;\n };\n clients: {\n matchAll(options: { type: \"window\"; includeUncontrolled?: boolean }): Promise<\n {\n url: string;\n focused: boolean;\n focus(): Promise<unknown>;\n navigate(url: string): Promise<unknown>;\n }[]\n >;\n openWindow(url: string): Promise<unknown>;\n };\n addEventListener(\n type: \"push\",\n listener: (event: {\n data: { json(): unknown; text(): string } | null;\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n addEventListener(\n type: \"notificationclick\",\n listener: (event: {\n notification: { close(): void; data?: unknown };\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n skipWaiting(): Promise<void>;\n}\n\nfunction getSwScope(): SwGlobal {\n return globalThis as unknown as SwGlobal;\n}\n\nexport interface PushPayload {\n title?: string;\n body?: string;\n icon?: string;\n badge?: string;\n image?: string;\n tag?: string;\n url?: string;\n /** Arbitrary extra data forwarded to `event.notification.data`. */\n data?: Record<string, unknown>;\n}\n\nexport interface InstallPushHandlerOptions {\n /** Title used when the payload omits one. */\n defaultTitle?: string;\n /** Icon used when the payload omits one. */\n defaultIcon?: string;\n /** Badge image (mobile). */\n defaultBadge?: string;\n /**\n * Transform the raw payload before showing the notification. Return `null`\n * to suppress the notification entirely (e.g. silent pings).\n */\n transform?: (payload: PushPayload) => PushPayload | null;\n}\n\n/**\n * Install a `push` event listener that parses the payload as JSON (with a\n * plain-text fallback) and shows a notification.\n */\nexport function installPushHandler(options: InstallPushHandlerOptions = {}): void {\n const sw = getSwScope();\n const { defaultTitle = \"Notificação\", defaultIcon, defaultBadge, transform } = options;\n\n sw.addEventListener(\"push\", (event) => {\n if (!event.data) return;\n\n let raw: PushPayload;\n try {\n raw = event.data.json() as PushPayload;\n } catch {\n raw = { title: defaultTitle, body: event.data.text() };\n }\n\n const payload = transform ? transform(raw) : raw;\n if (!payload) return;\n\n const title = payload.title ?? defaultTitle;\n const notification: NotificationOptions & { image?: string } = {\n body: payload.body,\n icon: payload.icon ?? defaultIcon,\n badge: payload.badge ?? defaultBadge,\n image: payload.image,\n tag: payload.tag,\n data: { url: payload.url ?? \"/\", ...(payload.data ?? {}) },\n };\n\n event.waitUntil(sw.registration.showNotification(title, notification));\n });\n}\n\nexport interface InstallNotificationClickHandlerOptions {\n /** Resolve the destination URL from the notification data. Default: `data.url`. */\n resolveUrl?: (data: unknown) => string;\n}\n\n/**\n * Install a `notificationclick` handler that focuses an existing client when\n * possible and falls back to opening a new window.\n */\nexport function installNotificationClickHandler(\n options: InstallNotificationClickHandlerOptions = {},\n): void {\n const sw = getSwScope();\n const resolveUrl =\n options.resolveUrl ??\n ((data: unknown) => {\n if (typeof data === \"string\") return data;\n if (data && typeof data === \"object\" && \"url\" in data) {\n const url = (data as Record<string, unknown>).url;\n return typeof url === \"string\" ? url : \"/\";\n }\n return \"/\";\n });\n\n sw.addEventListener(\"notificationclick\", (event) => {\n event.notification.close();\n const target = resolveUrl(event.notification.data);\n\n event.waitUntil(\n (async () => {\n const clients = await sw.clients.matchAll({\n type: \"window\",\n includeUncontrolled: true,\n });\n for (const client of clients) {\n if (client.url.includes(target)) {\n return client.focus();\n }\n }\n return sw.clients.openWindow(target);\n })(),\n );\n });\n}\n\n/**\n * Install a `message` listener that activates a waiting worker when the host\n * app sends `{ type: \"SKIP_WAITING\" }`.\n */\nexport function installSkipWaitingListener(): void {\n const sw = getSwScope() as SwGlobal & {\n addEventListener(\n type: \"message\",\n listener: (event: { data?: { type?: string } }) => void,\n ): void;\n };\n sw.addEventListener(\"message\", (event) => {\n if (event.data?.type === \"SKIP_WAITING\") {\n void sw.skipWaiting();\n }\n });\n}\n"],"mappings":";AA6CA,SAAS,IAAuB;CAC5B,OAAO;AACX;AAgCA,SAAgB,EAAmB,IAAqC,CAAC,GAAS;CAC9E,IAAM,IAAK,EAAW,GAChB,EAAE,kBAAe,eAAe,gBAAa,iBAAc,iBAAc;CAE/E,EAAG,iBAAiB,SAAS,MAAU;EACnC,IAAI,CAAC,EAAM,MAAM;EAEjB,IAAI;EACJ,IAAI;GACA,IAAM,EAAM,KAAK,KAAK;EAC1B,QAAQ;GACJ,IAAM;IAAE,OAAO;IAAc,MAAM,EAAM,KAAK,KAAK;GAAE;EACzD;EAEA,IAAM,IAAU,IAAY,EAAU,CAAG,IAAI;EAC7C,IAAI,CAAC,GAAS;EAEd,IAAM,IAAQ,EAAQ,SAAS,GACzB,IAAyD;GAC3D,MAAM,EAAQ;GACd,MAAM,EAAQ,QAAQ;GACtB,OAAO,EAAQ,SAAS;GACxB,OAAO,EAAQ;GACf,KAAK,EAAQ;GACb,MAAM;IAAE,KAAK,EAAQ,OAAO;IAAK,GAAI,EAAQ,QAAQ,CAAC;GAAG;EAC7D;EAEA,EAAM,UAAU,EAAG,aAAa,iBAAiB,GAAO,CAAY,CAAC;CACzE,CAAC;AACL;AAWA,SAAgB,EACZ,IAAkD,CAAC,GAC/C;CACJ,IAAM,IAAK,EAAW,GAChB,IACF,EAAQ,gBACN,MAAkB;EAChB,IAAI,OAAO,KAAS,UAAU,OAAO;EACrC,IAAI,KAAQ,OAAO,KAAS,YAAY,SAAS,GAAM;GACnD,IAAM,IAAO,EAAiC;GAC9C,OAAO,OAAO,KAAQ,WAAW,IAAM;EAC3C;EACA,OAAO;CACX;CAEJ,EAAG,iBAAiB,sBAAsB,MAAU;EAChD,EAAM,aAAa,MAAM;EACzB,IAAM,IAAS,EAAW,EAAM,aAAa,IAAI;EAEjD,EAAM,WACD,YAAY;GACT,IAAM,IAAU,MAAM,EAAG,QAAQ,SAAS;IACtC,MAAM;IACN,qBAAqB;GACzB,CAAC;GACD,KAAK,IAAM,KAAU,GACjB,IAAI,EAAO,IAAI,SAAS,CAAM,GAC1B,OAAO,EAAO,MAAM;GAG5B,OAAO,EAAG,QAAQ,WAAW,CAAM;EACvC,EAAA,CAAG,CACP;CACJ,CAAC;AACL;AAMA,SAAgB,IAAmC;CAC/C,IAAM,IAAK,EAAW;CAMtB,EAAG,iBAAiB,YAAY,MAAU;EACtC,AAAI,EAAM,MAAM,SAAS,kBACrB,EAAQ,YAAY;CAE5B,CAAC;AACL"}
|
|
1
|
+
{"version":3,"file":"create-push-handler.js","names":[],"sources":["../../src/sw/create-push-handler.ts"],"sourcesContent":["/**\n * Service-worker context helpers for handling `push` and `notificationclick`\n * events. Import these inside your own `sw.ts` — they expect to run in the\n * service-worker global scope, not in the main thread.\n *\n * @example\n * /// <reference lib=\"webworker\" />\n * import { installPushHandler, installNotificationClickHandler } from \"tempest-react-sdk\";\n *\n * installPushHandler({ defaultIcon: \"/icons/Logo.png\" });\n * installNotificationClickHandler();\n */\n\ninterface SwGlobal {\n registration: {\n showNotification(title: string, options?: NotificationOptions): Promise<void>;\n };\n clients: {\n matchAll(options: { type: \"window\"; includeUncontrolled?: boolean }): Promise<\n {\n url: string;\n focused: boolean;\n focus(): Promise<unknown>;\n navigate(url: string): Promise<unknown>;\n }[]\n >;\n openWindow(url: string): Promise<unknown>;\n };\n addEventListener(\n type: \"push\",\n listener: (event: {\n data: { json(): unknown; text(): string } | null;\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n addEventListener(\n type: \"notificationclick\",\n listener: (event: {\n notification: { close(): void; data?: unknown };\n action?: string;\n waitUntil(promise: Promise<unknown>): void;\n }) => void,\n ): void;\n skipWaiting(): Promise<void>;\n}\n\nfunction getSwScope(): SwGlobal {\n return globalThis as unknown as SwGlobal;\n}\n\n/**\n * A notification button as the push payload describes it.\n *\n * `action`, `title` and `icon` are forwarded to the browser as a\n * `NotificationAction`. `url` is not part of that type — the browser would drop\n * it — so {@link installPushHandler} moves it into `data.actionUrls`, where\n * {@link installNotificationClickHandler} reads it back when that button is\n * clicked.\n */\nexport interface PushNotificationAction {\n /** Identifier delivered as `event.action` when the button is clicked. */\n action: string;\n /** Button label. */\n title: string;\n /** Button icon URL. */\n icon?: string;\n /** URL opened when this button is clicked. Falls back to the top-level `url`. */\n url?: string;\n}\n\nexport interface PushPayload {\n title?: string;\n body?: string;\n icon?: string;\n badge?: string;\n image?: string;\n tag?: string;\n /** URL opened when the notification body is clicked. Default: `\"/\"`. */\n url?: string;\n /** Buttons rendered on the notification (browsers show up to `Notification.maxActions`). */\n actions?: PushNotificationAction[];\n /** Keep the notification on screen until the user interacts with it. */\n requireInteraction?: boolean;\n /** Vibration pattern in milliseconds (mobile). */\n vibrate?: number | number[];\n /** Show the notification without sound or vibration. */\n silent?: boolean;\n /** Alert again when a notification with the same `tag` replaces an older one. */\n renotify?: boolean;\n /** Event time in epoch milliseconds, shown by some platforms. */\n timestamp?: number;\n /** Text direction of the title and body. */\n dir?: NotificationDirection;\n /** BCP 47 language tag of the title and body. */\n lang?: string;\n /** Arbitrary extra data forwarded to `event.notification.data`. */\n data?: Record<string, unknown>;\n}\n\n/**\n * `NotificationOptions` plus the members TypeScript's DOM lib leaves out\n * because not every engine implements them. Chromium honours all of them;\n * engines without support ignore the unknown keys.\n */\ntype ExtendedNotificationOptions = NotificationOptions & {\n image?: string;\n actions?: Omit<PushNotificationAction, \"url\">[];\n vibrate?: number | number[];\n renotify?: boolean;\n timestamp?: number;\n};\n\n/**\n * Payload keys copied verbatim onto the notification options when present.\n */\nconst FORWARDED_OPTIONS = [\n \"body\",\n \"image\",\n \"tag\",\n \"requireInteraction\",\n \"vibrate\",\n \"silent\",\n \"renotify\",\n \"timestamp\",\n \"dir\",\n \"lang\",\n] as const satisfies readonly (keyof PushPayload & keyof ExtendedNotificationOptions)[];\n\n/**\n * Build the `showNotification` options from a push payload.\n *\n * Keys the payload omits stay absent instead of being set to `undefined`, so\n * the browser applies its own defaults. Per-action `url`s are collected into\n * `data.actionUrls` (only when at least one action carries one) and stripped\n * from the actions handed to the browser.\n *\n * @param payload - The parsed (and transformed) push payload.\n * @param defaultIcon - Icon used when the payload omits one.\n * @param defaultBadge - Badge used when the payload omits one.\n * @returns The options object for `registration.showNotification`.\n */\nfunction buildNotificationOptions(\n payload: PushPayload,\n defaultIcon: string | undefined,\n defaultBadge: string | undefined,\n): ExtendedNotificationOptions {\n const options: Record<string, unknown> = {\n icon: payload.icon ?? defaultIcon,\n badge: payload.badge ?? defaultBadge,\n };\n for (const key of FORWARDED_OPTIONS) {\n if (payload[key] !== undefined) options[key] = payload[key];\n }\n\n const data: Record<string, unknown> = { url: payload.url ?? \"/\", ...(payload.data ?? {}) };\n if (payload.actions) {\n const actionUrls: Record<string, string> = {};\n options.actions = payload.actions.map(({ url, ...action }) => {\n if (url !== undefined) actionUrls[action.action] = url;\n return action;\n });\n if (Object.keys(actionUrls).length > 0) data.actionUrls = actionUrls;\n }\n options.data = data;\n return options as ExtendedNotificationOptions;\n}\n\nexport interface InstallPushHandlerOptions {\n /** Title used when the payload omits one. */\n defaultTitle?: string;\n /** Icon used when the payload omits one. */\n defaultIcon?: string;\n /** Badge image (mobile). */\n defaultBadge?: string;\n /**\n * Transform the raw payload before showing the notification. Return `null`\n * to suppress the notification entirely (e.g. silent pings).\n */\n transform?: (payload: PushPayload) => PushPayload | null;\n}\n\n/**\n * Install a `push` event listener that parses the payload as JSON (with a\n * plain-text fallback) and shows a notification.\n *\n * Every `NotificationOptions` member the payload carries is forwarded —\n * `actions`, `requireInteraction`, `vibrate`, `silent`, `renotify`,\n * `timestamp`, `dir` and `lang` alongside `body`, `icon`, `badge`, `image` and\n * `tag`. `data` receives the top-level `url` (default `\"/\"`), the payload's\n * own `data`, and `actionUrls` mapping each action id to its `url`.\n */\nexport function installPushHandler(options: InstallPushHandlerOptions = {}): void {\n const sw = getSwScope();\n const { defaultTitle = \"Notificação\", defaultIcon, defaultBadge, transform } = options;\n\n sw.addEventListener(\"push\", (event) => {\n if (!event.data) return;\n\n let raw: PushPayload;\n try {\n raw = event.data.json() as PushPayload;\n } catch {\n raw = { title: defaultTitle, body: event.data.text() };\n }\n\n const payload = transform ? transform(raw) : raw;\n if (!payload) return;\n\n const title = payload.title ?? defaultTitle;\n const notification = buildNotificationOptions(payload, defaultIcon, defaultBadge);\n\n event.waitUntil(sw.registration.showNotification(title, notification));\n });\n}\n\nexport interface InstallNotificationClickHandlerOptions {\n /**\n * Resolve the destination URL from the notification data and the clicked\n * action id (`\"\"` or `undefined` for a click on the body). Default: the\n * action's entry in `data.actionUrls`, else `data.url`, else `\"/\"`.\n */\n resolveUrl?: (data: unknown, action: string | undefined) => string;\n}\n\n/**\n * Default URL resolution for a notification click.\n *\n * @param data - `event.notification.data`, as {@link installPushHandler} wrote it.\n * @param action - `event.action`; empty or `undefined` for a click on the body.\n * @returns The action's URL when it has one, else `data.url`, else `\"/\"`.\n */\nfunction defaultResolveUrl(data: unknown, action: string | undefined): string {\n if (typeof data === \"string\") return data;\n if (!data || typeof data !== \"object\") return \"/\";\n const record = data as Record<string, unknown>;\n if (action && record.actionUrls && typeof record.actionUrls === \"object\") {\n const actionUrl = (record.actionUrls as Record<string, unknown>)[action];\n if (typeof actionUrl === \"string\") return actionUrl;\n }\n return typeof record.url === \"string\" ? record.url : \"/\";\n}\n\n/**\n * Whether an open window client is already showing `target`.\n *\n * `target` is resolved against the client's own URL, so a relative path\n * compares on the client's origin, and the comparison is on the full href.\n * A substring test would let `/events/1` match a client on `/events/10`, and\n * `/` match every client.\n *\n * @param clientUrl - The window client's absolute URL.\n * @param target - The URL the click resolved to, absolute or relative.\n * @returns `true` when both point at the same document.\n */\nfunction clientShowsTarget(clientUrl: string, target: string): boolean {\n try {\n return new URL(target, clientUrl).href === new URL(clientUrl).href;\n } catch {\n return false;\n }\n}\n\n/**\n * Install a `notificationclick` handler that focuses an existing client when\n * possible and falls back to opening a new window.\n *\n * A click on an action button opens that action's URL (see\n * {@link PushNotificationAction.url}); a click on the body, or on an action\n * without one, opens the top-level `url`.\n */\nexport function installNotificationClickHandler(\n options: InstallNotificationClickHandlerOptions = {},\n): void {\n const sw = getSwScope();\n const resolveUrl = options.resolveUrl ?? defaultResolveUrl;\n\n sw.addEventListener(\"notificationclick\", (event) => {\n event.notification.close();\n const target = resolveUrl(event.notification.data, event.action);\n\n event.waitUntil(\n (async () => {\n const clients = await sw.clients.matchAll({\n type: \"window\",\n includeUncontrolled: true,\n });\n for (const client of clients) {\n if (clientShowsTarget(client.url, target)) {\n return client.focus();\n }\n }\n return sw.clients.openWindow(target);\n })(),\n );\n });\n}\n\n/**\n * Install a `message` listener that activates a waiting worker when the host\n * app sends `{ type: \"SKIP_WAITING\" }`.\n */\nexport function installSkipWaitingListener(): void {\n const sw = getSwScope() as SwGlobal & {\n addEventListener(\n type: \"message\",\n listener: (event: { data?: { type?: string } }) => void,\n ): void;\n };\n sw.addEventListener(\"message\", (event) => {\n if (event.data?.type === \"SKIP_WAITING\") {\n void sw.skipWaiting();\n }\n });\n}\n"],"mappings":";AA8CA,SAAS,IAAuB;CAC5B,OAAO;AACX;AAmEA,IAAM,IAAoB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACJ;AAeA,SAAS,EACL,GACA,GACA,GAC2B;CAC3B,IAAM,IAAmC;EACrC,MAAM,EAAQ,QAAQ;EACtB,OAAO,EAAQ,SAAS;CAC5B;CACA,KAAK,IAAM,KAAO,GACd,AAAI,EAAQ,OAAS,KAAA,MAAW,EAAQ,KAAO,EAAQ;CAG3D,IAAM,IAAgC;EAAE,KAAK,EAAQ,OAAO;EAAK,GAAI,EAAQ,QAAQ,CAAC;CAAG;CACzF,IAAI,EAAQ,SAAS;EACjB,IAAM,IAAqC,CAAC;EAK5C,AAJA,EAAQ,UAAU,EAAQ,QAAQ,KAAK,EAAE,QAAK,GAAG,SACzC,MAAQ,KAAA,MAAW,EAAW,EAAO,UAAU,IAC5C,EACV,GACG,OAAO,KAAK,CAAU,CAAC,CAAC,SAAS,MAAG,EAAK,aAAa;CAC9D;CAEA,OADA,EAAQ,OAAO,GACR;AACX;AA0BA,SAAgB,EAAmB,IAAqC,CAAC,GAAS;CAC9E,IAAM,IAAK,EAAW,GAChB,EAAE,kBAAe,eAAe,gBAAa,iBAAc,iBAAc;CAE/E,EAAG,iBAAiB,SAAS,MAAU;EACnC,IAAI,CAAC,EAAM,MAAM;EAEjB,IAAI;EACJ,IAAI;GACA,IAAM,EAAM,KAAK,KAAK;EAC1B,QAAQ;GACJ,IAAM;IAAE,OAAO;IAAc,MAAM,EAAM,KAAK,KAAK;GAAE;EACzD;EAEA,IAAM,IAAU,IAAY,EAAU,CAAG,IAAI;EAC7C,IAAI,CAAC,GAAS;EAEd,IAAM,IAAQ,EAAQ,SAAS,GACzB,IAAe,EAAyB,GAAS,GAAa,CAAY;EAEhF,EAAM,UAAU,EAAG,aAAa,iBAAiB,GAAO,CAAY,CAAC;CACzE,CAAC;AACL;AAkBA,SAAS,EAAkB,GAAe,GAAoC;CAC1E,IAAI,OAAO,KAAS,UAAU,OAAO;CACrC,IAAI,CAAC,KAAQ,OAAO,KAAS,UAAU,OAAO;CAC9C,IAAM,IAAS;CACf,IAAI,KAAU,EAAO,cAAc,OAAO,EAAO,cAAe,UAAU;EACtE,IAAM,IAAa,EAAO,WAAuC;EACjE,IAAI,OAAO,KAAc,UAAU,OAAO;CAC9C;CACA,OAAO,OAAO,EAAO,OAAQ,WAAW,EAAO,MAAM;AACzD;AAcA,SAAS,EAAkB,GAAmB,GAAyB;CACnE,IAAI;EACA,OAAO,IAAI,IAAI,GAAQ,CAAS,CAAC,CAAC,SAAS,IAAI,IAAI,CAAS,CAAC,CAAC;CAClE,QAAQ;EACJ,OAAO;CACX;AACJ;AAUA,SAAgB,EACZ,IAAkD,CAAC,GAC/C;CACJ,IAAM,IAAK,EAAW,GAChB,IAAa,EAAQ,cAAc;CAEzC,EAAG,iBAAiB,sBAAsB,MAAU;EAChD,EAAM,aAAa,MAAM;EACzB,IAAM,IAAS,EAAW,EAAM,aAAa,MAAM,EAAM,MAAM;EAE/D,EAAM,WACD,YAAY;GACT,IAAM,IAAU,MAAM,EAAG,QAAQ,SAAS;IACtC,MAAM;IACN,qBAAqB;GACzB,CAAC;GACD,KAAK,IAAM,KAAU,GACjB,IAAI,EAAkB,EAAO,KAAK,CAAM,GACpC,OAAO,EAAO,MAAM;GAG5B,OAAO,EAAG,QAAQ,WAAW,CAAM;EACvC,EAAA,CAAG,CACP;CACJ,CAAC;AACL;AAMA,SAAgB,IAAmC;CAC/C,IAAM,IAAK,EAAW;CAMtB,EAAG,iBAAiB,YAAY,MAAU;EACtC,AAAI,EAAM,MAAM,SAAS,kBACrB,EAAQ,YAAY;CAE5B,CAAC;AACL"}
|
package/dist/sw.d.ts
CHANGED
|
@@ -110,12 +110,20 @@ export declare interface InstallBackgroundSyncOptions {
|
|
|
110
110
|
/**
|
|
111
111
|
* Install a `notificationclick` handler that focuses an existing client when
|
|
112
112
|
* possible and falls back to opening a new window.
|
|
113
|
+
*
|
|
114
|
+
* A click on an action button opens that action's URL (see
|
|
115
|
+
* {@link PushNotificationAction.url}); a click on the body, or on an action
|
|
116
|
+
* without one, opens the top-level `url`.
|
|
113
117
|
*/
|
|
114
118
|
export declare function installNotificationClickHandler(options?: InstallNotificationClickHandlerOptions): void;
|
|
115
119
|
|
|
116
120
|
export declare interface InstallNotificationClickHandlerOptions {
|
|
117
|
-
/**
|
|
118
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Resolve the destination URL from the notification data and the clicked
|
|
123
|
+
* action id (`""` or `undefined` for a click on the body). Default: the
|
|
124
|
+
* action's entry in `data.actionUrls`, else `data.url`, else `"/"`.
|
|
125
|
+
*/
|
|
126
|
+
resolveUrl?: (data: unknown, action: string | undefined) => string;
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
/**
|
|
@@ -154,6 +162,12 @@ export declare interface InstallPrecacheOptions {
|
|
|
154
162
|
/**
|
|
155
163
|
* Install a `push` event listener that parses the payload as JSON (with a
|
|
156
164
|
* plain-text fallback) and shows a notification.
|
|
165
|
+
*
|
|
166
|
+
* Every `NotificationOptions` member the payload carries is forwarded —
|
|
167
|
+
* `actions`, `requireInteraction`, `vibrate`, `silent`, `renotify`,
|
|
168
|
+
* `timestamp`, `dir` and `lang` alongside `body`, `icon`, `badge`, `image` and
|
|
169
|
+
* `tag`. `data` receives the top-level `url` (default `"/"`), the payload's
|
|
170
|
+
* own `data`, and `actionUrls` mapping each action id to its `url`.
|
|
157
171
|
*/
|
|
158
172
|
export declare function installPushHandler(options?: InstallPushHandlerOptions): void;
|
|
159
173
|
|
|
@@ -201,6 +215,26 @@ export declare function installSkipWaitingListener(): void;
|
|
|
201
215
|
* installPushHandler({ defaultIcon: "/icons/Logo.png" });
|
|
202
216
|
* installNotificationClickHandler();
|
|
203
217
|
*/
|
|
218
|
+
/**
|
|
219
|
+
* A notification button as the push payload describes it.
|
|
220
|
+
*
|
|
221
|
+
* `action`, `title` and `icon` are forwarded to the browser as a
|
|
222
|
+
* `NotificationAction`. `url` is not part of that type — the browser would drop
|
|
223
|
+
* it — so {@link installPushHandler} moves it into `data.actionUrls`, where
|
|
224
|
+
* {@link installNotificationClickHandler} reads it back when that button is
|
|
225
|
+
* clicked.
|
|
226
|
+
*/
|
|
227
|
+
export declare interface PushNotificationAction {
|
|
228
|
+
/** Identifier delivered as `event.action` when the button is clicked. */
|
|
229
|
+
action: string;
|
|
230
|
+
/** Button label. */
|
|
231
|
+
title: string;
|
|
232
|
+
/** Button icon URL. */
|
|
233
|
+
icon?: string;
|
|
234
|
+
/** URL opened when this button is clicked. Falls back to the top-level `url`. */
|
|
235
|
+
url?: string;
|
|
236
|
+
}
|
|
237
|
+
|
|
204
238
|
export declare interface PushPayload {
|
|
205
239
|
title?: string;
|
|
206
240
|
body?: string;
|
|
@@ -208,7 +242,24 @@ export declare interface PushPayload {
|
|
|
208
242
|
badge?: string;
|
|
209
243
|
image?: string;
|
|
210
244
|
tag?: string;
|
|
245
|
+
/** URL opened when the notification body is clicked. Default: `"/"`. */
|
|
211
246
|
url?: string;
|
|
247
|
+
/** Buttons rendered on the notification (browsers show up to `Notification.maxActions`). */
|
|
248
|
+
actions?: PushNotificationAction[];
|
|
249
|
+
/** Keep the notification on screen until the user interacts with it. */
|
|
250
|
+
requireInteraction?: boolean;
|
|
251
|
+
/** Vibration pattern in milliseconds (mobile). */
|
|
252
|
+
vibrate?: number | number[];
|
|
253
|
+
/** Show the notification without sound or vibration. */
|
|
254
|
+
silent?: boolean;
|
|
255
|
+
/** Alert again when a notification with the same `tag` replaces an older one. */
|
|
256
|
+
renotify?: boolean;
|
|
257
|
+
/** Event time in epoch milliseconds, shown by some platforms. */
|
|
258
|
+
timestamp?: number;
|
|
259
|
+
/** Text direction of the title and body. */
|
|
260
|
+
dir?: NotificationDirection;
|
|
261
|
+
/** BCP 47 language tag of the title and body. */
|
|
262
|
+
lang?: string;
|
|
212
263
|
/** Arbitrary extra data forwarded to `event.notification.data`. */
|
|
213
264
|
data?: Record<string, unknown>;
|
|
214
265
|
}
|
|
@@ -8007,12 +8007,20 @@ export declare type InstallMethod = "native" | "ios" | "manual" | "none";
|
|
|
8007
8007
|
/**
|
|
8008
8008
|
* Install a `notificationclick` handler that focuses an existing client when
|
|
8009
8009
|
* possible and falls back to opening a new window.
|
|
8010
|
+
*
|
|
8011
|
+
* A click on an action button opens that action's URL (see
|
|
8012
|
+
* {@link PushNotificationAction.url}); a click on the body, or on an action
|
|
8013
|
+
* without one, opens the top-level `url`.
|
|
8010
8014
|
*/
|
|
8011
8015
|
export declare function installNotificationClickHandler(options?: InstallNotificationClickHandlerOptions): void;
|
|
8012
8016
|
|
|
8013
8017
|
export declare interface InstallNotificationClickHandlerOptions {
|
|
8014
|
-
/**
|
|
8015
|
-
|
|
8018
|
+
/**
|
|
8019
|
+
* Resolve the destination URL from the notification data and the clicked
|
|
8020
|
+
* action id (`""` or `undefined` for a click on the body). Default: the
|
|
8021
|
+
* action's entry in `data.actionUrls`, else `data.url`, else `"/"`.
|
|
8022
|
+
*/
|
|
8023
|
+
resolveUrl?: (data: unknown, action: string | undefined) => string;
|
|
8016
8024
|
}
|
|
8017
8025
|
|
|
8018
8026
|
export declare type InstallOutcome = "accepted" | "dismissed" | "unsupported";
|
|
@@ -8053,6 +8061,12 @@ export declare interface InstallPrecacheOptions {
|
|
|
8053
8061
|
/**
|
|
8054
8062
|
* Install a `push` event listener that parses the payload as JSON (with a
|
|
8055
8063
|
* plain-text fallback) and shows a notification.
|
|
8064
|
+
*
|
|
8065
|
+
* Every `NotificationOptions` member the payload carries is forwarded —
|
|
8066
|
+
* `actions`, `requireInteraction`, `vibrate`, `silent`, `renotify`,
|
|
8067
|
+
* `timestamp`, `dir` and `lang` alongside `body`, `icon`, `badge`, `image` and
|
|
8068
|
+
* `tag`. `data` receives the top-level `url` (default `"/"`), the payload's
|
|
8069
|
+
* own `data`, and `actionUrls` mapping each action id to its `url`.
|
|
8056
8070
|
*/
|
|
8057
8071
|
export declare function installPushHandler(options?: InstallPushHandlerOptions): void;
|
|
8058
8072
|
|
|
@@ -11692,6 +11706,26 @@ export declare const PUSH_TO_TALK_KEYS: readonly PushToTalkKey[];
|
|
|
11692
11706
|
* installPushHandler({ defaultIcon: "/icons/Logo.png" });
|
|
11693
11707
|
* installNotificationClickHandler();
|
|
11694
11708
|
*/
|
|
11709
|
+
/**
|
|
11710
|
+
* A notification button as the push payload describes it.
|
|
11711
|
+
*
|
|
11712
|
+
* `action`, `title` and `icon` are forwarded to the browser as a
|
|
11713
|
+
* `NotificationAction`. `url` is not part of that type — the browser would drop
|
|
11714
|
+
* it — so {@link installPushHandler} moves it into `data.actionUrls`, where
|
|
11715
|
+
* {@link installNotificationClickHandler} reads it back when that button is
|
|
11716
|
+
* clicked.
|
|
11717
|
+
*/
|
|
11718
|
+
export declare interface PushNotificationAction {
|
|
11719
|
+
/** Identifier delivered as `event.action` when the button is clicked. */
|
|
11720
|
+
action: string;
|
|
11721
|
+
/** Button label. */
|
|
11722
|
+
title: string;
|
|
11723
|
+
/** Button icon URL. */
|
|
11724
|
+
icon?: string;
|
|
11725
|
+
/** URL opened when this button is clicked. Falls back to the top-level `url`. */
|
|
11726
|
+
url?: string;
|
|
11727
|
+
}
|
|
11728
|
+
|
|
11695
11729
|
export declare interface PushPayload {
|
|
11696
11730
|
title?: string;
|
|
11697
11731
|
body?: string;
|
|
@@ -11699,7 +11733,24 @@ export declare interface PushPayload {
|
|
|
11699
11733
|
badge?: string;
|
|
11700
11734
|
image?: string;
|
|
11701
11735
|
tag?: string;
|
|
11736
|
+
/** URL opened when the notification body is clicked. Default: `"/"`. */
|
|
11702
11737
|
url?: string;
|
|
11738
|
+
/** Buttons rendered on the notification (browsers show up to `Notification.maxActions`). */
|
|
11739
|
+
actions?: PushNotificationAction[];
|
|
11740
|
+
/** Keep the notification on screen until the user interacts with it. */
|
|
11741
|
+
requireInteraction?: boolean;
|
|
11742
|
+
/** Vibration pattern in milliseconds (mobile). */
|
|
11743
|
+
vibrate?: number | number[];
|
|
11744
|
+
/** Show the notification without sound or vibration. */
|
|
11745
|
+
silent?: boolean;
|
|
11746
|
+
/** Alert again when a notification with the same `tag` replaces an older one. */
|
|
11747
|
+
renotify?: boolean;
|
|
11748
|
+
/** Event time in epoch milliseconds, shown by some platforms. */
|
|
11749
|
+
timestamp?: number;
|
|
11750
|
+
/** Text direction of the title and body. */
|
|
11751
|
+
dir?: NotificationDirection;
|
|
11752
|
+
/** BCP 47 language tag of the title and body. */
|
|
11753
|
+
lang?: string;
|
|
11703
11754
|
/** Arbitrary extra data forwarded to `event.notification.data`. */
|
|
11704
11755
|
data?: Record<string, unknown>;
|
|
11705
11756
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tempest-react-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.69.0",
|
|
4
4
|
"description": "SDK público da Tempest com componentes, hooks e integrações para projetos React.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
},
|
|
111
111
|
"scripts": {
|
|
112
112
|
"dev": "vite build --watch",
|
|
113
|
-
"build": "node scripts/gen-scoped-reset.mjs && tsc -b tsconfig.build.json && node --max-old-space-size=
|
|
113
|
+
"build": "node scripts/gen-scoped-reset.mjs && tsc -b tsconfig.build.json && node --max-old-space-size=4096 ./node_modules/vite/bin/vite.js build && node scripts/split-css.mjs && node scripts/gen-style-manifest.mjs && node scripts/copy-css-assets.mjs && node scripts/link-component-css.mjs",
|
|
114
114
|
"postbuild": "node scripts/check-dist-guards.mjs",
|
|
115
115
|
"typecheck": "tsc -b tsconfig.json --noEmit",
|
|
116
116
|
"lint": "eslint .",
|