@thg-agentic/hyper-personalisation-web 3.24.1 → 3.26.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/README.md +100 -25
- package/dist/hp.min.js +3 -3
- package/dist/onsite-answers.min.js +1 -1
- package/dist/onsite-audiences.min.js +1 -0
- package/dist/onsite-render.min.js +2 -2
- package/dist/onsite-wheel.min.js +1 -1
- package/dist/onsite.min.js +1 -1
- package/package.json +4 -3
- package/src/bootstrap.js +114 -49
- package/src/consent-gate.js +8 -5
- package/src/context.js +2 -1
- package/src/device-storage.js +4 -4
- package/src/index.js +142 -10
- package/src/onsite/api.js +22 -5
- package/src/onsite/audiences-loader.js +6 -0
- package/src/onsite/counters.js +2 -2
- package/src/onsite/index.js +52 -1
- package/src/onsite/render.js +49 -18
- package/src/onsite/rules.js +20 -5
- package/src/onsite/visit-audiences.js +94 -0
- package/src/onsite/wheel.js +61 -13
- package/src/settings.js +30 -2
- package/src/shape.js +4 -0
- package/src/social-proof.js +5 -1
- package/src/spec-version.js +1 -1
- package/src/storage-scope.js +8 -2
- package/src/transport.js +22 -2
- package/src/version.js +1 -1
- package/src/visit-campaign.js +168 -0
- package/types/bootstrap.d.ts +10 -2
- package/types/events.d.ts +30 -2
- package/types/hp.d.ts +3 -2
- package/types/index.d.ts +10 -3
- package/types/onsite.d.ts +9 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @thg-agentic/hyper-personalisation-web
|
|
2
2
|
|
|
3
3
|
The browser SDK for THG Hyper-Personalisation, a customer data platform. It implements the event
|
|
4
|
-
specification (spec 1.
|
|
4
|
+
specification (spec 1.20, stamped on every event as `context.spec.version`) for any storefront: it
|
|
5
5
|
sends events in the Segment wire format, applies consent strictly whichever CMP the site uses, reads
|
|
6
6
|
the GA4 `dataLayer` the site already pushes to, and has no runtime dependencies.
|
|
7
7
|
|
|
@@ -63,7 +63,7 @@ collects, is a settings change and never a deploy:
|
|
|
63
63
|
| `consent` | **Required.** The page's CMP: `{ provider: 'didomi' \| 'onetrust' \| 'cookie' \| 'forwarded' \| 'static', ...adapterOptions }`, or a consent adapter of your own. A preset may supply defaults for a provider. |
|
|
64
64
|
| `identityCookies` | `{ anonymousId, sessionId }`: the names of your own first-party device and session cookies. `sessionId` is always read from here; without it the SDK keeps its own session (see [Session ids](#session-ids)). `anonymousId` is read only when the settings name no device cookie: see [Device ids](#device-ids). |
|
|
65
65
|
| `autocapture` | `false`, or per tracker `{ promotions, productLists, recommendations, exposures }`, each `false` or its options. All on by default: they report only what the page tags. |
|
|
66
|
-
| `identity` | The signed-in visitor, `{ userId, emailSha256 }`. `userId` goes on every event; the `identify` is sent once per session and user. |
|
|
66
|
+
| `identity` | The signed-in visitor, `{ userId, emailSha256, token }`. `userId` goes on every event; the `identify` is sent once per session and user, and again once a `token` arrives. `token` is the identity token your server signs for the user on this page view (3.26, see [Verified sign-ins](#verified-sign-ins)). |
|
|
67
67
|
| `getCurrency`, `getShipToCountry`, `getLocale` | Read per event; see below. |
|
|
68
68
|
| `preset` | A platform preset, passed in by the host. |
|
|
69
69
|
| `pageType` | `context.page.type` of this page's page view. |
|
|
@@ -78,14 +78,23 @@ collects, is a settings change and never a deploy:
|
|
|
78
78
|
|
|
79
79
|
What `bootstrap` does:
|
|
80
80
|
|
|
81
|
-
- **Resolves to the client**, or `null` when the key is not enabled
|
|
82
|
-
on a first visit, or the options are invalid. Invalid options log one console warning listing
|
|
81
|
+
- **Resolves to the client**, or `null` when the key is not enabled or the options are invalid. Invalid options log one console warning listing
|
|
83
82
|
every problem. It never rejects, and a second call on the same page returns the first client.
|
|
84
83
|
- **Settings.** The last good answer is cached in `localStorage`, only once analytics is granted,
|
|
85
84
|
and forgotten on a refusal. A page with a cache starts from it at once and asks the API again for
|
|
86
85
|
the next page; a first visit waits for the API (which answers `Cache-Control: max-age=10`
|
|
87
|
-
and an `ETag`, so the browser revalidates for a `304`)
|
|
88
|
-
off too.
|
|
86
|
+
and an `ETag`, so the browser revalidates for a `304`), for at most 3 seconds (3.24.2). A key the
|
|
87
|
+
API switches off clears the cache, so the next page is off too. Onsite forms are the exception: they start from the fresh answer when it comes within a
|
|
88
|
+
second, else from the cache, so a form set live, changed or stopped shows on the next page.
|
|
89
|
+
- **An API it cannot reach never turns collection off** (3.25). A first visit whose settings fail
|
|
90
|
+
(a 5xx, 408 or 429, a timeout, an answer that is not settings; a 4xx is the key refused, and the
|
|
91
|
+
page is off) starts on built-in defaults: collection
|
|
92
|
+
only, with no property (the ingest stamps the key's, so a key scoped to several properties needs
|
|
93
|
+
the page's `propertyId`), campaign parameters only in URLs, the device id from
|
|
94
|
+
`identityCookies.anonymousId` else the SDK's own cookie, no sampling, no tracking plan and no
|
|
95
|
+
onsite forms. Consent is still the CMP's, strictly. They are never cached, so the next page asks
|
|
96
|
+
the API again, and what the ingest cannot take waits in the retry buffer (see
|
|
97
|
+
[Choosing a delivery path](#choosing-a-delivery-path)).
|
|
89
98
|
- **Consent**, identity, one page view, the dataLayer bridge and a once-per-session `identify`, as
|
|
90
99
|
configured. A page restored from the back/forward cache runs no script again, so `bootstrap`
|
|
91
100
|
reads consent again, then sends its `back_forward` page view on `pageshow` and reports its lists
|
|
@@ -96,8 +105,7 @@ What `bootstrap` does:
|
|
|
96
105
|
messageId and time it was made at, and are sent in order once it has. The page view is stamped
|
|
97
106
|
when the page was navigated to, so none of them precedes it (3.18). A page that navigates
|
|
98
107
|
first (a sign-in that redirects at once) carries them in sessionStorage, and the next page's
|
|
99
|
-
`bootstrap` sends them once, before its own page view (since 3.9).
|
|
100
|
-
request never answered: the API down, or the request cancelled by the navigation (3.9.1). The carry is a device write,
|
|
108
|
+
`bootstrap` sends them once, before its own page view (since 3.9). The carry is a device write,
|
|
101
109
|
so it is made only once this device has granted analytics, or unanswered with `holdAcrossPages`;
|
|
102
110
|
the next page's consent decides whether they are sent, and a refusal drops them. Once started,
|
|
103
111
|
the SDK hands its queue to `sendBeacon` at `pagehide` itself.
|
|
@@ -212,6 +220,7 @@ Since 3.19 (spec 1.12), a search box's suggestions are their own event:
|
|
|
212
220
|
| `queryParamAllowlist` | none | Keep only these query params (plus campaign params) in `context.page`, and in a clicked link's `href`, which leaves another site's link as origin and path only (3.22.2). Patterns are anchored and case-insensitive. |
|
|
213
221
|
| `queryParamBlocklist` | `[]` | Strip these params. Ignored when an allowlist is given. |
|
|
214
222
|
| `sensitiveParamAllowlist` | `[]` | Whatever else is configured, a param is always dropped from every reported URL when its name contains `email`, `token`, `password`, `secret`, `auth`, `session`, `phone`, `address`, `card`, `customerid` and the like (see `SENSITIVE_PARAM_FRAGMENTS` in `src/context.js`), or when its value is an email address or a card-length number — campaign params and the referrer included. This exempts names from the first rule, for a false positive such as an `author` filter; nothing exempts a value. |
|
|
223
|
+
| `identityToken` | none | The identity token your server signed for the user `getUserId` names, read once: on each `identify` as `context.identity_token` and with each onsite decision, until `reset()`, a sign-out or another user's `identify` (3.26, see [Verified sign-ins](#verified-sign-ins)). |
|
|
215
224
|
| `getAnonymousId`, `getUserId`, `getSessionId` | none | Identity accessors. Without `getSessionId` the SDK keeps its own session (see [Session ids](#session-ids)); a missing or empty device id gives way to the SDK's own (see [Device ids](#device-ids)). |
|
|
216
225
|
| `getExperiments`, `getAudiences`, `getDecisions`, `getRecommendations`, `getExtensions` | none | Read per event into the matching `context` field. Without `getDecisions`, `context.decisions` is what `recordExposure` last recorded per slot. |
|
|
217
226
|
| `pageType` | `null` | `context.page.type` before the first `page()` call. |
|
|
@@ -500,8 +509,8 @@ separate chunk and starts it; otherwise it is never fetched. A page on `createAn
|
|
|
500
509
|
itself: `import { startOnsite } from '@thg-agentic/hyper-personalisation-web/onsite'` then
|
|
501
510
|
`startOnsite(client, options)`. On the CDN, `hp.min.js` loads `onsite.min.js` (and that loads
|
|
502
511
|
`onsite-render.min.js` once a trigger fires, `onsite-wheel.min.js` for a wheel,
|
|
503
|
-
`onsite-answers.min.js` for a date or number question
|
|
504
|
-
all
|
|
512
|
+
`onsite-answers.min.js` for a date or number question, `onsite-audiences.min.js` for a form limited
|
|
513
|
+
to audiences) from beside itself; a self-hosted copy keeps all five beside `hp.min.js`. The runtime watches triggers, caps and consent and asks for the
|
|
505
514
|
decision; the renderer and its stylesheet are fetched only when a trigger fires, alongside the
|
|
506
515
|
decision request, so a page whose forms never fire never downloads them. If the renderer cannot
|
|
507
516
|
load, the form is not shown (`hp.onsite.debug()` says `the renderer did not load`) and the page goes
|
|
@@ -550,20 +559,40 @@ on desktop arms after `delay_ms` (5 s) and once the pointer has been below the t
|
|
|
550
559
|
viewport, and fires when it leaves through the top edge, moving up. On mobile it is a fast upward
|
|
551
560
|
flick (120px at 0.5 px/ms) after 30% of the page, or a return to the tab after 10 s away. Pages
|
|
552
561
|
(include/exclude globs on the path, `*` matching anything, in any case), devices (`mobile` is a coarse pointer
|
|
553
|
-
or under 768px),
|
|
562
|
+
or under 768px), the campaign the visit arrived from (`arrived_from`), dates and caps are checked
|
|
563
|
+
first; then `POST /v1/decisions` is asked for that one
|
|
554
564
|
slot. A held-out visitor, or no decision, sees nothing. One overlay per page view: of the forms that
|
|
555
565
|
fire within 50 ms, the highest `priority`; once one has shown (or the visitor was held out of it),
|
|
556
566
|
closing it lets no other form in on that page view. A `manual` form, which the site opens on the
|
|
557
567
|
shopper's click (its own sign-up button), is the shopper asking: it opens whatever its caps, the gap
|
|
558
568
|
and the page view's allowance say, as Klaviyo's custom-triggered forms do, though never over a
|
|
559
|
-
modal on screen (a popup, or a teaser opened from its tab); its pages, devices, dates and
|
|
560
|
-
still hold. Asked for twice while it opens, it opens once. With the property's `overlay_gap_minutes`, no popup, sheet or full-screen form
|
|
569
|
+
modal on screen (a popup, or a teaser opened from its tab); its pages, devices, campaign, dates and
|
|
570
|
+
audiences still hold. Asked for twice while it opens, it opens once. With the property's `overlay_gap_minutes`, no popup, sheet or full-screen form
|
|
561
571
|
shows until that long after the last one this session (1440 is once per session); bars, teasers and
|
|
562
572
|
inline forms are never held back. A form with `audiences` needs personalisation consent.
|
|
563
573
|
|
|
574
|
+
**Arrived from.** A form with `arrived_from` shows only to a visit that landed from a matching
|
|
575
|
+
campaign: `[{ field, match, values }]`, `field` one of `source`, `medium`, `campaign`, `content`,
|
|
576
|
+
`term` (the link's `utm_*` tags), `match` `is` or `contains`, compared trimmed and in any case.
|
|
577
|
+
Every rule must hold; any of a rule's values meets it. The campaign is the landing page's, as GA4's
|
|
578
|
+
session source is: the client keeps the first page of the visit with `utm_*` tags in one store,
|
|
579
|
+
`thg_hp_visit_campaign:<property>` (localStorage, so every tab of the visit reads it; the tags,
|
|
580
|
+
when the visit was last active and its session id once kept on the device, never a device or
|
|
581
|
+
person id, the same strictly necessary basis as the onsite counters). Later pages without tags, or
|
|
582
|
+
with others, keep it until the visit ends as the SDK's session does: 30 minutes without a page or
|
|
583
|
+
an event, or a new session id. Each tag is read up to 100 characters. The tags are read when the
|
|
584
|
+
client is created, so a later `replaceState` cannot lose them. A sign-out or a withdrawn consent
|
|
585
|
+
forgets it; a first refusal does not, as the forms judge it without consent. The session id is
|
|
586
|
+
written only while analytics is granted, and without localStorage the campaign holds for the page. It needs no consent; a visit with no campaign meets no rule
|
|
587
|
+
(`not eligible: not from its campaign`), and an `arrived_from` this SDK cannot read never matches.
|
|
588
|
+
The decision request for such a form carries it as `visit_campaign` (never logged), and the public API answers no
|
|
589
|
+
form unless the rules hold, so an SDK older than 3.26 never shows one. A preview link reads its own
|
|
590
|
+
URL's tags: the console's Preview on site link adds them. Click ids
|
|
591
|
+
(`fbclid`, `gclid`) are not a campaign.
|
|
592
|
+
|
|
564
593
|
| Option | |
|
|
565
594
|
|---|---|
|
|
566
|
-
| `audiences()` | The visitor's session audiences, when the page evaluates them: a form targeting none of them is skipped. Without it
|
|
595
|
+
| `audiences()` | The visitor's session audiences, when the page evaluates them: a form targeting none of them is skipped. Without it HP's server decides, from the visitor's stored audiences and the in-visit audiences whose rules hold in this visit (the runtime evaluates them from `GET /v1/audiences/datafile` and sends them with the decision). |
|
|
567
596
|
| `subscribed()` | True for a visitor the page knows is subscribed, for forms with `exclude_subscribed`. |
|
|
568
597
|
| `servingHost`, `gatewayHost` | Where decisions and claims go (default: the host the SDK was pointed at) and capture goes (default: where events go). |
|
|
569
598
|
| `debug` | Log why each form did or did not show. Also on with `?hp_debug=onsite`. |
|
|
@@ -634,11 +663,26 @@ The sign-up itself is sent whatever the analytics answer: it is the visitor's ma
|
|
|
634
663
|
close and submitted, plus one subscribed flag and the last claimed code (localStorage), and a
|
|
635
664
|
kept teaser tab (sessionStorage).
|
|
636
665
|
|
|
666
|
+
**Prizes that cannot be claimed.** A form with prizes claims as soon as the sign-up is taken. If
|
|
667
|
+
the claim fails (the offers API unreachable, an error, or no answer in 8 seconds), the sign-up
|
|
668
|
+
still counts: a spin offers one retry ("You're signed up, but we couldn't load your prize. Please
|
|
669
|
+
try again."), then thanks the shopper and says the prize could not be loaded, as does a `code`
|
|
670
|
+
block. The shopper stays subscribed and the form is done with them (`already submitted`), so they
|
|
671
|
+
are never asked for their address twice. No code was drawn, so the destinations get the sign-up
|
|
672
|
+
without one (ADR 0064, decision 5).
|
|
673
|
+
|
|
674
|
+
**Testing a live form again.** A browser that has seen a form keeps to its caps. To see it as a
|
|
675
|
+
new visitor, open a private window, or delete `thg_hp_onsite:<property>` from both localStorage
|
|
676
|
+
(shows ever, last close, submitted, subscribed, the last code) and sessionStorage (shows this
|
|
677
|
+
session, the gap between popups, a teaser's tab, the A/B version), then reload;
|
|
678
|
+
`thg_hp_settings:<writeKey>` in localStorage is the cached settings. `?hp_debug=onsite` logs why
|
|
679
|
+
each form did or did not show, and a preview link (`?hp_preview=`) counts nothing.
|
|
680
|
+
|
|
637
681
|
**Budgets**, enforced by `tests/onsite-chunks.test.mjs`, each about 5% over its chunk:
|
|
638
682
|
`onsite.min.js` under 5.875 KiB gzipped (about 5.6 KiB), `onsite-render.min.js` under 14 KiB (about
|
|
639
683
|
13.7 KiB), so a page that shows a form loads about 19 KiB of onsite code and one whose forms never
|
|
640
684
|
fire about 5.6 KiB; `onsite-wheel.min.js` under 8 KiB (about 1.3 KiB), `onsite-answers.min.js` under
|
|
641
|
-
2 KiB (about 1.5 KiB). The core carries only the loader.
|
|
685
|
+
2 KiB (about 1.5 KiB), `onsite-audiences.min.js` under 1.25 KiB (about 1.2 KiB). The core carries only the loader.
|
|
642
686
|
|
|
643
687
|
Try it locally: `node tools/local/serve.mjs`, then
|
|
644
688
|
`http://localhost:8099/onsite.html?hp_debug=onsite` (offline mocks by default, `?mode=live` for
|
|
@@ -660,12 +704,13 @@ With a `consent` adapter the SDK is strict:
|
|
|
660
704
|
is never a grant, however long it takes.
|
|
661
705
|
- **Page closed before an answer:** nothing is sent. Switching to another tab is not closing the
|
|
662
706
|
page: held events wait for the visitor to come back.
|
|
663
|
-
- **A navigation before an answer** (a
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
707
|
+
- **A navigation before an answer** (a visitor who answers the banner on their second page, a
|
|
708
|
+
sign-in that redirects) loses what the page held, landing page included, unless you opt in with
|
|
709
|
+
`holdAcrossPages: true` (since 3.5). Then held events are also kept in sessionStorage under
|
|
710
|
+
`thg_hp_held:<writeKey>` and sent when a later page's answer is a grant, stamped with it. A
|
|
711
|
+
refusal deletes them, and so does a tab left unanswered for 30 minutes. This writes event data
|
|
712
|
+
to the device before the visitor answers, which strict mode otherwise never does, so turn it on
|
|
713
|
+
only where your consent policy allows that.
|
|
669
714
|
- **The retry buffer:** a batch is kept in localStorage only while analytics is granted, and one
|
|
670
715
|
kept on an earlier page is replayed only once this page's answer is a grant. A refusal clears it.
|
|
671
716
|
- **Consent changes:** each change is recorded as a `Consent Updated` event. A decision that
|
|
@@ -880,6 +925,29 @@ base64). `bootstrap`'s `identity.emailSha256` and `identify`'s `email_sha256` (s
|
|
|
880
925
|
either, and `normaliseSha256(value)` converts either to the lowercase hex the spec wants. It re-encodes the same digest and never
|
|
881
926
|
re-hashes, so pass the hash, never the email.
|
|
882
927
|
|
|
928
|
+
## Verified sign-ins
|
|
929
|
+
|
|
930
|
+
A publishable key is public, so a `userId` the browser sends is only a claim: the identity graph
|
|
931
|
+
records it and never joins the device to that customer unless the integrator vouches for it (ADR
|
|
932
|
+
0067). Your server signs an identity token for each signed-in page view, a JWT `ES256` (header
|
|
933
|
+
`typ: "JWT"`) over `{ sub: <userId>, aud: "hyper-personalisation", exp }` (at most 24 hours) with a
|
|
934
|
+
P-256 key whose public half is registered on the source, and the page hands it over:
|
|
935
|
+
|
|
936
|
+
```js
|
|
937
|
+
bootstrap(writeKey, { consent, identity: { userId: customer.id, token } });
|
|
938
|
+
// or, calling identify yourself:
|
|
939
|
+
analytics.identify({ userId: customer.id }, { identity_token: token });
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
The token goes on the `identify` as `context.identity_token`, and with each onsite decision, where
|
|
943
|
+
it is what lets a promo reminder's code be shown. The SDK keeps one token, for the user it was
|
|
944
|
+
signed for: `reset()`, a `Signed Out` or another user's `identify` drops it, and the same user's
|
|
945
|
+
`identify` without one keeps it; a single-page app passes a fresh one on `identify` before the
|
|
946
|
+
last expires. A session identifies again once, when a token first appears for
|
|
947
|
+
the user, and never because a page (one served from a shared cache) has none. The ingest checks it,
|
|
948
|
+
stamps `context.identity_verified` and never stores it. Steps, key generation and rotation:
|
|
949
|
+
[Verify signed-in shoppers](../../apps/docs/src/content/docs/guides/verify-signed-in-shoppers.mdx).
|
|
950
|
+
|
|
883
951
|
## Handing off to server producers
|
|
884
952
|
|
|
885
953
|
Server-side producers (for example an order service using the Java SDK) read two first-party cookies:
|
|
@@ -907,8 +975,14 @@ delivery differs.
|
|
|
907
975
|
- Dependency-free.
|
|
908
976
|
- Batched, with a retry buffer in localStorage: each batch is stored before it is sent and
|
|
909
977
|
removed when the ingest accepts it (or rejects it for good), so an outage costs a delay, not the
|
|
910
|
-
events.
|
|
911
|
-
|
|
978
|
+
events. A batch is retried with exponential backoff on its page, then on every later page of the
|
|
979
|
+
site for 24 hours, with each event's `messageId`, so one that had landed is removed by the
|
|
980
|
+
warehouse's deduplication on `(org, source, messageId)`. The buffer is per write key (3.25; per
|
|
981
|
+
property before; what 3.24 kept per property is moved over on the first page) and capped at 20
|
|
982
|
+
batches and 1 MB, oldest dropped first.
|
|
983
|
+
- On page unload, sends by `sendBeacon` in chunks that fit. A beacon's answer is never seen, so
|
|
984
|
+
while the ingest is failing (a send failed on this page, or the settings could not be fetched)
|
|
985
|
+
each beaconed chunk is also kept in the retry buffer for the next page (3.25).
|
|
912
986
|
- Never throws into the page. An event JSON cannot serialise (a BigInt, a circular object, a
|
|
913
987
|
throwing `toJSON`) is dropped on its own, logged under `debug`, and the queue behind it carries
|
|
914
988
|
on. A `Date` in properties is sent as its ISO string.
|
|
@@ -1002,13 +1076,13 @@ SDK per page.
|
|
|
1002
1076
|
| Kind | Default name | Where | Scope |
|
|
1003
1077
|
|---|---|---|---|
|
|
1004
1078
|
| `consent` | `thg_hp_consent` | cookie | the consent decision, for your servers and `forwarded` pages |
|
|
1005
|
-
| `retry` | `thg_hp_retry:<
|
|
1079
|
+
| `retry` | `thg_hp_retry:<writeKey>` | localStorage | batches waiting for the ingest; only under a grant |
|
|
1006
1080
|
| `sessionSignals` | `thg_hp_session_signals:<property>:<session>` | localStorage (sessionStorage until a session is bound) | in-session audience counters |
|
|
1007
1081
|
| `identified` | `thg_hp_identified:<property>` | sessionStorage | the identify already sent this session |
|
|
1008
1082
|
| `previousPage` | `thg_hp_prev_page:<property>` | sessionStorage | the previous page view's messageId; only under a grant |
|
|
1009
1083
|
| `decisions` | `thg_hp_decisions:<property>` | sessionStorage | the decisions in force, latest per slot; only under a grant |
|
|
1010
1084
|
| `settings` | `thg_hp_settings:<writeKey>` | localStorage | the last good settings; only under a grant |
|
|
1011
|
-
| `held` | `thg_hp_held:<
|
|
1085
|
+
| `held` | `thg_hp_held:<writeKey>` | sessionStorage | events held for an unanswered CMP; only with `holdAcrossPages` |
|
|
1012
1086
|
| `carried` | `thg_hp_carried:<writeKey>` | sessionStorage | `hp` calls made before the SDK started, on a page that navigated first (3.9); only under a grant, or with `holdAcrossPages` |
|
|
1013
1087
|
| `recommendationContext` | `thg_hp_recommendation_context` | localStorage | The visit's wire form, for `hp.recommendationContext()` on the next page before the SDK starts there; 30 minutes from the last event, only while personalisation is granted (3.11) |
|
|
1014
1088
|
| `anonymousId` | `thg_hp_anonymous_id` | cookie, one per browser | Hyper-Personalisation's device id, for a source set to `hp_cookie`; only under a grant (see [Device ids](#device-ids)) |
|
|
@@ -1016,6 +1090,7 @@ SDK per page.
|
|
|
1016
1090
|
| `preview` | `thg_hp_preview` | session cookie | a trader's preview token from `?hp_preview=` (3.15, see [Preview](#preview)) |
|
|
1017
1091
|
| `debug` | `thg_hp_debug` | sessionStorage | a developer's debug token from `?hp_debug=` (3.18), checked against the settings on every page |
|
|
1018
1092
|
| `onsite` | `thg_hp_onsite:<property>` | localStorage, sessionStorage | onsite forms' opaque counters per slot and a subscribed flag; never contact data (see [Onsite forms](#onsite-forms)) |
|
|
1093
|
+
| `visitCampaign` | `thg_hp_visit_campaign:<property>` | localStorage | the campaign tags the visit landed with, when it was last active and its session id once kept; ended with the session (see [Onsite forms](#onsite-forms)) |
|
|
1019
1094
|
| — | `thg_hp_conformance_page_type` | sessionStorage | the previous page's type, only while the development-only conformance checker runs |
|
|
1020
1095
|
|
|
1021
1096
|
Keys are scoped by property because one origin can serve several tenants. The names the SDK used
|