@beignet/provider-redis 0.0.2 → 0.0.4

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 CHANGED
@@ -1,5 +1,53 @@
1
1
  # @beignet/provider-redis
2
2
 
3
+ ## 0.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 8bcb31f: Mark package READMEs with Beignet's experimental alpha status and 0.0.x stability expectations.
8
+ - d137044: Declare `@beignet/core` as a peer dependency with a lockstep version range in
9
+ every integration and provider package instead of a regular `"*"` dependency.
10
+ Installs now always resolve a single shared copy of core, so `instanceof`
11
+ checks such as `isContractError` and upload error identity keep working, and
12
+ mixed Beignet versions fail loudly at install time instead of at runtime.
13
+
14
+ If your package manager does not install peer dependencies automatically, add
15
+ `@beignet/core` to your app alongside these packages. `@beignet/nuqs` now also
16
+ declares `@beignet/react-query` as a peer dependency, and
17
+ `@beignet/provider-storage-s3` now expects you to install
18
+ `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner` yourself, matching
19
+ how other providers treat their SDKs.
20
+
21
+ - 603478f: Align package documentation with the canonical route registry and AppContext conventions.
22
+ - 1a79090: Emit Node-compatible ESM: all relative imports in published packages now carry explicit .js extensions, fixing ERR_MODULE_NOT_FOUND when running the CLI or importing package dist files under plain Node.
23
+ - 44f1192: Move first-party provider diagnostics to package-owned `beignet.provider`
24
+ manifest metadata and have doctor read installed provider package manifests.
25
+ - 2aa77ca: Add static provider metadata and provider wiring diagnostics for generated apps.
26
+ - 89390fe: Add `createRedisProvider(options)` factory with connection robustness controls
27
+ and move the raw client escape hatch to a separate port.
28
+
29
+ - New config: `REDIS_CONNECT_TIMEOUT_MS` (default 5000),
30
+ `REDIS_MAX_RETRIES_PER_REQUEST` (default 2), and
31
+ `REDIS_CONNECT_MAX_ATTEMPTS` (default 3), with matching factory options
32
+ `connectTimeoutMs`, `maxRetriesPerRequest`, `retryStrategy`, and `db`.
33
+ - Startup now fails fast with a clear error when Redis is unreachable instead
34
+ of retrying forever. After a successful connection, reconnects use capped
35
+ exponential backoff (max 2s).
36
+ - The provider now contributes `{ cache: CachePort, redis: { client } }`.
37
+ `RedisCachePort` is replaced by the exported `RedisProviderPorts` interface;
38
+ access the raw ioredis client through `ctx.ports.redis.client` instead of
39
+ casting `ctx.ports.cache`.
40
+
41
+ ## 0.0.3
42
+
43
+ ### Patch Changes
44
+
45
+ - Updated dependencies [3160184]
46
+ - Updated dependencies [254ef6d]
47
+ - Updated dependencies [4cb1784]
48
+ - Updated dependencies [8bd9085]
49
+ - @beignet/core@0.0.3
50
+
3
51
  ## 0.0.2
4
52
 
5
53
  ### Patch Changes
package/README.md CHANGED
@@ -1,15 +1,19 @@
1
1
  # @beignet/provider-redis
2
2
 
3
+ > [!CAUTION]
4
+ > Beignet is experimental alpha software. The `0.0.x` package line is for early
5
+ > evaluation, and APIs may change between releases while the framework settles.
6
+
3
7
  Redis-backed `CachePort` provider for Beignet applications.
4
8
 
5
9
  The provider installs `ctx.ports.cache` using
