@cimplify/sdk 0.102.0 → 0.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/advanced.d.mts +3 -3
  2. package/dist/advanced.d.ts +3 -3
  3. package/dist/advanced.js +23 -22
  4. package/dist/advanced.mjs +4 -3
  5. package/dist/{chunk-DCMXUKDQ.mjs → chunk-2WPVUIHX.mjs} +455 -105
  6. package/dist/{chunk-NIOUSKQQ.js → chunk-4BKOKHE3.js} +9 -0
  7. package/dist/{chunk-YDA46OZ6.js → chunk-5BDYDZ4U.js} +722 -90
  8. package/dist/{chunk-4KNFBPV2.mjs → chunk-5RT4HYMP.mjs} +61 -15
  9. package/dist/{chunk-WMEC5JWN.js → chunk-7PDEX54K.js} +50 -13
  10. package/dist/chunk-CNN7Z2IP.js +125 -0
  11. package/dist/{chunk-DLW3RWRK.mjs → chunk-DYIAX3WI.mjs} +46 -12
  12. package/dist/{chunk-LOWSMGRT.js → chunk-E4K2VQDD.js} +64 -16
  13. package/dist/chunk-GOHSXBKW.mjs +105 -0
  14. package/dist/{chunk-O3YUHLZL.mjs → chunk-H3RATH4B.mjs} +9 -0
  15. package/dist/{chunk-YER7DIOM.js → chunk-IQFFSKE2.js} +753 -594
  16. package/dist/{chunk-Z3K2D3TE.mjs → chunk-OCCNG6MO.mjs} +735 -558
  17. package/dist/{chunk-DKLHCZ5M.mjs → chunk-OKADBZAI.mjs} +652 -25
  18. package/dist/{chunk-EAZGA7LD.js → chunk-YVAEY3VC.js} +457 -107
  19. package/dist/{client-nsiMwuC1.d.ts → client-BVNWjk6W.d.ts} +2 -2
  20. package/dist/{client-B40KFazg.d.ts → client-BuLcOWLv.d.ts} +202 -319
  21. package/dist/{client-BizGfqB7.d.mts → client-D5AoobFc.d.mts} +202 -319
  22. package/dist/{client-BdErclfU.d.mts → client-fM716k7G.d.mts} +2 -2
  23. package/dist/{index-DpmnI__D.d.mts → index-BtcoqrY1.d.ts} +14 -4
  24. package/dist/{index-nEyXWIyR.d.ts → index-D7Tf3v7c.d.mts} +14 -4
  25. package/dist/index.d.mts +83 -8
  26. package/dist/index.d.ts +83 -8
  27. package/dist/index.js +203 -169
  28. package/dist/index.mjs +7 -5
  29. package/dist/link-D9iSVxt5.d.mts +308 -0
  30. package/dist/link-D9iSVxt5.d.ts +308 -0
  31. package/dist/mock/cli.mjs +463 -104
  32. package/dist/mock/library.js +463 -104
  33. package/dist/mock/library.mjs +463 -104
  34. package/dist/mock/msw.js +463 -104
  35. package/dist/mock/msw.mjs +463 -104
  36. package/dist/{product-Byf0unSy.d.mts → payment-g0HFkEDU.d.mts} +708 -61
  37. package/dist/{product-Byf0unSy.d.ts → payment-g0HFkEDU.d.ts} +708 -61
  38. package/dist/{price-BstChV9a.d.ts → price-BLIeAb8K.d.ts} +1 -1
  39. package/dist/{price-CS4E5zLS.d.mts → price-Czq_cTC_.d.mts} +1 -1
  40. package/dist/react.d.mts +4 -4
  41. package/dist/react.d.ts +4 -4
  42. package/dist/react.js +194 -169
  43. package/dist/react.mjs +55 -30
  44. package/dist/{server-DqbZh8Bk.d.mts → server-BkVhOIwn.d.mts} +14 -10
  45. package/dist/{server-Cj40R2ZD.d.ts → server-DKK_D-PP.d.ts} +14 -10
  46. package/dist/server.d.mts +4 -4
  47. package/dist/server.d.ts +4 -4
  48. package/dist/server.js +12 -10
  49. package/dist/server.mjs +6 -4
  50. package/dist/testing/msw.d.mts +2 -2
  51. package/dist/testing/msw.d.ts +2 -2
  52. package/dist/testing/msw.js +4 -3
  53. package/dist/testing/msw.mjs +3 -2
  54. package/dist/testing/suite.d.mts +6 -6
  55. package/dist/testing/suite.d.ts +6 -6
  56. package/dist/testing/suite.js +26 -25
  57. package/dist/testing/suite.mjs +7 -6
  58. package/dist/testing.d.mts +195 -56
  59. package/dist/testing.d.ts +195 -56
  60. package/dist/testing.js +95 -82
  61. package/dist/testing.mjs +14 -9
  62. package/dist/utils.d.mts +4 -4
  63. package/dist/utils.d.ts +4 -4
  64. package/dist/utils.js +37 -29
  65. package/dist/utils.mjs +2 -2
  66. package/package.json +7 -1
  67. package/registry/chat-widget.json +1 -1
  68. package/dist/payment-l0rkieEQ.d.mts +0 -113
  69. package/dist/payment-vzNnIi1n.d.ts +0 -113
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkYER7DIOM_js = require('./chunk-YER7DIOM.js');
4
- var chunkNIOUSKQQ_js = require('./chunk-NIOUSKQQ.js');
3
+ var chunkXREIZ56K_js = require('./chunk-XREIZ56K.js');
4
+ var chunkIQFFSKE2_js = require('./chunk-IQFFSKE2.js');
5
+ var chunk4BKOKHE3_js = require('./chunk-4BKOKHE3.js');
5
6
 
6
7
  // src/store-credit.ts
