effective-progress 0.2.2 → 0.2.4
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 +1 -1
- package/package.json +1 -1
- package/src/api.ts +28 -17
- package/src/colors.ts +2 -2
- package/src/renderer.ts +19 -7
- package/src/runtime.ts +74 -55
- package/src/types.ts +3 -1
package/README.md
CHANGED
|
@@ -212,7 +212,7 @@ Supported modifiers:
|
|
|
212
212
|
|
|
213
213
|
## Dependencies & package size
|
|
214
214
|
|
|
215
|
-
This library is designed for CLI workflows, where package size is typically a lower-priority concern. Alongside `effect
|
|
215
|
+
This library is designed for CLI workflows, where package size is typically a lower-priority concern. Alongside `effect` though, I will strive to only rely on other high quality packages.
|
|
216
216
|
|
|
217
217
|
## Notes
|
|
218
218
|
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -19,11 +19,15 @@ export interface EffectAllExecutionOptions extends EffectExecutionOptions {
|
|
|
19
19
|
|
|
20
20
|
export type AllOptions = Omit<TrackOptions, "total"> & EffectAllExecutionOptions;
|
|
21
21
|
export type AllReturn<
|
|
22
|
-
Arg extends ReadonlyArray<Effect.Effect<any, any, any>>,
|
|
22
|
+
Arg extends ReadonlyArray<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>,
|
|
23
23
|
O extends EffectAllExecutionOptions,
|
|
24
|
-
> = [
|
|
25
|
-
Effect.Effect<
|
|
26
|
-
|
|
24
|
+
> = [
|
|
25
|
+
[Arg] extends [ReadonlyArray<Effect.Effect<any, any, any>>]
|
|
26
|
+
? Effect.All.ReturnTuple<Arg, Effect.All.IsDiscard<O>, Effect.All.ExtractMode<O>>
|
|
27
|
+
: [Arg] extends [Record<string, Effect.Effect<any, any, any>>]
|
|
28
|
+
? Effect.All.ReturnObject<Arg, Effect.All.IsDiscard<O>, Effect.All.ExtractMode<O>>
|
|
29
|
+
: never,
|
|
30
|
+
] extends [Effect.Effect<infer A, infer E, infer R>]
|
|
27
31
|
? Effect.Effect<A, E, Exclude<R, Progress | Task>>
|
|
28
32
|
: never;
|
|
29
33
|
|
|
@@ -52,25 +56,30 @@ export const task: {
|
|
|
52
56
|
) as Effect.Effect<A, E, Exclude<R, Progress | Task>>,
|
|
53
57
|
);
|
|
54
58
|
|
|
59
|
+
type AllArg = ReadonlyArray<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>;
|
|
60
|
+
|
|
61
|
+
const wrapEffects = (
|
|
62
|
+
effects: AllArg,
|
|
63
|
+
tap: (effect: Effect.Effect<any, any, any>) => Effect.Effect<any, any, any>,
|
|
64
|
+
): AllArg =>
|
|
65
|
+
Array.isArray(effects)
|
|
66
|
+
? effects.map(tap)
|
|
67
|
+
: Object.fromEntries(Object.entries(effects).map(([k, effect]) => [k, tap(effect)]));
|
|
68
|
+
|
|
69
|
+
const countEffects = (effects: AllArg): number =>
|
|
70
|
+
Array.isArray(effects) ? effects.length : Object.keys(effects).length;
|
|
71
|
+
|
|
55
72
|
export const all: {
|
|
56
|
-
<
|
|
57
|
-
const Arg extends ReadonlyArray<Effect.Effect<any, any, any>>,
|
|
58
|
-
O extends EffectAllExecutionOptions,
|
|
59
|
-
>(
|
|
73
|
+
<const Arg extends AllArg, O extends EffectAllExecutionOptions>(
|
|
60
74
|
effects: Arg,
|
|
61
75
|
options: Omit<TrackOptions, "total"> & O,
|
|
62
76
|
): AllReturn<Arg, O>;
|
|
63
77
|
<O extends EffectAllExecutionOptions>(
|
|
64
78
|
options: Omit<TrackOptions, "total"> & O,
|
|
65
|
-
): <const Arg extends
|
|
66
|
-
effects: Arg,
|
|
67
|
-
) => AllReturn<Arg, O>;
|
|
79
|
+
): <const Arg extends AllArg>(effects: Arg) => AllReturn<Arg, O>;
|
|
68
80
|
} = dual(
|
|
69
81
|
2,
|
|
70
|
-
<
|
|
71
|
-
const Arg extends ReadonlyArray<Effect.Effect<any, any, any>>,
|
|
72
|
-
O extends EffectAllExecutionOptions,
|
|
73
|
-
>(
|
|
82
|
+
<const Arg extends AllArg, O extends EffectAllExecutionOptions>(
|
|
74
83
|
effects: Arg,
|
|
75
84
|
options: Omit<TrackOptions, "total"> & O,
|
|
76
85
|
) =>
|
|
@@ -82,7 +91,9 @@ export const all: {
|
|
|
82
91
|
const taskId = yield* Task;
|
|
83
92
|
const exit = yield* Effect.exit(
|
|
84
93
|
Effect.all(
|
|
85
|
-
effects
|
|
94
|
+
wrapEffects(effects, (effect) =>
|
|
95
|
+
Effect.tap(effect, () => progress.advanceTask(taskId, 1)),
|
|
96
|
+
),
|
|
86
97
|
{
|
|
87
98
|
concurrency: options.concurrency,
|
|
88
99
|
batching: options.batching,
|
|
@@ -106,7 +117,7 @@ export const all: {
|
|
|
106
117
|
}),
|
|
107
118
|
{
|
|
108
119
|
description: options.description,
|
|
109
|
-
total: effects
|
|
120
|
+
total: countEffects(effects),
|
|
110
121
|
transient: options.transient,
|
|
111
122
|
progressbar: options.progressbar,
|
|
112
123
|
},
|
package/src/colors.ts
CHANGED
|
@@ -104,10 +104,10 @@ export const defaultProgressBarColors: ProgressBarColors = {
|
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
const applyModifier = (instance: ChalkInstance, modifier: StyleModifier): ChalkInstance =>
|
|
107
|
-
instance[modifier]
|
|
107
|
+
instance[modifier];
|
|
108
108
|
|
|
109
109
|
const applyNamedColor = (instance: ChalkInstance, color: NamedColor): ChalkInstance =>
|
|
110
|
-
instance[color]
|
|
110
|
+
instance[color];
|
|
111
111
|
|
|
112
112
|
const resolveBaseStyle = (style: ColorStyle): ChalkInstance => {
|
|
113
113
|
switch (style.kind) {
|
package/src/renderer.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect, Ref, Schema } from "effect";
|
|
1
|
+
import { Clock, Duration, Effect, Ref, Schema } from "effect";
|
|
2
2
|
import {
|
|
3
3
|
type CompiledProgressBarColors,
|
|
4
4
|
compileProgressBarColors,
|
|
@@ -27,34 +27,45 @@ const renderDeterminate = (
|
|
|
27
27
|
return `${colors.brackets(progressbar.leftBracket)}${bar}${colors.brackets(progressbar.rightBracket)} ${units.completed}/${units.total} ${colors.percent(percent + "%")}`;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
const formatElapsed = (snapshot: TaskSnapshot, now: number): string => {
|
|
31
|
+
const elapsedMillis = (snapshot.completedAt ?? now) - snapshot.startedAt;
|
|
32
|
+
const duration =
|
|
33
|
+
snapshot.status === "running"
|
|
34
|
+
? Duration.seconds(Math.floor(elapsedMillis / 1000))
|
|
35
|
+
: Duration.millis(elapsedMillis);
|
|
36
|
+
return ` (${Duration.format(duration)})`;
|
|
37
|
+
};
|
|
38
|
+
|
|
30
39
|
const buildTaskLine = (
|
|
31
40
|
snapshot: TaskSnapshot,
|
|
32
41
|
depth: number,
|
|
33
42
|
tick: number,
|
|
34
43
|
colors: CompiledProgressBarColors,
|
|
44
|
+
now: number,
|
|
35
45
|
): string => {
|
|
36
46
|
const progressbar = snapshot.config;
|
|
37
47
|
const prefix = `${" ".repeat(depth)}- ${snapshot.description}: `;
|
|
48
|
+
const elapsed = formatElapsed(snapshot, now);
|
|
38
49
|
|
|
39
50
|
if (snapshot.status === "failed") {
|
|
40
|
-
return `${prefix}${colors.failed("[failed]")}`;
|
|
51
|
+
return `${prefix}${colors.failed("[failed]")}${elapsed}`;
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
if (snapshot.status === "done") {
|
|
44
55
|
if (snapshot.units._tag === "DeterminateTaskUnits") {
|
|
45
|
-
return `${prefix}${colors.done("[done]")} ${snapshot.units.completed}/${snapshot.units.total}`;
|
|
56
|
+
return `${prefix}${colors.done("[done]")} ${snapshot.units.completed}/${snapshot.units.total}${elapsed}`;
|
|
46
57
|
}
|
|
47
|
-
return `${prefix}${colors.done("[done]")}`;
|
|
58
|
+
return `${prefix}${colors.done("[done]")}${elapsed}`;
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
if (snapshot.units._tag === "DeterminateTaskUnits") {
|
|
51
|
-
return prefix + renderDeterminate(snapshot.units, progressbar, colors);
|
|
62
|
+
return prefix + renderDeterminate(snapshot.units, progressbar, colors) + elapsed;
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
const frames = progressbar.spinnerFrames;
|
|
55
66
|
const frameIndex = (snapshot.units.spinnerFrame + tick) % frames.length;
|
|
56
67
|
const frame = frames[frameIndex] ?? frames[0]!;
|
|
57
|
-
return `${prefix}${colors.spinner(frame)}`;
|
|
68
|
+
return `${prefix}${colors.spinner(frame)}${elapsed}`;
|
|
58
69
|
};
|
|
59
70
|
|
|
60
71
|
export const runProgressServiceRenderer = (
|
|
@@ -170,10 +181,11 @@ export const runProgressServiceRenderer = (
|
|
|
170
181
|
if (!snapshot || (snapshot.transient && snapshot.status !== "running")) return [];
|
|
171
182
|
return [{ snapshot, depth: row.depth }];
|
|
172
183
|
});
|
|
184
|
+
const now = yield* Clock.currentTimeMillis;
|
|
173
185
|
const frameTick = mode === "final" ? tick + 1 : tick;
|
|
174
186
|
const taskLines = ordered.map(({ snapshot, depth }) => {
|
|
175
187
|
const lineTick = isTTY ? frameTick : 0;
|
|
176
|
-
return buildTaskLine(snapshot, depth, lineTick, getCompiledColors(snapshot.config));
|
|
188
|
+
return buildTaskLine(snapshot, depth, lineTick, getCompiledColors(snapshot.config), now);
|
|
177
189
|
});
|
|
178
190
|
|
|
179
191
|
if (isTTY) {
|
package/src/runtime.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context, Effect, Exit, FiberRef, Layer, Option, Ref } from "effect";
|
|
1
|
+
import { Clock, Context, Effect, Exit, FiberRef, Layer, Option, Ref } from "effect";
|
|
2
2
|
import { dual } from "effect/Function";
|
|
3
3
|
import { mergeWith } from "es-toolkit/object";
|
|
4
4
|
import { formatWithOptions } from "node:util";
|
|
@@ -82,6 +82,8 @@ const updatedSnapshot = (snapshot: TaskSnapshot, options: UpdateTaskOptions): Ta
|
|
|
82
82
|
transient: options.transient ?? snapshot.transient,
|
|
83
83
|
units,
|
|
84
84
|
config: snapshot.config,
|
|
85
|
+
startedAt: snapshot.startedAt,
|
|
86
|
+
completedAt: snapshot.completedAt,
|
|
85
87
|
});
|
|
86
88
|
};
|
|
87
89
|
|
|
@@ -181,6 +183,7 @@ const makeProgressService = Effect.gen(function* () {
|
|
|
181
183
|
mergeConfig(inheritedProgressBarConfig, options.progressbar),
|
|
182
184
|
);
|
|
183
185
|
|
|
186
|
+
const now = yield* Clock.currentTimeMillis;
|
|
184
187
|
const parentIdValue = Option.getOrNull(resolvedParentId);
|
|
185
188
|
const snapshot = new TaskSnapshot({
|
|
186
189
|
id: taskId,
|
|
@@ -190,6 +193,8 @@ const makeProgressService = Effect.gen(function* () {
|
|
|
190
193
|
transient: options.transient ?? false,
|
|
191
194
|
units,
|
|
192
195
|
config: resolvedProgressBarConfig,
|
|
196
|
+
startedAt: now,
|
|
197
|
+
completedAt: null,
|
|
193
198
|
});
|
|
194
199
|
|
|
195
200
|
yield* Ref.update(storeRef, (s) => {
|
|
@@ -240,6 +245,8 @@ const makeProgressService = Effect.gen(function* () {
|
|
|
240
245
|
transient: snapshot.transient,
|
|
241
246
|
units,
|
|
242
247
|
config: snapshot.config,
|
|
248
|
+
startedAt: snapshot.startedAt,
|
|
249
|
+
completedAt: snapshot.completedAt,
|
|
243
250
|
}),
|
|
244
251
|
);
|
|
245
252
|
|
|
@@ -247,62 +254,74 @@ const makeProgressService = Effect.gen(function* () {
|
|
|
247
254
|
}).pipe(Effect.zipRight(markDirty));
|
|
248
255
|
|
|
249
256
|
const completeTask = (taskId: TaskId) =>
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
nextTasks.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
snapshot.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
257
|
+
Effect.gen(function* () {
|
|
258
|
+
const now = yield* Clock.currentTimeMillis;
|
|
259
|
+
yield* Ref.update(storeRef, (store) => {
|
|
260
|
+
const snapshot = store.tasks.get(taskId);
|
|
261
|
+
if (!snapshot) return store;
|
|
262
|
+
|
|
263
|
+
const nextTasks = new Map(store.tasks);
|
|
264
|
+
if (snapshot.transient) {
|
|
265
|
+
nextTasks.delete(taskId);
|
|
266
|
+
return { tasks: nextTasks, renderOrder: removeFromRenderOrder(store.renderOrder, taskId) };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
nextTasks.set(
|
|
270
|
+
taskId,
|
|
271
|
+
new TaskSnapshot({
|
|
272
|
+
id: snapshot.id,
|
|
273
|
+
parentId: snapshot.parentId,
|
|
274
|
+
description: snapshot.description,
|
|
275
|
+
status: "done",
|
|
276
|
+
transient: snapshot.transient,
|
|
277
|
+
units:
|
|
278
|
+
snapshot.units._tag === "DeterminateTaskUnits"
|
|
279
|
+
? new DeterminateTaskUnits({
|
|
280
|
+
completed: snapshot.units.total,
|
|
281
|
+
total: snapshot.units.total,
|
|
282
|
+
})
|
|
283
|
+
: snapshot.units,
|
|
284
|
+
config: snapshot.config,
|
|
285
|
+
startedAt: snapshot.startedAt,
|
|
286
|
+
completedAt: now,
|
|
287
|
+
}),
|
|
288
|
+
);
|
|
289
|
+
return { tasks: nextTasks, renderOrder: store.renderOrder };
|
|
290
|
+
});
|
|
291
|
+
yield* markDirty;
|
|
292
|
+
});
|
|
280
293
|
|
|
281
294
|
const failTask = (taskId: TaskId) =>
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
nextTasks.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
295
|
+
Effect.gen(function* () {
|
|
296
|
+
const now = yield* Clock.currentTimeMillis;
|
|
297
|
+
yield* Ref.update(storeRef, (store) => {
|
|
298
|
+
const snapshot = store.tasks.get(taskId);
|
|
299
|
+
if (!snapshot) return store;
|
|
300
|
+
|
|
301
|
+
const nextTasks = new Map(store.tasks);
|
|
302
|
+
if (snapshot.transient) {
|
|
303
|
+
nextTasks.delete(taskId);
|
|
304
|
+
return { tasks: nextTasks, renderOrder: removeFromRenderOrder(store.renderOrder, taskId) };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
nextTasks.set(
|
|
308
|
+
taskId,
|
|
309
|
+
new TaskSnapshot({
|
|
310
|
+
id: snapshot.id,
|
|
311
|
+
parentId: snapshot.parentId,
|
|
312
|
+
description: snapshot.description,
|
|
313
|
+
status: "failed",
|
|
314
|
+
transient: snapshot.transient,
|
|
315
|
+
units: snapshot.units,
|
|
316
|
+
config: snapshot.config,
|
|
317
|
+
startedAt: snapshot.startedAt,
|
|
318
|
+
completedAt: now,
|
|
319
|
+
}),
|
|
320
|
+
);
|
|
321
|
+
return { tasks: nextTasks, renderOrder: store.renderOrder };
|
|
322
|
+
});
|
|
323
|
+
yield* markDirty;
|
|
324
|
+
});
|
|
306
325
|
|
|
307
326
|
const appendLog = (args: ReadonlyArray<unknown>) =>
|
|
308
327
|
Effect.gen(function* () {
|
package/src/types.ts
CHANGED
|
@@ -25,7 +25,7 @@ export const decodeProgressBarConfigSync = Schema.decodeUnknownSync(ProgressBarC
|
|
|
25
25
|
|
|
26
26
|
export const defaultRendererConfig: RendererConfigShape = {
|
|
27
27
|
disableUserInput: true,
|
|
28
|
-
renderIntervalMillis:
|
|
28
|
+
renderIntervalMillis: 100, // 10 FPS
|
|
29
29
|
maxLogLines: 0,
|
|
30
30
|
nonTtyUpdateStep: 5,
|
|
31
31
|
};
|
|
@@ -103,6 +103,8 @@ export class TaskSnapshot extends Schema.TaggedClass<TaskSnapshot>()("TaskSnapsh
|
|
|
103
103
|
transient: Schema.Boolean,
|
|
104
104
|
units: TaskUnitsSchema,
|
|
105
105
|
config: ProgressBarConfigSchema,
|
|
106
|
+
startedAt: Schema.Number,
|
|
107
|
+
completedAt: Schema.NullOr(Schema.Number),
|
|
106
108
|
}) {}
|
|
107
109
|
|
|
108
110
|
export interface RenderRow {
|