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