7
8
  var StoreCreditQueries = class {
@@ -13,7 +14,7 @@ var StoreCreditQueries = class {
13
14
  * currency. Requires a signed-in customer; returns an error otherwise.
14
15
  */
15
16
  async balance() {
16
- return chunkYER7DIOM_js.safe(this.client.get("/api/v1/store-credit/balance"));
17
+ return chunkIQFFSKE2_js.safe(this.client.get("/api/v1/store-credit/balance"));
17
18
  }
18
19
  };
19
20
 
@@ -208,6 +209,7 @@ var ActivityService = class {
208
209
  utm_campaign: utm("campaign"),
209
210
  utm_term: utm("term"),
210
211
  utm_content: utm("content"),
212
+ social_commerce_ref: params.get("social_commerce_ref") || void 0,
211
213
  screen_width: window.screen?.width,
212
214
  screen_height: window.screen?.height,
213
215
  viewport_width: window.innerWidth,
@@ -262,7 +264,7 @@ var ActivityService = class {
262
264
  this.sendEvents([event]);
263
265
  }
264
266
  async getState() {
265
- return chunkYER7DIOM_js.safe(this.client.get("/api/v1/activity/state"));
267
+ return chunkIQFFSKE2_js.safe(this.client.get("/api/v1/activity/state"));
266
268
  }
267
269
  async getRecommendations(options) {
268
270
  const params = new URLSearchParams();
@@ -270,10 +272,10 @@ var ActivityService = class {
270
272
  if (options?.limit) params.set("limit", String(options.limit));
271
273
  const query = params.toString();
272
274
  const path = query ? `/api/v1/activity/recommendations?${query}` : "/api/v1/activity/recommendations";
273
- return chunkYER7DIOM_js.safe(this.client.get(path));
275
+ return chunkIQFFSKE2_js.safe(this.client.get(path));
274
276
  }
275
277
  async dismissMessage(code) {
276
- return chunkYER7DIOM_js.safe(
278
+ return chunkIQFFSKE2_js.safe(
277
279
  this.client.post("/api/v1/activity/messages/dismiss", { code })
278
280
  );
279
281
  }
@@ -369,16 +371,16 @@ var SubscriptionService = class {
369
371
  this.client = client;
370
372
  }
371
373
  async list() {
372
- return chunkYER7DIOM_js.safe(this.client.get("/api/v1/subscriptions"));
374
+ return chunkIQFFSKE2_js.safe(this.client.get("/api/v1/subscriptions"));
373
375
  }
374
376
  async get(id) {
375
377
  const encodedId = encodeURIComponent(id);
376
- return chunkYER7DIOM_js.safe(this.client.get(`/api/v1/subscriptions/${encodedId}`));
378
+ return chunkIQFFSKE2_js.safe(this.client.get(`/api/v1/subscriptions/${encodedId}`));
377
379
  }
378
380
  async cancel(id, reason, opts) {
379
381
  const encodedId = encodeURIComponent(id);
380
- const idempotencyKey = opts?.idempotencyKey ?? chunkYER7DIOM_js.generateIdempotencyKey();
381
- return chunkYER7DIOM_js.safe(
382
+ const idempotencyKey = opts?.idempotencyKey ?? chunkIQFFSKE2_js.generateIdempotencyKey();
383
+ return chunkIQFFSKE2_js.safe(
382
384
  this.client.post(
383
385
  `/api/v1/subscriptions/${encodedId}/cancel`,
384
386
  { reason },
@@ -388,20 +390,20 @@ var SubscriptionService = class {
388
390
  }
389
391
  async pause(id) {
390
392
  const encodedId = encodeURIComponent(id);
391
- return chunkYER7DIOM_js.safe(
393
+ return chunkIQFFSKE2_js.safe(
392
394
  this.client.post(`/api/v1/subscriptions/${encodedId}/pause`)
393
395
  );
394
396
  }
395
397
  async resume(id) {
396
398
  const encodedId = encodeURIComponent(id);
397
- return chunkYER7DIOM_js.safe(
399
+ return chunkIQFFSKE2_js.safe(
398
400
  this.client.post(`/api/v1/subscriptions/${encodedId}/resume`)
399
401
  );
400
402
  }
401
403
  async skipNextRenewal(id, opts) {
402
404
  const encodedId = encodeURIComponent(id);
403
- const idempotencyKey = opts?.idempotencyKey ?? chunkYER7DIOM_js.generateIdempotencyKey();
404
- return chunkYER7DIOM_js.safe(
405
+ const idempotencyKey = opts?.idempotencyKey ?? chunkIQFFSKE2_js.generateIdempotencyKey();
406
+ return chunkIQFFSKE2_js.safe(
405
407
  this.client.post(
406
408
  `/api/v1/subscriptions/${encodedId}/skip`,
407
409
  void 0,
@@ -416,14 +418,14 @@ var UploadService = class {
416
418
  constructor(client) {
417
419
  this.client = client;
418
420
  }
419
- async init(filename, contentType, sizeBytes, opts) {
420
- const idempotencyKey = opts?.idempotencyKey ?? chunkYER7DIOM_js.generateIdempotencyKey();
421
- return chunkYER7DIOM_js.safe(
421
+ async init(filename, contentType2, sizeBytes, opts) {
422
+ const idempotencyKey = opts?.idempotencyKey ?? chunkIQFFSKE2_js.generateIdempotencyKey();
423
+ return chunkIQFFSKE2_js.safe(
422
424
  this.client.post(
423
425
  "/api/v1/uploads/init",
424
426
  {
425
427
  filename,
426
- content_type: contentType,
428
+ content_type: contentType2,
427
429
  size_bytes: sizeBytes
428
430
  },
429
431
  { idempotencyKey }
@@ -431,7 +433,7 @@ var UploadService = class {
431
433
  );
432
434
  }
433
435
  async confirm(uploadId) {
434
- return chunkYER7DIOM_js.safe(
436
+ return chunkIQFFSKE2_js.safe(
435
437
  this.client.post("/api/v1/uploads/confirm", {
436
438
  upload_id: uploadId
437
439
  })
@@ -450,7 +452,7 @@ var UploadService = class {
450
452
  if (!uploadResponse.ok) {
451
453
  return {
452
454
  ok: false,
453
- error: new chunkNIOUSKQQ_js.CimplifyError(
455
+ error: new chunk4BKOKHE3_js.CimplifyError(
454
456
  "UPLOAD_FAILED",
455
457
  `Upload failed with status ${uploadResponse.status}`,
456
458
  true
@@ -460,7 +462,7 @@ var UploadService = class {
460
462
  } catch (error) {
461
463
  return {
462
464
  ok: false,
463
- error: new chunkNIOUSKQQ_js.CimplifyError(
465
+ error: new chunk4BKOKHE3_js.CimplifyError(
464
466
  "UPLOAD_FAILED",
465
467
  error instanceof Error ? error.message : "Upload failed",
466
468
  true
@@ -477,7 +479,7 @@ var PlacesService = class {
477
479
  this.client = client;
478
480
  }
479
481
  async autocomplete(input, sessionToken) {
480
- return chunkYER7DIOM_js.safe(
482
+ return chunkIQFFSKE2_js.safe(
481
483
  this.client.post("/api/v1/places/autocomplete", {
482
484
  input,
483
485
  sessionToken
@@ -485,7 +487,7 @@ var PlacesService = class {
485
487
  );
486
488
  }
487
489
  async details(placeId, sessionToken) {
488
- return chunkYER7DIOM_js.safe(
490
+ return chunkIQFFSKE2_js.safe(
489
491
  this.client.post("/api/v1/places/details", {
490
492
  place_id: placeId,
491
493
  sessionToken
@@ -504,7 +506,7 @@ var DeliveryService = class {
504
506
  params.set("dropoff_lat", String(dropoffLat));
505
507
  params.set("dropoff_lng", String(dropoffLng));
506
508
  if (country) params.set("country", country);
507
- return chunkYER7DIOM_js.safe(this.client.get(`/api/v1/delivery/fee?${params.toString()}`));
509
+ return chunkIQFFSKE2_js.safe(this.client.get(`/api/v1/delivery/fee?${params.toString()}`));
508
510
  }
509
511
  async getOptions(queryOrLatitude, longitude, legacyOptions) {
510
512
  const query = typeof queryOrLatitude === "number" ? {
@@ -518,12 +520,105 @@ var DeliveryService = class {
518
520
  if (query.country) params.set("country", query.country);
519
521
  if (query.currency) params.set("currency", query.currency);
520
522
  if (query.orderValue != null) params.set("order_value", String(query.orderValue));
521
- return chunkYER7DIOM_js.safe(
523
+ return chunkIQFFSKE2_js.safe(
522
524
  this.client.get(`/api/v1/delivery/options?${params.toString()}`)
523
525
  );
524
526
  }
525
527
  };
526
528
 
529
+ // src/chat-wire.ts
530
+ function senderType(message) {
531
+ if (message.sender_side === "account") return "customer";
532
+ if (message.sender_side === "system") return "system";
533
+ return message.authorship === "ai" ? "bot" : "agent";
534
+ }
535
+ function contentType(message) {
536
+ if (message.content_type === "rich") {
537
+ return message.structured_content?.["kind"] === "reaction" ? "reaction" : "interactive";
538
+ }
539
+ if (message.content_type === "media") {
540
+ const mime = message.media[0]?.mime_type ?? "";
541
+ if (mime.startsWith("image/")) return "image";
542
+ if (mime.startsWith("video/")) return "video";
543
+ if (mime.startsWith("audio/")) return "audio";
544
+ return "document";
545
+ }
546
+ return "text";
547
+ }
548
+ function unresolvedReply(id) {
549
+ return { id, sender_type: "system", preview: "Earlier message" };
550
+ }
551
+ function chatMessagesFromWire(messages) {
552
+ const converted = messages.map((message) => ({
553
+ id: message.id,
554
+ sequence: message.sequence,
555
+ sender_type: senderType(message),
556
+ content: message.content,
557
+ content_type: contentType(message),
558
+ attachments: Array.isArray(message.media) ? message.media : [],
559
+ metadata: message.structured_content ?? {},
560
+ reply_to: message.reply_to_message_id ? unresolvedReply(message.reply_to_message_id) : void 0,
561
+ edited_at: message.edited_at,
562
+ deleted_at: message.deleted_at,
563
+ created_at: message.created_at
564
+ }));
565
+ const byId = new Map(converted.map((message) => [message.id, message]));
566
+ return converted.map((message) => {
567
+ const targetId = message.reply_to?.id;
568
+ const target = targetId ? byId.get(targetId) : void 0;
569
+ if (!targetId || !target) return message;
570
+ return {
571
+ ...message,
572
+ reply_to: {
573
+ id: targetId,
574
+ sender_type: target.sender_type,
575
+ preview: chunkXREIZ56K_js.messagePreview(target.content)
576
+ }
577
+ };
578
+ });
579
+ }
580
+ function chatMessageFromWire(message) {
581
+ return chatMessagesFromWire([message])[0];
582
+ }
583
+ function chatRealtimeUpdateFromWire(envelope) {
584
+ if (envelope.type === "message_created" && envelope.data) {
585
+ return {
586
+ kind: "message",
587
+ message: chatMessageFromWire(envelope.data)
588
+ };
589
+ }
590
+ if (envelope.type === "message_updated" && envelope.data) {
591
+ return {
592
+ kind: "message_updated",
593
+ message: chatMessageFromWire(envelope.data)
594
+ };
595
+ }
596
+ if (envelope.type === "read" && envelope.data) {
597
+ const read = envelope.data;
598
+ if (read.reader_side === "business" && typeof read.occurred_at === "string") {
599
+ return { kind: "business_read", occurredAt: read.occurred_at };
600
+ }
601
+ return { kind: "none" };
602
+ }
603
+ if (envelope.type === "history_imported") {
604
+ return { kind: "reload" };
605
+ }
606
+ if (envelope.type === "ai_mode_changed" && envelope.data) {
607
+ const mode = envelope.data.mode;
608
+ if (mode === "auto" || mode === "assist" || mode === "off") {
609
+ return { kind: "ai_mode", mode };
610
+ }
611
+ return { kind: "none" };
612
+ }
613
+ if (envelope.type === "handoff_requested") {
614
+ return { kind: "handoff" };
615
+ }
616
+ if (envelope.type === "resync") {
617
+ return { kind: "resync", latestSequence: envelope.latest_sequence };
618
+ }
619
+ return { kind: "none" };
620
+ }
621
+
527
622
  // src/internal/events.ts
528
623
  var MUTATION_ERROR = "mutation:error";
529
624
  var AUTH_TOKEN_CHANGED = "auth:token-changed";
@@ -586,16 +681,18 @@ var SupportService = class {
586
681
  * Claim the anonymous session for the verified Link identity: the server
587
682
  * verifies the access token, rotates the session token (adopted here, so
588
683
  * every subsequent request rides the bound session), folds the guest cart
589
- * in, and adopts or merges the widget conversation.
684
+ * in, and opens or reuses the account's canonical direct Chat thread.
590
685
  *
591
686
  * Idempotent and single-flight: concurrent calls share one request, and a
592
687
  * token already claimed by this instance short-circuits without a network
593
- * round trip. Emits `session:claimed` on the client's event channel.
688
+ * round trip. A session already bound to another Link account is rejected
689
+ * rather than transferring that account's cart. Emits `session:claimed` on
690
+ * the client's event channel.
594
691
  */
595
692
  async claimConversation() {
596
693
  const accessToken = this.client.getAccessToken();
597
694
  if (!accessToken) {
598
- return chunkYER7DIOM_js.err(new chunkNIOUSKQQ_js.CimplifyError("not_authenticated", "No access token to claim with"));
695
+ return chunkIQFFSKE2_js.err(new chunk4BKOKHE3_js.CimplifyError("not_authenticated", "No access token to claim with"));
599
696
  }
600
697
  if (this.claimInFlight) {
601
698
  if (this.claimInFlightToken === accessToken) return this.claimInFlight;
@@ -603,12 +700,12 @@ var SupportService = class {
603
700
  return this.claimConversation();
604
701
  }
605
702
  if (this.lastClaimedAccessToken === accessToken) {
606
- return chunkYER7DIOM_js.err(new chunkNIOUSKQQ_js.CimplifyError("already_claimed", "Session already claimed for this token"));
703
+ return chunkIQFFSKE2_js.err(new chunk4BKOKHE3_js.CimplifyError("already_claimed", "Session already claimed for this token"));
607
704
  }
608
705
  const epoch = this.client.getSessionEpoch();
609
706
  this.claimInFlightToken = accessToken;
610
707
  this.claimInFlight = (async () => {
611
- const result = await chunkYER7DIOM_js.safe(
708
+ const result = await chunkIQFFSKE2_js.safe(
612
709
  this.client.post(
613
710
  "/api/v1/support/conversation/claim",
614
711
  { access_token: accessToken },
@@ -617,8 +714,8 @@ var SupportService = class {
617
714
  );
618
715
  if (result.ok) {
619
716
  if (this.client.getSessionEpoch() !== epoch) {
620
- return chunkYER7DIOM_js.err(
621
- new chunkNIOUSKQQ_js.CimplifyError("session_cleared", "Session was cleared during the claim")
717
+ return chunkIQFFSKE2_js.err(
718
+ new chunk4BKOKHE3_js.CimplifyError("session_cleared", "Session was cleared during the claim")
622
719
  );
623
720
  }
624
721
  this.lastClaimedAccessToken = accessToken;
@@ -627,7 +724,7 @@ var SupportService = class {
627
724
  this.client.publishSessionClaimed(this.claimedName);
628
725
  this.client.events.emit(SESSION_CLAIMED, {
629
726
  sessionToken: result.value.session_token,
630
- threadId: result.value.thread_id ?? null,
727
+ threadId: result.value.thread.id,
631
728
  customerName: result.value.customer_name
632
729
  });
633
730
  }
@@ -640,30 +737,54 @@ var SupportService = class {
640
737
  this.claimInFlightToken = null;
641
738
  }
642
739
  }
740
+ /**
741
+ * Retry verified external-history discovery for the current storefront.
742
+ * Launch scope is intentionally WhatsApp-only because it is the registered
743
+ * bidirectional transport. Results are per envelope and omit provider ids,
744
+ * contact values, account ids, and message bodies.
745
+ */
746
+ async claimExternalChats() {
747
+ if (!this.client.getAccessToken()) {
748
+ return chunkIQFFSKE2_js.err(new chunk4BKOKHE3_js.CimplifyError("not_authenticated", "No access token to claim with"));
749
+ }
750
+ return chunkIQFFSKE2_js.safe(
751
+ (async () => {
752
+ const businessId = await this.client.resolveBusinessId();
753
+ return this.client.post("/v1/me/chats/external-claims", {
754
+ business_id: businessId
755
+ });
756
+ })()
757
+ );
758
+ }
643
759
  /**
644
760
  * Peek at the conversation without creating one: resolves the existing
645
761
  * thread (or null) so a page load can hydrate history without littering
646
762
  * the inbox with empty threads.
647
763
  */
648
764
  async peekConversation() {
649
- const result = await chunkYER7DIOM_js.safe(
765
+ const result = await chunkIQFFSKE2_js.safe(
650
766
  this.client.get("/api/v1/support/conversation")
651
767
  );
652
768
  if (!result.ok) return result;
653
- if (!result.value.conversation) return chunkYER7DIOM_js.ok(null);
654
- return chunkYER7DIOM_js.ok({
769
+ if (!result.value.conversation) return chunkIQFFSKE2_js.ok(null);
770
+ return chunkIQFFSKE2_js.ok({
655
771
  conversation: result.value.conversation,
656
- messages: result.value.messages
772
+ messages: chatMessagesFromWire(result.value.messages)
657
773
  });
658
774
  }
659
775
  /** Open (or resume) the customer's widget conversation. */
660
776
  async openConversation(opts) {
661
- const idempotencyKey = opts?.idempotencyKey ?? chunkYER7DIOM_js.generateIdempotencyKey();
662
- return chunkYER7DIOM_js.safe(
777
+ const idempotencyKey = opts?.idempotencyKey ?? chunkIQFFSKE2_js.generateIdempotencyKey();
778
+ const result = await chunkIQFFSKE2_js.safe(
663
779
  this.client.post("/api/v1/support/conversation", void 0, {
664
780
  idempotencyKey
665
781
  })
666
782
  );
783
+ if (!result.ok) return result;
784
+ return chunkIQFFSKE2_js.ok({
785
+ conversation: result.value.conversation,
786
+ messages: chatMessagesFromWire(result.value.messages)
787
+ });
667
788
  }
668
789
  /**
669
790
  * Send a message in the active conversation.
@@ -674,8 +795,8 @@ var SupportService = class {
674
795
  * server resolves it thread-scoped and echoes the quote as `reply_to`.
675
796
  */
676
797
  async sendMessage(content, opts) {
677
- const idempotencyKey = opts?.idempotencyKey ?? chunkYER7DIOM_js.generateIdempotencyKey();
678
- return chunkYER7DIOM_js.safe(
798
+ const idempotencyKey = opts?.idempotencyKey ?? chunkIQFFSKE2_js.generateIdempotencyKey();
799
+ const result = await chunkIQFFSKE2_js.safe(
679
800
  this.client.post(
680
801
  "/api/v1/support/conversation/messages",
681
802
  {
@@ -687,6 +808,7 @@ var SupportService = class {
687
808
  { idempotencyKey }
688
809
  )
689
810
  );
811
+ return result.ok ? chunkIQFFSKE2_js.ok(chatMessageFromWire(result.value.message)) : result;
690
812
  }
691
813
  /**
692
814
  * Poll for messages. `after` pages forward (live updates); `before` pages
@@ -694,26 +816,32 @@ var SupportService = class {
694
816
  */
695
817
  async getMessages(options = {}) {
696
818
  const params = new URLSearchParams();
697
- if (options.after) params.set("after", options.after);
698
- if (options.before) params.set("before", options.before);
819
+ if (options.afterSequence !== void 0)
820
+ params.set("after_sequence", String(options.afterSequence));
821
+ if (options.beforeSequence !== void 0)
822
+ params.set("before_sequence", String(options.beforeSequence));
699
823
  if (options.limit) params.set("limit", String(options.limit));
700
824
  const query = params.toString();
701
825
  const path = query ? `/api/v1/support/conversation/messages?${query}` : "/api/v1/support/conversation/messages";
702
- return chunkYER7DIOM_js.safe(this.client.get(path));
826
+ const result = await chunkIQFFSKE2_js.safe(this.client.get(path));
827
+ return result.ok ? chunkIQFFSKE2_js.ok(chatMessagesFromWire(result.value)) : result;
703
828
  }
704
829
  /** React to a message in the active conversation by its `ChatMessage.id`. */
705
830
  async reactToMessage(messageId, emoji) {
706
- return chunkYER7DIOM_js.safe(
831
+ const clientId = chunkIQFFSKE2_js.generateIdempotencyKey();
832
+ const result = await chunkIQFFSKE2_js.safe(
707
833
  this.client.post(
708
834
  `/api/v1/support/conversation/messages/${messageId}/reactions`,
709
- { emoji }
835
+ { emoji, client_id: clientId },
836
+ { idempotencyKey: clientId }
710
837
  )
711
838
  );
839
+ return result.ok ? chunkIQFFSKE2_js.ok(chatMessageFromWire(result.value.message)) : result;
712
840
  }
713
841
  /** Ask for a person: flags the thread for the merchant's inbox. */
714
842
  async requestHuman(opts) {
715
- const idempotencyKey = opts?.idempotencyKey ?? chunkYER7DIOM_js.generateIdempotencyKey();
716
- return chunkYER7DIOM_js.safe(
843
+ const idempotencyKey = opts?.idempotencyKey ?? chunkIQFFSKE2_js.generateIdempotencyKey();
844
+ return chunkIQFFSKE2_js.safe(
717
845
  this.client.post(
718
846
  "/api/v1/support/conversation/handoff",
719
847
  void 0,
@@ -723,6 +851,456 @@ var SupportService = class {
723
851
  }
724
852
  };
725
853
 
854
+ // src/payment-authorization/providers/paystack.ts
855
+ async function openPaystackPopup(options, signal) {
856
+ if (typeof window === "undefined") {
857
+ return { success: false, error: "PROVIDER_UNAVAILABLE" };
858
+ }
859
+ let PaystackPop;
860
+ try {
861
+ const imported = await import('@paystack/inline-js');
862
+ PaystackPop = imported.default;
863
+ } catch {
864
+ return { success: false, error: "PROVIDER_UNAVAILABLE" };
865
+ }
866
+ return new Promise((resolve) => {
867
+ let settled = false;
868
+ let providerStarted = false;
869
+ const resolveOnce = (result) => {
870
+ if (settled) {
871
+ return;
872
+ }
873
+ settled = true;
874
+ if (signal) {
875
+ signal.removeEventListener("abort", onAbort);
876
+ }
877
+ resolve(result);
878
+ };
879
+ const onAbort = () => {
880
+ resolveOnce({
881
+ success: false,
882
+ // Once Paystack owns the transaction, cancellation is ambiguous. The
883
+ // adapter converts this into submitted so Cimplify polling decides.
884
+ error: providerStarted ? "SUBMISSION_UNKNOWN" : "CANCELLED"
885
+ });
886
+ };
887
+ if (signal?.aborted) {
888
+ resolveOnce({ success: false, error: "CANCELLED" });
889
+ return;
890
+ }
891
+ if (signal) {
892
+ signal.addEventListener("abort", onAbort, { once: true });
893
+ }
894
+ try {
895
+ const popup = new PaystackPop();
896
+ popup.newTransaction({
897
+ key: options.key,
898
+ email: options.email,
899
+ amount: options.amount,
900
+ currency: options.currency,
901
+ reference: options.reference,
902
+ accessCode: options.accessCode,
903
+ onSuccess: (transaction) => {
904
+ resolveOnce({
905
+ success: true,
906
+ reference: transaction.reference ?? options.reference
907
+ });
908
+ },
909
+ onCancel: () => {
910
+ resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
911
+ },
912
+ onError: (error) => {
913
+ resolveOnce({
914
+ success: false,
915
+ error: error?.message || "PAYMENT_FAILED"
916
+ });
917
+ }
918
+ });
919
+ providerStarted = true;
920
+ } catch {
921
+ resolveOnce({ success: false, error: "POPUP_BLOCKED" });
922
+ }
923
+ });
924
+ }
925
+ var PaystackAuthorizationAdapter = class {
926
+ constructor(openPopup = openPaystackPopup) {
927
+ this.openPopup = openPopup;
928
+ this.provider = "paystack";
929
+ }
930
+ async authorize(context) {
931
+ const result = await this.openPopup(
932
+ {
933
+ key: context.action.public_key,
934
+ email: context.email,
935
+ accessCode: context.action.client_secret,
936
+ reference: context.reference || context.action.client_secret,
937
+ currency: context.currency
938
+ },
939
+ context.signal
940
+ );
941
+ if (result.success) {
942
+ return { status: "submitted", reference: result.reference || context.reference };
943
+ }
944
+ if (result.error === "SUBMISSION_UNKNOWN") {
945
+ return { status: "submitted", reference: context.reference };
946
+ }
947
+ if (!result.error || result.error === "CANCELLED" || result.error === "PAYMENT_CANCELLED") {
948
+ return { status: "cancelled" };
949
+ }
950
+ if (result.error === "POPUP_BLOCKED") {
951
+ return {
952
+ status: "failed",
953
+ code: "POPUP_BLOCKED",
954
+ message: "Unable to open the Paystack payment popup. Please allow popups and try again.",
955
+ recoverable: true
956
+ };
957
+ }
958
+ if (result.error === "PROVIDER_UNAVAILABLE") {
959
+ return {
960
+ status: "failed",
961
+ code: "PROVIDER_UNAVAILABLE",
962
+ message: "Paystack is unavailable in this environment.",
963
+ recoverable: false
964
+ };
965
+ }
966
+ return {
967
+ status: "failed",
968
+ code: "PAYMENT_FAILED",
969
+ message: result.error || "Paystack payment could not be submitted.",
970
+ recoverable: true
971
+ };
972
+ }
973
+ };
974
+
975
+ // src/payment-authorization/providers/stripe.ts
976
+ async function loadStripeFromPeer(publicKey, options) {
977
+ try {
978
+ const imported = await import('@stripe/stripe-js');
979
+ return imported.loadStripe ? imported.loadStripe(publicKey, options) : null;
980
+ } catch {
981
+ return null;
982
+ }
983
+ }
984
+ function createStripeModal() {
985
+ if (typeof document === "undefined" || !document.body) return null;
986
+ const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
987
+ const root = document.createElement("div");
988
+ root.setAttribute("role", "dialog");
989
+ root.setAttribute("aria-modal", "true");
990
+ root.setAttribute("aria-label", "Secure card payment");
991
+ Object.assign(root.style, {
992
+ position: "fixed",
993
+ inset: "0",
994
+ zIndex: "2147483647",
995
+ display: "grid",
996
+ placeItems: "center",
997
+ padding: "16px",
998
+ background: "rgba(15, 23, 42, 0.58)"
999
+ });
1000
+ const form = document.createElement("form");
1001
+ Object.assign(form.style, {
1002
+ width: "min(100%, 480px)",
1003
+ maxHeight: "calc(100vh - 32px)",
1004
+ overflow: "auto",
1005
+ borderRadius: "12px",
1006
+ padding: "24px",
1007
+ background: "#fff",
1008
+ boxShadow: "0 24px 80px rgba(15, 23, 42, 0.32)"
1009
+ });
1010
+ const title = document.createElement("h2");
1011
+ title.textContent = "Complete card payment";
1012
+ Object.assign(title.style, { margin: "0 0 18px", font: "600 18px/1.4 system-ui" });
1013
+ const paymentMount = document.createElement("div");
1014
+ paymentMount.setAttribute("data-cimplify-stripe-payment-element", "");
1015
+ const error = document.createElement("p");
1016
+ error.setAttribute("role", "alert");
1017
+ Object.assign(error.style, {
1018
+ minHeight: "20px",
1019
+ margin: "14px 0 0",
1020
+ color: "#b91c1c",
1021
+ font: "14px/1.4 system-ui"
1022
+ });
1023
+ const actions = document.createElement("div");
1024
+ Object.assign(actions.style, {
1025
+ display: "flex",
1026
+ justifyContent: "flex-end",
1027
+ gap: "10px",
1028
+ marginTop: "18px"
1029
+ });
1030
+ const cancelButton = document.createElement("button");
1031
+ cancelButton.type = "button";
1032
+ cancelButton.textContent = "Cancel";
1033
+ Object.assign(cancelButton.style, {
1034
+ border: "1px solid #cbd5e1",
1035
+ borderRadius: "8px",
1036
+ padding: "10px 16px",
1037
+ background: "#fff",
1038
+ color: "#0f172a",
1039
+ cursor: "pointer"
1040
+ });
1041
+ const submitButton = document.createElement("button");
1042
+ submitButton.type = "submit";
1043
+ submitButton.textContent = "Pay securely";
1044
+ Object.assign(submitButton.style, {
1045
+ border: "0",
1046
+ borderRadius: "8px",
1047
+ padding: "10px 16px",
1048
+ background: "#0f172a",
1049
+ color: "#fff",
1050
+ cursor: "pointer"
1051
+ });
1052
+ actions.append(cancelButton, submitButton);
1053
+ form.append(title, paymentMount, error, actions);
1054
+ root.append(form);
1055
+ document.body.append(root);
1056
+ cancelButton.focus();
1057
+ return { root, form, paymentMount, submitButton, cancelButton, error, previousFocus };
1058
+ }
1059
+ function stripeFailure(error) {
1060
+ const message = error?.message || "Stripe could not authorize this payment.";
1061
+ if (error?.code === "payment_intent_authentication_failure" || error?.type === "invalid_request_error") {
1062
+ return {
1063
+ status: "failed",
1064
+ code: "AUTHORIZATION_FAILED",
1065
+ message,
1066
+ recoverable: true
1067
+ };
1068
+ }
1069
+ return { status: "failed", code: "PAYMENT_FAILED", message, recoverable: true };
1070
+ }
1071
+ var StripeAuthorizationAdapter = class {
1072
+ constructor(loadStripe = loadStripeFromPeer) {
1073
+ this.loadStripe = loadStripe;
1074
+ this.provider = "stripe";
1075
+ }
1076
+ async authorize(context) {
1077
+ if (typeof window === "undefined" || typeof document === "undefined" || context.signal?.aborted) {
1078
+ return context.signal?.aborted ? { status: "cancelled" } : {
1079
+ status: "failed",
1080
+ code: "PROVIDER_UNAVAILABLE",
1081
+ message: "Stripe Payment Element is unavailable in this environment.",
1082
+ recoverable: false
1083
+ };
1084
+ }
1085
+ let stripe;
1086
+ try {
1087
+ stripe = await this.loadStripe(
1088
+ context.action.public_key,
1089
+ context.action.provider_account_id ? { stripeAccount: context.action.provider_account_id } : void 0
1090
+ );
1091
+ } catch (error) {
1092
+ return {
1093
+ status: "failed",
1094
+ code: "PROVIDER_UNAVAILABLE",
1095
+ message: error instanceof Error ? error.message : "Stripe.js could not be loaded.",
1096
+ recoverable: true
1097
+ };
1098
+ }
1099
+ if (context.signal?.aborted) {
1100
+ return { status: "cancelled" };
1101
+ }
1102
+ if (!stripe) {
1103
+ return {
1104
+ status: "failed",
1105
+ code: "PROVIDER_UNAVAILABLE",
1106
+ message: "Stripe.js could not be loaded.",
1107
+ recoverable: false
1108
+ };
1109
+ }
1110
+ const modal = createStripeModal();
1111
+ if (!modal) {
1112
+ return {
1113
+ status: "failed",
1114
+ code: "PROVIDER_UNAVAILABLE",
1115
+ message: "Stripe Payment Element could not be mounted.",
1116
+ recoverable: false
1117
+ };
1118
+ }
1119
+ let paymentElement;
1120
+ let elements;
1121
+ try {
1122
+ elements = stripe.elements({ clientSecret: context.action.client_secret });
1123
+ paymentElement = elements.create("payment");
1124
+ paymentElement.mount(modal.paymentMount);
1125
+ } catch {
1126
+ modal.root.remove();
1127
+ modal.previousFocus?.focus();
1128
+ return {
1129
+ status: "failed",
1130
+ code: "PROVIDER_UNAVAILABLE",
1131
+ message: "Stripe Payment Element could not be initialized.",
1132
+ recoverable: false
1133
+ };
1134
+ }
1135
+ if (context.signal?.aborted) {
1136
+ if (paymentElement.destroy) paymentElement.destroy();
1137
+ else paymentElement.unmount?.();
1138
+ modal.root.remove();
1139
+ modal.previousFocus?.focus();
1140
+ return { status: "cancelled" };
1141
+ }
1142
+ return new Promise((resolve) => {
1143
+ let settled = false;
1144
+ let submitting = false;
1145
+ let confirmationStarted = false;
1146
+ const cleanup = () => {
1147
+ context.signal?.removeEventListener("abort", onAbort);
1148
+ modal.form.removeEventListener("submit", onSubmit);
1149
+ modal.cancelButton.removeEventListener("click", onCancel);
1150
+ if (paymentElement.destroy) paymentElement.destroy();
1151
+ else paymentElement.unmount?.();
1152
+ modal.root.remove();
1153
+ modal.previousFocus?.focus();
1154
+ };
1155
+ const settle = (outcome) => {
1156
+ if (settled) return;
1157
+ settled = true;
1158
+ cleanup();
1159
+ resolve(outcome);
1160
+ };
1161
+ const onAbort = () => {
1162
+ if (confirmationStarted) {
1163
+ settle({ status: "submitted", reference: context.reference });
1164
+ return;
1165
+ }
1166
+ settle({ status: "cancelled" });
1167
+ };
1168
+ const onCancel = () => {
1169
+ if (!confirmationStarted) settle({ status: "cancelled" });
1170
+ };
1171
+ const onSubmit = async (event) => {
1172
+ event.preventDefault();
1173
+ if (submitting || settled) return;
1174
+ submitting = true;
1175
+ modal.submitButton.disabled = true;
1176
+ modal.error.textContent = "";
1177
+ try {
1178
+ const validation = elements.submit ? await elements.submit() : {};
1179
+ if (validation.error) {
1180
+ modal.error.textContent = validation.error.message || "Check your payment details.";
1181
+ submitting = false;
1182
+ modal.submitButton.disabled = false;
1183
+ return;
1184
+ }
1185
+ if (settled || context.signal?.aborted) {
1186
+ settle({ status: "cancelled" });
1187
+ return;
1188
+ }
1189
+ confirmationStarted = true;
1190
+ modal.cancelButton.disabled = true;
1191
+ const confirmation = await stripe.confirmPayment({
1192
+ elements,
1193
+ confirmParams: { return_url: context.returnUrl || window.location.href },
1194
+ redirect: "if_required"
1195
+ });
1196
+ if (settled) return;
1197
+ if (confirmation.error) {
1198
+ settle(stripeFailure(confirmation.error));
1199
+ return;
1200
+ }
1201
+ settle({
1202
+ status: "submitted",
1203
+ reference: confirmation.paymentIntent?.id || context.reference
1204
+ });
1205
+ } catch (error) {
1206
+ if (confirmationStarted) {
1207
+ settle({ status: "submitted", reference: context.reference });
1208
+ return;
1209
+ }
1210
+ settle(
1211
+ stripeFailure({
1212
+ message: error instanceof Error ? error.message : "Stripe confirmation failed."
1213
+ })
1214
+ );
1215
+ }
1216
+ };
1217
+ modal.form.addEventListener("submit", onSubmit);
1218
+ modal.cancelButton.addEventListener("click", onCancel);
1219
+ context.signal?.addEventListener("abort", onAbort, { once: true });
1220
+ paymentElement.on?.("escape", onCancel);
1221
+ paymentElement.on?.("loaderror", (event) => {
1222
+ if (confirmationStarted) return;
1223
+ settle({
1224
+ status: "failed",
1225
+ code: "PROVIDER_UNAVAILABLE",
1226
+ message: event?.error?.message || "Stripe Payment Element failed to load.",
1227
+ recoverable: true
1228
+ });
1229
+ });
1230
+ });
1231
+ }
1232
+ };
1233
+
1234
+ // src/payment-authorization/registry.ts
1235
+ function normalizeProvider(provider) {
1236
+ return typeof provider === "string" ? provider.trim().toLowerCase() : "";
1237
+ }
1238
+ var PaymentAuthorizationAdapterRegistry = class {
1239
+ constructor(adapters = []) {
1240
+ this.adapters = /* @__PURE__ */ new Map();
1241
+ for (const adapter of adapters) {
1242
+ this.register(adapter);
1243
+ }
1244
+ }
1245
+ register(adapter) {
1246
+ const provider = normalizeProvider(adapter.provider);
1247
+ if (!provider) {
1248
+ throw new TypeError("Payment authorization adapter provider cannot be empty.");
1249
+ }
1250
+ this.adapters.set(provider, adapter);
1251
+ return this;
1252
+ }
1253
+ unregister(provider) {
1254
+ return this.adapters.delete(normalizeProvider(provider));
1255
+ }
1256
+ get(provider) {
1257
+ return this.adapters.get(normalizeProvider(provider));
1258
+ }
1259
+ supports(provider) {
1260
+ return this.adapters.has(normalizeProvider(provider));
1261
+ }
1262
+ providers() {
1263
+ return [...this.adapters.keys()];
1264
+ }
1265
+ async authorize(action, options) {
1266
+ const provider = normalizeProvider(action.provider);
1267
+ if (!provider) {
1268
+ return {
1269
+ status: "failed",
1270
+ code: "PROVIDER_UNAVAILABLE",
1271
+ message: "Card authorization response did not identify a payment provider.",
1272
+ recoverable: false
1273
+ };
1274
+ }
1275
+ const adapter = this.adapters.get(provider);
1276
+ if (!adapter) {
1277
+ return {
1278
+ status: "failed",
1279
+ code: "PROVIDER_UNAVAILABLE",
1280
+ message: `Card authorization provider '${provider}' is not supported by this SDK client.`,
1281
+ recoverable: false
1282
+ };
1283
+ }
1284
+ try {
1285
+ const normalizedAction = action.provider === provider ? action : { ...action, provider };
1286
+ return await adapter.authorize({ ...options, action: normalizedAction });
1287
+ } catch (error) {
1288
+ return {
1289
+ status: "failed",
1290
+ code: "PAYMENT_FAILED",
1291
+ message: error instanceof Error ? error.message : `Card authorization provider '${provider}' failed unexpectedly.`,
1292
+ recoverable: true
1293
+ };
1294
+ }
1295
+ }
1296
+ };
1297
+ function createDefaultPaymentAuthorizationAdapterRegistry() {
1298
+ return new PaymentAuthorizationAdapterRegistry([
1299
+ new PaystackAuthorizationAdapter(),
1300
+ new StripeAuthorizationAdapter()
1301
+ ]);
1302
+ }
1303
+
726
1304
  // src/client.ts
727
1305
  var SESSION_TOKEN_STORAGE_KEY = "cimplify_session_token";
728
1306
  var SESSION_SYNC_STORAGE_KEY = "cimplify_session_sync";
@@ -774,16 +1352,16 @@ function isRetryable(error) {
774
1352
  if (error instanceof DOMException && error.name === "AbortError") {
775
1353
  return true;
776
1354
  }
777
- if (error instanceof chunkNIOUSKQQ_js.CimplifyError) {
1355
+ if (error instanceof chunk4BKOKHE3_js.CimplifyError) {
778
1356
  return error.retryable;
779
1357
  }
780
1358
  return false;
781
1359
  }
782
1360
  function toNetworkError(error, isTestMode) {
783
1361
  if (error instanceof DOMException && error.name === "AbortError") {
784
- return chunkNIOUSKQQ_js.enrichError(
785
- new chunkNIOUSKQQ_js.CimplifyError(
786
- chunkNIOUSKQQ_js.ErrorCode.TIMEOUT,
1362
+ return chunk4BKOKHE3_js.enrichError(
1363
+ new chunk4BKOKHE3_js.CimplifyError(
1364
+ chunk4BKOKHE3_js.ErrorCode.TIMEOUT,
787
1365
  "Request timed out. Please check your connection and try again.",
788
1366
  true
789
1367
  ),
@@ -791,21 +1369,21 @@ function toNetworkError(error, isTestMode) {
791
1369
  );
792
1370
  }
793
1371
  if (error instanceof TypeError && error.message.includes("fetch")) {
794
- return chunkNIOUSKQQ_js.enrichError(
795
- new chunkNIOUSKQQ_js.CimplifyError(
796
- chunkNIOUSKQQ_js.ErrorCode.NETWORK_ERROR,
1372
+ return chunk4BKOKHE3_js.enrichError(
1373
+ new chunk4BKOKHE3_js.CimplifyError(
1374
+ chunk4BKOKHE3_js.ErrorCode.NETWORK_ERROR,
797
1375
  "Network error. Please check your internet connection.",
798
1376
  true
799
1377
  ),
800
1378
  { isTestMode }
801
1379
  );
802
1380
  }
803
- if (error instanceof chunkNIOUSKQQ_js.CimplifyError) {
804
- return chunkNIOUSKQQ_js.enrichError(error, { isTestMode });
1381
+ if (error instanceof chunk4BKOKHE3_js.CimplifyError) {
1382
+ return chunk4BKOKHE3_js.enrichError(error, { isTestMode });
805
1383
  }
806
- return chunkNIOUSKQQ_js.enrichError(
807
- new chunkNIOUSKQQ_js.CimplifyError(
808
- chunkNIOUSKQQ_js.ErrorCode.UNKNOWN_ERROR,
1384
+ return chunk4BKOKHE3_js.enrichError(
1385
+ new chunk4BKOKHE3_js.CimplifyError(
1386
+ chunk4BKOKHE3_js.ErrorCode.UNKNOWN_ERROR,
809
1387
  error instanceof Error ? error.message : "An unknown error occurred",
810
1388
  false
811
1389
  ),
@@ -847,6 +1425,7 @@ var CimplifyClient = class {
847
1425
  this.context = {};
848
1426
  this.businessId = null;
849
1427
  this.businessIdResolvePromise = null;
1428
+ this.publicKeyEpoch = 0;
850
1429
  this.inflightRequests = /* @__PURE__ */ new Map();
851
1430
  /**
852
1431
  * App-level event channel. Subscribe to surface mutation rollbacks, outbox
@@ -865,6 +1444,7 @@ var CimplifyClient = class {
865
1444
  this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
866
1445
  this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
867
1446
  this.hooks = config.hooks ?? {};
1447
+ this.authorizationAdapters = config.authorizationAdapterRegistry ?? createDefaultPaymentAuthorizationAdapterRegistry();
868
1448
  this.fetchImpl = (config.fetch ?? globalThis.fetch).bind(globalThis);
869
1449
  this.sessionToken = this.loadSessionToken() ?? this.mintSessionToken();
870
1450
  this.watchCrossTabSession();
@@ -930,7 +1510,8 @@ var CimplifyClient = class {
930
1510
  * refresh restore) binds the anonymous session so the conversation, cart
931
1511
  * and orders survive a cleared browser. Best-effort by design — a failed
932
1512
  * claim leaves the session anonymous, exactly as it was. Called by the
933
- * auth flows, never by the raw token setter.
1513
+ * merchant auth flows, never by the raw token setter. Global Link clients
1514
+ * have no storefront tenant and therefore activate without this step.
934
1515
  */
935
1516
  autoClaimChatIdentity() {
936
1517
  if (typeof window === "undefined" || !this.sessionToken || !this.accessToken) return;
@@ -972,9 +1553,28 @@ var CimplifyClient = class {
972
1553
  getSessionEpoch() {
973
1554
  return this.sessionEpoch;
974
1555
  }
1556
+ /** Monotonic fence for async work scoped to the current storefront tenant. */
1557
+ getPublicKeyEpoch() {
1558
+ return this.publicKeyEpoch;
1559
+ }
975
1560
  getSessionToken() {
976
1561
  return this.sessionToken;
977
1562
  }
1563
+ /** Whether the current transport session is known to have been claimed for
1564
+ * a Link identity, even when its in-memory bearer is absent after reload or
1565
+ * a failed refresh. The sync marker is written only after canonical claim
1566
+ * rotation and is replaced with `cleared` when a fresh session is minted. */
1567
+ hasClaimedSessionAuthority() {
1568
+ if (!this.sessionToken) return false;
1569
+ try {
1570
+ const raw = this.safeStorage()?.getItem(SESSION_SYNC_STORAGE_KEY);
1571
+ if (!raw) return false;
1572
+ const payload = JSON.parse(raw);
1573
+ return payload.type === "claimed" && payload.token === this.sessionToken;
1574
+ } catch {
1575
+ return false;
1576
+ }
1577
+ }
978
1578
  publishSessionClaimed(customerName) {
979
1579
  if (!this.sessionToken) return;
980
1580
  this.broadcastSessionSync({
@@ -1081,10 +1681,18 @@ var CimplifyClient = class {
1081
1681
  // X-API-Key and the business can't be resolved.
1082
1682
  setPublicKey(publicKey) {
1083
1683
  const normalized = publicKey.trim();
1084
- if (!normalized) {
1085
- return;
1086
- }
1684
+ if (normalized === this.publicKey) return;
1087
1685
  this.publicKey = normalized;
1686
+ this.publicKeyEpoch += 1;
1687
+ this.businessId = null;
1688
+ this.businessIdResolvePromise = null;
1689
+ this.inflightRequests.clear();
1690
+ this._activity?.resetSession();
1691
+ delete this.context.location_id;
1692
+ if (this.accessToken !== null) {
1693
+ this.setAccessToken(null);
1694
+ }
1695
+ this.events.emit(SESSION_CLEARED, {});
1088
1696
  }
1089
1697
  getBusinessId() {
1090
1698
  return this.businessId;
@@ -1096,22 +1704,33 @@ var CimplifyClient = class {
1096
1704
  if (this.businessIdResolvePromise) {
1097
1705
  return this.businessIdResolvePromise;
1098
1706
  }
1099
- this.businessIdResolvePromise = (async () => {
1707
+ const publicKeyEpoch = this.publicKeyEpoch;
1708
+ const resolution = (async () => {
1100
1709
  const result = await this.business.getInfo();
1101
1710
  if (!result.ok || !result.value?.id) {
1102
- throw new chunkNIOUSKQQ_js.CimplifyError(
1103
- chunkNIOUSKQQ_js.ErrorCode.NOT_FOUND,
1711
+ throw new chunk4BKOKHE3_js.CimplifyError(
1712
+ chunk4BKOKHE3_js.ErrorCode.NOT_FOUND,
1104
1713
  "Unable to resolve business ID from the current public key.",
1105
1714
  true
1106
1715
  );
1107
1716
  }
1717
+ if (this.publicKeyEpoch !== publicKeyEpoch) {
1718
+ throw new chunk4BKOKHE3_js.CimplifyError(
1719
+ chunk4BKOKHE3_js.ErrorCode.TENANT_CONTEXT_CHANGED,
1720
+ "The storefront tenant changed while resolving its business identity.",
1721
+ true
1722
+ );
1723
+ }
1108
1724
  this.businessId = result.value.id;
1109
1725
  return result.value.id;
1110
1726
  })();
1727
+ this.businessIdResolvePromise = resolution;
1111
1728
  try {
1112
- return await this.businessIdResolvePromise;
1729
+ return await resolution;
1113
1730
  } finally {
1114
- this.businessIdResolvePromise = null;
1731
+ if (this.businessIdResolvePromise === resolution) {
1732
+ this.businessIdResolvePromise = null;
1733
+ }
1115
1734
  }
1116
1735
  }
1117
1736
  async authedFetch(url, options, timeoutMs) {
@@ -1240,7 +1859,7 @@ var CimplifyClient = class {
1240
1859
  if (response.status >= 400 && response.status < 500) {
1241
1860
  this.hooks.onRequestError?.({
1242
1861
  ...context,
1243
- error: new chunkNIOUSKQQ_js.CimplifyError(
1862
+ error: new chunk4BKOKHE3_js.CimplifyError(
1244
1863
  `HTTP_${response.status}`,
1245
1864
  `Request failed with status ${response.status}`,
1246
1865
  false
@@ -1266,7 +1885,7 @@ var CimplifyClient = class {
1266
1885
  }
1267
1886
  this.hooks.onRequestError?.({
1268
1887
  ...context,
1269
- error: new chunkNIOUSKQQ_js.CimplifyError(
1888
+ error: new chunk4BKOKHE3_js.CimplifyError(
1270
1889
  "SERVER_ERROR",
1271
1890
  `Server error ${response.status} after ${retryCount} retries`,
1272
1891
  false
@@ -1314,7 +1933,13 @@ var CimplifyClient = class {
1314
1933
  }
1315
1934
  getDedupeKey(type, payload, cacheOptions) {
1316
1935
  const cacheKey = cacheOptions ? `|${JSON.stringify(cacheOptions)}` : "";
1317
- return `${type}:${JSON.stringify(payload)}${cacheKey}`;
1936
+ const identityKey = JSON.stringify([
1937
+ this.publicKey.trim(),
1938
+ this.publicKeyEpoch,
1939
+ this.sessionEpoch,
1940
+ this.context.location_id ?? null
1941
+ ]);
1942
+ return `${type}:${identityKey}:${JSON.stringify(payload)}${cacheKey}`;
1318
1943
  }
1319
1944
  async deduplicatedRequest(key, requestFn) {
1320
1945
  const existing = this.inflightRequests.get(key);
@@ -1322,7 +1947,9 @@ var CimplifyClient = class {
1322
1947
  return existing;
1323
1948
  }
1324
1949
  const request = requestFn().finally(() => {
1325
- this.inflightRequests.delete(key);
1950
+ if (this.inflightRequests.get(key) === request) {
1951
+ this.inflightRequests.delete(key);
1952
+ }
1326
1953
  });
1327
1954
  this.inflightRequests.set(key, request);
1328
1955
  return request;
@@ -1418,12 +2045,12 @@ var CimplifyClient = class {
1418
2045
  const retryable = typeof json.error === "object" && typeof json.error?.retryable === "boolean" ? json.error.retryable : false;
1419
2046
  if (errorCode === "IDEMPOTENCY_MISMATCH") {
1420
2047
  const originalCreatedAt = typeof json.error === "object" && json.error?.original_created_at || "";
1421
- throw new chunkNIOUSKQQ_js.IdempotencyMismatchError(errorMessage, originalCreatedAt);
2048
+ throw new chunk4BKOKHE3_js.IdempotencyMismatchError(errorMessage, originalCreatedAt);
1422
2049
  }
1423
- const error = chunkNIOUSKQQ_js.enrichError(new chunkNIOUSKQQ_js.CimplifyError(errorCode, errorMessage, retryable), {
2050
+ const error = chunk4BKOKHE3_js.enrichError(new chunk4BKOKHE3_js.CimplifyError(errorCode, errorMessage, retryable), {
1424
2051
  isTestMode: this.isTestMode()
1425
2052
  });
1426
- if ((response.status === 401 || error.code === chunkNIOUSKQQ_js.ErrorCode.UNAUTHORIZED) && this.accessToken) {
2053
+ if ((response.status === 401 || error.code === chunk4BKOKHE3_js.ErrorCode.UNAUTHORIZED) && this.accessToken) {
1427
2054
  console.warn(
1428
2055
  "[Cimplify] Received 401 Unauthorized. Access token may be missing/expired. Refresh authentication and retry the request."
1429
2056
  );
@@ -1431,8 +2058,8 @@ var CimplifyClient = class {
1431
2058
  throw error;
1432
2059
  }
1433
2060
  if (json?.success === false || json?.error?.code || json?.error?.message) {
1434
- const error = chunkNIOUSKQQ_js.enrichError(
1435
- new chunkNIOUSKQQ_js.CimplifyError(
2061
+ const error = chunk4BKOKHE3_js.enrichError(
2062
+ new chunk4BKOKHE3_js.CimplifyError(
1436
2063
  json.error?.code || "API_ERROR",
1437
2064
  json.error?.message || "An error occurred",
1438
2065
  json.error?.retryable || false
@@ -1448,25 +2075,25 @@ var CimplifyClient = class {
1448
2075
  }
1449
2076
  get catalogue() {
1450
2077
  if (!this._catalogue) {
1451
- this._catalogue = new chunkYER7DIOM_js.CatalogueQueries(this);
2078
+ this._catalogue = new chunkIQFFSKE2_js.CatalogueQueries(this);
1452
2079
  }
1453
2080
  return this._catalogue;
1454
2081
  }
1455
2082
  get cart() {
1456
2083
  if (!this._cart) {
1457
- this._cart = new chunkYER7DIOM_js.CartOperations(this);
2084
+ this._cart = new chunkIQFFSKE2_js.CartOperations(this);
1458
2085
  }
1459
2086
  return this._cart;
1460
2087
  }
1461
2088
  get checkout() {
1462
2089
  if (!this._checkout) {
1463
- this._checkout = new chunkYER7DIOM_js.CheckoutService(this);
2090
+ this._checkout = new chunkIQFFSKE2_js.CheckoutService(this);
1464
2091
  }
1465
2092
  return this._checkout;
1466
2093
  }
1467
2094
  get orders() {
1468
2095
  if (!this._orders) {
1469
- this._orders = new chunkYER7DIOM_js.OrderQueries(this);
2096
+ this._orders = new chunkIQFFSKE2_js.OrderQueries(this);
1470
2097
  }
1471
2098
  return this._orders;
1472
2099
  }
@@ -1496,43 +2123,43 @@ var CimplifyClient = class {
1496
2123
  }
1497
2124
  get link() {
1498
2125
  if (!this._link) {
1499
- this._link = new chunkYER7DIOM_js.LinkService(this);
2126
+ this._link = new chunkIQFFSKE2_js.LinkService(this);
1500
2127
  }
1501
2128
  return this._link;
1502
2129
  }
1503
2130
  get auth() {
1504
2131
  if (!this._auth) {
1505
- this._auth = new chunkYER7DIOM_js.AuthService(this);
2132
+ this._auth = new chunkIQFFSKE2_js.AuthService(this);
1506
2133
  }
1507
2134
  return this._auth;
1508
2135
  }
1509
2136
  get business() {
1510
2137
  if (!this._business) {
1511
- this._business = new chunkYER7DIOM_js.BusinessService(this);
2138
+ this._business = new chunkIQFFSKE2_js.BusinessService(this);
1512
2139
  }
1513
2140
  return this._business;
1514
2141
  }
1515
2142
  get inventory() {
1516
2143
  if (!this._inventory) {
1517
- this._inventory = new chunkYER7DIOM_js.InventoryService(this);
2144
+ this._inventory = new chunkIQFFSKE2_js.InventoryService(this);
1518
2145
  }
1519
2146
  return this._inventory;
1520
2147
  }
1521
2148
  get scheduling() {
1522
2149
  if (!this._scheduling) {
1523
- this._scheduling = new chunkYER7DIOM_js.SchedulingService(this);
2150
+ this._scheduling = new chunkIQFFSKE2_js.SchedulingService(this);
1524
2151
  }
1525
2152
  return this._scheduling;
1526
2153
  }
1527
2154
  get lite() {
1528
2155
  if (!this._lite) {
1529
- this._lite = new chunkYER7DIOM_js.LiteService(this);
2156
+ this._lite = new chunkIQFFSKE2_js.LiteService(this);
1530
2157
  }
1531
2158
  return this._lite;
1532
2159
  }
1533
2160
  get fx() {
1534
2161
  if (!this._fx) {
1535
- this._fx = new chunkYER7DIOM_js.FxService(this);
2162
+ this._fx = new chunkIQFFSKE2_js.FxService(this);
1536
2163
  }
1537
2164
  return this._fx;
1538
2165
  }
@@ -1567,7 +2194,7 @@ var CimplifyClient = class {
1567
2194
  if (businessId) {
1568
2195
  this.setBusinessId(businessId);
1569
2196
  }
1570
- return chunkYER7DIOM_js.createElements(this, businessId ?? this.businessId ?? void 0, options);
2197
+ return chunkIQFFSKE2_js.createElements(this, businessId ?? this.businessId ?? void 0, options);
1571
2198
  }
1572
2199
  };
1573
2200
  function createCimplifyClient(config = {}) {
@@ -1579,11 +2206,16 @@ exports.ActivityService = ActivityService;
1579
2206
  exports.CimplifyClient = CimplifyClient;
1580
2207
  exports.DeliveryService = DeliveryService;
1581
2208
  exports.MUTATION_ERROR = MUTATION_ERROR;
2209
+ exports.PaymentAuthorizationAdapterRegistry = PaymentAuthorizationAdapterRegistry;
2210
+ exports.PaystackAuthorizationAdapter = PaystackAuthorizationAdapter;
1582
2211
  exports.PlacesService = PlacesService;
1583
2212
  exports.SESSION_CLAIMED = SESSION_CLAIMED;
1584
2213
  exports.SESSION_CLEARED = SESSION_CLEARED;
1585
2214
  exports.StoreCreditQueries = StoreCreditQueries;
2215
+ exports.StripeAuthorizationAdapter = StripeAuthorizationAdapter;
1586
2216
  exports.SubscriptionService = SubscriptionService;
1587
2217
  exports.SupportService = SupportService;
1588
2218
  exports.UploadService = UploadService;
2219
+ exports.chatRealtimeUpdateFromWire = chatRealtimeUpdateFromWire;
1589
2220
  exports.createCimplifyClient = createCimplifyClient;
2221
+ exports.createDefaultPaymentAuthorizationAdapterRegistry = createDefaultPaymentAuthorizationAdapterRegistry;