effective-progress 0.2.3 → 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/package.json +1 -1
- package/src/renderer.ts +19 -7
- package/src/runtime.ts +74 -55
- package/src/types.ts +3 -1
package/package.json
CHANGED
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 {
|