@alxia/redis 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,19 @@
1
1
  # @alxia/redis
2
2
 
3
3
  Redis for [alxia](https://www.npmjs.com/package/@alxia/core), on
4
- [`@nxgt/redis`](https://www.npmjs.com/package/@nxgt/redis) and
5
- [`@nxgt/redis-guard`](https://www.npmjs.com/package/@nxgt/redis-guard) —
4
+ [`@nxgt/redis`](https://www.npmjs.com/package/@nxgt/redis) —
6
5
  Bun's own Redis client, no driver, no dependency:
7
6
 
8
7
  - `redisStore`: a rate-limit store every process shares;
9
- - `idempotency`: routes that run once per `Idempotency-Key`;
8
+ - `idempotency`: a middleware, so routes run once per `Idempotency-Key`;
10
9
  - `redisCacheStore`: an `@alxia/cache` store every process shares;
11
- - `redis`: the client, typed caches and a lock in the context.
10
+ - `redis`: the client, typed caches and a lock in the context;
11
+ - `redisCheck`: a readiness check for core's `health()`.
12
+
13
+ Each takes Bun's `RedisClient`, or an [`@nxgt/redis`](https://www.npmjs.com/package/@nxgt/redis) handle: see [With @nxgt/redis](#with-nxgtredis).
12
14
 
13
15
  ```sh
14
- bun add @alxia/redis @nxgt/redis @nxgt/redis-guard zod @alxia/core
16
+ bun add @alxia/redis @nxgt/redis zod @alxia/core
15
17
  bun add -d typescript
16
18
  ```
17
19
 
@@ -68,7 +70,7 @@ the response: the route runs, and the error is logged.
68
70
  ## Idempotent routes
69
71
 
70
72
  ```ts
71
- import { alxia } from '@alxia/core';
73
+ import { alxia, validate } from '@alxia/core';
72
74
  import { idempotency } from '@alxia/redis';
73
75
  import { connectRedis } from '@nxgt/redis';
74
76
  import { z } from 'zod';
@@ -78,7 +80,7 @@ const Payment = z.object({ amount: z.number().int().positive() });
78
80
 
79
81
  const app = alxia()
80
82
  .use(idempotency(connection.client, { name: 'payments', required: true }))
81
- .post('/payments', { body: Payment }, ({ body, reply }) =>
83
+ .post('/payments', validate({ body: Payment }), ({ body, reply }) =>
82
84
  reply(201, { id: crypto.randomUUID(), amount: body.amount }),
83
85
  );
84
86
  ```
@@ -95,12 +97,16 @@ the first response back — status, headers, body — with
95
97
  | no key, with `required` | `400 { error: 'idempotency_key_missing' }` |
96
98
  | the route answers a 5xx, or streams | answered, not kept: the key is free again |
97
99
 
98
- Every one is part of the guarded routes' types. Keys are scoped by the
100
+ Only the routes after the middleware answer them; a request no route matches
101
+ is not guarded: there is no route to scope its key by. What is kept is what the
102
+ route answers, an error's answer included. Keys are scoped by the
99
103
  route and by `scope(ctx)` — the client's address by default, a user id
100
104
  when there is one — so two clients choosing the same key never see each
101
- other's response. A replay never repeats `Set-Cookie`. Every response
105
+ other's response. A request with no scope — no address, no `scope` — runs
106
+ unguarded, nothing stored or replayed, and the middleware warns once. A replay never repeats `Set-Cookie`. Every response
102
107
  below 500 is kept, a 4xx included: declare a rate limit or an auth check
103
- **before** `idempotency`, or its refusal is replayed.
108
+ **before** `idempotency`, or its refusal is replayed (`app.use` in
109
+ declaration order: the guard first, then `idempotency`).
104
110
 
105
111
  | option | default | |
106
112
  | --- | --- | --- |
@@ -111,7 +117,7 @@ below 500 is kept, a 4xx included: declare a rate limit or an auth check
111
117
  | `methods` | `POST`, `PATCH` | |
112
118
  | `header` | `Idempotency-Key` | |
113
119
  | `required` | `false` | |
114
- | `scope` | the client's address | `(ctx) => string` |
120
+ | `scope` | the client's address | `(ctx) => string \| undefined`; `undefined` runs the request unguarded |
115
121
 
116
122
  ## Caches and locks in the context
117
123
 
@@ -128,7 +134,7 @@ const loadUser = async (id: string) => ({ id, name: 'Ada' }); // your database
128
134
  const touch = async (user: z.infer<typeof User>) => user;
129
135
 
130
136
  const app = alxia()
131
- .use(redis(connection.client, { caches: { users } }))
137
+ .plugin(redis(connection.client, { caches: { users } }))
132
138
  .get('/users/:id', async ({ caches, lock, params, reply }) => {
133
139
  const user = await caches.users.remember(params.id, () => loadUser(params.id)); // typed by User
134
140
  await lock(`user:${params.id}`, () => touch(user));
@@ -140,6 +146,71 @@ const app = alxia()
140
146
  for everything else. It sits beside `@alxia/cache`'s `ctx.cache` — the
141
147
  response cache's `{ tag, skip }` — without touching it, in either order.
142
148
 
149
+ ## With @nxgt/redis
150
+
151
+ Open the Redis once with `@nxgt/redis`'s `openRedis(defineRedis({ … }))` and
152
+ hand the handle to every export that takes a client: one `prefix` is in front
153
+ of every key of the deployment, and the handle closes with the app.
154
+
155
+ ```ts
156
+ import { alxia, health } from '@alxia/core';
157
+ import { redis, redisCheck, redisStore } from '@alxia/redis';
158
+ import { rateLimit } from '@alxia/rate-limit';
159
+ import { defineCache, defineRedis, openRedis } from '@nxgt/redis';
160
+ import { z } from 'zod';
161
+
162
+ const users = defineCache({ name: 'user', key: (id: string) => id, ttl: 300, schema: z.object({ id: z.string(), name: z.string() }) });
163
+ const handle = await openRedis(defineRedis({ uri: Bun.env['REDIS_URL']!, prefix: 'shop', caches: { users } }));
164
+
165
+ const app = alxia()
166
+ .use(rateLimit({ limit: 100, windowMs: 60_000, store: redisStore(handle, { name: 'api' }) })) // shop:api:…
167
+ .plugin(redis(handle)) // caches typed from handle.cache; handle.close() in onStop
168
+ .plugin(health({ checks: { redis: redisCheck(handle) } }))
169
+ .get('/users/:id', async ({ caches, lock, params, reply }) =>
170
+ reply.ok(await lock(params.id, () => caches.users.remember(params.id, () => ({ id: params.id, name: 'Ada' })))), // shop:user:…, lock:shop:…
171
+ );
172
+ ```
173
+
174
+ `redisStore`, `redisCacheStore` and `idempotency` take the handle where they
175
+ take a client, and put its `prefix` in front of their `name`: the keys are
176
+ `shop:api:…`, `shop:pages:response:…`, `shop:orders:…`. `redis(handle)` puts
177
+ `caches` (the handle's own bound caches), `lock` (under the prefix), `redis`
178
+ (the client) and `prefix` in the context, and closes the handle once when the
179
+ app stops, after the requests in flight finished: `redis(handle, { close: false })`
180
+ when something else closes it. The handle must wire one Redis instance.
181
+ The bare `RedisClient` forms are unchanged and add no prefix.
182
+
183
+ ### Defined once, in `defineRedis`
184
+
185
+ With `@nxgt/redis` 0.6 the rate limit and the idempotency are wired by
186
+ `defineRedis` too (`limits`, `idempotency`), and `redisStore` and `idempotency`
187
+ take the wired entry, so the definition lives in one place and its types flow
188
+ through:
189
+
190
+ ```ts
191
+ import { idempotency, idempotencyResult, redisStore } from '@alxia/redis';
192
+ import { defineIdempotency, defineRateLimit, defineRedis, openRedis } from '@nxgt/redis';
193
+
194
+ const api = defineRateLimit({ name: 'api', key: (ip: string) => ip, limit: 100, per: 60_000 });
195
+ const orders = defineIdempotency({ name: 'orders', key: (id: string) => id, ttl: 86_400, schema: idempotencyResult });
196
+ const wired = await openRedis(defineRedis({ uri: Bun.env['REDIS_URL']!, prefix: 'shop', limits: { api }, idempotency: { orders } }));
197
+
198
+ alxia()
199
+ .use(rateLimit({ limit: 100, windowMs: 60_000, store: redisStore(wired.limits.api) })) // shop:api:<address>
200
+ .use(idempotency(wired.idempotency.orders, { required: true })) // shop:orders:<route>:<scope>:<key>
201
+ .post('/orders', ({ reply }) => reply(201, { id: crypto.randomUUID() }));
202
+ ```
203
+
204
+ The keys are those `@nxgt/redis` writes, `<prefix>:<name>:<key>`, so another
205
+ consumer calling `wired.limits.api.consume(address)` shares the count. The
206
+ rate, `ttl` and `lease` are the definition's: `rateLimit`'s `limit` and
207
+ `windowMs` only write its headers, so repeat the definition's numbers, and the
208
+ wired `idempotency` takes no `name`, `ttl` or `lease`. The limit's key takes a
209
+ string, and the idempotency's `schema` is `idempotencyResult`: another is a
210
+ compile error. A rate limit moved from `redisStore(handle, { name })` starts
211
+ its counts again, the layouts differing; an idempotency keeps its keys. Both
212
+ older forms stay. [More](docs/guide/connecting.md#defined-once-in-defineredis).
213
+
143
214
  ## Testing
144
215
 
145
216
  The package's specs run against `$REDIS_URL`, or a `redis-server` on
@@ -149,11 +220,18 @@ The package's specs run against `$REDIS_URL`, or a `redis-server` on
149
220
 
150
221
  | export | |
151
222
  | --- | --- |
152
- | `redisStore(client, { name })`, `RedisStoreOptions` | an `@alxia/rate-limit` store |
153
- | `redisCacheStore(client, { name })`, `RedisCacheStoreOptions` | an `@alxia/cache` store |
154
- | `idempotency(client, options)` | the plugin |
155
- | `redis(client, { caches? })`, `RedisContextOptions` | the plugin: `redis`, `caches`, `lock` in the context |
223
+ | `redisStore(client \| handle, { name })`, `RedisStoreOptions` | an `@alxia/rate-limit` store |
224
+ | `redisStore(handle.limits.api)` | the same for a rate limit wired by `defineRedis` (`@nxgt/redis` 0.6): the definition holds the name and the rate, the keys are `<prefix>:<name>:<key>` |
225
+ | `redisCacheStore(client \| handle, { name })`, `RedisCacheStoreOptions` | an `@alxia/cache` store |
226
+ | `idempotency(client \| handle, options)` | the middleware, given to `app.use` |
227
+ | `idempotency(handle.idempotency.orders, options?)`, `WiredIdempotency`, `WiredIdempotencyOptions` | the same for an idempotency wired by `defineRedis`: its definition holds the `name`, `ttl` and `lease`, so the options take none of them |
228
+ | `idempotencyResult`, `IdempotencyResult` | the `schema` a wired idempotency given to `idempotency()` must have: the response it keeps |
229
+ | `redis(client, { caches? })`, `RedisContextOptions` | a plugin, given to `app.plugin`: `redis`, `caches`, `lock` in the context |
230
+ | `redis(handle, { close? })`, `RedisHandleOptions`, `RedisHandleContext` | the same from an `@nxgt/redis` handle: its typed caches, a prefixed `lock`, `prefix`; closes the handle in `onStop` unless `close: false` |
231
+ | `redisCheck(client \| handle, { timeout? })`, `RedisCheckOptions` | a check for core's `health({ checks })`: down when a Redis instance does not answer a `PING`; `timeout` bounds a handle's ping, a client's is bounded by `health({ timeout })` |
232
+ | `RedisTarget` | a client or a handle: what the factories above take |
156
233
  | `IdempotencyOptions`, `IdempotencyErrorBody`, `RedisContext`, `BoundCaches` | its types |
234
+ | `IdempotencyMiddleware` | what `idempotency()` returns: a middleware that adds nothing, and may answer a 400, a 409 or a 422 |
157
235
  | `AnyCache` | any cache definition: the constraint of a function generic over the caches it hands to `redis()` |
158
236
 
159
237
  ## Documentation
@@ -161,3 +239,4 @@ The package's specs run against `$REDIS_URL`, or a `redis-server` on
161
239
  - [Guide](https://github.com/softistx/alxia/tree/develop/packages/redis/docs): a page per area — connecting, rate limits, the response cache, idempotency, caches and locks, and testing against a real Redis.
162
240
  - [Troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/redis/docs/troubleshooting.md): an error message, a refusal a client got, or a limit, cache or replay that does not behave as expected, and what to do about it.
163
241
  - [Roadmap](https://github.com/softistx/alxia/blob/develop/packages/redis/docs/roadmap.md): what is coming, and what is not planned.
242
+ - [Recipes](https://github.com/softistx/alxia/blob/develop/docs/recipes/README.md): [Caching and rate limiting with Redis](https://github.com/softistx/alxia/blob/develop/docs/recipes/caching-and-rate-limiting.md), [Test an alxia app](https://github.com/softistx/alxia/blob/develop/docs/recipes/testing.md), [Health checks and graceful shutdown](https://github.com/softistx/alxia/blob/develop/docs/recipes/health-and-shutdown.md).
@@ -1,7 +1,7 @@
1
1
  import type { CacheStore } from '@alxia/cache';
2
- import type { RedisClient } from 'bun';
2
+ import { type RedisTarget } from './handle';
3
3
  export interface RedisCacheStoreOptions {
4
- /** Prepended to every key it writes: one name per app or deployment. */
4
+ /** Prepended to every key it writes: one name per app or deployment. Under a handle's prefix, when it is given one. */
5
5
  readonly name: string;
6
6
  }
7
7
  /**
@@ -13,6 +13,9 @@ export interface RedisCacheStoreOptions {
13
13
  * ```ts
14
14
  * app.use(cache({ ttl: 60, store: redisCacheStore(connection.client, { name: 'shop' }) }));
15
15
  * ```
16
+ *
17
+ * Given an `@nxgt/redis` handle instead of a client, every key, the tag sets
18
+ * included, is under the handle's `prefix`.
16
19
  */
17
- export declare function redisCacheStore(client: RedisClient, options: RedisCacheStoreOptions): CacheStore;
20
+ export declare function redisCacheStore(target: RedisTarget, options: RedisCacheStoreOptions): CacheStore;
18
21
  //# sourceMappingURL=cache-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache-store.d.ts","sourceRoot":"","sources":["../src/cache-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAGvC,MAAM,WAAW,sBAAsB;IACtC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAeD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,sBAAsB,GAC7B,UAAU,CA4EZ"}
1
+ {"version":3,"file":"cache-store.d.ts","sourceRoot":"","sources":["../src/cache-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvD,MAAM,WAAW,sBAAsB;IACtC,uHAAuH;IACvH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AA4CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,sBAAsB,GAC7B,UAAU,CAwDZ"}
@@ -0,0 +1,19 @@
1
+ import type { RedisTarget } from './handle';
2
+ export interface RedisCheckOptions {
3
+ /**
4
+ * Milliseconds a handle's ping waits for each instance, as
5
+ * `health({ timeout })` names its own: `@nxgt/redis`'s 2 s by default.
6
+ */
7
+ readonly timeout?: number;
8
+ }
9
+ /**
10
+ * A readiness check for core's `health({ checks })`: it passes when every
11
+ * Redis instance of the handle answers a `PING`, and is down when one does
12
+ * not, or when the call fails. Given a client, it sends the `PING` itself.
13
+ *
14
+ * ```ts
15
+ * app.plugin(health({ checks: { redis: redisCheck(handle) } }));
16
+ * ```
17
+ */
18
+ export declare function redisCheck(target: RedisTarget, options?: RedisCheckOptions): () => Promise<boolean>;
19
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,MAAM,WAAW,iBAAiB;IACjC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACzB,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,iBAAsB,GAC7B,MAAM,OAAO,CAAC,OAAO,CAAC,CAaxB"}
package/dist/context.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type BoundCache, type CacheDefinition, type LockOptions } from '@nxgt/redis';
1
+ import { type BoundCache, type CacheDefinition, type LockOptions, type Redis, type SoleInstance } from '@nxgt/redis';
2
2
  import type { RedisClient } from 'bun';
3
3
  import type { z } from 'zod';
4
4
  /**
@@ -8,7 +8,7 @@ import type { z } from 'zod';
8
8
  *
9
9
  * ```ts
10
10
  * function withCaches<const Caches extends Record<string, AnyCache>>(caches: Caches) {
11
- * return alxia().use(redis(client, { caches }));
11
+ * return alxia().plugin(redis(client, { caches }));
12
12
  * }
13
13
  * ```
14
14
  */
@@ -30,15 +30,46 @@ export interface RedisContext<Caches extends Record<string, AnyCache>> {
30
30
  /** `work` under a lock every process sharing the Redis respects: `@nxgt/redis`'s `withLock`. */
31
31
  lock<T>(key: string, work: () => Promise<T> | T, options?: LockOptions): Promise<T>;
32
32
  }
33
+ export interface RedisHandleOptions {
34
+ /**
35
+ * Whether the handle is closed when the app stops: once, after the drain,
36
+ * in `onStop`. On by default; `false` when something else closes it.
37
+ */
38
+ readonly close?: boolean;
39
+ }
40
+ /** What routes after `redis(handle)` read. */
41
+ export interface RedisHandleContext<C> {
42
+ /** Bun's own client, untouched: the handle's one instance. */
43
+ readonly redis: RedisClient;
44
+ /** The handle's caches, typed by their schemas, the prefix already in front of every key. */
45
+ readonly caches: SoleInstance<C>['cache'];
46
+ /** `work` under a lock every process sharing the Redis respects, under the handle's prefix. */
47
+ readonly lock: SoleInstance<C>['lock'];
48
+ /** What goes in front of every key, or `undefined`. */
49
+ readonly prefix: string | undefined;
50
+ }
51
+ declare function fromClient<const Caches extends Record<string, AnyCache> = Record<never, never>>(client: RedisClient, options?: RedisContextOptions<Caches>): import("@alxia/core").Alxia<import("@alxia/core").Empty & RedisContext<Caches>, "">;
52
+ declare function fromHandle<C>(handle: Redis<C>, options?: RedisHandleOptions): import("@alxia/core").Alxia<import("@alxia/core").Empty & RedisHandleContext<C>, "">;
33
53
  /**
34
54
  * Redis in the context, as a plugin: the client, the caches bound once, and
35
55
  * a lock — typed, for every route declared after it.
36
56
  *
37
57
  * ```ts
38
58
  * const users = defineCache({ name: 'user', key: (id: string) => id, ttl: 300, schema: User });
39
- * app.use(redis(connection.client, { caches: { users } }))
59
+ * app.plugin(redis(connection.client, { caches: { users } }))
40
60
  * .get('/users/:id', async ({ caches, params, reply }) => reply.ok(await caches.users.remember(params.id, load)));
41
61
  * ```
62
+ *
63
+ * Given an `@nxgt/redis` handle, the caches are the handle's own, the
64
+ * prefix is in front of every key and lock, and the handle is closed when
65
+ * the app stops, after the drain (`{ close: false }` to close it yourself):
66
+ *
67
+ * ```ts
68
+ * const handle = await openRedis(defineRedis({ uri, prefix: 'shop', caches }));
69
+ * app.plugin(redis(handle)).get('/u/:id', ({ caches, params }) => caches.users.get(params.id));
70
+ * ```
42
71
  */
43
- export declare function redis<const Caches extends Record<string, AnyCache> = Record<never, never>>(client: RedisClient, options?: RedisContextOptions<Caches>): import("@alxia/core").Alxia<import("@alxia/core").Empty & RedisContext<Caches>, import("@alxia/core").Empty, "", never>;
72
+ export declare function redis<const Caches extends Record<string, AnyCache> = Record<never, never>>(client: RedisClient, options?: RedisContextOptions<Caches>): ReturnType<typeof fromClient<Caches>>;
73
+ export declare function redis<C>(handle: Redis<C>, options?: RedisHandleOptions): ReturnType<typeof fromHandle<C>>;
74
+ export {};
44
75
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,UAAU,EAEf,KAAK,eAAe,EACpB,KAAK,WAAW,EAEhB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AACvC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;;;;;;;;;GAUG;AACH,MAAM,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;AAEvD,wDAAwD;AACxD,MAAM,MAAM,WAAW,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI;IAClE,QAAQ,EAAE,IAAI,IAAI,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,eAAe,CACpE,MAAM,MAAM,EACZ,MAAM,MAAM,CACZ,GACE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GACrD,KAAK;CACR,CAAC;AAEF,MAAM,WAAW,mBAAmB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC3E,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;IACpE,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,gGAAgG;IAChG,IAAI,CAAC,CAAC,EACL,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC1B,OAAO,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC,CAAC;CACd;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACpB,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACnE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,mBAAmB,CAAC,MAAM,CAAM,2HAa/D"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { BoundIdempotency, BoundRateLimit, Redis } from '@nxgt/redis';
2
+ import type { RedisClient } from 'bun';
3
+ /**
4
+ * What the factories take: Bun's own client, or the handle `@nxgt/redis`'s
5
+ * `openRedis(defineRedis({ … }))` gives back.
6
+ */
7
+ export type RedisTarget = RedisClient | Redis<any>;
8
+ /** The one instance of a handle, resolved: its client and the prefix in front of its keys. */
9
+ export interface ResolvedTarget {
10
+ readonly client: RedisClient;
11
+ /** `undefined` when nothing goes in front of the keys, and always for a bare client. */
12
+ readonly prefix: string | undefined;
13
+ }
14
+ /** A handle has `instances` and `close`; Bun's `RedisClient` has neither. */
15
+ export declare function isHandle(target: RedisTarget): target is Redis<any>;
16
+ /**
17
+ * The client and prefix of a target. A handle must wire exactly one Redis
18
+ * instance: with several, which one an app's keys live on is for the app to
19
+ * say, with the bare client of `handle.clients.<name>`.
20
+ */
21
+ export declare function resolve(target: RedisTarget): ResolvedTarget;
22
+ /** `name`, with the handle's prefix in front when there is one: the one name a deployment's keys share. */
23
+ export declare function nameUnder(target: RedisTarget, name: string): {
24
+ readonly client: RedisClient;
25
+ readonly name: string;
26
+ };
27
+ /** A rate limit bound by `@nxgt/redis`, wired or by hand: `consume`, `peek`, `reset` and `keyFor`. */
28
+ export declare function isWiredLimit(value: object): value is BoundRateLimit<string>;
29
+ /** An idempotency bound by `@nxgt/redis`, wired or by hand: `run`, `forget` and `keyFor`. */
30
+ export declare function isWiredIdempotency(value: object): value is BoundIdempotency<string, any, any>;
31
+ //# sourceMappingURL=handle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEvC;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;AAEnD,8FAA8F;AAC9F,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAMlE;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,cAAc,CAc3D;AAED,2GAA2G;AAC3G,wBAAgB,SAAS,CACxB,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,GACV;IACF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB,CAGA;AAED,sGAAsG;AACtG,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,cAAc,CAAC,MAAM,CAAC,CAQ3E;AAED,6FAA6F;AAC7F,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,MAAM,GACX,KAAK,IAAI,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,CAO7C"}
@@ -0,0 +1,26 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * What `idempotency()` keeps under a key: the response, as the route
4
+ * answered it. The `schema` of a wired `defineIdempotency` that
5
+ * `idempotency(handle.idempotency.orders)` is given:
6
+ *
7
+ * ```ts
8
+ * export const orders = defineIdempotency({ name: 'orders', key: (id: string) => id, ttl: 86_400, schema: idempotencyResult });
9
+ * ```
10
+ */
11
+ export declare const idempotencyResult: z.ZodObject<{
12
+ status: z.ZodNumber;
13
+ headers: z.ZodArray<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
14
+ body: z.ZodString;
15
+ }, z.core.$strip>;
16
+ export type IdempotencyResult = z.infer<typeof idempotencyResult>;
17
+ /** A response that is answered and not kept: a 5xx, a stream. */
18
+ export declare class Unstored extends Error {
19
+ readonly response: Response;
20
+ constructor(response: Response);
21
+ }
22
+ /** What a key is bound to: the method, the path and query, the body. */
23
+ export declare function fingerprintOf(request: Request, url: URL): Promise<Uint8Array>;
24
+ export declare function store(response: Response): Promise<IdempotencyResult>;
25
+ export declare function restore(stored: IdempotencyResult, replayed: boolean): Response;
26
+ //# sourceMappingURL=idempotency-response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency-response.d.ts","sourceRoot":"","sources":["../src/idempotency-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAKlE,iEAAiE;AACjE,qBAAa,QAAS,SAAQ,KAAK;IAClC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAChB,QAAQ,EAAE,QAAQ;CAI9B;AAED,wEAAwE;AACxE,wBAAsB,aAAa,CAClC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,GACN,OAAO,CAAC,UAAU,CAAC,CASrB;AAED,wBAAsB,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAiB1E;AAED,wBAAgB,OAAO,CACtB,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,OAAO,GACf,QAAQ,CAQV"}
@@ -1,11 +1,13 @@
1
- import { type BaseContext } from '@alxia/core';
2
- import type { RedisClient } from 'bun';
1
+ import { type BaseContext, type Empty, type Middleware, type Reply } from '@alxia/core';
2
+ import { type BoundIdempotency } from '@nxgt/redis';
3
+ import { type RedisTarget } from './handle';
4
+ import { type IdempotencyResult } from './idempotency-response';
3
5
  export interface IdempotencyOptions {
4
- /** Names the keys it stores. */
6
+ /** Names the keys it stores, under a handle's prefix when it is given one. */
5
7
  readonly name: string;
6
8
  /** Seconds a finished response is kept and replayed. A day by default. */
7
9
  readonly ttl?: number;
8
- /** Milliseconds a running request holds its key unless renewed. `@nxgt/redis-guard`'s 10 s by default. */
10
+ /** Milliseconds a running request holds its key unless renewed. `@nxgt/redis`'s 10 s by default. */
9
11
  readonly lease?: number;
10
12
  /** Milliseconds a repeat waits for the first to finish before a 409. None by default. */
11
13
  readonly wait?: number;
@@ -18,7 +20,9 @@ export interface IdempotencyOptions {
18
20
  /**
19
21
  * Whose key it is: keys are scoped by the route and by this, so two
20
22
  * clients choosing the same key never see each other's response. The
21
- * client's address by default; a user id when there is one.
23
+ * client's address by default; a user id when there is one. A request
24
+ * it gives no scope for runs unguarded — nothing stored, nothing
25
+ * replayed — with a warning, once.
22
26
  */
23
27
  readonly scope?: (ctx: BaseContext) => string | undefined;
24
28
  }
@@ -29,10 +33,26 @@ export interface IdempotencyErrorBody {
29
33
  readonly retryAfter?: number;
30
34
  }
31
35
  /**
32
- * Idempotent routes, as a plugin, with `@nxgt/redis-guard`: a `POST` or
36
+ * What `idempotency()` makes: a middleware that adds nothing to the
37
+ * context, and may answer a 400, a 409 or a 422.
38
+ */
39
+ export type IdempotencyMiddleware = Middleware<Empty, Promise<Response | Reply<400, IdempotencyErrorBody> | Reply<409, IdempotencyErrorBody> | Reply<422, IdempotencyErrorBody>>>;
40
+ /**
41
+ * An idempotency wired by `defineRedis` — `handle.idempotency.orders` — whose
42
+ * definition holds `idempotencyResult` as its schema and a key taken as a
43
+ * string.
44
+ */
45
+ export type WiredIdempotency = BoundIdempotency<string, IdempotencyResult, IdempotencyResult>;
46
+ /** What a wired idempotency leaves to the middleware: its name, `ttl` and `lease` are the definition's. */
47
+ export type WiredIdempotencyOptions = Omit<IdempotencyOptions, 'name' | 'ttl' | 'lease'>;
48
+ /**
49
+ * Idempotent routes, as a middleware, with `@nxgt/redis`: a `POST` or
33
50
  * `PATCH` carrying an `Idempotency-Key` runs once per key, and every repeat
34
51
  * gets the first response back, marked `Idempotent-Replayed: true` — across
35
- * every process sharing the Redis. Routes declared after it are guarded.
52
+ * every process sharing the Redis. Routes declared after it are guarded; a
53
+ * request no route matches is not: there is no route to scope its key by.
54
+ * What is kept is the response the route answers, an error's answer
55
+ * included.
36
56
  *
37
57
  * A repeat while the first still runs is a 409, and the same key with
38
58
  * another request — method, path or body — a 422: both are part of every
@@ -42,6 +62,18 @@ export interface IdempotencyErrorBody {
42
62
  * ```ts
43
63
  * app.use(idempotency(redis.client, { name: 'payments' })).post('/payments', ...);
44
64
  * ```
65
+ *
66
+ * Given an `@nxgt/redis` handle instead of a client, the keys are under the
67
+ * handle's `prefix`: `idempotency(handle, { name: 'payments' })`.
68
+ *
69
+ * Given an idempotency wired by `defineRedis`, the definition — name, `ttl`,
70
+ * `lease` — is the one place, and the keys are those `@nxgt/redis` writes,
71
+ * `<prefix>:<name>:<route>:<scope>:<key>`:
72
+ *
73
+ * ```ts
74
+ * app.use(idempotency(handle.idempotency.orders, { required: true })).post('/orders', ...);
75
+ * ```
45
76
  */
46
- export declare function idempotency(client: RedisClient, options: IdempotencyOptions): import("@alxia/core").Alxia<import("@alxia/core").Empty, import("@alxia/core").Empty, "", import("@alxia/core").Reply<400, IdempotencyErrorBody> | import("@alxia/core").Reply<409, IdempotencyErrorBody> | import("@alxia/core").Reply<422, IdempotencyErrorBody>>;
77
+ export declare function idempotency(target: RedisTarget, options: IdempotencyOptions): IdempotencyMiddleware;
78
+ export declare function idempotency(wired: WiredIdempotency, options?: WiredIdempotencyOptions): IdempotencyMiddleware;
47
79
  //# sourceMappingURL=idempotency.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAMtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAGvC,MAAM,WAAW,kBAAkB;IAClC,gCAAgC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,0GAA0G;IAC1G,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,yFAAyF;IACzF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,+FAA+F;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,MAAM,GAAG,SAAS,CAAC;CAC1D;AAED,6BAA6B;AAC7B,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,KAAK,EACX,yBAAyB,GACzB,yBAAyB,GACzB,yBAAyB,GACzB,wBAAwB,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAuBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,uQAsE3E"}
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../src/idempotency.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,WAAW,EAEhB,KAAK,KAAK,EACV,KAAK,UAAU,EAEf,KAAK,KAAK,EAEV,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,KAAK,gBAAgB,EAIrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAiC,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EAEN,KAAK,iBAAiB,EAKtB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,kBAAkB;IAClC,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,oGAAoG;IACpG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,yFAAyF;IACzF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,+FAA+F;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,MAAM,GAAG,SAAS,CAAC;CAC1D;AAED,6BAA6B;AAC7B,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,KAAK,EACX,yBAAyB,GACzB,yBAAyB,GACzB,yBAAyB,GACzB,wBAAwB,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAC7C,KAAK,EACL,OAAO,CACJ,QAAQ,GACR,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,GAChC,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,GAChC,KAAK,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAClC,CACD,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,CAC9C,MAAM,EACN,iBAAiB,EACjB,iBAAiB,CACjB,CAAC;AAEF,2GAA2G;AAC3G,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACzC,kBAAkB,EAClB,MAAM,GAAG,KAAK,GAAG,OAAO,CACxB,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,WAAW,CAC1B,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,kBAAkB,GACzB,qBAAqB,CAAC;AACzB,wBAAgB,WAAW,CAC1B,KAAK,EAAE,gBAAgB,EACvB,OAAO,CAAC,EAAE,uBAAuB,GAC/B,qBAAqB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export { type RedisCacheStoreOptions, redisCacheStore } from './cache-store';
2
- export { type AnyCache, type BoundCaches, type RedisContext, type RedisContextOptions, redis, } from './context';
3
- export { type IdempotencyErrorBody, type IdempotencyOptions, idempotency, } from './idempotency';
2
+ export { type RedisCheckOptions, redisCheck } from './check';
3
+ export { type AnyCache, type BoundCaches, type RedisContext, type RedisContextOptions, type RedisHandleContext, type RedisHandleOptions, redis, } from './context';
4
+ export type { RedisTarget } from './handle';
5
+ export { type IdempotencyErrorBody, type IdempotencyMiddleware, type IdempotencyOptions, idempotency, type WiredIdempotency, type WiredIdempotencyOptions, } from './idempotency';
6
+ export { type IdempotencyResult, idempotencyResult, } from './idempotency-response';
4
7
  export { type RedisStoreOptions, redisStore } from './store';
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,sBAAsB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACN,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,GACL,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,WAAW,GACX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,sBAAsB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EACN,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,GACL,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,GAC5B,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,KAAK,iBAAiB,EACtB,iBAAiB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}