taon-storage 21.0.8 → 21.0.11
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/browser/fesm2022/taon-storage-browser.mjs +468 -255
- package/browser/fesm2022/taon-storage-browser.mjs.map +1 -1
- package/browser/package.json +5 -5
- package/browser/types/taon-storage-browser.d.ts +78 -30
- package/lib/build-info._auto-generated_.d.ts +1 -1
- package/lib/build-info._auto-generated_.js +1 -1
- package/lib/build-info._auto-generated_.js.map +1 -1
- package/lib/storage.d.ts +76 -35
- package/lib/storage.js +479 -212
- package/lib/storage.js.map +1 -1
- package/package.json +1 -1
- package/websql/fesm2022/taon-storage-websql.mjs +468 -255
- package/websql/fesm2022/taon-storage-websql.mjs.map +1 -1
- package/websql/package.json +5 -5
- package/websql/types/taon-storage-websql.d.ts +78 -30
- package/lib/constants.d.ts +0 -3
- package/lib/constants.js +0 -40
- package/lib/constants.js.map +0 -1
- package/lib/file-stor.d.ts +0 -9
- package/lib/file-stor.js +0 -64
- package/lib/file-stor.js.map +0 -1
- package/lib/helpers.d.ts +0 -2
- package/lib/helpers.js +0 -15
- package/lib/helpers.js.map +0 -1
- package/lib/models.d.ts +0 -10
- package/lib/models.js +0 -5
- package/lib/models.js.map +0 -1
- package/lib/utils.d.ts +0 -0
- package/lib/utils.js +0 -5
- package/lib/utils.js.map +0 -1
- package/tmp-environment.json +0 -43
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { CommonModule } from '@angular/common';
|
|
4
|
-
import
|
|
5
|
-
import { Helpers, _ } from 'tnp-core/browser';
|
|
4
|
+
import { _, UtilsOs } from 'tnp-core/browser';
|
|
6
5
|
|
|
7
6
|
class SampleLogCmpComponent {
|
|
8
7
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SampleLogCmpComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
@@ -13,284 +12,498 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
13
12
|
args: [{ selector: 'sample-log-cmp', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule], standalone: true, template: "<p>\n sample log component works!\n <img\n src=\"assets/assets-for/taon-storage/assets/shared/logo.png\"\n alt=\"logo\" height=\"200\" />\n</p>", styles: [":host{display:block}\n"] }]
|
|
14
13
|
}] });
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
/* taon-storage (native, SSR-safe) */
|
|
16
|
+
const isBrowser = typeof window !== 'undefined' &&
|
|
17
|
+
typeof document !== 'undefined' &&
|
|
18
|
+
typeof navigator !== 'undefined';
|
|
19
|
+
function getGlobalEnv() {
|
|
20
|
+
return globalThis?.ENV;
|
|
21
|
+
}
|
|
22
|
+
function safeLocationPort() {
|
|
23
|
+
try {
|
|
24
|
+
return globalThis?.location?.port || 'no-port';
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return 'no-port';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Keeps the spirit of your old `storeName = taon-storage_<port>`
|
|
32
|
+
* plus project name namespacing (but without localForage).
|
|
33
|
+
*/
|
|
34
|
+
const storeName = `taon-storage_${safeLocationPort()}`;
|
|
35
|
+
function defaultNamespace() {
|
|
36
|
+
const env = getGlobalEnv();
|
|
37
|
+
const project = _.kebabCase(env?.currentProjectGenericName ?? '');
|
|
38
|
+
return project ? `${storeName}_${project}` : storeName;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Central config (optional).
|
|
42
|
+
* You can set it once at app bootstrap if you want a stable namespace.
|
|
43
|
+
*/
|
|
44
|
+
const StorConfig = {
|
|
45
|
+
namespace: defaultNamespace(),
|
|
46
|
+
indexedDb: {
|
|
47
|
+
dbName: `${defaultNamespace()}_INDEXEDDB`,
|
|
48
|
+
storeName: 'keyvaluepairs',
|
|
49
|
+
},
|
|
24
50
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
function normalizeScopeClass(cls) {
|
|
52
|
+
if (!cls)
|
|
53
|
+
return { name: '__GLOBAL_NAMESPACE__' };
|
|
54
|
+
// if it's a function/class
|
|
55
|
+
if (typeof cls === 'function')
|
|
56
|
+
return { name: cls.name || '__ANON__' };
|
|
57
|
+
// if it's already object with name
|
|
58
|
+
return { name: cls.name || '__ANON__' };
|
|
32
59
|
}
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
let environment = globalThis['ENV'];
|
|
37
|
-
//#region constant / stor local storage
|
|
38
|
-
const websql = Helpers.isWebSQL ? 'websql' : '';
|
|
39
|
-
//#region @browser
|
|
40
|
-
const storLocalStorage = localForge.createInstance({
|
|
41
|
-
driver: localForge.LOCALSTORAGE,
|
|
42
|
-
storeName: [
|
|
43
|
-
storeName,
|
|
44
|
-
'LOCALSTORAGE',
|
|
45
|
-
_.kebabCase(environment?.currentProjectGenericName) + websql,
|
|
46
|
-
].join('_'), // + _.kebabCase(window.location.origin),
|
|
47
|
-
}); // TODO UNCOMMENT any
|
|
48
|
-
//#endregion
|
|
49
|
-
//#endregion
|
|
50
|
-
//#region constant / stor idndexedb storage
|
|
51
|
-
//#region @browser
|
|
52
|
-
const storIndexdDb = localForge.createInstance({
|
|
53
|
-
driver: localForge.INDEXEDDB,
|
|
54
|
-
storeName: [
|
|
55
|
-
storeName,
|
|
56
|
-
'INDEXEDDB',
|
|
57
|
-
_.kebabCase(environment?.currentProjectGenericName) + websql,
|
|
58
|
-
].join('_'),
|
|
59
|
-
}); // TODO UNCOMMENT any
|
|
60
|
-
//#endregion
|
|
61
|
-
//#endregion
|
|
62
|
-
|
|
63
|
-
//#endregion
|
|
64
|
-
//#endregion
|
|
65
|
-
//#region constants
|
|
66
|
-
const AWAITING_INTERVAL_TIME = 200;
|
|
67
|
-
//#endregion
|
|
68
|
-
//#region public api / uncahce
|
|
69
|
-
function uncache(onlyInThisComponentClass, propertyValueToDeleteFromCache) {
|
|
70
|
-
if (!onlyInThisComponentClass) { // @ts-ignore
|
|
71
|
-
onlyInThisComponentClass = { name: '__GLOBAL_NAMESPACE__' };
|
|
72
|
-
}
|
|
73
|
-
return Promise.all([
|
|
74
|
-
//#region @browser
|
|
75
|
-
storLocalStorage.removeItem(keyValue(onlyInThisComponentClass, propertyValueToDeleteFromCache)),
|
|
76
|
-
storLocalStorage.removeItem(keyDefaultValueAreadySet(onlyInThisComponentClass, propertyValueToDeleteFromCache)),
|
|
77
|
-
storIndexdDb.removeItem(keyValue(onlyInThisComponentClass, propertyValueToDeleteFromCache)),
|
|
78
|
-
storIndexdDb.removeItem(keyDefaultValueAreadySet(onlyInThisComponentClass, propertyValueToDeleteFromCache)),
|
|
79
|
-
//#endregion
|
|
80
|
-
]);
|
|
60
|
+
function keyValue(scopeClass, memberName) {
|
|
61
|
+
const c = normalizeScopeClass(scopeClass);
|
|
62
|
+
return `${StorConfig.namespace}::taon.storage.class.${c.name}.prop.${memberName}`;
|
|
81
63
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
64
|
+
function keyDefaultValueAlreadySet(scopeClass, memberName) {
|
|
65
|
+
return `${keyValue(scopeClass, memberName)}::defaultvalueisset`;
|
|
66
|
+
}
|
|
67
|
+
/** Back-compat alias (your old typo) */
|
|
68
|
+
const keyDefaultValueAreadySet = keyDefaultValueAlreadySet;
|
|
69
|
+
class NoopStore {
|
|
70
|
+
async getItem(_key) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
async setItem(_key, _value) {
|
|
74
|
+
// noop
|
|
75
|
+
}
|
|
76
|
+
async removeItem(_key) {
|
|
77
|
+
// noop
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
class BrowserLocalStorageStore {
|
|
81
|
+
ls() {
|
|
82
|
+
if (!isBrowser)
|
|
83
|
+
return undefined;
|
|
84
|
+
try {
|
|
85
|
+
return window.localStorage;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async getItem(key) {
|
|
92
|
+
const ls = this.ls();
|
|
93
|
+
if (!ls)
|
|
94
|
+
return undefined;
|
|
95
|
+
const raw = ls.getItem(key);
|
|
96
|
+
if (raw === null)
|
|
97
|
+
return undefined;
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(raw);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// if something stored plain string by older versions
|
|
103
|
+
return raw;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async setItem(key, value) {
|
|
107
|
+
const ls = this.ls();
|
|
108
|
+
if (!ls)
|
|
109
|
+
return;
|
|
110
|
+
try {
|
|
111
|
+
ls.setItem(key, JSON.stringify(value));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// last resort: try as string
|
|
115
|
+
try {
|
|
116
|
+
ls.setItem(key, String(value));
|
|
92
117
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
else {
|
|
128
|
-
await new Promise((res, rej) => {
|
|
129
|
-
storageEngine.setItem(keyDefaultValueAreadySet(this.onlyInThisComponentClass, memberName), true, (err, v) => {
|
|
130
|
-
res();
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
await new Promise((res, rej) => {
|
|
134
|
-
storageEngine.setItem(keyValue(this.onlyInThisComponentClass, memberName), transformTo ? transformTo(defaultValue) : defaultValue, (err, val) => {
|
|
135
|
-
res();
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
currentValue = defaultValue;
|
|
139
|
-
// log.i(`["${memberName}"] defaultValue "${memberName}"`, currentValue)
|
|
140
|
-
resolve();
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
})).then(() => {
|
|
144
|
-
this.endObserverAction(observe);
|
|
145
|
-
});
|
|
146
|
-
//#endregion
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
setItemDefaultValue();
|
|
118
|
+
catch {
|
|
119
|
+
// ignore (quota/private mode)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async removeItem(key) {
|
|
124
|
+
const ls = this.ls();
|
|
125
|
+
if (!ls)
|
|
126
|
+
return;
|
|
127
|
+
try {
|
|
128
|
+
ls.removeItem(key);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// ignore
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
class BrowserIndexedDbStore {
|
|
136
|
+
constructor() {
|
|
137
|
+
this.dbPromise = null;
|
|
138
|
+
}
|
|
139
|
+
openDb() {
|
|
140
|
+
if (!isBrowser || !window.indexedDB) {
|
|
141
|
+
return Promise.reject(new Error('IndexedDB not available'));
|
|
142
|
+
}
|
|
143
|
+
if (this.dbPromise)
|
|
144
|
+
return this.dbPromise;
|
|
145
|
+
const { dbName, storeName } = StorConfig.indexedDb;
|
|
146
|
+
this.dbPromise = new Promise((resolve, reject) => {
|
|
147
|
+
const req = indexedDB.open(dbName, 1);
|
|
148
|
+
req.onupgradeneeded = () => {
|
|
149
|
+
const db = req.result;
|
|
150
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
151
|
+
db.createObjectStore(storeName);
|
|
150
152
|
}
|
|
151
|
-
Object.defineProperty(target, memberName, {
|
|
152
|
-
set: (newValue) => {
|
|
153
|
-
//#region setting new value on setter
|
|
154
|
-
const observe = {
|
|
155
|
-
engine,
|
|
156
|
-
id: 'setting in SET not rivial default value'
|
|
157
|
-
};
|
|
158
|
-
TaonStorage.pendingOperatins.push(observe);
|
|
159
|
-
(new Promise((resolve, reject) => {
|
|
160
|
-
storageEngine.setItem(keyValue(this.onlyInThisComponentClass, memberName), transformTo ? transformTo(newValue) : newValue, (err, savedValue) => {
|
|
161
|
-
resolve();
|
|
162
|
-
});
|
|
163
|
-
})).then(() => {
|
|
164
|
-
this.endObserverAction(observe);
|
|
165
|
-
});
|
|
166
|
-
//#endregion
|
|
167
|
-
currentValue = newValue;
|
|
168
|
-
},
|
|
169
|
-
get: () => currentValue,
|
|
170
|
-
});
|
|
171
153
|
};
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
154
|
+
req.onsuccess = () => resolve(req.result);
|
|
155
|
+
req.onerror = () => reject(req.error);
|
|
156
|
+
});
|
|
157
|
+
return this.dbPromise;
|
|
158
|
+
}
|
|
159
|
+
async withStore(mode, fn) {
|
|
160
|
+
const db = await this.openDb();
|
|
161
|
+
const { storeName } = StorConfig.indexedDb;
|
|
162
|
+
return await new Promise((resolve, reject) => {
|
|
163
|
+
const tx = db.transaction(storeName, mode);
|
|
164
|
+
const store = tx.objectStore(storeName);
|
|
165
|
+
const req = fn(store);
|
|
166
|
+
req.onsuccess = () => resolve(req.result);
|
|
167
|
+
req.onerror = () => reject(req.error);
|
|
168
|
+
tx.onabort = () => reject(tx.error);
|
|
169
|
+
// tx.oncomplete => nothing
|
|
170
|
+
});
|
|
175
171
|
}
|
|
176
|
-
|
|
177
|
-
|
|
172
|
+
async getItem(key) {
|
|
173
|
+
try {
|
|
174
|
+
const result = await this.withStore('readonly', s => s.get(key));
|
|
175
|
+
return result === undefined ? undefined : result;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async setItem(key, value) {
|
|
182
|
+
try {
|
|
183
|
+
await this.withStore('readwrite', s => s.put(value, key));
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// ignore
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async removeItem(key) {
|
|
190
|
+
try {
|
|
191
|
+
await this.withStore('readwrite', s => s.delete(key));
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// ignore
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Node-side file storage (optional). No top-level node imports (Angular-safe).
|
|
200
|
+
* Works only when executed in Node.
|
|
201
|
+
*/
|
|
202
|
+
class FileStor {
|
|
203
|
+
constructor(filePath, useJSON = false) {
|
|
204
|
+
this.filePath = filePath;
|
|
205
|
+
this.useJSON = useJSON;
|
|
206
|
+
}
|
|
207
|
+
isNodeRuntime() {
|
|
208
|
+
return UtilsOs.isNode;
|
|
209
|
+
// return (
|
|
210
|
+
// typeof process !== 'undefined' &&
|
|
211
|
+
// !!(process as any).versions?.node &&
|
|
212
|
+
// typeof (globalThis as any).window === 'undefined'
|
|
213
|
+
// );
|
|
214
|
+
}
|
|
215
|
+
async setItem(_key, value) {
|
|
216
|
+
if (!this.isNodeRuntime())
|
|
217
|
+
return;
|
|
218
|
+
/* */
|
|
219
|
+
/* */
|
|
220
|
+
/* */
|
|
221
|
+
/* */
|
|
222
|
+
/* */
|
|
223
|
+
/* */
|
|
224
|
+
/* */
|
|
225
|
+
/* */
|
|
226
|
+
/* */
|
|
227
|
+
return (void 0);
|
|
228
|
+
}
|
|
229
|
+
async getItem(_key) {
|
|
230
|
+
if (!this.isNodeRuntime())
|
|
231
|
+
return undefined;
|
|
232
|
+
/* */
|
|
233
|
+
/* */
|
|
234
|
+
/* */
|
|
235
|
+
/* */
|
|
236
|
+
/* */
|
|
237
|
+
/* */
|
|
238
|
+
/* */
|
|
239
|
+
/* */
|
|
240
|
+
/* */
|
|
241
|
+
/* */
|
|
242
|
+
return (void 0);
|
|
243
|
+
}
|
|
244
|
+
async removeItem(_key) {
|
|
245
|
+
if (!this.isNodeRuntime())
|
|
246
|
+
return;
|
|
247
|
+
/* */
|
|
248
|
+
/* */
|
|
249
|
+
/* */
|
|
250
|
+
/* */
|
|
251
|
+
/* */
|
|
252
|
+
/* */
|
|
253
|
+
/* */
|
|
254
|
+
return (void 0);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/* ---------------------------
|
|
258
|
+
* Pending ops (so you can still await)
|
|
259
|
+
* -------------------------- */
|
|
260
|
+
class StorPending {
|
|
261
|
+
static { this.pending = []; }
|
|
178
262
|
static { this.id = 0; }
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
*/
|
|
182
|
-
static async awaitPendingOperatios(id = TaonStorage.id++) {
|
|
183
|
-
// console.log('AWAITING')
|
|
263
|
+
static { this.AWAITING_INTERVAL_TIME = 200; }
|
|
264
|
+
static async awaitPendingOperations(id = StorPending.id++) {
|
|
184
265
|
if (id > Number.MAX_SAFE_INTEGER - 2) {
|
|
185
|
-
|
|
186
|
-
id =
|
|
266
|
+
StorPending.id = 0;
|
|
267
|
+
id = StorPending.id++;
|
|
187
268
|
}
|
|
188
|
-
const pending =
|
|
189
|
-
const
|
|
190
|
-
for (let index = 0; index < pending.length; index++) {
|
|
191
|
-
const op = pending[index];
|
|
269
|
+
const pending = StorPending.pending;
|
|
270
|
+
for (const op of pending) {
|
|
192
271
|
if (!op.isDone) {
|
|
193
|
-
await new Promise(
|
|
272
|
+
await new Promise(resolve => {
|
|
194
273
|
setTimeout(async () => {
|
|
195
|
-
await
|
|
196
|
-
|
|
197
|
-
}, AWAITING_INTERVAL_TIME);
|
|
274
|
+
await StorPending.awaitPendingOperations(id);
|
|
275
|
+
resolve();
|
|
276
|
+
}, StorPending.AWAITING_INTERVAL_TIME);
|
|
198
277
|
});
|
|
199
278
|
return;
|
|
200
279
|
}
|
|
201
|
-
else {
|
|
202
|
-
toDeleteIndex.push(index);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
for (let index = 0; index < toDeleteIndex.length; index++) {
|
|
206
|
-
const toDelete = toDeleteIndex[index];
|
|
207
|
-
pending.splice(toDelete, 1);
|
|
208
280
|
}
|
|
281
|
+
// cleanup
|
|
282
|
+
StorPending.pending = pending.filter(p => !p.isDone);
|
|
209
283
|
}
|
|
210
|
-
static
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
//#endregion
|
|
218
|
-
//#region public getters
|
|
219
|
-
get in() {
|
|
220
|
-
const that = this;
|
|
221
|
-
return {
|
|
222
|
-
get indexedb() {
|
|
223
|
-
that.engine = 'indexeddb';
|
|
224
|
-
return that;
|
|
225
|
-
},
|
|
226
|
-
get localstorage() {
|
|
227
|
-
that.engine = 'localstorage';
|
|
228
|
-
return that;
|
|
229
|
-
},
|
|
230
|
-
/* */
|
|
231
|
-
/* */
|
|
232
|
-
/* */
|
|
233
|
-
/* */
|
|
234
|
-
/* */
|
|
235
|
-
/* */
|
|
236
|
-
/* */
|
|
237
|
-
/* */
|
|
238
|
-
/* */
|
|
239
|
-
/* */
|
|
240
|
-
/* */
|
|
241
|
-
/* */
|
|
242
|
-
/* */
|
|
243
|
-
/* */
|
|
244
|
-
};
|
|
284
|
+
static start(engine, id) {
|
|
285
|
+
const op = { engine, id, isDone: false };
|
|
286
|
+
StorPending.pending.push(op);
|
|
287
|
+
return op;
|
|
288
|
+
}
|
|
289
|
+
static done(op) {
|
|
290
|
+
op.isDone = true;
|
|
245
291
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
292
|
+
}
|
|
293
|
+
/* ---------------------------
|
|
294
|
+
* Decorator builder
|
|
295
|
+
* -------------------------- */
|
|
296
|
+
class StorPropertyBuilder {
|
|
297
|
+
constructor(engine, store) {
|
|
298
|
+
this.useJsonFile = false;
|
|
299
|
+
this.engine = engine;
|
|
300
|
+
this.store = store;
|
|
301
|
+
}
|
|
302
|
+
for(scopeClass) {
|
|
303
|
+
this.scopeClass = scopeClass;
|
|
252
304
|
return this;
|
|
253
305
|
}
|
|
254
|
-
//#endregion
|
|
255
|
-
//#endregion
|
|
256
|
-
//#region public methods / with default value
|
|
257
306
|
withDefaultValue(defaultValue) {
|
|
258
|
-
|
|
259
|
-
return this.action(defaultValue, this.getEngine(), this.engine);
|
|
307
|
+
return this.withOptions({ defaultValue });
|
|
260
308
|
}
|
|
261
|
-
//#endregion
|
|
262
|
-
//#region public methods / with options
|
|
263
309
|
withOptions(options) {
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
310
|
+
const scopeClass = this.scopeClass;
|
|
311
|
+
// per-instance state (fixes prototype-closure sharing)
|
|
312
|
+
const values = new WeakMap();
|
|
313
|
+
const initStarted = new WeakMap();
|
|
314
|
+
const ensureInit = (instance) => {
|
|
315
|
+
if (initStarted.has(instance))
|
|
316
|
+
return;
|
|
317
|
+
const op = StorPending.start(this.engine, 'init');
|
|
318
|
+
const p = (async () => {
|
|
319
|
+
const memberName = ensureInit.__memberName;
|
|
320
|
+
const kVal = keyValue(scopeClass, memberName);
|
|
321
|
+
const kDef = keyDefaultValueAlreadySet(scopeClass, memberName);
|
|
322
|
+
const defProvided = options.defaultValue !== undefined;
|
|
323
|
+
if (!isBrowser &&
|
|
324
|
+
(this.engine === 'localstorage' || this.engine === 'indexeddb')) {
|
|
325
|
+
// SSR: just set defaults, no storage
|
|
326
|
+
if (defProvided)
|
|
327
|
+
values.set(instance, options.defaultValue);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
// Browser (or node file/json)
|
|
331
|
+
if (defProvided) {
|
|
332
|
+
const already = await this.store.getItem(kDef);
|
|
333
|
+
if (already) {
|
|
334
|
+
const stored = await this.store.getItem(kVal);
|
|
335
|
+
const v = options.transformFrom
|
|
336
|
+
? options.transformFrom(stored)
|
|
337
|
+
: stored;
|
|
338
|
+
if (v !== undefined)
|
|
339
|
+
values.set(instance, v);
|
|
340
|
+
else
|
|
341
|
+
values.set(instance, options.defaultValue);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
await this.store.setItem(kDef, true);
|
|
345
|
+
const toDb = options.transformTo
|
|
346
|
+
? options.transformTo(options.defaultValue)
|
|
347
|
+
: options.defaultValue;
|
|
348
|
+
await this.store.setItem(kVal, toDb);
|
|
349
|
+
values.set(instance, options.defaultValue);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
const stored = await this.store.getItem(kVal);
|
|
354
|
+
const v = options.transformFrom
|
|
355
|
+
? options.transformFrom(stored)
|
|
356
|
+
: stored;
|
|
357
|
+
if (v !== undefined)
|
|
358
|
+
values.set(instance, v);
|
|
359
|
+
}
|
|
360
|
+
})()
|
|
361
|
+
.catch(() => {
|
|
362
|
+
// swallow, keep app alive
|
|
363
|
+
})
|
|
364
|
+
.finally(() => StorPending.done(op));
|
|
365
|
+
initStarted.set(instance, p);
|
|
366
|
+
};
|
|
367
|
+
return (target, memberName) => {
|
|
368
|
+
ensureInit.__memberName = memberName;
|
|
369
|
+
Object.defineProperty(target, memberName, {
|
|
370
|
+
configurable: true,
|
|
371
|
+
enumerable: true,
|
|
372
|
+
get: function () {
|
|
373
|
+
ensureInit(this);
|
|
374
|
+
if (values.has(this))
|
|
375
|
+
return values.get(this);
|
|
376
|
+
if (options.defaultValue !== undefined)
|
|
377
|
+
return options.defaultValue;
|
|
378
|
+
return undefined;
|
|
379
|
+
},
|
|
380
|
+
set: function (newValue) {
|
|
381
|
+
values.set(this, newValue);
|
|
382
|
+
// if this is the first interaction, init will happen anyway
|
|
383
|
+
ensureInit(this);
|
|
384
|
+
const op = StorPending.start(target?.engine ?? 'localstorage', 'set');
|
|
385
|
+
const scope = scopeClass;
|
|
386
|
+
const kVal = keyValue(scope, memberName);
|
|
387
|
+
const toDb = options.transformTo
|
|
388
|
+
? options.transformTo(newValue)
|
|
389
|
+
: newValue;
|
|
390
|
+
Promise.resolve()
|
|
391
|
+
.then(() => target)
|
|
392
|
+
.then(() => this)
|
|
393
|
+
.then(() => this)
|
|
394
|
+
.then(async () => {
|
|
395
|
+
// If we are SSR + browser engine => no-op
|
|
396
|
+
if (!isBrowser && options)
|
|
397
|
+
return;
|
|
398
|
+
await options; // no-op line to keep TS happy about chaining in some builds
|
|
399
|
+
})
|
|
400
|
+
.catch(() => {
|
|
401
|
+
// ignore
|
|
402
|
+
});
|
|
403
|
+
// do real store write (async)
|
|
404
|
+
Promise.resolve()
|
|
405
|
+
.then(async () => {
|
|
406
|
+
// SSR guard for browser engines
|
|
407
|
+
if (!isBrowser && StorPropertyInLocalStorage) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
await thisStoreForEngineWrite(this, kVal, toDb);
|
|
411
|
+
})
|
|
412
|
+
.catch(() => {
|
|
413
|
+
// ignore
|
|
414
|
+
})
|
|
415
|
+
.finally(() => StorPending.done(op));
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
// small helper to keep closure clean
|
|
419
|
+
const builderStore = this.store;
|
|
420
|
+
const builderEngine = this.engine;
|
|
421
|
+
async function thisStoreForEngineWrite(_instance, key, value) {
|
|
422
|
+
// If browser engines but not browser, skip.
|
|
423
|
+
if (!isBrowser &&
|
|
424
|
+
(builderEngine === 'localstorage' || builderEngine === 'indexeddb'))
|
|
425
|
+
return;
|
|
426
|
+
await builderStore.setItem(key, value);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
/* optional node-only engines (same builder) */
|
|
431
|
+
file(filePath) {
|
|
432
|
+
this.engine = 'file';
|
|
433
|
+
this.filePath = filePath;
|
|
434
|
+
this.useJsonFile = false;
|
|
435
|
+
this.store = new FileStor(filePath, false);
|
|
436
|
+
return this;
|
|
437
|
+
}
|
|
438
|
+
jsonFile(filePath) {
|
|
439
|
+
this.engine = 'json';
|
|
440
|
+
this.filePath = filePath;
|
|
441
|
+
this.useJsonFile = true;
|
|
442
|
+
this.store = new FileStor(filePath, true);
|
|
443
|
+
return this;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/* ---------------------------
|
|
447
|
+
* Public: clean API exports
|
|
448
|
+
* -------------------------- */
|
|
449
|
+
const localStorageStore = isBrowser
|
|
450
|
+
? new BrowserLocalStorageStore()
|
|
451
|
+
: new NoopStore();
|
|
452
|
+
const indexedDbStore = isBrowser
|
|
453
|
+
? new BrowserIndexedDbStore()
|
|
454
|
+
: new NoopStore();
|
|
455
|
+
class StorPropertyInLocalStorage {
|
|
456
|
+
static for(scopeClass) {
|
|
457
|
+
return new StorPropertyBuilder('localstorage', localStorageStore).for(scopeClass);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
class StorPropertyInIndexedDb {
|
|
461
|
+
static for(scopeClass) {
|
|
462
|
+
return new StorPropertyBuilder('indexeddb', indexedDbStore).for(scopeClass);
|
|
285
463
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Helpers
|
|
467
|
+
*/
|
|
468
|
+
async function uncache(onlyInThisComponentClass, propertyValueToDeleteFromCache) {
|
|
469
|
+
const scope = onlyInThisComponentClass || { name: '__GLOBAL_NAMESPACE__' };
|
|
470
|
+
const prop = String(propertyValueToDeleteFromCache);
|
|
471
|
+
await Promise.all([
|
|
472
|
+
localStorageStore.removeItem(keyValue(scope, prop)),
|
|
473
|
+
localStorageStore.removeItem(keyDefaultValueAlreadySet(scope, prop)),
|
|
474
|
+
indexedDbStore.removeItem(keyValue(scope, prop)),
|
|
475
|
+
indexedDbStore.removeItem(keyDefaultValueAlreadySet(scope, prop)),
|
|
476
|
+
]);
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Backwards-compatible facade:
|
|
480
|
+
* Stor.property.in.localstorage.for(...).withDefaultValue(...)
|
|
481
|
+
*/
|
|
482
|
+
class TaonStorageFacade {
|
|
483
|
+
static async awaitPendingOperatios() {
|
|
484
|
+
await StorPending.awaitPendingOperations();
|
|
485
|
+
}
|
|
486
|
+
static get property() {
|
|
487
|
+
return {
|
|
488
|
+
in: {
|
|
489
|
+
get localstorage() {
|
|
490
|
+
return new StorPropertyBuilder('localstorage', localStorageStore);
|
|
491
|
+
},
|
|
492
|
+
get indexedb() {
|
|
493
|
+
return new StorPropertyBuilder('indexeddb', indexedDbStore);
|
|
494
|
+
},
|
|
495
|
+
// node-only (safe: dynamic import inside FileStor)
|
|
496
|
+
file(filePath) {
|
|
497
|
+
return new StorPropertyBuilder('file', new FileStor(filePath, false));
|
|
498
|
+
},
|
|
499
|
+
jsonFile(filePath) {
|
|
500
|
+
return new StorPropertyBuilder('json', new FileStor(filePath, true));
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
};
|
|
291
504
|
}
|
|
292
505
|
}
|
|
293
|
-
const Stor =
|
|
506
|
+
const Stor = TaonStorageFacade;
|
|
294
507
|
|
|
295
508
|
//#region @browser
|
|
296
509
|
|
|
@@ -298,5 +511,5 @@ const Stor = TaonStorage;
|
|
|
298
511
|
* Generated bundle index. Do not edit.
|
|
299
512
|
*/
|
|
300
513
|
|
|
301
|
-
export { SampleLogCmpComponent, Stor, uncache };
|
|
514
|
+
export { SampleLogCmpComponent, Stor, StorConfig, StorPropertyInIndexedDb, StorPropertyInLocalStorage, keyDefaultValueAlreadySet, keyDefaultValueAreadySet, keyValue, storeName, uncache };
|
|
302
515
|
//# sourceMappingURL=taon-storage-browser.mjs.map
|