taon-storage 21.0.18 → 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,6 +1,6 @@
1
1
  {
2
2
  "name": "taon-storage/browser",
3
- "version": "21.0.18",
3
+ "version": "21.0.20",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.0.0",
6
6
  "@angular/core": "^21.0.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taon-storage/browser-prod",
3
- "version": "21.0.18",
3
+ "version": "21.0.20",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.0.0",
6
6
  "@angular/core": "^21.0.0"
@@ -21,4 +21,4 @@ export declare const CURRENT_PACKAGE_TAON_VERSION = "v21";
21
21
  /**
22
22
  * Autogenerated by current cli tool. Use *tnp release* to bump version.
23
23
  */
24
- export declare const CURRENT_PACKAGE_VERSION = "21.0.18";
24
+ export declare const CURRENT_PACKAGE_VERSION = "21.0.20";
@@ -25,6 +25,6 @@ exports.CURRENT_PACKAGE_TAON_VERSION = 'v21';
25
25
  /**
26
26
  * Autogenerated by current cli tool. Use *tnp release* to bump version.
27
27
  */
28
- exports.CURRENT_PACKAGE_VERSION = '21.0.18';
28
+ exports.CURRENT_PACKAGE_VERSION = '21.0.20';
29
29
  // THIS FILE IS GENERATED - DO NOT MODIFY
30
30
  //# sourceMappingURL=build-info._auto-generated_.js.map
package/lib/package.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "taon-storage/lib",
3
- "version": "21.0.18"
3
+ "version": "21.0.20"
4
4
  }
@@ -22,6 +22,5 @@ export const CURRENT_PACKAGE_TAON_VERSION = 'v21';
22
22
  /**
23
23
  * Autogenerated by current cli tool. Use *tnp release* to bump version.
24
24
  */
25
- export const CURRENT_PACKAGE_VERSION = '21.0.18';
25
+ export const CURRENT_PACKAGE_VERSION = '21.0.20';
26
26
  // THIS FILE IS GENERATED - DO NOT MODIFY
27
-
@@ -63,4 +63,4 @@ export const ENV_ANGULAR_NODE_APP_IS_CI_PROCESS = undefined;
63
63
  export const ENV_ANGULAR_NODE_APP_DOCKER_ADDITIONAL_CONTAINER = undefined;
64
64
  export const ENV_ANGULAR_NODE_APP_DOCKER_SKIP_START_IN_ORDER = undefined;
65
65
  export const ENV_ANGULAR_NODE_APP_DOCKER_SKIP_USING_MYSQL_DB = undefined;
66
- // THIS FILE IS GENERATED - DO NOT MODIFY
66
+ // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -63,4 +63,4 @@ export const ENV_DOCS_WEBAPP_IS_CI_PROCESS = undefined;
63
63
  export const ENV_DOCS_WEBAPP_DOCKER_ADDITIONAL_CONTAINER = undefined;
64
64
  export const ENV_DOCS_WEBAPP_DOCKER_SKIP_START_IN_ORDER = undefined;
65
65
  export const ENV_DOCS_WEBAPP_DOCKER_SKIP_USING_MYSQL_DB = undefined;
66
- // THIS FILE IS GENERATED - DO NOT MODIFY
66
+ // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -63,4 +63,4 @@ export const ENV_ELECTRON_APP_IS_CI_PROCESS = undefined;
63
63
  export const ENV_ELECTRON_APP_DOCKER_ADDITIONAL_CONTAINER = undefined;
64
64
  export const ENV_ELECTRON_APP_DOCKER_SKIP_START_IN_ORDER = undefined;
65
65
  export const ENV_ELECTRON_APP_DOCKER_SKIP_USING_MYSQL_DB = undefined;
66
- // THIS FILE IS GENERATED - DO NOT MODIFY
66
+ // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -63,4 +63,4 @@ export const ENV_MOBILE_APP_IS_CI_PROCESS = undefined;
63
63
  export const ENV_MOBILE_APP_DOCKER_ADDITIONAL_CONTAINER = undefined;
64
64
  export const ENV_MOBILE_APP_DOCKER_SKIP_START_IN_ORDER = undefined;
65
65
  export const ENV_MOBILE_APP_DOCKER_SKIP_USING_MYSQL_DB = undefined;
66
- // THIS FILE IS GENERATED - DO NOT MODIFY
66
+ // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -63,4 +63,4 @@ export const ENV_NPM_LIB_AND_CLI_TOOL_IS_CI_PROCESS = undefined;
63
63
  export const ENV_NPM_LIB_AND_CLI_TOOL_DOCKER_ADDITIONAL_CONTAINER = undefined;
64
64
  export const ENV_NPM_LIB_AND_CLI_TOOL_DOCKER_SKIP_START_IN_ORDER = undefined;
65
65
  export const ENV_NPM_LIB_AND_CLI_TOOL_DOCKER_SKIP_USING_MYSQL_DB = undefined;
