@scalebun/web 1.0.0 → 1.2.1

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.
Files changed (291) hide show
  1. package/dist/bootstrap/SDKBootstrapper.d.ts +6 -6
  2. package/dist/bootstrap/SDKBootstrapper.js +8 -8
  3. package/dist/browser.d.ts +1 -1
  4. package/dist/browser.d.ts.map +1 -1
  5. package/dist/browser.js +8 -6
  6. package/dist/browser.js.map +1 -1
  7. package/dist/config/endpoints.d.ts +12 -0
  8. package/dist/config/endpoints.d.ts.map +1 -0
  9. package/dist/config/endpoints.js +12 -0
  10. package/dist/config/endpoints.js.map +1 -0
  11. package/dist/config/schema.d.ts.map +1 -1
  12. package/dist/config/schema.js +5 -3
  13. package/dist/config/schema.js.map +1 -1
  14. package/dist/core/breadcrumbs.js +1 -1
  15. package/dist/core/index.d.ts +13 -13
  16. package/dist/core/index.js +13 -13
  17. package/dist/core/observe.js +1 -1
  18. package/dist/core/pageContext.d.ts +1 -1
  19. package/dist/features/analytics/AnalyticsFeature.d.ts +2 -2
  20. package/dist/features/analytics/AnalyticsFeature.js +10 -10
  21. package/dist/features/analytics/autocapture.js +4 -4
  22. package/dist/features/analytics/sampler.js +1 -1
  23. package/dist/features/bugreport/BugReportFeature.d.ts +1 -1
  24. package/dist/features/bugreport/BugReportFeature.js +1 -1
  25. package/dist/features/bugreport/collect.js +1 -1
  26. package/dist/features/bugreport/widget.js +9 -9
  27. package/dist/features/coach/CoachmarkFeature.d.ts +1 -1
  28. package/dist/features/coach/CoachmarkFeature.js +5 -5
  29. package/dist/features/coach/anchor.d.ts +1 -1
  30. package/dist/features/coach/overlay.d.ts +1 -1
  31. package/dist/features/coach/overlay.js +21 -21
  32. package/dist/features/coach/position.d.ts +1 -1
  33. package/dist/features/coach/predicate.d.ts +1 -1
  34. package/dist/features/coach/tour.d.ts +4 -4
  35. package/dist/features/coach/tour.js +1 -1
  36. package/dist/features/coach/types.d.ts +1 -1
  37. package/dist/features/coach/wait.d.ts +2 -2
  38. package/dist/features/config/ConfigFeature.d.ts +1 -1
  39. package/dist/features/config/ConfigFeature.js +5 -5
  40. package/dist/features/config/configStore.d.ts +1 -1
  41. package/dist/features/config/targeting.d.ts +1 -1
  42. package/dist/features/console/ConsoleFeature.js +3 -3
  43. package/dist/features/console/normalize.js +2 -2
  44. package/dist/features/console/throttle.d.ts +1 -1
  45. package/dist/features/crash/CrashFeature.js +2 -2
  46. package/dist/features/engage/EngageFeature.d.ts +3 -3
  47. package/dist/features/engage/EngageFeature.js +5 -5
  48. package/dist/features/engage/attachments.d.ts +1 -1
  49. package/dist/features/engage/attachments.js +3 -3
  50. package/dist/features/engage/gameLogic.d.ts +1 -1
  51. package/dist/features/engage/gameResolver.d.ts +2 -2
  52. package/dist/features/engage/gameResolver.js +1 -1
  53. package/dist/features/engage/inappSlots.d.ts +1 -1
  54. package/dist/features/engage/inappSlots.js +1 -1
  55. package/dist/features/engage/normalize.d.ts +1 -1
  56. package/dist/features/engage/richInApp.d.ts +3 -3
  57. package/dist/features/engage/richInApp.js +6 -6
  58. package/dist/features/engage/runtime.d.ts +3 -3
  59. package/dist/features/engage/runtime.js +7 -7
  60. package/dist/features/engage/shakeDetector.js +1 -1
  61. package/dist/features/engage/throttle.d.ts +1 -1
  62. package/dist/features/engage/transport.d.ts +1 -1
  63. package/dist/features/engage/transport.js +1 -1
  64. package/dist/features/engage/triggerEngine.d.ts +1 -1
  65. package/dist/features/engage/widget/InAppWidget.d.ts +1 -1
  66. package/dist/features/engage/widget/InAppWidget.js +18 -18
  67. package/dist/features/engage/widget/SurveyWidget.d.ts +1 -1
  68. package/dist/features/engage/widget/SurveyWidget.js +66 -66
  69. package/dist/features/errors/ErrorsFeature.d.ts +1 -1
  70. package/dist/features/errors/ErrorsFeature.js +6 -6
  71. package/dist/features/errors/group/fingerprint.js +1 -1
  72. package/dist/features/errors/lazy.d.ts +3 -3
  73. package/dist/features/errors/lazy.js +3 -3
  74. package/dist/features/errors/normalize.d.ts +2 -2
  75. package/dist/features/errors/normalize.js +2 -2
  76. package/dist/features/errors/redact.js +1 -1
  77. package/dist/features/errors/sampling.js +1 -1
  78. package/dist/features/exit-intent/ExitIntentFeature.js +1 -1
  79. package/dist/features/exit-intent/engine.js +1 -1
  80. package/dist/features/forms/index.js +1 -1
  81. package/dist/features/inapp/InApp2Feature.js +1 -1
  82. package/dist/features/inapp/anchor/controller.d.ts +2 -2
  83. package/dist/features/inapp/anchor/controller.js +3 -3
  84. package/dist/features/inapp/api.d.ts +5 -5
  85. package/dist/features/inapp/api.js +3 -3
  86. package/dist/features/inapp/blocks/blocks.d.ts +1 -1
  87. package/dist/features/inapp/blocks/blocks.js +1 -1
  88. package/dist/features/inapp/blocks/feedback.d.ts +1 -1
  89. package/dist/features/inapp/blocks/heavy/carousel.d.ts +1 -1
  90. package/dist/features/inapp/blocks/heavy/chart.d.ts +1 -1
  91. package/dist/features/inapp/blocks/heavy/code.d.ts +1 -1
  92. package/dist/features/inapp/blocks/heavy/confetti.d.ts +1 -1
  93. package/dist/features/inapp/blocks/heavy/countdown.d.ts +1 -1
  94. package/dist/features/inapp/blocks/heavy/index.d.ts +1 -1
  95. package/dist/features/inapp/blocks/heavy/index.js +18 -18
  96. package/dist/features/inapp/blocks/heavy/markdown.d.ts +1 -1
  97. package/dist/features/inapp/blocks/heavy/productgrid.d.ts +1 -1
  98. package/dist/features/inapp/blocks/heavy/qr.d.ts +1 -1
  99. package/dist/features/inapp/blocks/heavy/scratch.d.ts +1 -1
  100. package/dist/features/inapp/blocks/heavy/scratch.js +1 -1
  101. package/dist/features/inapp/blocks/heavy/slot.d.ts +1 -1
  102. package/dist/features/inapp/blocks/heavy/slot.js +1 -1
  103. package/dist/features/inapp/blocks/heavy/spinwheel.d.ts +1 -1
  104. package/dist/features/inapp/blocks/heavy/spinwheel.js +1 -1
  105. package/dist/features/inapp/blocks/heavy/steps.d.ts +1 -1
  106. package/dist/features/inapp/blocks/heavy/streak.d.ts +1 -1
  107. package/dist/features/inapp/blocks/heavy/table.d.ts +1 -1
  108. package/dist/features/inapp/blocks/heavy/unavailable.d.ts +1 -1
  109. package/dist/features/inapp/blocks/heavy/video.d.ts +1 -1
  110. package/dist/features/inapp/blocks/styles.js +191 -191
  111. package/dist/features/inapp/center/bridge.d.ts +3 -3
  112. package/dist/features/inapp/center/bridge.js +1 -1
  113. package/dist/features/inapp/center/panel.d.ts +2 -2
  114. package/dist/features/inapp/center/panel.js +33 -33
  115. package/dist/features/inapp/center/store.d.ts +1 -1
  116. package/dist/features/inapp/center/sync.d.ts +1 -1
  117. package/dist/features/inapp/center/types.d.ts +1 -1
  118. package/dist/features/inapp/core/analytics.d.ts +3 -3
  119. package/dist/features/inapp/core/analytics.js +1 -1
  120. package/dist/features/inapp/core/compat.d.ts +2 -2
  121. package/dist/features/inapp/core/debugOverlay.d.ts +2 -2
  122. package/dist/features/inapp/core/debugOverlay.js +12 -12
  123. package/dist/features/inapp/core/engine.d.ts +3 -3
  124. package/dist/features/inapp/core/engine.js +10 -10
  125. package/dist/features/inapp/core/host.js +22 -22
  126. package/dist/features/inapp/core/metrics.d.ts +1 -1
  127. package/dist/features/inapp/core/migration.d.ts +2 -2
  128. package/dist/features/inapp/core/migration.js +2 -2
  129. package/dist/features/inapp/core/orchestrator.d.ts +1 -1
  130. package/dist/features/inapp/core/respect.d.ts +1 -1
  131. package/dist/features/inapp/core/targeting.d.ts +1 -1
  132. package/dist/features/inapp/core/targeting.js +1 -1
  133. package/dist/features/inapp/core/triggers.d.ts +1 -1
  134. package/dist/features/inapp/core/triggers.js +2 -2
  135. package/dist/features/inapp/inappContract.web.d.ts +1 -1
  136. package/dist/features/inapp/index.d.ts +15 -15
  137. package/dist/features/inapp/index.js +10 -10
  138. package/dist/features/inapp/registry.web.canonical.d.ts +1 -1
  139. package/dist/features/inapp/registry.web.catalog.d.ts +1 -1
  140. package/dist/features/inapp/surfaces/banner.d.ts +1 -1
  141. package/dist/features/inapp/surfaces/banner.js +1 -1
  142. package/dist/features/inapp/surfaces/checklist.d.ts +1 -1
  143. package/dist/features/inapp/surfaces/coachmark.d.ts +1 -1
  144. package/dist/features/inapp/surfaces/coachmark.js +2 -2
  145. package/dist/features/inapp/surfaces/hotspot.d.ts +1 -1
  146. package/dist/features/inapp/surfaces/hotspot.js +2 -2
  147. package/dist/features/inapp/surfaces/inline.d.ts +4 -4
  148. package/dist/features/inapp/surfaces/inline.js +11 -11
  149. package/dist/features/inapp/surfaces/launcher.d.ts +1 -1
  150. package/dist/features/inapp/surfaces/modal.d.ts +1 -1
  151. package/dist/features/inapp/surfaces/modal.js +2 -2
  152. package/dist/features/inapp/surfaces/mount.d.ts +4 -4
  153. package/dist/features/inapp/surfaces/mount.js +4 -4
  154. package/dist/features/inapp/surfaces/palette.d.ts +1 -1
  155. package/dist/features/inapp/surfaces/palette.js +1 -1
  156. package/dist/features/inapp/surfaces/panel.d.ts +1 -1
  157. package/dist/features/inapp/surfaces/popover.d.ts +1 -1
  158. package/dist/features/inapp/surfaces/popover.js +2 -2
  159. package/dist/features/inapp/surfaces/render.d.ts +4 -4
  160. package/dist/features/inapp/surfaces/render.d.ts.map +1 -1
  161. package/dist/features/inapp/surfaces/render.js +23 -19
  162. package/dist/features/inapp/surfaces/render.js.map +1 -1
  163. package/dist/features/inapp/surfaces/slideover.d.ts +1 -1
  164. package/dist/features/inapp/surfaces/slideover.js +1 -1
  165. package/dist/features/inapp/surfaces/styles.js +179 -179
  166. package/dist/features/inapp/surfaces/surface.d.ts +4 -4
  167. package/dist/features/inapp/surfaces/surface.js +2 -2
  168. package/dist/features/inapp/surfaces/takeover.d.ts +1 -1
  169. package/dist/features/inapp/surfaces/takeover.js +2 -2
  170. package/dist/features/inapp/surfaces/toast.d.ts +1 -1
  171. package/dist/features/inapp/surfaces/toast.js +1 -1
  172. package/dist/features/inapp/surfaces/toolbar.d.ts +1 -1
  173. package/dist/features/inapp/surfaces/toolbar.js +2 -2
  174. package/dist/features/inapp/surfaces/tour.d.ts +1 -1
  175. package/dist/features/inapp/surfaces/tour.js +5 -5
  176. package/dist/features/inbox/InboxFeature.d.ts +1 -1
  177. package/dist/features/inbox/InboxFeature.js +5 -5
  178. package/dist/features/inbox/runtime.d.ts +2 -2
  179. package/dist/features/inbox/runtime.js +3 -3
  180. package/dist/features/inbox/store.d.ts +1 -1
  181. package/dist/features/inbox/sync.d.ts +2 -2
  182. package/dist/features/inbox/sync.js +1 -1
  183. package/dist/features/inbox/widget/InboxWidget.d.ts +1 -1
  184. package/dist/features/inbox/widget/InboxWidget.js +85 -85
  185. package/dist/features/journey/InteractionTracker.js +1 -1
  186. package/dist/features/journey/JourneyFeature.js +3 -3
  187. package/dist/features/manifest.d.ts +1 -1
  188. package/dist/features/manifest.js +18 -18
  189. package/dist/features/network/NetworkFeature.d.ts +1 -1
  190. package/dist/features/network/NetworkFeature.js +10 -10
  191. package/dist/features/network/capture/body.d.ts +1 -1
  192. package/dist/features/network/capture/body.js +1 -1
  193. package/dist/features/network/classify.d.ts +1 -1
  194. package/dist/features/network/fetchInterceptor.d.ts +1 -1
  195. package/dist/features/network/fetchInterceptor.js +1 -1
  196. package/dist/features/network/intercept/beacon.d.ts +1 -1
  197. package/dist/features/network/intercept/sse.d.ts +1 -1
  198. package/dist/features/network/intercept/streams.d.ts +4 -4
  199. package/dist/features/network/intercept/streams.js +3 -3
  200. package/dist/features/network/intercept/websocket.d.ts +1 -1
  201. package/dist/features/network/lazy.d.ts +3 -3
  202. package/dist/features/network/lazy.js +2 -2
  203. package/dist/features/network/normalize.d.ts +1 -1
  204. package/dist/features/network/normalize.js +3 -3
  205. package/dist/features/network/parse/graphql.d.ts +1 -1
  206. package/dist/features/network/sampler.js +1 -1
  207. package/dist/features/network/timing.d.ts +1 -1
  208. package/dist/features/network/xhrInterceptor.d.ts +1 -1
  209. package/dist/features/network/xhrInterceptor.js +1 -1
  210. package/dist/features/performance/PerformanceFeature.d.ts +2 -2
  211. package/dist/features/performance/PerformanceFeature.js +11 -11
  212. package/dist/features/performance/longTasks.js +1 -1
  213. package/dist/features/performance/normalize.js +1 -1
  214. package/dist/features/performance/sampler.js +1 -1
  215. package/dist/features/performance/trace/index.d.ts +4 -4
  216. package/dist/features/performance/trace/index.js +3 -3
  217. package/dist/features/performance/trace/pageload.d.ts +1 -1
  218. package/dist/features/performance/trace/pageload.js +1 -1
  219. package/dist/features/performance/trace/transaction.d.ts +2 -2
  220. package/dist/features/performance/trace/transaction.js +2 -2
  221. package/dist/features/profiler/ProfilerFeature.js +1 -1
  222. package/dist/features/push/PushFeature.d.ts +4 -4
  223. package/dist/features/push/PushFeature.js +3 -3
  224. package/dist/features/push/softAsk.d.ts +1 -1
  225. package/dist/features/push/softAsk.js +26 -26
  226. package/dist/features/replay/ReplayFeature.d.ts +1 -1
  227. package/dist/features/replay/ReplayFeature.js +12 -12
  228. package/dist/features/replay/engine/canvas.d.ts +2 -2
  229. package/dist/features/replay/engine/crossOrigin.d.ts +1 -1
  230. package/dist/features/replay/engine/crossOrigin.js +3 -3
  231. package/dist/features/replay/engine/cssom.d.ts +2 -2
  232. package/dist/features/replay/engine/iframe.d.ts +2 -2
  233. package/dist/features/replay/engine/iframe.js +3 -3
  234. package/dist/features/replay/engine/interactions.d.ts +1 -1
  235. package/dist/features/replay/engine/interactions.js +1 -1
  236. package/dist/features/replay/engine/mutations.d.ts +3 -3
  237. package/dist/features/replay/engine/mutations.js +2 -2
  238. package/dist/features/replay/engine/player.d.ts +2 -2
  239. package/dist/features/replay/engine/player.js +1 -1
  240. package/dist/features/replay/engine/rebuild.d.ts +1 -1
  241. package/dist/features/replay/engine/replayWorker.js +16 -16
  242. package/dist/features/replay/engine/resources.d.ts +1 -1
  243. package/dist/features/replay/engine/resources.js +1 -1
  244. package/dist/features/replay/engine/snapshot.d.ts +2 -2
  245. package/dist/features/replay/engine/snapshot.js +1 -1
  246. package/dist/features/replay/engine/traverse.d.ts +1 -1
  247. package/dist/features/replay/lifecycle/recorder.d.ts +1 -1
  248. package/dist/features/replay/lifecycle/recorder.js +20 -20
  249. package/dist/features/replay/lifecycle/sampling.js +1 -1
  250. package/dist/features/replay/lifecycle/viewContext.js +1 -1
  251. package/dist/features/scroll/index.js +1 -1
  252. package/dist/features/session/sessionManager.d.ts +1 -1
  253. package/dist/features/session/sessionManager.js +2 -2
  254. package/dist/features/session/sessionStore.js +1 -1
  255. package/dist/features/watchdog/WatchdogFeature.d.ts +1 -1
  256. package/dist/features/watchdog/WatchdogFeature.js +1 -1
  257. package/dist/features/watchdog/collectors.d.ts +1 -1
  258. package/dist/features/watchdog/collectors.js +1 -1
  259. package/dist/features/watchdog/engine.js +8 -8
  260. package/dist/features/watchdog/incidents.d.ts +1 -1
  261. package/dist/features/watchdog/incidents.js +1 -1
  262. package/dist/features/watchdog/rollup.d.ts +1 -1
  263. package/dist/features/watchdog/rollup.js +1 -1
  264. package/dist/features/webVitals/WebVitalsFeature.js +4 -4
  265. package/dist/features/webVitals/observe.d.ts +1 -1
  266. package/dist/features/webVitals/observe.js +1 -1
  267. package/dist/features/webperf/WebPerfFeature.d.ts +1 -1
  268. package/dist/features/webperf/WebPerfFeature.js +3 -3
  269. package/dist/features/webperf/collectors.js +1 -1
  270. package/dist/inapp.d.ts +1 -1
  271. package/dist/inapp.js +1 -1
  272. package/dist/index.d.ts +3 -3
  273. package/dist/index.js +1 -1
  274. package/dist/pipeline/index.d.ts +1 -1
  275. package/dist/pipeline/index.js +1 -1
  276. package/dist/pipeline/pipeline.d.ts +3 -3
  277. package/dist/pipeline/pipeline.js +4 -4
  278. package/dist/public/ScaleBunFacade.d.ts +18 -18
  279. package/dist/public/ScaleBunFacade.js +11 -11
  280. package/dist/public/index.d.ts +4 -4
  281. package/dist/public/index.js +2 -2
  282. package/dist/scalebun-push-sw.js +236 -236
  283. package/dist/scalebun.min.js +17 -17
  284. package/dist/scalebun.min.js.map +4 -4
  285. package/dist/storage/index.d.ts +1 -1
  286. package/dist/storage/index.js +1 -1
  287. package/dist/transport/httpClient.d.ts +1 -1
  288. package/dist/transport/httpClient.js +3 -3
  289. package/dist/transport/index.d.ts +1 -1
  290. package/dist/transport/index.js +1 -1
  291. package/package.json +7 -7
