@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.
Files changed (80) hide show
  1. package/.ai/skills/framework-fs/SKILL.md +87 -186
  2. package/.ai/skills/framework-fs-split/SKILL.md +134 -314
  3. package/.ai/skills/framework-fs-write/SKILL.md +54 -214
  4. package/CHANGELOG.md +56 -0
  5. package/README.md +20 -351
  6. package/dist/activities/FileJoinActivity.d.ts +4 -5
  7. package/dist/activities/FileJoinActivity.d.ts.map +1 -1
  8. package/dist/activities/FileJoinActivity.js +38 -43
  9. package/dist/activities/FileJoinActivity.js.map +1 -1
  10. package/dist/activities/FileReadActivity.d.ts +1 -0
  11. package/dist/activities/FileReadActivity.d.ts.map +1 -1
  12. package/dist/activities/FileReadActivity.js +34 -26
  13. package/dist/activities/FileReadActivity.js.map +1 -1
  14. package/dist/activities/FileSplitActivity.d.ts +23 -43
  15. package/dist/activities/FileSplitActivity.d.ts.map +1 -1
  16. package/dist/activities/FileSplitActivity.js +23 -38
  17. package/dist/activities/FileSplitActivity.js.map +1 -1
  18. package/dist/constants/operations.d.ts +1 -1
  19. package/dist/constants/operations.d.ts.map +1 -1
  20. package/dist/constants/operations.js +3 -14
  21. package/dist/constants/operations.js.map +1 -1
  22. package/dist/fs.d.ts +5 -1
  23. package/dist/fs.d.ts.map +1 -1
  24. package/dist/helpers/CSV.d.ts +2 -2
  25. package/dist/helpers/CSV.d.ts.map +1 -1
  26. package/dist/helpers/CSV.js +20 -22
  27. package/dist/helpers/CSV.js.map +1 -1
  28. package/dist/helpers/group-by-key.d.ts +8 -0
  29. package/dist/helpers/group-by-key.d.ts.map +1 -0
  30. package/dist/helpers/group-by-key.js +35 -0
  31. package/dist/helpers/group-by-key.js.map +1 -0
  32. package/dist/helpers/group-records.d.ts +2 -2
  33. package/dist/helpers/group-records.d.ts.map +1 -1
  34. package/dist/helpers/group-records.js +3 -3
  35. package/dist/helpers/group-records.js.map +1 -1
  36. package/dist/helpers/in-chunks.d.ts +5 -0
  37. package/dist/helpers/in-chunks.d.ts.map +1 -0
  38. package/dist/helpers/in-chunks.js +17 -0
  39. package/dist/helpers/in-chunks.js.map +1 -0
  40. package/dist/helpers/split-records.d.ts +4 -3
  41. package/dist/helpers/split-records.d.ts.map +1 -1
  42. package/dist/helpers/split-records.js +75 -46
  43. package/dist/helpers/split-records.js.map +1 -1
  44. package/dist/helpers/where-matcher.d.ts +21 -0
  45. package/dist/helpers/where-matcher.d.ts.map +1 -0
  46. package/dist/helpers/where-matcher.js +11 -0
  47. package/dist/helpers/where-matcher.js.map +1 -0
  48. package/dist/models/BinaryRecordKey.d.ts +27 -0
  49. package/dist/models/BinaryRecordKey.d.ts.map +1 -0
  50. package/dist/models/BinaryRecordKey.js +52 -0
  51. package/dist/models/BinaryRecordKey.js.map +1 -0
  52. package/dist/models/BinarySource.d.ts +45 -0
  53. package/dist/models/BinarySource.d.ts.map +1 -0
  54. package/dist/models/BinarySource.js +58 -0
  55. package/dist/models/BinarySource.js.map +1 -0
  56. package/dist/models/BinarySplit.d.ts +45 -0
  57. package/dist/models/BinarySplit.d.ts.map +1 -0
  58. package/dist/models/BinarySplit.js +81 -0
  59. package/dist/models/BinarySplit.js.map +1 -0
  60. package/dist/models/BinarySplitInput.d.ts +21 -0
  61. package/dist/models/BinarySplitInput.d.ts.map +1 -0
  62. package/dist/models/BinarySplitInput.js +85 -0
  63. package/dist/models/BinarySplitInput.js.map +1 -0
  64. package/dist/models/Database.d.ts +15 -0
  65. package/dist/models/Database.d.ts.map +1 -0
  66. package/dist/models/Database.js +57 -0
  67. package/dist/models/Database.js.map +1 -0
  68. package/dist/models/OpenKeyGroup.d.ts +23 -0
  69. package/dist/models/OpenKeyGroup.d.ts.map +1 -0
  70. package/dist/models/OpenKeyGroup.js +42 -0
  71. package/dist/models/OpenKeyGroup.js.map +1 -0
  72. package/dist/models/SplitDatabaseWorker.d.ts +12 -0
  73. package/dist/models/SplitDatabaseWorker.d.ts.map +1 -0
  74. package/dist/models/SplitDatabaseWorker.js +65 -0
  75. package/dist/models/SplitDatabaseWorker.js.map +1 -0
  76. package/dist/workers/split-database.d.ts +10 -0
  77. package/dist/workers/split-database.d.ts.map +1 -0
  78. package/dist/workers/split-database.js +43 -0
  79. package/dist/workers/split-database.js.map +1 -0
  80. package/package.json +5 -4
