@angular-helpers/browser-web-apis 21.2.0 → 21.4.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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, inject, DestroyRef, PLATFORM_ID, signal, computed, ElementRef, makeEnvironmentProviders } from '@angular/core';
2
+ import { Injectable, inject, DestroyRef, PLATFORM_ID, signal, computed, isSignal, effect, ElementRef, makeEnvironmentProviders } from '@angular/core';
3
3
  import { isPlatformBrowser, isPlatformServer } from '@angular/common';
4
4
  import { Observable, fromEvent, Subject, of, map as map$1 } from 'rxjs';
5
5
  import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
@@ -30,48 +30,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
30
30
  }] });
31
31
 
32
32
  /**
33
- * Base class for all Browser Web API services
33
+ * Base class for all Browser Web API services.
34
34
  * Provides common functionality for:
35
- * - Support checking
36
- * - Permission management
37
- * - Error handling
35
+ * - Platform detection (browser vs server)
36
+ * - Support assertion via Template Method
37
+ * - Error creation with cause chaining
38
+ * - Structured logging
38
39
  * - Lifecycle management with destroyRef
39
- * - Logging
40
+ *
41
+ * Services that also need permission querying should extend
42
+ * `PermissionAwareBrowserApiBaseService` instead.
40
43
  */
41
44
  class BrowserApiBaseService {
42
- permissionsService = inject(PermissionsService);
43
45
  destroyRef = inject(DestroyRef);
44
46
  platformId = inject(PLATFORM_ID);
45
47
  /**
46
- * Check if running in browser environment using Angular's platform detection
48
+ * Check if running in browser environment using Angular's platform detection.
47
49
  */
48
50
  isBrowserEnvironment() {
49
51
  return isPlatformBrowser(this.platformId);
50
52
  }
51
53
  /**
52
- * Check if running in server environment using Angular's platform detection
54
+ * Check if running in server environment using Angular's platform detection.
53
55
  */
54
56
  isServerEnvironment() {
55
57
  return isPlatformServer(this.platformId);
56
58
  }
57
59
  /**
58
- * Request a permission
60
+ * Template Method: throws a standard error when the API is not available.
61
+ * Uses getApiName() to produce consistent error messages.
59
62
  */
60
- async requestPermission(permission) {
61
- if (this.isServerEnvironment()) {
63
+ ensureSupported() {
64
+ if (!this.isBrowserEnvironment()) {
62
65
  throw new Error(`${this.getApiName()} API not available in server environment`);
63
66
  }
64
- try {
65
- const status = await this.permissionsService.query({ name: permission });
66
- return status.state === 'granted';
67
- }
68
- catch (error) {
69
- console.error(`[${this.getApiName()}] Error requesting permission for ${permission}:`, error);
70
- return false;
71
- }
72
67
  }
73
68
  /**
74
- * Create an error with proper cause chaining
69
+ * Create an error with proper cause chaining.
75
70
  */
76
71
  createError(message, cause) {
77
72
  const error = new Error(message);
@@ -80,6 +75,18 @@ class BrowserApiBaseService {
80
75
  }
81
76
  return error;
82
77
  }
78
+ /**
79
+ * Log an error with the service name as prefix.
80
+ */
81
+ logError(message, error) {
82
+ console.error(`[${this.getApiName()}] ${message}`, error);
83
+ }
84
+ /**
85
+ * Log an informational message with the service name as prefix.
86
+ */
87
+ logInfo(message) {
88
+ console.info(`[${this.getApiName()}] ${message}`);
89
+ }
83
90
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BrowserApiBaseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
84
91
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BrowserApiBaseService });
85
92
  }
@@ -87,7 +94,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
87
94
  type: Injectable
88
95
  }] });
89
96
 
90
- class CameraService extends BrowserApiBaseService {
97
+ /**
98
+ * Extension of `BrowserApiBaseService` for services that need to query
99
+ * browser permissions via the Permissions API.
100
+ *
101
+ * Only services that actively call `requestPermission()` should extend this
102
+ * class. All other services should extend `BrowserApiBaseService` directly
103
+ * to avoid carrying an unnecessary PermissionsService dependency.
104
+ */
105
+ class PermissionAwareBrowserApiBaseService extends BrowserApiBaseService {
106
+ permissionsService = inject(PermissionsService);
107
+ /**
108
+ * Query the Permissions API for the given permission name.
109
+ * Returns `true` if the permission state is `'granted'`.
110
+ */
111
+ async requestPermission(permission) {
112
+ if (this.isServerEnvironment()) {
113
+ throw new Error(`${this.getApiName()} API not available in server environment`);
114
+ }
115
+ try {
116
+ const status = await this.permissionsService.query({ name: permission });
117
+ return status.state === 'granted';
118
+ }
119
+ catch (error) {
120
+ this.logError(`Error requesting permission for ${permission}:`, error);
121
+ return false;
122
+ }
123
+ }
124
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionAwareBrowserApiBaseService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
125
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionAwareBrowserApiBaseService });
126
+ }
127
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionAwareBrowserApiBaseService, decorators: [{
128
+ type: Injectable
129
+ }] });
130
+
131
+ class CameraService extends PermissionAwareBrowserApiBaseService {
91
132
  currentStream = null;
92
133
  getApiName() {
93
134
  return 'camera';
@@ -218,7 +259,7 @@ class GeolocationService extends BrowserApiBaseService {
218
259
  this.ensureGeolocationSupport();
219
260
  return new Promise((resolve, reject) => {
220
261
  navigator.geolocation.getCurrentPosition((position) => resolve(position), (error) => {
221
- console.error('[GeolocationService] Error getting position:', error);
262
+ this.logError('Error getting position:', error);
222
263
  reject(error);
223
264
  }, options);
224
265
  });
@@ -227,7 +268,7 @@ class GeolocationService extends BrowserApiBaseService {
227
268
  this.ensureGeolocationSupport();
228
269
  return new Observable((observer) => {
229
270
  const watchId = navigator.geolocation.watchPosition((position) => observer.next(position), (error) => {
230
- console.error('[GeolocationService] Error watching position:', error);
271
+ this.logError('Error watching position:', error);
231
272
  observer.error(error);
232
273
  }, options);
233
274
  return () => {
@@ -385,7 +426,7 @@ class NotificationService extends BrowserApiBaseService {
385
426
  return Notification.permission;
386
427
  }
387
428
  isSupported() {
388
- return 'Notification' in window;
429
+ return this.isBrowserEnvironment() && 'Notification' in window;
389
430
  }
390
431
  async requestNotificationPermission() {
391
432
  if (!this.isSupported()) {
@@ -404,7 +445,7 @@ class NotificationService extends BrowserApiBaseService {
404
445
  return new Notification(title, options);
405
446
  }
406
447
  catch (error) {
407
- console.error('[NotificationService] Error showing notification:', error);
448
+ this.logError('Error showing notification:', error);
408
449
  throw error;
409
450
  }
410
451
  }
@@ -415,7 +456,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
415
456
  type: Injectable
416
457
  }] });
417
458
 
418
- class ClipboardService extends BrowserApiBaseService {
459
+ class ClipboardService extends PermissionAwareBrowserApiBaseService {
419
460
  getApiName() {
420
461
  return 'clipboard';
421
462
  }
@@ -450,16 +491,6 @@ class ClipboardService extends BrowserApiBaseService {
450
491
  throw error;
451
492
  }
452
493
  }
453
- async writeTextSecure(text) {
454
- await this.ensureClipboardPermission('write');
455
- try {
456
- await navigator.clipboard.writeText(text);
457
- }
458
- catch (error) {
459
- console.error('[ClipboardService] Error writing to clipboard:', error);
460
- throw error;
461
- }
462
- }
463
494
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ClipboardService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
464
495
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ClipboardService });
465
496
  }
@@ -493,6 +524,18 @@ const BROWSER_CAPABILITIES = [
493
524
  { id: 'serverSentEvents', label: 'Server-Sent Events', requiresSecureContext: false },
494
525
  { id: 'vibration', label: 'Vibration API', requiresSecureContext: false },
495
526
  { id: 'speechSynthesis', label: 'Speech Synthesis API', requiresSecureContext: false },
527
+ { id: 'mutationObserver', label: 'Mutation Observer', requiresSecureContext: false },
528
+ { id: 'performanceObserver', label: 'Performance Observer', requiresSecureContext: false },
529
+ { id: 'idleDetector', label: 'Idle Detection API', requiresSecureContext: true },
530
+ { id: 'eyeDropper', label: 'EyeDropper API', requiresSecureContext: true },
531
+ { id: 'barcodeDetector', label: 'Barcode Detection API', requiresSecureContext: true },
532
+ { id: 'webAudio', label: 'Web Audio API', requiresSecureContext: false },
533
+ { id: 'gamepad', label: 'Gamepad API', requiresSecureContext: true },
534
+ { id: 'webBluetooth', label: 'Web Bluetooth API', requiresSecureContext: true },
535
+ { id: 'webUsb', label: 'WebUSB API', requiresSecureContext: true },
536
+ { id: 'webNfc', label: 'Web NFC API', requiresSecureContext: true },
537
+ { id: 'paymentRequest', label: 'Payment Request API', requiresSecureContext: true },
538
+ { id: 'credentialManagement', label: 'Credential Management API', requiresSecureContext: true },
496
539
  ];
497
540
  class BrowserCapabilityService {
498
541
  getCapabilities() {
@@ -553,6 +596,30 @@ class BrowserCapabilityService {
553
596
  return typeof navigator !== 'undefined' && 'vibrate' in navigator;
554
597
  case 'speechSynthesis':
555
598
  return typeof window !== 'undefined' && 'speechSynthesis' in window;
599
+ case 'mutationObserver':
600
+ return typeof MutationObserver !== 'undefined';
601
+ case 'performanceObserver':
602
+ return typeof PerformanceObserver !== 'undefined';
603
+ case 'idleDetector':
604
+ return typeof window !== 'undefined' && 'IdleDetector' in window;
605
+ case 'eyeDropper':
606
+ return typeof window !== 'undefined' && 'EyeDropper' in window;
607
+ case 'barcodeDetector':
608
+ return typeof window !== 'undefined' && 'BarcodeDetector' in window;
609
+ case 'webAudio':
610
+ return typeof window !== 'undefined' && 'AudioContext' in window;
611
+ case 'gamepad':
612
+ return typeof navigator !== 'undefined' && 'getGamepads' in navigator;
613
+ case 'webBluetooth':
614
+ return typeof navigator !== 'undefined' && 'bluetooth' in navigator;
615
+ case 'webUsb':
616
+ return typeof navigator !== 'undefined' && 'usb' in navigator;
617
+ case 'webNfc':
618
+ return typeof window !== 'undefined' && 'NDEFReader' in window;
619
+ case 'paymentRequest':
620
+ return typeof window !== 'undefined' && 'PaymentRequest' in window;
621
+ case 'credentialManagement':
622
+ return typeof navigator !== 'undefined' && 'credentials' in navigator;
556
623
  default:
557
624
  return false;
558
625
  }
@@ -604,7 +671,6 @@ class BatteryService extends BrowserApiBaseService {
604
671
  const nav = navigator;
605
672
  this.batteryManager = await nav.getBattery();
606
673
  const batteryInfo = this.getBatteryInfo();
607
- this.setupEventListeners();
608
674
  return batteryInfo;
609
675
  }
610
676
  catch (error) {
@@ -647,16 +713,6 @@ class BatteryService extends BrowserApiBaseService {
647
713
  };
648
714
  });
649
715
  }
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
- });
659
- }
660
716
  // Direct access to native battery API