6
- [ioredis](https://github.com/redis/ioredis) and exposes the Redis client only
7
- as an escape hatch for Redis-specific features.
10
+ [ioredis](https://github.com/redis/ioredis) and exposes the raw Redis client
11
+ separately as `ctx.ports.redis` for Redis-specific features.
8
12
 
9
13
  ## Install
10
14
 
11
15
  ```bash
12
- bun add @beignet/provider-redis ioredis
16
+ bun add @beignet/provider-redis @beignet/core ioredis
13
17
  ```
14
18
 
15
19
  ## Setup
@@ -29,7 +33,7 @@ const appPorts = definePorts({});
29
33
  export const server = await createNextServer({
30
34
  ports: appPorts,
31
35
  providers: [redisProvider],
32
- createContext: ({ ports }) => ({
36
+ context: ({ ports }) => ({
33
37
  ports,
34
38
  }),
35
39
  routes,
@@ -42,7 +46,7 @@ Once the provider is registered, your ports will include a `cache` property:
42
46
 
43
47
  ```typescript
44
48
  // In your use case
45
- async function getUserProfile(ctx: AppCtx) {
49
+ async function getUserProfile(ctx: AppContext) {
46
50
  const userId = ctx.actor.type === "user" ? ctx.actor.id : undefined;
47
51
  if (!userId) throw new Error("User actor required.");
48
52
 
@@ -76,6 +80,38 @@ The Redis provider reads configuration from environment variables with the `REDI
76
80
  |----------|----------|-------------|---------|
77
81
  | `REDIS_URL` | Yes | Redis connection URL | `redis://localhost:6379` |
78
82
  | `REDIS_DB` | No | Redis database number (default: 0) | `0` |
83
+ | `REDIS_CONNECT_TIMEOUT_MS` | No | Initial connection timeout in milliseconds (default: 5000) | `2000` |
84
+ | `REDIS_MAX_RETRIES_PER_REQUEST` | No | Per-command retry limit before the command rejects (default: 2) | `1` |
85
+ | `REDIS_CONNECT_MAX_ATTEMPTS` | No | Connection attempts before startup fails (default: 3) | `5` |
86
+
87
+ ### Factory options
88
+
89
+ Use `createRedisProvider(options)` when the app should own connection
90
+ defaults. Matching `REDIS_*` environment variables still win when both are
91
+ set:
92
+
93
+ ```typescript
94
+ import { createRedisProvider } from "@beignet/provider-redis";
95
+
96
+ const redisProvider = createRedisProvider({
97
+ connectTimeoutMs: 2000,
98
+ maxRetriesPerRequest: 1,
99
+ db: 1,
100
+ // Optional: replace the default retry strategy entirely.
101
+ retryStrategy: (times) => Math.min(times * 100, 1000),
102
+ });
103
+ ```
104
+
105
+ The default `redisProvider` export is `createRedisProvider()` with no options.
106
+
107
+ ### Connection behavior
108
+
109
+ During startup the provider connects eagerly and fails fast: the initial
110
+ `connect()` stops retrying after `REDIS_CONNECT_MAX_ATTEMPTS` attempts (or
111
+ after `REDIS_CONNECT_TIMEOUT_MS` per attempt) and throws a clear error instead
112
+ of hanging against an unreachable host. After a successful connection, lost
113
+ connections are retried forever with capped exponential backoff (maximum 2
114
+ seconds between attempts).
79
115
 
80
116
  ## Cache port API
81
117
 
@@ -130,16 +166,20 @@ const value = await ctx.ports.cache.remember(
130
166
  );
131
167
  ```
132
168
 
133
- ### `client: Redis`
169
+ ## Escape hatch
134
170
 
135
- Access the underlying ioredis client for advanced operations.
171
+ The provider also contributes `ctx.ports.redis` with the raw ioredis client
172
+ for operations the stable cache port does not model:
136
173
 
137
174
  ```typescript
138
175
  // Use ioredis methods directly
139
- await ctx.ports.cache.client.expire("key", 300);
140
- await ctx.ports.cache.client.incr("counter");
176
+ await ctx.ports.redis.client.expire("key", 300);
177
+ await ctx.ports.redis.client.incr("counter");
141
178
  ```
142
179
 
180
+ Use the stable `CachePort` for normal application behavior. Use the raw client
181
+ only when the Redis-specific operation is intentional.
182
+
143
183
  ## Devtools
144
184
 
145
185
  When `@beignet/devtools` is installed before this provider, Redis cache
@@ -151,25 +191,26 @@ duration. Cached values are not recorded.
151
191
 
152
192
  ## TypeScript support
153
193
 
154
- To get proper type inference for the cache port, extend your ports type:
194
+ To get proper type inference for the contributed ports, extend your ports
195
+ type with `RedisProviderPorts`:
155
196
 
156
197
  ```typescript
157
- import type { RedisCachePort } from "@beignet/provider-redis";
198
+ import type { RedisProviderPorts } from "@beignet/provider-redis";
158
199
 
159
200
  // Your base ports, if any
160
201
  const basePorts = definePorts({});
161
202
 
162
203
  // After using redisProvider, your ports will have this shape:
163
- type AppPorts = typeof basePorts & {
164
- cache: RedisCachePort;
165
- };
204
+ type AppPorts = typeof basePorts & RedisProviderPorts;
205
+ // { cache: CachePort; redis: { client: Redis } }
166
206
  ```
167
207
 
168
208
  ## Lifecycle
169
209
 
170
210
  The Redis provider:
171
211
 
172
- 1. **During `setup`**: Connects to Redis and returns the `cache` port
212
+ 1. **During `setup`**: Connects to Redis and returns the `cache` and `redis`
213
+ ports
173
214
  2. **During `stop`**: Gracefully closes the Redis connection
174
215
 
175
216
  ## Error handling
@@ -177,7 +218,8 @@ The Redis provider:
177
218
  The provider will throw errors in these cases:
178
219
 
179
220
  - Missing `REDIS_URL` environment variable
180
- - Failed connection to Redis server
221
+ - Failed connection to Redis server after `REDIS_CONNECT_MAX_ATTEMPTS`
222
+ attempts
181
223
 
182
224
  Make sure to handle these during application startup.
183
225
 
package/dist/index.d.ts CHANGED
@@ -6,6 +6,9 @@
6
6
  * Configuration:
7
7
  * - REDIS_URL: Redis connection URL (required)
8
8
  * - REDIS_DB: Redis database number (optional, default: 0)
9
+ * - REDIS_CONNECT_TIMEOUT_MS: Initial connection timeout in milliseconds (optional, default: 5000)
10
+ * - REDIS_MAX_RETRIES_PER_REQUEST: Per-command retry limit (optional, default: 2)
11
+ * - REDIS_CONNECT_MAX_ATTEMPTS: Connection attempts before startup fails (optional, default: 3)
9
12
  *
10
13
  * @example
11
14
  * ```ts
@@ -29,7 +32,7 @@
29
32
  * ```
30
33
  */
31
34
  import type { CachePort } from "@beignet/core/ports";
32
- import Redis from "ioredis";
35
+ import { Redis } from "ioredis";
33
36
  import { z } from "zod";
34
37
  /**
35
38
  * Configuration schema for the Redis provider.
@@ -37,29 +40,102 @@ import { z } from "zod";
37
40
  */
38
41
  declare const RedisConfigSchema: z.ZodObject<{
39
42
  URL: z.ZodString;
40
- DB: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
43
+ DB: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
44
+ CONNECT_TIMEOUT_MS: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
45
+ MAX_RETRIES_PER_REQUEST: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
46
+ CONNECT_MAX_ATTEMPTS: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
41
47
  }, z.core.$strip>;
42
48
  /**
43
49
  * Inferred configuration type for Redis provider.
44
50
  */
45
51
  export type RedisConfig = z.infer<typeof RedisConfigSchema>;
46
52
  /**
47
- * Extended cache port interface that includes the Redis client.
48
- * The Redis provider adds the underlying client for advanced operations.
53
+ * Options for {@link createRedisProvider}.
54
+ *
55
+ * Numeric options become schema defaults, so matching `REDIS_*` environment
56
+ * variables still win when both are set.
57
+ */
58
+ export interface RedisProviderOptions {
59
+ /**
60
+ * Timeout for the initial connection attempt, in milliseconds.
61
+ *
62
+ * @default 5000
63
+ */
64
+ connectTimeoutMs?: number;
65
+ /**
66
+ * How many times a single command is retried before its promise rejects.
67
+ *
68
+ * @default 2
69
+ */
70
+ maxRetriesPerRequest?: number;
71
+ /**
72
+ * Custom ioredis retry strategy. Replaces the default strategy, which stops
73
+ * retrying after `REDIS_CONNECT_MAX_ATTEMPTS` during the initial connect and
74
+ * reconnects with capped exponential backoff afterwards.
75
+ */
76
+ retryStrategy?: (times: number) => number | null;
77
+ /**
78
+ * Redis database number.
79
+ *
80
+ * @default 0
81
+ */
82
+ db?: number;
83
+ }
84
+ /**
85
+ * Escape hatch for apps that need the raw ioredis client.
49
86
  */
50
- export interface RedisCachePort extends CachePort {
87
+ export interface RedisEscapeHatch {
51
88
  /**
52
- * The underlying Redis client instance.
53
- * Use this for advanced operations not covered by the cache interface.
89
+ * Raw ioredis client.
90
+ * Use this for Redis operations the stable cache port does not model.
54
91
  */
55
92
  client: Redis;
56
93
  }
57
94
  /**
58
- * Redis provider that extends ports with cache capabilities.
95
+ * Ports contributed by the Redis provider.
96
+ */
97
+ export interface RedisProviderPorts {
98
+ /**
99
+ * Beignet cache port backed by Redis.
100
+ */
101
+ cache: CachePort;
102
+ /**
103
+ * Raw ioredis client escape hatch.
104
+ */
105
+ redis: RedisEscapeHatch;
106
+ }
107
+ /**
108
+ * Create an env-backed Redis cache provider.
109
+ *
110
+ * Reads `REDIS_*` env vars, contributes `ports.cache`, and exposes
111
+ * `ports.redis` for raw ioredis client access.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * const provider = createRedisProvider({ connectTimeoutMs: 2000 });
116
+ * ```
117
+ */
118
+ export declare function createRedisProvider(options?: RedisProviderOptions): import("@beignet/core/providers").ServiceProvider<unknown, z.ZodObject<{
119
+ URL: z.ZodString;
120
+ CONNECT_MAX_ATTEMPTS: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
121
+ DB: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>> | z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
122
+ CONNECT_TIMEOUT_MS: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
123
+ MAX_RETRIES_PER_REQUEST: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
124
+ }, z.core.$strip>, {
125
+ cache: CachePort;
126
+ redis: {
127
+ client: Redis;
128
+ };
129
+ }, unknown, void>;
130
+ /**
131
+ * Default env-backed Redis provider.
59
132
  *
60
133
  * Configuration via environment variables:
61
134
  * - REDIS_URL: Redis connection URL (required)
62
135
  * - REDIS_DB: Redis database number (optional)
136
+ * - REDIS_CONNECT_TIMEOUT_MS: Initial connection timeout in ms (optional)
137
+ * - REDIS_MAX_RETRIES_PER_REQUEST: Per-command retry limit (optional)
138
+ * - REDIS_CONNECT_MAX_ATTEMPTS: Connection attempts before startup fails (optional)
63
139
  *
64
140
  * @example
65
141
  * ```ts
@@ -72,9 +148,15 @@ export interface RedisCachePort extends CachePort {
72
148
  */
73
149
  export declare const redisProvider: import("@beignet/core/providers").ServiceProvider<unknown, z.ZodObject<{
74
150
  URL: z.ZodString;
75
- DB: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
151
+ CONNECT_MAX_ATTEMPTS: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
152
+ DB: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>> | z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
153
+ CONNECT_TIMEOUT_MS: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
154
+ MAX_RETRIES_PER_REQUEST: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>>;
76
155
  }, z.core.$strip>, {
77
- cache: RedisCachePort;
78
- }>;
156
+ cache: CachePort;
157
+ redis: {
158
+ client: Redis;
159
+ };
160
+ }, unknown, void>;
79
161
  export {};
80
162
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,QAAA,MAAM,iBAAiB;;;iBAYrB,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C;;;OAGG;IACH,MAAM,EAAE,KAAK,CAAC;CACf;AAMD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa;;;;;EAmPxB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB;;;GAGG;AACH,QAAA,MAAM,iBAAiB;;;;;;iBAgCrB,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAEjD;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,EAAE,KAAK,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,KAAK,EAAE,gBAAgB,CAAC;CACzB;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB;;;;;;;;;;;kBAoSrE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;iBAAwB,CAAC"}