@t4h.framework/sftp 0.8.5 → 0.9.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-sftp/SKILL.md +66 -289
- package/CHANGELOG.md +35 -0
- package/README.md +25 -165
- package/dist/models/SftpBinary.d.ts +17 -7
- package/dist/models/SftpBinary.d.ts.map +1 -1
- package/dist/models/SftpBinary.js +15 -2
- package/dist/models/SftpBinary.js.map +1 -1
- package/package.json +4 -2
|
@@ -1,324 +1,101 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: framework-sftp
|
|
3
3
|
description: >-
|
|
4
|
-
Guides
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
Guides the use of @t4h.framework/sftp to read and write files on an SFTP
|
|
5
|
+
server from a T4H Framework workflow: the Sftp client (get, put, append,
|
|
6
|
+
list, exists, stat, mkdir, rmdir, rename, copy, delete, chmod, downloadDir),
|
|
7
|
+
sftp:// connection strings as a Secret, the Binary returned by get and its
|
|
8
|
+
SftpBinary slice, and how to test with TestWorkflowEnvironment and mockClaim
|
|
9
|
+
of SftpClaim. Use when a workflow must download, upload, list, move or
|
|
10
|
+
delete remote files, or when someone asks for ssh2, ssh2-sftp-client or an
|
|
11
|
+
SFTP connection in a workflow.
|
|
10
12
|
---
|
|
11
13
|
|
|
12
14
|
# @t4h.framework/sftp
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
## Use the client
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
1. Declare the connection string as a `Secret` in the `EnvDesign` in
|
|
19
|
+
`src/constants/environments.ts`. The URL (`sftp://user:pass@host:22`)
|
|
20
|
+
carries the password.
|
|
21
|
+
2. Create one `Sftp` client and call its methods in the workflow body. Each
|
|
22
|
+
call is one durable step.
|
|
17
23
|
|
|
18
24
|
```typescript
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
SftpClaim,
|
|
22
|
-
SftpAppendActivity,
|
|
23
|
-
SftpChmodActivity,
|
|
24
|
-
SftpCopyActivity,
|
|
25
|
-
SftpCwdActivity,
|
|
26
|
-
SftpDeleteActivity,
|
|
27
|
-
SftpDownloadDirActivity,
|
|
28
|
-
SftpExistsActivity,
|
|
29
|
-
SftpGetActivity,
|
|
30
|
-
SftpListActivity,
|
|
31
|
-
SftpMkdirActivity,
|
|
32
|
-
SftpPutActivity,
|
|
33
|
-
SftpRealPathActivity,
|
|
34
|
-
SftpRenameActivity,
|
|
35
|
-
SftpRmdirActivity,
|
|
36
|
-
SftpStatActivity,
|
|
37
|
-
type SftpAppendActivityInput,
|
|
38
|
-
type SftpChmodActivityInput,
|
|
39
|
-
type SftpCopyActivityInput,
|
|
40
|
-
type SftpCwdActivityInput,
|
|
41
|
-
type SftpDeleteActivityInput,
|
|
42
|
-
type SftpDownloadDirActivityInput,
|
|
43
|
-
type SftpExistsActivityInput,
|
|
44
|
-
type SftpExistsResult,
|
|
45
|
-
type SftpGetActivityInput,
|
|
46
|
-
type SftpFileInfo,
|
|
47
|
-
type SftpListActivityInput,
|
|
48
|
-
type SftpMkdirActivityInput,
|
|
49
|
-
type SftpPutActivityInput,
|
|
50
|
-
type SftpRealPathActivityInput,
|
|
51
|
-
type SftpRenameActivityInput,
|
|
52
|
-
type SftpRmdirActivityInput,
|
|
53
|
-
type SftpStatActivityInput,
|
|
54
|
-
type SftpStat,
|
|
55
|
-
} from '@t4h.framework/sftp'
|
|
25
|
+
// src/constants/environments.ts
|
|
26
|
+
import { EnvDesign, Secret } from '@t4h.framework/core'
|
|
56
27
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Node `>=22`. See [README.md](../../../README.md).
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Architecture
|
|
65
|
-
|
|
66
|
-
`Workflow` → **`Sftp`** (`History.bind` per activity) → shipped `Sftp*Activity` → **`SftpClaim.<method>(input)`** (runtime impl).
|
|
67
|
-
|
|
68
|
-
From a workflow you call the **`Sftp`** client; it forwards each method to the
|
|
69
|
-
bound, replay-safe activity for you. The claim method name matches the client
|
|
70
|
-
method (`get`, `put`, `delete`, …) and the runtime supplies the `SftpClaim`
|
|
71
|
-
implementation.
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## Claims
|
|
76
|
-
|
|
77
|
-
### SftpClaim (required)
|
|
78
|
-
|
|
79
|
-
Runtime must register a concrete implementation:
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
{ provide: SftpClaim, value: new MySftpClaimImpl() }
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Abstract contract (each method accepts the corresponding `*ActivityInput` type):
|
|
86
|
-
|
|
87
|
-
| Method | Input type | Return |
|
|
88
|
-
|--------|------------|--------|
|
|
89
|
-
| `append` | `SftpAppendActivityInput` | `void` |
|
|
90
|
-
| `chmod` | `SftpChmodActivityInput` | `void` |
|
|
91
|
-
| `copy` | `SftpCopyActivityInput` | `void` |
|
|
92
|
-
| `cwd` | `SftpCwdActivityInput` | `string` |
|
|
93
|
-
| `delete` | `SftpDeleteActivityInput` | `void` |
|
|
94
|
-
| `downloadDir` | `SftpDownloadDirActivityInput` | `Binary` |
|
|
95
|
-
| `exists` | `SftpExistsActivityInput` | `SftpExistsResult` |
|
|
96
|
-
| `get` | `SftpGetActivityInput` | `Binary` |
|
|
97
|
-
| `list` | `SftpListActivityInput` | `SftpFileInfo[]` |
|
|
98
|
-
| `mkdir` | `SftpMkdirActivityInput` | `void` |
|
|
99
|
-
| `put` | `SftpPutActivityInput` | `void` |
|
|
100
|
-
| `realPath` | `SftpRealPathActivityInput` | `string` |
|
|
101
|
-
| `rename` | `SftpRenameActivityInput` | `void` |
|
|
102
|
-
| `rmdir` | `SftpRmdirActivityInput` | `void` |
|
|
103
|
-
| `stat` | `SftpStatActivityInput` | `SftpStat` |
|
|
104
|
-
|
|
105
|
-
Methods may return sync values or `Promise`s.
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## Sftp client (preferred in workflows)
|
|
110
|
-
|
|
111
|
-
Instantiate with the `Secret` an `EnvDesign` entry declared `type: Secret` hands the workflow (the URL `sftp://user:pass@host:22` carries the password, so a string is a `tsc` error; the host reads the value where each activity runs):
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
import { Sftp } from '@t4h.framework/sftp'
|
|
115
|
-
import { environments } from '../constants/environments.js'
|
|
116
|
-
|
|
117
|
-
const sftp = new Sftp(environments.SFTP_URL) // Secret
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
All calls run inside a workflow (the runtime, or `TestWorkflowEnvironment` in tests, provides the reconciler context).
|
|
121
|
-
|
|
122
|
-
### Methods
|
|
123
|
-
|
|
124
|
-
| Method | Args | Returns |
|
|
125
|
-
|--------|------|---------|
|
|
126
|
-
| `append` | `filepath`, `content: Binary` | `void` |
|
|
127
|
-
| `chmod` | `path`, `mode: string \| number` | `void` |
|
|
128
|
-
| `copy` | `srcPath`, `destPath` | `void` |
|
|
129
|
-
| `cwd` | — | `string` |
|
|
130
|
-
| `delete` | `path`, `noErrorOK?: boolean` | `void` |
|
|
131
|
-
| `downloadDir` | `path` | `Binary` |
|
|
132
|
-
| `exists` | `path` | `SftpExistsResult` |
|
|
133
|
-
| `get` | `filepath` | `Binary` |
|
|
134
|
-
| `list` | `path` | `SftpFileInfo[]` |
|
|
135
|
-
| `mkdir` | `path`, `recursive?: boolean` | `void` |
|
|
136
|
-
| `put` | `filepath`, `content: Binary` | `void` |
|
|
137
|
-
| `realPath` | `path` | `string` |
|
|
138
|
-
| `rename` | `srcPath`, `destPath` | `void` |
|
|
139
|
-
| `rmdir` | `path`, `recursive?: boolean` | `void` |
|
|
140
|
-
| `stat` | `path` | `SftpStat` |
|
|
141
|
-
|
|
142
|
-
**Flags:**
|
|
143
|
-
|
|
144
|
-
- **`delete(path, true)`** — `noErrorOK`: suppress error when file missing.
|
|
145
|
-
- **`mkdir(path, true)`** — `recursive`: create intermediate directories.
|
|
146
|
-
- **`rmdir(path, true)`** — `recursive`: remove directory tree.
|
|
147
|
-
|
|
148
|
-
**`exists`:** `false` if not found; otherwise `'d'` (directory), `'-'` (file), `'l'` (symlink).
|
|
149
|
-
|
|
150
|
-
### Example workflow usage
|
|
151
|
-
|
|
152
|
-
```typescript
|
|
153
|
-
import { Workflow } from '@t4h.framework/core'
|
|
154
|
-
import { Sftp } from '@t4h.framework/sftp'
|
|
155
|
-
|
|
156
|
-
const sftp = new Sftp(process.env.SFTP_URL!)
|
|
157
|
-
|
|
158
|
-
export const ingestRemoteFile = new Workflow(async () => {
|
|
159
|
-
const remotePath = '/incoming/report.csv'
|
|
160
|
-
|
|
161
|
-
if ((await sftp.exists(remotePath)) === false) {
|
|
162
|
-
return { ok: false, reason: 'missing' }
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const file = await sftp.get(remotePath)
|
|
166
|
-
await sftp.rename(remotePath, '/processed/report.csv')
|
|
167
|
-
|
|
168
|
-
return { ok: true, size: file /* use Binary APIs from core */ }
|
|
28
|
+
export const environments = EnvDesign({
|
|
29
|
+
SFTP_URL: { type: Secret },
|
|
169
30
|
})
|
|
170
31
|
```
|
|
171
32
|
|
|
172
|
-
Use **`Binary`** from `@t4h.framework/core` for upload/download content (`put`, `get`, `append`, `downloadDir`).
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## Using SFTP in a workflow via `History.reconciler`
|
|
177
|
-
|
|
178
|
-
Prefer the **`Sftp`** client. When you need the exported activity directly,
|
|
179
|
-
drive the **shipped** `Sftp*Activity` class through `History.reconciler` — this
|
|
180
|
-
is a ready, replay-safe helper. **Never define or extend your own activity**;
|
|
181
|
-
only pass the activity classes this package exports.
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
import { History } from '@t4h.framework/core'
|
|
185
|
-
import { Sftp, SftpGetActivity } from '@t4h.framework/sftp'
|
|
186
|
-
|
|
187
|
-
const sftp = new Sftp(environments.SFTP_URL) // Secret
|
|
188
|
-
const content = await History.reconciler(SftpGetActivity, sftp, '/remote/file.txt')
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
Activity ↔ client mapping:
|
|
192
|
-
|
|
193
|
-
| Activity | Client method |
|
|
194
|
-
|----------|---------------|
|
|
195
|
-
| `SftpAppendActivity` | `append` |
|
|
196
|
-
| `SftpChmodActivity` | `chmod` |
|
|
197
|
-
| `SftpCopyActivity` | `copy` |
|
|
198
|
-
| `SftpCwdActivity` | `cwd` |
|
|
199
|
-
| `SftpDeleteActivity` | `delete` |
|
|
200
|
-
| `SftpDownloadDirActivity` | `downloadDir` |
|
|
201
|
-
| `SftpExistsActivity` | `exists` |
|
|
202
|
-
| `SftpGetActivity` | `get` |
|
|
203
|
-
| `SftpListActivity` | `list` |
|
|
204
|
-
| `SftpMkdirActivity` | `mkdir` |
|
|
205
|
-
| `SftpPutActivity` | `put` |
|
|
206
|
-
| `SftpRealPathActivity` | `realPath` |
|
|
207
|
-
| `SftpRenameActivity` | `rename` |
|
|
208
|
-
| `SftpRmdirActivity` | `rmdir` |
|
|
209
|
-
| `SftpStatActivity` | `stat` |
|
|
210
|
-
|
|
211
|
-
---
|
|
212
|
-
|
|
213
|
-
## Activity input shapes
|
|
214
|
-
|
|
215
|
-
All inputs include **`connectionString: Secret`** (the name of the env entry; the value is read on the host). Path field names differ by operation:
|
|
216
|
-
|
|
217
|
-
| Input type | Fields (beyond `connectionString`) |
|
|
218
|
-
|------------|-----------------------------------|
|
|
219
|
-
| `SftpAppendActivityInput` | `filepath`, `content: Binary` |
|
|
220
|
-
| `SftpChmodActivityInput` | `path`, `mode: string \| number` |
|
|
221
|
-
| `SftpCopyActivityInput` | `srcPath`, `destPath` |
|
|
222
|
-
| `SftpCwdActivityInput` | _(none)_ |
|
|
223
|
-
| `SftpDeleteActivityInput` | `path`, `noErrorOK?: boolean` |
|
|
224
|
-
| `SftpDownloadDirActivityInput` | `path` |
|
|
225
|
-
| `SftpExistsActivityInput` | `path` |
|
|
226
|
-
| `SftpGetActivityInput` | `filepath` |
|
|
227
|
-
| `SftpListActivityInput` | `path` |
|
|
228
|
-
| `SftpMkdirActivityInput` | `path`, `recursive?: boolean` |
|
|
229
|
-
| `SftpPutActivityInput` | `filepath`, `content: Binary` |
|
|
230
|
-
| `SftpRealPathActivityInput` | `path` |
|
|
231
|
-
| `SftpRenameActivityInput` | `srcPath`, `destPath` |
|
|
232
|
-
| `SftpRmdirActivityInput` | `path`, `recursive?: boolean` |
|
|
233
|
-
| `SftpStatActivityInput` | `path` |
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
## Result types
|
|
238
|
-
|
|
239
|
-
### `SftpExistsResult`
|
|
240
|
-
|
|
241
33
|
```typescript
|
|
242
|
-
|
|
243
|
-
```
|
|
34
|
+
import { Sftp } from '@t4h.framework/sftp'
|
|
244
35
|
|
|
245
|
-
|
|
36
|
+
import { environments } from '../constants/environments.js'
|
|
246
37
|
|
|
247
|
-
|
|
248
|
-
type SftpFileInfo = {
|
|
249
|
-
name: string
|
|
250
|
-
type: 'd' | '-' | 'l'
|
|
251
|
-
size: number
|
|
252
|
-
modifyTime: number
|
|
253
|
-
accessTime: number
|
|
254
|
-
rights: { user: string; group: string; other: string }
|
|
255
|
-
owner: number
|
|
256
|
-
group: number
|
|
257
|
-
longname: string
|
|
258
|
-
}
|
|
259
|
-
```
|
|
38
|
+
const sftp = new Sftp(environments.SFTP_URL)
|
|
260
39
|
|
|
261
|
-
|
|
40
|
+
for (const entry of await sftp.list('/inbound')) {
|
|
41
|
+
if (entry.type !== '-') continue
|
|
262
42
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
uid: number
|
|
267
|
-
gid: number
|
|
268
|
-
size: number
|
|
269
|
-
accessTime: number
|
|
270
|
-
modifyTime: number
|
|
271
|
-
isDirectory: boolean
|
|
272
|
-
isFile: boolean
|
|
273
|
-
isBlockDevice: boolean
|
|
274
|
-
isCharacterDevice: boolean
|
|
275
|
-
isSymbolicLink: boolean
|
|
276
|
-
isFIFO: boolean
|
|
277
|
-
isSocket: boolean
|
|
43
|
+
const file = await sftp.get(`/inbound/${entry.name}`)
|
|
44
|
+
await sftp.put(`/archive/${entry.name}`, file)
|
|
45
|
+
await sftp.delete(`/inbound/${entry.name}`, true)
|
|
278
46
|
}
|
|
279
47
|
```
|
|
280
48
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
49
|
+
## Traps
|
|
50
|
+
|
|
51
|
+
- `new Sftp(...)` takes a `Secret`. A string literal or a `String` env entry is
|
|
52
|
+
a type error.
|
|
53
|
+
- `get` returns a `Binary`, a reference to the remote file. Pass it to the
|
|
54
|
+
next step (`put`, `split`, `unzip`). Do not buffer it in the workflow. To
|
|
55
|
+
read a header or a trailer, use `read` from `@t4h.framework/fs` with a
|
|
56
|
+
`range`; see the `framework-fs` skill.
|
|
57
|
+
- `put` and `append` take a `Binary`, not a string or a `Buffer`. Make one with
|
|
58
|
+
`write` from `@t4h.framework/fs`. Never build a `Binary` by hand.
|
|
59
|
+
- `exists` returns `false` or a type character (`'d'`, `'-'`, `'l'`), not a
|
|
60
|
+
boolean. `if (await sftp.exists(p))` works; `=== true` never matches.
|
|
61
|
+
- The boolean second arguments are positional: `delete(path, true)` gives no
|
|
62
|
+
error when the file is missing, `mkdir(path, true)` and `rmdir(path, true)`
|
|
63
|
+
are recursive.
|
|
64
|
+
- `SftpBinary.slice(range)`: `range` is relative to the binary you slice, `end`
|
|
65
|
+
is exclusive, and it throws a `RangeError` outside the binary.
|
|
66
|
+
- `@t4h.framework/core/no-io-in-workflow` rejects `ssh2`, `ssh2-sftp-client`
|
|
67
|
+
and `basic-ftp` in a workflow. Use `Sftp`.
|
|
68
|
+
- Do not `new` or `extend` an `Sftp*Activity`
|
|
69
|
+
(`@t4h.framework/core/no-framework-activity-in-app`). Use the client.
|
|
70
|
+
- The runtime must provide `SftpClaim`.
|
|
71
|
+
|
|
72
|
+
## Test
|
|
73
|
+
|
|
74
|
+
Mock `SftpClaim` with only the methods the workflow calls. Each claim method
|
|
75
|
+
has the name of the client method and receives an input object with
|
|
76
|
+
`connectionString` and the call arguments. The path field is `filepath` for
|
|
77
|
+
`get`, `put` and `append`, `srcPath` and `destPath` for `copy` and `rename`,
|
|
78
|
+
and `path` for the others.
|
|
290
79
|
|
|
291
80
|
```typescript
|
|
292
81
|
import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
|
|
293
82
|
import { SftpClaim } from '@t4h.framework/sftp'
|
|
294
83
|
|
|
295
|
-
const
|
|
84
|
+
const get = vi.fn().mockResolvedValue(binary)
|
|
296
85
|
|
|
297
86
|
const env = TestWorkflowEnvironment.create({ now: 0 })
|
|
298
87
|
|
|
299
|
-
|
|
300
|
-
claims: [mockClaim(SftpClaim, { get:
|
|
88
|
+
await env.execute(workflow, input, {
|
|
89
|
+
claims: [mockClaim(SftpClaim, { get, put: vi.fn() })],
|
|
90
|
+
env: { SFTP_URL: 'sftp://user:pass@sftp.example.com:22' },
|
|
301
91
|
})
|
|
302
92
|
|
|
303
|
-
|
|
304
|
-
expect(mockGet.mock.calls[0][0]).toMatchObject({ filepath: '/remote/file.txt' })
|
|
93
|
+
expect(get.mock.calls[0][0]).toMatchObject({ filepath: '/inbound/a.csv' })
|
|
305
94
|
```
|
|
306
95
|
|
|
307
|
-
|
|
308
|
-
workflow reaches.
|
|
309
|
-
|
|
310
|
-
---
|
|
311
|
-
|
|
312
|
-
## Checklist for agents
|
|
313
|
-
|
|
314
|
-
1. Prefer **`new Sftp(connectionString)`** in workflow code — not raw SSH/SFTP libraries.
|
|
315
|
-
2. Never perform non-replayable SFTP I/O inside workflows — use **`Sftp`** or **`History.reconciler(Sftp*Activity, ...)`**.
|
|
316
|
-
3. Ensure runtime provides **`SftpClaim`** (or pass a mock in tests).
|
|
317
|
-
4. Pass **`Binary`** for `put` / `append` / `get` / `downloadDir` content — from `@t4h.framework/core`.
|
|
318
|
-
5. Use **`filepath`** for get/put/append; **`path`** for most other operations; **`srcPath`/`destPath`** for copy/rename.
|
|
319
|
-
6. Use **`exists`** before destructive ops when idempotency matters; use **`delete(..., true)`** for tolerant deletes.
|
|
320
|
-
7. Use **`mkdir(..., true)`** / **`rmdir(..., true)`** only when recursive tree ops are intended.
|
|
321
|
-
8. Test workflows with **`TestWorkflowEnvironment`** + **`mockClaim(SftpClaim, ...)`**; assert on the mock's `mock.calls`.
|
|
322
|
-
9. Do not document or import APIs not exported from `@t4h.framework/sftp`.
|
|
96
|
+
## Reference
|
|
323
97
|
|
|
324
|
-
|
|
98
|
+
- Guide: https://tech4humans-brasil.github.io/framework/sftp/
|
|
99
|
+
- Secrets: https://tech4humans-brasil.github.io/framework/core/environment
|
|
100
|
+
- Workflow testing: https://tech4humans-brasil.github.io/framework/core/testing
|
|
101
|
+
- Types: `node_modules/@t4h.framework/sftp/dist/sftp.d.ts`
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# @t4h.framework/sftp
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#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.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#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 sftp README is now short and links to the site. The docs page now has the testing, slice and runtime sections, and the skill keeps only the traps that the types do not show.
|
|
12
|
+
|
|
13
|
+
## 0.8.6
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [#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`:
|
|
18
|
+
- `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.
|
|
19
|
+
- `no-binary-subclass` reports a class that extends `Binary`.
|
|
20
|
+
- `no-framework-activity-in-app` reports `new` and `extends` on an activity of a framework package other than the core.
|
|
21
|
+
- `no-testing-import-in-source` reports an import of a `@t4h.framework/<package>/testing` entry out of a test file.
|
|
22
|
+
- `require-workflow-id` reports a `Workflow` with no `id`.
|
|
23
|
+
- `no-secret-to-string` reports a `Secret` that becomes a string.
|
|
24
|
+
- `no-literal-secret-in-authorization` reports a string literal in the `secret` or `clientSecret` field of an `Authorization`.
|
|
25
|
+
|
|
26
|
+
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.
|
|
27
|
+
|
|
28
|
+
- [#243](https://github.com/tech4humans-brasil/framework/pull/243) [`0ffd663`](https://github.com/tech4humans-brasil/framework/commit/0ffd663ce8369edd7b54206cb3216568ad1de471) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The README examples read each value from `environments`, an `EnvDesign` in `src/constants/environments.ts`, and pass each credential as a `Secret`. They no longer use `process.env`, the raw `env` from the core or a literal in a `Secret` field. The jwks README describes the real context fields (`signature`, `rawPayload`) and says that a token that `jwt.verify` rejects throws.
|
|
29
|
+
|
|
30
|
+
- [#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.
|
|
31
|
+
|
|
32
|
+
- [#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.
|
|
33
|
+
- harness: the fix-issues prompt lists each sensor as `- checkTypes: PASS`. Before, it printed `- 0: [object Object]`.
|
|
34
|
+
- hmac: an algorithm name such as `constructor` throws `Unsupported algorithm`.
|
|
35
|
+
- oidc: a discovery response with no `introspection_endpoint`, or a null introspection body, throws a clear error.
|
|
36
|
+
- `HttpRequestActivity.toOutput` takes `HttpRequestActivityOutput` in place of `any`.
|
|
37
|
+
|
|
3
38
|
## 0.8.5
|
|
4
39
|
|
|
5
40
|
## 0.8.4
|
package/README.md
CHANGED
|
@@ -1,186 +1,46 @@
|
|
|
1
1
|
# @t4h.framework/sftp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Reads and writes files on an SFTP server from a T4H Framework workflow. Each
|
|
4
|
+
operation (`get`, `put`, `list`, `rename` and more) is a durable step.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## Install
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Single abstract dependency that backs all SFTP activities. The runtime must provide a concrete implementation.
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import { SftpClaim } from '@t4h.framework/sftp'
|
|
13
|
-
|
|
14
|
-
// the runtime provides this:
|
|
15
|
-
// { provide: SftpClaim, value: new MySftpClaimImpl() }
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Sftp
|
|
19
|
-
|
|
20
|
-
High-level SFTP client that wraps every activity behind a simple API. Instantiated with a connection string.
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
import { Sftp } from '@t4h.framework/sftp'
|
|
24
|
-
import type { Binary } from '@t4h.framework/core'
|
|
25
|
-
|
|
26
|
-
const sftp = new Sftp('sftp://user:pass@sftp.example.com:22')
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### append
|
|
30
|
-
|
|
31
|
-
Appends content to an existing remote file.
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
await sftp.append('/remote/path/file.txt', myBinaryContent) // void
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### chmod
|
|
38
|
-
|
|
39
|
-
Changes the permissions of a remote file or directory.
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
await sftp.chmod('/remote/path/file.txt', 0o755) // void
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### copy
|
|
46
|
-
|
|
47
|
-
Copies a remote file from one path to another.
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
await sftp.copy('/remote/src.txt', '/remote/dest.txt') // void
|
|
8
|
+
```bash
|
|
9
|
+
npm install @t4h.framework/sftp
|
|
51
10
|
```
|
|
52
11
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Returns the current working directory on the remote server.
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
const dir = await sftp.cwd() // string
|
|
59
|
-
```
|
|
12
|
+
Peer dependency: `@t4h.framework/core`.
|
|
60
13
|
|
|
61
|
-
|
|
14
|
+
## Usage
|
|
62
15
|
|
|
63
|
-
|
|
16
|
+
The connection string carries the password, so declare it with
|
|
17
|
+
`type: Secret` in the `EnvDesign` in `src/constants/environments.ts`:
|
|
64
18
|
|
|
65
19
|
```typescript
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### downloadDir
|
|
71
|
-
|
|
72
|
-
Downloads a remote directory as binary content.
|
|
20
|
+
// src/constants/environments.ts
|
|
21
|
+
import { EnvDesign, Secret } from '@t4h.framework/core'
|
|
73
22
|
|
|
74
|
-
|
|
75
|
-
|
|
23
|
+
export const environments = EnvDesign({
|
|
24
|
+
SFTP_URL: { type: Secret },
|
|
25
|
+
})
|
|
76
26
|
```
|
|
77
27
|
|
|
78
|
-
### exists
|
|
79
|
-
|
|
80
|
-
Checks whether a remote path exists. Returns `false` if not found, or its type character (`'d'` directory, `'-'` file, `'l'` symlink).
|
|
81
|
-
|
|
82
28
|
```typescript
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
### get
|
|
87
|
-
|
|
88
|
-
Downloads a single remote file.
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
const content = await sftp.get('/remote/path/file.txt') // Binary
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### list
|
|
95
|
-
|
|
96
|
-
Lists the contents of a remote directory.
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
const files = await sftp.list('/remote/path') // SftpFileInfo[]
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
`SftpFileInfo`:
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
type SftpFileInfo = {
|
|
106
|
-
name: string
|
|
107
|
-
type: 'd' | '-' | 'l'
|
|
108
|
-
size: number
|
|
109
|
-
modifyTime: number
|
|
110
|
-
accessTime: number
|
|
111
|
-
rights: { user: string; group: string; other: string }
|
|
112
|
-
owner: number
|
|
113
|
-
group: number
|
|
114
|
-
longname: string
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### mkdir
|
|
119
|
-
|
|
120
|
-
Creates a remote directory. Pass `true` as the second argument to create intermediate directories recursively.
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
await sftp.mkdir('/remote/path/new-dir') // void
|
|
124
|
-
await sftp.mkdir('/remote/path/a/b/c', true) // void, recursive
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### put
|
|
128
|
-
|
|
129
|
-
Uploads content to a remote file.
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
await sftp.put('/remote/path/file.txt', myBinaryContent) // void
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### realPath
|
|
136
|
-
|
|
137
|
-
Resolves the absolute path of a remote path (expanding symlinks, `.`, `..`, etc.).
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
const abs = await sftp.realPath('/remote/./path/../file.txt') // string
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### rename
|
|
144
|
-
|
|
145
|
-
Renames (moves) a remote file or directory.
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
await sftp.rename('/remote/old-name.txt', '/remote/new-name.txt') // void
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### rmdir
|
|
29
|
+
import { Workflow } from '@t4h.framework/core'
|
|
30
|
+
import { Sftp } from '@t4h.framework/sftp'
|
|
152
31
|
|
|
153
|
-
|
|
32
|
+
import { environments } from './constants/environments.js'
|
|
154
33
|
|
|
155
|
-
|
|
156
|
-
await sftp.rmdir('/remote/path/empty-dir') // void
|
|
157
|
-
await sftp.rmdir('/remote/path/full-dir', true) // void, recursive
|
|
158
|
-
```
|
|
34
|
+
const sftp = new Sftp(environments.SFTP_URL)
|
|
159
35
|
|
|
160
|
-
|
|
36
|
+
const ingest = new Workflow({ id: 'ingest' }, async () => {
|
|
37
|
+
const file = await sftp.get('/inbound/orders.csv')
|
|
161
38
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const info = await sftp.stat('/remote/path/file.txt') // SftpStat
|
|
39
|
+
await sftp.put('/archive/orders.csv', file)
|
|
40
|
+
await sftp.delete('/inbound/orders.csv')
|
|
41
|
+
})
|
|
166
42
|
```
|
|
167
43
|
|
|
168
|
-
|
|
44
|
+
## Documentation
|
|
169
45
|
|
|
170
|
-
|
|
171
|
-
type SftpStat = {
|
|
172
|
-
mode: number
|
|
173
|
-
uid: number
|
|
174
|
-
gid: number
|
|
175
|
-
size: number
|
|
176
|
-
accessTime: number
|
|
177
|
-
modifyTime: number
|
|
178
|
-
isDirectory: boolean
|
|
179
|
-
isFile: boolean
|
|
180
|
-
isBlockDevice: boolean
|
|
181
|
-
isCharacterDevice: boolean
|
|
182
|
-
isSymbolicLink: boolean
|
|
183
|
-
isFIFO: boolean
|
|
184
|
-
isSocket: boolean
|
|
185
|
-
}
|
|
186
|
-
```
|
|
46
|
+
https://tech4humans-brasil.github.io/framework/sftp/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Readable } from 'node:stream';
|
|
2
2
|
import { Binary, type Secret } from '@t4h.framework/core';
|
|
3
|
-
interface SftpBinaryJSON {
|
|
3
|
+
export interface SftpBinaryJSON {
|
|
4
4
|
filepath: string;
|
|
5
5
|
contentType: string;
|
|
6
6
|
contentLength: number;
|
|
@@ -11,16 +11,26 @@ export declare class SftpBinary extends Binary {
|
|
|
11
11
|
readonly contentType: string;
|
|
12
12
|
readonly contentLength: number;
|
|
13
13
|
readonly stream: (this: SftpBinary) => Readable | Promise<Readable>;
|
|
14
|
-
|
|
14
|
+
readonly range?: {
|
|
15
|
+
start: number;
|
|
16
|
+
end: number;
|
|
17
|
+
} | undefined;
|
|
18
|
+
protected constructor(connectionString: Secret, filepath: string, contentType: string, contentLength: number, stream: (this: SftpBinary) => Readable | Promise<Readable>, range?: {
|
|
19
|
+
start: number;
|
|
20
|
+
end: number;
|
|
21
|
+
} | undefined);
|
|
15
22
|
static from(connectionString: Secret, file: SftpBinaryJSON, stream: (self: SftpBinary) => Readable | Promise<Readable>): SftpBinary;
|
|
23
|
+
slice(range: {
|
|
24
|
+
start: number;
|
|
25
|
+
end: number;
|
|
26
|
+
}): SftpBinary;
|
|
16
27
|
toJSON(): {
|
|
17
28
|
connectionString: Secret;
|
|
18
|
-
file:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
file: SftpBinaryJSON;
|
|
30
|
+
range?: {
|
|
31
|
+
start: number;
|
|
32
|
+
end: number;
|
|
22
33
|
};
|
|
23
34
|
};
|
|
24
35
|
}
|
|
25
|
-
export {};
|
|
26
36
|
//# sourceMappingURL=SftpBinary.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SftpBinary.d.ts","sourceRoot":"","sources":["../../src/models/SftpBinary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEzD,
|
|
1
|
+
{"version":3,"file":"SftpBinary.d.ts","sourceRoot":"","sources":["../../src/models/SftpBinary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEzD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,qBAAa,UAAW,SAAQ,MAAM;aAElB,gBAAgB,EAAE,MAAM;aACxB,QAAQ,EAAE,MAAM;aAChB,WAAW,EAAE,MAAM;aACnB,aAAa,EAAE,MAAM;aACrB,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;aAC1D,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;IANxD,SAAS,aACS,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,EAC1D,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,YAAA;WAK1C,IAAI,CAChB,gBAAgB,EAAE,MAAM,EACxB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAarD,KAAK,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU;IAmBxD,MAAM,IAAI;QACf,gBAAgB,EAAE,MAAM,CAAA;QACxB,IAAI,EAAE,cAAc,CAAA;QACpB,KAAK,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAA;KACvC;CAcF"}
|
|
@@ -5,21 +5,31 @@ export class SftpBinary extends Binary {
|
|
|
5
5
|
contentType;
|
|
6
6
|
contentLength;
|
|
7
7
|
stream;
|
|
8
|
-
|
|
8
|
+
range;
|
|
9
|
+
constructor(connectionString, filepath, contentType, contentLength, stream, range) {
|
|
9
10
|
super();
|
|
10
11
|
this.connectionString = connectionString;
|
|
11
12
|
this.filepath = filepath;
|
|
12
13
|
this.contentType = contentType;
|
|
13
14
|
this.contentLength = contentLength;
|
|
14
15
|
this.stream = stream;
|
|
16
|
+
this.range = range;
|
|
15
17
|
}
|
|
16
18
|
static from(connectionString, file, stream) {
|
|
17
19
|
return new this(connectionString, file.filepath, file.contentType, file.contentLength, function () {
|
|
18
20
|
return stream(this);
|
|
19
21
|
});
|
|
20
22
|
}
|
|
23
|
+
slice(range) {
|
|
24
|
+
if (range.start < 0 || range.end < range.start)
|
|
25
|
+
throw new RangeError('Slice range must be a non-negative byte window');
|
|
26
|
+
if (range.end > this.contentLength)
|
|
27
|
+
throw new RangeError('Slice range must not exceed the binary length');
|
|
28
|
+
const base = this.range?.start ?? 0;
|
|
29
|
+
return new SftpBinary(this.connectionString, this.filepath, this.contentType, range.end - range.start, this.stream, { start: base + range.start, end: base + range.end });
|
|
30
|
+
}
|
|
21
31
|
toJSON() {
|
|
22
|
-
|
|
32
|
+
const json = {
|
|
23
33
|
connectionString: this.connectionString,
|
|
24
34
|
file: {
|
|
25
35
|
filepath: this.filepath,
|
|
@@ -27,6 +37,9 @@ export class SftpBinary extends Binary {
|
|
|
27
37
|
contentLength: this.contentLength,
|
|
28
38
|
},
|
|
29
39
|
};
|
|
40
|
+
if (!this.range)
|
|
41
|
+
return json;
|
|
42
|
+
return { ...json, range: { ...this.range } };
|
|
30
43
|
}
|
|
31
44
|
}
|
|
32
45
|
//# sourceMappingURL=SftpBinary.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SftpBinary.js","sourceRoot":"","sources":["../../src/models/SftpBinary.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAA;AAQzD,MAAM,OAAO,UAAW,SAAQ,MAAM;IAElB;IACA;IACA;IACA;IACA;
|
|
1
|
+
{"version":3,"file":"SftpBinary.js","sourceRoot":"","sources":["../../src/models/SftpBinary.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAA;AAQzD,MAAM,OAAO,UAAW,SAAQ,MAAM;IAElB;IACA;IACA;IACA;IACA;IACA;IANlB,YACkB,gBAAwB,EACxB,QAAgB,EAChB,WAAmB,EACnB,aAAqB,EACrB,MAA0D,EAC1D,KAAsC;QAEtD,KAAK,EAAE,CAAA;QAPS,qBAAgB,GAAhB,gBAAgB,CAAQ;QACxB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,gBAAW,GAAX,WAAW,CAAQ;QACnB,kBAAa,GAAb,aAAa,CAAQ;QACrB,WAAM,GAAN,MAAM,CAAoD;QAC1D,UAAK,GAAL,KAAK,CAAiC;IAGxD,CAAC;IAEM,MAAM,CAAC,IAAI,CAChB,gBAAwB,EACxB,IAAoB,EACpB,MAA0D;QAE1D,OAAO,IAAI,IAAI,CACb,gBAAgB,EAChB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,aAAa,EAClB;YACE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAA;QACrB,CAAC,CACF,CAAA;IACH,CAAC;IAEM,KAAK,CAAC,KAAqC;QAChD,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK;YAC5C,MAAM,IAAI,UAAU,CAAC,gDAAgD,CAAC,CAAA;QAExE,IAAI,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,aAAa;YAChC,MAAM,IAAI,UAAU,CAAC,+CAA+C,CAAC,CAAA;QAEvE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC,CAAA;QAEnC,OAAO,IAAI,UAAU,CACnB,IAAI,CAAC,gBAAgB,EACrB,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,WAAW,EAChB,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,EACvB,IAAI,CAAC,MAAM,EACX,EAAE,KAAK,EAAE,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,CACrD,CAAA;IACH,CAAC;IAEM,MAAM;QAKX,MAAM,IAAI,GAAG;YACX,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,IAAI,EAAE;gBACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,aAAa,EAAE,IAAI,CAAC,aAAa;aAClC;SACF,CAAA;QAED,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAE5B,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,CAAA;IAC9C,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/sftp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "SFTP module for the T4H Framework",
|
|
5
5
|
"homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/sftp",
|
|
6
6
|
"bugs": "https://github.com/tech4humans-brasil/framework/issues",
|
|
@@ -26,12 +26,14 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc --project tsconfig.build.json",
|
|
28
28
|
"check-types": "tsc --noEmit",
|
|
29
|
+
"check-package": "publint --strict && attw --pack . --profile esm-only",
|
|
30
|
+
"lint": "eslint . --max-warnings 0",
|
|
29
31
|
"prepublishOnly": "yarn build",
|
|
30
32
|
"test": "vitest run",
|
|
31
33
|
"test:watch": "vitest"
|
|
32
34
|
},
|
|
33
35
|
"devDependencies": {
|
|
34
|
-
"@t4h.framework/core": "^0.
|
|
36
|
+
"@t4h.framework/core": "^0.21.0",
|
|
35
37
|
"typescript": "^5.9.3",
|
|
36
38
|
"vitest": "^4.0.18"
|
|
37
39
|
},
|