@weldsuite/helpdesk-widget-sdk 1.0.2

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.
@@ -0,0 +1,2768 @@
1
+ import { Component, Input, Injectable, NgModule } from '@angular/core';
2
+
3
+ /******************************************************************************
4
+ Copyright (c) Microsoft Corporation.
5
+
6
+ Permission to use, copy, modify, and/or distribute this software for any
7
+ purpose with or without fee is hereby granted.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
16
+ ***************************************************************************** */
17
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
18
+
19
+
20
+ function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
21
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
22
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
23
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
24
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
25
+ var _, done = false;
26
+ for (var i = decorators.length - 1; i >= 0; i--) {
27
+ var context = {};
28
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
29
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
30
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
31
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
32
+ if (kind === "accessor") {
33
+ if (result === void 0) continue;
34
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
35
+ if (_ = accept(result.get)) descriptor.get = _;
36
+ if (_ = accept(result.set)) descriptor.set = _;
37
+ if (_ = accept(result.init)) initializers.unshift(_);
38
+ }
39
+ else if (_ = accept(result)) {
40
+ if (kind === "field") initializers.unshift(_);
41
+ else descriptor[key] = _;
42
+ }
43
+ }
44
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
45
+ done = true;
46
+ }
47
+ function __runInitializers(thisArg, initializers, value) {
48
+ var useValue = arguments.length > 2;
49
+ for (var i = 0; i < initializers.length; i++) {
50
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
51
+ }
52
+ return useValue ? value : void 0;
53
+ }
54
+ function __setFunctionName(f, name, prefix) {
55
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
56
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
57
+ }
58
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
59
+ var e = new Error(message);
60
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
61
+ };
62
+
63
+ /**
64
+ * Weld SDK - Configuration Types
65
+ * Type definitions for SDK initialization and configuration
66
+ */
67
+ /**
68
+ * Default configuration values
69
+ */
70
+ const DEFAULT_CONFIG = {
71
+ api: {
72
+ baseUrl: 'https://weldsuite-helpdesk-widget.vercel.app',
73
+ widgetId: '',
74
+ timeout: 30000,
75
+ retries: 3,
76
+ },
77
+ iframes: {
78
+ launcher: {
79
+ url: '/widget?mode=launcher',
80
+ name: 'weld-launcher-frame',
81
+ position: { bottom: '24px', right: '24px' },
82
+ size: '60px',
83
+ },
84
+ widget: {
85
+ url: '/widget?mode=widget',
86
+ name: 'weld-widget-frame',
87
+ position: { bottom: '100px', right: '24px' },
88
+ width: '400px',
89
+ height: 'min(680px, calc(100vh - 120px))',
90
+ },
91
+ backdrop: {
92
+ enabled: true,
93
+ closeOnClick: true,
94
+ },
95
+ },
96
+ customization: {
97
+ primaryColor: '#000000',
98
+ accentColor: '#3b82f6',
99
+ backgroundColor: '#ffffff',
100
+ textColor: '#111827',
101
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
102
+ fontSize: '14px',
103
+ borderRadius: '12px',
104
+ },
105
+ features: {
106
+ attachments: true,
107
+ reactions: true,
108
+ typing: true,
109
+ readReceipts: true,
110
+ offlineMode: false,
111
+ fileUpload: true,
112
+ imageUpload: true,
113
+ voiceMessages: false,
114
+ videoMessages: false,
115
+ },
116
+ mobile: {
117
+ fullScreen: true,
118
+ scrollLock: true,
119
+ keyboardHandling: 'auto',
120
+ safeAreaInsets: true,
121
+ },
122
+ auth: {
123
+ enabled: true,
124
+ mode: 'anonymous',
125
+ },
126
+ locale: {
127
+ locale: 'en',
128
+ dateFormat: 'MMM dd, yyyy',
129
+ timeFormat: 'HH:mm',
130
+ },
131
+ logging: {
132
+ enabled: true,
133
+ level: 'warn',
134
+ prefix: '[Weld]',
135
+ includeTimestamp: true,
136
+ },
137
+ performance: {
138
+ lazyLoad: true,
139
+ preload: false,
140
+ caching: true,
141
+ prefetch: false,
142
+ },
143
+ security: {
144
+ allowedOrigins: [],
145
+ validateMessages: true,
146
+ sanitizeInput: true,
147
+ },
148
+ };
149
+ /**
150
+ * Configuration validation
151
+ */
152
+ function validateConfig(config) {
153
+ if (!config.widgetId || typeof config.widgetId !== 'string') {
154
+ throw new Error('WeldConfig: widgetId is required and must be a string');
155
+ }
156
+ return true;
157
+ }
158
+ /**
159
+ * Merge configuration with defaults
160
+ */
161
+ function resolveConfig(config) {
162
+ validateConfig(config);
163
+ return {
164
+ widgetId: config.widgetId,
165
+ api: {
166
+ ...DEFAULT_CONFIG.api,
167
+ widgetId: config.widgetId,
168
+ ...config.api,
169
+ },
170
+ iframes: {
171
+ launcher: {
172
+ ...DEFAULT_CONFIG.iframes.launcher,
173
+ ...config.iframes?.launcher,
174
+ position: {
175
+ ...DEFAULT_CONFIG.iframes.launcher.position,
176
+ ...config.position?.launcher,
177
+ ...config.iframes?.launcher?.position,
178
+ },
179
+ },
180
+ widget: {
181
+ ...DEFAULT_CONFIG.iframes.widget,
182
+ ...config.iframes?.widget,
183
+ position: {
184
+ ...DEFAULT_CONFIG.iframes.widget.position,
185
+ ...config.position?.widget,
186
+ ...config.iframes?.widget?.position,
187
+ },
188
+ },
189
+ backdrop: {
190
+ ...DEFAULT_CONFIG.iframes.backdrop,
191
+ ...config.iframes?.backdrop,
192
+ },
193
+ },
194
+ customization: {
195
+ ...DEFAULT_CONFIG.customization,
196
+ ...config.customization,
197
+ },
198
+ features: {
199
+ ...DEFAULT_CONFIG.features,
200
+ ...config.features,
201
+ },
202
+ mobile: {
203
+ ...DEFAULT_CONFIG.mobile,
204
+ ...config.mobile,
205
+ },
206
+ auth: {
207
+ ...DEFAULT_CONFIG.auth,
208
+ ...config.auth,
209
+ },
210
+ locale: {
211
+ ...DEFAULT_CONFIG.locale,
212
+ ...config.locale,
213
+ },
214
+ logging: {
215
+ ...DEFAULT_CONFIG.logging,
216
+ ...config.logging,
217
+ },
218
+ performance: {
219
+ ...DEFAULT_CONFIG.performance,
220
+ ...config.performance,
221
+ },
222
+ security: {
223
+ ...DEFAULT_CONFIG.security,
224
+ ...config.security,
225
+ },
226
+ // Pass through callbacks
227
+ onReady: config.onReady,
228
+ onError: config.onError,
229
+ onOpen: config.onOpen,
230
+ onClose: config.onClose,
231
+ onMessage: config.onMessage,
232
+ onMinimize: config.onMinimize,
233
+ onMaximize: config.onMaximize,
234
+ onDestroy: config.onDestroy,
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Weld SDK - Logger Utility
240
+ * Centralized logging with configurable levels and formatting
241
+ */
242
+ /**
243
+ * Log levels
244
+ */
245
+ var LogLevel;
246
+ (function (LogLevel) {
247
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
248
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
249
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
250
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
251
+ LogLevel[LogLevel["SILENT"] = 4] = "SILENT";
252
+ })(LogLevel || (LogLevel = {}));
253
+ /**
254
+ * Logger class
255
+ */
256
+ class Logger {
257
+ constructor(config) {
258
+ this.config = {
259
+ enabled: true,
260
+ level: 'warn',
261
+ prefix: '[Weld]',
262
+ includeTimestamp: true,
263
+ ...config,
264
+ };
265
+ this.level = this.getLevelFromString(this.config.level);
266
+ }
267
+ /**
268
+ * Convert string level to LogLevel enum
269
+ */
270
+ getLevelFromString(level) {
271
+ switch (level.toLowerCase()) {
272
+ case 'debug':
273
+ return LogLevel.DEBUG;
274
+ case 'info':
275
+ return LogLevel.INFO;
276
+ case 'warn':
277
+ return LogLevel.WARN;
278
+ case 'error':
279
+ return LogLevel.ERROR;
280
+ default:
281
+ return LogLevel.WARN;
282
+ }
283
+ }
284
+ /**
285
+ * Check if logging is enabled for level
286
+ */
287
+ shouldLog(level) {
288
+ return (this.config.enabled ?? true) && level >= this.level;
289
+ }
290
+ /**
291
+ * Format log message
292
+ */
293
+ format(level, message, _data) {
294
+ const parts = [];
295
+ if (this.config.prefix) {
296
+ parts.push(this.config.prefix);
297
+ }
298
+ if (this.config.includeTimestamp) {
299
+ const timestamp = new Date().toISOString();
300
+ parts.push(`[${timestamp}]`);
301
+ }
302
+ parts.push(`[${level.toUpperCase()}]`);
303
+ parts.push(message);
304
+ return parts;
305
+ }
306
+ /**
307
+ * Log debug message
308
+ */
309
+ debug(message, data) {
310
+ if (!this.shouldLog(LogLevel.DEBUG))
311
+ return;
312
+ const formatted = this.format('debug', message, data);
313
+ if (data !== undefined) {
314
+ console.debug(...formatted, data);
315
+ }
316
+ else {
317
+ console.debug(...formatted);
318
+ }
319
+ }
320
+ /**
321
+ * Log info message
322
+ */
323
+ info(message, data) {
324
+ if (!this.shouldLog(LogLevel.INFO))
325
+ return;
326
+ const formatted = this.format('info', message, data);
327
+ if (data !== undefined) {
328
+ console.info(...formatted, data);
329
+ }
330
+ else {
331
+ console.info(...formatted);
332
+ }
333
+ }
334
+ /**
335
+ * Log warning message
336
+ */
337
+ warn(message, data) {
338
+ if (!this.shouldLog(LogLevel.WARN))
339
+ return;
340
+ const formatted = this.format('warn', message, data);
341
+ if (data !== undefined) {
342
+ console.warn(...formatted, data);
343
+ }
344
+ else {
345
+ console.warn(...formatted);
346
+ }
347
+ }
348
+ /**
349
+ * Log error message
350
+ */
351
+ error(message, error) {
352
+ if (!this.shouldLog(LogLevel.ERROR))
353
+ return;
354
+ const formatted = this.format('error', message);
355
+ if (error !== undefined) {
356
+ if (error instanceof Error) {
357
+ console.error(...formatted, error.message, error.stack);
358
+ }
359
+ else {
360
+ console.error(...formatted, error);
361
+ }
362
+ }
363
+ else {
364
+ console.error(...formatted);
365
+ }
366
+ }
367
+ /**
368
+ * Create child logger with prefix
369
+ */
370
+ child(prefix) {
371
+ return new Logger({
372
+ ...this.config,
373
+ prefix: `${this.config.prefix} ${prefix}`,
374
+ });
375
+ }
376
+ /**
377
+ * Update log level at runtime
378
+ */
379
+ setLevel(level) {
380
+ this.config.level = level;
381
+ this.level = this.getLevelFromString(level);
382
+ }
383
+ /**
384
+ * Enable/disable logging
385
+ */
386
+ setEnabled(enabled) {
387
+ this.config.enabled = enabled;
388
+ }
389
+ }
390
+ /**
391
+ * Default logger instance
392
+ */
393
+ new Logger({
394
+ enabled: true,
395
+ level: 'warn',
396
+ prefix: '[Weld]',
397
+ includeTimestamp: true,
398
+ });
399
+
400
+ /**
401
+ * Weld SDK - Iframe Manager
402
+ * Manages creation, lifecycle, and communication with multiple iframes
403
+ */
404
+ /**
405
+ * Iframe types
406
+ */
407
+ var IframeType;
408
+ (function (IframeType) {
409
+ IframeType["LAUNCHER"] = "launcher";
410
+ IframeType["WIDGET"] = "widget";
411
+ IframeType["BACKDROP"] = "backdrop";
412
+ })(IframeType || (IframeType = {}));
413
+ /**
414
+ * Device detection result
415
+ */
416
+ function detectDevice() {
417
+ const width = window.innerWidth;
418
+ const height = window.innerHeight;
419
+ const userAgent = navigator.userAgent;
420
+ const isMobile = width < 768;
421
+ const isTablet = width >= 768 && width <= 1024;
422
+ const isDesktop = width > 1024;
423
+ const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
424
+ return {
425
+ type: isMobile ? 'mobile' : isTablet ? 'tablet' : 'desktop',
426
+ isMobile,
427
+ isTablet,
428
+ isDesktop,
429
+ isTouchDevice,
430
+ screenWidth: width,
431
+ screenHeight: height,
432
+ orientation: width > height ? 'landscape' : 'portrait',
433
+ userAgent,
434
+ };
435
+ }
436
+ /**
437
+ * IframeManager class
438
+ * Orchestrates multiple iframes for the widget system
439
+ */
440
+ class IframeManager {
441
+ constructor(config) {
442
+ this.iframes = new Map();
443
+ this.rootContainer = null;
444
+ this.appContainer = null;
445
+ this.modalContainer = null;
446
+ this.styleElement = null;
447
+ this.config = config;
448
+ this.logger = new Logger(config.logging);
449
+ this.deviceInfo = detectDevice();
450
+ this.logger.info('IframeManager initialized', {
451
+ device: this.deviceInfo.type,
452
+ mobile: this.deviceInfo.isMobile,
453
+ });
454
+ }
455
+ /**
456
+ * Initialize all containers and iframes
457
+ */
458
+ async init() {
459
+ this.logger.debug('Initializing iframe manager');
460
+ try {
461
+ // Create root container structure
462
+ this.createRootContainer();
463
+ // Inject CSS
464
+ this.injectCSS();
465
+ // Create iframes
466
+ await this.createLauncherIframe();
467
+ await this.createBackdropIframe();
468
+ await this.createWidgetIframe();
469
+ // Setup event listeners
470
+ this.setupEventListeners();
471
+ this.logger.info('IframeManager initialized successfully');
472
+ }
473
+ catch (error) {
474
+ this.logger.error('Failed to initialize IframeManager', error);
475
+ throw error;
476
+ }
477
+ }
478
+ /**
479
+ * Create root container structure
480
+ */
481
+ createRootContainer() {
482
+ // Check if already exists
483
+ let existingContainer = document.getElementById('weld-container');
484
+ if (existingContainer) {
485
+ this.logger.warn('Weld container already exists, removing old instance');
486
+ existingContainer.remove();
487
+ }
488
+ // Create root container
489
+ this.rootContainer = document.createElement('div');
490
+ this.rootContainer.id = 'weld-container';
491
+ this.rootContainer.className = 'weld-namespace';
492
+ // Create app container
493
+ this.appContainer = document.createElement('div');
494
+ this.appContainer.className = 'weld-app';
495
+ this.appContainer.setAttribute('aria-live', 'polite');
496
+ this.appContainer.setAttribute('role', 'complementary');
497
+ this.appContainer.setAttribute('aria-label', 'Weld Helpdesk Widget');
498
+ // Create modal container
499
+ this.modalContainer = document.createElement('div');
500
+ this.modalContainer.id = 'weld-modal-container';
501
+ // Assemble structure
502
+ this.rootContainer.appendChild(this.appContainer);
503
+ this.rootContainer.appendChild(this.modalContainer);
504
+ document.body.appendChild(this.rootContainer);
505
+ this.logger.debug('Root container created');
506
+ }
507
+ /**
508
+ * Inject CSS into the page
509
+ */
510
+ injectCSS() {
511
+ // Remove existing style if present
512
+ const existingStyle = document.getElementById('weld-styles');
513
+ if (existingStyle) {
514
+ existingStyle.remove();
515
+ }
516
+ this.styleElement = document.createElement('style');
517
+ this.styleElement.id = 'weld-styles';
518
+ this.styleElement.textContent = this.generateCSS();
519
+ document.head.appendChild(this.styleElement);
520
+ this.logger.debug('CSS injected');
521
+ }
522
+ /**
523
+ * Generate CSS for containers
524
+ */
525
+ generateCSS() {
526
+ const { customization } = this.config;
527
+ return `
528
+ /* Weld Container */
529
+ #weld-container {
530
+ --weld-color-primary: ${customization.primaryColor};
531
+ --weld-color-accent: ${customization.accentColor};
532
+ --weld-font-family: ${customization.fontFamily};
533
+ --weld-font-size-base: ${customization.fontSize};
534
+ --weld-radius-xl: ${customization.borderRadius};
535
+ }
536
+
537
+ /* Import main stylesheet */
538
+ @import url('/styles/index.css');
539
+
540
+ /* Prevent page scroll when mobile widget is open */
541
+ body.weld-mobile-open {
542
+ overflow: hidden !important;
543
+ position: fixed !important;
544
+ width: 100% !important;
545
+ height: 100% !important;
546
+ }
547
+
548
+ /* High contrast mode support */
549
+ @media (prefers-contrast: high) {
550
+ .weld-namespace {
551
+ --weld-color-border: currentColor;
552
+ }
553
+ }
554
+ `;
555
+ }
556
+ /**
557
+ * Create launcher iframe
558
+ */
559
+ async createLauncherIframe() {
560
+ const { iframes } = this.config;
561
+ const { launcher } = iframes;
562
+ // Create container
563
+ const container = document.createElement('div');
564
+ container.className = 'weld-launcher-frame';
565
+ container.setAttribute('data-state', 'visible');
566
+ container.style.cssText = `
567
+ position: fixed;
568
+ bottom: ${launcher.position.bottom};
569
+ right: ${launcher.position.right};
570
+ width: ${launcher.size};
571
+ height: ${launcher.size};
572
+ z-index: 2147483003;
573
+ pointer-events: auto;
574
+ display: block;
575
+ `;
576
+ // Create iframe
577
+ const iframe = document.createElement('iframe');
578
+ iframe.name = launcher.name;
579
+ iframe.title = 'Weld Launcher';
580
+ iframe.src = this.buildIframeUrl(launcher.url);
581
+ iframe.style.cssText = `
582
+ width: 100%;
583
+ height: 100%;
584
+ border: none;
585
+ background: transparent;
586
+ display: block;
587
+ `;
588
+ iframe.setAttribute('allow', 'clipboard-write');
589
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups');
590
+ container.appendChild(iframe);
591
+ this.appContainer?.appendChild(container);
592
+ // Store metadata
593
+ this.iframes.set(IframeType.LAUNCHER, {
594
+ type: IframeType.LAUNCHER,
595
+ element: iframe,
596
+ container,
597
+ ready: false,
598
+ visible: true,
599
+ createdAt: Date.now(),
600
+ });
601
+ // Mark as ready when loaded
602
+ iframe.onload = () => {
603
+ const metadata = this.iframes.get(IframeType.LAUNCHER);
604
+ if (metadata) {
605
+ metadata.ready = true;
606
+ this.logger.debug('Launcher iframe loaded and ready');
607
+ }
608
+ };
609
+ this.logger.debug('Launcher iframe created');
610
+ }
611
+ /**
612
+ * Create widget iframe
613
+ */
614
+ async createWidgetIframe() {
615
+ const { iframes } = this.config;
616
+ const { widget } = iframes;
617
+ // Create container
618
+ const container = document.createElement('div');
619
+ container.className = 'weld-widget-frame';
620
+ container.setAttribute('data-state', 'closed');
621
+ container.style.cssText = `
622
+ position: fixed;
623
+ bottom: ${widget.position.bottom};
624
+ right: ${widget.position.right};
625
+ width: ${widget.width};
626
+ height: ${widget.height};
627
+ max-width: 100vw;
628
+ z-index: 2147483001;
629
+ pointer-events: none;
630
+ display: none;
631
+ border-radius: 16px;
632
+ overflow: hidden;
633
+ background: transparent;
634
+ `;
635
+ // Create iframe
636
+ const iframe = document.createElement('iframe');
637
+ iframe.name = widget.name;
638
+ iframe.title = 'Weld Widget';
639
+ iframe.src = this.buildIframeUrl(widget.url);
640
+ iframe.style.cssText = `
641
+ width: 100%;
642
+ height: 100%;
643
+ border: none;
644
+ background: transparent;
645
+ display: block;
646
+ border-radius: 16px;
647
+ `;
648
+ iframe.setAttribute('allow', 'clipboard-write; camera; microphone');
649
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups allow-downloads');
650
+ container.appendChild(iframe);
651
+ this.appContainer?.appendChild(container);
652
+ // Store metadata
653
+ this.iframes.set(IframeType.WIDGET, {
654
+ type: IframeType.WIDGET,
655
+ element: iframe,
656
+ container,
657
+ ready: false,
658
+ visible: false,
659
+ createdAt: Date.now(),
660
+ });
661
+ // Mark as ready when loaded
662
+ iframe.onload = () => {
663
+ const metadata = this.iframes.get(IframeType.WIDGET);
664
+ if (metadata) {
665
+ metadata.ready = true;
666
+ this.logger.debug('Widget iframe loaded and ready');
667
+ }
668
+ };
669
+ this.logger.debug('Widget iframe created');
670
+ }
671
+ /**
672
+ * Create backdrop iframe
673
+ */
674
+ async createBackdropIframe() {
675
+ if (!this.config.iframes.backdrop?.enabled) {
676
+ this.logger.debug('Backdrop disabled, skipping creation');
677
+ return;
678
+ }
679
+ // Create container
680
+ const container = document.createElement('div');
681
+ container.className = 'weld-backdrop-frame';
682
+ container.setAttribute('data-state', 'hidden');
683
+ container.style.cssText = `
684
+ position: fixed;
685
+ top: 0;
686
+ left: 0;
687
+ right: 0;
688
+ bottom: 0;
689
+ z-index: 2147483000;
690
+ background: transparent;
691
+ pointer-events: none;
692
+ opacity: 0;
693
+ transition: opacity 200ms ease;
694
+ `;
695
+ this.appContainer?.appendChild(container);
696
+ // Store metadata (backdrop doesn't have an iframe, just a div)
697
+ // We'll create a minimal "iframe" reference for consistency
698
+ const dummyIframe = document.createElement('iframe');
699
+ dummyIframe.style.display = 'none';
700
+ this.iframes.set(IframeType.BACKDROP, {
701
+ type: IframeType.BACKDROP,
702
+ element: dummyIframe,
703
+ container,
704
+ ready: true, // Backdrop is always ready
705
+ visible: false,
706
+ createdAt: Date.now(),
707
+ });
708
+ this.logger.debug('Backdrop created');
709
+ }
710
+ /**
711
+ * Build iframe URL with parameters
712
+ */
713
+ buildIframeUrl(path) {
714
+ const { widgetId, api } = this.config;
715
+ const baseUrl = api.baseUrl;
716
+ // Handle paths that may already have query parameters
717
+ const url = new URL(path, baseUrl);
718
+ url.searchParams.set('widgetId', widgetId);
719
+ url.searchParams.set('device', this.deviceInfo.type);
720
+ url.searchParams.set('mobile', String(this.deviceInfo.isMobile));
721
+ url.searchParams.set('parentOrigin', window.location.origin);
722
+ return url.toString();
723
+ }
724
+ /**
725
+ * Setup event listeners
726
+ */
727
+ setupEventListeners() {
728
+ // Window resize
729
+ window.addEventListener('resize', this.handleResize.bind(this));
730
+ // Orientation change
731
+ window.addEventListener('orientationchange', this.handleOrientationChange.bind(this));
732
+ this.logger.debug('Event listeners setup');
733
+ }
734
+ /**
735
+ * Handle window resize
736
+ */
737
+ handleResize() {
738
+ this.deviceInfo = detectDevice();
739
+ this.logger.debug('Window resized', { device: this.deviceInfo.type });
740
+ }
741
+ /**
742
+ * Handle orientation change
743
+ */
744
+ handleOrientationChange() {
745
+ this.deviceInfo = detectDevice();
746
+ this.logger.debug('Orientation changed', { orientation: this.deviceInfo.orientation });
747
+ }
748
+ /**
749
+ * Get iframe by type
750
+ */
751
+ getIframe(type) {
752
+ return this.iframes.get(type);
753
+ }
754
+ /**
755
+ * Get iframe element
756
+ */
757
+ getIframeElement(type) {
758
+ return this.iframes.get(type)?.element;
759
+ }
760
+ /**
761
+ * Get iframe container
762
+ */
763
+ getIframeContainer(type) {
764
+ return this.iframes.get(type)?.container;
765
+ }
766
+ /**
767
+ * Mark iframe as ready
768
+ */
769
+ setIframeReady(type) {
770
+ const iframe = this.iframes.get(type);
771
+ if (iframe) {
772
+ iframe.ready = true;
773
+ this.logger.debug(`Iframe ${type} marked as ready`);
774
+ }
775
+ }
776
+ /**
777
+ * Check if all iframes are ready
778
+ */
779
+ areAllIframesReady() {
780
+ for (const [type, iframe] of this.iframes) {
781
+ if (type !== IframeType.BACKDROP && !iframe.ready) {
782
+ return false;
783
+ }
784
+ }
785
+ return true;
786
+ }
787
+ /**
788
+ * Show iframe
789
+ */
790
+ showIframe(type) {
791
+ const iframe = this.iframes.get(type);
792
+ if (!iframe) {
793
+ console.warn(`[Weld SDK] showIframe: iframe ${type} not found`);
794
+ return;
795
+ }
796
+ console.log(`[Weld SDK] Showing iframe ${type}`, { currentDisplay: iframe.container.style.display });
797
+ iframe.visible = true;
798
+ iframe.container.setAttribute('data-state', type === IframeType.BACKDROP ? 'visible' : 'open');
799
+ iframe.container.style.pointerEvents = 'auto';
800
+ iframe.container.style.display = 'block';
801
+ // Handle mobile scroll lock
802
+ if (this.deviceInfo.isMobile && type === IframeType.WIDGET && this.config.mobile.scrollLock) {
803
+ document.body.classList.add('weld-mobile-open');
804
+ }
805
+ console.log(`[Weld SDK] Iframe ${type} shown`, { newDisplay: iframe.container.style.display });
806
+ }
807
+ /**
808
+ * Hide iframe
809
+ */
810
+ hideIframe(type) {
811
+ const iframe = this.iframes.get(type);
812
+ if (!iframe) {
813
+ console.warn(`[Weld SDK] hideIframe: iframe ${type} not found`);
814
+ return;
815
+ }
816
+ console.log(`[Weld SDK] Hiding iframe ${type}`, { currentDisplay: iframe.container.style.display });
817
+ iframe.visible = false;
818
+ iframe.container.setAttribute('data-state', type === IframeType.BACKDROP ? 'hidden' : 'closed');
819
+ iframe.container.style.pointerEvents = 'none';
820
+ iframe.container.style.display = 'none';
821
+ // Remove mobile scroll lock
822
+ if (this.deviceInfo.isMobile && type === IframeType.WIDGET) {
823
+ document.body.classList.remove('weld-mobile-open');
824
+ }
825
+ console.log(`[Weld SDK] Iframe ${type} hidden`, { newDisplay: iframe.container.style.display });
826
+ }
827
+ /**
828
+ * Get device info
829
+ */
830
+ getDeviceInfo() {
831
+ return this.deviceInfo;
832
+ }
833
+ /**
834
+ * Get modal container
835
+ */
836
+ getModalContainer() {
837
+ return this.modalContainer;
838
+ }
839
+ /**
840
+ * Destroy all iframes and cleanup
841
+ */
842
+ destroy() {
843
+ this.logger.debug('Destroying iframe manager');
844
+ // Remove event listeners
845
+ window.removeEventListener('resize', this.handleResize.bind(this));
846
+ window.removeEventListener('orientationchange', this.handleOrientationChange.bind(this));
847
+ // Remove mobile scroll lock
848
+ document.body.classList.remove('weld-mobile-open');
849
+ // Remove root container
850
+ if (this.rootContainer) {
851
+ this.rootContainer.remove();
852
+ this.rootContainer = null;
853
+ }
854
+ // Remove style element
855
+ if (this.styleElement) {
856
+ this.styleElement.remove();
857
+ this.styleElement = null;
858
+ }
859
+ // Clear iframe references
860
+ this.iframes.clear();
861
+ this.logger.info('IframeManager destroyed');
862
+ }
863
+ }
864
+
865
+ /**
866
+ * Weld SDK - Message Types
867
+ * Type definitions for postMessage communication between parent and iframes
868
+ */
869
+ /**
870
+ * Message origins for validation
871
+ */
872
+ var MessageOrigin;
873
+ (function (MessageOrigin) {
874
+ MessageOrigin["LAUNCHER"] = "launcher";
875
+ MessageOrigin["WIDGET"] = "widget";
876
+ MessageOrigin["PARENT"] = "parent";
877
+ MessageOrigin["BACKDROP"] = "backdrop";
878
+ })(MessageOrigin || (MessageOrigin = {}));
879
+ /**
880
+ * Message types for different communication patterns
881
+ */
882
+ var MessageType;
883
+ (function (MessageType) {
884
+ // Lifecycle
885
+ MessageType["READY"] = "weld:ready";
886
+ MessageType["INIT"] = "weld:init";
887
+ MessageType["DESTROY"] = "weld:destroy";
888
+ // State changes
889
+ MessageType["STATE_UPDATE"] = "weld:state:update";
890
+ MessageType["STATE_REQUEST"] = "weld:state:request";
891
+ MessageType["STATE_RESPONSE"] = "weld:state:response";
892
+ // Widget control
893
+ MessageType["WIDGET_OPEN"] = "weld:widget:open";
894
+ MessageType["WIDGET_CLOSE"] = "weld:widget:close";
895
+ MessageType["WIDGET_TOGGLE"] = "weld:widget:toggle";
896
+ MessageType["WIDGET_MINIMIZE"] = "weld:widget:minimize";
897
+ MessageType["WIDGET_MAXIMIZE"] = "weld:widget:maximize";
898
+ // Launcher control
899
+ MessageType["LAUNCHER_SHOW"] = "weld:launcher:show";
900
+ MessageType["LAUNCHER_HIDE"] = "weld:launcher:hide";
901
+ MessageType["LAUNCHER_UPDATE"] = "weld:launcher:update";
902
+ // Backdrop control
903
+ MessageType["BACKDROP_SHOW"] = "weld:backdrop:show";
904
+ MessageType["BACKDROP_HIDE"] = "weld:backdrop:hide";
905
+ MessageType["BACKDROP_CLICK"] = "weld:backdrop:click";
906
+ // User interactions
907
+ MessageType["MESSAGE_SEND"] = "weld:message:send";
908
+ MessageType["MESSAGE_RECEIVE"] = "weld:message:receive";
909
+ MessageType["TYPING_START"] = "weld:typing:start";
910
+ MessageType["TYPING_STOP"] = "weld:typing:stop";
911
+ // Badge updates
912
+ MessageType["BADGE_UPDATE"] = "weld:badge:update";
913
+ MessageType["BADGE_CLEAR"] = "weld:badge:clear";
914
+ // Mobile handling
915
+ MessageType["MOBILE_SCROLL_LOCK"] = "weld:mobile:scroll:lock";
916
+ MessageType["MOBILE_SCROLL_UNLOCK"] = "weld:mobile:scroll:unlock";
917
+ // Configuration
918
+ MessageType["CONFIG_UPDATE"] = "weld:config:update";
919
+ MessageType["THEME_UPDATE"] = "weld:theme:update";
920
+ MessageType["LOCALE_UPDATE"] = "weld:locale:update";
921
+ // Authentication
922
+ MessageType["AUTH_LOGIN"] = "weld:auth:login";
923
+ MessageType["AUTH_LOGOUT"] = "weld:auth:logout";
924
+ MessageType["AUTH_TOKEN_UPDATE"] = "weld:auth:token:update";
925
+ // Events
926
+ MessageType["EVENT_TRACK"] = "weld:event:track";
927
+ MessageType["ERROR_REPORT"] = "weld:error:report";
928
+ // API responses
929
+ MessageType["API_SUCCESS"] = "weld:api:success";
930
+ MessageType["API_ERROR"] = "weld:api:error";
931
+ })(MessageType || (MessageType = {}));
932
+ /**
933
+ * Type guards
934
+ */
935
+ function isBaseMessage(message) {
936
+ return (typeof message === 'object' &&
937
+ message !== null &&
938
+ 'type' in message &&
939
+ 'origin' in message &&
940
+ 'timestamp' in message &&
941
+ 'id' in message);
942
+ }
943
+ function isPayloadMessage(message) {
944
+ return isBaseMessage(message) && 'payload' in message;
945
+ }
946
+ /**
947
+ * Message creator utilities
948
+ */
949
+ function createMessage(type, origin, payload) {
950
+ return {
951
+ type,
952
+ origin,
953
+ timestamp: Date.now(),
954
+ id: `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
955
+ payload,
956
+ };
957
+ }
958
+
959
+ /**
960
+ * Weld SDK - Security Utilities
961
+ * Security validation and origin checking for postMessage communication
962
+ */
963
+ /**
964
+ * SecurityManager class
965
+ */
966
+ class SecurityManager {
967
+ constructor(config, logger) {
968
+ this.config = config;
969
+ this.logger = logger;
970
+ this.allowedOrigins = new Set(config.allowedOrigins || []);
971
+ // Always allow same origin
972
+ this.allowedOrigins.add(window.location.origin);
973
+ }
974
+ /**
975
+ * Validate message origin
976
+ */
977
+ isOriginAllowed(origin) {
978
+ // If no allowed origins specified, only allow same origin
979
+ if (this.config.allowedOrigins?.length === 0) {
980
+ return origin === window.location.origin;
981
+ }
982
+ // Check if origin is in allowed list
983
+ if (this.allowedOrigins.has(origin)) {
984
+ return true;
985
+ }
986
+ // Check for wildcard patterns
987
+ for (const allowed of this.allowedOrigins) {
988
+ if (this.matchesPattern(origin, allowed)) {
989
+ return true;
990
+ }
991
+ }
992
+ this.logger.warn('Origin not allowed', { origin });
993
+ return false;
994
+ }
995
+ /**
996
+ * Match origin against pattern (supports wildcards)
997
+ */
998
+ matchesPattern(origin, pattern) {
999
+ // Exact match
1000
+ if (origin === pattern) {
1001
+ return true;
1002
+ }
1003
+ // Wildcard pattern (e.g., "https://*.example.com")
1004
+ if (pattern.includes('*')) {
1005
+ const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
1006
+ return regex.test(origin);
1007
+ }
1008
+ return false;
1009
+ }
1010
+ /**
1011
+ * Validate postMessage event
1012
+ */
1013
+ validateMessageEvent(event) {
1014
+ // Check origin
1015
+ if (!this.isOriginAllowed(event.origin)) {
1016
+ this.logger.warn('Invalid message origin', { origin: event.origin });
1017
+ return false;
1018
+ }
1019
+ // Check if message has data
1020
+ if (!event.data) {
1021
+ this.logger.warn('Message has no data');
1022
+ return false;
1023
+ }
1024
+ // Check if message is an object
1025
+ if (typeof event.data !== 'object') {
1026
+ this.logger.warn('Message data is not an object');
1027
+ return false;
1028
+ }
1029
+ return true;
1030
+ }
1031
+ /**
1032
+ * Sanitize message data
1033
+ */
1034
+ sanitizeMessageData(data) {
1035
+ if (!this.config.sanitizeInput) {
1036
+ return data;
1037
+ }
1038
+ // Deep clone to avoid modifying original
1039
+ const sanitized = JSON.parse(JSON.stringify(data));
1040
+ // Recursively sanitize strings
1041
+ this.sanitizeObject(sanitized);
1042
+ return sanitized;
1043
+ }
1044
+ /**
1045
+ * Recursively sanitize object properties
1046
+ */
1047
+ sanitizeObject(obj) {
1048
+ if (typeof obj !== 'object' || obj === null) {
1049
+ return;
1050
+ }
1051
+ for (const key in obj) {
1052
+ if (typeof obj[key] === 'string') {
1053
+ obj[key] = this.sanitizeString(obj[key]);
1054
+ }
1055
+ else if (typeof obj[key] === 'object') {
1056
+ this.sanitizeObject(obj[key]);
1057
+ }
1058
+ }
1059
+ }
1060
+ /**
1061
+ * Sanitize string to prevent XSS
1062
+ */
1063
+ sanitizeString(str) {
1064
+ const div = document.createElement('div');
1065
+ div.textContent = str;
1066
+ return div.innerHTML
1067
+ .replace(/javascript:/gi, '')
1068
+ .replace(/on\w+\s*=/gi, '');
1069
+ }
1070
+ /**
1071
+ * Generate secure random ID
1072
+ */
1073
+ generateSecureId() {
1074
+ const array = new Uint8Array(16);
1075
+ crypto.getRandomValues(array);
1076
+ return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');
1077
+ }
1078
+ /**
1079
+ * Validate iframe source
1080
+ */
1081
+ isValidIframeSource(src) {
1082
+ try {
1083
+ const url = new URL(src, window.location.origin);
1084
+ // Check if URL is from allowed origin
1085
+ if (!this.isOriginAllowed(url.origin)) {
1086
+ this.logger.warn('Invalid iframe source origin', { src });
1087
+ return false;
1088
+ }
1089
+ // Prevent javascript: protocol
1090
+ if (url.protocol === 'javascript:') {
1091
+ this.logger.warn('Javascript protocol not allowed in iframe source');
1092
+ return false;
1093
+ }
1094
+ return true;
1095
+ }
1096
+ catch (error) {
1097
+ this.logger.warn('Invalid iframe source URL', { src, error });
1098
+ return false;
1099
+ }
1100
+ }
1101
+ /**
1102
+ * Create Content Security Policy
1103
+ */
1104
+ createCSP() {
1105
+ const origins = Array.from(this.allowedOrigins).join(' ');
1106
+ return [
1107
+ `default-src 'self' ${origins}`,
1108
+ `script-src 'self' 'unsafe-inline' ${origins}`,
1109
+ `style-src 'self' 'unsafe-inline' ${origins}`,
1110
+ `img-src 'self' data: https: ${origins}`,
1111
+ `font-src 'self' data: ${origins}`,
1112
+ `connect-src 'self' ${origins}`,
1113
+ `frame-src 'self' ${origins}`,
1114
+ `media-src 'self' ${origins}`,
1115
+ ].join('; ');
1116
+ }
1117
+ /**
1118
+ * Add allowed origin
1119
+ */
1120
+ addAllowedOrigin(origin) {
1121
+ this.allowedOrigins.add(origin);
1122
+ this.logger.debug('Added allowed origin', { origin });
1123
+ }
1124
+ /**
1125
+ * Remove allowed origin
1126
+ */
1127
+ removeAllowedOrigin(origin) {
1128
+ this.allowedOrigins.delete(origin);
1129
+ this.logger.debug('Removed allowed origin', { origin });
1130
+ }
1131
+ /**
1132
+ * Get all allowed origins
1133
+ */
1134
+ getAllowedOrigins() {
1135
+ return Array.from(this.allowedOrigins);
1136
+ }
1137
+ }
1138
+ /**
1139
+ * Rate limiting utility
1140
+ */
1141
+ class RateLimiter {
1142
+ constructor(maxRequests = 100, windowMs = 60000) {
1143
+ this.requests = new Map();
1144
+ this.maxRequests = maxRequests;
1145
+ this.windowMs = windowMs;
1146
+ }
1147
+ /**
1148
+ * Check if request is allowed
1149
+ */
1150
+ isAllowed(key) {
1151
+ const now = Date.now();
1152
+ const requests = this.requests.get(key) || [];
1153
+ // Remove old requests outside window
1154
+ const validRequests = requests.filter((time) => now - time < this.windowMs);
1155
+ // Check if limit exceeded
1156
+ if (validRequests.length >= this.maxRequests) {
1157
+ return false;
1158
+ }
1159
+ // Add current request
1160
+ validRequests.push(now);
1161
+ this.requests.set(key, validRequests);
1162
+ return true;
1163
+ }
1164
+ /**
1165
+ * Reset rate limit for key
1166
+ */
1167
+ reset(key) {
1168
+ this.requests.delete(key);
1169
+ }
1170
+ /**
1171
+ * Clear all rate limits
1172
+ */
1173
+ clearAll() {
1174
+ this.requests.clear();
1175
+ }
1176
+ }
1177
+
1178
+ /**
1179
+ * Weld SDK - Message Broker
1180
+ * Handles secure postMessage communication between parent and iframes
1181
+ */
1182
+ /**
1183
+ * MessageBroker class
1184
+ * Central hub for all postMessage communication
1185
+ */
1186
+ class MessageBroker {
1187
+ constructor(config, iframeManager, logger) {
1188
+ this.subscriptions = new Map();
1189
+ this.messageQueue = [];
1190
+ this.isReady = false;
1191
+ this.responseHandlers = new Map();
1192
+ this.config = config;
1193
+ this.logger = logger.child('[MessageBroker]');
1194
+ this.iframeManager = iframeManager;
1195
+ this.security = new SecurityManager(config.security, this.logger);
1196
+ this.rateLimiter = new RateLimiter(100, 60000); // 100 messages per minute
1197
+ this.setupMessageListener();
1198
+ this.logger.debug('MessageBroker initialized');
1199
+ }
1200
+ /**
1201
+ * Setup global message listener
1202
+ */
1203
+ setupMessageListener() {
1204
+ window.addEventListener('message', this.handleMessage.bind(this));
1205
+ this.logger.debug('Message listener setup');
1206
+ }
1207
+ /**
1208
+ * Handle incoming postMessage
1209
+ */
1210
+ handleMessage(event) {
1211
+ // Validate message event
1212
+ if (!this.security.validateMessageEvent(event)) {
1213
+ return;
1214
+ }
1215
+ // Validate message structure
1216
+ if (!isBaseMessage(event.data)) {
1217
+ this.logger.warn('Invalid message structure', event.data);
1218
+ return;
1219
+ }
1220
+ const message = event.data;
1221
+ // Rate limiting
1222
+ if (!this.rateLimiter.isAllowed(message.origin)) {
1223
+ this.logger.warn('Rate limit exceeded', { origin: message.origin });
1224
+ return;
1225
+ }
1226
+ // Sanitize message if enabled
1227
+ const sanitized = this.config.security.sanitizeInput
1228
+ ? this.security.sanitizeMessageData(message)
1229
+ : message;
1230
+ this.logger.debug('Message received', {
1231
+ type: sanitized.type,
1232
+ origin: sanitized.origin,
1233
+ id: sanitized.id,
1234
+ });
1235
+ // Check for response handlers
1236
+ if (this.responseHandlers.has(sanitized.id)) {
1237
+ const handler = this.responseHandlers.get(sanitized.id);
1238
+ handler?.(sanitized);
1239
+ this.responseHandlers.delete(sanitized.id);
1240
+ return;
1241
+ }
1242
+ // Dispatch to subscribers
1243
+ this.dispatchMessage(sanitized);
1244
+ }
1245
+ /**
1246
+ * Dispatch message to subscribers
1247
+ */
1248
+ dispatchMessage(message) {
1249
+ const subscriptions = Array.from(this.subscriptions.values());
1250
+ for (const subscription of subscriptions) {
1251
+ // Check if type matches
1252
+ const typeMatches = subscription.type === '*' || subscription.type === message.type;
1253
+ // Check if origin matches (if specified)
1254
+ const originMatches = !subscription.origin || subscription.origin === message.origin;
1255
+ if (typeMatches && originMatches) {
1256
+ try {
1257
+ if (isPayloadMessage(message)) {
1258
+ subscription.handler(message.payload, message);
1259
+ }
1260
+ else {
1261
+ subscription.handler(undefined, message);
1262
+ }
1263
+ }
1264
+ catch (error) {
1265
+ this.logger.error('Error in message handler', error);
1266
+ }
1267
+ }
1268
+ }
1269
+ }
1270
+ /**
1271
+ * Subscribe to messages
1272
+ */
1273
+ subscribe(type, handler, origin) {
1274
+ const id = this.security.generateSecureId();
1275
+ this.subscriptions.set(id, {
1276
+ id,
1277
+ type,
1278
+ origin,
1279
+ handler,
1280
+ });
1281
+ this.logger.debug('Subscribed to messages', { type, origin, id });
1282
+ return id;
1283
+ }
1284
+ /**
1285
+ * Unsubscribe from messages
1286
+ */
1287
+ unsubscribe(subscriptionId) {
1288
+ if (this.subscriptions.delete(subscriptionId)) {
1289
+ this.logger.debug('Unsubscribed from messages', { id: subscriptionId });
1290
+ }
1291
+ }
1292
+ /**
1293
+ * Send message to iframe
1294
+ */
1295
+ sendToIframe(iframeType, type, payload) {
1296
+ const iframe = this.iframeManager.getIframe(iframeType);
1297
+ if (!iframe) {
1298
+ this.logger.warn('Iframe not found', { iframeType });
1299
+ return;
1300
+ }
1301
+ if (!iframe.ready && type !== 'weld:init') {
1302
+ // Queue message if iframe not ready
1303
+ const message = createMessage(type, MessageOrigin.PARENT, payload);
1304
+ this.messageQueue.push(message);
1305
+ this.logger.debug('Message queued (iframe not ready)', {
1306
+ iframeType,
1307
+ type,
1308
+ });
1309
+ return;
1310
+ }
1311
+ const message = createMessage(type, MessageOrigin.PARENT, payload);
1312
+ this.postMessage(iframe.element, message);
1313
+ }
1314
+ /**
1315
+ * Send message to all iframes
1316
+ */
1317
+ broadcast(type, payload) {
1318
+ const message = createMessage(type, MessageOrigin.PARENT, payload);
1319
+ const launcherIframe = this.iframeManager.getIframe(IframeType.LAUNCHER);
1320
+ const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
1321
+ if (launcherIframe && launcherIframe.ready) {
1322
+ this.postMessage(launcherIframe.element, message);
1323
+ }
1324
+ if (widgetIframe && widgetIframe.ready) {
1325
+ this.postMessage(widgetIframe.element, message);
1326
+ }
1327
+ this.logger.debug('Message broadcast', { type });
1328
+ }
1329
+ /**
1330
+ * Send message and wait for response
1331
+ */
1332
+ async sendAndWaitForResponse(iframeType, type, payload, timeout = 5000) {
1333
+ return new Promise((resolve, reject) => {
1334
+ const message = createMessage(type, MessageOrigin.PARENT, payload);
1335
+ // Setup response handler
1336
+ const timeoutId = setTimeout(() => {
1337
+ this.responseHandlers.delete(message.id);
1338
+ reject(new Error('Message response timeout'));
1339
+ }, timeout);
1340
+ this.responseHandlers.set(message.id, (response) => {
1341
+ clearTimeout(timeoutId);
1342
+ if (isPayloadMessage(response)) {
1343
+ resolve(response.payload);
1344
+ }
1345
+ else {
1346
+ resolve(undefined);
1347
+ }
1348
+ });
1349
+ // Send message
1350
+ const iframe = this.iframeManager.getIframe(iframeType);
1351
+ if (!iframe) {
1352
+ reject(new Error('Iframe not found'));
1353
+ return;
1354
+ }
1355
+ this.postMessage(iframe.element, message);
1356
+ });
1357
+ }
1358
+ /**
1359
+ * Post message to iframe
1360
+ */
1361
+ postMessage(iframe, message) {
1362
+ if (!iframe.contentWindow) {
1363
+ this.logger.warn('Iframe contentWindow not available');
1364
+ return;
1365
+ }
1366
+ try {
1367
+ iframe.contentWindow.postMessage(message, '*');
1368
+ this.logger.debug('Message sent', {
1369
+ type: message.type,
1370
+ id: message.id,
1371
+ });
1372
+ }
1373
+ catch (error) {
1374
+ this.logger.error('Failed to post message', error);
1375
+ }
1376
+ }
1377
+ /**
1378
+ * Mark iframe as ready and flush queued messages
1379
+ */
1380
+ setIframeReady(iframeType) {
1381
+ this.iframeManager.setIframeReady(iframeType);
1382
+ // Flush queued messages for this iframe
1383
+ this.flushMessageQueue(iframeType);
1384
+ // Check if all iframes ready
1385
+ if (this.iframeManager.areAllIframesReady()) {
1386
+ this.isReady = true;
1387
+ this.logger.info('All iframes ready');
1388
+ }
1389
+ }
1390
+ /**
1391
+ * Flush queued messages to iframe
1392
+ */
1393
+ flushMessageQueue(iframeType) {
1394
+ const iframe = this.iframeManager.getIframe(iframeType);
1395
+ if (!iframe)
1396
+ return;
1397
+ const queuedMessages = this.messageQueue.splice(0);
1398
+ for (const message of queuedMessages) {
1399
+ this.postMessage(iframe.element, message);
1400
+ }
1401
+ if (queuedMessages.length > 0) {
1402
+ this.logger.debug('Flushed queued messages', {
1403
+ count: queuedMessages.length,
1404
+ iframeType,
1405
+ });
1406
+ }
1407
+ }
1408
+ /**
1409
+ * Check if broker is ready
1410
+ */
1411
+ isMessageBrokerReady() {
1412
+ return this.isReady;
1413
+ }
1414
+ /**
1415
+ * Get queued message count
1416
+ */
1417
+ getQueuedMessageCount() {
1418
+ return this.messageQueue.length;
1419
+ }
1420
+ /**
1421
+ * Clear message queue
1422
+ */
1423
+ clearMessageQueue() {
1424
+ this.messageQueue = [];
1425
+ this.logger.debug('Message queue cleared');
1426
+ }
1427
+ /**
1428
+ * Get active subscription count
1429
+ */
1430
+ getSubscriptionCount() {
1431
+ return this.subscriptions.size;
1432
+ }
1433
+ /**
1434
+ * Destroy broker and cleanup
1435
+ */
1436
+ destroy() {
1437
+ this.logger.debug('Destroying message broker');
1438
+ // Remove event listener
1439
+ window.removeEventListener('message', this.handleMessage.bind(this));
1440
+ // Clear subscriptions
1441
+ this.subscriptions.clear();
1442
+ // Clear message queue
1443
+ this.messageQueue = [];
1444
+ // Clear response handlers
1445
+ this.responseHandlers.clear();
1446
+ // Reset rate limiter
1447
+ this.rateLimiter.clearAll();
1448
+ this.isReady = false;
1449
+ this.logger.info('MessageBroker destroyed');
1450
+ }
1451
+ }
1452
+
1453
+ /**
1454
+ * Weld SDK - State Types
1455
+ * Type definitions for state management across iframes
1456
+ */
1457
+ /**
1458
+ * Widget visibility state
1459
+ */
1460
+ var WidgetVisibility;
1461
+ (function (WidgetVisibility) {
1462
+ WidgetVisibility["HIDDEN"] = "hidden";
1463
+ WidgetVisibility["VISIBLE"] = "visible";
1464
+ WidgetVisibility["MINIMIZED"] = "minimized";
1465
+ })(WidgetVisibility || (WidgetVisibility = {}));
1466
+ /**
1467
+ * Widget view types
1468
+ */
1469
+ var WidgetView;
1470
+ (function (WidgetView) {
1471
+ WidgetView["HOME"] = "home";
1472
+ WidgetView["CONVERSATION"] = "conversation";
1473
+ WidgetView["CONVERSATIONS"] = "conversations";
1474
+ WidgetView["HELP"] = "help";
1475
+ WidgetView["SETTINGS"] = "settings";
1476
+ WidgetView["SEARCH"] = "search";
1477
+ })(WidgetView || (WidgetView = {}));
1478
+ /**
1479
+ * Connection status
1480
+ */
1481
+ var ConnectionStatus;
1482
+ (function (ConnectionStatus) {
1483
+ ConnectionStatus["DISCONNECTED"] = "disconnected";
1484
+ ConnectionStatus["CONNECTING"] = "connecting";
1485
+ ConnectionStatus["CONNECTED"] = "connected";
1486
+ ConnectionStatus["RECONNECTING"] = "reconnecting";
1487
+ ConnectionStatus["ERROR"] = "error";
1488
+ })(ConnectionStatus || (ConnectionStatus = {}));
1489
+ /**
1490
+ * Message status
1491
+ */
1492
+ var MessageStatus;
1493
+ (function (MessageStatus) {
1494
+ MessageStatus["SENDING"] = "sending";
1495
+ MessageStatus["SENT"] = "sent";
1496
+ MessageStatus["DELIVERED"] = "delivered";
1497
+ MessageStatus["READ"] = "read";
1498
+ MessageStatus["FAILED"] = "failed";
1499
+ })(MessageStatus || (MessageStatus = {}));
1500
+ /**
1501
+ * Initial state factory
1502
+ */
1503
+ function createInitialState() {
1504
+ return {
1505
+ user: {
1506
+ isAuthenticated: false,
1507
+ isAnonymous: true,
1508
+ },
1509
+ conversation: {
1510
+ messages: [],
1511
+ participants: [],
1512
+ unreadCount: 0,
1513
+ isTyping: false,
1514
+ typingUsers: [],
1515
+ },
1516
+ widget: {
1517
+ visibility: WidgetVisibility.HIDDEN,
1518
+ view: WidgetView.HOME,
1519
+ isOpen: false,
1520
+ isMinimized: false,
1521
+ dimensions: {
1522
+ width: '400px',
1523
+ height: 'min(680px, 88vh)',
1524
+ },
1525
+ position: {
1526
+ bottom: '24px',
1527
+ right: '24px',
1528
+ },
1529
+ },
1530
+ launcher: {
1531
+ isVisible: true,
1532
+ badge: {
1533
+ count: 0,
1534
+ show: false,
1535
+ },
1536
+ },
1537
+ backdrop: {
1538
+ isVisible: false,
1539
+ closeOnClick: true,
1540
+ opacity: 0,
1541
+ },
1542
+ mobile: {
1543
+ isFullScreen: false,
1544
+ isScrollLocked: false,
1545
+ keyboardHeight: 0,
1546
+ orientation: 'portrait',
1547
+ safeAreaInsets: {
1548
+ top: 0,
1549
+ right: 0,
1550
+ bottom: 0,
1551
+ left: 0,
1552
+ },
1553
+ },
1554
+ network: {
1555
+ status: ConnectionStatus.DISCONNECTED,
1556
+ retryCount: 0,
1557
+ },
1558
+ ui: {
1559
+ theme: 'auto',
1560
+ locale: 'en',
1561
+ isLoading: false,
1562
+ },
1563
+ initialized: false,
1564
+ lastUpdated: Date.now(),
1565
+ };
1566
+ }
1567
+ /**
1568
+ * State path utility
1569
+ */
1570
+ function getStateValue(state, path) {
1571
+ const keys = path.split('.');
1572
+ let value = state;
1573
+ for (const key of keys) {
1574
+ if (value && typeof value === 'object' && key in value) {
1575
+ value = value[key];
1576
+ }
1577
+ else {
1578
+ return undefined;
1579
+ }
1580
+ }
1581
+ return value;
1582
+ }
1583
+ /**
1584
+ * State update utility
1585
+ */
1586
+ function setStateValue(state, path, value, merge = false) {
1587
+ const keys = path.split('.');
1588
+ const newState = JSON.parse(JSON.stringify(state));
1589
+ let current = newState;
1590
+ for (let i = 0; i < keys.length - 1; i++) {
1591
+ const key = keys[i];
1592
+ if (!(key in current)) {
1593
+ current[key] = {};
1594
+ }
1595
+ current = current[key];
1596
+ }
1597
+ const lastKey = keys[keys.length - 1];
1598
+ if (merge && typeof current[lastKey] === 'object' && typeof value === 'object') {
1599
+ current[lastKey] = { ...current[lastKey], ...value };
1600
+ }
1601
+ else {
1602
+ current[lastKey] = value;
1603
+ }
1604
+ newState.lastUpdated = Date.now();
1605
+ return newState;
1606
+ }
1607
+
1608
+ /**
1609
+ * Weld SDK - Validation Utilities
1610
+ * Input validation and sanitization functions
1611
+ */
1612
+ /**
1613
+ * Validate email format
1614
+ */
1615
+ /**
1616
+ * Deep clone object (safe for JSON-serializable objects)
1617
+ */
1618
+ function deepClone(obj) {
1619
+ if (obj === null || typeof obj !== 'object') {
1620
+ return obj;
1621
+ }
1622
+ if (obj instanceof Date) {
1623
+ return new Date(obj.getTime());
1624
+ }
1625
+ if (obj instanceof Array) {
1626
+ return obj.map((item) => deepClone(item));
1627
+ }
1628
+ if (obj instanceof Object) {
1629
+ const cloned = {};
1630
+ for (const key in obj) {
1631
+ if (obj.hasOwnProperty(key)) {
1632
+ cloned[key] = deepClone(obj[key]);
1633
+ }
1634
+ }
1635
+ return cloned;
1636
+ }
1637
+ return obj;
1638
+ }
1639
+
1640
+ /**
1641
+ * Weld SDK - State Coordinator
1642
+ * Manages and synchronizes state across iframes
1643
+ */
1644
+ /**
1645
+ * StateCoordinator class
1646
+ * Central state management for multi-iframe architecture
1647
+ */
1648
+ class StateCoordinator {
1649
+ constructor(messageBroker, logger) {
1650
+ this.subscriptions = new Map();
1651
+ this.stateHistory = [];
1652
+ this.maxHistorySize = 50;
1653
+ this.messageBroker = messageBroker;
1654
+ this.logger = logger.child('[StateCoordinator]');
1655
+ this.state = createInitialState();
1656
+ this.setupMessageHandlers();
1657
+ this.logger.debug('StateCoordinator initialized');
1658
+ }
1659
+ /**
1660
+ * Setup message handlers for state updates
1661
+ */
1662
+ setupMessageHandlers() {
1663
+ // Listen for state update requests from iframes
1664
+ this.messageBroker.subscribe('weld:state:update', this.handleStateUpdate.bind(this));
1665
+ // Listen for state request messages
1666
+ this.messageBroker.subscribe('weld:state:request', this.handleStateRequest.bind(this));
1667
+ this.logger.debug('Message handlers setup');
1668
+ }
1669
+ /**
1670
+ * Handle state update from iframe
1671
+ */
1672
+ handleStateUpdate(payload) {
1673
+ const { path, value, merge } = payload;
1674
+ if (!path) {
1675
+ this.logger.warn('State update missing path', payload);
1676
+ return;
1677
+ }
1678
+ this.updateState(path, value, merge);
1679
+ }
1680
+ /**
1681
+ * Handle state request from iframe
1682
+ */
1683
+ handleStateRequest(payload) {
1684
+ const { path } = payload;
1685
+ if (path) {
1686
+ const value = getStateValue(this.state, path);
1687
+ this.messageBroker.broadcast('weld:state:response', {
1688
+ path,
1689
+ value,
1690
+ });
1691
+ }
1692
+ else {
1693
+ // Send entire state
1694
+ this.messageBroker.broadcast('weld:state:response', {
1695
+ state: this.state,
1696
+ });
1697
+ }
1698
+ }
1699
+ /**
1700
+ * Get current state
1701
+ */
1702
+ getState() {
1703
+ return deepClone(this.state);
1704
+ }
1705
+ /**
1706
+ * Get state value by path
1707
+ */
1708
+ getValue(path) {
1709
+ return getStateValue(this.state, path);
1710
+ }
1711
+ /**
1712
+ * Update state
1713
+ */
1714
+ updateState(path, value, merge = false) {
1715
+ const oldState = deepClone(this.state);
1716
+ this.state = setStateValue(this.state, path, value, merge);
1717
+ // Create state action
1718
+ const action = {
1719
+ type: 'UPDATE',
1720
+ path,
1721
+ payload: value,
1722
+ merge,
1723
+ timestamp: Date.now(),
1724
+ };
1725
+ // Add to history
1726
+ this.addToHistory(action);
1727
+ // Notify subscribers
1728
+ this.notifySubscribers(path, value, getStateValue(oldState, path));
1729
+ // Broadcast to iframes
1730
+ this.messageBroker.broadcast('weld:state:update', {
1731
+ path,
1732
+ value,
1733
+ merge,
1734
+ });
1735
+ this.logger.debug('State updated', { path, merge });
1736
+ }
1737
+ /**
1738
+ * Batch update multiple state paths
1739
+ */
1740
+ batchUpdate(updates) {
1741
+ const oldState = deepClone(this.state);
1742
+ for (const update of updates) {
1743
+ this.state = setStateValue(this.state, update.path, update.value, update.merge);
1744
+ }
1745
+ // Notify subscribers for each update
1746
+ for (const update of updates) {
1747
+ this.notifySubscribers(update.path, getStateValue(this.state, update.path), getStateValue(oldState, update.path));
1748
+ }
1749
+ // Broadcast all updates
1750
+ this.messageBroker.broadcast('weld:state:update', {
1751
+ batch: updates,
1752
+ });
1753
+ this.logger.debug('Batch state update', { count: updates.length });
1754
+ }
1755
+ /**
1756
+ * Reset state to initial
1757
+ */
1758
+ resetState() {
1759
+ const oldState = this.state;
1760
+ this.state = createInitialState();
1761
+ // Notify all subscribers
1762
+ for (const [_id, subscription] of this.subscriptions) {
1763
+ const oldValue = getStateValue(oldState, subscription.path);
1764
+ const newValue = getStateValue(this.state, subscription.path);
1765
+ subscription.listener(newValue, oldValue);
1766
+ }
1767
+ // Broadcast reset
1768
+ this.messageBroker.broadcast('weld:state:update', {
1769
+ reset: true,
1770
+ state: this.state,
1771
+ });
1772
+ this.logger.debug('State reset');
1773
+ }
1774
+ /**
1775
+ * Subscribe to state changes
1776
+ */
1777
+ subscribe(path, listener, immediate = false) {
1778
+ const id = `sub_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1779
+ this.subscriptions.set(id, {
1780
+ id,
1781
+ path,
1782
+ listener,
1783
+ immediate,
1784
+ });
1785
+ // Call listener immediately if requested
1786
+ if (immediate) {
1787
+ const value = getStateValue(this.state, path);
1788
+ listener(value, value);
1789
+ }
1790
+ this.logger.debug('State subscription added', { id, path });
1791
+ return id;
1792
+ }
1793
+ /**
1794
+ * Unsubscribe from state changes
1795
+ */
1796
+ unsubscribe(subscriptionId) {
1797
+ if (this.subscriptions.delete(subscriptionId)) {
1798
+ this.logger.debug('State subscription removed', { id: subscriptionId });
1799
+ }
1800
+ }
1801
+ /**
1802
+ * Notify subscribers of state change
1803
+ */
1804
+ notifySubscribers(path, newValue, oldValue) {
1805
+ const subscriptions = Array.from(this.subscriptions.values());
1806
+ for (const subscription of subscriptions) {
1807
+ // Exact path match
1808
+ if (subscription.path === path) {
1809
+ try {
1810
+ subscription.listener(newValue, oldValue);
1811
+ }
1812
+ catch (error) {
1813
+ this.logger.error('Error in state listener', error);
1814
+ }
1815
+ continue;
1816
+ }
1817
+ // Parent path match (e.g., subscriber to "user" gets notified for "user.name")
1818
+ if (path.startsWith(subscription.path + '.')) {
1819
+ const subValue = getStateValue(this.state, subscription.path);
1820
+ try {
1821
+ subscription.listener(subValue, oldValue);
1822
+ }
1823
+ catch (error) {
1824
+ this.logger.error('Error in state listener', error);
1825
+ }
1826
+ continue;
1827
+ }
1828
+ // Child path match (e.g., subscriber to "user.name" gets notified for "user")
1829
+ if (subscription.path.startsWith(path + '.')) {
1830
+ const subValue = getStateValue(this.state, subscription.path);
1831
+ try {
1832
+ subscription.listener(subValue, undefined);
1833
+ }
1834
+ catch (error) {
1835
+ this.logger.error('Error in state listener', error);
1836
+ }
1837
+ }
1838
+ }
1839
+ }
1840
+ /**
1841
+ * Add action to history
1842
+ */
1843
+ addToHistory(action) {
1844
+ this.stateHistory.push(action);
1845
+ // Trim history if too large
1846
+ if (this.stateHistory.length > this.maxHistorySize) {
1847
+ this.stateHistory.shift();
1848
+ }
1849
+ }
1850
+ /**
1851
+ * Get state history
1852
+ */
1853
+ getHistory() {
1854
+ return [...this.stateHistory];
1855
+ }
1856
+ /**
1857
+ * Clear state history
1858
+ */
1859
+ clearHistory() {
1860
+ this.stateHistory = [];
1861
+ this.logger.debug('State history cleared');
1862
+ }
1863
+ /**
1864
+ * Get snapshot of state at specific time
1865
+ */
1866
+ getSnapshot(timestamp) {
1867
+ // Find all actions before timestamp
1868
+ const actions = this.stateHistory.filter((a) => a.timestamp <= timestamp);
1869
+ if (actions.length === 0) {
1870
+ return null;
1871
+ }
1872
+ // Replay actions to reconstruct state
1873
+ let state = createInitialState();
1874
+ for (const action of actions) {
1875
+ state = setStateValue(state, action.path, action.payload, action.merge);
1876
+ }
1877
+ return state;
1878
+ }
1879
+ /**
1880
+ * Widget-specific state helpers
1881
+ */
1882
+ openWidget() {
1883
+ this.batchUpdate([
1884
+ { path: 'widget.isOpen', value: true },
1885
+ { path: 'widget.visibility', value: 'visible' },
1886
+ { path: 'launcher.isVisible', value: false },
1887
+ { path: 'backdrop.isVisible', value: true },
1888
+ ]);
1889
+ }
1890
+ closeWidget() {
1891
+ this.batchUpdate([
1892
+ { path: 'widget.isOpen', value: false },
1893
+ { path: 'widget.visibility', value: 'hidden' },
1894
+ { path: 'launcher.isVisible', value: true },
1895
+ { path: 'backdrop.isVisible', value: false },
1896
+ ]);
1897
+ }
1898
+ minimizeWidget() {
1899
+ this.batchUpdate([
1900
+ { path: 'widget.isMinimized', value: true },
1901
+ { path: 'widget.visibility', value: 'minimized' },
1902
+ ]);
1903
+ }
1904
+ maximizeWidget() {
1905
+ this.batchUpdate([
1906
+ { path: 'widget.isMinimized', value: false },
1907
+ { path: 'widget.visibility', value: 'visible' },
1908
+ ]);
1909
+ }
1910
+ setBadgeCount(count) {
1911
+ this.batchUpdate([
1912
+ { path: 'launcher.badge.count', value: count },
1913
+ { path: 'launcher.badge.show', value: count > 0 },
1914
+ ]);
1915
+ }
1916
+ setUserAuth(userId, email, name) {
1917
+ this.batchUpdate([
1918
+ { path: 'user.isAuthenticated', value: true },
1919
+ { path: 'user.isAnonymous', value: false },
1920
+ { path: 'user.id', value: userId },
1921
+ { path: 'user.email', value: email },
1922
+ { path: 'user.name', value: name },
1923
+ ]);
1924
+ }
1925
+ setConnectionStatus(status) {
1926
+ this.updateState('network.status', status);
1927
+ }
1928
+ setLoading(isLoading) {
1929
+ this.updateState('ui.isLoading', isLoading);
1930
+ }
1931
+ setError(code, message, recoverable = true) {
1932
+ this.updateState('ui.error', { code, message, recoverable });
1933
+ }
1934
+ clearError() {
1935
+ this.updateState('ui.error', undefined);
1936
+ }
1937
+ /**
1938
+ * Get active subscription count
1939
+ */
1940
+ getSubscriptionCount() {
1941
+ return this.subscriptions.size;
1942
+ }
1943
+ /**
1944
+ * Validate state integrity
1945
+ */
1946
+ validateState() {
1947
+ try {
1948
+ // Check required properties exist
1949
+ const requiredPaths = [
1950
+ 'user',
1951
+ 'conversation',
1952
+ 'widget',
1953
+ 'launcher',
1954
+ 'backdrop',
1955
+ 'mobile',
1956
+ 'network',
1957
+ 'ui',
1958
+ ];
1959
+ for (const path of requiredPaths) {
1960
+ if (getStateValue(this.state, path) === undefined) {
1961
+ this.logger.error('Missing required state path', { path });
1962
+ return false;
1963
+ }
1964
+ }
1965
+ return true;
1966
+ }
1967
+ catch (error) {
1968
+ this.logger.error('State validation failed', error);
1969
+ return false;
1970
+ }
1971
+ }
1972
+ /**
1973
+ * Export state as JSON
1974
+ */
1975
+ exportState() {
1976
+ return JSON.stringify(this.state, null, 2);
1977
+ }
1978
+ /**
1979
+ * Import state from JSON
1980
+ */
1981
+ importState(json) {
1982
+ try {
1983
+ const imported = JSON.parse(json);
1984
+ this.state = { ...createInitialState(), ...imported };
1985
+ this.messageBroker.broadcast('weld:state:update', {
1986
+ reset: true,
1987
+ state: this.state,
1988
+ });
1989
+ this.logger.info('State imported');
1990
+ return true;
1991
+ }
1992
+ catch (error) {
1993
+ this.logger.error('Failed to import state', error);
1994
+ return false;
1995
+ }
1996
+ }
1997
+ /**
1998
+ * Destroy coordinator and cleanup
1999
+ */
2000
+ destroy() {
2001
+ this.logger.debug('Destroying state coordinator');
2002
+ // Clear subscriptions
2003
+ this.subscriptions.clear();
2004
+ // Clear history
2005
+ this.stateHistory = [];
2006
+ // Reset state
2007
+ this.state = createInitialState();
2008
+ this.logger.info('StateCoordinator destroyed');
2009
+ }
2010
+ }
2011
+
2012
+ var version = "1.0.2";
2013
+ var packageJson = {
2014
+ version: version};
2015
+
2016
+ /**
2017
+ * Weld SDK - Main Entry Point
2018
+ * Public API for the Weld helpdesk widget
2019
+ */
2020
+ /**
2021
+ * SDK initialization status
2022
+ */
2023
+ var SDKStatus;
2024
+ (function (SDKStatus) {
2025
+ SDKStatus["UNINITIALIZED"] = "uninitialized";
2026
+ SDKStatus["INITIALIZING"] = "initializing";
2027
+ SDKStatus["READY"] = "ready";
2028
+ SDKStatus["ERROR"] = "error";
2029
+ SDKStatus["DESTROYED"] = "destroyed";
2030
+ })(SDKStatus || (SDKStatus = {}));
2031
+ /**
2032
+ * WeldSDK class
2033
+ * Main SDK interface for embedding the widget
2034
+ */
2035
+ class WeldSDK {
2036
+ constructor(config) {
2037
+ this.status = SDKStatus.UNINITIALIZED;
2038
+ this.readyPromise = null;
2039
+ this.readyResolve = null;
2040
+ // Resolve configuration
2041
+ this.config = resolveConfig(config);
2042
+ // Initialize logger
2043
+ this.logger = new Logger(this.config.logging);
2044
+ this.logger.info('WeldSDK created', {
2045
+ widgetId: this.config.widgetId,
2046
+ });
2047
+ // Create ready promise
2048
+ this.readyPromise = new Promise((resolve) => {
2049
+ this.readyResolve = resolve;
2050
+ });
2051
+ // Initialize managers
2052
+ this.iframeManager = new IframeManager(this.config);
2053
+ this.messageBroker = new MessageBroker(this.config, this.iframeManager, this.logger);
2054
+ this.stateCoordinator = new StateCoordinator(this.messageBroker, this.logger);
2055
+ }
2056
+ /**
2057
+ * Initialize the SDK and render widget
2058
+ */
2059
+ async init() {
2060
+ if (this.status !== SDKStatus.UNINITIALIZED) {
2061
+ this.logger.warn('SDK already initialized');
2062
+ return this.readyPromise;
2063
+ }
2064
+ this.status = SDKStatus.INITIALIZING;
2065
+ this.logger.info('Initializing WeldSDK');
2066
+ try {
2067
+ // Initialize iframe manager
2068
+ await this.iframeManager.init();
2069
+ // Setup ready handlers
2070
+ this.setupReadyHandlers();
2071
+ // Wait for all iframes to be ready
2072
+ await this.waitForIframesReady();
2073
+ // Mark as ready
2074
+ this.status = SDKStatus.READY;
2075
+ this.readyResolve?.();
2076
+ this.logger.info('WeldSDK ready');
2077
+ // Call onReady callback
2078
+ this.config.onReady?.();
2079
+ }
2080
+ catch (error) {
2081
+ this.status = SDKStatus.ERROR;
2082
+ this.logger.error('Failed to initialize WeldSDK', error);
2083
+ this.config.onError?.(error);
2084
+ throw error;
2085
+ }
2086
+ return this.readyPromise;
2087
+ }
2088
+ /**
2089
+ * Setup ready message handlers
2090
+ */
2091
+ setupReadyHandlers() {
2092
+ this.messageBroker.subscribe('weld:ready', (payload) => {
2093
+ const { iframe } = payload;
2094
+ this.logger.debug('Iframe ready', { iframe });
2095
+ // Map iframe name to type
2096
+ const iframeType = this.mapIframeNameToType(iframe);
2097
+ if (iframeType) {
2098
+ this.messageBroker.setIframeReady(iframeType);
2099
+ }
2100
+ });
2101
+ // Listen for launcher click events from the iframe
2102
+ window.addEventListener('message', (event) => {
2103
+ // Log all messages from iframes for debugging
2104
+ if (event.data?.type) {
2105
+ console.log('[Weld SDK] Received message:', event.data.type);
2106
+ }
2107
+ if (event.data?.type === 'launcher:clicked') {
2108
+ // Toggle behavior - if widget is open, close it; if closed, open it
2109
+ const state = this.stateCoordinator.getState();
2110
+ if (state.widget.isOpen) {
2111
+ console.log('[Weld SDK] Launcher clicked - closing widget (toggle)');
2112
+ this.close();
2113
+ }
2114
+ else {
2115
+ console.log('[Weld SDK] Launcher clicked - opening widget');
2116
+ this.open();
2117
+ }
2118
+ }
2119
+ if (event.data?.type === 'weld:close') {
2120
+ console.log('[Weld SDK] Widget close requested');
2121
+ this.close();
2122
+ }
2123
+ });
2124
+ }
2125
+ /**
2126
+ * Map iframe name to type
2127
+ */
2128
+ mapIframeNameToType(name) {
2129
+ if (name.includes('launcher'))
2130
+ return IframeType.LAUNCHER;
2131
+ if (name.includes('widget'))
2132
+ return IframeType.WIDGET;
2133
+ return null;
2134
+ }
2135
+ /**
2136
+ * Wait for all iframes to be ready
2137
+ */
2138
+ async waitForIframesReady(timeout = 10000) {
2139
+ const startTime = Date.now();
2140
+ return new Promise((resolve, reject) => {
2141
+ const checkReady = () => {
2142
+ if (this.iframeManager.areAllIframesReady()) {
2143
+ resolve();
2144
+ return;
2145
+ }
2146
+ if (Date.now() - startTime > timeout) {
2147
+ reject(new Error('Timeout waiting for iframes to be ready'));
2148
+ return;
2149
+ }
2150
+ setTimeout(checkReady, 100);
2151
+ };
2152
+ checkReady();
2153
+ });
2154
+ }
2155
+ /**
2156
+ * Wait for SDK to be ready
2157
+ */
2158
+ async ready() {
2159
+ return this.readyPromise;
2160
+ }
2161
+ /**
2162
+ * Check if SDK is ready
2163
+ */
2164
+ isReady() {
2165
+ return this.status === SDKStatus.READY;
2166
+ }
2167
+ /**
2168
+ * Open the widget
2169
+ */
2170
+ open() {
2171
+ this.ensureReady();
2172
+ console.log('[Weld SDK] Opening widget...');
2173
+ this.stateCoordinator.openWidget();
2174
+ this.iframeManager.showIframe(IframeType.WIDGET);
2175
+ this.iframeManager.showIframe(IframeType.BACKDROP);
2176
+ // Keep launcher visible so user can click it to close the widget
2177
+ // Send open message to the widget iframe
2178
+ const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
2179
+ if (widgetIframe?.element?.contentWindow) {
2180
+ widgetIframe.element.contentWindow.postMessage({ type: 'weld:open' }, '*');
2181
+ }
2182
+ // Notify launcher that widget is now open (so it can show X icon)
2183
+ const launcherIframe = this.iframeManager.getIframe(IframeType.LAUNCHER);
2184
+ if (launcherIframe?.element?.contentWindow) {
2185
+ launcherIframe.element.contentWindow.postMessage({ type: 'weld:widget-opened' }, '*');
2186
+ }
2187
+ this.config.onOpen?.();
2188
+ }
2189
+ /**
2190
+ * Close the widget
2191
+ */
2192
+ close() {
2193
+ this.ensureReady();
2194
+ console.log('[Weld SDK] Closing widget...');
2195
+ this.stateCoordinator.closeWidget();
2196
+ this.iframeManager.hideIframe(IframeType.WIDGET);
2197
+ this.iframeManager.hideIframe(IframeType.BACKDROP);
2198
+ // Launcher stays visible
2199
+ // Send close message to the widget iframe
2200
+ const widgetIframe = this.iframeManager.getIframe(IframeType.WIDGET);
2201
+ if (widgetIframe?.element?.contentWindow) {
2202
+ widgetIframe.element.contentWindow.postMessage({ type: 'weld:close' }, '*');
2203
+ }
2204
+ // Notify launcher that widget is now closed (so it can show chat icon)
2205
+ const launcherIframe = this.iframeManager.getIframe(IframeType.LAUNCHER);
2206
+ if (launcherIframe?.element?.contentWindow) {
2207
+ launcherIframe.element.contentWindow.postMessage({ type: 'weld:widget-closed' }, '*');
2208
+ }
2209
+ this.config.onClose?.();
2210
+ }
2211
+ /**
2212
+ * Toggle widget open/close
2213
+ */
2214
+ toggle() {
2215
+ const state = this.stateCoordinator.getState();
2216
+ if (state.widget.isOpen) {
2217
+ this.close();
2218
+ }
2219
+ else {
2220
+ this.open();
2221
+ }
2222
+ }
2223
+ /**
2224
+ * Minimize the widget
2225
+ */
2226
+ minimize() {
2227
+ this.ensureReady();
2228
+ this.logger.debug('Minimizing widget');
2229
+ this.stateCoordinator.minimizeWidget();
2230
+ this.config.onMinimize?.();
2231
+ }
2232
+ /**
2233
+ * Maximize the widget
2234
+ */
2235
+ maximize() {
2236
+ this.ensureReady();
2237
+ this.logger.debug('Maximizing widget');
2238
+ this.stateCoordinator.maximizeWidget();
2239
+ this.config.onMaximize?.();
2240
+ }
2241
+ /**
2242
+ * Show the launcher
2243
+ */
2244
+ showLauncher() {
2245
+ this.ensureReady();
2246
+ this.logger.debug('Showing launcher');
2247
+ this.iframeManager.showIframe(IframeType.LAUNCHER);
2248
+ this.stateCoordinator.updateState('launcher.isVisible', true);
2249
+ }
2250
+ /**
2251
+ * Hide the launcher
2252
+ */
2253
+ hideLauncher() {
2254
+ this.ensureReady();
2255
+ this.logger.debug('Hiding launcher');
2256
+ this.iframeManager.hideIframe(IframeType.LAUNCHER);
2257
+ this.stateCoordinator.updateState('launcher.isVisible', false);
2258
+ }
2259
+ /**
2260
+ * Set badge count
2261
+ */
2262
+ setBadgeCount(count) {
2263
+ this.ensureReady();
2264
+ this.logger.debug('Setting badge count', { count });
2265
+ this.stateCoordinator.setBadgeCount(count);
2266
+ }
2267
+ /**
2268
+ * Clear badge
2269
+ */
2270
+ clearBadge() {
2271
+ this.setBadgeCount(0);
2272
+ }
2273
+ /**
2274
+ * Send a message
2275
+ */
2276
+ sendMessage(text, metadata) {
2277
+ this.ensureReady();
2278
+ this.logger.debug('Sending message', { text });
2279
+ this.messageBroker.sendToIframe(IframeType.WIDGET, 'weld:message:send', {
2280
+ text,
2281
+ metadata,
2282
+ timestamp: Date.now(),
2283
+ });
2284
+ }
2285
+ /**
2286
+ * Identify user
2287
+ */
2288
+ identify(identity) {
2289
+ this.ensureReady();
2290
+ this.logger.debug('Identifying user', { userId: identity.userId });
2291
+ this.stateCoordinator.setUserAuth(identity.userId, identity.email, identity.name);
2292
+ this.messageBroker.broadcast('weld:auth:login', identity);
2293
+ }
2294
+ /**
2295
+ * Logout user
2296
+ */
2297
+ logout() {
2298
+ this.ensureReady();
2299
+ this.logger.debug('Logging out user');
2300
+ this.stateCoordinator.updateState('user', {
2301
+ isAuthenticated: false,
2302
+ isAnonymous: true,
2303
+ });
2304
+ this.messageBroker.broadcast('weld:auth:logout', {});
2305
+ }
2306
+ /**
2307
+ * Update configuration
2308
+ */
2309
+ updateConfig(updates) {
2310
+ this.ensureReady();
2311
+ this.logger.debug('Updating configuration');
2312
+ // Merge config
2313
+ this.config = resolveConfig({ ...this.config, ...updates });
2314
+ // Broadcast config update
2315
+ this.messageBroker.broadcast('weld:config:update', updates);
2316
+ }
2317
+ /**
2318
+ * Update theme
2319
+ */
2320
+ setTheme(theme) {
2321
+ this.ensureReady();
2322
+ this.logger.debug('Setting theme', { theme });
2323
+ this.stateCoordinator.updateState('ui.theme', theme);
2324
+ this.messageBroker.broadcast('weld:theme:update', { mode: theme });
2325
+ }
2326
+ /**
2327
+ * Update locale
2328
+ */
2329
+ setLocale(locale) {
2330
+ this.ensureReady();
2331
+ this.logger.debug('Setting locale', { locale });
2332
+ this.stateCoordinator.updateState('ui.locale', locale);
2333
+ this.messageBroker.broadcast('weld:locale:update', { locale });
2334
+ }
2335
+ /**
2336
+ * Track custom event
2337
+ */
2338
+ track(eventName, properties) {
2339
+ this.ensureReady();
2340
+ this.logger.debug('Tracking event', { eventName });
2341
+ this.messageBroker.broadcast('weld:event:track', {
2342
+ name: eventName,
2343
+ properties,
2344
+ timestamp: Date.now(),
2345
+ });
2346
+ }
2347
+ /**
2348
+ * Get current state
2349
+ */
2350
+ getState() {
2351
+ return this.stateCoordinator.getState();
2352
+ }
2353
+ /**
2354
+ * Subscribe to state changes
2355
+ */
2356
+ onStateChange(path, listener) {
2357
+ this.ensureReady();
2358
+ const subscriptionId = this.stateCoordinator.subscribe(path, listener);
2359
+ // Return unsubscribe function
2360
+ return () => {
2361
+ this.stateCoordinator.unsubscribe(subscriptionId);
2362
+ };
2363
+ }
2364
+ /**
2365
+ * Get device info
2366
+ */
2367
+ getDeviceInfo() {
2368
+ return this.iframeManager.getDeviceInfo();
2369
+ }
2370
+ /**
2371
+ * Get SDK status
2372
+ */
2373
+ getStatus() {
2374
+ return this.status;
2375
+ }
2376
+ /**
2377
+ * Get SDK version
2378
+ */
2379
+ getVersion() {
2380
+ return packageJson.version;
2381
+ }
2382
+ /**
2383
+ * Enable debug mode
2384
+ */
2385
+ enableDebug() {
2386
+ this.logger.setLevel('debug');
2387
+ this.logger.info('Debug mode enabled');
2388
+ }
2389
+ /**
2390
+ * Disable debug mode
2391
+ */
2392
+ disableDebug() {
2393
+ this.logger.setLevel('warn');
2394
+ this.logger.info('Debug mode disabled');
2395
+ }
2396
+ /**
2397
+ * Ensure SDK is ready before operation
2398
+ */
2399
+ ensureReady() {
2400
+ if (this.status !== SDKStatus.READY) {
2401
+ throw new Error('SDK not ready. Call init() first.');
2402
+ }
2403
+ }
2404
+ /**
2405
+ * Destroy SDK and cleanup
2406
+ */
2407
+ destroy() {
2408
+ this.logger.info('Destroying WeldSDK');
2409
+ // Destroy modules
2410
+ this.stateCoordinator.destroy();
2411
+ this.messageBroker.destroy();
2412
+ this.iframeManager.destroy();
2413
+ // Reset status
2414
+ this.status = SDKStatus.DESTROYED;
2415
+ // Call onDestroy callback
2416
+ this.config.onDestroy?.();
2417
+ this.logger.info('WeldSDK destroyed');
2418
+ }
2419
+ }
2420
+
2421
+ /**
2422
+ * Angular component for the Helpdesk Widget
2423
+ *
2424
+ * This component provides a declarative way to add the widget to your app.
2425
+ * The widget loads in an iframe and doesn't render any visible children.
2426
+ *
2427
+ * @example
2428
+ * ```typescript
2429
+ * // app.component.ts
2430
+ * import { Component } from '@angular/core';
2431
+ *
2432
+ * @Component({
2433
+ * selector: 'app-root',
2434
+ * template: `
2435
+ * <h1>My App</h1>
2436
+ * <helpdesk-widget [widgetId]="'your-widget-id'"></helpdesk-widget>
2437
+ * `
2438
+ * })
2439
+ * export class AppComponent {}
2440
+ * ```
2441
+ *
2442
+ * @example With event handlers
2443
+ * ```typescript
2444
+ * @Component({
2445
+ * selector: 'app-root',
2446
+ * template: `
2447
+ * <helpdesk-widget
2448
+ * [widgetId]="'your-widget-id'"
2449
+ * [onReady]="handleReady"
2450
+ * [onOpen]="handleOpened">
2451
+ * </helpdesk-widget>
2452
+ * `
2453
+ * })
2454
+ * export class AppComponent {
2455
+ * handleReady = () => {
2456
+ * console.log('Widget ready');
2457
+ * }
2458
+ *
2459
+ * handleOpened = () => {
2460
+ * console.log('Widget opened');
2461
+ * }
2462
+ * }
2463
+ * ```
2464
+ */
2465
+ let HelpdeskWidgetComponent = (() => {
2466
+ let _classDecorators = [Component({
2467
+ selector: 'helpdesk-widget',
2468
+ template: '',
2469
+ standalone: true,
2470
+ })];
2471
+ let _classDescriptor;
2472
+ let _classExtraInitializers = [];
2473
+ let _classThis;
2474
+ let _widgetId_decorators;
2475
+ let _widgetId_initializers = [];
2476
+ let _widgetId_extraInitializers = [];
2477
+ let _onReady_decorators;
2478
+ let _onReady_initializers = [];
2479
+ let _onReady_extraInitializers = [];
2480
+ let _onOpen_decorators;
2481
+ let _onOpen_initializers = [];
2482
+ let _onOpen_extraInitializers = [];
2483
+ let _onClose_decorators;
2484
+ let _onClose_initializers = [];
2485
+ let _onClose_extraInitializers = [];
2486
+ let _onError_decorators;
2487
+ let _onError_initializers = [];
2488
+ let _onError_extraInitializers = [];
2489
+ _classThis = class {
2490
+ constructor() {
2491
+ this.widgetId = __runInitializers(this, _widgetId_initializers, void 0);
2492
+ this.onReady = (__runInitializers(this, _widgetId_extraInitializers), __runInitializers(this, _onReady_initializers, void 0));
2493
+ this.onOpen = (__runInitializers(this, _onReady_extraInitializers), __runInitializers(this, _onOpen_initializers, void 0));
2494
+ this.onClose = (__runInitializers(this, _onOpen_extraInitializers), __runInitializers(this, _onClose_initializers, void 0));
2495
+ this.onError = (__runInitializers(this, _onClose_extraInitializers), __runInitializers(this, _onError_initializers, void 0));
2496
+ this.widget = (__runInitializers(this, _onError_extraInitializers), null);
2497
+ }
2498
+ ngOnInit() {
2499
+ if (!this.widgetId) {
2500
+ throw new Error('widgetId is required for HelpdeskWidgetComponent');
2501
+ }
2502
+ const config = {
2503
+ widgetId: this.widgetId,
2504
+ onReady: this.onReady,
2505
+ onOpen: this.onOpen,
2506
+ onClose: this.onClose,
2507
+ onError: this.onError,
2508
+ };
2509
+ try {
2510
+ this.widget = new WeldSDK(config);
2511
+ this.widget.init();
2512
+ }
2513
+ catch (error) {
2514
+ console.error('Failed to initialize Helpdesk Widget:', error);
2515
+ }
2516
+ }
2517
+ ngOnDestroy() {
2518
+ if (this.widget) {
2519
+ this.widget.destroy();
2520
+ this.widget = null;
2521
+ }
2522
+ }
2523
+ /**
2524
+ * Open the widget programmatically
2525
+ */
2526
+ open() {
2527
+ this.widget?.open();
2528
+ }
2529
+ /**
2530
+ * Close the widget programmatically
2531
+ */
2532
+ close() {
2533
+ this.widget?.close();
2534
+ }
2535
+ /**
2536
+ * Toggle widget visibility programmatically
2537
+ */
2538
+ toggle() {
2539
+ this.widget?.toggle();
2540
+ }
2541
+ /**
2542
+ * Send a message to the widget
2543
+ */
2544
+ sendMessage(message) {
2545
+ this.widget?.sendMessage(message);
2546
+ }
2547
+ };
2548
+ __setFunctionName(_classThis, "HelpdeskWidgetComponent");
2549
+ (() => {
2550
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
2551
+ _widgetId_decorators = [Input()];
2552
+ _onReady_decorators = [Input()];
2553
+ _onOpen_decorators = [Input()];
2554
+ _onClose_decorators = [Input()];
2555
+ _onError_decorators = [Input()];
2556
+ __esDecorate(null, null, _widgetId_decorators, { kind: "field", name: "widgetId", static: false, private: false, access: { has: obj => "widgetId" in obj, get: obj => obj.widgetId, set: (obj, value) => { obj.widgetId = value; } }, metadata: _metadata }, _widgetId_initializers, _widgetId_extraInitializers);
2557
+ __esDecorate(null, null, _onReady_decorators, { kind: "field", name: "onReady", static: false, private: false, access: { has: obj => "onReady" in obj, get: obj => obj.onReady, set: (obj, value) => { obj.onReady = value; } }, metadata: _metadata }, _onReady_initializers, _onReady_extraInitializers);
2558
+ __esDecorate(null, null, _onOpen_decorators, { kind: "field", name: "onOpen", static: false, private: false, access: { has: obj => "onOpen" in obj, get: obj => obj.onOpen, set: (obj, value) => { obj.onOpen = value; } }, metadata: _metadata }, _onOpen_initializers, _onOpen_extraInitializers);
2559
+ __esDecorate(null, null, _onClose_decorators, { kind: "field", name: "onClose", static: false, private: false, access: { has: obj => "onClose" in obj, get: obj => obj.onClose, set: (obj, value) => { obj.onClose = value; } }, metadata: _metadata }, _onClose_initializers, _onClose_extraInitializers);
2560
+ __esDecorate(null, null, _onError_decorators, { kind: "field", name: "onError", static: false, private: false, access: { has: obj => "onError" in obj, get: obj => obj.onError, set: (obj, value) => { obj.onError = value; } }, metadata: _metadata }, _onError_initializers, _onError_extraInitializers);
2561
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
2562
+ _classThis = _classDescriptor.value;
2563
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
2564
+ __runInitializers(_classThis, _classExtraInitializers);
2565
+ })();
2566
+ return _classThis;
2567
+ })();
2568
+
2569
+ /**
2570
+ * Angular service for managing the Helpdesk Widget
2571
+ *
2572
+ * @example
2573
+ * ```typescript
2574
+ * import { Component } from '@angular/core';
2575
+ * import { HelpdeskWidgetService } from '@weldsuite/helpdesk-widget-sdk/angular';
2576
+ *
2577
+ * @Component({
2578
+ * selector: 'app-root',
2579
+ * template: `
2580
+ * <button (click)="openWidget()">Show Support</button>
2581
+ * `
2582
+ * })
2583
+ * export class AppComponent {
2584
+ * constructor(private helpdeskService: HelpdeskWidgetService) {
2585
+ * this.helpdeskService.initialize({ widgetId: 'your-widget-id' });
2586
+ * }
2587
+ *
2588
+ * openWidget() {
2589
+ * this.helpdeskService.open();
2590
+ * }
2591
+ * }
2592
+ * ```
2593
+ */
2594
+ let HelpdeskWidgetService = (() => {
2595
+ let _classDecorators = [Injectable({
2596
+ providedIn: 'root',
2597
+ })];
2598
+ let _classDescriptor;
2599
+ let _classExtraInitializers = [];
2600
+ let _classThis;
2601
+ _classThis = class {
2602
+ constructor() {
2603
+ this.widget = null;
2604
+ }
2605
+ /**
2606
+ * Initialize the widget with configuration
2607
+ */
2608
+ initialize(config) {
2609
+ if (this.widget) {
2610
+ console.warn('Helpdesk widget is already initialized. Destroying previous instance.');
2611
+ this.destroy();
2612
+ }
2613
+ try {
2614
+ this.widget = new WeldSDK(config);
2615
+ this.widget.init();
2616
+ }
2617
+ catch (error) {
2618
+ console.error('Failed to initialize Helpdesk Widget:', error);
2619
+ throw error;
2620
+ }
2621
+ }
2622
+ /**
2623
+ * Open the widget
2624
+ */
2625
+ open() {
2626
+ if (!this.widget) {
2627
+ console.warn('Widget not initialized. Call initialize() first.');
2628
+ return;
2629
+ }
2630
+ this.widget.open();
2631
+ }
2632
+ /**
2633
+ * Close the widget
2634
+ */
2635
+ close() {
2636
+ if (!this.widget) {
2637
+ console.warn('Widget not initialized. Call initialize() first.');
2638
+ return;
2639
+ }
2640
+ this.widget.close();
2641
+ }
2642
+ /**
2643
+ * Toggle widget visibility
2644
+ */
2645
+ toggle() {
2646
+ if (!this.widget) {
2647
+ console.warn('Widget not initialized. Call initialize() first.');
2648
+ return;
2649
+ }
2650
+ this.widget.toggle();
2651
+ }
2652
+ /**
2653
+ * Send a message to the widget
2654
+ */
2655
+ sendMessage(message) {
2656
+ if (!this.widget) {
2657
+ console.warn('Widget not initialized. Call initialize() first.');
2658
+ return;
2659
+ }
2660
+ this.widget.sendMessage(message);
2661
+ }
2662
+ /**
2663
+ * Subscribe to state changes
2664
+ * @returns Unsubscribe function
2665
+ */
2666
+ onStateChange(path, listener) {
2667
+ if (!this.widget) {
2668
+ console.warn('Widget not initialized. Call initialize() first.');
2669
+ return () => { };
2670
+ }
2671
+ return this.widget.onStateChange(path, listener);
2672
+ }
2673
+ /**
2674
+ * Destroy the widget and clean up resources
2675
+ */
2676
+ destroy() {
2677
+ if (this.widget) {
2678
+ this.widget.destroy();
2679
+ this.widget = null;
2680
+ }
2681
+ }
2682
+ /**
2683
+ * Get the widget instance
2684
+ */
2685
+ getWidget() {
2686
+ return this.widget;
2687
+ }
2688
+ /**
2689
+ * Check if SDK is ready
2690
+ */
2691
+ isReady() {
2692
+ return this.widget?.isReady() ?? false;
2693
+ }
2694
+ ngOnDestroy() {
2695
+ this.destroy();
2696
+ }
2697
+ };
2698
+ __setFunctionName(_classThis, "HelpdeskWidgetService");
2699
+ (() => {
2700
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
2701
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
2702
+ _classThis = _classDescriptor.value;
2703
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
2704
+ __runInitializers(_classThis, _classExtraInitializers);
2705
+ })();
2706
+ return _classThis;
2707
+ })();
2708
+
2709
+ /**
2710
+ * Angular module for the Helpdesk Widget
2711
+ *
2712
+ * This module is for backwards compatibility with Angular apps not using standalone components.
2713
+ * For modern Angular apps (v14+), prefer importing the standalone component directly.
2714
+ *
2715
+ * @example
2716
+ * ```typescript
2717
+ * // app.module.ts
2718
+ * import { HelpdeskWidgetModule } from '@weldsuite/helpdesk-widget-sdk/angular';
2719
+ *
2720
+ * @NgModule({
2721
+ * declarations: [AppComponent],
2722
+ * imports: [
2723
+ * BrowserModule,
2724
+ * HelpdeskWidgetModule
2725
+ * ],
2726
+ * providers: [],
2727
+ * bootstrap: [AppComponent]
2728
+ * })
2729
+ * export class AppModule { }
2730
+ * ```
2731
+ *
2732
+ * @example For standalone components (Angular 14+), import directly:
2733
+ * ```typescript
2734
+ * import { HelpdeskWidgetComponent } from '@weldsuite/helpdesk-widget-sdk/angular';
2735
+ *
2736
+ * @Component({
2737
+ * selector: 'app-root',
2738
+ * standalone: true,
2739
+ * imports: [HelpdeskWidgetComponent],
2740
+ * template: `<helpdesk-widget [widgetId]="'your-widget-id'"></helpdesk-widget>`
2741
+ * })
2742
+ * export class AppComponent {}
2743
+ * ```
2744
+ */
2745
+ let HelpdeskWidgetModule = (() => {
2746
+ let _classDecorators = [NgModule({
2747
+ imports: [HelpdeskWidgetComponent],
2748
+ exports: [HelpdeskWidgetComponent],
2749
+ providers: [HelpdeskWidgetService],
2750
+ })];
2751
+ let _classDescriptor;
2752
+ let _classExtraInitializers = [];
2753
+ let _classThis;
2754
+ _classThis = class {
2755
+ };
2756
+ __setFunctionName(_classThis, "HelpdeskWidgetModule");
2757
+ (() => {
2758
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
2759
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
2760
+ _classThis = _classDescriptor.value;
2761
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
2762
+ __runInitializers(_classThis, _classExtraInitializers);
2763
+ })();
2764
+ return _classThis;
2765
+ })();
2766
+
2767
+ export { DEFAULT_CONFIG, WeldSDK as HelpdeskWidget, HelpdeskWidgetComponent, HelpdeskWidgetModule, HelpdeskWidgetService, WeldSDK };
2768
+ //# sourceMappingURL=angular.esm.js.map