@fetchkit/ffetch 5.1.1 → 5.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/README.md +4 -1
- package/dist/plugins/download-progress.cjs +59 -0
- package/dist/plugins/download-progress.cjs.map +1 -0
- package/dist/plugins/download-progress.d.cts +11 -0
- package/dist/plugins/download-progress.d.ts +11 -0
- package/dist/plugins/download-progress.js +34 -0
- package/dist/plugins/download-progress.js.map +1 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -67,6 +67,7 @@ ffetch uses a plugin architecture for optional features, so you only include wha
|
|
|
67
67
|
- **Deduplication plugin (optional, prebuilt)** – automatic deduping of in-flight identical requests
|
|
68
68
|
- **Request shortcuts plugin (optional, prebuilt)** – call `client.get(url)` / `.post()` / `.put()` / `.patch()` / `.delete()` directly on the client
|
|
69
69
|
- **Response shortcuts plugin (optional, prebuilt)** – call `client(url).json()` / `.text()` / `.blob()` directly on the request promise
|
|
70
|
+
- **Download progress plugin (optional, prebuilt)** – stream download progress callbacks with bytes transferred and percentage
|
|
70
71
|
|
|
71
72
|
**Built-in error classes:** `TimeoutError`, `RetryLimitError`, `CircuitOpenError`, `HttpError`, `NetworkError`, `AbortError`
|
|
72
73
|
|
|
@@ -78,6 +79,7 @@ All plugins are tree-shakeable — import only what you use.
|
|
|
78
79
|
- **circuitPlugin (optional)**: fail fast after repeated failures.
|
|
79
80
|
- **requestShortcutsPlugin (optional)**: HTTP method shortcuts on the client (`.get()` / `.post()` / `.put()` / `.patch()` / `.delete()` / `.head()` / `.options()`).
|
|
80
81
|
- **responseShortcutsPlugin (optional)**: use `client(url).json()` / `.text()` / `.blob()` style parsing.
|
|
82
|
+
- **downloadProgressPlugin (optional)**: stream download progress via `onProgress(progress, chunk)` callback.
|
|
81
83
|
|
|
82
84
|
## What Problems Does ffetch Solve?
|
|
83
85
|
|
|
@@ -349,9 +351,10 @@ See [deduplication.md](./docs/deduplication.md) for full details.
|
|
|
349
351
|
| Hooks/Middleware | ❌ Not available | ✅ Interceptors | ✅ Hooks | ✅ Comprehensive lifecycle hooks |
|
|
350
352
|
| Bundle Size | ✅ Native (0kb) | ❌ ~13kb minified | ✅ Lightweight (fetch-based) | ✅ ~3kb minified |
|
|
351
353
|
| Modern APIs | ✅ Web standards | ❌ XMLHttpRequest | ✅ Fetch + modern APIs | ✅ Fetch + modern features |
|
|
354
|
+
| Download Progress | ❌ Manual ReadableStream | ❌ Manual | ✅ `onDownloadProgress` callback | ✅ Optional via `downloadProgressPlugin()` |
|
|
352
355
|
| Custom Fetch Support | ❌ No (global only) | ❌ No | ❌ No | ✅ Yes (wrap any fetch-compatible implementation, including framework or custom fetch) |
|
|
353
356
|
|
|
354
|
-
Note: built-in plugins in ffetch are opt-in. Use `dedupePlugin()` for deduplication, `circuitPlugin()` for circuit breaking, `requestShortcutsPlugin()` for client HTTP method shortcuts,
|
|
357
|
+
Note: built-in plugins in ffetch are opt-in. Use `dedupePlugin()` for deduplication, `circuitPlugin()` for circuit breaking, `requestShortcutsPlugin()` for client HTTP method shortcuts, `responseShortcutsPlugin()` for request-promise parsing shortcuts, and `downloadProgressPlugin()` for streaming download progress. Bundle size: ~3kb core, additional optional plugin imports are tree-shakeable.
|
|
355
358
|
|
|
356
359
|
### Try ffetch in Action
|
|
357
360
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/plugins/download-progress.ts
|
|
21
|
+
var download_progress_exports = {};
|
|
22
|
+
__export(download_progress_exports, {
|
|
23
|
+
downloadProgressPlugin: () => downloadProgressPlugin
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(download_progress_exports);
|
|
26
|
+
function downloadProgressPlugin(onProgress) {
|
|
27
|
+
return {
|
|
28
|
+
name: "downloadProgress",
|
|
29
|
+
wrapDispatch: (next) => async (ctx) => {
|
|
30
|
+
const response = await next(ctx);
|
|
31
|
+
if (!response.body) {
|
|
32
|
+
return response;
|
|
33
|
+
}
|
|
34
|
+
const contentLength = response.headers.get("content-length");
|
|
35
|
+
const totalBytes = contentLength ? parseInt(contentLength, 10) : 0;
|
|
36
|
+
let transferredBytes = 0;
|
|
37
|
+
const stream = response.body.pipeThrough(
|
|
38
|
+
new TransformStream({
|
|
39
|
+
transform(chunk, controller) {
|
|
40
|
+
transferredBytes += chunk.byteLength;
|
|
41
|
+
const percent = totalBytes > 0 ? transferredBytes / totalBytes : 0;
|
|
42
|
+
onProgress({ percent, transferredBytes, totalBytes }, chunk);
|
|
43
|
+
controller.enqueue(chunk);
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
return new Response(stream, {
|
|
48
|
+
status: response.status,
|
|
49
|
+
statusText: response.statusText,
|
|
50
|
+
headers: response.headers
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
56
|
+
0 && (module.exports = {
|
|
57
|
+
downloadProgressPlugin
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=download-progress.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/download-progress.ts"],"sourcesContent":["import type { ClientPlugin } from '../plugins.js'\n\nexport type DownloadProgressEvent = {\n percent: number\n transferredBytes: number\n totalBytes: number\n}\n\nexport type DownloadProgressCallback = (\n progress: DownloadProgressEvent,\n chunk: Uint8Array\n) => void\n\nexport function downloadProgressPlugin(\n onProgress: DownloadProgressCallback\n): ClientPlugin {\n return {\n name: 'downloadProgress',\n wrapDispatch: (next) => async (ctx) => {\n const response = await next(ctx)\n\n if (!response.body) {\n return response\n }\n\n const contentLength = response.headers.get('content-length')\n const totalBytes = contentLength ? parseInt(contentLength, 10) : 0\n let transferredBytes = 0\n\n const stream = response.body.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n transferredBytes += chunk.byteLength\n const percent = totalBytes > 0 ? transferredBytes / totalBytes : 0\n onProgress({ percent, transferredBytes, totalBytes }, chunk)\n controller.enqueue(chunk)\n },\n })\n )\n\n return new Response(stream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n })\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAaO,SAAS,uBACd,YACc;AACd,SAAO;AAAA,IACL,MAAM;AAAA,IACN,cAAc,CAAC,SAAS,OAAO,QAAQ;AACrC,YAAM,WAAW,MAAM,KAAK,GAAG;AAE/B,UAAI,CAAC,SAAS,MAAM;AAClB,eAAO;AAAA,MACT;AAEA,YAAM,gBAAgB,SAAS,QAAQ,IAAI,gBAAgB;AAC3D,YAAM,aAAa,gBAAgB,SAAS,eAAe,EAAE,IAAI;AACjE,UAAI,mBAAmB;AAEvB,YAAM,SAAS,SAAS,KAAK;AAAA,QAC3B,IAAI,gBAAwC;AAAA,UAC1C,UAAU,OAAO,YAAY;AAC3B,gCAAoB,MAAM;AAC1B,kBAAM,UAAU,aAAa,IAAI,mBAAmB,aAAa;AACjE,uBAAW,EAAE,SAAS,kBAAkB,WAAW,GAAG,KAAK;AAC3D,uBAAW,QAAQ,KAAK;AAAA,UAC1B;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO,IAAI,SAAS,QAAQ;AAAA,QAC1B,QAAQ,SAAS;AAAA,QACjB,YAAY,SAAS;AAAA,QACrB,SAAS,SAAS;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { C as ClientPlugin } from '../plugins-DqBQVM4I.cjs';
|
|
2
|
+
|
|
3
|
+
type DownloadProgressEvent = {
|
|
4
|
+
percent: number;
|
|
5
|
+
transferredBytes: number;
|
|
6
|
+
totalBytes: number;
|
|
7
|
+
};
|
|
8
|
+
type DownloadProgressCallback = (progress: DownloadProgressEvent, chunk: Uint8Array) => void;
|
|
9
|
+
declare function downloadProgressPlugin(onProgress: DownloadProgressCallback): ClientPlugin;
|
|
10
|
+
|
|
11
|
+
export { type DownloadProgressCallback, type DownloadProgressEvent, downloadProgressPlugin };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { C as ClientPlugin } from '../plugins-DqBQVM4I.js';
|
|
2
|
+
|
|
3
|
+
type DownloadProgressEvent = {
|
|
4
|
+
percent: number;
|
|
5
|
+
transferredBytes: number;
|
|
6
|
+
totalBytes: number;
|
|
7
|
+
};
|
|
8
|
+
type DownloadProgressCallback = (progress: DownloadProgressEvent, chunk: Uint8Array) => void;
|
|
9
|
+
declare function downloadProgressPlugin(onProgress: DownloadProgressCallback): ClientPlugin;
|
|
10
|
+
|
|
11
|
+
export { type DownloadProgressCallback, type DownloadProgressEvent, downloadProgressPlugin };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/plugins/download-progress.ts
|
|
2
|
+
function downloadProgressPlugin(onProgress) {
|
|
3
|
+
return {
|
|
4
|
+
name: "downloadProgress",
|
|
5
|
+
wrapDispatch: (next) => async (ctx) => {
|
|
6
|
+
const response = await next(ctx);
|
|
7
|
+
if (!response.body) {
|
|
8
|
+
return response;
|
|
9
|
+
}
|
|
10
|
+
const contentLength = response.headers.get("content-length");
|
|
11
|
+
const totalBytes = contentLength ? parseInt(contentLength, 10) : 0;
|
|
12
|
+
let transferredBytes = 0;
|
|
13
|
+
const stream = response.body.pipeThrough(
|
|
14
|
+
new TransformStream({
|
|
15
|
+
transform(chunk, controller) {
|
|
16
|
+
transferredBytes += chunk.byteLength;
|
|
17
|
+
const percent = totalBytes > 0 ? transferredBytes / totalBytes : 0;
|
|
18
|
+
onProgress({ percent, transferredBytes, totalBytes }, chunk);
|
|
19
|
+
controller.enqueue(chunk);
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
return new Response(stream, {
|
|
24
|
+
status: response.status,
|
|
25
|
+
statusText: response.statusText,
|
|
26
|
+
headers: response.headers
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
downloadProgressPlugin
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=download-progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugins/download-progress.ts"],"sourcesContent":["import type { ClientPlugin } from '../plugins.js'\n\nexport type DownloadProgressEvent = {\n percent: number\n transferredBytes: number\n totalBytes: number\n}\n\nexport type DownloadProgressCallback = (\n progress: DownloadProgressEvent,\n chunk: Uint8Array\n) => void\n\nexport function downloadProgressPlugin(\n onProgress: DownloadProgressCallback\n): ClientPlugin {\n return {\n name: 'downloadProgress',\n wrapDispatch: (next) => async (ctx) => {\n const response = await next(ctx)\n\n if (!response.body) {\n return response\n }\n\n const contentLength = response.headers.get('content-length')\n const totalBytes = contentLength ? parseInt(contentLength, 10) : 0\n let transferredBytes = 0\n\n const stream = response.body.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n transferredBytes += chunk.byteLength\n const percent = totalBytes > 0 ? transferredBytes / totalBytes : 0\n onProgress({ percent, transferredBytes, totalBytes }, chunk)\n controller.enqueue(chunk)\n },\n })\n )\n\n return new Response(stream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n })\n },\n }\n}\n"],"mappings":";AAaO,SAAS,uBACd,YACc;AACd,SAAO;AAAA,IACL,MAAM;AAAA,IACN,cAAc,CAAC,SAAS,OAAO,QAAQ;AACrC,YAAM,WAAW,MAAM,KAAK,GAAG;AAE/B,UAAI,CAAC,SAAS,MAAM;AAClB,eAAO;AAAA,MACT;AAEA,YAAM,gBAAgB,SAAS,QAAQ,IAAI,gBAAgB;AAC3D,YAAM,aAAa,gBAAgB,SAAS,eAAe,EAAE,IAAI;AACjE,UAAI,mBAAmB;AAEvB,YAAM,SAAS,SAAS,KAAK;AAAA,QAC3B,IAAI,gBAAwC;AAAA,UAC1C,UAAU,OAAO,YAAY;AAC3B,gCAAoB,MAAM;AAC1B,kBAAM,UAAU,aAAa,IAAI,mBAAmB,aAAa;AACjE,uBAAW,EAAE,SAAS,kBAAkB,WAAW,GAAG,KAAK;AAC3D,uBAAW,QAAQ,KAAK;AAAA,UAC1B;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO,IAAI,SAAS,QAAQ;AAAA,QAC1B,QAAQ,SAAS;AAAA,QACjB,YAAY,SAAS;AAAA,QACrB,SAAS,SAAS;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fetchkit/ffetch",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.1",
|
|
4
4
|
"description": "Fetch wrapper with configurable timeouts, retries, and TypeScript-first DX",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fetch",
|
|
@@ -46,6 +46,11 @@
|
|
|
46
46
|
"types": "./dist/plugins/request-shortcuts.d.ts",
|
|
47
47
|
"import": "./dist/plugins/request-shortcuts.js",
|
|
48
48
|
"require": "./dist/plugins/request-shortcuts.cjs"
|
|
49
|
+
},
|
|
50
|
+
"./plugins/download-progress": {
|
|
51
|
+
"types": "./dist/plugins/download-progress.d.ts",
|
|
52
|
+
"import": "./dist/plugins/download-progress.js",
|
|
53
|
+
"require": "./dist/plugins/download-progress.cjs"
|
|
49
54
|
}
|
|
50
55
|
},
|
|
51
56
|
"files": [
|