@@ -1,242 +1,82 @@
1
1
  ---
2
2
  name: framework-fs-write
3
3
  description: >-
4
- Guides the use of `write` from @t4h.framework/fs — the only supported way to
5
- turn content produced inside a workflow (a JSON string, a CSV, a positional
6
- return file, a Buffer) into a `Binary` that other primitives accept. Use
7
- whenever a workflow must upload, send or save content it built itself:
8
- `sftp.put`/`sftp.append` with generated content, an HTTP request carrying a
9
- file, persisting a report, or when the user mentions creating/building a
10
- Binary, converting a string or Buffer to a Binary, "stringToBinary",
11
- "BufferBinary", or hits `Unsupported binary instance`. A workflow must NEVER
12
- implement its own `Binary` subclass — this skill explains why and what to do
13
- instead.
4
+ Shows how to turn content that a T4H Framework workflow built (a JSON
5
+ string, a CSV, a positional return file, a Buffer) into a Binary with
6
+ write() from @t4h.framework/fs, and how to test it. Use when a workflow must
7
+ upload, send or save content it made itself (sftp.put or sftp.append with
8
+ generated content, an HTTP request with a file body, a report), or when
9
+ someone asks to create a Binary, convert a string or a Buffer to a Binary,
10
+ writes a stringToBinary or BufferBinary helper, or extends Binary. A
11
+ workflow never implements its own Binary.
14
12
  ---
15
13
 
16
- # `write` — turn workflow content into a `Binary`
14
+ # write — workflow content into a Binary
17
15
 
18
- Every file primitive in the framework speaks `Binary`: `sftp.put`,
19
- `sftp.append`, `split`, `join`, `pdf`, HTTP bodies. A `Binary` is a
20
- **reference** to stored content, not the bytes themselves.
16
+ Read the skill `framework-fs` first for the `Binary` rules.
21
17
 
22
- `write` is what produces one from content your workflow built:
18
+ ## Write and upload
23
19
 
24
20
  ```typescript
25
21
  import { write } from '@t4h.framework/fs'
26
22
 
27
23
  const file = await write(JSON.stringify(state, null, 2), {
28
24
  contentType: 'application/json',
25
+ filename: 'state.json',
29
26
  })
30
27
 
31
28
  await sftp.put('/out/state.json', file)
32
29
  ```
33
30
 
34
- Package path: `framework/packages/fs`. Import only from the package root.
35
-
36
- ---
37
-
38
- ## Never implement `Binary` yourself
39
-
40
- This is the rule this skill exists to enforce:
31
+ 1. Build the content as a `string` or a `Buffer` in the workflow.
32
+ 2. Call `write(content, { contentType })`. Set `contentType` when the
33
+ receiver cares. Without it, the claim infers one from `filename`, which
34
+ defaults to the activity id.
35
+ 3. Pass the `Binary` to the next primitive. One `Binary` can go to many
36
+ primitives (for example `sftp.put` to a `.tmp` path and then to the final
37
+ path); nothing is copied.
38
+
39
+ ## Traps
40
+
41
+ - Do not write a `Binary` subclass or a `stringToBinary` helper that wraps
42
+ bytes. Replace its body with `write`; keep the helper name if it reads well.
43
+ - The option is `filename`, not `path` or `name`. It is the storage name, not
44
+ the upload path.
45
+ - `encoding` applies only to a string `content` (default `'utf8'`). A
46
+ `Buffer` is written as it is.
47
+ - `write` takes only `string | Buffer`. A value that already is a `Binary`
48
+ goes straight to the next primitive.
49
+ - `write` is for content that fits in workflow memory: a state file, a
50
+ report, a return file. To build a large file from many records, use
51
+ `split` and `join` (skill `framework-fs-split`); never concatenate records in
52
+ the workflow.
53
+ - `write` is an activity. On replay it returns the recorded `Binary` and does
54
+ not write again.
55
+
56
+ ## Test
57
+
58
+ Mock `FileSystemClaim.write`. `write` passes the content to the claim as a
59
+ `Buffer`. Use the `TestBinary` double from the skill `framework-fs`:
41
60
 
