@t4h.framework/cache 0.3.0 → 0.5.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/.ai/skills/framework-cache/SKILL.md +188 -0
- package/dist/ActivityCacheClaim.d.ts +9 -0
- package/dist/ActivityCacheClaim.d.ts.map +1 -0
- package/dist/ActivityCacheClaim.js +3 -0
- package/dist/ActivityCacheClaim.js.map +1 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +13 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +5 -6
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: framework-cache
|
|
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.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# @t4h.framework/cache
|
|
12
|
+
|
|
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).
|
|
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
|
+
## 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.
|
|
87
|
+
|
|
88
|
+
Without a provider, claim resolution fails at runtime (`ClaimProviderNotFoundException` and related errors from core).
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Testing cache behavior
|
|
93
|
+
|
|
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.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
|
|
101
|
+
import { CacheClaim } from '@t4h.framework/cache'
|
|
102
|
+
|
|
103
|
+
const store = new Map<string, unknown>()
|
|
104
|
+
|
|
105
|
+
const env = TestWorkflowEnvironment.create({ now: 0 })
|
|
106
|
+
|
|
107
|
+
await env.execute(workflow, input, {
|
|
108
|
+
claims: [
|
|
109
|
+
mockClaim(CacheClaim, {
|
|
110
|
+
get: key => store.get(key) as any,
|
|
111
|
+
set: async (key, value) => { store.set(key, value) },
|
|
112
|
+
}),
|
|
113
|
+
// ...other claims the workflow's activities need (e.g. HttpClientRequestClaim)
|
|
114
|
+
],
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
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
|
|
182
|
+
|
|
183
|
+
| Package | Role |
|
|
184
|
+
|---------|------|
|
|
185
|
+
| `@t4h.framework/core` | `Claim`, `Serializable`, and the `TestWorkflowEnvironment` testing harness |
|
|
186
|
+
| `@t4h.framework/http` | OAuth2 token caching via `CacheClaim` |
|
|
187
|
+
|
|
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.
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityCacheClaim.js","sourceRoot":"","sources":["../src/ActivityCacheClaim.ts"],"names":[],"mappings":"AAMA,MAAM,OAAgB,kBAAkB;CAUvC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,wBAAgB,cAAc,CAC5B,cAAc,GAAE,OAAO,CAAC,UAAU,CAAM,2DAWzC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { mockClaim } from '@t4h.framework/core/testing';
|
|
2
|
+
import { CacheClaim } from '../CacheClaim.js';
|
|
3
|
+
export function mockCacheClaim(implementation = {}) {
|
|
4
|
+
const store = new Map();
|
|
5
|
+
return mockClaim(CacheClaim, {
|
|
6
|
+
get: async (key) => store.get(key),
|
|
7
|
+
set: async (key, value) => {
|
|
8
|
+
store.set(key, value);
|
|
9
|
+
},
|
|
10
|
+
...implementation,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AAEvD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,MAAM,UAAU,cAAc,CAC5B,iBAAsC,EAAE;IAExC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmB,CAAA;IAExC,OAAO,SAAS,CAAC,UAAU,EAAE;QAC3B,GAAG,EAAE,KAAK,EAAC,GAAG,EAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAU;QACzC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;YACxB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACvB,CAAC;QACD,GAAG,cAAc;KAClB,CAAC,CAAA;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/cache",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.8.0",
|
|
35
34
|
"typescript": "^5.9.3"
|
|
36
35
|
},
|
|
37
36
|
"peerDependencies": {
|
|
38
|
-
"@t4h.framework/core": "^0.
|
|
37
|
+
"@t4h.framework/core": "^0.8.0"
|
|
39
38
|
},
|
|
40
39
|
"packageManager": "yarn@4.12.0",
|
|
41
40
|
"engines": {
|