@angular-helpers/browser-web-apis 21.0.2 → 21.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, inject, DestroyRef, PLATFORM_ID } from '@angular/core';
2
+ import { Injectable, inject, DestroyRef, PLATFORM_ID, signal, computed, ElementRef, makeEnvironmentProviders } from '@angular/core';
3
3
  import { isPlatformBrowser, isPlatformServer } from '@angular/common';
4
- import { Observable } from 'rxjs';
4
+ import { Observable, fromEvent, Subject, of, map as map$1 } from 'rxjs';
5
+ import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
6
+ import { filter, distinctUntilChanged, map } from 'rxjs/operators';
7
+ import { Router } from '@angular/router';
5
8
 
6
9
  class PermissionsService {
7
10
  async query(descriptor) {
@@ -67,6 +70,16 @@ class BrowserApiBaseService {
67
70
  return false;
68
71
  }
69
72
  }
73
+ /**
74
+ * Create an error with proper cause chaining
75
+ */
76
+ createError(message, cause) {
77
+ const error = new Error(message);
78
+ if (cause !== undefined) {
79
+ error.cause = cause;
80
+ }
81
+ return error;
82
+ }
70
83
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BrowserApiBaseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
71
84
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BrowserApiBaseService });
72
85
  }
@@ -76,11 +89,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
76
89
 
77
90
  class CameraService extends BrowserApiBaseService {
78
91
  currentStream = null;
79
- createError(message, cause) {
80
- const error = new Error(message);
81
- error.cause = cause;
82
- return error;
83
- }
84
92
  getApiName() {
85
93
  return 'camera';
86
94
  }
@@ -103,8 +111,8 @@ class CameraService extends BrowserApiBaseService {
103
111
  video: {
104
112
  width: { ideal: 1280 },
105
113
  height: { ideal: 720 },
106
- facingMode: 'user'
107
- }
114
+ facingMode: 'user',
115
+ },
108
116
  };
109
117
  this.currentStream = await navigator.mediaDevices.getUserMedia(streamConstraints);
110
118
  return this.currentStream;
@@ -140,14 +148,14 @@ class CameraService extends BrowserApiBaseService {
140
148
  async switchCamera(deviceId, constraints = {
141
149
  video: {
142
150
  width: { ideal: 1280 },
143
- height: { ideal: 720 }
144
- }
151
+ height: { ideal: 720 },
152
+ },
145
153
  }) {
146
154
  this.stopCamera();
147
155
  const finalConstraints = {
148
156
  video: constraints.video && typeof constraints.video === 'object'
149
157
  ? { ...constraints.video, deviceId: { exact: deviceId } }
150
- : { deviceId: { exact: deviceId } }
158
+ : { deviceId: { exact: deviceId } },
151
159
  };
152
160
  return this.startCamera(finalConstraints);
153
161
  }
@@ -155,7 +163,7 @@ class CameraService extends BrowserApiBaseService {
155
163
  this.ensureCameraSupport();
156
164
  try {
157
165
  const stream = await navigator.mediaDevices.getUserMedia({
158
- video: { deviceId: { exact: deviceId } }
166
+ video: { deviceId: { exact: deviceId } },
159
167
  });
160
168
  const videoTrack = stream.getVideoTracks()[0];
161
169
  const capabilities = videoTrack.getCapabilities();
@@ -178,7 +186,7 @@ class CameraService extends BrowserApiBaseService {
178
186
  this.ensureCameraSupport();
179
187
  try {
180
188
  const devices = await navigator.mediaDevices.enumerateDevices();
181
- return devices.filter(device => device.kind === 'videoinput');
189
+ return devices.filter((device) => device.kind === 'videoinput');
182
190
  }
183
191
  catch (error) {
184
192
  console.error('[CameraService] Error enumerating video devices:', error);
@@ -243,11 +251,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
243
251
  }] });
244
252
 
