@bymax-one/nest-cache 1.2.5 → 1.2.7
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/CHANGELOG.md +43 -1
- package/README.md +39 -0
- package/dist/server/index.cjs +32 -14
- package/dist/server/index.d.cts +43 -1
- package/dist/server/index.d.ts +43 -1
- package/dist/server/index.mjs +32 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,46 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
### Tests
|
|
10
|
+
|
|
11
|
+
- `src/readme-quickstart.spec.ts` runs the README Quick Start as written: the standalone module and the
|
|
12
|
+
`ProfileService` cache-aside flow, over an in-memory Redis, asserting that the value is stored under
|
|
13
|
+
`app:user-profile:<userId>` with a 3600-second TTL and served from the cache on the second call. The
|
|
14
|
+
snippet gate compiles the Quick Start but cannot run it.
|
|
15
|
+
|
|
16
|
+
## [1.2.7] - 2026-11-01
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- A thrown or rejected value that cannot be printed no longer makes the error path throw. Caught values
|
|
21
|
+
were turned into text with `err instanceof Error ? err.message : String(err)`, and
|
|
22
|
+
`String()` throws for an object with no prototype (`Object.create(null)`) or a `toString` that throws.
|
|
23
|
+
A `toJSON` hook that threw such a value escaped `JsonSerializer.serialize` as a `TypeError` instead of
|
|
24
|
+
`cache.serialization_failed`; a Redis script rejection of that kind escaped `ScriptManagerService.eval`
|
|
25
|
+
as a `TypeError` instead of `cache.script_execution_failed`; and a Pub/Sub handler that threw one was
|
|
26
|
+
never reported to `onEvent`, because the reporter threw inside its own guard.
|
|
27
|
+
- Those sites now share a `describeError` helper that never throws. It reads a string `message`
|
|
28
|
+
from the value itself instead of testing `instanceof Error`, which is false for an `Error` created in
|
|
29
|
+
another realm (`vm`, a worker) and for an error-shaped object; otherwise it uses `String()`, and
|
|
30
|
+
falls back to the fixed text `unprintable error`.
|
|
31
|
+
|
|
32
|
+
## [1.2.6] - 2026-10-31
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **`createConnectionManager(options)`**, exported from the main entry. It takes the options
|
|
37
|
+
`BymaxCacheModule.forRoot` takes, validates and defaults them exactly as the module does, and returns a
|
|
38
|
+
`ConnectionManager` that Nest connects on `onModuleInit` and quits on `onModuleDestroy` when it is
|
|
39
|
+
registered as a provider. A consumer that needs a second, dedicated connection (for example a standalone
|
|
40
|
+
one beside a clustered cache) no longer has to hand-write `ResolvedOptions`, which skipped validation and
|
|
41
|
+
the defaults. The README has a section, "A second, dedicated connection", with a provider snippet.
|
|
42
|
+
|
|
43
|
+
### Internal
|
|
44
|
+
|
|
45
|
+
- `forRoot`, `forRootAsync` and `createConnectionManager` share one `resolveOptions` function, so the
|
|
46
|
+
sequence "validate, then apply defaults" is written once. Nothing about `forRoot` or `forRootAsync`
|
|
47
|
+
changes.
|
|
48
|
+
|
|
9
49
|
## [1.2.5] - 2026-10-31
|
|
10
50
|
|
|
11
51
|
A documentation and test release. Nothing you execute changes: `dist/` differs from 1.2.4 only in one comment, the
|
|
@@ -479,7 +519,9 @@ type or export moved.
|
|
|
479
519
|
- Published with npm OIDC provenance — no long-lived tokens
|
|
480
520
|
- Zero direct runtime dependencies (`dependencies: {}`) — `ioredis` and NestJS via peer deps
|
|
481
521
|
|
|
482
|
-
[Unreleased]: https://github.com/bymaxone/nest-cache/compare/v1.2.
|
|
522
|
+
[Unreleased]: https://github.com/bymaxone/nest-cache/compare/v1.2.7...HEAD
|
|
523
|
+
[1.2.7]: https://github.com/bymaxone/nest-cache/compare/v1.2.6...v1.2.7
|
|
524
|
+
[1.2.6]: https://github.com/bymaxone/nest-cache/compare/v1.2.5...v1.2.6
|
|
483
525
|
[1.2.5]: https://github.com/bymaxone/nest-cache/compare/v1.2.4...v1.2.5
|
|
484
526
|
[1.2.4]: https://github.com/bymaxone/nest-cache/compare/v1.2.3...v1.2.4
|
|
485
527
|
[1.2.3]: https://github.com/bymaxone/nest-cache/compare/v1.2.2...v1.2.3
|
package/README.md
CHANGED
|
@@ -279,6 +279,45 @@ Both `forRoot(options)` (synchronous) and `forRootAsync({ useFactory, inject, im
|
|
|
279
279
|
|
|
280
280
|
---
|
|
281
281
|
|
|
282
|
+
## 🔌 A second, dedicated connection
|
|
283
|
+
|
|
284
|
+
`BymaxCacheModule` registers one connection. When another part of the application needs its own, with different options (for example a standalone connection beside a cache that runs in cluster mode), build it with `createConnectionManager` rather than hand-writing the resolved options, which would skip validation and the defaults.
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
import { Inject, Injectable, Module } from '@nestjs/common'
|
|
288
|
+
import { ConnectionManager, createConnectionManager } from '@bymax-one/nest-cache'
|
|
289
|
+
|
|
290
|
+
export const AUTH_REDIS = Symbol('AUTH_REDIS')
|
|
291
|
+
|
|
292
|
+
@Module({
|
|
293
|
+
providers: [
|
|
294
|
+
{
|
|
295
|
+
provide: AUTH_REDIS,
|
|
296
|
+
useFactory: (): ConnectionManager =>
|
|
297
|
+
createConnectionManager({
|
|
298
|
+
mode: 'standalone',
|
|
299
|
+
connection: { url: process.env.AUTH_REDIS_URL ?? 'redis://localhost:6379' }
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
],
|
|
303
|
+
exports: [AUTH_REDIS]
|
|
304
|
+
})
|
|
305
|
+
export class AuthRedisModule {}
|
|
306
|
+
|
|
307
|
+
@Injectable()
|
|
308
|
+
export class SessionStore {
|
|
309
|
+
constructor(@Inject(AUTH_REDIS) private readonly redis: ConnectionManager) {}
|
|
310
|
+
|
|
311
|
+
async ping(): Promise<string> {
|
|
312
|
+
return this.redis.getClient().ping()
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
It takes the same options as `forRoot()` and runs the same validation and defaults, so an invalid set throws the same `CacheException` before any client exists. The manager owns its connection: Nest connects it on `onModuleInit` and quits it on `onModuleDestroy`, with the forced `disconnect()` after `shutdownTimeoutMs`, so you write neither hook. The connection is independent of the one the module registers, and the client you get from `getClient()` applies no namespace: its keys are not prefixed.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
282
321
|
## 🔑 Key Namespacing
|
|
283
322
|
|
|
284
323
|
Every key is composed as `{namespace}{separator}{prefix}{separator}{id}` (default separator `:`).
|
package/dist/server/index.cjs
CHANGED
|
@@ -290,6 +290,10 @@ function applyDefaults(options) {
|
|
|
290
290
|
}
|
|
291
291
|
return Object.freeze(resolved);
|
|
292
292
|
}
|
|
293
|
+
function resolveOptions(options) {
|
|
294
|
+
validateOptions(options);
|
|
295
|
+
return applyDefaults(options);
|
|
296
|
+
}
|
|
293
297
|
var _client, _redisOptionsResolved;
|
|
294
298
|
exports.ConnectionManager = class ConnectionManager {
|
|
295
299
|
/**
|
|
@@ -523,9 +527,21 @@ exports.ConnectionManager = __decorateClass([
|
|
|
523
527
|
__decorateParam(1, common.Optional()),
|
|
524
528
|
__decorateParam(1, common.Inject(BYMAX_CACHE_EVENTS))
|
|
525
529
|
], exports.ConnectionManager);
|
|
530
|
+
|
|
531
|
+
// src/server/utils/describe-error.ts
|
|
532
|
+
var UNPRINTABLE = "unprintable error";
|
|
533
|
+
function describeError(reason) {
|
|
534
|
+
try {
|
|
535
|
+
const message = reason?.message;
|
|
536
|
+
return typeof message === "string" ? message : String(reason);
|
|
537
|
+
} catch {
|
|
538
|
+
return UNPRINTABLE;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/server/services/script-manager.service.ts
|
|
526
543
|
var SCRIPT_LOAD = "LOAD";
|
|
527
544
|
var NOSCRIPT_MARKER = "NOSCRIPT";
|
|
528
|
-
var toErrorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
529
545
|
exports.ScriptManagerService = class ScriptManagerService {
|
|
530
546
|
/**
|
|
531
547
|
* @param options - Resolved module options; `options.scripts` seeds the registry.
|
|
@@ -633,7 +649,7 @@ exports.ScriptManagerService = class ScriptManagerService {
|
|
|
633
649
|
} catch (err) {
|
|
634
650
|
throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
|
|
635
651
|
name,
|
|
636
|
-
originalError:
|
|
652
|
+
originalError: describeError(err)
|
|
637
653
|
});
|
|
638
654
|
}
|
|
639
655
|
}
|
|
@@ -641,10 +657,10 @@ exports.ScriptManagerService = class ScriptManagerService {
|
|
|
641
657
|
const sha = entry.sha ?? await this.load(name);
|
|
642
658
|
return await client.evalsha(sha, keys.length, ...keys, ...args);
|
|
643
659
|
} catch (err) {
|
|
644
|
-
if (!
|
|
660
|
+
if (!describeError(err).includes(NOSCRIPT_MARKER)) {
|
|
645
661
|
throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
|
|
646
662
|
name,
|
|
647
|
-
originalError:
|
|
663
|
+
originalError: describeError(err)
|
|
648
664
|
});
|
|
649
665
|
}
|
|
650
666
|
try {
|
|
@@ -654,7 +670,7 @@ exports.ScriptManagerService = class ScriptManagerService {
|
|
|
654
670
|
} catch (retryErr) {
|
|
655
671
|
throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
|
|
656
672
|
name,
|
|
657
|
-
originalError:
|
|
673
|
+
originalError: describeError(retryErr)
|
|
658
674
|
});
|
|
659
675
|
}
|
|
660
676
|
}
|
|
@@ -719,7 +735,6 @@ exports.KeyBuilder = __decorateClass([
|
|
|
719
735
|
__decorateParam(0, common.Inject(BYMAX_CACHE_OPTIONS))
|
|
720
736
|
], exports.KeyBuilder);
|
|
721
737
|
var DESERIALIZATION_ERROR = "stored value is not valid JSON";
|
|
722
|
-
var extractErrorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
723
738
|
exports.JsonSerializer = class JsonSerializer {
|
|
724
739
|
/**
|
|
725
740
|
* Encodes a value as a JSON string.
|
|
@@ -745,7 +760,7 @@ exports.JsonSerializer = class JsonSerializer {
|
|
|
745
760
|
return JSON.stringify(value);
|
|
746
761
|
} catch (err) {
|
|
747
762
|
throw new CacheException(CACHE_ERROR_CODES.SERIALIZATION_FAILED, {
|
|
748
|
-
error:
|
|
763
|
+
error: describeError(err)
|
|
749
764
|
});
|
|
750
765
|
}
|
|
751
766
|
}
|
|
@@ -1618,7 +1633,7 @@ exports.PubSubService = class PubSubService {
|
|
|
1618
1633
|
role: "subscriber",
|
|
1619
1634
|
reason: "handler_error",
|
|
1620
1635
|
channel,
|
|
1621
|
-
error:
|
|
1636
|
+
error: describeError(error)
|
|
1622
1637
|
});
|
|
1623
1638
|
} catch {
|
|
1624
1639
|
}
|
|
@@ -1647,8 +1662,7 @@ exports.BymaxCacheModule = class BymaxCacheModule extends ConfigurableModuleClas
|
|
|
1647
1662
|
* fail bootstrap validation (e.g. missing connection, misconfigured mode).
|
|
1648
1663
|
*/
|
|
1649
1664
|
static forRoot(options) {
|
|
1650
|
-
|
|
1651
|
-
const resolved = applyDefaults(options);
|
|
1665
|
+
const resolved = resolveOptions(options);
|
|
1652
1666
|
const providers = [
|
|
1653
1667
|
{ provide: MODULE_OPTIONS_TOKEN, useValue: options },
|
|
1654
1668
|
{ provide: BYMAX_CACHE_OPTIONS, useValue: resolved },
|
|
@@ -1705,10 +1719,7 @@ exports.BymaxCacheModule = class BymaxCacheModule extends ConfigurableModuleClas
|
|
|
1705
1719
|
return [
|
|
1706
1720
|
{
|
|
1707
1721
|
provide: BYMAX_CACHE_OPTIONS,
|
|
1708
|
-
useFactory: (raw) =>
|
|
1709
|
-
validateOptions(raw);
|
|
1710
|
-
return applyDefaults(raw);
|
|
1711
|
-
},
|
|
1722
|
+
useFactory: (raw) => resolveOptions(raw),
|
|
1712
1723
|
inject: [MODULE_OPTIONS_TOKEN]
|
|
1713
1724
|
},
|
|
1714
1725
|
{
|
|
@@ -1773,6 +1784,12 @@ exports.BymaxCacheModule = __decorateClass([
|
|
|
1773
1784
|
common.Module({})
|
|
1774
1785
|
], exports.BymaxCacheModule);
|
|
1775
1786
|
|
|
1787
|
+
// src/server/connection/create-connection-manager.ts
|
|
1788
|
+
function createConnectionManager(options) {
|
|
1789
|
+
const resolved = resolveOptions(options);
|
|
1790
|
+
return new exports.ConnectionManager(resolved, resolved.events);
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1776
1793
|
// src/shared/constants/event-names.ts
|
|
1777
1794
|
var CACHE_EVENT_NAMES = {
|
|
1778
1795
|
CONNECT: "connect",
|
|
@@ -1796,3 +1813,4 @@ exports.CacheException = CacheException;
|
|
|
1796
1813
|
exports.DEFAULT_KEY_SEPARATOR = DEFAULT_KEY_SEPARATOR;
|
|
1797
1814
|
exports.DEFAULT_NAMESPACE = DEFAULT_NAMESPACE;
|
|
1798
1815
|
exports.DEFAULT_REDIS_PORT = DEFAULT_REDIS_PORT;
|
|
1816
|
+
exports.createConnectionManager = createConnectionManager;
|
package/dist/server/index.d.cts
CHANGED
|
@@ -433,6 +433,48 @@ declare class ConnectionManager implements OnModuleInit, OnModuleDestroy {
|
|
|
433
433
|
private waitUntilReady;
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Factory for a dedicated, lifecycle-managed Redis connection.
|
|
438
|
+
*
|
|
439
|
+
* Layer: server. A consumer that needs a second connection with its own options (for
|
|
440
|
+
* example a standalone one beside a clustered cache) builds it here instead of
|
|
441
|
+
* hand-writing the resolved options, which would skip validation and the defaults.
|
|
442
|
+
*/
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Builds a {@link ConnectionManager} from the same options `BymaxCacheModule.forRoot`
|
|
446
|
+
* takes: the options are validated and defaulted exactly as the module does, so an invalid
|
|
447
|
+
* set throws the same `CacheException` before any client exists.
|
|
448
|
+
*
|
|
449
|
+
* The manager owns its connection. Register it as a provider and Nest connects it on
|
|
450
|
+
* `onModuleInit` and quits it on `onModuleDestroy`, with the forced `disconnect()` after
|
|
451
|
+
* `shutdownTimeoutMs`; the caller writes neither hook. Reach the client with
|
|
452
|
+
* `manager.getClient()`. This connection is independent of the one `BymaxCacheModule`
|
|
453
|
+
* registers, and it applies no namespace: keys written through its client are not
|
|
454
|
+
* prefixed.
|
|
455
|
+
*
|
|
456
|
+
* @param options - The connection options, as for `BymaxCacheModule.forRoot`.
|
|
457
|
+
* @returns A new manager that has not connected yet.
|
|
458
|
+
* @throws {CacheException} `SENTINEL_MISCONFIGURED`, `CLUSTER_MISCONFIGURED`,
|
|
459
|
+
* `CONNECTION_FAILED` or `INVALID_NAMESPACE` when the options are invalid.
|
|
460
|
+
* @example
|
|
461
|
+
* ```ts
|
|
462
|
+
* export const AUTH_REDIS = Symbol('AUTH_REDIS')
|
|
463
|
+
*
|
|
464
|
+
* @Module({
|
|
465
|
+
* providers: [
|
|
466
|
+
* {
|
|
467
|
+
* provide: AUTH_REDIS,
|
|
468
|
+
* useFactory: () => createConnectionManager({ connection: { url: 'redis://localhost:6379' } })
|
|
469
|
+
* }
|
|
470
|
+
* ],
|
|
471
|
+
* exports: [AUTH_REDIS]
|
|
472
|
+
* })
|
|
473
|
+
* export class AuthRedisModule {}
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
declare function createConnectionManager(options: BymaxCacheModuleOptions): ConnectionManager;
|
|
477
|
+
|
|
436
478
|
/**
|
|
437
479
|
* Composes Redis keys following `{namespace}{separator}{prefix}{separator}{id}`.
|
|
438
480
|
*
|
|
@@ -1344,4 +1386,4 @@ declare const CACHE_EVENT_NAMES: {
|
|
|
1344
1386
|
readonly END: "end";
|
|
1345
1387
|
};
|
|
1346
1388
|
|
|
1347
|
-
export { BYMAX_CACHE_CONNECTION, BYMAX_CACHE_EVENTS, BYMAX_CACHE_KEY_BUILDER, BYMAX_CACHE_OPTIONS, BYMAX_CACHE_SCRIPT_REGISTRY, BYMAX_CACHE_SERIALIZER, type BymaxCacheClusterConnection, BymaxCacheModule, type BymaxCacheModuleAsyncOptions, type BymaxCacheModuleOptions, type BymaxCacheSentinelConnection, type BymaxCacheStandaloneConnection, CACHE_ERROR_CODES, CACHE_ERROR_MESSAGES, CACHE_EVENT_NAMES, type CacheConnectionStatus, type CacheErrorCode, type CacheEventName, CacheException, type CacheKeyPrefix, type CacheNamespace, CacheService, ConnectionManager, DEFAULT_KEY_SEPARATOR, DEFAULT_NAMESPACE, DEFAULT_REDIS_PORT, type ICacheEvents, type IPubSubHandler, type IPubSubPatternHandler, type IScriptDefinition, type ISerializer, JsonSerializer, KeyBuilder, PubSubService, type ResolvedOptions, ScriptManagerService, type SerializableValue, type Unsubscribe };
|
|
1389
|
+
export { BYMAX_CACHE_CONNECTION, BYMAX_CACHE_EVENTS, BYMAX_CACHE_KEY_BUILDER, BYMAX_CACHE_OPTIONS, BYMAX_CACHE_SCRIPT_REGISTRY, BYMAX_CACHE_SERIALIZER, type BymaxCacheClusterConnection, BymaxCacheModule, type BymaxCacheModuleAsyncOptions, type BymaxCacheModuleOptions, type BymaxCacheSentinelConnection, type BymaxCacheStandaloneConnection, CACHE_ERROR_CODES, CACHE_ERROR_MESSAGES, CACHE_EVENT_NAMES, type CacheConnectionStatus, type CacheErrorCode, type CacheEventName, CacheException, type CacheKeyPrefix, type CacheNamespace, CacheService, ConnectionManager, DEFAULT_KEY_SEPARATOR, DEFAULT_NAMESPACE, DEFAULT_REDIS_PORT, type ICacheEvents, type IPubSubHandler, type IPubSubPatternHandler, type IScriptDefinition, type ISerializer, JsonSerializer, KeyBuilder, PubSubService, type ResolvedOptions, ScriptManagerService, type SerializableValue, type Unsubscribe, createConnectionManager };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -433,6 +433,48 @@ declare class ConnectionManager implements OnModuleInit, OnModuleDestroy {
|
|
|
433
433
|
private waitUntilReady;
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Factory for a dedicated, lifecycle-managed Redis connection.
|
|
438
|
+
*
|
|
439
|
+
* Layer: server. A consumer that needs a second connection with its own options (for
|
|
440
|
+
* example a standalone one beside a clustered cache) builds it here instead of
|
|
441
|
+
* hand-writing the resolved options, which would skip validation and the defaults.
|
|
442
|
+
*/
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Builds a {@link ConnectionManager} from the same options `BymaxCacheModule.forRoot`
|
|
446
|
+
* takes: the options are validated and defaulted exactly as the module does, so an invalid
|
|
447
|
+
* set throws the same `CacheException` before any client exists.
|
|
448
|
+
*
|
|
449
|
+
* The manager owns its connection. Register it as a provider and Nest connects it on
|
|
450
|
+
* `onModuleInit` and quits it on `onModuleDestroy`, with the forced `disconnect()` after
|
|
451
|
+
* `shutdownTimeoutMs`; the caller writes neither hook. Reach the client with
|
|
452
|
+
* `manager.getClient()`. This connection is independent of the one `BymaxCacheModule`
|
|
453
|
+
* registers, and it applies no namespace: keys written through its client are not
|
|
454
|
+
* prefixed.
|
|
455
|
+
*
|
|
456
|
+
* @param options - The connection options, as for `BymaxCacheModule.forRoot`.
|
|
457
|
+
* @returns A new manager that has not connected yet.
|
|
458
|
+
* @throws {CacheException} `SENTINEL_MISCONFIGURED`, `CLUSTER_MISCONFIGURED`,
|
|
459
|
+
* `CONNECTION_FAILED` or `INVALID_NAMESPACE` when the options are invalid.
|
|
460
|
+
* @example
|
|
461
|
+
* ```ts
|
|
462
|
+
* export const AUTH_REDIS = Symbol('AUTH_REDIS')
|
|
463
|
+
*
|
|
464
|
+
* @Module({
|
|
465
|
+
* providers: [
|
|
466
|
+
* {
|
|
467
|
+
* provide: AUTH_REDIS,
|
|
468
|
+
* useFactory: () => createConnectionManager({ connection: { url: 'redis://localhost:6379' } })
|
|
469
|
+
* }
|
|
470
|
+
* ],
|
|
471
|
+
* exports: [AUTH_REDIS]
|
|
472
|
+
* })
|
|
473
|
+
* export class AuthRedisModule {}
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
declare function createConnectionManager(options: BymaxCacheModuleOptions): ConnectionManager;
|
|
477
|
+
|
|
436
478
|
/**
|
|
437
479
|
* Composes Redis keys following `{namespace}{separator}{prefix}{separator}{id}`.
|
|
438
480
|
*
|
|
@@ -1344,4 +1386,4 @@ declare const CACHE_EVENT_NAMES: {
|
|
|
1344
1386
|
readonly END: "end";
|
|
1345
1387
|
};
|
|
1346
1388
|
|
|
1347
|
-
export { BYMAX_CACHE_CONNECTION, BYMAX_CACHE_EVENTS, BYMAX_CACHE_KEY_BUILDER, BYMAX_CACHE_OPTIONS, BYMAX_CACHE_SCRIPT_REGISTRY, BYMAX_CACHE_SERIALIZER, type BymaxCacheClusterConnection, BymaxCacheModule, type BymaxCacheModuleAsyncOptions, type BymaxCacheModuleOptions, type BymaxCacheSentinelConnection, type BymaxCacheStandaloneConnection, CACHE_ERROR_CODES, CACHE_ERROR_MESSAGES, CACHE_EVENT_NAMES, type CacheConnectionStatus, type CacheErrorCode, type CacheEventName, CacheException, type CacheKeyPrefix, type CacheNamespace, CacheService, ConnectionManager, DEFAULT_KEY_SEPARATOR, DEFAULT_NAMESPACE, DEFAULT_REDIS_PORT, type ICacheEvents, type IPubSubHandler, type IPubSubPatternHandler, type IScriptDefinition, type ISerializer, JsonSerializer, KeyBuilder, PubSubService, type ResolvedOptions, ScriptManagerService, type SerializableValue, type Unsubscribe };
|
|
1389
|
+
export { BYMAX_CACHE_CONNECTION, BYMAX_CACHE_EVENTS, BYMAX_CACHE_KEY_BUILDER, BYMAX_CACHE_OPTIONS, BYMAX_CACHE_SCRIPT_REGISTRY, BYMAX_CACHE_SERIALIZER, type BymaxCacheClusterConnection, BymaxCacheModule, type BymaxCacheModuleAsyncOptions, type BymaxCacheModuleOptions, type BymaxCacheSentinelConnection, type BymaxCacheStandaloneConnection, CACHE_ERROR_CODES, CACHE_ERROR_MESSAGES, CACHE_EVENT_NAMES, type CacheConnectionStatus, type CacheErrorCode, type CacheEventName, CacheException, type CacheKeyPrefix, type CacheNamespace, CacheService, ConnectionManager, DEFAULT_KEY_SEPARATOR, DEFAULT_NAMESPACE, DEFAULT_REDIS_PORT, type ICacheEvents, type IPubSubHandler, type IPubSubPatternHandler, type IScriptDefinition, type ISerializer, JsonSerializer, KeyBuilder, PubSubService, type ResolvedOptions, ScriptManagerService, type SerializableValue, type Unsubscribe, createConnectionManager };
|
package/dist/server/index.mjs
CHANGED
|
@@ -288,6 +288,10 @@ function applyDefaults(options) {
|
|
|
288
288
|
}
|
|
289
289
|
return Object.freeze(resolved);
|
|
290
290
|
}
|
|
291
|
+
function resolveOptions(options) {
|
|
292
|
+
validateOptions(options);
|
|
293
|
+
return applyDefaults(options);
|
|
294
|
+
}
|
|
291
295
|
var _client, _redisOptionsResolved;
|
|
292
296
|
var ConnectionManager = class {
|
|
293
297
|
/**
|
|
@@ -521,9 +525,21 @@ ConnectionManager = __decorateClass([
|
|
|
521
525
|
__decorateParam(1, Optional()),
|
|
522
526
|
__decorateParam(1, Inject(BYMAX_CACHE_EVENTS))
|
|
523
527
|
], ConnectionManager);
|
|
528
|
+
|
|
529
|
+
// src/server/utils/describe-error.ts
|
|
530
|
+
var UNPRINTABLE = "unprintable error";
|
|
531
|
+
function describeError(reason) {
|
|
532
|
+
try {
|
|
533
|
+
const message = reason?.message;
|
|
534
|
+
return typeof message === "string" ? message : String(reason);
|
|
535
|
+
} catch {
|
|
536
|
+
return UNPRINTABLE;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// src/server/services/script-manager.service.ts
|
|
524
541
|
var SCRIPT_LOAD = "LOAD";
|
|
525
542
|
var NOSCRIPT_MARKER = "NOSCRIPT";
|
|
526
|
-
var toErrorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
527
543
|
var ScriptManagerService = class {
|
|
528
544
|
/**
|
|
529
545
|
* @param options - Resolved module options; `options.scripts` seeds the registry.
|
|
@@ -631,7 +647,7 @@ var ScriptManagerService = class {
|
|
|
631
647
|
} catch (err) {
|
|
632
648
|
throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
|
|
633
649
|
name,
|
|
634
|
-
originalError:
|
|
650
|
+
originalError: describeError(err)
|
|
635
651
|
});
|
|
636
652
|
}
|
|
637
653
|
}
|
|
@@ -639,10 +655,10 @@ var ScriptManagerService = class {
|
|
|
639
655
|
const sha = entry.sha ?? await this.load(name);
|
|
640
656
|
return await client.evalsha(sha, keys.length, ...keys, ...args);
|
|
641
657
|
} catch (err) {
|
|
642
|
-
if (!
|
|
658
|
+
if (!describeError(err).includes(NOSCRIPT_MARKER)) {
|
|
643
659
|
throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
|
|
644
660
|
name,
|
|
645
|
-
originalError:
|
|
661
|
+
originalError: describeError(err)
|
|
646
662
|
});
|
|
647
663
|
}
|
|
648
664
|
try {
|
|
@@ -652,7 +668,7 @@ var ScriptManagerService = class {
|
|
|
652
668
|
} catch (retryErr) {
|
|
653
669
|
throw new CacheException(CACHE_ERROR_CODES.SCRIPT_EXECUTION_FAILED, {
|
|
654
670
|
name,
|
|
655
|
-
originalError:
|
|
671
|
+
originalError: describeError(retryErr)
|
|
656
672
|
});
|
|
657
673
|
}
|
|
658
674
|
}
|
|
@@ -717,7 +733,6 @@ KeyBuilder = __decorateClass([
|
|
|
717
733
|
__decorateParam(0, Inject(BYMAX_CACHE_OPTIONS))
|
|
718
734
|
], KeyBuilder);
|
|
719
735
|
var DESERIALIZATION_ERROR = "stored value is not valid JSON";
|
|
720
|
-
var extractErrorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
721
736
|
var JsonSerializer = class {
|
|
722
737
|
/**
|
|
723
738
|
* Encodes a value as a JSON string.
|
|
@@ -743,7 +758,7 @@ var JsonSerializer = class {
|
|
|
743
758
|
return JSON.stringify(value);
|
|
744
759
|
} catch (err) {
|
|
745
760
|
throw new CacheException(CACHE_ERROR_CODES.SERIALIZATION_FAILED, {
|
|
746
|
-
error:
|
|
761
|
+
error: describeError(err)
|
|
747
762
|
});
|
|
748
763
|
}
|
|
749
764
|
}
|
|
@@ -1616,7 +1631,7 @@ var PubSubService = class {
|
|
|
1616
1631
|
role: "subscriber",
|
|
1617
1632
|
reason: "handler_error",
|
|
1618
1633
|
channel,
|
|
1619
|
-
error:
|
|
1634
|
+
error: describeError(error)
|
|
1620
1635
|
});
|
|
1621
1636
|
} catch {
|
|
1622
1637
|
}
|
|
@@ -1645,8 +1660,7 @@ var BymaxCacheModule = class extends ConfigurableModuleClass {
|
|
|
1645
1660
|
* fail bootstrap validation (e.g. missing connection, misconfigured mode).
|
|
1646
1661
|
*/
|
|
1647
1662
|
static forRoot(options) {
|
|
1648
|
-
|
|
1649
|
-
const resolved = applyDefaults(options);
|
|
1663
|
+
const resolved = resolveOptions(options);
|
|
1650
1664
|
const providers = [
|
|
1651
1665
|
{ provide: MODULE_OPTIONS_TOKEN, useValue: options },
|
|
1652
1666
|
{ provide: BYMAX_CACHE_OPTIONS, useValue: resolved },
|
|
@@ -1703,10 +1717,7 @@ var BymaxCacheModule = class extends ConfigurableModuleClass {
|
|
|
1703
1717
|
return [
|
|
1704
1718
|
{
|
|
1705
1719
|
provide: BYMAX_CACHE_OPTIONS,
|
|
1706
|
-
useFactory: (raw) =>
|
|
1707
|
-
validateOptions(raw);
|
|
1708
|
-
return applyDefaults(raw);
|
|
1709
|
-
},
|
|
1720
|
+
useFactory: (raw) => resolveOptions(raw),
|
|
1710
1721
|
inject: [MODULE_OPTIONS_TOKEN]
|
|
1711
1722
|
},
|
|
1712
1723
|
{
|
|
@@ -1771,6 +1782,12 @@ BymaxCacheModule = __decorateClass([
|
|
|
1771
1782
|
Module({})
|
|
1772
1783
|
], BymaxCacheModule);
|
|
1773
1784
|
|
|
1785
|
+
// src/server/connection/create-connection-manager.ts
|
|
1786
|
+
function createConnectionManager(options) {
|
|
1787
|
+
const resolved = resolveOptions(options);
|
|
1788
|
+
return new ConnectionManager(resolved, resolved.events);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1774
1791
|
// src/shared/constants/event-names.ts
|
|
1775
1792
|
var CACHE_EVENT_NAMES = {
|
|
1776
1793
|
CONNECT: "connect",
|
|
@@ -1781,4 +1798,4 @@ var CACHE_EVENT_NAMES = {
|
|
|
1781
1798
|
END: "end"
|
|
1782
1799
|
};
|
|
1783
1800
|
|
|
1784
|
-
export { BYMAX_CACHE_CONNECTION, BYMAX_CACHE_EVENTS, BYMAX_CACHE_KEY_BUILDER, BYMAX_CACHE_OPTIONS, BYMAX_CACHE_SCRIPT_REGISTRY, BYMAX_CACHE_SERIALIZER, BymaxCacheModule, CACHE_ERROR_CODES, CACHE_ERROR_MESSAGES, CACHE_EVENT_NAMES, CacheException, CacheService, ConnectionManager, DEFAULT_KEY_SEPARATOR, DEFAULT_NAMESPACE, DEFAULT_REDIS_PORT, JsonSerializer, KeyBuilder, PubSubService, ScriptManagerService };
|
|
1801
|
+
export { BYMAX_CACHE_CONNECTION, BYMAX_CACHE_EVENTS, BYMAX_CACHE_KEY_BUILDER, BYMAX_CACHE_OPTIONS, BYMAX_CACHE_SCRIPT_REGISTRY, BYMAX_CACHE_SERIALIZER, BymaxCacheModule, CACHE_ERROR_CODES, CACHE_ERROR_MESSAGES, CACHE_EVENT_NAMES, CacheException, CacheService, ConnectionManager, DEFAULT_KEY_SEPARATOR, DEFAULT_NAMESPACE, DEFAULT_REDIS_PORT, JsonSerializer, KeyBuilder, PubSubService, ScriptManagerService, createConnectionManager };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bymax-one/nest-cache",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"description": "Typed Redis cache for NestJS based on ioredis 6, with namespace strategy, Pub/Sub and Lua script management.",
|
|
5
5
|
"author": "Bymax One <support@bymax.one>",
|
|
6
6
|
"license": "MIT",
|