@t4h.framework/fs 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,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`.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { Binary } from '@t4h.framework/core';
|
|
3
|
+
export declare abstract class ActivityFileSystemClaim {
|
|
4
|
+
abstract write(path: string, readable: Readable | Buffer): Binary | Promise<Binary>;
|
|
5
|
+
abstract read(path: string): Binary | Promise<Binary>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=ActivityFileSystemClaim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityFileSystemClaim.d.ts","sourceRoot":"","sources":["../src/ActivityFileSystemClaim.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEjD,8BAAsB,uBAAuB;aAC3B,KAAK,CACnB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAC1B,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;aAEX,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAC7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityFileSystemClaim.js","sourceRoot":"","sources":["../src/ActivityFileSystemClaim.ts"],"names":[],"mappings":"AAIA,MAAM,OAAgB,uBAAuB;CAO5C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/fs",
|
|
3
|
-
"version": "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.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.
|
|
37
|
+
"@t4h.framework/core": "^0.6.0"
|
|
39
38
|
},
|
|
40
39
|
"packageManager": "yarn@4.12.0",
|
|
41
40
|
"engines": {
|