245
253
  class MediaDevicesService extends BrowserApiBaseService {
246
- createError(message, cause) {
247
- const error = new Error(message);
248
- error.cause = cause;
249
- return error;
250
- }
251
254
  getApiName() {
252
255
  return 'media-devices';
253
256
  }
@@ -272,7 +275,7 @@ class MediaDevicesService extends BrowserApiBaseService {
272
275
  try {
273
276
  const defaultConstraints = {
274
277
  video: true,
275
- audio: true
278
+ audio: true,
276
279
  };
277
280
  const finalConstraints = constraints || defaultConstraints;
278
281
  return await navigator.mediaDevices.getUserMedia(finalConstraints);
@@ -290,7 +293,7 @@ class MediaDevicesService extends BrowserApiBaseService {
290
293
  try {
291
294
  const defaultConstraints = {
292
295
  video: true,
293
- audio: false
296
+ audio: false,
294
297
  };
295
298
  const finalConstraints = constraints || defaultConstraints;
296
299
  return await navigator.mediaDevices.getDisplayMedia(finalConstraints);
@@ -332,7 +335,7 @@ class MediaDevicesService extends BrowserApiBaseService {
332
335
  }
333
336
  async getDevicesByKind(kind) {
334
337
  const devices = await this.getDevices();
335
- return devices.filter(device => device.kind === kind);
338
+ return devices.filter((device) => device.kind === kind);
336
339
  }
337
340
  handleMediaError(error) {
338
341
  let message;
@@ -378,12 +381,23 @@ class NotificationService extends BrowserApiBaseService {
378
381
  getApiName() {
379
382
  return 'notifications';
380
383
  }
384
+ get permission() {
385
+ return Notification.permission;
386
+ }
387
+ isSupported() {
388
+ return 'Notification' in window;
389
+ }
390
+ async requestNotificationPermission() {
391
+ if (!this.isSupported()) {
392
+ throw new Error('Notification API not supported in this browser');
393
+ }
394
+ return Notification.requestPermission();
395
+ }
381
396
  async showNotification(title, options) {
382
- if (!('Notification' in window)) {
397
+ if (!this.isSupported()) {
383
398
  throw new Error('Notification API not supported in this browser');
384
399
  }
385
- const permissionStatus = await this.permissionsService.query({ name: 'notifications' });
386
- if (permissionStatus.state !== 'granted') {
400
+ if (Notification.permission !== 'granted') {
387
401
  throw new Error('Notification permission required. Please grant notification access and try again.');
388
402
  }
389
403
  try {
@@ -410,7 +424,7 @@ class ClipboardService extends BrowserApiBaseService {
410
424
  throw new Error('Clipboard API not supported in this browser');
411
425
  }
412
426
  const permissionStatus = await this.permissionsService.query({
413
- name: `clipboard-${action}`
427
+ name: `clipboard-${action}`,
414
428
  });
415
429
  if (permissionStatus.state !== 'granted') {
416
430
  throw new Error(`Clipboard ${action} permission required. Please grant clipboard access and try again.`);
@@ -465,7 +479,20 @@ const BROWSER_CAPABILITIES = [
465
479
  { id: 'webStorage', label: 'Web Storage', requiresSecureContext: false },
466
480
  { id: 'webShare', label: 'Web Share', requiresSecureContext: true },
467
481
  { id: 'battery', label: 'Battery API', requiresSecureContext: false },
468
- { id: 'webSocket', label: 'WebSocket API', requiresSecureContext: false }
482
+ { id: 'webSocket', label: 'WebSocket API', requiresSecureContext: false },
483
+ { id: 'intersectionObserver', label: 'Intersection Observer', requiresSecureContext: false },
484
+ { id: 'resizeObserver', label: 'Resize Observer', requiresSecureContext: false },
485
+ { id: 'pageVisibility', label: 'Page Visibility API', requiresSecureContext: false },
486
+ { id: 'broadcastChannel', label: 'Broadcast Channel API', requiresSecureContext: false },
487
+ { id: 'networkInformation', label: 'Network Information API', requiresSecureContext: false },
488
+ { id: 'screenWakeLock', label: 'Screen Wake Lock API', requiresSecureContext: true },
489
+ { id: 'screenOrientation', label: 'Screen Orientation API', requiresSecureContext: false },
490
+ { id: 'fullscreen', label: 'Fullscreen API', requiresSecureContext: false },
491
+ { id: 'fileSystemAccess', label: 'File System Access API', requiresSecureContext: true },
492
+ { id: 'mediaRecorder', label: 'MediaRecorder API', requiresSecureContext: true },
493
+ { id: 'serverSentEvents', label: 'Server-Sent Events', requiresSecureContext: false },
494
+ { id: 'vibration', label: 'Vibration API', requiresSecureContext: false },
495
+ { id: 'speechSynthesis', label: 'Speech Synthesis API', requiresSecureContext: false },
469
496
  ];
470
497
  class BrowserCapabilityService {
471
498
  getCapabilities() {
@@ -498,6 +525,34 @@ class BrowserCapabilityService {
498
525
  return typeof navigator !== 'undefined' && 'getBattery' in navigator;
499
526
  case 'webSocket':
500
527
  return typeof WebSocket !== 'undefined';
528
+ case 'intersectionObserver':
529
+ return typeof IntersectionObserver !== 'undefined';
530
+ case 'resizeObserver':
531
+ return typeof ResizeObserver !== 'undefined';
532
+ case 'pageVisibility':
533
+ return typeof document !== 'undefined' && 'hidden' in document;
534
+ case 'broadcastChannel':
535
+ return typeof BroadcastChannel !== 'undefined';
536
+ case 'networkInformation':
537
+ return (typeof navigator !== 'undefined' &&
538
+ ('connection' in navigator || 'mozConnection' in navigator));
539
+ case 'screenWakeLock':
540
+ return typeof navigator !== 'undefined' && 'wakeLock' in navigator;
541
+ case 'screenOrientation':
542
+ return typeof screen !== 'undefined' && 'orientation' in screen;
543
+ case 'fullscreen':
544
+ return (typeof document !== 'undefined' &&
545
+ ('fullscreenEnabled' in document || 'webkitFullscreenEnabled' in document));
546
+ case 'fileSystemAccess':
547
+ return typeof window !== 'undefined' && 'showOpenFilePicker' in window;
548
+ case 'mediaRecorder':
549
+ return typeof MediaRecorder !== 'undefined';
550
+ case 'serverSentEvents':
551
+ return typeof EventSource !== 'undefined';
552
+ case 'vibration':
553
+ return typeof navigator !== 'undefined' && 'vibrate' in navigator;
554
+ case 'speechSynthesis':
555
+ return typeof window !== 'undefined' && 'speechSynthesis' in window;
501
556
  default:
502
557
  return false;
503
558
  }
@@ -509,7 +564,7 @@ class BrowserCapabilityService {
509
564
  label: capability.label,
510
565
  supported: this.isSupported(capability.id),
511
566
  secureContext,
512
- requiresSecureContext: capability.requiresSecureContext
567
+ requiresSecureContext: capability.requiresSecureContext,
513
568
  }));
514
569
  }
515
570
  async getPermissionState(permission) {
@@ -532,128 +587,2011 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
532
587
  args: [{ providedIn: 'root' }]
533
588
  }] });
534
589
 
535
- class BrowserSupportUtil {
536
- static isSupported(feature) {
537
- if (typeof window === 'undefined' || typeof navigator === 'undefined') {
538
- return false;
590
+ class BatteryService extends BrowserApiBaseService {
591
+ batteryManager = null;
592
+ getApiName() {
593
+ return 'battery';
594
+ }
595
+ ensureBatterySupport() {
596
+ const nav = navigator;
597
+ if (!('getBattery' in nav)) {
598
+ throw new Error('Battery API not supported in this browser');
539
599
  }
540
- switch (feature) {
541
- case 'permissions':
542
- return 'permissions' in navigator;
543
- case 'camera':
544
- return 'mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices;
545
- case 'microphone':
546
- return 'mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices;
547
- case 'geolocation':
548
- return 'geolocation' in navigator;
549
- case 'notifications':
550
- return 'Notification' in window;
551
- case 'clipboard':
552
- return 'clipboard' in navigator;
553
- case 'clipboard-read':
554
- return 'clipboard' in navigator && 'readText' in navigator.clipboard;
555
- case 'clipboard-write':
556
- return 'clipboard' in navigator && 'writeText' in navigator.clipboard;
557
- case 'persistent-storage':
558
- return 'storage' in navigator && 'persist' in navigator.storage;
559
- default:
560
- return false;
600
+ }
601
+ async initialize() {
602
+ this.ensureBatterySupport();
603
+ try {
604
+ const nav = navigator;
605
+ this.batteryManager = await nav.getBattery();
606
+ const batteryInfo = this.getBatteryInfo();
607
+ this.setupEventListeners();
608
+ return batteryInfo;
609
+ }
610
+ catch (error) {
611
+ console.error('[BatteryService] Error initializing battery API:', error);
612
+ throw this.createError('Failed to initialize battery API', error);
561
613
  }
562
614
  }
563
- static getUnsupportedFeatures() {
564
- const features = [
565
- 'permissions',
566
- 'camera',
567
- 'microphone',
568
- 'geolocation',
569
- 'notifications',
570
- 'clipboard',
571
- 'clipboard-read',
572
- 'clipboard-write',
573
- 'persistent-storage'
574
- ];
575
- return features.filter(feature => !this.isSupported(feature));
615
+ getBatteryInfo() {
616
+ if (!this.batteryManager) {
617
+ throw new Error('Battery service not initialized. Call initialize() first.');
618
+ }
619
+ return {
620
+ charging: this.batteryManager.charging,
621
+ chargingTime: this.batteryManager.chargingTime,
622
+ dischargingTime: this.batteryManager.dischargingTime,
623
+ level: this.batteryManager.level,
624
+ };
576
625
  }
577
- static isSecureContext() {
578
- return typeof window !== 'undefined' ? window.isSecureContext : false;
626
+ watchBatteryInfo() {
627
+ if (!this.batteryManager) {
628
+ throw new Error('Battery service not initialized. Call initialize() first.');
629
+ }
630
+ return new Observable((observer) => {
631
+ const updateBatteryInfo = () => {
632
+ observer.next(this.getBatteryInfo());
633
+ };
634
+ // Listen to all battery events
635
+ this.batteryManager.addEventListener('chargingchange', updateBatteryInfo);
636
+ this.batteryManager.addEventListener('levelchange', updateBatteryInfo);
637
+ this.batteryManager.addEventListener('chargingtimechange', updateBatteryInfo);
638
+ this.batteryManager.addEventListener('dischargingtimechange', updateBatteryInfo);
639
+ // Send initial value
640
+ updateBatteryInfo();
641
+ return () => {
642
+ // Cleanup event listeners
643
+ this.batteryManager.removeEventListener('chargingchange', updateBatteryInfo);
644
+ this.batteryManager.removeEventListener('levelchange', updateBatteryInfo);
645
+ this.batteryManager.removeEventListener('chargingtimechange', updateBatteryInfo);
646
+ this.batteryManager.removeEventListener('dischargingtimechange', updateBatteryInfo);
647
+ };
648
+ });
579
649
  }
580
- static getUserAgent() {
581
- return typeof navigator !== 'undefined' ? navigator.userAgent : '';
650
+ setupEventListeners() {
651
+ if (!this.batteryManager)
652
+ return;
653
+ this.batteryManager.addEventListener('chargingchange', () => {
654
+ console.log('[BatteryService] Charging status changed:', this.batteryManager.charging);
655
+ });
656
+ this.batteryManager.addEventListener('levelchange', () => {
657
+ console.log('[BatteryService] Battery level changed:', this.batteryManager.level);
658
+ });
582
659
  }
583
- static isMobile() {
584
- const userAgent = this.getUserAgent().toLowerCase();
585
- return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
660
+ // Direct access to native battery API
661
+ getNativeBatteryManager() {
662
+ if (!this.batteryManager) {
663
+ throw new Error('Battery service not initialized. Call initialize() first.');
664
+ }
665
+ return this.batteryManager;
586
666
  }
587
- static isDesktop() {
588
- return !this.isMobile();
667
+ isCharging() {
668
+ return this.getBatteryInfo().charging;
589
669
  }
590
- static getBrowserInfo() {
591
- const userAgent = this.getUserAgent();
592
- return {
593
- name: this.getBrowserName(userAgent),
594
- version: this.getBrowserVersion(userAgent),
595
- isChrome: /chrome/.test(userAgent) && !/edge/.test(userAgent),
596
- isFirefox: /firefox/.test(userAgent),
597
- isSafari: /safari/.test(userAgent) && !/chrome/.test(userAgent),
598
- isEdge: /edge/.test(userAgent) || /edg/.test(userAgent)
599
- };
670
+ getLevel() {
671
+ return this.getBatteryInfo().level;
600
672
  }
601
- static getBrowserName(userAgent) {
602
- if (/chrome/.test(userAgent) && !/edge/.test(userAgent))
603
- return 'Chrome';
604
- if (/firefox/.test(userAgent))
605
- return 'Firefox';
606
- if (/safari/.test(userAgent) && !/chrome/.test(userAgent))
607
- return 'Safari';
608
- if (/edge/.test(userAgent) || /edg/.test(userAgent))
609
- return 'Edge';
610
- return 'Unknown';
673
+ getChargingTime() {
674
+ return this.getBatteryInfo().chargingTime;
611
675
  }
612
- static getBrowserVersion(userAgent) {
613
- const match = userAgent.match(/(chrome|firefox|safari|edge|edg)\/(\d+)/i);
614
- return match ? match[2] : 'Unknown';
676
+ getDischargingTime() {
677
+ return this.getBatteryInfo().dischargingTime;
615
678
  }
679
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BatteryService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
680
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BatteryService });
616
681
  }
682
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BatteryService, decorators: [{
683
+ type: Injectable
684
+ }] });
617
685
 
618
- class PermissionGuard {
619
- permissionsService;
620
- router;
621
- constructor(permissionsService, router) {
622
- this.permissionsService = permissionsService;
623
- this.router = router;
686
+ class WebShareService extends BrowserApiBaseService {
687
+ getApiName() {
688
+ return 'web-share';
624
689
  }
625
- canActivate(route) {
626
- const permission = route.data?.['permission'];
627
- if (!permission) {
628
- return Promise.resolve(true);
690
+ ensureWebShareSupport() {
691
+ if (!('share' in navigator)) {
692
+ throw new Error('Web Share API not supported in this browser');
629
693
  }
630
- return this.checkPermission(permission);
631
694
  }
632
- async checkPermission(permission) {
695
+ async share(data) {
696
+ this.ensureWebShareSupport();
633
697
  try {
634
- const status = await this.permissionsService.query({ name: permission });
635
- if (status.state !== 'granted') {
636
- this.router.navigate(['/permission-denied'], {
637
- queryParams: { permission }
638
- });
639
- return false;
698
+ await navigator.share(data);
699
+ return { shared: true };
700
+ }
701
+ catch (error) {
702
+ console.error('[WebShareService] Error sharing:', error);
703
+ const errorMessage = error instanceof Error ? error.message : 'Share failed';
704
+ return { shared: false, error: errorMessage };
705
+ }
706
+ }
707
+ canShare() {
708
+ return 'share' in navigator;
709
+ }
710
+ canShareFiles() {
711
+ if (!('share' in navigator))
712
+ return false;
713
+ // Check if the browser supports file sharing
714
+ const testFiles = [new File([''], 'test.txt', { type: 'text/plain' })];
715
+ return navigator.canShare?.({ files: testFiles }) ?? false;
716
+ }
717
+ // Direct access to native share API
718
+ getNativeShare() {
719
+ this.ensureWebShareSupport();
720
+ return navigator.share;
721
+ }
722
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebShareService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
723
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebShareService });
724
+ }
725
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebShareService, decorators: [{
726
+ type: Injectable
727
+ }] });
728
+
729
+ class WebStorageService extends BrowserApiBaseService {
730
+ storageEvents = signal(null, ...(ngDevMode ? [{ debugName: "storageEvents" }] : /* istanbul ignore next */ []));
731
+ destroyRef = inject(DestroyRef);
732
+ constructor() {
733
+ super();
734
+ this.setupEventListeners();
735
+ }
736
+ getApiName() {
737
+ return 'storage';
738
+ }
739
+ ensureStorageSupport() {
740
+ if (!this.isBrowserEnvironment() || typeof Storage === 'undefined') {
741
+ throw new Error('Storage API not supported in this browser');
742
+ }
743
+ }
744
+ setupEventListeners() {
745
+ if (this.isBrowserEnvironment()) {
746
+ fromEvent(window, 'storage')
747
+ .pipe(takeUntilDestroyed(this.destroyRef))
748
+ .subscribe((event) => {
749
+ const storageEvent = event;
750
+ if (storageEvent.key && storageEvent.newValue !== null) {
751
+ this.storageEvents.set({
752
+ key: storageEvent.key,
753
+ newValue: this.deserializeValue(storageEvent.newValue),
754
+ oldValue: storageEvent.oldValue ? this.deserializeValue(storageEvent.oldValue) : null,
755
+ storageArea: storageEvent.storageArea === localStorage ? 'localStorage' : 'sessionStorage',
756
+ });
757
+ }
758
+ });
759
+ }
760
+ }
761
+ serializeValue(value, options) {
762
+ if (options?.serialize) {
763
+ return options.serialize(value);
764
+ }
765
+ return JSON.stringify(value);
766
+ }
767
+ deserializeValue(value, options) {
768
+ if (value === null)
769
+ return null;
770
+ if (options?.deserialize) {
771
+ return options.deserialize(value);
772
+ }
773
+ try {
774
+ return JSON.parse(value);
775
+ }
776
+ catch {
777
+ return value;
778
+ }
779
+ }
780
+ getKey(key, options) {
781
+ const prefix = options?.prefix || '';
782
+ return prefix ? `${prefix}:${key}` : key;
783
+ }
784
+ // Local Storage Methods
785
+ setLocalStorage(key, value, options = {}) {
786
+ this.ensureStorageSupport();
787
+ try {
788
+ const serializedValue = this.serializeValue(value, options);
789
+ const fullKey = this.getKey(key, options);
790
+ localStorage.setItem(fullKey, serializedValue);
791
+ return true;
792
+ }
793
+ catch (error) {
794
+ console.error('[WebStorageService] Error setting localStorage:', error);
795
+ return false;
796
+ }
797
+ }
798
+ getLocalStorage(key, defaultValue = null, options = {}) {
799
+ this.ensureStorageSupport();
800
+ try {
801
+ const fullKey = this.getKey(key, options);
802
+ const value = localStorage.getItem(fullKey);
803
+ return value !== null ? this.deserializeValue(value, options) : defaultValue;
804
+ }
805
+ catch (error) {
806
+ console.error('[WebStorageService] Error getting localStorage:', error);
807
+ return defaultValue;
808
+ }
809
+ }
810
+ removeLocalStorage(key, options = {}) {
811
+ this.ensureStorageSupport();
812
+ try {
813
+ const fullKey = this.getKey(key, options);
814
+ localStorage.removeItem(fullKey);
815
+ return true;
816
+ }
817
+ catch (error) {
818
+ console.error('[WebStorageService] Error removing localStorage:', error);
819
+ return false;
820
+ }
821
+ }
822
+ clearLocalStorage(options = {}) {
823
+ this.ensureStorageSupport();
824
+ try {
825
+ const prefix = options?.prefix;
826
+ if (prefix) {
827
+ // Only remove keys with the specified prefix
828
+ const keysToRemove = [];
829
+ for (let i = 0; i < localStorage.length; i++) {
830
+ const key = localStorage.key(i);
831
+ if (key && key.startsWith(`${prefix}:`)) {
832
+ keysToRemove.push(key);
833
+ }
834
+ }
835
+ keysToRemove.forEach((key) => localStorage.removeItem(key));
836
+ }
837
+ else {
838
+ localStorage.clear();
640
839
  }
641
840
  return true;
642
841
  }
643
842
  catch (error) {
644
- console.error('Permission guard error:', error);
645
- this.router.navigate(['/permission-denied'], {
646
- queryParams: { permission }
647
- });
843
+ console.error('[WebStorageService] Error clearing localStorage:', error);
844
+ return false;
845
+ }
846
+ }
847
+ // Session Storage Methods
848
+ setSessionStorage(key, value, options = {}) {
849
+ this.ensureStorageSupport();
850
+ try {
851
+ const serializedValue = this.serializeValue(value, options);
852
+ const fullKey = this.getKey(key, options);
853
+ sessionStorage.setItem(fullKey, serializedValue);
854
+ return true;
855
+ }
856
+ catch (error) {
857
+ console.error('[WebStorageService] Error setting sessionStorage:', error);
858
+ return false;
859
+ }
860
+ }
861
+ getSessionStorage(key, defaultValue = null, options = {}) {
862
+ this.ensureStorageSupport();
863
+ try {
864
+ const fullKey = this.getKey(key, options);
865
+ const value = sessionStorage.getItem(fullKey);
866
+ return value !== null ? this.deserializeValue(value, options) : defaultValue;
867
+ }
868
+ catch (error) {
869
+ console.error('[WebStorageService] Error getting sessionStorage:', error);
870
+ return defaultValue;
871
+ }
872
+ }
873
+ removeSessionStorage(key, options = {}) {
874
+ this.ensureStorageSupport();
875
+ try {
876
+ const fullKey = this.getKey(key, options);
877
+ sessionStorage.removeItem(fullKey);
878
+ return true;
879
+ }
880
+ catch (error) {
881
+ console.error('[WebStorageService] Error removing sessionStorage:', error);
882
+ return false;
883
+ }
884
+ }
885
+ clearSessionStorage(options = {}) {
886
+ this.ensureStorageSupport();
887
+ try {
888
+ const prefix = options?.prefix;
889
+ if (prefix) {
890
+ // Only remove keys with the specified prefix
891
+ const keysToRemove = [];
892
+ for (let i = 0; i < sessionStorage.length; i++) {
893
+ const key = sessionStorage.key(i);
894
+ if (key && key.startsWith(`${prefix}:`)) {
895
+ keysToRemove.push(key);
896
+ }
897
+ }
898
+ keysToRemove.forEach((key) => sessionStorage.removeItem(key));
899
+ }
900
+ else {
901
+ sessionStorage.clear();
902
+ }
903
+ return true;
904
+ }
905
+ catch (error) {
906
+ console.error('[WebStorageService] Error clearing sessionStorage:', error);
648
907
  return false;
649
908
  }
650
909
  }
910
+ // Utility Methods
911
+ getLocalStorageSize(options = {}) {
912
+ this.ensureStorageSupport();
913
+ let totalSize = 0;
914
+ const prefix = options?.prefix;
915
+ for (let i = 0; i < localStorage.length; i++) {
916
+ const key = localStorage.key(i);
917
+ if (key && (!prefix || key.startsWith(`${prefix}:`))) {
918
+ totalSize += (localStorage.getItem(key)?.length || 0) + key.length;
919
+ }
920
+ }
921
+ return totalSize;
922
+ }
923
+ getSessionStorageSize(options = {}) {
924
+ this.ensureStorageSupport();
925
+ let totalSize = 0;
926
+ const prefix = options?.prefix;
927
+ for (let i = 0; i < sessionStorage.length; i++) {
928
+ const key = sessionStorage.key(i);
929
+ if (key && (!prefix || key.startsWith(`${prefix}:`))) {
930
+ totalSize += (sessionStorage.getItem(key)?.length || 0) + key.length;
931
+ }
932
+ }
933
+ return totalSize;
934
+ }
935
+ getStorageEvents() {
936
+ return toObservable(this.storageEvents).pipe(filter((event) => event !== null), distinctUntilChanged((prev, curr) => prev.key === curr.key &&
937
+ prev.newValue === curr.newValue &&
938
+ prev.oldValue === curr.oldValue));
939
+ }
940
+ watchLocalStorage(key, options = {}) {
941
+ return this.getStorageEvents().pipe(map((event) => {
942
+ const fullKey = this.getKey(key, options);
943
+ if (event.key === fullKey && event.storageArea === 'localStorage') {
944
+ return event.newValue;
945
+ }
946
+ return this.getLocalStorage(key, null, options);
947
+ }));
948
+ }
949
+ watchSessionStorage(key, options = {}) {
950
+ return this.getStorageEvents().pipe(map((event) => {
951
+ const fullKey = this.getKey(key, options);
952
+ if (event.key === fullKey && event.storageArea === 'sessionStorage') {
953
+ return event.newValue;
954
+ }
955
+ return this.getSessionStorage(key, null, options);
956
+ }));
957
+ }
958
+ // Direct access to native storage APIs
959
+ getNativeLocalStorage() {
960
+ this.ensureStorageSupport();
961
+ return localStorage;
962
+ }
963
+ getNativeSessionStorage() {
964
+ this.ensureStorageSupport();
965
+ return sessionStorage;
966
+ }
967
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
968
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebStorageService });
651
969
  }
652
- function createPermissionGuard(permission) {
653
- return {
654
- canActivate: [PermissionGuard],
655
- data: { permission }
656
- };
970
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebStorageService, decorators: [{
971
+ type: Injectable
972
+ }], ctorParameters: () => [] });
973
+
974
+ class WebSocketService extends BrowserApiBaseService {
975
+ webSocket = null;
976
+ statusSubject = new Subject();
977
+ messageSubject = new Subject();
978
+ reconnectAttempts = 0;
979
+ reconnectTimer = null;
980
+ heartbeatTimer = null;
981
+ getApiName() {
982
+ return 'websocket';
983
+ }
984
+ ensureWebSocketSupport() {
985
+ if (typeof WebSocket === 'undefined') {
986
+ throw new Error('WebSocket API not supported in this browser');
987
+ }
988
+ }
989
+ connect(config) {
990
+ this.ensureWebSocketSupport();
991
+ return new Observable((observer) => {
992
+ this.disconnect(); // Disconnect existing connection if any
993
+ this.updateStatus({
994
+ connected: false,
995
+ connecting: true,
996
+ reconnecting: false,
997
+ reconnectAttempts: 0,
998
+ });
999
+ try {
1000
+ this.webSocket = new WebSocket(config.url, config.protocols);
1001
+ this.setupWebSocketHandlers(config);
1002
+ observer.next(this.getCurrentStatus());
1003
+ }
1004
+ catch (error) {
1005
+ console.error('[WebSocketService] Error creating WebSocket:', error);
1006
+ this.updateStatus({
1007
+ connected: false,
1008
+ connecting: false,
1009
+ reconnecting: false,
1010
+ error: error instanceof Error ? error.message : 'Connection failed',
1011
+ reconnectAttempts: 0,
1012
+ });
1013
+ observer.next(this.getCurrentStatus());
1014
+ }
1015
+ return () => {
1016
+ this.disconnect();
1017
+ };
1018
+ });
1019
+ }
1020
+ disconnect() {
1021
+ if (this.reconnectTimer) {
1022
+ clearTimeout(this.reconnectTimer);
1023
+ this.reconnectTimer = null;
1024
+ }
1025
+ if (this.heartbeatTimer) {
1026
+ clearInterval(this.heartbeatTimer);
1027
+ this.heartbeatTimer = null;
1028
+ }
1029
+ if (this.webSocket) {
1030
+ this.webSocket.close();
1031
+ this.webSocket = null;
1032
+ }
1033
+ this.updateStatus({
1034
+ connected: false,
1035
+ connecting: false,
1036
+ reconnecting: false,
1037
+ reconnectAttempts: 0,
1038
+ });
1039
+ }
1040
+ send(message) {
1041
+ if (!this.webSocket || this.webSocket.readyState !== WebSocket.OPEN) {
1042
+ throw new Error('WebSocket is not connected');
1043
+ }
1044
+ const messageWithTimestamp = {
1045
+ ...message,
1046
+ timestamp: Date.now(),
1047
+ };
1048
+ this.webSocket.send(JSON.stringify(messageWithTimestamp));
1049
+ }
1050
+ sendRaw(data) {
1051
+ if (!this.webSocket || this.webSocket.readyState !== WebSocket.OPEN) {
1052
+ throw new Error('WebSocket is not connected');
1053
+ }
1054
+ this.webSocket.send(data);
1055
+ }
1056
+ getStatus() {
1057
+ return this.statusSubject.asObservable();
1058
+ }
1059
+ getMessages() {
1060
+ return this.messageSubject
1061
+ .asObservable()
1062
+ .pipe(filter((msg) => true));
1063
+ }
1064
+ setupWebSocketHandlers(config) {
1065
+ if (!this.webSocket)
1066
+ return;
1067
+ this.webSocket.onopen = () => {
1068
+ console.log('[WebSocketService] Connected to:', config.url);
1069
+ this.reconnectAttempts = 0;
1070
+ this.updateStatus({
1071
+ connected: true,
1072
+ connecting: false,
1073
+ reconnecting: false,
1074
+ reconnectAttempts: 0,
1075
+ });
1076
+ // Start heartbeat if configured
1077
+ if (config.heartbeatInterval && config.heartbeatMessage) {
1078
+ this.startHeartbeat(config);
1079
+ }
1080
+ };
1081
+ this.webSocket.onclose = (event) => {
1082
+ console.log('[WebSocketService] Connection closed:', event.code, event.reason);
1083
+ this.updateStatus({
1084
+ connected: false,
1085
+ connecting: false,
1086
+ reconnecting: false,
1087
+ reconnectAttempts: this.reconnectAttempts,
1088
+ });
1089
+ // Attempt reconnection if not a clean close and reconnect is enabled
1090
+ if (!event.wasClean && config.reconnectInterval && config.maxReconnectAttempts) {
1091
+ this.attemptReconnect(config);
1092
+ }
1093
+ };
1094
+ this.webSocket.onerror = (error) => {
1095
+ console.error('[WebSocketService] WebSocket error:', error);
1096
+ this.updateStatus({
1097
+ connected: false,
1098
+ connecting: false,
1099
+ reconnecting: false,
1100
+ error: 'WebSocket connection error',
1101
+ reconnectAttempts: this.reconnectAttempts,
1102
+ });
1103
+ };
1104
+ this.webSocket.onmessage = (event) => {
1105
+ try {
1106
+ const message = JSON.parse(event.data);
1107
+ this.messageSubject.next(message);
1108
+ }
1109
+ catch (error) {
1110
+ console.error('[WebSocketService] Error parsing message:', error);
1111
+ }
1112
+ };
1113
+ }
1114
+ startHeartbeat(config) {
1115
+ this.heartbeatTimer = setInterval(() => {
1116
+ if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) {
1117
+ this.send({
1118
+ type: 'heartbeat',
1119
+ data: config.heartbeatMessage,
1120
+ });
1121
+ }
1122
+ }, config.heartbeatInterval);
1123
+ }
1124
+ attemptReconnect(config) {
1125
+ if (this.reconnectAttempts >= (config.maxReconnectAttempts || 5)) {
1126
+ console.log('[WebSocketService] Max reconnect attempts reached');
1127
+ return;
1128
+ }
1129
+ this.reconnectAttempts++;
1130
+ this.updateStatus({
1131
+ connected: false,
1132
+ connecting: false,
1133
+ reconnecting: true,
1134
+ reconnectAttempts: this.reconnectAttempts,
1135
+ });
1136
+ this.reconnectTimer = setTimeout(() => {
1137
+ console.log(`[WebSocketService] Reconnect attempt ${this.reconnectAttempts}`);
1138
+ this.connect(config);
1139
+ }, config.reconnectInterval || 3000);
1140
+ }
1141
+ updateStatus(status) {
1142
+ const currentStatus = this.getCurrentStatus();
1143
+ const newStatus = { ...currentStatus, ...status };
1144
+ this.statusSubject.next(newStatus);
1145
+ }
1146
+ getCurrentStatus() {
1147
+ return {
1148
+ connected: this.webSocket?.readyState === WebSocket.OPEN,
1149
+ connecting: false,
1150
+ reconnecting: false,
1151
+ reconnectAttempts: this.reconnectAttempts,
1152
+ };
1153
+ }
1154
+ // Direct access to native WebSocket
1155
+ getNativeWebSocket() {
1156
+ return this.webSocket;
1157
+ }
1158
+ isConnected() {
1159
+ return this.webSocket?.readyState === WebSocket.OPEN;
1160
+ }
1161
+ getReadyState() {
1162
+ return this.webSocket?.readyState ?? WebSocket.CLOSED;
1163
+ }
1164
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebSocketService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1165
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebSocketService });
1166
+ }
1167
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebSocketService, decorators: [{
1168
+ type: Injectable
1169
+ }] });
1170
+
1171
+ class WebWorkerService extends BrowserApiBaseService {
1172
+ destroyRef = inject(DestroyRef);
1173
+ workers = new Map();
1174
+ workerStatuses = new Map();
1175
+ workerMessages = new Map();
1176
+ currentWorkerStatuses = new Map();
1177
+ getApiName() {
1178
+ return 'webworker';
1179
+ }
1180
+ ensureWorkerSupport() {
1181
+ if (typeof Worker === 'undefined') {
1182
+ throw new Error('Web Workers not supported in this browser');
1183
+ }
1184
+ }
1185
+ createWorker(name, scriptUrl) {
1186
+ this.ensureWorkerSupport();
1187
+ return new Observable((observer) => {
1188
+ if (this.workers.has(name)) {
1189
+ observer.next(this.currentWorkerStatuses.get(name));
1190
+ return () => {
1191
+ // No-op: workers are managed explicitly via terminateWorker/terminateAllWorkers
1192
+ };
1193
+ }
1194
+ try {
1195
+ const worker = new Worker(scriptUrl);
1196
+ this.workers.set(name, worker);
1197
+ this.setupWorker(name, worker);
1198
+ const status = {
1199
+ initialized: true,
1200
+ running: true,
1201
+ messageCount: 0,
1202
+ };
1203
+ this.currentWorkerStatuses.set(name, status);
1204
+ this.updateWorkerStatus(name, status);
1205
+ observer.next(status);
1206
+ }
1207
+ catch (error) {
1208
+ console.error(`[WebWorkerService] Failed to create worker ${name}:`, error);
1209
+ const status = {
1210
+ initialized: false,
1211
+ running: false,
1212
+ error: error instanceof Error ? error.message : 'Unknown error',
1213
+ messageCount: 0,
1214
+ };
1215
+ this.currentWorkerStatuses.set(name, status);
1216
+ this.updateWorkerStatus(name, status);
1217
+ observer.next(status);
1218
+ }
1219
+ return () => {
1220
+ // No-op: workers are managed explicitly via terminateWorker/terminateAllWorkers
1221
+ };
1222
+ });
1223
+ }
1224
+ terminateWorker(name) {
1225
+ const worker = this.workers.get(name);
1226
+ if (worker) {
1227
+ worker.terminate();
1228
+ this.workers.delete(name);
1229
+ this.workerStatuses.delete(name);
1230
+ this.workerMessages.delete(name);
1231
+ this.currentWorkerStatuses.delete(name);
1232
+ }
1233
+ }
1234
+ terminateAllWorkers() {
1235
+ this.workers.forEach((_, name) => {
1236
+ this.terminateWorker(name);
1237
+ });
1238
+ }
1239
+ postMessage(workerName, task) {
1240
+ const worker = this.workers.get(workerName);
1241
+ if (!worker) {
1242
+ console.error(`[WebWorkerService] Worker ${workerName} not found`);
1243
+ return;
1244
+ }
1245
+ try {
1246
+ const message = { ...task, timestamp: Date.now() };
1247
+ if (task.transferable) {
1248
+ worker.postMessage(message, task.transferable);
1249
+ }
1250
+ else {
1251
+ worker.postMessage(message);
1252
+ }
1253
+ const currentStatus = this.currentWorkerStatuses.get(workerName);
1254
+ if (currentStatus) {
1255
+ currentStatus.messageCount++;
1256
+ this.updateWorkerStatus(workerName, currentStatus);
1257
+ }
1258
+ }
1259
+ catch (error) {
1260
+ console.error(`[WebWorkerService] Failed to post message to worker ${workerName}:`, error);
1261
+ }
1262
+ }
1263
+ getMessages(workerName) {
1264
+ if (!this.workerMessages.has(workerName)) {
1265
+ this.workerMessages.set(workerName, new Subject());
1266
+ }
1267
+ return this.workerMessages.get(workerName).asObservable();
1268
+ }
1269
+ getStatus(workerName) {
1270
+ if (!this.workerStatuses.has(workerName)) {
1271
+ this.workerStatuses.set(workerName, new Subject());
1272
+ }
1273
+ return this.workerStatuses.get(workerName).asObservable();
1274
+ }
1275
+ getCurrentStatus(workerName) {
1276
+ return this.currentWorkerStatuses.get(workerName);
1277
+ }
1278
+ getAllStatuses() {
1279
+ return new Map(this.currentWorkerStatuses);
1280
+ }
1281
+ isWorkerRunning(workerName) {
1282
+ const status = this.currentWorkerStatuses.get(workerName);
1283
+ return status?.running ?? false;
1284
+ }
1285
+ setupWorker(name, worker) {
1286
+ worker.onmessage = (event) => {
1287
+ const message = {
1288
+ id: event.data.id || `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
1289
+ type: event.data.type || 'message',
1290
+ data: event.data.data,
1291
+ timestamp: event.data.timestamp || Date.now(),
1292
+ };
1293
+ if (!this.workerMessages.has(name)) {
1294
+ this.workerMessages.set(name, new Subject());
1295
+ }
1296
+ this.workerMessages.get(name).next(message);
1297
+ };
1298
+ worker.onerror = (error) => {
1299
+ console.error(`[WebWorkerService] Worker ${name} error:`, error);
1300
+ const status = {
1301
+ initialized: true,
1302
+ running: false,
1303
+ error: error instanceof Error ? error.message : 'Worker error',
1304
+ messageCount: this.currentWorkerStatuses.get(name)?.messageCount ?? 0,
1305
+ };
1306
+ this.currentWorkerStatuses.set(name, status);
1307
+ this.updateWorkerStatus(name, status);
1308
+ };
1309
+ // Auto-cleanup when service is destroyed
1310
+ this.destroyRef.onDestroy(() => {
1311
+ this.terminateWorker(name);
1312
+ });
1313
+ }
1314
+ updateWorkerStatus(name, status) {
1315
+ if (!this.workerStatuses.has(name)) {
1316
+ this.workerStatuses.set(name, new Subject());
1317
+ }
1318
+ this.workerStatuses.get(name).next(status);
1319
+ }
1320
+ // Direct access to native Worker API
1321
+ getNativeWorker(name) {
1322
+ return this.workers.get(name);
1323
+ }
1324
+ getAllWorkers() {
1325
+ return new Map(this.workers);
1326
+ }
1327
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebWorkerService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1328
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebWorkerService });
1329
+ }
1330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebWorkerService, decorators: [{
1331
+ type: Injectable
1332
+ }] });
1333
+
1334
+ function isIntersectionObserverSupported() {
1335
+ return typeof window !== 'undefined' && 'IntersectionObserver' in window;
1336
+ }
1337
+ function intersectionObserverStream(element, options = {}) {
1338
+ return new Observable((observer) => {
1339
+ const io = new IntersectionObserver((entries) => {
1340
+ const entry = entries[entries.length - 1];
1341
+ observer.next(entry.isIntersecting);
1342
+ }, options);
1343
+ io.observe(element);
1344
+ return () => {
1345
+ io.unobserve(element);
1346
+ io.disconnect();
1347
+ };
1348
+ });
1349
+ }
1350
+ function intersectionObserverEntriesStream(element, options = {}) {
1351
+ return new Observable((observer) => {
1352
+ const io = new IntersectionObserver((entries) => observer.next(entries), options);
1353
+ io.observe(element);
1354
+ return () => {
1355
+ io.unobserve(element);
1356
+ io.disconnect();
1357
+ };
1358
+ });
1359
+ }
1360
+
1361
+ class IntersectionObserverService {
1362
+ platformId = inject(PLATFORM_ID);
1363
+ isSupported() {
1364
+ return isPlatformBrowser(this.platformId) && 'IntersectionObserver' in window;
1365
+ }
1366
+ observe(element, options = {}) {
1367
+ if (!this.isSupported()) {
1368
+ return new Observable((o) => o.error(new Error('IntersectionObserver API not supported')));
1369
+ }
1370
+ return intersectionObserverEntriesStream(element, options);
1371
+ }
1372
+ observeVisibility(element, options = {}) {
1373
+ if (!this.isSupported()) {
1374
+ return new Observable((o) => o.error(new Error('IntersectionObserver API not supported')));
1375
+ }
1376
+ return intersectionObserverStream(element, options);
1377
+ }
1378
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1379
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService });
1380
+ }
1381
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService, decorators: [{
1382
+ type: Injectable
1383
+ }] });
1384
+
1385
+ function isResizeObserverSupported() {
1386
+ return typeof window !== 'undefined' && 'ResizeObserver' in window;
1387
+ }
1388
+ function resizeObserverStream(element, options = {}) {
1389
+ return new Observable((observer) => {
1390
+ const ro = new ResizeObserver((entries) => {
1391
+ const entry = entries[entries.length - 1];
1392
+ const contentRect = entry.contentRect;
1393
+ const borderBoxSize = entry.borderBoxSize?.[0];
1394
+ observer.next({
1395
+ width: contentRect.width,
1396
+ height: contentRect.height,
1397
+ inlineSize: borderBoxSize?.inlineSize ?? contentRect.width,
1398
+ blockSize: borderBoxSize?.blockSize ?? contentRect.height,
1399
+ });
1400
+ });
1401
+ ro.observe(element, options);
1402
+ return () => {
1403
+ ro.unobserve(element);
1404
+ ro.disconnect();
1405
+ };
1406
+ });
1407
+ }
1408
+ function resizeObserverEntriesStream(element, options = {}) {
1409
+ return new Observable((observer) => {
1410
+ const ro = new ResizeObserver((entries) => observer.next(entries));
1411
+ ro.observe(element, options);
1412
+ return () => {
1413
+ ro.unobserve(element);
1414
+ ro.disconnect();
1415
+ };
1416
+ });
1417
+ }
1418
+
1419
+ class ResizeObserverService {
1420
+ platformId = inject(PLATFORM_ID);
1421
+ isSupported() {
1422
+ return isPlatformBrowser(this.platformId) && 'ResizeObserver' in window;
1423
+ }
1424
+ observe(element, options = {}) {
1425
+ if (!this.isSupported()) {
1426
+ return new Observable((o) => o.error(new Error('ResizeObserver API not supported')));
1427
+ }
1428
+ return resizeObserverEntriesStream(element, options);
1429
+ }
1430
+ observeSize(element, options = {}) {
1431
+ if (!this.isSupported()) {
1432
+ return new Observable((o) => o.error(new Error('ResizeObserver API not supported')));
1433
+ }
1434
+ return resizeObserverStream(element, options);
1435
+ }
1436
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1437
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService });
1438
+ }
1439
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, decorators: [{
1440
+ type: Injectable
1441
+ }] });
1442
+
1443
+ function isPageVisibilitySupported() {
1444
+ return typeof document !== 'undefined' && 'hidden' in document;
1445
+ }
1446
+ function pageVisibilityStream() {
1447
+ if (!isPageVisibilitySupported()) {
1448
+ return of('visible');
1449
+ }
1450
+ return new Observable((observer) => {
1451
+ const handler = () => observer.next(document.visibilityState);
1452
+ document.addEventListener('visibilitychange', handler);
1453
+ observer.next(document.visibilityState);
1454
+ return () => document.removeEventListener('visibilitychange', handler);
1455
+ });
1456
+ }
1457
+
1458
+ class PageVisibilityService {
1459
+ platformId = inject(PLATFORM_ID);
1460
+ isSupported() {
1461
+ return isPlatformBrowser(this.platformId) && 'hidden' in document;
1462
+ }
1463
+ get isHidden() {
1464
+ if (!this.isSupported())
1465
+ return false;
1466
+ return document.hidden;
1467
+ }
1468
+ get visibilityState() {
1469
+ if (!this.isSupported())
1470
+ return 'visible';
1471
+ return document.visibilityState;
1472
+ }
1473
+ watch() {
1474
+ return pageVisibilityStream();
1475
+ }
1476
+ watchVisibility() {
1477
+ return pageVisibilityStream().pipe(map$1((s) => s === 'visible'));
1478
+ }
1479
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1480
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService });
1481
+ }
1482
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService, decorators: [{
1483
+ type: Injectable
1484
+ }] });
1485
+
1486
+ class BroadcastChannelService {
1487
+ destroyRef = inject(DestroyRef);
1488
+ platformId = inject(PLATFORM_ID);
1489
+ channels = new Map();
1490
+ isSupported() {
1491
+ return isPlatformBrowser(this.platformId) && 'BroadcastChannel' in window;
1492
+ }
1493
+ ensureSupport() {
1494
+ if (!this.isSupported()) {
1495
+ throw new Error('BroadcastChannel API not supported in this environment');
1496
+ }
1497
+ }
1498
+ open(name) {
1499
+ this.ensureSupport();
1500
+ return new Observable((observer) => {
1501
+ let channel = this.channels.get(name);
1502
+ if (!channel) {
1503
+ channel = new BroadcastChannel(name);
1504
+ this.channels.set(name, channel);
1505
+ }
1506
+ const handler = (event) => observer.next(event.data);
1507
+ const errorHandler = () => observer.error(new Error(`BroadcastChannel "${name}" error`));
1508
+ channel.addEventListener('message', handler);
1509
+ channel.addEventListener('messageerror', errorHandler);
1510
+ const cleanup = () => {
1511
+ channel.removeEventListener('message', handler);
1512
+ channel.removeEventListener('messageerror', errorHandler);
1513
+ };
1514
+ this.destroyRef.onDestroy(() => this.close(name));
1515
+ return cleanup;
1516
+ });
1517
+ }
1518
+ post(name, data) {
1519
+ this.ensureSupport();
1520
+ let channel = this.channels.get(name);
1521
+ if (!channel) {
1522
+ channel = new BroadcastChannel(name);
1523
+ this.channels.set(name, channel);
1524
+ this.destroyRef.onDestroy(() => this.close(name));
1525
+ }
1526
+ channel.postMessage(data);
1527
+ }
1528
+ close(name) {
1529
+ const channel = this.channels.get(name);
1530
+ if (channel) {
1531
+ channel.close();
1532
+ this.channels.delete(name);
1533
+ }
1534
+ }
1535
+ closeAll() {
1536
+ this.channels.forEach((channel) => channel.close());
1537
+ this.channels.clear();
1538
+ }
1539
+ getOpenChannels() {
1540
+ return Array.from(this.channels.keys());
1541
+ }
1542
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1543
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService });
1544
+ }
1545
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService, decorators: [{
1546
+ type: Injectable
1547
+ }] });
1548
+
1549
+ function isNetworkInformationSupported() {
1550
+ if (typeof navigator === 'undefined')
1551
+ return false;
1552
+ const nav = navigator;
1553
+ return 'connection' in nav || 'mozConnection' in nav || 'webkitConnection' in nav;
1554
+ }
1555
+ function getNetworkConnection() {
1556
+ if (typeof navigator === 'undefined')
1557
+ return undefined;
1558
+ const nav = navigator;
1559
+ return nav.connection ?? nav.mozConnection ?? nav.webkitConnection;
1560
+ }
1561
+ function getNetworkSnapshot() {
1562
+ const online = typeof navigator !== 'undefined' ? navigator.onLine : true;
1563
+ const conn = getNetworkConnection();
1564
+ return {
1565
+ online,
1566
+ type: conn?.type,
1567
+ effectiveType: conn?.effectiveType,
1568
+ downlink: conn?.downlink,
1569
+ downlinkMax: conn?.downlinkMax,
1570
+ rtt: conn?.rtt,
1571
+ saveData: conn?.saveData,
1572
+ };
1573
+ }
1574
+ function networkInformationStream() {
1575
+ if (typeof window === 'undefined') {
1576
+ return of({ online: true });
1577
+ }
1578
+ return new Observable((observer) => {
1579
+ const emit = () => observer.next(getNetworkSnapshot());
1580
+ const conn = getNetworkConnection();
1581
+ if (conn)
1582
+ conn.addEventListener('change', emit);
1583
+ window.addEventListener('online', emit);
1584
+ window.addEventListener('offline', emit);
1585
+ emit();
1586
+ return () => {
1587
+ conn?.removeEventListener('change', emit);
1588
+ window.removeEventListener('online', emit);
1589
+ window.removeEventListener('offline', emit);
1590
+ };
1591
+ });
1592
+ }
1593
+
1594
+ class NetworkInformationService {
1595
+ platformId = inject(PLATFORM_ID);
1596
+ isSupported() {
1597
+ return isPlatformBrowser(this.platformId) && isNetworkInformationSupported();
1598
+ }
1599
+ getSnapshot() {
1600
+ return isPlatformBrowser(this.platformId) ? getNetworkSnapshot() : { online: true };
1601
+ }
1602
+ watch() {
1603
+ return networkInformationStream();
1604
+ }
1605
+ get isOnline() {
1606
+ return isPlatformBrowser(this.platformId) ? navigator.onLine : true;
1607
+ }
1608
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1609
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService });
1610
+ }
1611
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService, decorators: [{
1612
+ type: Injectable
1613
+ }] });
1614
+
1615
+ class ScreenWakeLockService extends BrowserApiBaseService {
1616
+ sentinel = null;
1617
+ getApiName() {
1618
+ return 'screen-wake-lock';
1619
+ }
1620
+ isSupported() {
1621
+ return isPlatformBrowser(this.platformId) && 'wakeLock' in navigator;
1622
+ }
1623
+ get isActive() {
1624
+ return this.sentinel !== null && !this.sentinel.released;
1625
+ }
1626
+ async request(type = 'screen') {
1627
+ if (!this.isSupported()) {
1628
+ throw new Error('Screen Wake Lock API not supported in this browser');
1629
+ }
1630
+ if (!window.isSecureContext) {
1631
+ throw new Error('Screen Wake Lock API requires a secure context (HTTPS)');
1632
+ }
1633
+ try {
1634
+ this.sentinel = await navigator.wakeLock.request(type);
1635
+ this.sentinel.addEventListener('release', () => {
1636
+ this.sentinel = null;
1637
+ });
1638
+ this.destroyRef.onDestroy(() => this.release());
1639
+ return { active: true, type, released: false };
1640
+ }
1641
+ catch (error) {
1642
+ console.error('[ScreenWakeLockService] Failed to acquire wake lock:', error);
1643
+ throw this.createError('Failed to acquire wake lock', error);
1644
+ }
1645
+ }
1646
+ async release() {
1647
+ if (this.sentinel && !this.sentinel.released) {
1648
+ try {
1649
+ await this.sentinel.release();
1650
+ }
1651
+ catch {
1652
+ // Sentinel may already be released
1653
+ }
1654
+ finally {
1655
+ this.sentinel = null;
1656
+ }
1657
+ }
1658
+ }
1659
+ watchStatus() {
1660
+ return new Observable((observer) => {
1661
+ const emit = () => observer.next({ active: this.isActive, released: !this.isActive });
1662
+ const handleVisibilityChange = async () => {
1663
+ if (document.visibilityState === 'visible' && !this.isActive) {
1664
+ try {
1665
+ await this.request();
1666
+ }
1667
+ catch {
1668
+ // Could not re-acquire
1669
+ }
1670
+ }
1671
+ emit();
1672
+ };
1673
+ document.addEventListener('visibilitychange', handleVisibilityChange);
1674
+ emit();
1675
+ const cleanup = () => document.removeEventListener('visibilitychange', handleVisibilityChange);
1676
+ this.destroyRef.onDestroy(cleanup);
1677
+ return cleanup;
1678
+ });
1679
+ }
1680
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenWakeLockService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1681
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenWakeLockService });
1682
+ }
1683
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenWakeLockService, decorators: [{
1684
+ type: Injectable
1685
+ }] });
1686
+
1687
+ function isScreenOrientationSupported() {
1688
+ return typeof window !== 'undefined' && 'screen' in window && 'orientation' in screen;
1689
+ }
1690
+ function getOrientationSnapshot() {
1691
+ if (!isScreenOrientationSupported()) {
1692
+ return { type: 'portrait-primary', angle: 0 };
1693
+ }
1694
+ return {
1695
+ type: screen.orientation.type,
1696
+ angle: screen.orientation.angle,
1697
+ };
1698
+ }
1699
+ function screenOrientationStream() {
1700
+ if (!isScreenOrientationSupported()) {
1701
+ return of({ type: 'portrait-primary', angle: 0 });
1702
+ }
1703
+ return new Observable((observer) => {
1704
+ const handler = () => observer.next(getOrientationSnapshot());
1705
+ screen.orientation.addEventListener('change', handler);
1706
+ observer.next(getOrientationSnapshot());
1707
+ return () => screen.orientation.removeEventListener('change', handler);
1708
+ });
1709
+ }
1710
+
1711
+ class ScreenOrientationService {
1712
+ platformId = inject(PLATFORM_ID);
1713
+ isSupported() {
1714
+ return isPlatformBrowser(this.platformId) && 'screen' in window && 'orientation' in screen;
1715
+ }
1716
+ getSnapshot() {
1717
+ return isPlatformBrowser(this.platformId)
1718
+ ? getOrientationSnapshot()
1719
+ : { type: 'portrait-primary', angle: 0 };
1720
+ }
1721
+ get isPortrait() {
1722
+ return this.getSnapshot().type.startsWith('portrait');
1723
+ }
1724
+ get isLandscape() {
1725
+ return this.getSnapshot().type.startsWith('landscape');
1726
+ }
1727
+ watch() {
1728
+ return screenOrientationStream();
1729
+ }
1730
+ async lock(orientation) {
1731
+ if (!this.isSupported()) {
1732
+ throw new Error('Screen Orientation API not supported');
1733
+ }
1734
+ try {
1735
+ await screen.orientation.lock(orientation);
1736
+ }
1737
+ catch (error) {
1738
+ console.error('[ScreenOrientationService] Failed to lock orientation:', error);
1739
+ throw error;
1740
+ }
1741
+ }
1742
+ unlock() {
1743
+ if (this.isSupported()) {
1744
+ screen.orientation.unlock();
1745
+ }
1746
+ }
1747
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1748
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService });
1749
+ }
1750
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService, decorators: [{
1751
+ type: Injectable
1752
+ }] });
1753
+
1754
+ class FullscreenService {
1755
+ destroyRef = inject(DestroyRef);
1756
+ platformId = inject(PLATFORM_ID);
1757
+ isSupported() {
1758
+ if (!isPlatformBrowser(this.platformId))
1759
+ return false;
1760
+ return !!(document.fullscreenEnabled ??
1761
+ document.webkitFullscreenEnabled);
1762
+ }
1763
+ get isFullscreen() {
1764
+ if (!isPlatformBrowser(this.platformId))
1765
+ return false;
1766
+ return !!(document.fullscreenElement ??
1767
+ document.webkitFullscreenElement);
1768
+ }
1769
+ get fullscreenElement() {
1770
+ if (!isPlatformBrowser(this.platformId))
1771
+ return null;
1772
+ return (document.fullscreenElement ??
1773
+ document.webkitFullscreenElement ??
1774
+ null);
1775
+ }
1776
+ async request(element = document.documentElement) {
1777
+ if (!this.isSupported()) {
1778
+ throw new Error('Fullscreen API not supported in this browser');
1779
+ }
1780
+ try {
1781
+ const el = element;
1782
+ if (el.requestFullscreen) {
1783
+ await el.requestFullscreen();
1784
+ }
1785
+ else if (el.webkitRequestFullscreen) {
1786
+ await el.webkitRequestFullscreen();
1787
+ }
1788
+ }
1789
+ catch (error) {
1790
+ console.error('[FullscreenService] Failed to enter fullscreen:', error);
1791
+ throw error;
1792
+ }
1793
+ }
1794
+ async exit() {
1795
+ if (!this.isFullscreen)
1796
+ return;
1797
+ try {
1798
+ const doc = document;
1799
+ if (doc.exitFullscreen) {
1800
+ await doc.exitFullscreen();
1801
+ }
1802
+ else if (doc.webkitExitFullscreen) {
1803
+ await doc.webkitExitFullscreen();
1804
+ }
1805
+ }
1806
+ catch (error) {
1807
+ console.error('[FullscreenService] Failed to exit fullscreen:', error);
1808
+ throw error;
1809
+ }
1810
+ }
1811
+ async toggle(element = document.documentElement) {
1812
+ if (this.isFullscreen) {
1813
+ await this.exit();
1814
+ }
1815
+ else {
1816
+ await this.request(element);
1817
+ }
1818
+ }
1819
+ watch() {
1820
+ return new Observable((observer) => {
1821
+ if (!isPlatformBrowser(this.platformId)) {
1822
+ observer.next(false);
1823
+ observer.complete();
1824
+ return undefined;
1825
+ }
1826
+ const handler = () => observer.next(this.isFullscreen);
1827
+ document.addEventListener('fullscreenchange', handler);
1828
+ document.addEventListener('webkitfullscreenchange', handler);
1829
+ observer.next(this.isFullscreen);
1830
+ const cleanup = () => {
1831
+ document.removeEventListener('fullscreenchange', handler);
1832
+ document.removeEventListener('webkitfullscreenchange', handler);
1833
+ };
1834
+ this.destroyRef.onDestroy(cleanup);
1835
+ return cleanup;
1836
+ });
1837
+ }
1838
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1839
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService });
1840
+ }
1841
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService, decorators: [{
1842
+ type: Injectable
1843
+ }] });
1844
+
1845
+ class FileSystemAccessService extends BrowserApiBaseService {
1846
+ getApiName() {
1847
+ return 'file-system-access';
1848
+ }
1849
+ isSupported() {
1850
+ return this.isBrowserEnvironment() && 'showOpenFilePicker' in window && window.isSecureContext;
1851
+ }
1852
+ get win() {
1853
+ return window;
1854
+ }
1855
+ ensureSupport() {
1856
+ if (this.isServerEnvironment()) {
1857
+ throw new Error('File System Access API not available in server environment');
1858
+ }
1859
+ if (!('showOpenFilePicker' in window)) {
1860
+ throw new Error('File System Access API not supported in this browser');
1861
+ }
1862
+ if (!window.isSecureContext) {
1863
+ throw new Error('File System Access API requires a secure context (HTTPS)');
1864
+ }
1865
+ }
1866
+ async openFile(options = {}) {
1867
+ this.ensureSupport();
1868
+ try {
1869
+ const handles = await this.win.showOpenFilePicker(options);
1870
+ return Promise.all(handles.map((h) => h.getFile()));
1871
+ }
1872
+ catch (error) {
1873
+ if (error instanceof DOMException && error.name === 'AbortError') {
1874
+ return [];
1875
+ }
1876
+ console.error('[FileSystemAccessService] Error opening file:', error);
1877
+ throw error;
1878
+ }
1879
+ }
1880
+ async saveFile(content, options = {}) {
1881
+ this.ensureSupport();
1882
+ if (!this.win.showSaveFilePicker) {
1883
+ throw new Error('showSaveFilePicker not supported');
1884
+ }
1885
+ try {
1886
+ const handle = await this.win.showSaveFilePicker(options);
1887
+ const writable = await handle.createWritable();
1888
+ await writable.write(content);
1889
+ await writable.close();
1890
+ }
1891
+ catch (error) {
1892
+ if (error instanceof DOMException && error.name === 'AbortError') {
1893
+ return;
1894
+ }
1895
+ console.error('[FileSystemAccessService] Error saving file:', error);
1896
+ throw error;
1897
+ }
1898
+ }
1899
+ async openDirectory(options = {}) {
1900
+ this.ensureSupport();
1901
+ if (!this.win.showDirectoryPicker) {
1902
+ throw new Error('showDirectoryPicker not supported');
1903
+ }
1904
+ try {
1905
+ return await this.win.showDirectoryPicker(options);
1906
+ }
1907
+ catch (error) {
1908
+ if (error instanceof DOMException && error.name === 'AbortError') {
1909
+ return null;
1910
+ }
1911
+ console.error('[FileSystemAccessService] Error opening directory:', error);
1912
+ throw error;
1913
+ }
1914
+ }
1915
+ async readFileAsText(file) {
1916
+ return file.text();
1917
+ }
1918
+ async readFileAsArrayBuffer(file) {
1919
+ return file.arrayBuffer();
1920
+ }
1921
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FileSystemAccessService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1922
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FileSystemAccessService });
1923
+ }
1924
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FileSystemAccessService, decorators: [{
1925
+ type: Injectable
1926
+ }] });
1927
+
1928
+ class MediaRecorderService extends BrowserApiBaseService {
1929
+ recorder = null;
1930
+ chunks = [];
1931
+ startTime = 0;
1932
+ dataSubject = new Subject();
1933
+ stateSubject = new Subject();
1934
+ getApiName() {
1935
+ return 'media-recorder';
1936
+ }
1937
+ isSupported() {
1938
+ return this.isBrowserEnvironment() && 'MediaRecorder' in window;
1939
+ }
1940
+ get state() {
1941
+ return this.recorder?.state ?? 'inactive';
1942
+ }
1943
+ static isTypeSupported(mimeType) {
1944
+ return typeof MediaRecorder !== 'undefined' && MediaRecorder.isTypeSupported(mimeType);
1945
+ }
1946
+ watchState() {
1947
+ return this.stateSubject.asObservable();
1948
+ }
1949
+ watchData() {
1950
+ return this.dataSubject.asObservable();
1951
+ }
1952
+ async start(stream, options = {}) {
1953
+ if (!this.isSupported()) {
1954
+ throw new Error('MediaRecorder API not supported in this browser');
1955
+ }
1956
+ if (!window.isSecureContext) {
1957
+ throw new Error('MediaRecorder requires a secure context (HTTPS)');
1958
+ }
1959
+ this.stop();
1960
+ this.chunks = [];
1961
+ this.startTime = Date.now();
1962
+ const { timeslice, ...recorderOptions } = options;
1963
+ try {
1964
+ this.recorder = new MediaRecorder(stream, recorderOptions);
1965
+ this.recorder.ondataavailable = (event) => {
1966
+ if (event.data.size > 0) {
1967
+ this.chunks.push(event.data);
1968
+ this.dataSubject.next(event.data);
1969
+ }
1970
+ };
1971
+ this.recorder.onstart = () => this.stateSubject.next('recording');
1972
+ this.recorder.onpause = () => this.stateSubject.next('paused');
1973
+ this.recorder.onresume = () => this.stateSubject.next('recording');
1974
+ this.recorder.onstop = () => this.stateSubject.next('inactive');
1975
+ this.recorder.start(timeslice);
1976
+ }
1977
+ catch (error) {
1978
+ console.error('[MediaRecorderService] Failed to start recording:', error);
1979
+ throw this.createError('Failed to start recording', error);
1980
+ }
1981
+ }
1982
+ pause() {
1983
+ if (this.recorder?.state === 'recording') {
1984
+ this.recorder.pause();
1985
+ }
1986
+ }
1987
+ resume() {
1988
+ if (this.recorder?.state === 'paused') {
1989
+ this.recorder.resume();
1990
+ }
1991
+ }
1992
+ stop() {
1993
+ if (!this.recorder || this.recorder.state === 'inactive') {
1994
+ return null;
1995
+ }
1996
+ this.recorder.stop();
1997
+ const mimeType = this.recorder.mimeType;
1998
+ const duration = Date.now() - this.startTime;
1999
+ const blob = new Blob(this.chunks, { type: mimeType });
2000
+ const url = URL.createObjectURL(blob);
2001
+ this.recorder = null;
2002
+ this.chunks = [];
2003
+ return { blob, url, mimeType, duration };
2004
+ }
2005
+ getResult() {
2006
+ if (this.chunks.length === 0)
2007
+ return null;
2008
+ const mimeType = this.recorder?.mimeType ?? 'video/webm';
2009
+ const blob = new Blob(this.chunks, { type: mimeType });
2010
+ const url = URL.createObjectURL(blob);
2011
+ const duration = Date.now() - this.startTime;
2012
+ return { blob, url, mimeType, duration };
2013
+ }
2014
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MediaRecorderService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2015
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MediaRecorderService });
2016
+ }
2017
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MediaRecorderService, decorators: [{
2018
+ type: Injectable
2019
+ }] });
2020
+
2021
+ class ServerSentEventsService {
2022
+ destroyRef = inject(DestroyRef);
2023
+ platformId = inject(PLATFORM_ID);
2024
+ sources = new Map();
2025
+ isSupported() {
2026
+ return isPlatformBrowser(this.platformId) && 'EventSource' in window;
2027
+ }
2028
+ ensureSupport() {
2029
+ if (!this.isSupported()) {
2030
+ throw new Error('Server-Sent Events (EventSource) not supported in this environment');
2031
+ }
2032
+ }
2033
+ connect(url, config = {}) {
2034
+ this.ensureSupport();
2035
+ return new Observable((observer) => {
2036
+ const source = new EventSource(url, { withCredentials: config.withCredentials ?? false });
2037
+ this.sources.set(url, source);
2038
+ const messageHandler = (event) => {
2039
+ try {
2040
+ observer.next({
2041
+ data: JSON.parse(event.data),
2042
+ type: event.type,
2043
+ lastEventId: event.lastEventId,
2044
+ origin: event.origin,
2045
+ });
2046
+ }
2047
+ catch {
2048
+ observer.next({
2049
+ data: event.data,
2050
+ type: event.type,
2051
+ lastEventId: event.lastEventId,
2052
+ origin: event.origin,
2053
+ });
2054
+ }
2055
+ };
2056
+ const errorHandler = (event) => {
2057
+ if (source.readyState === EventSource.CLOSED) {
2058
+ observer.error(new Error('SSE connection closed unexpectedly'));
2059
+ }
2060
+ else {
2061
+ console.warn('[ServerSentEventsService] SSE connection error, reconnecting...', event);
2062
+ }
2063
+ };
2064
+ source.addEventListener('message', messageHandler);
2065
+ source.addEventListener('error', errorHandler);
2066
+ if (config.eventTypes) {
2067
+ for (const type of config.eventTypes) {
2068
+ source.addEventListener(type, messageHandler);
2069
+ }
2070
+ }
2071
+ const cleanup = () => {
2072
+ this.disconnect(url);
2073
+ };
2074
+ this.destroyRef.onDestroy(cleanup);
2075
+ return cleanup;
2076
+ });
2077
+ }
2078
+ disconnect(url) {
2079
+ const source = this.sources.get(url);
2080
+ if (source) {
2081
+ source.close();
2082
+ this.sources.delete(url);
2083
+ }
2084
+ }
2085
+ disconnectAll() {
2086
+ this.sources.forEach((source) => source.close());
2087
+ this.sources.clear();
2088
+ }
2089
+ getState(url) {
2090
+ const source = this.sources.get(url);
2091
+ if (!source)
2092
+ return 'closed';
2093
+ const states = ['connecting', 'open', 'closed'];
2094
+ return states[source.readyState] ?? 'closed';
2095
+ }
2096
+ getActiveConnections() {
2097
+ return Array.from(this.sources.keys());
2098
+ }
2099
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2100
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService });
2101
+ }
2102
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService, decorators: [{
2103
+ type: Injectable
2104
+ }] });
2105
+
2106
+ class VibrationService {
2107
+ platformId = inject(PLATFORM_ID);
2108
+ presets = {
2109
+ success: [50, 30, 50],
2110
+ error: [100, 50, 100, 50, 100],
2111
+ notification: [200],
2112
+ doubleTap: [50, 100, 50],
2113
+ };
2114
+ isSupported() {
2115
+ return isPlatformBrowser(this.platformId) && 'vibrate' in navigator;
2116
+ }
2117
+ vibrate(pattern = 200) {
2118
+ if (!this.isSupported())
2119
+ return false;
2120
+ return navigator.vibrate(pattern);
2121
+ }
2122
+ success() {
2123
+ return this.vibrate(this.presets.success);
2124
+ }
2125
+ error() {
2126
+ return this.vibrate(this.presets.error);
2127
+ }
2128
+ notification() {
2129
+ return this.vibrate(this.presets.notification);
2130
+ }
2131
+ doubleTap() {
2132
+ return this.vibrate(this.presets.doubleTap);
2133
+ }
2134
+ stop() {
2135
+ return this.vibrate(0);
2136
+ }
2137
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2138
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService });
2139
+ }
2140
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService, decorators: [{
2141
+ type: Injectable
2142
+ }] });
2143
+
2144
+ class SpeechSynthesisService {
2145
+ destroyRef = inject(DestroyRef);
2146
+ platformId = inject(PLATFORM_ID);
2147
+ isSupported() {
2148
+ return isPlatformBrowser(this.platformId) && 'speechSynthesis' in window;
2149
+ }
2150
+ ensureSupport() {
2151
+ if (!this.isSupported()) {
2152
+ throw new Error('Speech Synthesis API not supported in this browser');
2153
+ }
2154
+ }
2155
+ get state() {
2156
+ if (!this.isSupported())
2157
+ return 'idle';
2158
+ if (speechSynthesis.speaking && !speechSynthesis.paused)
2159
+ return 'speaking';
2160
+ if (speechSynthesis.paused)
2161
+ return 'paused';
2162
+ return 'idle';
2163
+ }
2164
+ get isPending() {
2165
+ return this.isSupported() && speechSynthesis.pending;
2166
+ }
2167
+ getVoices() {
2168
+ if (!this.isSupported())
2169
+ return [];
2170
+ return speechSynthesis.getVoices();
2171
+ }
2172
+ watchVoices() {
2173
+ return new Observable((observer) => {
2174
+ if (!this.isSupported()) {
2175
+ observer.next([]);
2176
+ observer.complete();
2177
+ return undefined;
2178
+ }
2179
+ const emit = () => observer.next(speechSynthesis.getVoices());
2180
+ speechSynthesis.addEventListener('voiceschanged', emit);
2181
+ emit();
2182
+ const cleanup = () => speechSynthesis.removeEventListener('voiceschanged', emit);
2183
+ this.destroyRef.onDestroy(cleanup);
2184
+ return cleanup;
2185
+ });
2186
+ }
2187
+ speak(text, options = {}) {
2188
+ return new Observable((observer) => {
2189
+ this.ensureSupport();
2190
+ const utterance = new SpeechSynthesisUtterance(text);
2191
+ if (options.lang)
2192
+ utterance.lang = options.lang;
2193
+ if (options.voice)
2194
+ utterance.voice = options.voice;
2195
+ if (options.volume !== undefined)
2196
+ utterance.volume = options.volume;
2197
+ if (options.rate !== undefined)
2198
+ utterance.rate = options.rate;
2199
+ if (options.pitch !== undefined)
2200
+ utterance.pitch = options.pitch;
2201
+ utterance.onstart = () => observer.next('speaking');
2202
+ utterance.onpause = () => observer.next('paused');
2203
+ utterance.onresume = () => observer.next('speaking');
2204
+ utterance.onend = () => {
2205
+ observer.next('idle');
2206
+ observer.complete();
2207
+ };
2208
+ utterance.onerror = (event) => {
2209
+ observer.error(new Error(`Speech synthesis error: ${event.error}`));
2210
+ };
2211
+ observer.next('speaking');
2212
+ speechSynthesis.speak(utterance);
2213
+ const cleanup = () => {
2214
+ speechSynthesis.cancel();
2215
+ observer.next('idle');
2216
+ };
2217
+ this.destroyRef.onDestroy(cleanup);
2218
+ return cleanup;
2219
+ });
2220
+ }
2221
+ pause() {
2222
+ if (this.isSupported())
2223
+ speechSynthesis.pause();
2224
+ }
2225
+ resume() {
2226
+ if (this.isSupported())
2227
+ speechSynthesis.resume();
2228
+ }
2229
+ cancel() {
2230
+ if (this.isSupported())
2231
+ speechSynthesis.cancel();
2232
+ }
2233
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2234
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService });
2235
+ }
2236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService, decorators: [{
2237
+ type: Injectable
2238
+ }] });
2239
+
2240
+ // Common types for browser APIs
2241
+
2242
+ function injectPageVisibility() {
2243
+ const destroyRef = inject(DestroyRef);
2244
+ const platformId = inject(PLATFORM_ID);
2245
+ const initial = isPlatformBrowser(platformId) && typeof document !== 'undefined'
2246
+ ? document.visibilityState
2247
+ : 'visible';
2248
+ const state = signal(initial, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
2249
+ const sub = pageVisibilityStream().subscribe((s) => state.set(s));
2250
+ destroyRef.onDestroy(() => sub.unsubscribe());
2251
+ return {
2252
+ state: state.asReadonly(),
2253
+ isVisible: computed(() => state() === 'visible'),
2254
+ isHidden: computed(() => state() !== 'visible'),
2255
+ };
2256
+ }
2257
+
2258
+ function injectResizeObserver(elementOrRef, options) {
2259
+ const destroyRef = inject(DestroyRef);
2260
+ const platformId = inject(PLATFORM_ID);
2261
+ const size = signal(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2262
+ if (isPlatformBrowser(platformId) && isResizeObserverSupported()) {
2263
+ const el = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef;
2264
+ const sub = resizeObserverStream(el, options).subscribe((s) => size.set(s));
2265
+ destroyRef.onDestroy(() => sub.unsubscribe());
2266
+ }
2267
+ return {
2268
+ size: size.asReadonly(),
2269
+ width: computed(() => size()?.width ?? 0),
2270
+ height: computed(() => size()?.height ?? 0),
2271
+ inlineSize: computed(() => size()?.inlineSize ?? 0),
2272
+ blockSize: computed(() => size()?.blockSize ?? 0),
2273
+ };
2274
+ }
2275
+
2276
+ function injectIntersectionObserver(elementOrRef, options) {
2277
+ const destroyRef = inject(DestroyRef);
2278
+ const platformId = inject(PLATFORM_ID);
2279
+ const isIntersecting = signal(false, ...(ngDevMode ? [{ debugName: "isIntersecting" }] : /* istanbul ignore next */ []));
2280
+ if (isPlatformBrowser(platformId) && isIntersectionObserverSupported()) {
2281
+ const el = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef;
2282
+ const sub = intersectionObserverStream(el, options).subscribe((v) => isIntersecting.set(v));
2283
+ destroyRef.onDestroy(() => sub.unsubscribe());
2284
+ }
2285
+ return {
2286
+ isIntersecting: isIntersecting.asReadonly(),
2287
+ isVisible: computed(() => isIntersecting()),
2288
+ };
2289
+ }
2290
+
2291
+ function injectNetworkInformation() {
2292
+ const destroyRef = inject(DestroyRef);
2293
+ const platformId = inject(PLATFORM_ID);
2294
+ const snapshot = signal(isPlatformBrowser(platformId) ? getNetworkSnapshot() : { online: true }, ...(ngDevMode ? [{ debugName: "snapshot" }] : /* istanbul ignore next */ []));
2295
+ const sub = networkInformationStream().subscribe((n) => snapshot.set(n));
2296
+ destroyRef.onDestroy(() => sub.unsubscribe());
2297
+ return {
2298
+ snapshot: snapshot.asReadonly(),
2299
+ online: computed(() => snapshot().online),
2300
+ effectiveType: computed(() => snapshot().effectiveType),
2301
+ downlink: computed(() => snapshot().downlink),
2302
+ rtt: computed(() => snapshot().rtt),
2303
+ type: computed(() => snapshot().type),
2304
+ saveData: computed(() => snapshot().saveData),
2305
+ };
2306
+ }
2307
+
2308
+ function injectScreenOrientation() {
2309
+ const destroyRef = inject(DestroyRef);
2310
+ const platformId = inject(PLATFORM_ID);
2311
+ const orientation = signal(isPlatformBrowser(platformId)
2312
+ ? getOrientationSnapshot()
2313
+ : { type: 'portrait-primary', angle: 0 }, ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
2314
+ const sub = screenOrientationStream().subscribe((o) => orientation.set(o));
2315
+ destroyRef.onDestroy(() => sub.unsubscribe());
2316
+ return {
2317
+ orientation: orientation.asReadonly(),
2318
+ type: computed(() => orientation().type),
2319
+ angle: computed(() => orientation().angle),
2320
+ isPortrait: computed(() => orientation().type.startsWith('portrait')),
2321
+ isLandscape: computed(() => orientation().type.startsWith('landscape')),
2322
+ async lock(o) {
2323
+ await screen.orientation.lock(o);
2324
+ },
2325
+ unlock() {
2326
+ screen.orientation.unlock();
2327
+ },
2328
+ };
2329
+ }
2330
+
2331
+ class BrowserSupportUtil {
2332
+ static isSupported(feature) {
2333
+ if (typeof window === 'undefined' || typeof navigator === 'undefined') {
2334
+ return false;
2335
+ }
2336
+ switch (feature) {
2337
+ case 'permissions':
2338
+ return 'permissions' in navigator;
2339
+ case 'camera':
2340
+ return 'mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices;
2341
+ case 'microphone':
2342
+ return 'mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices;
2343
+ case 'geolocation':
2344
+ return 'geolocation' in navigator;
2345
+ case 'notifications':
2346
+ return 'Notification' in window;
2347
+ case 'clipboard':
2348
+ return 'clipboard' in navigator;
2349
+ case 'clipboard-read':
2350
+ return 'clipboard' in navigator && 'readText' in navigator.clipboard;
2351
+ case 'clipboard-write':
2352
+ return 'clipboard' in navigator && 'writeText' in navigator.clipboard;
2353
+ case 'persistent-storage':
2354
+ return 'storage' in navigator && 'persist' in navigator.storage;
2355
+ default:
2356
+ return false;
2357
+ }
2358
+ }
2359
+ static getUnsupportedFeatures() {
2360
+ const features = [
2361
+ 'permissions',
2362
+ 'camera',
2363
+ 'microphone',
2364
+ 'geolocation',
2365
+ 'notifications',
2366
+ 'clipboard',
2367
+ 'clipboard-read',
2368
+ 'clipboard-write',
2369
+ 'persistent-storage',
2370
+ ];
2371
+ return features.filter((feature) => !this.isSupported(feature));
2372
+ }
2373
+ static isSecureContext() {
2374
+ return typeof window !== 'undefined' ? window.isSecureContext : false;
2375
+ }
2376
+ static getUserAgent() {
2377
+ return typeof navigator !== 'undefined' ? navigator.userAgent : '';
2378
+ }
2379
+ static isMobile() {
2380
+ const userAgent = this.getUserAgent().toLowerCase();
2381
+ return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
2382
+ }
2383
+ static isDesktop() {
2384
+ return !this.isMobile();
2385
+ }
2386
+ static getBrowserInfo() {
2387
+ const userAgent = this.getUserAgent();
2388
+ return {
2389
+ name: this.getBrowserName(userAgent),
2390
+ version: this.getBrowserVersion(userAgent),
2391
+ isChrome: /chrome/.test(userAgent) && !/edge/.test(userAgent),
2392
+ isFirefox: /firefox/.test(userAgent),
2393
+ isSafari: /safari/.test(userAgent) && !/chrome/.test(userAgent),
2394
+ isEdge: /edge/.test(userAgent) || /edg/.test(userAgent),
2395
+ };
2396
+ }
2397
+ static getBrowserName(userAgent) {
2398
+ if (/chrome/.test(userAgent) && !/edge/.test(userAgent))
2399
+ return 'Chrome';
2400
+ if (/firefox/.test(userAgent))
2401
+ return 'Firefox';
2402
+ if (/safari/.test(userAgent) && !/chrome/.test(userAgent))
2403
+ return 'Safari';
2404
+ if (/edge/.test(userAgent) || /edg/.test(userAgent))
2405
+ return 'Edge';
2406
+ return 'Unknown';
2407
+ }
2408
+ static getBrowserVersion(userAgent) {
2409
+ const match = userAgent.match(/(chrome|firefox|safari|edge|edg)\/(\d+)/i);
2410
+ return match ? match[2] : 'Unknown';
2411
+ }
2412
+ }
2413
+
2414
+ /**
2415
+ * Functional guard that checks if the user has the required permission.
2416
+ * Usage in routes: { canActivate: [permissionGuard('camera')] }
2417
+ */
2418
+ const permissionGuard = (permission) => {
2419
+ return async (_route) => {
2420
+ const permissionsService = inject(PermissionsService);
2421
+ const router = inject(Router);
2422
+ if (!permission) {
2423
+ return true;
2424
+ }
2425
+ try {
2426
+ const status = await permissionsService.query({ name: permission });
2427
+ if (status.state !== 'granted') {
2428
+ router.navigate(['/permission-denied'], {
2429
+ queryParams: { permission },
2430
+ });
2431
+ return false;
2432
+ }
2433
+ return true;
2434
+ }
2435
+ catch (error) {
2436
+ console.error('Permission guard error:', error);
2437
+ router.navigate(['/permission-denied'], {
2438
+ queryParams: { permission },
2439
+ });
2440
+ return false;
2441
+ }
2442
+ };
2443
+ };
2444
+
2445
+ const defaultBrowserWebApisConfig = {
2446
+ enableCamera: true,
2447
+ enableGeolocation: true,
2448
+ enableNotifications: true,
2449
+ enableClipboard: true,
2450
+ enableBattery: false,
2451
+ enableMediaDevices: true,
2452
+ enableWebShare: false,
2453
+ enableWebStorage: false,
2454
+ enableWebSocket: false,
2455
+ enableWebWorker: false,
2456
+ enableIntersectionObserver: false,
2457
+ enableResizeObserver: false,
2458
+ enablePageVisibility: false,
2459
+ enableBroadcastChannel: false,
2460
+ enableNetworkInformation: false,
2461
+ enableScreenWakeLock: false,
2462
+ enableScreenOrientation: false,
2463
+ enableFullscreen: false,
2464
+ enableFileSystemAccess: false,
2465
+ enableMediaRecorder: false,
2466
+ enableServerSentEvents: false,
2467
+ enableVibration: false,
2468
+ enableSpeechSynthesis: false,
2469
+ };
2470
+ function provideBrowserWebApis(config = {}) {
2471
+ const mergedConfig = { ...defaultBrowserWebApisConfig, ...config };
2472
+ const providers = [PermissionsService];
2473
+ const conditionalProviders = [
2474
+ [mergedConfig.enableCamera, CameraService],
2475
+ [mergedConfig.enableGeolocation, GeolocationService],
2476
+ [mergedConfig.enableNotifications, NotificationService],
2477
+ [mergedConfig.enableClipboard, ClipboardService],
2478
+ [mergedConfig.enableMediaDevices, MediaDevicesService],
2479
+ [mergedConfig.enableBattery, BatteryService],
2480
+ [mergedConfig.enableWebShare, WebShareService],
2481
+ [mergedConfig.enableWebStorage, WebStorageService],
2482
+ [mergedConfig.enableWebSocket, WebSocketService],
2483
+ [mergedConfig.enableWebWorker, WebWorkerService],
2484
+ [mergedConfig.enableIntersectionObserver, IntersectionObserverService],
2485
+ [mergedConfig.enableResizeObserver, ResizeObserverService],
2486
+ [mergedConfig.enablePageVisibility, PageVisibilityService],
2487
+ [mergedConfig.enableBroadcastChannel, BroadcastChannelService],
2488
+ [mergedConfig.enableNetworkInformation, NetworkInformationService],
2489
+ [mergedConfig.enableScreenWakeLock, ScreenWakeLockService],
2490
+ [mergedConfig.enableScreenOrientation, ScreenOrientationService],
2491
+ [mergedConfig.enableFullscreen, FullscreenService],
2492
+ [mergedConfig.enableFileSystemAccess, FileSystemAccessService],
2493
+ [mergedConfig.enableMediaRecorder, MediaRecorderService],
2494
+ [mergedConfig.enableServerSentEvents, ServerSentEventsService],
2495
+ [mergedConfig.enableVibration, VibrationService],
2496
+ [mergedConfig.enableSpeechSynthesis, SpeechSynthesisService],
2497
+ ];
2498
+ for (const [enabled, provider] of conditionalProviders) {
2499
+ if (enabled) {
2500
+ providers.push(provider);
2501
+ }
2502
+ }
2503
+ return makeEnvironmentProviders(providers);
2504
+ }
2505
+ // Feature-specific providers for tree-shaking
2506
+ function provideCamera() {
2507
+ return makeEnvironmentProviders([PermissionsService, CameraService]);
2508
+ }
2509
+ function provideGeolocation() {
2510
+ return makeEnvironmentProviders([PermissionsService, GeolocationService]);
2511
+ }
2512
+ function provideNotifications() {
2513
+ return makeEnvironmentProviders([PermissionsService, NotificationService]);
2514
+ }
2515
+ function provideClipboard() {
2516
+ return makeEnvironmentProviders([PermissionsService, ClipboardService]);
2517
+ }
2518
+ function provideMediaDevices() {
2519
+ return makeEnvironmentProviders([PermissionsService, MediaDevicesService]);
2520
+ }
2521
+ function provideBattery() {
2522
+ return makeEnvironmentProviders([BatteryService]);
2523
+ }
2524
+ function provideWebShare() {
2525
+ return makeEnvironmentProviders([WebShareService]);
2526
+ }
2527
+ function provideWebStorage() {
2528
+ return makeEnvironmentProviders([WebStorageService]);
2529
+ }
2530
+ function provideWebSocket() {
2531
+ return makeEnvironmentProviders([WebSocketService]);
2532
+ }
2533
+ function provideWebWorker() {
2534
+ return makeEnvironmentProviders([WebWorkerService]);
2535
+ }
2536
+ function providePermissions() {
2537
+ return makeEnvironmentProviders([PermissionsService]);
2538
+ }
2539
+ // Combined providers for common use cases
2540
+ function provideMediaApis() {
2541
+ return makeEnvironmentProviders([PermissionsService, CameraService, MediaDevicesService]);
2542
+ }
2543
+ function provideLocationApis() {
2544
+ return makeEnvironmentProviders([PermissionsService, GeolocationService]);
2545
+ }
2546
+ function provideStorageApis() {
2547
+ return makeEnvironmentProviders([PermissionsService, ClipboardService, WebStorageService]);
2548
+ }
2549
+ function provideCommunicationApis() {
2550
+ return makeEnvironmentProviders([
2551
+ PermissionsService,
2552
+ NotificationService,
2553
+ WebShareService,
2554
+ WebSocketService,
2555
+ ]);
2556
+ }
2557
+ function provideIntersectionObserver() {
2558
+ return makeEnvironmentProviders([IntersectionObserverService]);
2559
+ }
2560
+ function provideResizeObserver() {
2561
+ return makeEnvironmentProviders([ResizeObserverService]);
2562
+ }
2563
+ function providePageVisibility() {
2564
+ return makeEnvironmentProviders([PageVisibilityService]);
2565
+ }
2566
+ function provideBroadcastChannel() {
2567
+ return makeEnvironmentProviders([BroadcastChannelService]);
2568
+ }
2569
+ function provideNetworkInformation() {
2570
+ return makeEnvironmentProviders([NetworkInformationService]);
2571
+ }
2572
+ function provideScreenWakeLock() {
2573
+ return makeEnvironmentProviders([PermissionsService, ScreenWakeLockService]);
2574
+ }
2575
+ function provideScreenOrientation() {
2576
+ return makeEnvironmentProviders([ScreenOrientationService]);
2577
+ }
2578
+ function provideFullscreen() {
2579
+ return makeEnvironmentProviders([FullscreenService]);
2580
+ }
2581
+ function provideFileSystemAccess() {
2582
+ return makeEnvironmentProviders([PermissionsService, FileSystemAccessService]);
2583
+ }
2584
+ function provideMediaRecorder() {
2585
+ return makeEnvironmentProviders([PermissionsService, MediaRecorderService]);
2586
+ }
2587
+ function provideServerSentEvents() {
2588
+ return makeEnvironmentProviders([ServerSentEventsService]);
2589
+ }
2590
+ function provideVibration() {
2591
+ return makeEnvironmentProviders([VibrationService]);
2592
+ }
2593
+ function provideSpeechSynthesis() {
2594
+ return makeEnvironmentProviders([SpeechSynthesisService]);
657
2595
  }
658
2596
 
659
2597
  // Browser Web APIs Services
@@ -664,5 +2602,4 @@ const version = '0.1.0';
664
2602
  * Generated bundle index. Do not edit.
665
2603
  */
666
2604
 
667
- export { BrowserCapabilityService, BrowserSupportUtil, CameraService, ClipboardService, GeolocationService, MediaDevicesService, NotificationService, PermissionGuard, PermissionsService, createPermissionGuard, version };
668
- //# sourceMappingURL=angular-helpers-browser-web-apis.mjs.map
2605
+ export { BatteryService, BroadcastChannelService, BrowserApiBaseService, BrowserCapabilityService, BrowserSupportUtil, CameraService, ClipboardService, FileSystemAccessService, FullscreenService, GeolocationService, IntersectionObserverService, MediaDevicesService, MediaRecorderService, NetworkInformationService, NotificationService, PageVisibilityService, PermissionsService, ResizeObserverService, ScreenOrientationService, ScreenWakeLockService, ServerSentEventsService, SpeechSynthesisService, VibrationService, WebShareService, WebSocketService, WebStorageService, WebWorkerService, permissionGuard as createPermissionGuard, defaultBrowserWebApisConfig, injectIntersectionObserver, injectNetworkInformation, injectPageVisibility, injectResizeObserver, injectScreenOrientation, permissionGuard, provideBattery, provideBroadcastChannel, provideBrowserWebApis, provideCamera, provideClipboard, provideCommunicationApis, provideFileSystemAccess, provideFullscreen, provideGeolocation, provideIntersectionObserver, provideLocationApis, provideMediaApis, provideMediaDevices, provideMediaRecorder, provideNetworkInformation, provideNotifications, providePageVisibility, providePermissions, provideResizeObserver, provideScreenOrientation, provideScreenWakeLock, provideServerSentEvents, provideSpeechSynthesis, provideStorageApis, provideVibration, provideWebShare, provideWebSocket, provideWebStorage, provideWebWorker, version };