@t4h.framework/cache 0.6.10 → 0.6.12

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.
@@ -1,188 +1,81 @@
1
1
  ---
2
2
  name: framework-cache
3
3
  description: >-
4
- Guides correct use of @t4h.framework/cache in T4H Framework workflows:
5
- CacheClaim that backs package caching (OAuth2 token caching via
6
- @t4h.framework/http), Serializable values, TTL semantics, and runtime Claim
7
- providers. Use when a workflow relies on cache-backed package primitives,
8
- wiring CacheClaim providers, or testing cache behavior in workflow tests.
4
+ Guides the use of CacheClaim from @t4h.framework/cache in T4H Framework
5
+ apps: reading and writing Serializable values from an activity, the ttl
6
+ option of CacheClaimSetOptions, registering a CacheClaim provider in the
7
+ runtime or in tests (mockClaim, TestWorkflowEnvironment), and the
8
+ CacheClaim that OAuth2 token caching in @t4h.framework/http needs. Use when
9
+ code imports CacheClaim, when a workflow that uses OAuth2 fails with
10
+ ClaimProviderNotFoundException for CacheClaim, or when you write a
11
+ CacheClaim implementation or test double.
9
12
  ---
10
13
 
11
14
  # @t4h.framework/cache
12
15
 
13
- Abstract **CacheClaim** for serializable key-value storage used by framework-package primitives (e.g. OAuth2 token caching). The package defines the claim contract only; runtimes supply concrete backends (e.g. Redis).
16
+ `CacheClaim` is an abstract claim with `get(key)` and
17
+ `set(key, value, options?)`. The package has no implementation. The runtime or
18
+ the test must give one.
14
19
 
15
- Package path: `framework/packages/cache`. Peer dependency: `@t4h.framework/core` (`Serializable`, `Claim`, activities).
20
+ ## Use the cache in an activity
16
21
 
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`
22
+ 1. Declare the claim in the activity with `new Claim({ cache: CacheClaim })`.
23
+ 2. Call it only in `run()`. A claim is not available in workflow code.
24
+ 3. Give each key a prefix (`feature:id`). All callers share one store.
44
25
 
45
26
  ```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>
27
+ private readonly claims = new Claim({ cache: CacheClaim })
28
+
29
+ public async run({ id }: { id: string }) {
30
+ const key = `config:${id}`
31
+ const cached = await this.claims.cache.get<Config>(key)
32
+ if (cached) return cached
33
+ const value = await loadConfig(id)
34
+ await this.claims.cache.set(key, value)
35
+ return value
56
36
  }
57
37
  ```
58
38
 
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
- ## How workflows use it
69
-
70
- Workflows never call `CacheClaim` directly. It backs the caching built into
71
- framework-package primitives — most notably **OAuth2 token caching** in
72
- `@t4h.framework/http`. Your only responsibility is that the runtime **registers
73
- a `CacheClaim` provider** (below) so those primitives work. See **Downstream
74
- consumer: OAuth2**.
75
-
76
- ---
77
-
78
- ## Runtime provider
79
-
80
- The runtime (or test harness) must register a concrete implementation:
81
-
82
- ```typescript
83
- { provide: CacheClaim, value: new RedisCacheClaimImpl() }
84
- ```
85
-
86
- `RedisCacheClaimImpl` is illustrative — implement `CacheClaim` in the runtime layer; this package does not ship a production backend.
39
+ ## Traps
87
40
 
88
- Without a provider, claim resolution fails at runtime (`ClaimProviderNotFoundException` and related errors from core).
41
+ - `get` can return a value or a `Promise`. Always `await` it.
42
+ - `value` must be `Serializable` from `@t4h.framework/core`. A `Date`, a `Map`
43
+ or a class instance that is not serializable fails `tsc`.
44
+ - `ttl` has no unit in the type. The implementation decides the unit. Do not
45
+ guess: read the implementation that your runtime gives. `OAuth2` in
46
+ `@t4h.framework/http` gives `ttl` in milliseconds.
47
+ - A workflow that uses an `OAuth2` grant from `@t4h.framework/http` needs a
48
+ `CacheClaim` provider, also if your code never imports `CacheClaim`.
49
+ - Import only from `@t4h.framework/cache`. The package has no deep exports.
89
50
 
90
- ---
91
-
92
- ## Testing cache behavior
51
+ ## Test
93
52
 
94
- Test the **workflow** with `TestWorkflowEnvironment` from
95
- `@t4h.framework/core/testing`, mocking `CacheClaim` (a workflow that uses OAuth2
96
- also needs `HttpClientRequestClaim`). An in-memory `Map` makes a good double. See
97
- the **framework-workflow-testing** skill for the harness API.
53
+ Give a `Map` double with `mockClaim`:
98
54
 
99
55
  ```typescript
