@owlmeans/redis-resource 0.1.18-rc.0 → 0.1.18-rc.10

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,11 +1,13 @@
1
1
  # @owlmeans/redis-resource
2
2
 
3
- Redis-backed resource with pub/sub and streaming support for real-time features.
3
+ Redis-backed resource with pub/sub, keyspace watching and streams for real-time features.
4
4
 
5
5
  ## Overview
6
6
 
7
- - `makeRedisResource(alias, dbAlias?)` — creates a Redis resource
8
- - `RedisResource<T>` — extends `Resource<T>` with `subscribe`, `publish`, `stream`, and `consume`
7
+ - `makeRedisResource(alias, dbAlias?, serviceAlias?)` — creates a Redis resource
8
+ - `RedisResource<T>` — `Resource<T>` composed with `PubSubResource`, `WatchableResource` and
9
+ `StreamResource`
10
+ - One JSON document per namespaced key: `resource.key(id)` → `<schema>-<alias>:<id>`
9
11
  - Used for authentication caches, pub/sub channels, and async event streams
10
12
 
11
13
  ## Installation
@@ -27,65 +29,110 @@ context.registerResource(makeRedisResource(AGENT_THINKING_STREAM))
27
29
  context.registerResource(makeRedisResource(AGENT_WATCHER_PUBSUB))
28
30
  ```
29
31
 
30
- Define typed resource interfaces:
32
+ Type the resource once, at the point it is taken out of the context:
31
33
 
32
34
  ```typescript
33
35
  import type { RedisResource } from '@owlmeans/redis-resource'
34
36
 
35
- interface AgentThinkingStream extends RedisResource<AgentThinkingEvent> {}
36
- interface AgentWatcherPubsub extends RedisResource<AgentWatcherEvent> {}
37
+ const cache = context.resource<RedisResource<SessionData>>(AUTH_CACHE)
38
+
39
+ await cache.create({ id, token }, { ttl: 3600 }) // seconds, or a Date
40
+ const session = await cache.load({ token }) // criteria, not just an id
41
+ await cache.take(id) // delete-and-return
37
42
  ```
38
43
 
39
44
  Publish and subscribe:
40
45
 
41
46
  ```typescript
42
- const pubsub = context.resource<AgentWatcherPubsub>(AGENT_WATCHER_PUBSUB)
47
+ const pubsub = context.resource<RedisResource<AgentWatcherEvent>>(AGENT_WATCHER_PUBSUB)
43
48
 
44
- // Publisher side
45
- await pubsub.publish({ type: 'update', payload: data })
49
+ await pubsub.publish({ type: 'update', payload: data }, channel)
46
50
 
47
- // Subscriber side
48
- const unsubscribe = await pubsub.subscribe(async (event) => {
49
- handleEvent(event)
50
- })
51
+ const unsubscribe = await pubsub.subscribe(
52
+ event => handleEvent(event), { channel, once: true }
53
+ )
54
+ ```
55
+
56
+ Watch one record for changes (keyspace notifications):
57
+
58
+ ```typescript
59
+ const unwatch = await cache.watch(id, record => {
60
+ // `null` once the key is gone — deleted or expired
61
+ render(record)
62
+ }, { once: true })
51
63
  ```
52
64
 
53
65
  Stream events (Redis Streams):
54
66
 
55
67
  ```typescript
56
- const stream = context.resource<AgentThinkingStream>(AGENT_THINKING_STREAM)
68
+ const stream = context.resource<RedisResource<AgentThinkingEvent>>(AGENT_THINKING_STREAM)
57
69
 
58
- // Producer
59
70
  await stream.stream('thinking-key', { step: 'reasoning', content: text })
60
71
 
