@t4h.framework/sftp 0.8.6 → 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.
@@ -1,152 +1,25 @@
1
1
  ---
2
2
  name: framework-sftp
3
3
  description: >-
4
- Guides correct use of @t4h.framework/sftp in T4H Framework workflows: SFTP
5
- activities, SftpClaim runtime dependency, the reconciler-aware Sftp client,
6
- Binary file payloads, and testing with TestWorkflowEnvironment. Use when
7
- implementing SFTP upload/download/list in workflows, wiring SftpClaim providers,
8
- mocking SftpClaim in workflow tests, working in packages/sftp, or integrating
9
- remote file transfer (sftp:// connection strings, put/get, mkdir, stat, exists).
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
- SFTP **activities**, **`SftpClaim`**, and a high-level **`Sftp`** client for replayable remote file operations in workflows. Package path: `framework/packages/sftp`. Peer dependency: `@t4h.framework/core` (`Binary`, `SyncActivity`, `Claim`, `History`).
16
+ ## Use the client
15
17
 
16
- Import surface (the package entry; its `exports` map has only `.`, so `tsc` rejects a deep import):
17
-
18
- ```typescript
19
- import {
20
- Sftp,
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'
56
-
57
- import type { Binary } from '@t4h.framework/core'
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 `EnvDesign` entry declared `type: Secret` (the constructor takes a `Secret`, so a string is a `tsc` error). The URL `sftp://user:pass@host:22` carries the password; the host reads the value where each activity runs:
112
-
113
- ```typescript
114
- import { Sftp } from '@t4h.framework/sftp'
115
-
116
- import { environments } from '../constants/environments.js'
117
-
118
- const sftp = new Sftp(environments.SFTP_URL) // Secret
119
- ```
120
-
121
- ### Methods
122
-
123
- | Method | Args | Returns |
124
- |--------|------|---------|
125
- | `append` | `filepath`, `content: Binary` | `void` |
126
- | `chmod` | `path`, `mode: string \| number` | `void` |
127
- | `copy` | `srcPath`, `destPath` | `void` |
128
- | `cwd` | — | `string` |
129
- | `delete` | `path`, `noErrorOK?: boolean` | `void` |
130
- | `downloadDir` | `path` | `Binary` |
131
- | `exists` | `path` | `SftpExistsResult` |
132
- | `get` | `filepath` | `Binary` |
133
- | `list` | `path` | `SftpFileInfo[]` |
134
- | `mkdir` | `path`, `recursive?: boolean` | `void` |
135
- | `put` | `filepath`, `content: Binary` | `void` |
136
- | `realPath` | `path` | `string` |
137
- | `rename` | `srcPath`, `destPath` | `void` |
138
- | `rmdir` | `path`, `recursive?: boolean` | `void` |
139
- | `stat` | `path` | `SftpStat` |
140
-
141
- **Flags:**
142
-
143
- - **`delete(path, true)`** — `noErrorOK`: suppress error when file missing.
144
- - **`mkdir(path, true)`** — `recursive`: create intermediate directories.
145
- - **`rmdir(path, true)`** — `recursive`: remove directory tree.
146
-
147
- **`exists`:** `false` if not found; otherwise `'d'` (directory), `'-'` (file), `'l'` (symlink).
148
-
149
- ### Example workflow usage
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.
150
23
 
151
24
  ```typescript
152
25
  // src/constants/environments.ts
@@ -158,170 +31,71 @@ export const environments = EnvDesign({
158
31
  ```
159
32
 
160
33
  ```typescript
161
- import { Workflow } from '@t4h.framework/core'
162
34
  import { Sftp } from '@t4h.framework/sftp'
163
35
 
164
36
  import { environments } from '../constants/environments.js'
165
37
 
166
38
  const sftp = new Sftp(environments.SFTP_URL)
167
39
 
168
- export const ingestRemoteFile = new Workflow({ id: 'ingest-remote-file' }, async () => {
169
- const remotePath = '/incoming/report.csv'
170
-
171
- if ((await sftp.exists(remotePath)) === false) {
172
- return { ok: false, reason: 'missing' }
173
- }
40
+ for (const entry of await sftp.list('/inbound')) {
41
+ if (entry.type !== '-') continue
174
42
 
175
- const file = await sftp.get(remotePath)
176
- await sftp.rename(remotePath, '/processed/report.csv')
177
-
178
- return { ok: true, size: file /* use Binary APIs from core */ }
179
- })
180
- ```
181
-
182
- ---
183
-
184
- ## Using SFTP in a workflow via `History.reconciler`
185
-
186
- Prefer the **`Sftp`** client. When you need the exported activity directly,
187
- drive the **shipped** `Sftp*Activity` class through `History.reconciler` — this
188
- is a ready, replay-safe helper.
189
- `@t4h.framework/core/no-framework-activity-in-app` rejects `new` and `extends`
190
- on these classes; pass them as they are.
191
-
192
- ```typescript
193
- import { History } from '@t4h.framework/core'
194
- import { Sftp, SftpGetActivity } from '@t4h.framework/sftp'
195
-
196
- const sftp = new Sftp(environments.SFTP_URL) // Secret
197
- const content = await History.reconciler(SftpGetActivity, sftp, '/remote/file.txt')
198
- ```
199
-
200
- Activity ↔ client mapping:
201
-
202
- | Activity | Client method |
203
- |----------|---------------|
204
- | `SftpAppendActivity` | `append` |
205
- | `SftpChmodActivity` | `chmod` |
206
- | `SftpCopyActivity` | `copy` |
207
- | `SftpCwdActivity` | `cwd` |
208
- | `SftpDeleteActivity` | `delete` |
209
- | `SftpDownloadDirActivity` | `downloadDir` |
210
- | `SftpExistsActivity` | `exists` |
211
- | `SftpGetActivity` | `get` |
212
- | `SftpListActivity` | `list` |
213
- | `SftpMkdirActivity` | `mkdir` |
214
- | `SftpPutActivity` | `put` |
215
- | `SftpRealPathActivity` | `realPath` |
216
- | `SftpRenameActivity` | `rename` |
217
- | `SftpRmdirActivity` | `rmdir` |
218
- | `SftpStatActivity` | `stat` |
219
-
220
- ---
221
-
222
- ## Activity input shapes
223
-
224
- All inputs include **`connectionString: Secret`** (the name of the env entry; the value is read on the host). Path field names differ by operation:
225
-
226
- | Input type | Fields (beyond `connectionString`) |
227
- |------------|-----------------------------------|
228
- | `SftpAppendActivityInput` | `filepath`, `content: Binary` |
229
- | `SftpChmodActivityInput` | `path`, `mode: string \| number` |
230
- | `SftpCopyActivityInput` | `srcPath`, `destPath` |
231
- | `SftpCwdActivityInput` | _(none)_ |
232
- | `SftpDeleteActivityInput` | `path`, `noErrorOK?: boolean` |
233
- | `SftpDownloadDirActivityInput` | `path` |
234
- | `SftpExistsActivityInput` | `path` |
235
- | `SftpGetActivityInput` | `filepath` |
236
- | `SftpListActivityInput` | `path` |
237
- | `SftpMkdirActivityInput` | `path`, `recursive?: boolean` |
238
- | `SftpPutActivityInput` | `filepath`, `content: Binary` |
239
- | `SftpRealPathActivityInput` | `path` |
240
- | `SftpRenameActivityInput` | `srcPath`, `destPath` |
241
- | `SftpRmdirActivityInput` | `path`, `recursive?: boolean` |
242
- | `SftpStatActivityInput` | `path` |
243
-
244
- ---
245
-
246
- ## Result types
247
-
248
- ### `SftpExistsResult`
249
-
250
- ```typescript
251
- type SftpExistsResult = false | 'd' | '-' | 'l'
252
- ```
253
-
254
- ### `SftpFileInfo` (`list`)
255
-
256
- ```typescript
257
- type SftpFileInfo = {
258
- name: string
259
- type: 'd' | '-' | 'l'
260
- size: number
261
- modifyTime: number
262
- accessTime: number
263
- rights: { user: string; group: string; other: string }
264
- owner: number
265
- group: number
266
- longname: string
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)
267
46
  }
268
47
  ```
269
48
 
270
- ### `SftpStat` (`stat`)
271
-
272
- ```typescript
273
- type SftpStat = {
274
- mode: number
275
- uid: number
276
- gid: number
277
- size: number
278
- accessTime: number
279
- modifyTime: number
280
- isDirectory: boolean
281
- isFile: boolean
282
- isBlockDevice: boolean
283
- isCharacterDevice: boolean
284
- isSymbolicLink: boolean
285
- isFIFO: boolean
286
- isSocket: boolean
287
- }
288
- ```
289
-
290
- ---
291
-
292
- ## Testing
293
-
294
- Test the **workflow** with `TestWorkflowEnvironment` from
295
- `@t4h.framework/core/testing`, mocking `SftpClaim` — the single claim every
296
- `Sftp*Activity` resolves. Provide only the methods your workflow triggers
297
- (`get`, `put`, `delete`, …). See the **framework-workflow-testing** skill for the
298
- harness API.
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.
299
79
 
300
80
  ```typescript
301
81
  import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
302
82
  import { SftpClaim } from '@t4h.framework/sftp'
303
83
 
304
- const mockGet = vi.fn().mockImplementation(async () => someBinary)
84
+ const get = vi.fn().mockResolvedValue(binary)
305
85
 
306
86
  const env = TestWorkflowEnvironment.create({ now: 0 })
307
87
 
308
- const output = await env.execute(workflow, input, {
309
- claims: [mockClaim(SftpClaim, { get: mockGet })],
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' },
310
91
  })
311
92
 
312
- // each SftpClaim method receives the activity's serializable input
313
- expect(mockGet.mock.calls[0][0]).toMatchObject({ filepath: '/remote/file.txt' })
93
+ expect(get.mock.calls[0][0]).toMatchObject({ filepath: '/inbound/a.csv' })
314
94
  ```
315
95
 
316
- ---
317
-
318
- ## Checklist for agents
319
-
320
- 1. Prefer **`new Sftp(connectionString)`** in workflow code — not raw SSH/SFTP libraries.
321
- 2. Never perform non-replayable SFTP I/O inside workflows — use **`Sftp`** or **`History.reconciler(Sftp*Activity, ...)`**.
322
- 3. Ensure runtime provides **`SftpClaim`** (or pass a mock in tests).
323
- 4. Use **`exists`** before destructive ops when idempotency matters; use **`delete(..., true)`** for tolerant deletes.
324
- 5. Use **`mkdir(..., true)`** / **`rmdir(..., true)`** only when recursive tree ops are intended.
325
- 6. Test workflows with **`TestWorkflowEnvironment`** + **`mockClaim(SftpClaim, ...)`**; assert on the mock's `mock.calls`.
96
+ ## Reference
326
97
 
327
- See also: `packages/sftp/README.md`; **framework-workflow-testing** for the test harness.
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,15 @@
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
+
3
13
  ## 0.8.6
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -1,25 +1,20 @@
1
1
  # @t4h.framework/sftp
2
2
 
3
- SFTP activities, claim, and a high-level client for file operations within workflows.
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
- ## Claim
6
+ ## Install
6
7
 
7
- ### SftpClaim
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() }
8
+ ```bash
9
+ npm install @t4h.framework/sftp
16
10
  ```