100
- import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
101
56
  import { CacheClaim } from '@t4h.framework/cache'
57
+ import type { Serializable } from '@t4h.framework/core'
58
+ import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
102
59
 
103
- const store = new Map<string, unknown>()
104
-
105
- const env = TestWorkflowEnvironment.create({ now: 0 })
60
+ const store = new Map<string, Serializable>()
106
61
 
107
- await env.execute(workflow, input, {
62
+ await TestWorkflowEnvironment.create().execute(workflow, input, {
108
63
  claims: [
109
64
  mockClaim(CacheClaim, {
110
- get: key => store.get(key) as any,
111
- set: async (key, value) => { store.set(key, value) },
65
+ get: <T extends Serializable>(key: string) =>
66
+ store.get(key) as T | undefined,
67
+ set: async (key, value) => {
68
+ store.set(key, value)
69
+ },
112
70
  }),
113
- // ...other claims the workflow's activities need (e.g. HttpClientRequestClaim)
114
71
  ],
115
72
  })
116
73
  ```
117
74
 
118
- When testing TTL-dependent logic, implement `set` with TTL semantics in the test double.
119
-
120
- ---
121
-
122
- ## Downstream consumer: OAuth2 (`@t4h.framework/http`)
123
-
124
- `@t4h.framework/http` depends on this package for token caching only — the
125
- `HttpAuth` / `OAuth2` grant classes use `CacheClaim` internally to store tokens:
126
-
127
- | Concern | Implementation |
128
- |---------|----------------|
129
- | Cache key | `` `oauth2:token:${this.config.clientId}` `` (`tokenCacheKey`) |
130
- | Stored shape | `OAuth2Token` plus `expiresAt: number` (ms timestamp) |
131
- | TTL on `set` | `Math.floor(token.expires_in * 1000 * 0.9)` — 90% of `expires_in` in **seconds**, converted to ms |
132
- | Read path | `get` → if missing, fetch token and `set`; if `expiresAt > Date.now()`, return; else refresh or re-fetch |
133
-
134
- Workflows using OAuth2 grant classes must wire **`CacheClaim`** and **`HttpClientRequestClaim`** in the runtime. See `framework-http` skill for HTTP details.
135
-
136
- ---
137
-
138
- ## Implementing a custom `CacheClaim`
139
-
140
- Subclass `CacheClaim` and implement both methods:
141
-
142
- ```typescript
143
- import { CacheClaim, type CacheClaimSetOptions } from '@t4h.framework/cache'
144
- import type { Serializable } from '@t4h.framework/core'
145
-
146
- export class MyCacheClaim extends CacheClaim {
147
- public get<T extends Serializable>(
148
- key: string,
149
- ): T | undefined | Promise<T | undefined> {
150
- // ...
151
- }
152
-
153
- public set(
154
- key: string,
155
- value: Serializable,
156
- options?: CacheClaimSetOptions,
157
- ): Promise<void> {
158
- // honor options?.ttl if the backend supports it
159
- return Promise.resolve()
160
- }
161
- }
162
- ```
163
-
164
- Register: `{ provide: CacheClaim, value: new MyCacheClaim() }`.
165
-
166
- ---
167
-
168
- ## Do / Don't
169
-
170
- | Do | Don't |
171
- |----|-------|
172
- | `await` both `get` and `set` | Assume `get` is always synchronous |
173
- | Store only `Serializable` values | Cache class instances, functions, or `Buffer` unless wrapped as `Binary` |
174
- | Namespace keys (`feature:id`) | Use bare global keys like `"token"` |
175
- | Provide `CacheClaim` in runtime/test `claims` | Use cache outside `Claim` / activity context |
176
- | Use a test double with explicit TTL when testing expiry | Assume any in-memory mock enforces TTL unless you implement it |
177
- | Match OAuth2 TTL units (ms) when mirroring that pattern | Mix seconds and milliseconds for `ttl` without checking the caller |
178
-
179
- ---
180
-
181
- ## Related packages
75
+ The double does not expire entries. If the test depends on `ttl`, write that
76
+ logic in `set` and `get`.
182
77
 
183
- | Package | Role |
184
- |---------|------|
185
- | `@t4h.framework/core` | `Claim`, `Serializable`, and the `TestWorkflowEnvironment` testing harness |
186
- | `@t4h.framework/http` | OAuth2 token caching via `CacheClaim` |
78
+ ## Reference
187
79
 
188
- For claim wiring use the **framework-core** skill; for the testing harness use **framework-workflow-testing**. For HTTP and OAuth2, use the **framework-http** skill.
80
+ - README: https://www.npmjs.com/package/@t4h.framework/cache
81
+ - Types: `node_modules/@t4h.framework/cache/dist/CacheClaim.d.ts`
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @t4h.framework/cache
2
2
 
3
+ ## 0.6.12
4
+
5
+ ### Patch Changes
6
+
7
+ - [#257](https://github.com/tech4humans-brasil/framework/pull/257) [`586bfb4`](https://github.com/tech4humans-brasil/framework/commit/586bfb4663f4a478527ee0c8db360603f034d615) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The cache README is the short human doc of `CacheClaim`, and its example now compiles. The README and the skill no longer give a unit for `ttl`, because the type does not fix one.
8
+
9
+ ## 0.6.11
10
+
11
+ ### Patch Changes
12
+
13
+ - [#240](https://github.com/tech4humans-brasil/framework/pull/240) [`0606115`](https://github.com/tech4humans-brasil/framework/commit/0606115e2e0baa994e9f0ed3559ee54ecac88e44) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The skills do not repeat a rule that a lint rule, `tsc` or a runtime error already enforces. Each such rule is now one line that names the gate. The examples that broke a gate (`process.env`, `as any`, an inline secret, an `EnvDesign` in a workflow file, `ref.output` before `wait()`) are correct now.
14
+
3
15
  ## 0.6.10
4
16
 
5
17
  ## 0.6.9
package/README.md CHANGED
@@ -1,70 +1,76 @@
1
1
  # @t4h.framework/cache
2
2
 
3
- Cache claim for key-value storage within activities.
3
+ `CacheClaim`: a claim for key-value storage of `Serializable` values in
4
+ activities. The package defines only the contract. The runtime gives the
5
+ implementation.
4
6
 
5
- ## Claim
7
+ ## Installation
6
8
 
7
- ### CacheClaim
8
-
9
- Abstract dependency for caching serializable values. An activity declares this claim when it needs to cache data (e.g. tokens, computed results). The runtime provides a concrete implementation (e.g. Redis, in-memory).
10
-
11
- ```typescript
12
- import { CacheClaim } from '@t4h.framework/cache'
9
+ ```bash
10
+ npm install @t4h.framework/cache
13
11
  ```
14
12
 
15
- ### Interface
13
+ ## Contract
16
14
 
17
- ```typescript
18
- interface CacheClaimSetOptions {
19
- ttl?: number
20
- }
15
+ | Method | Result |
16
+ | --------------------------- | ---------------------------------------------------------- |
17
+ | `get<T>(key)` | The stored value, or `undefined`. It can be sync or async. |
18
+ | `set(key, value, options?)` | `Promise<void>`. `value` must be `Serializable`. |
19
+ | `CacheClaimSetOptions.ttl` | Optional number. The type does not fix a unit. |
21
20
 
22
- abstract class CacheClaim {
23
- public abstract get<T extends Serializable>(key: string): T | undefined | Promise<T | undefined>
24
- public abstract set(key: string, value: Serializable, options?: CacheClaimSetOptions): Promise<void>
25
- }
26
- ```
21
+ The implementation decides how to apply `ttl`. Before you set `ttl`, make sure
22
+ that you use the unit of the implementation that your runtime gives.
23
+ `OAuth2` in `@t4h.framework/http` uses this claim and gives `ttl` in
24
+ milliseconds.
27
25
 
28
- ### Usage inside an activity
26
+ ## Usage in an activity
29
27
 
30
28
  ```typescript
31
- import { SyncActivity, Claim } from '@t4h.framework/core'
29
+ import { Claim, SyncActivity } from '@t4h.framework/core'
32
30
  import { CacheClaim } from '@t4h.framework/cache'
33
31
 
34
- class GetCachedConfigActivity extends SyncActivity<
32
+ type Config = { setting: string }
33
+
34
+ export class GetConfigActivity extends SyncActivity<
35
35
  [key: string],
36
36
  { key: string },
37
- any
37
+ Config,
38
+ Config
38
39
  > {
39
- private readonly claims = new Claim({
40
- cache: CacheClaim,
41
- })
40
+ private readonly claims = new Claim({ cache: CacheClaim })
42
41
 
43
- public async toInput(key: string) {
42
+ public toInput(key: string) {
44
43
  return { key }
45
44
  }
46
45
 
47
- public async run(input: { key: string }) {
48
- const cached = await this.claims.cache.get(input.key)
49
-
46
+ public async run({ key }: { key: string }) {
47
+ const cacheKey = `config:${key}`
48
+ const cached = await this.claims.cache.get<Config>(cacheKey)
50
49
  if (cached) return cached
51
50
 
52
51
  const value = { setting: 'default' }
53
- await this.claims.cache.set(input.key, value, { ttl: 3600 })
54
-
52
+ await this.claims.cache.set(cacheKey, value)
55
53
  return value
56
54
  }
57
55
 
58
- public toOutput(output: any) {
56
+ public toOutput(output: Config) {
59
57
  return output
60
58
  }
61
59
  }
62
60
  ```
63
61
 
64
- ### Runtime provider
62
+ Put a prefix on each key (`feature:id`), because all callers share one store.
63
+
64
+ ## Provider
65
65
 
66
- The runtime must supply a concrete implementation:
66
+ The runtime must give an implementation of `CacheClaim`:
67
67
 
68
68
  ```typescript
69
- { provide: CacheClaim, value: new RedisCacheClaimImpl() }
69
+ { provide: CacheClaim, value: new MyCacheClaim() }
70
70
  ```
71
+
72
+ If no provider is registered, the activity fails with
73
+ `ClaimProviderNotFoundException` from `@t4h.framework/core`. In tests, use
74
+ `mockClaim(CacheClaim, { get, set })` from `@t4h.framework/core/testing`. A
75
+ `Map` is a good test double. A double applies `ttl` only if you write that
76
+ logic.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t4h.framework/cache",
3
- "version": "0.6.10",
3
+ "version": "0.6.12",
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",
@@ -26,11 +26,12 @@
26
26
  "scripts": {
27
27
  "build": "tsc --project tsconfig.build.json",
28
28
  "check-types": "tsc --noEmit",
29
- "lint": "eslint .",
29
+ "check-package": "publint --strict && attw --pack . --profile esm-only",
30
+ "lint": "eslint . --max-warnings 0",
30
31
  "prepublishOnly": "yarn build"
31
32
  },
32
33
  "devDependencies": {
33
- "@t4h.framework/core": "^0.19.0",
34
+ "@t4h.framework/core": "^0.21.0",
34
35
  "typescript": "^5.9.3"
35
36
  },
36
37
  "peerDependencies": {