reslib 1.0.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.
- package/README.md +298 -0
- package/build/auth/index.d.ts +2034 -0
- package/build/auth/index.js +5 -0
- package/build/auth/types.d.ts +465 -0
- package/build/auth/types.js +1 -0
- package/build/countries/countries.d.ts +1454 -0
- package/build/countries/countries.js +1 -0
- package/build/countries/index.d.ts +159 -0
- package/build/countries/index.js +5 -0
- package/build/countries/types.d.ts +65 -0
- package/build/countries/types.js +1 -0
- package/build/currency/currencies.d.ts +8 -0
- package/build/currency/currencies.js +1 -0
- package/build/currency/index.d.ts +51 -0
- package/build/currency/index.js +5 -0
- package/build/currency/session.d.ts +23 -0
- package/build/currency/session.js +5 -0
- package/build/currency/types.d.ts +1039 -0
- package/build/currency/types.js +1 -0
- package/build/currency/utils.d.ts +25 -0
- package/build/currency/utils.js +1 -0
- package/build/i18n/index.d.ts +640 -0
- package/build/i18n/index.js +5 -0
- package/build/inputFormatter/index.d.ts +396 -0
- package/build/inputFormatter/index.js +5 -0
- package/build/inputFormatter/types.d.ts +544 -0
- package/build/inputFormatter/types.js +1 -0
- package/build/logger/index.d.ts +235 -0
- package/build/logger/index.js +5 -0
- package/build/observable/index.d.ts +329 -0
- package/build/observable/index.js +1 -0
- package/build/platform/index.d.ts +32 -0
- package/build/platform/index.js +1 -0
- package/build/resources/ResourcePaginationHelper.d.ts +537 -0
- package/build/resources/ResourcePaginationHelper.js +2 -0
- package/build/resources/decorators/create.decorator.d.ts +20 -0
- package/build/resources/decorators/create.decorator.js +1 -0
- package/build/resources/decorators/index.d.ts +41 -0
- package/build/resources/decorators/index.js +1 -0
- package/build/resources/fields/index.d.ts +33 -0
- package/build/resources/fields/index.js +1 -0
- package/build/resources/filters.d.ts +62 -0
- package/build/resources/filters.js +1 -0
- package/build/resources/index.d.ts +854 -0
- package/build/resources/index.js +6 -0
- package/build/resources/types/filters.d.ts +508 -0
- package/build/resources/types/filters.js +1 -0
- package/build/resources/types/index.d.ts +4138 -0
- package/build/resources/types/index.js +1 -0
- package/build/session/index.d.ts +1474 -0
- package/build/session/index.js +1 -0
- package/build/translations/auth.en.d.ts +3 -0
- package/build/translations/auth.en.js +1 -0
- package/build/translations/countries.en.d.ts +6 -0
- package/build/translations/countries.en.js +1 -0
- package/build/translations/currencies.en.d.ts +5 -0
- package/build/translations/currencies.en.js +1 -0
- package/build/translations/date.en.d.ts +19 -0
- package/build/translations/date.en.js +1 -0
- package/build/translations/index.d.ts +1583 -0
- package/build/translations/index.js +5 -0
- package/build/translations/resources.en.d.ts +6 -0
- package/build/translations/resources.en.js +1 -0
- package/build/translations/validator.en.d.ts +104 -0
- package/build/translations/validator.en.js +5 -0
- package/build/types/date.d.ts +44 -0
- package/build/types/date.js +1 -0
- package/build/types/dictionary.d.ts +29 -0
- package/build/types/dictionary.js +1 -0
- package/build/types/i18n.d.ts +121 -0
- package/build/types/i18n.js +1 -0
- package/build/types/index.d.ts +145 -0
- package/build/types/index.js +1 -0
- package/build/utils/areEquals.d.ts +19 -0
- package/build/utils/areEquals.js +1 -0
- package/build/utils/date/dateHelper.d.ts +371 -0
- package/build/utils/date/dateHelper.js +5 -0
- package/build/utils/date/index.d.ts +212 -0
- package/build/utils/date/index.js +5 -0
- package/build/utils/date/isDateObj.d.ts +14 -0
- package/build/utils/date/isDateObj.js +1 -0
- package/build/utils/debounce.d.ts +52 -0
- package/build/utils/debounce.js +1 -0
- package/build/utils/defaultArray.d.ts +18 -0
- package/build/utils/defaultArray.js +1 -0
- package/build/utils/defaultBool.d.ts +14 -0
- package/build/utils/defaultBool.js +1 -0
- package/build/utils/defaultStr.d.ts +17 -0
- package/build/utils/defaultStr.js +1 -0
- package/build/utils/defaultVal.d.ts +18 -0
- package/build/utils/defaultVal.js +1 -0
- package/build/utils/dom/index.d.ts +65 -0
- package/build/utils/dom/index.js +1 -0
- package/build/utils/dom/isDOMElement.d.ts +11 -0
- package/build/utils/dom/isDOMElement.js +1 -0
- package/build/utils/file/index.d.ts +26 -0
- package/build/utils/file/index.js +1 -0
- package/build/utils/global.d.ts +53 -0
- package/build/utils/global.js +1 -0
- package/build/utils/image.d.ts +56 -0
- package/build/utils/image.js +1 -0
- package/build/utils/index.d.ts +39 -0
- package/build/utils/index.js +6 -0
- package/build/utils/interpolate.d.ts +105 -0
- package/build/utils/interpolate.js +1 -0
- package/build/utils/isEmail.d.ts +57 -0
- package/build/utils/isEmail.js +1 -0
- package/build/utils/isEmpty.d.ts +18 -0
- package/build/utils/isEmpty.js +1 -0
- package/build/utils/isNonNullString.d.ts +17 -0
- package/build/utils/isNonNullString.js +1 -0
- package/build/utils/isNullable.d.ts +7 -0
- package/build/utils/isNullable.js +1 -0
- package/build/utils/isNumber.d.ts +36 -0
- package/build/utils/isNumber.js +1 -0
- package/build/utils/isPrimitive.d.ts +16 -0
- package/build/utils/isPrimitive.js +1 -0
- package/build/utils/isPromise.d.ts +14 -0
- package/build/utils/isPromise.js +1 -0
- package/build/utils/isRegex.d.ts +15 -0
- package/build/utils/isRegex.js +1 -0
- package/build/utils/isTime.d.ts +18 -0
- package/build/utils/isTime.js +1 -0
- package/build/utils/json.d.ts +224 -0
- package/build/utils/json.js +1 -0
- package/build/utils/numbers.d.ts +148 -0
- package/build/utils/numbers.js +5 -0
- package/build/utils/object.d.ts +567 -0
- package/build/utils/object.js +1 -0
- package/build/utils/sort.d.ts +67 -0
- package/build/utils/sort.js +1 -0
- package/build/utils/string.d.ts +165 -0
- package/build/utils/string.js +1 -0
- package/build/utils/stringify.d.ts +23 -0
- package/build/utils/stringify.js +1 -0
- package/build/utils/uniqid.d.ts +18 -0
- package/build/utils/uniqid.js +1 -0
- package/build/utils/uri/index.d.ts +333 -0
- package/build/utils/uri/index.js +2 -0
- package/build/validator/index.d.ts +4 -0
- package/build/validator/index.js +6 -0
- package/build/validator/rules/array.d.ts +848 -0
- package/build/validator/rules/array.js +5 -0
- package/build/validator/rules/boolean.d.ts +87 -0
- package/build/validator/rules/boolean.js +5 -0
- package/build/validator/rules/date.d.ts +551 -0
- package/build/validator/rules/date.js +5 -0
- package/build/validator/rules/default.d.ts +367 -0
- package/build/validator/rules/default.js +5 -0
- package/build/validator/rules/enum.d.ts +155 -0
- package/build/validator/rules/enum.js +5 -0
- package/build/validator/rules/file.d.ts +356 -0
- package/build/validator/rules/file.js +5 -0
- package/build/validator/rules/format.d.ts +2825 -0
- package/build/validator/rules/format.js +6 -0
- package/build/validator/rules/index.d.ts +16 -0
- package/build/validator/rules/index.js +6 -0
- package/build/validator/rules/multiRules.d.ts +475 -0
- package/build/validator/rules/multiRules.js +5 -0
- package/build/validator/rules/numeric.d.ts +1135 -0
- package/build/validator/rules/numeric.js +5 -0
- package/build/validator/rules/string.d.ts +504 -0
- package/build/validator/rules/string.js +5 -0
- package/build/validator/rules/target.d.ts +137 -0
- package/build/validator/rules/target.js +5 -0
- package/build/validator/rules/utils.d.ts +1 -0
- package/build/validator/rules/utils.js +1 -0
- package/build/validator/rulesMarkers.d.ts +11 -0
- package/build/validator/rulesMarkers.js +1 -0
- package/build/validator/types.d.ts +2906 -0
- package/build/validator/types.js +1 -0
- package/build/validator/validator.d.ts +3692 -0
- package/build/validator/validator.js +5 -0
- package/lib/cjs/auth.js +1 -0
- package/lib/cjs/countries.js +1 -0
- package/lib/cjs/currency.js +1 -0
- package/lib/cjs/i18n.js +1 -0
- package/lib/cjs/inputFormatter.js +1 -0
- package/lib/cjs/logger.js +1 -0
- package/lib/cjs/observable.js +1 -0
- package/lib/cjs/platform.js +1 -0
- package/lib/cjs/resources.js +1 -0
- package/lib/cjs/session.js +1 -0
- package/lib/cjs/types.js +1 -0
- package/lib/cjs/utils.js +1 -0
- package/lib/cjs/validator.js +1 -0
- package/lib/esm/auth.mjs +1 -0
- package/lib/esm/countries.mjs +1 -0
- package/lib/esm/currency.mjs +1 -0
- package/lib/esm/i18n.mjs +1 -0
- package/lib/esm/inputFormatter.mjs +1 -0
- package/lib/esm/logger.mjs +1 -0
- package/lib/esm/observable.mjs +1 -0
- package/lib/esm/platform.mjs +1 -0
- package/lib/esm/resources.mjs +1 -0
- package/lib/esm/session.mjs +1 -0
- package/lib/esm/types.mjs +1 -0
- package/lib/esm/utils.mjs +1 -0
- package/lib/esm/validator.mjs +1 -0
- package/package.json +244 -0
|
@@ -0,0 +1,1474 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ClassConstructor } from '../types/index';
|
|
3
|
+
declare class Manager {
|
|
4
|
+
static readonly sessionStorageMetaData: unique symbol;
|
|
5
|
+
/**
|
|
6
|
+
* The storage object used by the session manager.
|
|
7
|
+
*
|
|
8
|
+
* This property is initialized lazily when the `storage` getter is called.
|
|
9
|
+
*/
|
|
10
|
+
private static _storage;
|
|
11
|
+
/**
|
|
12
|
+
* The namespace prefix to use for all keys in the session storage.
|
|
13
|
+
*
|
|
14
|
+
* This property is optional and can be set using the `keyNamespace` setter.
|
|
15
|
+
*/
|
|
16
|
+
private static _keyNamespace?;
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves or initializes the session storage implementation used by the Manager.
|
|
19
|
+
*
|
|
20
|
+
* This getter implements a sophisticated storage initialization strategy with multiple fallback mechanisms
|
|
21
|
+
* to ensure reliable session storage across different environments and platforms. The method follows
|
|
22
|
+
* a priority-based approach to determine the most suitable storage implementation available.
|
|
23
|
+
*
|
|
24
|
+
* ### Storage Priority Order:
|
|
25
|
+
* 1. **Custom Attached Storage** - Storage registered via {@link AttachSessionStorage} decorator
|
|
26
|
+
* 2. **Browser localStorage** - Native browser localStorage (client-side only)
|
|
27
|
+
* 3. **In-Memory Storage** - Fallback dictionary-based storage (server-side or fallback)
|
|
28
|
+
*
|
|
29
|
+
* ### Initialization Process:
|
|
30
|
+
* The getter performs the following steps in order:
|
|
31
|
+
* 1. Checks for custom storage registered via reflection metadata
|
|
32
|
+
* 2. Validates the custom storage using {@link isValidStorage}
|
|
33
|
+
* 3. Falls back to browser localStorage if available and valid
|
|
34
|
+
* 4. Creates in-memory storage as the final fallback option
|
|
35
|
+
* 5. Caches the initialized storage for subsequent calls
|
|
36
|
+
*
|
|
37
|
+
* ### Platform Detection:
|
|
38
|
+
* Uses {@link Platform.isClientSide} to detect the execution environment and choose
|
|
39
|
+
* appropriate storage mechanisms. This ensures compatibility across:
|
|
40
|
+
* - **Browser environments** - Uses localStorage when available
|
|
41
|
+
* - **Server-side rendering** - Uses in-memory storage
|
|
42
|
+
* - **Node.js environments** - Uses in-memory storage
|
|
43
|
+
* - **React Native** - Can use custom storage implementations
|
|
44
|
+
*
|
|
45
|
+
* @returns The active session storage implementation conforming to {@link SessionStorage}
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Basic usage - get the current storage
|
|
50
|
+
* const storage = Manager.storage;
|
|
51
|
+
*
|
|
52
|
+
* // Use storage directly
|
|
53
|
+
* storage.set('userToken', 'abc123');
|
|
54
|
+
* const token = storage.get('userToken');
|
|
55
|
+
* storage.remove('userToken');
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* // Storage will automatically use localStorage in browser
|
|
61
|
+
* if (Platform.isClientSide()) {
|
|
62
|
+
* const storage = Manager.storage; // Uses localStorage
|
|
63
|
+
* storage.set('preferences', { theme: 'dark', lang: 'en' });
|
|
64
|
+
*
|
|
65
|
+
* // Data persists across page reloads
|
|
66
|
+
* window.location.reload();
|
|
67
|
+
* const prefs = storage.get('preferences'); // Still available
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* // Server-side usage automatically uses in-memory storage
|
|
74
|
+
* import { Manager } from 'reslib/session';
|
|
75
|
+
*
|
|
76
|
+
* // In Node.js environment
|
|
77
|
+
* const storage = Manager.storage; // Uses in-memory storage
|
|
78
|
+
* storage.set('sessionData', { userId: 123 });
|
|
79
|
+
*
|
|
80
|
+
* // Data only persists during application lifetime
|
|
81
|
+
* const data = storage.get('sessionData');
|
|
82
|
+
* console.log(data); // { userId: 123 }
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* // Custom storage integration example
|
|
88
|
+
* @AttachSessionStorage()
|
|
89
|
+
* class RedisStorage implements SessionStorage {
|
|
90
|
+
* constructor(private redis: RedisClient) {}
|
|
91
|
+
*
|
|
92
|
+
* get(key: string): any {
|
|
93
|
+
* return this.redis.get(key);
|
|
94
|
+
* }
|
|
95
|
+
*
|
|
96
|
+
* set(key: string, value: any): any {
|
|
97
|
+
* this.redis.set(key, JSON.stringify(value));
|
|
98
|
+
* return value;
|
|
99
|
+
* }
|
|
100
|
+
*
|
|
101
|
+
* remove(key: string): any {
|
|
102
|
+
* const value = this.get(key);
|
|
103
|
+
* this.redis.del(key);
|
|
104
|
+
* return value;
|
|
105
|
+
* }
|
|
106
|
+
*
|
|
107
|
+
* removeAll(): any {
|
|
108
|
+
* this.redis.flushall();
|
|
109
|
+
* }
|
|
110
|
+
* }
|
|
111
|
+
*
|
|
112
|
+
* // Now Manager.storage will use Redis
|
|
113
|
+
* const storage = Manager.storage; // Returns RedisStorage instance
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* // Storage with error handling
|
|
119
|
+
* try {
|
|
120
|
+
* const storage = Manager.storage;
|
|
121
|
+
*
|
|
122
|
+
* // Attempt to store large object
|
|
123
|
+
* const largeData = new Array(1000000).fill('data');
|
|
124
|
+
* storage.set('largeObject', largeData);
|
|
125
|
+
*
|
|
126
|
+
* } catch (error) {
|
|
127
|
+
* console.error('Storage quota exceeded:', error);
|
|
128
|
+
*
|
|
129
|
+
* // Fallback to essential data only
|
|
130
|
+
* const storage = Manager.storage;
|
|
131
|
+
* storage.set('essentialData', { id: 1 });
|
|
132
|
+
* }
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* // Checking storage capabilities
|
|
138
|
+
* const storage = Manager.storage;
|
|
139
|
+
*
|
|
140
|
+
* // Test if storage is persistent (localStorage) or temporary (in-memory)
|
|
141
|
+
* storage.set('test', 'value');
|
|
142
|
+
*
|
|
143
|
+
* if (Platform.isClientSide() && window.localStorage) {
|
|
144
|
+
* console.log('Using persistent localStorage');
|
|
145
|
+
* // Data will survive page reloads
|
|
146
|
+
* } else {
|
|
147
|
+
* console.log('Using temporary in-memory storage');
|
|
148
|
+
* // Data will be lost on page reload
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* // Advanced: Storage introspection
|
|
155
|
+
* const storage = Manager.storage;
|
|
156
|
+
*
|
|
157
|
+
* // Check which storage implementation is active
|
|
158
|
+
* if ('localStorage' in storage.get.toString()) {
|
|
159
|
+
* console.log('Using browser localStorage');
|
|
160
|
+
* } else if ('InMemoryStorage' in storage.get.toString()) {
|
|
161
|
+
* console.log('Using in-memory storage');
|
|
162
|
+
* } else {
|
|
163
|
+
* console.log('Using custom storage implementation');
|
|
164
|
+
* }
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @see {@link SessionStorage} - Interface that all storage implementations must follow
|
|
168
|
+
* @see {@link AttachSessionStorage} - Decorator for registering custom storage implementations
|
|
169
|
+
* @see {@link isValidStorage} - Function used to validate storage implementations
|
|
170
|
+
* @see {@link Platform.isClientSide} - Platform detection utility
|
|
171
|
+
* @see {@link JsonHelper} - JSON serialization utilities used by storage
|
|
172
|
+
*
|
|
173
|
+
*
|
|
174
|
+
* @public
|
|
175
|
+
*
|
|
176
|
+
* @remarks
|
|
177
|
+
* **Important Behavior Notes:**
|
|
178
|
+
* - The storage is initialized lazily on first access for optimal performance
|
|
179
|
+
* - Once initialized, the same storage instance is reused for all subsequent calls
|
|
180
|
+
* - Custom storage registered via {@link AttachSessionStorage} takes highest priority
|
|
181
|
+
* - Browser localStorage is only used in client-side environments with proper window object
|
|
182
|
+
* - In-memory storage is automatically garbage collected when the application ends
|
|
183
|
+
*
|
|
184
|
+
* **Browser Compatibility:**
|
|
185
|
+
* - Checks for `window.localStorage` availability before attempting to use it
|
|
186
|
+
* - Gracefully degrades to in-memory storage if localStorage is disabled/unavailable
|
|
187
|
+
* - Works in all major browsers including legacy versions
|
|
188
|
+
* - Supports private/incognito browsing modes that may disable localStorage
|
|
189
|
+
*
|
|
190
|
+
* **Performance Characteristics:**
|
|
191
|
+
* - **localStorage**: Synchronous, persistent, ~5-10MB limit per origin
|
|
192
|
+
* - **In-memory**: Synchronous, non-persistent, limited by available RAM
|
|
193
|
+
* - **Custom storage**: Performance depends on implementation (can be async)
|
|
194
|
+
*
|
|
195
|
+
* **Security Considerations:**
|
|
196
|
+
* - localStorage data is accessible to all scripts on the same origin
|
|
197
|
+
* - In-memory storage is more secure as it's not persistent
|
|
198
|
+
* - Custom storage implementations should implement appropriate security measures
|
|
199
|
+
* - Consider encryption for sensitive data regardless of storage type
|
|
200
|
+
*
|
|
201
|
+
* **Thread Safety:**
|
|
202
|
+
* - localStorage operations are synchronous and atomic
|
|
203
|
+
* - In-memory storage access is synchronous but not thread-safe across workers
|
|
204
|
+
* - Custom storage implementations should handle concurrency appropriately
|
|
205
|
+
*/
|
|
206
|
+
static get storage(): SessionStorage;
|
|
207
|
+
/**
|
|
208
|
+
* Sets the storage object used by the session manager.
|
|
209
|
+
*
|
|
210
|
+
* The provided storage object must be valid and have the required methods.
|
|
211
|
+
*
|
|
212
|
+
* @param {SessionStorage} storage - The storage object to use.
|
|
213
|
+
*/
|
|
214
|
+
static set storage(storage: SessionStorage);
|
|
215
|
+
/**
|
|
216
|
+
* Gets the current namespace prefix used for all session storage keys.
|
|
217
|
+
*
|
|
218
|
+
* This getter provides access to the global namespace prefix that is automatically prepended
|
|
219
|
+
* to all session storage keys when they are sanitized. Namespacing is essential for creating
|
|
220
|
+
* isolated storage contexts in applications that share the same storage backend, preventing
|
|
221
|
+
* key collisions between different modules, features, or application instances.
|
|
222
|
+
*
|
|
223
|
+
* ### Key Benefits:
|
|
224
|
+
* - **Isolation**: Prevents key conflicts between different application parts
|
|
225
|
+
* - **Organization**: Groups related storage keys under a common prefix
|
|
226
|
+
* - **Multi-tenancy**: Enables separate storage contexts for different users/tenants
|
|
227
|
+
* - **Environment Separation**: Allows different prefixes for dev/staging/production
|
|
228
|
+
*
|
|
229
|
+
* ### Namespace Format:
|
|
230
|
+
* The namespace is returned as a clean string without any trailing separators.
|
|
231
|
+
* The system automatically adds the `-` separator when constructing the final key.
|
|
232
|
+
*
|
|
233
|
+
* @returns The current namespace prefix as a string, or empty string if no namespace is set
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```typescript
|
|
237
|
+
* // Get the current namespace
|
|
238
|
+
* const namespace = Manager.keyNamespace;
|
|
239
|
+
* console.log(namespace); // "" (empty by default)
|
|
240
|
+
*
|
|
241
|
+
* // Set a namespace first
|
|
242
|
+
* Manager.keyNamespace = "myapp";
|
|
243
|
+
* console.log(Manager.keyNamespace); // "myapp"
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* // Multi-tenant application example
|
|
249
|
+
* class TenantSessionManager {
|
|
250
|
+
* static setTenant(tenantId: string) {
|
|
251
|
+
* Manager.keyNamespace = `tenant-${tenantId}`;
|
|
252
|
+
* }
|
|
253
|
+
*
|
|
254
|
+
* static getCurrentTenant(): string {
|
|
255
|
+
* const namespace = Manager.keyNamespace;
|
|
256
|
+
* return namespace.replace('tenant-', '');
|
|
257
|
+
* }
|
|
258
|
+
* }
|
|
259
|
+
*
|
|
260
|
+
* // Usage
|
|
261
|
+
* TenantSessionManager.setTenant('acme-corp');
|
|
262
|
+
* console.log(Manager.keyNamespace); // "tenant-acme-corp"
|
|
263
|
+
*
|
|
264
|
+
* // All session operations now use this namespace
|
|
265
|
+
* Session.set('userPrefs', { theme: 'dark' });
|
|
266
|
+
* // Stored as key: "tenant-acme-corp-userPrefs"
|
|
267
|
+
* ```
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* // Environment-based namespacing
|
|
272
|
+
* const environment = process.env.NODE_ENV || 'development';
|
|
273
|
+
* const appVersion = process.env.APP_VERSION || '1.0.0';
|
|
274
|
+
*
|
|
275
|
+
* Manager.keyNamespace = `${environment}-v${appVersion}`;
|
|
276
|
+
* console.log(Manager.keyNamespace); // "production-v2.1.0"
|
|
277
|
+
*
|
|
278
|
+
* // All storage operations are now environment-scoped
|
|
279
|
+
* Session.set('cache', data); // Stored as "production-v2.1.0-cache"
|
|
280
|
+
* ```
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```typescript
|
|
284
|
+
* // Dynamic namespace checking
|
|
285
|
+
* function ensureNamespace(requiredNamespace: string) {
|
|
286
|
+
* const current = Manager.keyNamespace;
|
|
287
|
+
* if (current !== requiredNamespace) {
|
|
288
|
+
* throw new Error(`Expected namespace '${requiredNamespace}', got '${current}'`);
|
|
289
|
+
* }
|
|
290
|
+
* }
|
|
291
|
+
*
|
|
292
|
+
* // Usage in critical operations
|
|
293
|
+
* Manager.keyNamespace = "secure-context";
|
|
294
|
+
* ensureNamespace("secure-context"); // Passes
|
|
295
|
+
* Session.set('sensitiveData', encryptedData);
|
|
296
|
+
* ```
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```typescript
|
|
300
|
+
* // Namespace validation and formatting
|
|
301
|
+
* function validateNamespace(): boolean {
|
|
302
|
+
* const namespace = Manager.keyNamespace;
|
|
303
|
+
*
|
|
304
|
+
* // Check for valid format
|
|
305
|
+
* const isValidFormat = /^[a-z0-9-]+$/.test(namespace);
|
|
306
|
+
* const hasNoConsecutiveDashes = !namespace.includes('--');
|
|
307
|
+
* const doesNotStartOrEndWithDash = !namespace.startsWith('-') && !namespace.endsWith('-');
|
|
308
|
+
*
|
|
309
|
+
* return isValidFormat && hasNoConsecutiveDashes && doesNotStartOrEndWithDash;
|
|
310
|
+
* }
|
|
311
|
+
*
|
|
312
|
+
* Manager.keyNamespace = "my-app-v1";
|
|
313
|
+
* console.log(validateNamespace()); // true
|
|
314
|
+
*
|
|
315
|
+
* Manager.keyNamespace = "invalid--namespace-";
|
|
316
|
+
* console.log(validateNamespace()); // false
|
|
317
|
+
* ```
|
|
318
|
+
*
|
|
319
|
+
* @see {@link keyNamespace} (setter) - Method to set the namespace prefix
|
|
320
|
+
* @see {@link sanitizeKey} - Method that applies the namespace to keys
|
|
321
|
+
* @see {@link Session.set} - Session storage method that uses namespaced keys
|
|
322
|
+
* @see {@link Session.get} - Session retrieval method that uses namespaced keys
|
|
323
|
+
*
|
|
324
|
+
*
|
|
325
|
+
* @public
|
|
326
|
+
*
|
|
327
|
+
* @remarks
|
|
328
|
+
* **Important Considerations:**
|
|
329
|
+
* - The namespace affects ALL subsequent session operations
|
|
330
|
+
* - Changing the namespace will effectively "switch" to a different storage context
|
|
331
|
+
* - Keys stored with one namespace won't be accessible with a different namespace
|
|
332
|
+
* - The namespace is stored globally and persists until explicitly changed
|
|
333
|
+
* - An empty namespace means no prefixing is applied to keys
|
|
334
|
+
*
|
|
335
|
+
* **Best Practices:**
|
|
336
|
+
* - Set the namespace early in your application lifecycle
|
|
337
|
+
* - Use consistent naming conventions (lowercase, hyphens for separation)
|
|
338
|
+
* - Consider including version numbers for schema evolution
|
|
339
|
+
* - Avoid changing namespaces frequently during application runtime
|
|
340
|
+
* - Document your namespace strategy for team members
|
|
341
|
+
*
|
|
342
|
+
* **Thread Safety:**
|
|
343
|
+
* - Reading the namespace is thread-safe
|
|
344
|
+
* - The value is cached until explicitly changed
|
|
345
|
+
* - Multiple reads return the same value consistently
|
|
346
|
+
*/
|
|
347
|
+
static get keyNamespace(): string;
|
|
348
|
+
/**
|
|
349
|
+
* Sets the namespace prefix that will be prepended to all session storage keys.
|
|
350
|
+
*
|
|
351
|
+
* This setter allows you to configure a global namespace prefix that will be automatically
|
|
352
|
+
* applied to all session storage keys when they are sanitized. Setting a namespace is crucial
|
|
353
|
+
* for applications that need to maintain separate storage contexts, avoid key collisions,
|
|
354
|
+
* or implement multi-tenant architectures where different users or application instances
|
|
355
|
+
* should have isolated storage spaces.
|
|
356
|
+
*
|
|
357
|
+
* ### Namespace Validation:
|
|
358
|
+
* The setter validates the input using {@link isNonNullString} to ensure only valid,
|
|
359
|
+
* non-empty strings are accepted. Invalid inputs (null, undefined, empty strings) are
|
|
360
|
+
* silently ignored, preserving the current namespace value.
|
|
361
|
+
*
|
|
362
|
+
* ### Impact on Storage Operations:
|
|
363
|
+
* Once a namespace is set, ALL subsequent session storage operations will use the
|
|
364
|
+
* namespaced keys. This includes {@link Session.get}, {@link Session.set},
|
|
365
|
+
* {@link Session.remove}, and all other storage methods.
|
|
366
|
+
*
|
|
367
|
+
* @param prefix - The namespace prefix to use for all storage keys
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* ```typescript
|
|
371
|
+
* // Basic namespace setup
|
|
372
|
+
* Manager.keyNamespace = "myapp";
|
|
373
|
+
*
|
|
374
|
+
* // Now all storage operations use the namespace
|
|
375
|
+
* Session.set('user', { id: 1, name: 'John' });
|
|
376
|
+
* // Stored with key: "myapp-user"
|
|
377
|
+
*
|
|
378
|
+
* const user = Session.get('user');
|
|
379
|
+
* // Retrieves using key: "myapp-user"
|
|
380
|
+
* ```
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```typescript
|
|
384
|
+
* // Multi-environment configuration
|
|
385
|
+
* const environment = process.env.NODE_ENV;
|
|
386
|
+
* const version = process.env.APP_VERSION;
|
|
387
|
+
*
|
|
388
|
+
* // Set environment-specific namespace
|
|
389
|
+
* Manager.keyNamespace = `${environment}-${version}`;
|
|
390
|
+
*
|
|
391
|
+
* // Examples:
|
|
392
|
+
* // Development: "development-1.0.0"
|
|
393
|
+
* // Production: "production-2.1.5"
|
|
394
|
+
* // Testing: "testing-1.2.0-beta"
|
|
395
|
+
*
|
|
396
|
+
* Session.set('config', appConfig);
|
|
397
|
+
* // Stored as "production-2.1.5-config"
|
|
398
|
+
* ```
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* // User-specific namespacing for multi-tenant apps
|
|
403
|
+
* class UserSessionManager {
|
|
404
|
+
* static loginUser(userId: string, organizationId: string) {
|
|
405
|
+
* // Create unique namespace for this user context
|
|
406
|
+
* Manager.keyNamespace = `org-${organizationId}-user-${userId}`;
|
|
407
|
+
*
|
|
408
|
+
* // All subsequent storage is user-specific
|
|
409
|
+
* Session.set('preferences', userPreferences);
|
|
410
|
+
* Session.set('cache', userData);
|
|
411
|
+
* }
|
|
412
|
+
*
|
|
413
|
+
* static logoutUser() {
|
|
414
|
+
* // Clear user-specific data
|
|
415
|
+
* Session.removeAll();
|
|
416
|
+
*
|
|
417
|
+
* // Reset to global namespace
|
|
418
|
+
* Manager.keyNamespace = "global";
|
|
419
|
+
* }
|
|
420
|
+
* }
|
|
421
|
+
*
|
|
422
|
+
* // Usage
|
|
423
|
+
* UserSessionManager.loginUser('john123', 'acme-corp');
|
|
424
|
+
* // Namespace: "org-acme-corp-user-john123"
|
|
425
|
+
*
|
|
426
|
+
* Session.set('lastAction', 'document_created');
|
|
427
|
+
* // Stored as: "org-acme-corp-user-john123-lastAction"
|
|
428
|
+
* ```
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```typescript
|
|
432
|
+
* // Feature-based namespacing
|
|
433
|
+
* class FeatureFlags {
|
|
434
|
+
* private static originalNamespace: string;
|
|
435
|
+
*
|
|
436
|
+
* static enableFeature(featureName: string) {
|
|
437
|
+
* // Save current namespace
|
|
438
|
+
* this.originalNamespace = Manager.keyNamespace;
|
|
439
|
+
*
|
|
440
|
+
* // Switch to feature-specific namespace
|
|
441
|
+
* Manager.keyNamespace = `feature-${featureName}`;
|
|
442
|
+
*
|
|
443
|
+
* return {
|
|
444
|
+
* store: (key: string, value: any) => Session.set(key, value),
|
|
445
|
+
* retrieve: (key: string) => Session.get(key),
|
|
446
|
+
* cleanup: () => {
|
|
447
|
+
* Session.removeAll();
|
|
448
|
+
* Manager.keyNamespace = this.originalNamespace;
|
|
449
|
+
* }
|
|
450
|
+
* };
|
|
451
|
+
* }
|
|
452
|
+
* }
|
|
453
|
+
*
|
|
454
|
+
* // Usage
|
|
455
|
+
* const betaFeature = FeatureFlags.enableFeature('beta-dashboard');
|
|
456
|
+
* betaFeature.store('userProgress', progressData);
|
|
457
|
+
* // Stored as: "feature-beta-dashboard-userProgress"
|
|
458
|
+
*
|
|
459
|
+
* betaFeature.cleanup(); // Removes all feature data and restores namespace
|
|
460
|
+
* ```
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```typescript
|
|
464
|
+
* // Namespace migration for application updates
|
|
465
|
+
* class NamespaceMigration {
|
|
466
|
+
* static migrateFrom(oldNamespace: string, newNamespace: string) {
|
|
467
|
+
* // Step 1: Read all data from old namespace
|
|
468
|
+
* Manager.keyNamespace = oldNamespace;
|
|
469
|
+
* const oldData = this.getAllStorageData();
|
|
470
|
+
*
|
|
471
|
+
* // Step 2: Switch to new namespace and write data
|
|
472
|
+
* Manager.keyNamespace = newNamespace;
|
|
473
|
+
* Object.entries(oldData).forEach(([key, value]) => {
|
|
474
|
+
* Session.set(key, value);
|
|
475
|
+
* });
|
|
476
|
+
*
|
|
477
|
+
* // Step 3: Clean up old namespace
|
|
478
|
+
* Manager.keyNamespace = oldNamespace;
|
|
479
|
+
* Session.removeAll();
|
|
480
|
+
*
|
|
481
|
+
* // Step 4: Set new namespace as active
|
|
482
|
+
* Manager.keyNamespace = newNamespace;
|
|
483
|
+
* }
|
|
484
|
+
*
|
|
485
|
+
* private static getAllStorageData(): Record<string, any> {
|
|
486
|
+
* // Implementation would depend on storage backend
|
|
487
|
+
* // This is a simplified example
|
|
488
|
+
* return {};
|
|
489
|
+
* }
|
|
490
|
+
* }
|
|
491
|
+
*
|
|
492
|
+
* // Usage during app update
|
|
493
|
+
* NamespaceMigration.migrateFrom('v1.0', 'v2.0');
|
|
494
|
+
* ```
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```typescript
|
|
498
|
+
* // Namespace validation and sanitization
|
|
499
|
+
* function setSecureNamespace(rawNamespace: string): boolean {
|
|
500
|
+
* // Sanitize the namespace
|
|
501
|
+
* const sanitized = rawNamespace
|
|
502
|
+
* .toLowerCase()
|
|
503
|
+
* .replace(/[^a-z0-9-]/g, '-')
|
|
504
|
+
* .replace(/-+/g, '-')
|
|
505
|
+
* .replace(/^-|-$/g, '');
|
|
506
|
+
*
|
|
507
|
+
* if (sanitized.length < 3) {
|
|
508
|
+
* console.warn('Namespace too short, using default');
|
|
509
|
+
* Manager.keyNamespace = 'default';
|
|
510
|
+
* return false;
|
|
511
|
+
* }
|
|
512
|
+
*
|
|
513
|
+
* Manager.keyNamespace = sanitized;
|
|
514
|
+
* return true;
|
|
515
|
+
* }
|
|
516
|
+
*
|
|
517
|
+
* // Usage
|
|
518
|
+
* setSecureNamespace('My App!! v2.0'); // Sets to "my-app-v2-0"
|
|
519
|
+
* setSecureNamespace('x'); // Sets to "default" (too short)
|
|
520
|
+
* ```
|
|
521
|
+
*
|
|
522
|
+
* @see {@link keyNamespace} (getter) - Method to retrieve the current namespace
|
|
523
|
+
* @see {@link sanitizeKey} - Method that applies the namespace to keys
|
|
524
|
+
* @see {@link isNonNullString} - Validation function used for input checking
|
|
525
|
+
* @see {@link Session} - Session utilities that use the namespaced keys
|
|
526
|
+
*
|
|
527
|
+
*
|
|
528
|
+
* @public
|
|
529
|
+
*
|
|
530
|
+
* @remarks
|
|
531
|
+
* **Critical Behavior Notes:**
|
|
532
|
+
* - Only valid, non-empty strings are accepted as namespace values
|
|
533
|
+
* - Invalid inputs (null, undefined, empty strings) are silently ignored
|
|
534
|
+
* - The namespace change affects ALL subsequent storage operations immediately
|
|
535
|
+
* - Previously stored data under different namespaces becomes inaccessible
|
|
536
|
+
* - The namespace is stored globally and persists until explicitly changed
|
|
537
|
+
*
|
|
538
|
+
* **Security Considerations:**
|
|
539
|
+
* - Ensure namespace values don't contain sensitive information
|
|
540
|
+
* - Validate namespace inputs in security-critical applications
|
|
541
|
+
* - Consider namespace as part of your access control strategy
|
|
542
|
+
* - Be aware that namespace switching can expose or hide data
|
|
543
|
+
*
|
|
544
|
+
* **Performance Impact:**
|
|
545
|
+
* - Namespace setting is a lightweight operation
|
|
546
|
+
* - The namespace is cached and doesn't impact storage operation performance
|
|
547
|
+
* - Frequent namespace changes can lead to memory fragmentation in some storage backends
|
|
548
|
+
*
|
|
549
|
+
* **Best Practices:**
|
|
550
|
+
* - Set the namespace once during application initialization
|
|
551
|
+
* - Use consistent, predictable naming patterns
|
|
552
|
+
* - Document your namespace strategy and conventions
|
|
553
|
+
* - Consider versioning in your namespace for future migrations
|
|
554
|
+
* - Test namespace switching thoroughly in your application
|
|
555
|
+
*/
|
|
556
|
+
static set keyNamespace(prefix: string);
|
|
557
|
+
/**
|
|
558
|
+
* Sanitizes and prepares a storage key by removing whitespace and applying the global namespace prefix.
|
|
559
|
+
*
|
|
560
|
+
* This method is the core key processing function that ensures all session storage keys are
|
|
561
|
+
* properly formatted and consistently namespaced. It performs essential cleaning operations
|
|
562
|
+
* to prevent storage errors and applies the global namespace to create isolated storage contexts.
|
|
563
|
+
*
|
|
564
|
+
* The sanitization process is critical for maintaining data integrity and preventing conflicts
|
|
565
|
+
* in shared storage environments. This method is automatically called by all session storage
|
|
566
|
+
* operations, ensuring consistent key handling throughout the application.
|
|
567
|
+
*
|
|
568
|
+
* ### Sanitization Process:
|
|
569
|
+
* 1. **Validation**: Checks if the key is valid using {@link isNonNullString}
|
|
570
|
+
* 2. **Trimming**: Removes leading and trailing whitespace
|
|
571
|
+
* 3. **Whitespace Removal**: Removes all internal whitespace characters
|
|
572
|
+
* 4. **Namespace Application**: Prepends the global namespace with a hyphen separator
|
|
573
|
+
* 5. **Return**: Provides the final, storage-ready key
|
|
574
|
+
*
|
|
575
|
+
* ### Key Format:
|
|
576
|
+
* - **Without namespace**: `"cleankey"`
|
|
577
|
+
* - **With namespace**: `"namespace-cleankey"`
|
|
578
|
+
* - **Invalid input**: `""` (empty string)
|
|
579
|
+
*
|
|
580
|
+
* @param key - The raw key string to sanitize (optional)
|
|
581
|
+
*
|
|
582
|
+
* @returns The sanitized key string ready for storage operations, or empty string if input is invalid
|
|
583
|
+
*
|
|
584
|
+
* @example
|
|
585
|
+
* ```typescript
|
|
586
|
+
* // Basic key sanitization
|
|
587
|
+
* const clean1 = Manager.sanitizeKey("userToken");
|
|
588
|
+
* console.log(clean1); // "userToken"
|
|
589
|
+
*
|
|
590
|
+
* const clean2 = Manager.sanitizeKey("user profile");
|
|
591
|
+
* console.log(clean2); // "userprofile"
|
|
592
|
+
*
|
|
593
|
+
* const clean3 = Manager.sanitizeKey(" spaced key ");
|
|
594
|
+
* console.log(clean3); // "spacedkey"
|
|
595
|
+
* ```
|
|
596
|
+
*
|
|
597
|
+
* @example
|
|
598
|
+
* ```typescript
|
|
599
|
+
* // Namespace application
|
|
600
|
+
* Manager.keyNamespace = "myapp";
|
|
601
|
+
*
|
|
602
|
+
* const key1 = Manager.sanitizeKey("settings");
|
|
603
|
+
* console.log(key1); // "myapp-settings"
|
|
604
|
+
*
|
|
605
|
+
* const key2 = Manager.sanitizeKey("user preferences");
|
|
606
|
+
* console.log(key2); // "myapp-userpreferences"
|
|
607
|
+
*
|
|
608
|
+
* const key3 = Manager.sanitizeKey("cache data");
|
|
609
|
+
* console.log(key3); // "myapp-cachedata"
|
|
610
|
+
* ```
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* ```typescript
|
|
614
|
+
* // Invalid input handling
|
|
615
|
+
* const invalid1 = Manager.sanitizeKey("");
|
|
616
|
+
* console.log(invalid1); // ""
|
|
617
|
+
*
|
|
618
|
+
* const invalid2 = Manager.sanitizeKey(null);
|
|
619
|
+
* console.log(invalid2); // ""
|
|
620
|
+
*
|
|
621
|
+
* const invalid3 = Manager.sanitizeKey(undefined);
|
|
622
|
+
* console.log(invalid3); // ""
|
|
623
|
+
*
|
|
624
|
+
* const invalid4 = Manager.sanitizeKey(" ");
|
|
625
|
+
* console.log(invalid4); // ""
|
|
626
|
+
* ```
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* ```typescript
|
|
630
|
+
* // Complex whitespace handling
|
|
631
|
+
* const complex1 = Manager.sanitizeKey("user\tprofile\ndata");
|
|
632
|
+
* console.log(complex1); // "userprofiledata"
|
|
633
|
+
*
|
|
634
|
+
* const complex2 = Manager.sanitizeKey("multi space key");
|
|
635
|
+
* console.log(complex2); // "multispacekey"
|
|
636
|
+
*
|
|
637
|
+
* const complex3 = Manager.sanitizeKey(" \t\n mixed \r\n whitespace \t ");
|
|
638
|
+
* console.log(complex3); // "mixedwhitespace"
|
|
639
|
+
* ```
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```typescript
|
|
643
|
+
* // Usage in custom storage operations
|
|
644
|
+
* class CustomStorage {
|
|
645
|
+
* static setWithMetadata(key: string, value: any, metadata: object) {
|
|
646
|
+
* const cleanKey = Manager.sanitizeKey(key);
|
|
647
|
+
* const metaKey = Manager.sanitizeKey(`${key}_meta`);
|
|
648
|
+
*
|
|
649
|
+
* Session.set(cleanKey, value);
|
|
650
|
+
* Session.set(metaKey, metadata);
|
|
651
|
+
* }
|
|
652
|
+
*
|
|
653
|
+
* static getWithMetadata(key: string) {
|
|
654
|
+
* const cleanKey = Manager.sanitizeKey(key);
|
|
655
|
+
* const metaKey = Manager.sanitizeKey(`${key}_meta`);
|
|
656
|
+
*
|
|
657
|
+
* return {
|
|
658
|
+
* value: Session.get(cleanKey),
|
|
659
|
+
* metadata: Session.get(metaKey)
|
|
660
|
+
* };
|
|
661
|
+
* }
|
|
662
|
+
* }
|
|
663
|
+
*
|
|
664
|
+
* // Usage
|
|
665
|
+
* Manager.keyNamespace = "app";
|
|
666
|
+
* CustomStorage.setWithMetadata("user data", userData, { created: Date.now() });
|
|
667
|
+
* // Stores: "app-userdata" and "app-userdata_meta"
|
|
668
|
+
*
|
|
669
|
+
* const result = CustomStorage.getWithMetadata("user data");
|
|
670
|
+
* console.log(result.value, result.metadata);
|
|
671
|
+
* ```
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* ```typescript
|
|
675
|
+
* // Key collision prevention
|
|
676
|
+
* class StorageKeyManager {
|
|
677
|
+
* static generateUniqueKey(baseKey: string, identifier: string): string {
|
|
678
|
+
* const timestamp = Date.now();
|
|
679
|
+
* const randomSuffix = Math.random().toString(36).substr(2, 9);
|
|
680
|
+
* const rawKey = `${baseKey}_${identifier}_${timestamp}_${randomSuffix}`;
|
|
681
|
+
*
|
|
682
|
+
* return Manager.sanitizeKey(rawKey);
|
|
683
|
+
* }
|
|
684
|
+
*
|
|
685
|
+
* static isKeyValid(key: string): boolean {
|
|
686
|
+
* const sanitized = Manager.sanitizeKey(key);
|
|
687
|
+
* return sanitized.length > 0 && sanitized === Manager.sanitizeKey(sanitized);
|
|
688
|
+
* }
|
|
689
|
+
* }
|
|
690
|
+
*
|
|
691
|
+
* // Usage
|
|
692
|
+
* const uniqueKey = StorageKeyManager.generateUniqueKey("temp cache", "user123");
|
|
693
|
+
* console.log(uniqueKey); // "namespace-tempcache_user123_1642123456789_abc123def"
|
|
694
|
+
*
|
|
695
|
+
* console.log(StorageKeyManager.isKeyValid("valid key")); // true
|
|
696
|
+
* console.log(StorageKeyManager.isKeyValid("")); // false
|
|
697
|
+
* ```
|
|
698
|
+
*
|
|
699
|
+
* @example
|
|
700
|
+
* ```typescript
|
|
701
|
+
* // Debugging key transformations
|
|
702
|
+
* function debugKeySanitization(rawKey: string) {
|
|
703
|
+
* console.log('=== Key Sanitization Debug ===');
|
|
704
|
+
* console.log('Input:', JSON.stringify(rawKey));
|
|
705
|
+
* console.log('Current namespace:', Manager.keyNamespace);
|
|
706
|
+
*
|
|
707
|
+
* const sanitized = Manager.sanitizeKey(rawKey);
|
|
708
|
+
* console.log('Output:', JSON.stringify(sanitized));
|
|
709
|
+
*
|
|
710
|
+
* const steps = {
|
|
711
|
+
* 'Original': rawKey,
|
|
712
|
+
* 'After trim': rawKey?.trim(),
|
|
713
|
+
* 'After whitespace removal': rawKey?.trim().replace(/\s+/g, ""),
|
|
714
|
+
* 'With namespace': sanitized
|
|
715
|
+
* };
|
|
716
|
+
*
|
|
717
|
+
* Object.entries(steps).forEach(([step, value]) => {
|
|
718
|
+
* console.log(`${step}: "${value}"`);
|
|
719
|
+
* });
|
|
720
|
+
* }
|
|
721
|
+
*
|
|
722
|
+
* // Usage
|
|
723
|
+
* Manager.keyNamespace = "debug";
|
|
724
|
+
* debugKeySanitization(" test key with spaces ");
|
|
725
|
+
* ```
|
|
726
|
+
*
|
|
727
|
+
* @see {@link keyNamespace} - The namespace property applied to keys
|
|
728
|
+
* @see {@link isNonNullString} - Validation function for key input
|
|
729
|
+
* @see {@link Session.set} - Method that uses sanitized keys for storage
|
|
730
|
+
* @see {@link Session.get} - Method that uses sanitized keys for retrieval
|
|
731
|
+
* @see {@link sanitizeKey} - Standalone function wrapper for this method
|
|
732
|
+
*
|
|
733
|
+
*
|
|
734
|
+
* @public
|
|
735
|
+
*
|
|
736
|
+
* @remarks
|
|
737
|
+
* **Key Behavior Notes:**
|
|
738
|
+
* - All whitespace characters (spaces, tabs, newlines, etc.) are completely removed
|
|
739
|
+
* - The method is case-sensitive - no case transformation is performed
|
|
740
|
+
* - Namespace application is automatic when a namespace is set
|
|
741
|
+
* - Empty or invalid inputs always return an empty string
|
|
742
|
+
* - The method is idempotent - sanitizing a sanitized key produces the same result
|
|
743
|
+
*
|
|
744
|
+
* **Performance Characteristics:**
|
|
745
|
+
* - Very fast operation, primarily string manipulation
|
|
746
|
+
* - No external dependencies or async operations
|
|
747
|
+
* - Minimal memory allocation for most inputs
|
|
748
|
+
* - Regex operations are optimized for common whitespace patterns
|
|
749
|
+
*
|
|
750
|
+
* **Integration Points:**
|
|
751
|
+
* - Called automatically by all Session storage methods
|
|
752
|
+
* - Used by the standalone {@link sanitizeKey} function
|
|
753
|
+
* - Can be called directly for key validation or preview
|
|
754
|
+
* - Essential for custom storage implementations
|
|
755
|
+
*
|
|
756
|
+
* **Whitespace Handling:**
|
|
757
|
+
* - **Spaces**: ` ` → removed
|
|
758
|
+
* - **Tabs**: `\t` → removed
|
|
759
|
+
* - **Newlines**: `\n`, `\r\n` → removed
|
|
760
|
+
* - **Form feeds**: `\f` → removed
|
|
761
|
+
* - **Vertical tabs**: `\v` → removed
|
|
762
|
+
* - **Unicode whitespace**: Various Unicode space characters → removed
|
|
763
|
+
*/
|
|
764
|
+
static sanitizeKey(key?: string): string;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Sanitizes a string for session storage.
|
|
768
|
+
*
|
|
769
|
+
* This function trims and removes whitespace from the key, and adds the namespace prefix if set.
|
|
770
|
+
* \nExample
|
|
771
|
+
* ```typescript
|
|
772
|
+
Manager.keyNamespace = "my-prefix";
|
|
773
|
+
const prefixedKey = sanitizeKey("my-key");
|
|
774
|
+
console.log(prefixedKey); // "my-prefix-my-key"
|
|
775
|
+
* ````
|
|
776
|
+
* @param {string} key - The key to sanitize.
|
|
777
|
+
* @returns {string} The sanitized key.
|
|
778
|
+
*/
|
|
779
|
+
declare function sanitizeKey(key: string): string;
|
|
780
|
+
/**
|
|
781
|
+
* Interface for a session storage object.
|
|
782
|
+
*
|
|
783
|
+
* This interface defines the methods for setting, getting, and removing values from a session storage object.
|
|
784
|
+
*/
|
|
785
|
+
export interface SessionStorage {
|
|
786
|
+
/**
|
|
787
|
+
* Sets a value in the session storage object.
|
|
788
|
+
*
|
|
789
|
+
* @param {string} key - The key to set the value for.
|
|
790
|
+
* @param {any} value - The value to set.
|
|
791
|
+
* @param {boolean} [decycle] - Optional parameter to decycle the value.
|
|
792
|
+
* @returns {any} The set value.
|
|
793
|
+
*/
|
|
794
|
+
set: (key: string, value: any, decycle?: boolean) => any;
|
|
795
|
+
/**
|
|
796
|
+
* Gets a value from the session storage object.
|
|
797
|
+
*
|
|
798
|
+
* @param {string} key - The key to get the value for.
|
|
799
|
+
* @returns {any} The value associated with the key.
|
|
800
|
+
*/
|
|
801
|
+
get: (key: string) => any;
|
|
802
|
+
/**
|
|
803
|
+
* Removes a value from the session storage object.
|
|
804
|
+
*
|
|
805
|
+
* @param {string} key - The key to remove the value for.
|
|
806
|
+
* @returns {any} The removed value.
|
|
807
|
+
*/
|
|
808
|
+
remove: (key: string) => any;
|
|
809
|
+
/**
|
|
810
|
+
* Removes all values from the session storage object.
|
|
811
|
+
*
|
|
812
|
+
*/
|
|
813
|
+
removeAll: () => any;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Session management utilities providing comprehensive storage operations with automatic serialization and namespace support.
|
|
817
|
+
*
|
|
818
|
+
* The Session object serves as the primary interface for all session storage operations in the application.
|
|
819
|
+
* It provides a clean, consistent API that abstracts away the complexity of different storage backends while
|
|
820
|
+
* offering advanced features like automatic JSON serialization, key sanitization, namespace management,
|
|
821
|
+
* and support for both synchronous and asynchronous storage operations.
|
|
822
|
+
*
|
|
823
|
+
* ### Core Features:
|
|
824
|
+
* - **Automatic Serialization**: JSON serialization/deserialization with decycling support
|
|
825
|
+
* - **Key Sanitization**: Automatic key cleaning and namespace prefixing
|
|
826
|
+
* - **Storage Abstraction**: Works with any storage backend implementing SessionStorage
|
|
827
|
+
* - **Type Safety**: Full TypeScript support with intelligent type inference
|
|
828
|
+
* - **Async Support**: Seamless handling of both sync and async storage operations
|
|
829
|
+
* - **Error Resilience**: Graceful handling of storage failures and edge cases
|
|
830
|
+
*
|
|
831
|
+
* @namespace Session
|
|
832
|
+
*
|
|
833
|
+
* @public
|
|
834
|
+
*/
|
|
835
|
+
export declare const Session: {
|
|
836
|
+
get: (key: string) => any;
|
|
837
|
+
/**
|
|
838
|
+
* Stores a value in session storage with automatic serialization and key sanitization.
|
|
839
|
+
*
|
|
840
|
+
* This method is the primary interface for persisting data to session storage. It provides
|
|
841
|
+
* intelligent handling of complex data types through JSON serialization, automatic key
|
|
842
|
+
* sanitization with namespace support, and optional object decycling to handle circular
|
|
843
|
+
* references safely.
|
|
844
|
+
*
|
|
845
|
+
* The method leverages the configured storage backend through the Manager, ensuring that
|
|
846
|
+
* your data is stored consistently regardless of whether you're using localStorage,
|
|
847
|
+
* sessionStorage, IndexedDB, or a custom storage implementation.
|
|
848
|
+
*
|
|
849
|
+
* ### Key Processing Pipeline:
|
|
850
|
+
* 1. **Key Sanitization**: Applies {@link sanitizeKey} to clean and prefix the key
|
|
851
|
+
* 2. **Value Processing**: Uses {@link handleSetValue} for JSON serialization
|
|
852
|
+
* 3. **Storage Delegation**: Passes processed data to the configured storage backend
|
|
853
|
+
* 4. **Result Return**: Returns the result from the underlying storage operation
|
|
854
|
+
*
|
|
855
|
+
* ### Serialization Features:
|
|
856
|
+
* - **JSON Serialization**: Converts objects, arrays, and primitives to JSON strings
|
|
857
|
+
* - **Circular Reference Handling**: Optional decycling prevents infinite recursion
|
|
858
|
+
* - **Type Preservation**: Maintains data types through intelligent parsing on retrieval
|
|
859
|
+
* - **Null/Undefined Handling**: Graceful handling of empty values
|
|
860
|
+
*
|
|
861
|
+
* @param key - The storage key identifier for the value
|
|
862
|
+
* @param value - The data to store (objects, arrays, primitives, etc.)
|
|
863
|
+
* @param decycle - Whether to remove circular references during serialization
|
|
864
|
+
*
|
|
865
|
+
* @returns The result of the storage operation (implementation-dependent)
|
|
866
|
+
*
|
|
867
|
+
* @example
|
|
868
|
+
* ```typescript
|
|
869
|
+
* // Basic primitive value storage
|
|
870
|
+
* Session.set('username', 'john_doe');
|
|
871
|
+
* Session.set('user_id', 12345);
|
|
872
|
+
* Session.set('is_authenticated', true);
|
|
873
|
+
* Session.set('last_login', new Date());
|
|
874
|
+
*
|
|
875
|
+
* console.log(Session.get('username')); // 'john_doe'
|
|
876
|
+
* console.log(Session.get('user_id')); // 12345
|
|
877
|
+
* console.log(Session.get('is_authenticated')); // true
|
|
878
|
+
* ```
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* // Complex object storage with automatic serialization
|
|
883
|
+
* const userProfile = {
|
|
884
|
+
* id: 1001,
|
|
885
|
+
* name: 'Alice Johnson',
|
|
886
|
+
* email: 'alice@example.com',
|
|
887
|
+
* preferences: {
|
|
888
|
+
* theme: 'dark',
|
|
889
|
+
* language: 'en',
|
|
890
|
+
* notifications: {
|
|
891
|
+
* email: true,
|
|
892
|
+
* push: false,
|
|
893
|
+
* sms: true
|
|
894
|
+
* }
|
|
895
|
+
* },
|
|
896
|
+
* roles: ['user', 'moderator'],
|
|
897
|
+
* metadata: {
|
|
898
|
+
* created_at: '2023-01-15T10:30:00Z',
|
|
899
|
+
* last_updated: '2023-07-20T14:45:30Z'
|
|
900
|
+
* }
|
|
901
|
+
* };
|
|
902
|
+
*
|
|
903
|
+
* // Store complex object - automatically serialized
|
|
904
|
+
* Session.set('user_profile', userProfile);
|
|
905
|
+
*
|
|
906
|
+
* // Retrieve and use - automatically deserialized
|
|
907
|
+
* const retrievedProfile = Session.get('user_profile');
|
|
908
|
+
* console.log(retrievedProfile.name); // 'Alice Johnson'
|
|
909
|
+
* console.log(retrievedProfile.preferences.theme); // 'dark'
|
|
910
|
+
* console.log(retrievedProfile.roles.length); // 2
|
|
911
|
+
* ```
|
|
912
|
+
*
|
|
913
|
+
* @example
|
|
914
|
+
* ```typescript
|
|
915
|
+
* // Array storage and manipulation
|
|
916
|
+
* const shoppingCart = [
|
|
917
|
+
* { id: 'prod1', name: 'Laptop', price: 999.99, quantity: 1 },
|
|
918
|
+
* { id: 'prod2', name: 'Mouse', price: 29.99, quantity: 2 },
|
|
919
|
+
* { id: 'prod3', name: 'Keyboard', price: 79.99, quantity: 1 }
|
|
920
|
+
* ];
|
|
921
|
+
*
|
|
922
|
+
* Session.set('shopping_cart', shoppingCart);
|
|
923
|
+
*
|
|
924
|
+
* // Retrieve and work with array
|
|
925
|
+
* const cart = Session.get('shopping_cart');
|
|
926
|
+
* const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
|
|
927
|
+
* const totalPrice = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
928
|
+
*
|
|
929
|
+
* console.log(`Cart has ${totalItems} items, total: $${totalPrice.toFixed(2)}`);
|
|
930
|
+
* ```
|
|
931
|
+
*
|
|
932
|
+
* @example
|
|
933
|
+
* ```typescript
|
|
934
|
+
* // Handling circular references with decycling
|
|
935
|
+
* interface Node {
|
|
936
|
+
* id: string;
|
|
937
|
+
* name: string;
|
|
938
|
+
* parent?: Node;
|
|
939
|
+
* children: Node[];
|
|
940
|
+
* }
|
|
941
|
+
*
|
|
942
|
+
* const parentNode: Node = {
|
|
943
|
+
* id: 'parent',
|
|
944
|
+
* name: 'Parent Node',
|
|
945
|
+
* children: []
|
|
946
|
+
* };
|
|
947
|
+
*
|
|
948
|
+
* const childNode: Node = {
|
|
949
|
+
* id: 'child',
|
|
950
|
+
* name: 'Child Node',
|
|
951
|
+
* parent: parentNode,
|
|
952
|
+
* children: []
|
|
953
|
+
* };
|
|
954
|
+
*
|
|
955
|
+
* parentNode.children.push(childNode);
|
|
956
|
+
*
|
|
957
|
+
* // Store with decycling enabled (default) to handle circular reference
|
|
958
|
+
* Session.set('node_tree', parentNode, true);
|
|
959
|
+
*
|
|
960
|
+
* // Store without decycling (use with caution for circular data)
|
|
961
|
+
* // Session.set('node_tree', parentNode, false); // May cause errors
|
|
962
|
+
* ```
|
|
963
|
+
*
|
|
964
|
+
* @example
|
|
965
|
+
* ```typescript
|
|
966
|
+
* // Application state management
|
|
967
|
+
* interface AppState {
|
|
968
|
+
* currentView: 'dashboard' | 'profile' | 'settings';
|
|
969
|
+
* user: {
|
|
970
|
+
* id: number;
|
|
971
|
+
* name: string;
|
|
972
|
+
* permissions: string[];
|
|
973
|
+
* };
|
|
974
|
+
* uiState: {
|
|
975
|
+
* sidebarCollapsed: boolean;
|
|
976
|
+
* activeTab: string;
|
|
977
|
+
* filters: Record<string, any>;
|
|
978
|
+
* };
|
|
979
|
+
* }
|
|
980
|
+
*
|
|
981
|
+
* const appState: AppState = {
|
|
982
|
+
* currentView: 'dashboard',
|
|
983
|
+
* user: {
|
|
984
|
+
* id: 123,
|
|
985
|
+
* name: 'John Doe',
|
|
986
|
+
* permissions: ['read', 'write', 'admin']
|
|
987
|
+
* },
|
|
988
|
+
* uiState: {
|
|
989
|
+
* sidebarCollapsed: false,
|
|
990
|
+
* activeTab: 'overview',
|
|
991
|
+
* filters: {
|
|
992
|
+
* dateRange: '30days',
|
|
993
|
+
* category: 'all',
|
|
994
|
+
* status: 'active'
|
|
995
|
+
* }
|
|
996
|
+
* }
|
|
997
|
+
* };
|
|
998
|
+
*
|
|
999
|
+
* // Persist entire application state
|
|
1000
|
+
* Session.set('app_state', appState);
|
|
1001
|
+
*
|
|
1002
|
+
* // Later, restore application state
|
|
1003
|
+
* const restoredState = Session.get('app_state') as AppState;
|
|
1004
|
+
* if (restoredState) {
|
|
1005
|
+
* console.log(`Welcome back, ${restoredState.user.name}!`);
|
|
1006
|
+
* console.log(`Current view: ${restoredState.currentView}`);
|
|
1007
|
+
* }
|
|
1008
|
+
* ```
|
|
1009
|
+
*
|
|
1010
|
+
* @example
|
|
1011
|
+
* ```typescript
|
|
1012
|
+
* // Form data persistence for better UX
|
|
1013
|
+
* interface FormData {
|
|
1014
|
+
* personalInfo: {
|
|
1015
|
+
* firstName: string;
|
|
1016
|
+
* lastName: string;
|
|
1017
|
+
* email: string;
|
|
1018
|
+
* phone: string;
|
|
1019
|
+
* };
|
|
1020
|
+
* address: {
|
|
1021
|
+
* street: string;
|
|
1022
|
+
* city: string;
|
|
1023
|
+
* state: string;
|
|
1024
|
+
* zipCode: string;
|
|
1025
|
+
* };
|
|
1026
|
+
* preferences: {
|
|
1027
|
+
* newsletter: boolean;
|
|
1028
|
+
* notifications: boolean;
|
|
1029
|
+
* };
|
|
1030
|
+
* }
|
|
1031
|
+
*
|
|
1032
|
+
* // Save form data as user types (draft functionality)
|
|
1033
|
+
* function saveFormDraft(formData: Partial<FormData>) {
|
|
1034
|
+
* const existingDraft = Session.get('form_draft') || {};
|
|
1035
|
+
* const updatedDraft = { ...existingDraft, ...formData };
|
|
1036
|
+
* Session.set('form_draft', updatedDraft);
|
|
1037
|
+
* console.log('Draft saved automatically');
|
|
1038
|
+
* }
|
|
1039
|
+
*
|
|
1040
|
+
* // Restore form data when user returns
|
|
1041
|
+
* function restoreFormDraft(): Partial<FormData> | null {
|
|
1042
|
+
* return Session.get('form_draft');
|
|
1043
|
+
* }
|
|
1044
|
+
*
|
|
1045
|
+
* // Clear draft after successful submission
|
|
1046
|
+
* function clearFormDraft() {
|
|
1047
|
+
* Session.remove('form_draft');
|
|
1048
|
+
* }
|
|
1049
|
+
* ```
|
|
1050
|
+
*
|
|
1051
|
+
* @example
|
|
1052
|
+
* ```typescript
|
|
1053
|
+
* // Configuration and settings management
|
|
1054
|
+
* interface UserSettings {
|
|
1055
|
+
* appearance: {
|
|
1056
|
+
* theme: 'light' | 'dark' | 'auto';
|
|
1057
|
+
* fontSize: 'small' | 'medium' | 'large';
|
|
1058
|
+
* accentColor: string;
|
|
1059
|
+
* };
|
|
1060
|
+
* behavior: {
|
|
1061
|
+
* autoSave: boolean;
|
|
1062
|
+
* confirmDeletes: boolean;
|
|
1063
|
+
* showTooltips: boolean;
|
|
1064
|
+
* };
|
|
1065
|
+
* privacy: {
|
|
1066
|
+
* shareUsageData: boolean;
|
|
1067
|
+
* allowCookies: boolean;
|
|
1068
|
+
* };
|
|
1069
|
+
* }
|
|
1070
|
+
*
|
|
1071
|
+
* const defaultSettings: UserSettings = {
|
|
1072
|
+
* appearance: {
|
|
1073
|
+
* theme: 'auto',
|
|
1074
|
+
* fontSize: 'medium',
|
|
1075
|
+
* accentColor: '#007bff'
|
|
1076
|
+
* },
|
|
1077
|
+
* behavior: {
|
|
1078
|
+
* autoSave: true,
|
|
1079
|
+
* confirmDeletes: true,
|
|
1080
|
+
* showTooltips: true
|
|
1081
|
+
* },
|
|
1082
|
+
* privacy: {
|
|
1083
|
+
* shareUsageData: false,
|
|
1084
|
+
* allowCookies: true
|
|
1085
|
+
* }
|
|
1086
|
+
* };
|
|
1087
|
+
*
|
|
1088
|
+
* // Initialize or update settings
|
|
1089
|
+
* function updateUserSettings(newSettings: Partial<UserSettings>) {
|
|
1090
|
+
* const currentSettings = Session.get('user_settings') || defaultSettings;
|
|
1091
|
+
* const mergedSettings = deepMerge(currentSettings, newSettings);
|
|
1092
|
+
* Session.set('user_settings', mergedSettings);
|
|
1093
|
+
* return mergedSettings;
|
|
1094
|
+
* }
|
|
1095
|
+
*
|
|
1096
|
+
* // Helper function for deep merging
|
|
1097
|
+
* function deepMerge(target: any, source: any): any {
|
|
1098
|
+
* const result = { ...target };
|
|
1099
|
+
* for (const key in source) {
|
|
1100
|
+
* if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
1101
|
+
* result[key] = deepMerge(target[key] || {}, source[key]);
|
|
1102
|
+
* } else {
|
|
1103
|
+
* result[key] = source[key];
|
|
1104
|
+
* }
|
|
1105
|
+
* }
|
|
1106
|
+
* return result;
|
|
1107
|
+
* }
|
|
1108
|
+
* ```
|
|
1109
|
+
*
|
|
1110
|
+
* @example
|
|
1111
|
+
* ```typescript
|
|
1112
|
+
* // Cache management for API responses
|
|
1113
|
+
* interface CacheEntry<T> {
|
|
1114
|
+
* data: T;
|
|
1115
|
+
* timestamp: number;
|
|
1116
|
+
* ttl: number; // time to live in milliseconds
|
|
1117
|
+
* }
|
|
1118
|
+
*
|
|
1119
|
+
* function setCachedData<T>(key: string, data: T, ttlMinutes: number = 30) {
|
|
1120
|
+
* const cacheEntry: CacheEntry<T> = {
|
|
1121
|
+
* data,
|
|
1122
|
+
* timestamp: Date.now(),
|
|
1123
|
+
* ttl: ttlMinutes * 60 * 1000
|
|
1124
|
+
* };
|
|
1125
|
+
*
|
|
1126
|
+
* Session.set(`cache_${key}`, cacheEntry);
|
|
1127
|
+
* console.log(`Cached data for ${key} (TTL: ${ttlMinutes} minutes)`);
|
|
1128
|
+
* }
|
|
1129
|
+
*
|
|
1130
|
+
* function getCachedData<T>(key: string): T | null {
|
|
1131
|
+
* const cacheEntry = Session.get(`cache_${key}`) as CacheEntry<T>;
|
|
1132
|
+
*
|
|
1133
|
+
* if (!cacheEntry) return null;
|
|
1134
|
+
*
|
|
1135
|
+
* const isExpired = Date.now() - cacheEntry.timestamp > cacheEntry.ttl;
|
|
1136
|
+
* if (isExpired) {
|
|
1137
|
+
* Session.remove(`cache_${key}`);
|
|
1138
|
+
* return null;
|
|
1139
|
+
* }
|
|
1140
|
+
*
|
|
1141
|
+
* return cacheEntry.data;
|
|
1142
|
+
* }
|
|
1143
|
+
*
|
|
1144
|
+
* // Usage example
|
|
1145
|
+
* interface User {
|
|
1146
|
+
* id: number;
|
|
1147
|
+
* name: string;
|
|
1148
|
+
* email: string;
|
|
1149
|
+
* }
|
|
1150
|
+
*
|
|
1151
|
+
* async function fetchUser(id: number): Promise<User> {
|
|
1152
|
+
* // Check cache first
|
|
1153
|
+
* const cached = getCachedData<User>(`user_${id}`);
|
|
1154
|
+
* if (cached) {
|
|
1155
|
+
* console.log('Returning cached user data');
|
|
1156
|
+
* return cached;
|
|
1157
|
+
* }
|
|
1158
|
+
*
|
|
1159
|
+
* // Fetch from API
|
|
1160
|
+
* const response = await fetch(`/api/users/${id}`);
|
|
1161
|
+
* const user: User = await response.json();
|
|
1162
|
+
*
|
|
1163
|
+
* // Cache for 15 minutes
|
|
1164
|
+
* setCachedData(`user_${id}`, user, 15);
|
|
1165
|
+
*
|
|
1166
|
+
* return user;
|
|
1167
|
+
* }
|
|
1168
|
+
* ```
|
|
1169
|
+
*
|
|
1170
|
+
* @example
|
|
1171
|
+
* ```typescript
|
|
1172
|
+
* // Advanced usage with namespacing
|
|
1173
|
+
* // Set namespace for application context
|
|
1174
|
+
* Session.Manager.keyNamespace = 'myapp';
|
|
1175
|
+
*
|
|
1176
|
+
* // All keys will be automatically prefixed
|
|
1177
|
+
* Session.set('user_data', { id: 1, name: 'John' });
|
|
1178
|
+
* // Actual key stored: 'myapp-user-data'
|
|
1179
|
+
*
|
|
1180
|
+
* Session.set('app settings', { theme: 'dark' });
|
|
1181
|
+
* // Actual key stored: 'myapp-app-settings' (spaces replaced with hyphens)
|
|
1182
|
+
*
|
|
1183
|
+
* // Switch namespace for different context
|
|
1184
|
+
* Session.Manager.keyNamespace = 'admin';
|
|
1185
|
+
* Session.set('permissions', ['read', 'write', 'delete']);
|
|
1186
|
+
* // Actual key stored: 'admin-permissions'
|
|
1187
|
+
*
|
|
1188
|
+
* // Clear namespace
|
|
1189
|
+
* Session.Manager.keyNamespace = '';
|
|
1190
|
+
* Session.set('global_config', { version: '1.0.0' });
|
|
1191
|
+
* // Actual key stored: 'global-config' (no prefix)
|
|
1192
|
+
* ```
|
|
1193
|
+
*
|
|
1194
|
+
* @see {@link get} - Retrieve values from session storage
|
|
1195
|
+
* @see {@link remove} - Remove specific values from storage
|
|
1196
|
+
* @see {@link removeAll} - Clear all session storage
|
|
1197
|
+
* @see {@link sanitizeKey} - Key sanitization and namespace handling
|
|
1198
|
+
* @see {@link handleSetValue} - Value serialization with decycling support
|
|
1199
|
+
* @see {@link Manager} - Global session manager configuration
|
|
1200
|
+
* @see {@link SessionStorage} - Storage backend interface
|
|
1201
|
+
*
|
|
1202
|
+
*
|
|
1203
|
+
* @public
|
|
1204
|
+
*
|
|
1205
|
+
* @remarks
|
|
1206
|
+
* **Important Implementation Details:**
|
|
1207
|
+
* - Keys are automatically sanitized and may be prefixed with namespace
|
|
1208
|
+
* - Values are JSON-serialized unless they're already strings
|
|
1209
|
+
* - Circular references are handled when decycle is true (default)
|
|
1210
|
+
* - Storage operations depend on the configured storage backend
|
|
1211
|
+
* - Return value format depends on the underlying storage implementation
|
|
1212
|
+
*
|
|
1213
|
+
* **Performance Considerations:**
|
|
1214
|
+
* - **Serialization Overhead**: Large objects take more time to serialize
|
|
1215
|
+
* - **Storage Limits**: Be aware of storage quotas (localStorage ~5-10MB)
|
|
1216
|
+
* - **Key Length**: Very long keys may impact performance
|
|
1217
|
+
* - **Decycling Cost**: Circular reference detection adds processing time
|
|
1218
|
+
*
|
|
1219
|
+
* **Error Handling:**
|
|
1220
|
+
* - Invalid JSON serialization may throw errors
|
|
1221
|
+
* - Storage quota exceeded errors are passed through
|
|
1222
|
+
* - Null/undefined Manager.storage results in no operation
|
|
1223
|
+
* - Circular references without decycling may cause infinite recursion
|
|
1224
|
+
*
|
|
1225
|
+
* **Browser Compatibility:**
|
|
1226
|
+
* - Works with any storage backend implementing SessionStorage
|
|
1227
|
+
* - JSON serialization uses native JSON.stringify/parse
|
|
1228
|
+
* - No dependencies on specific browser APIs
|
|
1229
|
+
* - Graceful degradation when storage is unavailable
|
|
1230
|
+
*/
|
|
1231
|
+
set: (key: string, value: any, decycle?: boolean) => any;
|
|
1232
|
+
remove: (key: string) => any;
|
|
1233
|
+
handleGetValue: any;
|
|
1234
|
+
sanitizeKey: typeof sanitizeKey;
|
|
1235
|
+
handleSetValue: (value: any, decycle?: boolean) => any;
|
|
1236
|
+
isValidStorage: (storage?: SessionStorage) => boolean;
|
|
1237
|
+
Manager: typeof Manager;
|
|
1238
|
+
removeAll: () => any;
|
|
1239
|
+
};
|
|
1240
|
+
/**
|
|
1241
|
+
* Class decorator that attaches a custom session storage implementation to the global session Manager.
|
|
1242
|
+
*
|
|
1243
|
+
* This decorator provides a clean and declarative way to register custom storage implementations
|
|
1244
|
+
* that will be used throughout the application for session management. When applied to a class,
|
|
1245
|
+
* it automatically instantiates the class and registers it as the global storage provider.
|
|
1246
|
+
*
|
|
1247
|
+
* The decorator implements the Dependency Injection pattern for storage providers, allowing
|
|
1248
|
+
* applications to easily swap between different storage implementations (localStorage,
|
|
1249
|
+
* sessionStorage, IndexedDB, in-memory storage, etc.) without changing the core session logic.
|
|
1250
|
+
*
|
|
1251
|
+
* ### Features:
|
|
1252
|
+
* - **Automatic Registration**: Instantiates and registers the storage class automatically
|
|
1253
|
+
* - **Validation**: Ensures the storage implementation meets the required interface
|
|
1254
|
+
* - **Error Handling**: Gracefully handles instantiation failures
|
|
1255
|
+
* - **Type Safety**: Enforces SessionStorage interface compliance at compile time
|
|
1256
|
+
* - **Global Scope**: Makes the storage available throughout the entire application
|
|
1257
|
+
*
|
|
1258
|
+
* ### Storage Requirements:
|
|
1259
|
+
* The decorated class must implement the {@link SessionStorage} interface with these methods:
|
|
1260
|
+
* - `get(key: string): any` - Retrieve a value by key
|
|
1261
|
+
* - `set(key: string, value: any, decycle?: boolean): any` - Store a value with optional decycling
|
|
1262
|
+
* - `remove(key: string): any` - Remove a value by key
|
|
1263
|
+
* - `removeAll(): any` - Clear all stored values
|
|
1264
|
+
*
|
|
1265
|
+
* @decorator
|
|
1266
|
+
* @param target - The class constructor that implements {@link SessionStorage}
|
|
1267
|
+
*
|
|
1268
|
+
* @returns A class decorator function that registers the storage implementation
|
|
1269
|
+
*
|
|
1270
|
+
* @throws {Error} Logs error to console if storage instantiation fails, but doesn't throw
|
|
1271
|
+
*
|
|
1272
|
+
* @example
|
|
1273
|
+
* ```typescript
|
|
1274
|
+
* // Basic localStorage implementation
|
|
1275
|
+
* @AttachSessionStorage()
|
|
1276
|
+
* class LocalStorageProvider implements SessionStorage {
|
|
1277
|
+
* get(key: string): any {
|
|
1278
|
+
* return localStorage.getItem(key);
|
|
1279
|
+
* }
|
|
1280
|
+
*
|
|
1281
|
+
* set(key: string, value: any): any {
|
|
1282
|
+
* localStorage.setItem(key, String(value));
|
|
1283
|
+
* return value;
|
|
1284
|
+
* }
|
|
1285
|
+
*
|
|
1286
|
+
* remove(key: string): any {
|
|
1287
|
+
* const value = this.get(key);
|
|
1288
|
+
* localStorage.removeItem(key);
|
|
1289
|
+
* return value;
|
|
1290
|
+
* }
|
|
1291
|
+
*
|
|
1292
|
+
* removeAll(): any {
|
|
1293
|
+
* localStorage.clear();
|
|
1294
|
+
* }
|
|
1295
|
+
* }
|
|
1296
|
+
*
|
|
1297
|
+
* // Now Session.get(), Session.set(), etc. will use localStorage
|
|
1298
|
+
* Session.set('user', { id: 1, name: 'John' });
|
|
1299
|
+
* const user = Session.get('user'); // Retrieves from localStorage
|
|
1300
|
+
* ```
|
|
1301
|
+
*
|
|
1302
|
+
* @example
|
|
1303
|
+
* ```typescript
|
|
1304
|
+
* // Custom encrypted storage implementation
|
|
1305
|
+
* @AttachSessionStorage()
|
|
1306
|
+
* class EncryptedStorageProvider implements SessionStorage {
|
|
1307
|
+
* private encrypt(value: string): string {
|
|
1308
|
+
* // Your encryption logic here
|
|
1309
|
+
* return btoa(value); // Simple base64 for demo
|
|
1310
|
+
* }
|
|
1311
|
+
*
|
|
1312
|
+
* private decrypt(value: string): string {
|
|
1313
|
+
* // Your decryption logic here
|
|
1314
|
+
* return atob(value); // Simple base64 decode for demo
|
|
1315
|
+
* }
|
|
1316
|
+
*
|
|
1317
|
+
* get(key: string): any {
|
|
1318
|
+
* const encrypted = localStorage.getItem(key);
|
|
1319
|
+
* return encrypted ? this.decrypt(encrypted) : null;
|
|
1320
|
+
* }
|
|
1321
|
+
*
|
|
1322
|
+
* set(key: string, value: any): any {
|
|
1323
|
+
* const encrypted = this.encrypt(String(value));
|
|
1324
|
+
* localStorage.setItem(key, encrypted);
|
|
1325
|
+
* return value;
|
|
1326
|
+
* }
|
|
1327
|
+
*
|
|
1328
|
+
* remove(key: string): any {
|
|
1329
|
+
* const value = this.get(key);
|
|
1330
|
+
* localStorage.removeItem(key);
|
|
1331
|
+
* return value;
|
|
1332
|
+
* }
|
|
1333
|
+
*
|
|
1334
|
+
* removeAll(): any {
|
|
1335
|
+
* localStorage.clear();
|
|
1336
|
+
* }
|
|
1337
|
+
* }
|
|
1338
|
+
* ```
|
|
1339
|
+
*
|
|
1340
|
+
* @example
|
|
1341
|
+
* ```typescript
|
|
1342
|
+
* // In-memory storage with expiration
|
|
1343
|
+
* @AttachSessionStorage()
|
|
1344
|
+
* class ExpiringMemoryStorage implements SessionStorage {
|
|
1345
|
+
* private storage = new Map<string, { value: any; expires: number }>();
|
|
1346
|
+
*
|
|
1347
|
+
* get(key: string): any {
|
|
1348
|
+
* const item = this.storage.get(key);
|
|
1349
|
+
* if (!item) return null;
|
|
1350
|
+
*
|
|
1351
|
+
* if (Date.now() > item.expires) {
|
|
1352
|
+
* this.storage.delete(key);
|
|
1353
|
+
* return null;
|
|
1354
|
+
* }
|
|
1355
|
+
*
|
|
1356
|
+
* return item.value;
|
|
1357
|
+
* }
|
|
1358
|
+
*
|
|
1359
|
+
* set(key: string, value: any, ttl: number = 3600000): any { // 1 hour default
|
|
1360
|
+
* this.storage.set(key, {
|
|
1361
|
+
* value,
|
|
1362
|
+
* expires: Date.now() + ttl
|
|
1363
|
+
* });
|
|
1364
|
+
* return value;
|
|
1365
|
+
* }
|
|
1366
|
+
*
|
|
1367
|
+
* remove(key: string): any {
|
|
1368
|
+
* const item = this.storage.get(key);
|
|
1369
|
+
* this.storage.delete(key);
|
|
1370
|
+
* return item?.value;
|
|
1371
|
+
* }
|
|
1372
|
+
*
|
|
1373
|
+
* removeAll(): any {
|
|
1374
|
+
* this.storage.clear();
|
|
1375
|
+
* }
|
|
1376
|
+
* }
|
|
1377
|
+
* ```
|
|
1378
|
+
*
|
|
1379
|
+
* @example
|
|
1380
|
+
* ```typescript
|
|
1381
|
+
* // Database-backed storage implementation
|
|
1382
|
+
* @AttachSessionStorage()
|
|
1383
|
+
* class DatabaseStorageProvider implements SessionStorage {
|
|
1384
|
+
* constructor(private db: Database) {}
|
|
1385
|
+
*
|
|
1386
|
+
* async get(key: string): Promise<any> {
|
|
1387
|
+
* const result = await this.db.query('SELECT value FROM sessions WHERE key = ?', [key]);
|
|
1388
|
+
* return result.length ? JSON.parse(result[0].value) : null;
|
|
1389
|
+
* }
|
|
1390
|
+
*
|
|
1391
|
+
* async set(key: string, value: any): Promise<any> {
|
|
1392
|
+
* const serialized = JSON.stringify(value);
|
|
1393
|
+
* await this.db.query(
|
|
1394
|
+
* 'INSERT OR REPLACE INTO sessions (key, value) VALUES (?, ?)',
|
|
1395
|
+
* [key, serialized]
|
|
1396
|
+
* );
|
|
1397
|
+
* return value;
|
|
1398
|
+
* }
|
|
1399
|
+
*
|
|
1400
|
+
* async remove(key: string): Promise<any> {
|
|
1401
|
+
* const value = await this.get(key);
|
|
1402
|
+
* await this.db.query('DELETE FROM sessions WHERE key = ?', [key]);
|
|
1403
|
+
* return value;
|
|
1404
|
+
* }
|
|
1405
|
+
*
|
|
1406
|
+
* async removeAll(): Promise<any> {
|
|
1407
|
+
* await this.db.query('DELETE FROM sessions');
|
|
1408
|
+
* }
|
|
1409
|
+
* }
|
|
1410
|
+
* ```
|
|
1411
|
+
*
|
|
1412
|
+
* @example
|
|
1413
|
+
* ```typescript
|
|
1414
|
+
* // Testing with mock storage
|
|
1415
|
+
* @AttachSessionStorage()
|
|
1416
|
+
* class MockStorageProvider implements SessionStorage {
|
|
1417
|
+
* private mockData = new Map<string, any>();
|
|
1418
|
+
*
|
|
1419
|
+
* get(key: string): any {
|
|
1420
|
+
* console.log(`[MOCK] Getting key: ${key}`);
|
|
1421
|
+
* return this.mockData.get(key);
|
|
1422
|
+
* }
|
|
1423
|
+
*
|
|
1424
|
+
* set(key: string, value: any): any {
|
|
1425
|
+
* console.log(`[MOCK] Setting key: ${key}, value:`, value);
|
|
1426
|
+
* this.mockData.set(key, value);
|
|
1427
|
+
* return value;
|
|
1428
|
+
* }
|
|
1429
|
+
*
|
|
1430
|
+
* remove(key: string): any {
|
|
1431
|
+
* console.log(`[MOCK] Removing key: ${key}`);
|
|
1432
|
+
* const value = this.mockData.get(key);
|
|
1433
|
+
* this.mockData.delete(key);
|
|
1434
|
+
* return value;
|
|
1435
|
+
* }
|
|
1436
|
+
*
|
|
1437
|
+
* removeAll(): any {
|
|
1438
|
+
* console.log(`[MOCK] Clearing all data`);
|
|
1439
|
+
* this.mockData.clear();
|
|
1440
|
+
* }
|
|
1441
|
+
* }
|
|
1442
|
+
* ```
|
|
1443
|
+
*
|
|
1444
|
+
* @see {@link SessionStorage} - The interface that storage implementations must implement
|
|
1445
|
+
* @see {@link Manager} - The session manager that uses the attached storage
|
|
1446
|
+
* @see {@link Session} - The exported session utilities that use the attached storage
|
|
1447
|
+
* @see {@link isValidStorage} - Function used to validate storage implementations
|
|
1448
|
+
*
|
|
1449
|
+
*
|
|
1450
|
+
* @public
|
|
1451
|
+
*
|
|
1452
|
+
* @remarks
|
|
1453
|
+
* **Important Notes:**
|
|
1454
|
+
* - Only one storage implementation can be active at a time
|
|
1455
|
+
* - The decorator should be applied before any session operations are performed
|
|
1456
|
+
* - Storage validation is performed automatically - invalid implementations are ignored
|
|
1457
|
+
* - If no custom storage is attached, the system falls back to localStorage or in-memory storage
|
|
1458
|
+
* - The storage instance is created immediately when the decorator is processed
|
|
1459
|
+
*
|
|
1460
|
+
* **Best Practices:**
|
|
1461
|
+
* - Implement proper error handling in your storage methods
|
|
1462
|
+
* - Consider implementing data serialization/deserialization for complex objects
|
|
1463
|
+
* - Add logging for debugging purposes in development environments
|
|
1464
|
+
* - Use appropriate storage mechanisms based on your application's needs
|
|
1465
|
+
* - Test your storage implementation thoroughly, especially error scenarios
|
|
1466
|
+
*
|
|
1467
|
+
* **Performance Considerations:**
|
|
1468
|
+
* - Storage operations should be fast as they're used frequently
|
|
1469
|
+
* - Consider implementing caching for expensive storage operations
|
|
1470
|
+
* - Be mindful of storage size limits (especially for localStorage)
|
|
1471
|
+
* - Implement cleanup strategies for temporary data
|
|
1472
|
+
*/
|
|
1473
|
+
export declare function AttachSessionStorage(): (target: ClassConstructor<SessionStorage>) => void;
|
|
1474
|
+
export {};
|