@arsel.sa/web-sdk 1.0.0 → 1.1.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.
- package/CHANGELOG.md +81 -0
- package/dist/arsel.js +1053 -329
- package/dist/arsel.js.map +1 -1
- package/dist/arsel.umd.cjs +107 -1
- package/dist/arsel.umd.cjs.map +1 -1
- package/dist/events.d.ts +2 -0
- package/dist/inapp-view.d.ts +16 -0
- package/dist/inapp.d.ts +83 -0
- package/dist/index.d.ts +20 -9
- package/dist/push.d.ts +16 -0
- package/dist/session.d.ts +5 -1
- package/dist/store.d.ts +30 -4
- package/dist/transport.d.ts +1 -1
- package/dist/types.d.ts +32 -0
- package/dist/version.d.ts +1 -1
- package/package.json +2 -1
- package/sw/arsel-sw.js +27 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,87 @@ Breaking changes to the public surface wait for a major release, and are listed
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.1.0] — 2026-08-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **In-app messaging.** Messages authored in Arsel now render in the page, with no notification
|
|
15
|
+
permission and no prompt of any kind. The server resolves audience, consent, campaign window,
|
|
16
|
+
grants and lifetime caps into a per-device bundle; the SDK evaluates the trigger and the
|
|
17
|
+
session-scoped caps locally, so drawing a message costs no network round-trip. Four layouts —
|
|
18
|
+
`MODAL`, `BANNER_TOP`, `BANNER_BOTTOM`, `IMAGE_ONLY` — rendered into a closed shadow root with
|
|
19
|
+
a constructed stylesheet (falling back to `<style>`), text set with `textContent` only, and
|
|
20
|
+
colours applied through CSSOM so a strict `style-src` cannot strip them.
|
|
21
|
+
- **`screen(name, properties?)`.** Records a screen or page view. One request, two consumers: the
|
|
22
|
+
event reaches segments and automations exactly as `track()` would, and it is the trigger source
|
|
23
|
+
for `SCREEN_VIEW` in-app messages.
|
|
24
|
+
- **`suppressInAppMessages(boolean)`.** Holds messages back for the moments a host app knows are
|
|
25
|
+
wrong — a checkout step, a video playing full-screen.
|
|
26
|
+
- **`inApp` config option.** `true` by default; pass `false` to disable, or `{ zIndex, closeLabel }`
|
|
27
|
+
to tune the layer. The default `zIndex` sits below the maximum so a host site's own top-most
|
|
28
|
+
modal still wins.
|
|
29
|
+
- **Device registration without a push subscription.** `init()` now registers the browser so the
|
|
30
|
+
bundle fetch can authenticate. It shows no prompt, creates no subscription, and never touches the
|
|
31
|
+
service worker — gating in-app behind `promptForPush()` would have restricted the channel to the
|
|
32
|
+
minority who accept notifications.
|
|
33
|
+
- **Accessibility.** Modals get `role="dialog"`, `aria-modal`, a focus trap reading `root.activeElement`
|
|
34
|
+
(a closed root makes `document.activeElement` resolve to the host), focus capture and restore, and
|
|
35
|
+
`Escape` to dismiss. Banners get `role="status"` with `aria-live="polite"` and never steal focus.
|
|
36
|
+
Direction is read from the host page, not assumed.
|
|
37
|
+
- **`arsel_iam_sync` service-worker handling.** A reserved silent push refreshes the bundle and
|
|
38
|
+
renders nothing. Ships inert — nothing emits it yet — so refresh is driven by `init()`, tab
|
|
39
|
+
visibility and the bundle's own TTL.
|
|
40
|
+
- Diagnostics gain `inAppMessages`, `inAppBundleVersion`, `inAppFetchedAtMs` and
|
|
41
|
+
`pendingInAppBeacons`.
|
|
42
|
+
- **`diagnostics().configError`.** Reports why initialization was refused, readable before
|
|
43
|
+
initialization — which is precisely the state it describes.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **`getJson()` returns the full `{ result, code, body }` envelope**, matching `post()`. The bundle
|
|
48
|
+
fetch has to branch on `304` *before* `classify()` sees it — `classify` maps 304 to `permanent`,
|
|
49
|
+
which would discard the cache on every successful revalidation.
|
|
50
|
+
- **The store's queue helpers take a leading queue name.** In-app beacons live in their own object
|
|
51
|
+
store: the events drain stops at the first retryable failure to preserve history order, so a
|
|
52
|
+
single stuck beacon sharing that queue would wedge the entire analytics pipeline behind it.
|
|
53
|
+
- IndexedDB schema version 1 → 2, adding the beacon store. The upgrade is guarded by store name, so
|
|
54
|
+
a browser installed at v1 gains only what it is missing.
|
|
55
|
+
- The service worker now closes its database handle after reading. It opens without a version, and
|
|
56
|
+
a connection left open makes the page's upgrade fire `blocked` and hang until the worker is
|
|
57
|
+
terminated.
|
|
58
|
+
- **`init()` no longer rejects on invalid configuration.** It is routinely called un-awaited, so the
|
|
59
|
+
rejection reached the page as an unhandled rejection the host could not catch — and an analytics
|
|
60
|
+
SDK must not break a page over its own misconfiguration. It logs, declines to start, and the
|
|
61
|
+
collecting calls then genuinely no-op: a refused SDK that still queued would grow IndexedDB
|
|
62
|
+
behind a flush that can never succeed. All three Arsel SDKs now behave identically here.
|
|
63
|
+
- The four configuration rules match the Android and iOS SDKs exactly, including the `pub_` prefix
|
|
64
|
+
check — the one that catches a secret API key pasted into page-readable source.
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
|
|
68
|
+
- **An unreachable push service no longer escapes `init()`.** `pushManager.subscribe()` rejects
|
|
69
|
+
whenever the browser cannot reach its push service — a firewalled network, a captive portal,
|
|
70
|
+
private browsing, a Chromium build without push support. None are the caller's mistake and none
|
|
71
|
+
are feature-detectable. Both call sites now resolve to `null` like every other failure here.
|
|
72
|
+
- **`init()` no longer leaks an unhandled rejection from its background work.** It kicks off the
|
|
73
|
+
stranded-events flush and the push reconcile fire-and-forget, and neither carried a catch — so
|
|
74
|
+
anything they threw (a queue row that will not parse, an IndexedDB failure) surfaced on the host
|
|
75
|
+
page as a rejection it could not intercept. The same failure mode `init()` itself was hardened
|
|
76
|
+
against in this release, through a different door. Both now match the catch every other
|
|
77
|
+
fire-and-forget call site here already used.
|
|
78
|
+
- **A revoked device no longer reports as subscribed.** A durable opt-out answers the register call
|
|
79
|
+
with `200` and deliberately leaves the row `REVOKED`; reading only the HTTP status, `subscribe()`
|
|
80
|
+
returned `true`, and `isSubscribed()` asked the browser, which keeps its `PushSubscription`
|
|
81
|
+
across an opt-out. So `promptForPush()` reported success and an app would show "notifications on"
|
|
82
|
+
to someone who can never receive another push. The backend's status is now persisted and
|
|
83
|
+
consulted as `subscriptionStatus`, matching the Android SDK.
|
|
84
|
+
|
|
85
|
+
### Requires
|
|
86
|
+
|
|
87
|
+
- Backend support for the in-app endpoints, CORS on `/api/v1/orgs/*/in-app/*`, `If-None-Match` in
|
|
88
|
+
the client-API allowed headers, and the org's `allowedOrigins` containing the customer's origin —
|
|
89
|
+
an empty allowlist matches nothing and rejects every visitor.
|
|
90
|
+
|
|
10
91
|
## [1.0.0] — 2026-08-17
|
|
11
92
|
|
|
12
93
|
### Added
|