effective-progress 0.5.3 → 0.6.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 +23 -31
- package/dist/index.d.mts +30 -21
- package/dist/index.mjs +1384 -586
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
<img alt="Showcase output" src="docs/images/showcase.gif" width="600" />
|
|
13
13
|
|
|
14
|
-
`effective-progress` is an [Effect](https://effect.website/)-
|
|
14
|
+
`effective-progress` is an [Effect](https://effect.website/)-native CLI progress bar library with:
|
|
15
15
|
|
|
16
16
|
- multiple nested tree-like progress bars
|
|
17
17
|
- spinner support for “we have no idea how long this takes” work
|
|
@@ -79,10 +79,17 @@ Effect.runPromise(program);
|
|
|
79
79
|
|
|
80
80
|
<img alt="Nested example output" src="docs/images/nesting.gif" width="600" />
|
|
81
81
|
|
|
82
|
+
### Effect.all modes
|
|
83
|
+
|
|
84
|
+
We support the `either`/`validate` modes of `Effect.all` and render the amount of sucesses/failures.
|
|
85
|
+
|
|
86
|
+
<img alt="Mixed outcomes modes output" src="docs/images/mixedOutcomes.gif" width="600" />
|
|
87
|
+
|
|
82
88
|
### Other examples
|
|
83
89
|
|
|
84
90
|
- `examples/simpleExample.ts` - low-boilerplate real-world flow
|
|
85
91
|
- `examples/advancedExample.ts` - full API usage and manual task control
|
|
92
|
+
- `examples/mixedOutcomes.ts` - fail-fast vs `either`/`validate` with mixed success/failure counters
|
|
86
93
|
- `examples/showcase.ts` - nested concurrent tasks, spinner workloads, and mixed Effect/Console logging
|
|
87
94
|
- `examples/performance.ts` - stress-style run with high log volume and deeply nested progress updates
|
|
88
95
|
|
|
@@ -98,11 +105,20 @@ Effect.runPromise(program);
|
|
|
98
105
|
|
|
99
106
|
- Rendering is powered by [Ink](https://github.com/vadimdemedes/ink).
|
|
100
107
|
- Built-in columns are: description, bar, amount/spinner, elapsed, and ETA.
|
|
108
|
+
- Determinate bars are segmented by outcome: succeeded (green), failed (red), and remaining (neutral).
|
|
109
|
+
- Determinate amount text shows counters without prefixes: `<succeeded> <failed> <processed>/<total>`.
|
|
101
110
|
- Column widths are shared per frame (widest visible cell wins), so rows stay aligned.
|
|
102
111
|
- Elapsed and ETA reserve stable widths to reduce jitter while tasks transition states.
|
|
103
112
|
- Layout uses a 100-column baseline and grows when content requires more space.
|
|
104
113
|
- On narrow terminals, layout compacts to fit available width and tree prefixes are suppressed when description space is too tight.
|
|
105
114
|
|
|
115
|
+
### Mixed outcomes and `mode`
|
|
116
|
+
|
|
117
|
+
- `Progress.all` in default mode (`mode: "default"`) remains fail-fast.
|
|
118
|
+
- In fail-fast runs, unresolved units remain unprocessed.
|
|
119
|
+
- `mode: "either"` and `mode: "validate"` run all effects and keep mixed outcomes in the task counters.
|
|
120
|
+
- Mixed outcomes can still finalize as `done` when all units are accounted for.
|
|
121
|
+
|
|
106
122
|
## Manual task control
|
|
107
123
|
|
|
108
124
|
For manual usage, `task` still provides the current `Task` context, while logs continue through your outer `Console`:
|
|
@@ -110,11 +126,16 @@ For manual usage, `task` still provides the current `Task` context, while logs c
|
|
|
110
126
|
```ts
|
|
111
127
|
const program = Progress.task(
|
|
112
128
|
Effect.gen(function* () {
|
|
129
|
+
const progress = yield* Progress.Progress;
|
|
113
130
|
const currentTask = yield* Progress.Task;
|
|
114
131
|
yield* Console.log("This log is handled by the outer Console", { taskId: currentTask });
|
|
132
|
+
|
|
133
|
+
// Manual determinate updates:
|
|
134
|
+
yield* progress.advanceTask(currentTask, 3);
|
|
135
|
+
yield* progress.advanceTaskFailed(currentTask, 1);
|
|
115
136
|
yield* Effect.sleep("1 second");
|
|
116
137
|
}),
|
|
117
|
-
{ description: "Manual task" },
|
|
138
|
+
{ description: "Manual task", total: 10 },
|
|
118
139
|
);
|
|
119
140
|
```
|
|
120
141
|
|
|
@@ -122,35 +143,6 @@ const program = Progress.task(
|
|
|
122
143
|
|
|
123
144
|
Custom column APIs are not part of the first Ink release. The renderer ships with built-in columns only, and old renderer config APIs (`RendererConfig`, `ProgressBarConfig`, custom column definitions) are intentionally removed in this iteration.
|
|
124
145
|
|
|
125
|
-
## Terminal service and mocking
|
|
126
|
-
|
|
127
|
-
`effective-progress` now exposes a `ProgressTerminal` service that controls terminal detection and I/O:
|
|
128
|
-
|
|
129
|
-
- `isTTY`
|
|
130
|
-
- `stderrRows`
|
|
131
|
-
- `stderrColumns`
|
|
132
|
-
- `writeStderr(text)`
|
|
133
|
-
- `withRawInputCapture(effect)`
|
|
134
|
-
|
|
135
|
-
You can provide a mock if you want to alter the behavior of terminal detection or if you want to capture the output for testing:
|
|
136
|
-
|
|
137
|
-
```ts
|
|
138
|
-
import { Effect } from "effect";
|
|
139
|
-
import * as Progress from "effective-progress";
|
|
140
|
-
|
|
141
|
-
const mockTerminal: Progress.ProgressTerminalService = {
|
|
142
|
-
isTTY: Effect.succeed(true),
|
|
143
|
-
stderrRows: Effect.succeed(40),
|
|
144
|
-
stderrColumns: Effect.succeed(120),
|
|
145
|
-
writeStderr: (_text) => Effect.void,
|
|
146
|
-
withRawInputCapture: (effect) => effect,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const program = Progress.task(Effect.sleep("100 millis"), { description: "work" }).pipe(
|
|
150
|
-
Effect.provideService(Progress.ProgressTerminal, mockTerminal),
|
|
151
|
-
);
|
|
152
|
-
```
|
|
153
|
-
|
|
154
146
|
## Notes
|
|
155
147
|
|
|
156
148
|
- As Effect 4.0 is around the corner with some changes to logging, there may be some adjustments needed to align with the new Effect APIs.
|
package/dist/index.d.mts
CHANGED
|
@@ -8,30 +8,35 @@ type TaskId = typeof TaskIdSchema.Type;
|
|
|
8
8
|
declare const TaskId: Brand.Brand.Constructor<number & Brand.Brand<"TaskId">>;
|
|
9
9
|
declare const TaskStatusSchema: Schema.Literal<["running", "done", "failed"]>;
|
|
10
10
|
type TaskStatus = typeof TaskStatusSchema.Type;
|
|
11
|
+
declare const TaskCountDisplaySchema: Schema.Literal<["processedOnly", "detailed"]>;
|
|
12
|
+
type TaskCountDisplay = typeof TaskCountDisplaySchema.Type;
|
|
11
13
|
interface AddTaskOptions {
|
|
12
14
|
readonly description: string;
|
|
13
15
|
readonly total?: number;
|
|
14
16
|
readonly transient?: boolean;
|
|
15
17
|
readonly parentId?: TaskId;
|
|
18
|
+
readonly countDisplay?: TaskCountDisplay;
|
|
16
19
|
}
|
|
17
20
|
interface UpdateTaskOptions {
|
|
18
21
|
readonly description?: string;
|
|
19
|
-
readonly
|
|
22
|
+
readonly succeeded?: number;
|
|
23
|
+
readonly failed?: number;
|
|
20
24
|
readonly total?: number;
|
|
21
25
|
readonly transient?: boolean;
|
|
26
|
+
readonly countDisplay?: TaskCountDisplay;
|
|
22
27
|
}
|
|
23
28
|
type TrackOptions = Exclude<AddTaskOptions, "parentId">;
|
|
24
29
|
declare const DeterminateTaskUnits_base: Schema.TaggedClass<DeterminateTaskUnits, "DeterminateTaskUnits", {
|
|
25
30
|
readonly _tag: Schema.tag<"DeterminateTaskUnits">;
|
|
26
31
|
} & {
|
|
27
|
-
|
|
32
|
+
succeeded: typeof Schema.Number;
|
|
33
|
+
failed: typeof Schema.Number;
|
|
34
|
+
processed: typeof Schema.Number;
|
|
28
35
|
total: typeof Schema.Number;
|
|
29
36
|
}>;
|
|
30
37
|
declare class DeterminateTaskUnits extends DeterminateTaskUnits_base {}
|
|
31
38
|
declare const IndeterminateTaskUnits_base: Schema.TaggedClass<IndeterminateTaskUnits, "IndeterminateTaskUnits", {
|
|
32
39
|
readonly _tag: Schema.tag<"IndeterminateTaskUnits">;
|
|
33
|
-
} & {
|
|
34
|
-
spinnerFrame: typeof Schema.Number;
|
|
35
40
|
}>;
|
|
36
41
|
declare class IndeterminateTaskUnits extends IndeterminateTaskUnits_base {}
|
|
37
42
|
declare const TaskUnitsSchema: Schema.Union<[typeof DeterminateTaskUnits, typeof IndeterminateTaskUnits]>;
|
|
@@ -43,6 +48,7 @@ declare const TaskSnapshot_base: Schema.TaggedClass<TaskSnapshot, "TaskSnapshot"
|
|
|
43
48
|
parentId: Schema.NullOr<Schema.brand<typeof Schema.Number, "TaskId">>;
|
|
44
49
|
description: typeof Schema.String;
|
|
45
50
|
status: Schema.Literal<["running", "done", "failed"]>;
|
|
51
|
+
countDisplay: Schema.Literal<["processedOnly", "detailed"]>;
|
|
46
52
|
transient: typeof Schema.Boolean;
|
|
47
53
|
units: Schema.Union<[typeof DeterminateTaskUnits, typeof IndeterminateTaskUnits]>;
|
|
48
54
|
startedAt: typeof Schema.Number;
|
|
@@ -61,6 +67,7 @@ interface ProgressService {
|
|
|
61
67
|
readonly addTask: (options: AddTaskOptions) => Effect.Effect<TaskId>;
|
|
62
68
|
readonly updateTask: (taskId: TaskId, options: UpdateTaskOptions) => Effect.Effect<void>;
|
|
63
69
|
readonly advanceTask: (taskId: TaskId, amount?: number) => Effect.Effect<void>;
|
|
70
|
+
readonly advanceTaskFailed: (taskId: TaskId, amount?: number) => Effect.Effect<void>;
|
|
64
71
|
readonly completeTask: (taskId: TaskId) => Effect.Effect<void>;
|
|
65
72
|
readonly failTask: (taskId: TaskId) => Effect.Effect<void>;
|
|
66
73
|
readonly log: (...args: ReadonlyArray<unknown>) => Effect.Effect<void>;
|
|
@@ -85,6 +92,7 @@ declare const TaskAddedEvent_base: Schema.TaggedClass<TaskAddedEvent, "TaskAdded
|
|
|
85
92
|
description: typeof Schema.String;
|
|
86
93
|
total: Schema.optional<typeof Schema.Number>;
|
|
87
94
|
transient: typeof Schema.Boolean;
|
|
95
|
+
countDisplay: Schema.Literal<["processedOnly", "detailed"]>;
|
|
88
96
|
}>;
|
|
89
97
|
declare class TaskAddedEvent extends TaskAddedEvent_base {}
|
|
90
98
|
declare const TaskUpdatedEvent_base: Schema.TaggedClass<TaskUpdatedEvent, "TaskUpdated", {
|
|
@@ -92,9 +100,12 @@ declare const TaskUpdatedEvent_base: Schema.TaggedClass<TaskUpdatedEvent, "TaskU
|
|
|
92
100
|
} & {
|
|
93
101
|
taskId: Schema.brand<typeof Schema.Number, "TaskId">;
|
|
94
102
|
description: Schema.optional<typeof Schema.String>;
|
|
95
|
-
|
|
103
|
+
succeeded: Schema.optional<typeof Schema.Number>;
|
|
104
|
+
failed: Schema.optional<typeof Schema.Number>;
|
|
105
|
+
processed: Schema.optional<typeof Schema.Number>;
|
|
96
106
|
total: Schema.optional<typeof Schema.Number>;
|
|
97
107
|
transient: Schema.optional<typeof Schema.Boolean>;
|
|
108
|
+
countDisplay: Schema.optional<Schema.Literal<["processedOnly", "detailed"]>>;
|
|
98
109
|
}>;
|
|
99
110
|
declare class TaskUpdatedEvent extends TaskUpdatedEvent_base {}
|
|
100
111
|
declare const TaskAdvancedEvent_base: Schema.TaggedClass<TaskAdvancedEvent, "TaskAdvanced", {
|
|
@@ -102,6 +113,7 @@ declare const TaskAdvancedEvent_base: Schema.TaggedClass<TaskAdvancedEvent, "Tas
|
|
|
102
113
|
} & {
|
|
103
114
|
taskId: Schema.brand<typeof Schema.Number, "TaskId">;
|
|
104
115
|
amount: typeof Schema.Number;
|
|
116
|
+
kind: Schema.Literal<["succeeded", "failed"]>;
|
|
105
117
|
}>;
|
|
106
118
|
declare class TaskAdvancedEvent extends TaskAdvancedEvent_base {}
|
|
107
119
|
declare const TaskCompletedEvent_base: Schema.TaggedClass<TaskCompletedEvent, "TaskCompleted", {
|
|
@@ -126,7 +138,7 @@ declare const ProgressTaskEventSchema: Schema.Union<[typeof TaskAddedEvent, type
|
|
|
126
138
|
type ProgressTaskEvent = typeof ProgressTaskEventSchema.Type;
|
|
127
139
|
declare const decodeProgressTaskEvent: (u: unknown, overrideOptions?: effect_SchemaAST0.ParseOptions) => TaskAddedEvent | TaskUpdatedEvent | TaskAdvancedEvent | TaskCompletedEvent | TaskFailedEvent | TaskRemovedEvent;
|
|
128
140
|
//#endregion
|
|
129
|
-
//#region src/
|
|
141
|
+
//#region src/services/progress.d.ts
|
|
130
142
|
declare const Progress_base: Context.TagClass<Progress, "stromseng.dev/effective-progress/Progress", ProgressService>;
|
|
131
143
|
declare class Progress extends Progress_base {
|
|
132
144
|
static readonly Default: Layer.Layer<Progress, never, never>;
|
|
@@ -142,12 +154,12 @@ interface EffectAllExecutionOptions extends EffectExecutionOptions {
|
|
|
142
154
|
readonly discard?: boolean;
|
|
143
155
|
readonly mode?: "default" | "validate" | "either";
|
|
144
156
|
}
|
|
145
|
-
type AllOptions = Omit<TrackOptions, "total"> & EffectAllExecutionOptions;
|
|
157
|
+
type AllOptions = Omit<TrackOptions, "total" | "countDisplay"> & EffectAllExecutionOptions;
|
|
146
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;
|
|
147
159
|
interface ForEachExecutionOptions extends EffectExecutionOptions {
|
|
148
160
|
readonly discard?: false | undefined;
|
|
149
161
|
}
|
|
150
|
-
type ForEachOptions = TrackOptions & ForEachExecutionOptions;
|
|
162
|
+
type ForEachOptions = Omit<TrackOptions, "countDisplay"> & ForEachExecutionOptions;
|
|
151
163
|
type TaskOptions = AddTaskOptions;
|
|
152
164
|
declare const task: {
|
|
153
165
|
<A, E, R>(effect: Effect.Effect<A, E, R>, options: TaskOptions): Effect.Effect<A, E, Exclude<R, Progress | Task>>;
|
|
@@ -155,25 +167,22 @@ declare const task: {
|
|
|
155
167
|
};
|
|
156
168
|
type AllArg = ReadonlyArray<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>;
|
|
157
169
|
declare const all: {
|
|
158
|
-
<const Arg extends AllArg, O extends EffectAllExecutionOptions>(effects: Arg, options: Omit<TrackOptions, "total"> & O): AllReturn<Arg, O>;
|
|
159
|
-
<O extends EffectAllExecutionOptions>(options: Omit<TrackOptions, "total"> & O): <const Arg extends AllArg>(effects: Arg) => AllReturn<Arg, O>;
|
|
170
|
+
<const Arg extends AllArg, O extends EffectAllExecutionOptions>(effects: Arg, options: Omit<TrackOptions, "total" | "countDisplay"> & O): AllReturn<Arg, O>;
|
|
171
|
+
<O extends EffectAllExecutionOptions>(options: Omit<TrackOptions, "total" | "countDisplay"> & O): <const Arg extends AllArg>(effects: Arg) => AllReturn<Arg, O>;
|
|
160
172
|
};
|
|
161
173
|
declare const forEach: {
|
|
162
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>>;
|
|
163
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>>;
|
|
164
176
|
};
|
|
165
177
|
//#endregion
|
|
166
|
-
//#region src/
|
|
167
|
-
interface
|
|
168
|
-
readonly
|
|
169
|
-
readonly
|
|
170
|
-
readonly stderrColumns: Effect.Effect<number | undefined>;
|
|
171
|
-
readonly writeStderr: (text: string) => Effect.Effect<void>;
|
|
172
|
-
readonly withRawInputCapture: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
178
|
+
//#region src/services/stdio.d.ts
|
|
179
|
+
interface ProgressStdioService {
|
|
180
|
+
readonly stdout: NodeJS.WriteStream;
|
|
181
|
+
readonly stderr: NodeJS.WriteStream;
|
|
173
182
|
}
|
|
174
|
-
declare const
|
|
175
|
-
declare class
|
|
176
|
-
static readonly Default: Layer.Layer<
|
|
183
|
+
declare const ProgressStdio_base: Context.TagClass<ProgressStdio, "stromseng.dev/effective-progress/ProgressStdio", ProgressStdioService>;
|
|
184
|
+
declare class ProgressStdio extends ProgressStdio_base {
|
|
185
|
+
static readonly Default: Layer.Layer<ProgressStdio, never, never>;
|
|
177
186
|
}
|
|
178
187
|
//#endregion
|
|
179
|
-
export { AddTaskOptions, AllOptions, AllReturn, DeterminateTaskUnits, EffectAllExecutionOptions, EffectExecutionOptions, ForEachExecutionOptions, ForEachOptions, IndeterminateTaskUnits, Progress, ProgressService,
|
|
188
|
+
export { AddTaskOptions, AllOptions, AllReturn, DeterminateTaskUnits, EffectAllExecutionOptions, EffectExecutionOptions, ForEachExecutionOptions, ForEachOptions, IndeterminateTaskUnits, 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 };
|