42
61
  ```typescript
43
- // ❌ NEVER — this class cannot be persisted
44
- class BufferBinary extends Binary {
45
- constructor(
46
- private readonly content: Buffer,
47
- readonly contentType = '...',
48
- ) {
49
- super()
50
- }
51
-
52
- get contentLength() {
53
- return this.content.length
54
- }
55
-
56
- stream() {
57
- return Readable.from(this.content)
58
- }
59
- }
60
-
61
- await sftp.put('/out/state.json', new BufferBinary(Buffer.from(json)))
62
- ```
63
-
64
- ```typescript
65
- // ✅ ALWAYS
66
- await sftp.put(
67
- '/out/state.json',
68
- await write(json, {
69
- contentType: 'application/json',
70
- }),
62
+ const writeFile = vi.fn(
63
+ async (_path: string, content: Buffer, contentType?: string) =>
64
+ new TestBinary(content, contentType),
71
65
  )
72
- ```
73
-
74
- ### Why a custom `Binary` cannot work
75
-
76
- A workflow is **durable**: the body replays, and each activity is persisted
77
- before it runs. When you pass a value to a primitive, that value becomes the
78
- activity **input**, and the input is serialized to storage.
79
-
80
- A `Binary` holding bytes in memory has nothing to serialize into — the buffer
81
- lives in the process that built it, and the activity may run in another
82
- process, minutes later, after a replay. The runtime therefore has to spool the
83
- content somewhere before the activity can be persisted at all.
84
-
85
- Symptoms of doing it by hand:
86
-
87
- - `TypeError: Unsupported binary instance` when the activity input is
88
- serialized, on older runtimes;
89
- - the activity is never persisted, the transaction rolls back, and the message
90
- ends up in the dead letter queue after the retries — the run stops mid-way
91
- with no obvious error in the workflow itself;
92
- - it may still "work" locally, because the dev server keeps values in memory
93
- and never crosses a process boundary. **Passing locally proves nothing here.**
94
-
95
- Current runtimes spool an unknown `Binary` to storage instead of failing, so a
96
- hand-rolled class may survive — but it pays a hidden copy, loses the content
97
- type you meant to set, and depends on runtime-version behavior. `write` is the
98
- contract.
99
-
100
- ---
101
-
102
- ## API
103
-
104
- ```typescript
105
- function write(
106
- content: string | Buffer,
107
- options?: FileWriteOptions,
108
- ): Promise<Binary>
109
- ```
110
-
111
- | Option | Type | Required | Purpose |
112
- | ------------- | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
113
- | `contentType` | `string` | No | Content type stored with the file — `'application/json'`, `'text/csv'`, … Defaults to what the filename extension implies, then `application/octet-stream`. |
114
- | `filename` | `string` | No | Name the file is stored under. Defaults to the activity id. |
115
- | `encoding` | `BufferEncoding` | No | How a **string** `content` is encoded. Defaults to `'utf8'`. Ignored for a `Buffer`, which always travels as base64. |
116
-
117
- `write` is a durable activity: on replay the content is **not** written again;
118
- the recorded `Binary` is returned.
119
-
120
- ---
121
-
122
- ## When to use it
123
-
124
- | Situation | Use |
125
- | ----------------------------------------------------------------------------- | ----------------------------------------------- |
126
- | Send content the workflow built (JSON, CSV, positional file) over SFTP/HTTP | `write` |
127
- | Read a small window of an existing file into the workflow | [`read`](./read) — see **framework-fs-split** |
128
- | Fan a large file out into one workflow per record | `split` — see **framework-fs-split** |
129
- | Fold a finished batch back into a file | `join` — see **framework-fs-split** |
130
- | Content already is a `Binary` (HTTP response body, `sftp.get`, `join` result) | pass it straight through — do not re-`write` it |
131
-
132
- ---
133
-
134
- ## Size matters: keep content bounded
135
-
136
- `write` takes content **in workflow memory**, so it is bounded by what the
137
- workflow can reasonably hold — a state file, a report, a return file. It is not
138
- the tool for gigabyte outputs.
139
-
140
- To build a large file out of many records, use `join` over a batch: it streams
141
- results one at a time into storage, and nothing but the resulting `Binary`
142
- reaches the workflow.
143
66
 