66
- // THIS FILE IS GENERATED - DO NOT MODIFY
66
+ // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -63,4 +63,4 @@ export const ENV_VSCODE_PLUGIN_IS_CI_PROCESS = undefined;
63
63
  export const ENV_VSCODE_PLUGIN_DOCKER_ADDITIONAL_CONTAINER = undefined;
64
64
  export const ENV_VSCODE_PLUGIN_DOCKER_SKIP_START_IN_ORDER = undefined;
65
65
  export const ENV_VSCODE_PLUGIN_DOCKER_SKIP_USING_MYSQL_DB = undefined;
66
- // THIS FILE IS GENERATED - DO NOT MODIFY
66
+ // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -2,4 +2,4 @@
2
2
  // This file is auto-generated during init process. Do not modify.
3
3
  // This is only placeholder.
4
4
  // Use property "shouldGenerateAutogenIndexFile: true"
5
- // in taon.jsonc to enable ts exports auto generation.
5
+ // in taon.jsonc to enable ts exports auto generation.
@@ -0,0 +1,3 @@
1
+ /* */
2
+ /* */
3
+ export * from './storage';
@@ -0,0 +1,2 @@
1
+ //@ts-nocheck
2
+ export * from './migrations_index._auto-generated_';
@@ -1,5 +1,3 @@
1
1
  // THIS FILE IS GENERATED - DO NOT MODIFY
2
-
3
2
  // THIS FILE IS GENERATED - DO NOT MODIFY
4
-
5
3
  // THIS FILE IS GENERATED - DO NOT MODIFY
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "taon-storage/lib-prod",
3
- "version": "21.0.18"
3
+ "version": "21.0.20"
4
4
  }
