@esportsplus/web-storage 0.4.0 → 0.5.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.
- package/README.md +248 -0
- package/build/drivers/localstorage.d.ts +1 -0
- package/build/drivers/localstorage.js +17 -3
- package/build/drivers/sessionstorage.d.ts +1 -0
- package/build/drivers/sessionstorage.js +17 -3
- package/build/index.d.ts +1 -1
- package/build/index.js +82 -44
- package/build/lz.d.ts +3 -0
- package/build/lz.js +138 -0
- package/build/types.d.ts +1 -1
- package/package.json +5 -5
- package/src/drivers/localstorage.ts +22 -3
- package/src/drivers/sessionstorage.ts +22 -3
- package/src/index.ts +110 -52
- package/src/lz.ts +200 -0
- package/src/types.ts +1 -1
- package/tests/drivers/localstorage.ts +86 -0
- package/tests/drivers/sessionstorage.ts +85 -0
- package/tests/index.ts +564 -10
- package/tests/lz.ts +371 -0
- package/storage/feature-research.md +0 -173
package/build/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { encryption } from '@esportsplus/utilities';
|
|
2
2
|
import { DriverType } from './constants.js';
|
|
3
3
|
import { IndexedDBDriver } from './drivers/indexeddb.js';
|
|
4
4
|
import { LocalStorageDriver } from './drivers/localstorage.js';
|
|
@@ -11,14 +11,13 @@ function isEnvelope(value) {
|
|
|
11
11
|
&& '__e' in value
|
|
12
12
|
&& '__v' in value;
|
|
13
13
|
}
|
|
14
|
-
async function deserialize(value,
|
|
14
|
+
async function deserialize(value, cipher) {
|
|
15
15
|
if (value === undefined || value === null) {
|
|
16
16
|
return undefined;
|
|
17
17
|
}
|
|
18
|
-
if (
|
|
18
|
+
if (cipher && typeof value === 'string') {
|
|
19
19
|
try {
|
|
20
|
-
value = await decrypt(value
|
|
21
|
-
value = JSON.parse(value);
|
|
20
|
+
value = await cipher.decrypt(value);
|
|
22
21
|
}
|
|
23
22
|
catch {
|
|
24
23
|
return undefined;
|
|
@@ -26,12 +25,12 @@ async function deserialize(value, secret) {
|
|
|
26
25
|
}
|
|
27
26
|
return value;
|
|
28
27
|
}
|
|
29
|
-
async function serialize(value,
|
|
28
|
+
async function serialize(value, cipher) {
|
|
30
29
|
if (value === undefined || value === null) {
|
|
31
30
|
return value;
|
|
32
31
|
}
|
|
33
|
-
if (
|
|
34
|
-
return encrypt(
|
|
32
|
+
if (cipher) {
|
|
33
|
+
return cipher.encrypt(value);
|
|
35
34
|
}
|
|
36
35
|
return value;
|
|
37
36
|
}
|
|
@@ -70,11 +69,11 @@ async function migrate(driver, migrations, version) {
|
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
71
|
let transformed = await migrations[keys[i]]({ all: () => Promise.resolve(data) });
|
|
73
|
-
await driver.clear();
|
|
74
72
|
let entries = [];
|
|
75
73
|
for (let key in transformed) {
|
|
76
74
|
entries.push([key, transformed[key]]);
|
|
77
75
|
}
|
|
76
|
+
await driver.clear();
|
|
78
77
|
if (entries.length > 0) {
|
|
79
78
|
await driver.replace(entries);
|
|
80
79
|
}
|
|
@@ -82,16 +81,16 @@ async function migrate(driver, migrations, version) {
|
|
|
82
81
|
await driver.set(VERSION_KEY, version);
|
|
83
82
|
}
|
|
84
83
|
class Local {
|
|
84
|
+
cipher;
|
|
85
85
|
driver;
|
|
86
86
|
globals;
|
|
87
87
|
listeners;
|
|
88
88
|
ready;
|
|
89
|
-
secret;
|
|
90
89
|
version;
|
|
91
90
|
constructor(options, secret) {
|
|
91
|
+
this.cipher = null;
|
|
92
92
|
this.globals = new Set();
|
|
93
93
|
this.listeners = new Map();
|
|
94
|
-
this.secret = secret || null;
|
|
95
94
|
let { migrations, name, version = 1 } = options;
|
|
96
95
|
this.version = version;
|
|
97
96
|
if (options.driver === DriverType.LocalStorage) {
|
|
@@ -106,11 +105,14 @@ class Local {
|
|
|
106
105
|
else {
|
|
107
106
|
this.driver = new IndexedDBDriver(name, version);
|
|
108
107
|
}
|
|
108
|
+
let init = secret
|
|
109
|
+
? encryption(secret).then((c) => { this.cipher = c; })
|
|
110
|
+
: Promise.resolve();
|
|
109
111
|
if (migrations) {
|
|
110
|
-
this.ready = migrate(this.driver, migrations, version);
|
|
112
|
+
this.ready = init.then(() => migrate(this.driver, migrations, version));
|
|
111
113
|
}
|
|
112
114
|
else {
|
|
113
|
-
this.ready =
|
|
115
|
+
this.ready = init;
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
async all() {
|
|
@@ -120,7 +122,7 @@ class Local {
|
|
|
120
122
|
if (key === VERSION_KEY) {
|
|
121
123
|
continue;
|
|
122
124
|
}
|
|
123
|
-
let deserialized = await deserialize(raw[key], this.
|
|
125
|
+
let deserialized = await deserialize(raw[key], this.cipher), unwrapped = unwrap(deserialized);
|
|
124
126
|
if (deserialized === undefined) {
|
|
125
127
|
continue;
|
|
126
128
|
}
|
|
@@ -131,7 +133,7 @@ class Local {
|
|
|
131
133
|
result[key] = unwrapped.value;
|
|
132
134
|
}
|
|
133
135
|
if (expired.length > 0) {
|
|
134
|
-
this.driver.delete(expired);
|
|
136
|
+
await this.driver.delete(expired);
|
|
135
137
|
}
|
|
136
138
|
return result;
|
|
137
139
|
}
|
|
@@ -151,7 +153,7 @@ class Local {
|
|
|
151
153
|
if (key === VERSION_KEY) {
|
|
152
154
|
return;
|
|
153
155
|
}
|
|
154
|
-
let deserialized = await deserialize(raw, this.
|
|
156
|
+
let deserialized = await deserialize(raw, this.cipher), unwrapped = unwrap(deserialized);
|
|
155
157
|
if (deserialized !== undefined && unwrapped.expired) {
|
|
156
158
|
expired.push(key);
|
|
157
159
|
oldValues.set(key, unwrapped.value);
|
|
@@ -166,15 +168,32 @@ class Local {
|
|
|
166
168
|
}
|
|
167
169
|
async count() {
|
|
168
170
|
await this.ready;
|
|
169
|
-
let
|
|
170
|
-
|
|
171
|
+
let expired = [], total = 0;
|
|
172
|
+
await this.driver.map(async (raw, key) => {
|
|
173
|
+
if (key === VERSION_KEY) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
let deserialized = await deserialize(raw, this.cipher), unwrapped = unwrap(deserialized);
|
|
177
|
+
if (deserialized === undefined) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (unwrapped.expired) {
|
|
181
|
+
expired.push(key);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
total++;
|
|
185
|
+
});
|
|
186
|
+
if (expired.length > 0) {
|
|
187
|
+
await this.driver.delete(expired);
|
|
188
|
+
}
|
|
189
|
+
return total;
|
|
171
190
|
}
|
|
172
191
|
async delete(...keys) {
|
|
173
192
|
await this.ready;
|
|
174
|
-
let oldValues = new Map();
|
|
175
|
-
for (let
|
|
176
|
-
let
|
|
177
|
-
oldValues.set(
|
|
193
|
+
let oldValues = new Map(), raw = await this.driver.only(keys);
|
|
194
|
+
for (let [key, value] of raw) {
|
|
195
|
+
let deserialized = await deserialize(value, this.cipher), unwrapped = unwrap(deserialized);
|
|
196
|
+
oldValues.set(key, deserialized === undefined ? undefined : unwrapped.value);
|
|
178
197
|
}
|
|
179
198
|
await this.driver.delete(keys);
|
|
180
199
|
for (let i = 0, n = keys.length; i < n; i++) {
|
|
@@ -188,7 +207,7 @@ class Local {
|
|
|
188
207
|
if (stopped || key === VERSION_KEY) {
|
|
189
208
|
return;
|
|
190
209
|
}
|
|
191
|
-
let deserialized = await deserialize(raw, this.
|
|
210
|
+
let deserialized = await deserialize(raw, this.cipher), unwrapped = unwrap(deserialized);
|
|
192
211
|
if (deserialized === undefined) {
|
|
193
212
|
return;
|
|
194
213
|
}
|
|
@@ -201,18 +220,18 @@ class Local {
|
|
|
201
220
|
}
|
|
202
221
|
});
|
|
203
222
|
if (expired.length > 0) {
|
|
204
|
-
this.driver.delete(expired);
|
|
223
|
+
await this.driver.delete(expired);
|
|
205
224
|
}
|
|
206
225
|
return result;
|
|
207
226
|
}
|
|
208
227
|
async get(key, factory) {
|
|
209
228
|
await this.ready;
|
|
210
|
-
let deserialized = await deserialize(await this.driver.get(key), this.
|
|
229
|
+
let deserialized = await deserialize(await this.driver.get(key), this.cipher), missing = false, unwrapped = unwrap(deserialized);
|
|
211
230
|
if (deserialized === undefined) {
|
|
212
231
|
missing = true;
|
|
213
232
|
}
|
|
214
233
|
else if (unwrapped.expired) {
|
|
215
|
-
this.driver.delete([key]);
|
|
234
|
+
await this.driver.delete([key]);
|
|
216
235
|
missing = true;
|
|
217
236
|
}
|
|
218
237
|
if (missing) {
|
|
@@ -227,8 +246,25 @@ class Local {
|
|
|
227
246
|
}
|
|
228
247
|
async keys() {
|
|
229
248
|
await this.ready;
|
|
230
|
-
let
|
|
231
|
-
|
|
249
|
+
let expired = [], result = [];
|
|
250
|
+
await this.driver.map(async (raw, key) => {
|
|
251
|
+
if (key === VERSION_KEY) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
let deserialized = await deserialize(raw, this.cipher), unwrapped = unwrap(deserialized);
|
|
255
|
+
if (deserialized === undefined) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (unwrapped.expired) {
|
|
259
|
+
expired.push(key);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
result.push(key);
|
|
263
|
+
});
|
|
264
|
+
if (expired.length > 0) {
|
|
265
|
+
await this.driver.delete(expired);
|
|
266
|
+
}
|
|
267
|
+
return result;
|
|
232
268
|
}
|
|
233
269
|
async length() {
|
|
234
270
|
await this.ready;
|
|
@@ -241,7 +277,7 @@ class Local {
|
|
|
241
277
|
if (key === VERSION_KEY) {
|
|
242
278
|
return;
|
|
243
279
|
}
|
|
244
|
-
let deserialized = await deserialize(raw, this.
|
|
280
|
+
let deserialized = await deserialize(raw, this.cipher), unwrapped = unwrap(deserialized);
|
|
245
281
|
if (deserialized === undefined) {
|
|
246
282
|
return;
|
|
247
283
|
}
|
|
@@ -252,14 +288,14 @@ class Local {
|
|
|
252
288
|
await fn(unwrapped.value, key, j++);
|
|
253
289
|
});
|
|
254
290
|
if (expired.length > 0) {
|
|
255
|
-
this.driver.delete(expired);
|
|
291
|
+
await this.driver.delete(expired);
|
|
256
292
|
}
|
|
257
293
|
}
|
|
258
294
|
async only(...keys) {
|
|
259
295
|
await this.ready;
|
|
260
296
|
let expired = [], raw = await this.driver.only(keys), result = {};
|
|
261
297
|
for (let [key, value] of raw) {
|
|
262
|
-
let deserialized = await deserialize(value, this.
|
|
298
|
+
let deserialized = await deserialize(value, this.cipher), unwrapped = unwrap(deserialized);
|
|
263
299
|
if (deserialized === undefined) {
|
|
264
300
|
continue;
|
|
265
301
|
}
|
|
@@ -270,36 +306,38 @@ class Local {
|
|
|
270
306
|
result[key] = unwrapped.value;
|
|
271
307
|
}
|
|
272
308
|
if (expired.length > 0) {
|
|
273
|
-
this.driver.delete(expired);
|
|
309
|
+
await this.driver.delete(expired);
|
|
274
310
|
}
|
|
275
311
|
return result;
|
|
276
312
|
}
|
|
277
313
|
async persist(key) {
|
|
278
314
|
await this.ready;
|
|
279
|
-
let raw = await this.driver.get(key), deserialized = await deserialize(raw, this.
|
|
315
|
+
let raw = await this.driver.get(key), deserialized = await deserialize(raw, this.cipher);
|
|
280
316
|
if (deserialized === undefined) {
|
|
281
317
|
return false;
|
|
282
318
|
}
|
|
283
319
|
let unwrapped = unwrap(deserialized);
|
|
284
320
|
if (unwrapped.expired) {
|
|
285
|
-
this.driver.delete([key]);
|
|
321
|
+
await this.driver.delete([key]);
|
|
286
322
|
return false;
|
|
287
323
|
}
|
|
288
324
|
if (!unwrapped.hasTTL) {
|
|
289
325
|
return true;
|
|
290
326
|
}
|
|
291
|
-
return this.driver.set(key, await serialize(unwrapped.value, this.
|
|
327
|
+
return this.driver.set(key, await serialize(unwrapped.value, this.cipher));
|
|
292
328
|
}
|
|
293
329
|
async replace(values) {
|
|
294
330
|
await this.ready;
|
|
295
|
-
let entries = [], failed = [], oldValues = new Map();
|
|
296
|
-
for (let key
|
|
297
|
-
let
|
|
331
|
+
let entries = [], failed = [], fetchKeys = Object.keys(values), oldValues = new Map(), raw = await this.driver.only(fetchKeys);
|
|
332
|
+
for (let key of fetchKeys) {
|
|
333
|
+
let value = raw.get(key), deserialized = value !== undefined
|
|
334
|
+
? await deserialize(value, this.cipher)
|
|
335
|
+
: undefined, unwrapped = unwrap(deserialized);
|
|
298
336
|
oldValues.set(key, deserialized === undefined ? undefined : unwrapped.value);
|
|
299
337
|
try {
|
|
300
338
|
entries.push([
|
|
301
339
|
key,
|
|
302
|
-
await serialize(values[key], this.
|
|
340
|
+
await serialize(values[key], this.cipher)
|
|
303
341
|
]);
|
|
304
342
|
}
|
|
305
343
|
catch {
|
|
@@ -318,16 +356,16 @@ class Local {
|
|
|
318
356
|
async set(key, value, options) {
|
|
319
357
|
await this.ready;
|
|
320
358
|
try {
|
|
321
|
-
let oldRaw = await this.driver.get(key), oldDeserialized = await deserialize(oldRaw, this.
|
|
359
|
+
let oldRaw = await this.driver.get(key), oldDeserialized = await deserialize(oldRaw, this.cipher), oldUnwrapped = unwrap(oldDeserialized), oldValue = oldDeserialized === undefined ? undefined : oldUnwrapped.value, stored;
|
|
322
360
|
if (options?.ttl != null && options.ttl > 0) {
|
|
323
361
|
let envelope = {
|
|
324
362
|
__e: Date.now() + options.ttl,
|
|
325
363
|
__v: value
|
|
326
364
|
};
|
|
327
|
-
stored = await serialize(envelope, this.
|
|
365
|
+
stored = await serialize(envelope, this.cipher);
|
|
328
366
|
}
|
|
329
367
|
else {
|
|
330
|
-
stored = await serialize(value, this.
|
|
368
|
+
stored = await serialize(value, this.cipher);
|
|
331
369
|
}
|
|
332
370
|
let result = await this.driver.set(key, stored);
|
|
333
371
|
notify(this.globals, this.listeners, key, value, oldValue);
|
|
@@ -353,7 +391,7 @@ class Local {
|
|
|
353
391
|
}
|
|
354
392
|
async ttl(key) {
|
|
355
393
|
await this.ready;
|
|
356
|
-
let raw = await this.driver.get(key), deserialized = await deserialize(raw, this.
|
|
394
|
+
let raw = await this.driver.get(key), deserialized = await deserialize(raw, this.cipher);
|
|
357
395
|
if (deserialized === undefined) {
|
|
358
396
|
return -1;
|
|
359
397
|
}
|
|
@@ -362,7 +400,7 @@ class Local {
|
|
|
362
400
|
}
|
|
363
401
|
let remaining = deserialized.__e - Date.now();
|
|
364
402
|
if (remaining <= 0) {
|
|
365
|
-
this.driver.delete([key]);
|
|
403
|
+
await this.driver.delete([key]);
|
|
366
404
|
return -1;
|
|
367
405
|
}
|
|
368
406
|
return remaining;
|
package/build/lz.d.ts
ADDED
package/build/lz.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
let MAX_DECOMPRESSED_SIZE = 10_485_760;
|
|
2
|
+
function emitLiteral(ctx, ch) {
|
|
3
|
+
let code = ch.charCodeAt(0);
|
|
4
|
+
if (code < 256) {
|
|
5
|
+
writeBits(ctx, ctx.numBits, 0);
|
|
6
|
+
writeBits(ctx, 8, code);
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
writeBits(ctx, ctx.numBits, 1);
|
|
10
|
+
writeBits(ctx, 16, code);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function readBits(ctx, n) {
|
|
14
|
+
let result = 0;
|
|
15
|
+
for (let i = 0; i < n; i++) {
|
|
16
|
+
if (ctx.bitPos > 15) {
|
|
17
|
+
if (ctx.pos >= ctx.compressed.length) {
|
|
18
|
+
throw new Error('LZ: unexpected end of compressed data');
|
|
19
|
+
}
|
|
20
|
+
ctx.currentValue = ctx.compressed.charCodeAt(ctx.pos++) - 1;
|
|
21
|
+
ctx.bitPos = 0;
|
|
22
|
+
}
|
|
23
|
+
result = (result << 1) | ((ctx.currentValue >> (15 - ctx.bitPos)) & 1);
|
|
24
|
+
ctx.bitPos++;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function writeBits(ctx, n, value) {
|
|
29
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
30
|
+
ctx.buffer = (ctx.buffer << 1) | ((value >> i) & 1);
|
|
31
|
+
ctx.bitsInBuffer++;
|
|
32
|
+
if (ctx.bitsInBuffer === 16) {
|
|
33
|
+
ctx.output.push(ctx.buffer + 1);
|
|
34
|
+
ctx.buffer = 0;
|
|
35
|
+
ctx.bitsInBuffer = 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const compress = (input) => {
|
|
40
|
+
if (!input) {
|
|
41
|
+
return '';
|
|
42
|
+
}
|
|
43
|
+
let ctx = { bitsInBuffer: 0, buffer: 0, numBits: 2, output: [] }, dictSize = 3, dictionary = new Map(), w = '';
|
|
44
|
+
for (let i = 0, n = input.length; i < n; i++) {
|
|
45
|
+
let c = input[i], wc = w + c;
|
|
46
|
+
if (dictionary.has(wc)) {
|
|
47
|
+
w = wc;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (w.length > 0) {
|
|
51
|
+
if (dictionary.has(w)) {
|
|
52
|
+
writeBits(ctx, ctx.numBits, dictionary.get(w));
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
emitLiteral(ctx, w);
|
|
56
|
+
}
|
|
57
|
+
dictionary.set(wc, dictSize++);
|
|
58
|
+
if (dictSize > (1 << ctx.numBits)) {
|
|
59
|
+
ctx.numBits++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
w = c;
|
|
63
|
+
}
|
|
64
|
+
if (w.length > 0) {
|
|
65
|
+
if (dictionary.has(w)) {
|
|
66
|
+
writeBits(ctx, ctx.numBits, dictionary.get(w));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
emitLiteral(ctx, w);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
dictSize++;
|
|
73
|
+
if (dictSize > (1 << ctx.numBits)) {
|
|
74
|
+
ctx.numBits++;
|
|
75
|
+
}
|
|
76
|
+
writeBits(ctx, ctx.numBits, 2);
|
|
77
|
+
if (ctx.bitsInBuffer > 0) {
|
|
78
|
+
ctx.output.push(((ctx.buffer << (16 - ctx.bitsInBuffer)) & 0xFFFF) + 1);
|
|
79
|
+
}
|
|
80
|
+
ctx.output.push((ctx.bitsInBuffer === 0 ? 16 : ctx.bitsInBuffer) + 1);
|
|
81
|
+
return String.fromCharCode(...ctx.output);
|
|
82
|
+
};
|
|
83
|
+
const decompress = (compressed) => {
|
|
84
|
+
if (!compressed) {
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
let ctx = { bitPos: 16, compressed: '', currentValue: 0, pos: 0 }, dictSize = 3, dictionary = [], numBits = 2;
|
|
88
|
+
ctx.compressed = compressed.substring(0, compressed.length - 1);
|
|
89
|
+
let code = readBits(ctx, numBits), entry;
|
|
90
|
+
if (code === 0) {
|
|
91
|
+
entry = String.fromCharCode(readBits(ctx, 8));
|
|
92
|
+
}
|
|
93
|
+
else if (code === 1) {
|
|
94
|
+
entry = String.fromCharCode(readBits(ctx, 16));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
return '';
|
|
98
|
+
}
|
|
99
|
+
let parts = [entry], totalLength = entry.length, w = entry;
|
|
100
|
+
while (true) {
|
|
101
|
+
let slotIdx = dictionary.length;
|
|
102
|
+
dictionary.push('');
|
|
103
|
+
dictSize++;
|
|
104
|
+
if (dictSize > (1 << numBits)) {
|
|
105
|
+
numBits++;
|
|
106
|
+
}
|
|
107
|
+
code = readBits(ctx, numBits);
|
|
108
|
+
if (code === 2) {
|
|
109
|
+
dictionary.pop();
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
let slotCode = slotIdx + 3;
|
|
113
|
+
if (code === 0) {
|
|
114
|
+
entry = String.fromCharCode(readBits(ctx, 8));
|
|
115
|
+
}
|
|
116
|
+
else if (code === 1) {
|
|
117
|
+
entry = String.fromCharCode(readBits(ctx, 16));
|
|
118
|
+
}
|
|
119
|
+
else if (code === slotCode) {
|
|
120
|
+
entry = w + w[0];
|
|
121
|
+
}
|
|
122
|
+
else if (code >= 3 && code < slotCode) {
|
|
123
|
+
entry = dictionary[code - 3];
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
throw new Error('LZ: invalid decompression code');
|
|
127
|
+
}
|
|
128
|
+
dictionary[slotIdx] = w + entry[0];
|
|
129
|
+
parts.push(entry);
|
|
130
|
+
totalLength += entry.length;
|
|
131
|
+
if (totalLength > MAX_DECOMPRESSED_SIZE) {
|
|
132
|
+
throw new Error('LZ: decompressed output exceeds size limit');
|
|
133
|
+
}
|
|
134
|
+
w = entry;
|
|
135
|
+
}
|
|
136
|
+
return parts.join('');
|
|
137
|
+
};
|
|
138
|
+
export { compress, decompress };
|
package/build/types.d.ts
CHANGED
|
@@ -36,4 +36,4 @@ type TTLEnvelope<V> = {
|
|
|
36
36
|
};
|
|
37
37
|
type GlobalCallback<T> = (key: keyof T, newValue: T[keyof T] | undefined, oldValue: T[keyof T] | undefined) => void;
|
|
38
38
|
type KeyCallback<T, K extends keyof T = keyof T> = (newValue: T[K] | undefined, oldValue: T[K] | undefined) => void;
|
|
39
|
-
export type { Driver, Filter, GlobalCallback, KeyCallback,
|
|
39
|
+
export type { Driver, Filter, GlobalCallback, KeyCallback, MigrationFn, Options, SetOptions, TTLEnvelope };
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"author": "ICJR",
|
|
3
3
|
"dependencies": {
|
|
4
|
-
"@esportsplus/utilities": "^0.
|
|
4
|
+
"@esportsplus/utilities": "^0.28.0"
|
|
5
5
|
},
|
|
6
6
|
"description": "Web storage utility",
|
|
7
7
|
"devDependencies": {
|
|
8
|
-
"@esportsplus/typescript": "^0.
|
|
8
|
+
"@esportsplus/typescript": "^0.29.1",
|
|
9
9
|
"fake-indexeddb": "^6.2.5",
|
|
10
|
-
"happy-dom": "^20.8.
|
|
11
|
-
"vitest": "^4.1.
|
|
10
|
+
"happy-dom": "^20.8.9",
|
|
11
|
+
"vitest": "^4.1.4"
|
|
12
12
|
},
|
|
13
13
|
"main": "build/index.js",
|
|
14
14
|
"name": "@esportsplus/web-storage",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"type": "module",
|
|
21
21
|
"types": "build/index.d.ts",
|
|
22
|
-
"version": "0.
|
|
22
|
+
"version": "0.5.1",
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "tsc && tsc-alias",
|
|
25
25
|
"test": "vitest run",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { compress, decompress } from '~/lz';
|
|
1
2
|
import type { Driver } from '~/types';
|
|
2
3
|
|
|
3
4
|
|
|
@@ -35,6 +36,10 @@ class LocalStorageDriver<T> implements Driver<T> {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
try {
|
|
39
|
+
if (value.charCodeAt(0) === 1) {
|
|
40
|
+
return JSON.parse(decompress(value.slice(1)));
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
return JSON.parse(value);
|
|
39
44
|
}
|
|
40
45
|
catch {
|
|
@@ -42,6 +47,12 @@ class LocalStorageDriver<T> implements Driver<T> {
|
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
private serialize(value: T[keyof T]): string {
|
|
51
|
+
let json = JSON.stringify(value);
|
|
52
|
+
|
|
53
|
+
return json.length >= 100 ? '\x01' + compress(json) : json;
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
|
|
46
57
|
async all(): Promise<T> {
|
|
47
58
|
let keys = this.getKeys(),
|
|
@@ -67,7 +78,15 @@ class LocalStorageDriver<T> implements Driver<T> {
|
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
async count(): Promise<number> {
|
|
70
|
-
|
|
81
|
+
let count = 0;
|
|
82
|
+
|
|
83
|
+
for (let i = 0, n = localStorage.length; i < n; i++) {
|
|
84
|
+
if (localStorage.key(i)?.startsWith(this.prefix)) {
|
|
85
|
+
count++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return count;
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
async delete(keys: (keyof T)[]): Promise<void> {
|
|
@@ -112,13 +131,13 @@ class LocalStorageDriver<T> implements Driver<T> {
|
|
|
112
131
|
|
|
113
132
|
async replace(entries: [keyof T, T[keyof T]][]): Promise<void> {
|
|
114
133
|
for (let i = 0, n = entries.length; i < n; i++) {
|
|
115
|
-
localStorage.setItem(this.key(entries[i][0]),
|
|
134
|
+
localStorage.setItem(this.key(entries[i][0]), this.serialize(entries[i][1]));
|
|
116
135
|
}
|
|
117
136
|
}
|
|
118
137
|
|
|
119
138
|
async set(key: keyof T, value: T[keyof T]): Promise<boolean> {
|
|
120
139
|
try {
|
|
121
|
-
localStorage.setItem(this.key(key),
|
|
140
|
+
localStorage.setItem(this.key(key), this.serialize(value));
|
|
122
141
|
return true;
|
|
123
142
|
}
|
|
124
143
|
catch {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { compress, decompress } from '~/lz';
|
|
1
2
|
import type { Driver } from '~/types';
|
|
2
3
|
|
|
3
4
|
|
|
@@ -35,6 +36,10 @@ class SessionStorageDriver<T> implements Driver<T> {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
try {
|
|
39
|
+
if (value.charCodeAt(0) === 1) {
|
|
40
|
+
return JSON.parse(decompress(value.slice(1)));
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
return JSON.parse(value);
|
|
39
44
|
}
|
|
40
45
|
catch {
|
|
@@ -42,6 +47,12 @@ class SessionStorageDriver<T> implements Driver<T> {
|
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
|
|
50
|
+
private serialize(value: T[keyof T]): string {
|
|
51
|
+
let json = JSON.stringify(value);
|
|
52
|
+
|
|
53
|
+
return json.length >= 100 ? '\x01' + compress(json) : json;
|
|
54
|
+
}
|
|
55
|
+
|
|
45
56
|
|
|
46
57
|
async all(): Promise<T> {
|
|
47
58
|
let keys = this.getKeys(),
|
|
@@ -67,7 +78,15 @@ class SessionStorageDriver<T> implements Driver<T> {
|
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
async count(): Promise<number> {
|
|
70
|
-
|
|
81
|
+
let count = 0;
|
|
82
|
+
|
|
83
|
+
for (let i = 0, n = sessionStorage.length; i < n; i++) {
|
|
84
|
+
if (sessionStorage.key(i)?.startsWith(this.prefix)) {
|
|
85
|
+
count++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return count;
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
async delete(keys: (keyof T)[]): Promise<void> {
|
|
@@ -112,13 +131,13 @@ class SessionStorageDriver<T> implements Driver<T> {
|
|
|
112
131
|
|
|
113
132
|
async replace(entries: [keyof T, T[keyof T]][]): Promise<void> {
|
|
114
133
|
for (let i = 0, n = entries.length; i < n; i++) {
|
|
115
|
-
sessionStorage.setItem(this.key(entries[i][0]),
|
|
134
|
+
sessionStorage.setItem(this.key(entries[i][0]), this.serialize(entries[i][1]));
|
|
116
135
|
}
|
|
117
136
|
}
|
|
118
137
|
|
|
119
138
|
async set(key: keyof T, value: T[keyof T]): Promise<boolean> {
|
|
120
139
|
try {
|
|
121
|
-
sessionStorage.setItem(this.key(key),
|
|
140
|
+
sessionStorage.setItem(this.key(key), this.serialize(value));
|
|
122
141
|
return true;
|
|
123
142
|
}
|
|
124
143
|
catch {
|