tubeless 0.1.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/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/abort.d.ts +12 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +49 -0
- package/dist/abort.js.map +1 -0
- package/dist/batch.d.ts +22 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/batch.js +73 -0
- package/dist/batch.js.map +1 -0
- package/dist/checkpoint.d.ts +33 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint.js +71 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/child-execution.d.ts +30 -0
- package/dist/child-execution.d.ts.map +1 -0
- package/dist/child-execution.js +270 -0
- package/dist/child-execution.js.map +1 -0
- package/dist/cli-command.d.ts +29 -0
- package/dist/cli-command.d.ts.map +1 -0
- package/dist/cli-command.js +314 -0
- package/dist/cli-command.js.map +1 -0
- package/dist/cli-parser.d.ts +28 -0
- package/dist/cli-parser.d.ts.map +1 -0
- package/dist/cli-parser.js +557 -0
- package/dist/cli-parser.js.map +1 -0
- package/dist/cli-pipeline-command.d.ts +84 -0
- package/dist/cli-pipeline-command.d.ts.map +1 -0
- package/dist/cli-pipeline-command.js +185 -0
- package/dist/cli-pipeline-command.js.map +1 -0
- package/dist/cli-types.d.ts +220 -0
- package/dist/cli-types.d.ts.map +1 -0
- package/dist/cli-types.js +19 -0
- package/dist/cli-types.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +8 -0
- package/dist/env.js.map +1 -0
- package/dist/file-utils.d.ts +4 -0
- package/dist/file-utils.d.ts.map +1 -0
- package/dist/file-utils.js +35 -0
- package/dist/file-utils.js.map +1 -0
- package/dist/interactive-reporter.d.ts +38 -0
- package/dist/interactive-reporter.d.ts.map +1 -0
- package/dist/interactive-reporter.js +308 -0
- package/dist/interactive-reporter.js.map +1 -0
- package/dist/lifecycle.d.ts +17 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +77 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/live-ticker.d.ts +21 -0
- package/dist/live-ticker.d.ts.map +1 -0
- package/dist/live-ticker.js +377 -0
- package/dist/live-ticker.js.map +1 -0
- package/dist/mapped-child-progress.d.ts +109 -0
- package/dist/mapped-child-progress.d.ts.map +1 -0
- package/dist/mapped-child-progress.js +133 -0
- package/dist/mapped-child-progress.js.map +1 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +5 -0
- package/dist/node.js.map +1 -0
- package/dist/paths.d.ts +9 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +19 -0
- package/dist/paths.js.map +1 -0
- package/dist/pipeline-command-marker.d.ts +5 -0
- package/dist/pipeline-command-marker.d.ts.map +1 -0
- package/dist/pipeline-command-marker.js +21 -0
- package/dist/pipeline-command-marker.js.map +1 -0
- package/dist/pipeline-diagnostics.d.ts +4 -0
- package/dist/pipeline-diagnostics.d.ts.map +1 -0
- package/dist/pipeline-diagnostics.js +17 -0
- package/dist/pipeline-diagnostics.js.map +1 -0
- package/dist/pipeline-module.d.ts +62 -0
- package/dist/pipeline-module.d.ts.map +1 -0
- package/dist/pipeline-module.js +157 -0
- package/dist/pipeline-module.js.map +1 -0
- package/dist/pipeline.d.ts +606 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +1444 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/progress.d.ts +4 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +8 -0
- package/dist/progress.js.map +1 -0
- package/dist/prompt-select.d.ts +81 -0
- package/dist/prompt-select.d.ts.map +1 -0
- package/dist/prompt-select.js +197 -0
- package/dist/prompt-select.js.map +1 -0
- package/dist/rate-limit.d.ts +7 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +23 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/render.d.ts +21 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +60 -0
- package/dist/render.js.map +1 -0
- package/dist/reporter-entry.d.ts +3 -0
- package/dist/reporter-entry.d.ts.map +1 -0
- package/dist/reporter-entry.js +3 -0
- package/dist/reporter-entry.js.map +1 -0
- package/dist/reporter.d.ts +50 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +167 -0
- package/dist/reporter.js.map +1 -0
- package/dist/retry.d.ts +28 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +59 -0
- package/dist/retry.js.map +1 -0
- package/dist/run-store-sqlite.d.ts +12 -0
- package/dist/run-store-sqlite.d.ts.map +1 -0
- package/dist/run-store-sqlite.js +187 -0
- package/dist/run-store-sqlite.js.map +1 -0
- package/dist/run-store-ui-http.d.ts +5 -0
- package/dist/run-store-ui-http.d.ts.map +1 -0
- package/dist/run-store-ui-http.js +19 -0
- package/dist/run-store-ui-http.js.map +1 -0
- package/dist/run-store-ui-page.d.ts +2 -0
- package/dist/run-store-ui-page.d.ts.map +1 -0
- package/dist/run-store-ui-page.js +759 -0
- package/dist/run-store-ui-page.js.map +1 -0
- package/dist/run-store-ui-protocol.d.ts +28 -0
- package/dist/run-store-ui-protocol.d.ts.map +1 -0
- package/dist/run-store-ui-protocol.js +84 -0
- package/dist/run-store-ui-protocol.js.map +1 -0
- package/dist/run-store-ui-state.d.ts +21 -0
- package/dist/run-store-ui-state.d.ts.map +1 -0
- package/dist/run-store-ui-state.js +65 -0
- package/dist/run-store-ui-state.js.map +1 -0
- package/dist/run-store-ui.d.ts +25 -0
- package/dist/run-store-ui.d.ts.map +1 -0
- package/dist/run-store-ui.js +217 -0
- package/dist/run-store-ui.js.map +1 -0
- package/dist/run-store.d.ts +109 -0
- package/dist/run-store.d.ts.map +1 -0
- package/dist/run-store.js +345 -0
- package/dist/run-store.js.map +1 -0
- package/dist/testing.d.ts +40 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +83 -0
- package/dist/testing.js.map +1 -0
- package/dist/tracing-internal.d.ts +21 -0
- package/dist/tracing-internal.d.ts.map +1 -0
- package/dist/tracing-internal.js +206 -0
- package/dist/tracing-internal.js.map +1 -0
- package/dist/tracing-json.d.ts +12 -0
- package/dist/tracing-json.d.ts.map +1 -0
- package/dist/tracing-json.js +8 -0
- package/dist/tracing-json.js.map +1 -0
- package/dist/tracing-otel.d.ts +36 -0
- package/dist/tracing-otel.d.ts.map +1 -0
- package/dist/tracing-otel.js +60 -0
- package/dist/tracing-otel.js.map +1 -0
- package/dist/tracing.d.ts +48 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +2 -0
- package/dist/tracing.js.map +1 -0
- package/dist/workbench-bin.d.ts +3 -0
- package/dist/workbench-bin.d.ts.map +1 -0
- package/dist/workbench-bin.js +8 -0
- package/dist/workbench-bin.js.map +1 -0
- package/dist/workbench-graph.d.ts +3 -0
- package/dist/workbench-graph.d.ts.map +1 -0
- package/dist/workbench-graph.js +64 -0
- package/dist/workbench-graph.js.map +1 -0
- package/dist/workbench-inspect.d.ts +3 -0
- package/dist/workbench-inspect.d.ts.map +1 -0
- package/dist/workbench-inspect.js +65 -0
- package/dist/workbench-inspect.js.map +1 -0
- package/dist/workbench-plan.d.ts +3 -0
- package/dist/workbench-plan.d.ts.map +1 -0
- package/dist/workbench-plan.js +72 -0
- package/dist/workbench-plan.js.map +1 -0
- package/dist/workbench-run.d.ts +13 -0
- package/dist/workbench-run.d.ts.map +1 -0
- package/dist/workbench-run.js +115 -0
- package/dist/workbench-run.js.map +1 -0
- package/dist/workbench-shared.d.ts +67 -0
- package/dist/workbench-shared.d.ts.map +1 -0
- package/dist/workbench-shared.js +163 -0
- package/dist/workbench-shared.js.map +1 -0
- package/dist/workbench-studio.d.ts +26 -0
- package/dist/workbench-studio.d.ts.map +1 -0
- package/dist/workbench-studio.js +57 -0
- package/dist/workbench-studio.js.map +1 -0
- package/dist/workbench-ui.d.ts +3 -0
- package/dist/workbench-ui.d.ts.map +1 -0
- package/dist/workbench-ui.js +262 -0
- package/dist/workbench-ui.js.map +1 -0
- package/dist/workbench.d.ts +5 -0
- package/dist/workbench.d.ts.map +1 -0
- package/dist/workbench.js +43 -0
- package/dist/workbench.js.map +1 -0
- package/docs/README.md +31 -0
- package/docs/agent-guide.md +168 -0
- package/docs/api-reference.md +269 -0
- package/docs/api-report.json +298 -0
- package/docs/child-pipeline-composition.md +185 -0
- package/docs/cli.md +118 -0
- package/docs/concepts.md +502 -0
- package/docs/getting-started.md +162 -0
- package/docs/llms.txt +62 -0
- package/docs/recipes.md +51 -0
- package/docs/studio.md +86 -0
- package/examples/best-effort.ts +42 -0
- package/examples/cancellation-and-testing.ts +45 -0
- package/examples/child-pipeline.ts +51 -0
- package/examples/cli-job.ts +86 -0
- package/examples/conditional-step.ts +41 -0
- package/examples/fan-out-progress.ts +53 -0
- package/examples/local-observability.ts +80 -0
- package/examples/publish-with-gates.ts +56 -0
- package/examples/rendering.ts +34 -0
- package/examples/resumable-enrichment.ts +75 -0
- package/examples/tracing.ts +35 -0
- package/examples/tsconfig.json +15 -0
- package/examples/typed-import.ts +54 -0
- package/examples/validated-boundaries.ts +75 -0
- package/package.json +156 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chet Mancini
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Tubeless
|
|
2
|
+
|
|
3
|
+
Typed, observable data pipelines you import from TypeScript or run from a Bun
|
|
4
|
+
CLI. It is a library, not a hosted workflow engine or a Make/npm-scripts
|
|
5
|
+
replacement.
|
|
6
|
+
|
|
7
|
+
The first public version is `0.1.0`. The API is still being proven; expect
|
|
8
|
+
change before 1.0.
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install tubeless
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Also: `pnpm add tubeless`, `yarn add tubeless`, `bun add tubeless`. Library
|
|
15
|
+
imports are ESM-only on Node.js 22+. The `tubeless` CLI requires Bun 1.3.14+;
|
|
16
|
+
`npx tubeless` is not supported. Linux and macOS are supported; Windows is
|
|
17
|
+
untested. See [install and runtime](./docs/getting-started.md#install).
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createSteps, definePipeline, requireOutputs } from "tubeless";
|
|
23
|
+
|
|
24
|
+
interface ImportOptions {
|
|
25
|
+
lines: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const step = createSteps<ImportOptions>();
|
|
29
|
+
|
|
30
|
+
const load = step("load", {
|
|
31
|
+
run: (_inputs, context) => context.options.lines,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const normalize = step("normalize", {
|
|
35
|
+
dependsOn: [load],
|
|
36
|
+
run: ({ load: rows }) => rows.map((row) => row.trim().toLowerCase()).filter(Boolean),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const ImportPipeline = definePipeline({
|
|
40
|
+
id: "import",
|
|
41
|
+
steps: [load, normalize],
|
|
42
|
+
targets: [normalize],
|
|
43
|
+
finalize: requireOutputs([normalize], ({ normalize }) => normalize),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const rows = await ImportPipeline.runOrThrow({ lines: [" Alpha ", "", "Beta"] });
|
|
47
|
+
// ["alpha", "beta"]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`createSteps` types domain options only. Runs accept built-in controls beside
|
|
51
|
+
them. Use `runOrThrow` when failure should throw, `run` for the structured
|
|
52
|
+
report, `plan` when nothing should execute, and `toMermaid` for the static
|
|
53
|
+
graph.
|
|
54
|
+
|
|
55
|
+
## Inspect, plan, or run
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
bunx tubeless inspect ./pipelines/my-pipeline.ts
|
|
59
|
+
bunx tubeless plan ./pipelines/my-pipeline.ts --target publish --explain
|
|
60
|
+
bunx tubeless graph ./pipelines/my-pipeline.ts --markdown
|
|
61
|
+
bunx tubeless run ./pipelines/my-command.ts -- --source input.json --target publish
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The CLI loads TypeScript modules with Bun. `run` executes a
|
|
65
|
+
`definePipelineCommand` export; application flags go after `--`. See
|
|
66
|
+
[the CLI](./docs/cli.md) and [the local studio](./docs/studio.md).
|
|
67
|
+
|
|
68
|
+
## Choose the right pattern
|
|
69
|
+
|
|
70
|
+
| You need to… | Start with |
|
|
71
|
+
| ----------------------------------------- | ------------------------------------------------------------------------- |
|
|
72
|
+
| Run typed steps in dependency order | [Sequential pipeline](./examples/typed-import.ts) |
|
|
73
|
+
| Validate external boundary values | [Validated boundaries](./examples/validated-boundaries.ts) |
|
|
74
|
+
| Preview writes safely | [Dry runs and write gates](./examples/publish-with-gates.ts) |
|
|
75
|
+
| Skip work intentionally at runtime | [Conditional step](./examples/conditional-step.ts) |
|
|
76
|
+
| Continue independent work after a failure | [Best-effort execution](./examples/best-effort.ts) |
|
|
77
|
+
| Reuse a pipeline inside another | [Child pipeline](./examples/child-pipeline.ts) |
|
|
78
|
+
| Run one child pipeline for many items | [Fan-out and progress](./examples/fan-out-progress.ts) |
|
|
79
|
+
| Resume long API work safely | [Retry, rate limit, and checkpoint](./examples/resumable-enrichment.ts) |
|
|
80
|
+
| Turn a pipeline into a typed script | [Pipeline CLI](./examples/cli-job.ts) |
|
|
81
|
+
| Render plans and errors consistently | [Human and JSON rendering](./examples/rendering.ts) |
|
|
82
|
+
| Test deterministic execution | [Cancellation and test injection](./examples/cancellation-and-testing.ts) |
|
|
83
|
+
| Export lifecycle telemetry | [Structured tracing](./examples/tracing.ts) |
|
|
84
|
+
| Persist and inspect local runs | [Local observability](./examples/local-observability.ts) |
|
|
85
|
+
|
|
86
|
+
The [recipe index](./docs/recipes.md) explains when to use each pattern.
|
|
87
|
+
|
|
88
|
+
## Next
|
|
89
|
+
|
|
90
|
+
- [Getting started](./docs/getting-started.md)
|
|
91
|
+
- [CLI](./docs/cli.md)
|
|
92
|
+
- [Local studio](./docs/studio.md)
|
|
93
|
+
- [Core concepts](./docs/concepts.md)
|
|
94
|
+
- [Documentation map](./docs/README.md)
|
|
95
|
+
- [Generated API inventory](./docs/api-reference.md)
|
|
96
|
+
- [Agent guide](./docs/agent-guide.md)
|
|
97
|
+
|
|
98
|
+
## Contributing
|
|
99
|
+
|
|
100
|
+
Pull requests are accepted for now; the maintainer set stays small. See
|
|
101
|
+
[CONTRIBUTING.md](./CONTRIBUTING.md). Report vulnerabilities privately
|
|
102
|
+
through [SECURITY.md](./SECURITY.md).
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
package/dist/abort.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `AbortSignal` handling for every module that accepts one (pipeline execution,
|
|
3
|
+
* retry/backoff, rate limiting, batching). Centralized so they all produce the same
|
|
4
|
+
* abort-error shape and the same cancellable-sleep behavior, differing only in the
|
|
5
|
+
* `label` used in error messages (e.g. "Retry", "Pipeline run").
|
|
6
|
+
*/
|
|
7
|
+
/** True only for standard AbortErrors or errors created from an observed abort signal. */
|
|
8
|
+
export declare function isAbortError(cause: unknown): cause is Error;
|
|
9
|
+
export declare function createAbortError(signal: AbortSignal, label: string): Error;
|
|
10
|
+
export declare function throwIfAborted(signal: AbortSignal | undefined, label: string): void;
|
|
11
|
+
export declare function abortableSleep(durationMs: number, signal: AbortSignal | undefined, label: string): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=abort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../src/abort.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAE3D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAQ1E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAInF;AAED,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
package/dist/abort.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `AbortSignal` handling for every module that accepts one (pipeline execution,
|
|
3
|
+
* retry/backoff, rate limiting, batching). Centralized so they all produce the same
|
|
4
|
+
* abort-error shape and the same cancellable-sleep behavior, differing only in the
|
|
5
|
+
* `label` used in error messages (e.g. "Retry", "Pipeline run").
|
|
6
|
+
*/
|
|
7
|
+
const abortErrors = new WeakSet();
|
|
8
|
+
/** True only for standard AbortErrors or errors created from an observed abort signal. */
|
|
9
|
+
export function isAbortError(cause) {
|
|
10
|
+
return cause instanceof Error && (cause.name === "AbortError" || abortErrors.has(cause));
|
|
11
|
+
}
|
|
12
|
+
export function createAbortError(signal, label) {
|
|
13
|
+
const { reason } = signal;
|
|
14
|
+
const error = reason instanceof Error
|
|
15
|
+
? reason
|
|
16
|
+
: new Error(reason === undefined ? `${label} aborted` : `${label} aborted: ${reason}`);
|
|
17
|
+
abortErrors.add(error);
|
|
18
|
+
return error;
|
|
19
|
+
}
|
|
20
|
+
export function throwIfAborted(signal, label) {
|
|
21
|
+
if (signal?.aborted) {
|
|
22
|
+
throw createAbortError(signal, label);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function abortableSleep(durationMs, signal, label) {
|
|
26
|
+
if (durationMs <= 0) {
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
}
|
|
29
|
+
if (!signal) {
|
|
30
|
+
return new Promise((resolve) => setTimeout(resolve, durationMs));
|
|
31
|
+
}
|
|
32
|
+
if (signal.aborted) {
|
|
33
|
+
return Promise.reject(createAbortError(signal, label));
|
|
34
|
+
}
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
37
|
+
const timeout = setTimeout(() => {
|
|
38
|
+
cleanup();
|
|
39
|
+
resolve();
|
|
40
|
+
}, durationMs);
|
|
41
|
+
const onAbort = () => {
|
|
42
|
+
clearTimeout(timeout);
|
|
43
|
+
cleanup();
|
|
44
|
+
reject(createAbortError(signal, label));
|
|
45
|
+
};
|
|
46
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=abort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abort.js","sourceRoot":"","sources":["../src/abort.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,GAAG,IAAI,OAAO,EAAS,CAAC;AAEzC,0FAA0F;AAC1F,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAmB,EAAE,KAAa;IACjE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,MAAM,KAAK,GACT,MAAM,YAAY,KAAK;QACrB,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,aAAa,MAAM,EAAE,CAAC,CAAC;IAC3F,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAA+B,EAAE,KAAa;IAC3E,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,MAAM,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,UAAkB,EAClB,MAA+B,EAC/B,KAAa;IAEb,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,UAAU,CAAC,CAAC;QACf,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAC1C,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/batch.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface RunConcurrentOptions {
|
|
2
|
+
/** Maximum in-flight workers. Defaults to one for deterministic sequential work. */
|
|
3
|
+
concurrency?: number;
|
|
4
|
+
/** Stops scheduling new items once aborted; in-flight workers are allowed to settle. */
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
}
|
|
7
|
+
export type ConcurrentWorker<T, R> = (item: T, index: number, signal?: AbortSignal) => Promise<R>;
|
|
8
|
+
export declare function chunk<T>(items: readonly T[], size: number): T[][];
|
|
9
|
+
/**
|
|
10
|
+
* Run individual items with bounded, lazy scheduling and input-order results.
|
|
11
|
+
*
|
|
12
|
+
* The first worker failure (or abort) stops future scheduling. Workers already
|
|
13
|
+
* in flight are allowed to settle before that failure is rethrown, avoiding
|
|
14
|
+
* orphaned work while keeping active work bounded by `concurrency`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runConcurrent<T, R>(items: readonly T[], options: RunConcurrentOptions, worker: ConcurrentWorker<T, R>): Promise<R[]>;
|
|
17
|
+
export declare function runBatched<T, R>(items: readonly T[], options: {
|
|
18
|
+
size: number;
|
|
19
|
+
concurrency?: number;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
}, worker: (batch: T[], batchIndex: number) => Promise<R>): Promise<R[]>;
|
|
22
|
+
//# sourceMappingURL=batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAElG,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CASjE;AASD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,CAAC,EACtC,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7B,OAAO,CAAC,CAAC,EAAE,CAAC,CAEd;AAoDD,wBAAsB,UAAU,CAAC,CAAC,EAAE,CAAC,EACnC,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,EACrE,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GACrD,OAAO,CAAC,CAAC,EAAE,CAAC,CAQd"}
|
package/dist/batch.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { throwIfAborted } from "./abort.js";
|
|
2
|
+
export function chunk(items, size) {
|
|
3
|
+
if (!Number.isInteger(size) || size <= 0) {
|
|
4
|
+
throw new Error(`chunk size must be a positive integer, got ${size}`);
|
|
5
|
+
}
|
|
6
|
+
const out = [];
|
|
7
|
+
for (let index = 0; index < items.length; index += size) {
|
|
8
|
+
out.push(items.slice(index, index + size));
|
|
9
|
+
}
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
function resolveConcurrency(concurrency) {
|
|
13
|
+
if (concurrency !== undefined && (!Number.isInteger(concurrency) || concurrency <= 0)) {
|
|
14
|
+
throw new Error(`concurrency must be a positive finite integer, got ${concurrency}`);
|
|
15
|
+
}
|
|
16
|
+
return Math.max(1, concurrency ?? 1);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run individual items with bounded, lazy scheduling and input-order results.
|
|
20
|
+
*
|
|
21
|
+
* The first worker failure (or abort) stops future scheduling. Workers already
|
|
22
|
+
* in flight are allowed to settle before that failure is rethrown, avoiding
|
|
23
|
+
* orphaned work while keeping active work bounded by `concurrency`.
|
|
24
|
+
*/
|
|
25
|
+
export async function runConcurrent(items, options, worker) {
|
|
26
|
+
return runConcurrentWithLabel(items, options, worker, "Concurrent run");
|
|
27
|
+
}
|
|
28
|
+
async function runConcurrentWithLabel(items, options, worker, label) {
|
|
29
|
+
const concurrency = resolveConcurrency(options.concurrency);
|
|
30
|
+
if (items.length === 0) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const results = new Array(items.length);
|
|
34
|
+
let failed = false;
|
|
35
|
+
let failure;
|
|
36
|
+
let nextIndex = 0;
|
|
37
|
+
async function runNext() {
|
|
38
|
+
while (!failed) {
|
|
39
|
+
try {
|
|
40
|
+
throwIfAborted(options.signal, label);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
failed = true;
|
|
44
|
+
failure = error;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const index = nextIndex++;
|
|
48
|
+
if (index >= items.length) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
results[index] = await worker(items[index], index, options.signal);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (!failed) {
|
|
56
|
+
failed = true;
|
|
57
|
+
failure = error;
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => runNext()));
|
|
64
|
+
if (failed) {
|
|
65
|
+
throw failure;
|
|
66
|
+
}
|
|
67
|
+
return results;
|
|
68
|
+
}
|
|
69
|
+
export async function runBatched(items, options, worker) {
|
|
70
|
+
const batches = chunk(items, options.size);
|
|
71
|
+
return runConcurrentWithLabel(batches, options, (batch, index) => worker(batch, index), "Batch run");
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface CheckpointStore<TMeta = unknown> {
|
|
2
|
+
has(key: string): boolean;
|
|
3
|
+
record(key: string, meta?: TMeta): void;
|
|
4
|
+
entries(): ReadonlyMap<string, TMeta | undefined>;
|
|
5
|
+
flush(): void;
|
|
6
|
+
/**
|
|
7
|
+
* Whether to call this is the caller's choice: clear-on-completion suits one-shot
|
|
8
|
+
* resumable jobs (a full successful run means "start fresh next time"), while
|
|
9
|
+
* never-clear suits pipelines with an incrementally-growing "done" set (e.g. entity
|
|
10
|
+
* enrichment) where auto-clearing would cause reprocessing and duplicate output.
|
|
11
|
+
*/
|
|
12
|
+
clear(): void;
|
|
13
|
+
}
|
|
14
|
+
export interface OpenCheckpointOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Called when the checkpoint file exists but fails to parse, right before falling
|
|
17
|
+
* back to an empty checkpoint. Starting fresh from a corrupt file is silent
|
|
18
|
+
* reprocessing of every previously-checkpointed item — the default logs a warning to
|
|
19
|
+
* `console` so that cost is visible instead of masked. Pass a no-op to suppress it.
|
|
20
|
+
*/
|
|
21
|
+
onCorruptFile?: (cause: unknown) => void;
|
|
22
|
+
}
|
|
23
|
+
export declare function openCheckpoint<TMeta = unknown>(filePath: string, options?: OpenCheckpointOptions): CheckpointStore<TMeta>;
|
|
24
|
+
/**
|
|
25
|
+
* Runs `persist()`, and only once it resolves without throwing, records every item in
|
|
26
|
+
* `batch` into `checkpoint` and flushes it. A `persist` failure (a write error, etc.)
|
|
27
|
+
* propagates and leaves the checkpoint untouched for this batch — the caller decides
|
|
28
|
+
* whether to skip the batch and retry it later or fail the whole run. This makes the
|
|
29
|
+
* checkpoint-outruns-persistence ordering bug impossible to write by construction:
|
|
30
|
+
* there is no way to call `checkpoint.record()` before `persist()` has already succeeded.
|
|
31
|
+
*/
|
|
32
|
+
export declare function withCheckpointedBatch<TItem>(checkpoint: CheckpointStore, batch: readonly TItem[], keyOf: (item: TItem) => string, persist: () => Promise<void> | void): Promise<void>;
|
|
33
|
+
//# sourceMappingURL=checkpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../src/checkpoint.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe,CAAC,KAAK,GAAG,OAAO;IAC9C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACxC,OAAO,IAAI,WAAW,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;IAClD,KAAK,IAAI,IAAI,CAAC;IACd;;;;;OAKG;IACH,KAAK,IAAI,IAAI,CAAC;CACf;AAsBD,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,wBAAgB,cAAc,CAAC,KAAK,GAAG,OAAO,EAC5C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,qBAA0B,GAClC,eAAe,CAAC,KAAK,CAAC,CA2CxB;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAC/C,UAAU,EAAE,eAAe,EAC3B,KAAK,EAAE,SAAS,KAAK,EAAE,EACvB,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,EAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAClC,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
function loadEntries(filePath, onCorruptFile) {
|
|
4
|
+
if (!fs.existsSync(filePath)) {
|
|
5
|
+
return new Map();
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
// SAFETY: the file is expected to hold a JSON object whose values are
|
|
9
|
+
// TMeta entries, as written by this store's flush(). Object.entries only
|
|
10
|
+
// yields string keys, so the `as Record<string, TMeta>` documents the
|
|
11
|
+
// caller-supplied TMeta contract; any mismatch is caught by the try/catch.
|
|
12
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
13
|
+
return new Map(Object.entries(parsed));
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
onCorruptFile(error);
|
|
17
|
+
return new Map();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function openCheckpoint(filePath, options = {}) {
|
|
21
|
+
const onCorruptFile = options.onCorruptFile ??
|
|
22
|
+
((cause) => {
|
|
23
|
+
console.warn(`Checkpoint file ${filePath} is corrupt; starting fresh (all previously checkpointed items will be reprocessed): ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
24
|
+
});
|
|
25
|
+
const entries = loadEntries(filePath, onCorruptFile);
|
|
26
|
+
return {
|
|
27
|
+
has: (key) => entries.has(key),
|
|
28
|
+
record: (key, meta) => {
|
|
29
|
+
entries.set(key, meta);
|
|
30
|
+
},
|
|
31
|
+
entries: () => entries,
|
|
32
|
+
flush: () => {
|
|
33
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
34
|
+
// Records made without a `meta` argument store `undefined`, which JSON.stringify
|
|
35
|
+
// silently drops from object properties — without the replacer below, every
|
|
36
|
+
// meta-less key would vanish from the file on flush. Substituting `null` keeps
|
|
37
|
+
// the key present; callers that never pass `meta` only care about `has()`, not
|
|
38
|
+
// the exact value round-tripped through `entries()`.
|
|
39
|
+
const json = JSON.stringify(Object.fromEntries(entries), (_key, value) => (value === undefined ? null : value), 2);
|
|
40
|
+
// Write to a sibling temp file and rename over the destination rather than
|
|
41
|
+
// writeFileSync-ing the target directly: a crash or process kill mid-write would
|
|
42
|
+
// otherwise leave a truncated, unparseable checkpoint file, and the next run would
|
|
43
|
+
// silently treat it as empty and reprocess everything already done. Rename is
|
|
44
|
+
// atomic on the same filesystem, so the checkpoint file is always either the
|
|
45
|
+
// previous complete flush or the new complete one, never a partial write.
|
|
46
|
+
const tmpPath = `${filePath}.tmp-${process.pid}`;
|
|
47
|
+
fs.writeFileSync(tmpPath, `${json}\n`);
|
|
48
|
+
fs.renameSync(tmpPath, filePath);
|
|
49
|
+
},
|
|
50
|
+
clear: () => {
|
|
51
|
+
entries.clear();
|
|
52
|
+
fs.rmSync(filePath, { force: true });
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Runs `persist()`, and only once it resolves without throwing, records every item in
|
|
58
|
+
* `batch` into `checkpoint` and flushes it. A `persist` failure (a write error, etc.)
|
|
59
|
+
* propagates and leaves the checkpoint untouched for this batch — the caller decides
|
|
60
|
+
* whether to skip the batch and retry it later or fail the whole run. This makes the
|
|
61
|
+
* checkpoint-outruns-persistence ordering bug impossible to write by construction:
|
|
62
|
+
* there is no way to call `checkpoint.record()` before `persist()` has already succeeded.
|
|
63
|
+
*/
|
|
64
|
+
export async function withCheckpointedBatch(checkpoint, batch, keyOf, persist) {
|
|
65
|
+
await persist();
|
|
66
|
+
for (const item of batch) {
|
|
67
|
+
checkpoint.record(keyOf(item));
|
|
68
|
+
}
|
|
69
|
+
checkpoint.flush();
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=checkpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../src/checkpoint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ToMappedChildStepProgressOptions } from "./mapped-child-progress.js";
|
|
2
|
+
import { type Pipeline, type PipelineExecutionContext, type PipelineRun, type PipelineRunOptions, type PipelineStepContext } from "./pipeline.js";
|
|
3
|
+
export declare class PipelineChildError extends Error {
|
|
4
|
+
readonly cancelled: boolean;
|
|
5
|
+
constructor(message: string, cancelled?: boolean, cause?: unknown);
|
|
6
|
+
}
|
|
7
|
+
type ChildPipeline = Pipeline<object, unknown, string, string>;
|
|
8
|
+
type ChildInputs = Record<string, unknown>;
|
|
9
|
+
interface ChildExecutionDependencies<TParentOptions extends object> {
|
|
10
|
+
createExecutionError(result: PipelineRun<unknown>, message: string): Error;
|
|
11
|
+
isCancellation(cause: unknown, context: PipelineExecutionContext<TParentOptions>): boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface SingleChildExecutionConfig<TParentOptions extends object> {
|
|
14
|
+
pipeline: ChildPipeline;
|
|
15
|
+
mapOptions(inputs: ChildInputs, context: PipelineExecutionContext<TParentOptions>): PipelineRunOptions;
|
|
16
|
+
mapResult?(value: unknown, result: PipelineRun<unknown>, context: PipelineStepContext<TParentOptions>): unknown;
|
|
17
|
+
}
|
|
18
|
+
export interface MappedChildExecutionConfig<TParentOptions extends object> {
|
|
19
|
+
pipeline: ChildPipeline;
|
|
20
|
+
items(inputs: ChildInputs, context: PipelineExecutionContext<TParentOptions>): readonly unknown[] | Promise<readonly unknown[]>;
|
|
21
|
+
key(item: unknown, index: number): string;
|
|
22
|
+
concurrency?: number | ((inputs: ChildInputs, context: PipelineExecutionContext<TParentOptions>) => number);
|
|
23
|
+
progress?: ToMappedChildStepProgressOptions;
|
|
24
|
+
mapOptions(item: unknown, index: number, inputs: ChildInputs, context: PipelineExecutionContext<TParentOptions>): PipelineRunOptions;
|
|
25
|
+
mapResult?(value: unknown, result: PipelineRun<unknown>, item: unknown, index: number, context: PipelineStepContext<TParentOptions>): unknown;
|
|
26
|
+
}
|
|
27
|
+
export declare function createSingleChildRunner<TParentOptions extends object>(config: SingleChildExecutionConfig<TParentOptions>, dependencies: ChildExecutionDependencies<TParentOptions>): (inputs: ChildInputs, context: PipelineStepContext<TParentOptions>) => Promise<unknown>;
|
|
28
|
+
export declare function createMappedChildRunner<TParentOptions extends object>(config: MappedChildExecutionConfig<TParentOptions>, dependencies: ChildExecutionDependencies<TParentOptions>): (inputs: ChildInputs, context: PipelineStepContext<TParentOptions>) => Promise<unknown[]>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=child-execution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child-execution.d.ts","sourceRoot":"","sources":["../src/child-execution.ts"],"names":[],"mappings":"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"}
|