fritreefw commited on
Commit
c8b703b
·
verified ·
1 Parent(s): 5acc5da

Delete storage.js

Browse files
Files changed (1) hide show
  1. storage.js +0 -599
storage.js DELETED
@@ -1,599 +0,0 @@
1
- // ============================================================================
2
- // File: storage.js
3
- // ============================================================================
4
-
5
- (global => {
6
- 'use strict';
7
-
8
- const DB_NAME = "FritreeEnterpriseDB";
9
- const DB_VERSION = 3;
10
-
11
- const STORES = {
12
- MEDIA: "media_assets",
13
- HISTORY: "campaign_history",
14
- SYSTEM: "system_metadata"
15
- };
16
-
17
- // Keys monitored under the Double-Anchor Signature Integrity Verification (SIS)
18
- // Enforces strict anti-tampering verification for level metrics, cards, and USD balances
19
- const SECURE_SIS_KEYS = new Set([
20
- "acc_userLevel",
21
- "acc_userXP",
22
- "acc_lifetimeFb",
23
- "acc_lifetimeWa",
24
- "acc_lifetimeTasks",
25
- "local_previous_posts",
26
- "wa_connected",
27
- "wa_sent_today",
28
- "acc_fbShares",
29
- "acc_waShares",
30
- "userPoints" // Represents USD balance
31
- ]);
32
-
33
- // Constant validation salt tied to SHA-256 integrity seal
34
- const INTEGRITY_SEAL_SALT = "FritreeSymmetricIntegritySeal_WebCrypto_Strict_SHA256_Production_Salt_2026";
35
-
36
- // Configuration keys stored via standard WebCrypto GCM encryption envelopes
37
- const LIGHTWEIGHT_CONFIG_KEYS = new Set([
38
- "userPoints",
39
- "userPointsSig",
40
- "fritree_account_id",
41
- "acc_fbShares",
42
- "acc_fbShares_sig",
43
- "acc_waShares",
44
- "acc_waShares_sig",
45
- "acc_userLevel",
46
- "acc_userXP",
47
- "acc_lifetimeFb",
48
- "acc_lifetimeWa",
49
- "acc_lifetimeTasks",
50
- "acc_activeSub",
51
- "acc_subExpiry",
52
- "local_sleep_intervals",
53
- "local_protection_settings",
54
- "local_shield_config_matrix",
55
- "wa_connected",
56
- "wa_phone_number",
57
- "wa_profile_name",
58
- "wa_sent_today"
59
- ]);
60
-
61
- let dbInstance = null;
62
- let isDbInitializing = false;
63
- const dbInitPromiseResolvers = [];
64
-
65
- // ============================================================================
66
- // Asynchronous Execution Queue to Mitigate Database Write Collisions
67
- // ============================================================================
68
- class AsyncWriteQueue {
69
- constructor() {
70
- this.queue = [];
71
- this.isProcessing = false;
72
- }
73
-
74
- enqueue(task) {
75
- return new Promise((resolve, reject) => {
76
- this.queue.push({ task, resolve, reject });
77
- this.processNext();
78
- });
79
- }
80
-
81
- async processNext() {
82
- if (this.isProcessing || this.queue.length === 0) return;
83
- this.isProcessing = true;
84
-
85
- const { task, resolve, reject } = this.queue.shift();
86
- try {
87
- const result = await task();
88
- resolve(result);
89
- } catch (err) {
90
- reject(err);
91
- } finally {
92
- this.isProcessing = false;
93
- this.processNext();
94
- }
95
- }
96
- }
97
-
98
- const storageWriteQueue = new AsyncWriteQueue();
99
-
100
- // ============================================================================
101
- // IndexedDB Connection Lifecycles
102
- // ============================================================================
103
- function initIndexedDB() {
104
- if (dbInstance) return Promise.resolve(dbInstance);
105
- if (isDbInitializing) {
106
- return new Promise((resolve, reject) => {
107
- dbInitPromiseResolvers.push({ resolve, reject });
108
- });
109
- }
110
-
111
- isDbInitializing = true;
112
- return new Promise((resolve, reject) => {
113
- const request = indexedDB.open(DB_NAME, DB_VERSION);
114
-
115
- request.onupgradeneeded = event => {
116
- const db = event.target.result;
117
- Object.values(STORES).forEach(storeName => {
118
- if (!db.objectStoreNames.contains(storeName)) {
119
- db.createObjectStore(storeName, { keyPath: "id" });
120
- }
121
- });
122
- };
123
-
124
- request.onsuccess = event => {
125
- dbInstance = event.target.result;
126
- isDbInitializing = false;
127
- resolve(dbInstance);
128
-
129
- dbInitPromiseResolvers.forEach(r => r.resolve(dbInstance));
130
- dbInitPromiseResolvers.length = 0;
131
- };
132
-
133
- request.onerror = event => {
134
- isDbInitializing = false;
135
- reject(event.target.error);
136
-
137
- dbInitPromiseResolvers.forEach(r => r.reject(event.target.error));
138
- dbInitPromiseResolvers.length = 0;
139
- };
140
- });
141
- }
142
-
143
- function base64ToBlob(base64Data, mimeType) {
144
- const byteCharacters = atob(base64Data.split(",")[1] || base64Data);
145
- const byteArrays = [];
146
-
147
- for (let offset = 0; offset < byteCharacters.length; offset += 512) {
148
- const slice = byteCharacters.slice(offset, offset + 512);
149
- const byteNumbers = new Array(slice.length);
150
- for (let i = 0; i < slice.length; i++) {
151
- byteNumbers[i] = slice.charCodeAt(i);
152
- }
153
- const byteArray = new Uint8Array(byteNumbers);
154
- byteArrays.push(byteArray);
155
- }
156
-
157
- return new Blob(byteArrays, { type: mimeType });
158
- }
159
-
160
- function blobToBase64(blob) {
161
- return new Promise((resolve, reject) => {
162
- const reader = new FileReader();
163
- reader.onloadend = () => resolve(reader.result);
164
- reader.onerror = reject;
165
- reader.readAsDataURL(blob);
166
- });
167
- }
168
-
169
- async function writeIndexedDBEntry(storeName, key, encryptedValue) {
170
- const db = await initIndexedDB();
171
- return new Promise((resolve, reject) => {
172
- const transaction = db.transaction([storeName], "readwrite");
173
- const store = transaction.objectStore(storeName);
174
-
175
- const putRequest = store.put({ id: key, payload: encryptedValue, updated_at: Date.now() });
176
-
177
- putRequest.onsuccess = () => resolve(true);
178
- putRequest.onerror = e => reject(e.target.error);
179
- });
180
- }
181
-
182
- async function readIndexedDBEntry(storeName, key) {
183
- const db = await initIndexedDB();
184
- return new Promise(resolve => {
185
- const transaction = db.transaction([storeName], "readonly");
186
- const store = transaction.objectStore(storeName);
187
- const getRequest = store.get(key);
188
-
189
- getRequest.onsuccess = e => {
190
- const record = e.target.result;
191
- resolve(record ? record.payload : null);
192
- };
193
-
194
- getRequest.onerror = () => {
195
- resolve(null);
196
- };
197
- });
198
- }
199
-
200
- async function deleteIndexedDBEntry(storeName, key) {
201
- const db = await initIndexedDB();
202
- return new Promise((resolve, reject) => {
203
- const transaction = db.transaction([storeName], "readwrite");
204
- const store = transaction.objectStore(storeName);
205
- const deleteRequest = store.delete(key);
206
-
207
- deleteRequest.onsuccess = () => resolve(true);
208
- deleteRequest.onerror = e => reject(e.target.error);
209
- });
210
- }
211
-
212
- function routeIndexedDBStore(key) {
213
- if (key === "campaignHistoryData") {
214
- return STORES.HISTORY;
215
- } else if (key === "local_previous_posts") {
216
- return STORES.MEDIA;
217
- }
218
- return STORES.SYSTEM;
219
- }
220
-
221
- // ============================================================================
222
- // Double-Anchor Secondary Verification Anchors
223
- // ============================================================================
224
- async function computeSecureSymmetricChecksum(key, data) {
225
- let stringData = "";
226
- if (typeof data === 'number') {
227
- // Precise floating-point serialization lock for USD to avoid numeric evaluation rounding errors
228
- stringData = key === 'userPoints' ? data.toFixed(2) : String(data);
229
- } else if (typeof data === 'object') {
230
- stringData = JSON.stringify(data);
231
- } else {
232
- stringData = String(data);
233
- }
234
-
235
- const payloadString = `${key}:${stringData}:${INTEGRITY_SEAL_SALT}`;
236
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') {
237
- return await FritreeCrypto.sha256(payloadString);
238
- }
239
-
240
- let hash = 0;
241
- const inputStr = payloadString;
242
- for (let i = 0; i < inputStr.length; i++) {
243
- const char = inputStr.charCodeAt(i);
244
- hash = ((hash << 5) - hash) + char;
245
- hash |= 0;
246
- }
247
- return "fallback_sha256_" + Math.abs(hash).toString(16);
248
- }
249
-
250
- async function writeSecondarySignatureAnchor(key, signature) {
251
- const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`);
252
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
253
- const payload = {};
254
- payload[obfuscatedSigKey] = signature;
255
- return new Promise(res => chrome.storage.local.set(payload, () => res(true)));
256
- } else {
257
- localStorage.setItem(syncHashKey(`sis_anchor_sig_${key}`), signature);
258
- return true;
259
- }
260
- }
261
-
262
- async function readSecondarySignatureAnchor(key) {
263
- const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`);
264
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
265
- return new Promise(res => {
266
- chrome.storage.local.get(obfuscatedSigKey, payload => {
267
- res(payload[obfuscatedSigKey] || null);
268
- });
269
- });
270
- } else {
271
- return localStorage.getItem(syncHashKey(`sis_anchor_sig_${key}`)) || null;
272
- }
273
- }
274
-
275
- async function deleteSecondarySignatureAnchor(key) {
276
- const obfuscatedSigKey = await asyncHashKey(`sis_anchor_sig_${key}`);
277
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
278
- return new Promise(res => chrome.storage.local.remove(obfuscatedSigKey, () => res(true)));
279
- } else {
280
- localStorage.removeItem(syncHashKey(`sis_anchor_sig_${key}`));
281
- return true;
282
- }
283
- }
284
-
285
- // ============================================================================
286
- // Storage Interface Operations
287
- // ============================================================================
288
- const UnifiedStorageManager = {
289
- set: function(key, value) {
290
- return storageWriteQueue.enqueue(async () => {
291
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.validateData === 'function') {
292
- if (!FritreeCrypto.validateData(value, key)) {
293
- return false;
294
- }
295
- }
296
-
297
- // Compile and seal state signatures across local stores
298
- if (SECURE_SIS_KEYS.has(key)) {
299
- const dynamicSig = await computeSecureSymmetricChecksum(key, value);
300
- await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, dynamicSig);
301
- await writeSecondarySignatureAnchor(key, dynamicSig);
302
-
303
- // Secure dual backup points to facilitate self-healing recovery
304
- await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`, value);
305
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') {
306
- await FritreeCrypto.setStorage(`sis_backup_${key}`, value);
307
- }
308
- }
309
-
310
- // Directly route raw Blob files to the IndexedDB media store
311
- if (value instanceof Blob) {
312
- try {
313
- const targetStore = STORES.MEDIA;
314
- await writeIndexedDBEntry(targetStore, key, value);
315
- return true;
316
- } catch (dbErr) {
317
- console.error("[Fritree Storage] Binary media write failure:", dbErr);
318
- return false;
319
- }
320
- }
321
-
322
- // Handle standard extension configurations via Chrome Local Storage
323
- if (LIGHTWEIGHT_CONFIG_KEYS.has(key)) {
324
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') {
325
- return await FritreeCrypto.setStorage(key, value);
326
- }
327
-
328
- const localBackupKey = syncHashKey(key);
329
- const plainSerialized = JSON.stringify(value);
330
- const localMockCipher = btoa(unescape(encodeURIComponent(plainSerialized)));
331
- localStorage.setItem(localBackupKey, `fte_fallback_enc:${localMockCipher}`);
332
- return true;
333
- }
334
-
335
- // Process high-capacity campaign histories and template registries
336
- const targetStore = routeIndexedDBStore(key);
337
- try {
338
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') {
339
- await FritreeCrypto.setStorage(key, value);
340
- } else {
341
- throw new Error("Cryptographic module is offline.");
342
- }
343
-
344
- const obfuscatedKey = await asyncHashKey(key);
345
- const freshCipher = await new Promise(res => {
346
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
347
- chrome.storage.local.get(obfuscatedKey, payload => res(payload[obfuscatedKey]));
348
- } else {
349
- res(localStorage.getItem(obfuscatedKey));
350
- }
351
- });
352
-
353
- if (freshCipher) {
354
- await writeIndexedDBEntry(targetStore, key, freshCipher);
355
-
356
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
357
- chrome.storage.local.remove(obfuscatedKey);
358
- } else {
359
- localStorage.removeItem(obfuscatedKey);
360
- }
361
- return true;
362
- }
363
- } catch (dbErr) {
364
- console.warn("[Fritree Storage] IndexedDB route missed. Fallback to LocalStorage:", dbErr);
365
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') {
366
- return await FritreeCrypto.setStorage(key, value);
367
- }
368
- }
369
- return false;
370
- });
371
- },
372
-
373
- get: function(key, defaultValue = null) {
374
- return new Promise(async resolve => {
375
- try {
376
- let decryptedValue = null;
377
-
378
- if (LIGHTWEIGHT_CONFIG_KEYS.has(key)) {
379
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.getStorage === 'function') {
380
- decryptedValue = await FritreeCrypto.getStorage(key, defaultValue);
381
- } else {
382
- const raw = localStorage.getItem(syncHashKey(key));
383
- if (raw) {
384
- if (raw.startsWith("fte_fallback_enc:")) {
385
- const cipherPart = raw.slice("fte_fallback_enc:".length);
386
- const plain = decodeURIComponent(escape(atob(cipherPart)));
387
- decryptedValue = JSON.parse(plain);
388
- } else {
389
- decryptedValue = JSON.parse(raw);
390
- }
391
- } else {
392
- decryptedValue = defaultValue;
393
- }
394
- }
395
- } else {
396
- const isMediaKey = key.startsWith("media_blob_") || key.startsWith("media_thumb_") || key.startsWith("media_") || key.startsWith("rot_media_") || key.startsWith("wa_composer_");
397
- const targetStore = isMediaKey ? STORES.MEDIA : routeIndexedDBStore(key);
398
- const cipherPacket = await readIndexedDBEntry(targetStore, key);
399
-
400
- if (cipherPacket === null) {
401
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.getStorage === 'function') {
402
- decryptedValue = await FritreeCrypto.getStorage(key, defaultValue);
403
- } else {
404
- decryptedValue = defaultValue;
405
- }
406
- } else {
407
- if (cipherPacket instanceof Blob) {
408
- resolve(cipherPacket);
409
- return;
410
- }
411
-
412
- const obfuscatedKey = await asyncHashKey(key);
413
- const tempPayload = {};
414
- tempPayload[obfuscatedKey] = cipherPacket;
415
-
416
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
417
- await new Promise(r => chrome.storage.local.set(tempPayload, r));
418
- decryptedValue = await FritreeCrypto.getStorage(key, defaultValue);
419
- chrome.storage.local.remove(obfuscatedKey);
420
- } else {
421
- localStorage.setItem(obfuscatedKey, cipherPacket);
422
- decryptedValue = await FritreeCrypto.getStorage(key, defaultValue);
423
- localStorage.removeItem(obfuscatedKey);
424
- }
425
- }
426
- }
427
-
428
- // Run the Double-Anchor verification and reject external settings tampering
429
- if (SECURE_SIS_KEYS.has(key) && decryptedValue !== null && decryptedValue !== defaultValue) {
430
- const actualComputedSig = await computeSecureSymmetricChecksum(key, decryptedValue);
431
-
432
- const savedAnchorASig = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`);
433
- const savedAnchorBSig = await readSecondarySignatureAnchor(key);
434
-
435
- const isAnchorAValid = savedAnchorASig === actualComputedSig;
436
- const isAnchorBValid = savedAnchorBSig === actualComputedSig;
437
-
438
- if (!isAnchorAValid || !isAnchorBValid) {
439
- console.warn(`[Fritree Storage] Security Guard: Tampering detected for key: "${key}"! Attempting secure recovery...`);
440
-
441
- let recoveredValue = null;
442
-
443
- // Try to recover from IndexedDB Backup (Anchor A)
444
- const backupA = await readIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`);
445
- if (backupA !== null) {
446
- const sigA = await computeSecureSymmetricChecksum(key, backupA);
447
- if (sigA === savedAnchorASig) {
448
- recoveredValue = backupA;
449
- console.log(`[Fritree Storage] Restored tampered "${key}" from secure IndexedDB backup.`);
450
- }
451
- }
452
-
453
- // Try to recover from LocalStorage Backup (Anchor B) if Anchor A was altered
454
- if (recoveredValue === null) {
455
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.getStorage === 'function') {
456
- const backupB = await FritreeCrypto.getStorage(`sis_backup_${key}`, null);
457
- if (backupB !== null) {
458
- const sigB = await computeSecureSymmetricChecksum(key, backupB);
459
- if (sigB === savedAnchorBSig) {
460
- recoveredValue = backupB;
461
- console.log(`[Fritree Storage] Restored tampered "${key}" from secure LocalStorage backup.`);
462
- }
463
- }
464
- }
465
- }
466
-
467
- if (recoveredValue !== null) {
468
- decryptedValue = recoveredValue;
469
- // Re-sync signatures
470
- const restoredSig = await computeSecureSymmetricChecksum(key, recoveredValue);
471
- await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, restoredSig);
472
- await writeSecondarySignatureAnchor(key, restoredSig);
473
- } else {
474
- // If both backups are compromised, reject manipulation and revert to safe default state
475
- console.error(`[Fritree Storage] Critical Violation: Recovery failed for "${key}". Reverting to safe default.`);
476
- decryptedValue = defaultValue;
477
-
478
- const defaultSig = await computeSecureSymmetricChecksum(key, defaultValue);
479
- await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`, defaultSig);
480
- await writeSecondarySignatureAnchor(key, defaultSig);
481
- await writeIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`, defaultValue);
482
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.setStorage === 'function') {
483
- await FritreeCrypto.setStorage(`sis_backup_${key}`, defaultValue);
484
- }
485
- }
486
- }
487
- }
488
-
489
- resolve(decryptedValue !== null ? decryptedValue : defaultValue);
490
-
491
- } catch (e) {
492
- console.error(`[Fritree Storage] Key retrieval failure for "${key}":`, e);
493
- resolve(defaultValue);
494
- }
495
- });
496
- },
497
-
498
- remove: function(key) {
499
- return storageWriteQueue.enqueue(async () => {
500
- const obfuscatedKey = await asyncHashKey(key);
501
-
502
- localStorage.removeItem(syncHashKey(key));
503
- localStorage.removeItem(obfuscatedKey);
504
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
505
- chrome.storage.local.remove(obfuscatedKey);
506
- }
507
-
508
- const isMediaKey = key.startsWith("media_blob_") || key.startsWith("media_thumb_") || key.startsWith("media_") || key.startsWith("rot_media_") || key.startsWith("wa_composer_");
509
- const targetStore = isMediaKey ? STORES.MEDIA : routeIndexedDBStore(key);
510
- try {
511
- await deleteIndexedDBEntry(targetStore, key);
512
- } catch (e) {
513
- console.error("[Fritree Storage] Store deletion error:", e);
514
- }
515
-
516
- if (SECURE_SIS_KEYS.has(key)) {
517
- try {
518
- await deleteIndexedDBEntry(STORES.SYSTEM, `local_sis_sig_${key}`);
519
- await deleteSecondarySignatureAnchor(key);
520
- await deleteIndexedDBEntry(STORES.SYSTEM, `local_sis_backup_${key}`);
521
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
522
- const obfuscatedBackupKey = await asyncHashKey(`sis_backup_${key}`);
523
- chrome.storage.local.remove(obfuscatedBackupKey);
524
- }
525
- } catch (e) {
526
- console.error("[Fritree Storage] Anchor removal error:", e);
527
- }
528
- }
529
-
530
- if (!isMediaKey) {
531
- try {
532
- await deleteIndexedDBEntry(STORES.MEDIA, key);
533
- } catch (e) {
534
- console.error("[Fritree Storage] Fallback store removal error:", e);
535
- }
536
- }
537
- return true;
538
- });
539
- },
540
-
541
- clear: function() {
542
- return storageWriteQueue.enqueue(async () => {
543
- localStorage.clear();
544
-
545
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
546
- await new Promise(r => chrome.storage.local.clear(r));
547
- }
548
-
549
- if (dbInstance) {
550
- dbInstance.close();
551
- dbInstance = null;
552
- }
553
-
554
- return new Promise(resolve => {
555
- const deleteReq = indexedDB.deleteDatabase(DB_NAME);
556
- deleteReq.onsuccess = () => resolve(true);
557
- deleteReq.onerror = () => resolve(false);
558
- });
559
- });
560
- },
561
-
562
- b64ToBlob: base64ToBlob,
563
- blobToB64: blobToBase64
564
- };
565
-
566
- async function asyncHashKey(key) {
567
- if (typeof FritreeCrypto !== 'undefined' && typeof FritreeCrypto.sha256 === 'function') {
568
- return "" + await FritreeCrypto.sha256(key + "FritreeKeySalt_2026_StrictSHA256_Hashed_Production_WebCrypto_Salt");
569
- }
570
- const input = key + "FritreeKeySalt_2026_StrictSHA256_Hashed_Production_WebCrypto_Salt";
571
- let hash = 0;
572
- for (let i = 0; i < input.length; i++) {
573
- const char = input.charCodeAt(i);
574
- hash = ((hash << 5) - hash) + char;
575
- hash |= 0;
576
- }
577
- return "fte_sh_sha256_" + Math.abs(hash).toString(16);
578
- }
579
-
580
- function syncHashKey(key) {
581
- const input = key + "FritreeKeySalt_2026_StrictSHA256_Hashed_Production_WebCrypto_Salt";
582
- let hash = 0;
583
- for (let i = 0; i < input.length; i++) {
584
- const char = input.charCodeAt(i);
585
- hash = ((hash << 5) - hash) + char;
586
- hash |= 0;
587
- }
588
- return "fte_sh_sha256_" + Math.abs(hash).toString(16);
589
- }
590
-
591
- global.FritreeStorage = UnifiedStorageManager;
592
-
593
- initIndexedDB().then(() => {
594
- console.log("[Fritree Storage] Unified IndexedDB system online.");
595
- }).catch(err => {
596
- console.error("[Fritree Storage] Database upgrade/initialization failed:", err);
597
- });
598
-
599
- })(typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : this);