661
717
  getNativeBatteryManager() {
662
718
  if (!this.batteryManager) {
@@ -1358,10 +1414,12 @@ function intersectionObserverEntriesStream(element, options = {}) {
1358
1414
  });
1359
1415
  }
1360
1416
 
1361
- class IntersectionObserverService {
1362
- platformId = inject(PLATFORM_ID);
1417
+ class IntersectionObserverService extends BrowserApiBaseService {
1418
+ getApiName() {
1419
+ return 'intersection-observer';
1420
+ }
1363
1421
  isSupported() {
1364
- return isPlatformBrowser(this.platformId) && 'IntersectionObserver' in window;
1422
+ return this.isBrowserEnvironment() && 'IntersectionObserver' in window;
1365
1423
  }
1366
1424
  observe(element, options = {}) {
1367
1425
  if (!this.isSupported()) {
@@ -1375,7 +1433,7 @@ class IntersectionObserverService {
1375
1433
  }
1376
1434
  return intersectionObserverStream(element, options);
1377
1435
  }
1378
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1436
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1379
1437
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService });
1380
1438
  }
1381
1439
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService, decorators: [{
@@ -1416,10 +1474,12 @@ function resizeObserverEntriesStream(element, options = {}) {
1416
1474
  });
1417
1475
  }
1418
1476
 
1419
- class ResizeObserverService {
1420
- platformId = inject(PLATFORM_ID);
1477
+ class ResizeObserverService extends BrowserApiBaseService {
1478
+ getApiName() {
1479
+ return 'resize-observer';
1480
+ }
1421
1481
  isSupported() {
1422
- return isPlatformBrowser(this.platformId) && 'ResizeObserver' in window;
1482
+ return this.isBrowserEnvironment() && 'ResizeObserver' in window;
1423
1483
  }
1424
1484
  observe(element, options = {}) {
1425
1485
  if (!this.isSupported()) {
@@ -1433,7 +1493,7 @@ class ResizeObserverService {
1433
1493
  }
1434
1494
  return resizeObserverStream(element, options);
1435
1495
  }
1436
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1496
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1437
1497
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService });
1438
1498
  }
1439
1499
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, decorators: [{
@@ -1455,10 +1515,12 @@ function pageVisibilityStream() {
1455
1515
  });
1456
1516
  }
1457
1517
 
1458
- class PageVisibilityService {
1459
- platformId = inject(PLATFORM_ID);
1518
+ class PageVisibilityService extends BrowserApiBaseService {
1519
+ getApiName() {
1520
+ return 'page-visibility';
1521
+ }
1460
1522
  isSupported() {
1461
- return isPlatformBrowser(this.platformId) && 'hidden' in document;
1523
+ return this.isBrowserEnvironment() && 'hidden' in document;
1462
1524
  }
1463
1525
  get isHidden() {
1464
1526
  if (!this.isSupported())
@@ -1476,32 +1538,79 @@ class PageVisibilityService {
1476
1538
  watchVisibility() {
1477
1539
  return pageVisibilityStream().pipe(map$1((s) => s === 'visible'));
1478
1540
  }
1479
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1541
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1480
1542
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService });
1481
1543
  }
1482
1544
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService, decorators: [{
1483
1545
  type: Injectable
1484
1546
  }] });
1485
1547
 
1486
- class BroadcastChannelService {
1487
- destroyRef = inject(DestroyRef);
1488
- platformId = inject(PLATFORM_ID);
1489
- channels = new Map();
1548
+ /**
1549
+ * Base class for services that manage a named registry of persistent
1550
+ * connections (e.g. BroadcastChannel, EventSource, WebSocket pools).
1551
+ *
1552
+ * Implements the Template Method pattern: subclasses define how a single
1553
+ * native connection is closed via `closeNativeConnection()`, while this
1554
+ * class handles the Map lifecycle (remove, closeAll, getActiveKeys).
1555
+ *
1556
+ * Public-facing method names (`close`, `disconnect`, `getOpenChannels`, etc.)
1557
+ * remain the responsibility of the concrete service to preserve the public API.
1558
+ */
1559
+ class ConnectionRegistryBaseService extends BrowserApiBaseService {
1560
+ connections = new Map();
1561
+ /**
1562
+ * Remove and close the connection registered under `key`.
1563
+ * Safe to call even if the key does not exist.
1564
+ */
1565
+ removeConnection(key) {
1566
+ const connection = this.connections.get(key);
1567
+ if (connection) {
1568
+ this.closeNativeConnection(connection);
1569
+ this.connections.delete(key);
1570
+ }
1571
+ }
1572
+ /**
1573
+ * Close all registered connections and clear the registry.
1574
+ */
1575
+ closeAllConnections() {
1576
+ this.connections.forEach((connection) => this.closeNativeConnection(connection));
1577
+ this.connections.clear();
1578
+ }
1579
+ /**
1580
+ * Return the keys of all currently open connections.
1581
+ */
1582
+ getConnectionKeys() {
1583
+ return Array.from(this.connections.keys());
1584
+ }
1585
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ConnectionRegistryBaseService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1586
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ConnectionRegistryBaseService });
1587
+ }
1588
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ConnectionRegistryBaseService, decorators: [{
1589
+ type: Injectable
1590
+ }] });
1591
+
1592
+ class BroadcastChannelService extends ConnectionRegistryBaseService {
1593
+ getApiName() {
1594
+ return 'broadcast-channel';
1595
+ }
1596
+ closeNativeConnection(channel) {
1597
+ channel.close();
1598
+ }
1490
1599
  isSupported() {
1491
- return isPlatformBrowser(this.platformId) && 'BroadcastChannel' in window;
1600
+ return this.isBrowserEnvironment() && 'BroadcastChannel' in window;
1492
1601
  }
1493
- ensureSupport() {
1602
+ ensureBroadcastChannelSupported() {
1494
1603
  if (!this.isSupported()) {
1495
1604
  throw new Error('BroadcastChannel API not supported in this environment');
1496
1605
  }
1497
1606
  }
1498
1607
  open(name) {
1499
- this.ensureSupport();
1608
+ this.ensureBroadcastChannelSupported();
1500
1609
  return new Observable((observer) => {
1501
- let channel = this.channels.get(name);
1610
+ let channel = this.connections.get(name);
1502
1611
  if (!channel) {
1503
1612
  channel = new BroadcastChannel(name);
1504
- this.channels.set(name, channel);
1613
+ this.connections.set(name, channel);
1505
1614
  }
1506
1615
  const handler = (event) => observer.next(event.data);
1507
1616
  const errorHandler = () => observer.error(new Error(`BroadcastChannel "${name}" error`));
@@ -1516,30 +1625,25 @@ class BroadcastChannelService {
1516
1625
  });
1517
1626
  }
1518
1627
  post(name, data) {
1519
- this.ensureSupport();
1520
- let channel = this.channels.get(name);
1628
+ this.ensureBroadcastChannelSupported();
1629
+ let channel = this.connections.get(name);
1521
1630
  if (!channel) {
1522
1631
  channel = new BroadcastChannel(name);
1523
- this.channels.set(name, channel);
1632
+ this.connections.set(name, channel);
1524
1633
  this.destroyRef.onDestroy(() => this.close(name));
1525
1634
  }
1526
1635
  channel.postMessage(data);
1527
1636
  }
1528
1637
  close(name) {
1529
- const channel = this.channels.get(name);
1530
- if (channel) {
1531
- channel.close();
1532
- this.channels.delete(name);
1533
- }
1638
+ this.removeConnection(name);
1534
1639
  }
1535
1640
  closeAll() {
1536
- this.channels.forEach((channel) => channel.close());
1537
- this.channels.clear();
1641
+ this.closeAllConnections();
1538
1642
  }
1539
1643
  getOpenChannels() {
1540
- return Array.from(this.channels.keys());
1644
+ return this.getConnectionKeys();
1541
1645
  }
1542
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1646
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1543
1647
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService });
1544
1648
  }
