@sebspark/promise-cache 3.7.0 → 3.9.0

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/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { RedisClientOptions, createClient, SetOptions } from 'redis';
1
+ import { createClient, RedisClientOptions, SetOptions } from 'redis';
2
2
  export { RedisClientOptions } from 'redis';
3
3
  import { Logger } from 'winston';
4
4
  import { UUID } from 'node:crypto';
@@ -353,7 +353,13 @@ interface IPersistor {
353
353
  * @returns Resolves to the number of elements removed.
354
354
  */
355
355
  zRem: (key: string, members: string | string[]) => Promise<number>;
356
+ isReady: boolean;
357
+ isOpen: boolean;
358
+ connect: () => Promise<IPersistor>;
359
+ once: (event: IPersistorEvent, cb: EventCallback) => IPersistor;
356
360
  }
361
+ type IPersistorEvent = 'ready' | 'connect' | 'reconnecting';
362
+ type EventCallback = () => void;
357
363
  /**
358
364
  * Interface for executing multiple storage commands in a batch operation.
359
365
  * All commands are queued and executed when `exec()` is called.
@@ -590,6 +596,10 @@ declare class InMemoryPersistor implements IPersistor {
590
596
  * Initializes an empty store, expiration map, and TTL tracker.
591
597
  */
592
598
  constructor();
599
+ connect(): Promise<this>;
600
+ get isReady(): boolean;
601
+ get isOpen(): boolean;
602
+ once(): this;
593
603
  /**
594
604
  * Stores a key-value pair with optional expiration settings.
595
605
  * If an expiration is provided (`EX`, `PX`, `EXAT`, `PXAT`), the key is automatically removed when TTL expires.
@@ -875,7 +885,7 @@ declare namespace time {
875
885
  }
876
886
 
877
887
  declare const serialize: <T>(data: T) => string;
878
- declare const deserialize: <T>(serialized: string) => T;
888
+ declare const deserialize: <T>(serialized: string | null) => T | null;
879
889
 
880
890
  declare const serializer_deserialize: typeof deserialize;
881
891
  declare const serializer_serialize: typeof serialize;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { RedisClientOptions, createClient, SetOptions } from 'redis';
1
+ import { createClient, RedisClientOptions, SetOptions } from 'redis';
2
2
  export { RedisClientOptions } from 'redis';
3
3
  import { Logger } from 'winston';
4
4
  import { UUID } from 'node:crypto';
@@ -353,7 +353,13 @@ interface IPersistor {
353
353
  * @returns Resolves to the number of elements removed.
354
354
  */
355
355
  zRem: (key: string, members: string | string[]) => Promise<number>;
356
+ isReady: boolean;
357
+ isOpen: boolean;
358
+ connect: () => Promise<IPersistor>;
359
+ once: (event: IPersistorEvent, cb: EventCallback) => IPersistor;
356
360
  }
361
+ type IPersistorEvent = 'ready' | 'connect' | 'reconnecting';
362
+ type EventCallback = () => void;
357
363
  /**
358
364
  * Interface for executing multiple storage commands in a batch operation.
359
365
  * All commands are queued and executed when `exec()` is called.
@@ -590,6 +596,10 @@ declare class InMemoryPersistor implements IPersistor {
590
596
  * Initializes an empty store, expiration map, and TTL tracker.
591
597
  */
592
598
  constructor();
599
+ connect(): Promise<this>;
600
+ get isReady(): boolean;
601
+ get isOpen(): boolean;
602
+ once(): this;
593
603
  /**
594
604
  * Stores a key-value pair with optional expiration settings.
595
605
  * If an expiration is provided (`EX`, `PX`, `EXAT`, `PXAT`), the key is automatically removed when TTL expires.
@@ -875,7 +885,7 @@ declare namespace time {
875
885
  }
876
886
 
877
887
  declare const serialize: <T>(data: T) => string;
878
- declare const deserialize: <T>(serialized: string) => T;
888
+ declare const deserialize: <T>(serialized: string | null) => T | null;
879
889
 
880
890
  declare const serializer_deserialize: typeof deserialize;
881
891
  declare const serializer_serialize: typeof serialize;
package/dist/index.js CHANGED
@@ -409,12 +409,14 @@ var createCache = (persistor, prefix) => {
409
409
  }
410
410
  const resultPromise = (async () => {
411
411
  try {
412
- const cached = await persistor.get(key);
412
+ await ensurePersistorIsReady(persistor);
413
+ const cached = deserialize(await persistor.get(key));
413
414
  if (cached !== null) {
414
- return deserialize(cached);
415
+ return cached;
415
416
  }
416
417
  const result = await delegate(...args);
417
418
  const expiry = typeof options.expiry === "function" ? options.expiry(args, result) : options.expiry;
419
+ await ensurePersistorIsReady(persistor);
418
420
  const serialized = serialize(result);
419
421
  const setOptions = toSetOptions(expiry);
420
422
  await persistor.set(key, serialized, setOptions);
@@ -430,6 +432,28 @@ var createCache = (persistor, prefix) => {
430
432
  };
431
433
  return cache;
432
434
  };
435
+ var ensurePersistorIsReady = async (persistor) => {
436
+ if (persistor.isReady) {
437
+ return;
438
+ }
439
+ if (persistor.isOpen) {
440
+ await new Promise((resolve) => {
441
+ if (persistor.isReady) return resolve();
442
+ persistor.once("ready", resolve);
443
+ return;
444
+ });
445
+ }
446
+ try {
447
+ await persistor.connect();
448
+ } catch (err) {
449
+ if (err.message === "Socket already opened") {
450
+ await new Promise((resolve) => {
451
+ if (persistor.isReady) return resolve();
452
+ persistor.once("ready", resolve);
453
+ });
454
+ }
455
+ }
456
+ };
433
457
 
434
458
  // src/inMemoryPersistor.ts
435
459
  var InMemoryPersistor = class {
@@ -459,6 +483,18 @@ var InMemoryPersistor = class {
459
483
  this.expirations = /* @__PURE__ */ new Map();
