builderman 1.2.0 → 1.4.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 +380 -200
- package/dist/graph.js +0 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/modules/signal-handler.d.ts +13 -0
- package/dist/modules/signal-handler.js +38 -0
- package/dist/modules/task-executor.d.ts +25 -0
- package/dist/modules/task-executor.js +333 -0
- package/dist/modules/teardown-manager.d.ts +22 -0
- package/dist/modules/teardown-manager.js +157 -0
- package/dist/pipeline-error.d.ts +11 -0
- package/dist/pipeline-error.js +50 -0
- package/dist/pipeline.d.ts +0 -12
- package/dist/pipeline.js +164 -481
- package/dist/scheduler.d.ts +4 -4
- package/dist/scheduler.js +3 -3
- package/dist/task.d.ts +0 -5
- package/dist/task.js +19 -23
- package/dist/types.d.ts +232 -26
- package/package.json +1 -1
- package/dist/pipeline.test.d.ts +0 -1
package/dist/scheduler.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { TaskGraph } from "./types.js";
|
|
2
2
|
export type SchedulerInput = {
|
|
3
3
|
type: "complete";
|
|
4
|
-
taskId:
|
|
4
|
+
taskId: string;
|
|
5
5
|
} | {
|
|
6
6
|
type: "ready";
|
|
7
|
-
taskId:
|
|
7
|
+
taskId: string;
|
|
8
8
|
} | {
|
|
9
9
|
type: "skip";
|
|
10
|
-
taskId:
|
|
10
|
+
taskId: string;
|
|
11
11
|
};
|
|
12
12
|
export type SchedulerOutput = {
|
|
13
13
|
type: "run";
|
|
14
|
-
taskId:
|
|
14
|
+
taskId: string;
|
|
15
15
|
} | {
|
|
16
16
|
type: "idle";
|
|
17
17
|
};
|
package/dist/scheduler.js
CHANGED
|
@@ -32,9 +32,9 @@ export function* createScheduler(graph) {
|
|
|
32
32
|
if (input?.taskId === undefined)
|
|
33
33
|
continue;
|
|
34
34
|
markDependencyReady(input.taskId);
|
|
35
|
-
if (input.type === "
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
if (input.type === "complete" || input.type === "skip") {
|
|
36
|
+
completed++;
|
|
37
|
+
}
|
|
38
38
|
}
|
|
39
39
|
return { type: "done" };
|
|
40
40
|
}
|
package/dist/task.d.ts
CHANGED
|
@@ -7,10 +7,5 @@ import type { TaskConfig, Task } from "./types.js";
|
|
|
7
7
|
* const build = task({ name: "build", commands: { build: "npm run build" }, cwd: "." })
|
|
8
8
|
* const deploy = task({ name: "deploy", commands: { build: "npm run deploy" }, cwd: ".", dependencies: [build] })
|
|
9
9
|
* await pipeline([build, deploy]).run()
|
|
10
|
-
*
|
|
11
|
-
* // alternatively, you can use the andThen method to chain tasks together:
|
|
12
|
-
* const buildAndDeploy = task({ name: "build", commands: { build: "npm run build" }, cwd: "." })
|
|
13
|
-
* .andThen({ name: "deploy", commands: { build: "npm run deploy" }, cwd: "." })
|
|
14
|
-
* await buildAndDeploy.run()
|
|
15
10
|
*/
|
|
16
11
|
export declare function task(config: TaskConfig): Task;
|
package/dist/task.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { $TASK_INTERNAL } from "./constants.js";
|
|
2
2
|
import { validateTasks } from "./util.js";
|
|
3
|
-
import { pipeline } from "./pipeline.js";
|
|
4
|
-
let taskId = 0;
|
|
5
3
|
/**
|
|
6
4
|
* Creates a task configuration.
|
|
7
5
|
* @param config - The configuration for the task.
|
|
@@ -10,30 +8,28 @@ let taskId = 0;
|
|
|
10
8
|
* const build = task({ name: "build", commands: { build: "npm run build" }, cwd: "." })
|
|
11
9
|
* const deploy = task({ name: "deploy", commands: { build: "npm run deploy" }, cwd: ".", dependencies: [build] })
|
|
12
10
|
* await pipeline([build, deploy]).run()
|
|
13
|
-
*
|
|
14
|
-
* // alternatively, you can use the andThen method to chain tasks together:
|
|
15
|
-
* const buildAndDeploy = task({ name: "build", commands: { build: "npm run build" }, cwd: "." })
|
|
16
|
-
* .andThen({ name: "deploy", commands: { build: "npm run deploy" }, cwd: "." })
|
|
17
|
-
* await buildAndDeploy.run()
|
|
18
11
|
*/
|
|
19
12
|
export function task(config) {
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
13
|
+
const { name, commands, cwd = ".", dependencies = [], env, allowSkip, } = config;
|
|
14
|
+
const dependenciesClone = [...dependencies];
|
|
15
|
+
validateTasks(dependenciesClone);
|
|
16
|
+
const commandsClone = Object.fromEntries(Object.entries(commands).map(([key, command]) => {
|
|
17
|
+
if (typeof command === "string") {
|
|
18
|
+
return [key, command];
|
|
19
|
+
}
|
|
20
|
+
const { run, readyWhen, readyTimeout, teardown, env } = command;
|
|
21
|
+
return [key, { run, readyWhen, readyTimeout, teardown, env: { ...env } }];
|
|
22
|
+
}));
|
|
23
|
+
return {
|
|
24
|
+
name,
|
|
23
25
|
[$TASK_INTERNAL]: {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
dependencies:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
...nextConfig,
|
|
32
|
-
dependencies: [taskInstance],
|
|
33
|
-
});
|
|
34
|
-
// Return a pipeline containing both tasks
|
|
35
|
-
return pipeline([taskInstance, nextTask]);
|
|
26
|
+
name,
|
|
27
|
+
cwd,
|
|
28
|
+
dependencies: dependenciesClone,
|
|
29
|
+
env: { ...env },
|
|
30
|
+
allowSkip,
|
|
31
|
+
id: crypto.randomUUID(),
|
|
32
|
+
commands: commandsClone,
|
|
36
33
|
},
|
|
37
34
|
};
|
|
38
|
-
return taskInstance;
|
|
39
35
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { $TASK_INTERNAL } from "./constants.js";
|
|
2
|
-
import { PipelineError } from "./pipeline.js";
|
|
2
|
+
import { PipelineError } from "./pipeline-error.js";
|
|
3
3
|
/**
|
|
4
4
|
* Configuration for a command to be executed as part of a task.
|
|
5
5
|
*/
|
|
@@ -25,6 +25,11 @@ export interface CommandConfig {
|
|
|
25
25
|
* Optional command to run during teardown (e.g., to stop a server).
|
|
26
26
|
*/
|
|
27
27
|
teardown?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Optional environment variables to set for the process spawned by this command.
|
|
30
|
+
* Overrides environment variables inherited from the parent process & task config.
|
|
31
|
+
*/
|
|
32
|
+
env?: Record<string, string>;
|
|
28
33
|
}
|
|
29
34
|
/**
|
|
30
35
|
* A command can be either a simple string or a CommandConfig object.
|
|
@@ -57,8 +62,9 @@ export interface TaskConfig {
|
|
|
57
62
|
/**
|
|
58
63
|
* Working directory for the task's commands.
|
|
59
64
|
* Can be absolute or relative to the current working directory.
|
|
65
|
+
* @default "."
|
|
60
66
|
*/
|
|
61
|
-
cwd
|
|
67
|
+
cwd?: string;
|
|
62
68
|
/**
|
|
63
69
|
* Optional array of tasks that must complete before this task can start.
|
|
64
70
|
* Dependencies are executed in parallel when possible.
|
|
@@ -69,10 +75,17 @@ export interface TaskConfig {
|
|
|
69
75
|
* Use this to explicitly mark tasks that are intentionally mode-specific.
|
|
70
76
|
*/
|
|
71
77
|
allowSkip?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Optional environment variables to set for the process spawned by this task.
|
|
80
|
+
* Overrides environment variables inherited from the parent process.
|
|
81
|
+
*/
|
|
82
|
+
env?: Record<string, string>;
|
|
72
83
|
}
|
|
73
84
|
interface TaskInternal extends TaskConfig {
|
|
74
|
-
id:
|
|
85
|
+
id: string;
|
|
86
|
+
cwd: string;
|
|
75
87
|
dependencies: Task[];
|
|
88
|
+
env: Record<string, string>;
|
|
76
89
|
pipeline?: Pipeline;
|
|
77
90
|
}
|
|
78
91
|
/**
|
|
@@ -89,13 +102,6 @@ export interface Task {
|
|
|
89
102
|
* @internal
|
|
90
103
|
*/
|
|
91
104
|
[$TASK_INTERNAL]: TaskInternal;
|
|
92
|
-
/**
|
|
93
|
-
* Creates a new pipeline that starts after this task completes.
|
|
94
|
-
* This allows for chaining tasks together.
|
|
95
|
-
* @param config Task configuration for the next task in the chain.
|
|
96
|
-
* @returns A new pipeline starting with the configured task.
|
|
97
|
-
*/
|
|
98
|
-
andThen(config: Omit<TaskConfig, "dependencies">): Pipeline;
|
|
99
105
|
}
|
|
100
106
|
/**
|
|
101
107
|
* Configuration options for running a pipeline.
|
|
@@ -106,6 +112,11 @@ export interface PipelineRunConfig {
|
|
|
106
112
|
* @default process.env.NODE_ENV === "production" ? "build" : "dev"
|
|
107
113
|
*/
|
|
108
114
|
command?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Optional environment variables to set for processes spawned by this pipeline.
|
|
117
|
+
* Overrides environment variables inherited from the parent process.
|
|
118
|
+
*/
|
|
119
|
+
env?: Record<string, string>;
|
|
109
120
|
/**
|
|
110
121
|
* Provides a custom abort signal for the pipeline.
|
|
111
122
|
* Aborting the signal will cause the pipeline to fail.
|
|
@@ -149,16 +160,6 @@ export interface PipelineRunConfig {
|
|
|
149
160
|
* @param error The error that occurred during teardown.
|
|
150
161
|
*/
|
|
151
162
|
onTaskTeardownError?: (taskName: string, error: Error) => void;
|
|
152
|
-
/**
|
|
153
|
-
* Callback invoked when the pipeline encounters an error and fails.
|
|
154
|
-
* @param error The PipelineError that caused the pipeline to fail.
|
|
155
|
-
*/
|
|
156
|
-
onPipelineError?: (error: PipelineError) => void;
|
|
157
|
-
/**
|
|
158
|
-
* Callback invoked when the pipeline completes successfully.
|
|
159
|
-
* This is called after all tasks have completed and all teardowns have finished.
|
|
160
|
-
*/
|
|
161
|
-
onPipelineComplete?: () => void;
|
|
162
163
|
}
|
|
163
164
|
/**
|
|
164
165
|
* Configuration for converting a pipeline into a task.
|
|
@@ -173,6 +174,11 @@ export interface PipelineTaskConfig {
|
|
|
173
174
|
* Optional array of tasks that must complete before this pipeline task can start.
|
|
174
175
|
*/
|
|
175
176
|
dependencies?: Task[];
|
|
177
|
+
/**
|
|
178
|
+
* Optional environment variables to set for the process spawned by this pipeline task.
|
|
179
|
+
* Overrides environment variables inherited from the parent process.
|
|
180
|
+
*/
|
|
181
|
+
env?: Record<string, string>;
|
|
176
182
|
}
|
|
177
183
|
/**
|
|
178
184
|
* A pipeline manages the execution of tasks with dependency-based coordination.
|
|
@@ -184,10 +190,10 @@ export interface Pipeline {
|
|
|
184
190
|
* Tasks with no dependencies start immediately, and tasks with dependencies
|
|
185
191
|
* wait for their dependencies to complete before starting.
|
|
186
192
|
* @param config Optional configuration for pipeline execution.
|
|
187
|
-
* @returns A promise that resolves
|
|
188
|
-
*
|
|
193
|
+
* @returns A promise that resolves with a RunResult containing execution stats.
|
|
194
|
+
* The result will have `ok: false` if any task fails or the pipeline is aborted.
|
|
189
195
|
*/
|
|
190
|
-
run(config?: PipelineRunConfig): Promise<
|
|
196
|
+
run(config?: PipelineRunConfig): Promise<RunResult>;
|
|
191
197
|
/**
|
|
192
198
|
* Converts this pipeline into a task that can be used as a dependency
|
|
193
199
|
* in another pipeline. This enables nested pipelines.
|
|
@@ -198,11 +204,11 @@ export interface Pipeline {
|
|
|
198
204
|
}
|
|
199
205
|
export interface TaskNode {
|
|
200
206
|
task: Task;
|
|
201
|
-
dependencies: Set<
|
|
202
|
-
dependents: Set<
|
|
207
|
+
dependencies: Set<string>;
|
|
208
|
+
dependents: Set<string>;
|
|
203
209
|
}
|
|
204
210
|
export interface TaskGraph {
|
|
205
|
-
nodes: Map<
|
|
211
|
+
nodes: Map<string, TaskNode>;
|
|
206
212
|
/**
|
|
207
213
|
* Validates the graph for circular dependencies.
|
|
208
214
|
* @throws Error if circular dependencies are detected
|
|
@@ -214,4 +220,204 @@ export interface TaskGraph {
|
|
|
214
220
|
*/
|
|
215
221
|
simplify(): void;
|
|
216
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Status of a task in the pipeline.
|
|
225
|
+
* - "pending": Task has not started yet
|
|
226
|
+
* - "skipped": Task was skipped (e.g., no command for the current mode)
|
|
227
|
+
* - "running": Task is currently executing
|
|
228
|
+
* - "completed": Task completed successfully
|
|
229
|
+
* - "failed": Task failed during execution
|
|
230
|
+
* - "aborted": Task was aborted (e.g., due to pipeline cancellation)
|
|
231
|
+
*/
|
|
232
|
+
export type TaskStatus = "pending" | "skipped" | "running" | "completed" | "failed" | "aborted";
|
|
233
|
+
/**
|
|
234
|
+
* Statistics for a single task in the pipeline.
|
|
235
|
+
*/
|
|
236
|
+
export interface TaskStats {
|
|
237
|
+
/**
|
|
238
|
+
* Unique identifier for the task.
|
|
239
|
+
*/
|
|
240
|
+
id: string;
|
|
241
|
+
/**
|
|
242
|
+
* Human-readable name of the task.
|
|
243
|
+
*/
|
|
244
|
+
name: string;
|
|
245
|
+
/**
|
|
246
|
+
* Current status of the task.
|
|
247
|
+
*/
|
|
248
|
+
status: TaskStatus;
|
|
249
|
+
/**
|
|
250
|
+
* Command name that was executed (e.g., "dev", "build").
|
|
251
|
+
* Only present if the task was executed or skipped (not if it's still pending).
|
|
252
|
+
*/
|
|
253
|
+
command?: string;
|
|
254
|
+
/**
|
|
255
|
+
* Timestamp (milliseconds since epoch) when the task started execution.
|
|
256
|
+
* Only present if the task started running.
|
|
257
|
+
*/
|
|
258
|
+
startedAt?: number;
|
|
259
|
+
/**
|
|
260
|
+
* Timestamp (milliseconds since epoch) when the task finished execution.
|
|
261
|
+
* Only present if the task completed, failed, was skipped, or was aborted.
|
|
262
|
+
*/
|
|
263
|
+
finishedAt?: number;
|
|
264
|
+
/**
|
|
265
|
+
* Duration of task execution in milliseconds.
|
|
266
|
+
* Only present if the task has finished (completed, failed, skipped, or aborted).
|
|
267
|
+
*/
|
|
268
|
+
durationMs?: number;
|
|
269
|
+
/**
|
|
270
|
+
* Exit code of the task's process.
|
|
271
|
+
* Only present if the task completed or failed.
|
|
272
|
+
* Typically 0 for success, non-zero for failure.
|
|
273
|
+
*/
|
|
274
|
+
exitCode?: number;
|
|
275
|
+
/**
|
|
276
|
+
* Signal that terminated the task's process (e.g., "SIGTERM", "SIGKILL").
|
|
277
|
+
* Only present if the task was terminated by a signal.
|
|
278
|
+
*/
|
|
279
|
+
signal?: string;
|
|
280
|
+
/**
|
|
281
|
+
* Error that occurred during task execution.
|
|
282
|
+
* Only present if the task failed or was aborted.
|
|
283
|
+
*/
|
|
284
|
+
error?: Error;
|
|
285
|
+
/**
|
|
286
|
+
* Teardown command execution status.
|
|
287
|
+
* Only present if the task had a teardown command configured.
|
|
288
|
+
*/
|
|
289
|
+
teardown?: {
|
|
290
|
+
/**
|
|
291
|
+
* Status of the teardown command execution.
|
|
292
|
+
* - "not-run": Teardown was registered but never executed (e.g., task failed before starting)
|
|
293
|
+
* - "completed": Teardown executed successfully
|
|
294
|
+
* - "failed": Teardown execution failed
|
|
295
|
+
*/
|
|
296
|
+
status: "not-run" | "completed" | "failed";
|
|
297
|
+
/**
|
|
298
|
+
* Error that occurred during teardown execution.
|
|
299
|
+
* Only present if teardown status is "failed".
|
|
300
|
+
*/
|
|
301
|
+
error?: Error;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Array of task IDs that this task depends on.
|
|
305
|
+
* These tasks must complete before this task can start.
|
|
306
|
+
*/
|
|
307
|
+
dependencies: string[];
|
|
308
|
+
/**
|
|
309
|
+
* Array of task IDs that depend on this task.
|
|
310
|
+
* These tasks cannot start until this task completes.
|
|
311
|
+
*/
|
|
312
|
+
dependents: string[];
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Statistics for the entire pipeline execution.
|
|
316
|
+
*/
|
|
317
|
+
export interface PipelineStats {
|
|
318
|
+
/**
|
|
319
|
+
* Command name that was executed for this pipeline run (e.g., "dev", "build").
|
|
320
|
+
*/
|
|
321
|
+
command: string;
|
|
322
|
+
/**
|
|
323
|
+
* Timestamp (milliseconds since epoch) when the pipeline started.
|
|
324
|
+
*/
|
|
325
|
+
startedAt: number;
|
|
326
|
+
/**
|
|
327
|
+
* Timestamp (milliseconds since epoch) when the pipeline finished.
|
|
328
|
+
*/
|
|
329
|
+
finishedAt: number;
|
|
330
|
+
/**
|
|
331
|
+
* Total duration of pipeline execution in milliseconds.
|
|
332
|
+
*/
|
|
333
|
+
durationMs: number;
|
|
334
|
+
/**
|
|
335
|
+
* Overall status of the pipeline.
|
|
336
|
+
* - "success": All tasks completed successfully
|
|
337
|
+
* - "failed": One or more tasks failed
|
|
338
|
+
* - "aborted": Pipeline was aborted (e.g., due to signal cancellation)
|
|
339
|
+
*/
|
|
340
|
+
status: "success" | "failed" | "aborted";
|
|
341
|
+
/**
|
|
342
|
+
* Map of task IDs to their statistics.
|
|
343
|
+
* Contains statistics for all tasks in the pipeline, regardless of their status.
|
|
344
|
+
*/
|
|
345
|
+
tasks: Record<string, TaskStats>;
|
|
346
|
+
/**
|
|
347
|
+
* Summary of task execution counts.
|
|
348
|
+
*/
|
|
349
|
+
summary: {
|
|
350
|
+
/**
|
|
351
|
+
* Total number of tasks in the pipeline.
|
|
352
|
+
*/
|
|
353
|
+
total: number;
|
|
354
|
+
/**
|
|
355
|
+
* Number of tasks that completed successfully.
|
|
356
|
+
*/
|
|
357
|
+
completed: number;
|
|
358
|
+
/**
|
|
359
|
+
* Number of tasks that failed.
|
|
360
|
+
*/
|
|
361
|
+
failed: number;
|
|
362
|
+
/**
|
|
363
|
+
* Number of tasks that were skipped.
|
|
364
|
+
*/
|
|
365
|
+
skipped: number;
|
|
366
|
+
/**
|
|
367
|
+
* Number of tasks that were still running when the pipeline ended.
|
|
368
|
+
* This is useful when the pipeline was aborted - it indicates how many
|
|
369
|
+
* tasks were in progress and had to be terminated.
|
|
370
|
+
*/
|
|
371
|
+
running: number;
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Result of running a pipeline.
|
|
376
|
+
* The pipeline never throws - it always returns a RunResult with detailed statistics.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* const result = await pipeline(tasks).run({ command: "dev" })
|
|
381
|
+
*
|
|
382
|
+
* if (!result.ok) {
|
|
383
|
+
* console.error("Pipeline failed:", result.error)
|
|
384
|
+
* }
|
|
385
|
+
*
|
|
386
|
+
* console.log("Pipeline stats:", result.stats)
|
|
387
|
+
* console.log(`Completed: ${result.stats.summary.completed}`)
|
|
388
|
+
* console.log(`Failed: ${result.stats.summary.failed}`)
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
export type RunResult = {
|
|
392
|
+
/**
|
|
393
|
+
* Indicates the pipeline completed successfully.
|
|
394
|
+
* When true, `error` is always `null`.
|
|
395
|
+
*/
|
|
396
|
+
ok: true;
|
|
397
|
+
/**
|
|
398
|
+
* Always `null` when `ok` is `true`.
|
|
399
|
+
*/
|
|
400
|
+
error: null;
|
|
401
|
+
/**
|
|
402
|
+
* Pipeline execution statistics.
|
|
403
|
+
*/
|
|
404
|
+
stats: PipelineStats;
|
|
405
|
+
} | {
|
|
406
|
+
/**
|
|
407
|
+
* Indicates the pipeline failed or was aborted.
|
|
408
|
+
* When false, `error` contains the PipelineError that caused the failure.
|
|
409
|
+
*/
|
|
410
|
+
ok: false;
|
|
411
|
+
/**
|
|
412
|
+
* The error that caused the pipeline to fail.
|
|
413
|
+
* Check `error.code` to determine the error type (e.g., `PipelineError.TaskFailed`).
|
|
414
|
+
*/
|
|
415
|
+
error: PipelineError;
|
|
416
|
+
/**
|
|
417
|
+
* Pipeline execution statistics.
|
|
418
|
+
* Even when the pipeline fails, stats contain information about all tasks,
|
|
419
|
+
* including which ones completed, failed, or were still running.
|
|
420
|
+
*/
|
|
421
|
+
stats: PipelineStats;
|
|
422
|
+
};
|
|
217
423
|
export {};
|
package/package.json
CHANGED
package/dist/pipeline.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|