17
11
 
18
- ## Sftp
12
+ Peer dependency: `@t4h.framework/core`.
19
13
 
20
- High-level SFTP client that wraps every activity behind a simple API. Instantiated with a connection string.
14
+ ## Usage
21
15
 
22
- The connection string `sftp://user:pass@host:22` carries the password, so the constructor takes a `Secret` and `tsc` rejects a string. Declare it with `type: Secret` in the `EnvDesign` in `src/constants/environments.ts`:
16
+ The connection string carries the password, so declare it with
17
+ `type: Secret` in the `EnvDesign` in `src/constants/environments.ts`:
23
18
 
24
19
  ```typescript
25
20
  // src/constants/environments.ts
@@ -31,168 +26,21 @@ export const environments = EnvDesign({
31
26
  ```
32
27
 
33
28
  ```typescript
29
+ import { Workflow } from '@t4h.framework/core'
34
30
  import { Sftp } from '@t4h.framework/sftp'
35
31
 
36
32
  import { environments } from './constants/environments.js'
37
33
 
38
34
  const sftp = new Sftp(environments.SFTP_URL)
39
- ```
40
-
41
- ### append
42
-
43
- Appends content to an existing remote file.
44
-
45
- ```typescript
46
- await sftp.append('/remote/path/file.txt', myBinaryContent) // void
47
- ```
48
-
49
- ### chmod
50
-
51
- Changes the permissions of a remote file or directory.
52
-
53
- ```typescript
54
- await sftp.chmod('/remote/path/file.txt', 0o755) // void
55
- ```
56
-
57
- ### copy
58
-
59
- Copies a remote file from one path to another.
60
-
61
- ```typescript
62
- await sftp.copy('/remote/src.txt', '/remote/dest.txt') // void
63
- ```
64
-
65
- ### cwd
66
-
67
- Returns the current working directory on the remote server.
68
-
69
- ```typescript
70
- const dir = await sftp.cwd() // string
71
- ```
72
-
73
- ### delete
74
-
75
- Deletes a remote file. Pass `true` as the second argument to suppress errors when the file does not exist.
76
-
77
- ```typescript
78
- await sftp.delete('/remote/path/file.txt') // void
79
- await sftp.delete('/remote/path/file.txt', true) // void, no error if missing
80
- ```
81
-
82
- ### downloadDir
83
-
84
- Downloads a remote directory as binary content.
85
-
86
- ```typescript
87
- const content = await sftp.downloadDir('/remote/path') // Binary
88
- ```
89
-
90
- ### exists
91
-
92
- Checks whether a remote path exists. Returns `false` if not found, or its type character (`'d'` directory, `'-'` file, `'l'` symlink).
93
-
94
- ```typescript
95
- const result = await sftp.exists('/remote/path') // false | 'd' | '-' | 'l'
96
- ```
97
-
98
- ### get
99
-
100
- Downloads a single remote file.
101
35
 