460
484
  this.expiryTimestamps = /* @__PURE__ */ new Map();
461
485
  }
486
+ async connect() {
487
+ return this;
488
+ }
489
+ get isReady() {
490
+ return true;
491
+ }
492
+ get isOpen() {
493
+ return true;
494
+ }
495
+ once() {
496
+ return this;
497
+ }
462
498
  /**
463
499
  * Stores a key-value pair with optional expiration settings.
464
500
  * If an expiration is provided (`EX`, `PX`, `EXAT`, `PXAT`), the key is automatically removed when TTL expires.
@@ -469,6 +505,12 @@ var InMemoryPersistor = class {
469
505
  * @returns {Promise<'OK' | null>} Resolves to `'OK'` on success, or `null` if a conditional set (`NX`) fails.
470
506
  */
471
507
  async set(key, value, options) {
508
+ if (options?.NX && this.store.has(key)) {
509
+ return null;
510
+ }
511
+ if (options?.XX && !this.store.has(key)) {
512
+ return null;
513
+ }
472
514
  this.store.set(key, value);
473
515
  if (options?.EX !== void 0) {
474
516
  this.setExpiration(key, options.EX * 1e3);
package/dist/index.mjs CHANGED
@@ -390,12 +390,14 @@ var createCache = (persistor, prefix) => {
390
390
  }
391
391
  const resultPromise = (async () => {
392
392
  try {
393
- const cached = await persistor.get(key);
393
+ await ensurePersistorIsReady(persistor);
394
+ const cached = deserialize(await persistor.get(key));
394
395
  if (cached !== null) {
395
- return deserialize(cached);
396
+ return cached;
396
397
  }
397
398
  const result = await delegate(...args);
398
399
  const expiry = typeof options.expiry === "function" ? options.expiry(args, result) : options.expiry;
400
+ await ensurePersistorIsReady(persistor);
399
401
  const serialized = serialize(result);
400
402
  const setOptions = toSetOptions(expiry);
401
403
  await persistor.set(key, serialized, setOptions);
@@ -411,6 +413,28 @@ var createCache = (persistor, prefix) => {
411
413
  };
412
414
  return cache;
413
415
  };
416
+ var ensurePersistorIsReady = async (persistor) => {
417
+ if (persistor.isReady) {
418
+ return;
419
+ }
420
+ if (persistor.isOpen) {
421
+ await new Promise((resolve) => {
422
+ if (persistor.isReady) return resolve();
423
+ persistor.once("ready", resolve);
424
+ return;
425
+ });
426
+ }
427
+ try {
428
+ await persistor.connect();
429
+ } catch (err) {
430
+ if (err.message === "Socket already opened") {
431
+ await new Promise((resolve) => {
432
+ if (persistor.isReady) return resolve();
433
+ persistor.once("ready", resolve);
434
+ });
435
+ }
436
+ }
437
+ };
414
438
 
415
439
  // src/inMemoryPersistor.ts
416
440
  var InMemoryPersistor = class {
@@ -440,6 +464,18 @@ var InMemoryPersistor = class {
440
464
  this.expirations = /* @__PURE__ */ new Map();
441
465
  this.expiryTimestamps = /* @__PURE__ */ new Map();
442
466
  }
467
+ async connect() {
468
+ return this;
469
+ }
470
+ get isReady() {
471
+ return true;
472
+ }
473
+ get isOpen() {
474
+ return true;
475
+ }
476
+ once() {
477
+ return this;
478
+ }
443
479
  /**
444
480
  * Stores a key-value pair with optional expiration settings.
445
481
  * If an expiration is provided (`EX`, `PX`, `EXAT`, `PXAT`), the key is automatically removed when TTL expires.
@@ -450,6 +486,12 @@ var InMemoryPersistor = class {
450
486
  * @returns {Promise<'OK' | null>} Resolves to `'OK'` on success, or `null` if a conditional set (`NX`) fails.
451
487
  */
452
488
  async set(key, value, options) {
489
+ if (options?.NX && this.store.has(key)) {
490
+ return null;
491
+ }
492
+ if (options?.XX && !this.store.has(key)) {
493
+ return null;
494
+ }
453
495
  this.store.set(key, value);
454
496
  if (options?.EX !== void 0) {
455
497
  this.setExpiration(key, options.EX * 1e3);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sebspark/promise-cache",
3
- "version": "3.7.0",
3
+ "version": "3.9.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -17,8 +17,8 @@
17
17
  "typecheck": "vitest --typecheck.only --passWithNoTests"
18
18
  },
19
19
  "devDependencies": {
20
- "@testcontainers/redis": "^10.17.2",
21
- "testcontainers": "^10.17.2",
20
+ "@testcontainers/redis": "^10.18.0",
21
+ "testcontainers": "^10.18.0",
22
22
  "tsconfig": "*"
23
23
  },
24
24
  "dependencies": {