@angular-helpers/browser-web-apis 21.4.0 → 21.5.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,33 +1,22 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, inject, DestroyRef, PLATFORM_ID, signal, computed, isSignal, effect, ElementRef, makeEnvironmentProviders } from '@angular/core';
2
+ import { InjectionToken, inject, DestroyRef, PLATFORM_ID, Injectable, signal, computed, isSignal, effect, ElementRef, makeEnvironmentProviders } from '@angular/core';
3
3
  import { isPlatformBrowser, isPlatformServer } from '@angular/common';
4
- import { Observable, fromEvent, Subject, of, map as map$1 } from 'rxjs';
4
+ import { Observable, fromEvent, BehaviorSubject, Subject, of, map as map$1 } from 'rxjs';
5
5
  import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
6
6
  import { filter, distinctUntilChanged, map } from 'rxjs/operators';
7
7
  import { Router } from '@angular/router';
8
8
 
9
- class PermissionsService {
10
- async query(descriptor) {
11
- if (!this.isSupported()) {
12
- throw new Error('Permissions API not supported');
13
- }
14
- try {
15
- return await navigator.permissions.query(descriptor);
16
- }
17
- catch (error) {
18
- console.error('Error querying permission:', error);
19
- throw error;
20
- }
21
- }
22
- isSupported() {
23
- return typeof navigator !== 'undefined' && 'permissions' in navigator;
24
- }
25
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
26
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionsService });
27
- }
28
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionsService, decorators: [{
29
- type: Injectable
30
- }] });
9
+ const BROWSER_API_LOGGER = new InjectionToken('BROWSER_API_LOGGER', {
10
+ providedIn: 'root',
11
+ factory: () => ({
12
+ // oxlint-disable-next-line no-console
13
+ info: (message) => console.info(message),
14
+ // oxlint-disable-next-line no-console
15
+ warn: (message) => console.warn(message),
16
+ // oxlint-disable-next-line no-console
17
+ error: (message, error) => console.error(message, error),
18
+ }),
19
+ });
31
20
 
32
21
  /**
33
22
  * Base class for all Browser Web API services.
@@ -35,7 +24,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
35
24
  * - Platform detection (browser vs server)
36
25
  * - Support assertion via Template Method
37
26
  * - Error creation with cause chaining
38
- * - Structured logging
27
+ * - Structured logging via injectable BROWSER_API_LOGGER token
39
28
  * - Lifecycle management with destroyRef
40
29
  *
41
30
  * Services that also need permission querying should extend
@@ -44,6 +33,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
44
33
  class BrowserApiBaseService {
45
34
  destroyRef = inject(DestroyRef);
46
35
  platformId = inject(PLATFORM_ID);
36
+ logger = inject(BROWSER_API_LOGGER);
47
37
  /**
48
38
  * Check if running in browser environment using Angular's platform detection.
49
39
  */
@@ -57,8 +47,8 @@ class BrowserApiBaseService {
57
47
  return isPlatformServer(this.platformId);
58
48
  }
59
49
  /**
60
- * Template Method: throws a standard error when the API is not available.
61
- * Uses getApiName() to produce consistent error messages.
50
+ * Template Method: asserts the service can run in the current environment.
51
+ * Subclasses must call super.ensureSupported() and then add their own API check.
62
52
  */
63
53
  ensureSupported() {
64
54
  if (!this.isBrowserEnvironment()) {
@@ -76,16 +66,22 @@ class BrowserApiBaseService {
76
66
  return error;
77
67
  }
78
68
  /**
79
- * Log an error with the service name as prefix.
69
+ * Log an error through the injected BROWSER_API_LOGGER (default: console).
80
70
  */
81
71
  logError(message, error) {
82
- console.error(`[${this.getApiName()}] ${message}`, error);
72
+ this.logger.error(`[${this.getApiName()}] ${message}`, error);
83
73
  }
84
74
  /**
85
- * Log an informational message with the service name as prefix.
75
+ * Log a warning through the injected BROWSER_API_LOGGER (default: console).
76
+ */
77
+ logWarn(message) {
78
+ this.logger.warn(`[${this.getApiName()}] ${message}`);
79
+ }
80
+ /**
81
+ * Log an informational message through the injected BROWSER_API_LOGGER (default: console).
86
82
  */
87
83
  logInfo(message) {
88
- console.info(`[${this.getApiName()}] ${message}`);
84
+ this.logger.info(`[${this.getApiName()}] ${message}`);
89
85
  }
90
86
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BrowserApiBaseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
91
87
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BrowserApiBaseService });
@@ -94,59 +90,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
94
90
  type: Injectable
95
91
  }] });
