with-ease-progress 0.1.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/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # with-ease-progress
2
+
3
+ Smooth progress tracking for async tasks in JavaScript/TypeScript.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install with-ease-progress
9
+ # or
10
+ yarn add with-ease-progress
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Direct
16
+
17
+ ```typescript
18
+ import { withProgress } from "with-ease-progress";
19
+
20
+ // Simulate an async task
21
+ await withProgress(
22
+ async (update) => {
23
+ for (let i = 0; i <= 100; i += 10) {
24
+ await new Promise(r => setTimeout(r, 100));
25
+ update({ loaded: i, total: 100 });
26
+ }
27
+ },
28
+ {
29
+ minDuration: 2000,
30
+ onProgress: (p) => console.log("Progress:", p + "%"),
31
+ }
32
+ );
33
+
34
+ // Output:
35
+ // Progress: 10%
36
+ // Progress: 25%
37
+ // Progress: 40%
38
+ // ...
39
+ ```
40
+
41
+ - `update` is a function that accepts an object `{ loaded: number, total: number }`
42
+ - `onProgress` is called with the smooth progress percentage
43
+ - `minDuration` ensures the progress bar moves smoothly even if the task finishes quickly
44
+
45
+ ### Curryable (functional-friendly)
46
+
47
+ ```typescript
48
+ const uploadWithProgress = withProgress({
49
+ minDuration: 1500,
50
+ onProgress: p => console.log(p),
51
+ });
52
+
53
+ await uploadWithProgress(update => uploadFile(update));
54
+ ```
55
+
56
+ ### Features
57
+
58
+ - Works with any async task
59
+ - Smooth progress updates, prevents jumps
60
+ - Supports TypeScript and provides typings
61
+ - Functional-friendly / curryable usage
62
+ - ESM + CJS compatible
63
+
64
+ ### License
65
+
66
+ MIT
@@ -0,0 +1,14 @@
1
+ type ProgressEventLike = {
2
+ loaded?: number;
3
+ total?: number;
4
+ };
5
+ type ProgressHandler = (e: ProgressEventLike) => void;
6
+ type Task<T> = (update: ProgressHandler) => Promise<T>;
7
+ interface ProgressOptions {
8
+ minDuration?: number;
9
+ onProgress?: (percent: number) => void;
10
+ }
11
+ declare function withProgress<T>(task: Task<T>, options?: ProgressOptions): Promise<T>;
12
+ declare function withProgress(options: ProgressOptions): <T>(task: Task<T>) => Promise<T>;
13
+
14
+ export { type ProgressEventLike, type ProgressHandler, type ProgressOptions, type Task, withProgress };
@@ -0,0 +1,14 @@
1
+ type ProgressEventLike = {
2
+ loaded?: number;
3
+ total?: number;
4
+ };
5
+ type ProgressHandler = (e: ProgressEventLike) => void;
6
+ type Task<T> = (update: ProgressHandler) => Promise<T>;
7
+ interface ProgressOptions {
8
+ minDuration?: number;
9
+ onProgress?: (percent: number) => void;
10
+ }
11
+ declare function withProgress<T>(task: Task<T>, options?: ProgressOptions): Promise<T>;
12
+ declare function withProgress(options: ProgressOptions): <T>(task: Task<T>) => Promise<T>;
13
+
14
+ export { type ProgressEventLike, type ProgressHandler, type ProgressOptions, type Task, withProgress };
package/dist/index.js ADDED
@@ -0,0 +1,91 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.ts
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ withProgress: () => withProgress
23
+ });
24
+ module.exports = __toCommonJS(index_exports);
25
+ var DEFAULT_MIN_DURATION = 2e3;
26
+ function withProgress(arg1, arg2) {
27
+ if (typeof arg1 === "function") {
28
+ return runWithProgress(arg1, arg2);
29
+ }
30
+ return (task) => runWithProgress(task, arg1);
31
+ }
32
+ async function runWithProgress(task, options = {}) {
33
+ const {
34
+ minDuration = DEFAULT_MIN_DURATION,
35
+ onProgress
36
+ } = options;
37
+ const controller = onProgress ? createProgressController(minDuration, onProgress) : null;
38
+ const startTime = Date.now();
39
+ let result;
40
+ let error;
41
+ try {
42
+ result = await task((controller == null ? void 0 : controller.updateReal) ?? noop);
43
+ } catch (e) {
44
+ error = e;
45
+ }
46
+ const elapsed = Date.now() - startTime;
47
+ const remaining = minDuration - elapsed;
48
+ if (remaining > 0) {
49
+ await sleep(remaining);
50
+ }
51
+ controller == null ? void 0 : controller.stop();
52
+ if (error) throw error;
53
+ return result;
54
+ }
55
+ function createProgressController(minDuration, onProgress) {
56
+ const start = Date.now();
57
+ let lastPercent = 0;
58
+ const update = (realPercent = 0) => {
59
+ const elapsed = Date.now() - start;
60
+ const easedPercent = Math.min(
61
+ 99,
62
+ Math.round(elapsed / minDuration * 100)
63
+ );
64
+ const percent = Math.max(easedPercent, realPercent);
65
+ if (percent > lastPercent && percent < 100) {
66
+ lastPercent = percent;
67
+ onProgress(percent);
68
+ }
69
+ };
70
+ const interval = setInterval(update, 50);
71
+ return {
72
+ updateReal(event) {
73
+ const real = event.total ? Math.round(event.loaded * 100 / event.total) : 0;
74
+ update(real);
75
+ },
76
+ stop() {
77
+ clearInterval(interval);
78
+ onProgress(100);
79
+ }
80
+ };
81
+ }
82
+ function sleep(ms) {
83
+ return new Promise((resolve) => setTimeout(resolve, ms));
84
+ }
85
+ function noop() {
86
+ }
87
+ // Annotate the CommonJS export names for ESM import in node:
88
+ 0 && (module.exports = {
89
+ withProgress
90
+ });
91
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type ProgressEventLike = {\n loaded?: number;\n total?: number;\n};\n\nexport type ProgressHandler = (e: ProgressEventLike) => void;\n\nexport type Task<T> = (update: ProgressHandler) => Promise<T>;\n\nexport interface ProgressOptions {\n minDuration?: number;\n onProgress?: (percent: number) => void;\n}\n\nconst DEFAULT_MIN_DURATION = 2000;\n\nexport function withProgress<T>(\n task: Task<T>,\n options?: ProgressOptions,\n): Promise<T>;\n\nexport function withProgress(\n options: ProgressOptions,\n): <T>(task: Task<T>) => Promise<T>;\n\nexport function withProgress<T>(\n arg1: Task<T> | ProgressOptions,\n arg2?: ProgressOptions,\n) {\n if (typeof arg1 === \"function\") {\n return runWithProgress(arg1, arg2);\n }\n\n return <T>(task: Task<T>) => runWithProgress(task, arg1);\n}\n\nasync function runWithProgress<T>(\n task: Task<T>,\n options: ProgressOptions = {},\n): Promise<T> {\n const {\n minDuration = DEFAULT_MIN_DURATION,\n onProgress,\n } = options;\n\n const controller = onProgress\n ? createProgressController(minDuration, onProgress)\n : null;\n\n const startTime = Date.now();\n let result: T | undefined;\n let error: unknown;\n\n try {\n result = await task(controller?.updateReal ?? noop);\n } catch (e) {\n error = e;\n }\n\n const elapsed = Date.now() - startTime;\n const remaining = minDuration - elapsed;\n\n if (remaining > 0) {\n await sleep(remaining);\n }\n\n controller?.stop();\n\n if (error) throw error;\n return result as T;\n}\n\nfunction createProgressController(\n minDuration: number,\n onProgress: (p: number) => void,\n) {\n const start = Date.now();\n let lastPercent = 0;\n\n const update = (realPercent = 0) => {\n const elapsed = Date.now() - start;\n\n const easedPercent = Math.min(\n 99,\n Math.round((elapsed / minDuration) * 100),\n );\n\n const percent = Math.max(easedPercent, realPercent);\n\n if (percent > lastPercent && percent < 100) {\n lastPercent = percent;\n onProgress(percent);\n }\n };\n\n const interval = setInterval(update, 50);\n\n return {\n updateReal(event: ProgressEventLike) {\n const real = event.total\n ? Math.round((event.loaded! * 100) / event.total)\n : 0;\n\n update(real);\n },\n\n stop() {\n clearInterval(interval);\n onProgress(100);\n },\n };\n}\n\nfunction sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nfunction noop() { }\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAcA,IAAM,uBAAuB;AAWtB,SAAS,aACd,MACA,MACA;AACA,MAAI,OAAO,SAAS,YAAY;AAC9B,WAAO,gBAAgB,MAAM,IAAI;AAAA,EACnC;AAEA,SAAO,CAAI,SAAkB,gBAAgB,MAAM,IAAI;AACzD;AAEA,eAAe,gBACb,MACA,UAA2B,CAAC,GAChB;AACZ,QAAM;AAAA,IACJ,cAAc;AAAA,IACd;AAAA,EACF,IAAI;AAEJ,QAAM,aAAa,aACf,yBAAyB,aAAa,UAAU,IAChD;AAEJ,QAAM,YAAY,KAAK,IAAI;AAC3B,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,aAAS,MAAM,MAAK,yCAAY,eAAc,IAAI;AAAA,EACpD,SAAS,GAAG;AACV,YAAQ;AAAA,EACV;AAEA,QAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,QAAM,YAAY,cAAc;AAEhC,MAAI,YAAY,GAAG;AACjB,UAAM,MAAM,SAAS;AAAA,EACvB;AAEA,2CAAY;AAEZ,MAAI,MAAO,OAAM;AACjB,SAAO;AACT;AAEA,SAAS,yBACP,aACA,YACA;AACA,QAAM,QAAQ,KAAK,IAAI;AACvB,MAAI,cAAc;AAElB,QAAM,SAAS,CAAC,cAAc,MAAM;AAClC,UAAM,UAAU,KAAK,IAAI,IAAI;AAE7B,UAAM,eAAe,KAAK;AAAA,MACxB;AAAA,MACA,KAAK,MAAO,UAAU,cAAe,GAAG;AAAA,IAC1C;AAEA,UAAM,UAAU,KAAK,IAAI,cAAc,WAAW;AAElD,QAAI,UAAU,eAAe,UAAU,KAAK;AAC1C,oBAAc;AACd,iBAAW,OAAO;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,WAAW,YAAY,QAAQ,EAAE;AAEvC,SAAO;AAAA,IACL,WAAW,OAA0B;AACnC,YAAM,OAAO,MAAM,QACf,KAAK,MAAO,MAAM,SAAU,MAAO,MAAM,KAAK,IAC9C;AAEJ,aAAO,IAAI;AAAA,IACb;AAAA,IAEA,OAAO;AACL,oBAAc,QAAQ;AACtB,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF;AACF;AAEA,SAAS,MAAM,IAAY;AACzB,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAEA,SAAS,OAAO;AAAE;","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,67 @@
1
+ // src/index.ts
2
+ var DEFAULT_MIN_DURATION = 2e3;
3
+ function withProgress(arg1, arg2) {
4
+ if (typeof arg1 === "function") {
5
+ return runWithProgress(arg1, arg2);
6
+ }
7
+ return (task) => runWithProgress(task, arg1);
8
+ }
9
+ async function runWithProgress(task, options = {}) {
10
+ const {
11
+ minDuration = DEFAULT_MIN_DURATION,
12
+ onProgress
13
+ } = options;
14
+ const controller = onProgress ? createProgressController(minDuration, onProgress) : null;
15
+ const startTime = Date.now();
16
+ let result;
17
+ let error;
18
+ try {
19
+ result = await task((controller == null ? void 0 : controller.updateReal) ?? noop);
20
+ } catch (e) {
21
+ error = e;
22
+ }
23
+ const elapsed = Date.now() - startTime;
24
+ const remaining = minDuration - elapsed;
25
+ if (remaining > 0) {
26
+ await sleep(remaining);
27
+ }
28
+ controller == null ? void 0 : controller.stop();
29
+ if (error) throw error;
30
+ return result;
31
+ }
32
+ function createProgressController(minDuration, onProgress) {
33
+ const start = Date.now();
34
+ let lastPercent = 0;
35
+ const update = (realPercent = 0) => {
36
+ const elapsed = Date.now() - start;
37
+ const easedPercent = Math.min(
38
+ 99,
39
+ Math.round(elapsed / minDuration * 100)
40
+ );
41
+ const percent = Math.max(easedPercent, realPercent);
42
+ if (percent > lastPercent && percent < 100) {
43
+ lastPercent = percent;
44
+ onProgress(percent);
45
+ }
46
+ };
47
+ const interval = setInterval(update, 50);
48
+ return {
49
+ updateReal(event) {
50
+ const real = event.total ? Math.round(event.loaded * 100 / event.total) : 0;
51
+ update(real);
52
+ },
53
+ stop() {
54
+ clearInterval(interval);
55
+ onProgress(100);
56
+ }
57
+ };
58
+ }
59
+ function sleep(ms) {
60
+ return new Promise((resolve) => setTimeout(resolve, ms));
61
+ }
62
+ function noop() {
63
+ }
64
+ export {
65
+ withProgress
66
+ };
67
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type ProgressEventLike = {\n loaded?: number;\n total?: number;\n};\n\nexport type ProgressHandler = (e: ProgressEventLike) => void;\n\nexport type Task<T> = (update: ProgressHandler) => Promise<T>;\n\nexport interface ProgressOptions {\n minDuration?: number;\n onProgress?: (percent: number) => void;\n}\n\nconst DEFAULT_MIN_DURATION = 2000;\n\nexport function withProgress<T>(\n task: Task<T>,\n options?: ProgressOptions,\n): Promise<T>;\n\nexport function withProgress(\n options: ProgressOptions,\n): <T>(task: Task<T>) => Promise<T>;\n\nexport function withProgress<T>(\n arg1: Task<T> | ProgressOptions,\n arg2?: ProgressOptions,\n) {\n if (typeof arg1 === \"function\") {\n return runWithProgress(arg1, arg2);\n }\n\n return <T>(task: Task<T>) => runWithProgress(task, arg1);\n}\n\nasync function runWithProgress<T>(\n task: Task<T>,\n options: ProgressOptions = {},\n): Promise<T> {\n const {\n minDuration = DEFAULT_MIN_DURATION,\n onProgress,\n } = options;\n\n const controller = onProgress\n ? createProgressController(minDuration, onProgress)\n : null;\n\n const startTime = Date.now();\n let result: T | undefined;\n let error: unknown;\n\n try {\n result = await task(controller?.updateReal ?? noop);\n } catch (e) {\n error = e;\n }\n\n const elapsed = Date.now() - startTime;\n const remaining = minDuration - elapsed;\n\n if (remaining > 0) {\n await sleep(remaining);\n }\n\n controller?.stop();\n\n if (error) throw error;\n return result as T;\n}\n\nfunction createProgressController(\n minDuration: number,\n onProgress: (p: number) => void,\n) {\n const start = Date.now();\n let lastPercent = 0;\n\n const update = (realPercent = 0) => {\n const elapsed = Date.now() - start;\n\n const easedPercent = Math.min(\n 99,\n Math.round((elapsed / minDuration) * 100),\n );\n\n const percent = Math.max(easedPercent, realPercent);\n\n if (percent > lastPercent && percent < 100) {\n lastPercent = percent;\n onProgress(percent);\n }\n };\n\n const interval = setInterval(update, 50);\n\n return {\n updateReal(event: ProgressEventLike) {\n const real = event.total\n ? Math.round((event.loaded! * 100) / event.total)\n : 0;\n\n update(real);\n },\n\n stop() {\n clearInterval(interval);\n onProgress(100);\n },\n };\n}\n\nfunction sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nfunction noop() { }\n"],"mappings":";AAcA,IAAM,uBAAuB;AAWtB,SAAS,aACd,MACA,MACA;AACA,MAAI,OAAO,SAAS,YAAY;AAC9B,WAAO,gBAAgB,MAAM,IAAI;AAAA,EACnC;AAEA,SAAO,CAAI,SAAkB,gBAAgB,MAAM,IAAI;AACzD;AAEA,eAAe,gBACb,MACA,UAA2B,CAAC,GAChB;AACZ,QAAM;AAAA,IACJ,cAAc;AAAA,IACd;AAAA,EACF,IAAI;AAEJ,QAAM,aAAa,aACf,yBAAyB,aAAa,UAAU,IAChD;AAEJ,QAAM,YAAY,KAAK,IAAI;AAC3B,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,aAAS,MAAM,MAAK,yCAAY,eAAc,IAAI;AAAA,EACpD,SAAS,GAAG;AACV,YAAQ;AAAA,EACV;AAEA,QAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,QAAM,YAAY,cAAc;AAEhC,MAAI,YAAY,GAAG;AACjB,UAAM,MAAM,SAAS;AAAA,EACvB;AAEA,2CAAY;AAEZ,MAAI,MAAO,OAAM;AACjB,SAAO;AACT;AAEA,SAAS,yBACP,aACA,YACA;AACA,QAAM,QAAQ,KAAK,IAAI;AACvB,MAAI,cAAc;AAElB,QAAM,SAAS,CAAC,cAAc,MAAM;AAClC,UAAM,UAAU,KAAK,IAAI,IAAI;AAE7B,UAAM,eAAe,KAAK;AAAA,MACxB;AAAA,MACA,KAAK,MAAO,UAAU,cAAe,GAAG;AAAA,IAC1C;AAEA,UAAM,UAAU,KAAK,IAAI,cAAc,WAAW;AAElD,QAAI,UAAU,eAAe,UAAU,KAAK;AAC1C,oBAAc;AACd,iBAAW,OAAO;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,WAAW,YAAY,QAAQ,EAAE;AAEvC,SAAO;AAAA,IACL,WAAW,OAA0B;AACnC,YAAM,OAAO,MAAM,QACf,KAAK,MAAO,MAAM,SAAU,MAAO,MAAM,KAAK,IAC9C;AAEJ,aAAO,IAAI;AAAA,IACb;AAAA,IAEA,OAAO;AACL,oBAAc,QAAQ;AACtB,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF;AACF;AAEA,SAAS,MAAM,IAAY;AACzB,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAEA,SAAS,OAAO;AAAE;","names":[]}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "with-ease-progress",
3
+ "version": "0.1.0",
4
+ "description": "Smooth progress tracking for async tasks",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": ["dist"],
9
+ "scripts": {
10
+ "build": "tsup",
11
+ "dev": "tsup --watch"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/matthieupinte/with-ease-progress.git"
16
+ },
17
+ "keywords": [
18
+ "progress",
19
+ "async",
20
+ "task",
21
+ "smooth",
22
+ "ease",
23
+ "typescript",
24
+ "javascript"
25
+ ],
26
+ "author": "Matthieu Pinte",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "tsup": "^8.5.1",
30
+ "typescript": "^5.9.3"
31
+ }
32
+ }