@t4h.framework/fs 0.2.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,249 @@
1
+ ---
2
+ name: framework-fs
3
+ description: >-
4
+ Guides correct use of @t4h.framework/fs in T4H Framework workflows: the
5
+ FileSystemClaim abstraction for reading/writing files inside activities,
6
+ Claim wiring, Binary results, and runtime providers. Use when implementing
7
+ activities that persist or load file payloads, mocking FileSystemClaim in tests,
8
+ registering fs claim providers, debugging missing fs provider errors, or working
9
+ in packages/fs, packages/http, or packages/jsx.
10
+ ---
11
+
12
+ # @t4h.framework/fs
13
+
14
+ File system **claim** for reading and writing files inside workflow activities. The package defines only the abstract contract; concrete storage (local disk, object storage, etc.) is supplied by the **runtime** or **dev** claim registry.
15
+
16
+ Package path: `framework/packages/fs`. Peer dependency: `@t4h.framework/core` (`Binary`).
17
+
18
+ ---
19
+
20
+ ## Import surface
21
+
22
+ The package exports a single public symbol from `src/fs.ts`:
23
+
24
+ ```typescript
25
+ import { FileSystemClaim } from '@t4h.framework/fs'
26
+ ```
27
+
28
+ Do not import from internal paths such as `src/FileSystemClaim.ts` in application code.
29
+
30
+ ---
31
+
32
+ ## FileSystemClaim contract
33
+
34
+ ```typescript
35
+ import type { Readable } from 'node:stream'
36
+ import type { Binary } from '@t4h.framework/core'
37
+
38
+ export abstract class FileSystemClaim {
39
+ public abstract write(
40
+ path: string,
41
+ readable: Readable | Buffer,
42
+ ): Binary | Promise<Binary>
43
+
44
+ public abstract read(path: string): Binary | Promise<Binary>
45
+ }
46
+ ```
47
+
48
+ | Method | Purpose |
49
+ |--------|---------|
50
+ | `write(path, readable)` | Persist stream or buffer at `path`; return a `Binary` handle (metadata + `stream()`). |
51
+ | `read(path)` | Load an existing file at `path`; return a `Binary` handle. |
52
+
53
+ Both methods may return synchronously or as a `Promise`. Implementations choose storage layout; callers should only rely on the returned `Binary` API.
54
+
55
+ ### Binary (from `@t4h.framework/core`)
56
+
57
+ ```typescript
58
+ export abstract class Binary {
59
+ public abstract readonly contentType: string
60
+ public abstract readonly contentLength: number
61
+ public abstract stream(): Readable | Promise<Readable>
62
+ }
63
+ ```
64
+
65
+ Typical activity flow after `write` or `read`:
66
+
67
+ ```typescript
68
+ const binary = await this.claims.fs.write('reports/output.pdf', buffer)
69
+ const stream = await binary.stream()
70
+ // consume stream (e.g. stream.toArray() then Buffer.concat)
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Declaring the claim in an activity
76
+
77
+ Activities that touch files declare `FileSystemClaim` via `Claim` from `@t4h.framework/core`. Access `this.claims.fs` only while execution is inside `Claim.run` (same as any claim).
78
+
79
+ ```typescript
80
+ import { SyncActivity, Claim } from '@t4h.framework/core'
81
+ import { FileSystemClaim } from '@t4h.framework/fs'
82
+
83
+ class SaveReportActivity extends SyncActivity<[data: Buffer], { path: string }, any, any> {
84
+ private readonly claims = new Claim({
85
+ fs: FileSystemClaim,
86
+ })
87
+
88
+ public async toInput(data: Buffer) {
89
+ return { path: 'reports/output.pdf' }
90
+ }
91
+
92
+ public async run(input: { path: string }) {
93
+ const binary = await this.claims.fs.write(input.path, Buffer.from('...'))
94
+ return { contentType: binary.contentType }
95
+ }
96
+
97
+ public toOutput(output: any) {
98
+ return output
99
+ }
100
+ }
101
+ ```
102
+
103
+ Claim key name (`fs` above) is arbitrary but must match how you access `this.claims.fs`. Downstream packages use `fs` consistently (`AbstractHttpRequestActivity`, jsx render activities).
104
+
105
+ ---
106
+
107
+ ## Runtime provider
108
+
109
+ The runtime must register a concrete subclass:
110
+
111
+ ```typescript
112
+ { provide: FileSystemClaim, value: new MyFileSystemClaimImpl() }
113
+ ```
114
+
115
+ `MyFileSystemClaimImpl` must extend `FileSystemClaim` and implement `write` and `read`. The README uses `LocalFileSystemClaimImpl` as a **placeholder name** for that implementation; there is no class by that name in this monorepo.
116
+
117
+ ---
118
+
119
+ ## Known consumers in this monorepo
120
+
121
+ These packages depend on `@t4h.framework/fs` and declare `FileSystemClaim`; read their skills/README for full workflow patterns.
122
+
123
+ | Package | Usage |
124
+ |---------|--------|
125
+ | `@t4h.framework/http` | `AbstractHttpRequestActivity` writes HTTP response bodies via `fs.write(\`${uuid}-http-request-activity-response-body.${ext}\`, response.body)`; wraps result in `Body`. |
126
+ | `@t4h.framework/jsx` | `RenderWorkflowActivity` / `RenderFormWorkflowActivity` use `fs.write('rsc.txt', ...)` in `toInput` and consume `Binary` in `run`. |
127
+
128
+ ### HTTP response persistence (reference)
129
+
130
+ From `AbstractHttpRequestActivity.run`:
131
+
132
+ ```typescript
133
+ const ext =
134
+ typeof contentType === 'string' ? mime.getExtension(contentType) : 'bin'
135
+
136
+ const binary = await this[CLAIMS_KEY].fs.write(
137
+ `${uuidv7()}-http-request-activity-response-body.${ext}`,
138
+ response.body,
139
+ )
140
+
141
+ return this.toHttpOutput({
142
+ status: response.status,
143
+ headers: response.headers,
144
+ body: new Body(binary),
145
+ })
146
+ ```
147
+
148
+ Extension comes from response `content-type` when present; otherwise `'bin'`. Path is unique per request (uuid prefix).
149
+
150
+ ### JSX render activities (reference)
151
+
152
+ ```typescript
153
+ public readonly claims = new Claim({
154
+ fs: FileSystemClaim,
155
+ })
156
+
157
+ public async toInput() {
158
+ return await this.claims.fs.write('rsc.txt', Buffer.from('Hello World!'))
159
+ }
160
+
161
+ public async run(binary: Binary) {
162
+ const stream = await binary.stream()
163
+ const chunks = await stream.toArray()
164
+ return Buffer.concat(chunks).toString()
165
+ }
166
+ ```
167
+
168
+ Current `toInput` stubs are placeholders; real implementations should still use `FileSystemClaim.write(path, Buffer | Readable)` per README.
169
+
170
+ ---
171
+
172
+ ## Testing
173
+
174
+ There are **no unit tests inside `packages/fs`**. Use patterns from `packages/http` activity tests.
175
+
176
+ ### Mocking FileSystemClaim in activity tests
177
+
178
+ From `AbstractHttpRequestActivity.spec.ts` — wrap activity execution in `Claim.run` and provide partial mocks:
179
+
180
+ ```typescript
181
+ import { Claim } from '@t4h.framework/core'
182
+ import { FileSystemClaim } from '@t4h.framework/fs'
183
+ import { HttpClientRequestClaim } from '@t4h.framework/http'
184
+
185
+ mockWrite.mockImplementation(async (_path: string, readable: Readable) => {
186
+ const chunks = await readable.toArray()
187
+ return new MockBinary(Buffer.concat(chunks), contentType)
188
+ })
189
+
190
+ await Claim.run(
191
+ [
192
+ {
193
+ provide: HttpClientRequestClaim,
194
+ value: { request: mockRequest } as HttpClientRequestClaim,
195
+ },
196
+ {
197
+ provide: FileSystemClaim,
198
+ value: {
199
+ write: mockWrite,
200
+ read: vi.fn(),
201
+ } as unknown as FileSystemClaim,
202
+ },
203
+ ],
204
+ () => new TestableActivity().run(input),
205
+ )
206
+ ```
207
+
208
+ Mock `write` to return a `Binary` compatible with what the activity expects (e.g. `Body` for HTTP). Assert `mockWrite` was called with expected path and body when relevant.
209
+
210
+ ### Workflow-level tests
211
+
212
+ `WorkflowTesting.run` from `@t4h.framework/core/testing` runs the full claim stack when you pass explicit `FileSystemClaim` providers. For isolated activity unit tests, prefer `Claim.run` with partial mocks.
213
+
214
+ ---
215
+
216
+ ## Implementing a custom FileSystemClaim
217
+
218
+ 1. **Extend** `FileSystemClaim` from `@t4h.framework/fs`.
219
+ 2. **Return** a `Binary` subclass (or core-compatible `Binary`) from `write` and `read` with correct `contentType`, `contentLength`, and `stream()`.
220
+ 3. **Accept** `Readable | Buffer` in `write` (match the abstract signature).
221
+ 4. **Register** `{ provide: FileSystemClaim, value: new YourImpl() }` in runtime or test `Claim.run` providers.
222
+ 5. **Keep paths deterministic** when activities replay — unpredictable temp paths are fine if only `Binary` metadata is serialized and replay re-executes `write`; know your serializer/replay story if `path` is persisted.
223
+
224
+ ---
225
+
226
+ ## App / monorepo checklist
227
+
228
+ 1. Add `"@t4h.framework/fs": "workspace:^"` (or published version) to app `dependencies` when activities use `FileSystemClaim`.
229
+ 2. Ensure **`@t4h.framework/core`** is available (peer of `fs`).
230
+ 3. Register **`FileSystemClaim`** in the runtime or test `claims` array.
231
+ 4. Declare `new Claim({ fs: FileSystemClaim })` (or equivalent key) on every activity that calls `this.claims.fs`.
232
+ 5. Build before publish: `yarn build` in `packages/fs` (`tsc --project tsconfig.build.json`).
233
+
234
+ ---
235
+
236
+ ## Anti-patterns
237
+
238
+ - Importing from `@t4h.framework/fs` paths other than the package export (`FileSystemClaim` only).
239
+ - Calling `this.claims.fs` **outside** `Claim.run` / workflow execution context → `ClaimStorageNotActiveException`.
240
+ - Omitting `FileSystemClaim` from providers while using `@t4h.framework/http` or `@t4h.framework/jsx` activities that require `fs`.
241
+ - Assuming `packages/fs` ships a concrete disk/S3 implementation — it does not; implement and register `FileSystemClaim` in your runtime or test harness.
242
+
243
+ ---
244
+
245
+ ## Related skills
246
+
247
+ - `framework-core` — `Claim`, `SyncActivity`, `Binary`, `Claim.run`, `WorkflowTesting`.
248
+ - `framework-http` — HTTP activities that persist bodies via `FileSystemClaim`.
249
+ - `framework-jsx` — render activities that write/read via `fs`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t4h.framework/fs",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "File system module for the T4H Framework",
5
5
  "homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/fs",
6
6
  "bugs": "https://github.com/tech4humans-brasil/framework/issues",
@@ -20,7 +20,8 @@
20
20
  "types": "./dist/fs.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.2.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.2.0"
37
+ "@t4h.framework/core": "^0.6.0"
39
38
  },
40
39
  "packageManager": "yarn@4.12.0",
41
40
  "engines": {