1545
1649
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService, decorators: [{
@@ -1591,21 +1695,23 @@ function networkInformationStream() {
1591
1695
  });
1592
1696
  }
1593
1697
 
1594
- class NetworkInformationService {
1595
- platformId = inject(PLATFORM_ID);
1698
+ class NetworkInformationService extends BrowserApiBaseService {
1699
+ getApiName() {
1700
+ return 'network-information';
1701
+ }
1596
1702
  isSupported() {
1597
- return isPlatformBrowser(this.platformId) && isNetworkInformationSupported();
1703
+ return this.isBrowserEnvironment() && isNetworkInformationSupported();
1598
1704
  }
1599
1705
  getSnapshot() {
1600
- return isPlatformBrowser(this.platformId) ? getNetworkSnapshot() : { online: true };
1706
+ return this.isBrowserEnvironment() ? getNetworkSnapshot() : { online: true };
1601
1707
  }
1602
1708
  watch() {
1603
1709
  return networkInformationStream();
1604
1710
  }
1605
1711
  get isOnline() {
1606
- return isPlatformBrowser(this.platformId) ? navigator.onLine : true;
1712
+ return this.isBrowserEnvironment() ? navigator.onLine : true;
1607
1713
  }
1608
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1714
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1609
1715
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService });
1610
1716
  }
1611
1717
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService, decorators: [{
@@ -1708,13 +1814,15 @@ function screenOrientationStream() {
1708
1814
  });
1709
1815
  }
1710
1816
 
1711
- class ScreenOrientationService {
1712
- platformId = inject(PLATFORM_ID);
1817
+ class ScreenOrientationService extends BrowserApiBaseService {
1818
+ getApiName() {
1819
+ return 'screen-orientation';
1820
+ }
1713
1821
  isSupported() {
1714
- return isPlatformBrowser(this.platformId) && 'screen' in window && 'orientation' in screen;
1822
+ return this.isBrowserEnvironment() && 'screen' in window && 'orientation' in screen;
1715
1823
  }
1716
1824
  getSnapshot() {
1717
- return isPlatformBrowser(this.platformId)
1825
+ return this.isBrowserEnvironment()
1718
1826
  ? getOrientationSnapshot()
1719
1827
  : { type: 'portrait-primary', angle: 0 };
1720
1828
  }
@@ -1735,7 +1843,7 @@ class ScreenOrientationService {
1735
1843
  await screen.orientation.lock(orientation);
1736
1844
  }
1737
1845
  catch (error) {
1738
- console.error('[ScreenOrientationService] Failed to lock orientation:', error);
1846
+ this.logError('Failed to lock orientation:', error);
1739
1847
  throw error;
1740
1848
  }
1741
1849
  }
@@ -1744,30 +1852,31 @@ class ScreenOrientationService {
1744
1852
  screen.orientation.unlock();
1745
1853
  }
1746
1854
  }
1747
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1855
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1748
1856
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService });
1749
1857
  }
1750
1858
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService, decorators: [{
1751
1859
  type: Injectable
1752
1860
  }] });
1753
1861
 
1754
- class FullscreenService {
1755
- destroyRef = inject(DestroyRef);
1756
- platformId = inject(PLATFORM_ID);
1862
+ class FullscreenService extends BrowserApiBaseService {
1863
+ getApiName() {
1864
+ return 'fullscreen';
1865
+ }
1757
1866
  isSupported() {
1758
- if (!isPlatformBrowser(this.platformId))
1867
+ if (!this.isBrowserEnvironment())
1759
1868
  return false;
1760
1869
  return !!(document.fullscreenEnabled ??
1761
1870
  document.webkitFullscreenEnabled);
1762
1871
  }
1763
1872
  get isFullscreen() {
1764
- if (!isPlatformBrowser(this.platformId))
1873
+ if (!this.isBrowserEnvironment())
1765
1874
  return false;
1766
1875
  return !!(document.fullscreenElement ??
1767
1876
  document.webkitFullscreenElement);
1768
1877
  }
1769
1878
  get fullscreenElement() {
1770
- if (!isPlatformBrowser(this.platformId))
1879
+ if (!this.isBrowserEnvironment())
1771
1880
  return null;
1772
1881
  return (document.fullscreenElement ??
1773
1882
  document.webkitFullscreenElement ??
@@ -1787,7 +1896,7 @@ class FullscreenService {
1787
1896
  }
1788
1897
  }
1789
1898
  catch (error) {
1790
- console.error('[FullscreenService] Failed to enter fullscreen:', error);
1899
+ this.logError('Failed to enter fullscreen:', error);
1791
1900
  throw error;
1792
1901
  }
1793
1902
  }
@@ -1804,7 +1913,7 @@ class FullscreenService {
1804
1913
  }
1805
1914
  }
1806
1915
  catch (error) {
1807
- console.error('[FullscreenService] Failed to exit fullscreen:', error);
1916
+ this.logError('Failed to exit fullscreen:', error);
1808
1917
  throw error;
1809
1918
  }
1810
1919
  }
@@ -1818,7 +1927,7 @@ class FullscreenService {
1818
1927
  }
1819
1928
  watch() {
1820
1929
  return new Observable((observer) => {
1821
- if (!isPlatformBrowser(this.platformId)) {
1930
+ if (!this.isBrowserEnvironment()) {
1822
1931
  observer.next(false);
1823
1932
  observer.complete();
1824
1933
  return undefined;
@@ -1835,7 +1944,7 @@ class FullscreenService {
1835
1944
  return cleanup;
1836
1945
  });
1837
1946
  }
1838
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1947
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1839
1948
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService });
1840
1949
  }
1841
1950
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService, decorators: [{
@@ -2018,23 +2127,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
2018
2127
  type: Injectable
2019
2128
  }] });
2020
2129
 