@@ -0,0 +1,491 @@
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';
6
+ function safeLocationPort() {
7
+ try {
8
+ return globalThis?.location?.port || 'no-port';
9
+ }
10
+ catch {
11
+ return 'no-port';
12
+ }
13
+ }
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()}`;
19
+ function defaultNamespace() {
20
+ const project = ___NS__kebabCase(globalThis['CURRENT_PROJECT_GENERIC_NAME'] ?? '');
21
+ return project ? `${storeName}_${project}` : storeName;
22
+ }
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
+ },
33
+ };
34
+ function normalizeScopeClass(cls) {
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__' };
42
+ }
43
+ export function keyValue(scopeClass, memberName) {
44
+ const c = normalizeScopeClass(scopeClass);
45
+ return `${StorConfig.namespace}::taon.storage.class.${c.name}.prop.${memberName}`;
46
+ }
47
+ export function keyDefaultValueAlreadySet(scopeClass, memberName) {
48
+ return `${keyValue(scopeClass, memberName)}::defaultvalueisset`;
49
+ }
50
+ /** Back-compat alias (your old typo) */
51
+ export const keyDefaultValueAreadySet = keyDefaultValueAlreadySet;
52
+ class NoopStore {
53
+ async getItem(_key) {
54
+ return undefined;
55
+ }
56
+ async setItem(_key, _value) {
57
+ // noop
58
+ }
59
+ async removeItem(_key) {
60
+ // noop
61
+ }
62
+ }
63
+ class BrowserLocalStorageStore {
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
+ }
116
+ }
117
+ }
118
+ class BrowserIndexedDbStore {
119
+ constructor() {
120
+ this.dbPromise = null;
121
+ }
122
+ openDb() {
123
+ if (!isBrowser || !window.indexedDB) {
124
+ return Promise.reject(new Error('IndexedDB not available'));
125
+ }
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;
141
+ }
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
+ });
154
+ }
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
+ }
179
+ }
180
+ }
181
+ /**
182
+ * Node-side file storage (optional). No top-level node imports (Angular-safe).
183
+ * Works only when executed in Node.
184
+ */
185
+ class FileStor {
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
240
+ }
241
+ }
242
+ /* ---------------------------
243
+ * Pending ops (so you can still await)
244
+ * -------------------------- */
245
+ class StorPending {
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++;
253
+ }
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;
264
+ }
265
+ }
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
+ }
277
+ }
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
+ }
444
+ }
445
+ export class StorPropertyInIndexedDb {
446
+ static for(scopeClass) {
447
+ return new StorPropertyBuilder('indexeddb', indexedDbStore).for(scopeClass);
448
+ }
449
+ }
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
+ ]);
462
+ }
463
+ /**
464
+ * Backwards-compatible facade:
465
+ * Stor.property.in.localstorage.for(...).withDefaultValue(...)
466
+ */
467
+ class TaonStorageFacade {
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
+ }
490
+ }
491
+ export const Stor = TaonStorageFacade;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taon-storage",
3
- "version": "21.0.18",
3
+ "version": "21.0.20",
4
4
  "scripts": {
5
5
  "taon init": "taon init",
6
6
  "taon start": "taon start",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taon-storage/websql",
3
- "version": "21.0.18",
3
+ "version": "21.0.20",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.0.0",
6
6
  "@angular/core": "^21.0.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taon-storage/websql-prod",
3
- "version": "21.0.18",
3
+ "version": "21.0.20",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.0.0",
6
6
  "@angular/core": "^21.0.0"
package/lib-prod/index.ts DELETED
@@ -1,4 +0,0 @@
1
- /* */
2
- /* */
3
-
4
- export * from './storage';
@@ -1,8 +0,0 @@
1
- THIS FILE IS GENERATED - DO NOT MODIFY
2
-
3
- This folder is an entry point for npm Angular/NodeJS library
4
-
5
- DON'T USE STUFF FROM PARENT FOLDER app.* FILES HERE (except src/migrations/** files).
6
-
7
- THIS FILE IS GENERATED - DO NOT MODIFY
8
-
@@ -1,2 +0,0 @@
1
- //@ts-nocheck
2
- export * from './migrations_index._auto-generated_';
@@ -1,6 +0,0 @@
1
- THIS FILE IS GENERATED - DO NOT MODIFY
2
-
3
- This folder is only for storing migration files with auto-generated names.
4
-
5
- THIS FILE IS GENERATED - DO NOT MODIFY
6
-
@@ -1,591 +0,0 @@
1
- /* taon-storage (native, SSR-safe) */
2
- import { ___NS__add, ___NS__after, ___NS__ary, ___NS__assign, ___NS__assignIn, ___NS__assignInWith, ___NS__assignWith, ___NS__at, ___NS__attempt, ___NS__before, ___NS__bind, ___NS__bindAll, ___NS__bindKey, ___NS__camelCase, ___NS__capitalize, ___NS__castArray, ___NS__ceil, ___NS__chain, ___NS__chunk, ___NS__clamp, ___NS__clone, ___NS__cloneDeep, ___NS__cloneDeepWith, ___NS__cloneWith, ___NS__compact, ___NS__concat, ___NS__cond, ___NS__conforms, ___NS__conformsTo, ___NS__constant, ___NS__countBy, ___NS__create, ___NS__curry, ___NS__curryRight, ___NS__debounce, ___NS__deburr, ___NS__defaults, ___NS__defaultsDeep, ___NS__defaultTo, ___NS__defer, ___NS__delay, ___NS__difference, ___NS__differenceBy, ___NS__differenceWith, ___NS__divide, ___NS__drop, ___NS__dropRight, ___NS__dropRightWhile, ___NS__dropWhile, ___NS__each, ___NS__eachRight, ___NS__endsWith, ___NS__entries, ___NS__entriesIn, ___NS__eq, ___NS__escape, ___NS__escapeRegExp, ___NS__every, ___NS__extend, ___NS__extendWith, ___NS__fill, ___NS__filter, ___NS__find, ___NS__findIndex, ___NS__findKey, ___NS__findLast, ___NS__findLastIndex, ___NS__findLastKey, ___NS__first, ___NS__flatMap, ___NS__flatMapDeep, ___NS__flatMapDepth, ___NS__flatten, ___NS__flattenDeep, ___NS__flattenDepth, ___NS__flip, ___NS__floor, ___NS__flow, ___NS__flowRight, ___NS__forEach, ___NS__forEachRight, ___NS__forIn, ___NS__forInRight, ___NS__forOwn, ___NS__forOwnRight, ___NS__fromPairs, ___NS__functions, ___NS__functionsIn, ___NS__get, ___NS__groupBy, ___NS__gt, ___NS__gte, ___NS__has, ___NS__hasIn, ___NS__head, ___NS__identity, ___NS__includes, ___NS__indexOf, ___NS__initial, ___NS__inRange, ___NS__intersection, ___NS__intersectionBy, ___NS__intersectionWith, ___NS__invert, ___NS__invertBy, ___NS__invoke, ___NS__invokeMap, ___NS__isArguments, ___NS__isArray, ___NS__isArrayBuffer, ___NS__isArrayLike, ___NS__isArrayLikeObject, ___NS__isBoolean, ___NS__isBuffer, ___NS__isDate, ___NS__isElement, ___NS__isEmpty, ___NS__isEqual, ___NS__isEqualWith, ___NS__isError, ___NS__isFinite, ___NS__isFunction, ___NS__isInteger, ___NS__isLength, ___NS__isMap, ___NS__isMatch, ___NS__isMatchWith, ___NS__isNaN, ___NS__isNative, ___NS__isNil, ___NS__isNull, ___NS__isNumber, ___NS__isObject, ___NS__isObjectLike, ___NS__isPlainObject, ___NS__isRegExp, ___NS__isSafeInteger, ___NS__isSet, ___NS__isString, ___NS__isSymbol, ___NS__isTypedArray, ___NS__isUndefined, ___NS__isWeakMap, ___NS__isWeakSet, ___NS__iteratee, ___NS__join, ___NS__kebabCase, ___NS__keyBy, ___NS__keys, ___NS__keysIn, ___NS__last, ___NS__lastIndexOf, ___NS__lowerCase, ___NS__lowerFirst, ___NS__lt, ___NS__lte, ___NS__map, ___NS__mapKeys, ___NS__mapValues, ___NS__matches, ___NS__matchesProperty, ___NS__max, ___NS__maxBy, ___NS__mean, ___NS__meanBy, ___NS__memoize, ___NS__merge, ___NS__mergeWith, ___NS__method, ___NS__methodOf, ___NS__min, ___NS__minBy, ___NS__mixin, ___NS__multiply, ___NS__negate, ___NS__noop, ___NS__now, ___NS__nth, ___NS__nthArg, ___NS__omit, ___NS__omitBy, ___NS__once, ___NS__orderBy, ___NS__over, ___NS__overArgs, ___NS__overEvery, ___NS__overSome, ___NS__pad, ___NS__padEnd, ___NS__padStart, ___NS__parseInt, ___NS__partial, ___NS__partialRight, ___NS__partition, ___NS__pick, ___NS__pickBy, ___NS__property, ___NS__propertyOf, ___NS__pull, ___NS__pullAll, ___NS__pullAllBy, ___NS__pullAllWith, ___NS__pullAt, ___NS__random, ___NS__range, ___NS__rangeRight, ___NS__rearg, ___NS__reduce, ___NS__reduceRight, ___NS__reject, ___NS__remove, ___NS__repeat, ___NS__replace, ___NS__rest, ___NS__result, ___NS__reverse, ___NS__round, ___NS__sample, ___NS__sampleSize, ___NS__set, ___NS__setWith, ___NS__shuffle, ___NS__size, ___NS__slice, ___NS__snakeCase, ___NS__some, ___NS__sortBy, ___NS__sortedIndex, ___NS__sortedIndexBy, ___NS__sortedIndexOf, ___NS__sortedLastIndex, ___NS__sortedLastIndexBy, ___NS__sortedLastIndexOf, ___NS__sortedUniq, ___NS__sortedUniqBy, ___NS__split, ___NS__spread, ___NS__startCase, ___NS__startsWith, ___NS__stubArray, ___NS__stubFalse, ___NS__stubObject, ___NS__stubString, ___NS__stubTrue, ___NS__subtract, ___NS__sum, ___NS__sumBy, ___NS__tail, ___NS__take, ___NS__takeRight, ___NS__takeRightWhile, ___NS__takeWhile, ___NS__tap, ___NS__template, ___NS__templateSettings, ___NS__throttle, ___NS__thru, ___NS__times, ___NS__toArray, ___NS__toFinite, ___NS__toInteger, ___NS__toLength, ___NS__toLower, ___NS__toNumber, ___NS__toPairs, ___NS__toPairsIn, ___NS__toPath, ___NS__toPlainObject, ___NS__toSafeInteger, ___NS__toString, ___NS__toUpper, ___NS__transform, ___NS__trim, ___NS__trimEnd, ___NS__trimStart, ___NS__truncate, ___NS__unary, ___NS__unescape, ___NS__union, ___NS__unionBy, ___NS__unionWith, ___NS__uniq, ___NS__uniqBy, ___NS__uniqueId, ___NS__uniqWith, ___NS__unset, ___NS__unzip, ___NS__unzipWith, ___NS__update, ___NS__updateWith, ___NS__upperCase, ___NS__upperFirst, ___NS__values, ___NS__valuesIn, ___NS__without, ___NS__words, ___NS__wrap, ___NS__xor, ___NS__xorBy, ___NS__xorWith, ___NS__zip, ___NS__zipObject, ___NS__zipObjectDeep, ___NS__zipWith, UtilsOs__NS__commandExistsAsync, UtilsOs__NS__commandExistsSync, UtilsOs__NS__detectEditor, UtilsOs__NS__Editor, UtilsOs__NS__EDITOR_PROCESSES, UtilsOs__NS__EditorArr, UtilsOs__NS__EditorProcess, UtilsOs__NS__getEditorSettingsJsonPath, UtilsOs__NS__getRealHomeDir, UtilsOs__NS__isBrowser, UtilsOs__NS__isDockerAvailable, UtilsOs__NS__isElectron, UtilsOs__NS__isNode, UtilsOs__NS__isNodeVersionOk, UtilsOs__NS__isPortInUse, UtilsOs__NS__isRunningInBrowser, UtilsOs__NS__isRunningInCliMode, UtilsOs__NS__isRunningInDocker, UtilsOs__NS__isRunningInElectron, UtilsOs__NS__isRunningInLinuxGraphicsCapableEnvironment, UtilsOs__NS__isRunningInMochaTest, UtilsOs__NS__isRunningInNode, UtilsOs__NS__isRunningInOsWithGraphicsCapableEnvironment, UtilsOs__NS__isRunningInSSRMode, UtilsOs__NS__isRunningInVscodeExtension, UtilsOs__NS__isRunningInWebSQL, UtilsOs__NS__isRunningInWindows, UtilsOs__NS__isRunningInWindowsCmd, UtilsOs__NS__isRunningInWindowsPowerShell, UtilsOs__NS__isRunningInWsl, UtilsOs__NS__isRunningNodeDebugger, UtilsOs__NS__isSSRMode, UtilsOs__NS__isVscodeExtension, UtilsOs__NS__isWebSQL, UtilsOs__NS__killAllEditor, UtilsOs__NS__openFolderInFileExplorer, UtilsOs__NS__openFolderInVSCode, UtilsOs__NS__pipxNestedPackageExists, UtilsOs__NS__pipxPackageExists, UtilsOs__NS__pythonModuleExists, UtilsOs__NS__UnknownEditor } from 'tnp-core/lib-prod';
3
- export type StorageEngine = 'localstorage' | 'indexeddb' | 'file' | 'json';
4
-
5
- export interface StorOptions<T = any> {
6
- defaultValue?: T;
7
- transformFrom?: (valueFromDb: any) => T;
8
- transformTo?: (valueToDb: T) => any;
9
- }
10
-
11
- export interface PendingOperation {
12
- engine: StorageEngine;
13
- id: string;
14
- isDone: boolean;
15
- }
16
-
17
- type ClassLike = Function | { name: string } | undefined;
18
-
19
- const isBrowser =
20
- typeof window !== 'undefined' &&
21
- typeof document !== 'undefined' &&
22
- typeof navigator !== 'undefined';
23
-
24
- function safeLocationPort(): string {
25
- try {
26
- return (globalThis as any)?.location?.port || 'no-port';
27
- } catch {
28
- return 'no-port';
29
- }
30
- }
31
-
32
- /**
33
- * Keeps the spirit of your old `storeName = taon-storage_<port>`
34
- * plus project name namespacing (but without localForage).
35
- */
36
- export const storeName = `taon-storage_${safeLocationPort()}`;
37
-
38
- function defaultNamespace(): string {
39
- const project = ___NS__kebabCase(globalThis['CURRENT_PROJECT_GENERIC_NAME'] ?? '');
40
- return project ? `${storeName}_${project}` : storeName;
41
- }
42
-
43
- /**
44
- * Central config (optional).
45
- * You can set it once at app bootstrap if you want a stable namespace.
46
- */
47
- export const StorConfig = {
48
- namespace: defaultNamespace(),
49
- indexedDb: {
50
- dbName: `${defaultNamespace()}_INDEXEDDB`,
51
- storeName: 'keyvaluepairs',
52
- },
53
- };
54
-
55
- function normalizeScopeClass(cls: ClassLike): { name: string } {
56
- if (!cls) return { name: '__GLOBAL_NAMESPACE__' };
57
- // if it's a function/class
58
- if (typeof cls === 'function')
59
- return { name: (cls as any).name || '__ANON__' };
60
- // if it's already object with name
61
- return { name: (cls as any).name || '__ANON__' };
62
- }
63
-
64
- export function keyValue(scopeClass: ClassLike, memberName: string) {
65
- const c = normalizeScopeClass(scopeClass);
66
- return `${StorConfig.namespace}::taon.storage.class.${c.name}.prop.${memberName}`;
67
- }
68
-
69
- export function keyDefaultValueAlreadySet(
70
- scopeClass: ClassLike,
71
- memberName: string,
72
- ) {
73
- return `${keyValue(scopeClass, memberName)}::defaultvalueisset`;
74
- }
75
-
76
- /** Back-compat alias (your old typo) */
77
- export const keyDefaultValueAreadySet = keyDefaultValueAlreadySet;
78
-
79
- /* ---------------------------
80
- * Stores
81
- * -------------------------- */
82
-
83
- interface AsyncKVStore {
84
- getItem<T = any>(key: string): Promise<T | undefined>;
85
- setItem<T = any>(key: string, value: T): Promise<void>;
86
- removeItem(key: string): Promise<void>;
87
- }
88
-
89
- class NoopStore implements AsyncKVStore {
90
- async getItem<T>(_key: string): Promise<T | undefined> {
91
- return undefined;
92
- }
93
- async setItem<T>(_key: string, _value: T): Promise<void> {
94
- // noop
95
- }
96
- async removeItem(_key: string): Promise<void> {
97
- // noop
98
- }
99
- }
100
-
101
- class BrowserLocalStorageStore implements AsyncKVStore {
102
- private ls(): Storage | undefined {
103
- if (!isBrowser) return undefined;
104
- try {
105
- return window.localStorage;
106
- } catch {
107
- return undefined;
108
- }
109
- }
110
-
111
- async getItem<T>(key: string): Promise<T | undefined> {
112
- const ls = this.ls();
113
- if (!ls) return undefined;
114
-
115
- const raw = ls.getItem(key);
116
- if (raw === null) return undefined;
117
-
118
- try {
119
- return JSON.parse(raw) as T;
120
- } catch {
121
- // if something stored plain string by older versions
122
- return raw as unknown as T;
123
- }
124
- }
125
-
126
- async setItem<T>(key: string, value: T): Promise<void> {
127
- const ls = this.ls();
128
- if (!ls) return;
129
-
130
- try {
131
- ls.setItem(key, JSON.stringify(value));
132
- } catch {
133
- // last resort: try as string
134
- try {
135
- ls.setItem(key, String(value));
136
- } catch {
137
- // ignore (quota/private mode)
138
- }
139
- }
140
- }
141
-
142
- async removeItem(key: string): Promise<void> {
143
- const ls = this.ls();
144
- if (!ls) return;
145
- try {
146
- ls.removeItem(key);
147
- } catch {
148
- // ignore
149
- }
150
- }
151
- }
152
-
153
- class BrowserIndexedDbStore implements AsyncKVStore {
154
- private dbPromise: Promise<IDBDatabase> | null = null;
155
-
156
- private openDb(): Promise<IDBDatabase> {
157
- if (!isBrowser || !(window as any).indexedDB) {
158
- return Promise.reject(new Error('IndexedDB not available'));
159
- }
160
- if (this.dbPromise) return this.dbPromise;
161
-
162
- const { dbName, storeName } = StorConfig.indexedDb;
163
-
164
- this.dbPromise = new Promise<IDBDatabase>((resolve, reject) => {
165
- const req = indexedDB.open(dbName, 1);
166
-
167
- req.onupgradeneeded = () => {
168
- const db = req.result;
169
- if (!db.objectStoreNames.contains(storeName)) {
170
- db.createObjectStore(storeName);
171
- }
172
- };
173
-
174
- req.onsuccess = () => resolve(req.result);
175
- req.onerror = () => reject(req.error);
176
- });
177
-
178
- return this.dbPromise;
179
- }
180
-
181
- private async withStore<T>(
182
- mode: IDBTransactionMode,
183
- fn: (store: IDBObjectStore) => IDBRequest<T>,
184
- ): Promise<T> {
185
- const db = await this.openDb();
186
- const { storeName } = StorConfig.indexedDb;
187
-
188
- return await new Promise<T>((resolve, reject) => {
189
- const tx = db.transaction(storeName, mode);
190
- const store = tx.objectStore(storeName);
191
- const req = fn(store);
192
-
193
- req.onsuccess = () => resolve(req.result);
194
- req.onerror = () => reject(req.error);
195
-
196
- tx.onabort = () => reject(tx.error);
197
- // tx.oncomplete => nothing
198
- });
199
- }
200
-
201
- async getItem<T>(key: string): Promise<T | undefined> {
202
- try {
203
- const result = await this.withStore('readonly', s => s.get(key));
204
- return (result as any) === undefined ? undefined : (result as any);
205
- } catch {
206
- return undefined;
207
- }
208
- }
209
-
210
- async setItem<T>(key: string, value: T): Promise<void> {
211
- try {
212
- await this.withStore('readwrite', s => s.put(value as any, key));
213
- } catch {
214
- // ignore
215
- }
216
- }
217
-
218
- async removeItem(key: string): Promise<void> {
219
- try {
220
- await this.withStore('readwrite', s => s.delete(key) as any);
221
- } catch {
222
- // ignore
223
- }
224
- }
225
- }
226
-
227
- /**
228
- * Node-side file storage (optional). No top-level node imports (Angular-safe).
229
- * Works only when executed in Node.
230
- */
231
- class FileStor implements AsyncKVStore {
232
- constructor(
233
- private filePath: string,
234
- private useJSON = false,
235
- ) {}
236
-
237
- private isNodeRuntime() {
238
- return UtilsOs__NS__isNode;
239
- // return (
240
- // typeof process !== 'undefined' &&
241
- // !!(process as any).versions?.node &&
242
- // typeof (globalThis as any).window === 'undefined'
243
- // );
244
- }
245
-
246
- async setItem<T>(_key: string, value: T): Promise<void> {
247
- if (!this.isNodeRuntime()) return;
248
- //#region @backendFunc
249
- const fs = await import('node:fs/promises');
250
- const data = this.useJSON ? JSON.stringify(value, null, 2) : (value as any);
251
-
252
- if (this.useJSON) {
253
- await fs.writeFile(this.filePath, String(data), 'utf8');
254
- } else {
255
- await fs.writeFile(this.filePath, String(data), 'utf8');
256
- }
257
- //#endregion
258
- }
259
-
260
- async getItem<T>(_key: string): Promise<T | undefined> {
261
- if (!this.isNodeRuntime()) return undefined;
262
- //#region @backendFunc
263
- const fs = await import('node:fs/promises');
264
-
265
- try {
266
- const buf = await fs.readFile(this.filePath, 'utf8');
267
- if (!this.useJSON) return buf as any as T;
268
- return JSON.parse(buf) as T;
269
- } catch {
270
- return undefined;
271
- }
272
- //#endregion
273
- }
274
-
275
- async removeItem(_key: string): Promise<void> {
276
- if (!this.isNodeRuntime()) return;
277
- //#region @backendFunc
278
- const fs = await import('node:fs/promises');
279
- try {
280
- await fs.rm(this.filePath, { force: true });
281
- } catch {
282
- // ignore
283
- }
284
- //#endregion
285
- }
286
- }
287
-
288
- /* ---------------------------
289
- * Pending ops (so you can still await)
290
- * -------------------------- */
291
-
292
- class StorPending {
293
- static pending: PendingOperation[] = [];
294
- static id = 0;
295
- static AWAITING_INTERVAL_TIME = 200;
296
-
297
- static async awaitPendingOperations(id = StorPending.id++): Promise<void> {
298
- if (id > Number.MAX_SAFE_INTEGER - 2) {
299
- StorPending.id = 0;
300
- id = StorPending.id++;
301
- }
302
-
303
- const pending = StorPending.pending;
304
-
305
- for (const op of pending) {
306
- if (!op.isDone) {
307
- await new Promise<void>(resolve => {
308
- setTimeout(async () => {
309
- await StorPending.awaitPendingOperations(id);
310
- resolve();
311
- }, StorPending.AWAITING_INTERVAL_TIME);
312
- });
313
- return;
314
- }
315
- }
316
-
317
- // cleanup
318
- StorPending.pending = pending.filter(p => !p.isDone);
319
- }
320
-
321
- static start(engine: StorageEngine, id: string): PendingOperation {
322
- const op: PendingOperation = { engine, id, isDone: false };
323
- StorPending.pending.push(op);
324
- return op;
325
- }
326
-
327
- static done(op: PendingOperation) {
328
- op.isDone = true;
329
- }
330
- }
331
-
332
- /* ---------------------------
333
- * Decorator builder
334
- * -------------------------- */
335
-
336
- class StorPropertyBuilder {
337
- private scopeClass?: ClassLike;
338
- private engine: StorageEngine;
339
- private store: AsyncKVStore;
340
- private filePath?: string;
341
- private useJsonFile = false;
342
-
343
- constructor(engine: StorageEngine, store: AsyncKVStore) {
344
- this.engine = engine;
345
- this.store = store;
346
- }
347
-
348
- for(scopeClass?: ClassLike) {
349
- this.scopeClass = scopeClass;
350
- return this;
351
- }
352
-
353
- withDefaultValue<T = any>(defaultValue: T) {
354
- return this.withOptions<T>({ defaultValue });
355
- }
356
-
357
- withOptions<T = any>(options: StorOptions<T>) {
358
- const scopeClass = this.scopeClass;
359
-
360
- // per-instance state (fixes prototype-closure sharing)
361
- const values = new WeakMap<object, T>();
362
- const initStarted = new WeakMap<object, Promise<void>>();
363
-
364
- const ensureInit = (instance: object) => {
365
- if (initStarted.has(instance)) return;
366
-
367
- const op = StorPending.start(this.engine, 'init');
368
- const p = (async () => {
369
- const memberName = (ensureInit as any).__memberName as string;
370
-
371
- const kVal = keyValue(scopeClass, memberName);
372
- const kDef = keyDefaultValueAlreadySet(scopeClass, memberName);
373
-
374
- const defProvided = options.defaultValue !== undefined;
375
-
376
- if (
377
- !isBrowser &&
378
- (this.engine === 'localstorage' || this.engine === 'indexeddb')
379
- ) {
380
- // SSR: just set defaults, no storage
381
- if (defProvided) values.set(instance, options.defaultValue as T);
382
- return;
383
- }
384
-
385
- // Browser (or node file/json)
386
- if (defProvided) {
387
- const already = await this.store.getItem<boolean>(kDef);
388
- if (already) {
389
- const stored = await this.store.getItem<any>(kVal);
390
- const v = options.transformFrom
391
- ? options.transformFrom(stored)
392
- : stored;
393
- if (v !== undefined) values.set(instance, v as T);
394
- else values.set(instance, options.defaultValue as T);
395
- } else {
396
- await this.store.setItem(kDef, true);
397
- const toDb = options.transformTo
398
- ? options.transformTo(options.defaultValue as T)
399
- : (options.defaultValue as any);
400
- await this.store.setItem(kVal, toDb);
401
- values.set(instance, options.defaultValue as T);
402
- }
403
- } else {
404
- const stored = await this.store.getItem<any>(kVal);
405
- const v = options.transformFrom
406
- ? options.transformFrom(stored)
407
- : stored;
408
- if (v !== undefined) values.set(instance, v as T);
409
- }
410
- })()
411
- .catch(() => {
412
- // swallow, keep app alive
413
- })
414
- .finally(() => StorPending.done(op));
415
-
416
- initStarted.set(instance, p);
417
- };
418
-
419
- return (target: any, memberName: string) => {
420
- (ensureInit as any).__memberName = memberName;
421
-
422
- Object.defineProperty(target, memberName, {
423
- configurable: true,
424
- enumerable: true,
425
- get: function () {
426
- ensureInit(this);
427
-
428
- if (values.has(this)) return values.get(this);
429
- if (options.defaultValue !== undefined) return options.defaultValue;
430
- return undefined;
431
- },
432
- set: function (newValue: T) {
433
- values.set(this, newValue);
434
-
435
- // if this is the first interaction, init will happen anyway
436
- ensureInit(this);
437
-
438
- const op = StorPending.start(
439
- (target as any)?.engine ?? 'localstorage',
440
- 'set',
441
- );
442
-
443
- const scope = scopeClass;
444
- const kVal = keyValue(scope, memberName);
445
-
446
- const toDb = options.transformTo
447
- ? options.transformTo(newValue)
448
- : (newValue as any);
449
-
450
- Promise.resolve()
451
- .then(() => target as any)
452
- .then(() => this as any)
453
- .then(() => this as any)
454
- .then(async () => {
455
- // If we are SSR + browser engine => no-op
456
- if (!isBrowser && (options as any)) return;
457
- await (options as any); // no-op line to keep TS happy about chaining in some builds
458
- })
459
- .catch(() => {
460
- // ignore
461
- });
462
-
463
- // do real store write (async)
464
- Promise.resolve()
465
- .then(async () => {
466
- // SSR guard for browser engines
467
- if (!isBrowser && (StorPropertyInLocalStorage as any)) {
468
- return;
469
- }
470
- await thisStoreForEngineWrite(this, kVal, toDb);
471
- })
472
- .catch(() => {
473
- // ignore
474
- })
475
- .finally(() => StorPending.done(op));
476
- },
477
- });
478
-
479
- // small helper to keep closure clean
480
- const builderStore = this.store;
481
- const builderEngine = this.engine;
482
-
483
- async function thisStoreForEngineWrite(
484
- _instance: any,
485
- key: string,
486
- value: any,
487
- ) {
488
- // If browser engines but not browser, skip.
489
- if (
490
- !isBrowser &&
491
- (builderEngine === 'localstorage' || builderEngine === 'indexeddb')
492
- )
493
- return;
494
- await builderStore.setItem(key, value);
495
- }
496
- };
497
- }
498
-
499
- /* optional node-only engines (same builder) */
500
- file(filePath: string) {
501
- this.engine = 'file';
502
- this.filePath = filePath;
503
- this.useJsonFile = false;
504
- this.store = new FileStor(filePath, false);
505
- return this;
506
- }
507
-
508
- jsonFile(filePath: string) {
509
- this.engine = 'json';
510
- this.filePath = filePath;
511
- this.useJsonFile = true;
512
- this.store = new FileStor(filePath, true);
513
- return this;
514
- }
515
- }
516
-
517
- /* ---------------------------
518
- * Public: clean API exports
519
- * -------------------------- */
520
-
521
- const localStorageStore: AsyncKVStore = isBrowser
522
- ? new BrowserLocalStorageStore()
523
- : new NoopStore();
524
- const indexedDbStore: AsyncKVStore = isBrowser
525
- ? new BrowserIndexedDbStore()
526
- : new NoopStore();
527
-
528
- export class StorPropertyInLocalStorage {
529
- static for(scopeClass?: ClassLike) {
530
- return new StorPropertyBuilder('localstorage', localStorageStore).for(
531
- scopeClass,
532
- );
533
- }
534
- }
535
-
536
- export class StorPropertyInIndexedDb {
537
- static for(scopeClass?: ClassLike) {
538
- return new StorPropertyBuilder('indexeddb', indexedDbStore).for(scopeClass);
539
- }
540
- }
541
-
542
- /**
543
- * Helpers
544
- */
545
- export async function uncache<CLASS_FUNCTION = any>(
546
- onlyInThisComponentClass: CLASS_FUNCTION,
547
- propertyValueToDeleteFromCache: keyof CLASS_FUNCTION,
548
- ) {
549
- const scope =
550
- onlyInThisComponentClass || ({ name: '__GLOBAL_NAMESPACE__' } as any);
551
- const prop = String(propertyValueToDeleteFromCache);
552
-
553
- await Promise.all([
554
- localStorageStore.removeItem(keyValue(scope as any, prop)),
555
- localStorageStore.removeItem(keyDefaultValueAlreadySet(scope as any, prop)),
556
- indexedDbStore.removeItem(keyValue(scope as any, prop)),
557
- indexedDbStore.removeItem(keyDefaultValueAlreadySet(scope as any, prop)),
558
- ]);
559
- }
560
-
561
- /**
562
- * Backwards-compatible facade:
563
- * Stor.property.in.localstorage.for(...).withDefaultValue(...)
564
- */
565
- class TaonStorageFacade {
566
- static async awaitPendingOperatios() {
567
- await StorPending.awaitPendingOperations();
568
- }
569
-
570
- static get property() {
571
- return {
572
- in: {
573
- get localstorage() {
574
- return new StorPropertyBuilder('localstorage', localStorageStore);
575
- },
576
- get indexedb() {
577
- return new StorPropertyBuilder('indexeddb', indexedDbStore);
578
- },
579
- // node-only (safe: dynamic import inside FileStor)
580
- file(filePath: string) {
581
- return new StorPropertyBuilder('file', new FileStor(filePath, false));
582
- },
583
- jsonFile(filePath: string) {
584
- return new StorPropertyBuilder('json', new FileStor(filePath, true));
585
- },
586
- },
587
- };
588
- }
589
- }
590
-
591
- export const Stor = TaonStorageFacade;
File without changes