@t4h.framework/fs 0.13.0 → 0.14.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-fs/SKILL.md +87 -186
- package/.ai/skills/framework-fs-split/SKILL.md +134 -314
- package/.ai/skills/framework-fs-write/SKILL.md +54 -214
- package/CHANGELOG.md +56 -0
- package/README.md +20 -351
- package/dist/activities/FileJoinActivity.d.ts +4 -5
- package/dist/activities/FileJoinActivity.d.ts.map +1 -1
- package/dist/activities/FileJoinActivity.js +38 -43
- package/dist/activities/FileJoinActivity.js.map +1 -1
- package/dist/activities/FileReadActivity.d.ts +1 -0
- package/dist/activities/FileReadActivity.d.ts.map +1 -1
- package/dist/activities/FileReadActivity.js +34 -26
- package/dist/activities/FileReadActivity.js.map +1 -1
- package/dist/activities/FileSplitActivity.d.ts +23 -43
- package/dist/activities/FileSplitActivity.d.ts.map +1 -1
- package/dist/activities/FileSplitActivity.js +23 -38
- package/dist/activities/FileSplitActivity.js.map +1 -1
- package/dist/constants/operations.d.ts +1 -1
- package/dist/constants/operations.d.ts.map +1 -1
- package/dist/constants/operations.js +3 -14
- package/dist/constants/operations.js.map +1 -1
- package/dist/fs.d.ts +5 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/helpers/CSV.d.ts +2 -2
- package/dist/helpers/CSV.d.ts.map +1 -1
- package/dist/helpers/CSV.js +20 -22
- package/dist/helpers/CSV.js.map +1 -1
- package/dist/helpers/group-by-key.d.ts +8 -0
- package/dist/helpers/group-by-key.d.ts.map +1 -0
- package/dist/helpers/group-by-key.js +35 -0
- package/dist/helpers/group-by-key.js.map +1 -0
- package/dist/helpers/group-records.d.ts +2 -2
- package/dist/helpers/group-records.d.ts.map +1 -1
- package/dist/helpers/group-records.js +3 -3
- package/dist/helpers/group-records.js.map +1 -1
- package/dist/helpers/in-chunks.d.ts +5 -0
- package/dist/helpers/in-chunks.d.ts.map +1 -0
- package/dist/helpers/in-chunks.js +17 -0
- package/dist/helpers/in-chunks.js.map +1 -0
- package/dist/helpers/split-records.d.ts +4 -3
- package/dist/helpers/split-records.d.ts.map +1 -1
- package/dist/helpers/split-records.js +75 -46
- package/dist/helpers/split-records.js.map +1 -1
- package/dist/helpers/where-matcher.d.ts +21 -0
- package/dist/helpers/where-matcher.d.ts.map +1 -0
- package/dist/helpers/where-matcher.js +11 -0
- package/dist/helpers/where-matcher.js.map +1 -0
- package/dist/models/BinaryRecordKey.d.ts +27 -0
- package/dist/models/BinaryRecordKey.d.ts.map +1 -0
- package/dist/models/BinaryRecordKey.js +52 -0
- package/dist/models/BinaryRecordKey.js.map +1 -0
- package/dist/models/BinarySource.d.ts +45 -0
- package/dist/models/BinarySource.d.ts.map +1 -0
- package/dist/models/BinarySource.js +58 -0
- package/dist/models/BinarySource.js.map +1 -0
- package/dist/models/BinarySplit.d.ts +45 -0
- package/dist/models/BinarySplit.d.ts.map +1 -0
- package/dist/models/BinarySplit.js +81 -0
- package/dist/models/BinarySplit.js.map +1 -0
- package/dist/models/BinarySplitInput.d.ts +21 -0
- package/dist/models/BinarySplitInput.d.ts.map +1 -0
- package/dist/models/BinarySplitInput.js +85 -0
- package/dist/models/BinarySplitInput.js.map +1 -0
- package/dist/models/Database.d.ts +15 -0
- package/dist/models/Database.d.ts.map +1 -0
- package/dist/models/Database.js +57 -0
- package/dist/models/Database.js.map +1 -0
- package/dist/models/OpenKeyGroup.d.ts +23 -0
- package/dist/models/OpenKeyGroup.d.ts.map +1 -0
- package/dist/models/OpenKeyGroup.js +42 -0
- package/dist/models/OpenKeyGroup.js.map +1 -0
- package/dist/models/SplitDatabaseWorker.d.ts +12 -0
- package/dist/models/SplitDatabaseWorker.d.ts.map +1 -0
- package/dist/models/SplitDatabaseWorker.js +65 -0
- package/dist/models/SplitDatabaseWorker.js.map +1 -0
- package/dist/workers/split-database.d.ts +10 -0
- package/dist/workers/split-database.d.ts.map +1 -0
- package/dist/workers/split-database.js +43 -0
- package/dist/workers/split-database.js.map +1 -0
- package/package.json +5 -4
|
@@ -1,226 +1,127 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: framework-fs
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Binary
|
|
10
|
-
|
|
4
|
+
Explains the shared rules of @t4h.framework/fs in a T4H Framework app: the
|
|
5
|
+
Binary reference that file primitives pass around, the FileSystemClaim that
|
|
6
|
+
the runtime provides, read() for a small byte window (header, trailer, one
|
|
7
|
+
record), asset() for files shipped with the project, and how to mock
|
|
8
|
+
FileSystemClaim and AssetClaim in a workflow test. Use when a workflow gets,
|
|
9
|
+
passes or reads a Binary (an HTTP body, sftp.get, a PDF), reads a file header
|
|
10
|
+
or trailer, loads a font or template from the project, hits
|
|
11
|
+
ClaimProviderNotFoundException for FileSystemClaim, or wants to call
|
|
12
|
+
readFileSync, fs/promises or binary.stream() in a workflow. For write() use
|
|
13
|
+
framework-fs-write; for split() and join() use framework-fs-split.
|
|
11
14
|
---
|
|
12
15
|
|
|
13
16
|
# @t4h.framework/fs
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
## The Binary
|
|
19
|
+
|
|
20
|
+
A `Binary` (from `@t4h.framework/core`) is a reference to a stored file:
|
|
21
|
+
`contentType`, `contentLength` and `stream()`. It is not the bytes. A workflow
|
|
22
|
+
gets a `Binary` from a framework primitive (an HTTP response body, `sftp.get`,
|
|
23
|
+
`write`, `join`, `asset`, a PDF render) and passes it to the next primitive.
|
|
24
|
+
|
|
25
|
+
Traps:
|
|
26
|
+
|
|
27
|
+
- Never write a class that extends `Binary` in app code. The
|
|
28
|
+
`@t4h.framework/core/no-binary-subclass` rule rejects it outside test files.
|
|
29
|
+
A primitive persists its input, and a `Binary` that holds bytes in memory
|
|
30
|
+
cannot be persisted. It can pass in one environment and fail in another. To
|
|
31
|
+
turn a string or a `Buffer` into a `Binary`, use `write` (skill
|
|
32
|
+
`framework-fs-write`).
|
|
33
|
+
- Never call `binary.stream()` in a workflow. The
|
|
34
|
+
`no-binary-stream-outside-activity` rule rejects it. Use `read` for a small
|
|
35
|
+
window and `split` for the records.
|
|
36
|
+
- Never use `node:fs`, `fs/promises` or `readFileSync` in a workflow. The
|
|
37
|
+
`no-io-in-workflow` rule rejects it. For a file that ships with the project,
|
|
38
|
+
use `asset`.
|
|
39
|
+
- Never construct `FileReadActivity`, `FileWriteActivity`,
|
|
40
|
+
`FileSplitActivity` or `FileJoinActivity`. The
|
|
41
|
+
`no-framework-activity-in-app` rule rejects it. Call the function.
|
|
42
|
+
- A workflow never calls `FileSystemClaim`. The package ships no
|
|
43
|
+
implementation: the runtime provides it. A missing provider gives
|
|
44
|
+
`ClaimProviderNotFoundException`.
|
|
45
|
+
|
|
46
|
+
## Read a header or a trailer
|
|
24
47
|
|
|
25
48
|
```typescript
|
|
26
|
-
import {
|
|
27
|
-
FileSystemClaim,
|
|
28
|
-
// file fan-out / fold / windows — see the framework-fs-split skill
|
|
29
|
-
split, Split, FileSplitActivity, type FileSplitOptions,
|
|
30
|
-
join, FileJoinActivity, type FileJoinOptions, type FileJoinWhere,
|
|
31
|
-
read, FileReadActivity, type FileReadOptions,
|
|
32
|
-
// turn workflow content into a Binary — see the framework-fs-write skill
|
|
33
|
-
write, FileWriteActivity, type FileWriteOptions,
|
|
34
|
-
} from '@t4h.framework/fs'
|
|
35
|
-
```
|
|
49
|
+
import { read } from '@t4h.framework/fs'
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
## FileSystemClaim contract
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
import type { Readable } from 'node:stream'
|
|
45
|
-
import type { Binary } from '@t4h.framework/core'
|
|
46
|
-
|
|
47
|
-
export abstract class FileSystemClaim {
|
|
48
|
-
public abstract write(
|
|
49
|
-
path: string,
|
|
50
|
-
readable: Readable | Buffer,
|
|
51
|
-
contentType?: string,
|
|
52
|
-
): Binary | Promise<Binary>
|
|
53
|
-
|
|
54
|
-
public abstract read(path: string): Binary | Promise<Binary>
|
|
55
|
-
|
|
56
|
-
public abstract slice(
|
|
57
|
-
binary: Binary,
|
|
58
|
-
range: { start: number; end: number },
|
|
59
|
-
encoding?: BufferEncoding,
|
|
60
|
-
): Binary | Promise<Binary>
|
|
61
|
-
}
|
|
51
|
+
const header = await read(file, { range: { start: 0, end: 240 } })
|
|
52
|
+
const trailer = await read(file, {
|
|
53
|
+
range: { start: file.contentLength - 240, end: file.contentLength },
|
|
54
|
+
})
|
|
62
55
|
```
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
- `range` is in bytes, `end` is exclusive, and `end` must not be more than
|
|
58
|
+
`contentLength`. With no `range`, `read` reads the whole file.
|
|
59
|
+
- The string goes into the workflow history, so `read` throws a `RangeError`
|
|
60
|
+
past 1 MiB. Never read a data file with `read`; use `split`.
|
|
61
|
+
- Do not call `read` in a loop. The `no-batch-query-in-loop` rule rejects it:
|
|
62
|
+
each call is one activity and one history entry. Read the records with
|
|
63
|
+
`split`.
|
|
64
|
+
- `encoding` defaults to `'utf8'`. Set it (for example `'latin1'`) when the
|
|
65
|
+
file is not UTF-8.
|
|
69
66
|
|
|
70
|
-
|
|
67
|
+
## Read a file shipped with the project
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
Declare the files in `framework.config.ts` with `build: { assets: ['assets/**'] }`,
|
|
70
|
+
then:
|
|
73
71
|
|
|
74
72
|
```typescript
|
|
75
|
-
|
|
76
|
-
public abstract readonly contentType: string
|
|
77
|
-
public abstract readonly contentLength: number
|
|
78
|
-
public abstract stream(): Readable | Promise<Readable>
|
|
79
|
-
}
|
|
80
|
-
```
|
|
73
|
+
import { asset } from '@t4h.framework/fs'
|
|
81
74
|
|
|
82
|
-
|
|
83
|
-
response `Body` is backed by it. Consume it with `stream()` (or the higher-level
|
|
84
|
-
`body.json()` / `text()` that packages expose):
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
const stream = await binary.stream()
|
|
88
|
-
// consume stream (e.g. stream.toArray() then Buffer.concat)
|
|
75
|
+
const font = await asset('assets/DejaVuSans.ttf')
|
|
89
76
|
```
|
|
90
77
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
## How workflows use it
|
|
94
|
-
|
|
95
|
-
Workflows never call `FileSystemClaim` directly. File I/O happens inside the
|
|
96
|
-
**ready-made package activities** that already own the claim — the `http` client
|
|
97
|
-
persists response bodies, jsx render activities write their output — and you
|
|
98
|
-
read the resulting `Binary` (e.g. `await res.body.json()` / `text()`). Your only
|
|
99
|
-
responsibility is that the runtime **registers a provider** (below). See
|
|
100
|
-
**Known consumers** for which package primitives to orchestrate.
|
|
101
|
-
|
|
102
|
-
This package's own workflow-facing primitives are `split`, `join`, `read`
|
|
103
|
-
(small byte windows — headers, trailers — hydrated into the workflow as a
|
|
104
|
-
string, capped at 1 MiB) and `write` (content produced in the workflow turned
|
|
105
|
-
into a file). See the `framework-fs-split` and `framework-fs-write` skills.
|
|
106
|
-
|
|
107
|
-
**A workflow never implements `Binary`.** Every `Binary` comes from the
|
|
108
|
-
framework — an HTTP response body, `sftp.get`, `write`, `join`. A class holding
|
|
109
|
-
bytes in memory cannot be persisted, and every primitive it is handed to
|
|
110
|
-
persists its input. See the `framework-fs-write` skill.
|
|
78
|
+
The path is relative to the package root. `asset` is not an activity and
|
|
79
|
+
returns a `Binary`. It throws when the path is not in `build.assets`.
|
|
111
80
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
## Runtime provider
|
|
81
|
+
## Test
|
|
115
82
|
|
|
116
|
-
|
|
83
|
+
Use `TestWorkflowEnvironment` and `mockClaim` from
|
|
84
|
+
`@t4h.framework/core/testing` (skill `framework-workflow-testing`). A test file
|
|
85
|
+
can extend `Binary` for a test double:
|
|
117
86
|
|
|
118
87
|
```typescript
|
|
119
|
-
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
`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.
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Known consumers in this monorepo
|
|
127
|
-
|
|
128
|
-
These packages depend on `@t4h.framework/fs` and declare `FileSystemClaim`; read their skills/README for full workflow patterns.
|
|
129
|
-
|
|
130
|
-
| Package | Usage |
|
|
131
|
-
|---------|--------|
|
|
132
|
-
| `@t4h.framework/http` | The HTTP activity persists response bodies to `fs` and returns them wrapped in a `Body` — you read them with `res.body.json()` / `text()`. |
|
|
133
|
-
| `@t4h.framework/jsx` | Render activities write their output to `fs` and hand back a `Binary` you consume in the workflow. |
|
|
134
|
-
| `@t4h.framework/fs` itself | `FileSplitActivity` streams the source and hands its slices to `startMany` as strings; `FileJoinActivity` streams batch results into a new file; `FileReadActivity` slices (or streams, when the binary is not file-backed) a small window into a string. See the `framework-fs-split` skill. |
|
|
135
|
-
|
|
136
|
-
Orchestrate these package primitives from your workflow; the `fs` write/read happens inside them.
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Testing
|
|
141
|
-
|
|
142
|
-
Test the **workflow** with `TestWorkflowEnvironment` from
|
|
143
|
-
`@t4h.framework/core/testing`, mocking `FileSystemClaim` (plus whatever other
|
|
144
|
-
claims the triggered activities need, e.g. `HttpClientRequestClaim` for an HTTP
|
|
145
|
-
call). See the **framework-workflow-testing** skill for the harness API.
|
|
146
|
-
|
|
147
|
-
Subclassing `Binary` is legitimate **only here**, in a test double standing in
|
|
148
|
-
for the claim — never in workflow or App code.
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
import { Readable } from 'node:stream'
|
|
152
|
-
import { Binary } from '@t4h.framework/core'
|
|
153
|
-
import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
|
|
154
|
-
import { FileSystemClaim } from '@t4h.framework/fs'
|
|
155
|
-
|
|
156
|
-
class MockBinary extends Binary {
|
|
157
|
-
public readonly contentType: string
|
|
88
|
+
class TestBinary extends Binary {
|
|
158
89
|
public readonly contentLength: number
|
|
159
90
|
|
|
160
|
-
constructor(
|
|
91
|
+
constructor(
|
|
92
|
+
private readonly data: Buffer,
|
|
93
|
+
public readonly contentType = 'application/octet-stream',
|
|
94
|
+
) {
|
|
161
95
|
super()
|
|
162
|
-
this.contentType = contentType
|
|
163
96
|
this.contentLength = data.byteLength
|
|
164
97
|
}
|
|
165
98
|
|
|
166
99
|
public stream() {
|
|
167
|
-
return Readable.from(this.data)
|
|
100
|
+
return Readable.from([this.data])
|
|
168
101
|
}
|
|
169
102
|
}
|
|
170
103
|
|
|
171
|
-
|
|
172
|
-
const mockWrite = vi.fn().mockImplementation(async (_path: string, readable: Readable) => {
|
|
173
|
-
const chunks = await readable.toArray()
|
|
174
|
-
return new MockBinary(Buffer.concat(chunks), 'application/json')
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
const env = TestWorkflowEnvironment.create({ now: 0 })
|
|
178
|
-
|
|
179
|
-
await env.execute(workflow, input, {
|
|
104
|
+
await TestWorkflowEnvironment.create().execute(workflow, input, {
|
|
180
105
|
claims: [
|
|
181
|
-
mockClaim(FileSystemClaim, {
|
|
182
|
-
|
|
106
|
+
mockClaim(FileSystemClaim, {
|
|
107
|
+
write: vi.fn(async (_path, content) => new TestBinary(content)),
|
|
108
|
+
}),
|
|
109
|
+
mockClaim(AssetClaim, {
|
|
110
|
+
resolve: async () => new TestBinary(fontBytes, 'font/ttf'),
|
|
111
|
+
}),
|
|
183
112
|
],
|
|
184
113
|
})
|
|
185
114
|
```
|
|
186
115
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
## Implementing a custom FileSystemClaim
|
|
192
|
-
|
|
193
|
-
1. **Extend** `FileSystemClaim` from `@t4h.framework/fs`.
|
|
194
|
-
2. **Return** a `Binary` subclass (or core-compatible `Binary`) from `write` and `read` with correct `contentType`, `contentLength`, and `stream()`.
|
|
195
|
-
3. **Accept** `Readable | Buffer` and the optional `contentType` in `write`
|
|
196
|
-
(match the abstract signature), falling back to inferring the content type
|
|
197
|
-
from the path when it is omitted.
|
|
198
|
-
4. **Register** `{ provide: FileSystemClaim, value: new YourImpl() }` in the runtime (tests use `mockClaim(FileSystemClaim, ...)` instead).
|
|
199
|
-
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.
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## App / monorepo checklist
|
|
204
|
-
|
|
205
|
-
1. Add `"@t4h.framework/fs": "workspace:^"` (or published version) to app `dependencies` when activities use `FileSystemClaim`.
|
|
206
|
-
2. Ensure **`@t4h.framework/core`** is available (peer of `fs`).
|
|
207
|
-
3. Register **`FileSystemClaim`** in the runtime or test `claims` array.
|
|
208
|
-
4. Orchestrate file I/O through the package activities that own `FileSystemClaim` (http, jsx) — workflow code never declares an activity or accesses `this.claims.fs`.
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
## Anti-patterns
|
|
213
|
-
|
|
214
|
-
- Importing from `@t4h.framework/fs` paths other than the package export (`FileSystemClaim` only).
|
|
215
|
-
- Omitting `FileSystemClaim` from providers while using `@t4h.framework/http` or `@t4h.framework/jsx` activities that require `fs`.
|
|
216
|
-
- Assuming `packages/fs` ships a concrete disk/S3 implementation — it does not; implement and register `FileSystemClaim` in your runtime or test harness.
|
|
217
|
-
|
|
218
|
-
---
|
|
116
|
+
The environment runs the workflow again to check the replay, so return a new
|
|
117
|
+
`Binary` on each call. In a `read` test, a `TestBinary` passes: when the claim
|
|
118
|
+
cannot slice, `read` streams the window.
|
|
219
119
|
|
|
220
|
-
##
|
|
120
|
+
## Reference
|
|
221
121
|
|
|
222
|
-
- `
|
|
223
|
-
- `framework
|
|
224
|
-
- `
|
|
225
|
-
- `framework
|
|
226
|
-
- `framework-
|
|
122
|
+
- Overview, `Binary` and `FileSystemClaim`: https://tech4humans-brasil.github.io/framework/fs/
|
|
123
|
+
- `read`: https://tech4humans-brasil.github.io/framework/fs/read
|
|
124
|
+
- `asset`: https://tech4humans-brasil.github.io/framework/fs/asset
|
|
125
|
+
- Types: `node_modules/@t4h.framework/fs/dist/fs.d.ts`
|
|
126
|
+
- Related skills: `framework-fs-write`, `framework-fs-split`,
|
|
127
|
+
`framework-workflow-testing`.
|