144
- ---
145
-
146
- ## Patterns
147
-
148
- ### A) Write then upload
149
-
150
- ```typescript
151
- const csv = await write(rows.map(toCsvLine).join('\n'), {
152
- contentType: 'text/csv',
153
- filename: `report-${input.date}.csv`,
67
+ await TestWorkflowEnvironment.create().execute(workflow, input, {
68
+ claims: [mockClaim(FileSystemClaim, { write: writeFile })],
154
69
  })
155
70
 
156
- await sftp.put(`/out/report-${input.date}.csv`, csv)
157
- ```
158
-
159
- ### B) Write once, use twice
160
-
161
- The same `Binary` can be handed to more than one primitive — it is a reference,
162
- so nothing is copied and nothing is re-uploaded:
163
-
164
- ```typescript
165
- const file = await write(json, { contentType: 'application/json' })
166
-
167
- await sftp.put(`${path}.tmp`, file)
168
- await sftp.put(path, file)
169
- ```
170
-
171
- Note that a hand-rolled `Binary` backed by a single-use stream would break on
172
- the second call — another reason not to build one.
173
-
174
- ### C) Binary content
175
-
176
- ```typescript
177
- const pdfBytes: Buffer = await buildPdf()
178
-
179
- await sftp.put(
180
- '/out/invoice.pdf',
181
- await write(pdfBytes, {
182
- contentType: 'application/pdf',
183
- }),
71
+ expect(writeFile).toHaveBeenCalledWith(
72
+ 'report.csv',
73
+ expect.any(Buffer),
74
+ 'text/csv',
184
75
  )
185
76
  ```
186
77
 
187
- ---
188
-
189
- ## Testing
190
-
191
- `write` goes through `FileSystemClaim.write`, so mock that claim like any
192
- other. See the **framework-workflow-testing** skill for the harness API.
193
-
194
- ```typescript
195
- import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
196
- import { FileSystemClaim } from '@t4h.framework/fs'
197
-
198
- // replay runs the workflow twice — return a fresh Binary per call
199
- const write = vi
200
- .fn()
201
- .mockImplementation(
202
- async (path, content, contentType) =>
203
- new MockBinary(
204
- Buffer.isBuffer(content) ? content : await toBuffer(content),
205
- contentType,
206
- ),
207
- )
208
-
209
- await env.execute(workflow, input, {
210
- claims: [
211
- mockClaim(FileSystemClaim, { write, read: vi.fn(), slice: vi.fn() }),
212
- ],
213
- })
214
-
215
- expect(write).toHaveBeenCalledWith('report.csv', expect.any(Buffer), 'text/csv')
216
- ```
217
-
218
- ---
219
-
220
- ## Anti-patterns
221
-
222
- - **Declaring a class that extends `Binary`** in an App. There is no valid
223
- reason to; use `write`.
224
- - Helper functions like `stringToBinary(value)` that wrap content in a custom
225
- class — replace the implementation with `write`, keeping the helper name if
226
- it reads well.
227
- - `write`-ing a value that already is a `Binary` (an HTTP response body, a
228
- `sftp.get` result, a `join` output) — that copies a file to obtain a
229
- reference you already had.
230
- - Building a large file by concatenating records in memory and calling `write`
231
- once — use `split`/`join` instead.
232
- - Relying on the filename extension to set the content type when the consumer
233
- cares about it — pass `contentType` explicitly.
234
-
235
- ---
236
-
237
- ## Related skills
78
+ ## Reference
238
79
 
