@actdim/utico 0.9.4 → 0.9.6
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/dist/cache/persistentCache.d.ts +10 -15
- package/dist/cache/persistentCache.d.ts.map +1 -1
- package/dist/cache/persistentCache.es.js +53 -81
- package/dist/cache/persistentCache.es.js.map +1 -1
- package/dist/mutex.d.ts +6 -4
- package/dist/mutex.d.ts.map +1 -1
- package/dist/mutex.es.js +36 -11
- package/dist/mutex.es.js.map +1 -1
- package/dist/store/dataStore.d.ts +27 -31
- package/dist/store/dataStore.d.ts.map +1 -1
- package/dist/store/dataStore.es.js +125 -41
- package/dist/store/dataStore.es.js.map +1 -1
- package/dist/store/persistentStore.d.ts +12 -17
- package/dist/store/persistentStore.d.ts.map +1 -1
- package/dist/store/persistentStore.es.js +19 -82
- package/dist/store/persistentStore.es.js.map +1 -1
- package/dist/store/storeContracts.d.ts +3 -2
- package/dist/store/storeContracts.d.ts.map +1 -1
- package/dist/store/storeDb.d.ts +42 -0
- package/dist/store/storeDb.d.ts.map +1 -0
- package/dist/store/storeDb.es.js +60 -0
- package/dist/store/storeDb.es.js.map +1 -0
- package/dist/typeCore.d.ts +13 -1
- package/dist/typeCore.d.ts.map +1 -1
- package/dist/typeCore.es.js.map +1 -1
- package/dist/typeUtils.d.ts +1 -0
- package/dist/typeUtils.d.ts.map +1 -1
- package/dist/typeUtils.es.js +34 -30
- package/dist/typeUtils.es.js.map +1 -1
- package/package.json +2 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IDataEntry, IDataItem } from '../store/
|
|
2
|
-
import {
|
|
1
|
+
import { IDataEntry, IDataItem, CacheDataEntry } from '../store/storeDb';
|
|
2
|
+
import { DataStore } from '../store/dataStore';
|
|
3
3
|
type Duration = number | {
|
|
4
4
|
seconds?: number;
|
|
5
5
|
minutes?: number;
|
|
@@ -20,27 +20,22 @@ export type CacheEvictionEvent = {
|
|
|
20
20
|
type PersistentCacheEventStruct = {
|
|
21
21
|
evict: CacheEvictionEvent;
|
|
22
22
|
};
|
|
23
|
-
export declare class PersistentCache extends
|
|
24
|
-
private _db;
|
|
25
|
-
private _isDisposed;
|
|
23
|
+
export declare class PersistentCache extends DataStore<"cache", PersistentCacheEventStruct> {
|
|
26
24
|
private _jobTimerId;
|
|
27
25
|
private readonly _options;
|
|
26
|
+
static deleteAsync(name: string): Promise<void>;
|
|
27
|
+
static existsAsync(name: string): Promise<boolean>;
|
|
28
|
+
static openAsync(name: string, options: PersistentCacheOptions): Promise<PersistentCache>;
|
|
28
29
|
constructor(name: string, options: PersistentCacheOptions);
|
|
29
30
|
scheduleServiceJob(): void;
|
|
30
31
|
deleteExpiredAsync(date?: Date): Promise<string[]>;
|
|
31
32
|
dispose(): void;
|
|
32
|
-
|
|
33
|
-
getKeysAsync(): Promise<string[]>;
|
|
34
|
-
getAsync(key: string): Promise<Readonly<IDataEntry & IDataItem>>;
|
|
33
|
+
getAsync(key: string): Promise<Readonly<CacheDataEntry & IDataItem>>;
|
|
35
34
|
bulkGetAsync(ids: string[]): Promise<{
|
|
36
|
-
[key: string]: Readonly<
|
|
35
|
+
[key: string]: Readonly<CacheDataEntry & IDataItem>;
|
|
37
36
|
}>;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
bulkDeleteAsync(ids: string[]): Promise<void>;
|
|
41
|
-
setAsync(id: string, value: any, options: CacheOptions): Promise<void>;
|
|
42
|
-
getOrSetAsync(key: string, factory: () => any, options: CacheOptions): Promise<void>;
|
|
43
|
-
clearAsync(): Promise<void>;
|
|
37
|
+
setAsync(key: string, value: any, options: CacheOptions): Promise<void>;
|
|
38
|
+
getOrSetAsync(key: string, factory: (key: string) => any, options: CacheOptions): Promise<Readonly<CacheDataEntry & IDataItem>>;
|
|
44
39
|
}
|
|
45
40
|
export {};
|
|
46
41
|
//# sourceMappingURL=persistentCache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistentCache.d.ts","sourceRoot":"","sources":["../../src/cache/persistentCache.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"persistentCache.d.ts","sourceRoot":"","sources":["../../src/cache/persistentCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,UAAU,EAAE,SAAS,EAAc,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGtG,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,KAAK,QAAQ,GAAG,MAAM,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG;IACjC,cAAc,EAAE,MAAM,CAAC;CAC1B,CAAC;AAMF,MAAM,MAAM,YAAY,GAAG;IACvB,kBAAkB,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACnC,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,iBAAiB,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CACrC,CAAC;AAGF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,KAAK,EAAE,UAAU,CAAC;IAElB,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACnC,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAC9B,KAAK,EAAE,kBAAkB,CAAC;CAC7B,CAAC;AAIF,qBAAa,eAAgB,SAAQ,SAAS,CAAC,OAAO,EAAE,0BAA0B,CAAC;IAE/E,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;IAElD,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM;IAI/B,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM;IAI/B,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB;gBAKlD,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB;IAQzD,kBAAkB;IAiBZ,kBAAkB,CAAC,IAAI,CAAC,EAAE,IAAI;IA2BpC,OAAO;IAUD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAgBpE,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,cAAc,GAAG,SAAS,CAAC,CAAA;KAAE,CAAC;IA8B7F,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY;IA6BvD,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,EAAE,OAAO,EAAE,YAAY;CAUxF"}
|
|
@@ -1,28 +1,25 @@
|
|
|
1
|
-
import { keyOf as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
const
|
|
1
|
+
import { keyOf as d } from "../typeUtils.es.js";
|
|
2
|
+
import { StructEvent as u } from "../structEvent.es.js";
|
|
3
|
+
import { v4 as l } from "uuid";
|
|
4
|
+
import { DataStore as c } from "../store/dataStore.es.js";
|
|
5
|
+
const y = {
|
|
6
6
|
cleanupTimeout: 1e3
|
|
7
7
|
};
|
|
8
|
-
class
|
|
9
|
-
// https://demo.agektmr.com/storage/
|
|
10
|
-
// https://www.html5rocks.com/en/tutorials/offline/quota-research/
|
|
11
|
-
// https://www.raymondcamden.com/2015/04/17/indexeddb-and-limits
|
|
12
|
-
// https://github.com/jonnysmith1981/getIndexedDbSize/blob/master/getIndexedDbSize.js
|
|
13
|
-
// https://developer.chrome.com/apps/offline_storage#query
|
|
14
|
-
// https://golb.hplar.ch/2018/01/IndexedDB-programming-with-Dexie-js.html
|
|
15
|
-
// http://www.forerunnerdb.com/licensing.html
|
|
16
|
-
// https://github.com/ignasbernotas/dexie-relationships
|
|
17
|
-
_db;
|
|
18
|
-
_isDisposed;
|
|
8
|
+
class o extends c {
|
|
19
9
|
_jobTimerId;
|
|
20
10
|
_options;
|
|
11
|
+
static deleteAsync(t) {
|
|
12
|
+
return c.$deleteAsync(t);
|
|
13
|
+
}
|
|
14
|
+
static existsAsync(t) {
|
|
15
|
+
return c.$existsAsync(t);
|
|
16
|
+
}
|
|
17
|
+
static openAsync(t, e) {
|
|
18
|
+
return c.$openAsync(t, () => new o(t, e));
|
|
19
|
+
}
|
|
21
20
|
// cleanupTimeout - serviceJobTimeout
|
|
22
21
|
constructor(t, e) {
|
|
23
|
-
|
|
24
|
-
throw new Error("Name cannot be empty");
|
|
25
|
-
this._isDisposed = !1, this._jobTimerId = null, this._db = new o(t), this._options = { ...e, ...u }, this.scheduleServiceJob();
|
|
22
|
+
super(t, "cache"), this._jobTimerId = null, this._options = { ...e, ...y }, this.scheduleServiceJob();
|
|
26
23
|
}
|
|
27
24
|
scheduleServiceJob() {
|
|
28
25
|
if (this._options.cleanupTimeout) {
|
|
@@ -41,84 +38,65 @@ class f extends y {
|
|
|
41
38
|
// evictExpiredAsync/clearExpiredAsync
|
|
42
39
|
async deleteExpiredAsync(t) {
|
|
43
40
|
const e = [];
|
|
44
|
-
return t || (t = /* @__PURE__ */ new Date()), await this
|
|
45
|
-
const
|
|
46
|
-
for (const
|
|
47
|
-
const
|
|
41
|
+
return t || (t = /* @__PURE__ */ new Date()), await this.$execAsync(async () => {
|
|
42
|
+
const i = await this._db.registry.where(d("expiresAt")).below(t.getTime()).toArray();
|
|
43
|
+
for (const n of i) {
|
|
44
|
+
const r = new u("evict", {
|
|
48
45
|
detail: {
|
|
49
|
-
entry:
|
|
46
|
+
entry: n
|
|
50
47
|
// keepAliveOptions: {}
|
|
51
48
|
},
|
|
52
49
|
target: this,
|
|
53
50
|
cancelable: !0
|
|
54
51
|
});
|
|
55
|
-
this.dispatchEvent(
|
|
52
|
+
this.dispatchEvent(r), this.deleteAsync(n.id), e.push(n.id);
|
|
56
53
|
}
|
|
57
54
|
}), e;
|
|
58
55
|
}
|
|
59
56
|
dispose() {
|
|
60
|
-
this._isDisposed ||
|
|
61
|
-
}
|
|
62
|
-
async execAsync(t) {
|
|
63
|
-
this._db.isOpen() || await this._db.open();
|
|
64
|
-
try {
|
|
65
|
-
return await t();
|
|
66
|
-
} catch (e) {
|
|
67
|
-
throw this._db.isOpen(), e;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
async getKeysAsync() {
|
|
71
|
-
return await this._db.registry.filter((t) => !0).primaryKeys();
|
|
57
|
+
this._isDisposed || this._jobTimerId && (window.clearTimeout(this._jobTimerId), this._jobTimerId = null), super.dispose();
|
|
72
58
|
}
|
|
73
59
|
async getAsync(t) {
|
|
74
|
-
return await this
|
|
60
|
+
return await this.$execAsync(async () => {
|
|
75
61
|
const e = await this._db.registry.get(t);
|
|
76
62
|
if (e) {
|
|
77
|
-
const
|
|
78
|
-
return { ...e, ...
|
|
63
|
+
const i = await this._db.data.get(t);
|
|
64
|
+
return { ...e, ...i };
|
|
79
65
|
}
|
|
80
66
|
return null;
|
|
81
|
-
});
|
|
67
|
+
}, "rw");
|
|
82
68
|
}
|
|
83
69
|
// getMany
|
|
84
70
|
async bulkGetAsync(t) {
|
|
85
71
|
const e = {};
|
|
86
|
-
return await this
|
|
87
|
-
const
|
|
88
|
-
for (const
|
|
89
|
-
e[
|
|
90
|
-
for (const
|
|
91
|
-
|
|
72
|
+
return await this.$execAsync(async () => {
|
|
73
|
+
const n = (await this._db.registry.bulkGet(t)).reduce((s, a, h) => (s[a.id] = a, s), {}), r = await this._db.data.bulkGet(t);
|
|
74
|
+
for (const s of r)
|
|
75
|
+
e[s.id] = { ...n[s.id], ...s }, delete n[s.id];
|
|
76
|
+
for (const s of Object.keys(n)) {
|
|
77
|
+
let a = {
|
|
78
|
+
id: s,
|
|
79
|
+
value: void 0
|
|
80
|
+
};
|
|
81
|
+
e[s] = { ...n[s], ...a };
|
|
82
|
+
}
|
|
92
83
|
return e;
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
async containsAsync(t) {
|
|
96
|
-
return await this.execAsync(async () => await this._db.registry.get(t) != null);
|
|
97
|
-
}
|
|
98
|
-
async deleteAsync(t) {
|
|
99
|
-
await this.execAsync(async () => {
|
|
100
|
-
await this._db.registry.delete(t);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
// deleteManyAsync
|
|
104
|
-
async bulkDeleteAsync(t) {
|
|
105
|
-
await this.execAsync(async () => {
|
|
106
|
-
await this._db.registry.bulkDelete(t);
|
|
107
|
-
});
|
|
84
|
+
}, "rw");
|
|
108
85
|
}
|
|
109
86
|
// upsertAsync
|
|
110
|
-
async setAsync(t, e,
|
|
111
|
-
return await this
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
87
|
+
async setAsync(t, e, i) {
|
|
88
|
+
return await this.$execAsync(async () => {
|
|
89
|
+
let n;
|
|
90
|
+
const r = (/* @__PURE__ */ new Date()).getTime();
|
|
91
|
+
t ? n = await this._db.registry.get(t) : t = l();
|
|
92
|
+
const s = typeof i.absoluteExpiration == "number" ? i.absoluteExpiration : i.absoluteExpiration?.getTime(), a = typeof i.slidingExpiration == "number" ? i.slidingExpiration : i.slidingExpiration?.getTime();
|
|
115
93
|
await this._db.registry.put({
|
|
116
94
|
id: t,
|
|
117
|
-
createdAt:
|
|
118
|
-
accessedAt:
|
|
119
|
-
updatedAt:
|
|
120
|
-
expiresAt:
|
|
121
|
-
slidingExpiration:
|
|
95
|
+
createdAt: n ? n.createdAt : r,
|
|
96
|
+
accessedAt: r,
|
|
97
|
+
updatedAt: r,
|
|
98
|
+
expiresAt: s,
|
|
99
|
+
slidingExpiration: a
|
|
122
100
|
}), await this._db.data.put({
|
|
123
101
|
id: t,
|
|
124
102
|
value: e
|
|
@@ -126,18 +104,12 @@ class f extends y {
|
|
|
126
104
|
});
|
|
127
105
|
}
|
|
128
106
|
// getOrAddAsync
|
|
129
|
-
async getOrSetAsync(t, e,
|
|
130
|
-
await this
|
|
131
|
-
}
|
|
132
|
-
// clearAllAsync/evictAllAsync
|
|
133
|
-
async clearAsync() {
|
|
134
|
-
await this.execAsync(async () => {
|
|
135
|
-
await this._db.registry.clear(), await this._db.data.clear();
|
|
136
|
-
});
|
|
107
|
+
async getOrSetAsync(t, e, i) {
|
|
108
|
+
return await this.$execAsync(async () => (await this.containsAsync(t) || await this.setAsync(t, e(t), i), await this.getAsync(t)));
|
|
137
109
|
}
|
|
138
110
|
// TODO: support bulkSetAsync
|
|
139
111
|
}
|
|
140
112
|
export {
|
|
141
|
-
|
|
113
|
+
o as PersistentCache
|
|
142
114
|
};
|
|
143
115
|
//# sourceMappingURL=persistentCache.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistentCache.es.js","sources":["../../src/cache/persistentCache.ts"],"sourcesContent":["import { keyOf } from \"@/typeUtils\";\r\nimport { DataStore, IDataEntry, IDataItem } from \"../store/dataStore\";\r\nimport { StructEvent, StructEventTarget } from \"@/structEvent\";\r\nimport { v4 as uuid } from \"uuid\";\r\n\r\ntype Duration = number | { seconds?: number; minutes?: number; hours?: number };\r\n\r\nexport type PersistentCacheOptions = {\r\n cleanupTimeout: number;\r\n};\r\n\r\nconst defaultPersistentCacheOptions = {\r\n cleanupTimeout: 1000\r\n} satisfies PersistentCacheOptions;\r\n\r\nexport type CacheOptions = {\r\n absoluteExpiration?: Date | number;\r\n ttl?: Duration;\r\n slidingExpiration?: Date | number; // \"autoRenewOnUse\" pattern\r\n};\r\n\r\n// CacheEntryEvictionEvent\r\nexport type CacheEvictionEvent = {\r\n entry: IDataEntry;\r\n // or optionsOverride?\r\n keepAliveOptions?: CacheOptions; // TODO: delayed eviction (pending eviction) after gracePeriod\r\n};\r\n\r\ntype PersistentCacheEventStruct = {\r\n evict: CacheEvictionEvent;\r\n};\r\n\r\n// TODO: remove class, create factory method\r\n// implements Struct<StructEventTarget<PersistentCacheEventStruct>>\r\n// PersistentCacheManager\r\nexport class PersistentCache extends StructEventTarget<PersistentCacheEventStruct> {\r\n // https://demo.agektmr.com/storage/\r\n // https://www.html5rocks.com/en/tutorials/offline/quota-research/\r\n // https://www.raymondcamden.com/2015/04/17/indexeddb-and-limits\r\n // https://github.com/jonnysmith1981/getIndexedDbSize/blob/master/getIndexedDbSize.js\r\n // https://developer.chrome.com/apps/offline_storage#query\r\n // https://golb.hplar.ch/2018/01/IndexedDB-programming-with-Dexie-js.html\r\n // http://www.forerunnerdb.com/licensing.html\r\n // https://github.com/ignasbernotas/dexie-relationships\r\n\r\n private _db: DataStore;\r\n\r\n private _isDisposed: boolean;\r\n\r\n private _jobTimerId: number;\r\n\r\n private readonly _options: PersistentCacheOptions;\r\n\r\n // cleanupTimeout - serviceJobTimeout\r\n constructor(name: string, options: PersistentCacheOptions) {\r\n super();\r\n if (!name) {\r\n throw new Error(\"Name cannot be empty\");\r\n }\r\n this._isDisposed = false;\r\n this._jobTimerId = null;\r\n this._db = new DataStore(name);\r\n // https://docs.nestjs.com/techniques/task-scheduling\r\n this._options = { ...options, ...defaultPersistentCacheOptions };\r\n this.scheduleServiceJob();\r\n }\r\n\r\n scheduleServiceJob() {\r\n if (this._options.cleanupTimeout) {\r\n const doWork = async () => {\r\n try {\r\n // purge expired entries\r\n await this.deleteExpiredAsync();\r\n } catch (err) {\r\n console.error(\"Cache cleanup failed:\", err);\r\n } finally {\r\n setTimeout(doWork, this._options.cleanupTimeout);\r\n }\r\n };\r\n setTimeout(doWork, this._options.cleanupTimeout);\r\n }\r\n }\r\n\r\n // evictExpiredAsync/clearExpiredAsync\r\n async deleteExpiredAsync(date?: Date) {\r\n const result: string[] = []; // output ids\r\n if (!date) {\r\n date = new Date();\r\n }\r\n\r\n await this.execAsync(async () => {\r\n const entries = await this._db.registry.where(keyOf<IDataEntry>(\"expiresAt\")).below(date.getTime()).toArray();\r\n for (const entry of entries) {\r\n const evt = new StructEvent<PersistentCacheEventStruct, this>(\"evict\", {\r\n detail: {\r\n entry: entry\r\n // keepAliveOptions: {}\r\n },\r\n target: this,\r\n cancelable: true\r\n });\r\n this.dispatchEvent(evt);\r\n // evt.defaultPrevented?\r\n // TODO: support evt.detail.keepAliveOptions\r\n await this._db.registry.delete(entry.id);\r\n // await this._db.data.delete(entry.id);\r\n result.push(entry.id);\r\n // TODO: use bulkDelete\r\n // TODO: use transaction\r\n }\r\n });\r\n return result;\r\n }\r\n\r\n dispose() {\r\n if (!this._isDisposed) {\r\n this._isDisposed = true;\r\n\r\n if (this._jobTimerId) {\r\n window.clearTimeout(this._jobTimerId);\r\n this._jobTimerId = null;\r\n }\r\n\r\n if (this._db) {\r\n // this.exec(async () => {\r\n // \t// ...\r\n // }).then(() => {\r\n // \tthis._db = null;\r\n // });\r\n if (this._db.isOpen()) {\r\n this._db.close();\r\n }\r\n this._db = null;\r\n }\r\n }\r\n }\r\n\r\n private async execAsync<T>(action: () => Promise<T>) {\r\n if (!this._db.isOpen()) {\r\n await this._db.open();\r\n }\r\n try {\r\n const result = await action();\r\n return result;\r\n } catch (err) {\r\n if (this._db.isOpen()) {\r\n // this._db.close(); // generally speaking: we don't (never) need to close a connection\r\n }\r\n throw err;\r\n }\r\n }\r\n\r\n async getKeysAsync() {\r\n return await this._db.registry.filter((_) => true).primaryKeys();\r\n }\r\n\r\n async getAsync(key: string): Promise<Readonly<IDataEntry & IDataItem>> {\r\n return await this.execAsync(async () => {\r\n const entry = await this._db.registry.get(key);\r\n // const entry = await this._db.registry.where(keyOf<ICacheEntry>(\"id\")).equals(key).first();\r\n if (entry) {\r\n const data = await this._db.data.get(key);\r\n // const data = await this._db.data.where(keyOf<ICacheEntry>(\"id\")).equals(key).first();\r\n return { ...entry, ...data };\r\n }\r\n return null;\r\n });\r\n }\r\n\r\n // getMany\r\n async bulkGetAsync(ids: string[]): Promise<{ [key: string]: Readonly<IDataEntry & IDataItem> }> {\r\n const result: { [key: string]: Readonly<IDataEntry & IDataItem> } = {};\r\n return await this.execAsync(async () => {\r\n // const entries = await this._db.registry.where(keyOf<ICacheEntry>(\"id\")).anyOf(ids).toArray();\r\n const entries = await this._db.registry.bulkGet(ids);\r\n const entryMap: { [key: string]: IDataEntry } = entries.reduce((map, entry, i) => {\r\n map[entry.id] = entry;\r\n return map;\r\n }, {});\r\n\r\n // const dataItems = this._db.data.where(keyOf<ICacheEntry>(\"id\")).anyOf(ids);\r\n // await dataItems.each((dataItem) => {\r\n // result[dataItem.id] = { ...entryMap[dataItem.id], ...dataItem };\r\n // delete entryMap[dataItem.id];\r\n // });\r\n\r\n const dataItems = await this._db.data.bulkGet(ids);\r\n for (const dataItem of dataItems) {\r\n result[dataItem.id] = { ...entryMap[dataItem.id], ...dataItem };\r\n delete entryMap[dataItem.id];\r\n }\r\n\r\n // TODO: update cache entry \r\n // accessedAt,\r\n // updatedAt,\r\n // expiresAt // for sliding expiration\r\n\r\n for (const key of Object.keys(entryMap)) {\r\n // abandoned/orphaned entries:\r\n result[key] = { ...entryMap[key], value: undefined };\r\n\r\n // Object.defineProperty(result[key], keyOf<ICacheDataItem>(\"value\"), {\r\n // writable: false,\r\n // get: function () {\r\n // throw new Error(\"Not found\");\r\n // }\r\n // });\r\n }\r\n\r\n return result;\r\n });\r\n }\r\n\r\n async containsAsync(key: string) {\r\n return await this.execAsync(async () => {\r\n const entry = await this._db.registry.get(key);\r\n // const entry = await this._db.registry.where(keyOf<ICacheEntry>(\"id\")).equals(key).first();\r\n return entry != undefined;\r\n });\r\n }\r\n\r\n async deleteAsync(id: string) {\r\n await this.execAsync(async () => {\r\n await this._db.registry.delete(id);\r\n });\r\n }\r\n\r\n // deleteManyAsync\r\n async bulkDeleteAsync(ids: string[]) {\r\n await this.execAsync(async () => {\r\n await this._db.registry.bulkDelete(ids);\r\n });\r\n }\r\n\r\n // upsertAsync\r\n async setAsync(id: string, value: any, options: CacheOptions) {\r\n return await this.execAsync(async () => {\r\n const entry = await this._db.registry.get(id);\r\n // const entry = await this._db.registry.where(keyOf<ICacheEntry>(\"id\")).equals(key).first();\r\n const now = new Date().getTime();\r\n if (!id) {\r\n id = uuid();\r\n }\r\n const expiresAt =\r\n typeof options.absoluteExpiration === \"number\" ? options.absoluteExpiration : options.absoluteExpiration?.getTime();\r\n const slidingExpiration =\r\n typeof options.slidingExpiration === \"number\" ? options.slidingExpiration : options.slidingExpiration?.getTime();\r\n\r\n await this._db.registry.put({\r\n id: id,\r\n createdAt: entry ? entry.createdAt : now,\r\n accessedAt: now,\r\n updatedAt: now,\r\n expiresAt: expiresAt,\r\n slidingExpiration: slidingExpiration\r\n });\r\n await this._db.data.put({\r\n id: id,\r\n value: value\r\n });\r\n });\r\n }\r\n\r\n // getOrAddAsync\r\n async getOrSetAsync(key: string, factory: () => any, options: CacheOptions) {\r\n await this.execAsync(async () => {\r\n if (!(await this.containsAsync(key))) {\r\n await this.setAsync(key, factory(), options);\r\n }\r\n return await this.getAsync(key);\r\n });\r\n }\r\n\r\n // clearAllAsync/evictAllAsync\r\n async clearAsync() {\r\n await this.execAsync(async () => {\r\n await this._db.registry.clear();\r\n await this._db.data.clear();\r\n });\r\n }\r\n\r\n // TODO: support bulkSetAsync\r\n}\r\n\r\n// https://medium.com/square-corner-blog/useful-tools-headless-chrome-puppeteer-for-browser-automation-testing-1ac7707bad40\r\n// https://developers.google.com/web/updates/2017/06/headless-karma-mocha-chai?hl=ru\r\n// https://github.com/puppeteer/puppeteer\r\n// https://medium.com/web-standards/puppeteer-crawl-to-markdown-7752dff36b68\r\n"],"names":["defaultPersistentCacheOptions","PersistentCache","StructEventTarget","name","options","DataStore","doWork","err","date","result","entries","keyOf","entry","evt","StructEvent","action","_","key","data","ids","entryMap","map","i","dataItems","dataItem","id","value","now","uuid","expiresAt","slidingExpiration","factory"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"persistentCache.es.js","sources":["../../src/cache/persistentCache.ts"],"sourcesContent":["import { keyOf } from \"@/typeUtils\";\r\nimport { ICacheDataEntry, IDataEntry, IDataItem, EntryTypes, CacheDataEntry } from \"../store/storeDb\";\r\nimport { StructEvent } from \"@/structEvent\";\r\nimport { v4 as uuid } from \"uuid\";\r\nimport { DataStore } from \"@/store/dataStore\";\r\n\r\ntype Duration = number | { seconds?: number; minutes?: number; hours?: number };\r\n\r\nexport type PersistentCacheOptions = {\r\n cleanupTimeout: number;\r\n};\r\n\r\nconst defaultPersistentCacheOptions = {\r\n cleanupTimeout: 1000\r\n} satisfies PersistentCacheOptions;\r\n\r\nexport type CacheOptions = {\r\n absoluteExpiration?: Date | number;\r\n ttl?: Duration;\r\n slidingExpiration?: Date | number; // \"autoRenewOnUse\" pattern\r\n};\r\n\r\n// CacheEntryEvictionEvent\r\nexport type CacheEvictionEvent = {\r\n entry: IDataEntry;\r\n // or optionsOverride?\r\n keepAliveOptions?: CacheOptions; // TODO: delayed eviction (pending eviction) after gracePeriod\r\n};\r\n\r\ntype PersistentCacheEventStruct = {\r\n evict: CacheEvictionEvent;\r\n};\r\n\r\n// implements Struct<StructEventTarget<PersistentCacheEventStruct>>\r\n// PersistentCacheManager\r\nexport class PersistentCache extends DataStore<\"cache\", PersistentCacheEventStruct> {\r\n\r\n private _jobTimerId: number;\r\n\r\n private readonly _options: PersistentCacheOptions;\r\n\r\n static deleteAsync(name: string) {\r\n return DataStore.$deleteAsync(name);\r\n }\r\n\r\n static existsAsync(name: string) {\r\n return DataStore.$existsAsync(name);\r\n }\r\n\r\n static openAsync(name: string, options: PersistentCacheOptions) {\r\n return DataStore.$openAsync(name, () => new PersistentCache(name, options));\r\n }\r\n\r\n // cleanupTimeout - serviceJobTimeout\r\n constructor(name: string, options: PersistentCacheOptions) {\r\n super(name, \"cache\");\r\n this._jobTimerId = null;\r\n this._options = { ...options, ...defaultPersistentCacheOptions };\r\n // https://docs.nestjs.com/techniques/task-scheduling\r\n this.scheduleServiceJob();\r\n }\r\n\r\n scheduleServiceJob() {\r\n if (this._options.cleanupTimeout) {\r\n const doWork = async () => {\r\n try {\r\n // purge expired entries\r\n await this.deleteExpiredAsync();\r\n } catch (err) {\r\n console.error(\"Cache cleanup failed:\", err);\r\n } finally {\r\n setTimeout(doWork, this._options.cleanupTimeout);\r\n }\r\n };\r\n setTimeout(doWork, this._options.cleanupTimeout);\r\n }\r\n }\r\n\r\n // evictExpiredAsync/clearExpiredAsync\r\n async deleteExpiredAsync(date?: Date) {\r\n const result: string[] = []; // output ids\r\n if (!date) {\r\n date = new Date();\r\n }\r\n await this.$execAsync(async () => {\r\n const entries = await this._db.registry.where(keyOf<ICacheDataEntry>(\"expiresAt\")).below(date.getTime()).toArray();\r\n for (const entry of entries) {\r\n const evt = new StructEvent<PersistentCacheEventStruct, this>(\"evict\", {\r\n detail: {\r\n entry: entry\r\n // keepAliveOptions: {}\r\n },\r\n target: this,\r\n cancelable: true\r\n });\r\n this.dispatchEvent(evt);\r\n // evt.defaultPrevented?\r\n // TODO: support evt.detail.keepAliveOptions\r\n this.deleteAsync(entry.id);\r\n result.push(entry.id);\r\n // TODO: use bulkDelete\r\n }\r\n });\r\n return result;\r\n }\r\n\r\n dispose() {\r\n if (!this._isDisposed) {\r\n if (this._jobTimerId) {\r\n window.clearTimeout(this._jobTimerId);\r\n this._jobTimerId = null;\r\n }\r\n }\r\n super.dispose();\r\n }\r\n\r\n async getAsync(key: string): Promise<Readonly<CacheDataEntry & IDataItem>> {\r\n return await this.$execAsync(async () => {\r\n const entry = await this._db.registry.get(key);\r\n if (entry) {\r\n const data = await this._db.data.get(key);\r\n // TODO: update data entry\r\n // accessedAt,\r\n // updatedAt,\r\n // expiresAt // for sliding expiration\r\n return { ...entry, ...data };\r\n }\r\n return null;\r\n }, \"rw\");\r\n }\r\n\r\n // getMany\r\n async bulkGetAsync(ids: string[]): Promise<{ [key: string]: Readonly<CacheDataEntry & IDataItem> }> {\r\n const result: { [key: string]: Readonly<EntryTypes[\"cache\"] & IDataItem> } = {};\r\n return await this.$execAsync(async () => {\r\n const entries = await this._db.registry.bulkGet(ids);\r\n const entryMap: { [key: string]: CacheDataEntry } = entries.reduce((map, entry, i) => {\r\n map[entry.id] = entry;\r\n return map;\r\n }, {});\r\n const dataItems = await this._db.data.bulkGet(ids);\r\n // TODO: update data entries\r\n // accessedAt,\r\n // updatedAt,\r\n // expiresAt // for sliding expiration\r\n for (const dataItem of dataItems) {\r\n result[dataItem.id] = { ...entryMap[dataItem.id], ...dataItem };\r\n delete entryMap[dataItem.id];\r\n }\r\n for (const key of Object.keys(entryMap)) {\r\n // abandoned/orphaned entries:\r\n let dataItem: IDataItem = {\r\n id: key,\r\n value: undefined\r\n }\r\n result[key] = { ...entryMap[key], ...dataItem };\r\n }\r\n return result;\r\n }, \"rw\");\r\n }\r\n\r\n // upsertAsync\r\n async setAsync(key: string, value: any, options: CacheOptions) {\r\n return await this.$execAsync(async () => {\r\n let entry;\r\n const now = new Date().getTime();\r\n if (key) {\r\n entry = await this._db.registry.get(key);\r\n } else {\r\n key = uuid();\r\n }\r\n const expiresAt =\r\n typeof options.absoluteExpiration === \"number\" ? options.absoluteExpiration : options.absoluteExpiration?.getTime();\r\n const slidingExpiration =\r\n typeof options.slidingExpiration === \"number\" ? options.slidingExpiration : options.slidingExpiration?.getTime();\r\n await this._db.registry.put({\r\n id: key,\r\n createdAt: entry ? entry.createdAt : now,\r\n accessedAt: now,\r\n updatedAt: now,\r\n expiresAt: expiresAt,\r\n slidingExpiration: slidingExpiration\r\n });\r\n await this._db.data.put({\r\n id: key,\r\n value: value\r\n });\r\n });\r\n }\r\n\r\n // getOrAddAsync\r\n async getOrSetAsync(key: string, factory: (key: string) => any, options: CacheOptions) {\r\n return await this.$execAsync(async () => {\r\n if (!(await this.containsAsync(key))) {\r\n await this.setAsync(key, factory(key), options);\r\n }\r\n return await this.getAsync(key);\r\n });\r\n }\r\n\r\n // TODO: support bulkSetAsync\r\n}\r\n\r\n// extra links:\r\n// https://demo.agektmr.com/storage/\r\n// https://www.html5rocks.com/en/tutorials/offline/quota-research/\r\n// https://www.raymondcamden.com/2015/04/17/indexeddb-and-limits\r\n// https://github.com/jonnysmith1981/getIndexedDbSize/blob/master/getIndexedDbSize.js\r\n// https://developer.chrome.com/apps/offline_storage#query\r\n// https://golb.hplar.ch/2018/01/IndexedDB-programming-with-Dexie-js.html\r\n// http://www.forerunnerdb.com/licensing.html\r\n// https://github.com/ignasbernotas/dexie-relationships\r\n// https://medium.com/square-corner-blog/useful-tools-headless-chrome-puppeteer-for-browser-automation-testing-1ac7707bad40\r\n// https://developers.google.com/web/updates/2017/06/headless-karma-mocha-chai?hl=ru\r\n// https://github.com/puppeteer/puppeteer\r\n// https://medium.com/web-standards/puppeteer-crawl-to-markdown-7752dff36b68\r\n"],"names":["defaultPersistentCacheOptions","PersistentCache","DataStore","name","options","doWork","err","date","result","entries","keyOf","entry","evt","StructEvent","key","data","ids","entryMap","map","i","dataItems","dataItem","value","now","uuid","expiresAt","slidingExpiration","factory"],"mappings":"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"}
|
package/dist/mutex.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Executor } from './typeCore';
|
|
2
|
-
declare class
|
|
2
|
+
declare class AsyncMutex {
|
|
3
3
|
private mutex;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
private locked;
|
|
5
|
+
lock(timeoutMs?: number): Promise<() => void>;
|
|
6
|
+
tryLock(): () => void | null;
|
|
7
|
+
dispatch<T>(fn: Executor<T>, timeoutMs?: number): Promise<T>;
|
|
6
8
|
}
|
|
7
|
-
export {
|
|
9
|
+
export { AsyncMutex };
|
|
8
10
|
//# sourceMappingURL=mutex.d.ts.map
|
package/dist/mutex.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../src/mutex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../src/mutex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAQtC,cAAM,UAAU;IACZ,OAAO,CAAC,KAAK,CAAqB;IAElC,OAAO,CAAC,MAAM,CAAS;IAEjB,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC;IAgCnD,OAAO,IAAI,MAAM,IAAI,GAAG,IAAI;IAiBtB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAQrE;AAGD,OAAO,EACH,UAAU,EACb,CAAC"}
|
package/dist/mutex.es.js
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
|
-
class
|
|
1
|
+
class n {
|
|
2
|
+
// or Mutex
|
|
2
3
|
mutex = Promise.resolve();
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
locked = !1;
|
|
5
|
+
async lock(e) {
|
|
6
|
+
let t = () => {
|
|
7
|
+
}, o;
|
|
8
|
+
const i = this.mutex;
|
|
9
|
+
this.mutex = this.mutex.then(() => new Promise(t)), this.locked = !0;
|
|
10
|
+
const r = new Promise((s, l) => {
|
|
11
|
+
t = s, e && (o = setTimeout(() => {
|
|
12
|
+
l(new Error("Mutex lock timeout"));
|
|
13
|
+
}, e));
|
|
8
14
|
});
|
|
15
|
+
try {
|
|
16
|
+
const s = await r;
|
|
17
|
+
return clearTimeout(o), () => {
|
|
18
|
+
this.locked = !1, s();
|
|
19
|
+
};
|
|
20
|
+
} catch (s) {
|
|
21
|
+
throw this.mutex = i, this.locked = !1, s;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
tryLock() {
|
|
25
|
+
if (this.locked) return null;
|
|
26
|
+
let e;
|
|
27
|
+
return this.locked = !0, this.mutex = this.mutex.then(
|
|
28
|
+
() => new Promise((t) => {
|
|
29
|
+
e = () => {
|
|
30
|
+
this.locked = !1, t();
|
|
31
|
+
};
|
|
32
|
+
})
|
|
33
|
+
), e;
|
|
9
34
|
}
|
|
10
|
-
async dispatch(e) {
|
|
11
|
-
const
|
|
35
|
+
async dispatch(e, t) {
|
|
36
|
+
const o = await this.lock(t);
|
|
12
37
|
try {
|
|
13
|
-
return await
|
|
38
|
+
return await e();
|
|
14
39
|
} finally {
|
|
15
|
-
|
|
40
|
+
o();
|
|
16
41
|
}
|
|
17
42
|
}
|
|
18
43
|
}
|
|
19
44
|
export {
|
|
20
|
-
|
|
45
|
+
n as AsyncMutex
|
|
21
46
|
};
|
|
22
47
|
//# sourceMappingURL=mutex.es.js.map
|
package/dist/mutex.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutex.es.js","sources":["../src/mutex.ts"],"sourcesContent":["import { Executor } from
|
|
1
|
+
{"version":3,"file":"mutex.es.js","sources":["../src/mutex.ts"],"sourcesContent":["import { Executor } from './typeCore';\r\n/**\r\n * Example:\r\n * const mutex = new Mutex();\r\n * await mutex.dispatch(async () => {...});\r\n */\r\n// type Executor<T> = () => Promise<T> | T;\r\n\r\nclass AsyncMutex { // or Mutex\r\n private mutex = Promise.resolve();\r\n\r\n private locked = false;\r\n \r\n async lock(timeoutMs?: number): Promise<() => void> {\r\n let begin: (unlock: () => void) => void = () => { };\r\n let timer: NodeJS.Timeout | undefined;\r\n\r\n const previous = this.mutex;\r\n this.mutex = this.mutex.then(() => new Promise(begin));\r\n this.locked = true;\r\n \r\n const lockPromise = new Promise<() => void>((resolve, reject) => {\r\n begin = resolve;\r\n\r\n if (timeoutMs) {\r\n timer = setTimeout(() => {\r\n reject(new Error(\"Mutex lock timeout\"));\r\n }, timeoutMs);\r\n }\r\n });\r\n\r\n try {\r\n const unlock = await lockPromise;\r\n clearTimeout(timer);\r\n return () => {\r\n this.locked = false;\r\n unlock();\r\n };\r\n } catch (err) {\r\n this.mutex = previous;\r\n this.locked = false;\r\n throw err;\r\n }\r\n }\r\n\r\n tryLock(): () => void | null {\r\n if (this.locked) return null;\r\n\r\n let unlock!: () => void;\r\n this.locked = true;\r\n this.mutex = this.mutex.then(\r\n () =>\r\n new Promise<void>(res => {\r\n unlock = () => {\r\n this.locked = false;\r\n res();\r\n };\r\n })\r\n );\r\n return unlock;\r\n }\r\n\r\n async dispatch<T>(fn: Executor<T>, timeoutMs?: number): Promise<T> {\r\n const unlock = await this.lock(timeoutMs);\r\n try {\r\n return await fn();\r\n } finally {\r\n unlock();\r\n }\r\n }\r\n}\r\n\r\n\r\nexport {\r\n AsyncMutex\r\n};"],"names":["AsyncMutex","timeoutMs","begin","timer","previous","lockPromise","resolve","reject","unlock","err","res","fn"],"mappings":"AAQA,MAAMA,EAAW;AAAA;AAAA,EACL,QAAQ,QAAQ,QAAA;AAAA,EAEhB,SAAS;AAAA,EAEjB,MAAM,KAAKC,GAAyC;AAChD,QAAIC,IAAsC,MAAM;AAAA,IAAE,GAC9CC;AAEJ,UAAMC,IAAW,KAAK;AACtB,SAAK,QAAQ,KAAK,MAAM,KAAK,MAAM,IAAI,QAAQF,CAAK,CAAC,GACrD,KAAK,SAAS;AAEd,UAAMG,IAAc,IAAI,QAAoB,CAACC,GAASC,MAAW;AAC7D,MAAAL,IAAQI,GAEJL,MACAE,IAAQ,WAAW,MAAM;AACrB,QAAAI,EAAO,IAAI,MAAM,oBAAoB,CAAC;AAAA,MAC1C,GAAGN,CAAS;AAAA,IAEpB,CAAC;AAED,QAAI;AACA,YAAMO,IAAS,MAAMH;AACrB,0BAAaF,CAAK,GACX,MAAM;AACT,aAAK,SAAS,IACdK,EAAA;AAAA,MACJ;AAAA,IACJ,SAASC,GAAK;AACV,iBAAK,QAAQL,GACb,KAAK,SAAS,IACRK;AAAA,IACV;AAAA,EACJ;AAAA,EAEA,UAA6B;AACzB,QAAI,KAAK,OAAQ,QAAO;AAExB,QAAID;AACJ,gBAAK,SAAS,IACd,KAAK,QAAQ,KAAK,MAAM;AAAA,MACpB,MACI,IAAI,QAAc,CAAAE,MAAO;AACrB,QAAAF,IAAS,MAAM;AACX,eAAK,SAAS,IACdE,EAAA;AAAA,QACJ;AAAA,MACJ,CAAC;AAAA,IAAA,GAEFF;AAAA,EACX;AAAA,EAEA,MAAM,SAAYG,GAAiBV,GAAgC;AAC/D,UAAMO,IAAS,MAAM,KAAK,KAAKP,CAAS;AACxC,QAAI;AACA,aAAO,MAAMU,EAAA;AAAA,IACjB,UAAA;AACI,MAAAH,EAAA;AAAA,IACJ;AAAA,EACJ;AACJ;"}
|
|
@@ -1,33 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
registry: Dexie.Table<IDataEntry, string>;
|
|
29
|
-
data: Dexie.Table<IDataItem, string>;
|
|
30
|
-
constructor(name: string);
|
|
31
|
-
static delete(name: string): Promise<void>;
|
|
1
|
+
import { EntryTypes, IDataItem, StoreDb } from './storeDb';
|
|
2
|
+
import { StructEventTarget } from '../structEvent';
|
|
3
|
+
import { TransactionMode } from 'dexie';
|
|
4
|
+
export type IDataStoreBase = {
|
|
5
|
+
openAsync(): PromiseLike<void>;
|
|
6
|
+
};
|
|
7
|
+
export declare class DataStore<TEntryTemplate extends keyof EntryTypes, TEventStruct extends Record<string, any> = unknown> extends StructEventTarget<TEventStruct> implements IDataStoreBase {
|
|
8
|
+
protected _db: StoreDb<TEntryTemplate>;
|
|
9
|
+
protected _isDisposed: boolean;
|
|
10
|
+
protected static $deleteAsync(name: string): Promise<void>;
|
|
11
|
+
protected static $existsAsync(name: string): Promise<boolean>;
|
|
12
|
+
protected static $openAsync<T extends IDataStoreBase>(name: string, factory: (name: string) => T): Promise<T>;
|
|
13
|
+
constructor(name: string, entryTemplate: TEntryTemplate);
|
|
14
|
+
openAsync(): Promise<void>;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
protected $execAsync<T>(action: () => Promise<T>, transactionMode?: TransactionMode): Promise<T>;
|
|
17
|
+
getKeysAsync(): Promise<string[]>;
|
|
18
|
+
getAsync(key: string): Promise<Readonly<EntryTypes[TEntryTemplate] & IDataItem>>;
|
|
19
|
+
bulkGetAsync(ids: string[]): Promise<{
|
|
20
|
+
[key: string]: Readonly<EntryTypes[TEntryTemplate] & IDataItem>;
|
|
21
|
+
}>;
|
|
22
|
+
containsAsync(key: string): Promise<boolean>;
|
|
23
|
+
deleteAsync(key: string): Promise<void>;
|
|
24
|
+
bulkDeleteAsync(keys: string[]): Promise<void>;
|
|
25
|
+
protected $setAsync(key: string, value: any): Promise<void>;
|
|
26
|
+
protected $getOrSetAsync(key: string, factory: (key: string) => any): Promise<Readonly<EntryTypes[TEntryTemplate] & IDataItem>>;
|
|
27
|
+
clearAsync(): Promise<void>;
|
|
32
28
|
}
|
|
33
29
|
//# sourceMappingURL=dataStore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataStore.d.ts","sourceRoot":"","sources":["../../src/store/dataStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dataStore.d.ts","sourceRoot":"","sources":["../../src/store/dataStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAc,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAU/C,MAAM,MAAM,cAAc,GAAG;IACzB,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;CAClC,CAAA;AAED,qBAAa,SAAS,CAAC,cAAc,SAAS,MAAM,UAAU,EAAE,YAAY,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAE,SAAQ,iBAAiB,CAAC,YAAY,CAAE,YAAW,cAAc;IAEjL,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAEvC,SAAS,CAAC,WAAW,EAAE,OAAO,CAAC;qBAER,YAAY,CAAC,IAAI,EAAE,MAAM;IAgBhD,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM;qBAInB,UAAU,CAAC,CAAC,SAAS,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC;gBAQ1F,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc;IASjD,SAAS;IAef,OAAO;cAiBS,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,eAAe,GAAE,eAAsB;IAezF,YAAY;IAIZ,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC,CAAC;IAchF,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC,CAAA;KAAE,CAAC;IA8CzG,aAAa,CAAC,GAAG,EAAE,MAAM;IAQzB,WAAW,CAAC,GAAG,EAAE,MAAM;IAQvB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE;cAQpB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;cAuBjC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG;IAUnE,UAAU;CAQnB"}
|