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