@active-reach/web-sdk 1.42.0 → 1.43.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 (183) hide show
  1. package/package.json +1 -1
  2. package/dist/aegis-push.min.js +0 -2
  3. package/dist/aegis-push.min.js.map +0 -1
  4. package/dist/aegis-sw.js +0 -1
  5. package/dist/aegis.min.js +0 -2
  6. package/dist/aegis.min.js.map +0 -1
  7. package/dist/analytics-C-Go3Pm5.mjs +0 -3509
  8. package/dist/analytics-C-Go3Pm5.mjs.map +0 -1
  9. package/dist/cdn.d.ts +0 -26
  10. package/dist/cdn.d.ts.map +0 -1
  11. package/dist/chat/AegisChat.d.ts +0 -169
  12. package/dist/chat/AegisChat.d.ts.map +0 -1
  13. package/dist/chat/index.d.ts +0 -3
  14. package/dist/chat/index.d.ts.map +0 -1
  15. package/dist/core/analytics.d.ts +0 -320
  16. package/dist/core/analytics.d.ts.map +0 -1
  17. package/dist/core/bootstrap.d.ts +0 -96
  18. package/dist/core/bootstrap.d.ts.map +0 -1
  19. package/dist/core/prefetch-bundle-client.d.ts +0 -175
  20. package/dist/core/prefetch-bundle-client.d.ts.map +0 -1
  21. package/dist/core/queue.d.ts +0 -35
  22. package/dist/core/queue.d.ts.map +0 -1
  23. package/dist/core/rate-limiter.d.ts +0 -45
  24. package/dist/core/rate-limiter.d.ts.map +0 -1
  25. package/dist/core/sdk-config-poller.d.ts +0 -59
  26. package/dist/core/sdk-config-poller.d.ts.map +0 -1
  27. package/dist/core/session.d.ts +0 -52
  28. package/dist/core/session.d.ts.map +0 -1
  29. package/dist/core/transport.d.ts +0 -50
  30. package/dist/core/transport.d.ts.map +0 -1
  31. package/dist/core/user-namespace.d.ts +0 -48
  32. package/dist/core/user-namespace.d.ts.map +0 -1
  33. package/dist/ecommerce/index.d.ts +0 -94
  34. package/dist/ecommerce/index.d.ts.map +0 -1
  35. package/dist/ecommerce/types.d.ts +0 -81
  36. package/dist/ecommerce/types.d.ts.map +0 -1
  37. package/dist/governance/bloom-filter.d.ts +0 -47
  38. package/dist/governance/bloom-filter.d.ts.map +0 -1
  39. package/dist/governance/index.d.ts +0 -8
  40. package/dist/governance/index.d.ts.map +0 -1
  41. package/dist/governance/murmur3.d.ts +0 -43
  42. package/dist/governance/murmur3.d.ts.map +0 -1
  43. package/dist/governance/name-governor.d.ts +0 -98
  44. package/dist/governance/name-governor.d.ts.map +0 -1
  45. package/dist/governance/trait-governor.d.ts +0 -63
  46. package/dist/governance/trait-governor.d.ts.map +0 -1
  47. package/dist/inapp/AegisInAppManager.d.ts +0 -888
  48. package/dist/inapp/AegisInAppManager.d.ts.map +0 -1
  49. package/dist/inapp/actions.d.ts +0 -91
  50. package/dist/inapp/actions.d.ts.map +0 -1
  51. package/dist/inapp/devicePairing.d.ts +0 -70
  52. package/dist/inapp/devicePairing.d.ts.map +0 -1
  53. package/dist/inapp/eval-throttle.d.ts +0 -33
  54. package/dist/inapp/eval-throttle.d.ts.map +0 -1
  55. package/dist/inapp/font.d.ts +0 -16
  56. package/dist/inapp/font.d.ts.map +0 -1
  57. package/dist/inapp/index.d.ts +0 -8
  58. package/dist/inapp/index.d.ts.map +0 -1
  59. package/dist/inapp/lucide-svg.d.ts +0 -46
  60. package/dist/inapp/lucide-svg.d.ts.map +0 -1
  61. package/dist/inapp/renderPreview.d.ts +0 -40
  62. package/dist/inapp/renderPreview.d.ts.map +0 -1
  63. package/dist/inapp/renderers/active-web-chat.d.ts +0 -45
  64. package/dist/inapp/renderers/active-web-chat.d.ts.map +0 -1
  65. package/dist/inapp/renderers/card-style.d.ts +0 -31
  66. package/dist/inapp/renderers/card-style.d.ts.map +0 -1
  67. package/dist/inapp/renderers/card-template.d.ts +0 -233
  68. package/dist/inapp/renderers/card-template.d.ts.map +0 -1
  69. package/dist/inapp/renderers/carousel-cards.d.ts +0 -15
  70. package/dist/inapp/renderers/carousel-cards.d.ts.map +0 -1
  71. package/dist/inapp/renderers/coachmark-tour.d.ts +0 -24
  72. package/dist/inapp/renderers/coachmark-tour.d.ts.map +0 -1
  73. package/dist/inapp/renderers/games.d.ts +0 -27
  74. package/dist/inapp/renderers/games.d.ts.map +0 -1
  75. package/dist/inapp/renderers/hero.d.ts +0 -23
  76. package/dist/inapp/renderers/hero.d.ts.map +0 -1
  77. package/dist/inapp/renderers/index.d.ts +0 -22
  78. package/dist/inapp/renderers/index.d.ts.map +0 -1
  79. package/dist/inapp/renderers/interactive-contract.d.ts +0 -48
  80. package/dist/inapp/renderers/interactive-contract.d.ts.map +0 -1
  81. package/dist/inapp/renderers/product-recommendation.d.ts +0 -37
  82. package/dist/inapp/renderers/product-recommendation.d.ts.map +0 -1
  83. package/dist/inapp/renderers/progress-bar.d.ts +0 -38
  84. package/dist/inapp/renderers/progress-bar.d.ts.map +0 -1
  85. package/dist/inapp/renderers/progress-core.d.ts +0 -80
  86. package/dist/inapp/renderers/progress-core.d.ts.map +0 -1
  87. package/dist/inapp/renderers/reviews.d.ts +0 -38
  88. package/dist/inapp/renderers/reviews.d.ts.map +0 -1
  89. package/dist/inapp/renderers/sticky-bar.d.ts +0 -14
  90. package/dist/inapp/renderers/sticky-bar.d.ts.map +0 -1
  91. package/dist/inapp/renderers/stories.d.ts +0 -31
  92. package/dist/inapp/renderers/stories.d.ts.map +0 -1
  93. package/dist/inapp/renderers/types.d.ts +0 -147
  94. package/dist/inapp/renderers/types.d.ts.map +0 -1
  95. package/dist/inapp/renderers/video.d.ts +0 -20
  96. package/dist/inapp/renderers/video.d.ts.map +0 -1
  97. package/dist/inbox/AegisInbox.d.ts +0 -106
  98. package/dist/inbox/AegisInbox.d.ts.map +0 -1
  99. package/dist/inbox/index.d.ts +0 -3
  100. package/dist/inbox/index.d.ts.map +0 -1
  101. package/dist/index.d.ts +0 -40
  102. package/dist/index.d.ts.map +0 -1
  103. package/dist/index.js +0 -15311
  104. package/dist/index.js.map +0 -1
  105. package/dist/integrations/react.d.ts +0 -19
  106. package/dist/integrations/react.d.ts.map +0 -1
  107. package/dist/loyalty/AegisLoyaltyManager.d.ts +0 -120
  108. package/dist/loyalty/AegisLoyaltyManager.d.ts.map +0 -1
  109. package/dist/loyalty/index.d.ts +0 -3
  110. package/dist/loyalty/index.d.ts.map +0 -1
  111. package/dist/plugins/registry.d.ts +0 -13
  112. package/dist/plugins/registry.d.ts.map +0 -1
  113. package/dist/push/AegisWebPush.d.ts +0 -199
  114. package/dist/push/AegisWebPush.d.ts.map +0 -1
  115. package/dist/push/AegisWebPush.js +0 -542
  116. package/dist/push/AegisWebPush.js.map +0 -1
  117. package/dist/push/cdn-push.d.ts +0 -21
  118. package/dist/push/cdn-push.d.ts.map +0 -1
  119. package/dist/react.js +0 -70
  120. package/dist/react.js.map +0 -1
  121. package/dist/runtime/AegisMessageRuntime.d.ts +0 -276
  122. package/dist/runtime/AegisMessageRuntime.d.ts.map +0 -1
  123. package/dist/runtime/index.d.ts +0 -3
  124. package/dist/runtime/index.d.ts.map +0 -1
  125. package/dist/snippet.min.js +0 -1
  126. package/dist/state/intent_ledger.d.ts +0 -136
  127. package/dist/state/intent_ledger.d.ts.map +0 -1
  128. package/dist/triggers/ContactScoresFetcher.d.ts +0 -113
  129. package/dist/triggers/ContactScoresFetcher.d.ts.map +0 -1
  130. package/dist/triggers/IntentRuleEvaluator.d.ts +0 -213
  131. package/dist/triggers/IntentRuleEvaluator.d.ts.map +0 -1
  132. package/dist/triggers/IntentSnapshotCollector.d.ts +0 -83
  133. package/dist/triggers/IntentSnapshotCollector.d.ts.map +0 -1
  134. package/dist/triggers/SelectorBinder.d.ts +0 -118
  135. package/dist/triggers/SelectorBinder.d.ts.map +0 -1
  136. package/dist/triggers/TriggerEngine.d.ts +0 -296
  137. package/dist/triggers/TriggerEngine.d.ts.map +0 -1
  138. package/dist/triggers/behaviouralTriggers.d.ts +0 -44
  139. package/dist/triggers/behaviouralTriggers.d.ts.map +0 -1
  140. package/dist/triggers/index.d.ts +0 -11
  141. package/dist/triggers/index.d.ts.map +0 -1
  142. package/dist/triggers/microIntentTriggers.d.ts +0 -80
  143. package/dist/triggers/microIntentTriggers.d.ts.map +0 -1
  144. package/dist/types/config.d.ts +0 -77
  145. package/dist/types/config.d.ts.map +0 -1
  146. package/dist/types/events.d.ts +0 -147
  147. package/dist/types/events.d.ts.map +0 -1
  148. package/dist/types/index.d.ts +0 -5
  149. package/dist/types/index.d.ts.map +0 -1
  150. package/dist/types/plugin.d.ts +0 -21
  151. package/dist/types/plugin.d.ts.map +0 -1
  152. package/dist/types/type-safe-events.d.ts +0 -14
  153. package/dist/types/type-safe-events.d.ts.map +0 -1
  154. package/dist/utils/canonical-identity.d.ts +0 -29
  155. package/dist/utils/canonical-identity.d.ts.map +0 -1
  156. package/dist/utils/consent.d.ts +0 -40
  157. package/dist/utils/consent.d.ts.map +0 -1
  158. package/dist/utils/debounce.d.ts +0 -3
  159. package/dist/utils/debounce.d.ts.map +0 -1
  160. package/dist/utils/device.d.ts +0 -6
  161. package/dist/utils/device.d.ts.map +0 -1
  162. package/dist/utils/identity.d.ts +0 -27
  163. package/dist/utils/identity.d.ts.map +0 -1
  164. package/dist/utils/index.d.ts +0 -7
  165. package/dist/utils/index.d.ts.map +0 -1
  166. package/dist/utils/logger.d.ts +0 -15
  167. package/dist/utils/logger.d.ts.map +0 -1
  168. package/dist/utils/meta-cookies.d.ts +0 -65
  169. package/dist/utils/meta-cookies.d.ts.map +0 -1
  170. package/dist/utils/redact-url.d.ts +0 -56
  171. package/dist/utils/redact-url.d.ts.map +0 -1
  172. package/dist/utils/storage.d.ts +0 -20
  173. package/dist/utils/storage.d.ts.map +0 -1
  174. package/dist/utils/url-parser.d.ts +0 -61
  175. package/dist/utils/url-parser.d.ts.map +0 -1
  176. package/dist/utils/uuid.d.ts +0 -4
  177. package/dist/utils/uuid.d.ts.map +0 -1
  178. package/dist/utils/visitor.d.ts +0 -43
  179. package/dist/utils/visitor.d.ts.map +0 -1
  180. package/dist/widgets/AegisWidgetManager.d.ts +0 -209
  181. package/dist/widgets/AegisWidgetManager.d.ts.map +0 -1
  182. package/dist/widgets/index.d.ts +0 -3
  183. package/dist/widgets/index.d.ts.map +0 -1