102
- ```typescript
103
- const content = await sftp.get('/remote/path/file.txt') // Binary
104
- ```
105
-
106
- ### list
107
-
108
- Lists the contents of a remote directory.
109
-
110
- ```typescript
111
- const files = await sftp.list('/remote/path') // SftpFileInfo[]
112
- ```
113
-
114
- `SftpFileInfo`:
115
-
116
- ```typescript
117
- type SftpFileInfo = {
118
- name: string
119
- type: 'd' | '-' | 'l'
120
- size: number
121
- modifyTime: number
122
- accessTime: number
123
- rights: { user: string; group: string; other: string }
124
- owner: number
125
- group: number
126
- longname: string
127
- }
128
- ```
129
-
130
- ### mkdir
131
-
132
- Creates a remote directory. Pass `true` as the second argument to create intermediate directories recursively.
133
-
134
- ```typescript
135
- await sftp.mkdir('/remote/path/new-dir') // void
136
- await sftp.mkdir('/remote/path/a/b/c', true) // void, recursive
137
- ```
138
-
139
- ### put
140
-
141
- Uploads content to a remote file.
142
-
143
- ```typescript
144
- await sftp.put('/remote/path/file.txt', myBinaryContent) // void
145
- ```
36
+ const ingest = new Workflow({ id: 'ingest' }, async () => {
37
+ const file = await sftp.get('/inbound/orders.csv')
146
38
 
147
- ### realPath
148
-
149
- Resolves the absolute path of a remote path (expanding symlinks, `.`, `..`, etc.).
150
-
151
- ```typescript
152
- const abs = await sftp.realPath('/remote/./path/../file.txt') // string
153
- ```
154
-
155
- ### rename
156
-
157
- Renames (moves) a remote file or directory.
158
-
159
- ```typescript
160
- await sftp.rename('/remote/old-name.txt', '/remote/new-name.txt') // void
161
- ```
162
-
163
- ### rmdir
164
-
165
- Removes a remote directory. Pass `true` as the second argument to remove recursively.
166
-
167
- ```typescript
168
- await sftp.rmdir('/remote/path/empty-dir') // void
169
- await sftp.rmdir('/remote/path/full-dir', true) // void, recursive
170
- ```
171
-
172
- ### stat
173
-
174
- Returns detailed metadata about a remote path.
175
-
176
- ```typescript
177
- 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
+ })
178
42
  ```
179
43
 
180
- `SftpStat`:
44
+ ## Documentation
181
45
 
182
- ```typescript
183
- type SftpStat = {
184
- mode: number
185
- uid: number
186
- gid: number
187
- size: number
188
- accessTime: number
189
- modifyTime: number
190
- isDirectory: boolean
191
- isFile: boolean
192
- isBlockDevice: boolean
193
- isCharacterDevice: boolean
194
- isSymbolicLink: boolean
195
- isFIFO: boolean
196
- isSocket: boolean
197
- }
198
- ```
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
- protected constructor(connectionString: Secret, filepath: string, contentType: string, contentLength: number, stream: (this: SftpBinary) => Readable | Promise<Readable>);
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
- filepath: string;
20
- contentType: string;
21
- contentLength: number;
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,UAAU,cAAc;IACtB,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;IAL5E,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;WAK9D,IAAI,CAChB,gBAAgB,EAAE,MAAM,EACxB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAarD,MAAM;;;;;;;;CAUd"}
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
- constructor(connectionString, filepath, contentType, contentLength, stream) {
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
- return {
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;IALlB,YACkB,gBAAwB,EACxB,QAAgB,EAChB,WAAmB,EACnB,aAAqB,EACrB,MAA0D;QAE1E,KAAK,EAAE,CAAA;QANS,qBAAgB,GAAhB,gBAAgB,CAAQ;QACxB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,gBAAW,GAAX,WAAW,CAAQ;QACnB,kBAAa,GAAb,aAAa,CAAQ;QACrB,WAAM,GAAN,MAAM,CAAoD;IAG5E,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,MAAM;QACX,OAAO;YACL,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;IACH,CAAC;CACF"}
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.8.6",
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",
@@ -33,7 +33,7 @@
33
33
  "test:watch": "vitest"
34
34
  },
35
35
  "devDependencies": {
36
- "@t4h.framework/core": "^0.20.0",
36
+ "@t4h.framework/core": "^0.21.0",
37
37
  "typescript": "^5.9.3",
38
38
  "vitest": "^4.0.18"
39
39
  },