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,153 @@
1
+ /**
2
+ * Custom error classes for Strata Storage
3
+ */
4
+ /**
5
+ * Base error class for all Strata errors
6
+ */
7
+ export class StrataError extends Error {
8
+ code;
9
+ details;
10
+ constructor(message, code, details) {
11
+ super(message);
12
+ this.name = 'StrataError';
13
+ this.code = code;
14
+ this.details = details;
15
+ // Maintains proper stack trace for where our error was thrown
16
+ if (Error.captureStackTrace) {
17
+ Error.captureStackTrace(this, this.constructor);
18
+ }
19
+ }
20
+ }
21
+ /**
22
+ * Error thrown when storage quota is exceeded
23
+ */
24
+ export class QuotaExceededError extends StrataError {
25
+ constructor(message = 'Storage quota exceeded', details) {
26
+ super(message, 'QUOTA_EXCEEDED', details);
27
+ this.name = 'QuotaExceededError';
28
+ }
29
+ }
30
+ /**
31
+ * Error thrown when adapter is not available
32
+ */
33
+ export class AdapterNotAvailableError extends StrataError {
34
+ constructor(adapterName, details) {
35
+ super(`Storage adapter '${adapterName}' is not available`, 'ADAPTER_NOT_AVAILABLE', details);
36
+ this.name = 'AdapterNotAvailableError';
37
+ }
38
+ }
39
+ /**
40
+ * Error thrown when operation is not supported
41
+ */
42
+ export class NotSupportedError extends StrataError {
43
+ constructor(operation, adapterName, details) {
44
+ const message = adapterName
45
+ ? `Operation '${operation}' is not supported by ${adapterName} adapter`
46
+ : `Operation '${operation}' is not supported`;
47
+ super(message, 'NOT_SUPPORTED', details);
48
+ this.name = 'NotSupportedError';
49
+ }
50
+ }
51
+ /**
52
+ * Error thrown when encryption fails
53
+ */
54
+ export class EncryptionError extends StrataError {
55
+ constructor(message = 'Encryption operation failed', details) {
56
+ super(message, 'ENCRYPTION_ERROR', details);
57
+ this.name = 'EncryptionError';
58
+ }
59
+ }
60
+ /**
61
+ * Error thrown when compression fails
62
+ */
63
+ export class CompressionError extends StrataError {
64
+ constructor(message = 'Compression operation failed', details) {
65
+ super(message, 'COMPRESSION_ERROR', details);
66
+ this.name = 'CompressionError';
67
+ }
68
+ }
69
+ /**
70
+ * Error thrown when serialization fails
71
+ */
72
+ export class SerializationError extends StrataError {
73
+ constructor(message = 'Serialization failed', details) {
74
+ super(message, 'SERIALIZATION_ERROR', details);
75
+ this.name = 'SerializationError';
76
+ }
77
+ }
78
+ /**
79
+ * Error thrown when validation fails
80
+ */
81
+ export class ValidationError extends StrataError {
82
+ constructor(message, details) {
83
+ super(message, 'VALIDATION_ERROR', details);
84
+ this.name = 'ValidationError';
85
+ }
86
+ }
87
+ /**
88
+ * Error thrown when transaction fails
89
+ */
90
+ export class TransactionError extends StrataError {
91
+ constructor(message = 'Transaction failed', details) {
92
+ super(message, 'TRANSACTION_ERROR', details);
93
+ this.name = 'TransactionError';
94
+ }
95
+ }
96
+ /**
97
+ * Error thrown when migration fails
98
+ */
99
+ export class MigrationError extends StrataError {
100
+ constructor(message, details) {
101
+ super(message, 'MIGRATION_ERROR', details);
102
+ this.name = 'MigrationError';
103
+ }
104
+ }
105
+ /**
106
+ * Error thrown when sync operation fails
107
+ */
108
+ export class SyncError extends StrataError {
109
+ constructor(message = 'Sync operation failed', details) {
110
+ super(message, 'SYNC_ERROR', details);
111
+ this.name = 'SyncError';
112
+ }
113
+ }
114
+ /**
115
+ * Check if error is a Strata error
116
+ */
117
+ export function isStrataError(error) {
118
+ return error instanceof StrataError;
119
+ }
120
+ /**
121
+ * General storage error
122
+ */
123
+ export class StorageError extends StrataError {
124
+ constructor(message, details) {
125
+ super(message, 'STORAGE_ERROR', details);
126
+ this.name = 'StorageError';
127
+ }
128
+ }
129
+ /**
130
+ * Error thrown when a key is not found
131
+ */
132
+ export class NotFoundError extends StrataError {
133
+ constructor(key, details) {
134
+ super(`Key '${key}' not found`, 'NOT_FOUND', details);
135
+ this.name = 'NotFoundError';
136
+ }
137
+ }
138
+ /**
139
+ * Check if error is a quota exceeded error
140
+ */
141
+ export function isQuotaError(error) {
142
+ if (error instanceof QuotaExceededError)
143
+ return true;
144
+ // Check for native quota errors
145
+ if (error instanceof Error) {
146
+ const message = error.message.toLowerCase();
147
+ return (message.includes('quota') ||
148
+ message.includes('storage exhausted') ||
149
+ error.name === 'QuotaExceededError' ||
150
+ error.name === 'NS_ERROR_DOM_QUOTA_REACHED');
151
+ }
152
+ return false;
153
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Utility functions for Strata Storage
3
+ * Zero dependencies - all utilities implemented from scratch
4
+ */
5
+ /**
6
+ * Check if code is running in a browser environment
7
+ */
8
+ export declare function isBrowser(): boolean;
9
+ /**
10
+ * Check if code is running in Node.js
11
+ */
12
+ export declare function isNode(): boolean;
13
+ /**
14
+ * Check if code is running in a Web Worker
15
+ */
16
+ export declare function isWebWorker(): boolean;
17
+ /**
18
+ * Check if code is running in Capacitor
19
+ */
20
+ export declare function isCapacitor(): boolean;
21
+ /**
22
+ * Deep clone an object (no dependencies!)
23
+ */
24
+ export declare function deepClone<T>(obj: T): T;
25
+ /**
26
+ * Deep merge objects
27
+ */
28
+ export declare function deepMerge<T extends Record<string, unknown>>(target: T, ...sources: Partial<T>[]): T;
29
+ /**
30
+ * Check if value is a plain object
31
+ */
32
+ export declare function isObject(item: unknown): item is Record<string, unknown>;
33
+ /**
34
+ * Generate a unique ID
35
+ */
36
+ export declare function generateId(): string;
37
+ /**
38
+ * Simple glob pattern matching
39
+ */
40
+ export declare function matchGlob(pattern: string, str: string): boolean;
41
+ /**
42
+ * Format bytes to human readable
43
+ */
44
+ export declare function formatBytes(bytes: number, decimals?: number): string;
45
+ /**
46
+ * Parse size string to bytes
47
+ */
48
+ export declare function parseSize(size: string | number): number;
49
+ /**
50
+ * Debounce function
51
+ */
52
+ export declare function debounce<T extends (...args: unknown[]) => unknown>(func: T, wait: number): (...args: Parameters<T>) => void;
53
+ /**
54
+ * Throttle function
55
+ */
56
+ export declare function throttle<T extends (...args: unknown[]) => unknown>(func: T, limit: number): (...args: Parameters<T>) => void;
57
+ /**
58
+ * Promise with timeout
59
+ */
60
+ export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage?: string): Promise<T>;
61
+ /**
62
+ * Retry with exponential backoff
63
+ */
64
+ export declare function retry<T>(fn: () => Promise<T>, options?: {
65
+ maxRetries?: number;
66
+ initialDelay?: number;
67
+ maxDelay?: number;
68
+ factor?: number;
69
+ }): Promise<T>;
70
+ /**
71
+ * Sleep for specified milliseconds
72
+ */
73
+ export declare function sleep(ms: number): Promise<void>;
74
+ /**
75
+ * Create a deferred promise
76
+ */
77
+ export declare function createDeferred<T>(): {
78
+ promise: Promise<T>;
79
+ resolve: (value: T) => void;
80
+ reject: (reason?: unknown) => void;
81
+ };
82
+ /**
83
+ * Serialize value to JSON with support for special types
84
+ */
85
+ export declare function serialize(value: unknown): string;
86
+ /**
87
+ * Deserialize JSON with support for special types
88
+ */
89
+ export declare function deserialize(json: string): unknown;
90
+ /**
91
+ * Calculate object size in bytes (rough estimate)
92
+ */
93
+ export declare function getObjectSize(obj: unknown): number;
94
+ /**
95
+ * Create a simple event emitter
96
+ */
97
+ export declare class EventEmitter {
98
+ private events;
99
+ on(event: string, handler: (...args: unknown[]) => void): void;
100
+ off(event: string, handler: (...args: unknown[]) => void): void;
101
+ emit(event: string, ...args: unknown[]): void;
102
+ once(event: string, handler: (...args: unknown[]) => void): void;
103
+ removeAllListeners(event?: string): void;
104
+ }
105
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,OAAO,CAMhC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAKrC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAatC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzD,MAAM,EAAE,CAAC,EACT,GAAG,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GACvB,CAAC,CAgBH;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEvE;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAU/D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAoBvD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAChE,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,MAAM,GACX,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAYlC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,EAChE,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,MAAM,GACZ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAUlC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,SAAwB,GACnC,OAAO,CAAC,CAAC,CAAC,CAWZ;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,CAAC,EAC3B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACZ,GACL,OAAO,CAAC,CAAC,CAAC,CAoBZ;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,KAAK;IACnC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC,CAUA;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAShD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAoBjD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAqClD;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA6D;IAE3E,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI;IAO9D,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI;IAI/D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAU7C,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI;IAQhE,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;CAOzC"}
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Utility functions for Strata Storage
3
+ * Zero dependencies - all utilities implemented from scratch
4
+ */
5
+ /**
6
+ * Check if code is running in a browser environment
7
+ */
8
+ export function isBrowser() {
9
+ return typeof window !== 'undefined' && typeof window.document !== 'undefined';
10
+ }
11
+ /**
12
+ * Check if code is running in Node.js
13
+ */
14
+ export function isNode() {
15
+ return (typeof process !== 'undefined' &&
16
+ process.versions !== undefined &&
17
+ process.versions.node !== undefined);
18
+ }
19
+ /**
20
+ * Check if code is running in a Web Worker
21
+ */
22
+ export function isWebWorker() {
23
+ return (typeof self !== 'undefined' &&
24
+ typeof self.importScripts === 'function');
25
+ }
26
+ /**
27
+ * Check if code is running in Capacitor
28
+ */
29
+ export function isCapacitor() {
30
+ return typeof window?.Capacitor !== 'undefined';
31
+ }
32
+ /**
33
+ * Deep clone an object (no dependencies!)
34
+ */
35
+ export function deepClone(obj) {
36
+ if (obj === null || typeof obj !== 'object')
37
+ return obj;
38
+ if (obj instanceof Date)
39
+ return new Date(obj.getTime());
40
+ if (obj instanceof Array)
41
+ return obj.map((item) => deepClone(item));
42
+ if (obj instanceof RegExp)
43
+ return new RegExp(obj.source, obj.flags);
44
+ const clonedObj = {};
45
+ for (const key in obj) {
46
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
47
+ clonedObj[key] = deepClone(obj[key]);
48
+ }
49
+ }
50
+ return clonedObj;
51
+ }
52
+ /**
53
+ * Deep merge objects
54
+ */
55
+ export function deepMerge(target, ...sources) {
56
+ if (!sources.length)
57
+ return target;
58
+ const source = sources.shift();
59
+ if (isObject(target) && isObject(source)) {
60
+ for (const key in source) {
61
+ if (isObject(source[key])) {
62
+ if (!target[key])
63
+ Object.assign(target, { [key]: {} });
64
+ deepMerge(target[key], source[key]);
65
+ }
66
+ else {
67
+ Object.assign(target, { [key]: source[key] });
68
+ }
69
+ }
70
+ }
71
+ return deepMerge(target, ...sources);
72
+ }
73
+ /**
74
+ * Check if value is a plain object
75
+ */
76
+ export function isObject(item) {
77
+ return item !== null && typeof item === 'object' && item.constructor === Object;
78
+ }
79
+ /**
80
+ * Generate a unique ID
81
+ */
82
+ export function generateId() {
83
+ const timestamp = Date.now().toString(36);
84
+ const randomPart = Math.random().toString(36).substring(2, 9);
85
+ return `${timestamp}-${randomPart}`;
86
+ }
87
+ /**
88
+ * Simple glob pattern matching
89
+ */
90
+ export function matchGlob(pattern, str) {
91
+ const regexPattern = pattern
92
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
93
+ .replace(/\*/g, '.*')
94
+ .replace(/\?/g, '.');
95
+ return new RegExp(`^${regexPattern}$`).test(str);
96
+ }
97
+ /**
98
+ * Format bytes to human readable
99
+ */
100
+ export function formatBytes(bytes, decimals = 2) {
101
+ if (bytes === 0)
102
+ return '0 Bytes';
103
+ const k = 1024;
104
+ const dm = decimals < 0 ? 0 : decimals;
105
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
106
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
107
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
108
+ }
109
+ /**
110
+ * Parse size string to bytes
111
+ */
112
+ export function parseSize(size) {
113
+ if (typeof size === 'number')
114
+ return size;
115
+ const units = {
116
+ b: 1,
117
+ byte: 1,
118
+ bytes: 1,
119
+ kb: 1024,
120
+ mb: 1024 * 1024,
121
+ gb: 1024 * 1024 * 1024,
122
+ tb: 1024 * 1024 * 1024 * 1024,
123
+ };
124
+ const match = size.toLowerCase().match(/^(\d+(?:\.\d+)?)\s*([a-z]+)?$/);
125
+ if (!match)
126
+ throw new Error(`Invalid size format: ${size}`);
127
+ const [, num, unit = 'b'] = match;
128
+ const multiplier = units[unit] || 1;
129
+ return parseFloat(num) * multiplier;
130
+ }
131
+ /**
132
+ * Debounce function
133
+ */
134
+ export function debounce(func, wait) {
135
+ let timeout = null;
136
+ return function executedFunction(...args) {
137
+ const later = () => {
138
+ timeout = null;
139
+ func(...args);
140
+ };
141
+ if (timeout)
142
+ clearTimeout(timeout);
143
+ timeout = setTimeout(later, wait);
144
+ };
145
+ }
146
+ /**
147
+ * Throttle function
148
+ */
149
+ export function throttle(func, limit) {
150
+ let inThrottle = false;
151
+ return function executedFunction(...args) {
152
+ if (!inThrottle) {
153
+ func(...args);
154
+ inThrottle = true;
155
+ setTimeout(() => (inThrottle = false), limit);
156
+ }
157
+ };
158
+ }
159
+ /**
160
+ * Promise with timeout
161
+ */
162
+ export function withTimeout(promise, timeoutMs, errorMessage = 'Operation timed out') {
163
+ return new Promise((resolve, reject) => {
164
+ const timer = setTimeout(() => {
165
+ reject(new Error(errorMessage));
166
+ }, timeoutMs);
167
+ promise
168
+ .then(resolve)
169
+ .catch(reject)
170
+ .finally(() => clearTimeout(timer));
171
+ });
172
+ }
173
+ /**
174
+ * Retry with exponential backoff
175
+ */
176
+ export async function retry(fn, options = {}) {
177
+ const { maxRetries = 3, initialDelay = 100, maxDelay = 10000, factor = 2 } = options;
178
+ let lastError;
179
+ let delay = initialDelay;
180
+ for (let i = 0; i <= maxRetries; i++) {
181
+ try {
182
+ return await fn();
183
+ }
184
+ catch (error) {
185
+ lastError = error;
186
+ if (i === maxRetries)
187
+ break;
188
+ await sleep(delay);
189
+ delay = Math.min(delay * factor, maxDelay);
190
+ }
191
+ }
192
+ throw lastError;
193
+ }
194
+ /**
195
+ * Sleep for specified milliseconds
196
+ */
197
+ export function sleep(ms) {
198
+ return new Promise((resolve) => setTimeout(resolve, ms));
199
+ }
200
+ /**
201
+ * Create a deferred promise
202
+ */
203
+ export function createDeferred() {
204
+ let resolve;
205
+ let reject;
206
+ const promise = new Promise((res, rej) => {
207
+ resolve = res;
208
+ reject = rej;
209
+ });
210
+ return { promise, resolve, reject };
211
+ }
212
+ /**
213
+ * Serialize value to JSON with support for special types
214
+ */
215
+ export function serialize(value) {
216
+ return JSON.stringify(value, (_, val) => {
217
+ if (val instanceof Date)
218
+ return { __type: 'Date', value: val.toISOString() };
219
+ if (val instanceof RegExp)
220
+ return { __type: 'RegExp', value: val.toString() };
221
+ if (val instanceof Map)
222
+ return { __type: 'Map', value: Array.from(val.entries()) };
223
+ if (val instanceof Set)
224
+ return { __type: 'Set', value: Array.from(val) };
225
+ if (typeof val === 'bigint')
226
+ return { __type: 'BigInt', value: val.toString() };
227
+ return val;
228
+ });
229
+ }
230
+ /**
231
+ * Deserialize JSON with support for special types
232
+ */
233
+ export function deserialize(json) {
234
+ return JSON.parse(json, (_, val) => {
235
+ if (val && typeof val === 'object' && '__type' in val) {
236
+ switch (val.__type) {
237
+ case 'Date':
238
+ return new Date(val.value);
239
+ case 'RegExp': {
240
+ const match = val.value.match(/^\/(.*)\/([gimuy]*)$/);
241
+ return match ? new RegExp(match[1], match[2]) : new RegExp(val.value);
242
+ }
243
+ case 'Map':
244
+ return new Map(val.value);
245
+ case 'Set':
246
+ return new Set(val.value);
247
+ case 'BigInt':
248
+ return BigInt(val.value);
249
+ }
250
+ }
251
+ return val;
252
+ });
253
+ }
254
+ /**
255
+ * Calculate object size in bytes (rough estimate)
256
+ */
257
+ export function getObjectSize(obj) {
258
+ const seen = new WeakSet();
259
+ function calculateSize(item) {
260
+ if (item === null || item === undefined)
261
+ return 0;
262
+ const type = typeof item;
263
+ switch (type) {
264
+ case 'boolean':
265
+ return 4;
266
+ case 'number':
267
+ return 8;
268
+ case 'string':
269
+ return item.length * 2; // UTF-16
270
+ case 'bigint':
271
+ return item.toString().length;
272
+ case 'object':
273
+ if (seen.has(item))
274
+ return 0;
275
+ seen.add(item);
276
+ if (item instanceof Date)
277
+ return 8;
278
+ if (item instanceof RegExp)
279
+ return item.toString().length * 2;
280
+ if (Array.isArray(item)) {
281
+ return item.reduce((sum, val) => sum + calculateSize(val), 0);
282
+ }
283
+ return Object.entries(item).reduce((sum, [key, val]) => sum + key.length * 2 + calculateSize(val), 0);
284
+ default:
285
+ return 0;
286
+ }
287
+ }
288
+ return calculateSize(obj);
289
+ }
290
+ /**
291
+ * Create a simple event emitter
292
+ */
293
+ export class EventEmitter {
294
+ events = new Map();
295
+ on(event, handler) {
296
+ if (!this.events.has(event)) {
297
+ this.events.set(event, new Set());
298
+ }
299
+ this.events.get(event).add(handler);
300
+ }
301
+ off(event, handler) {
302
+ this.events.get(event)?.delete(handler);
303
+ }
304
+ emit(event, ...args) {
305
+ this.events.get(event)?.forEach((handler) => {
306
+ try {
307
+ handler(...args);
308
+ }
309
+ catch (error) {
310
+ console.error(`Error in event handler for ${event}:`, error);
311
+ }
312
+ });
313
+ }
314
+ once(event, handler) {
315
+ const onceHandler = (...args) => {
316
+ handler(...args);
317
+ this.off(event, onceHandler);
318
+ };
319
+ this.on(event, onceHandler);
320
+ }
321
+ removeAllListeners(event) {
322
+ if (event) {
323
+ this.events.delete(event);
324
+ }
325
+ else {
326
+ this.events.clear();
327
+ }
328
+ }
329
+ }
@@ -0,0 +1,87 @@
1
+ import Foundation
2
+ import Security
3
+
4
+ @objc public class KeychainStorage: NSObject {
5
+ private let service: String
6
+ private let accessGroup: String?
7
+
8
+ @objc public init(service: String? = nil, accessGroup: String? = nil) {
9
+ self.service = service ?? Bundle.main.bundleIdentifier ?? "StrataStorage"
10
+ self.accessGroup = accessGroup
11
+ super.init()
12
+ }
13
+
14
+ @objc public func set(key: String, value: Data) -> Bool {
15
+ let query = createQuery(key: key)
16
+ SecItemDelete(query as CFDictionary)
17
+
18
+ var newItem = query
19
+ newItem[kSecValueData as String] = value
20
+
21
+ let status = SecItemAdd(newItem as CFDictionary, nil)
22
+ return status == errSecSuccess
23
+ }
24
+
25
+ @objc public func get(key: String) -> Data? {
26
+ var query = createQuery(key: key)
27
+ query[kSecReturnData as String] = true
28
+ query[kSecMatchLimit as String] = kSecMatchLimitOne
29
+
30
+ var result: AnyObject?
31
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
32
+
33
+ guard status == errSecSuccess else { return nil }
34
+ return result as? Data
35
+ }
36
+
37
+ @objc public func remove(key: String) -> Bool {
38
+ let query = createQuery(key: key)
39
+ let status = SecItemDelete(query as CFDictionary)
40
+ return status == errSecSuccess
41
+ }
42
+
43
+ @objc public func clear() -> Bool {
44
+ let query: [String: Any] = [
45
+ kSecClass as String: kSecClassGenericPassword,
46
+ kSecAttrService as String: service
47
+ ]
48
+ let status = SecItemDelete(query as CFDictionary)
49
+ return status == errSecSuccess || status == errSecItemNotFound
50
+ }
51
+
52
+ @objc public func keys() -> [String] {
53
+ var query: [String: Any] = [
54
+ kSecClass as String: kSecClassGenericPassword,
55
+ kSecAttrService as String: service,
56
+ kSecReturnAttributes as String: true,
57
+ kSecMatchLimit as String: kSecMatchLimitAll
58
+ ]
59
+
60
+ if let accessGroup = accessGroup {
61
+ query[kSecAttrAccessGroup as String] = accessGroup
62
+ }
63
+
64
+ var result: AnyObject?
65
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
66
+
67
+ guard status == errSecSuccess,
68
+ let items = result as? [[String: Any]] else { return [] }
69
+
70
+ return items.compactMap { $0[kSecAttrAccount as String] as? String }
71
+ }
72
+
73
+ private func createQuery(key: String) -> [String: Any] {
74
+ var query: [String: Any] = [
75
+ kSecClass as String: kSecClassGenericPassword,
76
+ kSecAttrService as String: service,
77
+ kSecAttrAccount as String: key,
78
+ kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
79
+ ]
80
+
81
+ if let accessGroup = accessGroup {
82
+ query[kSecAttrAccessGroup as String] = accessGroup
83
+ }
84
+
85
+ return query
86
+ }
87
+ }