sfiledl 2.2.0 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/readme.md +161 -54
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sfiledl",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Implement and automate downloading of any file from sfile.co with javascripts library, written entirely in typescripts",
5
5
  "type": "module",
6
6
  "main": "./build/lib.cjs",
package/readme.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # sfiledl
2
2
 
3
- > Implement and automate downloading of any file from https://sfile.co/ with javascripts library, written entirely in typescripts
3
+ > Automate file downloads from [sfile.co](https://sfile.co/) reliable, retry‑aware, and fully typed.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/sfiledl)](https://www.npmjs.com/package/sfiledl)
6
6
  [![License](https://img.shields.io/npm/l/sfiledl)](https://github.com/neuxdotdev/sfiledl/blob/main/license)
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue)](https://www.typescriptlang.org)
8
+ [![Playwright](https://img.shields.io/badge/Playwright-1.40-green)](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/file.zip',
36
+ // filePath: './downloads/document.pdf',
40
37
  // size: 1048576,
41
- // method: 'direct' | 'fallback'
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
- | Parameter | Type | Required | Description |
52
- | --------- | ----------------- | -------- | -------------------------- |
53
- | `url` | `string` | | sfile.co URL to download |
54
- | `saveDir` | `string` | | Directory to save the file |
55
- | `options` | `DownloadOptions` | | Optional configuration |
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 // Run browser headless (default: true)
62
- debug?: boolean // Enable debug logging (default: false)
63
- userAgent?: string // Custom user agent string
64
- timeout?: number // Operation timeout in ms (default: 100000)
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 // Full path to saved file
73
- size: number // File size in bytes
74
- method: 'direct' | 'fallback' // Download strategy used
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, ValidationError, NetworkError } from 'sfiledl'
137
+ import { downloadSfile, isRetryableError, NetworkError } from 'sfiledl'
84
138
 
85
139
  try {
86
- await downloadSfile('https://sfile.co/file/xyz', './out')
140
+ await downloadSfile(url, './out')
87
141
  } catch (err) {
88
- if (err instanceof ValidationError) {
89
- console.error('Invalid input:', err.message)
142
+ if (isRetryableError(err)) {
143
+ console.log('Will be retried automatically by the library')
90
144
  }
91
- if (err instanceof NetworkError && err.retryable) {
92
- console.log('Network issue, retry possible')
145
+ if (err instanceof NetworkError) {
146
+ console.error('Network issue:', err.context)
93
147
  }
94
- // All errors include: code, timestamp, context, retryable flag
148
+ // All errors have .code, .retryable, .context
95
149
  }
96
150
  ```
97
151
 
98
- ### Error Types
152
+ ### Error types
99
153
 
100
- | Error | Code | Retryable | When |
101
- | ----------------- | ------------------ | --------- | -------------------------- |
102
- | `ValidationError` | `VALIDATION_ERROR` | | Invalid URL or input |
103
- | `NetworkError` | `NETWORK_ERROR` | | Navigation/fetch failures |
104
- | `FileError` | `FILE_ERROR` | | File system issues |
105
- | `BrowserError` | `BROWSER_ERROR` | | Playwright launch failures |
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
- Enable `debug: true` for verbose logging:
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
- On error, debug artifacts are auto-saved to `/tmp/sfile_debug_<timestamp>/`:
178
+ ---
118
179
 
119
- - `error.png` Full page screenshot
120
- - `error.html` — Page source at failure
121
- - `error.txt` Error message
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
- # Rebuild (clean + lint + build + format)
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 (`git commit -m 'feat: add your feature'`)
147
- 4. Push to branch (`git push origin feat/your-feature`)
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** See [LICENSE](license) for details.
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
- - Optimized for [Bun](https://bun.sh) runtime performance
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