@fluojs/redis 1.0.0-beta.1
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/LICENSE +21 -0
- package/README.ko.md +157 -0
- package/README.md +157 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/module.d.ts +71 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +150 -0
- package/dist/redis-service.d.ts +79 -0
- package/dist/redis-service.d.ts.map +1 -0
- package/dist/redis-service.js +138 -0
- package/dist/service.d.ts +17 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +83 -0
- package/dist/status.d.ts +22 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +72 -0
- package/dist/tokens.d.ts +38 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +68 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fluo contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# @fluojs/redis
|
|
2
|
+
|
|
3
|
+
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
|
+
|
|
5
|
+
fluo를 위한 공유 Redis 연결 계층입니다. 애플리케이션 수명 주기에 따라 관리되는 단일 `ioredis` 클라이언트를 제공합니다.
|
|
6
|
+
|
|
7
|
+
## 목차
|
|
8
|
+
|
|
9
|
+
- [설치](#설치)
|
|
10
|
+
- [사용 시점](#사용-시점)
|
|
11
|
+
- [빠른 시작](#빠른-시작)
|
|
12
|
+
- [일반적인 패턴](#일반적인-패턴)
|
|
13
|
+
- [공개 API 개요](#공개-api-개요)
|
|
14
|
+
- [관련 패키지](#관련-패키지)
|
|
15
|
+
- [예제 소스](#예제-소스)
|
|
16
|
+
|
|
17
|
+
## 설치
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @fluojs/redis ioredis
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 사용 시점
|
|
24
|
+
|
|
25
|
+
- 캐싱, 큐, 전송률 제한(Throttler) 등 여러 모듈에서 공유할 Redis 연결이 필요할 때.
|
|
26
|
+
- 애플리케이션 시작 시 자동 연결, 종료 시 안전한 연결 해제 기능을 원할 때.
|
|
27
|
+
- JSON 데이터를 다루기 편한 고수준의 Redis 파사드(Facade)가 필요할 때.
|
|
28
|
+
|
|
29
|
+
## 빠른 시작
|
|
30
|
+
|
|
31
|
+
### 모듈 등록
|
|
32
|
+
|
|
33
|
+
`RedisModule.forRoot(options)`는 기본 Redis 클라이언트와 `RedisService` 파사드를 등록하는 지원되는 root entrypoint입니다.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { Module } from '@fluojs/core';
|
|
37
|
+
import { RedisModule } from '@fluojs/redis';
|
|
38
|
+
|
|
39
|
+
@Module({
|
|
40
|
+
imports: [
|
|
41
|
+
RedisModule.forRoot({
|
|
42
|
+
host: 'localhost',
|
|
43
|
+
port: 6379,
|
|
44
|
+
}),
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
export class AppModule {}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Redis 서비스 사용
|
|
51
|
+
|
|
52
|
+
`RedisService`를 주입받아 고수준 작업을 수행하거나, `REDIS_CLIENT`를 통해 원시 `ioredis` 인스턴스를 직접 사용할 수 있습니다.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Inject } from '@fluojs/core';
|
|
56
|
+
import { RedisService } from '@fluojs/redis';
|
|
57
|
+
|
|
58
|
+
@Inject(RedisService)
|
|
59
|
+
export class CacheRepository {
|
|
60
|
+
constructor(private readonly redis: RedisService) {}
|
|
61
|
+
|
|
62
|
+
async saveUser(id: string, user: object) {
|
|
63
|
+
await this.redis.set(`user:${id}`, user, 3600);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getUser(id: string) {
|
|
67
|
+
return await this.redis.get(`user:${id}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 일반적인 패턴
|
|
73
|
+
|
|
74
|
+
### 이름 있는 클라이언트
|
|
75
|
+
|
|
76
|
+
하나의 애플리케이션에서 여러 Redis 연결이 필요하면 `RedisModule.forRootNamed(name, options)`를 사용하세요. `RedisModule.forRoot(options)`는 기본 `REDIS_CLIENT`와 `RedisService` 별칭을 제공하고, 이름 있는 등록은 `getRedisClientToken(name)`과 `getRedisServiceToken(name)`으로 해석합니다.
|
|
77
|
+
|
|
78
|
+
- `name`을 생략하면 기본 별칭인 `REDIS_CLIENT` / `RedisService`를 사용합니다.
|
|
79
|
+
- `name`을 지정하면 `getRedisClientToken(name)` / `getRedisServiceToken(name)`으로 이름 있는 바인딩을 가져옵니다.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { Module, Inject } from '@fluojs/core';
|
|
83
|
+
import type Redis from 'ioredis';
|
|
84
|
+
import {
|
|
85
|
+
getRedisClientToken,
|
|
86
|
+
getRedisServiceToken,
|
|
87
|
+
RedisModule,
|
|
88
|
+
RedisService,
|
|
89
|
+
} from '@fluojs/redis';
|
|
90
|
+
|
|
91
|
+
const ANALYTICS_REDIS = getRedisServiceToken('analytics');
|
|
92
|
+
const ANALYTICS_REDIS_CLIENT = getRedisClientToken('analytics');
|
|
93
|
+
|
|
94
|
+
@Module({
|
|
95
|
+
imports: [
|
|
96
|
+
RedisModule.forRoot({ host: 'localhost', port: 6379 }),
|
|
97
|
+
RedisModule.forRootNamed('analytics', { host: 'localhost', port: 6380 }),
|
|
98
|
+
],
|
|
99
|
+
})
|
|
100
|
+
export class AppModule {}
|
|
101
|
+
|
|
102
|
+
@Inject(RedisService, ANALYTICS_REDIS, ANALYTICS_REDIS_CLIENT)
|
|
103
|
+
export class AnalyticsStore {
|
|
104
|
+
constructor(
|
|
105
|
+
private readonly defaultRedis: RedisService,
|
|
106
|
+
private readonly analyticsRedis: RedisService,
|
|
107
|
+
private readonly analyticsClient: Redis,
|
|
108
|
+
) {}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 원시 클라이언트 접근 (Raw Client Access)
|
|
113
|
+
|
|
114
|
+
파이프라인, Lua 스크립트, Pub/Sub 등 복잡한 Redis 명령이 필요한 경우 원시 클라이언트를 직접 주입받아 사용합니다.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { Inject } from '@fluojs/core';
|
|
118
|
+
import { REDIS_CLIENT } from '@fluojs/redis';
|
|
119
|
+
import type Redis from 'ioredis';
|
|
120
|
+
|
|
121
|
+
@Inject(REDIS_CLIENT)
|
|
122
|
+
export class AdvancedService {
|
|
123
|
+
constructor(private readonly client: Redis) {}
|
|
124
|
+
|
|
125
|
+
async executeComplex() {
|
|
126
|
+
return await this.client.pipeline().set('foo', 'bar').get('foo').exec();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 공개 API 개요
|
|
132
|
+
|
|
133
|
+
### 핵심 구성 요소
|
|
134
|
+
- `RedisModule`: 전역 Redis 클라이언트 등록 및 수명 주기 훅을 관리합니다.
|
|
135
|
+
- `RedisModule.forRoot(options)`: 기본 Redis 클라이언트와 `RedisService` 파사드를 위한 지원되는 root entrypoint입니다.
|
|
136
|
+
- `RedisModule.forRootNamed(name, options)`: 기본 별칭을 유지한 채 추가 Redis 클라이언트를 등록합니다.
|
|
137
|
+
- `RedisService`: JSON 코덱 지원 및 `get`/`set`/`del` 메서드를 제공하는 파사드입니다.
|
|
138
|
+
- `REDIS_CLIENT`: 내부 `ioredis` 인스턴스에 접근하기 위한 DI 토큰입니다.
|
|
139
|
+
- `getRedisClientToken(name)`: 이름 있는 raw client 토큰 헬퍼입니다. `name`을 생략하면 기본 `REDIS_CLIENT` 토큰을 돌려줍니다.
|
|
140
|
+
- `getRedisServiceToken(name)`: 이름 있는 `RedisService` 토큰 헬퍼입니다. `name`을 생략하면 기본 `RedisService` 토큰을 돌려줍니다.
|
|
141
|
+
- `getRedisComponentId(name)`: Redis 소비 패키지들이 사용하는 상태/의존성 식별자 헬퍼입니다 (`redis.default`, `redis.cache` 등).
|
|
142
|
+
- `createRedisPlatformStatusSnapshot(input)`: Redis 연결 상태를 Fluo 플랫폼 health/readiness 스냅샷으로 변환합니다.
|
|
143
|
+
|
|
144
|
+
### 타입
|
|
145
|
+
- `RedisModuleOptions`: `ioredis` 생성자에 전달되는 설정 옵션입니다.
|
|
146
|
+
|
|
147
|
+
## 관련 패키지
|
|
148
|
+
|
|
149
|
+
- `@fluojs/cache-manager`: Redis를 백엔드로 사용하는 캐싱 패키지입니다.
|
|
150
|
+
- `@fluojs/queue`: Redis 기반의 분산 작업 큐 패키지입니다.
|
|
151
|
+
- `@fluojs/throttler`: Redis 기반의 분산 전송률 제한 패키지입니다.
|
|
152
|
+
|
|
153
|
+
## 예제 소스
|
|
154
|
+
|
|
155
|
+
- `packages/redis/src/module.test.ts`: 모듈 수명 주기 및 DI 연결 예제.
|
|
156
|
+
- `packages/redis/src/public-api.test.ts`: 문서화된 Redis 공개 export를 검증하는 테스트입니다.
|
|
157
|
+
- `packages/redis/src/redis-service.ts`: 파사드 구현 및 코덱 로직.
|
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# @fluojs/redis
|
|
2
|
+
|
|
3
|
+
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
4
|
+
|
|
5
|
+
Shared Redis connection layer for fluo. It provides a singleton `ioredis` client managed by the application lifecycle.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [When to use](#when-to-use)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Common Patterns](#common-patterns)
|
|
13
|
+
- [Public API](#public-api)
|
|
14
|
+
- [Related Packages](#related-packages)
|
|
15
|
+
- [Example Sources](#example-sources)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @fluojs/redis ioredis
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## When to Use
|
|
24
|
+
|
|
25
|
+
- When you need a shared Redis connection across multiple modules (caching, queues, throttlers).
|
|
26
|
+
- When you want automatic connection management (connect on bootstrap, quit on shutdown).
|
|
27
|
+
- When you need a JSON-aware facade for common key-value operations.
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Register the Module
|
|
32
|
+
|
|
33
|
+
Use `RedisModule.forRoot(options)` to register the default Redis client and `RedisService` facade.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { Module } from '@fluojs/core';
|
|
37
|
+
import { RedisModule } from '@fluojs/redis';
|
|
38
|
+
|
|
39
|
+
@Module({
|
|
40
|
+
imports: [
|
|
41
|
+
RedisModule.forRoot({
|
|
42
|
+
host: 'localhost',
|
|
43
|
+
port: 6379,
|
|
44
|
+
}),
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
export class AppModule {}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Use the Redis Service
|
|
51
|
+
|
|
52
|
+
Inject `RedisService` for high-level operations or `REDIS_CLIENT` for the raw `ioredis` instance.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Inject } from '@fluojs/core';
|
|
56
|
+
import { RedisService } from '@fluojs/redis';
|
|
57
|
+
|
|
58
|
+
@Inject(RedisService)
|
|
59
|
+
export class CacheRepository {
|
|
60
|
+
constructor(private readonly redis: RedisService) {}
|
|
61
|
+
|
|
62
|
+
async saveUser(id: string, user: object) {
|
|
63
|
+
await this.redis.set(`user:${id}`, user, 3600);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getUser(id: string) {
|
|
67
|
+
return await this.redis.get(`user:${id}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Common Patterns
|
|
73
|
+
|
|
74
|
+
### Named Clients
|
|
75
|
+
|
|
76
|
+
Use `RedisModule.forRootNamed(name, options)` when one application needs more than one Redis connection. `RedisModule.forRoot(options)` provides the default `REDIS_CLIENT` and `RedisService` aliases, and named registrations are resolved with `getRedisClientToken(name)` and `getRedisServiceToken(name)`.
|
|
77
|
+
|
|
78
|
+
- Omit `name` when you want the default aliases: `REDIS_CLIENT` / `RedisService`.
|
|
79
|
+
- Pass `name` when you want the named helpers: `getRedisClientToken(name)` / `getRedisServiceToken(name)`.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { Module, Inject } from '@fluojs/core';
|
|
83
|
+
import type Redis from 'ioredis';
|
|
84
|
+
import {
|
|
85
|
+
getRedisClientToken,
|
|
86
|
+
getRedisServiceToken,
|
|
87
|
+
RedisModule,
|
|
88
|
+
RedisService,
|
|
89
|
+
} from '@fluojs/redis';
|
|
90
|
+
|
|
91
|
+
const ANALYTICS_REDIS = getRedisServiceToken('analytics');
|
|
92
|
+
const ANALYTICS_REDIS_CLIENT = getRedisClientToken('analytics');
|
|
93
|
+
|
|
94
|
+
@Module({
|
|
95
|
+
imports: [
|
|
96
|
+
RedisModule.forRoot({ host: 'localhost', port: 6379 }),
|
|
97
|
+
RedisModule.forRootNamed('analytics', { host: 'localhost', port: 6380 }),
|
|
98
|
+
],
|
|
99
|
+
})
|
|
100
|
+
export class AppModule {}
|
|
101
|
+
|
|
102
|
+
@Inject(RedisService, ANALYTICS_REDIS, ANALYTICS_REDIS_CLIENT)
|
|
103
|
+
export class AnalyticsStore {
|
|
104
|
+
constructor(
|
|
105
|
+
private readonly defaultRedis: RedisService,
|
|
106
|
+
private readonly analyticsRedis: RedisService,
|
|
107
|
+
private readonly analyticsClient: Redis,
|
|
108
|
+
) {}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Raw Client Access
|
|
113
|
+
|
|
114
|
+
If you need advanced Redis commands (pipelines, lua scripts, pub/sub), inject the raw client directly.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { Inject } from '@fluojs/core';
|
|
118
|
+
import { REDIS_CLIENT } from '@fluojs/redis';
|
|
119
|
+
import type Redis from 'ioredis';
|
|
120
|
+
|
|
121
|
+
@Inject(REDIS_CLIENT)
|
|
122
|
+
export class AdvancedService {
|
|
123
|
+
constructor(private readonly client: Redis) {}
|
|
124
|
+
|
|
125
|
+
async executeComplex() {
|
|
126
|
+
return await this.client.pipeline().set('foo', 'bar').get('foo').exec();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Public API Overview
|
|
132
|
+
|
|
133
|
+
### Core
|
|
134
|
+
- `RedisModule`: Registers the global Redis client and lifecycle hooks.
|
|
135
|
+
- `RedisModule.forRoot(options)`: Registers the default Redis client plus `RedisService` facade.
|
|
136
|
+
- `RedisModule.forRootNamed(name, options)`: Registers an additional named Redis client without replacing the default aliases.
|
|
137
|
+
- `RedisService`: Facade with JSON codec support and `get`/`set`/`del` methods.
|
|
138
|
+
- `REDIS_CLIENT`: DI token for the underlying `ioredis` instance.
|
|
139
|
+
- `getRedisClientToken(name)`: DI token helper for a named raw client. Omitting `name` returns the default `REDIS_CLIENT` token.
|
|
140
|
+
- `getRedisServiceToken(name)`: DI token helper for a named `RedisService` facade. Omitting `name` returns the default `RedisService` token.
|
|
141
|
+
- `getRedisComponentId(name)`: Status/dependency id helper used by Redis-consuming packages (`redis.default`, `redis.cache`, etc.).
|
|
142
|
+
- `createRedisPlatformStatusSnapshot(input)`: Adapts Redis connection state into Fluo's platform health/readiness snapshot contract.
|
|
143
|
+
|
|
144
|
+
### Types
|
|
145
|
+
- `RedisModuleOptions`: Configuration options passed directly to the `ioredis` constructor.
|
|
146
|
+
|
|
147
|
+
## Related Packages
|
|
148
|
+
|
|
149
|
+
- `@fluojs/cache-manager`: Uses this package for Redis-backed caching.
|
|
150
|
+
- `@fluojs/queue`: Uses this package for distributed job processing.
|
|
151
|
+
- `@fluojs/throttler`: Uses this package for distributed rate limiting.
|
|
152
|
+
|
|
153
|
+
## Example Sources
|
|
154
|
+
|
|
155
|
+
- `packages/redis/src/module.test.ts`: Module lifecycle and DI wiring.
|
|
156
|
+
- `packages/redis/src/public-api.test.ts`: Root-barrel export guard for the documented Redis surface.
|
|
157
|
+
- `packages/redis/src/redis-service.ts`: Facade implementation and codec logic.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type ModuleType } from '@fluojs/runtime';
|
|
2
|
+
import type { RedisModuleOptions } from './types.js';
|
|
3
|
+
/** Runtime module entrypoint for the shared Redis client integration. */
|
|
4
|
+
export declare class RedisModule {
|
|
5
|
+
/**
|
|
6
|
+
* Registers a global Redis client and exports the facade used across Fluo packages.
|
|
7
|
+
*
|
|
8
|
+
* @param options Redis client options used to construct the shared connection.
|
|
9
|
+
* @returns A module definition that exports {@link REDIS_CLIENT} and {@link RedisService}.
|
|
10
|
+
*
|
|
11
|
+
* @see RedisModule.forRootNamed
|
|
12
|
+
* @see getRedisClientToken
|
|
13
|
+
* @see getRedisServiceToken
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { Module } from '@fluojs/core';
|
|
18
|
+
* import { RedisModule } from '@fluojs/redis';
|
|
19
|
+
*
|
|
20
|
+
* @Module({
|
|
21
|
+
* imports: [RedisModule.forRoot({ host: 'localhost', port: 6379 })],
|
|
22
|
+
* })
|
|
23
|
+
* export class AppModule {}
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
static forRoot(options: RedisModuleOptions): ModuleType;
|
|
27
|
+
/**
|
|
28
|
+
* Registers one additional named Redis client without changing the default aliases.
|
|
29
|
+
*
|
|
30
|
+
* @param name Stable Redis client name used to derive named raw-client and facade tokens.
|
|
31
|
+
* @param options Redis client options used to construct the named connection.
|
|
32
|
+
* @returns A module definition that exports the named raw-client token and named `RedisService` token.
|
|
33
|
+
*
|
|
34
|
+
* @see getRedisClientToken
|
|
35
|
+
* @see getRedisServiceToken
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { Module, Inject } from '@fluojs/core';
|
|
40
|
+
* import type Redis from 'ioredis';
|
|
41
|
+
* import {
|
|
42
|
+
* getRedisClientToken,
|
|
43
|
+
* getRedisServiceToken,
|
|
44
|
+
* RedisModule,
|
|
45
|
+
* RedisService,
|
|
46
|
+
* } from '@fluojs/redis';
|
|
47
|
+
*
|
|
48
|
+
* const ANALYTICS_REDIS = getRedisServiceToken('analytics');
|
|
49
|
+
* const ANALYTICS_REDIS_CLIENT = getRedisClientToken('analytics');
|
|
50
|
+
*
|
|
51
|
+
* @Module({
|
|
52
|
+
* imports: [
|
|
53
|
+
* RedisModule.forRoot({ host: 'localhost', port: 6379 }),
|
|
54
|
+
* RedisModule.forRootNamed('analytics', { host: 'localhost', port: 6380 }),
|
|
55
|
+
* ],
|
|
56
|
+
* })
|
|
57
|
+
* export class AppModule {}
|
|
58
|
+
*
|
|
59
|
+
* @Inject(RedisService, ANALYTICS_REDIS, ANALYTICS_REDIS_CLIENT)
|
|
60
|
+
* export class AnalyticsStore {
|
|
61
|
+
* constructor(
|
|
62
|
+
* private readonly defaultRedis: RedisService,
|
|
63
|
+
* private readonly analyticsRedis: RedisService,
|
|
64
|
+
* private readonly analyticsClient: Redis,
|
|
65
|
+
* ) {}
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
static forRootNamed(name: string, options: RedisModuleOptions): ModuleType;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMhE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA6ErD,yEAAyE;AACzE,qBAAa,WAAW;IACtB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,kBAAkB,GAAG,UAAU;IAUvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyCG;IACH,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,UAAU;CAW3E"}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { defineModule } from '@fluojs/runtime';
|
|
2
|
+
import Redis from 'ioredis';
|
|
3
|
+
import { getRedisServiceToken, RedisService } from './redis-service.js';
|
|
4
|
+
import { RedisLifecycleService } from './service.js';
|
|
5
|
+
import { getRedisClientToken, REDIS_CLIENT } from './tokens.js';
|
|
6
|
+
const redisLifecycleTokens = new Map();
|
|
7
|
+
function getRedisLifecycleToken(name) {
|
|
8
|
+
const existing = redisLifecycleTokens.get(name);
|
|
9
|
+
if (existing) {
|
|
10
|
+
return existing;
|
|
11
|
+
}
|
|
12
|
+
const created = Symbol(`fluo.redis.lifecycle:${name}`);
|
|
13
|
+
redisLifecycleTokens.set(name, created);
|
|
14
|
+
return created;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates the providers that back Fluo's shared Redis integration.
|
|
19
|
+
*
|
|
20
|
+
* @param options Redis constructor options forwarded to `ioredis` with `lazyConnect` forced on.
|
|
21
|
+
* @param name Optional Redis client name for additional named registrations.
|
|
22
|
+
* @returns Providers for the raw client token, the JSON-aware facade, and lifecycle hooks.
|
|
23
|
+
*/
|
|
24
|
+
function createRedisProviders(options, name) {
|
|
25
|
+
const clientToken = getRedisClientToken(name);
|
|
26
|
+
if (clientToken === REDIS_CLIENT) {
|
|
27
|
+
return [{
|
|
28
|
+
scope: 'singleton',
|
|
29
|
+
provide: REDIS_CLIENT,
|
|
30
|
+
useFactory: () => new Redis({
|
|
31
|
+
...options,
|
|
32
|
+
lazyConnect: true
|
|
33
|
+
})
|
|
34
|
+
}, RedisService, RedisLifecycleService];
|
|
35
|
+
}
|
|
36
|
+
if (name === undefined) {
|
|
37
|
+
throw new Error('Redis client name must be defined for named provider creation.');
|
|
38
|
+
}
|
|
39
|
+
const serviceToken = getRedisServiceToken(name);
|
|
40
|
+
return [{
|
|
41
|
+
scope: 'singleton',
|
|
42
|
+
provide: clientToken,
|
|
43
|
+
useFactory: () => new Redis({
|
|
44
|
+
...options,
|
|
45
|
+
lazyConnect: true
|
|
46
|
+
})
|
|
47
|
+
}, {
|
|
48
|
+
inject: [clientToken],
|
|
49
|
+
provide: serviceToken,
|
|
50
|
+
scope: 'singleton',
|
|
51
|
+
useFactory: (...deps) => {
|
|
52
|
+
const [client] = deps;
|
|
53
|
+
return new RedisService(client);
|
|
54
|
+
}
|
|
55
|
+
}, {
|
|
56
|
+
inject: [clientToken],
|
|
57
|
+
provide: getRedisLifecycleToken(name),
|
|
58
|
+
scope: 'singleton',
|
|
59
|
+
useFactory: (...deps) => {
|
|
60
|
+
const [client] = deps;
|
|
61
|
+
return new RedisLifecycleService(client, name);
|
|
62
|
+
}
|
|
63
|
+
}];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Runtime module entrypoint for the shared Redis client integration. */
|
|
67
|
+
export class RedisModule {
|
|
68
|
+
/**
|
|
69
|
+
* Registers a global Redis client and exports the facade used across Fluo packages.
|
|
70
|
+
*
|
|
71
|
+
* @param options Redis client options used to construct the shared connection.
|
|
72
|
+
* @returns A module definition that exports {@link REDIS_CLIENT} and {@link RedisService}.
|
|
73
|
+
*
|
|
74
|
+
* @see RedisModule.forRootNamed
|
|
75
|
+
* @see getRedisClientToken
|
|
76
|
+
* @see getRedisServiceToken
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* import { Module } from '@fluojs/core';
|
|
81
|
+
* import { RedisModule } from '@fluojs/redis';
|
|
82
|
+
*
|
|
83
|
+
* @Module({
|
|
84
|
+
* imports: [RedisModule.forRoot({ host: 'localhost', port: 6379 })],
|
|
85
|
+
* })
|
|
86
|
+
* export class AppModule {}
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
static forRoot(options) {
|
|
90
|
+
class RedisModuleDefinition {}
|
|
91
|
+
return defineModule(RedisModuleDefinition, {
|
|
92
|
+
global: true,
|
|
93
|
+
exports: [REDIS_CLIENT, RedisService],
|
|
94
|
+
providers: createRedisProviders(options)
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Registers one additional named Redis client without changing the default aliases.
|
|
100
|
+
*
|
|
101
|
+
* @param name Stable Redis client name used to derive named raw-client and facade tokens.
|
|
102
|
+
* @param options Redis client options used to construct the named connection.
|
|
103
|
+
* @returns A module definition that exports the named raw-client token and named `RedisService` token.
|
|
104
|
+
*
|
|
105
|
+
* @see getRedisClientToken
|
|
106
|
+
* @see getRedisServiceToken
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* import { Module, Inject } from '@fluojs/core';
|
|
111
|
+
* import type Redis from 'ioredis';
|
|
112
|
+
* import {
|
|
113
|
+
* getRedisClientToken,
|
|
114
|
+
* getRedisServiceToken,
|
|
115
|
+
* RedisModule,
|
|
116
|
+
* RedisService,
|
|
117
|
+
* } from '@fluojs/redis';
|
|
118
|
+
*
|
|
119
|
+
* const ANALYTICS_REDIS = getRedisServiceToken('analytics');
|
|
120
|
+
* const ANALYTICS_REDIS_CLIENT = getRedisClientToken('analytics');
|
|
121
|
+
*
|
|
122
|
+
* @Module({
|
|
123
|
+
* imports: [
|
|
124
|
+
* RedisModule.forRoot({ host: 'localhost', port: 6379 }),
|
|
125
|
+
* RedisModule.forRootNamed('analytics', { host: 'localhost', port: 6380 }),
|
|
126
|
+
* ],
|
|
127
|
+
* })
|
|
128
|
+
* export class AppModule {}
|
|
129
|
+
*
|
|
130
|
+
* @Inject(RedisService, ANALYTICS_REDIS, ANALYTICS_REDIS_CLIENT)
|
|
131
|
+
* export class AnalyticsStore {
|
|
132
|
+
* constructor(
|
|
133
|
+
* private readonly defaultRedis: RedisService,
|
|
134
|
+
* private readonly analyticsRedis: RedisService,
|
|
135
|
+
* private readonly analyticsClient: Redis,
|
|
136
|
+
* ) {}
|
|
137
|
+
* }
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
static forRootNamed(name, options) {
|
|
141
|
+
const clientToken = getRedisClientToken(name);
|
|
142
|
+
const serviceToken = getRedisServiceToken(name);
|
|
143
|
+
class NamedRedisModuleDefinition {}
|
|
144
|
+
return defineModule(NamedRedisModuleDefinition, {
|
|
145
|
+
global: true,
|
|
146
|
+
exports: [clientToken, serviceToken],
|
|
147
|
+
providers: createRedisProviders(options, name)
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type Token } from '@fluojs/core';
|
|
2
|
+
import type Redis from 'ioredis';
|
|
3
|
+
/**
|
|
4
|
+
* JSON-aware facade over the shared Redis client.
|
|
5
|
+
*
|
|
6
|
+
* Use this service for common key-value access when you want Fluo to handle
|
|
7
|
+
* JSON serialization and lifecycle-managed connection reuse.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { Inject } from '@fluojs/core';
|
|
12
|
+
* import { RedisService } from '@fluojs/redis';
|
|
13
|
+
*
|
|
14
|
+
* @Inject(RedisService)
|
|
15
|
+
* export class SessionStore {
|
|
16
|
+
* constructor(private readonly redis: RedisService) {}
|
|
17
|
+
*
|
|
18
|
+
* async save(sessionId: string, value: object) {
|
|
19
|
+
* await this.redis.set(`session:${sessionId}`, value, 300);
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @see getRedisServiceToken For resolving a named `RedisService` binding.
|
|
25
|
+
*/
|
|
26
|
+
export declare class RedisService {
|
|
27
|
+
private readonly client;
|
|
28
|
+
constructor(client: Redis);
|
|
29
|
+
/**
|
|
30
|
+
* Reads one Redis value and attempts to decode JSON payloads automatically.
|
|
31
|
+
*
|
|
32
|
+
* @param key Redis key to fetch from the shared client.
|
|
33
|
+
* @returns The parsed JSON value, the raw string payload, or `null` when the key is missing.
|
|
34
|
+
*/
|
|
35
|
+
get<T>(key: string): Promise<T | string | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Stores one value after JSON serialization.
|
|
38
|
+
*
|
|
39
|
+
* @param key Redis key to write.
|
|
40
|
+
* @param value Serializable value stored as JSON.
|
|
41
|
+
* @param ttlSeconds Optional TTL in seconds. Omit or pass a non-positive value for a persistent key.
|
|
42
|
+
* @returns A promise that resolves after Redis acknowledges the write.
|
|
43
|
+
*/
|
|
44
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Deletes one key from Redis.
|
|
47
|
+
*
|
|
48
|
+
* @param key Redis key to remove.
|
|
49
|
+
* @returns A promise that resolves once the delete command completes.
|
|
50
|
+
*/
|
|
51
|
+
del(key: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Returns the underlying `ioredis` client for advanced commands.
|
|
54
|
+
*
|
|
55
|
+
* @returns The lifecycle-managed raw Redis client instance.
|
|
56
|
+
*/
|
|
57
|
+
getRawClient(): Redis;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolves the facade token for the default or a named `RedisService` binding.
|
|
61
|
+
*
|
|
62
|
+
* @param name Optional Redis client name registered through `RedisModule.forRootNamed(...)`.
|
|
63
|
+
* @returns `RedisService` for the default client path, otherwise a stable named service token.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const ANALYTICS_REDIS = getRedisServiceToken('analytics');
|
|
68
|
+
*
|
|
69
|
+
* @Inject(ANALYTICS_REDIS)
|
|
70
|
+
* export class AnalyticsStore {
|
|
71
|
+
* constructor(private readonly redis: RedisService) {}
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @see RedisModule.forRootNamed
|
|
76
|
+
* @see getRedisClientToken
|
|
77
|
+
*/
|
|
78
|
+
export declare function getRedisServiceToken(name?: string): Token<RedisService>;
|
|
79
|
+
//# sourceMappingURL=redis-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-service.d.ts","sourceRoot":"","sources":["../src/redis-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AA0BjC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBACa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,KAAK;IAE1C;;;;;OAKG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;IASrD;;;;;;;OAOG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASvE;;;;;OAKG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC;;;;OAIG;IACH,YAAY,IAAI,KAAK;CAGtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,CAMvE"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
let _initClass;
|
|
2
|
+
function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
|
|
3
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
4
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
5
|
+
function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
|
|
6
|
+
function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
|
|
7
|
+
import { Inject } from '@fluojs/core';
|
|
8
|
+
import { REDIS_CLIENT, getRedisClientToken } from './tokens.js';
|
|
9
|
+
const namedRedisServiceTokens = new Map();
|
|
10
|
+
function getOrCreateNamedRedisServiceToken(name) {
|
|
11
|
+
const existing = namedRedisServiceTokens.get(name);
|
|
12
|
+
if (existing) {
|
|
13
|
+
return existing;
|
|
14
|
+
}
|
|
15
|
+
const created = Symbol(`fluo.redis.service:${name}`);
|
|
16
|
+
namedRedisServiceTokens.set(name, created);
|
|
17
|
+
return created;
|
|
18
|
+
}
|
|
19
|
+
function decodeRedisValue(raw) {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(raw);
|
|
22
|
+
} catch {
|
|
23
|
+
return raw;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* JSON-aware facade over the shared Redis client.
|
|
29
|
+
*
|
|
30
|
+
* Use this service for common key-value access when you want Fluo to handle
|
|
31
|
+
* JSON serialization and lifecycle-managed connection reuse.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { Inject } from '@fluojs/core';
|
|
36
|
+
* import { RedisService } from '@fluojs/redis';
|
|
37
|
+
*
|
|
38
|
+
* @Inject(RedisService)
|
|
39
|
+
* export class SessionStore {
|
|
40
|
+
* constructor(private readonly redis: RedisService) {}
|
|
41
|
+
*
|
|
42
|
+
* async save(sessionId: string, value: object) {
|
|
43
|
+
* await this.redis.set(`session:${sessionId}`, value, 300);
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @see getRedisServiceToken For resolving a named `RedisService` binding.
|
|
49
|
+
*/
|
|
50
|
+
let _RedisService;
|
|
51
|
+
class RedisService {
|
|
52
|
+
static {
|
|
53
|
+
[_RedisService, _initClass] = _applyDecs(this, [Inject(REDIS_CLIENT)], []).c;
|
|
54
|
+
}
|
|
55
|
+
constructor(client) {
|
|
56
|
+
this.client = client;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Reads one Redis value and attempts to decode JSON payloads automatically.
|
|
61
|
+
*
|
|
62
|
+
* @param key Redis key to fetch from the shared client.
|
|
63
|
+
* @returns The parsed JSON value, the raw string payload, or `null` when the key is missing.
|
|
64
|
+
*/
|
|
65
|
+
async get(key) {
|
|
66
|
+
const raw = await this.client.get(key);
|
|
67
|
+
if (raw === null) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return decodeRedisValue(raw);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Stores one value after JSON serialization.
|
|
75
|
+
*
|
|
76
|
+
* @param key Redis key to write.
|
|
77
|
+
* @param value Serializable value stored as JSON.
|
|
78
|
+
* @param ttlSeconds Optional TTL in seconds. Omit or pass a non-positive value for a persistent key.
|
|
79
|
+
* @returns A promise that resolves after Redis acknowledges the write.
|
|
80
|
+
*/
|
|
81
|
+
async set(key, value, ttlSeconds) {
|
|
82
|
+
const serialized = JSON.stringify(value);
|
|
83
|
+
if (ttlSeconds !== undefined && ttlSeconds > 0) {
|
|
84
|
+
await this.client.set(key, serialized, 'EX', ttlSeconds);
|
|
85
|
+
} else {
|
|
86
|
+
await this.client.set(key, serialized);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Deletes one key from Redis.
|
|
92
|
+
*
|
|
93
|
+
* @param key Redis key to remove.
|
|
94
|
+
* @returns A promise that resolves once the delete command completes.
|
|
95
|
+
*/
|
|
96
|
+
async del(key) {
|
|
97
|
+
await this.client.del(key);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns the underlying `ioredis` client for advanced commands.
|
|
102
|
+
*
|
|
103
|
+
* @returns The lifecycle-managed raw Redis client instance.
|
|
104
|
+
*/
|
|
105
|
+
getRawClient() {
|
|
106
|
+
return this.client;
|
|
107
|
+
}
|
|
108
|
+
static {
|
|
109
|
+
_initClass();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Resolves the facade token for the default or a named `RedisService` binding.
|
|
115
|
+
*
|
|
116
|
+
* @param name Optional Redis client name registered through `RedisModule.forRootNamed(...)`.
|
|
117
|
+
* @returns `RedisService` for the default client path, otherwise a stable named service token.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* const ANALYTICS_REDIS = getRedisServiceToken('analytics');
|
|
122
|
+
*
|
|
123
|
+
* @Inject(ANALYTICS_REDIS)
|
|
124
|
+
* export class AnalyticsStore {
|
|
125
|
+
* constructor(private readonly redis: RedisService) {}
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @see RedisModule.forRootNamed
|
|
130
|
+
* @see getRedisClientToken
|
|
131
|
+
*/
|
|
132
|
+
export { _RedisService as RedisService };
|
|
133
|
+
export function getRedisServiceToken(name) {
|
|
134
|
+
if (getRedisClientToken(name) === REDIS_CLIENT) {
|
|
135
|
+
return _RedisService;
|
|
136
|
+
}
|
|
137
|
+
return getOrCreateNamedRedisServiceToken(name?.trim() ?? '');
|
|
138
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type Redis from 'ioredis';
|
|
2
|
+
import type { OnApplicationShutdown, OnModuleInit } from '@fluojs/runtime';
|
|
3
|
+
/**
|
|
4
|
+
* Manages Redis client startup and shutdown as part of the application lifecycle.
|
|
5
|
+
*/
|
|
6
|
+
export declare class RedisLifecycleService implements OnModuleInit, OnApplicationShutdown {
|
|
7
|
+
private readonly client;
|
|
8
|
+
private readonly clientName?;
|
|
9
|
+
constructor(client: Redis, clientName?: string | undefined);
|
|
10
|
+
onModuleInit(): Promise<void>;
|
|
11
|
+
onApplicationShutdown(): Promise<void>;
|
|
12
|
+
createPlatformStatusSnapshot(): import("./status.js").PersistencePlatformStatusSnapshot;
|
|
13
|
+
private shouldConnectOnInit;
|
|
14
|
+
private disconnectIfPossible;
|
|
15
|
+
private quitWithDisconnectFallback;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAwB3E;;GAEG;AACH,qBACa,qBAAsB,YAAW,YAAY,EAAE,qBAAqB;IAE7E,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;gBADX,MAAM,EAAE,KAAK,EACb,UAAU,CAAC,EAAE,MAAM,YAAA;IAGhC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5C,4BAA4B;IAO5B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,oBAAoB;YAMd,0BAA0B;CAYzC"}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
let _initClass;
|
|
2
|
+
function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
|
|
3
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
4
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
5
|
+
function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
|
|
6
|
+
function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
|
|
7
|
+
import { Inject } from '@fluojs/core';
|
|
8
|
+
import { createRedisPlatformStatusSnapshot } from './status.js';
|
|
9
|
+
import { getRedisComponentId, REDIS_CLIENT } from './tokens.js';
|
|
10
|
+
const QUITTABLE_STATUSES = new Set(['connect', 'connecting', 'ready', 'reconnecting']);
|
|
11
|
+
const DISCONNECTABLE_STATUSES = new Set(['close', 'connect', 'connecting', 'ready', 'reconnecting', 'wait']);
|
|
12
|
+
function isClosed(status) {
|
|
13
|
+
return status === 'end';
|
|
14
|
+
}
|
|
15
|
+
function isConnectable(status) {
|
|
16
|
+
return status === 'wait';
|
|
17
|
+
}
|
|
18
|
+
function isQuittable(status) {
|
|
19
|
+
return QUITTABLE_STATUSES.has(status);
|
|
20
|
+
}
|
|
21
|
+
function isDisconnectable(status) {
|
|
22
|
+
return DISCONNECTABLE_STATUSES.has(status);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Manages Redis client startup and shutdown as part of the application lifecycle.
|
|
27
|
+
*/
|
|
28
|
+
let _RedisLifecycleServic;
|
|
29
|
+
class RedisLifecycleService {
|
|
30
|
+
static {
|
|
31
|
+
[_RedisLifecycleServic, _initClass] = _applyDecs(this, [Inject(REDIS_CLIENT)], []).c;
|
|
32
|
+
}
|
|
33
|
+
constructor(client, clientName) {
|
|
34
|
+
this.client = client;
|
|
35
|
+
this.clientName = clientName;
|
|
36
|
+
}
|
|
37
|
+
async onModuleInit() {
|
|
38
|
+
if (!this.shouldConnectOnInit()) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
await this.client.connect();
|
|
42
|
+
}
|
|
43
|
+
async onApplicationShutdown() {
|
|
44
|
+
const status = this.client.status;
|
|
45
|
+
if (isClosed(status)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!isQuittable(status)) {
|
|
49
|
+
this.disconnectIfPossible(status);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
await this.quitWithDisconnectFallback();
|
|
53
|
+
}
|
|
54
|
+
createPlatformStatusSnapshot() {
|
|
55
|
+
return createRedisPlatformStatusSnapshot({
|
|
56
|
+
componentId: getRedisComponentId(this.clientName),
|
|
57
|
+
status: this.client.status
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
shouldConnectOnInit() {
|
|
61
|
+
return isConnectable(this.client.status);
|
|
62
|
+
}
|
|
63
|
+
disconnectIfPossible(status) {
|
|
64
|
+
if (isDisconnectable(status)) {
|
|
65
|
+
this.client.disconnect();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async quitWithDisconnectFallback() {
|
|
69
|
+
try {
|
|
70
|
+
await this.client.quit();
|
|
71
|
+
return;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
this.disconnectIfPossible(this.client.status);
|
|
74
|
+
if (!isClosed(this.client.status)) {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
static {
|
|
80
|
+
_initClass();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export { _RedisLifecycleServic as RedisLifecycleService };
|
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type Redis from 'ioredis';
|
|
2
|
+
import type { PlatformHealthReport, PlatformReadinessReport, PlatformSnapshot } from '@fluojs/runtime';
|
|
3
|
+
/** Normalized Redis platform snapshot shape used by health/readiness integrations. */
|
|
4
|
+
export interface PersistencePlatformStatusSnapshot {
|
|
5
|
+
readiness: PlatformReadinessReport;
|
|
6
|
+
health: PlatformHealthReport;
|
|
7
|
+
ownership: PlatformSnapshot['ownership'];
|
|
8
|
+
details: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
/** Input consumed by the Redis status adapter when translating runtime state. */
|
|
11
|
+
export interface RedisStatusAdapterInput {
|
|
12
|
+
componentId?: string;
|
|
13
|
+
status: Redis['status'];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Adapts one Redis client status into Fluo's platform snapshot contract.
|
|
17
|
+
*
|
|
18
|
+
* @param input Redis client status and optional component identity.
|
|
19
|
+
* @returns A normalized snapshot describing readiness, health, ownership, and details.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createRedisPlatformStatusSnapshot(input: RedisStatusAdapterInput): PersistencePlatformStatusSnapshot;
|
|
22
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,sFAAsF;AACtF,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,iFAAiF;AACjF,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;CACzB;AAwDD;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,uBAAuB,GAAG,iCAAiC,CAcnH"}
|
package/dist/status.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Normalized Redis platform snapshot shape used by health/readiness integrations. */
|
|
2
|
+
|
|
3
|
+
/** Input consumed by the Redis status adapter when translating runtime state. */
|
|
4
|
+
|
|
5
|
+
const DEGRADATION_STATUSES = new Set(['wait', 'connecting', 'connect', 'reconnecting']);
|
|
6
|
+
const UNHEALTHY_STATUSES = new Set(['close', 'end']);
|
|
7
|
+
function createReadiness(input) {
|
|
8
|
+
if (input.status === 'ready') {
|
|
9
|
+
return {
|
|
10
|
+
critical: true,
|
|
11
|
+
status: 'ready'
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (input.status === 'wait') {
|
|
15
|
+
return {
|
|
16
|
+
critical: true,
|
|
17
|
+
reason: 'Redis client is still in lazyConnect wait state.',
|
|
18
|
+
status: 'not-ready'
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (input.status === 'connect' || input.status === 'connecting' || input.status === 'reconnecting') {
|
|
22
|
+
return {
|
|
23
|
+
critical: true,
|
|
24
|
+
reason: `Redis client is ${input.status}.`,
|
|
25
|
+
status: 'degraded'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
critical: true,
|
|
30
|
+
reason: `Redis client is ${input.status}.`,
|
|
31
|
+
status: 'not-ready'
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function createHealth(input) {
|
|
35
|
+
if (UNHEALTHY_STATUSES.has(input.status)) {
|
|
36
|
+
return {
|
|
37
|
+
reason: `Redis client is ${input.status}.`,
|
|
38
|
+
status: 'unhealthy'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (DEGRADATION_STATUSES.has(input.status)) {
|
|
42
|
+
return {
|
|
43
|
+
reason: `Redis client is ${input.status}.`,
|
|
44
|
+
status: 'degraded'
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
status: 'healthy'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Adapts one Redis client status into Fluo's platform snapshot contract.
|
|
54
|
+
*
|
|
55
|
+
* @param input Redis client status and optional component identity.
|
|
56
|
+
* @returns A normalized snapshot describing readiness, health, ownership, and details.
|
|
57
|
+
*/
|
|
58
|
+
export function createRedisPlatformStatusSnapshot(input) {
|
|
59
|
+
return {
|
|
60
|
+
details: {
|
|
61
|
+
componentId: input.componentId,
|
|
62
|
+
connectionState: input.status,
|
|
63
|
+
lazyConnect: true
|
|
64
|
+
},
|
|
65
|
+
health: createHealth(input),
|
|
66
|
+
ownership: {
|
|
67
|
+
externallyManaged: false,
|
|
68
|
+
ownsResources: true
|
|
69
|
+
},
|
|
70
|
+
readiness: createReadiness(input)
|
|
71
|
+
};
|
|
72
|
+
}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Injection token for the shared default `ioredis` client managed by {@link RedisModule}.
|
|
3
|
+
*
|
|
4
|
+
* @see getRedisClientToken For resolving the default or a named raw-client token through one helper.
|
|
5
|
+
*/
|
|
6
|
+
export declare const REDIS_CLIENT: unique symbol;
|
|
7
|
+
/** Stable name used when callers request the default Redis client contract. */
|
|
8
|
+
export declare const DEFAULT_REDIS_CLIENT_NAME = "default";
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the DI token for the default or a named Redis client.
|
|
11
|
+
*
|
|
12
|
+
* @param name Optional Redis client name registered through `RedisModule.forRootNamed(...)`.
|
|
13
|
+
* @returns The default `REDIS_CLIENT` token when `name` is omitted, otherwise a stable named token.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const CACHE_REDIS = getRedisClientToken('cache');
|
|
18
|
+
*
|
|
19
|
+
* @Inject(CACHE_REDIS)
|
|
20
|
+
* export class CacheInspector {
|
|
21
|
+
* constructor(private readonly redis: Redis) {}
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @see RedisModule.forRootNamed
|
|
26
|
+
* @see getRedisServiceToken
|
|
27
|
+
*/
|
|
28
|
+
export declare function getRedisClientToken(name?: string): symbol;
|
|
29
|
+
/**
|
|
30
|
+
* Builds the stable component/dependency id used by platform status snapshots.
|
|
31
|
+
*
|
|
32
|
+
* @param name Optional Redis client name registered through `RedisModule.forRootNamed(...)`.
|
|
33
|
+
* @returns A stable component id such as `redis.default` or `redis.jobs`.
|
|
34
|
+
*
|
|
35
|
+
* @see getRedisClientToken
|
|
36
|
+
*/
|
|
37
|
+
export declare function getRedisComponentId(name?: string): string;
|
|
38
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,YAAY,eAA8B,CAAC;AAExD,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,YAAY,CAAC;AA8BnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzD"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Injection token for the shared default `ioredis` client managed by {@link RedisModule}.
|
|
3
|
+
*
|
|
4
|
+
* @see getRedisClientToken For resolving the default or a named raw-client token through one helper.
|
|
5
|
+
*/
|
|
6
|
+
export const REDIS_CLIENT = Symbol('fluo.redis.client');
|
|
7
|
+
|
|
8
|
+
/** Stable name used when callers request the default Redis client contract. */
|
|
9
|
+
export const DEFAULT_REDIS_CLIENT_NAME = 'default';
|
|
10
|
+
function normalizeRedisClientName(name) {
|
|
11
|
+
if (name === undefined) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const normalized = name.trim();
|
|
15
|
+
if (normalized.length === 0) {
|
|
16
|
+
throw new Error('Redis client name must be a non-empty string.');
|
|
17
|
+
}
|
|
18
|
+
return normalized;
|
|
19
|
+
}
|
|
20
|
+
const namedRedisClientTokens = new Map();
|
|
21
|
+
function getOrCreateNamedRedisClientToken(name) {
|
|
22
|
+
const existing = namedRedisClientTokens.get(name);
|
|
23
|
+
if (existing) {
|
|
24
|
+
return existing;
|
|
25
|
+
}
|
|
26
|
+
const created = Symbol(`fluo.redis.client:${name}`);
|
|
27
|
+
namedRedisClientTokens.set(name, created);
|
|
28
|
+
return created;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolves the DI token for the default or a named Redis client.
|
|
33
|
+
*
|
|
34
|
+
* @param name Optional Redis client name registered through `RedisModule.forRootNamed(...)`.
|
|
35
|
+
* @returns The default `REDIS_CLIENT` token when `name` is omitted, otherwise a stable named token.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const CACHE_REDIS = getRedisClientToken('cache');
|
|
40
|
+
*
|
|
41
|
+
* @Inject(CACHE_REDIS)
|
|
42
|
+
* export class CacheInspector {
|
|
43
|
+
* constructor(private readonly redis: Redis) {}
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @see RedisModule.forRootNamed
|
|
48
|
+
* @see getRedisServiceToken
|
|
49
|
+
*/
|
|
50
|
+
export function getRedisClientToken(name) {
|
|
51
|
+
const normalizedName = normalizeRedisClientName(name);
|
|
52
|
+
if (normalizedName === undefined) {
|
|
53
|
+
return REDIS_CLIENT;
|
|
54
|
+
}
|
|
55
|
+
return getOrCreateNamedRedisClientToken(normalizedName);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Builds the stable component/dependency id used by platform status snapshots.
|
|
60
|
+
*
|
|
61
|
+
* @param name Optional Redis client name registered through `RedisModule.forRootNamed(...)`.
|
|
62
|
+
* @returns A stable component id such as `redis.default` or `redis.jobs`.
|
|
63
|
+
*
|
|
64
|
+
* @see getRedisClientToken
|
|
65
|
+
*/
|
|
66
|
+
export function getRedisComponentId(name) {
|
|
67
|
+
return `redis.${normalizeRedisClientName(name) ?? DEFAULT_REDIS_CLIENT_NAME}`;
|
|
68
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RedisOptions } from 'ioredis';
|
|
2
|
+
/**
|
|
3
|
+
* Options accepted by {@link RedisModule.forRoot}.
|
|
4
|
+
*
|
|
5
|
+
* Fluo always enables `lazyConnect` internally so the client connects during
|
|
6
|
+
* application bootstrap instead of import time.
|
|
7
|
+
*/
|
|
8
|
+
export type RedisModuleOptions = Omit<RedisOptions, 'lazyConnect'>;
|
|
9
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fluojs/redis",
|
|
3
|
+
"description": "App-scoped Redis lifecycle management, raw client injection, and RedisService facade for Fluo.",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"fluo",
|
|
6
|
+
"redis",
|
|
7
|
+
"ioredis",
|
|
8
|
+
"cache",
|
|
9
|
+
"connection",
|
|
10
|
+
"lifecycle"
|
|
11
|
+
],
|
|
12
|
+
"version": "1.0.0-beta.1",
|
|
13
|
+
"private": false,
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/fluojs/fluo.git",
|
|
18
|
+
"directory": "packages/redis"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20.0.0"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"ioredis": "^5.10.0",
|
|
40
|
+
"@fluojs/runtime": "^1.0.0-beta.1",
|
|
41
|
+
"@fluojs/di": "^1.0.0-beta.1",
|
|
42
|
+
"@fluojs/core": "^1.0.0-beta.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"vitest": "^3.2.4"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|
|
49
|
+
"build": "pnpm exec babel src --extensions .ts --ignore 'src/**/*.test.ts' --out-dir dist --config-file ../../tooling/babel/babel.config.cjs && pnpm exec tsc -p tsconfig.build.json",
|
|
50
|
+
"typecheck": "pnpm exec tsc -p tsconfig.json --noEmit",
|
|
51
|
+
"test": "pnpm exec vitest run -c vitest.config.ts",
|
|
52
|
+
"test:watch": "pnpm exec vitest -c vitest.config.ts"
|
|
53
|
+
}
|
|
54
|
+
}
|