strata-storage 2.3.0 → 2.4.1

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 (29) hide show
  1. package/android/src/main/java/com/strata/storage/EncryptedStorage.java +84 -21
  2. package/android/src/main/java/com/strata/storage/SQLiteStorage.java +140 -58
  3. package/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +66 -5
  4. package/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +83 -10
  5. package/dist/adapters/web/MemoryAdapter.d.ts.map +1 -1
  6. package/dist/adapters/web/MemoryAdapter.js +7 -1
  7. package/dist/adapters/web/SessionStorageAdapter.d.ts.map +1 -1
  8. package/dist/adapters/web/SessionStorageAdapter.js +3 -2
  9. package/dist/android/src/main/java/com/strata/storage/EncryptedStorage.java +84 -21
  10. package/dist/android/src/main/java/com/strata/storage/SQLiteStorage.java +140 -58
  11. package/dist/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +66 -5
  12. package/dist/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +83 -10
  13. package/dist/core/Strata.d.ts.map +1 -1
  14. package/dist/core/Strata.js +4 -6
  15. package/dist/firebase.d.ts.map +1 -1
  16. package/dist/firebase.js +75 -13
  17. package/dist/ios/Plugin/SQLiteStorage.swift +100 -30
  18. package/dist/ios/Plugin/UserDefaultsStorage.swift +13 -2
  19. package/dist/package.json +5 -5
  20. package/dist/plugin/index.d.ts.map +1 -1
  21. package/dist/plugin/index.js +31 -6
  22. package/dist/plugin/web.d.ts +5 -1
  23. package/dist/plugin/web.d.ts.map +1 -1
  24. package/dist/plugin/web.js +55 -32
  25. package/dist/utils/index.d.ts.map +1 -1
  26. package/dist/utils/index.js +7 -2
  27. package/ios/Plugin/SQLiteStorage.swift +100 -30
  28. package/ios/Plugin/UserDefaultsStorage.swift +13 -2
  29. package/package.json +17 -19
@@ -1,29 +1,47 @@
1
1
  import Foundation
2
2
  import SQLite3
3
+ import os.log
3
4
 
