strata-storage 2.1.0 → 2.3.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/LICENSE +35 -21
- package/Readme.md +11 -1
- package/android/src/main/java/com/strata/storage/EncryptedStorage.java +56 -7
- package/android/src/main/java/com/strata/storage/SQLiteStorage.java +57 -2
- package/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +38 -4
- package/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +10 -20
- package/dist/LICENSE +35 -21
- package/dist/adapters/web/LocalStorageAdapter.d.ts.map +1 -1
- package/dist/adapters/web/LocalStorageAdapter.js +11 -2
- package/dist/adapters/web/MemoryAdapter.d.ts.map +1 -1
- package/dist/adapters/web/MemoryAdapter.js +27 -6
- package/dist/adapters/web/SessionStorageAdapter.d.ts +1 -1
- package/dist/adapters/web/SessionStorageAdapter.d.ts.map +1 -1
- package/dist/adapters/web/SessionStorageAdapter.js +10 -5
- package/dist/android/src/main/java/com/strata/storage/EncryptedStorage.java +56 -7
- package/dist/android/src/main/java/com/strata/storage/SQLiteStorage.java +57 -2
- package/dist/android/src/main/java/com/strata/storage/SharedPreferencesStorage.java +38 -4
- package/dist/android/src/main/java/com/stratastorage/StrataStoragePlugin.java +10 -20
- package/dist/core/BaseAdapter.d.ts.map +1 -1
- package/dist/core/BaseAdapter.js +32 -5
- package/dist/core/Strata.d.ts.map +1 -1
- package/dist/core/Strata.js +12 -1
- package/dist/ios/Plugin/KeychainStorage.swift +44 -9
- package/dist/ios/Plugin/SQLiteStorage.swift +36 -2
- package/dist/ios/Plugin/UserDefaultsStorage.swift +14 -0
- package/dist/package.json +2 -2
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -1
- package/ios/Plugin/KeychainStorage.swift +44 -9
- package/ios/Plugin/SQLiteStorage.swift +36 -2
- package/ios/Plugin/UserDefaultsStorage.swift +14 -0
- package/package.json +9 -9
|
@@ -20,7 +20,7 @@ export class SessionStorageAdapter extends LocalStorageAdapter {
|
|
|
20
20
|
encrypted: false,
|
|
21
21
|
crossTab: false, // Session storage is per-tab
|
|
22
22
|
};
|
|
23
|
-
constructor(prefix = '
|
|
23
|
+
constructor(prefix = '') {
|
|
24
24
|
super(prefix);
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
@@ -101,7 +101,8 @@ export class SessionStorageAdapter extends LocalStorageAdapter {
|
|
|
101
101
|
* Clear sessionStorage
|
|
102
102
|
*/
|
|
103
103
|
async clear(options) {
|
|
104
|
-
if (!options ||
|
|
104
|
+
if (!options ||
|
|
105
|
+
(!options.pattern && !options.prefix && !options.tags && !options.expiredOnly)) {
|
|
105
106
|
// Clear all with our prefix
|
|
106
107
|
const keysToRemove = [];
|
|
107
108
|
for (let i = 0; i < window.sessionStorage.length; i++) {
|
|
@@ -143,23 +144,27 @@ export class SessionStorageAdapter extends LocalStorageAdapter {
|
|
|
143
144
|
let count = 0;
|
|
144
145
|
let keySize = 0;
|
|
145
146
|
let valueSize = 0;
|
|
147
|
+
const byKey = {};
|
|
146
148
|
for (let i = 0; i < window.sessionStorage.length; i++) {
|
|
147
149
|
const fullKey = window.sessionStorage.key(i);
|
|
148
150
|
if (fullKey?.startsWith(this.prefix)) {
|
|
149
151
|
const item = window.sessionStorage.getItem(fullKey);
|
|
150
152
|
if (item) {
|
|
151
153
|
count++;
|
|
154
|
+
const key = fullKey.substring(this.prefix.length);
|
|
152
155
|
const itemSize = (fullKey.length + item.length) * 2; // UTF-16
|
|
153
156
|
total += itemSize;
|
|
154
157
|
if (detailed) {
|
|
155
158
|
keySize += fullKey.length * 2;
|
|
156
159
|
valueSize += item.length * 2;
|
|
160
|
+
byKey[key] = itemSize;
|
|
157
161
|
}
|
|
158
162
|
}
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
const result = { total, count };
|
|
162
166
|
if (detailed) {
|
|
167
|
+
result.byKey = byKey;
|
|
163
168
|
result.detailed = {
|
|
164
169
|
keys: keySize,
|
|
165
170
|
values: valueSize,
|
|
@@ -172,9 +177,9 @@ export class SessionStorageAdapter extends LocalStorageAdapter {
|
|
|
172
177
|
* Subscribe to storage changes
|
|
173
178
|
* Note: sessionStorage doesn't fire storage events in the same tab
|
|
174
179
|
*/
|
|
175
|
-
subscribe(
|
|
180
|
+
subscribe(callback) {
|
|
176
181
|
// For sessionStorage, we only get local changes, not cross-tab
|
|
177
|
-
//
|
|
178
|
-
return ()
|
|
182
|
+
// Use the base class subscription for local changes
|
|
183
|
+
return super.subscribe(callback);
|
|
179
184
|
}
|
|
180
185
|
}
|
|
@@ -8,6 +8,9 @@ import androidx.security.crypto.MasterKey;
|
|
|
8
8
|
import java.util.Set;
|
|
9
9
|
import java.util.HashSet;
|
|
10
10
|
import java.util.Map;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
import java.util.ArrayList;
|
|
13
|
+
import org.json.JSONObject;
|
|
11
14
|
|
|
12
15
|
public class EncryptedStorage {
|
|
13
16
|
private SharedPreferences encryptedPrefs;
|
|
@@ -42,9 +45,23 @@ public class EncryptedStorage {
|
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
public boolean set(String key,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
public boolean set(String key, Object value) {
|
|
49
|
+
try {
|
|
50
|
+
String stringValue;
|
|
51
|
+
if (value instanceof String) {
|
|
52
|
+
stringValue = (String) value;
|
|
53
|
+
} else {
|
|
54
|
+
// Convert complex objects to JSON
|
|
55
|
+
stringValue = value instanceof JSONObject ?
|
|
56
|
+
((JSONObject) value).toString() :
|
|
57
|
+
new JSONObject(value).toString();
|
|
58
|
+
}
|
|
59
|
+
editor.putString(key, stringValue);
|
|
60
|
+
return editor.commit();
|
|
61
|
+
} catch (Exception e) {
|
|
62
|
+
e.printStackTrace();
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
48
65
|
}
|
|
49
66
|
|
|
50
67
|
public String get(String key) {
|
|
@@ -79,19 +96,19 @@ public class EncryptedStorage {
|
|
|
79
96
|
return editor.commit();
|
|
80
97
|
}
|
|
81
98
|
|
|
82
|
-
public
|
|
99
|
+
public List<String> keys() {
|
|
83
100
|
return keys(null);
|
|
84
101
|
}
|
|
85
102
|
|
|
86
|
-
public
|
|
103
|
+
public List<String> keys(String pattern) {
|
|
87
104
|
Set<String> allKeys = encryptedPrefs.getAll().keySet();
|
|
88
105
|
|
|
89
106
|
if (pattern == null) {
|
|
90
|
-
return allKeys;
|
|
107
|
+
return new ArrayList<>(allKeys);
|
|
91
108
|
}
|
|
92
109
|
|
|
93
110
|
// Filter keys by pattern
|
|
94
|
-
|
|
111
|
+
List<String> filteredKeys = new ArrayList<>();
|
|
95
112
|
for (String key : allKeys) {
|
|
96
113
|
if (key.startsWith(pattern) || key.contains(pattern)) {
|
|
97
114
|
filteredKeys.add(key);
|
|
@@ -103,4 +120,36 @@ public class EncryptedStorage {
|
|
|
103
120
|
public boolean has(String key) {
|
|
104
121
|
return encryptedPrefs.contains(key);
|
|
105
122
|
}
|
|
123
|
+
|
|
124
|
+
public SizeInfo size() {
|
|
125
|
+
Map<String, ?> all = encryptedPrefs.getAll();
|
|
126
|
+
long totalSize = 0;
|
|
127
|
+
int count = all.size();
|
|
128
|
+
|
|
129
|
+
for (Map.Entry<String, ?> entry : all.entrySet()) {
|
|
130
|
+
String key = entry.getKey();
|
|
131
|
+
Object value = entry.getValue();
|
|
132
|
+
|
|
133
|
+
// Estimate size (key + value in bytes)
|
|
134
|
+
totalSize += key.getBytes().length;
|
|
135
|
+
if (value != null) {
|
|
136
|
+
totalSize += value.toString().getBytes().length;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return new SizeInfo(totalSize, count);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Size information class
|
|
145
|
+
*/
|
|
146
|
+
public static class SizeInfo {
|
|
147
|
+
public final long total;
|
|
148
|
+
public final int count;
|
|
149
|
+
|
|
150
|
+
public SizeInfo(long total, int count) {
|
|
151
|
+
this.total = total;
|
|
152
|
+
this.count = count;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
106
155
|
}
|
|
@@ -9,6 +9,8 @@ import java.util.ArrayList;
|
|
|
9
9
|
import java.util.List;
|
|
10
10
|
import java.util.HashMap;
|
|
11
11
|
import java.util.Map;
|
|
12
|
+
import org.json.JSONObject;
|
|
13
|
+
import java.nio.charset.StandardCharsets;
|
|
12
14
|
|
|
13
15
|
public class SQLiteStorage extends SQLiteOpenHelper {
|
|
14
16
|
private static final int DATABASE_VERSION = 1;
|
|
@@ -50,13 +52,31 @@ public class SQLiteStorage extends SQLiteOpenHelper {
|
|
|
50
52
|
onCreate(db);
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
public boolean set(String key,
|
|
55
|
+
public boolean set(String key, Object value, Long expires, String tags, String metadata) {
|
|
56
|
+
byte[] valueBytes;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
if (value instanceof byte[]) {
|
|
60
|
+
valueBytes = (byte[]) value;
|
|
61
|
+
} else if (value instanceof String) {
|
|
62
|
+
valueBytes = ((String) value).getBytes(StandardCharsets.UTF_8);
|
|
63
|
+
} else {
|
|
64
|
+
// Convert complex objects to JSON then to bytes
|
|
65
|
+
String json = value instanceof JSONObject ?
|
|
66
|
+
((JSONObject) value).toString() :
|
|
67
|
+
new JSONObject(value).toString();
|
|
68
|
+
valueBytes = json.getBytes(StandardCharsets.UTF_8);
|
|
69
|
+
}
|
|
70
|
+
} catch (Exception e) {
|
|
71
|
+
e.printStackTrace();
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
54
74
|
SQLiteDatabase db = this.getWritableDatabase();
|
|
55
75
|
ContentValues values = new ContentValues();
|
|
56
76
|
|
|
57
77
|
long now = System.currentTimeMillis();
|
|
58
78
|
values.put(KEY_ID, key);
|
|
59
|
-
values.put(KEY_VALUE,
|
|
79
|
+
values.put(KEY_VALUE, valueBytes);
|
|
60
80
|
values.put(KEY_CREATED, now);
|
|
61
81
|
values.put(KEY_UPDATED, now);
|
|
62
82
|
|
|
@@ -75,6 +95,11 @@ public class SQLiteStorage extends SQLiteOpenHelper {
|
|
|
75
95
|
return result != -1;
|
|
76
96
|
}
|
|
77
97
|
|
|
98
|
+
// Convenience method for simple Object values
|
|
99
|
+
public boolean set(String key, Object value) {
|
|
100
|
+
return set(key, value, null, null, null);
|
|
101
|
+
}
|
|
102
|
+
|
|
78
103
|
public Map<String, Object> get(String key) {
|
|
79
104
|
SQLiteDatabase db = this.getReadableDatabase();
|
|
80
105
|
Cursor cursor = db.query(TABLE_NAME, null, KEY_ID + "=?",
|
|
@@ -174,4 +199,34 @@ public class SQLiteStorage extends SQLiteOpenHelper {
|
|
|
174
199
|
db.close();
|
|
175
200
|
return exists;
|
|
176
201
|
}
|
|
202
|
+
|
|
203
|
+
public SizeInfo size() {
|
|
204
|
+
SQLiteDatabase db = this.getReadableDatabase();
|
|
205
|
+
Cursor cursor = db.rawQuery("SELECT COUNT(*), SUM(LENGTH(" + KEY_VALUE + ")) FROM " + TABLE_NAME, null);
|
|
206
|
+
|
|
207
|
+
long totalSize = 0;
|
|
208
|
+
int count = 0;
|
|
209
|
+
|
|
210
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
211
|
+
count = cursor.getInt(0);
|
|
212
|
+
totalSize = cursor.getLong(1);
|
|
213
|
+
cursor.close();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
db.close();
|
|
217
|
+
return new SizeInfo(totalSize, count);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Size information class
|
|
222
|
+
*/
|
|
223
|
+
public static class SizeInfo {
|
|
224
|
+
public final long total;
|
|
225
|
+
public final int count;
|
|
226
|
+
|
|
227
|
+
public SizeInfo(long total, int count) {
|
|
228
|
+
this.total = total;
|
|
229
|
+
this.count = count;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
177
232
|
}
|
|
@@ -5,6 +5,8 @@ import android.content.SharedPreferences;
|
|
|
5
5
|
import java.util.Map;
|
|
6
6
|
import java.util.Set;
|
|
7
7
|
import java.util.HashSet;
|
|
8
|
+
import java.util.List;
|
|
9
|
+
import java.util.ArrayList;
|
|
8
10
|
import org.json.JSONObject;
|
|
9
11
|
import org.json.JSONArray;
|
|
10
12
|
|
|
@@ -83,19 +85,19 @@ public class SharedPreferencesStorage {
|
|
|
83
85
|
return editor.commit();
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
public
|
|
88
|
+
public List<String> keys() {
|
|
87
89
|
return keys(null);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
public
|
|
92
|
+
public List<String> keys(String pattern) {
|
|
91
93
|
Set<String> allKeys = prefs.getAll().keySet();
|
|
92
94
|
|
|
93
95
|
if (pattern == null) {
|
|
94
|
-
return allKeys;
|
|
96
|
+
return new ArrayList<>(allKeys);
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
// Filter keys by pattern
|
|
98
|
-
|
|
100
|
+
List<String> filteredKeys = new ArrayList<>();
|
|
99
101
|
for (String key : allKeys) {
|
|
100
102
|
if (key.startsWith(pattern) || key.contains(pattern)) {
|
|
101
103
|
filteredKeys.add(key);
|
|
@@ -111,4 +113,36 @@ public class SharedPreferencesStorage {
|
|
|
111
113
|
public Map<String, ?> getAll() {
|
|
112
114
|
return prefs.getAll();
|
|
113
115
|
}
|
|
116
|
+
|
|
117
|
+
public SizeInfo size() {
|
|
118
|
+
Map<String, ?> all = prefs.getAll();
|
|
119
|
+
long totalSize = 0;
|
|
120
|
+
int count = all.size();
|
|
121
|
+
|
|
122
|
+
for (Map.Entry<String, ?> entry : all.entrySet()) {
|
|
123
|
+
String key = entry.getKey();
|
|
124
|
+
Object value = entry.getValue();
|
|
125
|
+
|
|
126
|
+
// Estimate size (key + value in bytes)
|
|
127
|
+
totalSize += key.getBytes().length;
|
|
128
|
+
if (value != null) {
|
|
129
|
+
totalSize += value.toString().getBytes().length;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return new SizeInfo(totalSize, count);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Size information class
|
|
138
|
+
*/
|
|
139
|
+
public static class SizeInfo {
|
|
140
|
+
public final long total;
|
|
141
|
+
public final int count;
|
|
142
|
+
|
|
143
|
+
public SizeInfo(long total, int count) {
|
|
144
|
+
this.total = total;
|
|
145
|
+
this.count = count;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
114
148
|
}
|
|
@@ -11,7 +11,6 @@ import com.strata.storage.SQLiteStorage;
|
|
|
11
11
|
import org.json.JSONArray;
|
|
12
12
|
import org.json.JSONException;
|
|
13
13
|
import java.util.List;
|
|
14
|
-
import java.util.Set;
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* Main Capacitor plugin for Strata Storage
|
|
@@ -226,40 +225,31 @@ public class StrataStoragePlugin extends Plugin {
|
|
|
226
225
|
String storage = call.getString("storage", "preferences");
|
|
227
226
|
|
|
228
227
|
try {
|
|
229
|
-
|
|
228
|
+
JSObject result = new JSObject();
|
|
230
229
|
|
|
231
230
|
switch (storage) {
|
|
232
231
|
case "secure":
|
|
233
|
-
|
|
232
|
+
EncryptedStorage.SizeInfo encryptedSizeInfo = encryptedStorage.size();
|
|
233
|
+
result.put("total", encryptedSizeInfo.total);
|
|
234
|
+
result.put("count", encryptedSizeInfo.count);
|
|
234
235
|
break;
|
|
235
236
|
case "sqlite":
|
|
236
|
-
|
|
237
|
+
SQLiteStorage.SizeInfo sqliteSizeInfo = sqliteStorage.size();
|
|
238
|
+
result.put("total", sqliteSizeInfo.total);
|
|
239
|
+
result.put("count", sqliteSizeInfo.count);
|
|
237
240
|
break;
|
|
238
241
|
case "preferences":
|
|
239
242
|
default:
|
|
240
|
-
|
|
243
|
+
SharedPreferencesStorage.SizeInfo prefsSizeInfo = sharedPrefsStorage.size();
|
|
244
|
+
result.put("total", prefsSizeInfo.total);
|
|
245
|
+
result.put("count", prefsSizeInfo.count);
|
|
241
246
|
break;
|
|
242
247
|
}
|
|
243
248
|
|
|
244
|
-
JSObject result = new JSObject();
|
|
245
|
-
result.put("total", sizeInfo.total);
|
|
246
|
-
result.put("count", sizeInfo.count);
|
|
247
249
|
call.resolve(result);
|
|
248
250
|
} catch (Exception e) {
|
|
249
251
|
call.reject("Failed to get size", e);
|
|
250
252
|
}
|
|
251
253
|
}
|
|
252
254
|
|
|
253
|
-
/**
|
|
254
|
-
* Size information class
|
|
255
|
-
*/
|
|
256
|
-
static class SizeInfo {
|
|
257
|
-
public final long total;
|
|
258
|
-
public final int count;
|
|
259
|
-
|
|
260
|
-
public SizeInfo(long total, int count) {
|
|
261
|
-
this.total = total;
|
|
262
|
-
this.count = count;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
255
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseAdapter.d.ts","sourceRoot":"","sources":["../../src/core/BaseAdapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,WAAW,EACX,mBAAmB,EAEnB,YAAY,EACZ,QAAQ,EACR,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,YAAY,EAA4B,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;GAEG;AACH,8BAAsB,WAAY,YAAW,cAAc;IACzD,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IACpC,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAEpD,SAAS,CAAC,YAAY,eAAsB;IAC5C,SAAS,CAAC,WAAW,cAAqB;IAC1C,SAAS,CAAC,kBAAkB,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IAC9D,SAAS,CAAC,gBAAgB,SAAS;IAEnC;;OAEG;IACH,SAAS,CAAC,eAAe,IAAI,IAAI;IAYjC;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAOhC;;OAEG;cACa,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAY/C;;OAEG;IACH,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO;IAKjD;;OAEG;IACH,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE;
|
|
1
|
+
{"version":3,"file":"BaseAdapter.d.ts","sourceRoot":"","sources":["../../src/core/BaseAdapter.ts"],"names":[],"mappings":"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"}
|
package/dist/core/BaseAdapter.js
CHANGED
|
@@ -66,6 +66,10 @@ export class BaseAdapter {
|
|
|
66
66
|
if (pattern instanceof RegExp) {
|
|
67
67
|
return keys.filter((key) => pattern.test(key));
|
|
68
68
|
}
|
|
69
|
+
// If pattern doesn't contain glob characters, treat it as a prefix
|
|
70
|
+
if (!pattern.includes('*') && !pattern.includes('?')) {
|
|
71
|
+
return keys.filter((key) => key.startsWith(pattern));
|
|
72
|
+
}
|
|
69
73
|
return keys.filter((key) => matchGlob(pattern, key));
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
@@ -88,8 +92,10 @@ export class BaseAdapter {
|
|
|
88
92
|
const keys = await this.keys();
|
|
89
93
|
for (const key of keys) {
|
|
90
94
|
let shouldDelete = true;
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
// Support both pattern and prefix options
|
|
96
|
+
const pattern = options?.pattern || options?.prefix;
|
|
97
|
+
if (pattern) {
|
|
98
|
+
shouldDelete = this.filterKeys([key], pattern).length > 0;
|
|
93
99
|
}
|
|
94
100
|
if (shouldDelete && options?.tags) {
|
|
95
101
|
const value = await this.get(key);
|
|
@@ -117,14 +123,19 @@ export class BaseAdapter {
|
|
|
117
123
|
let keySize = 0;
|
|
118
124
|
let valueSize = 0;
|
|
119
125
|
let metadataSize = 0;
|
|
126
|
+
const byKey = {};
|
|
120
127
|
for (const key of keys) {
|
|
121
|
-
|
|
128
|
+
const keyLength = key.length * 2; // UTF-16
|
|
129
|
+
keySize += keyLength;
|
|
122
130
|
const item = await this.get(key);
|
|
123
131
|
if (item) {
|
|
124
132
|
const size = this.calculateSize(item);
|
|
125
133
|
valueSize += getObjectSize(item.value);
|
|
126
134
|
metadataSize += size - getObjectSize(item.value);
|
|
127
135
|
total += size;
|
|
136
|
+
if (detailed) {
|
|
137
|
+
byKey[key] = size + keyLength;
|
|
138
|
+
}
|
|
128
139
|
}
|
|
129
140
|
}
|
|
130
141
|
const result = {
|
|
@@ -132,6 +143,7 @@ export class BaseAdapter {
|
|
|
132
143
|
count: keys.length,
|
|
133
144
|
};
|
|
134
145
|
if (detailed) {
|
|
146
|
+
result.byKey = byKey;
|
|
135
147
|
result.detailed = {
|
|
136
148
|
keys: keySize,
|
|
137
149
|
values: valueSize,
|
|
@@ -181,8 +193,23 @@ export class BaseAdapter {
|
|
|
181
193
|
const keys = await this.keys();
|
|
182
194
|
for (const key of keys) {
|
|
183
195
|
const item = await this.get(key);
|
|
184
|
-
if (item && !this.isExpired(item)
|
|
185
|
-
|
|
196
|
+
if (item && !this.isExpired(item)) {
|
|
197
|
+
// Check if querying storage metadata (tags, metadata, etc) or the actual value
|
|
198
|
+
let matches = false;
|
|
199
|
+
// Check for storage-level properties
|
|
200
|
+
const storageProps = ['tags', 'metadata', 'created', 'updated', 'expires'];
|
|
201
|
+
const isStorageQuery = Object.keys(condition).some((k) => storageProps.includes(k));
|
|
202
|
+
if (isStorageQuery) {
|
|
203
|
+
// Query against the storage wrapper
|
|
204
|
+
matches = this.queryEngine.matches(item, condition);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// Query against the stored value
|
|
208
|
+
matches = this.queryEngine.matches(item.value, condition);
|
|
209
|
+
}
|
|
210
|
+
if (matches) {
|
|
211
|
+
results.push({ key, value: item.value });
|
|
212
|
+
}
|
|
186
213
|
}
|
|
187
214
|
}
|
|
188
215
|
return results;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Strata.d.ts","sourceRoot":"","sources":["../../src/core/Strata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAQpD;;GAEG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,iBAAiB,CAAC,CAAoB;IAC9C,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,YAAY,CAAkB;gBAE1B,MAAM,GAAE,YAAiB;IAMrC;;OAEG;IACH,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,QAAQ,CAEvB;IAED;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmDjC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA2DhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAwDtF;;;;;;;;;;;;;;;;OAgBG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAelE;;;;;;;;;;;;;;;;;OAiBG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlE;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnE;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAgBlF;;OAEG;IACG,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"Strata.d.ts","sourceRoot":"","sources":["../../src/core/Strata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAQpD;;GAEG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,iBAAiB,CAAC,CAAoB;IAC9C,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,YAAY,CAAkB;gBAE1B,MAAM,GAAE,YAAiB;IAMrC;;OAEG;IACH,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,QAAQ,CAEvB;IAED;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmDjC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA2DhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACG,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAwDtF;;;;;;;;;;;;;;;;OAgBG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAelE;;;;;;;;;;;;;;;;;OAiBG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlE;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnE;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAgBlF;;OAEG;IACG,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAiCjD;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,mBAAmB;IAuBxF;;OAEG;IACG,KAAK,CAAC,CAAC,GAAG,OAAO,EACrB,SAAS,EAAE,cAAc,EACzB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IAU5C;;OAEG;IACG,MAAM,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAyBtD;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BlE;;OAEG;IACH,wBAAwB,IAAI,WAAW,EAAE;IAIzC;;OAEG;IACH,eAAe,CACb,OAAO,CAAC,EAAE,WAAW,GACpB,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAc5D;;OAEG;IACH,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM;IAOzC;;OAEG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOzC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAU3E;;OAEG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBxF;;OAEG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBnE;;OAEG;IACG,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAWrD;;OAEG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAa/D;;;;;;;;;OASG;IACH,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAI9C;;;OAGG;IACH,WAAW,IAAI,eAAe;IAI9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B5B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,kBAAkB;YAgBZ,oBAAoB;YAsCpB,kBAAkB;YAKlB,aAAa;CA2B5B"}
|
package/dist/core/Strata.js
CHANGED
|
@@ -375,17 +375,28 @@ export class Strata {
|
|
|
375
375
|
let total = 0;
|
|
376
376
|
let count = 0;
|
|
377
377
|
const byStorage = {};
|
|
378
|
+
const allByKey = {};
|
|
378
379
|
for (const [type, adapter] of this.adapters.entries()) {
|
|
379
380
|
const sizeInfo = await adapter.size(detailed);
|
|
380
381
|
total += sizeInfo.total;
|
|
381
382
|
count += sizeInfo.count;
|
|
382
383
|
byStorage[type] = sizeInfo.total;
|
|
384
|
+
// Aggregate byKey data if detailed
|
|
385
|
+
if (detailed && sizeInfo.byKey) {
|
|
386
|
+
for (const [key, size] of Object.entries(sizeInfo.byKey)) {
|
|
387
|
+
allByKey[key] = (allByKey[key] || 0) + size;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
383
390
|
}
|
|
384
|
-
|
|
391
|
+
const result = {
|
|
385
392
|
total,
|
|
386
393
|
count,
|
|
387
394
|
byStorage: byStorage,
|
|
388
395
|
};
|
|
396
|
+
if (detailed) {
|
|
397
|
+
result.byKey = allByKey;
|
|
398
|
+
}
|
|
399
|
+
return result;
|
|
389
400
|
}
|
|
390
401
|
/**
|
|
391
402
|
* Subscribe to storage changes
|
|
@@ -11,18 +11,30 @@ import Security
|
|
|
11
11
|
super.init()
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
@objc public func set(key: String, value:
|
|
14
|
+
@objc public func set(key: String, value: Any) throws -> Bool {
|
|
15
|
+
let data: Data
|
|
16
|
+
|
|
17
|
+
if let dataValue = value as? Data {
|
|
18
|
+
data = dataValue
|
|
19
|
+
} else if let stringValue = value as? String {
|
|
20
|
+
data = stringValue.data(using: .utf8) ?? Data()
|
|
21
|
+
} else {
|
|
22
|
+
// Convert to JSON for complex objects
|
|
23
|
+
let jsonData = try JSONSerialization.data(withJSONObject: value, options: [])
|
|
24
|
+
data = jsonData
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
let query = createQuery(key: key)
|
|
16
28
|
SecItemDelete(query as CFDictionary)
|
|
17
29
|
|
|
18
30
|
var newItem = query
|
|
19
|
-
newItem[kSecValueData as String] =
|
|
31
|
+
newItem[kSecValueData as String] = data
|
|
20
32
|
|
|
21
33
|
let status = SecItemAdd(newItem as CFDictionary, nil)
|
|
22
34
|
return status == errSecSuccess
|
|
23
35
|
}
|
|
24
36
|
|
|
25
|
-
@objc public func get(key: String) ->
|
|
37
|
+
@objc public func get(key: String) throws -> Any? {
|
|
26
38
|
var query = createQuery(key: key)
|
|
27
39
|
query[kSecReturnData as String] = true
|
|
28
40
|
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
|
@@ -31,22 +43,29 @@ import Security
|
|
|
31
43
|
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
32
44
|
|
|
33
45
|
guard status == errSecSuccess else { return nil }
|
|
34
|
-
|
|
46
|
+
guard let data = result as? Data else { return nil }
|
|
47
|
+
|
|
48
|
+
// Try to parse as JSON first, fallback to string
|
|
49
|
+
if let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []) {
|
|
50
|
+
return jsonObject
|
|
51
|
+
} else {
|
|
52
|
+
return String(data: data, encoding: .utf8)
|
|
53
|
+
}
|
|
35
54
|
}
|
|
36
55
|
|
|
37
|
-
@objc public func remove(key: String) -> Bool {
|
|
56
|
+
@objc public func remove(key: String) throws -> Bool {
|
|
38
57
|
let query = createQuery(key: key)
|
|
39
58
|
let status = SecItemDelete(query as CFDictionary)
|
|
40
59
|
return status == errSecSuccess
|
|
41
60
|
}
|
|
42
61
|
|
|
43
|
-
@objc public func clear(prefix: String? = nil) -> Bool {
|
|
62
|
+
@objc public func clear(prefix: String? = nil) throws -> Bool {
|
|
44
63
|
if let prefix = prefix {
|
|
45
64
|
// Clear only keys with the given prefix
|
|
46
|
-
let keysToRemove = keys(pattern: prefix)
|
|
65
|
+
let keysToRemove = try keys(pattern: prefix)
|
|
47
66
|
var allSuccess = true
|
|
48
67
|
for key in keysToRemove {
|
|
49
|
-
if !remove(key: key) {
|
|
68
|
+
if !(try remove(key: key)) {
|
|
50
69
|
allSuccess = false
|
|
51
70
|
}
|
|
52
71
|
}
|
|
@@ -62,7 +81,7 @@ import Security
|
|
|
62
81
|
}
|
|
63
82
|
}
|
|
64
83
|
|
|
65
|
-
@objc public func keys(pattern: String? = nil) -> [String] {
|
|
84
|
+
@objc public func keys(pattern: String? = nil) throws -> [String] {
|
|
66
85
|
var query: [String: Any] = [
|
|
67
86
|
kSecClass as String: kSecClassGenericPassword,
|
|
68
87
|
kSecAttrService as String: service,
|
|
@@ -106,4 +125,20 @@ import Security
|
|
|
106
125
|
|
|
107
126
|
return query
|
|
108
127
|
}
|
|
128
|
+
|
|
129
|
+
@objc public func size() throws -> (total: Int, count: Int) {
|
|
130
|
+
let allKeys = try keys()
|
|
131
|
+
var totalSize = 0
|
|
132
|
+
|
|
133
|
+
for key in allKeys {
|
|
134
|
+
if let data = try get(key: key) as? Data {
|
|
135
|
+
totalSize += data.count
|
|
136
|
+
} else if let string = try get(key: key) as? String {
|
|
137
|
+
totalSize += string.data(using: .utf8)?.count ?? 0
|
|
138
|
+
}
|
|
139
|
+
totalSize += key.data(using: .utf8)?.count ?? 0
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return (total: totalSize, count: allKeys.count)
|
|
143
|
+
}
|
|
109
144
|
}
|
|
@@ -53,7 +53,18 @@ import SQLite3
|
|
|
53
53
|
sqlite3_finalize(createTableStatement)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
@objc public func set(key: String, value:
|
|
56
|
+
@objc public func set(key: String, value: Any, expires: Int64? = nil, tags: [String]? = nil, metadata: [String: Any]? = nil) throws -> Bool {
|
|
57
|
+
let data: Data
|
|
58
|
+
|
|
59
|
+
if let dataValue = value as? Data {
|
|
60
|
+
data = dataValue
|
|
61
|
+
} else if let stringValue = value as? String {
|
|
62
|
+
data = stringValue.data(using: .utf8) ?? Data()
|
|
63
|
+
} else {
|
|
64
|
+
// Convert to JSON for complex objects
|
|
65
|
+
data = try JSONSerialization.data(withJSONObject: value, options: [])
|
|
66
|
+
}
|
|
67
|
+
|
|
57
68
|
let now = Int64(Date().timeIntervalSince1970 * 1000)
|
|
58
69
|
let tagsJson = tags != nil ? try? JSONSerialization.data(withJSONObject: tags!, options: []) : nil
|
|
59
70
|
let metadataJson = metadata != nil ? try? JSONSerialization.data(withJSONObject: metadata!, options: []) : nil
|
|
@@ -67,7 +78,7 @@ import SQLite3
|
|
|
67
78
|
var statement: OpaquePointer?
|
|
68
79
|
let result = sqlite3_prepare_v2(db, insertSQL, -1, &statement, nil) == SQLITE_OK &&
|
|
69
80
|
sqlite3_bind_text(statement, 1, key, -1, nil) == SQLITE_OK &&
|
|
70
|
-
sqlite3_bind_blob(statement, 2, (
|
|
81
|
+
sqlite3_bind_blob(statement, 2, (data as NSData).bytes, Int32(data.count), nil) == SQLITE_OK &&
|
|
71
82
|
sqlite3_bind_int64(statement, 3, now) == SQLITE_OK &&
|
|
72
83
|
sqlite3_bind_int64(statement, 4, now) == SQLITE_OK &&
|
|
73
84
|
(expires != nil ? sqlite3_bind_int64(statement, 5, expires!) : sqlite3_bind_null(statement, 5)) == SQLITE_OK &&
|
|
@@ -79,6 +90,11 @@ import SQLite3
|
|
|
79
90
|
return result
|
|
80
91
|
}
|
|
81
92
|
|
|
93
|
+
// Convenience method for simple values
|
|
94
|
+
@objc public func set(key: String, value: Any) throws -> Bool {
|
|
95
|
+
return try set(key: key, value: value, expires: nil, tags: nil, metadata: nil)
|
|
96
|
+
}
|
|
97
|
+
|
|
82
98
|
@objc public func get(key: String) -> [String: Any]? {
|
|
83
99
|
let querySQL = "SELECT * FROM \(tableName) WHERE key = ? LIMIT 1"
|
|
84
100
|
var statement: OpaquePointer?
|
|
@@ -187,4 +203,22 @@ import SQLite3
|
|
|
187
203
|
sqlite3_finalize(statement)
|
|
188
204
|
return keys
|
|
189
205
|
}
|
|
206
|
+
|
|
207
|
+
@objc public func size() throws -> (total: Int, count: Int) {
|
|
208
|
+
let querySQL = "SELECT COUNT(*), SUM(LENGTH(value)) FROM \(tableName)"
|
|
209
|
+
var statement: OpaquePointer?
|
|
210
|
+
|
|
211
|
+
var totalSize = 0
|
|
212
|
+
var count = 0
|
|
213
|
+
|
|
214
|
+
if sqlite3_prepare_v2(db, querySQL, -1, &statement, nil) == SQLITE_OK {
|
|
215
|
+
if sqlite3_step(statement) == SQLITE_ROW {
|
|
216
|
+
count = Int(sqlite3_column_int(statement, 0))
|
|
217
|
+
totalSize = Int(sqlite3_column_int64(statement, 1))
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
sqlite3_finalize(statement)
|
|
222
|
+
return (total: totalSize, count: count)
|
|
223
|
+
}
|
|
190
224
|
}
|