tubeless 0.2.6 → 0.2.7
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 +3 -3
- package/dist/core/pipeline-steps.d.ts +61 -77
- package/dist/core/pipeline-steps.js +25 -44
- package/dist/core/pipeline-steps.js.map +1 -1
- package/dist/project/project-compiler.js +1 -1
- package/dist/project/project-compiler.js.map +1 -1
- package/docs/agent-guide.md +9 -7
- package/docs/api-reference.md +4 -4
- package/docs/api-report.json +4 -4
- package/docs/child-pipeline-composition.md +24 -19
- package/docs/concepts.md +13 -11
- package/docs/getting-started.md +3 -3
- package/docs/recipes.md +5 -5
- package/docs/remote-step-composition.md +4 -3
- package/examples/best-effort.ts +1 -1
- package/examples/cancellation-and-testing.ts +1 -1
- package/examples/catalog/pipelines/enrich.ts +3 -3
- package/examples/catalog/pipelines/import.ts +2 -2
- package/examples/catalog/pipelines/normalize.ts +1 -1
- package/examples/catalog/pipelines/publish.ts +1 -1
- package/examples/catalog/tubeless.project.ts +2 -0
- package/examples/child-pipeline.ts +4 -4
- package/examples/cli-job.ts +1 -1
- package/examples/conditional-step.ts +2 -2
- package/examples/fan-out-progress.ts +3 -3
- package/examples/host-embedding.ts +1 -1
- package/examples/live-tui.ts +1 -1
- package/examples/node-artifacts.ts +1 -1
- package/examples/peloton.ts +9 -6
- package/examples/publish-with-gates.ts +1 -1
- package/examples/remote-steps.ts +2 -2
- package/examples/resumable-enrichment.ts +1 -1
- package/examples/tracing.ts +1 -1
- package/examples/typed-import.ts +1 -1
- package/examples/validated-boundaries.ts +1 -1
- package/package.json +1 -1
- package/skills/tubeless/SKILL.md +6 -4
- package/skills/tubeless-make-pipeline/SKILL.md +5 -4
package/docs/api-report.json
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"StepFactory",
|
|
70
70
|
"StepSkipDecision"
|
|
71
71
|
],
|
|
72
|
-
"sha256": "
|
|
72
|
+
"sha256": "84b22a259773f3dcfeb91a3bae96ccef79e4279664946faa25d0219778ef6fd2",
|
|
73
73
|
"specifier": "tubeless"
|
|
74
74
|
},
|
|
75
75
|
{
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"ReporterTerminalCapabilities",
|
|
110
110
|
"RunReporterConfig"
|
|
111
111
|
],
|
|
112
|
-
"sha256": "
|
|
112
|
+
"sha256": "fe37491a52df175b666ebffc497f8afea9ad94aa2bb97841b5e7f22e1b71a04c",
|
|
113
113
|
"specifier": "tubeless/cli"
|
|
114
114
|
},
|
|
115
115
|
{
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
"PipelineProjectManifestInput",
|
|
172
172
|
"validatePipelineDocument"
|
|
173
173
|
],
|
|
174
|
-
"sha256": "
|
|
174
|
+
"sha256": "316af8da2ed6a5708004ed3d52fb2311a81e19789796bdc926a5902764472a72",
|
|
175
175
|
"specifier": "tubeless/project"
|
|
176
176
|
},
|
|
177
177
|
{
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
"PipelineTestRuntimeOptions",
|
|
186
186
|
"PipelineTestSleep"
|
|
187
187
|
],
|
|
188
|
-
"sha256": "
|
|
188
|
+
"sha256": "6eb6f55eaa2bb1b75a1085dafd5e136dc5d76566dfe405a60b40942a5e40b958",
|
|
189
189
|
"specifier": "tubeless/testing"
|
|
190
190
|
},
|
|
191
191
|
{
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Use a child pipeline when part of a workflow is useful on its own and should
|
|
4
4
|
also run inside a larger pipeline. Tubeless provides two step builders:
|
|
5
5
|
|
|
6
|
-
| Builder
|
|
7
|
-
|
|
|
8
|
-
| `
|
|
9
|
-
| `
|
|
6
|
+
| Builder | Use it to | Step output |
|
|
7
|
+
| ----------------- | ----------------------------------------------- | ---------------------------------------- |
|
|
8
|
+
| `fromPipeline` | Run one child pipeline | The child's final result |
|
|
9
|
+
| `forEachPipeline` | Run the same child pipeline for a list of items | An array of child results in input order |
|
|
10
10
|
|
|
11
11
|
Both builders infer the child options and result types. They forward the
|
|
12
12
|
parent's runtime context and report child progress through the parent step.
|
|
@@ -20,7 +20,9 @@ parent step's dependency outputs and context, and returns the inputs the child
|
|
|
20
20
|
needs. Add `mapResult` if the parent needs a different result shape.
|
|
21
21
|
|
|
22
22
|
```ts
|
|
23
|
-
const
|
|
23
|
+
const { fromPipeline } = createSteps<ParentOptions>();
|
|
24
|
+
|
|
25
|
+
const seedIndexStage = fromPipeline("seed-index", {
|
|
24
26
|
pipeline: IndexSeedPipeline,
|
|
25
27
|
dependsOn: [seedCatalogStage],
|
|
26
28
|
description: "Seed a precomputed search index",
|
|
@@ -38,7 +40,9 @@ Use `items` to return the list to process, `key` for stable item IDs, and
|
|
|
38
40
|
`concurrency` to limit simultaneous child runs:
|
|
39
41
|
|
|
40
42
|
```ts
|
|
41
|
-
const
|
|
43
|
+
const { forEachPipeline } = createSteps<ParentOptions>();
|
|
44
|
+
|
|
45
|
+
const processShards = forEachPipeline("process-shards", {
|
|
42
46
|
pipeline: ShardPipeline,
|
|
43
47
|
dependsOn: [resolveShards],
|
|
44
48
|
items: ({ "resolve-shards": shards }) => shards,
|
|
@@ -53,10 +57,12 @@ const processShards = step.forEachPipeline("process-shards", {
|
|
|
53
57
|
});
|
|
54
58
|
```
|
|
55
59
|
|
|
56
|
-
To skip the whole step when there are no items,
|
|
60
|
+
To skip the whole step when there are no items, add `skip` to its definition:
|
|
57
61
|
|
|
58
62
|
```ts
|
|
59
|
-
const
|
|
63
|
+
const { forEachPipeline } = createSteps<ParentOptions>();
|
|
64
|
+
|
|
65
|
+
const processShards = forEachPipeline("process-shards", {
|
|
60
66
|
pipeline: ShardPipeline,
|
|
61
67
|
dependsOn: [resolveShards],
|
|
62
68
|
skip: ({ "resolve-shards": shards }) =>
|
|
@@ -80,17 +86,16 @@ Keys must be stable and unique; duplicate keys fail before any child starts.
|
|
|
80
86
|
All running children finish or cancel before the parent step returns a failure.
|
|
81
87
|
The parent therefore cannot finalize while those children are still running.
|
|
82
88
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
skip, or `{ reason, value }` to skip with an output.
|
|
86
|
-
publishes `undefined`. Policy skips allow required
|
|
87
|
-
dependents must handle `undefined` explicitly.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`
|
|
91
|
-
`
|
|
92
|
-
|
|
93
|
-
run children, or apply `mapResult`.
|
|
89
|
+
Add `skip` to a `fromPipeline` or `forEachPipeline` definition when the entire
|
|
90
|
+
child step may be intentionally omitted. The callback returns `false` to proceed,
|
|
91
|
+
a non-empty reason string to skip, or `{ reason, value }` to skip with an output.
|
|
92
|
+
A skip without a value publishes `undefined`. Policy skips allow required
|
|
93
|
+
dependents to run, so those dependents must handle `undefined` explicitly.
|
|
94
|
+
|
|
95
|
+
Without `skip`, `fromPipeline` returns `T` and `forEachPipeline` returns
|
|
96
|
+
`readonly T[]`. Adding `skip` widens those types to `T | undefined` and
|
|
97
|
+
`readonly T[] | undefined`. For fan-out, the skip value is the complete result
|
|
98
|
+
array. Skipping does not call `items`, run children, or apply `mapResult`.
|
|
94
99
|
|
|
95
100
|
## Planning and execution controls
|
|
96
101
|
|
package/docs/concepts.md
CHANGED
|
@@ -6,8 +6,9 @@ A pipeline describes a series of steps and the values passed between them.
|
|
|
6
6
|
Each step has an ID, a function to run, and any dependencies. A finalizer
|
|
7
7
|
combines step outputs into the value returned to the caller.
|
|
8
8
|
|
|
9
|
-
Create steps with one `createSteps<TOptions>()`
|
|
10
|
-
step object to another step's
|
|
9
|
+
Create steps with one `createSteps<TOptions>()` factory per pipeline. Take its
|
|
10
|
+
`step` constructor, then pass a step object to another step's
|
|
11
|
+
`dependsOn` array to require its output;
|
|
11
12
|
TypeScript then infers that input's type.
|
|
12
13
|
|
|
13
14
|
`definePipeline` checks the graph immediately. Duplicate or reserved IDs,
|
|
@@ -45,15 +46,16 @@ or fail while running. These cases have different effects on dependent steps.
|
|
|
45
46
|
| Outcome | What happened | What dependents receive |
|
|
46
47
|
| --------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
47
48
|
| Structural skip | A filter, dry-run rule, missing dependency, abort, or earlier failure prevented execution | No output; required dependents cannot run |
|
|
48
|
-
| Policy skip | A
|
|
49
|
+
| Policy skip | A step's `skip` predicate decided that work was unnecessary | The supplied skip value, or `undefined` |
|
|
49
50
|
| Failure | The step's work failed | No successful output; required dependents cannot run |
|
|
50
51
|
|
|
51
52
|
Fail-fast is the default: a failure stops new work. Set `continueOnError` to
|
|
52
53
|
allow independent steps to proceed. The run still reports failure, and
|
|
53
54
|
`runOrThrow` still throws. Use `run` to inspect any partial result.
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
Adding `skip` to a step definition makes it skippable. Its output is always
|
|
57
|
+
typed as `T | undefined`, even if all current skip paths return a value.
|
|
58
|
+
Dependent steps must handle that possible absence.
|
|
57
59
|
|
|
58
60
|
## Execution controls
|
|
59
61
|
|
|
@@ -302,7 +304,7 @@ Schema V1. Tubeless accepts these schemas without importing a validation library
|
|
|
302
304
|
`resultSchema` to `definePipeline`:
|
|
303
305
|
|
|
304
306
|
```ts
|
|
305
|
-
const step = createSteps(optionsSchema);
|
|
307
|
+
const { step } = createSteps(optionsSchema);
|
|
306
308
|
|
|
307
309
|
const parse = step("parse", {
|
|
308
310
|
outputSchema: parsedRowsSchema,
|
|
@@ -349,18 +351,18 @@ options-schema factory scopes.
|
|
|
349
351
|
|
|
350
352
|
## Child pipelines
|
|
351
353
|
|
|
352
|
-
Use `
|
|
353
|
-
|
|
354
|
+
Use `fromPipeline` to run one reusable child pipeline. Use `forEachPipeline` to
|
|
355
|
+
run that child for each item in a list. The parent
|
|
354
356
|
plan contains one wrapper step; child activity appears as progress beneath it.
|
|
355
357
|
Read [child-pipeline composition](./child-pipeline-composition.md) for option
|
|
356
358
|
mapping, selection, failures, and progress controls.
|
|
357
359
|
|
|
358
|
-
|
|
359
|
-
omitted. Its output can be `undefined`, which dependent code must handle.
|
|
360
|
+
Add `skip` to either child-step definition when the entire child step may be
|
|
361
|
+
intentionally omitted. Its output can be `undefined`, which dependent code must handle.
|
|
360
362
|
|
|
361
363
|
## Remote steps
|
|
362
364
|
|
|
363
|
-
Use `
|
|
365
|
+
Use `fromRemote` to call a service or execution engine from one step.
|
|
364
366
|
The parent pipeline still runs locally. The step's `remote.engine` and optional
|
|
365
367
|
`remote.target` describe the destination for display and inspection.
|
|
366
368
|
|
package/docs/getting-started.md
CHANGED
|
@@ -51,15 +51,15 @@ const lines = [" First row ", "", "Second row"];
|
|
|
51
51
|
|
|
52
52
|
## 2. Create typed steps
|
|
53
53
|
|
|
54
|
-
Call `createSteps` once
|
|
55
|
-
step returns the input lines. The `normalize` step declares `dependsOn: [load]`,
|
|
54
|
+
Call `createSteps` once and take its `step` constructor for these options. The
|
|
55
|
+
`load` step returns the input lines. The `normalize` step declares `dependsOn: [load]`,
|
|
56
56
|
so it receives the output of `load` as a typed input. TypeScript infers that
|
|
57
57
|
`rows` is a read-only array of strings.
|
|
58
58
|
|
|
59
59
|
```ts
|
|
60
60
|
import { createSteps } from "tubeless";
|
|
61
61
|
|
|
62
|
-
const step = createSteps<ImportOptions>();
|
|
62
|
+
const { step } = createSteps<ImportOptions>();
|
|
63
63
|
|
|
64
64
|
const load = step("load", {
|
|
65
65
|
description: "Read source rows",
|
package/docs/recipes.md
CHANGED
|
@@ -15,12 +15,12 @@ one of these recipes.
|
|
|
15
15
|
| Validate options, outputs, and results | [`validated-boundaries.ts`](../examples/validated-boundaries.ts) | Standard Schema, `outputSchema`, `resultSchema` |
|
|
16
16
|
| Inspect, plan, or graph a pipeline or command | [`typed-import.ts`](../examples/typed-import.ts) | `tubeless inspect`, `tubeless plan`, `tubeless graph`, `toMermaid` |
|
|
17
17
|
| Safe write/publish preview | [`publish-with-gates.ts`](../examples/publish-with-gates.ts) | `dryRun`, `optionalDependsOn`, `skipAfterFailureOf` |
|
|
18
|
-
| Deliberately omit unnecessary work | [`conditional-step.ts`](../examples/conditional-step.ts) | `step
|
|
18
|
+
| Deliberately omit unnecessary work | [`conditional-step.ts`](../examples/conditional-step.ts) | `step` with `skip`, valued skip, skip-aware output typing |
|
|
19
19
|
| Preserve independent work after failure | [`best-effort.ts`](../examples/best-effort.ts) | `continueOnError`, structured `run` result |
|
|
20
20
|
| Compose one reusable workflow | [`child-pipeline.ts`](../examples/child-pipeline.ts) | `fromPipeline`, `mapOptions`, resolved async `mapResult` |
|
|
21
21
|
| Call a real HTTP service | [`remote-steps.ts`](../examples/remote-steps.ts) | `fromRemote`, fetch cancellation, validated HTTP output |
|
|
22
22
|
| Host a pipeline in a durable engine | [`host-embedding.ts`](../examples/host-embedding.ts) | `runOrThrow`, pass `correlationId` / `parentRunId` |
|
|
23
|
-
| Fan out over runtime items | [`fan-out-progress.ts`](../examples/fan-out-progress.ts) | `forEachPipeline
|
|
23
|
+
| Fan out over runtime items | [`fan-out-progress.ts`](../examples/fan-out-progress.ts) | `forEachPipeline` with `skip`, stable keys, concurrency, progress |
|
|
24
24
|
| Inspect keyed fan-out failures | [`fan-out-progress.ts`](../examples/fan-out-progress.ts) | `error.fanOut`, bounded diagnostics, caller-directed reruns |
|
|
25
25
|
| Show determinate progress | [`fan-out-progress.ts`](../examples/fan-out-progress.ts) | `reportProgress`, bounded live CLI rows, complete final trees |
|
|
26
26
|
| Watch the live TTY reporter | [`live-tui.ts`](../examples/live-tui.ts) | named steps, nested `details`; persist with `--store` |
|
|
@@ -68,12 +68,12 @@ pipeline does not require credentials.
|
|
|
68
68
|
1. Use an ordinary step for one unit of domain work.
|
|
69
69
|
2. Set `dryRun: "skip"` or provide a side-effect-free `dryRun` handler before
|
|
70
70
|
exposing side-effecting work through CLI dry-run support.
|
|
71
|
-
3.
|
|
71
|
+
3. Add `skip` only for a successful policy decision, never to hide an error.
|
|
72
72
|
4. Use a child pipeline when the child has value independently; use a normal
|
|
73
73
|
helper function when it does not.
|
|
74
74
|
5. Use `forEachPipeline` when every item needs child-pipeline lifecycle and
|
|
75
|
-
reporting.
|
|
76
|
-
|
|
75
|
+
reporting. Add `skip` to `forEachPipeline` when policy may omit the whole
|
|
76
|
+
fan-out and a skip should appear in reports. Use `runConcurrent`
|
|
77
77
|
for lightweight worker functions that should
|
|
78
78
|
throw on the first failure. Use `runConcurrentSettled` when the caller needs
|
|
79
79
|
completed results plus that failure without throwing.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Remote-step composition
|
|
2
2
|
|
|
3
|
-
Use `
|
|
3
|
+
Use `fromRemote` when a pipeline step needs to call a service or execution
|
|
4
4
|
engine outside the current process. The step sends input through an adapter,
|
|
5
5
|
waits for the result, and validates it before dependent steps run.
|
|
6
6
|
|
|
@@ -10,8 +10,9 @@ The required fields are `adapter`, `mapInput`, and `outputSchema`:
|
|
|
10
10
|
- `mapInput` builds the request from local step inputs and context.
|
|
11
11
|
- `outputSchema` checks the returned value and provides its type to dependents.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
with other skippable steps, dependent code must handle a possible
|
|
13
|
+
Add `skip` to the `fromRemote` definition when the request may be intentionally
|
|
14
|
+
omitted. As with other skippable steps, dependent code must handle a possible
|
|
15
|
+
`undefined`.
|
|
15
16
|
See the [HTTP example](../examples/remote-steps.ts) for a complete implementation.
|
|
16
17
|
|
|
17
18
|
## Choose where the pipeline runs
|
package/examples/best-effort.ts
CHANGED
|
@@ -43,7 +43,7 @@ interface EnrichOptions {
|
|
|
43
43
|
lines: readonly string[];
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const step = createSteps<EnrichOptions>();
|
|
46
|
+
const { step, fromRemote } = createSteps<EnrichOptions>();
|
|
47
47
|
|
|
48
48
|
const parseRows = step("parse-rows", {
|
|
49
49
|
description: "Trim and drop blank rows from caller input.",
|
|
@@ -66,7 +66,7 @@ const enrichAdapter: RemoteStepAdapter<
|
|
|
66
66
|
},
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
const enrichRows =
|
|
69
|
+
const enrichRows = fromRemote("enrich-rows", {
|
|
70
70
|
dependsOn: [parseRows],
|
|
71
71
|
description: "Rehearse a remote enrich during dry-run.",
|
|
72
72
|
adapter: enrichAdapter,
|
|
@@ -84,7 +84,7 @@ const chargeAdapter: RemoteStepAdapter<EnrichOptions, { orderId: string }, Charg
|
|
|
84
84
|
invoke: async (payload) => ({ charged: true, orderId: payload.orderId }),
|
|
85
85
|
};
|
|
86
86
|
|
|
87
|
-
const chargeOrder =
|
|
87
|
+
const chargeOrder = fromRemote("charge-order", {
|
|
88
88
|
dependsOn: [enrichRows],
|
|
89
89
|
description: "Skip remote charge during dry-run.",
|
|
90
90
|
adapter: chargeAdapter,
|
|
@@ -5,14 +5,14 @@ interface ImportOptions {
|
|
|
5
5
|
lines: readonly string[];
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
const step = createSteps<ImportOptions>();
|
|
8
|
+
const { step, forEachPipeline } = createSteps<ImportOptions>();
|
|
9
9
|
|
|
10
10
|
const loadRows = step("load-rows", {
|
|
11
11
|
description: "Read raw input records from the caller.",
|
|
12
12
|
run: (_inputs, context) => context.options.lines,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
const normalizedImport =
|
|
15
|
+
const normalizedImport = forEachPipeline("normalized-import", {
|
|
16
16
|
dependsOn: [loadRows],
|
|
17
17
|
description: "Normalize rows through mapped child pipelines when input is present.",
|
|
18
18
|
pipeline: NormalizePipeline,
|
|
@@ -4,7 +4,7 @@ interface NormalizeOptions {
|
|
|
4
4
|
rows: readonly string[];
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
const step = createSteps<NormalizeOptions>();
|
|
7
|
+
const { step } = createSteps<NormalizeOptions>();
|
|
8
8
|
|
|
9
9
|
const normalizeRows = step("normalize-rows", {
|
|
10
10
|
description: "Trim, lowercase, and drop blank rows.",
|
|
@@ -9,7 +9,7 @@ interface Artifact {
|
|
|
9
9
|
size: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const step = createSteps<PublishOptions>();
|
|
12
|
+
const { step } = createSteps<PublishOptions>();
|
|
13
13
|
|
|
14
14
|
const buildArtifact = step("build-artifact", {
|
|
15
15
|
description: "Build an artifact that can be validated and published.",
|
|
@@ -7,6 +7,8 @@ import { definePipelineProject } from "tubeless/project";
|
|
|
7
7
|
// execution. Command argv flags stay --step/--target, while mapOptions and hooks
|
|
8
8
|
// receive stepIds and targets.
|
|
9
9
|
// When converting existing code, use the tubeless-make-pipeline agent skill.
|
|
10
|
+
// Pipeline files destructure the constructors they need from createSteps.
|
|
11
|
+
// Adding skip to any definition marks that step as intentionally omittable.
|
|
10
12
|
// Adapt this layout to the consumer; retain its established IDs and caller contracts.
|
|
11
13
|
// For caller-directed fan-out reruns, see ../fan-out-progress.ts: inspect
|
|
12
14
|
// error.fanOut and check omitted entries and truncated keys before selecting inputs.
|
|
@@ -4,7 +4,7 @@ interface NormalizeOptions {
|
|
|
4
4
|
rows: readonly string[];
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
const normalizeStep = createSteps<NormalizeOptions>();
|
|
7
|
+
const { step: normalizeStep } = createSteps<NormalizeOptions>();
|
|
8
8
|
|
|
9
9
|
const normalizeRows = normalizeStep("normalize-rows", {
|
|
10
10
|
run: (_inputs, context) =>
|
|
@@ -22,9 +22,9 @@ interface ImportOptions {
|
|
|
22
22
|
lines: readonly string[];
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const { fromPipeline } = createSteps<ImportOptions>();
|
|
26
26
|
|
|
27
|
-
const normalizedImport =
|
|
27
|
+
const normalizedImport = fromPipeline("normalized-import", {
|
|
28
28
|
pipeline: NormalizePipeline,
|
|
29
29
|
mapOptions: (_inputs, context) => ({ rows: context.options.lines }),
|
|
30
30
|
mapResult: (rows) => ({ count: rows.length, rows }),
|
|
@@ -44,7 +44,7 @@ export async function runChildPipelineExample() {
|
|
|
44
44
|
|
|
45
45
|
// oxlint-disable-next-line no-constant-condition -- typecheck-only compile probe
|
|
46
46
|
if (false) {
|
|
47
|
-
|
|
47
|
+
fromPipeline("invalid-child-selection", {
|
|
48
48
|
pipeline: NormalizePipeline,
|
|
49
49
|
// @ts-expect-error Child run options are checked against its declared target IDs.
|
|
50
50
|
mapOptions: () => ({ rows: [], targets: ["normalise-rows"] }),
|
package/examples/cli-job.ts
CHANGED
|
@@ -5,9 +5,9 @@ interface CacheOptions {
|
|
|
5
5
|
refresh: boolean;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
const step = createSteps<CacheOptions>();
|
|
8
|
+
const { step } = createSteps<CacheOptions>();
|
|
9
9
|
|
|
10
|
-
const resolveValue = step
|
|
10
|
+
const resolveValue = step("resolve-value", {
|
|
11
11
|
description: "Reuse a cached value unless a refresh is required",
|
|
12
12
|
skip: (_inputs, context) =>
|
|
13
13
|
!context.options.refresh && context.options.cachedValue !== undefined
|
|
@@ -5,7 +5,7 @@ interface ShardOptions {
|
|
|
5
5
|
shardId: string;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
const shardStep = createSteps<ShardOptions>();
|
|
8
|
+
const { step: shardStep } = createSteps<ShardOptions>();
|
|
9
9
|
|
|
10
10
|
const processRecords = shardStep("process-records", {
|
|
11
11
|
name: "Process records",
|
|
@@ -46,9 +46,9 @@ interface FanOutOptions {
|
|
|
46
46
|
shards: readonly { id: string; records: readonly string[] }[];
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
const
|
|
49
|
+
const { forEachPipeline } = createSteps<FanOutOptions>();
|
|
50
50
|
|
|
51
|
-
const processShards =
|
|
51
|
+
const processShards = forEachPipeline("process-shards", {
|
|
52
52
|
pipeline: ShardPipeline,
|
|
53
53
|
description: "Process shards with bounded concurrency and stable identities",
|
|
54
54
|
skip: (_inputs, context) =>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSteps, definePipeline, requireOutputs } from "tubeless";
|
|
2
2
|
|
|
3
|
-
const step = createSteps<{ lines: readonly string[] }>();
|
|
3
|
+
const { step } = createSteps<{ lines: readonly string[] }>();
|
|
4
4
|
const normalize = step("normalize", {
|
|
5
5
|
description: "Normalize rows within one host-owned invocation",
|
|
6
6
|
run: (_inputs, context) => context.options.lines.map((line) => line.trim()),
|
package/examples/live-tui.ts
CHANGED
|
@@ -11,7 +11,7 @@ const SOURCES = ["west", "east", "north", "south"] as const;
|
|
|
11
11
|
const ARTIFACTS = ["index.json", "manifest.json", "checksums.txt"] as const;
|
|
12
12
|
const DEFAULT_DELAY_MS = 450;
|
|
13
13
|
|
|
14
|
-
const step = createSteps<LiveTuiOptions>();
|
|
14
|
+
const { step } = createSteps<LiveTuiOptions>();
|
|
15
15
|
|
|
16
16
|
function sequentialStatus(index: number, current: number): DetailStatus {
|
|
17
17
|
if (index < current) return "completed";
|
|
@@ -2,7 +2,7 @@ import { createSteps, definePipeline } from "tubeless";
|
|
|
2
2
|
import { definePaths, writeJson } from "tubeless/node";
|
|
3
3
|
|
|
4
4
|
const paths = definePaths({ artifact: "build/artifacts/rows.json" });
|
|
5
|
-
const step = createSteps<{ rows: readonly string[] }>();
|
|
5
|
+
const { step } = createSteps<{ rows: readonly string[] }>();
|
|
6
6
|
|
|
7
7
|
const normalize = step("normalize", {
|
|
8
8
|
description: "Normalize the caller's rows before writing an artifact.",
|
package/examples/peloton.ts
CHANGED
|
@@ -39,13 +39,16 @@ const START_LIST_FILES = ["start-list.json", "timing.log", "weather.txt"] as con
|
|
|
39
39
|
|
|
40
40
|
type DetailStatus = "completed" | "pending" | "running";
|
|
41
41
|
|
|
42
|
-
const normalizeStep = createSteps<{
|
|
43
|
-
|
|
42
|
+
const { step: normalizeStep } = createSteps<{
|
|
43
|
+
delay: number;
|
|
44
|
+
riders: readonly PelotonRider[];
|
|
45
|
+
}>();
|
|
46
|
+
const { step: inspectStep } = createSteps<{
|
|
44
47
|
delay: number;
|
|
45
48
|
kit: readonly string[];
|
|
46
49
|
riderId: string;
|
|
47
50
|
}>();
|
|
48
|
-
const step = createSteps<PelotonOptions>();
|
|
51
|
+
const { step, fromPipeline, forEachPipeline } = createSteps<PelotonOptions>();
|
|
49
52
|
|
|
50
53
|
function sequentialStatus(index: number, current: number): DetailStatus {
|
|
51
54
|
if (index < current) return "completed";
|
|
@@ -176,7 +179,7 @@ const discoverPeloton = step("discover-peloton", {
|
|
|
176
179
|
},
|
|
177
180
|
});
|
|
178
181
|
|
|
179
|
-
const resolveWeather = step
|
|
182
|
+
const resolveWeather = step("resolve-weather", {
|
|
180
183
|
name: "Resolve Weather",
|
|
181
184
|
description: "Reuse a posted forecast unless the race asks for a refresh.",
|
|
182
185
|
skip: (_inputs, context) =>
|
|
@@ -192,7 +195,7 @@ const resolveWeather = step.skippable("resolve-weather", {
|
|
|
192
195
|
},
|
|
193
196
|
});
|
|
194
197
|
|
|
195
|
-
const normalizedBikes =
|
|
198
|
+
const normalizedBikes = fromPipeline("normalize-bikes", {
|
|
196
199
|
name: "Normalize Bikes",
|
|
197
200
|
dependsOn: [discoverPeloton],
|
|
198
201
|
description: "Normalize kit lists through the independently useful child pipeline.",
|
|
@@ -203,7 +206,7 @@ const normalizedBikes = step.fromPipeline("normalize-bikes", {
|
|
|
203
206
|
}),
|
|
204
207
|
});
|
|
205
208
|
|
|
206
|
-
const inspectBikes =
|
|
209
|
+
const inspectBikes = forEachPipeline("inspect-bikes", {
|
|
207
210
|
name: "Inspect Bikes",
|
|
208
211
|
dependsOn: [normalizedBikes],
|
|
209
212
|
description: "Fan out bike inspections with bounded concurrency and stable rider keys.",
|
|
@@ -9,7 +9,7 @@ interface Artifact {
|
|
|
9
9
|
size: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const step = createSteps<PublishOptions>();
|
|
12
|
+
const { step } = createSteps<PublishOptions>();
|
|
13
13
|
|
|
14
14
|
const buildArtifact = step("build-artifact", {
|
|
15
15
|
description: "Build an artifact that can be validated and published.",
|
package/examples/remote-steps.ts
CHANGED
|
@@ -75,13 +75,13 @@ const enrichAdapter: RemoteStepAdapter<RemoteStepsOptions, RemotePayload, unknow
|
|
|
75
75
|
},
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
const step = createSteps<RemoteStepsOptions>();
|
|
78
|
+
const { step, fromRemote } = createSteps<RemoteStepsOptions>();
|
|
79
79
|
const parse = step("parse", {
|
|
80
80
|
description: "Trim input rows before sending them to the remote service",
|
|
81
81
|
run: (_inputs, context) =>
|
|
82
82
|
context.options.lines.map((line) => line.trim()).filter((line) => line.length > 0),
|
|
83
83
|
});
|
|
84
|
-
const enrich =
|
|
84
|
+
const enrich = fromRemote("enrich", {
|
|
85
85
|
description: "Validate the HTTP service result before local consumption",
|
|
86
86
|
dependsOn: [parse],
|
|
87
87
|
adapter: enrichAdapter,
|
|
@@ -15,7 +15,7 @@ interface EnrichedItem {
|
|
|
15
15
|
summary: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const step = createSteps<EnrichmentOptions>();
|
|
18
|
+
const { step } = createSteps<EnrichmentOptions>();
|
|
19
19
|
|
|
20
20
|
const loadPendingItems = step("load-pending-items", {
|
|
21
21
|
description: "Skip work that was already checkpointed by a previous run.",
|
package/examples/tracing.ts
CHANGED
|
@@ -99,7 +99,7 @@ interface TracingExampleOptions {
|
|
|
99
99
|
rows: readonly string[];
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
const step = createSteps<TracingExampleOptions>();
|
|
102
|
+
const { step } = createSteps<TracingExampleOptions>();
|
|
103
103
|
const normalize = step("normalize", {
|
|
104
104
|
run: async (_inputs, context) =>
|
|
105
105
|
withRetry(
|
package/examples/typed-import.ts
CHANGED
package/package.json
CHANGED
package/skills/tubeless/SKILL.md
CHANGED
|
@@ -25,16 +25,18 @@ the installed declarations before using them; do not silently upgrade Tubeless.
|
|
|
25
25
|
|
|
26
26
|
## Authoring decisions
|
|
27
27
|
|
|
28
|
-
- Use `createSteps<TDomainOptions>()` per pipeline
|
|
29
|
-
|
|
28
|
+
- Use `createSteps<TDomainOptions>()` per pipeline and destructure every
|
|
29
|
+
constructor it needs: `step`, `fromPipeline`, `fromRemote`, and/or
|
|
30
|
+
`forEachPipeline`. Domain options contain business inputs; pass built-in
|
|
31
|
+
controls separately to `run(options, controls?)`.
|
|
30
32
|
- Give steps stable kebab-case IDs and descriptions of their domain work.
|
|
31
33
|
`name` is an optional display label. Return values from steps and consume
|
|
32
34
|
inferred dependency outputs instead of sharing mutable state.
|
|
33
35
|
- Use `dependsOn` for required outputs, `optionalDependsOn` for expected
|
|
34
36
|
absence, and `skipAfterFailureOf` for a failure gate without a required value.
|
|
35
37
|
Keep publication dependent on successful validation.
|
|
36
|
-
-
|
|
37
|
-
`T | undefined` output explicitly; do not turn exceptions into skips.
|
|
38
|
+
- Add `skip` to a step definition for an intentional successful omission. Handle
|
|
39
|
+
its `T | undefined` output explicitly; do not turn exceptions into skips.
|
|
38
40
|
- Set `dryRun: "skip"` on writes and other external side effects, or supply a
|
|
39
41
|
side-effect-free typed preview handler. Unmarked steps still run in dry runs.
|
|
40
42
|
- Use `requireOutputs` when the final result requires specific outputs. A plain
|
|
@@ -57,9 +57,10 @@ has a coherent output and a reason to appear in a plan or failure report.
|
|
|
57
57
|
|
|
58
58
|
## Keep the graph typed
|
|
59
59
|
|
|
60
|
-
Use one `createSteps<TDomainOptions>()`
|
|
61
|
-
pipeline.
|
|
62
|
-
in domain options; built-in run
|
|
60
|
+
Use one `createSteps<TDomainOptions>()` factory and one `definePipeline` per
|
|
61
|
+
pipeline. Define ordinary steps with the returned `step` constructor. Import from `tubeless` and
|
|
62
|
+
its public subpaths. Keep business inputs in domain options; built-in run
|
|
63
|
+
controls belong in the separate controls argument.
|
|
63
64
|
|
|
64
65
|
Give each step a stable literal kebab-case ID and an operational description.
|
|
65
66
|
Let `dependsOn: [step]` infer input types from returned outputs. Preserve
|
|
@@ -88,7 +89,7 @@ a missing required output to an empty array, `false`, or a fake success value.
|
|
|
88
89
|
write to make dry runs pass.
|
|
89
90
|
- Keep validation required for publication. Test this even with
|
|
90
91
|
`continueOnError` if the caller enables best-effort execution.
|
|
91
|
-
-
|
|
92
|
+
- Add `skip` only for a successful policy decision. Preserve thrown
|
|
92
93
|
failures and their causes; branch on structured Tubeless error fields.
|
|
93
94
|
Map pipeline errors at the caller boundary if existing callers require a
|
|
94
95
|
specific error contract.
|