@t4h.framework/cache 0.3.0 → 0.4.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.
@@ -0,0 +1,236 @@
1
+ ---
2
+ name: framework-cache
3
+ description: >-
4
+ Guides correct use of @t4h.framework/cache in T4H Framework workflows:
5
+ CacheClaim for key-value storage inside activities, Serializable values, TTL
6
+ on set, runtime Claim providers, and OAuth2 token caching via @t4h.framework/http.
7
+ Use when implementing cache-backed activities, wiring CacheClaim providers,
8
+ testing cache get/set, or working in packages/cache.
9
+ ---
10
+
11
+ # @t4h.framework/cache
12
+
13
+ Abstract **CacheClaim** for serializable key-value storage inside activities. The package defines the claim contract only; runtimes supply concrete backends (e.g. Redis).
14
+
15
+ Package path: `framework/packages/cache`. Peer dependency: `@t4h.framework/core` (`Serializable`, `Claim`, activities).
16
+
17
+ ---
18
+
19
+ ## Import surface
20
+
21
+ Only symbols exported from `src/cache.ts` exist in the public API:
22
+
23
+ ```typescript
24
+ import { CacheClaim, type CacheClaimSetOptions } from '@t4h.framework/cache'
25
+ ```
26
+
27
+ Do not invent additional exports or helpers not present in this package.
28
+
29
+ ---
30
+
31
+ ## API contract
32
+
33
+ ### `CacheClaimSetOptions`
34
+
35
+ ```typescript
36
+ interface CacheClaimSetOptions {
37
+ ttl?: number
38
+ }
39
+ ```
40
+
41
+ `ttl` is optional. The README and `OAuth2` pass it in **milliseconds** (see OAuth2 section). Runtime implementations decide how to honor TTL; the abstract type does not document units beyond the optional number.
42
+
43
+ ### `CacheClaim`
44
+
45
+ ```typescript
46
+ abstract class CacheClaim {
47
+ public abstract get<T extends Serializable>(
48
+ key: string,
49
+ ): T | undefined | Promise<T | undefined>
50
+
51
+ public abstract set(
52
+ key: string,
53
+ value: Serializable,
54
+ options?: CacheClaimSetOptions,
55
+ ): Promise<void>
56
+ }
57
+ ```
58
+
59
+ | Method | Behavior |
60
+ |--------|----------|
61
+ | `get` | Returns stored value or `undefined`. May be sync or async — **always `await`** at call sites. |
62
+ | `set` | Stores a `Serializable` value. Always returns `Promise<void>`. Optional `ttl` in `options`. |
63
+
64
+ Values must satisfy `Serializable` from `@t4h.framework/core`: primitives, `null`, `undefined`, `Binary`, `SerializableClass`, and arrays/objects composed of those.
65
+
66
+ ---
67
+
68
+ ## Declaring CacheClaim in an activity
69
+
70
+ Follow the same `Claim` pattern as other framework claims (see `framework-core` skill). Declare the claim on the activity, access it only inside activity methods while the claim context is active (`Claim.run` stack).
71
+
72
+ ```typescript
73
+ import { SyncActivity, Claim } from '@t4h.framework/core'
74
+ import { CacheClaim } from '@t4h.framework/cache'
75
+
76
+ class GetCachedConfigActivity extends SyncActivity<
77
+ [key: string],
78
+ { key: string },
79
+ any,
80
+ any
81
+ > {
82
+ private readonly claims = new Claim({
83
+ cache: CacheClaim,
84
+ })
85
+
86
+ public async toInput(key: string) {
87
+ return { key }
88
+ }
89
+
90
+ public async run(input: { key: string }) {
91
+ const cached = await this.claims.cache.get(input.key)
92
+
93
+ if (cached) return cached
94
+
95
+ const value = { setting: 'default' }
96
+ await this.claims.cache.set(input.key, value, { ttl: 3600 })
97
+
98
+ return value
99
+ }
100
+
101
+ public toOutput(output: any) {
102
+ return output
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### Cache-aside pattern
108
+
109
+ 1. `await cache.get(key)` — if hit, return.
110
+ 2. Compute or fetch the value.
111
+ 3. `await cache.set(key, value, { ttl })` — store for later runs/replays.
112
+ 4. Return the value.
113
+
114
+ Use **stable, namespaced keys** (prefix + id) to avoid collisions across features or tenants.
115
+
116
+ ---
117
+
118
+ ## Runtime provider
119
+
120
+ The runtime (or test harness) must register a concrete implementation:
121
+
122
+ ```typescript
123
+ { provide: CacheClaim, value: new RedisCacheClaimImpl() }
124
+ ```
125
+
126
+ `RedisCacheClaimImpl` is illustrative — implement `CacheClaim` in the runtime layer; this package does not ship a production backend.
127
+
128
+ Without a provider, claim resolution fails at runtime (`ClaimProviderNotFoundException` and related errors from core).
129
+
130
+ ---
131
+
132
+ ## Testing cache behavior
133
+
134
+ There are no unit tests under `packages/cache/`. Test with an in-memory `CacheClaim` double or `Claim.run`:
135
+
136
+ ```typescript
137
+ import { describe, expect, it, vi } from 'vitest'
138
+ import { Claim } from '@t4h.framework/core'
139
+ import { CacheClaim } from '@t4h.framework/cache'
140
+
141
+ const store = new Map<string, unknown>()
142
+
143
+ const mockCache: CacheClaim = {
144
+ get: key => store.get(key) as any,
145
+ set: async (key, value) => { store.set(key, value) },
146
+ } as CacheClaim
147
+
148
+ await Claim.run(
149
+ [{ provide: CacheClaim, value: mockCache }],
150
+ () => myActivity.run(input),
151
+ )
152
+ ```
153
+
154
+ For full workflows, pass the same provider via `WorkflowTesting.run` (see **framework-core** skill). When testing TTL-dependent logic, implement `set` with TTL semantics in the test double.
155
+
156
+ ---
157
+
158
+ ## Downstream consumer: OAuth2 (`@t4h.framework/http`)
159
+
160
+ `@t4h.framework/http` depends on this package for token caching only. `HttpAuth` and `OAuth2` declare `CacheClaim` via `Claim`:
161
+
162
+ ```typescript
163
+ // HttpAuth — Symbol-keyed claims bag
164
+ public readonly [HTTP_AUTH_CLAIMS_KEY] = new Claim({ cache: CacheClaim })
165
+
166
+ // OAuth2
167
+ protected readonly claims = new Claim({
168
+ http: HttpClientRequestClaim,
169
+ cache: CacheClaim,
170
+ })
171
+ ```
172
+
173
+ OAuth2 cache usage (`packages/http/src/models/OAuth2.ts`):
174
+
175
+ | Concern | Implementation |
176
+ |---------|----------------|
177
+ | Cache key | `` `oauth2:token:${this.config.clientId}` `` (`tokenCacheKey`) |
178
+ | Stored shape | `OAuth2Token` plus `expiresAt: number` (ms timestamp) |
179
+ | TTL on `set` | `Math.floor(token.expires_in * 1000 * 0.9)` — 90% of `expires_in` in **seconds**, converted to ms |
180
+ | Read path | `get` → if missing, fetch token and `set`; if `expiresAt > Date.now()`, return; else refresh or re-fetch |
181
+
182
+ Workflows using OAuth2 grant classes must wire **`CacheClaim`** and **`HttpClientRequestClaim`** in the runtime. See `framework-http` skill for HTTP details.
183
+
184
+ ---
185
+
186
+ ## Implementing a custom `CacheClaim`
187
+
188
+ Subclass `CacheClaim` and implement both methods:
189
+
190
+ ```typescript
191
+ import { CacheClaim, type CacheClaimSetOptions } from '@t4h.framework/cache'
192
+ import type { Serializable } from '@t4h.framework/core'
193
+
194
+ export class MyCacheClaim extends CacheClaim {
195
+ public get<T extends Serializable>(
196
+ key: string,
197
+ ): T | undefined | Promise<T | undefined> {
198
+ // ...
199
+ }
200
+
201
+ public set(
202
+ key: string,
203
+ value: Serializable,
204
+ options?: CacheClaimSetOptions,
205
+ ): Promise<void> {
206
+ // honor options?.ttl if the backend supports it
207
+ return Promise.resolve()
208
+ }
209
+ }
210
+ ```
211
+
212
+ Register: `{ provide: CacheClaim, value: new MyCacheClaim() }`.
213
+
214
+ ---
215
+
216
+ ## Do / Don't
217
+
218
+ | Do | Don't |
219
+ |----|-------|
220
+ | `await` both `get` and `set` | Assume `get` is always synchronous |
221
+ | Store only `Serializable` values | Cache class instances, functions, or `Buffer` unless wrapped as `Binary` |
222
+ | Namespace keys (`feature:id`) | Use bare global keys like `"token"` |
223
+ | Provide `CacheClaim` in runtime/test `claims` | Use cache outside `Claim` / activity context |
224
+ | Use a test double with explicit TTL when testing expiry | Assume any in-memory mock enforces TTL unless you implement it |
225
+ | Match OAuth2 TTL units (ms) when mirroring that pattern | Mix seconds and milliseconds for `ttl` without checking the caller |
226
+
227
+ ---
228
+
229
+ ## Related packages
230
+
231
+ | Package | Role |
232
+ |---------|------|
233
+ | `@t4h.framework/core` | `Claim`, `Serializable`, activities, `WorkflowTesting`, `Internals` |
234
+ | `@t4h.framework/http` | OAuth2 token caching via `CacheClaim` |
235
+
236
+ For claim wiring, activity structure, and testing harnesses, use the **framework-core** skill. For HTTP and OAuth2, use the **framework-http** skill.
@@ -0,0 +1,9 @@
1
+ import type { Serializable } from '@t4h.framework/core';
2
+ export interface ActivityCacheClaimSetOptions {
3
+ ttl?: number;
4
+ }
5
+ export declare abstract class ActivityCacheClaim {
6
+ abstract get<T extends Serializable>(key: string): T | undefined | Promise<T | undefined>;
7
+ abstract set(key: string, value: Serializable, options?: ActivityCacheClaimSetOptions): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=ActivityCacheClaim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActivityCacheClaim.d.ts","sourceRoot":"","sources":["../src/ActivityCacheClaim.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD,MAAM,WAAW,4BAA4B;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,8BAAsB,kBAAkB;aACtB,GAAG,CAAC,CAAC,SAAS,YAAY,EACxC,GAAG,EAAE,MAAM,GACV,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;aAEzB,GAAG,CACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,4BAA4B,GACrC,OAAO,CAAC,IAAI,CAAC;CACjB"}
@@ -0,0 +1,3 @@
1
+ export class ActivityCacheClaim {
2
+ }
3
+ //# sourceMappingURL=ActivityCacheClaim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActivityCacheClaim.js","sourceRoot":"","sources":["../src/ActivityCacheClaim.ts"],"names":[],"mappings":"AAMA,MAAM,OAAgB,kBAAkB;CAUvC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t4h.framework/cache",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Cache module for the T4H Framework",
5
5
  "homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/cache",
6
6
  "bugs": "https://github.com/tech4humans-brasil/framework/issues",
@@ -20,7 +20,8 @@
20
20
  "types": "./dist/cache.d.ts",
21
21
  "files": [
22
22
  "dist",
23
- "LICENSE"
23
+ "LICENSE",
24
+ ".ai"
24
25
  ],
25
26
  "scripts": {
26
27
  "build": "tsc --project tsconfig.build.json",
@@ -28,14 +29,12 @@
28
29
  "lint": "eslint .",
29
30
  "prepublishOnly": "yarn build"
30
31
  },
31
- "dependencies": {
32
- "@t4h.framework/core": "^0.3.0"
33
- },
34
32
  "devDependencies": {
33
+ "@t4h.framework/core": "^0.6.0",
35
34
  "typescript": "^5.9.3"
36
35
  },
37
36
  "peerDependencies": {
38
- "@t4h.framework/core": "^0.3.0"
37
+ "@t4h.framework/core": "^0.6.0"
39
38
  },
40
39
  "packageManager": "yarn@4.12.0",
41
40
  "engines": {