@cimplify/sdk 0.103.0 → 0.104.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 (72) hide show
  1. package/dist/advanced.d.mts +3 -7
  2. package/dist/advanced.d.ts +3 -7
  3. package/dist/advanced.js +23 -23
  4. package/dist/advanced.mjs +4 -4
  5. package/dist/{client-DHBwvh7U.d.ts → catalogue-BlQ85Uma.d.mts} +492 -748
  6. package/dist/{client-BfpNjJ_a.d.mts → catalogue-C8TqOj9K.d.ts} +492 -748
  7. package/dist/{payment-H5LoIvXU.d.mts → checkout-BqK9XUz5.d.mts} +4338 -1495
  8. package/dist/{payment-H5LoIvXU.d.ts → checkout-BqK9XUz5.d.ts} +4338 -1495
  9. package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
  10. package/dist/{chunk-C2JSNJUC.mjs → chunk-6G3ANKQA.mjs} +1080 -369
  11. package/dist/{chunk-QOWPGS7Z.js → chunk-BWEIMKLF.js} +572 -17
  12. package/dist/{chunk-JSWJ4XDE.js → chunk-DQEXY542.js} +1630 -824
  13. package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
  14. package/dist/{chunk-VIL6IQCG.js → chunk-HYFA67A7.js} +1084 -373
  15. package/dist/{chunk-H7ACMHM3.mjs → chunk-L5SNEU2O.mjs} +553 -16
  16. package/dist/{chunk-JG5CQNCB.js → chunk-RBNBSQI7.mjs} +221 -19
  17. package/dist/{chunk-VNPEYLB2.mjs → chunk-SFUJMS5E.js} +265 -2
  18. package/dist/{chunk-M4URVKHE.mjs → chunk-U54CWUUT.mjs} +11 -2
  19. package/dist/{chunk-GERXZQSX.mjs → chunk-UFOWPN3D.mjs} +967 -582
  20. package/dist/{chunk-J6PNHDHL.mjs → chunk-V4457OKB.mjs} +1580 -785
  21. package/dist/{chunk-R6OLQ33I.js → chunk-WXREIBFW.js} +1041 -654
  22. package/dist/{chunk-NNG6IFFX.js → chunk-XAUSDMMZ.js} +13 -3
  23. package/dist/{client-DpqRobY9.d.mts → client-DhXeiUrf.d.ts} +2 -2
  24. package/dist/{client-Dqg2mc-7.d.ts → client-mXiUrV4X.d.mts} +2 -2
  25. package/dist/{index-C8uDqLwJ.d.ts → index-B0AdZvrM.d.mts} +9 -4
  26. package/dist/{index-e2y6AK70.d.mts → index-CK8Nk5uQ.d.ts} +9 -4
  27. package/dist/index-DPWI5XcU.d.ts +8 -0
  28. package/dist/index-DVfUh-ci.d.mts +8 -0
  29. package/dist/index.d.mts +34 -15
  30. package/dist/index.d.ts +34 -15
  31. package/dist/index.js +306 -133
  32. package/dist/index.mjs +7 -6
  33. package/dist/mock/cli.mjs +1494 -395
  34. package/dist/mock/library.js +1220 -361
  35. package/dist/mock/library.mjs +1220 -361
  36. package/dist/mock/msw.js +1220 -361
  37. package/dist/mock/msw.mjs +1220 -361
  38. package/dist/{price-C2RqscMY.d.ts → price-6hQIIac9.d.ts} +1 -1
  39. package/dist/{price-BvePgDXe.d.mts → price-BUDv1rI7.d.mts} +1 -1
  40. package/dist/react.d.mts +10 -5
  41. package/dist/react.d.ts +10 -5
  42. package/dist/react.js +279 -225
  43. package/dist/react.mjs +140 -86
  44. package/dist/{server-DoXIpSd7.d.ts → server-DJ0mSs9i.d.mts} +33 -57
  45. package/dist/{server-C7FyLdrz.d.mts → server-DSTX76Ki.d.ts} +33 -57
  46. package/dist/server.d.mts +3 -4
  47. package/dist/server.d.ts +3 -4
  48. package/dist/server.js +12 -11
  49. package/dist/server.mjs +6 -5
  50. package/dist/styles.css +2 -2
  51. package/dist/testing/msw.d.mts +2 -2
  52. package/dist/testing/msw.d.ts +2 -2
  53. package/dist/testing/msw.js +4 -4
  54. package/dist/testing/msw.mjs +3 -3
  55. package/dist/testing/suite.d.mts +5 -6
  56. package/dist/testing/suite.d.ts +5 -6
  57. package/dist/testing/suite.js +29 -28
  58. package/dist/testing/suite.mjs +10 -9
  59. package/dist/testing.d.mts +2217 -14
  60. package/dist/testing.d.ts +2217 -14
  61. package/dist/testing.js +173 -83
  62. package/dist/testing.mjs +28 -10
  63. package/dist/utils.d.mts +3 -4
  64. package/dist/utils.d.ts +3 -4
  65. package/dist/utils.js +35 -31
  66. package/dist/utils.mjs +2 -2
  67. package/package.json +9 -9
  68. package/registry/chat-widget.json +1 -1
  69. package/registry/order-history.json +1 -1
  70. package/registry/order-summary.json +1 -1
  71. package/dist/link-cuNVYc2s.d.mts +0 -297
  72. package/dist/link-cuNVYc2s.d.ts +0 -297
@@ -1,7 +1,335 @@
1
1
  'use strict';
2
2
 
3
- var chunkJSWJ4XDE_js = require('./chunk-JSWJ4XDE.js');
4
- var chunkJG5CQNCB_js = require('./chunk-JG5CQNCB.js');
3
+ var chunkXREIZ56K_js = require('./chunk-XREIZ56K.js');
4
+ var chunkDQEXY542_js = require('./chunk-DQEXY542.js');
5
+ var chunkSFUJMS5E_js = require('./chunk-SFUJMS5E.js');
6
+
7
+ // src/types/observability.ts
8
+ var SESSION_CHANGE_SOURCE = {
9
+ RESPONSE: "response",
10
+ MANUAL: "manual",
11
+ CLEAR: "clear"
12
+ };
13
+
14
+ // src/utils/purchase-intent-resolver.ts
15
+ var DEFAULT_POLL_INTERVAL_MS = 3e3;
16
+ var DEFAULT_MAX_POLL_ATTEMPTS = 60;
17
+ var DEFAULT_MAX_ACTION_STEPS = 4;
18
+ var DEFAULT_MAX_TERMS_REVIEW_STEPS = 4;
19
+ function nonnegativeInteger(value, fallback) {
20
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
21
+ return Math.max(0, Math.floor(value));
22
+ }
23
+ function abortError() {
24
+ const error = new Error("Purchase-intent resolution was cancelled.");
25
+ error.name = "AbortError";
26
+ return error;
27
+ }
28
+ async function abortableWait(milliseconds, signal) {
29
+ if (signal?.aborted) throw abortError();
30
+ if (milliseconds <= 0) return;
31
+ await new Promise((resolve, reject) => {
32
+ const timer = setTimeout(() => {
33
+ signal?.removeEventListener("abort", onAbort);
34
+ resolve();
35
+ }, milliseconds);
36
+ const onAbort = () => {
37
+ clearTimeout(timer);
38
+ reject(abortError());
39
+ };
40
+ signal?.addEventListener("abort", onAbort, { once: true });
41
+ });
42
+ }
43
+ var PurchaseIntentResolver = class {
44
+ constructor(dependencies) {
45
+ this.confirmation = dependencies.confirmation;
46
+ this.wait = dependencies.wait ?? abortableWait;
47
+ }
48
+ async resolve(intentId, options) {
49
+ const pollIntervalMs = nonnegativeInteger(
50
+ options.pollIntervalMs,
51
+ DEFAULT_POLL_INTERVAL_MS
52
+ );
53
+ const maxPollAttempts = nonnegativeInteger(
54
+ options.maxPollAttempts,
55
+ DEFAULT_MAX_POLL_ATTEMPTS
56
+ );
57
+ const maxActionSteps = nonnegativeInteger(options.maxActionSteps, DEFAULT_MAX_ACTION_STEPS);
58
+ const maxTermsReviewSteps = nonnegativeInteger(
59
+ options.maxTermsReviewSteps,
60
+ DEFAULT_MAX_TERMS_REVIEW_STEPS
61
+ );
62
+ let request = options.confirmation;
63
+ let pollAttempts = 0;
64
+ let actionSteps = 0;
65
+ let termsReviewSteps = 0;
66
+ this.ensureActive(options.signal);
67
+ this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.CONFIRMING);
68
+ let responseResult = options.initialResponse ? chunkDQEXY542_js.ok(options.initialResponse) : await this.confirmation.confirm(intentId, request, { signal: options.signal });
69
+ while (responseResult.ok) {
70
+ this.ensureActive(options.signal);
71
+ const response = responseResult.value;
72
+ switch (response.status) {
73
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED:
74
+ this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.COMPLETED, response);
75
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.COMPLETED, response });
76
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_FAILED:
77
+ this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.FAILED, response);
78
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.FAILED, response });
79
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_OUTCOME_UNKNOWN:
80
+ this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.OUTCOME_UNKNOWN, response);
81
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.OUTCOME_UNKNOWN, response });
82
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.COLLECTION_PROCESSING:
83
+ if (pollAttempts >= maxPollAttempts) {
84
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.TIMED_OUT, response });
85
+ }
86
+ pollAttempts += 1;
87
+ this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.POLLING, response);
88
+ await this.wait(pollIntervalMs, options.signal);
89
+ responseResult = await this.confirmation.confirm(intentId, request, {
90
+ signal: options.signal
91
+ });
92
+ continue;
93
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.REDIRECT_REQUIRED:
94
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED:
95
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED: {
96
+ this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.AWAITING_ACTION, response);
97
+ if (!options.onActionRequired || actionSteps >= maxActionSteps) {
98
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.ACTION_REQUIRED, response });
99
+ }
100
+ actionSteps += 1;
101
+ const decision = await options.onActionRequired(response);
102
+ if (decision === chunkSFUJMS5E_js.PURCHASE_INTENT_ACTION_DECISION.STOP) {
103
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.ACTION_REQUIRED, response });
104
+ }
105
+ if (typeof decision === "object" && decision.kind === chunkSFUJMS5E_js.PURCHASE_INTENT_ACTION_DECISION.SUBMIT) {
106
+ if (!this.confirmation.submitAction) {
107
+ return chunkDQEXY542_js.err(
108
+ new chunkSFUJMS5E_js.CimplifyError(
109
+ chunkSFUJMS5E_js.PURCHASE_INTENT_ERROR_CODE.ACTION_UNSUPPORTED,
110
+ "This confirmation transport cannot submit purchase-intent actions.",
111
+ false
112
+ )
113
+ );
114
+ }
115
+ responseResult = await this.confirmation.submitAction(
116
+ intentId,
117
+ response.attempt_id,
118
+ decision.request,
119
+ { signal: options.signal }
120
+ );
121
+ continue;
122
+ }
123
+ if (decision !== chunkSFUJMS5E_js.PURCHASE_INTENT_ACTION_DECISION.CONTINUE) {
124
+ return chunkDQEXY542_js.err(
125
+ new chunkSFUJMS5E_js.CimplifyError(
126
+ chunkSFUJMS5E_js.PURCHASE_INTENT_ERROR_CODE.ACTION_UNSUPPORTED,
127
+ "The action callback returned an unsupported decision.",
128
+ false
129
+ )
130
+ );
131
+ }
132
+ await this.wait(pollIntervalMs, options.signal);
133
+ responseResult = await this.confirmation.confirm(intentId, request, {
134
+ signal: options.signal
135
+ });
136
+ continue;
137
+ }
138
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED: {
139
+ this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.REVIEWING_TERMS, response);
140
+ if (!options.onTermsChanged || termsReviewSteps >= maxTermsReviewSteps) {
141
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
142
+ }
143
+ termsReviewSteps += 1;
144
+ const replacementRequest = await options.onTermsChanged(response);
145
+ if (!replacementRequest) {
146
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
147
+ }
148
+ request = replacementRequest;
149
+ responseResult = await this.confirmation.confirm(intentId, request, {
150
+ signal: options.signal
151
+ });
152
+ continue;
153
+ }
154
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE:
155
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.OFFER_ENDED:
156
+ case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE:
157
+ return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
158
+ }
159
+ }
160
+ return responseResult;
161
+ }
162
+ ensureActive(signal) {
163
+ if (signal?.aborted) throw abortError();
164
+ }
165
+ notify(options, status, response) {
166
+ try {
167
+ options.onStatusChange?.(status, response);
168
+ } catch {
169
+ }
170
+ }
171
+ };
172
+
173
+ // src/purchase-intent.ts
174
+ var PURCHASE_INTENT_ROUTE = {
175
+ DIRECT_COLLECTION: "/api/v1/purchase-intents",
176
+ HOSTED_SESSION_COLLECTION: "/v1/checkout/sessions",
177
+ PREPARE: "prepare",
178
+ REFRESH: "refresh",
179
+ CONFIRM: "confirm",
180
+ REPLAY: "replay",
181
+ ATTEMPTS: "attempts",
182
+ ACTIONS: "actions"
183
+ };
184
+ function directIntentPath(intentId) {
185
+ const normalized = intentId.trim();
186
+ if (!normalized) throw new TypeError("intentId must not be empty");
187
+ return `${PURCHASE_INTENT_ROUTE.DIRECT_COLLECTION}/${encodeURIComponent(normalized)}`;
188
+ }
189
+ function hostedSessionPath(sessionId) {
190
+ const normalized = sessionId.trim();
191
+ if (!normalized) throw new TypeError("sessionId must not be empty");
192
+ return `${PURCHASE_INTENT_ROUTE.HOSTED_SESSION_COLLECTION}/${encodeURIComponent(normalized)}`;
193
+ }
194
+ var PurchaseIntentService = class {
195
+ constructor(transport) {
196
+ this.transport = transport;
197
+ this.resolver = new PurchaseIntentResolver({ confirmation: this });
198
+ }
199
+ mint(request, options) {
200
+ return chunkDQEXY542_js.safe(
201
+ this.transport.post(PURCHASE_INTENT_ROUTE.DIRECT_COLLECTION, request, {
202
+ idempotencyKey: request.client_intent_key,
203
+ signal: options?.signal,
204
+ timeoutMs: options?.timeoutMs
205
+ })
206
+ );
207
+ }
208
+ get(intentId, options) {
209
+ return chunkDQEXY542_js.safe(
210
+ this.transport.get(this.intentPath(intentId), {
211
+ signal: options?.signal,
212
+ timeoutMs: options?.timeoutMs
213
+ })
214
+ );
215
+ }
216
+ refresh(intentId, expectedRevision, options) {
217
+ const request = {
218
+ expected_intent_revision: expectedRevision
219
+ };
220
+ return chunkDQEXY542_js.safe(
221
+ this.transport.post(
222
+ `${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.REFRESH}`,
223
+ request,
224
+ {
225
+ signal: options?.signal,
226
+ timeoutMs: options?.timeoutMs
227
+ }
228
+ )
229
+ );
230
+ }
231
+ confirm(intentId, request, options) {
232
+ return chunkDQEXY542_js.safe(
233
+ this.transport.post(
234
+ `${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.CONFIRM}`,
235
+ request,
236
+ {
237
+ idempotencyKey: request.idempotency_key,
238
+ signal: options?.signal,
239
+ timeoutMs: options?.timeoutMs
240
+ }
241
+ )
242
+ );
243
+ }
244
+ submitAction(intentId, attemptId, request, options) {
245
+ const normalizedAttemptId = attemptId.trim();
246
+ if (!normalizedAttemptId) throw new TypeError("attemptId must not be empty");
247
+ return chunkDQEXY542_js.safe(
248
+ this.transport.post(
249
+ `${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.ATTEMPTS}/${encodeURIComponent(normalizedAttemptId)}/${PURCHASE_INTENT_ROUTE.ACTIONS}`,
250
+ request,
251
+ {
252
+ signal: options?.signal,
253
+ timeoutMs: options?.timeoutMs
254
+ }
255
+ )
256
+ );
257
+ }
258
+ resolve(intentId, options) {
259
+ return this.resolver.resolve(intentId, options);
260
+ }
261
+ /**
262
+ * Bind subsequent preparation/confirmation to one hosted checkout-session
263
+ * owner. The session ID is a route authority, not an intent-owner rewrite.
264
+ */
265
+ forHostedSession(sessionId) {
266
+ return new HostedPurchaseIntentService(this.transport, sessionId);
267
+ }
268
+ intentPath(intentId) {
269
+ return directIntentPath(intentId);
270
+ }
271
+ };
272
+ var HostedPurchaseIntentService = class {
273
+ constructor(transport, sessionId) {
274
+ this.transport = transport;
275
+ this.sessionPath = hostedSessionPath(sessionId);
276
+ this.resolver = new PurchaseIntentResolver({ confirmation: this });
277
+ }
278
+ prepare(request, options) {
279
+ return chunkDQEXY542_js.safe(
280
+ this.transport.post(
281
+ `${this.sessionPath}/${PURCHASE_INTENT_ROUTE.PREPARE}`,
282
+ request,
283
+ {
284
+ idempotencyKey: request.idempotency_key,
285
+ signal: options?.signal,
286
+ timeoutMs: options?.timeoutMs
287
+ }
288
+ )
289
+ );
290
+ }
291
+ confirm(_intentId, request, options) {
292
+ return chunkDQEXY542_js.safe(
293
+ this.transport.post(
294
+ `${this.sessionPath}/${PURCHASE_INTENT_ROUTE.CONFIRM}`,
295
+ request,
296
+ {
297
+ idempotencyKey: request.idempotency_key,
298
+ signal: options?.signal,
299
+ timeoutMs: options?.timeoutMs
300
+ }
301
+ )
302
+ );
303
+ }
304
+ replay(options) {
305
+ return chunkDQEXY542_js.safe(
306
+ this.transport.get(
307
+ `${this.sessionPath}/${PURCHASE_INTENT_ROUTE.REPLAY}`,
308
+ {
309
+ signal: options?.signal,
310
+ timeoutMs: options?.timeoutMs
311
+ }
312
+ )
313
+ );
314
+ }
315
+ submitAction(_intentId, attemptId, request, options) {
316
+ const normalized = attemptId.trim();
317
+ if (!normalized) throw new TypeError("attemptId must not be empty");
318
+ return chunkDQEXY542_js.safe(
319
+ this.transport.post(
320
+ `${this.sessionPath}/${PURCHASE_INTENT_ROUTE.ATTEMPTS}/${encodeURIComponent(normalized)}/${PURCHASE_INTENT_ROUTE.ACTIONS}`,
321
+ request,
322
+ {
323
+ signal: options?.signal,
324
+ timeoutMs: options?.timeoutMs
325
+ }
326
+ )
327
+ );
328
+ }
329
+ resolve(intentId, options) {
330
+ return this.resolver.resolve(intentId, options);
331
+ }
332
+ };
5
333
 