@@ -1,236 +1,236 @@
1
- /*!
2
- * ScaleBun Web Push service worker (scalebun-push-sw.js)
3
- * ------------------------------------------------------------------------------------------------
4
- * The HOST serves this file at their own origin (like favicon.ico) — a service worker CANNOT be
5
- * bundled into the SDK or loaded cross-origin, so ship it as a static asset and register it with
6
- * `ScaleBun.enablePush({ serviceWorkerUrl: '/scalebun-push-sw.js' })`.
7
- *
8
- * Responsibilities (all dependency-free, tiny, versioned):
9
- * 1. `push` — render the notification the backend signed with the VAPID key.
10
- * 2. `notificationclick` — focus/open the target URL AND postMessage the page so the SDK
11
- * can report the open/click (opens → analytics, replay-correlated).
12
- * 3. `pushsubscriptionchange` — the browser rotated the subscription: re-subscribe with the same
13
- * VAPID key and tell the page to re-register the device token.
14
- * 4. `message` (version ping) — report WZB_SW_VERSION so ScaleBun.push.check() can tell the host
15
- * their hand-copied worker has gone stale. Because the host copies
16
- * this file manually, a worker older than v3 cannot answer at all —
17
- * check() reports "missing OR pre-v3" rather than guessing.
18
- *
19
- * Payload shape (from WebPushProvider.sendToToken):
20
- * { title, body, icon?, badge?, image?, actions?, tag?, renotify?, requireInteraction?, silent?,
21
- * dir?, lang?, timestamp?, data?: { url?, scalebunCampaignId?, scalebunType?, ... } }
22
- *
23
- * ⚠️ VERSION COUPLING: the host COPIES this file to their own origin, so a payload field added here
24
- * only renders once they re-copy it. `ScaleBun.push.check()` compares WZB_SW_VERSION against the
25
- * SDK's EXPECTED_SW_VERSION and reports a stale worker — that report is what stops a v4 host
26
- * silently dropping v5 fields and reading it as "the browser ignored my image".
27
- */
28
- 'use strict';
29
-
30
- var WZB_SW_VERSION = 5; // v5: authorable notification presentation (tag/renotify/requireInteraction/silent/dir/lang/timestamp)
31
-
32
- // Take control ASAP so the first push after registration is handled by THIS worker.
33
- self.addEventListener('install', function () {
34
- self.skipWaiting();
35
- });
36
- self.addEventListener('activate', function (event) {
37
- event.waitUntil(self.clients.claim());
38
- });
39
-
40
- // ── 1. Render the push ──────────────────────────────────────────────────────────────────────────
41
- // Post a message to every open page (best-effort; a dead client just throws and is skipped).
42
- // ── Push Doctor beacon (v4) ──────────────────────────────────────────────────────────────────────
43
- // ONLY fires for a "Test Push" — production pushes carry no scalebunTestRunId and therefore never
44
- // beacon. When no tab is open, the worker is the only layer that knows a push arrived, so this is the
45
- // only way the dashboard can show "received".
46
- //
47
- // The worker holds NO client key (deliberately — see the pushsubscriptionchange note below), so the
48
- // test payload carries an absolute beacon URL and a short-lived signed token. The worker POSTs blind:
49
- // it never learns anything about the tenant, and the token authorises exactly one thing — appending
50
- // one stage to one run, for a few minutes.
51
- function scalebunBeacon(data, stage) {
52
- if (!data || !data.scalebunTestRunId || !data.scalebunTestBeacon || !data.scalebunTestToken) return;
53
- try {
54
- fetch(data.scalebunTestBeacon, {
55
- method: 'POST',
56
- headers: { 'content-type': 'application/json' },
57
- keepalive: true,
58
- body: JSON.stringify({ token: data.scalebunTestToken, stage: stage }),
59
- }).catch(function () {
60
- /* diagnostics must never affect delivery — a failed beacon just shows as "no report" */
61
- });
62
- } catch (e) {
63
- /* ignore */
64
- }
65
- }
66
-
67
- function scalebunPostToClients(clientList, message) {
68
- for (var i = 0; i < clientList.length; i++) {
69
- try {
70
- clientList[i].postMessage(message);
71
- } catch (e) {
72
- /* ignore a dead client */
73
- }
74
- }
75
- }
76
-
77
- self.addEventListener('push', function (event) {
78
- var payload = {};
79
- if (event.data) {
80
- try {
81
- payload = event.data.json();
82
- } catch (e) {
83
- // Non-JSON payload — fall back to the raw text as the body.
84
- try {
85
- payload = { body: event.data.text() };
86
- } catch (e2) {
87
- payload = {};
88
- }
89
- }
90
- }
91
-
92
- var data = payload.data || {};
93
- // Campaigns delivered to BOTH push + inbox flag the push so this worker can (a) poke an open page to
94
- // pull the new inbox message live and (b) dedupe — suppress the OS banner when a tab is already
95
- // focused (the in-app inbox surfaces it, so a second OS notification would double-notify).
96
- var isInbox = !!(data.scalebunInbox || data.scalebunType === 'inbox');
97
- // A pure sync poke (data-only, no visible content) never renders a banner — it only refreshes.
98
- //
99
- // ⚠️ v5 — `payload.silent` is NO LONGER a signal here, and removing it was required rather than
100
- // tidy. `silent` became an AUTHORABLE presentation field (deliver without sound/vibration), and it
101
- // used to also mean "this is an internal sync poke, show nothing". An author ticking Silent would
102
- // therefore have suppressed their own notification entirely — a campaign that sends successfully,
103
- // reports delivered, and never appears. The internal poke has always carried its own explicit
104
- // marker, so that marker is now the only thing that means it.
105
- var isSilentSync = data.scalebunType === 'inbox-sync';
106
-
107
- // Report arrival before anything else can go wrong (rendering, dedupe, a closed tab).
108
- scalebunBeacon(data, 'received');
109
-
110
- event.waitUntil(
111
- self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clientList) {
112
- if (isInbox || isSilentSync) {
113
- scalebunPostToClients(clientList, { type: 'scalebun-inbox-sync', campaignId: data.scalebunCampaignId || null });
114
- }
115
-
116
- // Silent sync poke: pull-only, never show a notification.
117
- if (isSilentSync) return undefined;
118
-
119
- // Foreground dedupe: if this push mirrors an inbox message and a tab is focused, let the in-app
120
- // inbox handle it instead of stacking an OS banner on top.
121
- if (isInbox) {
122
- for (var k = 0; k < clientList.length; k++) {
123
- if (clientList[k].focused || clientList[k].visibilityState === 'visible') return undefined;
124
- }
125
- }
126
-
127
- var title = payload.title || 'Notification';
128
- var options = {
129
- body: payload.body || '',
130
- // Keep the full payload on the notification so notificationclick can read data.url etc.
131
- data: data,
132
- // Collapse repeat sends into one banner. v5: an AUTHORED `payload.tag` wins; `data.tag` stays
133
- // supported for older senders, and the inbox campaign tag remains the fallback.
134
- tag:
135
- payload.tag ||
136
- data.tag ||
137
- (isInbox && data.scalebunCampaignId ? 'scalebun-inbox-' + data.scalebunCampaignId : undefined),
138
- };
139
- // v5 — renotify is authorable. It is only legal WITH a tag, so it is derived when unset rather
140
- // than sent blindly: a renotify with no tag makes some browsers reject the whole notification.
141
- var hasTag = !!options.tag;
142
- options.renotify =
143
- payload.renotify != null ? !!payload.renotify && hasTag : hasTag && !!(data.tag || (isInbox && data.scalebunCampaignId));
144
-
145
- if (payload.icon) options.icon = payload.icon;
146
- if (payload.badge) options.badge = payload.badge;
147
- if (payload.image) options.image = payload.image;
148
- if (payload.actions) options.actions = payload.actions;
149
- // v5 — the rest of what a browser can render. Each is set only when the sender asked, so an
150
- // older payload behaves exactly as it did before.
151
- if (payload.requireInteraction != null) options.requireInteraction = !!payload.requireInteraction;
152
- if (payload.silent != null) options.silent = !!payload.silent;
153
- if (payload.dir) options.dir = payload.dir;
154
- if (payload.lang) options.lang = payload.lang;
155
- if (typeof payload.timestamp === 'number') options.timestamp = payload.timestamp;
156
-
157
- return self.registration.showNotification(title, options);
158
- })
159
- );
160
- });
161
-
162
- // ── 2. Handle a click → focus/open the target + notify the page ───────────────────────────────────
163
- self.addEventListener('notificationclick', function (event) {
164
- event.notification.close();
165
- var data = event.notification.data || {};
166
- var url = data.url || data.deepLink || '/';
167
- scalebunBeacon(data, 'clicked');
168
-
169
- event.waitUntil(
170
- self.clients
171
- .matchAll({ type: 'window', includeUncontrolled: true })
172
- .then(function (clientList) {
173
- // Tell any open page it was opened from a push, so the SDK reports the click. Carries the
174
- // campaign id so an inbox-mirrored push marks its inbox copy read (push↔inbox correlation).
175
- scalebunPostToClients(clientList, { type: 'scalebun-push-click', data: data, action: event.action || null });
176
- // Focus an existing tab on the same origin if we have one; else open a new one.
177
- for (var j = 0; j < clientList.length; j++) {
178
- var client = clientList[j];
179
- if ('focus' in client) {
180
- try {
181
- if (url && 'navigate' in client) client.navigate(url);
182
- } catch (e) {
183
- /* cross-origin navigate can throw — ignore, just focus */
184
- }
185
- return client.focus();
186
- }
187
- }
188
- if (self.clients.openWindow) return self.clients.openWindow(url);
189
- return undefined;
190
- })
191
- );
192
- });
193
-
194
- // ── 4. Version ping (diagnostics) ─────────────────────────────────────────────────────────────────
195
- // Replies on the MessagePort the page supplied. Deliberately answers ONLY this one message type and
196
- // leaks nothing else about the worker.
197
- self.addEventListener('message', function (event) {
198
- var data = event.data;
199
- if (!data || data.type !== 'scalebun-sw-ping') return;
200
- var reply = { type: 'scalebun-sw-pong', version: WZB_SW_VERSION };
201
- try {
202
- if (event.ports && event.ports[0]) event.ports[0].postMessage(reply);
203
- else if (event.source && event.source.postMessage) event.source.postMessage(reply);
204
- } catch (e) {
205
- /* the asking page went away — nothing to do */
206
- }
207
- });
208
-
209
- // ── 3. Re-subscribe on browser-driven rotation ────────────────────────────────────────────────────
210
- self.addEventListener('pushsubscriptionchange', function (event) {
211
- // The old subscription is dead; mint a new one with the same VAPID key (kept on the old sub, or
212
- // fall back to the event's newSubscription when the browser supplies it). Then tell the page to
213
- // re-register the device token — the page holds the client key, the worker does not.
214
- event.waitUntil(
215
- (function () {
216
- var applicationServerKey =
217
- (event.oldSubscription && event.oldSubscription.options && event.oldSubscription.options.applicationServerKey) ||
218
- undefined;
219
- var resub = event.newSubscription
220
- ? Promise.resolve(event.newSubscription)
221
- : self.registration.pushManager.subscribe({
222
- userVisibleOnly: true,
223
- applicationServerKey: applicationServerKey,
224
- });
225
- return resub
226
- .then(function (sub) {
227
- return self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clientList) {
228
- scalebunPostToClients(clientList, { type: 'scalebun-push-resubscribe', subscription: sub ? sub.toJSON() : null });
229
- });
230
- })
231
- .catch(function () {
232
- /* best-effort — a failed re-subscribe simply means the next enablePush() re-registers */
233
- });
234
- })()
235
- );
236
- });
1
+ /*!
2
+ * ScaleBun Web Push service worker (scalebun-push-sw.js)
3
+ * ------------------------------------------------------------------------------------------------
4
+ * The HOST serves this file at their own origin (like favicon.ico) — a service worker CANNOT be
5
+ * bundled into the SDK or loaded cross-origin, so ship it as a static asset and register it with
6
+ * `ScaleBun.enablePush({ serviceWorkerUrl: '/scalebun-push-sw.js' })`.
7
+ *
8
+ * Responsibilities (all dependency-free, tiny, versioned):
9
+ * 1. `push` — render the notification the backend signed with the VAPID key.
10
+ * 2. `notificationclick` — focus/open the target URL AND postMessage the page so the SDK
11
+ * can report the open/click (opens → analytics, replay-correlated).
12
+ * 3. `pushsubscriptionchange` — the browser rotated the subscription: re-subscribe with the same
13
+ * VAPID key and tell the page to re-register the device token.
14
+ * 4. `message` (version ping) — report WZB_SW_VERSION so ScaleBun.push.check() can tell the host
15
+ * their hand-copied worker has gone stale. Because the host copies
16
+ * this file manually, a worker older than v3 cannot answer at all —
17
+ * check() reports "missing OR pre-v3" rather than guessing.
18
+ *
19
+ * Payload shape (from WebPushProvider.sendToToken):
20
+ * { title, body, icon?, badge?, image?, actions?, tag?, renotify?, requireInteraction?, silent?,
21
+ * dir?, lang?, timestamp?, data?: { url?, scalebunCampaignId?, scalebunType?, ... } }
22
+ *
23
+ * ⚠️ VERSION COUPLING: the host COPIES this file to their own origin, so a payload field added here
24
+ * only renders once they re-copy it. `ScaleBun.push.check()` compares WZB_SW_VERSION against the
25
+ * SDK's EXPECTED_SW_VERSION and reports a stale worker — that report is what stops a v4 host
26
+ * silently dropping v5 fields and reading it as "the browser ignored my image".
27
+ */
28
+ 'use strict';
29
+
30
+ var WZB_SW_VERSION = 5; // v5: authorable notification presentation (tag/renotify/requireInteraction/silent/dir/lang/timestamp)
31
+
32
+ // Take control ASAP so the first push after registration is handled by THIS worker.
33
+ self.addEventListener('install', function () {
34
+ self.skipWaiting();
35
+ });
36
+ self.addEventListener('activate', function (event) {
37
+ event.waitUntil(self.clients.claim());
38
+ });
39
+
40
+ // ── 1. Render the push ──────────────────────────────────────────────────────────────────────────
41
+ // Post a message to every open page (best-effort; a dead client just throws and is skipped).
42
+ // ── Push Doctor beacon (v4) ──────────────────────────────────────────────────────────────────────
43
+ // ONLY fires for a "Test Push" — production pushes carry no scalebunTestRunId and therefore never
44
+ // beacon. When no tab is open, the worker is the only layer that knows a push arrived, so this is the
45
+ // only way the dashboard can show "received".
46
+ //
47
+ // The worker holds NO client key (deliberately — see the pushsubscriptionchange note below), so the
48
+ // test payload carries an absolute beacon URL and a short-lived signed token. The worker POSTs blind:
49
+ // it never learns anything about the tenant, and the token authorises exactly one thing — appending
50
+ // one stage to one run, for a few minutes.
51
+ function scalebunBeacon(data, stage) {
52
+ if (!data || !data.scalebunTestRunId || !data.scalebunTestBeacon || !data.scalebunTestToken) return;
53
+ try {
54
+ fetch(data.scalebunTestBeacon, {
55
+ method: 'POST',
56
+ headers: { 'content-type': 'application/json' },
57
+ keepalive: true,
58
+ body: JSON.stringify({ token: data.scalebunTestToken, stage: stage }),
59
+ }).catch(function () {
60
+ /* diagnostics must never affect delivery — a failed beacon just shows as "no report" */
61
+ });
62
+ } catch (e) {
63
+ /* ignore */
64
+ }
65
+ }
66
+
67
+ function scalebunPostToClients(clientList, message) {
68
+ for (var i = 0; i < clientList.length; i++) {
69
+ try {
70
+ clientList[i].postMessage(message);
71
+ } catch (e) {
72
+ /* ignore a dead client */
73
+ }
74
+ }
75
+ }
76
+
77
+ self.addEventListener('push', function (event) {
78
+ var payload = {};
79
+ if (event.data) {
80
+ try {
81
+ payload = event.data.json();
82
+ } catch (e) {
83
+ // Non-JSON payload — fall back to the raw text as the body.
84
+ try {
85
+ payload = { body: event.data.text() };
86
+ } catch (e2) {
87
+ payload = {};
88
+ }
89
+ }
90
+ }
91
+
92
+ var data = payload.data || {};
93
+ // Campaigns delivered to BOTH push + inbox flag the push so this worker can (a) poke an open page to
94
+ // pull the new inbox message live and (b) dedupe — suppress the OS banner when a tab is already
95
+ // focused (the in-app inbox surfaces it, so a second OS notification would double-notify).
96
+ var isInbox = !!(data.scalebunInbox || data.scalebunType === 'inbox');
97
+ // A pure sync poke (data-only, no visible content) never renders a banner — it only refreshes.
98
+ //
99
+ // ⚠️ v5 — `payload.silent` is NO LONGER a signal here, and removing it was required rather than
100
+ // tidy. `silent` became an AUTHORABLE presentation field (deliver without sound/vibration), and it
101
+ // used to also mean "this is an internal sync poke, show nothing". An author ticking Silent would
102
+ // therefore have suppressed their own notification entirely — a campaign that sends successfully,
103
+ // reports delivered, and never appears. The internal poke has always carried its own explicit
104
+ // marker, so that marker is now the only thing that means it.
105
+ var isSilentSync = data.scalebunType === 'inbox-sync';
106
+
107
+ // Report arrival before anything else can go wrong (rendering, dedupe, a closed tab).
108
+ scalebunBeacon(data, 'received');
109
+
110
+ event.waitUntil(
111
+ self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clientList) {
112
+ if (isInbox || isSilentSync) {
113
+ scalebunPostToClients(clientList, { type: 'scalebun-inbox-sync', campaignId: data.scalebunCampaignId || null });
114
+ }
115
+
116
+ // Silent sync poke: pull-only, never show a notification.
117
+ if (isSilentSync) return undefined;
118
+
119
+ // Foreground dedupe: if this push mirrors an inbox message and a tab is focused, let the in-app
120
+ // inbox handle it instead of stacking an OS banner on top.
121
+ if (isInbox) {
122
+ for (var k = 0; k < clientList.length; k++) {
123
+ if (clientList[k].focused || clientList[k].visibilityState === 'visible') return undefined;
124
+ }
125
+ }
126
+
127
+ var title = payload.title || 'Notification';
128
+ var options = {
129
+ body: payload.body || '',
130
+ // Keep the full payload on the notification so notificationclick can read data.url etc.
131
+ data: data,
132
+ // Collapse repeat sends into one banner. v5: an AUTHORED `payload.tag` wins; `data.tag` stays
133
+ // supported for older senders, and the inbox campaign tag remains the fallback.
134
+ tag:
135
+ payload.tag ||
136
+ data.tag ||
137
+ (isInbox && data.scalebunCampaignId ? 'scalebun-inbox-' + data.scalebunCampaignId : undefined),
138
+ };
139
+ // v5 — renotify is authorable. It is only legal WITH a tag, so it is derived when unset rather
140
+ // than sent blindly: a renotify with no tag makes some browsers reject the whole notification.
141
+ var hasTag = !!options.tag;
142
+ options.renotify =
143
+ payload.renotify != null ? !!payload.renotify && hasTag : hasTag && !!(data.tag || (isInbox && data.scalebunCampaignId));
144
+
145
+ if (payload.icon) options.icon = payload.icon;
146
+ if (payload.badge) options.badge = payload.badge;
147
+ if (payload.image) options.image = payload.image;
148
+ if (payload.actions) options.actions = payload.actions;
149
+ // v5 — the rest of what a browser can render. Each is set only when the sender asked, so an
150
+ // older payload behaves exactly as it did before.
151
+ if (payload.requireInteraction != null) options.requireInteraction = !!payload.requireInteraction;
152
+ if (payload.silent != null) options.silent = !!payload.silent;
153
+ if (payload.dir) options.dir = payload.dir;
154
+ if (payload.lang) options.lang = payload.lang;
155
+ if (typeof payload.timestamp === 'number') options.timestamp = payload.timestamp;
156
+
157
+ return self.registration.showNotification(title, options);
158
+ })
159
+ );
160
+ });
161
+
162
+ // ── 2. Handle a click → focus/open the target + notify the page ───────────────────────────────────
163
+ self.addEventListener('notificationclick', function (event) {
164
+ event.notification.close();
165
+ var data = event.notification.data || {};
166
+ var url = data.url || data.deepLink || '/';
167
+ scalebunBeacon(data, 'clicked');
168
+
169
+ event.waitUntil(
170
+ self.clients
171
+ .matchAll({ type: 'window', includeUncontrolled: true })
172
+ .then(function (clientList) {
173
+ // Tell any open page it was opened from a push, so the SDK reports the click. Carries the
174
+ // campaign id so an inbox-mirrored push marks its inbox copy read (push↔inbox correlation).
175
+ scalebunPostToClients(clientList, { type: 'scalebun-push-click', data: data, action: event.action || null });
176
+ // Focus an existing tab on the same origin if we have one; else open a new one.
177
+ for (var j = 0; j < clientList.length; j++) {
178
+ var client = clientList[j];
179
+ if ('focus' in client) {
180
+ try {
181
+ if (url && 'navigate' in client) client.navigate(url);
182
+ } catch (e) {
183
+ /* cross-origin navigate can throw — ignore, just focus */
184
+ }
185
+ return client.focus();
186
+ }
187
+ }
188
+ if (self.clients.openWindow) return self.clients.openWindow(url);
189
+ return undefined;
190
+ })
191
+ );
192
+ });
193
+
194
+ // ── 4. Version ping (diagnostics) ─────────────────────────────────────────────────────────────────
195
+ // Replies on the MessagePort the page supplied. Deliberately answers ONLY this one message type and
196
+ // leaks nothing else about the worker.
197
+ self.addEventListener('message', function (event) {
198
+ var data = event.data;
199
+ if (!data || data.type !== 'scalebun-sw-ping') return;
200
+ var reply = { type: 'scalebun-sw-pong', version: WZB_SW_VERSION };
201
+ try {
202
+ if (event.ports && event.ports[0]) event.ports[0].postMessage(reply);
203
+ else if (event.source && event.source.postMessage) event.source.postMessage(reply);
204
+ } catch (e) {
205
+ /* the asking page went away — nothing to do */
206
+ }
207
+ });
208
+
209
+ // ── 3. Re-subscribe on browser-driven rotation ────────────────────────────────────────────────────
210
+ self.addEventListener('pushsubscriptionchange', function (event) {
211
+ // The old subscription is dead; mint a new one with the same VAPID key (kept on the old sub, or
212
+ // fall back to the event's newSubscription when the browser supplies it). Then tell the page to
213
+ // re-register the device token — the page holds the client key, the worker does not.
214
+ event.waitUntil(
215
+ (function () {
216
+ var applicationServerKey =
217
+ (event.oldSubscription && event.oldSubscription.options && event.oldSubscription.options.applicationServerKey) ||
218
+ undefined;
219
+ var resub = event.newSubscription
220
+ ? Promise.resolve(event.newSubscription)
221
+ : self.registration.pushManager.subscribe({
222
+ userVisibleOnly: true,
223
+ applicationServerKey: applicationServerKey,
224
+ });
225
+ return resub
226
+ .then(function (sub) {
227
+ return self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clientList) {
228
+ scalebunPostToClients(clientList, { type: 'scalebun-push-resubscribe', subscription: sub ? sub.toJSON() : null });
229
+ });
230
+ })
231
+ .catch(function () {
232
+ /* best-effort — a failed re-subscribe simply means the next enablePush() re-registers */
233
+ });
234
+ })()
235
+ );
236
+ });