strata-storage 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.
Files changed (102) hide show
  1. package/Readme.md +113 -0
  2. package/android/src/main/java/com/strata/storage/EncryptedStorage.java +65 -0
  3. package/android/src/main/java/com/strata/storage/SQLiteStorage.java +147 -0
  4. package/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +74 -0
  5. package/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +256 -0
  6. package/dist/adapters/capacitor/FilesystemAdapter.d.ts +46 -0
  7. package/dist/adapters/capacitor/FilesystemAdapter.d.ts.map +1 -0
  8. package/dist/adapters/capacitor/FilesystemAdapter.js +162 -0
  9. package/dist/adapters/capacitor/PreferencesAdapter.d.ts +46 -0
  10. package/dist/adapters/capacitor/PreferencesAdapter.d.ts.map +1 -0
  11. package/dist/adapters/capacitor/PreferencesAdapter.js +162 -0
  12. package/dist/adapters/capacitor/SecureAdapter.d.ts +69 -0
  13. package/dist/adapters/capacitor/SecureAdapter.d.ts.map +1 -0
  14. package/dist/adapters/capacitor/SecureAdapter.js +214 -0
  15. package/dist/adapters/capacitor/SqliteAdapter.d.ts +68 -0
  16. package/dist/adapters/capacitor/SqliteAdapter.d.ts.map +1 -0
  17. package/dist/adapters/capacitor/SqliteAdapter.js +277 -0
  18. package/dist/adapters/capacitor/index.d.ts +9 -0
  19. package/dist/adapters/capacitor/index.d.ts.map +1 -0
  20. package/dist/adapters/capacitor/index.js +8 -0
  21. package/dist/adapters/web/CacheAdapter.d.ts +91 -0
  22. package/dist/adapters/web/CacheAdapter.d.ts.map +1 -0
  23. package/dist/adapters/web/CacheAdapter.js +291 -0
  24. package/dist/adapters/web/CookieAdapter.d.ts +77 -0
  25. package/dist/adapters/web/CookieAdapter.d.ts.map +1 -0
  26. package/dist/adapters/web/CookieAdapter.js +260 -0
  27. package/dist/adapters/web/IndexedDBAdapter.d.ts +78 -0
  28. package/dist/adapters/web/IndexedDBAdapter.d.ts.map +1 -0
  29. package/dist/adapters/web/IndexedDBAdapter.js +371 -0
  30. package/dist/adapters/web/LocalStorageAdapter.d.ts +63 -0
  31. package/dist/adapters/web/LocalStorageAdapter.d.ts.map +1 -0
  32. package/dist/adapters/web/LocalStorageAdapter.js +238 -0
  33. package/dist/adapters/web/MemoryAdapter.d.ts +69 -0
  34. package/dist/adapters/web/MemoryAdapter.d.ts.map +1 -0
  35. package/dist/adapters/web/MemoryAdapter.js +165 -0
  36. package/dist/adapters/web/SessionStorageAdapter.d.ts +53 -0
  37. package/dist/adapters/web/SessionStorageAdapter.d.ts.map +1 -0
  38. package/dist/adapters/web/SessionStorageAdapter.js +180 -0
  39. package/dist/adapters/web/index.d.ts +10 -0
  40. package/dist/adapters/web/index.d.ts.map +1 -0
  41. package/dist/adapters/web/index.js +9 -0
  42. package/dist/core/AdapterRegistry.d.ts +52 -0
  43. package/dist/core/AdapterRegistry.d.ts.map +1 -0
  44. package/dist/core/AdapterRegistry.js +102 -0
  45. package/dist/core/BaseAdapter.d.ts +79 -0
  46. package/dist/core/BaseAdapter.d.ts.map +1 -0
  47. package/dist/core/BaseAdapter.js +197 -0
  48. package/dist/core/StorageStrategy.d.ts +55 -0
  49. package/dist/core/StorageStrategy.d.ts.map +1 -0
  50. package/dist/core/StorageStrategy.js +199 -0
  51. package/dist/core/Strata.d.ts +122 -0
  52. package/dist/core/Strata.d.ts.map +1 -0
  53. package/dist/core/Strata.js +568 -0
  54. package/dist/features/compression.d.ts +65 -0
  55. package/dist/features/compression.d.ts.map +1 -0
  56. package/dist/features/compression.js +205 -0
  57. package/dist/features/encryption.d.ts +68 -0
  58. package/dist/features/encryption.d.ts.map +1 -0
  59. package/dist/features/encryption.js +172 -0
  60. package/dist/features/migration.d.ts +17 -0
  61. package/dist/features/migration.d.ts.map +1 -0
  62. package/dist/features/migration.js +43 -0
  63. package/dist/features/query.d.ts +75 -0
  64. package/dist/features/query.d.ts.map +1 -0
  65. package/dist/features/query.js +305 -0
  66. package/dist/features/sync.d.ts +87 -0
  67. package/dist/features/sync.d.ts.map +1 -0
  68. package/dist/features/sync.js +233 -0
  69. package/dist/features/ttl.d.ts +124 -0
  70. package/dist/features/ttl.d.ts.map +1 -0
  71. package/dist/features/ttl.js +236 -0
  72. package/dist/index.d.ts +44 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +46 -0
  75. package/dist/package.json +60 -0
  76. package/dist/plugin/definitions.d.ts +219 -0
  77. package/dist/plugin/definitions.d.ts.map +1 -0
  78. package/dist/plugin/definitions.js +5 -0
  79. package/dist/plugin/index.d.ts +8 -0
  80. package/dist/plugin/index.d.ts.map +1 -0
  81. package/dist/plugin/index.js +27 -0
  82. package/dist/plugin/web.d.ts +24 -0
  83. package/dist/plugin/web.d.ts.map +1 -0
  84. package/dist/plugin/web.js +35 -0
  85. package/dist/types/index.d.ts +558 -0
  86. package/dist/types/index.d.ts.map +1 -0
  87. package/dist/types/index.js +14 -0
  88. package/dist/utils/errors.d.ts +92 -0
  89. package/dist/utils/errors.d.ts.map +1 -0
  90. package/dist/utils/errors.js +153 -0
  91. package/dist/utils/index.d.ts +105 -0
  92. package/dist/utils/index.d.ts.map +1 -0
  93. package/dist/utils/index.js +329 -0
  94. package/ios/Plugin/KeychainStorage.swift +87 -0
  95. package/ios/Plugin/SQLiteStorage.swift +167 -0
  96. package/ios/Plugin/StrataStoragePlugin.swift +204 -0
  97. package/ios/Plugin/UserDefaultsStorage.swift +44 -0
  98. package/package.json +126 -0
  99. package/scripts/build.js +52 -0
  100. package/scripts/cli.js +60 -0
  101. package/scripts/configure.js +444 -0
  102. package/scripts/postinstall.js +33 -0
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Cache Adapter - Service Worker Cache API implementation
3
+ * Provides network-aware storage for offline support
4
+ */
5
+ import { BaseAdapter } from '@/core/BaseAdapter';
6
+ import { getObjectSize } from '@/utils';
7
+ import { StorageError, QuotaExceededError, NotSupportedError } from '@/utils/errors';
8
+ /**
9
+ * Cache API adapter for Service Worker environments
10
+ */
11
+ export class CacheAdapter extends BaseAdapter {
12
+ name = 'cache';
13
+ capabilities = {
14
+ persistent: true,
15
+ synchronous: false,
16
+ observable: false, // No native change events
17
+ transactional: false,
18
+ queryable: true,
19
+ maxSize: -1, // Browser dependent, typically GBs
20
+ binary: true, // Supports binary data via Response
21
+ encrypted: false,
22
+ crossTab: true, // Shared across tabs via Service Worker
23
+ };
24
+ cacheName;
25
+ baseUrl;
26
+ cache;
27
+ constructor(cacheName = 'strata-storage-v1', baseUrl = 'https://strata.local/') {
28
+ super();
29
+ this.cacheName = cacheName;
30
+ this.baseUrl = baseUrl;
31
+ }
32
+ /**
33
+ * Check if Cache API is available
34
+ */
35
+ async isAvailable() {
36
+ return typeof window !== 'undefined' && 'caches' in window && typeof caches.open === 'function';
37
+ }
38
+ /**
39
+ * Initialize the adapter
40
+ */
41
+ async initialize(config) {
42
+ if (config?.cacheName)
43
+ this.cacheName = config.cacheName;
44
+ if (config?.baseUrl)
45
+ this.baseUrl = config.baseUrl;
46
+ await this.openCache();
47
+ this.startTTLCleanup();
48
+ }
49
+ /**
50
+ * Open cache
51
+ */
52
+ async openCache() {
53
+ if (this.cache)
54
+ return this.cache;
55
+ if (!('caches' in window)) {
56
+ throw new NotSupportedError('Cache API not available');
57
+ }
58
+ this.cache = await caches.open(this.cacheName);
59
+ return this.cache;
60
+ }
61
+ /**
62
+ * Create URL for key
63
+ */
64
+ keyToUrl(key) {
65
+ return new URL(encodeURIComponent(key), this.baseUrl).href;
66
+ }
67
+ /**
68
+ * Extract key from URL
69
+ */
70
+ urlToKey(url) {
71
+ const urlObj = new URL(url);
72
+ const pathname = urlObj.pathname;
73
+ const lastSegment = pathname.split('/').pop() || '';
74
+ return decodeURIComponent(lastSegment);
75
+ }
76
+ /**
77
+ * Get a value from cache
78
+ */
79
+ async get(key) {
80
+ const cache = await this.openCache();
81
+ const url = this.keyToUrl(key);
82
+ try {
83
+ const response = await cache.match(url);
84
+ if (!response)
85
+ return null;
86
+ const data = (await response.json());
87
+ // Check TTL
88
+ if (this.isExpired(data)) {
89
+ await this.remove(key);
90
+ return null;
91
+ }
92
+ return data;
93
+ }
94
+ catch (error) {
95
+ console.error(`Failed to get key ${key} from cache:`, error);
96
+ return null;
97
+ }
98
+ }
99
+ /**
100
+ * Set a value in cache
101
+ */
102
+ async set(key, value) {
103
+ const cache = await this.openCache();
104
+ const url = this.keyToUrl(key);
105
+ const oldValue = await this.get(key);
106
+ try {
107
+ // Create Response with the data
108
+ const response = new Response(JSON.stringify(value), {
109
+ headers: {
110
+ 'Content-Type': 'application/json',
111
+ 'X-Strata-Created': value.created.toString(),
112
+ 'X-Strata-Updated': value.updated.toString(),
113
+ 'X-Strata-Expires': value.expires?.toString() || '',
114
+ },
115
+ });
116
+ await cache.put(url, response);
117
+ this.emitChange(key, oldValue?.value, value.value, 'local');
118
+ }
119
+ catch (error) {
120
+ if (this.isQuotaError(error)) {
121
+ throw new QuotaExceededError('Cache quota exceeded', { key, size: getObjectSize(value) });
122
+ }
123
+ throw new StorageError(`Failed to set key ${key} in cache: ${error}`);
124
+ }
125
+ }
126
+ /**
127
+ * Remove a value from cache
128
+ */
129
+ async remove(key) {
130
+ const cache = await this.openCache();
131
+ const url = this.keyToUrl(key);
132
+ const oldValue = await this.get(key);
133
+ const deleted = await cache.delete(url);
134
+ if (deleted && oldValue) {
135
+ this.emitChange(key, oldValue.value, undefined, 'local');
136
+ }
137
+ }
138
+ /**
139
+ * Clear cache
140
+ */
141
+ async clear(options) {
142
+ if (!options || (!options.pattern && !options.tags && !options.expiredOnly)) {
143
+ // Delete and recreate cache
144
+ await caches.delete(this.cacheName);
145
+ this.cache = await caches.open(this.cacheName);
146
+ this.emitChange('*', undefined, undefined, 'local');
147
+ return;
148
+ }
149
+ // Use base implementation for filtered clear
150
+ await super.clear(options);
151
+ }
152
+ /**
153
+ * Get all keys
154
+ */
155
+ async keys(pattern) {
156
+ const cache = await this.openCache();
157
+ const requests = await cache.keys();
158
+ const keys = [];
159
+ for (const request of requests) {
160
+ const key = this.urlToKey(request.url);
161
+ // Check if not expired
162
+ const value = await this.get(key);
163
+ if (value) {
164
+ keys.push(key);
165
+ }
166
+ }
167
+ return this.filterKeys(keys, pattern);
168
+ }
169
+ /**
170
+ * Query cache with conditions
171
+ */
172
+ async query(condition) {
173
+ const cache = await this.openCache();
174
+ const requests = await cache.keys();
175
+ const results = [];
176
+ for (const request of requests) {
177
+ const key = this.urlToKey(request.url);
178
+ const value = await this.get(key);
179
+ if (value && this.queryEngine.matches(value.value, condition)) {
180
+ results.push({ key, value: value.value });
181
+ }
182
+ }
183
+ return results;
184
+ }
185
+ /**
186
+ * Get storage size
187
+ */
188
+ async size(detailed) {
189
+ const cache = await this.openCache();
190
+ const requests = await cache.keys();
191
+ let total = 0;
192
+ let count = 0;
193
+ let keySize = 0;
194
+ let valueSize = 0;
195
+ let metadataSize = 0;
196
+ for (const request of requests) {
197
+ const response = await cache.match(request);
198
+ if (response) {
199
+ count++;
200
+ const key = this.urlToKey(request.url);
201
+ const blob = await response.blob();
202
+ const contentSize = blob.size;
203
+ total += key.length * 2 + contentSize;
204
+ if (detailed) {
205
+ keySize += key.length * 2;
206
+ valueSize += contentSize;
207
+ // Headers contribute to metadata
208
+ const headers = response.headers;
209
+ headers.forEach((value, key) => {
210
+ metadataSize += (key.length + value.length) * 2;
211
+ });
212
+ }
213
+ }
214
+ }
215
+ const result = { total, count };
216
+ if (detailed) {
217
+ result.detailed = {
218
+ keys: keySize,
219
+ values: valueSize,
220
+ metadata: metadataSize,
221
+ };
222
+ }
223
+ return result;
224
+ }
225
+ /**
226
+ * Store binary data
227
+ */
228
+ async setBinary(key, data, metadata) {
229
+ const cache = await this.openCache();
230
+ const url = this.keyToUrl(key);
231
+ const now = Date.now();
232
+ // Create storage value for metadata
233
+ const storageMetadata = {
234
+ value: metadata || {},
235
+ created: now,
236
+ updated: now,
237
+ metadata: { binary: true, size: data instanceof ArrayBuffer ? data.byteLength : data.size },
238
+ };
239
+ // Create Response with binary data
240
+ const response = new Response(data, {
241
+ headers: {
242
+ 'Content-Type': 'application/octet-stream',
243
+ 'X-Strata-Metadata': JSON.stringify(storageMetadata),
244
+ },
245
+ });
246
+ await cache.put(url, response);
247
+ this.emitChange(key, undefined, metadata || {}, 'local');
248
+ }
249
+ /**
250
+ * Get binary data
251
+ */
252
+ async getBinary(key) {
253
+ const cache = await this.openCache();
254
+ const url = this.keyToUrl(key);
255
+ try {
256
+ const response = await cache.match(url);
257
+ if (!response)
258
+ return null;
259
+ const metadataHeader = response.headers.get('X-Strata-Metadata');
260
+ const metadata = metadataHeader ? JSON.parse(metadataHeader) : null;
261
+ // Check if it's binary data
262
+ if (response.headers.get('Content-Type') !== 'application/octet-stream') {
263
+ return null;
264
+ }
265
+ const data = await response.arrayBuffer();
266
+ return { data, metadata: metadata?.value };
267
+ }
268
+ catch (error) {
269
+ console.error(`Failed to get binary data for key ${key}:`, error);
270
+ return null;
271
+ }
272
+ }
273
+ /**
274
+ * Close the adapter
275
+ */
276
+ async close() {
277
+ this.cache = undefined;
278
+ if (super.close) {
279
+ await super.close();
280
+ }
281
+ }
282
+ /**
283
+ * Check if error is quota exceeded
284
+ */
285
+ isQuotaError(error) {
286
+ if (error instanceof Error || error instanceof DOMException) {
287
+ return error.name === 'QuotaExceededError' || error.message.toLowerCase().includes('quota');
288
+ }
289
+ return false;
290
+ }
291
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Cookie Adapter - Browser cookie implementation
3
+ * Provides limited storage with 4KB per cookie limit
4
+ */
5
+ import { BaseAdapter } from '@/core/BaseAdapter';
6
+ import type { StorageType, StorageCapabilities, StorageValue, ClearOptions, SizeInfo } from '@/types';
7
+ /**
8
+ * Cookie options
9
+ */
10
+ interface CookieOptions {
11
+ domain?: string;
12
+ path?: string;
13
+ secure?: boolean;
14
+ sameSite?: 'strict' | 'lax' | 'none';
15
+ maxAge?: number;
16
+ }
17
+ /**
18
+ * Browser cookie adapter
19
+ */
20
+ export declare class CookieAdapter extends BaseAdapter {
21
+ readonly name: StorageType;
22
+ readonly capabilities: StorageCapabilities;
23
+ private prefix;
24
+ private cookieOptions;
25
+ private maxCookieSize;
26
+ constructor(prefix?: string, options?: CookieOptions);
27
+ /**
28
+ * Check if cookies are available
29
+ */
30
+ isAvailable(): Promise<boolean>;
31
+ /**
32
+ * Initialize the adapter
33
+ */
34
+ initialize(config?: CookieOptions): Promise<void>;
35
+ /**
36
+ * Get a value from cookies
37
+ */
38
+ get<T = unknown>(key: string): Promise<StorageValue<T> | null>;
39
+ /**
40
+ * Set a value in cookies
41
+ */
42
+ set<T = unknown>(key: string, value: StorageValue<T>): Promise<void>;
43
+ /**
44
+ * Remove a value from cookies
45
+ */
46
+ remove(key: string): Promise<void>;
47
+ /**
48
+ * Clear cookies
49
+ */
50
+ clear(options?: ClearOptions): Promise<void>;
51
+ /**
52
+ * Get all keys
53
+ */
54
+ keys(pattern?: string | RegExp): Promise<string[]>;
55
+ /**
56
+ * Get storage size
57
+ */
58
+ size(detailed?: boolean): Promise<SizeInfo>;
59
+ /**
60
+ * Get a cookie value
61
+ */
62
+ private getCookie;
63
+ /**
64
+ * Set a cookie
65
+ */
66
+ private setCookie;
67
+ /**
68
+ * Delete a cookie
69
+ */
70
+ private deleteCookie;
71
+ /**
72
+ * Get all cookies as key-value pairs
73
+ */
74
+ private getAllCookies;
75
+ }
76
+ export {};
77
+ //# sourceMappingURL=CookieAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CookieAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/web/CookieAdapter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,QAAQ,EACT,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,UAAU,aAAa;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,WAAW;IAC5C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAa;IACvC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAUxC;IAEF,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,aAAa,CAAQ;gBAEjB,MAAM,SAAY,EAAE,OAAO,GAAE,aAAkB;IAS3D;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAiBrC;;OAEG;IACG,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvD;;OAEG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAuBpE;;OAEG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC1E;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxC;;OAEG;IACG,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD;;OAEG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmBxD;;OAEG;IACG,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAmCjD;;OAEG;IACH,OAAO,CAAC,SAAS;IAgBjB;;OAEG;IACH,OAAO,CAAC,SAAS;IA4BjB;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,aAAa;CAmBtB"}
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Cookie Adapter - Browser cookie implementation
3
+ * Provides limited storage with 4KB per cookie limit
4
+ */
5
+ import { BaseAdapter } from '@/core/BaseAdapter';
6
+ import { StorageError, QuotaExceededError } from '@/utils/errors';
7
+ /**
8
+ * Browser cookie adapter
9
+ */
10
+ export class CookieAdapter extends BaseAdapter {
11
+ name = 'cookies';
12
+ capabilities = {
13
+ persistent: true,
14
+ synchronous: false,
15
+ observable: false, // No native change events
16
+ transactional: false,
17
+ queryable: true,
18
+ maxSize: 4096, // 4KB per cookie
19
+ binary: false,
20
+ encrypted: false,
21
+ crossTab: true, // Cookies are shared across tabs
22
+ };
23
+ prefix;
24
+ cookieOptions;
25
+ maxCookieSize = 4096; // 4KB limit per cookie
26
+ constructor(prefix = 'strata_', options = {}) {
27
+ super();
28
+ this.prefix = prefix;
29
+ this.cookieOptions = {
30
+ path: '/',
31
+ ...options,
32
+ };
33
+ }
34
+ /**
35
+ * Check if cookies are available
36
+ */
37
+ async isAvailable() {
38
+ if (typeof window === 'undefined' || !navigator.cookieEnabled) {
39
+ return false;
40
+ }
41
+ // Test if we can actually set cookies
42
+ try {
43
+ const testKey = `${this.prefix}test`;
44
+ this.setCookie(testKey, 'test', { maxAge: 1 });
45
+ const result = this.getCookie(testKey) === 'test';
46
+ this.deleteCookie(testKey);
47
+ return result;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ /**
54
+ * Initialize the adapter
55
+ */
56
+ async initialize(config) {
57
+ if (config) {
58
+ this.cookieOptions = { ...this.cookieOptions, ...config };
59
+ }
60
+ this.startTTLCleanup();
61
+ }
62
+ /**
63
+ * Get a value from cookies
64
+ */
65
+ async get(key) {
66
+ const cookieKey = this.prefix + key;
67
+ const value = this.getCookie(cookieKey);
68
+ if (!value)
69
+ return null;
70
+ try {
71
+ const decoded = decodeURIComponent(value);
72
+ const parsed = JSON.parse(decoded);
73
+ // Check TTL
74
+ if (this.isExpired(parsed)) {
75
+ await this.remove(key);
76
+ return null;
77
+ }
78
+ return parsed;
79
+ }
80
+ catch (error) {
81
+ console.error(`Failed to parse cookie ${key}:`, error);
82
+ return null;
83
+ }
84
+ }
85
+ /**
86
+ * Set a value in cookies
87
+ */
88
+ async set(key, value) {
89
+ const cookieKey = this.prefix + key;
90
+ const oldValue = await this.get(key);
91
+ try {
92
+ const serialized = JSON.stringify(value);
93
+ const encoded = encodeURIComponent(serialized);
94
+ // Check size limit
95
+ if (encoded.length > this.maxCookieSize) {
96
+ throw new QuotaExceededError(`Cookie size ${encoded.length} exceeds limit ${this.maxCookieSize}`, { key, size: encoded.length });
97
+ }
98
+ // Set cookie with options
99
+ const options = { ...this.cookieOptions };
100
+ // Handle TTL
101
+ if (value.expires) {
102
+ const maxAge = Math.floor((value.expires - Date.now()) / 1000);
103
+ if (maxAge > 0) {
104
+ options.maxAge = maxAge;
105
+ }
106
+ }
107
+ this.setCookie(cookieKey, encoded, options);
108
+ this.emitChange(key, oldValue?.value, value.value, 'local');
109
+ }
110
+ catch (error) {
111
+ if (error instanceof QuotaExceededError) {
112
+ throw error;
113
+ }
114
+ throw new StorageError(`Failed to set cookie ${key}: ${error}`);
115
+ }
116
+ }
117
+ /**
118
+ * Remove a value from cookies
119
+ */
120
+ async remove(key) {
121
+ const oldValue = await this.get(key);
122
+ const cookieKey = this.prefix + key;
123
+ this.deleteCookie(cookieKey);
124
+ if (oldValue) {
125
+ this.emitChange(key, oldValue.value, undefined, 'local');
126
+ }
127
+ }
128
+ /**
129
+ * Clear cookies
130
+ */
131
+ async clear(options) {
132
+ if (!options || (!options.pattern && !options.tags && !options.expiredOnly)) {
133
+ // Clear all cookies with our prefix
134
+ const cookies = this.getAllCookies();
135
+ for (const [cookieKey] of cookies) {
136
+ if (cookieKey.startsWith(this.prefix)) {
137
+ this.deleteCookie(cookieKey);
138
+ }
139
+ }
140
+ this.emitChange('*', undefined, undefined, 'local');
141
+ return;
142
+ }
143
+ // Use base implementation for filtered clear
144
+ await super.clear(options);
145
+ }
146
+ /**
147
+ * Get all keys
148
+ */
149
+ async keys(pattern) {
150
+ const cookies = this.getAllCookies();
151
+ const keys = [];
152
+ for (const [cookieKey] of cookies) {
153
+ if (cookieKey.startsWith(this.prefix)) {
154
+ const key = cookieKey.substring(this.prefix.length);
155
+ // Check if not expired
156
+ const value = await this.get(key);
157
+ if (value) {
158
+ keys.push(key);
159
+ }
160
+ }
161
+ }
162
+ return this.filterKeys(keys, pattern);
163
+ }
164
+ /**
165
+ * Get storage size
166
+ */
167
+ async size(detailed) {
168
+ const cookies = this.getAllCookies();
169
+ let total = 0;
170
+ let count = 0;
171
+ let keySize = 0;
172
+ let valueSize = 0;
173
+ for (const [cookieKey, cookieValue] of cookies) {
174
+ if (cookieKey.startsWith(this.prefix)) {
175
+ count++;
176
+ const itemSize = (cookieKey.length + cookieValue.length) * 2; // UTF-16
177
+ total += itemSize;
178
+ if (detailed) {
179
+ keySize += cookieKey.length * 2;
180
+ valueSize += cookieValue.length * 2;
181
+ }
182
+ }
183
+ }
184
+ const result = { total, count };
185
+ if (detailed) {
186
+ result.detailed = {
187
+ keys: keySize,
188
+ values: valueSize,
189
+ metadata: 0,
190
+ };
191
+ }
192
+ return result;
193
+ }
194
+ // Cookie manipulation helpers
195
+ /**
196
+ * Get a cookie value
197
+ */
198
+ getCookie(name) {
199
+ if (typeof document === 'undefined')
200
+ return null;
201
+ const nameEQ = name + '=';
202
+ const cookies = document.cookie.split(';');
203
+ for (const cookie of cookies) {
204
+ const trimmed = cookie.trim();
205
+ if (trimmed.indexOf(nameEQ) === 0) {
206
+ return trimmed.substring(nameEQ.length);
207
+ }
208
+ }
209
+ return null;
210
+ }
211
+ /**
212
+ * Set a cookie
213
+ */
214
+ setCookie(name, value, options = {}) {
215
+ if (typeof document === 'undefined')
216
+ return;
217
+ let cookie = `${name}=${value}`;
218
+ if (options.maxAge !== undefined) {
219
+ cookie += `; max-age=${options.maxAge}`;
220
+ }
221
+ if (options.domain) {
222
+ cookie += `; domain=${options.domain}`;
223
+ }
224
+ if (options.path) {
225
+ cookie += `; path=${options.path}`;
226
+ }
227
+ if (options.secure) {
228
+ cookie += '; secure';
229
+ }
230
+ if (options.sameSite) {
231
+ cookie += `; samesite=${options.sameSite}`;
232
+ }
233
+ document.cookie = cookie;
234
+ }
235
+ /**
236
+ * Delete a cookie
237
+ */
238
+ deleteCookie(name) {
239
+ this.setCookie(name, '', { ...this.cookieOptions, maxAge: 0 });
240
+ }
241
+ /**
242
+ * Get all cookies as key-value pairs
243
+ */
244
+ getAllCookies() {
245
+ if (typeof document === 'undefined')
246
+ return [];
247
+ const cookies = [];
248
+ const cookieStrings = document.cookie.split(';');
249
+ for (const cookie of cookieStrings) {
250
+ const trimmed = cookie.trim();
251
+ const eqIndex = trimmed.indexOf('=');
252
+ if (eqIndex > 0) {
253
+ const name = trimmed.substring(0, eqIndex);
254
+ const value = trimmed.substring(eqIndex + 1);
255
+ cookies.push([name, value]);
256
+ }
257
+ }
258
+ return cookies;
259
+ }
260
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * IndexedDB Adapter - Browser IndexedDB implementation
3
+ * Provides large-scale persistent storage with advanced features
4
+ */
5
+ import { BaseAdapter } from '@/core/BaseAdapter';
6
+ import type { StorageType, StorageCapabilities, StorageValue, ClearOptions, SizeInfo, QueryCondition, Transaction } from '@/types';
7
+ /**
8
+ * Browser IndexedDB adapter
9
+ */
10
+ export declare class IndexedDBAdapter extends BaseAdapter {
11
+ readonly name: StorageType;
12
+ readonly capabilities: StorageCapabilities;
13
+ private dbName;
14
+ private storeName;
15
+ private version;
16
+ private db?;
17
+ constructor(dbName?: string, storeName?: string, version?: number);
18
+ /**
19
+ * Check if IndexedDB is available
20
+ */
21
+ isAvailable(): Promise<boolean>;
22
+ /**
23
+ * Initialize the adapter
24
+ */
25
+ initialize(config?: {
26
+ dbName?: string;
27
+ storeName?: string;
28
+ version?: number;
29
+ }): Promise<void>;
30
+ /**
31
+ * Open IndexedDB database
32
+ */
33
+ private openDatabase;
34
+ /**
35
+ * Get a value from IndexedDB
36
+ */
37
+ get<T = unknown>(key: string): Promise<StorageValue<T> | null>;
38
+ /**
39
+ * Set a value in IndexedDB
40
+ */
41
+ set<T = unknown>(key: string, value: StorageValue<T>): Promise<void>;
42
+ /**
43
+ * Remove a value from IndexedDB
44
+ */
45
+ remove(key: string): Promise<void>;
46
+ /**
47
+ * Clear IndexedDB
48
+ */
49
+ clear(options?: ClearOptions): Promise<void>;
50
+ /**
51
+ * Get all keys
52
+ */
53
+ keys(pattern?: string | RegExp): Promise<string[]>;
54
+ /**
55
+ * Query IndexedDB with conditions
56
+ */
57
+ query<T = unknown>(condition: QueryCondition): Promise<Array<{
58
+ key: string;
59
+ value: T;
60
+ }>>;
61
+ /**
62
+ * Get storage size
63
+ */
64
+ size(detailed?: boolean): Promise<SizeInfo>;
65
+ /**
66
+ * Begin a transaction
67
+ */
68
+ transaction(): Promise<Transaction>;
69
+ /**
70
+ * Close the adapter
71
+ */
72
+ close(): Promise<void>;
73
+ /**
74
+ * Check if error is quota exceeded
75
+ */
76
+ private isQuotaError;
77
+ }
78
+ //# sourceMappingURL=IndexedDBAdapter.d.ts.map