61
- // Consumer (async generator)
62
- for await (const event of stream.consume('thinking-key', 'workers', 'worker-1')) {
72
+ for await (const event of stream.consume('thinking-key', { group: 'workers', consumer: 'worker-1' })) {
63
73
  process(event)
64
74
  }
65
75
  ```
66
76
 
77
+ ## Reads
78
+
79
+ `load(id)` / `get(id)` / `delete(id)` / `take(id)` are O(1) commands on one key. `load({ ... })`,
80
+ `get({ ... })`, `list`, `count` and `purge` walk this resource's **own namespace** with `SCAN`
81
+ (never `KEYS`, which blocks the server for the whole sweep), read the values with `MGET`, and
82
+ evaluate the criteria in memory. That is O(N) over the namespace: right for a small namespaced set
83
+ — a session cache, an adapter store — and wrong for anything unbounded or on a hot path. A
84
+ resource that needs real queries belongs in `@owlmeans/mongo-resource` or
85
+ `@owlmeans/postgres-resource`.
86
+
87
+ Redis is **unpaged**: `list(where)` with no `size` returns every match, and `list(where, { page })`
88
+ without a `size` throws `UnsupportedArgumentError('page-without-size')`. `ListResult.total` always
89
+ counts every match.
90
+
91
+ The namespace walk reaches one server, so `list` / `count` / `purge` are not cluster-safe — in a
92
+ clustered deployment use the by-id operations only.
93
+
94
+ ## TTL
95
+
96
+ | `opts.ttl` | Meaning | Command |
97
+ |---|---|---|
98
+ | `number` | seconds from now | `EXPIRE` |
99
+ | `Date` | the absolute instant to expire at | `PEXPIREAT` (milliseconds — what `Date.getTime()` gives) |
100
+ | omitted | persistent (`PTTL` reports `-1`) | — |
101
+
102
+ A `Date` already in the past drops the record immediately. `update` and `save` rewrite the key, so
103
+ a renewal has to pass its TTL again — otherwise the record becomes persistent.
104
+
67
105
  ## API
68
106
 
69
- ### `makeRedisResource(alias, dbAlias?): RedisResource<T>`
107
+ ### `makeRedisResource<R>(alias, dbAlias?, serviceAlias?): RedisResource<R>`
70
108
 
71
- Creates a Redis resource. `dbAlias` defaults to `DEFAULT_DB_ALIAS` (`'redis'`).
109
+ Creates a Redis resource. Both alias arguments default to `DEFAULT_DB_ALIAS` (`'redis'`).
72
110
 
73
111
  ### `RedisResource<T>`
74
112
 
75
- Extends `Resource<T>` with:
76
- - `subscribe<T>(handler, key?): Promise<() => Promise<void>>` — subscribe to pub/sub channel
77
- - `publish<T>(value, key?): Promise<void>` — publish to pub/sub channel
78
- - `stream<T>(key, data): Promise<void>` — append to a Redis Stream
79
- - `consume<T>(key, group?, consumer?): AsyncGenerator<T>` — consume from a Redis Stream
113
+ `Resource<T>` plus:
114
+ - `publish(value, channel?): Promise<void>` — publish to a namespaced pub/sub channel
115
+ - `subscribe(handler, { channel?, once?, ttl? }): Promise<Unsubscribe>` — channel pub/sub
116
+ - `watch(id, handler, { once?, ttl? }): Promise<Unsubscribe>` — keyspace notifications for ONE
117
+ record; the handler is given `null` once the key is gone. Bound to **db 0**, so deployments
118
+ sharing an instance isolate on the key prefix, and the server needs `notify-keyspace-events`
119
+ - `stream(key, value): Promise<void>` — append to a Redis Stream
120
+ - `consume(key, { group?, consumer?, block? }): AsyncGenerator<T>` — consume from a Redis Stream
121
+ - `key(key?): string` — the namespaced key, or the namespace's glob
80
122
 
81
123
  ### Constants
82
124
 
83
125
  - `DEFAULT_DB_ALIAS` — `'redis'`
126
+ - `DEFAULT_PAGE_SIZE` — `0`, meaning unpaged
127
+
128
+ ## Requirements
129
+
130
+ Redis 6.2 or newer: `delete` and `take` use `GETDEL`.
84
131
 
85
132
  ## Related Packages
86
133
 
87
134
  - [`@owlmeans/redis`](../redis) — Redis connection service
88
- - [`@owlmeans/resource`](../resource) — `Resource<T>` base interface
135
+ - [`@owlmeans/resource`](../resource) — `Resource<T>` base interface and the criteria engine
89
136
  - [`@owlmeans/server-socket`](../server-socket) — WebSocket handlers often use Redis resources for streaming
90
137
 
91
138
  <!-- owlmeans:agent-guidance:start -->
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/redis-resource",
4
- "version": "0.1.18-rc.0",
5
- "generatedAt": "2026-08-16T22:20:50.508Z",
4
+ "version": "0.1.18-rc.10",
5
+ "generatedAt": "2026-09-01T16:28:56.991Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: redis-resource
3
- description: How to use @owlmeans/redis-resource — Redis-backed Resource implementation for caching/state with TTL support. Auto-invoked when defining a resource backed by Redis.
3
+ description: How to use @owlmeans/redis-resource — Redis-backed Resource implementation for caching/state with TTL, pub/sub, keyspace watching and streams. Auto-invoked when defining a resource backed by Redis.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,17 +8,19 @@ user-invocable: false
8
8
  # @owlmeans/redis-resource
9
9
 
10
10
  **Layer:** Infra
11
- **Install:** `"@owlmeans/redis-resource": "^0.1.18-rc.0"` in `dependencies`
11
+ **Install:** `"@owlmeans/redis-resource": "^0.1.18-rc.10"` in `dependencies`
12
12
 
13
13
  ## Key Exports
14
14
 
15
15
  | Export | Description |
16
16
  |--------|-------------|
17
- | `makeRedisResource<R>(alias, dbAlias?, serviceAlias?, makeCustomResource?)` | Factory for a Redis-backed `Resource<R>`. Both alias defaults are `DEFAULT_DB_ALIAS` (`'redis'`) |
18
- | `RedisResource<R>`, `RedisDb`, `RedisClient`, `RedisDbService` | Types |
19
- | `DEFAULT_DB_ALIAS`, `DEFAULT_PAGE_SIZE` | Constants |
17
+ | `makeRedisResource<R>(alias, dbAlias?, serviceAlias?)` | Factory for a Redis-backed `Resource<R>`. Both alias defaults are `DEFAULT_DB_ALIAS` (`'redis'`) |
18
+ | `RedisResource<R>` | `Resource<R>` plus the `PubSubResource`, `WatchableResource` and `StreamResource` capabilities, `db` and `key(id?)` |
19
+ | `RedisDb`, `RedisClient`, `RedisDbService` | Types |
20
+ | `DEFAULT_DB_ALIAS`, `DEFAULT_PAGE_SIZE`, `SCAN_BATCH`, `READ_BATCH`, `STREAM_MAX_LENGTH` | Constants |
20
21
 
21
- Keys are namespaced by the db config's `schema`: `resource.key(id)` → `<prefix>:<id>`.
22
+ Keys are namespaced by the db config's `schema` plus the resource alias:
23
+ `resource.key(id)` → `<schema>-<alias>:<id>`.
22
24
 
23
25
  ## Usage
24
26
 
@@ -27,47 +29,89 @@ import { makeRedisResource } from '@owlmeans/redis-resource'
27
29
 
28
30
  context.registerResource(makeRedisResource<SessionData>('sessions'))
29
31
 
32
+ // Type the RESOURCE once — never a method
30
33
  const sessions = context.resource<RedisResource<SessionData>>('sessions')
31
34
  await sessions.create({ id, token }, { ttl: 3600 }) // seconds, or a Date
35
+ const live = await sessions.load({ token }) // criteria, not just an id
32
36
  ```
33
37
 
34
- TTL is a **per-call option** on `create`/`update`, not a property of the factory. The prefix
35
- comes from the `dbs[]` config entry (`schema`), not from the resource.
38
+ TTL is a **per-call option** on `create` / `update` / `save`, not a property of the factory. The
39
+ prefix comes from the `dbs[]` config entry (`schema`), not from the resource.
40
+
41
+ ## Reads: one key is cheap, everything else is a walk
42
+
43
+ `load(id)`, `get(id)`, `delete(id)`, `take(id)` are single-key commands.
44
+
45
+ `load(where)`, `get(where)`, `list`, `count` and `purge` walk the resource's **own namespace** with
46
+ `SCAN` + `MGET` and evaluate the criteria in memory with `firstMatch` / `filterRecords` /
47
+ `applyQuery` from `@owlmeans/resource` — the same engine every other store without a query planner
48
+ uses, so a criteria object means here exactly what it means in SQL. That is O(N) over the
49
+ namespace, and `{ sort }` orders the records after the walk, not in redis.
50
+
51
+ - Use it for a small namespaced set — a session cache, an OTP store, an adapter store.
52
+ - Never for an unbounded keyspace or a hot path. A resource that needs real queries belongs in
53
+ mongo or postgres.
54
+ - `SCAN`, never `KEYS`: `KEYS` blocks the server for the whole sweep.
55
+ - The walk reaches one server, so `list` / `count` / `purge` are **not cluster-safe**. In a
56
+ clustered deployment stay on the by-id operations.
57
+ - `load({ id })` is recognised and answered by key — the walk only starts for a real criteria.
58
+ - A key in the namespace holding something other than a string — this resource's own streams
59
+ included — reads back as `null` from `MGET` and is skipped, so a stream never shows up as a
60
+ record.
61
+
62
+ ## Paging
63
+
64
+ Redis is **UNPAGED**: `DEFAULT_PAGE_SIZE` is `0`, `list(where)` with no `size` returns every match,
65
+ and `ListResult.total` always counts every match. `list(where, { page })` without a `size` throws
66
+ `UnsupportedArgumentError('page-without-size')` — there is no implied default to take a window of.
36
67
 
37
68
  ## TTL
38
69
 
39
70
  | `opts.ttl` | Meaning | Command |
40
71
  |---|---|---|
41
- | `number` / numeric `string` | seconds from now | `EXPIRE` |
72
+ | `number` | seconds from now | `EXPIRE` |
42
73
  | `Date` | the absolute instant to expire at | `PEXPIREAT` (milliseconds — what `Date.getTime()` gives) |
43
74
  | omitted | persistent (`PTTL` reports `-1`) | — |
44
75
 
45
- A `Date` already in the past drops the record immediately. `update` re-applies the TTL, so a
46
- renewal must pass one again — otherwise the key keeps whatever expiry it had.
76
+ A `Date` already in the past drops the record immediately. `update` and `save` rewrite the key, so
77
+ a renewal must pass a TTL again — otherwise the record becomes persistent.
47
78
 
48
79
  ## Rules
49
80
 
50
- - `delete(id)` — the plain by-id form — is the normal call and must remove the key. It returns the
51
- removed record, or `null` when the id was not there. `pick(id)` is delete-and-return that
52
- *throws* `UnknownRecordError` when the key is gone; choose by whether absence is an error.
53
- - `create` throws `RecordExists` when the id is present. For an upsert, use `save` (load → update
54
- or create) rather than delete-then-create; the latter turns any lost delete into a hard failure
55
- on the next write.
56
- - `load` rejects an `opts` argument (`UnsupportedArgumentError`) — pass only `id` and an optional
57
- field name.
58
- - `list` walks `KEYS <prefix>:*`. Fine for small, namespaced sets; never for a hot path or an
59
- unbounded keyspace.
60
- - `subscribe` relies on keyspace notifications, which are bound to **db 0** — deployments sharing
61
- one instance must isolate on the key prefix, not on the db index.
81
+ - `delete(id)` returns the removed record or `null`; `take(id)` is the same read but **throws**
82
+ `UnknownRecordError` when the key is gone. Both use `GETDEL`, so two callers can never be handed
83
+ the same record. Choose by whether absence is an error.
84
+ - `create` generates an id when the record carries none, and refuses an id that is already there
85
+ (`RecordExists`) using `SET NX` — no read-then-write race.
86
+ - `update` **replaces** the whole record and throws `UnknownRecordError` for an unknown id. It does
87
+ not merge over what was stored; pass every field that must survive.
88
+ - `save` creates when the record carries no id and replaces otherwise — that is the upsert.
89
+ - `purge(where)` refuses an empty criteria object. Use it instead of a page loop that deletes each
90
+ item.
91
+ - `publish(value, channel?)` / `subscribe(handler, { channel?, once?, ttl? })` are channel pub/sub;
92
+ the channel name is namespaced like a key, and an omitted channel listens to the resource's whole
93
+ namespace. `watch(id, handler, { once?, ttl? })` is the keyspace-notification form for ONE
94
+ record — the handler receives the current value on every write, and `null` once the key is gone.
95
+ They are separate methods; a channel is never passed as a bare string.
96
+ - Keyspace notifications are bound to **db 0**, so deployments sharing one instance isolate on the
97
+ key prefix, not on the db index — and the server needs `notify-keyspace-events` enabled.
98
+ - `stream(key, value)` appends to a namespaced redis stream, trimmed approximately to
99
+ `STREAM_MAX_LENGTH` so trimming stays cheap. `consume(key, { group?, consumer?, block? })` is the
100
+ async generator that reads it — with no `group` it tails from now with `XREAD`, with one it joins
101
+ the consumer group and reclaims stale entries under the **passed** consumer name. Claiming them
102
+ for a fixed name leaves them owned by a consumer that never reads.
103
+ - Requires redis 6.2 or newer (`GETDEL`).
62
104
  - Assert an expiry by reading `PTTL` back from redis, never by trusting that a command was issued —
63
105
  the unit bug this contract had still "set an expiry", just one ~50 millennia away.
64
106
 
65
107
  ## Depends On
66
108
 
67
- - `@owlmeans/redis`, `@owlmeans/resource`, `@owlmeans/server-context`
109
+ - `@owlmeans/redis`, `@owlmeans/resource`, `@owlmeans/server-context`, `@owlmeans/basic-ids`
68
110
 
69
111
  ## Tests
70
112
 
71
- Integration specs live in `@owlmeans/redis` (`packages/redis/tests/`) — the db package that backs
113
+ Integration specs live in the `@owlmeans/redis` package's own `tests/` — the db package that backs
72
114
  this contract; putting them here would need a devDependency on this package's own dependent.
73
- Gated on `REDIS_URL` (see `/.env.example`); each suite namespaces its keys and flushes them.
115
+ Gated on `REDIS_URL` (see `/.env.example`); each suite namespaces its keys and flushes them, and a
116
+ spec that walks the namespace boots under its own resource alias so it never sees another test's
117
+ records.
package/build/consts.d.ts CHANGED
@@ -1,3 +1,22 @@
1
1
  export declare const DEFAULT_DB_ALIAS = "redis";
2
- export declare const DEFAULT_PAGE_SIZE = 10;
2
+ /**
3
+ * Redis is UNPAGED: `list()` without a `size` returns every match, and `0` says exactly that.
4
+ * There is no server side LIMIT to fall back on — answering a criteria already walked the whole
5
+ * namespace — so an implied default would only hide records without saving any work.
6
+ */
7
+ export declare const DEFAULT_PAGE_SIZE = 0;
8
+ /** Keys per SCAN round trip. Bounded so one sweep never blocks the server the way KEYS does. */
9
+ export declare const SCAN_BATCH = 200;
10
+ /** Keys per MGET / DEL round trip once SCAN has collected them. */
11
+ export declare const READ_BATCH = 200;
12
+ /** Entries kept in a stream, trimmed approximately (`MAXLEN ~`) so trimming stays O(1)-ish. */
13
+ export declare const STREAM_MAX_LENGTH = 10000;
14
+ /** How long a stream read blocks before the consumer loops and looks for pending entries. */
15
+ export declare const DEFAULT_STREAM_BLOCK = 1000;
16
+ /** How long an unacknowledged entry may sit with its consumer before another may reclaim it. */
17
+ export declare const RECLAIM_IDLE = 60000;
18
+ /** Entries reclaimed per XAUTOCLAIM pass. */
19
+ export declare const RECLAIM_COUNT = 10;
20
+ /** Length of the generated consumer name when `consume` is not given one. */
21
+ export declare const CONSUMER_ID_LENGTH = 15;
3
22
  //# sourceMappingURL=consts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB,UAAU,CAAA;AAEvC,eAAO,MAAM,iBAAiB,KAAK,CAAA"}
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,UAAU,CAAA;AAEvC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAA;AAElC,gGAAgG;AAChG,eAAO,MAAM,UAAU,MAAM,CAAA;AAE7B,mEAAmE;AACnE,eAAO,MAAM,UAAU,MAAM,CAAA;AAE7B,+FAA+F;AAC/F,eAAO,MAAM,iBAAiB,QAAQ,CAAA;AAEtC,6FAA6F;AAC7F,eAAO,MAAM,oBAAoB,OAAO,CAAA;AAExC,gGAAgG;AAChG,eAAO,MAAM,YAAY,QAAQ,CAAA;AAEjC,6CAA6C;AAC7C,eAAO,MAAM,aAAa,KAAK,CAAA;AAE/B,6EAA6E;AAC7E,eAAO,MAAM,kBAAkB,KAAK,CAAA"}
package/build/consts.js CHANGED
@@ -1,3 +1,22 @@
1
1
  export const DEFAULT_DB_ALIAS = 'redis';
2
- export const DEFAULT_PAGE_SIZE = 10;
2
+ /**
3
+ * Redis is UNPAGED: `list()` without a `size` returns every match, and `0` says exactly that.
4
+ * There is no server side LIMIT to fall back on — answering a criteria already walked the whole
5
+ * namespace — so an implied default would only hide records without saving any work.
6
+ */
7
+ export const DEFAULT_PAGE_SIZE = 0;
8
+ /** Keys per SCAN round trip. Bounded so one sweep never blocks the server the way KEYS does. */
9
+ export const SCAN_BATCH = 200;
10
+ /** Keys per MGET / DEL round trip once SCAN has collected them. */
11
+ export const READ_BATCH = 200;
12
+ /** Entries kept in a stream, trimmed approximately (`MAXLEN ~`) so trimming stays O(1)-ish. */
13
+ export const STREAM_MAX_LENGTH = 10000;
14
+ /** How long a stream read blocks before the consumer loops and looks for pending entries. */
15
+ export const DEFAULT_STREAM_BLOCK = 1000;
16
+ /** How long an unacknowledged entry may sit with its consumer before another may reclaim it. */
17
+ export const RECLAIM_IDLE = 60000;
18
+ /** Entries reclaimed per XAUTOCLAIM pass. */
19
+ export const RECLAIM_COUNT = 10;
20
+ /** Length of the generated consumer name when `consume` is not given one. */
21
+ export const CONSUMER_ID_LENGTH = 15;
3
22
  //# sourceMappingURL=consts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAA;AAEvC,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAA"}
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAA;AAEvC;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAA;AAElC,gGAAgG;AAChG,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,CAAA;AAE7B,mEAAmE;AACnE,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,CAAA;AAE7B,+FAA+F;AAC/F,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAA;AAEtC,6FAA6F;AAC7F,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAA;AAExC,gGAAgG;AAChG,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAA;AAEjC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAA;AAE/B,6EAA6E;AAC7E,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAA"}
@@ -1,4 +1,13 @@
1
- import type { ResourceMaker, ResourceRecord } from '@owlmeans/resource';
1
+ import type { ResourceRecord } from '@owlmeans/resource';
2
2
  import type { RedisResource } from './types.js';
3
- export declare const makeRedisResource: <R extends ResourceRecord, T extends RedisResource<R> = RedisResource<R>>(alias: string, dbAlias?: string, serviceAlias?: string, makeCustomResource?: ResourceMaker<R, T>) => T;
3
+ /**
4
+ * A `Resource` over plain redis strings: one JSON document per namespaced key.
5
+ *
6
+ * Reads by id are O(1) GETs. Everything else — a criteria read, a listing, a count, a purge —
7
+ * walks this resource's OWN namespace with SCAN and evaluates the criteria in memory, because
8
+ * redis has no index to ask. That is affordable for a small namespaced set (a session cache, an
9
+ * adapter store) and wrong for anything unbounded or hot; a resource that needs real queries
10
+ * belongs in mongo or postgres.
11
+ */
12
+ export declare const makeRedisResource: <R extends ResourceRecord, T extends RedisResource<R> = RedisResource<R>>(alias: string, dbAlias?: string, serviceAlias?: string) => T;
4
13
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEvE,OAAO,KAAK,EAA+B,aAAa,EAAE,MAAM,YAAY,CAAA;AAW5E,eAAO,MAAM,iBAAiB,GAC5B,CAAC,SAAS,cAAc,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,4BAE7C,MAAM,gEACQ,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,KACvC,CA0WF,CAAA"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACuC,cAAc,EAEhE,MAAM,oBAAoB,CAAA;AAK3B,OAAO,KAAK,EAA+B,aAAa,EAAE,MAAM,YAAY,CAAA;AAY5E;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,GAC5B,CAAC,SAAS,cAAc,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,EAEvE,OAAO,MAAM,EAAE,gBAA0B,EAAE,qBAA+B,KACzE,CAmZF,CAAA"}