@zolomedia/bifrost-client 1.7.74
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/L1_Foundation/L1_Foundation.js +13 -0
- package/L1_Foundation/bootstrap/bootstrap.js +11 -0
- package/L1_Foundation/bootstrap/bootstrap_hooks.js +123 -0
- package/L1_Foundation/bootstrap/bootstrap_index.js +15 -0
- package/L1_Foundation/bootstrap/bootstrap_logger.js +135 -0
- package/L1_Foundation/bootstrap/cdn_loader.js +217 -0
- package/L1_Foundation/bootstrap/module_registry.js +102 -0
- package/L1_Foundation/bootstrap/prism_loader.js +164 -0
- package/L1_Foundation/config/client_config.js +110 -0
- package/L1_Foundation/config/config.js +7 -0
- package/L1_Foundation/connection/connection.js +8 -0
- package/L1_Foundation/connection/websocket_connection.js +122 -0
- package/L1_Foundation/constants/bifrost_constants.js +284 -0
- package/L1_Foundation/constants/constants.js +7 -0
- package/L1_Foundation/logger/logger.js +10 -0
- package/L2_Handling/L2_Handling.js +15 -0
- package/L2_Handling/cache/cache.js +22 -0
- package/L2_Handling/cache/cache_constants.js +69 -0
- package/L2_Handling/cache/orchestration/cache_manager.js +299 -0
- package/L2_Handling/cache/orchestration/cache_orchestrator.js +260 -0
- package/L2_Handling/cache/orchestration/orchestration.js +12 -0
- package/L2_Handling/cache/storage/session_manager.js +289 -0
- package/L2_Handling/cache/storage/storage.js +10 -0
- package/L2_Handling/cache/storage/storage_manager.js +590 -0
- package/L2_Handling/display/composite/composite.js +13 -0
- package/L2_Handling/display/composite/dashboard_renderer.js +221 -0
- package/L2_Handling/display/composite/swiper_renderer.js +564 -0
- package/L2_Handling/display/composite/terminal_renderer.js +922 -0
- package/L2_Handling/display/composite/wizard_conditional_renderer.js +274 -0
- package/L2_Handling/display/display.js +30 -0
- package/L2_Handling/display/feedback/feedback.js +11 -0
- package/L2_Handling/display/feedback/progressbar_renderer.js +418 -0
- package/L2_Handling/display/feedback/spinner_renderer.js +246 -0
- package/L2_Handling/display/inputs/button_renderer.js +634 -0
- package/L2_Handling/display/inputs/form_renderer.js +583 -0
- package/L2_Handling/display/inputs/input_renderer.js +658 -0
- package/L2_Handling/display/inputs/inputs.js +12 -0
- package/L2_Handling/display/navigation/menu_renderer.js +206 -0
- package/L2_Handling/display/navigation/navigation.js +11 -0
- package/L2_Handling/display/navigation/navigation_renderer.js +703 -0
- package/L2_Handling/display/orchestration/orchestration.js +11 -0
- package/L2_Handling/display/orchestration/renderer.js +430 -0
- package/L2_Handling/display/orchestration/zdisplay_orchestrator.js +1759 -0
- package/L2_Handling/display/outputs/alert_renderer.js +161 -0
- package/L2_Handling/display/outputs/audio_renderer.js +94 -0
- package/L2_Handling/display/outputs/card_renderer.js +229 -0
- package/L2_Handling/display/outputs/code_renderer.js +66 -0
- package/L2_Handling/display/outputs/dl_renderer.js +131 -0
- package/L2_Handling/display/outputs/header_renderer.js +162 -0
- package/L2_Handling/display/outputs/icon_renderer.js +107 -0
- package/L2_Handling/display/outputs/image_renderer.js +145 -0
- package/L2_Handling/display/outputs/list_renderer.js +190 -0
- package/L2_Handling/display/outputs/outputs.js +19 -0
- package/L2_Handling/display/outputs/table_renderer.js +765 -0
- package/L2_Handling/display/outputs/text_renderer.js +818 -0
- package/L2_Handling/display/outputs/typography_renderer.js +293 -0
- package/L2_Handling/display/outputs/video_renderer.js +116 -0
- package/L2_Handling/display/primitives/document_structure_primitives.js +319 -0
- package/L2_Handling/display/primitives/form_primitives.js +526 -0
- package/L2_Handling/display/primitives/generic_containers.js +109 -0
- package/L2_Handling/display/primitives/interactive_primitives.js +305 -0
- package/L2_Handling/display/primitives/link_primitives.js +552 -0
- package/L2_Handling/display/primitives/lists_primitives.js +262 -0
- package/L2_Handling/display/primitives/media_primitives.js +383 -0
- package/L2_Handling/display/primitives/primitives.js +19 -0
- package/L2_Handling/display/primitives/semantic_element_primitive.js +226 -0
- package/L2_Handling/display/primitives/table_primitives.js +528 -0
- package/L2_Handling/display/primitives/typography_primitives.js +175 -0
- package/L2_Handling/display/specialized/input_request_renderer.js +467 -0
- package/L2_Handling/display/specialized/specialized.js +10 -0
- package/L2_Handling/hooks/hooks.js +9 -0
- package/L2_Handling/hooks/menu_integration.js +57 -0
- package/L2_Handling/hooks/widget_hook_manager.js +292 -0
- package/L2_Handling/message/message.js +8 -0
- package/L2_Handling/message/message_handler.js +701 -0
- package/L2_Handling/navigation/navigation.js +8 -0
- package/L2_Handling/navigation/navigation_manager.js +403 -0
- package/L2_Handling/zhooks/features/cache_live.js +287 -0
- package/L2_Handling/zhooks/features/crumbs_live.js +292 -0
- package/L2_Handling/zhooks/zhooks_manager.js +65 -0
- package/L2_Handling/zvaf/zvaf.js +8 -0
- package/L2_Handling/zvaf/zvaf_manager.js +334 -0
- package/L3_Abstraction/L3_Abstraction.js +12 -0
- package/L3_Abstraction/orchestrator/container_unwrapper.js +101 -0
- package/L3_Abstraction/orchestrator/group_renderer.js +698 -0
- package/L3_Abstraction/orchestrator/input_event_handler.js +797 -0
- package/L3_Abstraction/orchestrator/metadata_processor.js +249 -0
- package/L3_Abstraction/orchestrator/navbar_builder.js +201 -0
- package/L3_Abstraction/orchestrator/orchestrator.js +13 -0
- package/L3_Abstraction/orchestrator/wizard_gate_handler.js +360 -0
- package/L3_Abstraction/renderer/renderer.js +1 -0
- package/L3_Abstraction/session/session.js +1 -0
- package/L4_Orchestration/L4_Orchestration.js +11 -0
- package/L4_Orchestration/client/client.js +1 -0
- package/L4_Orchestration/facade/facade.js +9 -0
- package/L4_Orchestration/facade/manager_registry.js +118 -0
- package/L4_Orchestration/facade/renderer_registry.js +274 -0
- package/L4_Orchestration/lifecycle/asset_loader.js +255 -0
- package/L4_Orchestration/lifecycle/initializer.js +135 -0
- package/L4_Orchestration/lifecycle/lifecycle.js +8 -0
- package/L4_Orchestration/rendering/facade.js +94 -0
- package/L4_Orchestration/rendering/rendering.js +7 -0
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/bifrost_client.js +204 -0
- package/bifrost_core.js +1686 -0
- package/docs/ARCHITECTURE.md +111 -0
- package/docs/PROTOCOL.md +106 -0
- package/docs/RENDERERS.md +101 -0
- package/docs/SECURITY.md +92 -0
- package/package.json +24 -0
- package/syntax/prism-zconfig.js +41 -0
- package/syntax/prism-zenv.js +69 -0
- package/syntax/prism-zolo-theme.css +288 -0
- package/syntax/prism-zolo.js +380 -0
- package/syntax/prism-zschema.js +38 -0
- package/syntax/prism-zspark.js +25 -0
- package/syntax/prism-zui.js +68 -0
- package/zSys/accessibility/accessibility.js +10 -0
- package/zSys/accessibility/emoji_accessibility.js +173 -0
- package/zSys/dom/block_utils.js +122 -0
- package/zSys/dom/container_utils.js +370 -0
- package/zSys/dom/dom.js +13 -0
- package/zSys/dom/dom_utils.js +328 -0
- package/zSys/dom/encoding_utils.js +117 -0
- package/zSys/dom/style_utils.js +71 -0
- package/zSys/errors/error_display.js +299 -0
- package/zSys/errors/errors.js +10 -0
- package/zSys/theme/color_utils.js +274 -0
- package/zSys/theme/dark_mode_utils.js +272 -0
- package/zSys/theme/size_utils.js +256 -0
- package/zSys/theme/spacing_utils.js +405 -0
- package/zSys/theme/theme.js +14 -0
- package/zSys/theme/zbase.css +1735 -0
- package/zSys/theme/zbase_inject.js +161 -0
- package/zSys/theme/ztheme_utils.js +305 -0
- package/zSys/validation/error_boundary.js +201 -0
- package/zSys/validation/validation.js +11 -0
- package/zSys/validation/validation_utils.js +238 -0
- package/zSys/zSys.js +14 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* zCLI - StorageManager (Layer 0 Primitive)
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
* Unified storage interface with progressive enhancement:
|
|
7
|
+
* - Primary: IndexedDB (~50MB, async, modern browsers)
|
|
8
|
+
* - Fallback: localStorage (~5-10MB, sync, universal)
|
|
9
|
+
*
|
|
10
|
+
* Mirrors zLoader's backend cache architecture for consistency.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const storage = new StorageManager('zBifrost');
|
|
14
|
+
* await storage.init();
|
|
15
|
+
* await storage.set('key', {data: 'value'}, 'system');
|
|
16
|
+
* const data = await storage.get('key', 'system');
|
|
17
|
+
*
|
|
18
|
+
* Architecture:
|
|
19
|
+
* - Single unified interface (get/set/clear/remove)
|
|
20
|
+
* - Automatic fallback (IndexedDB → localStorage)
|
|
21
|
+
* - Namespace isolation (prevents key collisions)
|
|
22
|
+
* - Type-safe (validates JSON serialization)
|
|
23
|
+
*
|
|
24
|
+
* @version 1.6.0
|
|
25
|
+
* @since 2025-12-16
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
(function(root, factory) {
|
|
30
|
+
if (typeof define === 'function' && define.amd) {
|
|
31
|
+
define([], factory);
|
|
32
|
+
} else if (typeof module === 'object' && module.exports) {
|
|
33
|
+
module.exports = factory();
|
|
34
|
+
} else {
|
|
35
|
+
root.StorageManager = factory();
|
|
36
|
+
}
|
|
37
|
+
}(typeof self !== 'undefined' ? self : this, () => {
|
|
38
|
+
'use strict';
|
|
39
|
+
|
|
40
|
+
//
|
|
41
|
+
// Constants (imported from cache_constants for SSOT)
|
|
42
|
+
//
|
|
43
|
+
// Note: These are duplicated here for UMD module compatibility
|
|
44
|
+
// SSOT is in ../cache_constants.js
|
|
45
|
+
//
|
|
46
|
+
|
|
47
|
+
const DB_VERSION = 2; // SSOT: cache_constants.DB_VERSION (v2 drops legacy tiers)
|
|
48
|
+
const STORE_NAMES = ['rendered']; // SSOT: cache_constants.STORE_NAMES (trail only)
|
|
49
|
+
const LEGACY_STORE_NAMES = ['system', 'pinned', 'plugin']; // dropped in v2
|
|
50
|
+
|
|
51
|
+
//
|
|
52
|
+
// StorageManager Class
|
|
53
|
+
//
|
|
54
|
+
|
|
55
|
+
class StorageManager {
|
|
56
|
+
/**
|
|
57
|
+
* Create a new storage manager instance
|
|
58
|
+
*
|
|
59
|
+
* @param {string} namespace - Namespace for storage isolation (e.g., 'zBifrost')
|
|
60
|
+
* @param {Object} logger - Optional logger instance
|
|
61
|
+
*/
|
|
62
|
+
constructor(namespace = 'zBifrost', logger = null) {
|
|
63
|
+
this.namespace = namespace;
|
|
64
|
+
this.dbName = `${namespace}_cache`;
|
|
65
|
+
this.db = null;
|
|
66
|
+
this.useIndexedDB = false;
|
|
67
|
+
this.useLocalStorage = false;
|
|
68
|
+
this.initialized = false;
|
|
69
|
+
this.logger = logger || console;
|
|
70
|
+
|
|
71
|
+
// In-memory session cache (never persisted)
|
|
72
|
+
this.sessionCache = new Map();
|
|
73
|
+
|
|
74
|
+
this.logger.debug(`[StorageManager] Created with namespace: ${namespace}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Initialize storage (must be called before use)
|
|
79
|
+
*
|
|
80
|
+
* Attempts IndexedDB first, falls back to localStorage if unavailable.
|
|
81
|
+
*
|
|
82
|
+
* @returns {Promise<boolean>} Success status
|
|
83
|
+
*/
|
|
84
|
+
async init() {
|
|
85
|
+
if (this.initialized) {
|
|
86
|
+
this.logger.debug('[StorageManager] Already initialized');
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Try IndexedDB first (modern, async, large capacity)
|
|
91
|
+
try {
|
|
92
|
+
await this._initIndexedDB();
|
|
93
|
+
this.useIndexedDB = true;
|
|
94
|
+
this.logger.debug('[StorageManager] Initialized with IndexedDB');
|
|
95
|
+
this.initialized = true;
|
|
96
|
+
return true;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.logger.warn('[StorageManager] [WARN] IndexedDB unavailable, falling back to localStorage:', error.message);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Fallback to localStorage (universal, sync, limited capacity)
|
|
102
|
+
try {
|
|
103
|
+
this._initLocalStorage();
|
|
104
|
+
this.useLocalStorage = true;
|
|
105
|
+
this.logger.debug('[StorageManager] Initialized with localStorage (fallback)');
|
|
106
|
+
this.initialized = true;
|
|
107
|
+
return true;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
this.logger.error('[StorageManager] [ERROR] Both IndexedDB and localStorage unavailable:', error);
|
|
110
|
+
this.initialized = false;
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Initialize IndexedDB
|
|
117
|
+
*
|
|
118
|
+
* Creates object stores for each cache tier (system, pinned, plugin, rendered).
|
|
119
|
+
* Session cache is in-memory only and not persisted.
|
|
120
|
+
*
|
|
121
|
+
* @private
|
|
122
|
+
* @returns {Promise<void>}
|
|
123
|
+
*/
|
|
124
|
+
_initIndexedDB() {
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
if (!window.indexedDB) {
|
|
127
|
+
reject(new Error('IndexedDB not supported'));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const request = indexedDB.open(this.dbName, DB_VERSION);
|
|
132
|
+
|
|
133
|
+
request.onerror = () => {
|
|
134
|
+
reject(new Error(`IndexedDB open failed: ${request.error}`));
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
request.onsuccess = () => {
|
|
138
|
+
this.db = request.result;
|
|
139
|
+
this.logger.info(`[StorageManager] IndexedDB opened: ${this.dbName}`);
|
|
140
|
+
resolve();
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
request.onupgradeneeded = (event) => {
|
|
144
|
+
const db = event.target.result;
|
|
145
|
+
|
|
146
|
+
// Create the trail store (rendered pages)
|
|
147
|
+
STORE_NAMES.forEach(storeName => {
|
|
148
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
149
|
+
const store = db.createObjectStore(storeName, { keyPath: 'key' });
|
|
150
|
+
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
151
|
+
this.logger.info(`[StorageManager] Created object store: ${storeName}`);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// v2: drop the legacy mirror-of-zLoader stores — the client no longer
|
|
156
|
+
// caches UI files / configs / plugins (the server is the cache of record).
|
|
157
|
+
LEGACY_STORE_NAMES.forEach(storeName => {
|
|
158
|
+
if (db.objectStoreNames.contains(storeName)) {
|
|
159
|
+
db.deleteObjectStore(storeName);
|
|
160
|
+
this.logger.info(`[StorageManager] Dropped legacy object store: ${storeName}`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Initialize localStorage (fallback)
|
|
169
|
+
*
|
|
170
|
+
* Validates localStorage is available and writable.
|
|
171
|
+
*
|
|
172
|
+
* @private
|
|
173
|
+
*/
|
|
174
|
+
_initLocalStorage() {
|
|
175
|
+
if (typeof localStorage === 'undefined') {
|
|
176
|
+
throw new Error('localStorage not supported');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Test write/read
|
|
180
|
+
const testKey = `${this.namespace}_test`;
|
|
181
|
+
try {
|
|
182
|
+
localStorage.setItem(testKey, 'test');
|
|
183
|
+
localStorage.removeItem(testKey);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
throw new Error(`localStorage not writable: ${error.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get value from storage
|
|
191
|
+
*
|
|
192
|
+
* @param {string} key - Storage key
|
|
193
|
+
* @param {string} tier - Cache tier ('system', 'pinned', 'plugin', 'session', 'rendered')
|
|
194
|
+
* @returns {Promise<any|null>} Stored value or null if not found
|
|
195
|
+
*/
|
|
196
|
+
async get(key, tier = 'system') {
|
|
197
|
+
if (!this.initialized) {
|
|
198
|
+
this.logger.warn('[StorageManager] Not initialized, call init() first');
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Session cache is in-memory only
|
|
203
|
+
if (tier === 'session') {
|
|
204
|
+
return this.sessionCache.get(key) || null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Try persistent storage
|
|
208
|
+
if (this.useIndexedDB) {
|
|
209
|
+
return await this._getFromIndexedDB(key, tier);
|
|
210
|
+
} else if (this.useLocalStorage) {
|
|
211
|
+
return this._getFromLocalStorage(key, tier);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Set value in storage
|
|
219
|
+
*
|
|
220
|
+
* @param {string} key - Storage key
|
|
221
|
+
* @param {any} value - Value to store (must be JSON-serializable)
|
|
222
|
+
* @param {string} tier - Cache tier ('system', 'pinned', 'plugin', 'session', 'rendered')
|
|
223
|
+
* @param {number} ttl - Time-to-live in milliseconds (optional, for cache validation)
|
|
224
|
+
* @returns {Promise<boolean>} Success status
|
|
225
|
+
*/
|
|
226
|
+
async set(key, value, tier = 'system', ttl = null) {
|
|
227
|
+
if (!this.initialized) {
|
|
228
|
+
this.logger.warn('[StorageManager] Not initialized, call init() first');
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Validate JSON serialization
|
|
233
|
+
try {
|
|
234
|
+
JSON.stringify(value);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
this.logger.error('[StorageManager] Value not JSON-serializable:', error);
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Session cache is in-memory only
|
|
241
|
+
if (tier === 'session') {
|
|
242
|
+
this.sessionCache.set(key, value);
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Add metadata
|
|
247
|
+
const entry = {
|
|
248
|
+
key: key,
|
|
249
|
+
value: value,
|
|
250
|
+
timestamp: Date.now(),
|
|
251
|
+
ttl: ttl
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// Store persistently
|
|
255
|
+
if (this.useIndexedDB) {
|
|
256
|
+
return await this._setInIndexedDB(entry, tier);
|
|
257
|
+
} else if (this.useLocalStorage) {
|
|
258
|
+
return this._setInLocalStorage(entry, tier);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Remove value from storage
|
|
266
|
+
*
|
|
267
|
+
* @param {string} key - Storage key
|
|
268
|
+
* @param {string} tier - Cache tier
|
|
269
|
+
* @returns {Promise<boolean>} Success status
|
|
270
|
+
*/
|
|
271
|
+
async remove(key, tier = 'system') {
|
|
272
|
+
if (!this.initialized) {
|
|
273
|
+
this.logger.warn('[StorageManager] Not initialized, call init() first');
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (tier === 'session') {
|
|
278
|
+
return this.sessionCache.delete(key);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (this.useIndexedDB) {
|
|
282
|
+
return await this._removeFromIndexedDB(key, tier);
|
|
283
|
+
} else if (this.useLocalStorage) {
|
|
284
|
+
return this._removeFromLocalStorage(key, tier);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Clear entire cache tier
|
|
292
|
+
*
|
|
293
|
+
* @param {string} tier - Cache tier to clear (or 'all' to clear everything)
|
|
294
|
+
* @returns {Promise<boolean>} Success status
|
|
295
|
+
*/
|
|
296
|
+
async clear(tier = 'all') {
|
|
297
|
+
if (!this.initialized) {
|
|
298
|
+
this.logger.warn('[StorageManager] Not initialized, call init() first');
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const tiersToClean = tier === 'all' ? ['session', 'rendered'] : [tier];
|
|
303
|
+
|
|
304
|
+
for (const t of tiersToClean) {
|
|
305
|
+
if (t === 'session') {
|
|
306
|
+
this.sessionCache.clear();
|
|
307
|
+
} else if (this.useIndexedDB) {
|
|
308
|
+
await this._clearIndexedDBStore(t);
|
|
309
|
+
} else if (this.useLocalStorage) {
|
|
310
|
+
this._clearLocalStorageTier(t);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.logger.info(`[StorageManager] Cleared tier(s): ${tier}`);
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get all keys in a tier
|
|
320
|
+
*
|
|
321
|
+
* @param {string} tier - Cache tier
|
|
322
|
+
* @returns {Promise<string[]>} Array of keys
|
|
323
|
+
*/
|
|
324
|
+
async keys(tier = 'system') {
|
|
325
|
+
if (!this.initialized) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (tier === 'session') {
|
|
330
|
+
return Array.from(this.sessionCache.keys());
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (this.useIndexedDB) {
|
|
334
|
+
return await this._getIndexedDBKeys(tier);
|
|
335
|
+
} else if (this.useLocalStorage) {
|
|
336
|
+
return this._getLocalStorageKeys(tier);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return [];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get all entries in a tier as [{ key, value, timestamp }]. Used for
|
|
344
|
+
* LRU eviction and trail diagnostics.
|
|
345
|
+
*
|
|
346
|
+
* @param {string} tier - Cache tier
|
|
347
|
+
* @returns {Promise<Array<{key:string, value:any, timestamp:number}>>}
|
|
348
|
+
*/
|
|
349
|
+
async getAll(tier = 'rendered') {
|
|
350
|
+
if (!this.initialized) {
|
|
351
|
+
return [];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (tier === 'session') {
|
|
355
|
+
return Array.from(this.sessionCache.entries()).map(([key, value]) => ({
|
|
356
|
+
key, value, timestamp: 0
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (this.useIndexedDB) {
|
|
361
|
+
return await this._getAllFromIndexedDB(tier);
|
|
362
|
+
} else if (this.useLocalStorage) {
|
|
363
|
+
return this._getAllFromLocalStorage(tier);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
//
|
|
370
|
+
// IndexedDB Private Methods
|
|
371
|
+
//
|
|
372
|
+
|
|
373
|
+
_getFromIndexedDB(key, tier) {
|
|
374
|
+
return new Promise((resolve, _reject) => {
|
|
375
|
+
try {
|
|
376
|
+
const transaction = this.db.transaction([tier], 'readonly');
|
|
377
|
+
const store = transaction.objectStore(tier);
|
|
378
|
+
const request = store.get(key);
|
|
379
|
+
|
|
380
|
+
request.onsuccess = () => {
|
|
381
|
+
const entry = request.result;
|
|
382
|
+
resolve(entry ? entry.value : null);
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
request.onerror = () => {
|
|
386
|
+
this.logger.error('[StorageManager] IndexedDB get error:', request.error);
|
|
387
|
+
resolve(null);
|
|
388
|
+
};
|
|
389
|
+
} catch (error) {
|
|
390
|
+
this.logger.error('[StorageManager] IndexedDB get exception:', error);
|
|
391
|
+
resolve(null);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
_setInIndexedDB(entry, tier) {
|
|
397
|
+
return new Promise((resolve, _reject) => {
|
|
398
|
+
try {
|
|
399
|
+
const transaction = this.db.transaction([tier], 'readwrite');
|
|
400
|
+
const store = transaction.objectStore(tier);
|
|
401
|
+
const request = store.put(entry);
|
|
402
|
+
|
|
403
|
+
request.onsuccess = () => resolve(true);
|
|
404
|
+
request.onerror = () => {
|
|
405
|
+
this.logger.error('[StorageManager] IndexedDB set error:', request.error);
|
|
406
|
+
resolve(false);
|
|
407
|
+
};
|
|
408
|
+
} catch (error) {
|
|
409
|
+
this.logger.error('[StorageManager] IndexedDB set exception:', error);
|
|
410
|
+
resolve(false);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
_removeFromIndexedDB(key, tier) {
|
|
416
|
+
return new Promise((resolve) => {
|
|
417
|
+
try {
|
|
418
|
+
const transaction = this.db.transaction([tier], 'readwrite');
|
|
419
|
+
const store = transaction.objectStore(tier);
|
|
420
|
+
const request = store.delete(key);
|
|
421
|
+
|
|
422
|
+
request.onsuccess = () => resolve(true);
|
|
423
|
+
request.onerror = () => resolve(false);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
resolve(false);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
_clearIndexedDBStore(tier) {
|
|
431
|
+
return new Promise((resolve) => {
|
|
432
|
+
try {
|
|
433
|
+
const transaction = this.db.transaction([tier], 'readwrite');
|
|
434
|
+
const store = transaction.objectStore(tier);
|
|
435
|
+
const request = store.clear();
|
|
436
|
+
|
|
437
|
+
request.onsuccess = () => resolve(true);
|
|
438
|
+
request.onerror = () => resolve(false);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
resolve(false);
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
_getIndexedDBKeys(tier) {
|
|
446
|
+
return new Promise((resolve) => {
|
|
447
|
+
try {
|
|
448
|
+
const transaction = this.db.transaction([tier], 'readonly');
|
|
449
|
+
const store = transaction.objectStore(tier);
|
|
450
|
+
const request = store.getAllKeys();
|
|
451
|
+
|
|
452
|
+
request.onsuccess = () => resolve(request.result || []);
|
|
453
|
+
request.onerror = () => resolve([]);
|
|
454
|
+
} catch (error) {
|
|
455
|
+
resolve([]);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
_getAllFromIndexedDB(tier) {
|
|
461
|
+
return new Promise((resolve) => {
|
|
462
|
+
try {
|
|
463
|
+
const transaction = this.db.transaction([tier], 'readonly');
|
|
464
|
+
const store = transaction.objectStore(tier);
|
|
465
|
+
const request = store.getAll();
|
|
466
|
+
|
|
467
|
+
request.onsuccess = () => resolve(request.result || []);
|
|
468
|
+
request.onerror = () => resolve([]);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
resolve([]);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
//
|
|
476
|
+
// localStorage Private Methods
|
|
477
|
+
//
|
|
478
|
+
|
|
479
|
+
_makeLocalStorageKey(key, tier) {
|
|
480
|
+
return `${this.namespace}_${tier}_${key}`;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
_getFromLocalStorage(key, tier) {
|
|
484
|
+
try {
|
|
485
|
+
const storageKey = this._makeLocalStorageKey(key, tier);
|
|
486
|
+
const json = localStorage.getItem(storageKey);
|
|
487
|
+
if (!json) {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const entry = JSON.parse(json);
|
|
492
|
+
return entry.value || null;
|
|
493
|
+
} catch (error) {
|
|
494
|
+
this.logger.error('[StorageManager] localStorage get error:', error);
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
_setInLocalStorage(entry, tier) {
|
|
500
|
+
try {
|
|
501
|
+
const storageKey = this._makeLocalStorageKey(entry.key, tier);
|
|
502
|
+
localStorage.setItem(storageKey, JSON.stringify(entry));
|
|
503
|
+
return true;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
this.logger.error('[StorageManager] localStorage set error (quota exceeded?):', error);
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
_removeFromLocalStorage(key, tier) {
|
|
511
|
+
try {
|
|
512
|
+
const storageKey = this._makeLocalStorageKey(key, tier);
|
|
513
|
+
localStorage.removeItem(storageKey);
|
|
514
|
+
return true;
|
|
515
|
+
} catch (error) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
_clearLocalStorageTier(tier) {
|
|
521
|
+
try {
|
|
522
|
+
const prefix = `${this.namespace}_${tier}_`;
|
|
523
|
+
const keysToRemove = [];
|
|
524
|
+
|
|
525
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
526
|
+
const key = localStorage.key(i);
|
|
527
|
+
if (key && key.startsWith(prefix)) {
|
|
528
|
+
keysToRemove.push(key);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
533
|
+
return true;
|
|
534
|
+
} catch (error) {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
_getLocalStorageKeys(tier) {
|
|
540
|
+
try {
|
|
541
|
+
const prefix = `${this.namespace}_${tier}_`;
|
|
542
|
+
const keys = [];
|
|
543
|
+
|
|
544
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
545
|
+
const key = localStorage.key(i);
|
|
546
|
+
if (key && key.startsWith(prefix)) {
|
|
547
|
+
// Remove prefix to get original key
|
|
548
|
+
keys.push(key.substring(prefix.length));
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return keys;
|
|
553
|
+
} catch (error) {
|
|
554
|
+
return [];
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
_getAllFromLocalStorage(tier) {
|
|
559
|
+
try {
|
|
560
|
+
const prefix = `${this.namespace}_${tier}_`;
|
|
561
|
+
const out = [];
|
|
562
|
+
|
|
563
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
564
|
+
const storageKey = localStorage.key(i);
|
|
565
|
+
if (storageKey && storageKey.startsWith(prefix)) {
|
|
566
|
+
try {
|
|
567
|
+
const entry = JSON.parse(localStorage.getItem(storageKey));
|
|
568
|
+
out.push({
|
|
569
|
+
key: storageKey.substring(prefix.length),
|
|
570
|
+
value: entry.value,
|
|
571
|
+
timestamp: entry.timestamp || 0
|
|
572
|
+
});
|
|
573
|
+
} catch (_e) { /* skip malformed entry */ }
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return out;
|
|
578
|
+
} catch (error) {
|
|
579
|
+
return [];
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
//
|
|
585
|
+
// Export
|
|
586
|
+
//
|
|
587
|
+
|
|
588
|
+
return StorageManager;
|
|
589
|
+
}));
|
|
590
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite Rendering Module Barrel Export
|
|
3
|
+
*
|
|
4
|
+
* Complex composite components (dashboards, terminals, swipers, wizards).
|
|
5
|
+
*
|
|
6
|
+
* @module rendering/composite
|
|
7
|
+
* @layer 3 (Composite Rendering)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export * from './dashboard_renderer.js';
|
|
11
|
+
export * from './swiper_renderer.js';
|
|
12
|
+
export * from './terminal_renderer.js';
|
|
13
|
+
export * from './wizard_conditional_renderer.js';
|