sfiledl 2.1.1 → 2.2.1
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/build/lib.cjs +680 -116
- package/build/lib.cjs.map +1 -1
- package/build/lib.d.ts +113 -13
- package/build/lib.mjs +662 -116
- package/build/lib.mjs.map +1 -1
- package/changelog +108 -0
- package/package.json +10 -8
- package/readme.md +161 -54
package/readme.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# sfiledl
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Automate file downloads from [sfile.co](https://sfile.co/) — reliable, retry‑aware, and fully typed.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/sfiledl)
|
|
6
6
|
[](https://github.com/neuxdotdev/sfiledl/blob/main/license)
|
|
7
7
|
[](https://www.typescriptlang.org)
|
|
8
|
+
[](https://playwright.dev)
|
|
8
9
|
|
|
9
10
|
---
|
|
10
11
|
|
|
@@ -18,7 +19,7 @@ bun add sfiledl
|
|
|
18
19
|
npm install sfiledl
|
|
19
20
|
|
|
20
21
|
# Install Playwright browser (required)
|
|
21
|
-
bunx playwright install chromium
|
|
22
|
+
bunx playwright install chromium # or npx playwright install chromium
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
---
|
|
@@ -28,17 +29,16 @@ bunx playwright install chromium
|
|
|
28
29
|
```typescript
|
|
29
30
|
import { downloadSfile } from 'sfiledl'
|
|
30
31
|
|
|
31
|
-
const result = await downloadSfile(
|
|
32
|
-
'https://sfile.co/file/abc123', // sfile.co URL
|
|
33
|
-
'./downloads', // Save directory
|
|
34
|
-
{ headless: true, debug: false }, // Optional options
|
|
35
|
-
)
|
|
32
|
+
const result = await downloadSfile('https://sfile.co/file/abc123', './downloads')
|
|
36
33
|
|
|
37
34
|
console.log(result)
|
|
38
35
|
// {
|
|
39
|
-
// filePath: './downloads/
|
|
36
|
+
// filePath: './downloads/document.pdf',
|
|
40
37
|
// size: 1048576,
|
|
41
|
-
// method: 'direct'
|
|
38
|
+
// method: 'direct',
|
|
39
|
+
// correlationId: '550e8400-e29b-41d4-a716-446655440000',
|
|
40
|
+
// durationMs: 1234,
|
|
41
|
+
// attempts: 1
|
|
42
42
|
// }
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -48,20 +48,63 @@ console.log(result)
|
|
|
48
48
|
|
|
49
49
|
### `downloadSfile(url, saveDir, options?)`
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
| `
|
|
51
|
+
Main download function. Throws errors on failure (see [Error Handling](#error-handling)).
|
|
52
|
+
|
|
53
|
+
| Param | Type | Description |
|
|
54
|
+
| --------- | ----------------- | -------------------------------------- |
|
|
55
|
+
| `url` | `string` | sfile.co URL (must contain the domain) |
|
|
56
|
+
| `saveDir` | `string` | Directory where the file will be saved |
|
|
57
|
+
| `options` | `DownloadOptions` | Optional configuration |
|
|
58
|
+
|
|
59
|
+
**Returns:** `Promise<DownloadResult>`
|
|
60
|
+
|
|
61
|
+
### `downloadSfileSafe(url, saveDir, options?)`
|
|
62
|
+
|
|
63
|
+
Same as `downloadSfile` but never throws. Returns a `Result` object.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { downloadSfileSafe, isSuccess } from 'sfiledl'
|
|
67
|
+
|
|
68
|
+
const res = await downloadSfileSafe(url, './out')
|
|
69
|
+
if (isSuccess(res)) {
|
|
70
|
+
console.log('OK:', res.value.filePath)
|
|
71
|
+
} else {
|
|
72
|
+
console.error('Error:', res.error.message)
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `createDownloader(defaultOptions?)`
|
|
77
|
+
|
|
78
|
+
Creates a reusable downloader with preset options.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const dl = createDownloader({ headless: false, debug: true })
|
|
82
|
+
const result = await dl.download('https://sfile.co/file/xyz', './out')
|
|
83
|
+
// or safe variant:
|
|
84
|
+
const safeResult = await dl.downloadSafe(url, './out')
|
|
85
|
+
// chain new defaults:
|
|
86
|
+
const quietDl = dl.withOptions({ debug: false })
|
|
87
|
+
```
|
|
56
88
|
|
|
57
89
|
### `DownloadOptions`
|
|
58
90
|
|
|
59
91
|
```typescript
|
|
60
92
|
interface DownloadOptions {
|
|
61
|
-
headless?: boolean //
|
|
62
|
-
debug?: boolean //
|
|
63
|
-
userAgent?: string //
|
|
64
|
-
timeout?: number //
|
|
93
|
+
headless?: boolean // default: true
|
|
94
|
+
debug?: boolean // default: false
|
|
95
|
+
userAgent?: string // custom UA (default: Chrome on Windows)
|
|
96
|
+
timeout?: number // navigation & download timeout (ms) – default: 60000
|
|
97
|
+
downloadButtonTimeout?: number // wait for button timeout – default: 30000
|
|
98
|
+
retries?: number // total attempts – default: 3
|
|
99
|
+
retryDelay?: number // base delay before exponential backoff – default: 1000
|
|
100
|
+
onProgress?: (
|
|
101
|
+
percent: number,
|
|
102
|
+
total: 100,
|
|
103
|
+
meta: { stage: string; message: string; attempt?: number },
|
|
104
|
+
) => void
|
|
105
|
+
correlationId?: string // for tracing across logs
|
|
106
|
+
saveDebugArtifacts?: boolean // save screenshot/html on error – default: true
|
|
107
|
+
logFile?: string // write structured logs to file
|
|
65
108
|
}
|
|
66
109
|
```
|
|
67
110
|
|
|
@@ -69,9 +112,12 @@ interface DownloadOptions {
|
|
|
69
112
|
|
|
70
113
|
```typescript
|
|
71
114
|
interface DownloadResult {
|
|
72
|
-
filePath: string //
|
|
73
|
-
size: number //
|
|
74
|
-
method: 'direct' | 'fallback'
|
|
115
|
+
filePath: string // absolute path to saved file
|
|
116
|
+
size: number // bytes
|
|
117
|
+
method: 'direct' | 'fallback'
|
|
118
|
+
correlationId?: string
|
|
119
|
+
durationMs?: number
|
|
120
|
+
attempts?: number
|
|
75
121
|
}
|
|
76
122
|
```
|
|
77
123
|
|
|
@@ -79,89 +125,150 @@ interface DownloadResult {
|
|
|
79
125
|
|
|
80
126
|
## Error Handling
|
|
81
127
|
|
|
128
|
+
All errors extend `AppError` and include:
|
|
129
|
+
|
|
130
|
+
- `code` – unique string identifier
|
|
131
|
+
- `retryable` – `true` for network/browser issues, `false` for validation/file errors
|
|
132
|
+
- `context` – frozen object with debugging info
|
|
133
|
+
- `timestamp` – ISO string
|
|
134
|
+
- `toJSON()` – serialisable for logging
|
|
135
|
+
|
|
82
136
|
```typescript
|
|
83
|
-
import { downloadSfile,
|
|
137
|
+
import { downloadSfile, isRetryableError, NetworkError } from 'sfiledl'
|
|
84
138
|
|
|
85
139
|
try {
|
|
86
|
-
await downloadSfile(
|
|
140
|
+
await downloadSfile(url, './out')
|
|
87
141
|
} catch (err) {
|
|
88
|
-
if (err
|
|
89
|
-
console.
|
|
142
|
+
if (isRetryableError(err)) {
|
|
143
|
+
console.log('Will be retried automatically by the library')
|
|
90
144
|
}
|
|
91
|
-
if (err instanceof NetworkError
|
|
92
|
-
console.
|
|
145
|
+
if (err instanceof NetworkError) {
|
|
146
|
+
console.error('Network issue:', err.context)
|
|
93
147
|
}
|
|
94
|
-
// All errors
|
|
148
|
+
// All errors have .code, .retryable, .context
|
|
95
149
|
}
|
|
96
150
|
```
|
|
97
151
|
|
|
98
|
-
### Error
|
|
152
|
+
### Error types
|
|
99
153
|
|
|
100
|
-
|
|
|
101
|
-
| ----------------- | ------------------ | --------- |
|
|
102
|
-
| `ValidationError` | `VALIDATION_ERROR` |
|
|
103
|
-
| `NetworkError` | `NETWORK_ERROR` |
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
154
|
+
| Class | Code | Retryable | When |
|
|
155
|
+
| ----------------- | ------------------ | --------- | -------------------------------------------------------------- |
|
|
156
|
+
| `ValidationError` | `VALIDATION_ERROR` | `false` | Invalid URL, save directory, or options |
|
|
157
|
+
| `NetworkError` | `NETWORK_ERROR` | `true` | Navigation failures, missing download button, request timeouts |
|
|
158
|
+
| `BrowserError` | `BROWSER_ERROR` | `true` | Playwright launch or page initialisation errors |
|
|
159
|
+
| `FileError` | `FILE_ERROR` | `false` | Filesystem write errors, missing permissions |
|
|
106
160
|
|
|
107
161
|
---
|
|
108
162
|
|
|
109
|
-
## Debug Mode
|
|
163
|
+
## Debug Mode & Artifacts
|
|
110
164
|
|
|
111
|
-
|
|
165
|
+
Set `debug: true` in options to enable:
|
|
166
|
+
|
|
167
|
+
- Detailed console logging (including Playwright console/request failures)
|
|
168
|
+
- Automatic debug artifacts when an error occurs (saved to `/tmp/sfile_debug_<timestamp>/`):
|
|
169
|
+
- `error.png` – full page screenshot
|
|
170
|
+
- `error.html` – page source at failure
|
|
171
|
+
- `error.txt` – error message and stack
|
|
172
|
+
- `stages.json` – timeline of internal steps (launch, navigation, button wait, etc.)
|
|
112
173
|
|
|
113
174
|
```typescript
|
|
114
|
-
await downloadSfile(url, './out', { debug: true })
|
|
175
|
+
await downloadSfile(url, './out', { debug: true, saveDebugArtifacts: true })
|
|
115
176
|
```
|
|
116
177
|
|
|
117
|
-
|
|
178
|
+
---
|
|
118
179
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
180
|
+
## Logging & Correlation
|
|
181
|
+
|
|
182
|
+
The library exports its own `Logger` class. You can create a logger with a correlation ID that will be automatically attached to every log line.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { Logger } from 'sfiledl'
|
|
186
|
+
|
|
187
|
+
const logger = new Logger({
|
|
188
|
+
debugMode: true,
|
|
189
|
+
correlationId: 'my-session-123',
|
|
190
|
+
logFile: './app.log',
|
|
191
|
+
prefix: 'downloader',
|
|
192
|
+
})
|
|
193
|
+
logger.info('Starting download')
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The main download function also accepts a `correlationId` in `DownloadOptions`. That ID is used for all internal log messages and attached to the final `DownloadResult`.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Progress Tracking
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
await downloadSfile(url, './out', {
|
|
204
|
+
onProgress: (percent, total, meta) => {
|
|
205
|
+
console.log(`${meta.stage}: ${meta.message} – ${percent}%`)
|
|
206
|
+
if (meta.attempt) console.log(`Retry attempt ${meta.attempt}`)
|
|
207
|
+
},
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Stages emitted:
|
|
212
|
+
|
|
213
|
+
- `launch` (10%) – browser launching
|
|
214
|
+
- `navigation` (30%) – page loaded
|
|
215
|
+
- `button` (50%) – download button ready
|
|
216
|
+
- `trigger` (70%) – download triggered
|
|
217
|
+
- `complete` (100%) – file saved
|
|
218
|
+
- `retry` (0%) – before a retry
|
|
122
219
|
|
|
123
220
|
---
|
|
124
221
|
|
|
125
222
|
## Development
|
|
126
223
|
|
|
127
224
|
```bash
|
|
128
|
-
# Clone & install
|
|
129
225
|
git clone https://github.com/neuxdotdev/sfiledl.git
|
|
130
226
|
cd sfiledl
|
|
131
227
|
bun install
|
|
132
228
|
bunx playwright install chromium
|
|
133
|
-
|
|
229
|
+
|
|
230
|
+
# Full rebuild (clean, typecheck, build, format)
|
|
134
231
|
bun run rebuild
|
|
135
232
|
|
|
136
|
-
# Run tests
|
|
233
|
+
# Run tests (if available)
|
|
137
234
|
bun run test
|
|
138
235
|
```
|
|
139
236
|
|
|
237
|
+
|
|
238
|
+
### Scripts
|
|
239
|
+
|
|
240
|
+
| Command | Description |
|
|
241
|
+
| ---------------------- | ------------------------------ |
|
|
242
|
+
| `bun run clean` | Remove `build/` and cache |
|
|
243
|
+
| `bun run typecheck` | Run `tsc --noEmit` |
|
|
244
|
+
| `bun run build:ts` | Compile TypeScript to `build/` |
|
|
245
|
+
| `bun run build:bundle` | Bundle with Rollup |
|
|
246
|
+
| `bun run rebuild` | Full rebuild pipeline |
|
|
247
|
+
| `bun run format` | Format all files with Prettier |
|
|
248
|
+
|
|
140
249
|
---
|
|
141
250
|
|
|
142
251
|
## Contributing
|
|
143
252
|
|
|
144
253
|
1. Fork the repository
|
|
145
254
|
2. Create a feature branch (`git checkout -b feat/your-feature`)
|
|
146
|
-
3. Commit changes
|
|
147
|
-
4. Push
|
|
148
|
-
5. Open a Pull Request
|
|
255
|
+
3. Commit changes using [Conventional Commits](https://www.conventionalcommits.org/)
|
|
256
|
+
4. Push and open a Pull Request
|
|
149
257
|
|
|
150
258
|
---
|
|
151
259
|
|
|
152
260
|
## License
|
|
153
261
|
|
|
154
|
-
**AGPL-3.0-only**
|
|
155
|
-
|
|
156
|
-
> This license ensures that any network-distributed modifications remain open source.
|
|
262
|
+
**AGPL-3.0-only** – see [LICENSE](license) for details.
|
|
263
|
+
This license ensures that any network‑distributed modifications remain open source.
|
|
157
264
|
|
|
158
265
|
---
|
|
159
266
|
|
|
160
267
|
## Credits
|
|
161
268
|
|
|
162
|
-
- Built with [Playwright](https://playwright.dev) for reliable automation
|
|
163
|
-
-
|
|
164
|
-
- Inspired by the need for simple, scriptable file downloads
|
|
269
|
+
- Built with [Playwright](https://playwright.dev) for reliable browser automation
|
|
270
|
+
- Optimised for [Bun](https://bun.sh) runtime performance
|
|
271
|
+
- Inspired by the need for simple, scriptable file downloads from sfile.co
|
|
165
272
|
|
|
166
273
|
---
|
|
167
274
|
|