@@ -1,888 +0,0 @@
1
- /**
2
- * AegisInAppManager - Web SDK In-App Messaging Module
3
- *
4
- * HTTP polling architecture with client-side evaluation.
5
- *
6
- * Security:
7
- * - XSS Protection: Uses DOM APIs (createElement, textContent) instead of innerHTML
8
- * - URL Validation: Prevents javascript: protocol injection
9
- * - CSP Compatible: No inline styles in HTML strings
10
- *
11
- * Architecture (RFC 2026-04-16):
12
- * - Polls /v1/in-app/active on initialize + on identity change
13
- * - Caches campaigns locally in memory
14
- * - Evaluates trigger rules client-side
15
- * - Tracks events (impression, click, dismiss) asynchronously
16
- *
17
- * NOTE: Persistent SSE for downstream delivery is disabled by default
18
- * (enableSSE=false). Tenant dashboards that need realtime (inbox, chat,
19
- * live page) open SSE directly via Next.js API routes — not via this SDK.
20
- * End-user storefronts use polling + Web Push for background delivery.
21
- */
22
- import type { CartState } from './renderers';
23
- import { type AegisAction } from './actions';
24
- export interface InAppCampaign {
25
- id: string;
26
- type: 'modal' | 'banner' | 'tooltip' | 'full_screen' | 'half_interstitial' | 'alert' | 'pip' | 'video' | 'stories' | 'carousel_cards' | 'sticky_bar' | 'progress_bar' | 'coachmark_tour' | 'product_recommendation' | 'active_web_chat' | 'custom_html';
27
- sub_type?: string;
28
- title: string;
29
- body: string;
30
- image_url?: string;
31
- video_url?: string;
32
- action_url?: string;
33
- button_text?: string;
34
- background_color?: string;
35
- text_color?: string;
36
- priority: number;
37
- expires_at?: string;
38
- frequency?: {
39
- max_impressions?: number;
40
- max_impressions_per_day?: number;
41
- cooldown_seconds?: number;
42
- suppress_after_conversion_seconds?: number;
43
- suppress_after_dismiss_seconds?: number;
44
- scope?: 'session' | 'user' | 'user_sku';
45
- };
46
- interactive_config?: Record<string, unknown>;
47
- client_trigger?: {
48
- type: string;
49
- config?: Record<string, unknown>;
50
- };
51
- assigned_variant_id?: string;
52
- delivery_modes?: Array<'in_app_overlay' | 'embedded_card' | 'standalone_page'>;
53
- widget_category?: 'feedback' | 'loyalty' | 'promo' | 'commerce' | 'support' | 'custom' | 'onboarding' | 'hero' | 'announcement' | 'reorder' | 'reviews';
54
- page_key?: 'bill' | 'rewards' | 'feedback' | 'reviews' | 'members';
55
- surface?: string[];
56
- target_screens?: string[];
57
- journey_step_execution_id?: string | null;
58
- }
59
- /**
60
- * Payload for `campaign-click` lifecycle event. CANCELLABLE — return `false`
61
- * (or call `evt.preventDefault()`) from the handler to suppress the SDK's
62
- * default `window.location.href` navigation, e.g. to route via a SPA router.
63
- */
64
- export interface CampaignClickEvent {
65
- campaign: InAppCampaign;
66
- /** Sanitized destination URL — already passed through `sanitizeUrl()`. */
67
- action_url: string;
68
- /** Best-effort button identifier — populated when the click came from a
69
- * named button in a multi-button campaign (modals, alerts). `cta` for the
70
- * primary CTA on single-button campaigns. */
71
- button_id?: string;
72
- /** Mutate via `evt.preventDefault()` to signal the SDK to skip its
73
- * default hard-navigation. Identical semantics to DOM CustomEvent. */
74
- preventDefault: () => void;
75
- defaultPrevented: boolean;
76
- }
77
- /** Payload for `campaign-dismiss` lifecycle event. */
78
- export interface CampaignDismissEvent {
79
- campaign: InAppCampaign;
80
- /** Source of dismissal — close button, swipe, ESC key, programmatic. */
81
- source: 'close_button' | 'overlay' | 'esc' | 'auto_timeout' | 'programmatic';
82
- }
83
- /** Payload for `error` lifecycle event. Aggregates non-fatal SDK errors so
84
- * host apps can wire them into Sentry / Datadog RUM / similar. */
85
- export interface AegisErrorEvent {
86
- message: string;
87
- error: unknown;
88
- /** Optional structured context — `{ event: 'campaign-click' }` for handler
89
- * throws, `{ campaign_id, stage }` for render path failures, etc. */
90
- context: Record<string, unknown>;
91
- }
92
- /**
93
- * Map of lifecycle event name → handler signature. Used by the typed
94
- * `on(event, handler)` overload below so TS correctly narrows the payload
95
- * type per event without a discriminated union at the call site.
96
- */
97
- export interface InAppLifecycleEventMap {
98
- 'campaigns-loaded': (campaigns: InAppCampaign[]) => void;
99
- 'campaign-will-show': (campaign: InAppCampaign) => void | false;
100
- 'campaign-shown': (campaign: InAppCampaign) => void;
101
- 'campaign-click': (evt: CampaignClickEvent) => void | false;
102
- 'campaign-dismiss': (evt: CampaignDismissEvent) => void;
103
- 'error': (evt: AegisErrorEvent) => void;
104
- }
105
- export interface AegisInAppConfig {
106
- writeKey: string;
107
- apiHost?: string;
108
- userId?: string;
109
- contactId?: string;
110
- /** Server-minted proof of `contactId` (S/C1). Without it, in-app answers are
111
- * kept as anonymous events and never written onto the contact's profile. */
112
- contactToken?: string;
113
- organizationId?: string;
114
- propertyId?: string;
115
- debugMode?: boolean;
116
- enableSSE?: boolean;
117
- /**
118
- * Canonical CTA-action handler (CANONICAL_ACTION_MODEL_TRACKER.md). ONE host
119
- * callback receives every CTA action (add_to_cart / open_product / open_cart /
120
- * open_login / navigate / custom …) — the CEP-standard shape. Return `true`
121
- * when handled; otherwise the SDK applies the canonical fallback (navigate to
122
- * the product/cart page) so a CTA is never a dead no-op. Legacy `aegis:*` DOM
123
- * events still fire alongside this during the migration.
124
- */
125
- onAction?: (action: AegisAction) => boolean | void;
126
- /**
127
- * Platform-aware product-URL resolver (sku → PDP url). Lets a Shopify/Woo/
128
- * Magento adapter map to `/products/{handle}` etc. instead of the canonical
129
- * `/p/{sku}`. Defaults to `/p/{sku}` when unset.
130
- */
131
- productUrl?: (sku: string) => string;
132
- /**
133
- * Workspace cascade reader. Set this from the host SDK as
134
- * `getWorkspaceId: () => aegis.getEffectiveWorkspaceId()` so in-app
135
- * impressions/clicks/dismisses carry the same workspace context the
136
- * main analytics SDK sees (URL path slug, ?ws=, setWorkspace runtime).
137
- * Without it, in-app engagement events arrive at event-ingress with
138
- * no workspace_id stamped → cross-outlet attribution silently falls
139
- * back to the org's primary workspace.
140
- */
141
- getWorkspaceId?: () => string | undefined;
142
- /**
143
- * Live cart-state reader, set by the runtime from its imperative
144
- * `setCartState(...)`. Threaded into the renderer `RenderContext` so the
145
- * progress-bar renderer can read a first-party cart value instead of
146
- * `window.*` cart globals. Undefined for embeds that never feed a cart.
147
- */
148
- getCartState?: () => CartState | undefined;
149
- }
150
- export declare function productIdentitiesFor(campaign: unknown): string | undefined;
151
- export declare class AegisInAppManager {
152
- private writeKey;
153
- private apiHost;
154
- private userId?;
155
- private contactId?;
156
- private contactToken?;
157
- private organizationId?;
158
- private propertyId?;
159
- private debugMode;
160
- private enableSSE;
161
- private onActionHandler?;
162
- private productUrl;
163
- private getWorkspaceId?;
164
- private getCartState?;
165
- private campaigns;
166
- private displayedCampaigns;
167
- private _displaySizeMul;
168
- private suppressedUntil;
169
- private eventSource?;
170
- private isInitialized;
171
- private reconnectAttempts;
172
- private maxReconnectAttempts;
173
- private static readonly CONVERSION_STORAGE_PREFIX;
174
- private hooks;
175
- private readyResolve;
176
- readonly ready: Promise<void>;
177
- private emit;
178
- private emitError;
179
- private register;
180
- /**
181
- * Subscribe to in-app message lifecycle events. Returns an unsubscribe
182
- * function — call to remove the handler (matches React `useEffect` cleanup
183
- * convention).
184
- *
185
- * Supported events: `campaigns-loaded`, `campaign-will-show` (cancellable),
186
- * `campaign-shown`, `campaign-click` (cancellable), `campaign-dismiss`,
187
- * `error`. Cancellable events: handler returns `false` to suppress the
188
- * SDK's default behaviour (e.g., prevent the hard navigation on click,
189
- * skip rendering on will-show).
190
- */
191
- on<E extends keyof InAppLifecycleEventMap>(event: E, handler: InAppLifecycleEventMap[E]): () => void;
192
- /** Sugar: subscribe to `campaigns-loaded`. Fires after every successful
193
- * `/v1/in-app/active` poll with the full active-campaign list. */
194
- onCampaignsLoaded(handler: (campaigns: InAppCampaign[]) => void): () => void;
195
- /** Sugar: subscribe to `campaign-will-show`. CANCELLABLE — return `false`
196
- * to suppress rendering this campaign (host-side suppression rules,
197
- * route-aware blocking, etc.). */
198
- onCampaignWillShow(handler: (campaign: InAppCampaign) => void | false): () => void;
199
- /** Sugar: subscribe to `campaign-shown`. Fires after the SDK has painted
200
- * the campaign to the DOM (post-impression). */
201
- onCampaignShown(handler: (campaign: InAppCampaign) => void): () => void;
202
- /** Sugar: subscribe to `campaign-click`. CANCELLABLE — return `false` (or
203
- * call `evt.preventDefault()`) to suppress the SDK's default
204
- * `window.location.href` navigation, e.g. to route via a SPA router. */
205
- onCampaignClick(handler: (evt: CampaignClickEvent) => void | false): () => void;
206
- /** Sugar: subscribe to `campaign-dismiss`. Fires when user closes/swipes
207
- * away the campaign before clicking through. */
208
- onCampaignDismiss(handler: (evt: CampaignDismissEvent) => void): () => void;
209
- /** Sugar: subscribe to `error`. Aggregates non-fatal SDK errors — fetch
210
- * failures, render exceptions, host-handler throws. Wire this into your
211
- * error monitoring (Sentry, Datadog RUM, etc.). */
212
- onError(handler: (evt: AegisErrorEvent) => void): () => void;
213
- constructor(config: AegisInAppConfig);
214
- initialize(): Promise<void>;
215
- private _lastHealOutcome;
216
- private _sendPushBootBeacon;
217
- updateUserId(userId: string): void;
218
- updateContactId(contactId: string, contactToken?: string): void;
219
- private refreshDebounceTimer?;
220
- private static readonly REFRESH_DEBOUNCE_MS;
221
- private pendingPreInitRefresh;
222
- private currentSurface;
223
- private currentScreen;
224
- private currentLocationId;
225
- private static readonly KNOWN_SURFACES;
226
- /**
227
- * Qualifying-event refresh — schedule a debounced `refreshCampaigns()`
228
- * call in response to a host-app SDK event (`aegis.page()`,
229
- * `aegis.screen()`, `aegis.track(name)`). Matches CleverTap / MoEngage /
230
- * WebEngage's "refresh on qualifying event" shape, translated to the
231
- * browser session.
232
- *
233
- * Why: new server-side eligibility opens on these events (a `page` view
234
- * arms route-targeted campaigns; a `track('product_viewed')` arms
235
- * trigger-gated campaigns whose `client_trigger.config.event` matches).
236
- * Pre-P1, the SDK only refreshed on `initialize()` + `updateContactId()`
237
- * + the SSE update channel (which storefront has disabled by design),
238
- * so newly armed campaigns were invisible until the next identity flip.
239
- *
240
- * Debounce: 300ms. Five `page()` calls inside the window coalesce to
241
- * one fetch; a sixth call ≥300ms after the prior burst starts a new
242
- * fetch. The trailing-edge schedule (re-arm on every call) is correct
243
- * for storefront rage-clicks + SPA route bursts — we want the LATEST
244
- * URL's eligibility, not the first.
245
- *
246
- * Safe to call on the server (no-op when `setTimeout` is unavailable
247
- * — though that's vanishingly rare in any modern environment we ship to).
248
- *
249
- * Surface capture (In-App Surface Filter tracker P2): when `eventName`
250
- * matches one of the 8 canonical surface keys, it is captured into
251
- * `currentSurface` and threaded to `/v1/in-app/active?current_surface=<name>`
252
- * on the next refresh — wiring the existing `aegis.page(name)` /
253
- * `aegis.screen(name)` argument through to campaign targeting. Unknown names
254
- * (e.g. `aegis.track('product_viewed')`) still trigger a refresh but do NOT
255
- * change `currentSurface`, so arbitrary events never poison the surface gate.
256
- */
257
- refreshOnEvent(eventName: string, isScreenDeclaration?: boolean): void;
258
- /**
259
- * Outlet context — declare the outlet the customer is actively viewing
260
- * (storefront outlet switcher / location-code). The host calls this when the
261
- * shopper picks an outlet (or on initial outlet-scoped page load) and again
262
- * with the new id when they switch. Threaded to
263
- * `/v1/in-app/active?location_id=<id>` so the server serves outlet-scoped
264
- * campaigns for THIS outlet (preferred over the property's static outlet) —
265
- * the fix that makes a SHARED multi-outlet storefront's switcher actually
266
- * drive in-app targeting. Pass `null` to clear (back to property/brand-wide).
267
- *
268
- * No-ops when the value is unchanged; otherwise debounce-refreshes so the
269
- * armed campaign set follows the switcher within ~300ms.
270
- */
271
- setLocation(locationId: string | null): void;
272
- /**
273
- * Conversion-aware suppression. Call this when the host app observes a
274
- * goal event (purchase, order_placed, checkout_completed, or any custom
275
- * goal the workspace has configured per-campaign).
276
- *
277
- * For every armed campaign whose `frequency.suppress_after_conversion_seconds`
278
- * is set, this:
279
- * 1. Persists the conversion in sessionStorage (so subsequent page
280
- * loads in the same tab retain silence).
281
- * 2. Computes an expiry epoch_ms and stores it in `suppressedUntil`.
282
- * 3. The two campaign-evaluation paths (`tryDisplayNextCampaign`
283
- * and `onClientEvent`) skip campaigns whose `suppressedUntil`
284
- * has not yet passed.
285
- *
286
- * Scope: only `session` is implemented in P0a. `user` and `user_sku`
287
- * scopes degrade to session-only with a debug log; the schema accepts
288
- * them so future expansion is non-breaking.
289
- *
290
- * Safe to call repeatedly for the same goal — each call refreshes the
291
- * sessionStorage timestamp and re-applies suppression to any campaigns
292
- * loaded since the last call.
293
- */
294
- notifyConversion(goalName: string): void;
295
- /**
296
- * Compute and store `suppressedUntil` entries for every armed campaign
297
- * whose `frequency.suppress_after_conversion_seconds` is set. Called by
298
- * `notifyConversion()` and `refreshCampaigns()` (the latter rehydrates
299
- * suppression for newly-loaded campaigns when a prior conversion event
300
- * exists in sessionStorage).
301
- *
302
- * `convertedAt` is the epoch_ms of the conversion; pass `Date.now()`
303
- * when called from `notifyConversion`, or the `ts` recovered from
304
- * sessionStorage when rehydrating.
305
- */
306
- private applySuppressionFromCampaigns;
307
- /**
308
- * Recover any prior conversion timestamps from sessionStorage and apply
309
- * suppression to currently-loaded campaigns. Called from
310
- * `refreshCampaigns()` after `this.campaigns` is populated. This is the
311
- * mechanism that prevents a converted buyer from seeing a discount popup
312
- * on the next page load within the same session.
313
- */
314
- private rehydrateSuppressionFromStorage;
315
- /**
316
- * Returns true if the campaign is currently suppressed (Date.now() is
317
- * before the stored expiry). Used by both display paths.
318
- */
319
- private isSuppressed;
320
- /**
321
- * Whether this tab recorded a conversion (`notifyConversion`, persisted per
322
- * goal in sessionStorage). The smart trigger's `converted_in_session`.
323
- */
324
- hasConvertedThisSession(): boolean;
325
- /**
326
- * Display one loaded trigger-gated campaign by id — a smart trigger's fire
327
- * decision (MicroIntentArming). False when it is not loaded, already shown,
328
- * suppressed, or the host cancelled it (`campaign-will-show`).
329
- */
330
- showTriggeredCampaign(campaignId: string): boolean;
331
- private connectSSE;
332
- private disconnectSSE;
333
- private attemptReconnect;
334
- private refreshCampaigns;
335
- private static readonly SERVED_DEDUP_PREFIX;
336
- private emitServedEvents;
337
- private detectDeviceType;
338
- private getABAssignments;
339
- private processABAssignments;
340
- private getVariantId;
341
- private tryDisplayNextCampaign;
342
- private _lastDispatch;
343
- /** Persistent launchers (active_web_chat) mount AMBIENTLY on every arm — a
344
- * single always-on entry point, NOT an interruption, so they must not
345
- * compete in the one-at-a-time overlay queue (tryDisplayNextCampaign).
346
- * Idempotent: renderActiveWebChatLauncher no-ops once a launcher is live and
347
- * displayedCampaigns guards re-display. Before this, the launcher only
348
- * mounted when it happened to be the single campaign tryDisplayNextCampaign
349
- * picked — so aegis.chat.open() / the mobile "Ask" tab silently did nothing
350
- * whenever another non-triggered campaign was armed first. */
351
- private mountPersistentLaunchers;
352
- private filledSlots;
353
- private slotSignatures;
354
- private slotTimers;
355
- private slotObserver;
356
- private slotRenderScheduled;
357
- private slotUnfilledBeaconed;
358
- private slotUnfilledSweepTimer;
359
- /**
360
- * Scan the page for `[data-aegis-slot]` anchors and render any
361
- * campaign whose `widget_category` matches the slot key AND whose
362
- * `delivery_modes` includes `'embedded_card'`.
363
- *
364
- * Runs after every refresh-campaigns success — the WeakSet guard
365
- * keeps it idempotent across re-fetches. The slot key is the campaign's
366
- * `widget_category` (the wide grouping discriminator from F3), so a
367
- * `<div data-aegis-slot="feedback">` will accept the first active
368
- * campaign with `widget_category='feedback'` + embedded_card mode.
369
- */
370
- /** Normalize a declared free-form screen name to a canonical surface key
371
- * ('storefront.catalog' → 'storefront_catalog'), or null when it maps
372
- * outside the enum (e.g. 'shopify.product'). Mirrors the server's
373
- * `_canonical_surface_from_screen`. */
374
- private canonicalSurfaceFromScreen;
375
- /**
376
- * Dynamic Screens P2 + substrate isolation — combined surface + free-form
377
- * screen predicate, mirroring the server's `_screen_or_surface_matches`:
378
- * - screen-only campaign matches strictly on `currentScreen`
379
- * - a declared screen NORMALIZES into the canonical tier
380
- * ('storefront.catalog' → storefront_catalog) so surface pins match
381
- * - a declared screen that maps OUTSIDE the enum (e.g. 'shopify.product')
382
- * does NOT fall through to the all-surfaces default for surface-pinned
383
- * campaigns — actii.me-pinned campaigns never render on Shopify
384
- * - hosts that declare nothing keep the legacy render-everything default
385
- */
386
- private matchesCurrentScreenOrSurface;
387
- private ensureSlotObserver;
388
- private renderIntoSlots;
389
- /**
390
- * slot_unfilled telemetry (Surface/Slot Contract tracker P2).
391
- *
392
- * A served embedded_card campaign whose `widget_category` has NO
393
- * `data-aegis-slot` anchor on the page is silently discarded by
394
- * `renderIntoSlots` — the exact failure mode that made the storefront
395
- * value bar dark for browse traffic while the dashboard reported the
396
- * campaign "active". This sweep runs ~3s after a render attempt (late
397
- * mounts / SPA hydration get time to appear; the MutationObserver re-fill
398
- * handles anchors that arrive even later) and beacons one `slot_unfilled`
399
- * per (campaign, path) per session so the dashboard can show
400
- * "served but never rendered".
401
- */
402
- private scheduleSlotUnfilledSweep;
403
- private sweepUnfilledSlots;
404
- /**
405
- * Rotating slot — cycles eligible campaigns one at a time inside a single host
406
- * slot (the storefront sticky-footer "value bar"). Each campaign is pre-rendered
407
- * into its own hidden pane so a live `progress_bar` keeps tracking the cart even
408
- * while off-screen; only the visibility toggles every `rotateMs`. The impression
409
- * is deferred at render and fired per campaign on FIRST visibility, so hidden
410
- * panes never over-count. One rendered pane → no rotation (just shows it).
411
- */
412
- private renderRotatingSlot;
413
- /**
414
- * Surface-Canvas inline element anchoring (Plotline / WebEngage model).
415
- *
416
- * Render inline-format campaigns into a HOST element the operator picked via
417
- * the element inspector — `interactive_config.anchor.element_selector` — instead
418
- * of a host-declared `[data-aegis-slot]`. `position` controls placement:
419
- * inside (default) — append into the element (e.g. the storefront footer card)
420
- * replace — clear the element's host-default children, then render in
421
- * before / after — render into a wrapper inserted as a sibling
422
- *
423
- * Reuses `renderCampaignIntoSlot` (same sub_type dispatch → `renderProgressBarInline`
424
- * etc.) so inline render code is single-sourced with the slot path. Embedded-card
425
- * gated (so an anchored campaign never ALSO renders as an overlay), surface-gated,
426
- * and idempotent via `filledSlots` keyed on the resolved element. A selector that
427
- * doesn't resolve emits `anchor_unresolved` (parity with tooltip anchoring).
428
- */
429
- private renderIntoAnchors;
430
- /**
431
- * U7 — render server-injected token-bound campaigns.
432
- *
433
- * The standalone-page route at `apps/cashier-portal/src/app/s/[slug]/
434
- * [workspace]/[page_key]/page.tsx` (and the `/c/[token]` Aegis-hosted
435
- * fallback) verify the URL token server-side via the F16 internal
436
- * endpoint, then embed the resolved campaign payload as JSON inside
437
- * a `<div data-aegis-token-data="...">` anchor. This SDK method
438
- * scans for those anchors, parses the payload, and dispatches to
439
- * the SAME `renderCampaignIntoSlot` used by the [data-aegis-slot]
440
- * path — so widget rendering code is single-sourced.
441
- *
442
- * Why server-injected (not client-fetched): keeps INTERNAL_API_SECRET
443
- * server-side, avoids an extra browser round-trip for verify, gives
444
- * Next.js SSR-friendly initial HTML for SEO/no-JS users (rendered
445
- * via the page handler's React tree alongside the SDK anchor).
446
- *
447
- * Idempotency: filledSlots is the same WeakSet used by
448
- * renderIntoSlots — re-running on SSE/poll refresh is a no-op for
449
- * already-filled anchors.
450
- */
451
- private renderTokenAnchors;
452
- /**
453
- * Slot-mode counterpart to `displayCampaign`. Reuses the existing
454
- * lifecycle hooks (`campaign-will-show`, `displayedCampaigns`,
455
- * `campaign-shown`, impression tracking) so analytics + frequency
456
- * caps work the same way as overlay renders.
457
- *
458
- * Only the sub_types that make UX sense embedded are handled; the
459
- * rest (modal, full_screen, half_interstitial, alert, pip) silently
460
- * skip — those types only make sense as fullscreen overlays.
461
- */
462
- private renderCampaignIntoSlot;
463
- /** §7.13.H — generic embedded card: image / title / body / coupon chip / CTA.
464
- * The catch-all for banner + nudge sub_types (birthday, referral, app-install,
465
- * win-back, etc.) so EVERY gallery nudge renders on the bill (previously only
466
- * star_rating / nps_survey / form were handled → nudges never appeared). */
467
- /**
468
- * Whether `interactive_config` carries a composed `card_template` (non-empty
469
- * `elements[]`). The ONE detector shared by all three composed-card cutover
470
- * gates — inline slot, chrome overlay, and the P6 survey-overlay cutover — so
471
- * they can't drift (the inline gate previously omitted the non-empty check).
472
- */
473
- private _hasComposedTemplate;
474
- /**
475
- * P6.full — flat survey → card_template adapter. A `nps_survey` / `star_rating`
476
- * / `quick_poll` authored WITHOUT a card_template gets one synthesised in place
477
- * (carrying its follow-up config), so the EXISTING composed-template gates route
478
- * it through the ONE card-template engine + the shared element builders (with
479
- * the ported reason-capture/thank-you flows) — overlay AND slot. Back-compat:
480
- * the field names + follow-up defaults match the retired bespoke renderers.
481
- * No-op when a template already exists or the type isn't a single-card survey.
482
- */
483
- private _ensureSurveyTemplate;
484
- /**
485
- * Composed single card — renders a campaign's top-level `card_template`
486
- * (frame + elements: icon/text/progress/image/cta/group/…) through the generic
487
- * `renderCardFromTemplate` engine. The canonical path for the rich card editor:
488
- * the storefront value bar, loyalty nudges, and any operator-composed card all
489
- * render here from declarative JSON, on ACS / custom / third-party sites.
490
- */
491
- private renderComposedCardSlot;
492
- /**
493
- * C2 — composed card body in an OVERLAY (modal / banner / full_screen /
494
- * half_interstitial). The overlay counterpart to `renderComposedCardSlot`: it
495
- * builds the same `TemplateCard` from the campaign's canonical fields and paints
496
- * it with the generic `renderCardFromTemplate` engine, then wraps it in the
497
- * standard overlay chrome (close button + size scaling) so a composed card looks
498
- * identical whether overlaid or slotted. Only invoked when a `card_template` is
499
- * present (see displayCampaign) — flat campaigns never reach here.
500
- * See IN_APP_FORMAT_LOCK_AND_COMPOSABLE_CARDS_TRACKER.md (Part B / C2).
501
- */
502
- private renderComposedCardOverlay;
503
- private renderGenericCardSlot;
504
- /**
505
- * §7.13.I — embedded profile FORM. Renders one input per `form_fields` entry
506
- * and, on submit, POSTs `{response_type:'form', payload:{fields:{key:value}}}`
507
- * to `/in-app/responses` — the cell-plane enrichment writes the mapped answers
508
- * back onto the contact. Keyed by each field's `key` so it matches the
509
- * campaign's field→contact map.
510
- */
511
- private renderFormSlot;
512
- /** Shared form body — the input fields + submit. Used embedded (slot) today;
513
- * the overlay form path reuses it too. */
514
- private _buildFormBody;
515
- /** POST a form response to /in-app/responses (gateway → cell-plane). The
516
- * cell-plane enriches the contact from the campaign's field-map. Best-effort. */
517
- private submitFormResponse;
518
- /**
519
- * Generic interactive response submit → POST /v1/in-app/responses. Returns the
520
- * parsed server response (which for spin/scratch carries the server-picked
521
- * `prize`) or null. Interactive Widgets Parity tracker — every embedded widget
522
- * records a response so analytics + prize-from-segments work uniformly.
523
- */
524
- private submitWidgetResponse;
525
- /**
526
- * Built-in embedded SPIN WHEEL (no AegisMessageRuntime callback dependency —
527
- * works on the bill). Slices come from `interactive_config.segments`; on Spin
528
- * it submits to /in-app/responses, the server picks the prize from the AUTHORED
529
- * segments, and the wheel animates to that segment + reveals the prize/coupon.
530
- */
531
- private renderSpinWheelSlot;
532
- /**
533
- * Built-in embedded IMAGE/VIDEO CAROUSEL — a swipeable horizontal strip of
534
- * `interactive_config.cards` (image OR video + title + body + CTA), contained
535
- * to the slot (the parent overflow:hidden + this strip's own scroll). Each card
536
- * reuses the same shape as the In-App carousel editor authors.
537
- */
538
- private renderCarouselCardsSlot;
539
- /** Built-in embedded COUNTDOWN — live H:M:S digits that pulse red in the
540
- * final stretch (<60s) to create urgency, plus the CTA. */
541
- private renderCountdownSlot;
542
- /** Foil palette — a brushed-metal gradient + a contrast "ink" (for the
543
- * SCRATCH-HERE text, sparkle motif, and logo tint) derived from the operator's
544
- * chosen foil colour (brand primary / custom hex). Unset / invalid → the
545
- * default silver. Light foils get dark ink, dark foils get white ink, so the
546
- * prompt stays legible on any colour. */
547
- private _scratchFoilPalette;
548
- /** Built-in embedded SCRATCH CARD — a canvas foil the customer scratches to
549
- * reveal the server-picked prize, then submits (records + grants). Confetti
550
- * on reveal. No AegisMessageRuntime callback dependency (works on the bill). */
551
- private renderScratchCardSlot;
552
- /** Extract a human prize label from a spin/scratch server response. */
553
- private _extractPrizeLabel;
554
- /** Extract a redeemable coupon code from a spin/scratch server response. */
555
- private _extractCouponCode;
556
- /**
557
- * Slot styling — the operator's look-&-feel knobs (Style panel) applied to the
558
- * EMBEDDED render, CLAMPED to boundary-safe ranges so an authored value can
559
- * never break out of the dynamic block. Unauthored knobs are `undefined` so
560
- * each renderer keeps its own compact default (no visual regression). Colors
561
- * default to the current slot defaults. The slot card keeps `width:100%;
562
- * overflow:hidden` regardless, so the boundary guarantee is independent of
563
- * these values.
564
- */
565
- private _slotStyle;
566
- private _wrapInSlotCard;
567
- /**
568
- * Flush a rendered slot card into its host card — drop the per-campaign radius
569
- * + shadow so it reads as one unit with the host wrapper (used for the
570
- * storefront value bar via `data-aegis-slot-flush`). Targets every
571
- * `[data-campaign-id]` card inside `container` (or the container itself).
572
- */
573
- private _flattenSlotCard;
574
- /**
575
- * Evaluate the currently armed campaigns against a client-side event
576
- * and render any that match their `client_trigger`.
577
- *
578
- * Called by the host app (e.g., the EcommerceTracker on product_viewed),
579
- * or by future TriggerEngine bridges. Safe to call repeatedly for the
580
- * same event — dedup happens via `displayedCampaigns`.
581
- *
582
- * Supported trigger types (align with the cell-plane server-side
583
- * `display_rules.trigger_type`):
584
- * - `custom_event` : fire when eventName matches config.event
585
- * - `product_match` : fire on `product_viewed` when eventData.product_id
586
- * matches config.product_id (string or string[])
587
- * - `delay` : client-side setTimeout — evaluated at armeng
588
- * time (kicked off from `displayCampaign`), not here.
589
- * - `exit_intent` / `scroll_depth` / `time_on_page` / `inactivity` : the
590
- * TriggerEngine event named by `behaviouralTriggerEvent`;
591
- * AegisMessageRuntime arms the engine and forwards it.
592
- */
593
- onClientEvent(eventName: string, eventData?: Record<string, unknown>): void;
594
- private matchesClientTrigger;
595
- private displayCampaign;
596
- /** The single chat launcher on the page (Active Web Chat). Persistent — not
597
- * re-mounted on SSE/poll refresh; torn down on manager teardown. */
598
- private activeWebChat?;
599
- /**
600
- * Mount the Active Web Chat launcher. Idempotent: a repeat delivery of the
601
- * same (or another) chat campaign is a no-op once one launcher is live — at
602
- * most one bubble per page, and an open session is never yanked out from
603
- * under the customer.
604
- */
605
- private renderActiveWebChatLauncher;
606
- /**
607
- * Build the shared context passed into the preload-first renderers.
608
- * Matches the interface in `./renderers/types.ts`. Kept as a private
609
- * method (rather than inlined at each call-site) so future renderer
610
- * additions stay consistent and easy to audit.
611
- */
612
- private buildRenderContext;
613
- /**
614
- * Renders interactive sub-type campaigns (spin wheel, NPS, quiz, etc.)
615
- * using DOM-safe rendering. These sub-types use the campaign's
616
- * interactive_config payload for type-specific behavior.
617
- */
618
- private renderInteractive;
619
- /**
620
- * Built-in overlay for spin_wheel / scratch_card when no host runtime is
621
- * wired (dashboard preview, runtime-less hosts). Wraps the same interactive
622
- * slot renderers (`renderSpinWheelSlot` / `renderScratchCardSlot`) in a modal
623
- * so the gamified widget is fully playable — not a dead frame.
624
- */
625
- /** Display-size multiplier — operator control over how big a bounded overlay
626
- * renders (small 0.82 / medium 1 / large 1.18). Default medium = prior size.
627
- * Read from interactive_config.display_size; mirrors the schema enum + the
628
- * dashboard preview harness so the operator's choice is honored identically. */
629
- private _sizeMul;
630
- private renderGamificationOverlay;
631
- /** A circular ✕ close button in the modal's top-right corner. */
632
- private _addCornerClose;
633
- /**
634
- * Custom HTML type — render operator-authored HTML in a SANDBOXED iframe.
635
- * The frame runs with `sandbox="allow-scripts"` and NO `allow-same-origin`, so
636
- * it's an opaque/null origin: no access to the host DOM, cookies, localStorage,
637
- * the SDK write-key, or same-origin creds. The only channel out is a validated
638
- * `postMessage` bridge (close / track / cta / resize), exposed to authors as a
639
- * tiny injected `aegis` shim. See IN_APP_CUSTOM_HTML_TRACKER.md (security model).
640
- */
641
- private renderCustomHtml;
642
- /** Build the sandboxed iframe + the validated message bridge. Reused by the
643
- * overlay + embedded-slot custom-HTML renderers. */
644
- private _buildCustomHtmlFrame;
645
- /** Embedded-slot custom HTML (e.g. on the bill) — the same sandboxed frame in
646
- * a slot card instead of an overlay. */
647
- private renderCustomHtmlSlot;
648
- private renderCountdownOffer;
649
- /**
650
- * Multi-step form (SDK Demo v4 W1b) — in-app render type matching the
651
- * Plotline multi-step flow pattern: a progress bar + per-step question with
652
- * options, Back/Next navigation, Submit on the last step. `ic.steps` is an
653
- * array of `{ question, options[] }`. DOM-safe (no innerHTML).
654
- */
655
- private renderMultiStepForm;
656
- /**
657
- * Push primer (SDK Demo v4 W3) — in-app render type that "soft-asks" before
658
- * the OS hard-ask. The Enable CTA calls `Notification.requestPermission()`;
659
- * we report `permission_granted` / `permission_denied` / `permission_default`
660
- * so journeys can react. Degrades gracefully where Notification is absent.
661
- */
662
- private renderPushPrimer;
663
- private _pushOptInCache;
664
- private _pushOptInLoading;
665
- /** Synchronous "can the primer render RIGHT NOW" gate used by the overlay
666
- * selection loop. Never tracks events — an ineligible primer is a
667
- * non-event, not a dismissal. */
668
- private _primerEligibleNow;
669
- /**
670
- * Fetch the property's push opt-in mode/trigger from /v1/sdk/config
671
- * (browser HTTP cache + ETag make repeat calls cheap). Fail-open to
672
- * 'standard'/'immediate' — a config blip must not silently kill the
673
- * only web-push opt-in surface.
674
- */
675
- private _fetchPushOptInConfig;
676
- /**
677
- * Prompt-timing gate for the standard primer.
678
- * immediate → always
679
- * second_page_view → from the 2nd page load on (localStorage counter,
680
- * incremented once per manager init)
681
- * after_first_order→ once `aegis_ordered` is set (host runtimes set it
682
- * on purchase; storefront checkout does)
683
- * manual → never auto-shows; host calls the primer explicitly
684
- */
685
- private _promptTriggerSatisfied;
686
- private _renderPushPrimerModal;
687
- /**
688
- * Push-primer Enable handler — runs the SAME end-to-end subscribe flow the
689
- * (now-retired) ACS PushOptInBanner did, so the primer is a complete web-push
690
- * opt-in on every surface (ACS / Shopify / 3rd-party site), not just an OS
691
- * permission prompt. Steps: fetch VAPID public key → register SW → hard-ask →
692
- * subscribe → POST /v1/web_push/subscriptions → stitch known contact.
693
- *
694
- * Push runtime resolution prefers the already-loaded `window.AegisWebPush`
695
- * global (the Shopify embed lazy-loads it via /apps/active_reach/push.js, so
696
- * we reuse that single instance instead of registering the SW twice), and
697
- * only dynamic-imports the module as a fallback for hosts that didn't preload
698
- * it (ACS / 3rd-party sites). Returns the resolved permission string for
699
- * engagement reporting; falls back to a bare permission ask if push can't be
700
- * wired (no propertyId, no VAPID).
701
- */
702
- private _subscribeFromPushPrimer;
703
- private renderQuiz;
704
- /**
705
- * Embedded quiz on a bill/page slot — same question-stepping flow as the
706
- * overlay, rendered inline with no overlay/close (Open/Closed: both paths
707
- * share `_runQuizFlow`, so a feature added once lights up both). Returns
708
- * false when there are no questions so dispatch can fall back to a card.
709
- */
710
- private renderQuizSlot;
711
- /**
712
- * Shared quiz engine — drives the question stepper, scoring (knowledge =
713
- * correct_index/points, personality = outcome tally), result-band selection,
714
- * CTA, reaction and the `quiz` response submit. Renders into the provided
715
- * `body`. `overlay` is the dismiss target for the close button on the overlay
716
- * path; pass null on the slot path (no close affordance inline).
717
- */
718
- private _runQuizFlow;
719
- private createOverlay;
720
- /** Wrap a card in a display-size scaling container (no-op at medium/×1). Used
721
- * by formats that build their overlay inline (renderModal) rather than via
722
- * createOverlay, so size is honored identically. */
723
- private _wrapScaled;
724
- private createCTAButton;
725
- private addCloseButton;
726
- /**
727
- * Navigate to a campaign CTA URL.
728
- *
729
- * Dispatches a cancellable `aegis:campaign-click` CustomEvent so host
730
- * apps (e.g., the storefront SPA) can intercept and route in-place via
731
- * their client-side router, avoiding a full page reload that re-runs
732
- * SSR + re-renders the location picker / banner / cart drawer state.
733
- *
734
- * The host calls `event.preventDefault()` to suppress the hard nav and
735
- * handle the URL itself. If no listener calls preventDefault, the SDK
736
- * falls through to `window.location.href` so plain HTML pages without a
737
- * SPA still get the user to the destination.
738
- *
739
- * Industry parallel: CleverTap dispatches `clevertap-deeplink-click`,
740
- * MoEngage dispatches `MoE_inappRedirect`. The pattern lets the same
741
- * SDK serve both vanilla websites (full nav fallback) and SPAs
742
- * (in-place state change) without per-host configuration.
743
- */
744
- private navigateToCampaignAction;
745
- /** Register/replace the canonical CTA-action handler after construction (the
746
- * config `onAction` is the construct-time equivalent). Idempotent. */
747
- setOnAction(handler: (action: AegisAction) => boolean | void): void;
748
- /**
749
- * Canonical CTA-action dispatcher (CANONICAL_ACTION_MODEL_TRACKER.md). ONE path
750
- * for every host-facing CTA action:
751
- * 1. give the host's `onAction` handler first refusal (return `true` = handled);
752
- * 2. ALSO fire the legacy `aegis:*` DOM event (cancelable) so existing
753
- * listeners (ACS) keep working unchanged during the migration;
754
- * 3. if STILL unhandled → canonical fallback (navigate to the product/cart
755
- * page) so a CTA is never a dead no-op (the rail-"Add" 3rd-party gap).
756
- * Both add-to-cart paths (card "Add" + the `aegis://cart-add` verb) funnel here,
757
- * so they're one behaviour delivered to the host's single `onAction` handler.
758
- * (Phase E.2 hard cutover — the legacy `aegis:*` DOM events were removed.)
759
- */
760
- dispatchAction(action: AegisAction, _campaignId?: string): void;
761
- /**
762
- * Phase-2 CTA action verbs (`aegis://<verb>?<params>`). Performs an in-app
763
- * behaviour instead of navigating. Verbs:
764
- * dismiss — close the message (tracked as dismissed)
765
- * request-push — prompt for web-push permission, then close
766
- * cart-add?sku=X — canonical `add_to_cart` action via dispatchAction
767
- * (cancelable + product-page fallback)
768
- */
769
- private runCampaignActionVerb;
770
- /** Remove a rendered campaign's element(s) from the DOM by campaign id. */
771
- private removeCampaignEl;
772
- /**
773
- * Resolve the operator's colour + style choices into concrete CSS values.
774
- * Mirrors the studio preview's styleFrom() 1:1 (apps/web/src/app/api/preview/
775
- * in-app/route.tsx) — same keys, same defaults — so what the operator designs
776
- * in the editor is what the live SDK renders. The container uses the
777
- * campaign's background_color/text_color and the CTA button is the inverse
778
- * (so it pops on the coloured surface), exactly like the preview. Earlier the
779
- * card renderers hardcoded a white card + dark text and ignored the Style
780
- * panel entirely, so the live message looked nothing like the preview.
781
- */
782
- /** Pick a legible label colour (#fff or near-black) for a solid fill, by
783
- * relative luminance — so a brand-accent button is always readable. */
784
- private _contrastText;
785
- /** Central surface palette — the ONE place the brand colour becomes either a
786
- * surface fill or an accent, so every widget is consistent.
787
- *
788
- * `surface_style` (interactive_config), default `clean`:
789
- * - `clean` → NEUTRAL surface (white) + dark text + the brand colour as the
790
- * CTA ACCENT. The 60-30-10 / WCAG-safe default: legible copy and
791
- * a CTA that actually pops (higher CTR than a flat colour card).
792
- * - `branded`→ brand-colour SURFACE + contrasting text + inverse CTA. For
793
- * high-impact, low-text moments (flash sale, celebration).
794
- * `background_color` is the operator's brand colour in BOTH modes — surface
795
- * fill when branded, CTA accent when clean. */
796
- private _surfacePalette;
797
- private inAppStyle;
798
- private renderBanner;
799
- private renderModal;
800
- private _heroVideo;
801
- private renderFullScreen;
802
- private renderHalfInterstitial;
803
- private renderAlert;
804
- private _pipCornerCss;
805
- private renderPIP;
806
- private removeBanner;
807
- private removeModal;
808
- /**
809
- * Spotlight backdrop colour → a dim of the chosen base at `dim` opacity.
810
- * `dark` (slate, default) · `brand` (the campaign brand colour) · `black` ·
811
- * `custom` (a picked hex). Encoded as 8-digit hex alpha; falls back to slate
812
- * rgba when the base isn't a 6-digit hex (e.g. an rgb() brand colour).
813
- */
814
- private _spotlightDimColor;
815
- private renderTooltip;
816
- /** Fire a short confetti burst from the top of an anchor element. Vanilla
817
- * (no deps) — mirrors the cashier-portal confetti. Respects
818
- * prefers-reduced-motion. Used on positive moments (high rating, won prize,
819
- * form complete). */
820
- private _fireConfetti;
821
- /**
822
- * Emotional reaction layer (CEP parity — CleverTap exposes configurable
823
- * outcome animation; MoEngage sets distinct win/lose icons). Three CONTEXT-
824
- * AWARE intents drive a premium Lottie animation (with an instant SVG
825
- * placeholder + graceful fallback):
826
- *
827
- * - `celebrate` → a real WIN in a game (spin/scratch prize). Trophy Lottie,
828
- * gold rings, confetti. The big, earned moment.
829
- * - `affirm` → positive FEEDBACK (high rating, NPS promoter, quiz/form
830
- * done). An animated success check — friendly, NOT a trophy
831
- * (a survey isn't a contest).
832
- * - `empathize` → a negative outcome (low rating, NPS detractor, no-prize /
833
- * "Try again"). A soft breathing heart — supportive, NO
834
- * confetti, NO rings.
835
- *
836
- * Honors prefers-reduced-motion (static badge, no motion).
837
- */
838
- private _playReaction;
839
- /** Hand-built, brand-neutral animated SVG badges for the reaction layer — a
840
- * premium alternative to system emoji (consistent across browsers, no Lottie
841
- * dependency). The check strokes itself in; the heart breathes gently. */
842
- private _reactionSvg;
843
- /** Resolve the base URL the SDK assets are served from (so Lottie JSON +
844
- * player load from the same origin/path as the SDK bundle — works in the
845
- * dashboard preview and same-origin installs; cross-origin CDN installs fall
846
- * back to the inline SVG when the asset 404s). */
847
- private _assetBase;
848
- /** Lazy-load the Lottie player once (cached on window). Resolves to the
849
- * global `lottie`, or rejects if it can't be fetched (→ SVG fallback). */
850
- private _loadLottie;
851
- private addAnimationStyles;
852
- private sanitizeUrl;
853
- private sanitizeColor;
854
- /**
855
- * Track in-app event via the standard Event Ingress pipeline.
856
- *
857
- * Events flow: SDK → Event Ingress → Kafka → Governance Consumer
858
- * → Event Engine → ClickHouse (in_app_events + events_with_ttl)
859
- *
860
- * The event_type uses the canonical prefix `in_app.` so the event
861
- * switchboard maps it to the correct canonical form:
862
- * in_app.impression → engagement.view
863
- * in_app.clicked → engagement.click
864
- * in_app.dismissed → engagement.dismiss
865
- */
866
- private trackEvent;
867
- /** campaign id → journey_step_execution_id for queued items seen this page. */
868
- private journeyStepByCampaign;
869
- /** `${step}:${action}` acks sent (or in flight) this page — one per render. */
870
- private journeyAcks;
871
- private static readonly JOURNEY_ACK_RETRY_MS;
872
- /**
873
- * POST /v1/in-app/widgets/{step}/ack — the canonical writer of a queue row's
874
- * seen-state (the same endpoint the Android and iOS SDKs call). Fail-soft by
875
- * construction: it runs after the render, is never awaited by it, and never
876
- * throws. A network error or 5xx is retried once; if that also fails the key
877
- * is released, so the next render of the (still re-served) message tries
878
- * again. A 4xx is the server's answer and is not retried. The server side is
879
- * idempotent — a repeated action is answered `late_arrival` and writes
880
- * nothing — so a duplicate ack is harmless.
881
- */
882
- private ackJourneyQueueItem;
883
- private log;
884
- destroy(options?: {
885
- clearABState?: boolean;
886
- }): void;
887
- }
888
- //# sourceMappingURL=AegisInAppManager.d.ts.map