@t4h.framework/fs 0.13.1 → 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 -306
- package/.ai/skills/framework-fs-write/SKILL.md +54 -212
- package/CHANGELOG.md +26 -0
- package/README.md +20 -351
- package/dist/activities/FileJoinActivity.d.ts +1 -2
- 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.map +1 -1
- package/dist/activities/FileReadActivity.js +18 -15
- package/dist/activities/FileReadActivity.js.map +1 -1
- package/dist/activities/FileSplitActivity.d.ts +21 -34
- 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.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 -24
- 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 +60 -41
- 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 +3 -3
|
@@ -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
|
-
```
|
|
36
|
-
|
|
37
|
-
---
|
|
49
|
+
import { read } from '@t4h.framework/fs'
|
|
38
50
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
import type { Binary } from '@t4h.framework/core'
|
|
44
|
-
|
|
45
|
-
export abstract class FileSystemClaim {
|
|
46
|
-
public abstract write(
|
|
47
|
-
path: string,
|
|
48
|
-
readable: Readable | Buffer,
|
|
49
|
-
contentType?: string,
|
|
50
|
-
): Binary | Promise<Binary>
|
|
51
|
-
|
|
52
|
-
public abstract read(path: string): Binary | Promise<Binary>
|
|
53
|
-
|
|
54
|
-
public abstract slice(
|
|
55
|
-
binary: Binary,
|
|
56
|
-
range: { start: number; end: number },
|
|
57
|
-
encoding?: BufferEncoding,
|
|
58
|
-
): Binary | Promise<Binary>
|
|
59
|
-
}
|
|
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
|
+
})
|
|
60
55
|
```
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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.
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
## Read a file shipped with the project
|
|
69
68
|
|
|
70
|
-
|
|
69
|
+
Declare the files in `framework.config.ts` with `build: { assets: ['assets/**'] }`,
|
|
70
|
+
then:
|
|
71
71
|
|
|
72
72
|
```typescript
|
|
73
|
-
|
|
74
|
-
public abstract readonly contentType: string
|
|
75
|
-
public abstract readonly contentLength: number
|
|
76
|
-
public abstract stream(): Readable | Promise<Readable>
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
The package activities return this `Binary` to your workflow — e.g. an `http`
|
|
81
|
-
response `Body` is backed by it. The workflow only passes the `Binary` to
|
|
82
|
-
another activity. An activity reads it in `run`:
|
|
73
|
+
import { asset } from '@t4h.framework/fs'
|
|
83
74
|
|
|
84
|
-
|
|
85
|
-
async run(file: Binary) {
|
|
86
|
-
const stream = await file.stream()
|
|
87
|
-
// consume stream (e.g. stream.toArray() then Buffer.concat)
|
|
88
|
-
}
|
|
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, the pdf render activity writes its 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
|
-
`FileSystemClaim` is abstract, so `tsc` rejects a subclass that does not implement `write`, `read` and `slice`. 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/pdf` | The render activity writes the PDF to `fs` and hands 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
|
-
`@t4h.framework/core/no-binary-subclass` rejects a `Binary` subclass out of a
|
|
148
|
-
test file. A test double for the claim can extend `Binary`:
|
|
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`. `tsc` checks the method signatures.
|
|
194
|
-
2. **Return** a `Binary` from `write`, `read` and `slice` with correct `contentType`, `contentLength`, and `stream()`.
|
|
195
|
-
3. **Infer** the content type from the path when `write` gets no `contentType`.
|
|
196
|
-
4. **Compose** ranges in `slice`: a slice of a slice is relative to it.
|
|
197
|
-
5. **Register** `{ provide: FileSystemClaim, value: new YourImpl() }` in the runtime (tests use `mockClaim(FileSystemClaim, ...)` instead).
|
|
198
|
-
6. **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.
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
## App / monorepo checklist
|
|
203
|
-
|
|
204
|
-
1. Add `"@t4h.framework/fs": "workspace:^"` (or published version) to app `dependencies` when activities use `FileSystemClaim`.
|
|
205
|
-
2. Ensure **`@t4h.framework/core`** is available (peer of `fs`).
|
|
206
|
-
3. Register **`FileSystemClaim`** in the runtime or test `claims` array.
|
|
207
|
-
4. Orchestrate file I/O through the package activities that own `FileSystemClaim` (http, pdf) — workflow code never declares an activity or accesses `this.claims.fs`.
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## Anti-patterns
|
|
212
|
-
|
|
213
|
-
- A deep import. The package exports only its entry, so a deep import fails in `tsc`.
|
|
214
|
-
- Calling `stream()` in a workflow, or in an activity `constructor`, `toInput` or `toOutput`. The `no-binary-stream-outside-activity` rule of `@t4h.framework/core/eslint` reports it.
|
|
215
|
-
- A missing `FileSystemClaim` provider. The activity throws `ClaimProviderNotFoundException` when it reads the claim.
|
|
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`.
|