2021
- class ServerSentEventsService {
2022
- destroyRef = inject(DestroyRef);
2023
- platformId = inject(PLATFORM_ID);
2024
- sources = new Map();
2130
+ class ServerSentEventsService extends ConnectionRegistryBaseService {
2131
+ getApiName() {
2132
+ return 'server-sent-events';
2133
+ }
2134
+ closeNativeConnection(source) {
2135
+ source.close();
2136
+ }
2025
2137
  isSupported() {
2026
- return isPlatformBrowser(this.platformId) && 'EventSource' in window;
2138
+ return this.isBrowserEnvironment() && 'EventSource' in window;
2027
2139
  }
2028
- ensureSupport() {
2140
+ ensureSSESupported() {
2029
2141
  if (!this.isSupported()) {
2030
2142
  throw new Error('Server-Sent Events (EventSource) not supported in this environment');
2031
2143
  }
2032
2144
  }
2033
2145
  connect(url, config = {}) {
2034
- this.ensureSupport();
2146
+ this.ensureSSESupported();
2035
2147
  return new Observable((observer) => {
2036
2148
  const source = new EventSource(url, { withCredentials: config.withCredentials ?? false });
2037
- this.sources.set(url, source);
2149
+ this.connections.set(url, source);
2038
2150
  const messageHandler = (event) => {
2039
2151
  try {
2040
2152
  observer.next({
@@ -2058,7 +2170,7 @@ class ServerSentEventsService {
2058
2170
  observer.error(new Error('SSE connection closed unexpectedly'));
2059
2171
  }
2060
2172
  else {
2061
- console.warn('[ServerSentEventsService] SSE connection error, reconnecting...', event);
2173
+ console.warn(`[${this.getApiName()}] SSE connection error, reconnecting...`, event);
2062
2174
  }
2063
2175
  };
2064
2176
  source.addEventListener('message', messageHandler);
@@ -2076,35 +2188,32 @@ class ServerSentEventsService {
2076
2188
  });
2077
2189
  }
2078
2190
  disconnect(url) {
2079
- const source = this.sources.get(url);
2080
- if (source) {
2081
- source.close();
2082
- this.sources.delete(url);
2083
- }
2191
+ this.removeConnection(url);
2084
2192
  }
2085
2193
  disconnectAll() {
2086
- this.sources.forEach((source) => source.close());
2087
- this.sources.clear();
2194
+ this.closeAllConnections();
2088
2195
  }
2089
2196
  getState(url) {
2090
- const source = this.sources.get(url);
2197
+ const source = this.connections.get(url);
2091
2198
  if (!source)
2092
2199
  return 'closed';
2093
2200
  const states = ['connecting', 'open', 'closed'];
2094
2201
  return states[source.readyState] ?? 'closed';
2095
2202
  }
2096
2203
  getActiveConnections() {
2097
- return Array.from(this.sources.keys());
2204
+ return this.getConnectionKeys();
2098
2205
  }
2099
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2206
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2100
2207
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService });
2101
2208
  }
2102
2209
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService, decorators: [{
2103
2210
  type: Injectable
2104
2211
  }] });
2105
2212
 
2106
- class VibrationService {
2107
- platformId = inject(PLATFORM_ID);
2213
+ class VibrationService extends BrowserApiBaseService {
2214
+ getApiName() {
2215
+ return 'vibration';
2216
+ }
2108
2217
  presets = {
2109
2218
  success: [50, 30, 50],
2110
2219
  error: [100, 50, 100, 50, 100],
@@ -2112,7 +2221,7 @@ class VibrationService {
2112
2221
  doubleTap: [50, 100, 50],
2113
2222
  };
2114
2223
  isSupported() {
2115
- return isPlatformBrowser(this.platformId) && 'vibrate' in navigator;
2224
+ return this.isBrowserEnvironment() && 'vibrate' in navigator;
2116
2225
  }
2117
2226
  vibrate(pattern = 200) {
2118
2227
  if (!this.isSupported())
@@ -2134,20 +2243,21 @@ class VibrationService {
2134
2243
  stop() {
2135
2244
  return this.vibrate(0);
2136
2245
  }
2137
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2246
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2138
2247
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService });
2139
2248
  }
2140
2249
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService, decorators: [{
2141
2250
  type: Injectable
2142
2251
  }] });
2143
2252
 
2144
- class SpeechSynthesisService {
2145
- destroyRef = inject(DestroyRef);
2146
- platformId = inject(PLATFORM_ID);
2253
+ class SpeechSynthesisService extends BrowserApiBaseService {
2254
+ getApiName() {
2255
+ return 'speech-synthesis';
2256
+ }
2147
2257
  isSupported() {
2148
- return isPlatformBrowser(this.platformId) && 'speechSynthesis' in window;
2258
+ return this.isBrowserEnvironment() && 'speechSynthesis' in window;
2149
2259
  }
2150
- ensureSupport() {
2260
+ ensureSpeechSynthesisSupported() {
2151
2261
  if (!this.isSupported()) {
2152
2262
  throw new Error('Speech Synthesis API not supported in this browser');
2153
2263
  }
@@ -2186,7 +2296,7 @@ class SpeechSynthesisService {
2186
2296
  }
2187
2297
  speak(text, options = {}) {
2188
2298
  return new Observable((observer) => {
2189
- this.ensureSupport();
2299
+ this.ensureSpeechSynthesisSupported();
2190
2300
  const utterance = new SpeechSynthesisUtterance(text);
2191
2301
  if (options.lang)
2192
2302
  utterance.lang = options.lang;
@@ -2230,13 +2340,702 @@ class SpeechSynthesisService {
2230
2340
  if (this.isSupported())
2231
2341
  speechSynthesis.cancel();
2232
2342
  }
2233
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2343
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2234
2344
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService });
2235
2345
  }
2236
2346
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService, decorators: [{
2237
2347
  type: Injectable
2238
2348
  }] });
2239
2349
 
2350
+ const DEFAULT_OPTIONS = {
2351
+ childList: true,
2352
+ attributes: true,
2353
+ characterData: false,
2354
+ subtree: true,
2355
+ };
2356
+ function isMutationObserverSupported() {
2357
+ return typeof MutationObserver !== 'undefined';
2358
+ }
2359
+ function mutationObserverStream(element, options = DEFAULT_OPTIONS) {
2360
+ return new Observable((subscriber) => {
2361
+ const observer = new MutationObserver((mutations) => {
2362
+ subscriber.next(mutations);
2363
+ });
2364
+ observer.observe(element, options);
2365
+ return () => observer.disconnect();
2366
+ });
2367
+ }
2368
+
2369
+ class MutationObserverService extends BrowserApiBaseService {
2370
+ getApiName() {
2371
+ return 'mutation-observer';
2372
+ }
2373
+ isSupported() {
2374
+ return this.isBrowserEnvironment() && isMutationObserverSupported();
2375
+ }
2376
+ observe(target, options) {
2377
+ if (!this.isSupported()) {
2378
+ return new Observable((o) => o.error(new Error('MutationObserver API not supported')));
2379
+ }
2380
+ return mutationObserverStream(target, options);
2381
+ }
2382
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2383
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService });
2384
+ }
2385
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService, decorators: [{
2386
+ type: Injectable
2387
+ }] });
2388
+
2389
+ function isPerformanceObserverSupported() {
2390
+ return typeof PerformanceObserver !== 'undefined';
2391
+ }
2392
+ function performanceObserverStream(config) {
2393
+ return new Observable((subscriber) => {
2394
+ const observer = new PerformanceObserver((list) => {
2395
+ subscriber.next(list.getEntries());
2396
+ });
2397
+ if (config.type) {
2398
+ observer.observe({ type: config.type, buffered: config.buffered ?? true });
2399
+ }
2400
+ else if (config.entryTypes) {
2401
+ observer.observe({ entryTypes: config.entryTypes });
2402
+ }
2403
+ return () => observer.disconnect();
2404
+ });
2405
+ }
2406
+
2407
+ class PerformanceObserverService extends BrowserApiBaseService {
2408
+ getApiName() {
2409
+ return 'performance-observer';
2410
+ }
2411
+ isSupported() {
2412
+ return this.isBrowserEnvironment() && isPerformanceObserverSupported();
2413
+ }
2414
+ observe(config) {
2415
+ if (!this.isSupported()) {
2416
+ return new Observable((o) => o.error(new Error('PerformanceObserver API not supported')));
2417
+ }
2418
+ return performanceObserverStream(config);
2419
+ }
2420
+ observeByType(type, buffered = true) {
2421
+ return this.observe({ type, buffered });
2422
+ }
2423
+ getSupportedEntryTypes() {
2424
+ if (!this.isSupported())
2425
+ return [];
2426
+ return (PerformanceObserver.supportedEntryTypes ?? []);
2427
+ }
2428
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PerformanceObserverService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2429
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PerformanceObserverService });
2430
+ }
2431
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PerformanceObserverService, decorators: [{
2432
+ type: Injectable
2433
+ }] });
2434
+
2435
+ function getIdleDetectorClass$1() {
2436
+ return window.IdleDetector;
2437
+ }
2438
+ class IdleDetectorService extends BrowserApiBaseService {
2439
+ getApiName() {
2440
+ return 'idle-detector';
2441
+ }
2442
+ isSupported() {
2443
+ return this.isBrowserEnvironment() && 'IdleDetector' in window;
2444
+ }
2445
+ async requestPermission() {
2446
+ if (!this.isSupported()) {
2447
+ throw new Error('IdleDetector API not supported');
2448
+ }
2449
+ return getIdleDetectorClass$1().requestPermission();
2450
+ }
2451
+ watch(options = {}) {
2452
+ if (!this.isSupported()) {
2453
+ return new Observable((o) => o.error(new Error('IdleDetector API not supported')));
2454
+ }
2455
+ return new Observable((subscriber) => {
2456
+ const abortController = new AbortController();
2457
+ const detector = new (getIdleDetectorClass$1())();
2458
+ detector.addEventListener('change', () => {
2459
+ subscriber.next({
2460
+ user: detector.userState,
2461
+ screen: detector.screenState,
2462
+ });
2463
+ });
2464
+ detector
2465
+ .start({
2466
+ threshold: options.threshold ?? 60_000,
2467
+ signal: abortController.signal,
2468
+ })
2469
+ .catch((err) => subscriber.error(err));
2470
+ return () => abortController.abort();
2471
+ });
2472
+ }
2473
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IdleDetectorService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2474
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IdleDetectorService });
2475
+ }
2476
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IdleDetectorService, decorators: [{
2477
+ type: Injectable
2478
+ }] });
2479
+
2480
+ function getEyeDropperClass() {
2481
+ return window.EyeDropper;
2482
+ }
2483
+ class EyeDropperService extends BrowserApiBaseService {
2484
+ getApiName() {
2485
+ return 'eye-dropper';
2486
+ }
2487
+ isSupported() {
2488
+ return this.isBrowserEnvironment() && 'EyeDropper' in window;
2489
+ }
2490
+ async open(signal) {
2491
+ if (!this.isSupported()) {
2492
+ throw new Error('EyeDropper API not supported');
2493
+ }
2494
+ const dropper = new (getEyeDropperClass())();
2495
+ return dropper.open(signal ? { signal } : undefined);
2496
+ }
2497
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EyeDropperService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2498
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EyeDropperService });
2499
+ }
2500
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EyeDropperService, decorators: [{
2501
+ type: Injectable
2502
+ }] });
2503
+
2504
+ function getBarcodeDetectorClass() {
2505
+ return window.BarcodeDetector;
2506
+ }
2507
+ class BarcodeDetectorService extends BrowserApiBaseService {
2508
+ getApiName() {
2509
+ return 'barcode-detector';
2510
+ }
2511
+ isSupported() {
2512
+ return this.isBrowserEnvironment() && 'BarcodeDetector' in window;
2513
+ }
2514
+ async getSupportedFormats() {
2515
+ if (!this.isSupported())
2516
+ return [];
2517
+ return getBarcodeDetectorClass().getSupportedFormats();
2518
+ }
2519
+ async detect(image, formats) {
2520
+ if (!this.isSupported()) {
2521
+ throw new Error('BarcodeDetector API not supported');
2522
+ }
2523
+ const detector = formats
2524
+ ? new (getBarcodeDetectorClass())({ formats })
2525
+ : new (getBarcodeDetectorClass())();
2526
+ return detector.detect(image);
2527
+ }
2528
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BarcodeDetectorService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2529
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BarcodeDetectorService });
2530
+ }
2531
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BarcodeDetectorService, decorators: [{
2532
+ type: Injectable
2533
+ }] });
2534
+
2535
+ class WebAudioService extends BrowserApiBaseService {
2536
+ getApiName() {
2537
+ return 'web-audio';
2538
+ }
2539
+ context = null;
2540
+ isSupported() {
2541
+ return this.isBrowserEnvironment() && 'AudioContext' in window;
2542
+ }
2543
+ getContext() {
2544
+ if (!this.isSupported()) {
2545
+ throw new Error('Web Audio API not supported');
2546
+ }
2547
+ if (!this.context || this.context.state === 'closed') {
2548
+ this.context = new AudioContext();
2549
+ this.destroyRef.onDestroy(() => this.close());
2550
+ }
2551
+ return this.context;
2552
+ }
2553
+ async resume() {
2554
+ const ctx = this.getContext();
2555
+ if (ctx.state === 'suspended') {
2556
+ await ctx.resume();
2557
+ }
2558
+ }
2559
+ async close() {
2560
+ if (this.context && this.context.state !== 'closed') {
2561
+ await this.context.close();
2562
+ this.context = null;
2563
+ }
2564
+ }
2565
+ getState() {
2566
+ return (this.context?.state ?? 'closed');
2567
+ }
2568
+ createOscillator(type = 'sine', frequency = 440) {
2569
+ const ctx = this.getContext();
2570
+ const oscillator = ctx.createOscillator();
2571
+ oscillator.type = type;
2572
+ oscillator.frequency.value = frequency;
2573
+ return oscillator;
2574
+ }
2575
+ createGain(value = 1) {
2576
+ const ctx = this.getContext();
2577
+ const gain = ctx.createGain();
2578
+ gain.gain.value = value;
2579
+ return gain;
2580
+ }
2581
+ createAnalyser(fftSize = 2048) {
2582
+ const ctx = this.getContext();
2583
+ const analyser = ctx.createAnalyser();
2584
+ analyser.fftSize = fftSize;
2585
+ return analyser;
2586
+ }
2587
+ watchAnalyser(analyser, intervalMs = 50) {
2588
+ return new Observable((subscriber) => {
2589
+ const frequencyData = new Uint8Array(analyser.frequencyBinCount);
2590
+ const timeDomainData = new Uint8Array(analyser.frequencyBinCount);
2591
+ const interval = setInterval(() => {
2592
+ analyser.getByteFrequencyData(frequencyData);
2593
+ analyser.getByteTimeDomainData(timeDomainData);
2594
+ subscriber.next({
2595
+ frequencyData: new Uint8Array(frequencyData),
2596
+ timeDomainData: new Uint8Array(timeDomainData),
2597
+ });
2598
+ }, intervalMs);
2599
+ return () => clearInterval(interval);
2600
+ });
2601
+ }
2602
+ async decodeAudioData(arrayBuffer) {
2603
+ const ctx = this.getContext();
2604
+ return ctx.decodeAudioData(arrayBuffer);
2605
+ }
2606
+ playBuffer(buffer, loop = false) {
2607
+ const ctx = this.getContext();
2608
+ const source = ctx.createBufferSource();
2609
+ source.buffer = buffer;
2610
+ source.loop = loop;
2611
+ source.connect(ctx.destination);
2612
+ source.start(0);
2613
+ return source;
2614
+ }
2615
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebAudioService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2616
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebAudioService });
2617
+ }
2618
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebAudioService, decorators: [{
2619
+ type: Injectable
2620
+ }] });
2621
+
2622
+ function isGamepadSupported() {
2623
+ return typeof navigator !== 'undefined' && 'getGamepads' in navigator;
2624
+ }
2625
+ function gamepadSnapshot(index) {
2626
+ const gp = navigator.getGamepads()[index];
2627
+ if (!gp)
2628
+ return null;
2629
+ return {
2630
+ id: gp.id,
2631
+ index: gp.index,
2632
+ connected: gp.connected,
2633
+ buttons: gp.buttons.map((b) => ({ pressed: b.pressed, value: b.value })),
2634
+ axes: Array.from(gp.axes),
2635
+ timestamp: gp.timestamp,
2636
+ };
2637
+ }
2638
+ function gamepadConnectionStream() {
2639
+ return new Observable((subscriber) => {
2640
+ const onConnect = (e) => {
2641
+ const gp = e.gamepad;
2642
+ subscriber.next({
2643
+ type: 'connected',
2644
+ gamepad: {
2645
+ id: gp.id,
2646
+ index: gp.index,
2647
+ connected: true,
2648
+ buttons: gp.buttons.map((b) => ({ pressed: b.pressed, value: b.value })),
2649
+ axes: Array.from(gp.axes),
2650
+ timestamp: gp.timestamp,
2651
+ },
2652
+ });
2653
+ };
2654
+ const onDisconnect = (e) => {
2655
+ subscriber.next({
2656
+ type: 'disconnected',
2657
+ gamepad: {
2658
+ id: e.gamepad.id,
2659
+ index: e.gamepad.index,
2660
+ connected: false,
2661
+ buttons: [],
2662
+ axes: [],
2663
+ timestamp: e.gamepad.timestamp,
2664
+ },
2665
+ });
2666
+ };
2667
+ window.addEventListener('gamepadconnected', onConnect);
2668
+ window.addEventListener('gamepaddisconnected', onDisconnect);
2669
+ return () => {
2670
+ window.removeEventListener('gamepadconnected', onConnect);
2671
+ window.removeEventListener('gamepaddisconnected', onDisconnect);
2672
+ };
2673
+ });
2674
+ }
2675
+ function gamepadPollStream(index, intervalMs = 16) {
2676
+ return new Observable((subscriber) => {
2677
+ let rafId;
2678
+ const poll = () => {
2679
+ const state = gamepadSnapshot(index);
2680
+ if (state) {
2681
+ subscriber.next(state);
2682
+ }
2683
+ rafId = requestAnimationFrame(poll);
2684
+ };
2685
+ if (intervalMs <= 16) {
2686
+ rafId = requestAnimationFrame(poll);
2687
+ return () => cancelAnimationFrame(rafId);
2688
+ }
2689
+ const intervalId = setInterval(() => {
2690
+ const state = gamepadSnapshot(index);
2691
+ if (state)
2692
+ subscriber.next(state);
2693
+ }, intervalMs);
2694
+ return () => clearInterval(intervalId);
2695
+ });
2696
+ }
2697
+
2698
+ class GamepadService extends BrowserApiBaseService {
2699
+ getApiName() {
2700
+ return 'gamepad';
2701
+ }
2702
+ isSupported() {
2703
+ return this.isBrowserEnvironment() && isGamepadSupported();
2704
+ }
2705
+ getSnapshot(index) {
2706
+ if (!this.isSupported())
2707
+ return null;
2708
+ return gamepadSnapshot(index);
2709
+ }
2710
+ getConnectedGamepads() {
2711
+ if (!this.isSupported())
2712
+ return [];
2713
+ return navigator
2714
+ .getGamepads()
2715
+ .filter((gp) => gp !== null)
2716
+ .map((gp) => ({
2717
+ id: gp.id,
2718
+ index: gp.index,
2719
+ connected: gp.connected,
2720
+ buttons: gp.buttons.map((b) => ({ pressed: b.pressed, value: b.value })),
2721
+ axes: Array.from(gp.axes),
2722
+ timestamp: gp.timestamp,
2723
+ }));
2724
+ }
2725
+ watchConnections() {
2726
+ if (!this.isSupported()) {
2727
+ return new Observable((o) => o.error(new Error('Gamepad API not supported')));
2728
+ }
2729
+ return gamepadConnectionStream();
2730
+ }
2731
+ poll(index, intervalMs = 16) {
2732
+ if (!this.isSupported()) {
2733
+ return new Observable((o) => o.error(new Error('Gamepad API not supported')));
2734
+ }
2735
+ return gamepadPollStream(index, intervalMs);
2736
+ }
2737
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GamepadService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2738
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GamepadService });
2739
+ }
2740
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GamepadService, decorators: [{
2741
+ type: Injectable
2742
+ }] });
2743
+
2744
+ function getBluetooth() {
2745
+ return navigator.bluetooth;
2746
+ }
2747
+ class WebBluetoothService extends BrowserApiBaseService {
2748
+ getApiName() {
2749
+ return 'web-bluetooth';
2750
+ }
2751
+ isSupported() {
2752
+ return this.isBrowserEnvironment() && !!getBluetooth();
2753
+ }
2754
+ async requestDevice(options = { acceptAllDevices: true }) {
2755
+ if (!this.isSupported()) {
2756
+ throw new Error('Web Bluetooth API not supported');
2757
+ }
2758
+ return getBluetooth().requestDevice(options);
2759
+ }
2760
+ async connect(device) {
2761
+ if (!device.gatt) {
2762
+ throw new Error('GATT server not available on this device');
2763
+ }
2764
+ return device.gatt.connect();
2765
+ }
2766
+ disconnect(device) {
2767
+ device.gatt?.disconnect();
2768
+ }
2769
+ watchDisconnection(device) {
2770
+ return new Observable((subscriber) => {
2771
+ const handler = () => subscriber.next();
2772
+ device.addEventListener('gattserverdisconnected', handler);
2773
+ return () => device.removeEventListener('gattserverdisconnected', handler);
2774
+ });
2775
+ }
2776
+ async readCharacteristic(server, serviceUuid, characteristicUuid) {
2777
+ const service = await server.getPrimaryService(serviceUuid);
2778
+ const characteristic = await service.getCharacteristic(characteristicUuid);
2779
+ return characteristic.readValue();
2780
+ }
2781
+ async writeCharacteristic(server, serviceUuid, characteristicUuid, value) {
2782
+ const service = await server.getPrimaryService(serviceUuid);
2783
+ const characteristic = await service.getCharacteristic(characteristicUuid);
2784
+ await characteristic.writeValue(value);
2785
+ }
2786
+ watchCharacteristic(server, serviceUuid, characteristicUuid) {
2787
+ return new Observable((subscriber) => {
2788
+ let characteristic;
2789
+ server
2790
+ .getPrimaryService(serviceUuid)
2791
+ .then((service) => service.getCharacteristic(characteristicUuid))
2792
+ .then((char) => {
2793
+ characteristic = char;
2794
+ const handler = (event) => {
2795
+ const target = event.target;
2796
+ if (target.value)
2797
+ subscriber.next(target.value);
2798
+ };
2799
+ characteristic.addEventListener('characteristicvaluechanged', handler);
2800
+ return characteristic.startNotifications();
2801
+ })
2802
+ .catch((err) => subscriber.error(err));
2803
+ return () => {
2804
+ characteristic?.stopNotifications().catch(() => { });
2805
+ };
2806
+ });
2807
+ }
2808
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebBluetoothService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2809
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebBluetoothService });
2810
+ }
2811
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebBluetoothService, decorators: [{
2812
+ type: Injectable
2813
+ }] });
2814
+
2815
+ function getUsb() {
2816
+ return navigator.usb;
2817
+ }
2818
+ class WebUsbService extends BrowserApiBaseService {
2819
+ getApiName() {
2820
+ return 'web-usb';
2821
+ }
2822
+ isSupported() {
2823
+ return this.isBrowserEnvironment() && !!getUsb();
2824
+ }
2825
+ async requestDevice(filters = []) {
2826
+ if (!this.isSupported()) {
2827
+ throw new Error('WebUSB API not supported');
2828
+ }
2829
+ return getUsb().requestDevice({ filters });
2830
+ }
2831
+ async getDevices() {
2832
+ if (!this.isSupported())
2833
+ return [];
2834
+ return getUsb().getDevices();
2835
+ }
2836
+ async open(device) {
2837
+ await device.open();
2838
+ }
2839
+ async close(device) {
2840
+ await device.close();
2841
+ }
2842
+ async selectConfiguration(device, configurationValue) {
2843
+ await device.selectConfiguration(configurationValue);
2844
+ }
2845
+ async claimInterface(device, interfaceNumber) {
2846
+ await device.claimInterface(interfaceNumber);
2847
+ }
2848
+ async releaseInterface(device, interfaceNumber) {
2849
+ await device.releaseInterface(interfaceNumber);
2850
+ }
2851
+ async transferIn(device, endpointNumber, length) {
2852
+ return device.transferIn(endpointNumber, length);
2853
+ }
2854
+ async transferOut(device, endpointNumber, data) {
2855
+ return device.transferOut(endpointNumber, data);
2856
+ }
2857
+ watchConnection() {
2858
+ if (!this.isSupported()) {
2859
+ return new Observable((o) => o.error(new Error('WebUSB API not supported')));
2860
+ }
2861
+ return new Observable((subscriber) => {
2862
+ const usb = getUsb();
2863
+ const onConnect = (e) => subscriber.next({ device: e.device, type: 'connect' });
2864
+ const onDisconnect = (e) => subscriber.next({ device: e.device, type: 'disconnect' });
2865
+ usb.addEventListener('connect', onConnect);
2866
+ usb.addEventListener('disconnect', onDisconnect);
2867
+ return () => {
2868
+ usb.removeEventListener('connect', onConnect);
2869
+ usb.removeEventListener('disconnect', onDisconnect);
2870
+ };
2871
+ });
2872
+ }
2873
+ getDeviceInfo(device) {
2874
+ return {
2875
+ vendorId: device.vendorId,
2876
+ productId: device.productId,
2877
+ productName: device.productName,
2878
+ manufacturerName: device.manufacturerName,
2879
+ serialNumber: device.serialNumber,
2880
+ opened: device.opened,
2881
+ };
2882
+ }
2883
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebUsbService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2884
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebUsbService });
2885
+ }
2886
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebUsbService, decorators: [{
2887
+ type: Injectable
2888
+ }] });
2889
+
2890
+ function getNdefReaderClass() {
2891
+ return window.NDEFReader;
2892
+ }
2893
+ class WebNfcService extends BrowserApiBaseService {
2894
+ getApiName() {
2895
+ return 'web-nfc';
2896
+ }
2897
+ isSupported() {
2898
+ return this.isBrowserEnvironment() && 'NDEFReader' in window;
2899
+ }
2900
+ scan() {
2901
+ if (!this.isSupported()) {
2902
+ return new Observable((o) => o.error(new Error('Web NFC API not supported')));
2903
+ }
2904
+ return new Observable((subscriber) => {
2905
+ const abortController = new AbortController();
2906
+ const reader = new (getNdefReaderClass())();
2907
+ const onReading = (event) => {
2908
+ const e = event;
2909
+ subscriber.next({
2910
+ serialNumber: e.serialNumber,
2911
+ message: e.message,
2912
+ });
2913
+ };
2914
+ const onError = (event) => {
2915
+ subscriber.error(event.error ?? new Error('NFC read error'));
2916
+ };
2917
+ reader.addEventListener('reading', onReading);
2918
+ reader.addEventListener('readingerror', onError);
2919
+ reader
2920
+ .scan({ signal: abortController.signal })
2921
+ .catch((err) => subscriber.error(err));
2922
+ return () => abortController.abort();
2923
+ });
2924
+ }
2925
+ async write(message, options) {
2926
+ if (!this.isSupported()) {
2927
+ throw new Error('Web NFC API not supported');
2928
+ }
2929
+ const reader = new (getNdefReaderClass())();
2930
+ await reader.write(message, options);
2931
+ }
2932
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebNfcService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2933
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebNfcService });
2934
+ }
2935
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebNfcService, decorators: [{
2936
+ type: Injectable
2937
+ }] });
2938
+
2939
+ class PaymentRequestService extends BrowserApiBaseService {
2940
+ getApiName() {
2941
+ return 'payment-request';
2942
+ }
2943
+ isSupported() {
2944
+ return this.isBrowserEnvironment() && 'PaymentRequest' in window;
2945
+ }
2946
+ async canMakePayment(methods, details) {
2947
+ if (!this.isSupported())
2948
+ return false;
2949
+ const request = new PaymentRequest(methods, details);
2950
+ return request.canMakePayment();
2951
+ }
2952
+ async show(methods, details, options) {
2953
+ if (!this.isSupported()) {
2954
+ throw new Error('Payment Request API not supported');
2955
+ }
2956
+ const request = new PaymentRequest(methods, details, options);
2957
+ const response = await request.show();
2958
+ const result = {
2959
+ methodName: response.methodName,
2960
+ details: response.details,
2961
+ payerName: response.payerName ?? null,
2962
+ payerEmail: response.payerEmail ?? null,
2963
+ payerPhone: response.payerPhone ?? null,
2964
+ };
2965
+ await response.complete('success');
2966
+ return result;
2967
+ }
2968
+ async abort(methods, details) {
2969
+ if (!this.isSupported())
2970
+ return;
2971
+ const request = new PaymentRequest(methods, details);
2972
+ await request.abort();
2973
+ }
2974
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PaymentRequestService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
2975
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PaymentRequestService });
2976
+ }
2977
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PaymentRequestService, decorators: [{
2978
+ type: Injectable
2979
+ }] });
2980
+
2981
+ class CredentialManagementService extends BrowserApiBaseService {
2982
+ getApiName() {
2983
+ return 'credential-management';
2984
+ }
2985
+ isSupported() {
2986
+ return this.isBrowserEnvironment() && 'credentials' in navigator;
2987
+ }
2988
+ isPublicKeySupported() {
2989
+ return this.isSupported() && 'PublicKeyCredential' in window;
2990
+ }
2991
+ async get(options) {
2992
+ if (!this.isSupported()) {
2993
+ throw new Error('Credential Management API not supported');
2994
+ }
2995
+ return navigator.credentials.get(options);
2996
+ }
2997
+ async store(credential) {
2998
+ if (!this.isSupported()) {
2999
+ throw new Error('Credential Management API not supported');
3000
+ }
3001
+ await navigator.credentials.store(credential);
3002
+ }
3003
+ async createPasswordCredential(data) {
3004
+ if (!this.isSupported()) {
3005
+ throw new Error('Credential Management API not supported');
3006
+ }
3007
+ return navigator.credentials.create({
3008
+ password: data,
3009
+ });
3010
+ }
3011
+ async createPublicKeyCredential(options) {
3012
+ if (!this.isPublicKeySupported()) {
3013
+ throw new Error('PublicKeyCredential API not supported');
3014
+ }
3015
+ return navigator.credentials.create({
3016
+ publicKey: options,
3017
+ });
3018
+ }
3019
+ async preventSilentAccess() {
3020
+ if (!this.isSupported())
3021
+ return;
3022
+ await navigator.credentials.preventSilentAccess();
3023
+ }
3024
+ async isConditionalMediationAvailable() {
3025
+ if (!this.isPublicKeySupported())
3026
+ return false;
3027
+ if ('isConditionalMediationAvailable' in PublicKeyCredential) {
3028
+ return PublicKeyCredential.isConditionalMediationAvailable();
3029
+ }
3030
+ return false;
3031
+ }
3032
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CredentialManagementService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
3033
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CredentialManagementService });
3034
+ }
3035
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CredentialManagementService, decorators: [{
3036
+ type: Injectable
3037
+ }] });
3038
+
2240
3039
  // Common types for browser APIs