4
5
  @objc public class SQLiteStorage: NSObject {
5
6
  private var db: OpaquePointer?
6
7
  private let dbName: String
7
8
  private let tableName = "strata_storage"
9
+ private let logger = OSLog(subsystem: "com.strata.storage", category: "SQLiteStorage")
8
10
 
9
11
  @objc public init(dbName: String = "strata.db") {
10
12
  self.dbName = dbName
11
13
  super.init()
12
- openDatabase()
13
- createTable()
14
+ do {
15
+ try openDatabase()
16
+ try createTable()
17
+ } catch {
18
+ os_log("Failed to initialize SQLite storage: %{public}@", log: logger, type: .error, error.localizedDescription)
19
+ }
14
20
  }
15
21
 
16
22
  deinit {
17
23
  closeDatabase()
18
24
  }
19
25
 
20
- private func openDatabase() {
21
- let fileURL = try! FileManager.default
26
+ private func openDatabase() throws {
27
+ guard let fileURL = try? FileManager.default
22
28
  .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")
29
+ .appendingPathComponent(dbName) else {
30
+ throw NSError(
31
+ domain: "StrataStorage.SQLiteStorage",
32
+ code: 1001,
33
+ userInfo: [NSLocalizedDescriptionKey: "Unable to access document directory"]
34
+ )
35
+ }
36
+
37
+ guard sqlite3_open(fileURL.path, &db) == SQLITE_OK else {
38
+ let errorMessage = String(cString: sqlite3_errmsg(db))
39
+ sqlite3_close(db)
40
+ throw NSError(
41
+ domain: "StrataStorage.SQLiteStorage",
42
+ code: 1002,
43
+ userInfo: [NSLocalizedDescriptionKey: "Unable to open database: \(errorMessage)"]
44
+ )
27
45
  }
28
46
  }
29
47
 
@@ -31,7 +49,7 @@ import SQLite3
31
49
  sqlite3_close(db)
32
50
  }
33
51
 
34
- private func createTable() {
52
+ private func createTable() throws {
35
53
  let createTableString = """
36
54
  CREATE TABLE IF NOT EXISTS \(tableName) (
37
55
  key TEXT PRIMARY KEY NOT NULL,
@@ -43,19 +61,43 @@ import SQLite3
43
61
  metadata TEXT
44
62
  );
45
63
  """
46
-
64
+
47
65
  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
- }
66
+
67
+ guard sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK else {
68
+ let errorMessage = String(cString: sqlite3_errmsg(db))
69
+ throw NSError(
70
+ domain: "StrataStorage.SQLiteStorage",
71
+ code: 1003,
72
+ userInfo: [NSLocalizedDescriptionKey: "Failed to prepare CREATE TABLE: \(errorMessage)"]
73
+ )
74
+ }
75
+
76
+ defer {
77
+ sqlite3_finalize(createTableStatement)
78
+ }
79
+
80
+ guard sqlite3_step(createTableStatement) == SQLITE_DONE else {
81
+ let errorMessage = String(cString: sqlite3_errmsg(db))
82
+ throw NSError(
83
+ domain: "StrataStorage.SQLiteStorage",
84
+ code: 1004,
85
+ userInfo: [NSLocalizedDescriptionKey: "Failed to create table: \(errorMessage)"]
86
+ )
52
87
  }
53
- sqlite3_finalize(createTableStatement)
54
88
  }
55
89
 
56
90
  @objc public func set(key: String, value: Any, expires: Int64? = nil, tags: [String]? = nil, metadata: [String: Any]? = nil) throws -> Bool {
91
+ guard let db = db else {
92
+ throw NSError(
93
+ domain: "StrataStorage.SQLiteStorage",
94
+ code: 1000,
95
+ userInfo: [NSLocalizedDescriptionKey: "Database not initialized"]
96
+ )
97
+ }
98
+
57
99
  let data: Data
58
-
100
+
59
101
  if let dataValue = value as? Data {
60
102
  data = dataValue
61
103
  } else if let stringValue = value as? String {
@@ -96,9 +138,14 @@ import SQLite3
96
138
  }
97
139
 
98
140
  @objc public func get(key: String) -> [String: Any]? {
141
+ guard db != nil else {
142
+ os_log("Database not initialized", log: logger, type: .error)
143
+ return nil
144
+ }
145
+
99
146
  let querySQL = "SELECT * FROM \(tableName) WHERE key = ? LIMIT 1"
100
147
  var statement: OpaquePointer?
101
-
148
+
102
149
  guard sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK,
103
150
  sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK else {
104
151
  sqlite3_finalize(statement)
@@ -142,82 +189,105 @@ import SQLite3
142
189
  }
143
190
 
144
191
  @objc public func remove(key: String) -> Bool {
192
+ guard db != nil else {
193
+ os_log("Database not initialized", log: logger, type: .error)
194
+ return false
195
+ }
196
+
145
197
  let deleteSQL = "DELETE FROM \(tableName) WHERE key = ?"
146
198
  var statement: OpaquePointer?
147
-
199
+
148
200
  let result = sqlite3_prepare_v2(db, deleteSQL, -1, &statement, nil) == SQLITE_OK &&
149
201
  sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK &&
150
202
  sqlite3_step(statement) == SQLITE_DONE
151
-
203
+
152
204
  sqlite3_finalize(statement)
153
205
  return result
154
206
  }
155
207
 
156
208
  @objc public func clear(prefix: String? = nil) -> Bool {
209
+ guard db != nil else {
210
+ os_log("Database not initialized", log: logger, type: .error)
211
+ return false
212
+ }
213
+
157
214
  let deleteSQL: String
158
215
  if let prefix = prefix {
159
216
  deleteSQL = "DELETE FROM \(tableName) WHERE key LIKE ?"
160
217
  } else {
161
218
  deleteSQL = "DELETE FROM \(tableName)"
162
219
  }
163
-
220
+
164
221
  var statement: OpaquePointer?
165
222
  var result = sqlite3_prepare_v2(db, deleteSQL, -1, &statement, nil) == SQLITE_OK
166
-
223
+
167
224
  if result && prefix != nil {
168
225
  result = sqlite3_bind_text(statement, 1, "\(prefix!)%", -1, nil) == SQLITE_OK
169
226
  }
170
-
227
+
171
228
  if result {
172
229
  result = sqlite3_step(statement) == SQLITE_DONE
173
230
  }
174
-
231
+
175
232
  sqlite3_finalize(statement)
176
233
  return result
177
234
  }
178
235
 
179
236
  @objc public func keys(pattern: String? = nil) -> [String] {
237
+ guard db != nil else {
238
+ os_log("Database not initialized", log: logger, type: .error)
239
+ return []
240
+ }
241
+
180
242
  let querySQL: String
181
243
  if let pattern = pattern {
182
244
  querySQL = "SELECT key FROM \(tableName) WHERE key LIKE ?"
183
245
  } else {
184
246
  querySQL = "SELECT key FROM \(tableName)"
185
247
  }
186
-
248
+
187
249
  var statement: OpaquePointer?
188
250
  var keys: [String] = []
189
-
251
+
190
252
  if sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK {
191
253
  if let pattern = pattern {
192
254
  // Use % wildcard for SQL LIKE pattern matching
193
255
  sqlite3_bind_text(statement, 1, "%\(pattern)%", -1, nil)
194
256
  }
195
-
257
+
196
258
  while sqlite3_step(statement) == SQLITE_ROW {
197
259
  if let key = sqlite3_column_text(statement, 0) {
198
260
  keys.append(String(cString: key))
199
261
  }
200
262
  }
201
263
  }
202
-
264
+
203
265
  sqlite3_finalize(statement)
204
266
  return keys
205
267
  }
206
268
 
207
269
  @objc public func size() throws -> (total: Int, count: Int) {
270
+ guard db != nil else {
271
+ throw NSError(
272
+ domain: "StrataStorage.SQLiteStorage",
273
+ code: 1000,
274
+ userInfo: [NSLocalizedDescriptionKey: "Database not initialized"]
275
+ )
276
+ }
277
+
208
278
  let querySQL = "SELECT COUNT(*), SUM(LENGTH(value)) FROM \(tableName)"
209
279
  var statement: OpaquePointer?
210
-
280
+
211
281
  var totalSize = 0
212
282
  var count = 0
213
-
283
+
214
284
  if sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK {
215
285
  if sqlite3_step(statement) == SQLITE_ROW {
216
286
  count = Int(sqlite3_column_int(statement, 0))
217
287
  totalSize = Int(sqlite3_column_int64(statement, 1))
218
288
  }
219
289
  }
220
-
290
+
221
291
  sqlite3_finalize(statement)
222
292
  return (total: totalSize, count: count)
223
293
  }
@@ -6,7 +6,11 @@ import Foundation
6
6
 
7
7
  @objc public init(suiteName: String? = nil) {
8
8
  self.suiteName = suiteName
9
- self.userDefaults = suiteName != nil ? UserDefaults(suiteName: suiteName)! : UserDefaults.standard
9
+ if let suiteName = suiteName, let customDefaults = UserDefaults(suiteName: suiteName) {
10
+ self.userDefaults = customDefaults
11
+ } else {
12
+ self.userDefaults = UserDefaults.standard
13
+ }
10
14
  super.init()
11
15
  }
12
16
 
@@ -36,7 +40,14 @@ import Foundation
36
40
  if let suiteName = suiteName {
37
41
  UserDefaults(suiteName: suiteName)?.removePersistentDomain(forName: suiteName)
38
42
  } else {
39
- let domain = Bundle.main.bundleIdentifier!
43
+ guard let domain = Bundle.main.bundleIdentifier else {
44
+ // Fallback: manually clear all keys
45
+ let keys = Array(userDefaults.dictionaryRepresentation().keys)
46
+ for key in keys {
47
+ userDefaults.removeObject(forKey: key)
48
+ }
49
+ return userDefaults.synchronize()
50
+ }
40
51
  userDefaults.removePersistentDomain(forName: domain)
41
52
  }
42
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strata-storage",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "description": "Zero-dependency universal storage plugin providing a unified API for all storage operations across web, Android, and iOS platforms",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "typecheck": "tsc --noEmit",
37
37
  "test": "vitest",
38
38
  "test:coverage": "vitest --coverage",
39
- "prepublishOnly": "yarn build && yarn lint && yarn typecheck",
39
+ "prepublishOnly": "pnpm build && pnpm lint && pnpm typecheck",
40
40
  "postinstall": "node scripts/postinstall.js || true"
41
41
  },
42
42
  "keywords": [
@@ -67,12 +67,11 @@
67
67
  "url": "https://github.com/aoneahsan/strata-storage/issues"
68
68
  },
69
69
  "homepage": "https://github.com/aoneahsan/strata-storage#readme",
70
- "dependencies": {},
71
70
  "peerDependencies": {
72
- "@angular/core": ">=20.3.0",
73
- "@capacitor/core": ">=7.4.0",
74
- "react": ">=19.1.0",
75
- "vue": ">=3.6.0"
71
+ "@angular/core": ">=21.0.6",
72
+ "@capacitor/core": ">=8.0.0",
73
+ "react": ">=19.2.3",
74
+ "vue": ">=3.5.26"
76
75
  },
77
76
  "peerDependenciesMeta": {
78
77
  "@capacitor/core": {
@@ -89,23 +88,22 @@
89
88
  }
90
89
  },
91
90
  "devDependencies": {
92
- "@eslint/js": "^10.0.0",
93
- "@types/node": "^24.7.0",
94
- "@types/react": "^19.2.2",
95
- "@typescript-eslint/eslint-plugin": "^8.46.0",
96
- "@typescript-eslint/parser": "^8.46.0",
97
- "@vitest/coverage-v8": "^3.2.4",
98
- "eslint": "^9.37.0",
91
+ "@types/node": "^25.0.3",
92
+ "@types/react": "^19.2.7",
93
+ "@typescript-eslint/eslint-plugin": "^8.50.1",
94
+ "@typescript-eslint/parser": "^8.50.1",
95
+ "@vitest/coverage-v8": "^4.0.16",
96
+ "eslint": "^9.39.2",
99
97
  "eslint-config-prettier": "^10.1.8",
100
98
  "eslint-plugin-prettier": "^5.5.4",
101
- "jsdom": "^27.0.0",
102
- "prettier": "^3.6.2",
99
+ "jsdom": "^27.4.0",
100
+ "prettier": "^3.7.4",
103
101
  "typescript": "^5.9.3",
104
- "typescript-eslint": "^8.46.0",
105
- "vitest": "^3.2.4"
102
+ "typescript-eslint": "^8.50.1",
103
+ "vitest": "^4.0.16"
106
104
  },
107
105
  "engines": {
108
- "node": ">=24.2.0"
106
+ "node": ">=25.0.3"
109
107
  },
110
108
  "capacitor": {
111
109
  "ios": {