taon-storage 21.0.19 → 21.0.20

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