6
334
  // src/store-credit.ts
7
335
  var StoreCreditQueries = class {
@@ -13,18 +341,19 @@ var StoreCreditQueries = class {
13
341
  * currency. Requires a signed-in customer; returns an error otherwise.
14
342
  */
15
343
  async balance() {
16
- return chunkJSWJ4XDE_js.safe(this.client.get("/api/v1/store-credit/balance"));
344
+ return chunkDQEXY542_js.safe(this.client.get("/api/v1/store-credit/balance"));
17
345
  }
18
346
  };
19
347
 
20
348
  // src/internal/analytics-session.ts
21
349
  var ANALYTICS_SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
22
- var ANALYTICS_SESSION_STORAGE_KEY = "cimplify_analytics_session";
23
- var ANALYTICS_SESSION_STATE_VERSION = 1;
350
+ var ANALYTICS_SESSION_STORAGE_KEY_PREFIX = "cimplify_analytics_session:v2:";
351
+ var ANALYTICS_SESSION_STATE_VERSION = 2;
24
352
  var MICROSECONDS_PER_MILLISECOND = 1e3;
25
353
  var MICROSECONDS_PER_SECOND = 1e6;
26
354
  var AnalyticsSessionCoordinator = class {
27
- constructor() {
355
+ constructor(identity) {
356
+ this.identity = identity;
28
357
  this.fallbackState = null;
29
358
  }
30
359
  beginEvent(nowMs = Date.now()) {
@@ -62,7 +391,7 @@ var AnalyticsSessionCoordinator = class {
62
391
  reset() {
63
392
  this.fallbackState = null;
64
393
  try {
65
- sessionStorage.removeItem(ANALYTICS_SESSION_STORAGE_KEY);
394
+ sessionStorage.removeItem(this.storageKey());
66
395
  } catch {
67
396
  }
68
397
  }
@@ -72,11 +401,11 @@ var AnalyticsSessionCoordinator = class {
72
401
  }
73
402
  read() {
74
403
  try {
75
- const serialized = sessionStorage.getItem(ANALYTICS_SESSION_STORAGE_KEY);
404
+ const serialized = sessionStorage.getItem(this.storageKey());
76
405
  if (!serialized) return this.fallbackState;
77
406
  const state = JSON.parse(serialized);
78
407
  if (!isPersistedSession(state)) {
79
- sessionStorage.removeItem(ANALYTICS_SESSION_STORAGE_KEY);
408
+ sessionStorage.removeItem(this.storageKey());
80
409
  return this.fallbackState;
81
410
  }
82
411
  this.fallbackState = state;
@@ -88,10 +417,13 @@ var AnalyticsSessionCoordinator = class {
88
417
  write(state) {
89
418
  this.fallbackState = state;
90
419
  try {
91
- sessionStorage.setItem(ANALYTICS_SESSION_STORAGE_KEY, JSON.stringify(state));
420
+ sessionStorage.setItem(this.storageKey(), JSON.stringify(state));
92
421
  } catch {
93
422
  }
94
423
  }
424
+ storageKey() {
425
+ return `${ANALYTICS_SESSION_STORAGE_KEY_PREFIX}${encodeURIComponent(this.identity)}`;
426
+ }
95
427
  };
96
428
  function newSession(nowMs) {
97
429
  return {
@@ -158,11 +490,127 @@ function generateSessionId() {
158
490
 
159
491
  // src/activity.ts
160
492
  var VISITOR_ID_STORAGE_KEY = "cimplify_visitor_id";
493
+ var ACTIVITY_RETRY_POLICY = {
494
+ maxRetries: 1,
495
+ jitterRatio: 0.2
496
+ };
497
+ var PAGE_VIEW_TRACKER_KEY = /* @__PURE__ */ Symbol.for("@cimplify/sdk.page-view-tracker-hub");
498
+ var PAGE_VIEW_TRACKER_VERSION = 2;
499
+ function trackerHost() {
500
+ return window;
501
+ }
502
+ function currentWindowTrackerHub() {
503
+ const candidate = trackerHost()[PAGE_VIEW_TRACKER_KEY];
504
+ return candidate?.version === PAGE_VIEW_TRACKER_VERSION && candidate.registrations instanceof Map && typeof candidate.recordAll === "function" ? candidate : null;
505
+ }
506
+ function weakReference(target) {
507
+ const WeakReference = globalThis.WeakRef;
508
+ if (WeakReference) return new WeakReference(target);
509
+ return { deref: () => target };
510
+ }
511
+ function latestLiveOwner(registration) {
512
+ let latest = null;
513
+ for (const [token, reference] of registration.owners) {
514
+ const owner = reference.deref();
515
+ if (!owner || owner.released) {
516
+ registration.owners.delete(token);
517
+ } else {
518
+ latest = owner;
519
+ }
520
+ }
521
+ return latest;
522
+ }
523
+ function recordRegistration(registration, path) {
524
+ if (path === registration.lastPath) return;
525
+ const owner = registration.activeOwner && !registration.activeOwner.released ? registration.activeOwner : latestLiveOwner(registration);
526
+ if (!owner) return;
527
+ registration.activeOwner = owner;
528
+ registration.lastPath = path;
529
+ owner.recorder(path);
530
+ }
531
+ function createWindowTrackerHub() {
532
+ const originalPushState = window.history.pushState;
533
+ const originalReplaceState = window.history.replaceState;
534
+ const hub = {
535
+ version: PAGE_VIEW_TRACKER_VERSION,
536
+ registrations: /* @__PURE__ */ new Map(),
537
+ originalPushState,
538
+ originalReplaceState
539
+ };
540
+ hub.recordAll = () => {
541
+ const path = window.location.pathname;
542
+ for (const registration of hub.registrations.values()) {
543
+ recordRegistration(registration, path);
544
+ }
545
+ };
546
+ hub.patchedPushState = function(...args) {
547
+ originalPushState.apply(this, args);
548
+ hub.recordAll();
549
+ };
550
+ hub.patchedReplaceState = function(...args) {
551
+ originalReplaceState.apply(this, args);
552
+ hub.recordAll();
553
+ };
554
+ hub.popstateListener = () => hub.recordAll();
555
+ window.history.pushState = hub.patchedPushState;
556
+ window.history.replaceState = hub.patchedReplaceState;
557
+ window.addEventListener("popstate", hub.popstateListener);
558
+ trackerHost()[PAGE_VIEW_TRACKER_KEY] = hub;
559
+ return hub;
560
+ }
561
+ function pageViewTrackerIdentity(client) {
562
+ return JSON.stringify([client.getBaseUrl(), client.getPublicKey().trim()]);
563
+ }
564
+ function acquireWindowPageViewTracker(identity, recorder) {
565
+ const hub = currentWindowTrackerHub() ?? createWindowTrackerHub();
566
+ let registration = hub.registrations.get(identity);
567
+ const isNewIdentity = !registration;
568
+ if (!registration) {
569
+ registration = { owners: /* @__PURE__ */ new Map(), activeOwner: null, lastPath: null };
570
+ hub.registrations.set(identity, registration);
571
+ }
572
+ const owner = {
573
+ token: Symbol(identity),
574
+ recorder,
575
+ released: false
576
+ };
577
+ registration.owners.set(owner.token, weakReference(owner));
578
+ registration.activeOwner = owner;
579
+ if (isNewIdentity) {
580
+ recordRegistration(registration, window.location.pathname);
581
+ }
582
+ let released = false;
583
+ return () => {
584
+ if (released) return;
585
+ released = true;
586
+ owner.released = true;
587
+ registration.owners.delete(owner.token);
588
+ if (registration.activeOwner === owner) {
589
+ registration.activeOwner = latestLiveOwner(registration);
590
+ }
591
+ if (registration.activeOwner) return;
592
+ if (hub.registrations.get(identity) === registration) {
593
+ hub.registrations.delete(identity);
594
+ }
595
+ if (hub.registrations.size !== 0) return;
596
+ window.removeEventListener("popstate", hub.popstateListener);
597
+ if (window.history.pushState === hub.patchedPushState) {
598
+ window.history.pushState = hub.originalPushState;
599
+ }
600
+ if (window.history.replaceState === hub.patchedReplaceState) {
601
+ window.history.replaceState = hub.originalReplaceState;
602
+ }
603
+ if (currentWindowTrackerHub() === hub) {
604
+ delete trackerHost()[PAGE_VIEW_TRACKER_KEY];
605
+ }
606
+ };
607
+ }
161
608
  var ActivityService = class {
162
609
  constructor(client) {
163
610
  this.client = client;
164
- this.sessions = new AnalyticsSessionCoordinator();
611
+ this.localGeneration = 0;
165
612
  this.bootstrapRequest = null;
613
+ this.sessions = this.createSessionCoordinator();
166
614
  }
167
615
  /**
168
616
  * Record session start (landing, referrer, UTM, device). Fire-and-forget,
@@ -172,7 +620,7 @@ var ActivityService = class {
172
620
  startSession() {
173
621
  if (typeof window === "undefined") return;
174
622
  const session = this.sessions.beginEvent();
175
- void this.bootstrapSession(session).catch(() => {
623
+ void this.bootstrapSession(session, false, this.captureGeneration()).catch(() => {
176
624
  });
177
625
  }
178
626
  /** Rebind an existing analytics session after the server authenticates a customer. */
@@ -180,13 +628,19 @@ var ActivityService = class {
180
628
  if (typeof window === "undefined") return;
181
629
  const session = this.sessions.current();
182
630
  if (!session) return;
183
- void this.bootstrapSession(session, true).catch(() => {
631
+ void this.bootstrapSession(session, true, this.captureGeneration()).catch(() => {
184
632
  });
185
633
  }
186
634
  /** A signed-out browser starts a fresh anonymous analytics session. */
187
635
  resetSession() {
636
+ this.invalidatePendingActivity();
188
637
  this.sessions.reset();
189
- this.bootstrapRequest = null;
638
+ }
639
+ /** Drop the old storefront's analytics state before binding this service to the new one. */
640
+ resetForStorefrontIdentityChange() {
641
+ this.invalidatePendingActivity();
642
+ this.sessions.reset();
643
+ this.sessions = this.createSessionCoordinator();
190
644
  }
191
645
  buildSessionPayload(session) {
192
646
  const params = new URLSearchParams(window.location.search);
@@ -208,6 +662,7 @@ var ActivityService = class {
208
662
  utm_campaign: utm("campaign"),
209
663
  utm_term: utm("term"),
210
664
  utm_content: utm("content"),
665
+ social_commerce_ref: params.get("social_commerce_ref") || void 0,
211
666
  screen_width: window.screen?.width,
212
667
  screen_height: window.screen?.height,
213
668
  viewport_width: window.innerWidth,
@@ -262,7 +717,7 @@ var ActivityService = class {
262
717
  this.sendEvents([event]);
263
718
  }
264
719
  async getState() {
265
- return chunkJSWJ4XDE_js.safe(this.client.get("/api/v1/activity/state"));
720
+ return chunkDQEXY542_js.safe(this.client.get("/api/v1/activity/state"));
266
721
  }
267
722
  async getRecommendations(options) {
268
723
  const params = new URLSearchParams();
@@ -270,10 +725,10 @@ var ActivityService = class {
270
725
  if (options?.limit) params.set("limit", String(options.limit));
271
726
  const query = params.toString();
272
727
  const path = query ? `/api/v1/activity/recommendations?${query}` : "/api/v1/activity/recommendations";
273
- return chunkJSWJ4XDE_js.safe(this.client.get(path));
728
+ return chunkDQEXY542_js.safe(this.client.get(path));
274
729
  }
275
730
  async dismissMessage(code) {
276
- return chunkJSWJ4XDE_js.safe(
731
+ return chunkDQEXY542_js.safe(
277
732
  this.client.post("/api/v1/activity/messages/dismiss", { code })
278
733
  );
279
734
  }
@@ -281,32 +736,12 @@ var ActivityService = class {
281
736
  autoTrackPageViews() {
282
737
  if (typeof window === "undefined") return () => {
283
738
  };
284
- let lastPath = null;
285
- const record = () => {
286
- const path = window.location.pathname;
287
- if (path === lastPath) return;
288
- lastPath = path;
739
+ return acquireWindowPageViewTracker(pageViewTrackerIdentity(this.client), (path) => {
289
740
  try {
290
741
  this.trackPageView({ pagePath: path });
291
742
  } catch {
292
743
  }
293
- };
294
- const { pushState, replaceState } = window.history;
295
- window.history.pushState = function(...args) {
296
- pushState.apply(this, args);
297
- record();
298
- };
299
- window.history.replaceState = function(...args) {
300
- replaceState.apply(this, args);
301
- record();
302
- };
303
- window.addEventListener("popstate", record);
304
- record();
305
- return () => {
306
- window.history.pushState = pushState;
307
- window.history.replaceState = replaceState;
308
- window.removeEventListener("popstate", record);
309
- };
744
+ });
310
745
  }
311
746
  sendEvents(events, session) {
312
747
  if (typeof window === "undefined") return;
@@ -314,29 +749,59 @@ var ActivityService = class {
314
749
  });
315
750
  }
316
751
  async postEvents(events, session) {
317
- await this.bootstrapSession(session);
318
- await this.client.post("/api/v1/activity/events", {
319
- analytics_session_id: session.id,
320
- occurred_at: session.occurredAt,
321
- events
322
- });
752
+ const generation = this.captureGeneration();
753
+ await this.bootstrapSession(session, false, generation);
754
+ if (!this.isCurrentGeneration(generation)) return;
755
+ await this.client.post(
756
+ "/api/v1/activity/events",
757
+ {
758
+ analytics_session_id: session.id,
759
+ occurred_at: session.occurredAt,
760
+ events
761
+ },
762
+ { retryPolicy: ACTIVITY_RETRY_POLICY }
763
+ );
323
764
  }
324
- bootstrapSession(session, force = false) {
765
+ bootstrapSession(session, force = false, generation = this.captureGeneration()) {
325
766
  if (!force && !session.requiresBootstrap) return Promise.resolve();
326
- if (!force && this.bootstrapRequest?.sessionId === session.id) {
767
+ if (!force && this.bootstrapRequest?.sessionId === session.id && sameActivityGeneration(this.bootstrapRequest.generation, generation)) {
327
768
  return this.bootstrapRequest.promise;
328
769
  }
329
- const promise = this.client.post("/api/v1/activity/session", this.buildSessionPayload(session)).then(() => {
330
- this.sessions.markBootstrapped(session.id);
770
+ const promise = this.client.post("/api/v1/activity/session", this.buildSessionPayload(session), {
771
+ retryPolicy: ACTIVITY_RETRY_POLICY
772
+ }).then(() => {
773
+ if (this.isCurrentGeneration(generation)) {
774
+ this.sessions.markBootstrapped(session.id);
775
+ }
331
776
  }).finally(() => {
332
777
  if (this.bootstrapRequest?.promise === promise) {
333
778
  this.bootstrapRequest = null;
334
779
  }
335
780
  });
336
- this.bootstrapRequest = { sessionId: session.id, promise };
781
+ this.bootstrapRequest = { sessionId: session.id, generation, promise };
337
782
  return promise;
338
783
  }
784
+ captureGeneration() {
785
+ return {
786
+ local: this.localGeneration,
787
+ publicKey: this.client.getPublicKeyEpoch(),
788
+ session: this.client.getSessionEpoch()
789
+ };
790
+ }
791
+ isCurrentGeneration(generation) {
792
+ return sameActivityGeneration(generation, this.captureGeneration());
793
+ }
794
+ invalidatePendingActivity() {
795
+ this.localGeneration += 1;
796
+ this.bootstrapRequest = null;
797
+ }
798
+ createSessionCoordinator() {
799
+ return new AnalyticsSessionCoordinator(pageViewTrackerIdentity(this.client));
800
+ }
339
801
  };
802
+ function sameActivityGeneration(left, right) {
803
+ return left.local === right.local && left.publicKey === right.publicKey && left.session === right.session;
804
+ }
340
805
  function currentPath() {
341
806
  if (typeof window !== "undefined") {
342
807
  return window.location.pathname;
@@ -369,16 +834,16 @@ var SubscriptionService = class {
369
834
  this.client = client;
370
835
  }
371
836
  async list() {
372
- return chunkJSWJ4XDE_js.safe(this.client.get("/api/v1/subscriptions"));
837
+ return chunkDQEXY542_js.safe(this.client.get("/api/v1/subscriptions"));
373
838
  }
374
839
  async get(id) {
375
840
  const encodedId = encodeURIComponent(id);
376
- return chunkJSWJ4XDE_js.safe(this.client.get(`/api/v1/subscriptions/${encodedId}`));
841
+ return chunkDQEXY542_js.safe(this.client.get(`/api/v1/subscriptions/${encodedId}`));
377
842
  }
378
843
  async cancel(id, reason, opts) {
379
844
  const encodedId = encodeURIComponent(id);
380
- const idempotencyKey = opts?.idempotencyKey ?? chunkJSWJ4XDE_js.generateIdempotencyKey();
381
- return chunkJSWJ4XDE_js.safe(
845
+ const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
846
+ return chunkDQEXY542_js.safe(
382
847
  this.client.post(
383
848
  `/api/v1/subscriptions/${encodedId}/cancel`,
384
849
  { reason },
@@ -388,20 +853,20 @@ var SubscriptionService = class {
388
853
  }
389
854
  async pause(id) {
390
855
  const encodedId = encodeURIComponent(id);
391
- return chunkJSWJ4XDE_js.safe(
856
+ return chunkDQEXY542_js.safe(
392
857
  this.client.post(`/api/v1/subscriptions/${encodedId}/pause`)
393
858
  );
394
859
  }
395
860
  async resume(id) {
396
861
  const encodedId = encodeURIComponent(id);
397
- return chunkJSWJ4XDE_js.safe(
862
+ return chunkDQEXY542_js.safe(
398
863
  this.client.post(`/api/v1/subscriptions/${encodedId}/resume`)
399
864
  );
400
865
  }
401
866
  async skipNextRenewal(id, opts) {
402
867
  const encodedId = encodeURIComponent(id);
403
- const idempotencyKey = opts?.idempotencyKey ?? chunkJSWJ4XDE_js.generateIdempotencyKey();
404
- return chunkJSWJ4XDE_js.safe(
868
+ const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
869
+ return chunkDQEXY542_js.safe(
405
870
  this.client.post(
406
871
  `/api/v1/subscriptions/${encodedId}/skip`,
407
872
  void 0,
@@ -416,14 +881,14 @@ var UploadService = class {
416
881
  constructor(client) {
417
882
  this.client = client;
418
883
  }
419
- async init(filename, contentType, sizeBytes, opts) {
420
- const idempotencyKey = opts?.idempotencyKey ?? chunkJSWJ4XDE_js.generateIdempotencyKey();
421
- return chunkJSWJ4XDE_js.safe(
884
+ async init(filename, contentType2, sizeBytes, opts) {
885
+ const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
886
+ return chunkDQEXY542_js.safe(
422
887
  this.client.post(
423
888
  "/api/v1/uploads/init",
424
889
  {
425
890
  filename,
426
- content_type: contentType,
891
+ content_type: contentType2,
427
892
  size_bytes: sizeBytes
428
893
  },
429
894
  { idempotencyKey }
@@ -431,7 +896,7 @@ var UploadService = class {
431
896
  );
432
897
  }
433
898
  async confirm(uploadId) {
434
- return chunkJSWJ4XDE_js.safe(
899
+ return chunkDQEXY542_js.safe(
435
900
  this.client.post("/api/v1/uploads/confirm", {
436
901
  upload_id: uploadId
437
902
  })
@@ -450,7 +915,7 @@ var UploadService = class {
450
915
  if (!uploadResponse.ok) {
451
916
  return {
452
917
  ok: false,
453
- error: new chunkJG5CQNCB_js.CimplifyError(
918
+ error: new chunkSFUJMS5E_js.CimplifyError(
454
919
  "UPLOAD_FAILED",
455
920
  `Upload failed with status ${uploadResponse.status}`,
456
921
  true
@@ -460,7 +925,7 @@ var UploadService = class {
460
925
  } catch (error) {
461
926
  return {
462
927
  ok: false,
463
- error: new chunkJG5CQNCB_js.CimplifyError(
928
+ error: new chunkSFUJMS5E_js.CimplifyError(
464
929
  "UPLOAD_FAILED",
465
930
  error instanceof Error ? error.message : "Upload failed",
466
931
  true
@@ -477,7 +942,7 @@ var PlacesService = class {
477
942
  this.client = client;
478
943
  }
479
944
  async autocomplete(input, sessionToken) {
480
- return chunkJSWJ4XDE_js.safe(
945
+ return chunkDQEXY542_js.safe(
481
946
  this.client.post("/api/v1/places/autocomplete", {
482
947
  input,
483
948
  sessionToken
@@ -485,7 +950,7 @@ var PlacesService = class {
485
950
  );
486
951
  }
487
952
  async details(placeId, sessionToken) {
488
- return chunkJSWJ4XDE_js.safe(
953
+ return chunkDQEXY542_js.safe(
489
954
  this.client.post("/api/v1/places/details", {
490
955
  place_id: placeId,
491
956
  sessionToken
@@ -504,7 +969,7 @@ var DeliveryService = class {
504
969
  params.set("dropoff_lat", String(dropoffLat));
505
970
  params.set("dropoff_lng", String(dropoffLng));
506
971
  if (country) params.set("country", country);
507
- return chunkJSWJ4XDE_js.safe(this.client.get(`/api/v1/delivery/fee?${params.toString()}`));
972
+ return chunkDQEXY542_js.safe(this.client.get(`/api/v1/delivery/fee?${params.toString()}`));
508
973
  }
509
974
  async getOptions(queryOrLatitude, longitude, legacyOptions) {
510
975
  const query = typeof queryOrLatitude === "number" ? {
@@ -518,12 +983,105 @@ var DeliveryService = class {
518
983
  if (query.country) params.set("country", query.country);
519
984
  if (query.currency) params.set("currency", query.currency);
520
985
  if (query.orderValue != null) params.set("order_value", String(query.orderValue));
521
- return chunkJSWJ4XDE_js.safe(
986
+ return chunkDQEXY542_js.safe(
522
987
  this.client.get(`/api/v1/delivery/options?${params.toString()}`)
523
988
  );
524
989
  }
525
990
  };
526
991
 
992
+ // src/chat-wire.ts
993
+ function senderType(message) {
994
+ if (message.sender_side === "account") return "customer";
995
+ if (message.sender_side === "system") return "system";
996
+ return message.authorship === "ai" ? "bot" : "agent";
997
+ }
998
+ function contentType(message) {
999
+ if (message.content_type === "rich") {
1000
+ return message.structured_content?.["kind"] === "reaction" ? "reaction" : "interactive";
1001
+ }
1002
+ if (message.content_type === "media") {
1003
+ const mime = message.media[0]?.mime_type ?? "";
1004
+ if (mime.startsWith("image/")) return "image";
1005
+ if (mime.startsWith("video/")) return "video";
1006
+ if (mime.startsWith("audio/")) return "audio";
1007
+ return "document";
1008
+ }
1009
+ return "text";
1010
+ }
1011
+ function unresolvedReply(id) {
1012
+ return { id, sender_type: "system", preview: "Earlier message" };
1013
+ }
1014
+ function chatMessagesFromWire(messages) {
1015
+ const converted = messages.map((message) => ({
1016
+ id: message.id,
1017
+ sequence: message.sequence,
1018
+ sender_type: senderType(message),
1019
+ content: message.content,
1020
+ content_type: contentType(message),
1021
+ attachments: Array.isArray(message.media) ? message.media : [],
1022
+ metadata: message.structured_content ?? {},
1023
+ reply_to: message.reply_to_message_id ? unresolvedReply(message.reply_to_message_id) : void 0,
1024
+ edited_at: message.edited_at,
1025
+ deleted_at: message.deleted_at,
1026
+ created_at: message.created_at
1027
+ }));
1028
+ const byId = new Map(converted.map((message) => [message.id, message]));
1029
+ return converted.map((message) => {
1030
+ const targetId = message.reply_to?.id;
1031
+ const target = targetId ? byId.get(targetId) : void 0;
1032
+ if (!targetId || !target) return message;
1033
+ return {
1034
+ ...message,
1035
+ reply_to: {
1036
+ id: targetId,
1037
+ sender_type: target.sender_type,
1038
+ preview: chunkXREIZ56K_js.messagePreview(target.content)
1039
+ }
1040
+ };
1041
+ });
1042
+ }
1043
+ function chatMessageFromWire(message) {
1044
+ return chatMessagesFromWire([message])[0];
1045
+ }
1046
+ function chatRealtimeUpdateFromWire(envelope) {
1047
+ if (envelope.type === "message_created" && envelope.data) {
1048
+ return {
1049
+ kind: "message",
1050
+ message: chatMessageFromWire(envelope.data)
1051
+ };
1052
+ }
1053
+ if (envelope.type === "message_updated" && envelope.data) {
1054
+ return {
1055
+ kind: "message_updated",
1056
+ message: chatMessageFromWire(envelope.data)
1057
+ };
1058
+ }
1059
+ if (envelope.type === "read" && envelope.data) {
1060
+ const read = envelope.data;
1061
+ if (read.reader_side === "business" && typeof read.occurred_at === "string") {
1062
+ return { kind: "business_read", occurredAt: read.occurred_at };
1063
+ }
1064
+ return { kind: "none" };
1065
+ }
1066
+ if (envelope.type === "history_imported") {
1067
+ return { kind: "reload" };
1068
+ }
1069
+ if (envelope.type === "ai_mode_changed" && envelope.data) {
1070
+ const mode = envelope.data.mode;
1071
+ if (mode === "auto" || mode === "assist" || mode === "off") {
1072
+ return { kind: "ai_mode", mode };
1073
+ }
1074
+ return { kind: "none" };
1075
+ }
1076
+ if (envelope.type === "handoff_requested") {
1077
+ return { kind: "handoff" };
1078
+ }
1079
+ if (envelope.type === "resync") {
1080
+ return { kind: "resync", latestSequence: envelope.latest_sequence };
1081
+ }
1082
+ return { kind: "none" };
1083
+ }
1084
+
527
1085
  // src/internal/events.ts
528
1086
  var MUTATION_ERROR = "mutation:error";
529
1087
  var AUTH_TOKEN_CHANGED = "auth:token-changed";
@@ -586,16 +1144,18 @@ var SupportService = class {
586
1144
  * Claim the anonymous session for the verified Link identity: the server
587
1145
  * verifies the access token, rotates the session token (adopted here, so
588
1146
  * every subsequent request rides the bound session), folds the guest cart
589
- * in, and adopts or merges the widget conversation.
1147
+ * in, and opens or reuses the account's canonical direct Chat thread.
590
1148
  *
591
1149
  * Idempotent and single-flight: concurrent calls share one request, and a
592
1150
  * token already claimed by this instance short-circuits without a network
593
- * round trip. Emits `session:claimed` on the client's event channel.
1151
+ * round trip. A session already bound to another Link account is rejected
1152
+ * rather than transferring that account's cart. Emits `session:claimed` on
1153
+ * the client's event channel.
594
1154
  */
595
1155
  async claimConversation() {
596
1156
  const accessToken = this.client.getAccessToken();
597
1157
  if (!accessToken) {
598
- return chunkJSWJ4XDE_js.err(new chunkJG5CQNCB_js.CimplifyError("not_authenticated", "No access token to claim with"));
1158
+ return chunkDQEXY542_js.err(new chunkSFUJMS5E_js.CimplifyError("not_authenticated", "No access token to claim with"));
599
1159
  }
600
1160
  if (this.claimInFlight) {
601
1161
  if (this.claimInFlightToken === accessToken) return this.claimInFlight;
@@ -603,12 +1163,12 @@ var SupportService = class {
603
1163
  return this.claimConversation();
604
1164
  }
605
1165
  if (this.lastClaimedAccessToken === accessToken) {
606
- return chunkJSWJ4XDE_js.err(new chunkJG5CQNCB_js.CimplifyError("already_claimed", "Session already claimed for this token"));
1166
+ return chunkDQEXY542_js.err(new chunkSFUJMS5E_js.CimplifyError("already_claimed", "Session already claimed for this token"));
607
1167
  }
608
1168
  const epoch = this.client.getSessionEpoch();
609
1169
  this.claimInFlightToken = accessToken;
610
1170
  this.claimInFlight = (async () => {
611
- const result = await chunkJSWJ4XDE_js.safe(
1171
+ const result = await chunkDQEXY542_js.safe(
612
1172
  this.client.post(
613
1173
  "/api/v1/support/conversation/claim",
614
1174
  { access_token: accessToken },
@@ -617,8 +1177,8 @@ var SupportService = class {
617
1177
  );
618
1178
  if (result.ok) {
619
1179
  if (this.client.getSessionEpoch() !== epoch) {
620
- return chunkJSWJ4XDE_js.err(
621
- new chunkJG5CQNCB_js.CimplifyError("session_cleared", "Session was cleared during the claim")
1180
+ return chunkDQEXY542_js.err(
1181
+ new chunkSFUJMS5E_js.CimplifyError("session_cleared", "Session was cleared during the claim")
622
1182
  );
623
1183
  }
624
1184
  this.lastClaimedAccessToken = accessToken;
@@ -627,7 +1187,7 @@ var SupportService = class {
627
1187
  this.client.publishSessionClaimed(this.claimedName);
628
1188
  this.client.events.emit(SESSION_CLAIMED, {
629
1189
  sessionToken: result.value.session_token,
630
- threadId: result.value.thread_id ?? null,
1190
+ threadId: result.value.thread.id,
631
1191
  customerName: result.value.customer_name
632
1192
  });
633
1193
  }
@@ -640,30 +1200,54 @@ var SupportService = class {
640
1200
  this.claimInFlightToken = null;
641
1201
  }
642
1202
  }
1203
+ /**
1204
+ * Retry verified external-history discovery for the current storefront.
1205
+ * Launch scope is intentionally WhatsApp-only because it is the registered
1206
+ * bidirectional transport. Results are per envelope and omit provider ids,
1207
+ * contact values, account ids, and message bodies.
1208
+ */
1209
+ async claimExternalChats() {
1210
+ if (!this.client.getAccessToken()) {
1211
+ return chunkDQEXY542_js.err(new chunkSFUJMS5E_js.CimplifyError("not_authenticated", "No access token to claim with"));
1212
+ }
1213
+ return chunkDQEXY542_js.safe(
1214
+ (async () => {
1215
+ const businessId = await this.client.resolveBusinessId();
1216
+ return this.client.post("/v1/me/chats/external-claims", {
1217
+ business_id: businessId
1218
+ });
1219
+ })()
1220
+ );
1221
+ }
643
1222
  /**
644
1223
  * Peek at the conversation without creating one: resolves the existing
645
1224
  * thread (or null) so a page load can hydrate history without littering
646
1225
  * the inbox with empty threads.
647
1226
  */
648
1227
  async peekConversation() {
649
- const result = await chunkJSWJ4XDE_js.safe(
1228
+ const result = await chunkDQEXY542_js.safe(
650
1229
  this.client.get("/api/v1/support/conversation")
651
1230
  );
652
1231
  if (!result.ok) return result;
653
- if (!result.value.conversation) return chunkJSWJ4XDE_js.ok(null);
654
- return chunkJSWJ4XDE_js.ok({
1232
+ if (!result.value.conversation) return chunkDQEXY542_js.ok(null);
1233
+ return chunkDQEXY542_js.ok({
655
1234
  conversation: result.value.conversation,
656
- messages: result.value.messages
1235
+ messages: chatMessagesFromWire(result.value.messages)
657
1236
  });
658
1237
  }
659
1238
  /** Open (or resume) the customer's widget conversation. */
660
1239
  async openConversation(opts) {
661
- const idempotencyKey = opts?.idempotencyKey ?? chunkJSWJ4XDE_js.generateIdempotencyKey();
662
- return chunkJSWJ4XDE_js.safe(
1240
+ const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
1241
+ const result = await chunkDQEXY542_js.safe(
663
1242
  this.client.post("/api/v1/support/conversation", void 0, {
664
1243
  idempotencyKey
665
1244
  })
666
1245
  );
1246
+ if (!result.ok) return result;
1247
+ return chunkDQEXY542_js.ok({
1248
+ conversation: result.value.conversation,
1249
+ messages: chatMessagesFromWire(result.value.messages)
1250
+ });
667
1251
  }
668
1252
  /**
669
1253
  * Send a message in the active conversation.
@@ -674,8 +1258,8 @@ var SupportService = class {
674
1258
  * server resolves it thread-scoped and echoes the quote as `reply_to`.
675
1259
  */
676
1260
  async sendMessage(content, opts) {
677
- const idempotencyKey = opts?.idempotencyKey ?? chunkJSWJ4XDE_js.generateIdempotencyKey();
678
- return chunkJSWJ4XDE_js.safe(
1261
+ const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
1262
+ const result = await chunkDQEXY542_js.safe(
679
1263
  this.client.post(
680
1264
  "/api/v1/support/conversation/messages",
681
1265
  {
@@ -687,6 +1271,7 @@ var SupportService = class {
687
1271
  { idempotencyKey }
688
1272
  )
689
1273
  );
1274
+ return result.ok ? chunkDQEXY542_js.ok(chatMessageFromWire(result.value.message)) : result;
690
1275
  }
691
1276
  /**
692
1277
  * Poll for messages. `after` pages forward (live updates); `before` pages
@@ -694,26 +1279,32 @@ var SupportService = class {
694
1279
  */
695
1280
  async getMessages(options = {}) {
696
1281
  const params = new URLSearchParams();
697
- if (options.after) params.set("after", options.after);
698
- if (options.before) params.set("before", options.before);
1282
+ if (options.afterSequence !== void 0)
1283
+ params.set("after_sequence", String(options.afterSequence));
1284
+ if (options.beforeSequence !== void 0)
1285
+ params.set("before_sequence", String(options.beforeSequence));
699
1286
  if (options.limit) params.set("limit", String(options.limit));
700
1287
  const query = params.toString();
701
1288
  const path = query ? `/api/v1/support/conversation/messages?${query}` : "/api/v1/support/conversation/messages";
702
- return chunkJSWJ4XDE_js.safe(this.client.get(path));
1289
+ const result = await chunkDQEXY542_js.safe(this.client.get(path));
1290
+ return result.ok ? chunkDQEXY542_js.ok(chatMessagesFromWire(result.value)) : result;
703
1291
  }
704
1292
  /** React to a message in the active conversation by its `ChatMessage.id`. */
705
1293
  async reactToMessage(messageId, emoji) {
706
- return chunkJSWJ4XDE_js.safe(
1294
+ const clientId = chunkDQEXY542_js.generateIdempotencyKey();
1295
+ const result = await chunkDQEXY542_js.safe(
707
1296
  this.client.post(
708
1297
  `/api/v1/support/conversation/messages/${messageId}/reactions`,
709
- { emoji }
1298
+ { emoji, client_id: clientId },
1299
+ { idempotencyKey: clientId }
710
1300
  )
711
1301
  );
1302
+ return result.ok ? chunkDQEXY542_js.ok(chatMessageFromWire(result.value.message)) : result;
712
1303
  }
713
1304
  /** Ask for a person: flags the thread for the merchant's inbox. */
714
1305
  async requestHuman(opts) {
715
- const idempotencyKey = opts?.idempotencyKey ?? chunkJSWJ4XDE_js.generateIdempotencyKey();
716
- return chunkJSWJ4XDE_js.safe(
1306
+ const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
1307
+ return chunkDQEXY542_js.safe(
717
1308
  this.client.post(
718
1309
  "/api/v1/support/conversation/handoff",
719
1310
  void 0,
@@ -723,456 +1314,108 @@ var SupportService = class {
723
1314
  }
724
1315
  };
725
1316
 
726
- // src/payment-authorization/providers/paystack.ts
727
- async function openPaystackPopup(options, signal) {
728
- if (typeof window === "undefined") {
729
- return { success: false, error: "PROVIDER_UNAVAILABLE" };
730
- }
731
- let PaystackPop;
732
- try {
733
- const imported = await import('@paystack/inline-js');
734
- PaystackPop = imported.default;
735
- } catch {
736
- return { success: false, error: "PROVIDER_UNAVAILABLE" };
737
- }
738
- return new Promise((resolve) => {
739
- let settled = false;
740
- let providerStarted = false;
741
- const resolveOnce = (result) => {
742
- if (settled) {
743
- return;
744
- }
745
- settled = true;
746
- if (signal) {
747
- signal.removeEventListener("abort", onAbort);
748
- }
749
- resolve(result);
750
- };
751
- const onAbort = () => {
752
- resolveOnce({
753
- success: false,
754
- // Once Paystack owns the transaction, cancellation is ambiguous. The
755
- // adapter converts this into submitted so Cimplify polling decides.
756
- error: providerStarted ? "SUBMISSION_UNKNOWN" : "CANCELLED"
757
- });
758
- };
759
- if (signal?.aborted) {
760
- resolveOnce({ success: false, error: "CANCELLED" });
761
- return;
762
- }
763
- if (signal) {
764
- signal.addEventListener("abort", onAbort, { once: true });
765
- }
766
- try {
767
- const popup = new PaystackPop();
768
- popup.newTransaction({
769
- key: options.key,
770
- email: options.email,
771
- amount: options.amount,
772
- currency: options.currency,
773
- reference: options.reference,
774
- accessCode: options.accessCode,
775
- onSuccess: (transaction) => {
776
- resolveOnce({
777
- success: true,
778
- reference: transaction.reference ?? options.reference
779
- });
780
- },
781
- onCancel: () => {
782
- resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
783
- },
784
- onError: (error) => {
785
- resolveOnce({
786
- success: false,
787
- error: error?.message || "PAYMENT_FAILED"
788
- });
789
- }
790
- });
791
- providerStarted = true;
792
- } catch {
793
- resolveOnce({ success: false, error: "POPUP_BLOCKED" });
794
- }
795
- });
1317
+ // src/internal/cross-tab-session.ts
1318
+ var CROSS_TAB_SESSION_HUB_KEY = /* @__PURE__ */ Symbol.for("@cimplify/sdk.cross-tab-session-hub");
1319
+ var SESSION_SYNC_STORAGE_KEY = "cimplify_session_sync";
1320
+ var MAX_RECENT_ENVELOPES = 64;
1321
+ function hubHost() {
1322
+ return window;
796
1323
  }
797
- var PaystackAuthorizationAdapter = class {
798
- constructor(openPopup = openPaystackPopup) {
799
- this.openPopup = openPopup;
800
- this.provider = "paystack";
801
- }
802
- async authorize(context) {
803
- const result = await this.openPopup(
804
- {
805
- key: context.action.public_key,
806
- email: context.email,
807
- accessCode: context.action.client_secret,
808
- reference: context.reference || context.action.client_secret,
809
- currency: context.currency
810
- },
811
- context.signal
812
- );
813
- if (result.success) {
814
- return { status: "submitted", reference: result.reference || context.reference };
815
- }
816
- if (result.error === "SUBMISSION_UNKNOWN") {
817
- return { status: "submitted", reference: context.reference };
818
- }
819
- if (!result.error || result.error === "CANCELLED" || result.error === "PAYMENT_CANCELLED") {
820
- return { status: "cancelled" };
821
- }
822
- if (result.error === "POPUP_BLOCKED") {
823
- return {
824
- status: "failed",
825
- code: "POPUP_BLOCKED",
826
- message: "Unable to open the Paystack payment popup. Please allow popups and try again.",
827
- recoverable: true
828
- };
829
- }
830
- if (result.error === "PROVIDER_UNAVAILABLE") {
831
- return {
832
- status: "failed",
833
- code: "PROVIDER_UNAVAILABLE",
834
- message: "Paystack is unavailable in this environment.",
835
- recoverable: false
836
- };
837
- }
838
- return {
839
- status: "failed",
840
- code: "PAYMENT_FAILED",
841
- message: result.error || "Paystack payment could not be submitted.",
842
- recoverable: true
843
- };
1324
+ function currentHub() {
1325
+ const candidate = hubHost()[CROSS_TAB_SESSION_HUB_KEY];
1326
+ return candidate?.subscribers instanceof Map && candidate.recentEnvelopes instanceof Set && typeof candidate.storageListener === "function" && typeof candidate.dispatch === "function" ? candidate : null;
1327
+ }
1328
+ function subscriberReference(target) {
1329
+ const WeakReference = globalThis.WeakRef;
1330
+ if (WeakReference) return new WeakReference(target);
1331
+ return { deref: () => target };
1332
+ }
1333
+ function pruneSubscribers(hub) {
1334
+ for (const [token, reference] of hub.subscribers) {
1335
+ if (!reference.deref()) hub.subscribers.delete(token);
844
1336
  }
845
- };
846
-
847
- // src/payment-authorization/providers/stripe.ts
848
- async function loadStripeFromPeer(publicKey) {
1337
+ }
1338
+ function parseSessionSyncPayload(serialized) {
1339
+ if (!serialized) return null;
849
1340
  try {
850
- const imported = await import('@stripe/stripe-js');
851
- return imported.loadStripe ? imported.loadStripe(publicKey) : null;
1341
+ const payload = JSON.parse(serialized);
1342
+ return (payload.type === "claimed" || payload.type === "cleared") && typeof payload.token === "string" && payload.token.length > 0 && typeof payload.at === "number" && Number.isFinite(payload.at) && (payload.name === void 0 || payload.name === null || typeof payload.name === "string") ? payload : null;
852
1343
  } catch {
853
1344
  return null;
854
1345
  }
855
1346
  }
856
- function createStripeModal() {
857
- if (typeof document === "undefined" || !document.body) return null;
858
- const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
859
- const root = document.createElement("div");
860
- root.setAttribute("role", "dialog");
861
- root.setAttribute("aria-modal", "true");
862
- root.setAttribute("aria-label", "Secure card payment");
863
- Object.assign(root.style, {
864
- position: "fixed",
865
- inset: "0",
866
- zIndex: "2147483647",
867
- display: "grid",
868
- placeItems: "center",
869
- padding: "16px",
870
- background: "rgba(15, 23, 42, 0.58)"
871
- });
872
- const form = document.createElement("form");
873
- Object.assign(form.style, {
874
- width: "min(100%, 480px)",
875
- maxHeight: "calc(100vh - 32px)",
876
- overflow: "auto",
877
- borderRadius: "12px",
878
- padding: "24px",
879
- background: "#fff",
880
- boxShadow: "0 24px 80px rgba(15, 23, 42, 0.32)"
881
- });
882
- const title = document.createElement("h2");
883
- title.textContent = "Complete card payment";
884
- Object.assign(title.style, { margin: "0 0 18px", font: "600 18px/1.4 system-ui" });
885
- const paymentMount = document.createElement("div");
886
- paymentMount.setAttribute("data-cimplify-stripe-payment-element", "");
887
- const error = document.createElement("p");
888
- error.setAttribute("role", "alert");
889
- Object.assign(error.style, {
890
- minHeight: "20px",
891
- margin: "14px 0 0",
892
- color: "#b91c1c",
893
- font: "14px/1.4 system-ui"
894
- });
895
- const actions = document.createElement("div");
896
- Object.assign(actions.style, {
897
- display: "flex",
898
- justifyContent: "flex-end",
899
- gap: "10px",
900
- marginTop: "18px"
901
- });
902
- const cancelButton = document.createElement("button");
903
- cancelButton.type = "button";
904
- cancelButton.textContent = "Cancel";
905
- Object.assign(cancelButton.style, {
906
- border: "1px solid #cbd5e1",
907
- borderRadius: "8px",
908
- padding: "10px 16px",
909
- background: "#fff",
910
- color: "#0f172a",
911
- cursor: "pointer"
912
- });
913
- const submitButton = document.createElement("button");
914
- submitButton.type = "submit";
915
- submitButton.textContent = "Pay securely";
916
- Object.assign(submitButton.style, {
917
- border: "0",
918
- borderRadius: "8px",
919
- padding: "10px 16px",
920
- background: "#0f172a",
921
- color: "#fff",
922
- cursor: "pointer"
923
- });
924
- actions.append(cancelButton, submitButton);
925
- form.append(title, paymentMount, error, actions);
926
- root.append(form);
927
- document.body.append(root);
928
- cancelButton.focus();
929
- return { root, form, paymentMount, submitButton, cancelButton, error, previousFocus };
1347
+ function sessionSyncEnvelopeKey(payload) {
1348
+ const name = payload.name === void 0 ? ["missing"] : ["present", payload.name];
1349
+ return JSON.stringify([payload.type, payload.token, name, payload.at]);
930
1350
  }
931
- function stripeFailure(error) {
932
- const message = error?.message || "Stripe could not authorize this payment.";
933
- if (error?.code === "payment_intent_authentication_failure" || error?.type === "invalid_request_error") {
934
- return {
935
- status: "failed",
936
- code: "AUTHORIZATION_FAILED",
937
- message,
938
- recoverable: true
939
- };
940
- }
941
- return { status: "failed", code: "PAYMENT_FAILED", message, recoverable: true };
1351
+ function rememberEnvelope(hub, envelope) {
1352
+ if (hub.recentEnvelopes.has(envelope)) return false;
1353
+ hub.recentEnvelopes.add(envelope);
1354
+ if (hub.recentEnvelopes.size > MAX_RECENT_ENVELOPES) {
1355
+ const oldest = hub.recentEnvelopes.values().next().value;
1356
+ if (oldest) hub.recentEnvelopes.delete(oldest);
1357
+ }
1358
+ return true;
942
1359
  }
943
- var StripeAuthorizationAdapter = class {
944
- constructor(loadStripe = loadStripeFromPeer) {
945
- this.loadStripe = loadStripe;
946
- this.provider = "stripe";
947
- }
948
- async authorize(context) {
949
- if (typeof window === "undefined" || typeof document === "undefined" || context.signal?.aborted) {
950
- return context.signal?.aborted ? { status: "cancelled" } : {
951
- status: "failed",
952
- code: "PROVIDER_UNAVAILABLE",
953
- message: "Stripe Payment Element is unavailable in this environment.",
954
- recoverable: false
955
- };
956
- }
957
- let stripe;
958
- try {
959
- stripe = await this.loadStripe(context.action.public_key);
960
- } catch (error) {
961
- return {
962
- status: "failed",
963
- code: "PROVIDER_UNAVAILABLE",
964
- message: error instanceof Error ? error.message : "Stripe.js could not be loaded.",
965
- recoverable: true
966
- };
967
- }
968
- if (context.signal?.aborted) {
969
- return { status: "cancelled" };
970
- }
971
- if (!stripe) {
972
- return {
973
- status: "failed",
974
- code: "PROVIDER_UNAVAILABLE",
975
- message: "Stripe.js could not be loaded.",
976
- recoverable: false
977
- };
978
- }
979
- const modal = createStripeModal();
980
- if (!modal) {
981
- return {
982
- status: "failed",
983
- code: "PROVIDER_UNAVAILABLE",
984
- message: "Stripe Payment Element could not be mounted.",
985
- recoverable: false
986
- };
987
- }
988
- let paymentElement;
989
- let elements;
990
- try {
991
- elements = stripe.elements({ clientSecret: context.action.client_secret });
992
- paymentElement = elements.create("payment");
993
- paymentElement.mount(modal.paymentMount);
994
- } catch {
995
- modal.root.remove();
996
- modal.previousFocus?.focus();
997
- return {
998
- status: "failed",
999
- code: "PROVIDER_UNAVAILABLE",
1000
- message: "Stripe Payment Element could not be initialized.",
1001
- recoverable: false
1002
- };
1360
+ function createHub() {
1361
+ const hub = {
1362
+ subscribers: /* @__PURE__ */ new Map(),
1363
+ recentEnvelopes: /* @__PURE__ */ new Set()
1364
+ };
1365
+ hub.dispatch = (payload) => {
1366
+ if (!rememberEnvelope(hub, sessionSyncEnvelopeKey(payload))) return;
1367
+ for (const [token, reference] of hub.subscribers) {
1368
+ const subscriber = reference.deref();
1369
+ if (!subscriber) {
1370
+ hub.subscribers.delete(token);
1371
+ continue;
1372
+ }
1373
+ try {
1374
+ subscriber.notify(payload);
1375
+ } catch {
1376
+ }
1003
1377
  }
1004
- if (context.signal?.aborted) {
1005
- if (paymentElement.destroy) paymentElement.destroy();
1006
- else paymentElement.unmount?.();
1007
- modal.root.remove();
1008
- modal.previousFocus?.focus();
1009
- return { status: "cancelled" };
1010
- }
1011
- return new Promise((resolve) => {
1012
- let settled = false;
1013
- let submitting = false;
1014
- let confirmationStarted = false;
1015
- const cleanup = () => {
1016
- context.signal?.removeEventListener("abort", onAbort);
1017
- modal.form.removeEventListener("submit", onSubmit);
1018
- modal.cancelButton.removeEventListener("click", onCancel);
1019
- if (paymentElement.destroy) paymentElement.destroy();
1020
- else paymentElement.unmount?.();
1021
- modal.root.remove();
1022
- modal.previousFocus?.focus();
1023
- };
1024
- const settle = (outcome) => {
1025
- if (settled) return;
1026
- settled = true;
1027
- cleanup();
1028
- resolve(outcome);
1029
- };
1030
- const onAbort = () => {
1031
- if (confirmationStarted) {
1032
- settle({ status: "submitted", reference: context.reference });
1033
- return;
1034
- }
1035
- settle({ status: "cancelled" });
1036
- };
1037
- const onCancel = () => {
1038
- if (!confirmationStarted) settle({ status: "cancelled" });
1039
- };
1040
- const onSubmit = async (event) => {
1041
- event.preventDefault();
1042
- if (submitting || settled) return;
1043
- submitting = true;
1044
- modal.submitButton.disabled = true;
1045
- modal.error.textContent = "";
1046
- try {
1047
- const validation = elements.submit ? await elements.submit() : {};
1048
- if (validation.error) {
1049
- modal.error.textContent = validation.error.message || "Check your payment details.";
1050
- submitting = false;
1051
- modal.submitButton.disabled = false;
1052
- return;
1053
- }
1054
- if (settled || context.signal?.aborted) {
1055
- settle({ status: "cancelled" });
1056
- return;
1057
- }
1058
- confirmationStarted = true;
1059
- modal.cancelButton.disabled = true;
1060
- const confirmation = await stripe.confirmPayment({
1061
- elements,
1062
- confirmParams: { return_url: context.returnUrl || window.location.href },
1063
- redirect: "if_required"
1064
- });
1065
- if (settled) return;
1066
- if (confirmation.error) {
1067
- settle(stripeFailure(confirmation.error));
1068
- return;
1069
- }
1070
- settle({
1071
- status: "submitted",
1072
- reference: confirmation.paymentIntent?.id || context.reference
1073
- });
1074
- } catch (error) {
1075
- if (confirmationStarted) {
1076
- settle({ status: "submitted", reference: context.reference });
1077
- return;
1078
- }
1079
- settle(
1080
- stripeFailure({
1081
- message: error instanceof Error ? error.message : "Stripe confirmation failed."
1082
- })
1083
- );
1084
- }
1085
- };
1086
- modal.form.addEventListener("submit", onSubmit);
1087
- modal.cancelButton.addEventListener("click", onCancel);
1088
- context.signal?.addEventListener("abort", onAbort, { once: true });
1089
- paymentElement.on?.("escape", onCancel);
1090
- paymentElement.on?.("loaderror", (event) => {
1091
- if (confirmationStarted) return;
1092
- settle({
1093
- status: "failed",
1094
- code: "PROVIDER_UNAVAILABLE",
1095
- message: event?.error?.message || "Stripe Payment Element failed to load.",
1096
- recoverable: true
1097
- });
1098
- });
1099
- });
1100
- }
1101
- };
1102
-
1103
- // src/payment-authorization/registry.ts
1104
- function normalizeProvider(provider) {
1105
- return typeof provider === "string" ? provider.trim().toLowerCase() : "";
1378
+ };
1379
+ hub.storageListener = (event) => {
1380
+ const storageEvent = event;
1381
+ if (storageEvent.key !== SESSION_SYNC_STORAGE_KEY) return;
1382
+ const payload = parseSessionSyncPayload(storageEvent.newValue);
1383
+ if (payload) hub.dispatch(payload);
1384
+ };
1385
+ window.addEventListener("storage", hub.storageListener);
1386
+ hubHost()[CROSS_TAB_SESSION_HUB_KEY] = hub;
1387
+ return hub;
1106
1388
  }
1107
- var PaymentAuthorizationAdapterRegistry = class {
1108
- constructor(adapters = []) {
1109
- this.adapters = /* @__PURE__ */ new Map();
1110
- for (const adapter of adapters) {
1111
- this.register(adapter);
1112
- }
1113
- }
1114
- register(adapter) {
1115
- const provider = normalizeProvider(adapter.provider);
1116
- if (!provider) {
1117
- throw new TypeError("Payment authorization adapter provider cannot be empty.");
1118
- }
1119
- this.adapters.set(provider, adapter);
1120
- return this;
1121
- }
1122
- unregister(provider) {
1123
- return this.adapters.delete(normalizeProvider(provider));
1124
- }
1125
- get(provider) {
1126
- return this.adapters.get(normalizeProvider(provider));
1127
- }
1128
- supports(provider) {
1129
- return this.adapters.has(normalizeProvider(provider));
1130
- }
1131
- providers() {
1132
- return [...this.adapters.keys()];
1133
- }
1134
- async authorize(action, options) {
1135
- const provider = normalizeProvider(action.provider);
1136
- if (!provider) {
1137
- return {
1138
- status: "failed",
1139
- code: "PROVIDER_UNAVAILABLE",
1140
- message: "Card authorization response did not identify a payment provider.",
1141
- recoverable: false
1142
- };
1143
- }
1144
- const adapter = this.adapters.get(provider);
1145
- if (!adapter) {
1146
- return {
1147
- status: "failed",
1148
- code: "PROVIDER_UNAVAILABLE",
1149
- message: `Card authorization provider '${provider}' is not supported by this SDK client.`,
1150
- recoverable: false
1151
- };
1152
- }
1153
- try {
1154
- const normalizedAction = action.provider === provider ? action : { ...action, provider };
1155
- return await adapter.authorize({ ...options, action: normalizedAction });
1156
- } catch (error) {
1157
- return {
1158
- status: "failed",
1159
- code: "PAYMENT_FAILED",
1160
- message: error instanceof Error ? error.message : `Card authorization provider '${provider}' failed unexpectedly.`,
1161
- recoverable: true
1162
- };
1163
- }
1389
+ function publishLocalSessionSync(payload) {
1390
+ if (typeof window === "undefined" || typeof window.addEventListener !== "function") return;
1391
+ (currentHub() ?? createHub()).dispatch(payload);
1392
+ }
1393
+ function subscribeCrossTabSession(notify) {
1394
+ if (typeof window === "undefined" || typeof window.addEventListener !== "function") {
1395
+ return () => {
1396
+ };
1164
1397
  }
1165
- };
1166
- function createDefaultPaymentAuthorizationAdapterRegistry() {
1167
- return new PaymentAuthorizationAdapterRegistry([
1168
- new PaystackAuthorizationAdapter(),
1169
- new StripeAuthorizationAdapter()
1170
- ]);
1398
+ const hub = currentHub() ?? createHub();
1399
+ pruneSubscribers(hub);
1400
+ const token = /* @__PURE__ */ Symbol("cross-tab-session-subscriber");
1401
+ const subscriber = { notify };
1402
+ hub.subscribers.set(token, subscriberReference(subscriber));
1403
+ let unsubscribed = false;
1404
+ return () => {
1405
+ if (unsubscribed) return;
1406
+ unsubscribed = true;
1407
+ subscriber.notify = () => {
1408
+ };
1409
+ hub.subscribers.delete(token);
1410
+ pruneSubscribers(hub);
1411
+ if (hub.subscribers.size !== 0) return;
1412
+ window.removeEventListener("storage", hub.storageListener);
1413
+ if (currentHub() === hub) delete hubHost()[CROSS_TAB_SESSION_HUB_KEY];
1414
+ };
1171
1415
  }
1172
1416
 
1173
1417
  // src/client.ts
1174
1418
  var SESSION_TOKEN_STORAGE_KEY = "cimplify_session_token";
1175
- var SESSION_SYNC_STORAGE_KEY = "cimplify_session_sync";
1176
1419
  var ORDER_TOKEN_PREFIX = "cimplify_ot_";
1177
1420
  var SESSION_TOKEN_HEADER = "x-session-token";
1178
1421
  var LOCATION_ID_QUERY_PARAMETER = "location_id";
@@ -1180,8 +1423,37 @@ var ORDER_TOKEN_TTL_MS = 24 * 60 * 60 * 1e3;
1180
1423
  var DEFAULT_TIMEOUT_MS = 5e3;
1181
1424
  var DEFAULT_MAX_RETRIES = 3;
1182
1425
  var DEFAULT_RETRY_DELAY_MS = 1e3;
1183
- function sleep(ms) {
1184
- return new Promise((resolve) => setTimeout(resolve, ms));
1426
+ var MAX_RETRY_AFTER_MS = 3e4;
1427
+ function sleep(ms, signal) {
1428
+ if (signal?.aborted) {
1429
+ return Promise.reject(new chunkSFUJMS5E_js.CimplifyError(chunkSFUJMS5E_js.ErrorCode.CANCELLED, "Request was cancelled.", true));
1430
+ }
1431
+ return new Promise((resolve, reject) => {
1432
+ const timer = setTimeout(() => {
1433
+ signal?.removeEventListener("abort", onAbort);
1434
+ resolve();
1435
+ }, ms);
1436
+ const onAbort = () => {
1437
+ clearTimeout(timer);
1438
+ reject(new chunkSFUJMS5E_js.CimplifyError(chunkSFUJMS5E_js.ErrorCode.CANCELLED, "Request was cancelled.", true));
1439
+ };
1440
+ signal?.addEventListener("abort", onAbort, { once: true });
1441
+ });
1442
+ }
1443
+ function parseRetryAfterMs(value) {
1444
+ if (!value) return null;
1445
+ const seconds = Number(value);
1446
+ if (Number.isFinite(seconds) && seconds >= 0) {
1447
+ return Math.min(seconds * 1e3, MAX_RETRY_AFTER_MS);
1448
+ }
1449
+ const retryAt = Date.parse(value);
1450
+ if (!Number.isFinite(retryAt)) return null;
1451
+ return Math.min(Math.max(0, retryAt - Date.now()), MAX_RETRY_AFTER_MS);
1452
+ }
1453
+ function withPositiveJitter(delayMs, jitterRatio = 0) {
1454
+ const boundedRatio = Math.min(Math.max(jitterRatio, 0), 1);
1455
+ if (boundedRatio === 0 || delayMs === 0) return delayMs;
1456
+ return Math.round(delayMs + delayMs * boundedRatio * Math.random());
1185
1457
  }
1186
1458
  function nextInit(cacheOptions) {
1187
1459
  if (!cacheOptions) return {};
@@ -1221,16 +1493,16 @@ function isRetryable(error) {
1221
1493
  if (error instanceof DOMException && error.name === "AbortError") {
1222
1494
  return true;
1223
1495
  }
1224
- if (error instanceof chunkJG5CQNCB_js.CimplifyError) {
1496
+ if (error instanceof chunkSFUJMS5E_js.CimplifyError) {
1225
1497
  return error.retryable;
1226
1498
  }
1227
1499
  return false;
1228
1500
  }
1229
1501
  function toNetworkError(error, isTestMode) {
1230
1502
  if (error instanceof DOMException && error.name === "AbortError") {
1231
- return chunkJG5CQNCB_js.enrichError(
1232
- new chunkJG5CQNCB_js.CimplifyError(
1233
- chunkJG5CQNCB_js.ErrorCode.TIMEOUT,
1503
+ return chunkSFUJMS5E_js.enrichError(
1504
+ new chunkSFUJMS5E_js.CimplifyError(
1505
+ chunkSFUJMS5E_js.ErrorCode.TIMEOUT,
1234
1506
  "Request timed out. Please check your connection and try again.",
1235
1507
  true
1236
1508
  ),
@@ -1238,21 +1510,21 @@ function toNetworkError(error, isTestMode) {
1238
1510
  );
1239
1511
  }
1240
1512
  if (error instanceof TypeError && error.message.includes("fetch")) {
1241
- return chunkJG5CQNCB_js.enrichError(
1242
- new chunkJG5CQNCB_js.CimplifyError(
1243
- chunkJG5CQNCB_js.ErrorCode.NETWORK_ERROR,
1513
+ return chunkSFUJMS5E_js.enrichError(
1514
+ new chunkSFUJMS5E_js.CimplifyError(
1515
+ chunkSFUJMS5E_js.ErrorCode.NETWORK_ERROR,
1244
1516
  "Network error. Please check your internet connection.",
1245
1517
  true
1246
1518
  ),
1247
1519
  { isTestMode }
1248
1520
  );
1249
1521
  }
1250
- if (error instanceof chunkJG5CQNCB_js.CimplifyError) {
1251
- return chunkJG5CQNCB_js.enrichError(error, { isTestMode });
1522
+ if (error instanceof chunkSFUJMS5E_js.CimplifyError) {
1523
+ return chunkSFUJMS5E_js.enrichError(error, { isTestMode });
1252
1524
  }
1253
- return chunkJG5CQNCB_js.enrichError(
1254
- new chunkJG5CQNCB_js.CimplifyError(
1255
- chunkJG5CQNCB_js.ErrorCode.UNKNOWN_ERROR,
1525
+ return chunkSFUJMS5E_js.enrichError(
1526
+ new chunkSFUJMS5E_js.CimplifyError(
1527
+ chunkSFUJMS5E_js.ErrorCode.UNKNOWN_ERROR,
1256
1528
  error instanceof Error ? error.message : "An unknown error occurred",
1257
1529
  false
1258
1530
  ),
@@ -1295,6 +1567,9 @@ var CimplifyClient = class {
1295
1567
  this.businessId = null;
1296
1568
  this.businessIdResolvePromise = null;
1297
1569
  this.publicKeyEpoch = 0;
1570
+ this.stopCrossTabSessionWatch = null;
1571
+ this.latestSessionSyncAt = Number.NEGATIVE_INFINITY;
1572
+ this.latestSessionSyncOrderKey = null;
1298
1573
  this.inflightRequests = /* @__PURE__ */ new Map();
1299
1574
  /**
1300
1575
  * App-level event channel. Subscribe to surface mutation rollbacks, outbox
@@ -1313,10 +1588,14 @@ var CimplifyClient = class {
1313
1588
  this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
1314
1589
  this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
1315
1590
  this.hooks = config.hooks ?? {};
1316
- this.authorizationAdapters = config.authorizationAdapterRegistry ?? createDefaultPaymentAuthorizationAdapterRegistry();
1591
+ this.authorizationAdapters = config.authorizationAdapterRegistry ?? chunkDQEXY542_js.createDefaultPaymentAuthorizationAdapterRegistry();
1317
1592
  this.fetchImpl = (config.fetch ?? globalThis.fetch).bind(globalThis);
1318
1593
  this.sessionToken = this.loadSessionToken() ?? this.mintSessionToken();
1319
- this.watchCrossTabSession();
1594
+ const persistedSessionSync = this.loadSessionSyncPayload();
1595
+ if (persistedSessionSync) this.observeSessionSync(persistedSessionSync);
1596
+ this.stopCrossTabSessionWatch = subscribeCrossTabSession((payload) => {
1597
+ this.applyCrossTabSession(payload);
1598
+ });
1320
1599
  if (!this.publicKey && !config.suppressPublicKeyWarning) {
1321
1600
  console.warn(
1322
1601
  '[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "cpk_..." } to createCimplifyClient().'
@@ -1343,6 +1622,12 @@ var CimplifyClient = class {
1343
1622
  this.stopPageViewTracking?.();
1344
1623
  this.stopPageViewTracking = null;
1345
1624
  }
1625
+ /** Release window-level subscriptions and automatic tracking owned by this client. */
1626
+ destroy() {
1627
+ this.stopTrackingPageViews();
1628
+ this.stopCrossTabSessionWatch?.();
1629
+ this.stopCrossTabSessionWatch = null;
1630
+ }
1346
1631
  getAccessToken() {
1347
1632
  return this.accessToken;
1348
1633
  }
@@ -1370,7 +1655,7 @@ var CimplifyClient = class {
1370
1655
  this.hooks.onSessionChange?.({
1371
1656
  previousToken: previous,
1372
1657
  newToken: token,
1373
- source: "manual"
1658
+ source: SESSION_CHANGE_SOURCE.MANUAL
1374
1659
  });
1375
1660
  this.events.emit(AUTH_TOKEN_CHANGED, { token });
1376
1661
  }
@@ -1379,7 +1664,8 @@ var CimplifyClient = class {
1379
1664
  * refresh restore) binds the anonymous session so the conversation, cart
1380
1665
  * and orders survive a cleared browser. Best-effort by design — a failed
1381
1666
  * claim leaves the session anonymous, exactly as it was. Called by the
1382
- * auth flows, never by the raw token setter.
1667
+ * merchant auth flows, never by the raw token setter. Global Link clients
1668
+ * have no storefront tenant and therefore activate without this step.
1383
1669
  */
1384
1670
  autoClaimChatIdentity() {
1385
1671
  if (typeof window === "undefined" || !this.sessionToken || !this.accessToken) return;
@@ -1413,7 +1699,7 @@ var CimplifyClient = class {
1413
1699
  this.hooks.onSessionChange?.({
1414
1700
  previousToken: previous,
1415
1701
  newToken: null,
1416
- source: "clear"
1702
+ source: SESSION_CHANGE_SOURCE.CLEAR
1417
1703
  });
1418
1704
  this.events.emit(AUTH_TOKEN_CHANGED, { token: null });
1419
1705
  this.events.emit(SESSION_CLEARED, {});
@@ -1421,9 +1707,29 @@ var CimplifyClient = class {
1421
1707
  getSessionEpoch() {
1422
1708
  return this.sessionEpoch;
1423
1709
  }
1710
+ /** Monotonic fence for async work scoped to the current storefront tenant. */
1711
+ getPublicKeyEpoch() {
1712
+ return this.publicKeyEpoch;
1713
+ }
1424
1714
  getSessionToken() {
1425
1715
  return this.sessionToken;
1426
1716
  }
1717
+ /** Whether the current transport session is known to have been claimed for
1718
+ * a Link identity, even when its in-memory bearer is absent after reload or
1719
+ * a failed refresh. The sync marker is written only after canonical claim
1720
+ * rotation and is replaced with `cleared` when a fresh session is minted. */
1721
+ hasClaimedSessionAuthority() {
1722
+ if (!this.sessionToken) return false;
1723
+ try {
1724
+ const payload = parseSessionSyncPayload(
1725
+ this.safeStorage()?.getItem(SESSION_SYNC_STORAGE_KEY) ?? null
1726
+ );
1727
+ if (!payload) return false;
1728
+ return payload.type === "claimed" && payload.token === this.sessionToken;
1729
+ } catch {
1730
+ return false;
1731
+ }
1732
+ }
1427
1733
  publishSessionClaimed(customerName) {
1428
1734
  if (!this.sessionToken) return;
1429
1735
  this.broadcastSessionSync({
@@ -1433,41 +1739,49 @@ var CimplifyClient = class {
1433
1739
  });
1434
1740
  }
1435
1741
  broadcastSessionSync(payload) {
1742
+ const envelope = {
1743
+ ...payload,
1744
+ at: this.nextSessionSyncTimestamp()
1745
+ };
1436
1746
  try {
1437
- this.safeStorage()?.setItem(
1438
- SESSION_SYNC_STORAGE_KEY,
1439
- JSON.stringify({ ...payload, at: Date.now() })
1440
- );
1747
+ this.safeStorage()?.setItem(SESSION_SYNC_STORAGE_KEY, JSON.stringify(envelope));
1441
1748
  } catch {
1442
1749
  }
1750
+ publishLocalSessionSync(envelope);
1443
1751
  }
1444
- watchCrossTabSession() {
1445
- if (typeof window === "undefined" || typeof window.addEventListener !== "function") return;
1446
- window.addEventListener("storage", (event) => {
1447
- if (event.key !== SESSION_SYNC_STORAGE_KEY || !event.newValue) return;
1448
- let payload;
1449
- try {
1450
- payload = JSON.parse(event.newValue);
1451
- } catch {
1452
- return;
1453
- }
1454
- if (!payload?.token || payload.token === this.sessionToken) return;
1455
- if (payload.type === "claimed") {
1456
- this.sessionToken = payload.token;
1457
- this.sessionEpoch++;
1458
- this.events.emit(SESSION_CLAIMED, {
1459
- sessionToken: payload.token,
1460
- threadId: null,
1461
- customerName: payload.name ?? ""
1462
- });
1463
- } else if (payload.type === "cleared") {
1464
- this.accessToken = null;
1465
- this.sessionToken = payload.token;
1466
- this.sessionEpoch++;
1467
- this.events.emit(AUTH_TOKEN_CHANGED, { token: null });
1468
- this.events.emit(SESSION_CLEARED, {});
1469
- }
1470
- });
1752
+ applyCrossTabSession(payload) {
1753
+ if (!this.observeSessionSync(payload)) return;
1754
+ if (payload.token === this.sessionToken) return;
1755
+ if (payload.type === "claimed") {
1756
+ this.sessionToken = payload.token;
1757
+ this.sessionEpoch++;
1758
+ this.events.emit(SESSION_CLAIMED, {
1759
+ sessionToken: payload.token,
1760
+ threadId: null,
1761
+ customerName: payload.name ?? ""
1762
+ });
1763
+ } else {
1764
+ this.accessToken = null;
1765
+ this._activity?.resetSession();
1766
+ this.sessionToken = payload.token;
1767
+ this.sessionEpoch++;
1768
+ this.events.emit(AUTH_TOKEN_CHANGED, { token: null });
1769
+ this.events.emit(SESSION_CLEARED, {});
1770
+ }
1771
+ }
1772
+ observeSessionSync(payload) {
1773
+ if (payload.at < this.latestSessionSyncAt) return false;
1774
+ const envelope = sessionSyncEnvelopeKey(payload);
1775
+ if (payload.at === this.latestSessionSyncAt && this.latestSessionSyncOrderKey !== null && envelope <= this.latestSessionSyncOrderKey) {
1776
+ return false;
1777
+ }
1778
+ this.latestSessionSyncAt = payload.at;
1779
+ this.latestSessionSyncOrderKey = envelope;
1780
+ return true;
1781
+ }
1782
+ nextSessionSyncTimestamp() {
1783
+ const now = Date.now();
1784
+ return Number.isFinite(this.latestSessionSyncAt) ? Math.max(now, this.latestSessionSyncAt + 1) : now;
1471
1785
  }
1472
1786
  setOrderToken(orderId, token) {
1473
1787
  try {
@@ -1531,17 +1845,24 @@ var CimplifyClient = class {
1531
1845
  setPublicKey(publicKey) {
1532
1846
  const normalized = publicKey.trim();
1533
1847
  if (normalized === this.publicKey) return;
1848
+ const restartPageViewTracking = this.stopPageViewTracking !== null;
1849
+ if (restartPageViewTracking) {
1850
+ this.stopTrackingPageViews();
1851
+ }
1534
1852
  this.publicKey = normalized;
1535
1853
  this.publicKeyEpoch += 1;
1536
1854
  this.businessId = null;
1537
1855
  this.businessIdResolvePromise = null;
1538
1856
  this.inflightRequests.clear();
1539
- this._activity?.resetSession();
1857
+ this._activity?.resetForStorefrontIdentityChange();
1540
1858
  delete this.context.location_id;
1541
1859
  if (this.accessToken !== null) {
1542
1860
  this.setAccessToken(null);
1543
1861
  }
1544
1862
  this.events.emit(SESSION_CLEARED, {});
1863
+ if (restartPageViewTracking) {
1864
+ this.stopPageViewTracking = this.activity.autoTrackPageViews();
1865
+ }
1545
1866
  }
1546
1867
  getBusinessId() {
1547
1868
  return this.businessId;
@@ -1557,15 +1878,15 @@ var CimplifyClient = class {
1557
1878
  const resolution = (async () => {
1558
1879
  const result = await this.business.getInfo();
1559
1880
  if (!result.ok || !result.value?.id) {
1560
- throw new chunkJG5CQNCB_js.CimplifyError(
1561
- chunkJG5CQNCB_js.ErrorCode.NOT_FOUND,
1881
+ throw new chunkSFUJMS5E_js.CimplifyError(
1882
+ chunkSFUJMS5E_js.ErrorCode.NOT_FOUND,
1562
1883
  "Unable to resolve business ID from the current public key.",
1563
1884
  true
1564
1885
  );
1565
1886
  }
1566
1887
  if (this.publicKeyEpoch !== publicKeyEpoch) {
1567
- throw new chunkJG5CQNCB_js.CimplifyError(
1568
- chunkJG5CQNCB_js.ErrorCode.TENANT_CONTEXT_CHANGED,
1888
+ throw new chunkSFUJMS5E_js.CimplifyError(
1889
+ chunkSFUJMS5E_js.ErrorCode.TENANT_CONTEXT_CHANGED,
1569
1890
  "The storefront tenant changed while resolving its business identity.",
1570
1891
  true
1571
1892
  );
@@ -1582,8 +1903,8 @@ var CimplifyClient = class {
1582
1903
  }
1583
1904
  }
1584
1905
  }
1585
- async authedFetch(url, options, timeoutMs) {
1586
- return this.resilientFetch(url, options, timeoutMs);
1906
+ async authedFetch(url, options, timeoutMs, retryPolicy, signal) {
1907
+ return this.resilientFetch(url, options, timeoutMs, retryPolicy, signal);
1587
1908
  }
1588
1909
  // Storage guards: even the `window.localStorage` property access throws in
1589
1910
  // some sandboxed iframes and strict private-browsing modes. These run in the
@@ -1605,6 +1926,13 @@ var CimplifyClient = class {
1605
1926
  return null;
1606
1927
  }
1607
1928
  }
1929
+ loadSessionSyncPayload() {
1930
+ try {
1931
+ return parseSessionSyncPayload(this.safeStorage()?.getItem(SESSION_SYNC_STORAGE_KEY) ?? null);
1932
+ } catch {
1933
+ return null;
1934
+ }
1935
+ }
1608
1936
  /**
1609
1937
  * Mint the session token client-side so the very first request already
1610
1938
  * carries it. Without this, parallel first-load requests each get a
@@ -1660,7 +1988,7 @@ var CimplifyClient = class {
1660
1988
  }
1661
1989
  return headers;
1662
1990
  }
1663
- async resilientFetch(url, options, timeoutMs) {
1991
+ async resilientFetch(url, options, timeoutMs, retryPolicy, signal) {
1664
1992
  const method = options.method || "GET";
1665
1993
  const path = url.replace(this.baseUrl, "").replace(this.linkApiUrl, "");
1666
1994
  const startTime = Date.now();
@@ -1674,15 +2002,31 @@ var CimplifyClient = class {
1674
2002
  };
1675
2003
  this.hooks.onRequestStart?.(context);
1676
2004
  let lastError;
1677
- for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
2005
+ const configuredMaxRetries = Math.max(0, Math.floor(this.maxRetries));
2006
+ const maxRetries = Math.min(
2007
+ configuredMaxRetries,
2008
+ Math.max(0, Math.floor(retryPolicy?.maxRetries ?? configuredMaxRetries))
2009
+ );
2010
+ const jitterRatio = retryPolicy?.jitterRatio ?? 0;
2011
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1678
2012
  try {
2013
+ if (signal?.aborted) {
2014
+ throw new chunkSFUJMS5E_js.CimplifyError(chunkSFUJMS5E_js.ErrorCode.CANCELLED, "Request was cancelled.", true);
2015
+ }
1679
2016
  const controller = new AbortController();
1680
2017
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs ?? this.timeout);
1681
- const response = await this.fetchImpl(url, {
1682
- ...options,
1683
- signal: controller.signal
1684
- });
1685
- clearTimeout(timeoutId);
2018
+ const onAbort = () => controller.abort();
2019
+ signal?.addEventListener("abort", onAbort, { once: true });
2020
+ let response;
2021
+ try {
2022
+ response = await this.fetchImpl(url, {
2023
+ ...options,
2024
+ signal: controller.signal
2025
+ });
2026
+ } finally {
2027
+ clearTimeout(timeoutId);
2028
+ signal?.removeEventListener("abort", onAbort);
2029
+ }
1686
2030
  this.captureSessionToken(response);
1687
2031
  if (response.ok) {
1688
2032
  this.hooks.onRequestSuccess?.({
@@ -1692,23 +2036,25 @@ var CimplifyClient = class {
1692
2036
  });
1693
2037
  return response;
1694
2038
  }
1695
- if ((response.status === 429 || response.status === 503) && attempt < this.maxRetries) {
2039
+ if ((response.status === 429 || response.status === 503) && attempt < maxRetries) {
1696
2040
  retryCount++;
1697
- const retryAfter = response.headers.get("Retry-After");
1698
- const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3, 3e4) || this.retryDelay * Math.pow(2, attempt) : this.retryDelay * Math.pow(2, attempt);
2041
+ const retryAfterMs = parseRetryAfterMs(response.headers.get("Retry-After"));
2042
+ const retryBaseMs = retryAfterMs ?? this.retryDelay * Math.pow(2, attempt);
2043
+ const jitteredDelayMs = withPositiveJitter(retryBaseMs, jitterRatio);
2044
+ const delay = retryAfterMs === null ? jitteredDelayMs : Math.min(jitteredDelayMs, MAX_RETRY_AFTER_MS);
1699
2045
  this.hooks.onRetry?.({
1700
2046
  ...context,
1701
2047
  attempt: retryCount,
1702
2048
  delayMs: delay,
1703
2049
  error: new Error(`Retryable: ${response.status}`)
1704
2050
  });
1705
- await sleep(delay);
2051
+ await sleep(delay, signal);
1706
2052
  continue;
1707
2053
  }
1708
2054
  if (response.status >= 400 && response.status < 500) {
1709
2055
  this.hooks.onRequestError?.({
1710
2056
  ...context,
1711
- error: new chunkJG5CQNCB_js.CimplifyError(
2057
+ error: new chunkSFUJMS5E_js.CimplifyError(
1712
2058
  `HTTP_${response.status}`,
1713
2059
  `Request failed with status ${response.status}`,
1714
2060
  false
@@ -1720,22 +2066,22 @@ var CimplifyClient = class {
1720
2066
  });
1721
2067
  return response;
1722
2068
  }
1723
- if (response.status >= 500 && attempt < this.maxRetries) {
2069
+ if (response.status >= 500 && attempt < maxRetries) {
1724
2070
  retryCount++;
1725
- const delay = this.retryDelay * Math.pow(2, attempt);
2071
+ const delay = withPositiveJitter(this.retryDelay * Math.pow(2, attempt), jitterRatio);
1726
2072
  this.hooks.onRetry?.({
1727
2073
  ...context,
1728
2074
  attempt: retryCount,
1729
2075
  delayMs: delay,
1730
2076
  error: new Error(`Server error: ${response.status}`)
1731
2077
  });
1732
- await sleep(delay);
2078
+ await sleep(delay, signal);
1733
2079
  continue;
1734
2080
  }
1735
2081
  this.hooks.onRequestError?.({
1736
2082
  ...context,
1737
- error: new chunkJG5CQNCB_js.CimplifyError(
1738
- "SERVER_ERROR",
2083
+ error: new chunkSFUJMS5E_js.CimplifyError(
2084
+ chunkSFUJMS5E_js.ErrorCode.SERVER_ERROR,
1739
2085
  `Server error ${response.status} after ${retryCount} retries`,
1740
2086
  false
1741
2087
  ),
@@ -1747,9 +2093,12 @@ var CimplifyClient = class {
1747
2093
  return response;
1748
2094
  } catch (error) {
1749
2095
  lastError = error;
2096
+ if (signal?.aborted) {
2097
+ throw new chunkSFUJMS5E_js.CimplifyError(chunkSFUJMS5E_js.ErrorCode.CANCELLED, "Request was cancelled.", true);
2098
+ }
1750
2099
  const networkError = toNetworkError(error, this.isTestMode());
1751
2100
  const errorRetryable = isRetryable(error);
1752
- if (!errorRetryable || attempt >= this.maxRetries) {
2101
+ if (!errorRetryable || attempt >= maxRetries) {
1753
2102
  this.hooks.onRequestError?.({
1754
2103
  ...context,
1755
2104
  error: networkError,
@@ -1760,14 +2109,14 @@ var CimplifyClient = class {
1760
2109
  throw networkError;
1761
2110
  }
1762
2111
  retryCount++;
1763
- const delay = this.retryDelay * Math.pow(2, attempt);
2112
+ const delay = withPositiveJitter(this.retryDelay * Math.pow(2, attempt), jitterRatio);
1764
2113
  this.hooks.onRetry?.({
1765
2114
  ...context,
1766
2115
  attempt: retryCount,
1767
2116
  delayMs: delay,
1768
2117
  error: networkError
1769
2118
  });
1770
- await sleep(delay);
2119
+ await sleep(delay, signal);
1771
2120
  }
1772
2121
  }
1773
2122
  const finalError = toNetworkError(lastError, this.isTestMode());
@@ -1804,16 +2153,24 @@ var CimplifyClient = class {
1804
2153
  return request;
1805
2154
  }
1806
2155
  async get(path, opts) {
1807
- const key = this.getDedupeKey("get", path, opts?.cacheOptions);
1808
- return this.deduplicatedRequest(key, async () => {
1809
- const response = await this.authedFetch(this.buildUrl(this.baseUrl, path), {
1810
- method: "GET",
1811
- credentials: this.credentials,
1812
- headers: this.getHeaders(),
1813
- ...nextInit(opts?.cacheOptions)
1814
- });
2156
+ const request = async () => {
2157
+ const response = await this.authedFetch(
2158
+ this.buildUrl(this.baseUrl, path),
2159
+ {
2160
+ method: "GET",
2161
+ credentials: this.credentials,
2162
+ headers: this.getHeaders(),
2163
+ ...nextInit(opts?.cacheOptions)
2164
+ },
2165
+ opts?.timeoutMs,
2166
+ opts?.retryPolicy,
2167
+ opts?.signal
2168
+ );
1815
2169
  return this.handleRestResponse(response);
1816
- });
2170
+ };
2171
+ if (opts?.signal) return request();
2172
+ const key = this.getDedupeKey("get", path, opts?.cacheOptions);
2173
+ return this.deduplicatedRequest(key, request);
1817
2174
  }
1818
2175
  async post(path, body, opts) {
1819
2176
  const response = await this.authedFetch(
@@ -1824,7 +2181,9 @@ var CimplifyClient = class {
1824
2181
  headers: mergeOptionHeaders(this.getHeaders(), opts),
1825
2182
  body: body ? JSON.stringify(body) : void 0
1826
2183
  },
1827
- opts?.timeoutMs
2184
+ opts?.timeoutMs,
2185
+ opts?.retryPolicy,
2186
+ opts?.signal
1828
2187
  );
1829
2188
  return this.handleRestResponse(response);
1830
2189
  }
@@ -1837,7 +2196,9 @@ var CimplifyClient = class {
1837
2196
  headers: mergeOptionHeaders(this.getHeaders(), opts),
1838
2197
  body: body ? JSON.stringify(body) : void 0
1839
2198
  },
1840
- opts?.timeoutMs
2199
+ opts?.timeoutMs,
2200
+ opts?.retryPolicy,
2201
+ opts?.signal
1841
2202
  );
1842
2203
  return this.handleRestResponse(response);
1843
2204
  }
@@ -1849,21 +2210,31 @@ var CimplifyClient = class {
1849
2210
  credentials: this.credentials,
1850
2211
  headers: mergeOptionHeaders(this.getHeaders(), opts)
1851
2212
  },
1852
- opts?.timeoutMs
2213
+ opts?.timeoutMs,
2214
+ opts?.retryPolicy,
2215
+ opts?.signal
1853
2216
  );
1854
2217
  return this.handleRestResponse(response);
1855
2218
  }
1856
2219
  async linkGet(path, opts) {
1857
- const key = this.getDedupeKey("linkGet", path, opts?.cacheOptions);
1858
- return this.deduplicatedRequest(key, async () => {
1859
- const response = await this.authedFetch(`${this.linkApiUrl}${path}`, {
1860
- method: "GET",
1861
- credentials: this.credentials,
1862
- headers: this.getHeaders(),
1863
- ...nextInit(opts?.cacheOptions)
1864
- });
2220
+ const request = async () => {
2221
+ const response = await this.authedFetch(
2222
+ `${this.linkApiUrl}${path}`,
2223
+ {
2224
+ method: "GET",
2225
+ credentials: this.credentials,
2226
+ headers: this.getHeaders(),
2227
+ ...nextInit(opts?.cacheOptions)
2228
+ },
2229
+ opts?.timeoutMs,
2230
+ opts?.retryPolicy,
2231
+ opts?.signal
2232
+ );
1865
2233
  return this.handleRestResponse(response);
1866
- });
2234
+ };
2235
+ if (opts?.signal) return request();
2236
+ const key = this.getDedupeKey("linkGet", path, opts?.cacheOptions);
2237
+ return this.deduplicatedRequest(key, request);
1867
2238
  }
1868
2239
  async linkPost(path, body, opts) {
1869
2240
  const response = await this.authedFetch(
@@ -1874,32 +2245,40 @@ var CimplifyClient = class {
1874
2245
  headers: mergeOptionHeaders(this.getHeaders(), opts),
1875
2246
  body: body ? JSON.stringify(body) : void 0
1876
2247
  },
1877
- opts?.timeoutMs
2248
+ opts?.timeoutMs,
2249
+ opts?.retryPolicy,
2250
+ opts?.signal
1878
2251
  );
1879
2252
  return this.handleRestResponse(response);
1880
2253
  }
1881
2254
  async linkDelete(path, opts) {
1882
- const response = await this.authedFetch(`${this.linkApiUrl}${path}`, {
1883
- method: "DELETE",
1884
- credentials: this.credentials,
1885
- headers: mergeOptionHeaders(this.getHeaders(), opts)
1886
- });
2255
+ const response = await this.authedFetch(
2256
+ `${this.linkApiUrl}${path}`,
2257
+ {
2258
+ method: "DELETE",
2259
+ credentials: this.credentials,
2260
+ headers: mergeOptionHeaders(this.getHeaders(), opts)
2261
+ },
2262
+ opts?.timeoutMs,
2263
+ opts?.retryPolicy,
2264
+ opts?.signal
2265
+ );
1887
2266
  return this.handleRestResponse(response);
1888
2267
  }
1889
2268
  async handleRestResponse(response) {
1890
2269
  const json = await response.json();
1891
2270
  if (!response.ok) {
1892
- const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code || "API_ERROR";
2271
+ const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code || chunkSFUJMS5E_js.ErrorCode.API_ERROR;
1893
2272
  const errorMessage = typeof json.error === "object" && json.error?.error_message || typeof json.error === "object" && json.error?.message || typeof json.error === "string" && json.error || typeof json.message === "string" && json.message || "An error occurred";
1894
2273
  const retryable = typeof json.error === "object" && typeof json.error?.retryable === "boolean" ? json.error.retryable : false;
1895
- if (errorCode === "IDEMPOTENCY_MISMATCH") {
2274
+ if (errorCode === chunkSFUJMS5E_js.ErrorCode.IDEMPOTENCY_MISMATCH) {
1896
2275
  const originalCreatedAt = typeof json.error === "object" && json.error?.original_created_at || "";
1897
- throw new chunkJG5CQNCB_js.IdempotencyMismatchError(errorMessage, originalCreatedAt);
2276
+ throw new chunkSFUJMS5E_js.IdempotencyMismatchError(errorMessage, originalCreatedAt);
1898
2277
  }
1899
- const error = chunkJG5CQNCB_js.enrichError(new chunkJG5CQNCB_js.CimplifyError(errorCode, errorMessage, retryable), {
2278
+ const error = chunkSFUJMS5E_js.enrichError(new chunkSFUJMS5E_js.CimplifyError(errorCode, errorMessage, retryable), {
1900
2279
  isTestMode: this.isTestMode()
1901
2280
  });
1902
- if ((response.status === 401 || error.code === chunkJG5CQNCB_js.ErrorCode.UNAUTHORIZED) && this.accessToken) {
2281
+ if ((response.status === 401 || error.code === chunkSFUJMS5E_js.ErrorCode.UNAUTHORIZED) && this.accessToken) {
1903
2282
  console.warn(
1904
2283
  "[Cimplify] Received 401 Unauthorized. Access token may be missing/expired. Refresh authentication and retry the request."
1905
2284
  );
@@ -1907,9 +2286,9 @@ var CimplifyClient = class {
1907
2286
  throw error;
1908
2287
  }
1909
2288
  if (json?.success === false || json?.error?.code || json?.error?.message) {
1910
- const error = chunkJG5CQNCB_js.enrichError(
1911
- new chunkJG5CQNCB_js.CimplifyError(
1912
- json.error?.code || "API_ERROR",
2289
+ const error = chunkSFUJMS5E_js.enrichError(
2290
+ new chunkSFUJMS5E_js.CimplifyError(
2291
+ json.error?.code || chunkSFUJMS5E_js.ErrorCode.API_ERROR,
1913
2292
  json.error?.message || "An error occurred",
1914
2293
  json.error?.retryable || false
1915
2294
  ),
@@ -1924,25 +2303,31 @@ var CimplifyClient = class {
1924
2303
  }
1925
2304
  get catalogue() {
1926
2305
  if (!this._catalogue) {
1927
- this._catalogue = new chunkJSWJ4XDE_js.CatalogueQueries(this);
2306
+ this._catalogue = new chunkDQEXY542_js.CatalogueQueries(this);
1928
2307
  }
1929
2308
  return this._catalogue;
1930
2309
  }
1931
2310
  get cart() {
1932
2311
  if (!this._cart) {
1933
- this._cart = new chunkJSWJ4XDE_js.CartOperations(this);
2312
+ this._cart = new chunkDQEXY542_js.CartOperations(this);
1934
2313
  }
1935
2314
  return this._cart;
1936
2315
  }
1937
2316
  get checkout() {
1938
2317
  if (!this._checkout) {
1939
- this._checkout = new chunkJSWJ4XDE_js.CheckoutService(this);
2318
+ this._checkout = new chunkDQEXY542_js.CheckoutService(this);
1940
2319
  }
1941
2320
  return this._checkout;
1942
2321
  }
2322
+ get purchaseIntents() {
2323
+ if (!this._purchaseIntents) {
2324
+ this._purchaseIntents = new PurchaseIntentService(this);
2325
+ }
2326
+ return this._purchaseIntents;
2327
+ }
1943
2328
  get orders() {
1944
2329
  if (!this._orders) {
1945
- this._orders = new chunkJSWJ4XDE_js.OrderQueries(this);
2330
+ this._orders = new chunkDQEXY542_js.OrderQueries(this);
1946
2331
  }
1947
2332
  return this._orders;
1948
2333
  }
@@ -1972,43 +2357,43 @@ var CimplifyClient = class {
1972
2357
  }
1973
2358
  get link() {
1974
2359
  if (!this._link) {
1975
- this._link = new chunkJSWJ4XDE_js.LinkService(this);
2360
+ this._link = new chunkDQEXY542_js.LinkService(this);
1976
2361
  }
1977
2362
  return this._link;
1978
2363
  }
1979
2364
  get auth() {
1980
2365
  if (!this._auth) {
1981
- this._auth = new chunkJSWJ4XDE_js.AuthService(this);
2366
+ this._auth = new chunkDQEXY542_js.AuthService(this);
1982
2367
  }
1983
2368
  return this._auth;
1984
2369
  }
1985
2370
  get business() {
1986
2371
  if (!this._business) {
1987
- this._business = new chunkJSWJ4XDE_js.BusinessService(this);
2372
+ this._business = new chunkDQEXY542_js.BusinessService(this);
1988
2373
  }
1989
2374
  return this._business;
1990
2375
  }
1991
2376
  get inventory() {
1992
2377
  if (!this._inventory) {
1993
- this._inventory = new chunkJSWJ4XDE_js.InventoryService(this);
2378
+ this._inventory = new chunkDQEXY542_js.InventoryService(this);
1994
2379
  }
1995
2380
  return this._inventory;
1996
2381
  }
1997
2382
  get scheduling() {
1998
2383
  if (!this._scheduling) {
1999
- this._scheduling = new chunkJSWJ4XDE_js.SchedulingService(this);
2384
+ this._scheduling = new chunkDQEXY542_js.SchedulingService(this);
2000
2385
  }
2001
2386
  return this._scheduling;
2002
2387
  }
2003
2388
  get lite() {
2004
2389
  if (!this._lite) {
2005
- this._lite = new chunkJSWJ4XDE_js.LiteService(this);
2390
+ this._lite = new chunkDQEXY542_js.LiteService(this);
2006
2391
  }
2007
2392
  return this._lite;
2008
2393
  }
2009
2394
  get fx() {
2010
2395
  if (!this._fx) {
2011
- this._fx = new chunkJSWJ4XDE_js.FxService(this);
2396
+ this._fx = new chunkDQEXY542_js.FxService(this);
2012
2397
  }
2013
2398
  return this._fx;
2014
2399
  }
@@ -2043,7 +2428,7 @@ var CimplifyClient = class {
2043
2428
  if (businessId) {
2044
2429
  this.setBusinessId(businessId);
2045
2430
  }
2046
- return chunkJSWJ4XDE_js.createElements(this, businessId ?? this.businessId ?? void 0, options);
2431
+ return chunkDQEXY542_js.createElements(this, businessId ?? this.businessId ?? void 0, options);
2047
2432
  }
2048
2433
  };
2049
2434
  function createCimplifyClient(config = {}) {
@@ -2054,16 +2439,18 @@ exports.AUTH_TOKEN_CHANGED = AUTH_TOKEN_CHANGED;
2054
2439
  exports.ActivityService = ActivityService;
2055
2440
  exports.CimplifyClient = CimplifyClient;
2056
2441
  exports.DeliveryService = DeliveryService;
2442
+ exports.HostedPurchaseIntentService = HostedPurchaseIntentService;
2057
2443
  exports.MUTATION_ERROR = MUTATION_ERROR;
2058
- exports.PaymentAuthorizationAdapterRegistry = PaymentAuthorizationAdapterRegistry;
2059
- exports.PaystackAuthorizationAdapter = PaystackAuthorizationAdapter;
2444
+ exports.PURCHASE_INTENT_ROUTE = PURCHASE_INTENT_ROUTE;
2060
2445
  exports.PlacesService = PlacesService;
2446
+ exports.PurchaseIntentResolver = PurchaseIntentResolver;
2447
+ exports.PurchaseIntentService = PurchaseIntentService;
2448
+ exports.SESSION_CHANGE_SOURCE = SESSION_CHANGE_SOURCE;
2061
2449
  exports.SESSION_CLAIMED = SESSION_CLAIMED;
2062
2450
  exports.SESSION_CLEARED = SESSION_CLEARED;
2063
2451
  exports.StoreCreditQueries = StoreCreditQueries;
2064
- exports.StripeAuthorizationAdapter = StripeAuthorizationAdapter;
2065
2452
  exports.SubscriptionService = SubscriptionService;
2066
2453
  exports.SupportService = SupportService;
2067
2454
  exports.UploadService = UploadService;
2455
+ exports.chatRealtimeUpdateFromWire = chatRealtimeUpdateFromWire;
2068
2456
  exports.createCimplifyClient = createCimplifyClient;
2069
- exports.createDefaultPaymentAuthorizationAdapterRegistry = createDefaultPaymentAuthorizationAdapterRegistry;