@angular-helpers/browser-web-apis 21.6.0 → 21.7.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.
@@ -2,8 +2,8 @@ import * as i0 from '@angular/core';
2
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
4
  import { Observable, fromEvent, Subject, of, map as map$1 } from 'rxjs';
5
- import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
6
- import { filter, distinctUntilChanged, map } from 'rxjs/operators';
5
+ import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
+ import { filter, map, distinctUntilChanged } from 'rxjs/operators';
7
7
  import { Router } from '@angular/router';
8
8
 
9
9
  const BROWSER_API_LOGGER = new InjectionToken('BROWSER_API_LOGGER', {
@@ -779,255 +779,340 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
779
779
  type: Injectable
780
780
  }] });
781
781
 
782
- class WebStorageService extends BrowserApiBaseService {
783
- storageEvents = signal(null, ...(ngDevMode ? [{ debugName: "storageEvents" }] : /* istanbul ignore next */ []));
784
- constructor() {
785
- super();
786
- this.setupEventListeners();
787
- }
788
- getApiName() {
789
- return 'storage';
790
- }
791
- ensureSupported() {
792
- super.ensureSupported();
793
- if (typeof Storage === 'undefined') {
794
- throw new Error('Storage API not supported in this browser');
795
- }
796
- }
797
- setupEventListeners() {
798
- if (this.isBrowserEnvironment()) {
799
- fromEvent(window, 'storage')
800
- .pipe(takeUntilDestroyed(this.destroyRef))
801
- .subscribe((event) => {
802
- const storageEvent = event;
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
- });
810
- });
811
- }
812
- }
813
- serializeValue(value, options) {
814
- if (options?.serialize) {
815
- return options.serialize(value);
816
- }
817
- return JSON.stringify(value);
782
+ const SECURITY_WARN_KEY = Symbol('storage-security-warned');
783
+ /**
784
+ * Implementation of `StorageNamespace` shared by `local` and `session` namespaces.
785
+ * Wraps every native access in try/catch so Safari private mode and sandboxed iframes
786
+ * (which throw `SecurityError`) degrade gracefully instead of crashing the app.
787
+ */
788
+ class StorageNamespaceImpl {
789
+ area;
790
+ events;
791
+ logger;
792
+ supportedCache = null;
793
+ constructor(area, events, logger) {
794
+ this.area = area;
795
+ this.events = events;
796
+ this.logger = logger;
818
797
  }
819
- deserializeValue(value, options) {
820
- if (value === null)
821
- return null;
822
- if (options?.deserialize) {
823
- return options.deserialize(value);
798
+ isSupported() {
799
+ if (this.supportedCache !== null)
800
+ return this.supportedCache;
801
+ if (typeof window === 'undefined' || typeof Storage === 'undefined') {
802
+ this.supportedCache = false;
803
+ return false;
824
804
  }
825
805
  try {
826
- return JSON.parse(value);
806
+ const store = this.getStore();
807
+ const probe = `__bwa_probe_${Date.now()}__`;
808
+ store.setItem(probe, '1');
809
+ store.removeItem(probe);
810
+ this.supportedCache = true;
827
811
  }
828
812
  catch {
829
- return value;
813
+ this.warnSecurityOnce();
814
+ this.supportedCache = false;
830
815
  }
816
+ return this.supportedCache;
831
817
  }
832
- getKey(key, options) {
833
- const prefix = options?.prefix || '';
834
- return prefix ? `${prefix}:${key}` : key;
835
- }
836
- emitStorageChange(fullKey, newValue, oldValue, area) {
837
- this.storageEvents.set({ key: fullKey, newValue, oldValue, storageArea: area });
838
- }
839
- // Local Storage Methods
840
- setLocalStorage(key, value, options = {}) {
841
- this.ensureSupported();
818
+ set(key, value, opts = {}) {
819
+ if (!this.isSupported())
820
+ return false;
842
821
  try {
843
- const serializedValue = this.serializeValue(value, options);
844
- const fullKey = this.getKey(key, options);
845
- const oldRaw = localStorage.getItem(fullKey);
846
- const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, options) : null;
847
- localStorage.setItem(fullKey, serializedValue);
848
- this.emitStorageChange(fullKey, value, oldValue, 'localStorage');
822
+ const fullKey = this.getKey(key, opts);
823
+ const oldRaw = this.getStore().getItem(fullKey);
824
+ const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, opts) : null;
825
+ const serialized = this.serializeValue(value, opts);
826
+ this.getStore().setItem(fullKey, serialized);
827
+ this.events.emit({ key: fullKey, newValue: value, oldValue, storageArea: this.area });
849
828
  return true;
850
829
  }
851
830
  catch (error) {
852
- this.logError('Error setting localStorage:', error);
831
+ this.logger.error(`[storage:${this.area}] set("${key}") failed`, error);
853
832
  return false;
854
833
  }
855
834
  }
856
- getLocalStorage(key, defaultValue = null, options = {}) {
857
- this.ensureSupported();
835
+ get(key, defaultValue = null, opts = {}) {
836
+ if (!this.isSupported())
837
+ return defaultValue;
858
838
  try {
859
- const fullKey = this.getKey(key, options);
860
- const value = localStorage.getItem(fullKey);
861
- return value !== null ? this.deserializeValue(value, options) : defaultValue;
839
+ const fullKey = this.getKey(key, opts);
840
+ const raw = this.getStore().getItem(fullKey);
841
+ return raw !== null ? this.deserializeValue(raw, opts) : defaultValue;
862
842
  }
863
843
  catch (error) {
864
- this.logError('Error getting localStorage:', error);
844
+ this.logger.error(`[storage:${this.area}] get("${key}") failed`, error);
865
845
  return defaultValue;
866
846
  }
867
847
  }
868
- removeLocalStorage(key, options = {}) {
869
- this.ensureSupported();
848
+ remove(key, opts = {}) {
849
+ if (!this.isSupported())
850
+ return false;
870
851
  try {
871
- const fullKey = this.getKey(key, options);
872
- const oldRaw = localStorage.getItem(fullKey);
873
- const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, options) : null;
874
- localStorage.removeItem(fullKey);
875
- this.emitStorageChange(fullKey, null, oldValue, 'localStorage');
852
+ const fullKey = this.getKey(key, opts);
853
+ const oldRaw = this.getStore().getItem(fullKey);
854
+ const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, opts) : null;
855
+ this.getStore().removeItem(fullKey);
856
+ this.events.emit({ key: fullKey, newValue: null, oldValue, storageArea: this.area });
876
857
  return true;
877
858
  }
878
859
  catch (error) {
879
- this.logError('Error removing localStorage:', error);
860
+ this.logger.error(`[storage:${this.area}] remove("${key}") failed`, error);
880
861
  return false;
881
862
  }
882
863
  }
883
- clearLocalStorage(options = {}) {
884
- this.ensureSupported();
864
+ clear(opts = {}) {
865
+ if (!this.isSupported())
866
+ return false;
885
867
  try {
886
- const prefix = options?.prefix;
868
+ const prefix = opts?.prefix;
869
+ const store = this.getStore();
887
870
  if (prefix) {
888
- const keysToRemove = [];
889
- for (let i = 0; i < localStorage.length; i++) {
890
- const key = localStorage.key(i);
891
- if (key && key.startsWith(`${prefix}:`)) {
892
- keysToRemove.push(key);
893
- }
871
+ const toRemove = [];
872
+ for (let i = 0; i < store.length; i++) {
873
+ const k = store.key(i);
874
+ if (k && k.startsWith(`${prefix}:`))
875
+ toRemove.push(k);
876
+ }
877
+ for (const k of toRemove) {
878
+ const oldRaw = store.getItem(k);
879
+ store.removeItem(k);
880
+ this.events.emit({
881
+ key: k,
882
+ newValue: null,
883
+ oldValue: oldRaw,
884
+ storageArea: this.area,
885
+ });
894
886
  }
895
- keysToRemove.forEach((key) => {
896
- const oldRaw = localStorage.getItem(key);
897
- localStorage.removeItem(key);
898
- this.emitStorageChange(key, null, oldRaw, 'localStorage');
899
- });
900
887
  }
901
888
  else {
902
- localStorage.clear();
903
- this.emitStorageChange(null, null, null, 'localStorage');
889
+ store.clear();
890
+ this.events.emit({ key: null, newValue: null, oldValue: null, storageArea: this.area });
904
891
  }
905
892
  return true;
906
893
  }
907
894
  catch (error) {
908
- this.logError('Error clearing localStorage:', error);
895
+ this.logger.error(`[storage:${this.area}] clear() failed`, error);
909
896
  return false;
910
897
  }
911
898
  }
912
- // Session Storage Methods
913
- setSessionStorage(key, value, options = {}) {
914
- this.ensureSupported();
899
+ size(opts = {}) {
900
+ if (!this.isSupported())
901
+ return 0;
915
902
  try {
916
- const serializedValue = this.serializeValue(value, options);
917
- const fullKey = this.getKey(key, options);
918
- const oldRaw = sessionStorage.getItem(fullKey);
919
- const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, options) : null;
920
- sessionStorage.setItem(fullKey, serializedValue);
921
- this.emitStorageChange(fullKey, value, oldValue, 'sessionStorage');
922
- return true;
903
+ const store = this.getStore();
904
+ const prefix = opts?.prefix;
905
+ let total = 0;
906
+ for (let i = 0; i < store.length; i++) {
907
+ const k = store.key(i);
908
+ if (k && (!prefix || k.startsWith(`${prefix}:`))) {
909
+ total += (store.getItem(k)?.length ?? 0) + k.length;
910
+ }
911
+ }
912
+ return total;
923
913
  }
924
914
  catch (error) {
925
- this.logError('Error setting sessionStorage:', error);
926
- return false;
915
+ this.logger.error(`[storage:${this.area}] size() failed`, error);
916
+ return 0;
927
917
  }
928
918
  }
929
- getSessionStorage(key, defaultValue = null, options = {}) {
930
- this.ensureSupported();
931
- try {
932
- const fullKey = this.getKey(key, options);
933
- const value = sessionStorage.getItem(fullKey);
934
- return value !== null ? this.deserializeValue(value, options) : defaultValue;
935
- }
936
- catch (error) {
937
- this.logError('Error getting sessionStorage:', error);
938
- return defaultValue;
919
+ watch(key, opts = {}) {
920
+ const fullKey = this.getKey(key, opts);
921
+ return this.events.events$.pipe(filter((event) => event.storageArea === this.area && (event.key === null || event.key === fullKey)), map((event) => event.newValue));
922
+ }
923
+ native() {
924
+ if (!this.isSupported()) {
925
+ throw new Error(`${this.area} not supported in this environment`);
939
926
  }
927
+ return this.getStore();
940
928
  }
941
- removeSessionStorage(key, options = {}) {
942
- this.ensureSupported();
929
+ getStore() {
930
+ return this.area === 'localStorage' ? window.localStorage : window.sessionStorage;
931
+ }
932
+ getKey(key, opts) {
933
+ const prefix = opts?.prefix ?? '';
934
+ return prefix ? `${prefix}:${key}` : key;
935
+ }
936
+ serializeValue(value, opts) {
937
+ if (opts?.serialize)
938
+ return opts.serialize(value);
939
+ return JSON.stringify(value);
940
+ }
941
+ deserializeValue(value, opts) {
942
+ if (opts?.deserialize)
943
+ return opts.deserialize(value);
943
944
  try {
944
- const fullKey = this.getKey(key, options);
945
- const oldRaw = sessionStorage.getItem(fullKey);
946
- const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw, options) : null;
947
- sessionStorage.removeItem(fullKey);
948
- this.emitStorageChange(fullKey, null, oldValue, 'sessionStorage');
949
- return true;
945
+ return JSON.parse(value);
950
946
  }
951
- catch (error) {
952
- this.logError('Error removing sessionStorage:', error);
953
- return false;
947
+ catch {
948
+ return value;
954
949
  }
955
950
  }
956
- clearSessionStorage(options = {}) {
957
- this.ensureSupported();
951
+ warnSecurityOnce() {
952
+ const holder = globalThis;
953
+ if (!holder[SECURITY_WARN_KEY])
954
+ holder[SECURITY_WARN_KEY] = new Set();
955
+ if (holder[SECURITY_WARN_KEY].has(this.area))
956
+ return;
957
+ holder[SECURITY_WARN_KEY].add(this.area);
958
+ this.logger.warn(`[storage:${this.area}] access denied (SecurityError). Common causes: Safari private mode, ` +
959
+ 'sandboxed iframes, or browser storage disabled. Falling back to default values.');
960
+ }
961
+ }
962
+
963
+ let legacyDeprecationLogged$1 = false;
964
+ /**
965
+ * Web Storage service with two namespaces (`local`, `session`) sharing one method
966
+ * surface. SecurityError-safe (Safari private mode, sandboxed iframes return defaults
967
+ * instead of throwing).
968
+ *
969
+ * Preferred usage:
970
+ * ```ts
971
+ * const storage = inject(WebStorageService);
972
+ * storage.local.set('user', { id: 1 });
973
+ * const user = storage.local.get<{ id: number }>('user');
974
+ * storage.local.watch<{ id: number }>('user').subscribe(console.log);
975
+ * ```
976
+ *
977
+ * Legacy methods (`setLocalStorage`, `getLocalStorage`, etc.) remain as deprecated
978
+ * wrappers for one minor cycle; removal slated for v22.
979
+ */
980
+ class WebStorageService extends BrowserApiBaseService {
981
+ storageLogger = inject(BROWSER_API_LOGGER);
982
+ storageEvents = signal(null, ...(ngDevMode ? [{ debugName: "storageEvents" }] : /* istanbul ignore next */ []));
983
+ eventBus = {
984
+ emit: (event) => this.storageEvents.set(event),
985
+ events$: toObservable(this.storageEvents).pipe(filter((event) => event !== null), distinctUntilChanged((a, b) => a.key === b.key &&
986
+ a.newValue === b.newValue &&
987
+ a.oldValue === b.oldValue &&
988
+ a.storageArea === b.storageArea)),
989
+ };
990
+ /** Local storage namespace. */
991
+ local = new StorageNamespaceImpl('localStorage', this.eventBus, this.storageLogger);
992
+ /** Session storage namespace. */
993
+ session = new StorageNamespaceImpl('sessionStorage', this.eventBus, this.storageLogger);
994
+ constructor() {
995
+ super();
996
+ this.setupCrossTabListener();
997
+ }
998
+ getApiName() {
999
+ return 'storage';
1000
+ }
1001
+ ensureSupported() {
1002
+ super.ensureSupported();
1003
+ if (typeof Storage === 'undefined') {
1004
+ throw new Error('Storage API not supported in this browser');
1005
+ }
1006
+ }
1007
+ /** Returns true if either local or session storage is usable. */
1008
+ isSupported() {
1009
+ return this.local.isSupported() || this.session.isSupported();
1010
+ }
1011
+ /** Stream of every storage mutation observed in this tab or other tabs. */
1012
+ getStorageEvents() {
1013
+ return this.eventBus.events$;
1014
+ }
1015
+ setupCrossTabListener() {
1016
+ if (!this.isBrowserEnvironment())
1017
+ return;
1018
+ fromEvent(window, 'storage')
1019
+ .pipe(takeUntilDestroyed(this.destroyRef))
1020
+ .subscribe((event) => {
1021
+ const area = event.storageArea === window.localStorage ? 'localStorage' : 'sessionStorage';
1022
+ this.storageEvents.set({
1023
+ key: event.key,
1024
+ newValue: event.newValue ? this.safeParse(event.newValue) : null,
1025
+ oldValue: event.oldValue ? this.safeParse(event.oldValue) : null,
1026
+ storageArea: area,
1027
+ });
1028
+ });
1029
+ }
1030
+ safeParse(value) {
958
1031
  try {
959
- const prefix = options?.prefix;
960
- if (prefix) {
961
- const keysToRemove = [];
962
- for (let i = 0; i < sessionStorage.length; i++) {
963
- const key = sessionStorage.key(i);
964
- if (key && key.startsWith(`${prefix}:`)) {
965
- keysToRemove.push(key);
966
- }
967
- }
968
- keysToRemove.forEach((key) => {
969
- const oldRaw = sessionStorage.getItem(key);
970
- sessionStorage.removeItem(key);
971
- this.emitStorageChange(key, null, oldRaw, 'sessionStorage');
972
- });
973
- }
974
- else {
975
- sessionStorage.clear();
976
- this.emitStorageChange(null, null, null, 'sessionStorage');
977
- }
978
- return true;
1032
+ return JSON.parse(value);
979
1033
  }
980
- catch (error) {
981
- this.logError('Error clearing sessionStorage:', error);
982
- return false;
1034
+ catch {
1035
+ return value;
983
1036
  }
984
1037
  }
985
- // Utility Methods
1038
+ // ---------- legacy API (deprecated) ----------
1039
+ /** @deprecated Use `storage.local.set(key, value, opts)`. Removed in v22. */
1040
+ setLocalStorage(key, value, options = {}) {
1041
+ this.warnLegacyOnce();
1042
+ return this.local.set(key, value, options);
1043
+ }
1044
+ /** @deprecated Use `storage.local.get(key, defaultValue, opts)`. Removed in v22. */
1045
+ getLocalStorage(key, defaultValue = null, options = {}) {
1046
+ this.warnLegacyOnce();
1047
+ return this.local.get(key, defaultValue, options);
1048
+ }
1049
+ /** @deprecated Use `storage.local.remove(key, opts)`. Removed in v22. */
1050
+ removeLocalStorage(key, options = {}) {
1051
+ this.warnLegacyOnce();
1052
+ return this.local.remove(key, options);
1053
+ }
1054
+ /** @deprecated Use `storage.local.clear(opts)`. Removed in v22. */
1055
+ clearLocalStorage(options = {}) {
1056
+ this.warnLegacyOnce();
1057
+ return this.local.clear(options);
1058
+ }
1059
+ /** @deprecated Use `storage.session.set(key, value, opts)`. Removed in v22. */
1060
+ setSessionStorage(key, value, options = {}) {
1061
+ this.warnLegacyOnce();
1062
+ return this.session.set(key, value, options);
1063
+ }
1064
+ /** @deprecated Use `storage.session.get(key, defaultValue, opts)`. Removed in v22. */
1065
+ getSessionStorage(key, defaultValue = null, options = {}) {
1066
+ this.warnLegacyOnce();
1067
+ return this.session.get(key, defaultValue, options);
1068
+ }
1069
+ /** @deprecated Use `storage.session.remove(key, opts)`. Removed in v22. */
1070
+ removeSessionStorage(key, options = {}) {
1071
+ this.warnLegacyOnce();
1072
+ return this.session.remove(key, options);
1073
+ }
1074
+ /** @deprecated Use `storage.session.clear(opts)`. Removed in v22. */
1075
+ clearSessionStorage(options = {}) {
1076
+ this.warnLegacyOnce();
1077
+ return this.session.clear(options);
1078
+ }
1079
+ /** @deprecated Use `storage.local.size(opts)`. Removed in v22. */
986
1080
  getLocalStorageSize(options = {}) {
987
- this.ensureSupported();
988
- let totalSize = 0;
989
- const prefix = options?.prefix;
990
- for (let i = 0; i < localStorage.length; i++) {
991
- const key = localStorage.key(i);
992
- if (key && (!prefix || key.startsWith(`${prefix}:`))) {
993
- totalSize += (localStorage.getItem(key)?.length || 0) + key.length;
994
- }
995
- }
996
- return totalSize;
1081
+ this.warnLegacyOnce();
1082
+ return this.local.size(options);
997
1083
  }
1084
+ /** @deprecated Use `storage.session.size(opts)`. Removed in v22. */
998
1085
  getSessionStorageSize(options = {}) {
999
- this.ensureSupported();
1000
- let totalSize = 0;
1001
- const prefix = options?.prefix;
1002
- for (let i = 0; i < sessionStorage.length; i++) {
1003
- const key = sessionStorage.key(i);
1004
- if (key && (!prefix || key.startsWith(`${prefix}:`))) {
1005
- totalSize += (sessionStorage.getItem(key)?.length || 0) + key.length;
1006
- }
1007
- }
1008
- return totalSize;
1009
- }
1010
- getStorageEvents() {
1011
- return toObservable(this.storageEvents).pipe(filter((event) => event !== null), distinctUntilChanged((prev, curr) => prev.key === curr.key &&
1012
- prev.newValue === curr.newValue &&
1013
- prev.oldValue === curr.oldValue));
1086
+ this.warnLegacyOnce();
1087
+ return this.session.size(options);
1014
1088
  }
1089
+ /** @deprecated Use `storage.local.watch<T>(key, opts)`. Removed in v22. */
1015
1090
  watchLocalStorage(key, options = {}) {
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));
1091
+ this.warnLegacyOnce();
1092
+ return this.local.watch(key, options);
1018
1093
  }
1094
+ /** @deprecated Use `storage.session.watch<T>(key, opts)`. Removed in v22. */
1019
1095
  watchSessionStorage(key, options = {}) {
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));
1096
+ this.warnLegacyOnce();
1097
+ return this.session.watch(key, options);
1022
1098
  }
1023
- // Direct access to native storage APIs
1099
+ /** @deprecated Use `storage.local.native()`. Removed in v22. */
1024
1100
  getNativeLocalStorage() {
1025
- this.ensureSupported();
1026
- return localStorage;
1101
+ this.warnLegacyOnce();
1102
+ return this.local.native();
1027
1103
  }
1104
+ /** @deprecated Use `storage.session.native()`. Removed in v22. */
1028
1105
  getNativeSessionStorage() {
1029
- this.ensureSupported();
1030
- return sessionStorage;
1106
+ this.warnLegacyOnce();
1107
+ return this.session.native();
1108
+ }
1109
+ warnLegacyOnce() {
1110
+ if (legacyDeprecationLogged$1)
1111
+ return;
1112
+ legacyDeprecationLogged$1 = true;
1113
+ this.storageLogger.warn('[storage] WebStorageService.{set,get,remove,clear,watch}{Local,Session}Storage are ' +
1114
+ 'deprecated. Use storage.local and storage.session namespaces. Legacy methods will be ' +
1115
+ 'removed in v22.');
1031
1116
  }
1032
1117
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1033
1118
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebStorageService });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular-helpers/browser-web-apis",
3
- "version": "21.6.0",
3
+ "version": "21.7.0",
4
4
  "description": "Sistema de servicios Angular para acceso formalizado a Browser Web APIs (cámara, permisos, geolocalización, etc.)",
5
5
  "homepage": "https://gaspar1992.github.io/angular-helpers/docs/browser-web-apis",
6
6
  "repository": {
@@ -377,53 +377,105 @@ type ErrorCallback = (error: BrowserError) => void;
377
377
  */
378
378
  type ElementInput = Element | ElementRef<Element> | Signal<Element | ElementRef<Element> | undefined>;
379
379
 
380
+ interface BrowserApiLogger {
381
+ info(message: string): void;
382
+ warn(message: string): void;
383
+ error(message: string, error?: unknown): void;
384
+ }
385
+ declare const BROWSER_API_LOGGER: InjectionToken<BrowserApiLogger>;
386
+
380
387
  interface StorageOptions {
388
+ /** Optional prefix used to namespace keys (e.g. `app:` -> `app:userId`). */
381
389
  prefix?: string;
382
390
  serialize?: (value: StorageValue) => string;
383
391
  deserialize?: (value: string) => StorageValue;
384
392
  }
393
+ type StorageArea = 'localStorage' | 'sessionStorage';
385
394
  interface StorageEvent {
386
395
  key: string | null;
387
396
  newValue: StorageValue | null;
388
397
  oldValue: StorageValue | null;
389
- storageArea: 'localStorage' | 'sessionStorage';
398
+ storageArea: StorageArea;
390
399
  }
400
+ interface StorageNamespace {
401
+ readonly area: StorageArea;
402
+ isSupported(): boolean;
403
+ set<T extends StorageValue>(key: string, value: T, opts?: StorageOptions): boolean;
404
+ get<T extends StorageValue>(key: string, defaultValue?: T | null, opts?: StorageOptions): T | null;
405
+ remove(key: string, opts?: StorageOptions): boolean;
406
+ clear(opts?: StorageOptions): boolean;
407
+ size(opts?: StorageOptions): number;
408
+ watch<T extends StorageValue>(key: string, opts?: StorageOptions): Observable<T | null>;
409
+ /** Direct access to the native Storage object. Throws if unsupported. */
410
+ native(): Storage;
411
+ }
412
+
413
+ /**
414
+ * Web Storage service with two namespaces (`local`, `session`) sharing one method
415
+ * surface. SecurityError-safe (Safari private mode, sandboxed iframes return defaults
416
+ * instead of throwing).
417
+ *
418
+ * Preferred usage:
419
+ * ```ts
420
+ * const storage = inject(WebStorageService);
421
+ * storage.local.set('user', { id: 1 });
422
+ * const user = storage.local.get<{ id: number }>('user');
423
+ * storage.local.watch<{ id: number }>('user').subscribe(console.log);
424
+ * ```
425
+ *
426
+ * Legacy methods (`setLocalStorage`, `getLocalStorage`, etc.) remain as deprecated
427
+ * wrappers for one minor cycle; removal slated for v22.
428
+ */
391
429
  declare class WebStorageService extends BrowserApiBaseService {
430
+ private readonly storageLogger;
392
431
  private storageEvents;
432
+ private readonly eventBus;
433
+ /** Local storage namespace. */
434
+ readonly local: StorageNamespace;
435
+ /** Session storage namespace. */
436
+ readonly session: StorageNamespace;
393
437
  constructor();
394
438
  protected getApiName(): string;
395
439
  protected ensureSupported(): void;
396
- private setupEventListeners;
397
- private serializeValue;
398
- private deserializeValue;
399
- private getKey;
400
- private emitStorageChange;
440
+ /** Returns true if either local or session storage is usable. */
441
+ isSupported(): boolean;
442
+ /** Stream of every storage mutation observed in this tab or other tabs. */
443
+ getStorageEvents(): Observable<StorageEvent>;
444
+ private setupCrossTabListener;
445
+ private safeParse;
446
+ /** @deprecated Use `storage.local.set(key, value, opts)`. Removed in v22. */
401
447
  setLocalStorage<T extends StorageValue>(key: string, value: T, options?: StorageOptions): boolean;
448
+ /** @deprecated Use `storage.local.get(key, defaultValue, opts)`. Removed in v22. */
402
449
  getLocalStorage<T extends StorageValue>(key: string, defaultValue?: T | null, options?: StorageOptions): T | null;
450
+ /** @deprecated Use `storage.local.remove(key, opts)`. Removed in v22. */
403
451
  removeLocalStorage(key: string, options?: StorageOptions): boolean;
452
+ /** @deprecated Use `storage.local.clear(opts)`. Removed in v22. */
404
453
  clearLocalStorage(options?: StorageOptions): boolean;
454
+ /** @deprecated Use `storage.session.set(key, value, opts)`. Removed in v22. */
405
455
  setSessionStorage<T extends StorageValue>(key: string, value: T, options?: StorageOptions): boolean;
456
+ /** @deprecated Use `storage.session.get(key, defaultValue, opts)`. Removed in v22. */
406
457
  getSessionStorage<T extends StorageValue>(key: string, defaultValue?: T | null, options?: StorageOptions): T | null;
458
+ /** @deprecated Use `storage.session.remove(key, opts)`. Removed in v22. */
407
459
  removeSessionStorage(key: string, options?: StorageOptions): boolean;
460
+ /** @deprecated Use `storage.session.clear(opts)`. Removed in v22. */
408
461
  clearSessionStorage(options?: StorageOptions): boolean;
462
+ /** @deprecated Use `storage.local.size(opts)`. Removed in v22. */
409
463
  getLocalStorageSize(options?: StorageOptions): number;
464
+ /** @deprecated Use `storage.session.size(opts)`. Removed in v22. */
410
465
  getSessionStorageSize(options?: StorageOptions): number;
411
- getStorageEvents(): Observable<StorageEvent>;
466
+ /** @deprecated Use `storage.local.watch<T>(key, opts)`. Removed in v22. */
412
467
  watchLocalStorage<T extends StorageValue>(key: string, options?: StorageOptions): Observable<T | null>;
468
+ /** @deprecated Use `storage.session.watch<T>(key, opts)`. Removed in v22. */
413
469
  watchSessionStorage<T extends StorageValue>(key: string, options?: StorageOptions): Observable<T | null>;
470
+ /** @deprecated Use `storage.local.native()`. Removed in v22. */
414
471
  getNativeLocalStorage(): Storage;
472
+ /** @deprecated Use `storage.session.native()`. Removed in v22. */
415
473
  getNativeSessionStorage(): Storage;
474
+ private warnLegacyOnce;
416
475
  static ɵfac: i0.ɵɵFactoryDeclaration<WebStorageService, never>;
417
476
  static ɵprov: i0.ɵɵInjectableDeclaration<WebStorageService>;
418
477
  }
419
478
 
420
- interface BrowserApiLogger {
421
- info(message: string): void;
422
- warn(message: string): void;
423
- error(message: string, error?: unknown): void;
424
- }
425
- declare const BROWSER_API_LOGGER: InjectionToken<BrowserApiLogger>;
426
-
427
479
  /**
428
480
  * Configuration for a single WebSocket connection.
429
481
  */
@@ -1306,4 +1358,4 @@ declare function provideBrowserWebApis(config?: BrowserWebApisConfig): Environme
1306
1358
  declare const version = "0.1.0";
1307
1359
 
1308
1360
  export { BROWSER_API_LOGGER, BatteryService, BroadcastChannelService, BrowserApiBaseService, BrowserCapabilityService, BrowserSupportUtil, CameraService, ClipboardService, ConnectionRegistryBaseService, FileSystemAccessService, FullscreenService, GamepadService, GeolocationService, IntersectionObserverService, MediaDevicesService, MediaRecorderService, MutationObserverService, NetworkInformationService, NotificationService, PageVisibilityService, PerformanceObserverService, PermissionsService, ResizeObserverService, ScreenOrientationService, ScreenWakeLockService, ServerSentEventsService, SpeechSynthesisService, VibrationService, WebAudioService, WebShareService, WebSocketClient, WebSocketService, WebStorageService, WebWorkerService, permissionGuard as createPermissionGuard, defaultBrowserWebApisConfig, injectGamepad, injectIntersectionObserver, injectMutationObserver, injectNetworkInformation, injectPageVisibility, injectPerformanceObserver, injectResizeObserver, injectScreenOrientation, permissionGuard, provideBattery, provideBroadcastChannel, provideBrowserWebApis, provideCamera, provideClipboard, provideCommunicationApis, provideFileSystemAccess, provideFullscreen, provideGamepad, provideGeolocation, provideIntersectionObserver, provideLocationApis, provideMediaApis, provideMediaDevices, provideMediaRecorder, provideMutationObserver, provideNetworkInformation, provideNotifications, providePageVisibility, providePerformanceObserver, providePermissions, provideResizeObserver, provideScreenOrientation, provideScreenWakeLock, provideServerSentEvents, provideSpeechSynthesis, provideStorageApis, provideVibration, provideWebAudio, provideWebShare, provideWebSocket, provideWebStorage, provideWebWorker, version };
1309
- export type { AudioAnalyserData, AudioContextState, BatteryInfo, BatteryManager, BrowserApiLogger, BrowserCapabilityId, BrowserError, BrowserPermissions, BrowserWebApisConfig, CameraCapabilities, CameraInfo, ConnectionType, EffectiveConnectionType, ElementInput, ElementSize, ErrorCallback, EventHandler, FileOpenOptions, FileSaveOptions, GamepadRef, GamepadState, GeolocationCoordinates, GeolocationError, GeolocationOptions, GeolocationPosition$1 as GeolocationPosition, GeolocationWatchOptions, IntersectionObserverOptions, IntersectionRef, MediaDevice, MediaDeviceKind, MediaDevicesInfo, MediaStreamConstraints$1 as MediaStreamConstraints, MediaTrackConstraints$1 as MediaTrackConstraints, MutationObserverOptions, MutationRef, NetworkInformation, NetworkInformationRef, OrientationInfo, OrientationLockType, OrientationType, PageVisibilityRef, PerformanceEntryType, PerformanceObserverConfig, PerformanceObserverRef, PermissionNameExt, PermissionRequest, RecordingOptions, RecordingResult, RecordingState, ResizeObserverOptions, ResizeRef, SSEConfig, SSEConnectionState, SSEMessage, ScreenOrientationRef, SpeechOptions, SpeechState, StorageEvent, StorageOptions, StorageValue, VibrationPattern, VibrationPreset, VisibilityState, WakeLockStatus, WakeLockType, WebSocketClientConfig, WebSocketConfig, WebSocketMessage, WebSocketRequestOptions, WebSocketState, WebSocketStatus, WebSocketStatusV2, WorkerMessage, WorkerStatus, WorkerTask };
1361
+ export type { AudioAnalyserData, AudioContextState, BatteryInfo, BatteryManager, BrowserApiLogger, BrowserCapabilityId, BrowserError, BrowserPermissions, BrowserWebApisConfig, CameraCapabilities, CameraInfo, ConnectionType, EffectiveConnectionType, ElementInput, ElementSize, ErrorCallback, EventHandler, FileOpenOptions, FileSaveOptions, GamepadRef, GamepadState, GeolocationCoordinates, GeolocationError, GeolocationOptions, GeolocationPosition$1 as GeolocationPosition, GeolocationWatchOptions, IntersectionObserverOptions, IntersectionRef, MediaDevice, MediaDeviceKind, MediaDevicesInfo, MediaStreamConstraints$1 as MediaStreamConstraints, MediaTrackConstraints$1 as MediaTrackConstraints, MutationObserverOptions, MutationRef, NetworkInformation, NetworkInformationRef, OrientationInfo, OrientationLockType, OrientationType, PageVisibilityRef, PerformanceEntryType, PerformanceObserverConfig, PerformanceObserverRef, PermissionNameExt, PermissionRequest, RecordingOptions, RecordingResult, RecordingState, ResizeObserverOptions, ResizeRef, SSEConfig, SSEConnectionState, SSEMessage, ScreenOrientationRef, SpeechOptions, SpeechState, StorageEvent, StorageNamespace, StorageOptions, StorageValue, VibrationPattern, VibrationPreset, VisibilityState, WakeLockStatus, WakeLockType, WebSocketClientConfig, WebSocketConfig, WebSocketMessage, WebSocketRequestOptions, WebSocketState, WebSocketStatus, WebSocketStatusV2, WorkerMessage, WorkerStatus, WorkerTask };