@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 {
|
|
6
|
-
import { filter,
|
|
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
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
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
|
-
|
|
820
|
-
if (
|
|
821
|
-
return
|
|
822
|
-
if (
|
|
823
|
-
|
|
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
|
-
|
|
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
|
-
|
|
813
|
+
this.warnSecurityOnce();
|
|
814
|
+
this.supportedCache = false;
|
|
830
815
|
}
|
|
816
|
+
return this.supportedCache;
|
|
831
817
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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
|
|
844
|
-
const
|
|
845
|
-
const
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
this.
|
|
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.
|
|
831
|
+
this.logger.error(`[storage:${this.area}] set("${key}") failed`, error);
|
|
853
832
|
return false;
|
|
854
833
|
}
|
|
855
834
|
}
|
|
856
|
-
|
|
857
|
-
this.
|
|
835
|
+
get(key, defaultValue = null, opts = {}) {
|
|
836
|
+
if (!this.isSupported())
|
|
837
|
+
return defaultValue;
|
|
858
838
|
try {
|
|
859
|
-
const fullKey = this.getKey(key,
|
|
860
|
-
const
|
|
861
|
-
return
|
|
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.
|
|
844
|
+
this.logger.error(`[storage:${this.area}] get("${key}") failed`, error);
|
|
865
845
|
return defaultValue;
|
|
866
846
|
}
|
|
867
847
|
}
|
|
868
|
-
|
|
869
|
-
this.
|
|
848
|
+
remove(key, opts = {}) {
|
|
849
|
+
if (!this.isSupported())
|
|
850
|
+
return false;
|
|
870
851
|
try {
|
|
871
|
-
const fullKey = this.getKey(key,
|
|
872
|
-
const oldRaw =
|
|
873
|
-
const oldValue = oldRaw !== null ? this.deserializeValue(oldRaw,
|
|
874
|
-
|
|
875
|
-
this.
|
|
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.
|
|
860
|
+
this.logger.error(`[storage:${this.area}] remove("${key}") failed`, error);
|
|
880
861
|
return false;
|
|
881
862
|
}
|
|
882
863
|
}
|
|
883
|
-
|
|
884
|
-
this.
|
|
864
|
+
clear(opts = {}) {
|
|
865
|
+
if (!this.isSupported())
|
|
866
|
+
return false;
|
|
885
867
|
try {
|
|
886
|
-
const prefix =
|
|
868
|
+
const prefix = opts?.prefix;
|
|
869
|
+
const store = this.getStore();
|
|
887
870
|
if (prefix) {
|
|
888
|
-
const
|
|
889
|
-
for (let i = 0; i <
|
|
890
|
-
const
|
|
891
|
-
if (
|
|
892
|
-
|
|
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
|
-
|
|
903
|
-
this.
|
|
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.
|
|
895
|
+
this.logger.error(`[storage:${this.area}] clear() failed`, error);
|
|
909
896
|
return false;
|
|
910
897
|
}
|
|
911
898
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
899
|
+
size(opts = {}) {
|
|
900
|
+
if (!this.isSupported())
|
|
901
|
+
return 0;
|
|
915
902
|
try {
|
|
916
|
-
const
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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.
|
|
926
|
-
return
|
|
915
|
+
this.logger.error(`[storage:${this.area}] size() failed`, error);
|
|
916
|
+
return 0;
|
|
927
917
|
}
|
|
928
918
|
}
|
|
929
|
-
|
|
930
|
-
this.
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
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
|
-
|
|
942
|
-
this.
|
|
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
|
-
|
|
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
|
|
952
|
-
|
|
953
|
-
return false;
|
|
947
|
+
catch {
|
|
948
|
+
return value;
|
|
954
949
|
}
|
|
955
950
|
}
|
|
956
|
-
|
|
957
|
-
|
|
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
|
-
|
|
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
|
|
981
|
-
|
|
982
|
-
return false;
|
|
1034
|
+
catch {
|
|
1035
|
+
return value;
|
|
983
1036
|
}
|
|
984
1037
|
}
|
|
985
|
-
//
|
|
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.
|
|
988
|
-
|
|
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.
|
|
1000
|
-
|
|
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
|
-
|
|
1017
|
-
return this.
|
|
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
|
-
|
|
1021
|
-
return this.
|
|
1096
|
+
this.warnLegacyOnce();
|
|
1097
|
+
return this.session.watch(key, options);
|
|
1022
1098
|
}
|
|
1023
|
-
|
|
1099
|
+
/** @deprecated Use `storage.local.native()`. Removed in v22. */
|
|
1024
1100
|
getNativeLocalStorage() {
|
|
1025
|
-
this.
|
|
1026
|
-
return
|
|
1101
|
+
this.warnLegacyOnce();
|
|
1102
|
+
return this.local.native();
|
|
1027
1103
|
}
|
|
1104
|
+
/** @deprecated Use `storage.session.native()`. Removed in v22. */
|
|
1028
1105
|
getNativeSessionStorage() {
|
|
1029
|
-
this.
|
|
1030
|
-
return
|
|
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.
|
|
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:
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
private
|
|
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
|
-
|
|
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 };
|