@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.
@@ -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,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"}
@@ -0,0 +1,3 @@
1
+ import { CacheClaim } from '../CacheClaim.js';
2
+ export declare function mockCacheClaim(implementation?: Partial<CacheClaim>): import("@t4h.framework/core").ClaimProvider<CacheClaim>;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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.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.3.0"
37
+ "@t4h.framework/core": "^0.8.0"
39
38
  },
40
39
  "packageManager": "yarn@4.12.0",
41
40
  "engines": {