@rapidrest/core 2.0.0 → 3.0.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.
@@ -15,8 +15,7 @@ import { Destroy } from "./decorators/ObjectDecorators.js";
15
15
  /** How often the sweep interval reclaims expired, never-reloaded sessions. */
16
16
  const SWEEP_INTERVAL_MS = 60000;
17
17
  /**
18
- * Implements a simple key-value storage system in-memory for storing temporary records with a specified
19
- * lifetime (TTL) and size.
18
+ * Implements a SimpleStore storage system that uses a local in-memory storage map.
20
19
  *
21
20
  * @author Jean-Philippe Steinmetz
22
21
  */
@@ -46,7 +45,7 @@ export class MemoryStore {
46
45
  }
47
46
  return entry.data;
48
47
  }
49
- save(id, data, ttlSeconds = this.defaultTTL) {
48
+ save(id, data, ttl = this.defaultTTL) {
50
49
  if (!this.entries.has(id) && this.entries.size >= this.maxSize) {
51
50
  // Reclaim space by sweeping expired entries first, then evicting the oldest surviving entries
52
51
  this.sweep();
@@ -59,7 +58,7 @@ export class MemoryStore {
59
58
  // at its original (oldest) position and `evictOldest()` would evict it ahead of a genuinely idle entry
60
59
  // inserted later.
61
60
  this.entries.delete(id);
62
- this.entries.set(id, { data, expiresAt: Date.now() + ttlSeconds * 1000 });
61
+ this.entries.set(id, { data, expiresAt: Date.now() + ttl * 1000 });
63
62
  }
64
63
  delete(id) {
65
64
  this.entries.delete(id);
@@ -1 +1 @@
1
- {"version":3,"file":"MemoryStore.js","sourceRoot":"","sources":["../../src/MemoryStore.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAE3D,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,KAAM,CAAC;AAOjC;;;;;GAKG;AACH,MAAM,OAAO,WAAW;IAWpB;QAVA,2CAA2C;QACpC,eAAU,GAAW,EAAE,CAAC;QAErB,YAAO,GAAkC,IAAI,GAAG,EAAE,CAAC;QAE7D,8CAA8C;QACvC,YAAO,GAAW,KAAK,CAAC;QAK3B,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,iBAAiB,CAAC,CAAC;QACrE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,CAAC;IAED,4FAA4F;IAErF,OAAO;QACV,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAEM,IAAI,CAAC,EAAU;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,2CAA2C;QAC3C,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC;IACtB,CAAC;IAEM,IAAI,CAAC,EAAU,EAAE,IAAyB,EAAE,aAAqB,IAAI,CAAC,UAAU;QACnF,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7D,8FAA8F;YAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAChE,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzC,CAAC;QACL,CAAC;QACD,uGAAuG;QACvG,0GAA0G;QAC1G,uGAAuG;QACvG,kBAAkB;QAClB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAEM,MAAM,CAAC,EAAU;QACpB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAEO,KAAK;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACtD,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;CACJ;AA7CU;IADN,OAAO;;;;0CAGP"}
1
+ {"version":3,"file":"MemoryStore.js","sourceRoot":"","sources":["../../src/MemoryStore.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAG3D,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,KAAM,CAAC;AAEjC;;;;GAIG;AACH,MAAM,OAAO,WAAW;IAWpB;QAVA,2CAA2C;QACpC,eAAU,GAAW,EAAE,CAAC;QAErB,YAAO,GAAqC,IAAI,GAAG,EAAE,CAAC;QAEhE,8CAA8C;QACvC,YAAO,GAAW,KAAK,CAAC;QAK3B,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,iBAAiB,CAAC,CAAC;QACrE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC;IAC9B,CAAC;IAED,4FAA4F;IAErF,OAAO;QACV,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAEM,IAAI,CAAC,EAAU;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,2CAA2C;QAC3C,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC;IACtB,CAAC;IAEM,IAAI,CAAC,EAAU,EAAE,IAAuB,EAAE,MAAc,IAAI,CAAC,UAAU;QAC1E,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7D,8FAA8F;YAC9F,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAChE,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzC,CAAC;QACL,CAAC;QACD,uGAAuG;QACvG,0GAA0G;QAC1G,uGAAuG;QACvG,kBAAkB;QAClB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC;IAEM,MAAM,CAAC,EAAU;QACpB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAEO,KAAK;QACT,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACtD,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;CACJ;AA7CU;IADN,OAAO;;;;0CAGP"}
@@ -0,0 +1,129 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ ///////////////////////////////////////////////////////////////////////////////
11
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
+ ///////////////////////////////////////////////////////////////////////////////
13
+ import { CacheUtils } from "./CacheUtils.js";
14
+ import { Destroy } from "./decorators/ObjectDecorators.js";
15
+ /** How often the sweep interval reclaims expired, never-reloaded sessions. */
16
+ const SWEEP_INTERVAL_MS = 60000;
17
+ /**
18
+ * Implements a SimpleStore storage system that combines a local in-memory store optionally backed by Redis. When
19
+ * no Redis client is given, the store behaves as a plain in-memory store (equivalent to `MemoryStore`).
20
+ *
21
+ * @author Jean-Philippe Steinmetz
22
+ */
23
+ export class RedisStore {
24
+ /**
25
+ * @param client The Redis client to back this store with. When omitted, the store operates purely in-memory.
26
+ */
27
+ constructor(client) {
28
+ /** The default record TTL (in seconds). */
29
+ this.defaultTTL = 60;
30
+ this.entries = new Map();
31
+ /** The maximum number of records to store. */
32
+ this.maxSize = 10000;
33
+ this.client = client;
34
+ this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
35
+ this.sweepTimer.unref?.();
36
+ }
37
+ destroy() {
38
+ clearInterval(this.sweepTimer);
39
+ }
40
+ async load(id) {
41
+ const entry = this.entries.get(id);
42
+ if (entry) {
43
+ // If the local copy hasn't expired, it's safe to serve directly.
44
+ if (entry.expiresAt > Date.now()) {
45
+ return entry.data;
46
+ }
47
+ // The local copy has expired, but Redis is the source of truth: another process may have renewed
48
+ // the entry (longer TTL) since we cached it here. Drop the stale local copy and fall through to
49
+ // check Redis instead of reporting "not found" based on local state alone.
50
+ this.entries.delete(id);
51
+ }
52
+ // Without a Redis client, this is a pure in-memory store: a local miss/expiry is a real miss.
53
+ if (!this.client) {
54
+ return undefined;
55
+ }
56
+ // The entry wasn't found locally. Try retrieving from redis.
57
+ // We'll also grab the ttl so we can add it to our local map.
58
+ const result = await this.client
59
+ .multi([
60
+ ["get", id],
61
+ ["ttl", id],
62
+ ])
63
+ .exec();
64
+ if (!result) {
65
+ return undefined;
66
+ }
67
+ const data = result[0][1] ? result[0][1] : undefined;
68
+ const ttl = result[1][1] ?? undefined;
69
+ if (!data) {
70
+ return undefined;
71
+ }
72
+ // Store the entry in our in-memory map for faster retrieval in the future. Bound the local map the same
73
+ // way save() does — otherwise repeated load()s of distinct ids that already exist in Redis (a normal
74
+ // read path) would grow the local map without limit, regardless of maxSize.
75
+ if (!this.entries.has(id) && this.entries.size >= this.maxSize) {
76
+ this.sweep();
77
+ while (this.entries.size >= this.maxSize && this.entries.size > 0) {
78
+ CacheUtils.evictOldest(this.entries);
79
+ }
80
+ }
81
+ const newEntry = {
82
+ data: JSON.parse(data),
83
+ expiresAt: Date.now() + ttl * 1000,
84
+ };
85
+ this.entries.set(id, newEntry);
86
+ return newEntry.data;
87
+ }
88
+ async save(id, data, ttl = this.defaultTTL) {
89
+ // Write to Redis first (when configured): if it fails (bad connection, etc.), the local cache is left
90
+ // untouched instead of reporting a "saved" value locally that never made it to the shared store other
91
+ // instances read from.
92
+ if (this.client) {
93
+ await this.client.setex(id, ttl, JSON.stringify(data));
94
+ }
95
+ if (!this.entries.has(id) && this.entries.size >= this.maxSize) {
96
+ // Reclaim space by sweeping expired entries first, then evicting the oldest surviving entries
97
+ this.sweep();
98
+ while (this.entries.size >= this.maxSize && this.entries.size > 0) {
99
+ CacheUtils.evictOldest(this.entries);
100
+ }
101
+ }
102
+ // Delete before re-inserting so a renewed entry moves to the end of the Map's iteration order. A `Map`
103
+ // does not reorder on `set()` of an already-present key, so without this, an actively-renewed entry stays
104
+ // at its original (oldest) position and `evictOldest()` would evict it ahead of a genuinely idle entry
105
+ // inserted later.
106
+ this.entries.delete(id);
107
+ this.entries.set(id, { data, expiresAt: Date.now() + ttl * 1000 });
108
+ }
109
+ async delete(id) {
110
+ this.entries.delete(id);
111
+ if (this.client) {
112
+ await this.client.del(id);
113
+ }
114
+ }
115
+ sweep() {
116
+ const now = Date.now();
117
+ for (const [sessionId, entry] of this.entries.entries()) {
118
+ if (entry.expiresAt <= now)
119
+ this.entries.delete(sessionId);
120
+ }
121
+ }
122
+ }
123
+ __decorate([
124
+ Destroy,
125
+ __metadata("design:type", Function),
126
+ __metadata("design:paramtypes", []),
127
+ __metadata("design:returntype", void 0)
128
+ ], RedisStore.prototype, "destroy", null);
129
+ //# sourceMappingURL=RedisStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedisStore.js","sourceRoot":"","sources":["../../src/RedisStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ ///////////////////////////////////////////////////////////////////////////////
4
+ export {};
5
+ //# sourceMappingURL=SimpleStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SimpleStore.js","sourceRoot":"","sources":["../../src/SimpleStore.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E"}
@@ -1,25 +1,21 @@
1
- export interface MemoryStoreEntry {
2
- data: Record<string, any>;
3
- expiresAt: number;
4
- }
1
+ import { MemoryStoreEntry, SimpleStore } from "./SimpleStore.js";
5
2
  /**
6
- * Implements a simple key-value storage system in-memory for storing temporary records with a specified
7
- * lifetime (TTL) and size.
3
+ * Implements a SimpleStore storage system that uses a local in-memory storage map.
8
4
  *
9
5
  * @author Jean-Philippe Steinmetz
10
6
  */
11
- export declare class MemoryStore {
7
+ export declare class MemoryStore<T> implements SimpleStore<T> {
12
8
  /** The default record TTL (in seconds). */
13
9
  defaultTTL: number;
14
- protected entries: Map<string, MemoryStoreEntry>;
10
+ protected entries: Map<string, MemoryStoreEntry<T>>;
15
11
  /** The maximum number of records to store. */
16
12
  maxSize: number;
17
13
  private sweepTimer;
18
14
  constructor();
19
15
  /** Stops the background sweep timer. Call when the owning `SessionManager` is destroyed. */
20
16
  destroy(): void;
21
- load(id: string): Record<string, any> | undefined;
22
- save(id: string, data: Record<string, any>, ttlSeconds?: number): void;
17
+ load(id: string): Record<string, T> | undefined;
18
+ save(id: string, data: Record<string, T>, ttl?: number): void;
23
19
  delete(id: string): void;
24
20
  private sweep;
25
21
  }
@@ -0,0 +1,26 @@
1
+ import { MemoryStoreEntry, SimpleStore } from "./SimpleStore.js";
2
+ import type * as ioredis from "ioredis";
3
+ /**
4
+ * Implements a SimpleStore storage system that combines a local in-memory store optionally backed by Redis. When
5
+ * no Redis client is given, the store behaves as a plain in-memory store (equivalent to `MemoryStore`).
6
+ *
7
+ * @author Jean-Philippe Steinmetz
8
+ */
9
+ export declare class RedisStore<T> implements SimpleStore<T> {
10
+ client?: ioredis.Redis;
11
+ /** The default record TTL (in seconds). */
12
+ defaultTTL: number;
13
+ protected entries: Map<string, MemoryStoreEntry<T>>;
14
+ /** The maximum number of records to store. */
15
+ maxSize: number;
16
+ private sweepTimer;
17
+ /**
18
+ * @param client The Redis client to back this store with. When omitted, the store operates purely in-memory.
19
+ */
20
+ constructor(client?: ioredis.Redis);
21
+ destroy(): void;
22
+ load(id: string): Promise<Record<string, T> | undefined>;
23
+ save(id: string, data: Record<string, T>, ttl?: number): Promise<void>;
24
+ delete(id: string): Promise<void>;
25
+ private sweep;
26
+ }
@@ -0,0 +1,33 @@
1
+ export interface MemoryStoreEntry<T> {
2
+ data: Record<string, T>;
3
+ expiresAt: number;
4
+ }
5
+ /**
6
+ * Defines an interface for a simple key-value storage system that stores temporary records with a specified
7
+ * lifetime (TTL) and size.
8
+ *
9
+ * @author Jean-Philippe Steinmetz
10
+ */
11
+ export interface SimpleStore<T> {
12
+ /** The default record TTL (in seconds). */
13
+ defaultTTL: number;
14
+ /** The maximum number of records to store. */
15
+ maxSize: number;
16
+ /**
17
+ * Deletes the record with the given id.
18
+ * @param id The id of the record to delete.
19
+ */
20
+ delete(id: string): Promise<void> | void;
21
+ /**
22
+ * Retrieves the record with the given id.
23
+ * @param id The id of the record to retrieve.
24
+ */
25
+ load(id: string): Promise<Record<string, T> | undefined> | Record<string, T> | undefined;
26
+ /**
27
+ * Stores the record with the given id for the specified TTL.
28
+ * @param id The id of the record to retrieve.
29
+ * @param data The record to store.
30
+ * @param ttl The number of seconds that the record will be stored.
31
+ */
32
+ save(id: string, data: Record<string, T>, ttl: number): Promise<void> | void;
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rapidrest/core",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "A collection of common utilities and core functionality for rapidly building RESTful applications.",
5
5
  "repository": "https://github.com/rapidrest/core.git",
6
6
  "author": "RapidREST <rapidrests@gmail.com>",
@@ -34,6 +34,7 @@
34
34
  },
35
35
  "peerDependencies": {
36
36
  "axios": "1.x",
37
+ "ioredis": "6.x",
37
38
  "reflect-metadata": "0.2.x",
38
39
  "winston": "3.x",
39
40
  "winston-transport": "4.x"
@@ -58,6 +59,7 @@
58
59
  "eslint-config-prettier": "^10.1.8",
59
60
  "eslint-plugin-import": "^2.32.0",
60
61
  "eslint-plugin-jsdoc": "^63.0.2",
62
+ "ioredis": "^6.0.0",
61
63
  "mkdirp": "^3.0.1",
62
64
  "nconf": "^0.13.0",
63
65
  "nock": "^14.0.15",