effective-progress 0.6.1 → 0.6.2
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 -0
- package/dist/index.d.mts +27 -23
- package/dist/index.mjs +119 -78
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,6 +89,7 @@ Support for `either`/`validate` modes of `Effect.all` and render the amount of s
|
|
|
89
89
|
- `examples/simpleExample.ts` - low-boilerplate real-world flow
|
|
90
90
|
- `examples/advancedExample.ts` - full API usage and manual task control
|
|
91
91
|
- `examples/mixedOutcomes.ts` - fail-fast vs `either`/`validate` with mixed success/failure counters
|
|
92
|
+
- `examples/unknownTotalCounting.ts` - count successes/failures without a known total and render `processed/?`
|
|
92
93
|
- `examples/showcase.ts` - nested concurrent tasks, spinner workloads, and mixed Effect/Console logging
|
|
93
94
|
- `examples/performance.ts` - stress-style run with high log volume and deeply nested progress updates
|
|
94
95
|
|
package/dist/index.d.mts
CHANGED
|
@@ -10,6 +10,13 @@ declare const TaskStatusSchema: Schema.Literal<["running", "done", "failed"]>;
|
|
|
10
10
|
type TaskStatus = typeof TaskStatusSchema.Type;
|
|
11
11
|
declare const TaskCountDisplaySchema: Schema.Literal<["processedOnly", "detailed"]>;
|
|
12
12
|
type TaskCountDisplay = typeof TaskCountDisplaySchema.Type;
|
|
13
|
+
declare const InvalidTaskTotalError_base: Schema.TaggedErrorClass<InvalidTaskTotalError, "InvalidTaskTotalError", {
|
|
14
|
+
readonly _tag: Schema.tag<"InvalidTaskTotalError">;
|
|
15
|
+
} & {
|
|
16
|
+
total: typeof Schema.Number;
|
|
17
|
+
message: typeof Schema.String;
|
|
18
|
+
}>;
|
|
19
|
+
declare class InvalidTaskTotalError extends InvalidTaskTotalError_base {}
|
|
13
20
|
interface AddTaskOptions {
|
|
14
21
|
readonly description: string;
|
|
15
22
|
readonly total?: number;
|
|
@@ -26,20 +33,12 @@ interface UpdateTaskOptions {
|
|
|
26
33
|
readonly countDisplay?: TaskCountDisplay;
|
|
27
34
|
}
|
|
28
35
|
type TrackOptions = Exclude<AddTaskOptions, "parentId">;
|
|
29
|
-
declare const
|
|
30
|
-
readonly _tag: Schema.tag<"DeterminateTaskUnits">;
|
|
31
|
-
} & {
|
|
36
|
+
declare const TaskUnitsSchema: Schema.Struct<{
|
|
32
37
|
succeeded: typeof Schema.Number;
|
|
33
38
|
failed: typeof Schema.Number;
|
|
34
39
|
processed: typeof Schema.Number;
|
|
35
|
-
total: typeof Schema.Number
|
|
36
|
-
}>;
|
|
37
|
-
declare class DeterminateTaskUnits extends DeterminateTaskUnits_base {}
|
|
38
|
-
declare const IndeterminateTaskUnits_base: Schema.TaggedClass<IndeterminateTaskUnits, "IndeterminateTaskUnits", {
|
|
39
|
-
readonly _tag: Schema.tag<"IndeterminateTaskUnits">;
|
|
40
|
+
total: Schema.optional<typeof Schema.Number>;
|
|
40
41
|
}>;
|
|
41
|
-
declare class IndeterminateTaskUnits extends IndeterminateTaskUnits_base {}
|
|
42
|
-
declare const TaskUnitsSchema: Schema.Union<[typeof DeterminateTaskUnits, typeof IndeterminateTaskUnits]>;
|
|
43
42
|
type TaskUnits = typeof TaskUnitsSchema.Type;
|
|
44
43
|
declare const TaskSnapshot_base: Schema.TaggedClass<TaskSnapshot, "TaskSnapshot", {
|
|
45
44
|
readonly _tag: Schema.tag<"TaskSnapshot">;
|
|
@@ -50,7 +49,12 @@ declare const TaskSnapshot_base: Schema.TaggedClass<TaskSnapshot, "TaskSnapshot"
|
|
|
50
49
|
status: Schema.Literal<["running", "done", "failed"]>;
|
|
51
50
|
countDisplay: Schema.Literal<["processedOnly", "detailed"]>;
|
|
52
51
|
transient: typeof Schema.Boolean;
|
|
53
|
-
units: Schema.
|
|
52
|
+
units: Schema.Struct<{
|
|
53
|
+
succeeded: typeof Schema.Number;
|
|
54
|
+
failed: typeof Schema.Number;
|
|
55
|
+
processed: typeof Schema.Number;
|
|
56
|
+
total: Schema.optional<typeof Schema.Number>;
|
|
57
|
+
}>;
|
|
54
58
|
startedAt: typeof Schema.Number;
|
|
55
59
|
completedAt: Schema.NullOr<typeof Schema.Number>;
|
|
56
60
|
}>;
|
|
@@ -64,8 +68,8 @@ interface TaskStore {
|
|
|
64
68
|
readonly renderOrder: ReadonlyArray<RenderRow>;
|
|
65
69
|
}
|
|
66
70
|
interface ProgressService {
|
|
67
|
-
readonly addTask: (options: AddTaskOptions) => Effect.Effect<TaskId>;
|
|
68
|
-
readonly updateTask: (taskId: TaskId, options: UpdateTaskOptions) => Effect.Effect<void>;
|
|
71
|
+
readonly addTask: (options: AddTaskOptions) => Effect.Effect<TaskId, InvalidTaskTotalError>;
|
|
72
|
+
readonly updateTask: (taskId: TaskId, options: UpdateTaskOptions) => Effect.Effect<void, InvalidTaskTotalError>;
|
|
69
73
|
readonly incrementSucceeded: (taskId: TaskId, amount?: number) => Effect.Effect<void>;
|
|
70
74
|
readonly incrementFailed: (taskId: TaskId, amount?: number) => Effect.Effect<void>;
|
|
71
75
|
readonly completeTask: (taskId: TaskId) => Effect.Effect<void>;
|
|
@@ -74,12 +78,12 @@ interface ProgressService {
|
|
|
74
78
|
readonly getTask: (taskId: TaskId) => Effect.Effect<Option.Option<TaskSnapshot>>;
|
|
75
79
|
readonly listTasks: Effect.Effect<ReadonlyArray<TaskSnapshot>>;
|
|
76
80
|
readonly runTask: {
|
|
77
|
-
<A, E, R>(effect: Effect.Effect<A, E, R>, options: AddTaskOptions): Effect.Effect<A, E, Exclude<R, Task>>;
|
|
78
|
-
<A, E, R>(options: AddTaskOptions): (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Task>>;
|
|
81
|
+
<A, E, R>(effect: Effect.Effect<A, E, R>, options: AddTaskOptions): Effect.Effect<A, E | InvalidTaskTotalError, Exclude<R, Task>>;
|
|
82
|
+
<A, E, R>(options: AddTaskOptions): (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | InvalidTaskTotalError, Exclude<R, Task>>;
|
|
79
83
|
};
|
|
80
84
|
readonly withTask: {
|
|
81
|
-
<A, E, R>(effect: Effect.Effect<A, E, R>, options: AddTaskOptions): Effect.Effect<A, E, Exclude<R, Task>>;
|
|
82
|
-
<A, E, R>(options: AddTaskOptions): (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Task>>;
|
|
85
|
+
<A, E, R>(effect: Effect.Effect<A, E, R>, options: AddTaskOptions): Effect.Effect<A, E | InvalidTaskTotalError, Exclude<R, Task>>;
|
|
86
|
+
<A, E, R>(options: AddTaskOptions): (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | InvalidTaskTotalError, Exclude<R, Task>>;
|
|
83
87
|
};
|
|
84
88
|
}
|
|
85
89
|
declare const Task_base: Context.TagClass<Task, "stromseng.dev/effective-progress/Task", number & Brand.Brand<"TaskId">>;
|
|
@@ -155,15 +159,15 @@ interface EffectAllExecutionOptions extends EffectExecutionOptions {
|
|
|
155
159
|
readonly mode?: "default" | "validate" | "either";
|
|
156
160
|
}
|
|
157
161
|
type AllOptions = Omit<TrackOptions, "total" | "countDisplay"> & EffectAllExecutionOptions;
|
|
158
|
-
type AllReturn<Arg extends ReadonlyArray<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>, O extends EffectAllExecutionOptions> = [[Arg] extends [ReadonlyArray<Effect.Effect<any, any, any>>] ? Effect.All.ReturnTuple<Arg, Effect.All.IsDiscard<O>, Effect.All.ExtractMode<O>> : [Arg] extends [Record<string, Effect.Effect<any, any, any>>] ? Effect.All.ReturnObject<Arg, Effect.All.IsDiscard<O>, Effect.All.ExtractMode<O>> : never] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, Exclude<R, Progress | Task>> : never;
|
|
162
|
+
type AllReturn<Arg extends ReadonlyArray<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>, O extends EffectAllExecutionOptions> = [[Arg] extends [ReadonlyArray<Effect.Effect<any, any, any>>] ? Effect.All.ReturnTuple<Arg, Effect.All.IsDiscard<O>, Effect.All.ExtractMode<O>> : [Arg] extends [Record<string, Effect.Effect<any, any, any>>] ? Effect.All.ReturnObject<Arg, Effect.All.IsDiscard<O>, Effect.All.ExtractMode<O>> : never] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E | InvalidTaskTotalError, Exclude<R, Progress | Task>> : never;
|
|
159
163
|
interface ForEachExecutionOptions extends EffectExecutionOptions {
|
|
160
164
|
readonly discard?: false | undefined;
|
|
161
165
|
}
|
|
162
166
|
type ForEachOptions = Omit<TrackOptions, "countDisplay"> & ForEachExecutionOptions;
|
|
163
167
|
type TaskOptions = AddTaskOptions;
|
|
164
168
|
declare const task: {
|
|
165
|
-
<A, E, R>(effect: Effect.Effect<A, E, R>, options: TaskOptions): Effect.Effect<A, E, Exclude<R, Progress | Task>>;
|
|
166
|
-
<A, E, R>(options: TaskOptions): (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Progress | Task>>;
|
|
169
|
+
<A, E, R>(effect: Effect.Effect<A, E, R>, options: TaskOptions): Effect.Effect<A, E | InvalidTaskTotalError, Exclude<R, Progress | Task>>;
|
|
170
|
+
<A, E, R>(options: TaskOptions): (effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | InvalidTaskTotalError, Exclude<R, Progress | Task>>;
|
|
167
171
|
};
|
|
168
172
|
type AllArg = ReadonlyArray<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>;
|
|
169
173
|
declare const all: {
|
|
@@ -171,8 +175,8 @@ declare const all: {
|
|
|
171
175
|
<O extends EffectAllExecutionOptions>(options: Omit<TrackOptions, "total" | "countDisplay"> & O): <const Arg extends AllArg>(effects: Arg) => AllReturn<Arg, O>;
|
|
172
176
|
};
|
|
173
177
|
declare const forEach: {
|
|
174
|
-
<A, B, E, R>(iterable: Iterable<A>, f: (item: A, index: number) => Effect.Effect<B, E, R>, options: ForEachOptions): Effect.Effect<ReadonlyArray<B>, E, Exclude<R, Progress | Task>>;
|
|
175
|
-
<A, B, E, R>(f: (item: A, index: number) => Effect.Effect<B, E, R>, options: ForEachOptions): (iterable: Iterable<A>) => Effect.Effect<ReadonlyArray<B>, E, Exclude<R, Progress | Task>>;
|
|
178
|
+
<A, B, E, R>(iterable: Iterable<A>, f: (item: A, index: number) => Effect.Effect<B, E, R>, options: ForEachOptions): Effect.Effect<ReadonlyArray<B>, E | InvalidTaskTotalError, Exclude<R, Progress | Task>>;
|
|
179
|
+
<A, B, E, R>(f: (item: A, index: number) => Effect.Effect<B, E, R>, options: ForEachOptions): (iterable: Iterable<A>) => Effect.Effect<ReadonlyArray<B>, E | InvalidTaskTotalError, Exclude<R, Progress | Task>>;
|
|
176
180
|
};
|
|
177
181
|
//#endregion
|
|
178
182
|
//#region src/services/stdio.d.ts
|
|
@@ -185,4 +189,4 @@ declare class ProgressStdio extends ProgressStdio_base {
|
|
|
185
189
|
static readonly Default: Layer.Layer<ProgressStdio, never, never>;
|
|
186
190
|
}
|
|
187
191
|
//#endregion
|
|
188
|
-
export { AddTaskOptions, AllOptions, AllReturn,
|
|
192
|
+
export { AddTaskOptions, AllOptions, AllReturn, EffectAllExecutionOptions, EffectExecutionOptions, ForEachExecutionOptions, ForEachOptions, InvalidTaskTotalError, Progress, ProgressService, ProgressStdio, ProgressStdioService, ProgressTaskEvent, ProgressTaskEventSchema, RenderRow, Task, TaskAddedEvent, TaskAdvancedEvent, TaskCompletedEvent, TaskCountDisplay, TaskCountDisplaySchema, TaskFailedEvent, TaskId, TaskOptions, TaskRemovedEvent, TaskSnapshot, TaskStatus, TaskStatusSchema, TaskStore, TaskUnits, TaskUnitsSchema, TaskUpdatedEvent, TrackOptions, UpdateTaskOptions, all, decodeProgressTaskEvent, forEach, task };
|
package/dist/index.mjs
CHANGED
|
@@ -10,14 +10,16 @@ const TaskIdSchema = Schema.Number.pipe(Schema.brand("TaskId"));
|
|
|
10
10
|
const TaskId = Brand.nominal();
|
|
11
11
|
const TaskStatusSchema = Schema.Literal("running", "done", "failed");
|
|
12
12
|
const TaskCountDisplaySchema = Schema.Literal("processedOnly", "detailed");
|
|
13
|
-
var
|
|
13
|
+
var InvalidTaskTotalError = class extends Schema.TaggedError()("InvalidTaskTotalError", {
|
|
14
|
+
total: Schema.Number,
|
|
15
|
+
message: Schema.String
|
|
16
|
+
}) {};
|
|
17
|
+
const TaskUnitsSchema = Schema.Struct({
|
|
14
18
|
succeeded: Schema.Number,
|
|
15
19
|
failed: Schema.Number,
|
|
16
20
|
processed: Schema.Number,
|
|
17
|
-
total: Schema.Number
|
|
18
|
-
})
|
|
19
|
-
var IndeterminateTaskUnits = class extends Schema.TaggedClass()("IndeterminateTaskUnits", {}) {};
|
|
20
|
-
const TaskUnitsSchema = Schema.Union(DeterminateTaskUnits, IndeterminateTaskUnits);
|
|
21
|
+
total: Schema.optional(Schema.Number)
|
|
22
|
+
});
|
|
21
23
|
var TaskSnapshot = class extends Schema.TaggedClass()("TaskSnapshot", {
|
|
22
24
|
id: TaskIdSchema,
|
|
23
25
|
parentId: Schema.NullOr(TaskIdSchema),
|
|
@@ -114,51 +116,75 @@ const toRenderSnapshot = (store) => {
|
|
|
114
116
|
|
|
115
117
|
//#endregion
|
|
116
118
|
//#region src/ink-renderer/store.ts
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
+
const hasExplicitTotal = (options) => Object.prototype.hasOwnProperty.call(options, "total");
|
|
120
|
+
const INVALID_TASK_TOTAL_MESSAGE = "Task total must be greater than 0 when provided.";
|
|
121
|
+
const normalizeTotal = (total) => {
|
|
122
|
+
if (total <= 0) return Effect.fail(new InvalidTaskTotalError({
|
|
123
|
+
total,
|
|
124
|
+
message: INVALID_TASK_TOTAL_MESSAGE
|
|
125
|
+
}));
|
|
126
|
+
return Effect.succeed(total);
|
|
127
|
+
};
|
|
128
|
+
const normalizeUnits = (counts) => {
|
|
129
|
+
const total = counts.total;
|
|
130
|
+
if (total === void 0) {
|
|
131
|
+
const succeeded = Math.max(0, counts.succeeded);
|
|
132
|
+
const failed = Math.max(0, counts.failed);
|
|
133
|
+
return Effect.succeed({
|
|
134
|
+
succeeded,
|
|
135
|
+
failed,
|
|
136
|
+
processed: succeeded + failed
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return Effect.map(normalizeTotal(total), (normalizedTotal) => {
|
|
140
|
+
const failed = Math.min(normalizedTotal, Math.max(0, counts.failed));
|
|
141
|
+
const succeeded = Math.min(normalizedTotal - failed, Math.max(0, counts.succeeded));
|
|
142
|
+
return {
|
|
143
|
+
succeeded,
|
|
144
|
+
failed,
|
|
145
|
+
processed: succeeded + failed,
|
|
146
|
+
total: normalizedTotal
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
const normalizeUnitsUnsafe = (counts) => {
|
|
151
|
+
const total = counts.total;
|
|
152
|
+
if (total === void 0) {
|
|
153
|
+
const succeeded = Math.max(0, counts.succeeded);
|
|
154
|
+
const failed = Math.max(0, counts.failed);
|
|
155
|
+
return {
|
|
156
|
+
succeeded,
|
|
157
|
+
failed,
|
|
158
|
+
processed: succeeded + failed
|
|
159
|
+
};
|
|
160
|
+
}
|
|
119
161
|
const failed = Math.min(total, Math.max(0, counts.failed));
|
|
120
162
|
const succeeded = Math.min(total - failed, Math.max(0, counts.succeeded));
|
|
121
|
-
return
|
|
163
|
+
return {
|
|
122
164
|
succeeded,
|
|
123
165
|
failed,
|
|
124
166
|
processed: succeeded + failed,
|
|
125
167
|
total
|
|
126
|
-
}
|
|
168
|
+
};
|
|
127
169
|
};
|
|
128
|
-
const updateDeterminateCounts = (units, options) => normalizeDeterminateCounts({
|
|
129
|
-
succeeded: options.succeeded ?? units.succeeded,
|
|
130
|
-
failed: options.failed ?? units.failed,
|
|
131
|
-
total: options.total ?? units.total
|
|
132
|
-
});
|
|
133
170
|
const updatedSnapshot = (snapshot, options) => {
|
|
134
171
|
const currentUnits = snapshot.units;
|
|
135
|
-
const units = (()
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
failed: options.failed ?? 0,
|
|
142
|
-
total: options.total
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
if (currentUnits._tag === "DeterminateTaskUnits") {
|
|
146
|
-
if (options.succeeded === void 0 && options.failed === void 0) return currentUnits;
|
|
147
|
-
return updateDeterminateCounts(currentUnits, options);
|
|
148
|
-
}
|
|
149
|
-
return currentUnits;
|
|
150
|
-
})();
|
|
151
|
-
return new TaskSnapshot({
|
|
172
|
+
const units = options.succeeded === void 0 && options.failed === void 0 && options.total === void 0 && !hasExplicitTotal(options) ? Effect.succeed(currentUnits) : normalizeUnits({
|
|
173
|
+
succeeded: options.succeeded ?? currentUnits.succeeded,
|
|
174
|
+
failed: options.failed ?? currentUnits.failed,
|
|
175
|
+
total: hasExplicitTotal(options) ? options.total : currentUnits.total
|
|
176
|
+
});
|
|
177
|
+
return Effect.map(units, (resolvedUnits) => new TaskSnapshot({
|
|
152
178
|
id: snapshot.id,
|
|
153
179
|
parentId: snapshot.parentId,
|
|
154
180
|
description: options.description ?? snapshot.description,
|
|
155
181
|
status: snapshot.status,
|
|
156
182
|
countDisplay: options.countDisplay ?? snapshot.countDisplay,
|
|
157
183
|
transient: options.transient ?? snapshot.transient,
|
|
158
|
-
units,
|
|
184
|
+
units: resolvedUnits,
|
|
159
185
|
startedAt: snapshot.startedAt,
|
|
160
186
|
completedAt: snapshot.completedAt
|
|
161
|
-
});
|
|
187
|
+
}));
|
|
162
188
|
};
|
|
163
189
|
const withTransient = (snapshot, transient) => new TaskSnapshot({
|
|
164
190
|
id: snapshot.id,
|
|
@@ -265,7 +291,7 @@ const makeProgressRenderStore = () => {
|
|
|
265
291
|
},
|
|
266
292
|
addTask: (options) => Effect.gen(function* () {
|
|
267
293
|
const taskId = TaskId(++nextTaskId);
|
|
268
|
-
const units =
|
|
294
|
+
const units = yield* normalizeUnits({
|
|
269
295
|
succeeded: 0,
|
|
270
296
|
failed: 0,
|
|
271
297
|
total: options.total
|
|
@@ -301,12 +327,13 @@ const makeProgressRenderStore = () => {
|
|
|
301
327
|
});
|
|
302
328
|
return taskId;
|
|
303
329
|
}),
|
|
304
|
-
updateTask: (taskId, options) => Effect.
|
|
330
|
+
updateTask: (taskId, options) => Effect.gen(function* () {
|
|
331
|
+
const currentTask = state.tasks.get(taskId);
|
|
332
|
+
if (!currentTask) return;
|
|
333
|
+
const nextTask = yield* updatedSnapshot(currentTask, options);
|
|
305
334
|
updateState((current) => {
|
|
306
|
-
|
|
307
|
-
if (!currentTask) return current;
|
|
335
|
+
if (!current.tasks.get(taskId)) return current;
|
|
308
336
|
const nextTasks = new Map(current.tasks);
|
|
309
|
-
const nextTask = updatedSnapshot(currentTask, options);
|
|
310
337
|
nextTasks.set(taskId, nextTask);
|
|
311
338
|
if (options.transient !== void 0) for (const [candidateId, candidate] of current.tasks.entries()) {
|
|
312
339
|
if (candidateId === taskId) continue;
|
|
@@ -330,7 +357,7 @@ const makeProgressRenderStore = () => {
|
|
|
330
357
|
incrementSucceeded: (taskId, amount = 1) => Effect.sync(() => {
|
|
331
358
|
updateState((current) => {
|
|
332
359
|
const currentTask = current.tasks.get(taskId);
|
|
333
|
-
if (!currentTask
|
|
360
|
+
if (!currentTask) return current;
|
|
334
361
|
const nextTasks = new Map(current.tasks);
|
|
335
362
|
nextTasks.set(taskId, new TaskSnapshot({
|
|
336
363
|
id: currentTask.id,
|
|
@@ -339,7 +366,7 @@ const makeProgressRenderStore = () => {
|
|
|
339
366
|
status: currentTask.status,
|
|
340
367
|
countDisplay: currentTask.countDisplay,
|
|
341
368
|
transient: currentTask.transient,
|
|
342
|
-
units:
|
|
369
|
+
units: normalizeUnitsUnsafe({
|
|
343
370
|
succeeded: currentTask.units.succeeded + amount,
|
|
344
371
|
failed: currentTask.units.failed,
|
|
345
372
|
total: currentTask.units.total
|
|
@@ -356,7 +383,7 @@ const makeProgressRenderStore = () => {
|
|
|
356
383
|
incrementFailed: (taskId, amount = 1) => Effect.sync(() => {
|
|
357
384
|
updateState((current) => {
|
|
358
385
|
const currentTask = current.tasks.get(taskId);
|
|
359
|
-
if (!currentTask
|
|
386
|
+
if (!currentTask) return current;
|
|
360
387
|
const nextTasks = new Map(current.tasks);
|
|
361
388
|
nextTasks.set(taskId, new TaskSnapshot({
|
|
362
389
|
id: currentTask.id,
|
|
@@ -365,7 +392,7 @@ const makeProgressRenderStore = () => {
|
|
|
365
392
|
status: currentTask.status,
|
|
366
393
|
countDisplay: currentTask.countDisplay,
|
|
367
394
|
transient: currentTask.transient,
|
|
368
|
-
units:
|
|
395
|
+
units: normalizeUnitsUnsafe({
|
|
369
396
|
succeeded: currentTask.units.succeeded,
|
|
370
397
|
failed: currentTask.units.failed + amount,
|
|
371
398
|
total: currentTask.units.total
|
|
@@ -399,10 +426,14 @@ const makeProgressRenderStore = () => {
|
|
|
399
426
|
status: "done",
|
|
400
427
|
countDisplay: currentTask.countDisplay,
|
|
401
428
|
transient: currentTask.transient,
|
|
402
|
-
units: currentTask.units.
|
|
429
|
+
units: currentTask.units.total !== void 0 ? normalizeUnitsUnsafe({
|
|
403
430
|
succeeded: currentTask.units.total - currentTask.units.failed,
|
|
404
431
|
failed: currentTask.units.failed,
|
|
405
432
|
total: currentTask.units.total
|
|
433
|
+
}) : currentTask.units.processed > 0 ? normalizeUnitsUnsafe({
|
|
434
|
+
succeeded: currentTask.units.succeeded,
|
|
435
|
+
failed: currentTask.units.failed,
|
|
436
|
+
total: currentTask.units.processed
|
|
406
437
|
}) : currentTask.units,
|
|
407
438
|
startedAt: currentTask.startedAt,
|
|
408
439
|
completedAt: now
|
|
@@ -462,6 +493,8 @@ const SPINNER_FRAMES = [
|
|
|
462
493
|
"⠇",
|
|
463
494
|
"⠏"
|
|
464
495
|
];
|
|
496
|
+
const isDeterminate$1 = (task) => task.units.total !== void 0;
|
|
497
|
+
const showsUnknownTotalCounts = (task) => task.units.total === void 0 && task.units.processed > 0;
|
|
465
498
|
const formatDurationSeconds = (seconds) => {
|
|
466
499
|
const value = Math.max(0, Math.floor(seconds));
|
|
467
500
|
if (value < 60) return `${value}s`;
|
|
@@ -478,7 +511,7 @@ const formatElapsed = (task, now) => {
|
|
|
478
511
|
return formatDurationSeconds(Math.max(0, (task.completedAt ?? now) - task.startedAt) / 1e3);
|
|
479
512
|
};
|
|
480
513
|
const formatEta = (task, now) => {
|
|
481
|
-
if (task.status !== "running" || task
|
|
514
|
+
if (task.status !== "running" || !isDeterminate$1(task)) return "";
|
|
482
515
|
const { processed, total } = task.units;
|
|
483
516
|
const remaining = total - processed;
|
|
484
517
|
if (processed <= 0 || remaining <= 0) return "";
|
|
@@ -494,7 +527,7 @@ const getTaskIndicator = (task, tick) => {
|
|
|
494
527
|
symbol: "✗",
|
|
495
528
|
color: "red"
|
|
496
529
|
};
|
|
497
|
-
if (task
|
|
530
|
+
if (!isDeterminate$1(task)) return {
|
|
498
531
|
symbol: "✓",
|
|
499
532
|
color: "green"
|
|
500
533
|
};
|
|
@@ -517,7 +550,7 @@ const getTaskIndicator = (task, tick) => {
|
|
|
517
550
|
};
|
|
518
551
|
};
|
|
519
552
|
const formatDeterminateAmountParts = (task) => {
|
|
520
|
-
if (task
|
|
553
|
+
if (!isDeterminate$1(task)) return;
|
|
521
554
|
const totalText = `${task.units.total}`;
|
|
522
555
|
const width = totalText.length;
|
|
523
556
|
const processedText = `${task.units.processed}`;
|
|
@@ -529,19 +562,23 @@ const formatDeterminateAmountParts = (task) => {
|
|
|
529
562
|
};
|
|
530
563
|
};
|
|
531
564
|
const formatAmount = (task, _tick) => {
|
|
532
|
-
if (task
|
|
565
|
+
if (isDeterminate$1(task)) {
|
|
533
566
|
const parts = formatDeterminateAmountParts(task);
|
|
534
567
|
if (parts === void 0) return "";
|
|
535
568
|
if (task.countDisplay === "detailed") return `${parts.succeeded} ${parts.failed} ${parts.processed}/${parts.total}`;
|
|
536
569
|
return `${parts.processed}/${parts.total}`;
|
|
537
570
|
}
|
|
538
|
-
if (task
|
|
571
|
+
if (showsUnknownTotalCounts(task)) {
|
|
572
|
+
if (task.countDisplay === "detailed") return `${task.units.succeeded} ${task.units.failed} ${task.units.processed}/?`;
|
|
573
|
+
return `${task.units.processed}/?`;
|
|
574
|
+
}
|
|
575
|
+
if (task.status === "running") return "";
|
|
539
576
|
return task.status === "failed" ? "✗" : "";
|
|
540
577
|
};
|
|
541
578
|
|
|
542
579
|
//#endregion
|
|
543
580
|
//#region src/ink-renderer/columns/determinate.ts
|
|
544
|
-
const isDeterminate = (task) => task.units.
|
|
581
|
+
const isDeterminate = (task) => task.units.total !== void 0;
|
|
545
582
|
const hasDeterminateRows = (rows) => rows.some((row) => isDeterminate(row.task));
|
|
546
583
|
|
|
547
584
|
//#endregion
|
|
@@ -578,10 +615,11 @@ const resolveColumnSpecs = (specs, resistance) => specs.flatMap((spec) => spec.v
|
|
|
578
615
|
const padLeft = (value, width) => value.padStart(Math.max(0, width), " ");
|
|
579
616
|
const padRight = (value, width) => value.padEnd(Math.max(0, width), " ");
|
|
580
617
|
const blank = (width) => " ".repeat(Math.max(0, width));
|
|
581
|
-
const
|
|
618
|
+
const shouldShowCountAmount = (task) => task.units.total !== void 0 || task.units.processed > 0;
|
|
619
|
+
const shouldShowDetailedCounts = (task) => shouldShowCountAmount(task) && task.countDisplay === "detailed";
|
|
582
620
|
const SucceededCountColumn = ({ task, width }) => {
|
|
583
621
|
if (width <= 0) return null;
|
|
584
|
-
if (task
|
|
622
|
+
if (!shouldShowCountAmount(task)) return /* @__PURE__ */ jsx(Text, { children: blank(width) });
|
|
585
623
|
if (!shouldShowDetailedCounts(task)) return /* @__PURE__ */ jsx(Text, { children: blank(width) });
|
|
586
624
|
return /* @__PURE__ */ jsx(Text, {
|
|
587
625
|
color: "green",
|
|
@@ -590,7 +628,7 @@ const SucceededCountColumn = ({ task, width }) => {
|
|
|
590
628
|
};
|
|
591
629
|
const FailedCountColumn = ({ task, width }) => {
|
|
592
630
|
if (width <= 0) return null;
|
|
593
|
-
if (task
|
|
631
|
+
if (!shouldShowCountAmount(task)) return /* @__PURE__ */ jsx(Text, { children: blank(width) });
|
|
594
632
|
if (!shouldShowDetailedCounts(task)) return /* @__PURE__ */ jsx(Text, { children: blank(width) });
|
|
595
633
|
return /* @__PURE__ */ jsx(Text, {
|
|
596
634
|
color: "red",
|
|
@@ -599,11 +637,11 @@ const FailedCountColumn = ({ task, width }) => {
|
|
|
599
637
|
};
|
|
600
638
|
const ProcessedCountColumn = ({ task, width }) => {
|
|
601
639
|
if (width <= 0) return null;
|
|
602
|
-
if (task
|
|
640
|
+
if (!shouldShowCountAmount(task)) return /* @__PURE__ */ jsx(Text, { children: blank(width) });
|
|
603
641
|
return /* @__PURE__ */ jsx(Text, { children: padLeft(`${task.units.processed}`, width) });
|
|
604
642
|
};
|
|
605
643
|
const AmountSeparatorColumn = ({ task, tick }) => {
|
|
606
|
-
if (task
|
|
644
|
+
if (shouldShowCountAmount(task)) return /* @__PURE__ */ jsx(Text, { children: "/" });
|
|
607
645
|
const symbol = formatAmount(task, tick);
|
|
608
646
|
if (task.status === "failed") return /* @__PURE__ */ jsx(Text, {
|
|
609
647
|
color: "red",
|
|
@@ -617,8 +655,8 @@ const AmountSeparatorColumn = ({ task, tick }) => {
|
|
|
617
655
|
};
|
|
618
656
|
const TotalCountColumn = ({ task, width }) => {
|
|
619
657
|
if (width <= 0) return null;
|
|
620
|
-
if (task
|
|
621
|
-
return /* @__PURE__ */ jsx(Text, { children: padRight(`${task.units.total}
|
|
658
|
+
if (!shouldShowCountAmount(task)) return /* @__PURE__ */ jsx(Text, { children: blank(width) });
|
|
659
|
+
return /* @__PURE__ */ jsx(Text, { children: padRight(isDeterminate(task) ? `${task.units.total}` : "?", width) });
|
|
622
660
|
};
|
|
623
661
|
const AmountColumn = ({ task, tick, layout }) => {
|
|
624
662
|
if (layout.kind === "text") {
|
|
@@ -682,44 +720,47 @@ const AmountColumn = ({ task, tick, layout }) => {
|
|
|
682
720
|
});
|
|
683
721
|
};
|
|
684
722
|
const computeAmountMetrics = (rows, tick) => {
|
|
685
|
-
let
|
|
723
|
+
let hasStructuredCounts = false;
|
|
686
724
|
let hasDetailed = false;
|
|
687
|
-
let
|
|
725
|
+
let countDigits = 0;
|
|
726
|
+
let totalWidth = 0;
|
|
688
727
|
let simpleTextWidth = 0;
|
|
689
728
|
for (const row of rows) {
|
|
690
729
|
const { task } = row;
|
|
691
|
-
if (
|
|
692
|
-
|
|
693
|
-
|
|
730
|
+
if (shouldShowCountAmount(task)) {
|
|
731
|
+
hasStructuredCounts = true;
|
|
732
|
+
countDigits = Math.max(countDigits, textWidth(`${task.units.succeeded}`), textWidth(`${task.units.failed}`), textWidth(`${task.units.processed}`));
|
|
733
|
+
totalWidth = Math.max(totalWidth, textWidth(isDeterminate(task) ? `${task.units.total}` : "?"));
|
|
694
734
|
if (task.countDisplay === "detailed") hasDetailed = true;
|
|
695
735
|
continue;
|
|
696
736
|
}
|
|
697
737
|
simpleTextWidth = Math.max(simpleTextWidth, textWidth(formatAmount(task, tick)));
|
|
698
738
|
}
|
|
699
739
|
return {
|
|
700
|
-
|
|
740
|
+
hasStructuredCounts,
|
|
701
741
|
hasDetailed,
|
|
702
|
-
|
|
742
|
+
countDigits: Math.max(1, countDigits),
|
|
743
|
+
totalWidth: Math.max(1, totalWidth),
|
|
703
744
|
simpleTextWidth
|
|
704
745
|
};
|
|
705
746
|
};
|
|
706
747
|
const detailedAmountLayout = (metrics) => ({
|
|
707
748
|
kind: "detailed",
|
|
708
|
-
succeededWidth: metrics.hasDetailed ? metrics.
|
|
709
|
-
failedWidth: metrics.hasDetailed ? metrics.
|
|
710
|
-
processedWidth: metrics.
|
|
711
|
-
totalWidth: metrics.
|
|
749
|
+
succeededWidth: metrics.hasDetailed ? metrics.countDigits : 0,
|
|
750
|
+
failedWidth: metrics.hasDetailed ? metrics.countDigits : 0,
|
|
751
|
+
processedWidth: metrics.countDigits,
|
|
752
|
+
totalWidth: metrics.totalWidth
|
|
712
753
|
});
|
|
713
754
|
const processedAmountLayout = (metrics) => ({
|
|
714
755
|
kind: "processed",
|
|
715
|
-
processedWidth: metrics.
|
|
716
|
-
totalWidth: metrics.
|
|
756
|
+
processedWidth: metrics.countDigits,
|
|
757
|
+
totalWidth: metrics.totalWidth
|
|
717
758
|
});
|
|
718
|
-
const detailedAmountWidth = (metrics) => metrics.
|
|
719
|
-
const processedAmountWidth = (metrics) => metrics.
|
|
759
|
+
const detailedAmountWidth = (metrics) => metrics.countDigits + 1 + metrics.totalWidth + (metrics.hasDetailed ? metrics.countDigits + 1 + metrics.countDigits + 1 : 0);
|
|
760
|
+
const processedAmountWidth = (metrics) => metrics.countDigits + 1 + metrics.totalWidth;
|
|
720
761
|
const createAmountColumnSpec = (context) => {
|
|
721
762
|
const metrics = computeAmountMetrics(context.rows, context.tick);
|
|
722
|
-
if (!metrics.
|
|
763
|
+
if (!metrics.hasStructuredCounts && metrics.simpleTextWidth <= 0) return;
|
|
723
764
|
const detailedLayout = detailedAmountLayout(metrics);
|
|
724
765
|
const processedLayout = processedAmountLayout(metrics);
|
|
725
766
|
const detailedWidth = detailedAmountWidth(metrics);
|
|
@@ -728,7 +769,7 @@ const createAmountColumnSpec = (context) => {
|
|
|
728
769
|
id: "amount",
|
|
729
770
|
grow: 0,
|
|
730
771
|
canHide: true,
|
|
731
|
-
variants: metrics.
|
|
772
|
+
variants: metrics.hasStructuredCounts && metrics.hasDetailed ? [{
|
|
732
773
|
id: "detailed",
|
|
733
774
|
minWidth: detailedWidth,
|
|
734
775
|
idealWidth: detailedWidth,
|
|
@@ -746,7 +787,7 @@ const createAmountColumnSpec = (context) => {
|
|
|
746
787
|
tick: context.tick,
|
|
747
788
|
layout: processedLayout
|
|
748
789
|
})
|
|
749
|
-
}] : metrics.
|
|
790
|
+
}] : metrics.hasStructuredCounts ? [{
|
|
750
791
|
id: "processed",
|
|
751
792
|
minWidth: processedWidth,
|
|
752
793
|
idealWidth: processedWidth,
|
|
@@ -789,7 +830,7 @@ const segmentLengths = (width, total, succeeded, failed) => {
|
|
|
789
830
|
};
|
|
790
831
|
};
|
|
791
832
|
const BarColumn = ({ task, width }) => {
|
|
792
|
-
if (task
|
|
833
|
+
if (!isDeterminate(task)) return /* @__PURE__ */ jsx(Text, {});
|
|
793
834
|
const lengths = segmentLengths(Math.max(1, Math.floor(width)), task.units.total, task.units.succeeded, task.units.failed);
|
|
794
835
|
return /* @__PURE__ */ jsxs(Text, {
|
|
795
836
|
wrap: "truncate-end",
|
|
@@ -1555,9 +1596,9 @@ const wrapTrackedEffect = (progress, taskId, effect) => Effect.gen(function* ()
|
|
|
1555
1596
|
});
|
|
1556
1597
|
const isTaskFullyProcessed = (progress, taskId) => Effect.gen(function* () {
|
|
1557
1598
|
const taskOption = yield* progress.getTask(taskId);
|
|
1558
|
-
if (Option.isNone(taskOption)
|
|
1599
|
+
if (Option.isNone(taskOption)) return false;
|
|
1559
1600
|
const { processed, total } = taskOption.value.units;
|
|
1560
|
-
return processed >= total;
|
|
1601
|
+
return total !== void 0 && processed >= total;
|
|
1561
1602
|
});
|
|
1562
1603
|
const all = dual(2, (effects, options) => provideProgress(Effect.gen(function* () {
|
|
1563
1604
|
const progress = yield* Progress;
|
|
@@ -1610,4 +1651,4 @@ const forEach = dual(3, (iterable, f, options) => provideProgress(Effect.gen(fun
|
|
|
1610
1651
|
})));
|
|
1611
1652
|
|
|
1612
1653
|
//#endregion
|
|
1613
|
-
export {
|
|
1654
|
+
export { InvalidTaskTotalError, Progress, ProgressStdio, ProgressTaskEventSchema, Task, TaskAddedEvent, TaskAdvancedEvent, TaskCompletedEvent, TaskCountDisplaySchema, TaskFailedEvent, TaskId, TaskRemovedEvent, TaskSnapshot, TaskStatusSchema, TaskUnitsSchema, TaskUpdatedEvent, all, decodeProgressTaskEvent, forEach, task };
|
package/package.json
CHANGED