ngx-sp-auth 4.5.0 → 4.5.2
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/fesm2022/ngx-sp-auth.mjs
CHANGED
|
@@ -230,11 +230,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
|
|
|
230
230
|
args: [LIB_CUSTOM_ENVIRONMENT_SERVICE]
|
|
231
231
|
}] }] });
|
|
232
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Mecanismo simples de lock para evitar race conditions
|
|
235
|
+
* Garante que apenas uma operação crítica (init/delete) execute por vez
|
|
236
|
+
*/
|
|
237
|
+
class DatabaseLock {
|
|
238
|
+
constructor() {
|
|
239
|
+
this.isLocked = false;
|
|
240
|
+
this.queue = [];
|
|
241
|
+
}
|
|
242
|
+
async acquire(operation) {
|
|
243
|
+
while (this.isLocked) {
|
|
244
|
+
// Aguarda até que a lock seja liberada
|
|
245
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
246
|
+
}
|
|
247
|
+
this.isLocked = true;
|
|
248
|
+
try {
|
|
249
|
+
return await operation();
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
this.isLocked = false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
233
256
|
class IndexedDBService {
|
|
234
257
|
// #endregion ==========> PROPERTIES <==========
|
|
235
258
|
constructor(_customEnvironment) {
|
|
236
259
|
this._customEnvironment = _customEnvironment;
|
|
237
260
|
this._dbName = "Sp_Filtros_";
|
|
261
|
+
this._lock = new DatabaseLock();
|
|
262
|
+
this._isInitialized = false;
|
|
238
263
|
if (!window.indexedDB) {
|
|
239
264
|
alert("Seu navegador não suporta uma versão estável do IndexedDB. Salvamento de filtros em sessão não estará disponível.");
|
|
240
265
|
}
|
|
@@ -249,15 +274,16 @@ class IndexedDBService {
|
|
|
249
274
|
* @param value Valor a ser inserido
|
|
250
275
|
*/
|
|
251
276
|
async add(value) {
|
|
252
|
-
|
|
277
|
+
await this._ensureInitialized();
|
|
278
|
+
if (!this.request) {
|
|
279
|
+
throw new Error('Database not initialized. Call initializeDatabase() first.');
|
|
280
|
+
}
|
|
253
281
|
try {
|
|
254
|
-
await
|
|
282
|
+
await this.request.add('filters', value);
|
|
255
283
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
catch (e) { /* não faz nada */ }
|
|
284
|
+
catch (error) {
|
|
285
|
+
console.error('Error adding value to IndexedDB:', error);
|
|
286
|
+
throw error;
|
|
261
287
|
}
|
|
262
288
|
}
|
|
263
289
|
// #endregion ADD
|
|
@@ -265,20 +291,20 @@ class IndexedDBService {
|
|
|
265
291
|
/**
|
|
266
292
|
* Busca um valor na base dentro de um objectStore.
|
|
267
293
|
*
|
|
268
|
-
* @param storeName Nome do objectStore
|
|
269
294
|
* @param key Valor da chave única
|
|
270
295
|
* @returns Promise<> com o valor encontrado ou undefined se não encontrar
|
|
271
296
|
*/
|
|
272
297
|
async get(key) {
|
|
273
|
-
|
|
298
|
+
await this._ensureInitialized();
|
|
299
|
+
if (!this.request) {
|
|
300
|
+
throw new Error('Database not initialized. Call initializeDatabase() first.');
|
|
301
|
+
}
|
|
274
302
|
try {
|
|
275
|
-
return await
|
|
303
|
+
return await this.request.get('filters', key);
|
|
276
304
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
catch (e) { /* não faz nada */ }
|
|
305
|
+
catch (error) {
|
|
306
|
+
console.error('Error getting value from IndexedDB:', error);
|
|
307
|
+
throw error;
|
|
282
308
|
}
|
|
283
309
|
}
|
|
284
310
|
// #endregion GET
|
|
@@ -289,15 +315,16 @@ class IndexedDBService {
|
|
|
289
315
|
* @param value Valor atualizado
|
|
290
316
|
*/
|
|
291
317
|
async update(value) {
|
|
292
|
-
|
|
318
|
+
await this._ensureInitialized();
|
|
319
|
+
if (!this.request) {
|
|
320
|
+
throw new Error('Database not initialized. Call initializeDatabase() first.');
|
|
321
|
+
}
|
|
293
322
|
try {
|
|
294
|
-
await
|
|
323
|
+
await this.request.put('filters', value);
|
|
295
324
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
catch (e) { /* não faz nada */ }
|
|
325
|
+
catch (error) {
|
|
326
|
+
console.error('Error updating value in IndexedDB:', error);
|
|
327
|
+
throw error;
|
|
301
328
|
}
|
|
302
329
|
}
|
|
303
330
|
// #endregion UPDATE
|
|
@@ -308,82 +335,154 @@ class IndexedDBService {
|
|
|
308
335
|
* @param key Valor da chave única
|
|
309
336
|
*/
|
|
310
337
|
async delete(key) {
|
|
311
|
-
|
|
338
|
+
await this._ensureInitialized();
|
|
339
|
+
if (!this.request) {
|
|
340
|
+
throw new Error('Database not initialized. Call initializeDatabase() first.');
|
|
341
|
+
}
|
|
312
342
|
try {
|
|
313
|
-
await
|
|
343
|
+
await this.request.delete('filters', key);
|
|
314
344
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
catch (e) { /* não faz nada */ }
|
|
345
|
+
catch (error) {
|
|
346
|
+
console.error('Error deleting value from IndexedDB:', error);
|
|
347
|
+
throw error;
|
|
320
348
|
}
|
|
321
349
|
}
|
|
322
350
|
// #endregion DELETE
|
|
323
351
|
// #endregion ==========> ACTIONS <==========
|
|
324
352
|
// #region ==========> UTILS <==========
|
|
353
|
+
/**
|
|
354
|
+
* Garante que a database foi inicializada antes de usar qualquer operação.
|
|
355
|
+
* Se `initializeDatabase()` foi chamado mas ainda não completou, aguarda a promise.
|
|
356
|
+
* Previne race conditions ao tentar usar operações durante a inicialização.
|
|
357
|
+
*
|
|
358
|
+
* @private
|
|
359
|
+
*/
|
|
360
|
+
async _ensureInitialized() {
|
|
361
|
+
// Se já está inicializando, aguarda a promise existente
|
|
362
|
+
if (this._initPromise) {
|
|
363
|
+
await this._initPromise;
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
// Se já foi inicializado com sucesso, retorna imediatamente
|
|
367
|
+
if (this._isInitialized && this.request) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
// Se chegou aqui e não está inicializado, significa que initializeDatabase() não foi chamado
|
|
371
|
+
throw new Error('IndexedDB not initialized. Call initializeDatabase() and await it before using any operations.');
|
|
372
|
+
}
|
|
325
373
|
/**
|
|
326
374
|
* Inicializa as configurações iniciais do IndexedDB e já cria o objectStore que será utilizado caso não exista.
|
|
327
375
|
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
376
|
+
* ⚠️ IMPORTANTE: Deve ser chamado com `await` no ngOnInit() do seu componente, não no constructor!
|
|
377
|
+
*
|
|
378
|
+
* O object store que será criado terá sua chave inline na propriedade `key` e o índice será a propriedade `context`,
|
|
379
|
+
* portanto todos os valores que forem inseridos **DEVEM** ser um objeto com pelo menos a propriedade `key` e `context`.
|
|
330
380
|
*
|
|
331
|
-
* @
|
|
381
|
+
* @example
|
|
382
|
+
* async ngOnInit() {
|
|
383
|
+
* await this._indexedDB.initializeDatabase();
|
|
384
|
+
* const restored = await this._indexedDB.get('minha-chave');
|
|
385
|
+
* }
|
|
332
386
|
*/
|
|
333
387
|
async initializeDatabase() {
|
|
334
|
-
this.
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
console.warn('IndexedDB blocking — considere fechar esta conexão');
|
|
388
|
+
return this._lock.acquire(async () => {
|
|
389
|
+
// Se já está inicializado, não refaz
|
|
390
|
+
if (this._isInitialized && this.request) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
// Marca que está inicializando
|
|
394
|
+
const initPromise = this._performInitialization();
|
|
395
|
+
this._initPromise = initPromise;
|
|
396
|
+
try {
|
|
397
|
+
await initPromise;
|
|
398
|
+
this._isInitialized = true;
|
|
399
|
+
}
|
|
400
|
+
finally {
|
|
401
|
+
this._initPromise = undefined;
|
|
349
402
|
}
|
|
350
403
|
});
|
|
351
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* Executa a inicialização real da database.
|
|
407
|
+
* @private
|
|
408
|
+
*/
|
|
409
|
+
async _performInitialization() {
|
|
410
|
+
try {
|
|
411
|
+
this.request = await openDB(this._dbName, 1, {
|
|
412
|
+
upgrade(db) {
|
|
413
|
+
// Criar objectStore se não houver um mesmo com este nome
|
|
414
|
+
if (!db.objectStoreNames.contains('filters')) {
|
|
415
|
+
const store = db.createObjectStore('filters', { keyPath: 'key' });
|
|
416
|
+
store.createIndex('context', 'context');
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
blocked() {
|
|
420
|
+
console.warn('IndexedDB blocked — feche outras abas com esta aplicação');
|
|
421
|
+
},
|
|
422
|
+
blocking() {
|
|
423
|
+
console.warn('IndexedDB blocking — recarregue a página se persistir');
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
console.error('Error initializing IndexedDB:', error);
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
352
432
|
/**
|
|
353
433
|
* Exclui uma database do IndexedDB com base no nome.
|
|
354
434
|
*
|
|
355
|
-
*
|
|
435
|
+
* Deve ser chamado durante o logout para limpar dados do usuário.
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* await this._indexedDB.closeOpenConnection();
|
|
439
|
+
* await this._indexedDB.deleteDatabase();
|
|
356
440
|
*/
|
|
357
441
|
async deleteDatabase() {
|
|
358
|
-
|
|
359
|
-
|
|
442
|
+
return this._lock.acquire(async () => {
|
|
443
|
+
// Fecha a conexão persistente local, se existir, antes de tentar excluir a DB
|
|
444
|
+
await this._closeConnection();
|
|
360
445
|
try {
|
|
361
|
-
this.
|
|
446
|
+
await deleteDB(this._dbName);
|
|
447
|
+
this._isInitialized = false;
|
|
362
448
|
}
|
|
363
449
|
catch (err) {
|
|
364
|
-
console.warn('
|
|
450
|
+
console.warn('Error deleting IndexedDB:', err);
|
|
451
|
+
throw err;
|
|
365
452
|
}
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
return await deleteDB(this._dbName);
|
|
453
|
+
});
|
|
369
454
|
}
|
|
370
455
|
/**
|
|
371
456
|
* Fecha a conexão persistente (se existir) sem excluir a database.
|
|
372
457
|
* Útil para cenários onde se precisa liberar a conexão antes de chamar `deleteDatabase()`.
|
|
458
|
+
*
|
|
459
|
+
* @example
|
|
460
|
+
* await this._indexedDB.closeOpenConnection();
|
|
373
461
|
*/
|
|
374
462
|
async closeOpenConnection() {
|
|
463
|
+
return this._lock.acquire(async () => {
|
|
464
|
+
await this._closeConnection();
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Executa o fechamento real da conexão.
|
|
469
|
+
* @private
|
|
470
|
+
*/
|
|
471
|
+
async _closeConnection() {
|
|
375
472
|
if (this.request) {
|
|
376
473
|
try {
|
|
377
474
|
this.request.close();
|
|
378
475
|
}
|
|
379
476
|
catch (err) {
|
|
380
|
-
console.warn('
|
|
477
|
+
console.warn('Error closing IndexedDB connection:', err);
|
|
381
478
|
}
|
|
382
479
|
this.request = undefined;
|
|
480
|
+
this._isInitialized = false;
|
|
383
481
|
}
|
|
384
482
|
}
|
|
385
483
|
/**
|
|
386
|
-
* Valida se já existe um valor cadastrado na base com a chave-única que foi informada,
|
|
484
|
+
* Valida se já existe um valor cadastrado na base com a chave-única que foi informada,
|
|
485
|
+
* se houver retorna ele, caso contrário cria um registro placeholder para poder atualizar depois.
|
|
387
486
|
*
|
|
388
487
|
* @param key Valor da chave única
|
|
389
488
|
* @param value (opcional) Valor placeholder caso não exista um valor previamente criado
|
|
@@ -396,6 +495,7 @@ class IndexedDBService {
|
|
|
396
495
|
if (!this._restored && value) {
|
|
397
496
|
// Se não existir nada, inicializa um registro placeholder dentro do objectStore existente
|
|
398
497
|
await this.add(value);
|
|
498
|
+
return value?.payload;
|
|
399
499
|
}
|
|
400
500
|
else {
|
|
401
501
|
// Se encontrar valor retorna apenas o payload com os dados que serão usados na tela
|