2241
3040
 
2242
3041
  function injectPageVisibility() {
@@ -2260,9 +3059,23 @@ function injectResizeObserver(elementOrRef, options) {
2260
3059
  const platformId = inject(PLATFORM_ID);
2261
3060
  const size = signal(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2262
3061
  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());
3062
+ if (isSignal(elementOrRef)) {
3063
+ let sub;
3064
+ effect((onCleanup) => {
3065
+ sub?.unsubscribe();
3066
+ const raw = elementOrRef();
3067
+ if (raw) {
3068
+ const el = raw instanceof ElementRef ? raw.nativeElement : raw;
3069
+ sub = resizeObserverStream(el, options).subscribe((s) => size.set(s));
3070
+ }
3071
+ onCleanup(() => sub?.unsubscribe());
3072
+ });
3073
+ }
3074
+ else {
3075
+ const el = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef;
3076
+ const sub = resizeObserverStream(el, options).subscribe((s) => size.set(s));
3077
+ destroyRef.onDestroy(() => sub.unsubscribe());
3078
+ }
2266
3079
  }
2267
3080
  return {
2268
3081
  size: size.asReadonly(),
@@ -2278,9 +3091,23 @@ function injectIntersectionObserver(elementOrRef, options) {
2278
3091
  const platformId = inject(PLATFORM_ID);
2279
3092
  const isIntersecting = signal(false, ...(ngDevMode ? [{ debugName: "isIntersecting" }] : /* istanbul ignore next */ []));
2280
3093
  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());
3094
+ if (isSignal(elementOrRef)) {
3095
+ let sub;
3096
+ effect((onCleanup) => {
3097
+ sub?.unsubscribe();
3098
+ const raw = elementOrRef();
3099
+ if (raw) {
3100
+ const el = raw instanceof ElementRef ? raw.nativeElement : raw;
3101
+ sub = intersectionObserverStream(el, options).subscribe((v) => isIntersecting.set(v));
3102
+ }
3103
+ onCleanup(() => sub?.unsubscribe());
3104
+ });
3105
+ }
3106
+ else {
3107
+ const el = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef;
3108
+ const sub = intersectionObserverStream(el, options).subscribe((v) => isIntersecting.set(v));
3109
+ destroyRef.onDestroy(() => sub.unsubscribe());
3110
+ }
2284
3111
  }
2285
3112
  return {
2286
3113
  isIntersecting: isIntersecting.asReadonly(),
@@ -2328,6 +3155,102 @@ function injectScreenOrientation() {
2328
3155
  };
2329
3156
  }
2330
3157
 
3158
+ function injectMutationObserver(elementOrRef, options) {
3159
+ const destroyRef = inject(DestroyRef);
3160
+ const platformId = inject(PLATFORM_ID);
3161
+ const mutations = signal([], ...(ngDevMode ? [{ debugName: "mutations" }] : /* istanbul ignore next */ []));
3162
+ if (isPlatformBrowser(platformId) && isMutationObserverSupported()) {
3163
+ if (isSignal(elementOrRef)) {
3164
+ let sub;
3165
+ effect((onCleanup) => {
3166
+ sub?.unsubscribe();
3167
+ const raw = elementOrRef();
3168
+ if (raw) {
3169
+ const el = raw instanceof ElementRef ? raw.nativeElement : raw;
3170
+ sub = mutationObserverStream(el, options).subscribe((m) => mutations.set(m));
3171
+ }
3172
+ onCleanup(() => sub?.unsubscribe());
3173
+ });
3174
+ }
3175
+ else {
3176
+ const el = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef;
3177
+ const sub = mutationObserverStream(el, options).subscribe((m) => mutations.set(m));
3178
+ destroyRef.onDestroy(() => sub.unsubscribe());
3179
+ }
3180
+ }
3181
+ return {
3182
+ mutations: mutations.asReadonly(),
3183
+ mutationCount: computed(() => mutations().length),
3184
+ };
3185
+ }
3186
+
3187
+ function injectPerformanceObserver(config) {
3188
+ const destroyRef = inject(DestroyRef);
3189
+ const platformId = inject(PLATFORM_ID);
3190
+ const entries = signal([], ...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
3191
+ if (isPlatformBrowser(platformId) && isPerformanceObserverSupported()) {
3192
+ const sub = performanceObserverStream(config).subscribe((e) => entries.set(e));
3193
+ destroyRef.onDestroy(() => sub.unsubscribe());
3194
+ }
3195
+ return {
3196
+ entries: entries.asReadonly(),
3197
+ entryCount: computed(() => entries().length),
3198
+ latestEntry: computed(() => entries().at(-1)),
3199
+ };
3200
+ }
3201
+
3202
+ function getIdleDetectorClass() {
3203
+ return window.IdleDetector;
3204
+ }
3205
+ function injectIdleDetector(options = {}) {
3206
+ const destroyRef = inject(DestroyRef);
3207
+ const platformId = inject(PLATFORM_ID);
3208
+ const defaultState = { user: 'active', screen: 'unlocked' };
3209
+ const state = signal(defaultState, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
3210
+ if (isPlatformBrowser(platformId) && 'IdleDetector' in window) {
3211
+ const abortController = new AbortController();
3212
+ const detector = new (getIdleDetectorClass())();
3213
+ detector.addEventListener('change', () => {
3214
+ state.set({
3215
+ user: detector.userState,
3216
+ screen: detector.screenState,
3217
+ });
3218
+ });
3219
+ detector
3220
+ .start({
3221
+ threshold: options.threshold ?? 60_000,
3222
+ signal: abortController.signal,
3223
+ })
3224
+ .catch(() => {
3225
+ /* permission denied or unsupported — keep defaults */
3226
+ });
3227
+ destroyRef.onDestroy(() => abortController.abort());
3228
+ }
3229
+ return {
3230
+ state: state.asReadonly(),
3231
+ userState: computed(() => state().user),
3232
+ screenState: computed(() => state().screen),
3233
+ isUserIdle: computed(() => state().user === 'idle'),
3234
+ isScreenLocked: computed(() => state().screen === 'locked'),
3235
+ };
3236
+ }
3237
+
3238
+ function injectGamepad(index, intervalMs = 16) {
3239
+ const destroyRef = inject(DestroyRef);
3240
+ const platformId = inject(PLATFORM_ID);
3241
+ const state = signal(null, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
3242
+ if (isPlatformBrowser(platformId) && isGamepadSupported()) {
3243
+ const sub = gamepadPollStream(index, intervalMs).subscribe((s) => state.set(s));
3244
+ destroyRef.onDestroy(() => sub.unsubscribe());
3245
+ }
3246
+ return {
3247
+ state: state.asReadonly(),
3248
+ connected: computed(() => state()?.connected ?? false),
3249
+ buttons: computed(() => state()?.buttons ?? []),
3250
+ axes: computed(() => state()?.axes ?? []),
3251
+ };
3252
+ }
3253
+
2331
3254
  class BrowserSupportUtil {
2332
3255
  static isSupported(feature) {
2333
3256
  if (typeof window === 'undefined' || typeof navigator === 'undefined') {
@@ -2466,6 +3389,18 @@ const defaultBrowserWebApisConfig = {
2466
3389
  enableServerSentEvents: false,
2467
3390
  enableVibration: false,
2468
3391
  enableSpeechSynthesis: false,
3392
+ enableMutationObserver: false,
3393
+ enablePerformanceObserver: false,
3394
+ enableIdleDetector: false,
3395
+ enableEyeDropper: false,
3396
+ enableBarcodeDetector: false,
3397
+ enableWebAudio: false,
3398
+ enableGamepad: false,
3399
+ enableWebBluetooth: false,
3400
+ enableWebUsb: false,
3401
+ enableWebNfc: false,
3402
+ enablePaymentRequest: false,
3403
+ enableCredentialManagement: false,
2469
3404
  };
2470
3405
  function provideBrowserWebApis(config = {}) {
2471
3406
  const mergedConfig = { ...defaultBrowserWebApisConfig, ...config };
@@ -2494,6 +3429,18 @@ function provideBrowserWebApis(config = {}) {
2494
3429
  [mergedConfig.enableServerSentEvents, ServerSentEventsService],
2495
3430
  [mergedConfig.enableVibration, VibrationService],
2496
3431
  [mergedConfig.enableSpeechSynthesis, SpeechSynthesisService],
3432
+ [mergedConfig.enableMutationObserver, MutationObserverService],
3433
+ [mergedConfig.enablePerformanceObserver, PerformanceObserverService],
3434
+ [mergedConfig.enableIdleDetector, IdleDetectorService],
3435
+ [mergedConfig.enableEyeDropper, EyeDropperService],
3436
+ [mergedConfig.enableBarcodeDetector, BarcodeDetectorService],
3437
+ [mergedConfig.enableWebAudio, WebAudioService],
3438
+ [mergedConfig.enableGamepad, GamepadService],
3439
+ [mergedConfig.enableWebBluetooth, WebBluetoothService],
3440
+ [mergedConfig.enableWebUsb, WebUsbService],
3441
+ [mergedConfig.enableWebNfc, WebNfcService],
3442
+ [mergedConfig.enablePaymentRequest, PaymentRequestService],
3443
+ [mergedConfig.enableCredentialManagement, CredentialManagementService],
2497
3444
  ];
2498
3445
  for (const [enabled, provider] of conditionalProviders) {
2499
3446
  if (enabled) {
@@ -2593,6 +3540,42 @@ function provideVibration() {
2593
3540
  function provideSpeechSynthesis() {
2594
3541
  return makeEnvironmentProviders([SpeechSynthesisService]);
2595
3542
  }
3543
+ function provideMutationObserver() {
3544
+ return makeEnvironmentProviders([MutationObserverService]);
3545
+ }
3546
+ function providePerformanceObserver() {
3547
+ return makeEnvironmentProviders([PerformanceObserverService]);
3548
+ }
3549
+ function provideIdleDetector() {
3550
+ return makeEnvironmentProviders([PermissionsService, IdleDetectorService]);
3551
+ }
3552
+ function provideEyeDropper() {
3553
+ return makeEnvironmentProviders([EyeDropperService]);
3554
+ }
3555
+ function provideBarcodeDetector() {
3556
+ return makeEnvironmentProviders([BarcodeDetectorService]);
3557
+ }
3558
+ function provideWebAudio() {
3559
+ return makeEnvironmentProviders([WebAudioService]);
3560
+ }
3561
+ function provideGamepad() {
3562
+ return makeEnvironmentProviders([GamepadService]);
3563
+ }
3564
+ function provideWebBluetooth() {
3565
+ return makeEnvironmentProviders([WebBluetoothService]);
3566
+ }
3567
+ function provideWebUsb() {
3568
+ return makeEnvironmentProviders([WebUsbService]);
3569
+ }
3570
+ function provideWebNfc() {
3571
+ return makeEnvironmentProviders([WebNfcService]);
3572
+ }
3573
+ function providePaymentRequest() {
3574
+ return makeEnvironmentProviders([PaymentRequestService]);
3575
+ }
3576
+ function provideCredentialManagement() {
3577
+ return makeEnvironmentProviders([CredentialManagementService]);
3578
+ }
2596
3579
 
2597
3580
  // Browser Web APIs Services
2598
3581
  // Version
@@ -2602,4 +3585,4 @@ const version = '0.1.0';
2602
3585
  * Generated bundle index. Do not edit.
2603
3586
  */
2604
3587
 
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 };
3588
+ export { BarcodeDetectorService, BatteryService, BroadcastChannelService, BrowserApiBaseService, BrowserCapabilityService, BrowserSupportUtil, CameraService, ClipboardService, ConnectionRegistryBaseService, CredentialManagementService, EyeDropperService, FileSystemAccessService, FullscreenService, GamepadService, GeolocationService, IdleDetectorService, IntersectionObserverService, MediaDevicesService, MediaRecorderService, MutationObserverService, NetworkInformationService, NotificationService, PageVisibilityService, PaymentRequestService, PerformanceObserverService, PermissionAwareBrowserApiBaseService, PermissionsService, ResizeObserverService, ScreenOrientationService, ScreenWakeLockService, ServerSentEventsService, SpeechSynthesisService, VibrationService, WebAudioService, WebBluetoothService, WebNfcService, WebShareService, WebSocketService, WebStorageService, WebUsbService, WebWorkerService, permissionGuard as createPermissionGuard, defaultBrowserWebApisConfig, injectGamepad, injectIdleDetector, injectIntersectionObserver, injectMutationObserver, injectNetworkInformation, injectPageVisibility, injectPerformanceObserver, injectResizeObserver, injectScreenOrientation, permissionGuard, provideBarcodeDetector, provideBattery, provideBroadcastChannel, provideBrowserWebApis, provideCamera, provideClipboard, provideCommunicationApis, provideCredentialManagement, provideEyeDropper, provideFileSystemAccess, provideFullscreen, provideGamepad, provideGeolocation, provideIdleDetector, provideIntersectionObserver, provideLocationApis, provideMediaApis, provideMediaDevices, provideMediaRecorder, provideMutationObserver, provideNetworkInformation, provideNotifications, providePageVisibility, providePaymentRequest, providePerformanceObserver, providePermissions, provideResizeObserver, provideScreenOrientation, provideScreenWakeLock, provideServerSentEvents, provideSpeechSynthesis, provideStorageApis, provideVibration, provideWebAudio, provideWebBluetooth, provideWebNfc, provideWebShare, provideWebSocket, provideWebStorage, provideWebUsb, provideWebWorker, version };