96
92
 
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`);
93
+ class PermissionsService extends BrowserApiBaseService {
94
+ getApiName() {
95
+ return 'permissions';
96
+ }
97
+ async query(descriptor) {
98
+ if (!this.isSupported()) {
99
+ throw new Error('Permissions API not supported in this environment');
114
100
  }
115
101
  try {
116
- const status = await this.permissionsService.query({ name: permission });
117
- return status.state === 'granted';
102
+ return await navigator.permissions.query(descriptor);
118
103
  }
119
104
  catch (error) {
120
- this.logError(`Error requesting permission for ${permission}:`, error);
121
- return false;
105
+ // Firefox does not support querying 'camera', 'microphone', or 'speaker' via
106
+ // the Permissions API and throws a TypeError. Return a synthetic 'prompt' status
107
+ // so callers fall through to the native getUserMedia / requestPermission flow.
108
+ if (error instanceof TypeError) {
109
+ return { state: 'prompt', onchange: null };
110
+ }
111
+ this.logError(`Error querying permission for ${descriptor.name}:`, error);
112
+ throw error;
122
113
  }
123
114
  }
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 });
115
+ isSupported() {
116
+ return this.isBrowserEnvironment() && 'permissions' in navigator;
117
+ }
118
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionsService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
119
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionsService });
126
120
  }
127
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionAwareBrowserApiBaseService, decorators: [{
121
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionsService, decorators: [{
128
122
  type: Injectable
129
123
  }] });
130
124
 
131
- class CameraService extends PermissionAwareBrowserApiBaseService {
125
+ class CameraService extends BrowserApiBaseService {
132
126
  currentStream = null;
133
127
  getApiName() {
134
128
  return 'camera';
135
129
  }
136
- ensureCameraSupport() {
137
- if (!('mediaDevices' in navigator) || !('getUserMedia' in navigator.mediaDevices)) {
138
- throw new Error('Camera API not supported in this browser');
130
+ ensureSupported() {
131
+ super.ensureSupported();
132
+ if (!navigator.mediaDevices?.getUserMedia) {
133
+ throw new Error('Camera API not supported — a secure context (HTTPS) is required');
139
134
  }
140
135
  }
141
136
  async startCamera(constraints) {
142
- this.ensureCameraSupport();
137
+ this.ensureSupported();
143
138
  if (this.currentStream) {
144
139
  this.stopCamera();
145
140
  }
146
- const permissionStatus = await this.permissionsService.query({ name: 'camera' });
147
- if (permissionStatus.state !== 'granted') {
148
- throw new Error('Camera permission required. Please grant camera access and try again.');
149
- }
150
141
  try {
151
142
  const streamConstraints = constraints || {
152
143
  video: {
@@ -159,7 +150,7 @@ class CameraService extends PermissionAwareBrowserApiBaseService {
159
150
  return this.currentStream;
160
151
  }
161
152
  catch (error) {
162
- console.error('[CameraService] Error starting camera:', error);
153
+ this.logError('Error starting camera:', error);
163
154
  if (error instanceof Error && error.name === 'NotAllowedError') {
164
155
  throw this.createError('Camera permission denied by user. Please allow camera access in your browser settings and refresh the page.', error);
165
156
  }
@@ -201,19 +192,24 @@ class CameraService extends PermissionAwareBrowserApiBaseService {
201
192
  return this.startCamera(finalConstraints);
202
193
  }
203
194
  async getCameraCapabilities(deviceId) {
204
- this.ensureCameraSupport();
195
+ this.ensureSupported();
205
196
  try {
197
+ const activeTrack = this.currentStream
198
+ ?.getVideoTracks()
199
+ .find((t) => t.getSettings().deviceId === deviceId);
200
+ if (activeTrack) {
201
+ return activeTrack.getCapabilities() ?? null;
202
+ }
206
203
  const stream = await navigator.mediaDevices.getUserMedia({
207
204
  video: { deviceId: { exact: deviceId } },
208
205
  });
209
206
  const videoTrack = stream.getVideoTracks()[0];
210
207
  const capabilities = videoTrack.getCapabilities();
211
- // Clean up the stream
212
208
  stream.getTracks().forEach((track) => track.stop());
213
- return capabilities || null;
209
+ return capabilities ?? null;
214
210
  }
215
211
  catch (error) {
216
- console.error('[CameraService] Error getting camera capabilities:', error);
212
+ this.logError('Error getting camera capabilities:', error);
217
213
  return null;
218
214
  }
219
215
  }
@@ -224,19 +220,19 @@ class CameraService extends PermissionAwareBrowserApiBaseService {
224
220
  return this.currentStream !== null;
225
221
  }
226
222
  async getVideoInputDevices() {
227
- this.ensureCameraSupport();
223
+ this.ensureSupported();
228
224
  try {
229
225
  const devices = await navigator.mediaDevices.enumerateDevices();
230
226
  return devices.filter((device) => device.kind === 'videoinput');
231
227
  }
232
228
  catch (error) {
233
- console.error('[CameraService] Error enumerating video devices:', error);
229
+ this.logError('Error enumerating video devices:', error);
234
230
  throw this.createError('Failed to enumerate video devices', error);
235
231
  }
236
232
  }
237
233
  // Direct access to native camera API
238
234
  getNativeMediaDevices() {
239
- this.ensureCameraSupport();
235
+ this.ensureSupported();
240
236
  return navigator.mediaDevices;
241
237
  }
242
238
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CameraService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
@@ -250,13 +246,14 @@ class GeolocationService extends BrowserApiBaseService {
250
246
  getApiName() {
251
247
  return 'geolocation';
252
248
  }
253
- ensureGeolocationSupport() {
249
+ ensureSupported() {
250
+ super.ensureSupported();
254
251
  if (!('geolocation' in navigator)) {
255
- throw new Error('Geolocation API not supported in this browser');
252
+ throw new Error('Geolocation API not supported a secure context (HTTPS) is required');
256
253
  }
257
254
  }
258
255
  getCurrentPosition(options) {
259
- this.ensureGeolocationSupport();
256
+ this.ensureSupported();
260
257
  return new Promise((resolve, reject) => {
261
258
  navigator.geolocation.getCurrentPosition((position) => resolve(position), (error) => {
262
259
  this.logError('Error getting position:', error);
@@ -265,7 +262,7 @@ class GeolocationService extends BrowserApiBaseService {
265
262
  });
266
263
  }
267
264
  watchPosition(options) {
268
- this.ensureGeolocationSupport();
265
+ this.ensureSupported();
269
266
  return new Observable((observer) => {
270
267
  const watchId = navigator.geolocation.watchPosition((position) => observer.next(position), (error) => {
271
268
  this.logError('Error watching position:', error);
@@ -281,7 +278,7 @@ class GeolocationService extends BrowserApiBaseService {
281
278
  }
282
279
  // Direct access to native geolocation API
283
280
  getNativeGeolocation() {
284
- this.ensureGeolocationSupport();
281
+ this.ensureSupported();
285
282
  return navigator.geolocation;
286
283
  }
287
284
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GeolocationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
@@ -295,24 +292,25 @@ class MediaDevicesService extends BrowserApiBaseService {
295
292
  getApiName() {
296
293
  return 'media-devices';
297
294
  }
298
- ensureMediaDevicesSupport() {
299
- if (!('mediaDevices' in navigator)) {
300
- throw new Error('MediaDevices API not supported in this browser');
295
+ ensureSupported() {
296
+ super.ensureSupported();
297
+ if (!navigator.mediaDevices) {
298
+ throw new Error('MediaDevices API not supported — a secure context (HTTPS) is required');
301
299
  }
302
300
  }
303
301
  async getDevices() {
304
- this.ensureMediaDevicesSupport();
302
+ this.ensureSupported();
305
303
  try {
306
304
  const devices = await navigator.mediaDevices.enumerateDevices();
307
305
  return devices;
308
306
  }
309
307
  catch (error) {
310
- console.error('[MediaDevicesService] Error enumerating devices:', error);
308
+ this.logError('Error enumerating devices:', error);
311
309
  throw this.createError('Failed to enumerate media devices', error);
312
310
  }
313
311
  }
314
312
  async getUserMedia(constraints) {
315
- this.ensureMediaDevicesSupport();
313
+ this.ensureSupported();
316
314
  try {
317
315
  const defaultConstraints = {
318
316
  video: true,
@@ -322,12 +320,12 @@ class MediaDevicesService extends BrowserApiBaseService {
322
320
  return await navigator.mediaDevices.getUserMedia(finalConstraints);
323
321
  }
324
322
  catch (error) {
325
- console.error('[MediaDevicesService] Error getting user media:', error);
323
+ this.logError('Error getting user media:', error);
326
324
  throw this.handleMediaError(error);
327
325
  }
328
326
  }
329
327
  async getDisplayMedia(constraints) {
330
- this.ensureMediaDevicesSupport();
328
+ this.ensureSupported();
331
329
  if (!('getDisplayMedia' in navigator.mediaDevices)) {
332
330
  throw new Error('Display media API not supported in this browser');
333
331
  }
@@ -340,12 +338,12 @@ class MediaDevicesService extends BrowserApiBaseService {
340
338
  return await navigator.mediaDevices.getDisplayMedia(finalConstraints);
341
339
  }
342
340
  catch (error) {
343
- console.error('[MediaDevicesService] Error getting display media:', error);
341
+ this.logError('Error getting display media:', error);
344
342
  throw this.createError('Failed to get display media', error);
345
343
  }
346
344
  }
347
345
  watchDeviceChanges() {
348
- this.ensureMediaDevicesSupport();
346
+ this.ensureSupported();
349
347
  return new Observable((observer) => {
350
348
  const handleDeviceChange = async () => {
351
349
  try {
@@ -353,7 +351,7 @@ class MediaDevicesService extends BrowserApiBaseService {
353
351
  observer.next(devices);
354
352
  }
355
353
  catch (error) {
356
- console.error('[MediaDevicesService] Error handling device change:', error);
354
+ this.logError('Error handling device change:', error);
357
355
  }
358
356
  };
359
357
  // Listen for device changes
@@ -408,7 +406,7 @@ class MediaDevicesService extends BrowserApiBaseService {
408
406
  }
409
407
  // Direct access to native media devices API
410
408
  getNativeMediaDevices() {
411
- this.ensureMediaDevicesSupport();
409
+ this.ensureSupported();
412
410
  return navigator.mediaDevices;
413
411
  }
414
412
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MediaDevicesService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
@@ -422,22 +420,24 @@ class NotificationService extends BrowserApiBaseService {
422
420
  getApiName() {
423
421
  return 'notifications';
424
422
  }
423
+ ensureSupported() {
424
+ super.ensureSupported();
425
+ if (!('Notification' in window)) {
426
+ throw new Error('Notifications API not supported in this browser');
427
+ }
428
+ }
425
429
  get permission() {
430
+ if (!this.isBrowserEnvironment() || !('Notification' in window)) {
431
+ return 'default';
432
+ }
426
433
  return Notification.permission;
427
434
  }
428
- isSupported() {
429
- return this.isBrowserEnvironment() && 'Notification' in window;
430
- }
431
435
  async requestNotificationPermission() {
432
- if (!this.isSupported()) {
433
- throw new Error('Notification API not supported in this browser');
434
- }
436
+ this.ensureSupported();
435
437
  return Notification.requestPermission();
436
438
  }
437
439
  async showNotification(title, options) {
438
- if (!this.isSupported()) {
439
- throw new Error('Notification API not supported in this browser');
440
- }
440
+ this.ensureSupported();
441
441
  if (Notification.permission !== 'granted') {
442
442
  throw new Error('Notification permission required. Please grant notification access and try again.');
443
443
  }
@@ -456,38 +456,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
456
456
  type: Injectable
457
457
  }] });
458
458
 
459
- class ClipboardService extends PermissionAwareBrowserApiBaseService {
459
+ class ClipboardService extends BrowserApiBaseService {
460
460
  getApiName() {
461
461
  return 'clipboard';
462
462
  }
463
- async ensureClipboardPermission(action) {
464
- if (!('clipboard' in navigator)) {
465
- throw new Error('Clipboard API not supported in this browser');
466
- }
467
- const permissionStatus = await this.permissionsService.query({
468
- name: `clipboard-${action}`,
469
- });
470
- if (permissionStatus.state !== 'granted') {
471
- throw new Error(`Clipboard ${action} permission required. Please grant clipboard access and try again.`);
463
+ ensureSupported() {
464
+ super.ensureSupported();
465
+ if (!navigator.clipboard) {
466
+ throw new Error('Clipboard API not supported \u2014 a secure context (HTTPS) is required');
472
467
  }
473
468
  }
474
469
  async writeText(text) {
475
- await this.ensureClipboardPermission('write');
470
+ this.ensureSupported();
476
471
  try {
477
472
  await navigator.clipboard.writeText(text);
478
473
  }
479
474
  catch (error) {
480
- console.error('[ClipboardService] Error writing to clipboard:', error);
475
+ this.logError('Error writing to clipboard:', error);
481
476
  throw error;
482
477
  }
483
478
  }
484
479
  async readText() {
485
- await this.ensureClipboardPermission('read');
480
+ this.ensureSupported();
486
481
  try {
487
482
  return await navigator.clipboard.readText();
488
483
  }
489
484
  catch (error) {
490
- console.error('[ClipboardService] Error reading from clipboard:', error);
485
+ this.logError('Error reading from clipboard:', error);
491
486
  throw error;
492
487
  }
493
488
  }
@@ -659,14 +654,15 @@ class BatteryService extends BrowserApiBaseService {
659
654
  getApiName() {
660
655
  return 'battery';
661
656
  }
662
- ensureBatterySupport() {
657
+ ensureSupported() {
658
+ super.ensureSupported();
663
659
  const nav = navigator;
664
660
  if (!('getBattery' in nav)) {
665
- throw new Error('Battery API not supported in this browser');
661
+ throw new Error('Battery Status API not supported in this browser');
666
662
  }
667
663
  }
668
664
  async initialize() {
669
- this.ensureBatterySupport();
665
+ this.ensureSupported();
670
666
  try {
671
667
  const nav = navigator;
672
668
  this.batteryManager = await nav.getBattery();
@@ -674,7 +670,7 @@ class BatteryService extends BrowserApiBaseService {
674
670
  return batteryInfo;
675
671
  }
676
672
  catch (error) {
677
- console.error('[BatteryService] Error initializing battery API:', error);
673
+ this.logError('Error initializing battery API:', error);
678
674
  throw this.createError('Failed to initialize battery API', error);
679
675
  }
680
676
  }
@@ -743,19 +739,20 @@ class WebShareService extends BrowserApiBaseService {
743
739
  getApiName() {
744
740
  return 'web-share';
745
741
  }
746
- ensureWebShareSupport() {
742
+ ensureSupported() {
743
+ super.ensureSupported();
747
744
  if (!('share' in navigator)) {
748
745
  throw new Error('Web Share API not supported in this browser');
749
746
  }
750
747
  }
751
748
  async share(data) {
752
- this.ensureWebShareSupport();
749
+ this.ensureSupported();
753
750
  try {
754
751
  await navigator.share(data);
755
752
  return { shared: true };
756
753
  }
757
754
  catch (error) {
758
- console.error('[WebShareService] Error sharing:', error);
755
+ this.logError('Error sharing:', error);
759
756
  const errorMessage = error instanceof Error ? error.message : 'Share failed';
760
757
  return { shared: false, error: errorMessage };
761
758
  }
@@ -772,7 +769,7 @@ class WebShareService extends BrowserApiBaseService {
772
769
  }
773
770
  // Direct access to native share API
774
771
  getNativeShare() {
775
- this.ensureWebShareSupport();
772
+ this.ensureSupported();
776
773
  return navigator.share;
777
774
  }
778
775
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebShareService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
@@ -784,7 +781,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
784
781
 
785
782
  class WebStorageService extends BrowserApiBaseService {
786
783
  storageEvents = signal(null, ...(ngDevMode ? [{ debugName: "storageEvents" }] : /* istanbul ignore next */ []));
787
- destroyRef = inject(DestroyRef);
788
784
  constructor() {
789
785
  super();
790
786
  this.setupEventListeners();
@@ -792,8 +788,9 @@ class WebStorageService extends BrowserApiBaseService {
792
788
  getApiName() {
793
789
  return 'storage';
794
790
  }
795
- ensureStorageSupport() {
796
- if (!this.isBrowserEnvironment() || typeof Storage === 'undefined') {
791
+ ensureSupported() {
792
+ super.ensureSupported();
793
+ if (typeof Storage === 'undefined') {
797
794
  throw new Error('Storage API not supported in this browser');
798
795
  }
799
796
  }
@@ -803,14 +800,13 @@ class WebStorageService extends BrowserApiBaseService {
803
800
  .pipe(takeUntilDestroyed(this.destroyRef))
804
801
  .subscribe((event) => {
805
802
  const storageEvent = event;
806
- if (storageEvent.key && storageEvent.newValue !== null) {
807
- this.storageEvents.set({
808
- key: storageEvent.key,
809
- newValue: this.deserializeValue(storageEvent.newValue),
810
- oldValue: storageEvent.oldValue ? this.deserializeValue(storageEvent.oldValue) : null,
811
- storageArea: storageEvent.storageArea === localStorage ? 'localStorage' : 'sessionStorage',
812
- });
813
- }
803
+ const area = storageEvent.storageArea === localStorage ? 'localStorage' : 'sessionStorage';
804
+ this.storageEvents.set({
805
+ key: storageEvent.key,
806
+ newValue: storageEvent.newValue ? this.deserializeValue(storageEvent.newValue) : null,
807
+ oldValue: storageEvent.oldValue ? this.deserializeValue(storageEvent.oldValue) : null,
808
+ storageArea: area,
809
+ });
814
810
  });
815
811
  }
816
812
  }
@@ -837,50 +833,58 @@ class WebStorageService extends BrowserApiBaseService {
837
833
  const prefix = options?.prefix || '';
838
834
  return prefix ? `${prefix}:${key}` : key;
839
835
  }
836
+ emitStorageChange(fullKey, newValue, oldValue, area) {
837
+ this.storageEvents.set({ key: fullKey, newValue, oldValue, storageArea: area });
838
+ }
840
839
  // Local Storage Methods
841
840
  setLocalStorage(key, value, options = {}) {
842
- this.ensureStorageSupport();
841
+ this.ensureSupported();
843
842
  try {
844
843
  const serializedValue = this.serializeValue(value, options);
845
844
  const fullKey = this.getKey(key, options);
845
+ const oldRaw = localStorage.getItem(fullKey);
846
+ const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, options) : null;
846
847
  localStorage.setItem(fullKey, serializedValue);
848
+ this.emitStorageChange(fullKey, value, oldValue, 'localStorage');
847
849
  return true;
848
850
  }
849
851
  catch (error) {
850
- console.error('[WebStorageService] Error setting localStorage:', error);
852
+ this.logError('Error setting localStorage:', error);
851
853
  return false;
852
854
  }
853
855
  }
854
856
  getLocalStorage(key, defaultValue = null, options = {}) {
855
- this.ensureStorageSupport();
857
+ this.ensureSupported();
856
858
  try {
857
859
  const fullKey = this.getKey(key, options);
858
860
  const value = localStorage.getItem(fullKey);
859
861
  return value !== null ? this.deserializeValue(value, options) : defaultValue;
860
862
  }
861
863
  catch (error) {
862
- console.error('[WebStorageService] Error getting localStorage:', error);
864
+ this.logError('Error getting localStorage:', error);
863
865
  return defaultValue;
864
866
  }
865
867
  }
866
868
  removeLocalStorage(key, options = {}) {
867
- this.ensureStorageSupport();
869
+ this.ensureSupported();
868
870
  try {
869
871
  const fullKey = this.getKey(key, options);
872
+ const oldRaw = localStorage.getItem(fullKey);
873
+ const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, options) : null;
870
874
  localStorage.removeItem(fullKey);
875
+ this.emitStorageChange(fullKey, null, oldValue, 'localStorage');
871
876
  return true;
872
877
  }
873
878
  catch (error) {
874
- console.error('[WebStorageService] Error removing localStorage:', error);
879
+ this.logError('Error removing localStorage:', error);
875
880
  return false;
876
881
  }
877
882
  }
878
883
  clearLocalStorage(options = {}) {
879
- this.ensureStorageSupport();
884
+ this.ensureSupported();
880
885
  try {
881
886
  const prefix = options?.prefix;
882
887
  if (prefix) {
883
- // Only remove keys with the specified prefix
884
888
  const keysToRemove = [];
885
889
  for (let i = 0; i < localStorage.length; i++) {
886
890
  const key = localStorage.key(i);
@@ -888,62 +892,72 @@ class WebStorageService extends BrowserApiBaseService {
888
892
  keysToRemove.push(key);
889
893
  }
890
894
  }
891
- keysToRemove.forEach((key) => localStorage.removeItem(key));
895
+ keysToRemove.forEach((key) => {
896
+ const oldRaw = localStorage.getItem(key);
897
+ localStorage.removeItem(key);
898
+ this.emitStorageChange(key, null, oldRaw, 'localStorage');
899
+ });
892
900
  }
893
901
  else {
894
902
  localStorage.clear();
903
+ this.emitStorageChange(null, null, null, 'localStorage');
895
904
  }
896
905
  return true;
897
906
  }
898
907
  catch (error) {
899
- console.error('[WebStorageService] Error clearing localStorage:', error);
908
+ this.logError('Error clearing localStorage:', error);
900
909
  return false;
901
910
  }
902
911
  }
903
912
  // Session Storage Methods
904
913
  setSessionStorage(key, value, options = {}) {
905
- this.ensureStorageSupport();
914
+ this.ensureSupported();
906
915
  try {
907
916
  const serializedValue = this.serializeValue(value, options);
908
917
  const fullKey = this.getKey(key, options);
918
+ const oldRaw = sessionStorage.getItem(fullKey);
919
+ const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, options) : null;
909
920
  sessionStorage.setItem(fullKey, serializedValue);
921
+ this.emitStorageChange(fullKey, value, oldValue, 'sessionStorage');
910
922
  return true;
911
923
  }
912
924
  catch (error) {
913
- console.error('[WebStorageService] Error setting sessionStorage:', error);
925
+ this.logError('Error setting sessionStorage:', error);
914
926
  return false;
915
927
  }
916
928
  }
917
929
  getSessionStorage(key, defaultValue = null, options = {}) {
918
- this.ensureStorageSupport();
930
+ this.ensureSupported();
919
931
  try {
920
932
  const fullKey = this.getKey(key, options);
921
933
  const value = sessionStorage.getItem(fullKey);
922
934
  return value !== null ? this.deserializeValue(value, options) : defaultValue;
923
935
  }
924
936
  catch (error) {
925
- console.error('[WebStorageService] Error getting sessionStorage:', error);
937
+ this.logError('Error getting sessionStorage:', error);
926
938
  return defaultValue;
927
939
  }
928
940
  }
929
941
  removeSessionStorage(key, options = {}) {
930
- this.ensureStorageSupport();
942
+ this.ensureSupported();
931
943
  try {
932
944
  const fullKey = this.getKey(key, options);
945
+ const oldRaw = sessionStorage.getItem(fullKey);
946
+ const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, options) : null;
933
947
  sessionStorage.removeItem(fullKey);
948
+ this.emitStorageChange(fullKey, null, oldValue, 'sessionStorage');
934
949
  return true;
935
950
  }
936
951
  catch (error) {
937
- console.error('[WebStorageService] Error removing sessionStorage:', error);
952
+ this.logError('Error removing sessionStorage:', error);
938
953
  return false;
939
954
  }
940
955
  }
941
956
  clearSessionStorage(options = {}) {
942
- this.ensureStorageSupport();
957
+ this.ensureSupported();
943
958
  try {
944
959
  const prefix = options?.prefix;
945
960
  if (prefix) {
946
- // Only remove keys with the specified prefix
947
961
  const keysToRemove = [];
948
962
  for (let i = 0; i < sessionStorage.length; i++) {
949
963
  const key = sessionStorage.key(i);
@@ -951,21 +965,26 @@ class WebStorageService extends BrowserApiBaseService {
951
965
  keysToRemove.push(key);
952
966
  }
953
967
  }
954
- keysToRemove.forEach((key) => sessionStorage.removeItem(key));
968
+ keysToRemove.forEach((key) => {
969
+ const oldRaw = sessionStorage.getItem(key);
970
+ sessionStorage.removeItem(key);
971
+ this.emitStorageChange(key, null, oldRaw, 'sessionStorage');
972
+ });
955
973
  }
956
974
  else {
957
975
  sessionStorage.clear();
976
+ this.emitStorageChange(null, null, null, 'sessionStorage');
958
977
  }
959
978
  return true;
960
979
  }
961
980
  catch (error) {
962
- console.error('[WebStorageService] Error clearing sessionStorage:', error);
981
+ this.logError('Error clearing sessionStorage:', error);
963
982
  return false;
964
983
  }
965
984
  }
966
985
  // Utility Methods
967
986
  getLocalStorageSize(options = {}) {
968
- this.ensureStorageSupport();
987
+ this.ensureSupported();
969
988
  let totalSize = 0;
970
989
  const prefix = options?.prefix;
971
990
  for (let i = 0; i < localStorage.length; i++) {
@@ -977,7 +996,7 @@ class WebStorageService extends BrowserApiBaseService {
977
996
  return totalSize;
978
997
  }
979
998
  getSessionStorageSize(options = {}) {
980
- this.ensureStorageSupport();
999
+ this.ensureSupported();
981
1000
  let totalSize = 0;
982
1001
  const prefix = options?.prefix;
983
1002
  for (let i = 0; i < sessionStorage.length; i++) {
@@ -994,30 +1013,20 @@ class WebStorageService extends BrowserApiBaseService {
994
1013
  prev.oldValue === curr.oldValue));
995
1014
  }
996
1015
  watchLocalStorage(key, options = {}) {
997
- return this.getStorageEvents().pipe(map((event) => {
998
- const fullKey = this.getKey(key, options);
999
- if (event.key === fullKey && event.storageArea === 'localStorage') {
1000
- return event.newValue;
1001
- }
1002
- return this.getLocalStorage(key, null, options);
1003
- }));
1016
+ const fullKey = this.getKey(key, options);
1017
+ return this.getStorageEvents().pipe(filter((event) => event.storageArea === 'localStorage' && (event.key === null || event.key === fullKey)), map((event) => event.newValue));
1004
1018
  }
1005
1019
  watchSessionStorage(key, options = {}) {
1006
- return this.getStorageEvents().pipe(map((event) => {
1007
- const fullKey = this.getKey(key, options);
1008
- if (event.key === fullKey && event.storageArea === 'sessionStorage') {
1009
- return event.newValue;
1010
- }
1011
- return this.getSessionStorage(key, null, options);
1012
- }));
1020
+ const fullKey = this.getKey(key, options);
1021
+ return this.getStorageEvents().pipe(filter((event) => event.storageArea === 'sessionStorage' && (event.key === null || event.key === fullKey)), map((event) => event.newValue));
1013
1022
  }
1014
1023
  // Direct access to native storage APIs
1015
1024
  getNativeLocalStorage() {
1016
- this.ensureStorageSupport();
1025
+ this.ensureSupported();
1017
1026
  return localStorage;
1018
1027
  }
1019
1028
  getNativeSessionStorage() {
1020
- this.ensureStorageSupport();
1029
+ this.ensureSupported();
1021
1030
  return sessionStorage;
1022
1031
  }
1023
1032
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -1029,21 +1038,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
1029
1038
 
1030
1039
  class WebSocketService extends BrowserApiBaseService {
1031
1040
  webSocket = null;
1032
- statusSubject = new Subject();
1041
+ statusSubject = new BehaviorSubject({
1042
+ connected: false,
1043
+ connecting: false,
1044
+ reconnecting: false,
1045
+ reconnectAttempts: 0,
1046
+ });
1033
1047
  messageSubject = new Subject();
1034
1048
  reconnectAttempts = 0;
1035
1049
  reconnectTimer = null;
1036
1050
  heartbeatTimer = null;
1051
+ _cleanup = this.destroyRef.onDestroy(() => this.disconnect());
1037
1052
  getApiName() {
1038
1053
  return 'websocket';
1039
1054
  }
1040
- ensureWebSocketSupport() {
1055
+ ensureSupported() {
1056
+ super.ensureSupported();
1041
1057
  if (typeof WebSocket === 'undefined') {
1042
1058
  throw new Error('WebSocket API not supported in this browser');
1043
1059
  }
1044
1060
  }
1045
1061
  connect(config) {
1046
- this.ensureWebSocketSupport();
1062
+ this.ensureSupported();
1047
1063
  return new Observable((observer) => {
1048
1064
  this.disconnect(); // Disconnect existing connection if any
1049
1065
  this.updateStatus({
@@ -1055,10 +1071,10 @@ class WebSocketService extends BrowserApiBaseService {
1055
1071
  try {
1056
1072
  this.webSocket = new WebSocket(config.url, config.protocols);
1057
1073
  this.setupWebSocketHandlers(config);
1058
- observer.next(this.getCurrentStatus());
1074
+ observer.next(this.statusSubject.getValue());
1059
1075
  }
1060
1076
  catch (error) {
1061
- console.error('[WebSocketService] Error creating WebSocket:', error);
1077
+ this.logError('Error creating WebSocket:', error);
1062
1078
  this.updateStatus({
1063
1079
  connected: false,
1064
1080
  connecting: false,
@@ -1066,7 +1082,7 @@ class WebSocketService extends BrowserApiBaseService {
1066
1082
  error: error instanceof Error ? error.message : 'Connection failed',
1067
1083
  reconnectAttempts: 0,
1068
1084
  });
1069
- observer.next(this.getCurrentStatus());
1085
+ observer.next(this.statusSubject.getValue());
1070
1086
  }
1071
1087
  return () => {
1072
1088
  this.disconnect();
@@ -1113,15 +1129,18 @@ class WebSocketService extends BrowserApiBaseService {
1113
1129
  return this.statusSubject.asObservable();
1114
1130
  }
1115
1131
  getMessages() {
1132
+ return this.messageSubject.asObservable();
1133
+ }
1134
+ getMessagesByType(type) {
1116
1135
  return this.messageSubject
1117
1136
  .asObservable()
1118
- .pipe(filter((msg) => true));
1137
+ .pipe(filter((msg) => msg.type === type));
1119
1138
  }
1120
1139
  setupWebSocketHandlers(config) {
1121
1140
  if (!this.webSocket)
1122
1141
  return;
1123
1142
  this.webSocket.onopen = () => {
1124
- console.log('[WebSocketService] Connected to:', config.url);
1143
+ this.logInfo(`Connected to: ${config.url}`);
1125
1144
  this.reconnectAttempts = 0;
1126
1145
  this.updateStatus({
1127
1146
  connected: true,
@@ -1135,7 +1154,7 @@ class WebSocketService extends BrowserApiBaseService {
1135
1154
  }
1136
1155
  };
1137
1156
  this.webSocket.onclose = (event) => {
1138
- console.log('[WebSocketService] Connection closed:', event.code, event.reason);
1157
+ this.logInfo(`Connection closed: ${event.code} ${event.reason}`);
1139
1158
  this.updateStatus({
1140
1159
  connected: false,
1141
1160
  connecting: false,
@@ -1148,7 +1167,7 @@ class WebSocketService extends BrowserApiBaseService {
1148
1167
  }
1149
1168
  };
1150
1169
  this.webSocket.onerror = (error) => {
1151
- console.error('[WebSocketService] WebSocket error:', error);
1170
+ this.logError('WebSocket error:', error);
1152
1171
  this.updateStatus({
1153
1172
  connected: false,
1154
1173
  connecting: false,
@@ -1163,7 +1182,7 @@ class WebSocketService extends BrowserApiBaseService {
1163
1182
  this.messageSubject.next(message);
1164
1183
  }
1165
1184
  catch (error) {
1166
- console.error('[WebSocketService] Error parsing message:', error);
1185
+ this.logError('Error parsing message:', error);
1167
1186
  }
1168
1187
  };
1169
1188
  }
@@ -1179,7 +1198,7 @@ class WebSocketService extends BrowserApiBaseService {
1179
1198
  }
1180
1199
  attemptReconnect(config) {
1181
1200
  if (this.reconnectAttempts >= (config.maxReconnectAttempts || 5)) {
1182
- console.log('[WebSocketService] Max reconnect attempts reached');
1201
+ this.logInfo('Max reconnect attempts reached');
1183
1202
  return;
1184
1203
  }
1185
1204
  this.reconnectAttempts++;
@@ -1190,23 +1209,14 @@ class WebSocketService extends BrowserApiBaseService {
1190
1209
  reconnectAttempts: this.reconnectAttempts,
1191
1210
  });
1192
1211
  this.reconnectTimer = setTimeout(() => {
1193
- console.log(`[WebSocketService] Reconnect attempt ${this.reconnectAttempts}`);
1212
+ this.logInfo(`Reconnect attempt ${this.reconnectAttempts}`);
1194
1213
  this.connect(config);
1195
1214
  }, config.reconnectInterval || 3000);
1196
1215
  }
1197
1216
  updateStatus(status) {
1198
- const currentStatus = this.getCurrentStatus();
1199
- const newStatus = { ...currentStatus, ...status };
1217
+ const newStatus = { ...this.statusSubject.getValue(), ...status };
1200
1218
  this.statusSubject.next(newStatus);
1201
1219
  }
1202
- getCurrentStatus() {
1203
- return {
1204
- connected: this.webSocket?.readyState === WebSocket.OPEN,
1205
- connecting: false,
1206
- reconnecting: false,
1207
- reconnectAttempts: this.reconnectAttempts,
1208
- };
1209
- }
1210
1220
  // Direct access to native WebSocket
1211
1221
  getNativeWebSocket() {
1212
1222
  return this.webSocket;
@@ -1225,21 +1235,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
1225
1235
  }] });
1226
1236
 
1227
1237
  class WebWorkerService extends BrowserApiBaseService {
1228
- destroyRef = inject(DestroyRef);
1229
1238
  workers = new Map();
1230
1239
  workerStatuses = new Map();
1231
1240
  workerMessages = new Map();
1232
1241
  currentWorkerStatuses = new Map();
1242
+ _cleanup = this.destroyRef.onDestroy(() => this.terminateAllWorkers());
1233
1243
  getApiName() {
1234
1244
  return 'webworker';
1235
1245
  }
1236
- ensureWorkerSupport() {
1246
+ ensureSupported() {
1247
+ super.ensureSupported();
1237
1248
  if (typeof Worker === 'undefined') {
1238
1249
  throw new Error('Web Workers not supported in this browser');
1239
1250
  }
1240
1251
  }
1241
1252
  createWorker(name, scriptUrl) {
1242
- this.ensureWorkerSupport();
1253
+ this.ensureSupported();
1243
1254
  return new Observable((observer) => {
1244
1255
  if (this.workers.has(name)) {
1245
1256
  observer.next(this.currentWorkerStatuses.get(name));
@@ -1261,7 +1272,7 @@ class WebWorkerService extends BrowserApiBaseService {
1261
1272
  observer.next(status);
1262
1273
  }
1263
1274
  catch (error) {
1264
- console.error(`[WebWorkerService] Failed to create worker ${name}:`, error);
1275
+ this.logError(`Failed to create worker ${name}:`, error);
1265
1276
  const status = {
1266
1277
  initialized: false,
1267
1278
  running: false,
@@ -1295,7 +1306,7 @@ class WebWorkerService extends BrowserApiBaseService {
1295
1306
  postMessage(workerName, task) {
1296
1307
  const worker = this.workers.get(workerName);
1297
1308
  if (!worker) {
1298
- console.error(`[WebWorkerService] Worker ${workerName} not found`);
1309
+ this.logError(`Worker ${workerName} not found`);
1299
1310
  return;
1300
1311
  }
1301
1312
  try {
@@ -1313,7 +1324,7 @@ class WebWorkerService extends BrowserApiBaseService {
1313
1324
  }
1314
1325
  }
1315
1326
  catch (error) {
1316
- console.error(`[WebWorkerService] Failed to post message to worker ${workerName}:`, error);
1327
+ this.logError(`Failed to post message to worker ${workerName}:`, error);
1317
1328
  }
1318
1329
  }
1319
1330
  getMessages(workerName) {
@@ -1352,7 +1363,7 @@ class WebWorkerService extends BrowserApiBaseService {
1352
1363
  this.workerMessages.get(name).next(message);
1353
1364
  };
1354
1365
  worker.onerror = (error) => {
1355
- console.error(`[WebWorkerService] Worker ${name} error:`, error);
1366
+ this.logError(`Worker ${name} error:`, error);
1356
1367
  const status = {
1357
1368
  initialized: true,
1358
1369
  running: false,
@@ -1724,7 +1735,7 @@ class ScreenWakeLockService extends BrowserApiBaseService {
1724
1735
  return 'screen-wake-lock';
1725
1736
  }
1726
1737
  isSupported() {
1727
- return isPlatformBrowser(this.platformId) && 'wakeLock' in navigator;
1738
+ return this.isBrowserEnvironment() && 'wakeLock' in navigator;
1728
1739
  }
1729
1740
  get isActive() {
1730
1741
  return this.sentinel !== null && !this.sentinel.released;
@@ -1745,7 +1756,7 @@ class ScreenWakeLockService extends BrowserApiBaseService {
1745
1756
  return { active: true, type, released: false };
1746
1757
  }
1747
1758
  catch (error) {
1748
- console.error('[ScreenWakeLockService] Failed to acquire wake lock:', error);
1759
+ this.logError('Failed to acquire wake lock:', error);
1749
1760
  throw this.createError('Failed to acquire wake lock', error);
1750
1761
  }
1751
1762
  }
@@ -1961,10 +1972,8 @@ class FileSystemAccessService extends BrowserApiBaseService {
1961
1972
  get win() {
1962
1973
  return window;
1963
1974
  }
1964
- ensureSupport() {
1965
- if (this.isServerEnvironment()) {
1966
- throw new Error('File System Access API not available in server environment');
1967
- }
1975
+ ensureSupported() {
1976
+ super.ensureSupported();
1968
1977
  if (!('showOpenFilePicker' in window)) {
1969
1978
  throw new Error('File System Access API not supported in this browser');
1970
1979
  }
@@ -1973,7 +1982,7 @@ class FileSystemAccessService extends BrowserApiBaseService {
1973
1982
  }
1974
1983
  }
1975
1984
  async openFile(options = {}) {
1976
- this.ensureSupport();
1985
+ this.ensureSupported();
1977
1986
  try {
1978
1987
  const handles = await this.win.showOpenFilePicker(options);
1979
1988
  return Promise.all(handles.map((h) => h.getFile()));
@@ -1982,12 +1991,12 @@ class FileSystemAccessService extends BrowserApiBaseService {
1982
1991
  if (error instanceof DOMException && error.name === 'AbortError') {
1983
1992
  return [];
1984
1993
  }
1985
- console.error('[FileSystemAccessService] Error opening file:', error);
1994
+ this.logError('Error opening file:', error);
1986
1995
  throw error;
1987
1996
  }
1988
1997
  }
1989
1998
  async saveFile(content, options = {}) {
1990
- this.ensureSupport();
1999
+ this.ensureSupported();
1991
2000
  if (!this.win.showSaveFilePicker) {
1992
2001
  throw new Error('showSaveFilePicker not supported');
1993
2002
  }
@@ -2001,12 +2010,12 @@ class FileSystemAccessService extends BrowserApiBaseService {
2001
2010
  if (error instanceof DOMException && error.name === 'AbortError') {
2002
2011
  return;
2003
2012
  }
2004
- console.error('[FileSystemAccessService] Error saving file:', error);
2013
+ this.logError('Error saving file:', error);
2005
2014
  throw error;
2006
2015
  }
2007
2016
  }
2008
2017
  async openDirectory(options = {}) {
2009
- this.ensureSupport();
2018
+ this.ensureSupported();
2010
2019
  if (!this.win.showDirectoryPicker) {
2011
2020
  throw new Error('showDirectoryPicker not supported');
2012
2021
  }
@@ -2017,7 +2026,7 @@ class FileSystemAccessService extends BrowserApiBaseService {
2017
2026
  if (error instanceof DOMException && error.name === 'AbortError') {
2018
2027
  return null;
2019
2028
  }
2020
- console.error('[FileSystemAccessService] Error opening directory:', error);
2029
+ this.logError('Error opening directory:', error);
2021
2030
  throw error;
2022
2031
  }
2023
2032
  }
@@ -2084,7 +2093,7 @@ class MediaRecorderService extends BrowserApiBaseService {
2084
2093
  this.recorder.start(timeslice);
2085
2094
  }
2086
2095
  catch (error) {
2087
- console.error('[MediaRecorderService] Failed to start recording:', error);
2096
+ this.logError('Failed to start recording:', error);
2088
2097
  throw this.createError('Failed to start recording', error);
2089
2098
  }
2090
2099
  }
@@ -2170,7 +2179,7 @@ class ServerSentEventsService extends ConnectionRegistryBaseService {
2170
2179
  observer.error(new Error('SSE connection closed unexpectedly'));
2171
2180
  }
2172
2181
  else {
2173
- console.warn(`[${this.getApiName()}] SSE connection error, reconnecting...`, event);
2182
+ this.logWarn('SSE connection error, reconnecting...');
2174
2183
  }
2175
2184
  };
2176
2185
  source.addEventListener('message', messageHandler);
@@ -3365,218 +3374,253 @@ const permissionGuard = (permission) => {
3365
3374
  };
3366
3375
  };
3367
3376
 
3368
- const defaultBrowserWebApisConfig = {
3369
- enableCamera: true,
3370
- enableGeolocation: true,
3371
- enableNotifications: true,
3372
- enableClipboard: true,
3373
- enableBattery: false,
3374
- enableMediaDevices: true,
3375
- enableWebShare: false,
3376
- enableWebStorage: false,
3377
- enableWebSocket: false,
3378
- enableWebWorker: false,
3379
- enableIntersectionObserver: false,
3380
- enableResizeObserver: false,
3381
- enablePageVisibility: false,
3382
- enableBroadcastChannel: false,
3383
- enableNetworkInformation: false,
3384
- enableScreenWakeLock: false,
3385
- enableScreenOrientation: false,
3386
- enableFullscreen: false,
3387
- enableFileSystemAccess: false,
3388
- enableMediaRecorder: false,
3389
- enableServerSentEvents: false,
3390
- enableVibration: false,
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,
3404
- };
3405
- function provideBrowserWebApis(config = {}) {
3406
- const mergedConfig = { ...defaultBrowserWebApisConfig, ...config };
3407
- const providers = [PermissionsService];
3408
- const conditionalProviders = [
3409
- [mergedConfig.enableCamera, CameraService],
3410
- [mergedConfig.enableGeolocation, GeolocationService],
3411
- [mergedConfig.enableNotifications, NotificationService],
3412
- [mergedConfig.enableClipboard, ClipboardService],
3413
- [mergedConfig.enableMediaDevices, MediaDevicesService],
3414
- [mergedConfig.enableBattery, BatteryService],
3415
- [mergedConfig.enableWebShare, WebShareService],
3416
- [mergedConfig.enableWebStorage, WebStorageService],
3417
- [mergedConfig.enableWebSocket, WebSocketService],
3418
- [mergedConfig.enableWebWorker, WebWorkerService],
3419
- [mergedConfig.enableIntersectionObserver, IntersectionObserverService],
3420
- [mergedConfig.enableResizeObserver, ResizeObserverService],
3421
- [mergedConfig.enablePageVisibility, PageVisibilityService],
3422
- [mergedConfig.enableBroadcastChannel, BroadcastChannelService],
3423
- [mergedConfig.enableNetworkInformation, NetworkInformationService],
3424
- [mergedConfig.enableScreenWakeLock, ScreenWakeLockService],
3425
- [mergedConfig.enableScreenOrientation, ScreenOrientationService],
3426
- [mergedConfig.enableFullscreen, FullscreenService],
3427
- [mergedConfig.enableFileSystemAccess, FileSystemAccessService],
3428
- [mergedConfig.enableMediaRecorder, MediaRecorderService],
3429
- [mergedConfig.enableServerSentEvents, ServerSentEventsService],
3430
- [mergedConfig.enableVibration, VibrationService],
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],
3444
- ];
3445
- for (const [enabled, provider] of conditionalProviders) {
3446
- if (enabled) {
3447
- providers.push(provider);
3448
- }
3449
- }
3450
- return makeEnvironmentProviders(providers);
3377
+ function providePermissions() {
3378
+ return makeEnvironmentProviders([PermissionsService]);
3451
3379
  }
3452
- // Feature-specific providers for tree-shaking
3380
+
3453
3381
  function provideCamera() {
3454
- return makeEnvironmentProviders([PermissionsService, CameraService]);
3382
+ return makeEnvironmentProviders([CameraService]);
3455
3383
  }
3384
+
3456
3385
  function provideGeolocation() {
3457
3386
  return makeEnvironmentProviders([PermissionsService, GeolocationService]);
3458
3387
  }
3388
+
3459
3389
  function provideNotifications() {
3460
3390
  return makeEnvironmentProviders([PermissionsService, NotificationService]);
3461
3391
  }
3392
+
3462
3393
  function provideClipboard() {
3463
- return makeEnvironmentProviders([PermissionsService, ClipboardService]);
3394
+ return makeEnvironmentProviders([ClipboardService]);
3464
3395
  }
3396
+
3465
3397
  function provideMediaDevices() {
3466
3398
  return makeEnvironmentProviders([PermissionsService, MediaDevicesService]);
3467
3399
  }
3400
+
3401
+ function provideScreenWakeLock() {
3402
+ return makeEnvironmentProviders([PermissionsService, ScreenWakeLockService]);
3403
+ }
3404
+
3405
+ function provideFileSystemAccess() {
3406
+ return makeEnvironmentProviders([PermissionsService, FileSystemAccessService]);
3407
+ }
3408
+
3409
+ function provideMediaRecorder() {
3410
+ return makeEnvironmentProviders([PermissionsService, MediaRecorderService]);
3411
+ }
3412
+
3413
+ function provideIdleDetector() {
3414
+ return makeEnvironmentProviders([PermissionsService, IdleDetectorService]);
3415
+ }
3416
+
3468
3417
  function provideBattery() {
3469
3418
  return makeEnvironmentProviders([BatteryService]);
3470
3419
  }
3420
+
3471
3421
  function provideWebShare() {
3472
3422
  return makeEnvironmentProviders([WebShareService]);
3473
3423
  }
3424
+
3474
3425
  function provideWebStorage() {
3475
3426
  return makeEnvironmentProviders([WebStorageService]);
3476
3427
  }
3428
+
3477
3429
  function provideWebSocket() {
3478
3430
  return makeEnvironmentProviders([WebSocketService]);
3479
3431
  }
3432
+
3480
3433
  function provideWebWorker() {
3481
3434
  return makeEnvironmentProviders([WebWorkerService]);
3482
3435
  }
3483
- function providePermissions() {
3484
- return makeEnvironmentProviders([PermissionsService]);
3485
- }
3486
- // Combined providers for common use cases
3487
- function provideMediaApis() {
3488
- return makeEnvironmentProviders([PermissionsService, CameraService, MediaDevicesService]);
3489
- }
3490
- function provideLocationApis() {
3491
- return makeEnvironmentProviders([PermissionsService, GeolocationService]);
3492
- }
3493
- function provideStorageApis() {
3494
- return makeEnvironmentProviders([PermissionsService, ClipboardService, WebStorageService]);
3495
- }
3496
- function provideCommunicationApis() {
3497
- return makeEnvironmentProviders([
3498
- PermissionsService,
3499
- NotificationService,
3500
- WebShareService,
3501
- WebSocketService,
3502
- ]);
3503
- }
3436
+
3504
3437
  function provideIntersectionObserver() {
3505
3438
  return makeEnvironmentProviders([IntersectionObserverService]);
3506
3439
  }
3440
+
3507
3441
  function provideResizeObserver() {
3508
3442
  return makeEnvironmentProviders([ResizeObserverService]);
3509
3443
  }
3444
+
3510
3445
  function providePageVisibility() {
3511
3446
  return makeEnvironmentProviders([PageVisibilityService]);
3512
3447
  }
3448
+
3513
3449
  function provideBroadcastChannel() {
3514
3450
  return makeEnvironmentProviders([BroadcastChannelService]);
3515
3451
  }
3452
+
3516
3453
  function provideNetworkInformation() {
3517
3454
  return makeEnvironmentProviders([NetworkInformationService]);
3518
3455
  }
3519
- function provideScreenWakeLock() {
3520
- return makeEnvironmentProviders([PermissionsService, ScreenWakeLockService]);
3521
- }
3456
+
3522
3457
  function provideScreenOrientation() {
3523
3458
  return makeEnvironmentProviders([ScreenOrientationService]);
3524
3459
  }
3460
+
3525
3461
  function provideFullscreen() {
3526
3462
  return makeEnvironmentProviders([FullscreenService]);
3527
3463
  }
3528
- function provideFileSystemAccess() {
3529
- return makeEnvironmentProviders([PermissionsService, FileSystemAccessService]);
3530
- }
3531
- function provideMediaRecorder() {
3532
- return makeEnvironmentProviders([PermissionsService, MediaRecorderService]);
3533
- }
3464
+
3534
3465
  function provideServerSentEvents() {
3535
3466
  return makeEnvironmentProviders([ServerSentEventsService]);
3536
3467
  }
3468
+
3537
3469
  function provideVibration() {
3538
3470
  return makeEnvironmentProviders([VibrationService]);
3539
3471
  }
3472
+
3540
3473
  function provideSpeechSynthesis() {
3541
3474
  return makeEnvironmentProviders([SpeechSynthesisService]);
3542
3475
  }
3476
+
3543
3477
  function provideMutationObserver() {
3544
3478
  return makeEnvironmentProviders([MutationObserverService]);
3545
3479
  }
3480
+
3546
3481
  function providePerformanceObserver() {
3547
3482
  return makeEnvironmentProviders([PerformanceObserverService]);
3548
3483
  }
3549
- function provideIdleDetector() {
3550
- return makeEnvironmentProviders([PermissionsService, IdleDetectorService]);
3551
- }
3484
+
3552
3485
  function provideEyeDropper() {
3553
3486
  return makeEnvironmentProviders([EyeDropperService]);
3554
3487
  }
3488
+
3555
3489
  function provideBarcodeDetector() {
3556
3490
  return makeEnvironmentProviders([BarcodeDetectorService]);
3557
3491
  }
3492
+
3558
3493
  function provideWebAudio() {
3559
3494
  return makeEnvironmentProviders([WebAudioService]);
3560
3495
  }
3496
+
3561
3497
  function provideGamepad() {
3562
3498
  return makeEnvironmentProviders([GamepadService]);
3563
3499
  }
3500
+
3564
3501
  function provideWebBluetooth() {
3565
3502
  return makeEnvironmentProviders([WebBluetoothService]);
3566
3503
  }
3504
+
3567
3505
  function provideWebUsb() {
3568
3506
  return makeEnvironmentProviders([WebUsbService]);
3569
3507
  }
3508
+
3570
3509
  function provideWebNfc() {
3571
3510
  return makeEnvironmentProviders([WebNfcService]);
3572
3511
  }
3512
+
3573
3513
  function providePaymentRequest() {
3574
3514
  return makeEnvironmentProviders([PaymentRequestService]);
3575
3515
  }
3516
+
3576
3517
  function provideCredentialManagement() {
3577
3518
  return makeEnvironmentProviders([CredentialManagementService]);
3578
3519
  }
3579
3520
 
3521
+ function provideMediaApis() {
3522
+ return makeEnvironmentProviders([PermissionsService, CameraService, MediaDevicesService]);
3523
+ }
3524
+ function provideLocationApis() {
3525
+ return makeEnvironmentProviders([PermissionsService, GeolocationService]);
3526
+ }
3527
+ function provideStorageApis() {
3528
+ return makeEnvironmentProviders([PermissionsService, ClipboardService, WebStorageService]);
3529
+ }
3530
+ function provideCommunicationApis() {
3531
+ return makeEnvironmentProviders([
3532
+ PermissionsService,
3533
+ NotificationService,
3534
+ WebShareService,
3535
+ WebSocketService,
3536
+ ]);
3537
+ }
3538
+
3539
+ const defaultBrowserWebApisConfig = {
3540
+ enableCamera: true,
3541
+ enableGeolocation: true,
3542
+ enableNotifications: true,
3543
+ enableClipboard: true,
3544
+ enableBattery: false,
3545
+ enableMediaDevices: true,
3546
+ enableWebShare: false,
3547
+ enableWebStorage: false,
3548
+ enableWebSocket: false,
3549
+ enableWebWorker: false,
3550
+ enableIntersectionObserver: false,
3551
+ enableResizeObserver: false,
3552
+ enablePageVisibility: false,
3553
+ enableBroadcastChannel: false,
3554
+ enableNetworkInformation: false,
3555
+ enableScreenWakeLock: false,
3556
+ enableScreenOrientation: false,
3557
+ enableFullscreen: false,
3558
+ enableFileSystemAccess: false,
3559
+ enableMediaRecorder: false,
3560
+ enableServerSentEvents: false,
3561
+ enableVibration: false,
3562
+ enableSpeechSynthesis: false,
3563
+ enableMutationObserver: false,
3564
+ enablePerformanceObserver: false,
3565
+ enableIdleDetector: false,
3566
+ enableEyeDropper: false,
3567
+ enableBarcodeDetector: false,
3568
+ enableWebAudio: false,
3569
+ enableGamepad: false,
3570
+ enableWebBluetooth: false,
3571
+ enableWebUsb: false,
3572
+ enableWebNfc: false,
3573
+ enablePaymentRequest: false,
3574
+ enableCredentialManagement: false,
3575
+ };
3576
+ function provideBrowserWebApis(config = {}) {
3577
+ const mergedConfig = { ...defaultBrowserWebApisConfig, ...config };
3578
+ const providers = [PermissionsService];
3579
+ const conditionalProviders = [
3580
+ [mergedConfig.enableCamera, CameraService],
3581
+ [mergedConfig.enableGeolocation, GeolocationService],
3582
+ [mergedConfig.enableNotifications, NotificationService],
3583
+ [mergedConfig.enableClipboard, ClipboardService],
3584
+ [mergedConfig.enableMediaDevices, MediaDevicesService],
3585
+ [mergedConfig.enableBattery, BatteryService],
3586
+ [mergedConfig.enableWebShare, WebShareService],
3587
+ [mergedConfig.enableWebStorage, WebStorageService],
3588
+ [mergedConfig.enableWebSocket, WebSocketService],
3589
+ [mergedConfig.enableWebWorker, WebWorkerService],
3590
+ [mergedConfig.enableIntersectionObserver, IntersectionObserverService],
3591
+ [mergedConfig.enableResizeObserver, ResizeObserverService],
3592
+ [mergedConfig.enablePageVisibility, PageVisibilityService],
3593
+ [mergedConfig.enableBroadcastChannel, BroadcastChannelService],
3594
+ [mergedConfig.enableNetworkInformation, NetworkInformationService],
3595
+ [mergedConfig.enableScreenWakeLock, ScreenWakeLockService],
3596
+ [mergedConfig.enableScreenOrientation, ScreenOrientationService],
3597
+ [mergedConfig.enableFullscreen, FullscreenService],
3598
+ [mergedConfig.enableFileSystemAccess, FileSystemAccessService],
3599
+ [mergedConfig.enableMediaRecorder, MediaRecorderService],
3600
+ [mergedConfig.enableServerSentEvents, ServerSentEventsService],
3601
+ [mergedConfig.enableVibration, VibrationService],
3602
+ [mergedConfig.enableSpeechSynthesis, SpeechSynthesisService],
3603
+ [mergedConfig.enableMutationObserver, MutationObserverService],
3604
+ [mergedConfig.enablePerformanceObserver, PerformanceObserverService],
3605
+ [mergedConfig.enableIdleDetector, IdleDetectorService],
3606
+ [mergedConfig.enableEyeDropper, EyeDropperService],
3607
+ [mergedConfig.enableBarcodeDetector, BarcodeDetectorService],
3608
+ [mergedConfig.enableWebAudio, WebAudioService],
3609
+ [mergedConfig.enableGamepad, GamepadService],
3610
+ [mergedConfig.enableWebBluetooth, WebBluetoothService],
3611
+ [mergedConfig.enableWebUsb, WebUsbService],
3612
+ [mergedConfig.enableWebNfc, WebNfcService],
3613
+ [mergedConfig.enablePaymentRequest, PaymentRequestService],
3614
+ [mergedConfig.enableCredentialManagement, CredentialManagementService],
3615
+ ];
3616
+ for (const [enabled, provider] of conditionalProviders) {
3617
+ if (enabled) {
3618
+ providers.push(provider);
3619
+ }
3620
+ }
3621
+ return makeEnvironmentProviders(providers);
3622
+ }
3623
+
3580
3624
  // Browser Web APIs Services
3581
3625
  // Version
3582
3626
  const version = '0.1.0';
@@ -3585,4 +3629,4 @@ const version = '0.1.0';
3585
3629
  * Generated bundle index. Do not edit.
3586
3630
  */
3587
3631
 
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 };
3632
+ export { BROWSER_API_LOGGER, 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, 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 };