strata-storage 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +66 -20
- package/dist/README.md +162 -0
- package/dist/adapters/capacitor/FilesystemAdapter.js +4 -4
- package/dist/adapters/capacitor/PreferencesAdapter.js +4 -4
- package/dist/adapters/capacitor/SecureAdapter.js +4 -4
- package/dist/adapters/capacitor/SqliteAdapter.js +4 -4
- package/dist/adapters/capacitor/index.js +4 -4
- package/dist/adapters/web/CacheAdapter.js +3 -3
- package/dist/adapters/web/CookieAdapter.js +2 -2
- package/dist/adapters/web/IndexedDBAdapter.js +3 -3
- package/dist/adapters/web/LocalStorageAdapter.js +3 -3
- package/dist/adapters/web/MemoryAdapter.js +3 -3
- package/dist/adapters/web/SessionStorageAdapter.js +1 -1
- package/dist/adapters/web/index.js +6 -6
- package/dist/android/src/main/java/com/strata/storage/EncryptedStorage.java +65 -0
- package/dist/android/src/main/java/com/strata/storage/SQLiteStorage.java +147 -0
- package/dist/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +74 -0
- package/dist/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +256 -0
- package/dist/capacitor.d.ts +20 -0
- package/dist/capacitor.d.ts.map +1 -0
- package/dist/capacitor.js +48 -0
- package/dist/core/AdapterRegistry.js +1 -1
- package/dist/core/BaseAdapter.js +3 -3
- package/dist/core/StorageStrategy.js +1 -0
- package/dist/core/Strata.d.ts +28 -3
- package/dist/core/Strata.d.ts.map +1 -1
- package/dist/core/Strata.js +57 -58
- package/dist/features/compression/index.d.ts +2 -0
- package/dist/features/compression/index.d.ts.map +1 -0
- package/dist/features/compression/index.js +1 -0
- package/dist/features/compression.js +1 -1
- package/dist/features/encryption/index.d.ts +2 -0
- package/dist/features/encryption/index.d.ts.map +1 -0
- package/dist/features/encryption/index.js +1 -0
- package/dist/features/encryption.d.ts.map +1 -1
- package/dist/features/encryption.js +6 -5
- package/dist/features/observer/index.d.ts +2 -0
- package/dist/features/observer/index.d.ts.map +1 -0
- package/dist/features/observer/index.js +1 -0
- package/dist/features/observer.d.ts +20 -0
- package/dist/features/observer.d.ts.map +1 -0
- package/dist/features/observer.js +32 -0
- package/dist/features/query/index.d.ts +2 -0
- package/dist/features/query/index.d.ts.map +1 -0
- package/dist/features/query/index.js +1 -0
- package/dist/features/query.d.ts.map +1 -1
- package/dist/features/query.js +8 -1
- package/dist/features/sync/index.d.ts +2 -0
- package/dist/features/sync/index.d.ts.map +1 -0
- package/dist/features/sync/index.js +1 -0
- package/dist/features/sync.js +1 -1
- package/dist/features/ttl/index.d.ts +2 -0
- package/dist/features/ttl/index.d.ts.map +1 -0
- package/dist/features/ttl/index.js +1 -0
- package/dist/features/ttl.js +1 -1
- package/dist/firebase.d.ts +26 -0
- package/dist/firebase.d.ts.map +1 -0
- package/dist/firebase.js +147 -0
- package/dist/index.d.ts +13 -47
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -58
- package/dist/ios/Plugin/KeychainStorage.swift +87 -0
- package/dist/ios/Plugin/SQLiteStorage.swift +167 -0
- package/dist/ios/Plugin/StrataStoragePlugin.swift +204 -0
- package/dist/ios/Plugin/UserDefaultsStorage.swift +44 -0
- package/dist/package.json +22 -4
- package/dist/plugin/definitions.d.ts +2 -2
- package/dist/plugin/definitions.d.ts.map +1 -1
- package/dist/plugin/index.d.ts +2 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +77 -11
- package/dist/types/index.d.ts +190 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +13 -0
- package/dist/utils/index.d.ts +20 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +34 -0
- package/package.json +15 -24
- package/scripts/build.js +127 -14
- package/scripts/cli.js +6 -2
- package/scripts/configure.js +7 -3
- package/scripts/postinstall.js +21 -10
package/dist/index.d.ts
CHANGED
|
@@ -1,56 +1,22 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Strata Storage - Zero-dependency universal storage plugin
|
|
3
|
-
*
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
1
|
export { Strata } from './core/Strata';
|
|
7
|
-
import { Strata } from './core/Strata';
|
|
8
|
-
export type { Platform, StorageType, StorageStrategy, StorageOptions, StorageValue, StorageChange, StorageCapabilities, StrataConfig, StorageAdapter, Transaction, ClearOptions, SizeInfo, ExportOptions, ImportOptions, QueryCondition, QueryOperators, SubscriptionCallback, UnsubscribeFunction, Migration, ExportFormat, } from './types';
|
|
9
|
-
export { StrataError, StorageError, NotFoundError, QuotaExceededError, AdapterNotAvailableError, NotSupportedError, EncryptionError, CompressionError, SerializationError, ValidationError, TransactionError, MigrationError, SyncError, isStrataError, isQuotaError, } from './utils/errors';
|
|
10
|
-
export { serialize, deserialize, deepClone, deepMerge, formatBytes, parseSize, EventEmitter, } from './utils';
|
|
11
2
|
export { BaseAdapter } from './core/BaseAdapter';
|
|
12
3
|
export { AdapterRegistry } from './core/AdapterRegistry';
|
|
13
|
-
export { EncryptionManager } from './features/encryption';
|
|
14
|
-
export type { EncryptionConfig, EncryptedData } from './features/encryption';
|
|
15
|
-
export { CompressionManager } from './features/compression';
|
|
16
|
-
export type { CompressionConfig, CompressedData } from './features/compression';
|
|
17
|
-
export { SyncManager, createSyncManager } from './features/sync';
|
|
18
|
-
export type { SyncConfig, SyncMessage } from './features/sync';
|
|
19
|
-
export { QueryEngine, createQueryEngine } from './features/query';
|
|
20
|
-
export { TTLManager, createTTLManager } from './features/ttl';
|
|
21
|
-
export type { TTLConfig, TTLOptions, ExpiredItem } from './features/ttl';
|
|
22
|
-
export { MigrationManager } from './features/migration';
|
|
23
|
-
export type { Migration as MigrationDefinition } from './features/migration';
|
|
24
|
-
export * from './plugin/definitions';
|
|
25
|
-
export { StrataStorage } from './plugin';
|
|
26
|
-
export { MemoryAdapter } from './adapters/web/MemoryAdapter';
|
|
27
4
|
export { LocalStorageAdapter } from './adapters/web/LocalStorageAdapter';
|
|
28
5
|
export { SessionStorageAdapter } from './adapters/web/SessionStorageAdapter';
|
|
29
6
|
export { IndexedDBAdapter } from './adapters/web/IndexedDBAdapter';
|
|
30
7
|
export { CookieAdapter } from './adapters/web/CookieAdapter';
|
|
31
8
|
export { CacheAdapter } from './adapters/web/CacheAdapter';
|
|
32
|
-
export {
|
|
33
|
-
export {
|
|
34
|
-
export
|
|
35
|
-
export {
|
|
36
|
-
export {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
* defaultStorages: ['indexedDB', 'localStorage', 'memory'],
|
|
46
|
-
* encryption: { enabled: true },
|
|
47
|
-
* compression: { enabled: true }
|
|
48
|
-
* });
|
|
49
|
-
*
|
|
50
|
-
* await storage.initialize();
|
|
51
|
-
* await storage.set('key', 'value');
|
|
52
|
-
* const value = await storage.get('key');
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export declare function createStrata(config?: import('./types').StrataConfig): InstanceType<typeof Strata>;
|
|
9
|
+
export { MemoryAdapter } from './adapters/web/MemoryAdapter';
|
|
10
|
+
export { EncryptionManager } from './features/encryption';
|
|
11
|
+
export { CompressionManager } from './features/compression';
|
|
12
|
+
export { TTLManager } from './features/ttl';
|
|
13
|
+
export { QueryEngine } from './features/query';
|
|
14
|
+
export { SyncManager } from './features/sync';
|
|
15
|
+
export { StorageObserver } from './features/observer';
|
|
16
|
+
export type { StorageType, StorageOptions, StorageValue, StorageAdapter, AdapterConfig, QueryOptions, SyncConfig, EncryptionConfig, CompressionConfig, ObserverCallback, StorageEvent, StorageError, StorageCapabilities, StorageMetadata, TTLConfig, } from './types';
|
|
17
|
+
export { isValidKey, isValidValue, serializeValue, deserializeValue, generateId, createError, retry, debounce, throttle, } from './utils';
|
|
18
|
+
import { Strata } from './core/Strata';
|
|
19
|
+
declare const storage: Strata;
|
|
20
|
+
export { storage };
|
|
21
|
+
export default storage;
|
|
56
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAG7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,YAAY,EACV,WAAW,EACX,cAAc,EACd,YAAY,EACZ,cAAc,EACd,aAAa,EACb,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,SAAS,GACV,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,UAAU,EACV,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,KAAK,EACL,QAAQ,EACR,QAAQ,GACT,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AASvC,QAAA,MAAM,OAAO,QAGX,CAAC;AAqBH,OAAO,EAAE,OAAO,EAAE,CAAC;AAGnB,eAAe,OAAO,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,58 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
|
|
17
|
-
export {
|
|
18
|
-
export {
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
export function createStrata(config) {
|
|
57
|
-
return new Strata(config);
|
|
58
|
-
}
|
|
1
|
+
// Core exports - zero dependencies, works everywhere
|
|
2
|
+
export { Strata } from "./core/Strata.js";
|
|
3
|
+
export { BaseAdapter } from "./core/BaseAdapter.js";
|
|
4
|
+
export { AdapterRegistry } from "./core/AdapterRegistry.js";
|
|
5
|
+
// Web adapters - work in any JavaScript environment
|
|
6
|
+
export { LocalStorageAdapter } from "./adapters/web/LocalStorageAdapter.js";
|
|
7
|
+
export { SessionStorageAdapter } from "./adapters/web/SessionStorageAdapter.js";
|
|
8
|
+
export { IndexedDBAdapter } from "./adapters/web/IndexedDBAdapter.js";
|
|
9
|
+
export { CookieAdapter } from "./adapters/web/CookieAdapter.js";
|
|
10
|
+
export { CacheAdapter } from "./adapters/web/CacheAdapter.js";
|
|
11
|
+
export { MemoryAdapter } from "./adapters/web/MemoryAdapter.js";
|
|
12
|
+
// Core features
|
|
13
|
+
export { EncryptionManager } from "./features/encryption.js";
|
|
14
|
+
export { CompressionManager } from "./features/compression.js";
|
|
15
|
+
export { TTLManager } from "./features/ttl.js";
|
|
16
|
+
export { QueryEngine } from "./features/query.js";
|
|
17
|
+
export { SyncManager } from "./features/sync.js";
|
|
18
|
+
export { StorageObserver } from "./features/observer.js";
|
|
19
|
+
// Utils
|
|
20
|
+
export { isValidKey, isValidValue, serializeValue, deserializeValue, generateId, createError, retry, debounce, throttle, } from "./utils/index.js";
|
|
21
|
+
// Create and export a default storage instance that works immediately
|
|
22
|
+
import { Strata } from "./core/Strata.js";
|
|
23
|
+
import { LocalStorageAdapter } from "./adapters/web/LocalStorageAdapter.js";
|
|
24
|
+
import { SessionStorageAdapter } from "./adapters/web/SessionStorageAdapter.js";
|
|
25
|
+
import { IndexedDBAdapter } from "./adapters/web/IndexedDBAdapter.js";
|
|
26
|
+
import { CookieAdapter } from "./adapters/web/CookieAdapter.js";
|
|
27
|
+
import { CacheAdapter } from "./adapters/web/CacheAdapter.js";
|
|
28
|
+
import { MemoryAdapter } from "./adapters/web/MemoryAdapter.js";
|
|
29
|
+
// Create a singleton instance with web adapters pre-registered
|
|
30
|
+
const storage = new Strata({
|
|
31
|
+
defaultStorage: 'memory', // Always available fallback
|
|
32
|
+
autoInitialize: false, // We'll initialize it ourselves
|
|
33
|
+
});
|
|
34
|
+
// Register only web adapters by default
|
|
35
|
+
storage.registerAdapter(new MemoryAdapter());
|
|
36
|
+
storage.registerAdapter(new LocalStorageAdapter());
|
|
37
|
+
storage.registerAdapter(new SessionStorageAdapter());
|
|
38
|
+
storage.registerAdapter(new IndexedDBAdapter());
|
|
39
|
+
storage.registerAdapter(new CookieAdapter());
|
|
40
|
+
storage.registerAdapter(new CacheAdapter());
|
|
41
|
+
// Initialize the storage instance
|
|
42
|
+
(async () => {
|
|
43
|
+
try {
|
|
44
|
+
await storage.initialize();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.warn('Strata Storage initialization warning:', error);
|
|
48
|
+
// Continue working even if some adapters fail
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
// Export the ready-to-use storage instance
|
|
52
|
+
export { storage };
|
|
53
|
+
// Default export for convenience
|
|
54
|
+
export default storage;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import SQLite3
|
|
3
|
+
|
|
4
|
+
@objc public class SQLiteStorage: NSObject {
|
|
5
|
+
private var db: OpaquePointer?
|
|
6
|
+
private let dbName: String
|
|
7
|
+
private let tableName = "strata_storage"
|
|
8
|
+
|
|
9
|
+
@objc public init(dbName: String = "strata.db") {
|
|
10
|
+
self.dbName = dbName
|
|
11
|
+
super.init()
|
|
12
|
+
openDatabase()
|
|
13
|
+
createTable()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
deinit {
|
|
17
|
+
closeDatabase()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private func openDatabase() {
|
|
21
|
+
let fileURL = try! FileManager.default
|
|
22
|
+
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
|
|
23
|
+
.appendingPathComponent(dbName)
|
|
24
|
+
|
|
25
|
+
if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
|
|
26
|
+
print("Unable to open database")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private func closeDatabase() {
|
|
31
|
+
sqlite3_close(db)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private func createTable() {
|
|
35
|
+
let createTableString = """
|
|
36
|
+
CREATE TABLE IF NOT EXISTS \(tableName) (
|
|
37
|
+
key TEXT PRIMARY KEY NOT NULL,
|
|
38
|
+
value BLOB NOT NULL,
|
|
39
|
+
created INTEGER NOT NULL,
|
|
40
|
+
updated INTEGER NOT NULL,
|
|
41
|
+
expires INTEGER,
|
|
42
|
+
tags TEXT,
|
|
43
|
+
metadata TEXT
|
|
44
|
+
);
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
var createTableStatement: OpaquePointer?
|
|
48
|
+
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
|
|
49
|
+
if sqlite3_step(createTableStatement) == SQLITE_DONE {
|
|
50
|
+
print("Storage table created.")
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
sqlite3_finalize(createTableStatement)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@objc public func set(key: String, value: Data, expires: Int64? = nil, tags: [String]? = nil, metadata: [String: Any]? = nil) -> Bool {
|
|
57
|
+
let now = Int64(Date().timeIntervalSince1970 * 1000)
|
|
58
|
+
let tagsJson = tags != nil ? try? JSONSerialization.data(withJSONObject: tags!, options: []) : nil
|
|
59
|
+
let metadataJson = metadata != nil ? try? JSONSerialization.data(withJSONObject: metadata!, options: []) : nil
|
|
60
|
+
|
|
61
|
+
let insertSQL = """
|
|
62
|
+
INSERT OR REPLACE INTO \(tableName)
|
|
63
|
+
(key, value, created, updated, expires, tags, metadata)
|
|
64
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
var statement: OpaquePointer?
|
|
68
|
+
let result = sqlite3_prepare_v2(db, insertSQL, -1, &statement, nil) == SQLITE_OK &&
|
|
69
|
+
sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK &&
|
|
70
|
+
sqlite3_bind_blob(statement, 2, (value as NSData).bytes, Int32(value.count), nil) == SQLITE_OK &&
|
|
71
|
+
sqlite3_bind_int64(statement, 3, now) == SQLITE_OK &&
|
|
72
|
+
sqlite3_bind_int64(statement, 4, now) == SQLITE_OK &&
|
|
73
|
+
(expires != nil ? sqlite3_bind_int64(statement, 5, expires!) : sqlite3_bind_null(statement, 5)) == SQLITE_OK &&
|
|
74
|
+
(tagsJson != nil ? sqlite3_bind_blob(statement, 6, (tagsJson! as NSData).bytes, Int32(tagsJson!.count), nil) : sqlite3_bind_null(statement, 6)) == SQLITE_OK &&
|
|
75
|
+
(metadataJson != nil ? sqlite3_bind_blob(statement, 7, (metadataJson! as NSData).bytes, Int32(metadataJson!.count), nil) : sqlite3_bind_null(statement, 7)) == SQLITE_OK &&
|
|
76
|
+
sqlite3_step(statement) == SQLITE_DONE
|
|
77
|
+
|
|
78
|
+
sqlite3_finalize(statement)
|
|
79
|
+
return result
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@objc public func get(key: String) -> [String: Any]? {
|
|
83
|
+
let querySQL = "SELECT * FROM \(tableName) WHERE key = ? LIMIT 1"
|
|
84
|
+
var statement: OpaquePointer?
|
|
85
|
+
|
|
86
|
+
guard sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK,
|
|
87
|
+
sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK else {
|
|
88
|
+
sqlite3_finalize(statement)
|
|
89
|
+
return nil
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
var result: [String: Any]?
|
|
93
|
+
if sqlite3_step(statement) == SQLITE_ROW {
|
|
94
|
+
let valueData = Data(bytes: sqlite3_column_blob(statement, 1), count: Int(sqlite3_column_bytes(statement, 1)))
|
|
95
|
+
let created = sqlite3_column_int64(statement, 2)
|
|
96
|
+
let updated = sqlite3_column_int64(statement, 3)
|
|
97
|
+
|
|
98
|
+
result = [
|
|
99
|
+
"key": key,
|
|
100
|
+
"value": valueData,
|
|
101
|
+
"created": created,
|
|
102
|
+
"updated": updated
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
if sqlite3_column_type(statement, 4) != SQLITE_NULL {
|
|
106
|
+
result!["expires"] = sqlite3_column_int64(statement, 4)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if sqlite3_column_type(statement, 5) != SQLITE_NULL {
|
|
110
|
+
let tagsData = Data(bytes: sqlite3_column_blob(statement, 5), count: Int(sqlite3_column_bytes(statement, 5)))
|
|
111
|
+
if let tags = try? JSONSerialization.jsonObject(with: tagsData, options: []) {
|
|
112
|
+
result!["tags"] = tags
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if sqlite3_column_type(statement, 6) != SQLITE_NULL {
|
|
117
|
+
let metadataData = Data(bytes: sqlite3_column_blob(statement, 6), count: Int(sqlite3_column_bytes(statement, 6)))
|
|
118
|
+
if let metadata = try? JSONSerialization.jsonObject(with: metadataData, options: []) {
|
|
119
|
+
result!["metadata"] = metadata
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
sqlite3_finalize(statement)
|
|
125
|
+
return result
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@objc public func remove(key: String) -> Bool {
|
|
129
|
+
let deleteSQL = "DELETE FROM \(tableName) WHERE key = ?"
|
|
130
|
+
var statement: OpaquePointer?
|
|
131
|
+
|
|
132
|
+
let result = sqlite3_prepare_v2(db, deleteSQL, -1, &statement, nil) == SQLITE_OK &&
|
|
133
|
+
sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK &&
|
|
134
|
+
sqlite3_step(statement) == SQLITE_DONE
|
|
135
|
+
|
|
136
|
+
sqlite3_finalize(statement)
|
|
137
|
+
return result
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@objc public func clear() -> Bool {
|
|
141
|
+
let deleteSQL = "DELETE FROM \(tableName)"
|
|
142
|
+
var statement: OpaquePointer?
|
|
143
|
+
|
|
144
|
+
let result = sqlite3_prepare_v2(db, deleteSQL, -1, &statement, nil) == SQLITE_OK &&
|
|
145
|
+
sqlite3_step(statement) == SQLITE_DONE
|
|
146
|
+
|
|
147
|
+
sqlite3_finalize(statement)
|
|
148
|
+
return result
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@objc public func keys() -> [String] {
|
|
152
|
+
let querySQL = "SELECT key FROM \(tableName)"
|
|
153
|
+
var statement: OpaquePointer?
|
|
154
|
+
var keys: [String] = []
|
|
155
|
+
|
|
156
|
+
if sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK {
|
|
157
|
+
while sqlite3_step(statement) == SQLITE_ROW {
|
|
158
|
+
if let key = sqlite3_column_text(statement, 0) {
|
|
159
|
+
keys.append(String(cString: key))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
sqlite3_finalize(statement)
|
|
165
|
+
return keys
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Main Capacitor plugin for Strata Storage
|
|
6
|
+
* Coordinates between different storage types on iOS
|
|
7
|
+
*/
|
|
8
|
+
@objc(StrataStoragePlugin)
|
|
9
|
+
public class StrataStoragePlugin: CAPPlugin {
|
|
10
|
+
private let userDefaultsStorage = UserDefaultsStorage()
|
|
11
|
+
private let keychainStorage = KeychainStorage()
|
|
12
|
+
private let sqliteStorage = SQLiteStorage()
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check if storage is available
|
|
16
|
+
*/
|
|
17
|
+
@objc func isAvailable(_ call: CAPPluginCall) {
|
|
18
|
+
call.resolve([
|
|
19
|
+
"available": true,
|
|
20
|
+
"platform": "ios",
|
|
21
|
+
"adapters": [
|
|
22
|
+
"preferences": true,
|
|
23
|
+
"secure": true,
|
|
24
|
+
"sqlite": true,
|
|
25
|
+
"filesystem": true
|
|
26
|
+
]
|
|
27
|
+
])
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get value from storage
|
|
32
|
+
*/
|
|
33
|
+
@objc func get(_ call: CAPPluginCall) {
|
|
34
|
+
guard let key = call.getString("key") else {
|
|
35
|
+
call.reject("Key is required")
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let storage = call.getString("storage") ?? "preferences"
|
|
40
|
+
|
|
41
|
+
do {
|
|
42
|
+
let value: Any?
|
|
43
|
+
|
|
44
|
+
switch storage {
|
|
45
|
+
case "secure":
|
|
46
|
+
value = try keychainStorage.get(key: key)
|
|
47
|
+
case "sqlite":
|
|
48
|
+
value = try sqliteStorage.get(key: key)
|
|
49
|
+
case "preferences":
|
|
50
|
+
fallthrough
|
|
51
|
+
default:
|
|
52
|
+
value = userDefaultsStorage.get(key: key)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
call.resolve([
|
|
56
|
+
"value": value ?? NSNull()
|
|
57
|
+
])
|
|
58
|
+
} catch {
|
|
59
|
+
call.reject("Failed to get value", error.localizedDescription)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Set value in storage
|
|
65
|
+
*/
|
|
66
|
+
@objc func set(_ call: CAPPluginCall) {
|
|
67
|
+
guard let key = call.getString("key") else {
|
|
68
|
+
call.reject("Key is required")
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let value = call.getValue("value") ?? NSNull()
|
|
73
|
+
let storage = call.getString("storage") ?? "preferences"
|
|
74
|
+
|
|
75
|
+
do {
|
|
76
|
+
switch storage {
|
|
77
|
+
case "secure":
|
|
78
|
+
try keychainStorage.set(key: key, value: value)
|
|
79
|
+
case "sqlite":
|
|
80
|
+
try sqliteStorage.set(key: key, value: value)
|
|
81
|
+
case "preferences":
|
|
82
|
+
fallthrough
|
|
83
|
+
default:
|
|
84
|
+
userDefaultsStorage.set(key: key, value: value)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
call.resolve()
|
|
88
|
+
} catch {
|
|
89
|
+
call.reject("Failed to set value", error.localizedDescription)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Remove value from storage
|
|
95
|
+
*/
|
|
96
|
+
@objc func remove(_ call: CAPPluginCall) {
|
|
97
|
+
guard let key = call.getString("key") else {
|
|
98
|
+
call.reject("Key is required")
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let storage = call.getString("storage") ?? "preferences"
|
|
103
|
+
|
|
104
|
+
do {
|
|
105
|
+
switch storage {
|
|
106
|
+
case "secure":
|
|
107
|
+
try keychainStorage.remove(key: key)
|
|
108
|
+
case "sqlite":
|
|
109
|
+
try sqliteStorage.remove(key: key)
|
|
110
|
+
case "preferences":
|
|
111
|
+
fallthrough
|
|
112
|
+
default:
|
|
113
|
+
userDefaultsStorage.remove(key: key)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
call.resolve()
|
|
117
|
+
} catch {
|
|
118
|
+
call.reject("Failed to remove value", error.localizedDescription)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Clear storage
|
|
124
|
+
*/
|
|
125
|
+
@objc func clear(_ call: CAPPluginCall) {
|
|
126
|
+
let storage = call.getString("storage") ?? "preferences"
|
|
127
|
+
let prefix = call.getString("prefix")
|
|
128
|
+
|
|
129
|
+
do {
|
|
130
|
+
switch storage {
|
|
131
|
+
case "secure":
|
|
132
|
+
try keychainStorage.clear(prefix: prefix)
|
|
133
|
+
case "sqlite":
|
|
134
|
+
try sqliteStorage.clear(prefix: prefix)
|
|
135
|
+
case "preferences":
|
|
136
|
+
fallthrough
|
|
137
|
+
default:
|
|
138
|
+
userDefaultsStorage.clear(prefix: prefix)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
call.resolve()
|
|
142
|
+
} catch {
|
|
143
|
+
call.reject("Failed to clear storage", error.localizedDescription)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get all keys from storage
|
|
149
|
+
*/
|
|
150
|
+
@objc func keys(_ call: CAPPluginCall) {
|
|
151
|
+
let storage = call.getString("storage") ?? "preferences"
|
|
152
|
+
let pattern = call.getString("pattern")
|
|
153
|
+
|
|
154
|
+
do {
|
|
155
|
+
let keys: [String]
|
|
156
|
+
|
|
157
|
+
switch storage {
|
|
158
|
+
case "secure":
|
|
159
|
+
keys = try keychainStorage.keys(pattern: pattern)
|
|
160
|
+
case "sqlite":
|
|
161
|
+
keys = try sqliteStorage.keys(pattern: pattern)
|
|
162
|
+
case "preferences":
|
|
163
|
+
fallthrough
|
|
164
|
+
default:
|
|
165
|
+
keys = userDefaultsStorage.keys(pattern: pattern)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
call.resolve([
|
|
169
|
+
"keys": keys
|
|
170
|
+
])
|
|
171
|
+
} catch {
|
|
172
|
+
call.reject("Failed to get keys", error.localizedDescription)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get storage size information
|
|
178
|
+
*/
|
|
179
|
+
@objc func size(_ call: CAPPluginCall) {
|
|
180
|
+
let storage = call.getString("storage") ?? "preferences"
|
|
181
|
+
|
|
182
|
+
do {
|
|
183
|
+
let sizeInfo: (total: Int, count: Int)
|
|
184
|
+
|
|
185
|
+
switch storage {
|
|
186
|
+
case "secure":
|
|
187
|
+
sizeInfo = try keychainStorage.size()
|
|
188
|
+
case "sqlite":
|
|
189
|
+
sizeInfo = try sqliteStorage.size()
|
|
190
|
+
case "preferences":
|
|
191
|
+
fallthrough
|
|
192
|
+
default:
|
|
193
|
+
sizeInfo = userDefaultsStorage.size()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
call.resolve([
|
|
197
|
+
"total": sizeInfo.total,
|
|
198
|
+
"count": sizeInfo.count
|
|
199
|
+
])
|
|
200
|
+
} catch {
|
|
201
|
+
call.reject("Failed to get size", error.localizedDescription)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|