239
- - `framework-fs` — the `FileSystemClaim` contract and runtime providers.
240
- - `framework-fs-split` — `read`/`split`/`join`: windows, fan-out and fold.
241
- - `framework-sftp` — `sftp.put`/`append`, the most common consumer of `write`.
242
- - `framework-workflow-creation` — the rules a workflow body must follow.
80
+ - `write`: https://tech4humans-brasil.github.io/framework/fs/write
81
+ - Types: `node_modules/@t4h.framework/fs/dist/activities/FileWriteActivity.d.ts`
82
+ - Related skills: `framework-fs`, `framework-fs-split`, `framework-sftp`.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # @t4h.framework/fs
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#260](https://github.com/tech4humans-brasil/framework/pull/260) [`e9e8036`](https://github.com/tech4humans-brasil/framework/commit/e9e80366440b8da611489267dd02680592ca32e9) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - `split` gets two options. `groupBy` starts one child per key, with all the records of the key. `with` gives each child the rows of other files that have the same key. The key is a `match` `RegExp` with optional `replace` steps. The `input` template gets two more markers: `Split.key` and `Split.rows(name)`. `split` reads each file one time into a temporary SQLite file on the local disk, keeps only the rows of `with` whose key is in the main file, and removes the file at the end. The files can have 1 GiB in total. The package now needs Node 22.13 or later, the first version with `node:sqlite` and `iterate()` without a flag.
8
+
9
+ The types of `split` now have the `Binary` prefix: `BinarySplitOptions`, `BinarySourceOptions`, `BinaryRecordKeyOptions`, `BinaryReplace`, `BinaryDelimiter` and `BinaryWhere`, with a `JSON` form for the stored options. `FileSplitOptions` and `FileSplitActivityOptions` extend them and keep their shape. `FileSplitDelimiter` and `FileSplitWhere` are deprecated: use `BinaryDelimiter` and `BinaryWhere`.
10
+
11
+ - [#262](https://github.com/tech4humans-brasil/framework/pull/262) [`e46ebbb`](https://github.com/tech4humans-brasil/framework/commit/e46ebbbe1e711febd21ddcb920e890125fbb9f0a) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - `split` gets the `input` option: a template that each child gets, with its slice in place of the `Split.slice` marker. The other values of the template stay as they are. `tsc` checks the template against the schema of the child workflow, so `split` and `FileSplitOptions` take the child workflow as a type parameter. `FileSplitActivityInput` stores the template in `input`, and its `options` use the new `FileSplitActivityOptions` type, which has no `input`.
12
+
13
+ ### Patch Changes
14
+
15
+ - [#248](https://github.com/tech4humans-brasil/framework/pull/248) [`ae6d224`](https://github.com/tech4humans-brasil/framework/commit/ae6d22475ee07307ac59582581a6077788b902ca) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Adds the `no-batch-query-in-loop` rule to `@t4h.framework/core/eslint` and turns it on in `configs.recommended`, at the `error` level, for TypeScript files that are not tests. After the upgrade, a project that uses the recommended config can get new lint errors. The rule reports these calls in a loop or in the callback of an array method:
16
+ - `join`, `split` and `index` from `@t4h.framework/fs`, and `size()`, `take()` and `toArray()` on a `WorkflowBatchCollectionRef` (also after `.filter(...)`). Each call queries or scans the whole batch or file, so a loop does N scans.
17
+ - `read` from `@t4h.framework/fs`. Each call is a separate activity and a separate file read, and each result goes into the workflow history.
18
+
19
+ The rule checks all the functions of the file. In an activity class, it checks only the `constructor`, `toInput` and `toOutput`. It checks only the call site, not a function that the loop calls. The core docs and skills, and the `framework-fs-split` skill of `@t4h.framework/fs`, tell you to build an index one time and call `lookup` in the loop.
20
+
21
+ - [#253](https://github.com/tech4humans-brasil/framework/pull/253) [`d9f5640`](https://github.com/tech4humans-brasil/framework/commit/d9f56403f90ffa1e04640b244f8c9247a119c084) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The fs docs and skills no longer describe how the dev server or a runtime stores a hand-rolled `Binary`. They say that such a class can pass in one environment and fail in another.
22
+
23
+ - [#257](https://github.com/tech4humans-brasil/framework/pull/257) [`586bfb4`](https://github.com/tech4humans-brasil/framework/commit/586bfb4663f4a478527ee0c8db360603f034d615) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The fs site pages now hold the full contract, the README is short, and each skill gives only the steps and traps for a consumer app. The docs now name the `split` output `WorkflowBatchCollectionRef` and no longer name a runtime limit.
24
+
25
+ - [#258](https://github.com/tech4humans-brasil/framework/pull/258) [`421f163`](https://github.com/tech4humans-brasil/framework/commit/421f16390834a26b87da5936dfa7c30c669785ed) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - `join` now fails the file stream when the batch fails before the end. Before, the stream stayed open and the write of the host did not end. `read` now rejects a range with an `end` that is not an integer. The other changes are internal and the public API does not change.
26
+
27
+ - [#246](https://github.com/tech4humans-brasil/framework/pull/246) [`b99b41f`](https://github.com/tech4humans-brasil/framework/commit/b99b41f60d69b3037187e9fcdc27ab0d47ff18a2) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Adds byte windows to `SftpBinary`. `binary.slice(range)` returns a view of a byte window of the remote file that reuses the stream of `binary`. The new `range` field is absolute in the file, with an exclusive `end`, and `contentLength` is `range.end - range.start`. A slice of a slice composes. `toJSON()` includes `range` on a slice only, so the JSON of a whole file does not change. `slice` throws a `RangeError` when the window is not inside the binary. In `@t4h.framework/fs`, the docs no longer give an SFTP reference as an example of a binary that cannot be sliced.
28
+
29
+ ## 0.13.1
30
+
31
+ ### Patch Changes
32
+
33
+ - [#229](https://github.com/tech4humans-brasil/framework/pull/229) [`d4d7d97`](https://github.com/tech4humans-brasil/framework/commit/d4d7d973233ec1f4d78628a69283701b9a55fbf9) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Fixes the published packages:
34
+ - harness: the published types import `typings/PublicProperties.js`, which was not in `dist`. `uuidv7` is now a dependency.
35
+ - dev: `tar` is now a dependency.
36
+ - fs: the published types of `FileSplitActivity` no longer import `typebox`, which `fs` does not depend on.
37
+
38
+ - [#241](https://github.com/tech4humans-brasil/framework/pull/241) [`f6b8454`](https://github.com/tech4humans-brasil/framework/commit/f6b845484d22e7c8e7240fe2e113f1cdea7c8ce3) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Adds seven rules to `@t4h.framework/core/eslint` and turns them on in `configs.recommended`:
39
+ - `no-io-in-workflow` reports `fetch` and calls to network, file, database and mail modules in a workflow and in the `constructor`, `toInput` and `toOutput` of an activity. It also reports `zlib` and `stream` calls there, because they work on the content in memory on each replay.
40
+ - `no-binary-subclass` reports a class that extends `Binary`.
41
+ - `no-framework-activity-in-app` reports `new` and `extends` on an activity of a framework package other than the core.
42
+ - `no-testing-import-in-source` reports an import of a `@t4h.framework/<package>/testing` entry out of a test file.
43
+ - `require-workflow-id` reports a `Workflow` with no `id`.
44
+ - `no-secret-to-string` reports a `Secret` that becomes a string.
45
+ - `no-literal-secret-in-authorization` reports a string literal in the `secret` or `clientSecret` field of an `Authorization`.
46
+
47
+ The package skills now name these rules in place of the text that the rules enforce. The docs and skills give each `Workflow` an `id`, read the HMAC secret from `EnvDesign`, and do not document a custom HTTP activity.
48
+
49
+ - [#238](https://github.com/tech4humans-brasil/framework/pull/238) [`8567806`](https://github.com/tech4humans-brasil/framework/commit/8567806ec537288076c3a18a4816f26d58a67905) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The `framework-fs` skill says to call `Binary.stream()` only in a method of an activity, not in the workflow.
50
+
51
+ - [#240](https://github.com/tech4humans-brasil/framework/pull/240) [`0606115`](https://github.com/tech4humans-brasil/framework/commit/0606115e2e0baa994e9f0ed3559ee54ecac88e44) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The skills do not repeat a rule that a lint rule, `tsc` or a runtime error already enforces. Each such rule is now one line that names the gate. The examples that broke a gate (`process.env`, `as any`, an inline secret, an `EnvDesign` in a workflow file, `ref.output` before `wait()`) are correct now.
52
+
53
+ - [#228](https://github.com/tech4humans-brasil/framework/pull/228) [`d29e1ab`](https://github.com/tech4humans-brasil/framework/commit/d29e1ab29a039b13c3e4b075472bd615566ae75e) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Uses exact types and `unknown` in place of `any`, and checks external data with guards.
54
+ - harness: the fix-issues prompt lists each sensor as `- checkTypes: PASS`. Before, it printed `- 0: [object Object]`.
55
+ - hmac: an algorithm name such as `constructor` throws `Unsupported algorithm`.
56
+ - oidc: a discovery response with no `introspection_endpoint`, or a null introspection body, throws a clear error.
57
+ - `HttpRequestActivity.toOutput` takes `HttpRequestActivityOutput` in place of `any`.
58
+
3
59
  ## 0.13.0
4
60
 
5
61
  ### Minor Changes