@buni.ai/chatbot-angular 1.0.21 → 1.0.23

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.
@@ -1,9 +1,9 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { BuniChatOptions, BuniChatState, CustomerData, SessionVariables } from '@buni.ai/chatbot-core';
3
3
  export declare class BuniChatService {
4
- private widget;
5
- private stateSubject;
6
- private readySubject;
4
+ private readonly widget;
5
+ private readonly stateSubject;
6
+ private readonly readySubject;
7
7
  constructor();
8
8
  initialize(options: BuniChatOptions): Promise<void>;
9
9
  private setupEventListeners;
@@ -1 +1 @@
1
- {"version":3,"file":"buni-chat.service.d.ts","sourceRoot":"","sources":["../src/buni-chat.service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,UAAU,EAAE,MAAM,MAAM,CAAC;AAEnD,OAAO,EAEL,eAAe,EACf,aAAa,EACb,YAAY,EACZ,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAE/B,qBAGa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,YAAY,CAKjB;IACH,OAAO,CAAC,YAAY,CAAuC;;IAMrD,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAYzD,OAAO,CAAC,mBAAmB;IAa3B,IAAI,MAAM,IAAI,UAAU,CAAC,aAAa,CAAC,CAEtC;IAED,IAAI,QAAQ,IAAI,UAAU,CAAC,OAAO,CAAC,CAElC;IAED,IAAI,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAIjC;IAED,IAAI,YAAY,IAAI,UAAU,CAAC,MAAM,CAAC,CAIrC;IAGD,IAAI,IAAI,IAAI;IAIZ,IAAI,IAAI,IAAI;IAIZ,MAAM,IAAI,IAAI;IAId,QAAQ,IAAI,IAAI;IAIhB,QAAQ,IAAI,IAAI;IAKhB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAIzC,eAAe,IAAI,YAAY,GAAG,IAAI;IAItC,mBAAmB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAItD,mBAAmB,IAAI,gBAAgB,GAAG,IAAI;IAK9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlC,SAAS,IAAI,IAAI;IAKjB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI;IAIlD,IAAI,IAAI,IAAI;IAIZ,KAAK,IAAI,IAAI;IAKb,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAI3C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI;IAK7C,OAAO,IAAI,IAAI;CAKhB"}
1
+ {"version":3,"file":"buni-chat.service.d.ts","sourceRoot":"","sources":["../src/buni-chat.service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,UAAU,EAAE,MAAM,MAAM,CAAC;AAEnD,OAAO,EAEL,eAAe,EACf,aAAa,EACb,YAAY,EACZ,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAE/B,qBAGa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAK1B;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuC;;IAM9D,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAWzD,OAAO,CAAC,mBAAmB;IAgB3B,IAAI,MAAM,IAAI,UAAU,CAAC,aAAa,CAAC,CAEtC;IAED,IAAI,QAAQ,IAAI,UAAU,CAAC,OAAO,CAAC,CAElC;IAED,IAAI,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,CAIjC;IAED,IAAI,YAAY,IAAI,UAAU,CAAC,MAAM,CAAC,CAIrC;IAGD,IAAI,IAAI,IAAI;IAIZ,IAAI,IAAI,IAAI;IAIZ,MAAM,IAAI,IAAI;IAId,QAAQ,IAAI,IAAI;IAIhB,QAAQ,IAAI,IAAI;IAKhB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAIzC,eAAe,IAAI,YAAY,GAAG,IAAI;IAItC,mBAAmB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAItD,mBAAmB,IAAI,gBAAgB,GAAG,IAAI;IAK9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlC,SAAS,IAAI,IAAI;IAKjB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI;IAIlD,IAAI,IAAI,IAAI;IAIZ,KAAK,IAAI,IAAI;IAKb,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAI3C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI;IAK7C,OAAO,IAAI,IAAI;CAKhB"}
package/dist/index.esm.js CHANGED
@@ -38,6 +38,8 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
38
38
 
39
39
  // Default BuniAI platform URL
40
40
  const BUNI_PLATFORM_URL = "https://www.buni.ai";
41
+ const PARENT_HANDSHAKE_TYPE = "buni:parent_handshake";
42
+ const IFRAME_HANDSHAKE_ACK_TYPE = "buni:iframe_handshake_ack";
41
43
  // Core widget loader class
42
44
  class BuniChatWidget {
43
45
  constructor() {
@@ -53,6 +55,9 @@ class BuniChatWidget {
53
55
  this.chatIframe = null;
54
56
  this.customerData = null;
55
57
  this.sessionVariables = null;
58
+ this.chatTargetOrigin = null;
59
+ this.handshakeComplete = false;
60
+ this.outboundMessageQueue = [];
56
61
  }
57
62
  async initialize(options) {
58
63
  this.options = options;
@@ -239,7 +244,18 @@ class BuniChatWidget {
239
244
  chatIframe.onload = () => {
240
245
  // Set visibility hidden after iframe loads to allow content initialization
241
246
  chatIframe.style.visibility = "hidden";
247
+ // Compute the iframe origin once we know the final src.
248
+ try {
249
+ this.chatTargetOrigin = new URL(chatIframe.src).origin;
250
+ }
251
+ catch (_a) {
252
+ this.chatTargetOrigin = this.getBaseUrl();
253
+ }
254
+ // Reset handshake state for this iframe instance.
255
+ this.handshakeComplete = false;
256
+ this.outboundMessageQueue = [];
242
257
  this.setupPostMessageAPI(chatIframe);
258
+ this.sendParentHandshake();
243
259
  // Now that both iframes are loaded, resolve the promise
244
260
  this.widgetElement = container;
245
261
  this.triggerIframe = triggerIframe;
@@ -251,13 +267,17 @@ class BuniChatWidget {
251
267
  };
252
268
  // Listen for trigger and connection events
253
269
  window.addEventListener("message", (event) => {
254
- var _a;
255
- if (!event.data.type)
270
+ var _a, _b;
271
+ const payload = event.data;
272
+ if (!payload || typeof payload !== "object")
273
+ return;
274
+ if (typeof payload.type !== "string")
256
275
  return;
257
- switch (event.data.type) {
276
+ switch (payload.type) {
258
277
  case "connection_ready":
259
278
  // Check if message is from chat iframe
260
- if (event.source === ((_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow)) {
279
+ if (event.source === ((_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) &&
280
+ event.origin === this.getBaseUrl()) {
261
281
  // Connection is ready, show the trigger button
262
282
  if (container && !config.hideDefaultTrigger) {
263
283
  container.style.display = "block";
@@ -271,7 +291,10 @@ class BuniChatWidget {
271
291
  }
272
292
  break;
273
293
  case "trigger_clicked":
274
- this.openChat();
294
+ if (event.source === ((_b = this.triggerIframe) === null || _b === void 0 ? void 0 : _b.contentWindow) &&
295
+ event.origin === window.location.origin) {
296
+ this.openChat();
297
+ }
275
298
  break;
276
299
  }
277
300
  });
@@ -386,11 +409,15 @@ class BuniChatWidget {
386
409
  </div>
387
410
  <script>
388
411
  function handleClick() {
389
- window.parent.postMessage({ type: 'trigger_clicked' }, '*');
412
+ // Trigger iframe is same-origin with parent (about:blank + document.write),
413
+ // so we can safely target the parent's origin.
414
+ window.parent.postMessage({ type: 'trigger_clicked' }, window.location.origin);
390
415
  }
391
416
 
392
417
  // Listen for unread count updates
393
418
  window.addEventListener('message', function(event) {
419
+ if (event.source !== window.parent) return;
420
+ if (event.origin !== window.location.origin) return;
394
421
  if (event.data.type === 'updateUnreadCount') {
395
422
  const badge = document.getElementById('badge');
396
423
  const count = event.data.count || 0;
@@ -538,12 +565,24 @@ class BuniChatWidget {
538
565
  // Listen for messages from the iframe
539
566
  window.addEventListener("message", (event) => {
540
567
  var _a;
568
+ if (event.source !== iframe.contentWindow) {
569
+ return;
570
+ }
541
571
  // Verify origin for security
542
572
  if (event.origin !== this.getBaseUrl()) {
543
573
  return;
544
574
  }
545
- const { type, data } = event.data;
575
+ const payload = event.data;
576
+ if (!payload || typeof payload !== "object")
577
+ return;
578
+ const { type, data } = payload;
579
+ if (typeof type !== "string")
580
+ return;
546
581
  switch (type) {
582
+ case IFRAME_HANDSHAKE_ACK_TYPE:
583
+ this.handshakeComplete = true;
584
+ this.flushOutboundMessageQueue();
585
+ break;
547
586
  case "ready":
548
587
  this.state.isLoaded = true;
549
588
  this.emit("ready", data);
@@ -575,7 +614,7 @@ class BuniChatWidget {
575
614
  // Update unread count in trigger
576
615
  this.state.unreadCount++;
577
616
  if ((_a = this.triggerIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) {
578
- this.triggerIframe.contentWindow.postMessage({ type: "updateUnreadCount", count: this.state.unreadCount }, "*");
617
+ this.triggerIframe.contentWindow.postMessage({ type: "updateUnreadCount", count: this.state.unreadCount }, window.location.origin);
579
618
  }
580
619
  break;
581
620
  case "customer_data_updated":
@@ -595,17 +634,69 @@ class BuniChatWidget {
595
634
  }
596
635
  });
597
636
  }
637
+ sendParentHandshake() {
638
+ var _a, _b;
639
+ const iframeWindow = (_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow;
640
+ if (!iframeWindow)
641
+ return;
642
+ const targetOrigin = (_b = this.chatTargetOrigin) !== null && _b !== void 0 ? _b : this.getBaseUrl();
643
+ try {
644
+ iframeWindow.postMessage({
645
+ type: PARENT_HANDSHAKE_TYPE,
646
+ data: { origin: window.location.origin },
647
+ }, targetOrigin);
648
+ }
649
+ catch (_c) {
650
+ // Best-effort; iframe may fall back to document.referrer for origin discovery.
651
+ }
652
+ }
653
+ flushOutboundMessageQueue() {
654
+ var _a, _b;
655
+ const iframeWindow = (_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow;
656
+ if (!iframeWindow)
657
+ return;
658
+ const targetOrigin = (_b = this.chatTargetOrigin) !== null && _b !== void 0 ? _b : this.getBaseUrl();
659
+ const queued = this.outboundMessageQueue;
660
+ if (queued.length === 0)
661
+ return;
662
+ this.outboundMessageQueue = [];
663
+ for (const msg of queued) {
664
+ iframeWindow.postMessage({ type: msg.type, data: msg.data }, targetOrigin);
665
+ }
666
+ }
598
667
  postMessageToWidget(type, data) {
599
- var _a;
600
- if ((_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow) {
601
- this.chatIframe.contentWindow.postMessage({ type, data }, this.getBaseUrl());
668
+ var _a, _b;
669
+ const iframeWindow = (_a = this.chatIframe) === null || _a === void 0 ? void 0 : _a.contentWindow;
670
+ if (!iframeWindow)
671
+ return;
672
+ const targetOrigin = (_b = this.chatTargetOrigin) !== null && _b !== void 0 ? _b : this.getBaseUrl();
673
+ // Ensure handshake is sent before any other commands; queue until ack.
674
+ if (!this.handshakeComplete && type !== PARENT_HANDSHAKE_TYPE) {
675
+ const queue = this.outboundMessageQueue;
676
+ if (queue.length < 50) {
677
+ queue.push({ type, data });
678
+ }
679
+ else {
680
+ this.outboundMessageQueue = [...queue.slice(-49), { type, data }];
681
+ }
682
+ this.sendParentHandshake();
683
+ return;
602
684
  }
685
+ iframeWindow.postMessage({ type, data }, targetOrigin);
603
686
  }
604
687
  getBaseUrl() {
688
+ var _a, _b;
605
689
  // Return the base URL for the BuniAI platform
606
690
  // Priority: 1. config.baseUrl, 2. global BUNI_API_URL, 3. default platform URL
691
+ const configUrl = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.baseUrl;
607
692
  const globalUrl = globalThis.BUNI_API_URL;
608
- return globalUrl || BUNI_PLATFORM_URL;
693
+ const rawUrl = configUrl || globalUrl || BUNI_PLATFORM_URL;
694
+ try {
695
+ return new URL(rawUrl).origin;
696
+ }
697
+ catch (_c) {
698
+ return rawUrl;
699
+ }
609
700
  }
610
701
  destroy() {
611
702
  this.postMessageToWidget("destroy");
@@ -753,7 +844,6 @@ let BuniChatService = class BuniChatService {
753
844
  async initialize(options) {
754
845
  try {
755
846
  await this.widget.initialize({ ...options, framework: "angular" });
756
- this.readySubject.next(true);
757
847
  }
758
848
  catch (error) {
759
849
  console.error('Failed to initialize BuniChat widget:', error);
@@ -765,6 +855,9 @@ let BuniChatService = class BuniChatService {
765
855
  const updateState = () => {
766
856
  this.stateSubject.next(this.widget.getState());
767
857
  };
858
+ this.widget.on('connection_ready', () => {
859
+ this.readySubject.next(true);
860
+ });
768
861
  this.widget.on('ready', updateState);
769
862
  this.widget.on('visibility_changed', updateState);
770
863
  this.widget.on('minimized', updateState);