tubeless 0.2.7 → 0.2.8

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.
Files changed (46) hide show
  1. package/dist/core/pipeline-finalizer.js +0 -1
  2. package/dist/core/pipeline-finalizer.js.map +1 -1
  3. package/dist/project/project-compiler.d.ts +21 -4
  4. package/dist/project/project-compiler.js +120 -13
  5. package/dist/project/project-compiler.js.map +1 -1
  6. package/dist/project/project-document.d.ts +32 -2
  7. package/dist/project/project-document.js +39 -4
  8. package/dist/project/project-document.js.map +1 -1
  9. package/dist/project/project.d.ts +2 -2
  10. package/dist/project/project.js.map +1 -1
  11. package/dist/reporter/interactive-reporter.js +2 -6
  12. package/dist/reporter/interactive-reporter.js.map +1 -1
  13. package/dist/reporter/live-ticker-worker.js +55 -50
  14. package/dist/reporter/live-ticker-worker.js.map +1 -1
  15. package/dist/reporter/live-ticker.d.ts +0 -3
  16. package/dist/reporter/live-ticker.js +92 -170
  17. package/dist/reporter/live-ticker.js.map +1 -1
  18. package/dist/workbench/workbench-bin.d.ts +2 -6
  19. package/dist/workbench/workbench-bin.js +8 -20
  20. package/dist/workbench/workbench-bin.js.map +1 -1
  21. package/dist/workbench/workbench-shared.d.ts +1 -15
  22. package/dist/workbench/workbench-shared.js +10 -42
  23. package/dist/workbench/workbench-shared.js.map +1 -1
  24. package/dist/workbench/workbench-ui.js +0 -3
  25. package/dist/workbench/workbench-ui.js.map +1 -1
  26. package/docs/README.md +12 -13
  27. package/docs/agent-guide.md +8 -5
  28. package/docs/api-reference.md +1 -7
  29. package/docs/api-report.json +1 -7
  30. package/docs/cli.md +4 -6
  31. package/docs/declarative-pipelines.md +74 -26
  32. package/docs/getting-started.md +2 -3
  33. package/docs/llms.txt +0 -3
  34. package/docs/pipeline-document.schema.json +53 -2
  35. package/docs/recipes.md +1 -1
  36. package/examples/catalog/tubeless.project.ts +2 -0
  37. package/examples/declarative/handlers.ts +47 -4
  38. package/examples/declarative/pipelines.yaml +29 -7
  39. package/package.json +2 -5
  40. package/skills/tubeless/SKILL.md +6 -5
  41. package/dist/reporter/live-ticker-supervisor.d.ts +0 -1
  42. package/dist/reporter/live-ticker-supervisor.js +0 -44
  43. package/dist/reporter/live-ticker-supervisor.js.map +0 -1
  44. package/dist/workbench/workbench-trampoline.d.ts +0 -71
  45. package/dist/workbench/workbench-trampoline.js +0 -136
  46. package/dist/workbench/workbench-trampoline.js.map +0 -1
@@ -18,7 +18,6 @@ export function requireOutputs(requiredSteps, finalize) {
18
18
  }
19
19
  // SAFETY: every required step id was verified present above, so the partial
20
20
  // outputs contain all required keys and are a complete required-outputs map.
21
- // oxlint-disable-next-line anti-slop/no-chained-type-assertions
22
21
  return finalize(outputs, context);
23
22
  };
24
23
  const requiredFinalizer = (outputs, context) => invoke(uniqueRequiredSteps.map((step) => step.id), outputs, context);
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline-finalizer.js","sourceRoot":"","sources":["../../src/core/pipeline-finalizer.ts"],"names":[],"mappings":"AAIA,MAAM,0BAA0B,GAAkB,MAAM,CAAC,mCAAmC,CAAC,CAAC;AAW9F,MAAM,UAAU,yBAAyB,CACvC,QAAgB;IAEhB,yEAAyE;IACzE,mEAAmE;IACnE,OAAQ,QAAmF,CACzF,0BAA0B,CAC3B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,aAA6B,EAC7B,QAG+B;IAK/B,MAAM,mBAAmB,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,CACb,eAAkC,EAClC,OAAyD,EACzD,OAA+D,EAC/D,EAAE;QACF,MAAM,cAAc,GAAG,eAAe,CAAC,MAAM,CAC3C,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CACnE,CAAC;QACF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,sCAAsC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrF,CAAC;QACD,4EAA4E;QAC5E,6EAA6E;QAC7E,gEAAgE;QAChE,OAAO,QAAQ,CAAC,OAA6D,EAAE,OAAO,CAAC,CAAC;IAC1F,CAAC,CAAC;IACF,MAAM,iBAAiB,GAAG,CACxB,OAAyD,EACzD,OAA+D,EAC/D,EAAE,CACF,MAAM,CACJ,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAC1C,OAAO,EACP,OAAO,CACR,CAAC;IACJ,MAAM,QAAQ,GAA4D,MAAM,CAAC,MAAM,CAAC;QACtF,OAAO,EAAE,CAAC,eAAkC,EAAE,EAAE;YAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC;YACrD,OAAO,CACL,OAAyD,EACzD,OAA+D,EAC/D,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC;KAC/C,CAAC,CAAC;IACH,MAAM,CAAC,cAAc,CAAC,iBAAiB,EAAE,0BAA0B,EAAE;QACnE,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;IACH,OAAO,iBAAiB,CAAC;AAC3B,CAAC","sourcesContent":["import type { AnyStep, StepOutput } from \"./pipeline-steps.js\";\nimport type { StepsOptions } from \"./pipeline-definition.js\";\nimport type { PipelineExecutionContext } from \"./pipeline-types.js\";\n\nconst REQUIRED_FINALIZER_OUTPUTS: unique symbol = Symbol(\"tubeless.requiredFinalizerOutputs\");\n\nexport interface RequiredFinalizerMetadata<TOptions extends object> {\n readonly steps: readonly AnyStep<TOptions>[];\n compile(requiredStepIds: readonly string[]): (...args: never[]) => unknown;\n}\n\ntype RequiredPipelineOutputs<TSteps extends readonly AnyStep[]> = {\n [S in TSteps[number] as S[\"id\"]]-?: StepOutput<S>;\n};\n\nexport function requiredFinalizerMetadata<TOptions extends object>(\n finalize: object\n): RequiredFinalizerMetadata<TOptions> | undefined {\n // SAFETY: `requireOutputs` stamps internal compilation metadata onto the\n // finalizer; the intersection only exposes that optional property.\n return (finalize as { [REQUIRED_FINALIZER_OUTPUTS]?: RequiredFinalizerMetadata<TOptions> })[\n REQUIRED_FINALIZER_OUTPUTS\n ];\n}\n\n/**\n * Build a finalizer that only runs when every listed step published an output.\n * Presence is checked by output slot, so a successfully published `undefined`\n * remains distinct from a structural skip or failure.\n */\nexport function requireOutputs<const TRequiredSteps extends readonly AnyStep[], TResult>(\n requiredSteps: TRequiredSteps,\n finalize: (\n outputs: RequiredPipelineOutputs<TRequiredSteps>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n ) => TResult | Promise<TResult>\n): (\n outputs: Partial<RequiredPipelineOutputs<TRequiredSteps>>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n) => TResult | Promise<TResult> {\n const uniqueRequiredSteps = [...new Set(requiredSteps)];\n const invoke = (\n requiredStepIds: readonly string[],\n outputs: Partial<RequiredPipelineOutputs<TRequiredSteps>>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n ) => {\n const missingStepIds = requiredStepIds.filter(\n (stepId) => !Object.prototype.hasOwnProperty.call(outputs, stepId)\n );\n if (missingStepIds.length > 0) {\n throw new Error(`Required pipeline outputs missing: ${missingStepIds.join(\", \")}`);\n }\n // SAFETY: every required step id was verified present above, so the partial\n // outputs contain all required keys and are a complete required-outputs map.\n // oxlint-disable-next-line anti-slop/no-chained-type-assertions\n return finalize(outputs as unknown as RequiredPipelineOutputs<TRequiredSteps>, context);\n };\n const requiredFinalizer = (\n outputs: Partial<RequiredPipelineOutputs<TRequiredSteps>>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n ) =>\n invoke(\n uniqueRequiredSteps.map((step) => step.id),\n outputs,\n context\n );\n const metadata: RequiredFinalizerMetadata<StepsOptions<TRequiredSteps>> = Object.freeze({\n compile: (requiredStepIds: readonly string[]) => {\n const snapshot = Object.freeze([...requiredStepIds]);\n return (\n outputs: Partial<RequiredPipelineOutputs<TRequiredSteps>>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n ) => invoke(snapshot, outputs, context);\n },\n steps: Object.freeze([...uniqueRequiredSteps]),\n });\n Object.defineProperty(requiredFinalizer, REQUIRED_FINALIZER_OUTPUTS, {\n value: metadata,\n });\n return requiredFinalizer;\n}\n"]}
1
+ {"version":3,"file":"pipeline-finalizer.js","sourceRoot":"","sources":["../../src/core/pipeline-finalizer.ts"],"names":[],"mappings":"AAIA,MAAM,0BAA0B,GAAkB,MAAM,CAAC,mCAAmC,CAAC,CAAC;AAW9F,MAAM,UAAU,yBAAyB,CACvC,QAAgB;IAEhB,yEAAyE;IACzE,mEAAmE;IACnE,OAAQ,QAAmF,CACzF,0BAA0B,CAC3B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,aAA6B,EAC7B,QAG+B;IAK/B,MAAM,mBAAmB,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,CACb,eAAkC,EAClC,OAAyD,EACzD,OAA+D,EAC/D,EAAE;QACF,MAAM,cAAc,GAAG,eAAe,CAAC,MAAM,CAC3C,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CACnE,CAAC;QACF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,sCAAsC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrF,CAAC;QACD,4EAA4E;QAC5E,6EAA6E;QAC7E,OAAO,QAAQ,CAAC,OAA6D,EAAE,OAAO,CAAC,CAAC;IAC1F,CAAC,CAAC;IACF,MAAM,iBAAiB,GAAG,CACxB,OAAyD,EACzD,OAA+D,EAC/D,EAAE,CACF,MAAM,CACJ,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAC1C,OAAO,EACP,OAAO,CACR,CAAC;IACJ,MAAM,QAAQ,GAA4D,MAAM,CAAC,MAAM,CAAC;QACtF,OAAO,EAAE,CAAC,eAAkC,EAAE,EAAE;YAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC;YACrD,OAAO,CACL,OAAyD,EACzD,OAA+D,EAC/D,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC;KAC/C,CAAC,CAAC;IACH,MAAM,CAAC,cAAc,CAAC,iBAAiB,EAAE,0BAA0B,EAAE;QACnE,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;IACH,OAAO,iBAAiB,CAAC;AAC3B,CAAC","sourcesContent":["import type { AnyStep, StepOutput } from \"./pipeline-steps.js\";\nimport type { StepsOptions } from \"./pipeline-definition.js\";\nimport type { PipelineExecutionContext } from \"./pipeline-types.js\";\n\nconst REQUIRED_FINALIZER_OUTPUTS: unique symbol = Symbol(\"tubeless.requiredFinalizerOutputs\");\n\nexport interface RequiredFinalizerMetadata<TOptions extends object> {\n readonly steps: readonly AnyStep<TOptions>[];\n compile(requiredStepIds: readonly string[]): (...args: never[]) => unknown;\n}\n\ntype RequiredPipelineOutputs<TSteps extends readonly AnyStep[]> = {\n [S in TSteps[number] as S[\"id\"]]-?: StepOutput<S>;\n};\n\nexport function requiredFinalizerMetadata<TOptions extends object>(\n finalize: object\n): RequiredFinalizerMetadata<TOptions> | undefined {\n // SAFETY: `requireOutputs` stamps internal compilation metadata onto the\n // finalizer; the intersection only exposes that optional property.\n return (finalize as { [REQUIRED_FINALIZER_OUTPUTS]?: RequiredFinalizerMetadata<TOptions> })[\n REQUIRED_FINALIZER_OUTPUTS\n ];\n}\n\n/**\n * Build a finalizer that only runs when every listed step published an output.\n * Presence is checked by output slot, so a successfully published `undefined`\n * remains distinct from a structural skip or failure.\n */\nexport function requireOutputs<const TRequiredSteps extends readonly AnyStep[], TResult>(\n requiredSteps: TRequiredSteps,\n finalize: (\n outputs: RequiredPipelineOutputs<TRequiredSteps>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n ) => TResult | Promise<TResult>\n): (\n outputs: Partial<RequiredPipelineOutputs<TRequiredSteps>>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n) => TResult | Promise<TResult> {\n const uniqueRequiredSteps = [...new Set(requiredSteps)];\n const invoke = (\n requiredStepIds: readonly string[],\n outputs: Partial<RequiredPipelineOutputs<TRequiredSteps>>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n ) => {\n const missingStepIds = requiredStepIds.filter(\n (stepId) => !Object.prototype.hasOwnProperty.call(outputs, stepId)\n );\n if (missingStepIds.length > 0) {\n throw new Error(`Required pipeline outputs missing: ${missingStepIds.join(\", \")}`);\n }\n // SAFETY: every required step id was verified present above, so the partial\n // outputs contain all required keys and are a complete required-outputs map.\n return finalize(outputs as unknown as RequiredPipelineOutputs<TRequiredSteps>, context);\n };\n const requiredFinalizer = (\n outputs: Partial<RequiredPipelineOutputs<TRequiredSteps>>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n ) =>\n invoke(\n uniqueRequiredSteps.map((step) => step.id),\n outputs,\n context\n );\n const metadata: RequiredFinalizerMetadata<StepsOptions<TRequiredSteps>> = Object.freeze({\n compile: (requiredStepIds: readonly string[]) => {\n const snapshot = Object.freeze([...requiredStepIds]);\n return (\n outputs: Partial<RequiredPipelineOutputs<TRequiredSteps>>,\n context: PipelineExecutionContext<StepsOptions<TRequiredSteps>>\n ) => invoke(snapshot, outputs, context);\n },\n steps: Object.freeze([...uniqueRequiredSteps]),\n });\n Object.defineProperty(requiredFinalizer, REQUIRED_FINALIZER_OUTPUTS, {\n value: metadata,\n });\n return requiredFinalizer;\n}\n"]}
@@ -1,14 +1,31 @@
1
- import { type Pipeline, type PipelineExecutionContext, type PipelineStepContext, type StandardSchemaV1 } from "../core/pipeline.js";
1
+ import { type MappedChildProgressOptions, type Pipeline, type PipelineExecutionContext, type PipelineRun, type PipelineRunOptions, type PipelineStepContext, type StandardSchemaV1, type StepSkipDecision } from "../core/pipeline.js";
2
2
  import { PipelineDocumentError } from "./project-document.js";
3
3
  export { PipelineDocumentError };
4
- export type { PipelineDocument, PipelineDocumentDefinition, PipelineDocumentStep, } from "./project-document.js";
5
4
  /** Dependency values are checked at runtime rather than inferred from a project document. */
6
- export type PipelineDocumentHandler = (inputs: Record<string, unknown>, context: PipelineStepContext<object>) => unknown;
7
- export type PipelineDocumentFinalizer = (outputs: Record<string, unknown>, context: PipelineExecutionContext<object>) => unknown;
5
+ type PipelineDocumentHandler = (inputs: Record<string, unknown>, context: PipelineStepContext<object>) => unknown;
6
+ type PipelineDocumentFinalizer = (outputs: Record<string, unknown>, context: PipelineExecutionContext<object>) => unknown;
7
+ type PipelineDocumentSkipPredicate = (inputs: Record<string, unknown>, context: PipelineExecutionContext<object>) => StepSkipDecision | Promise<StepSkipDecision>;
8
+ /** Application-owned wiring for one declarative `fromPipeline` step. */
9
+ interface PipelineDocumentFromPipelineAdapter {
10
+ mapOptions(inputs: Record<string, unknown>, context: PipelineExecutionContext<object>): PipelineRunOptions;
11
+ mapResult?(value: unknown, result: PipelineRun<unknown>, context: PipelineStepContext<object>): unknown;
12
+ }
13
+ /** Application-owned wiring for one declarative `forEachPipeline` step. */
14
+ interface PipelineDocumentForEachPipelineAdapter {
15
+ items(inputs: Record<string, unknown>, context: PipelineExecutionContext<object>): readonly unknown[] | Promise<readonly unknown[]>;
16
+ key(item: unknown, index: number): string;
17
+ concurrency?: number | ((inputs: Record<string, unknown>, context: PipelineExecutionContext<object>) => number);
18
+ progress?: MappedChildProgressOptions;
19
+ mapOptions(item: unknown, index: number, inputs: Record<string, unknown>, context: PipelineExecutionContext<object>): PipelineRunOptions;
20
+ mapResult?(value: unknown, result: PipelineRun<unknown>, item: unknown, index: number, context: PipelineStepContext<object>): unknown;
21
+ }
8
22
  /** Only explicitly registered functions and schemas can be referenced by a document. */
9
23
  export interface PipelineDocumentRegistry {
10
24
  steps: Readonly<Record<string, PipelineDocumentHandler>>;
11
25
  finalizers: Readonly<Record<string, PipelineDocumentFinalizer>>;
26
+ skipPredicates?: Readonly<Record<string, PipelineDocumentSkipPredicate>>;
27
+ fromPipelineAdapters?: Readonly<Record<string, PipelineDocumentFromPipelineAdapter>>;
28
+ forEachPipelineAdapters?: Readonly<Record<string, PipelineDocumentForEachPipelineAdapter>>;
12
29
  optionsSchemas?: Readonly<Record<string, StandardSchemaV1<object, object>>>;
13
30
  schemas?: Readonly<Record<string, StandardSchemaV1>>;
14
31
  }
@@ -24,17 +24,73 @@ function schema(registry, name, path) {
24
24
  }
25
25
  return value;
26
26
  }
27
+ function registeredObject(registry, name, path) {
28
+ const value = resolve(registry, name, path);
29
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
30
+ throw new PipelineDocumentError(path, "Expected a registered adapter object");
31
+ }
32
+ return value;
33
+ }
34
+ function fromPipelineAdapter(registry, name, path) {
35
+ const adapter = registeredObject(registry, name, path);
36
+ if (typeof adapter.mapOptions !== "function") {
37
+ throw new PipelineDocumentError(path, "Expected an adapter with mapOptions");
38
+ }
39
+ if (adapter.mapResult !== undefined && typeof adapter.mapResult !== "function") {
40
+ throw new PipelineDocumentError(path, "Expected adapter mapResult to be a function");
41
+ }
42
+ return { mapOptions: adapter.mapOptions, mapResult: adapter.mapResult };
43
+ }
44
+ function forEachPipelineAdapter(registry, name, path) {
45
+ const adapter = registeredObject(registry, name, path);
46
+ if (typeof adapter.items !== "function" ||
47
+ typeof adapter.key !== "function" ||
48
+ typeof adapter.mapOptions !== "function") {
49
+ throw new PipelineDocumentError(path, "Expected an adapter with items, key, and mapOptions");
50
+ }
51
+ if (adapter.concurrency !== undefined &&
52
+ typeof adapter.concurrency !== "number" &&
53
+ typeof adapter.concurrency !== "function") {
54
+ throw new PipelineDocumentError(path, "Expected adapter concurrency to be a number or function");
55
+ }
56
+ if (adapter.mapResult !== undefined && typeof adapter.mapResult !== "function") {
57
+ throw new PipelineDocumentError(path, "Expected adapter mapResult to be a function");
58
+ }
59
+ return {
60
+ items: adapter.items,
61
+ key: adapter.key,
62
+ concurrency: adapter.concurrency,
63
+ progress: adapter.progress,
64
+ mapOptions: adapter.mapOptions,
65
+ mapResult: adapter.mapResult,
66
+ };
67
+ }
27
68
  /**
28
69
  * Compile parsed YAML or JSON into ordinary pipelines. Does not load modules,
29
70
  * invoke handlers or schemas, or perform I/O. Graph validation belongs to core.
30
71
  */
31
72
  export function compilePipelineDocument(document, registry) {
32
73
  const parsed = validatePipelineDocument(document);
33
- const pipelines = new Map();
34
- for (const [id, definition] of Object.entries(parsed.pipelines)) {
74
+ const compiled = new Map();
75
+ const compiling = new Set();
76
+ const resolvePipeline = (id, path) => {
77
+ if (!Object.hasOwn(parsed.pipelines, id)) {
78
+ throw new PipelineDocumentError(path, `Unknown pipeline ${JSON.stringify(id)}`);
79
+ }
80
+ if (compiling.has(id)) {
81
+ throw new PipelineDocumentError(path, `Pipeline composition cycle through ${JSON.stringify(id)}`);
82
+ }
83
+ return compile(id);
84
+ };
85
+ const compile = (id) => {
86
+ const existing = compiled.get(id);
87
+ if (existing)
88
+ return existing;
89
+ const definition = parsed.pipelines[id];
35
90
  const path = `$.pipelines[${JSON.stringify(id)}]`;
91
+ compiling.add(id);
36
92
  const optionsSchema = schema(registry.optionsSchemas, definition.optionsSchema, `${path}.optionsSchema`);
37
- const { step } = optionsSchema ? createSteps(optionsSchema) : createSteps();
93
+ const factory = optionsSchema ? createSteps(optionsSchema) : createSteps();
38
94
  const entries = definition.steps.map((definition, index) => {
39
95
  const stepPath = `${path}.steps[${index}]`;
40
96
  // Populate these arrays after all steps exist, allowing forward references
@@ -46,24 +102,72 @@ export function compilePipelineDocument(document, registry) {
46
102
  ? definition.dryRun
47
103
  : handler(registry.steps, definition.dryRun.run, `${stepPath}.dryRun.run`);
48
104
  const outputSchema = schema(registry.schemas, definition.outputSchema, `${stepPath}.outputSchema`);
49
- const config = {
105
+ const common = {
50
106
  name: definition.name,
51
107
  description: definition.description,
52
- run: handler(registry.steps, definition.run, `${stepPath}.run`),
53
- dryRun,
54
108
  dependsOn,
55
109
  optionalDependsOn,
56
110
  skipAfterFailureOf,
57
111
  };
112
+ const skip = definition.skip === undefined
113
+ ? undefined
114
+ : handler(registry.skipPredicates ?? {}, definition.skip, `${stepPath}.skip`);
115
+ let built;
116
+ if (definition.run !== undefined) {
117
+ const config = {
118
+ ...common,
119
+ dryRun,
120
+ run: handler(registry.steps, definition.run, `${stepPath}.run`),
121
+ skip,
122
+ };
123
+ built = outputSchema
124
+ ? factory.step(definition.id, { ...config, outputSchema })
125
+ : factory.step(definition.id, config);
126
+ }
127
+ else if (definition.fromPipeline !== undefined) {
128
+ const reference = definition.fromPipeline;
129
+ const adapter = fromPipelineAdapter(registry.fromPipelineAdapters, reference.adapter, `${stepPath}.fromPipeline.adapter`);
130
+ const config = {
131
+ ...common,
132
+ dryRun: definition.dryRun,
133
+ pipeline: resolvePipeline(reference.pipeline, `${stepPath}.fromPipeline.pipeline`),
134
+ mapOptions: adapter.mapOptions,
135
+ skip,
136
+ };
137
+ built =
138
+ adapter.mapResult === undefined
139
+ ? factory.fromPipeline(definition.id, config)
140
+ : factory.fromPipeline(definition.id, { ...config, mapResult: adapter.mapResult });
141
+ }
142
+ else {
143
+ const reference = definition.forEachPipeline;
144
+ const adapter = forEachPipelineAdapter(registry.forEachPipelineAdapters, reference.adapter, `${stepPath}.forEachPipeline.adapter`);
145
+ // SAFETY: document pipelines publish unknown values. The fan-out builder
146
+ // still represents a valued skip as the complete unknown result array.
147
+ const fanOutSkip = skip;
148
+ const config = {
149
+ ...common,
150
+ dryRun: definition.dryRun,
151
+ pipeline: resolvePipeline(reference.pipeline, `${stepPath}.forEachPipeline.pipeline`),
152
+ items: adapter.items,
153
+ key: adapter.key,
154
+ concurrency: adapter.concurrency,
155
+ progress: adapter.progress,
156
+ mapOptions: adapter.mapOptions,
157
+ skip: fanOutSkip,
158
+ };
159
+ built =
160
+ adapter.mapResult === undefined
161
+ ? factory.forEachPipeline(definition.id, config)
162
+ : factory.forEachPipeline(definition.id, { ...config, mapResult: adapter.mapResult });
163
+ }
58
164
  return {
59
165
  definition,
60
166
  path: stepPath,
61
167
  dependsOn,
62
168
  optionalDependsOn,
63
169
  skipAfterFailureOf,
64
- step: outputSchema
65
- ? step(definition.id, { ...config, outputSchema })
66
- : step(definition.id, config),
170
+ step: built,
67
171
  };
68
172
  });
69
173
  const byId = new Map(entries.map((entry) => [entry.step.id, entry.step]));
@@ -79,7 +183,7 @@ export function compilePipelineDocument(document, registry) {
79
183
  }
80
184
  }
81
185
  const finalize = handler(registry.finalizers, definition.finalize.run, `${path}.finalize.run`);
82
- pipelines.set(id, definePipeline({
186
+ const pipeline = definePipeline({
83
187
  id,
84
188
  steps: entries.map((entry) => entry.step),
85
189
  targets: resolveSteps(definition.targets, `${path}.targets`),
@@ -87,8 +191,11 @@ export function compilePipelineDocument(document, registry) {
87
191
  finalize: definition.finalize.requireOutputs === undefined
88
192
  ? finalize
89
193
  : requireOutputs(resolveSteps(definition.finalize.requireOutputs, `${path}.finalize.requireOutputs`), finalize),
90
- }));
91
- }
92
- return pipelines;
194
+ });
195
+ compiled.set(id, pipeline);
196
+ compiling.delete(id);
197
+ return pipeline;
198
+ };
199
+ return new Map(Object.keys(parsed.pipelines).map((id) => [id, compile(id)]));
93
200
  }
94
201
  //# sourceMappingURL=project-compiler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"project-compiler.js","sourceRoot":"","sources":["../../src/project/project-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,cAAc,EACd,cAAc,GAMf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAExF,OAAO,EAAE,qBAAqB,EAAE,CAAC;AA0BjC,SAAS,OAAO,CACd,QAAiD,EACjD,IAAY,EACZ,IAAY;IAEZ,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,qBAAqB,CAAC,IAAI,EAAE,2BAA2B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,OAAO,CACd,QAAqC,EACrC,IAAY,EACZ,IAAY;IAEZ,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,OAAO,KAAK,KAAK,UAAU;QAC7B,MAAM,IAAI,qBAAqB,CAAC,IAAI,EAAE,gCAAgC,CAAC,CAAC;IAC1E,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,MAAM,CACb,QAAiD,EACjD,IAAwB,EACxB,IAAY;IAEZ,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,IACE,CAAC,KAAK;QACN,KAAK,CAAC,WAAW,CAAC,EAAE,OAAO,KAAK,CAAC;QACjC,OAAO,KAAK,CAAC,WAAW,CAAC,CAAC,QAAQ,KAAK,UAAU,EACjD,CAAC;QACD,MAAM,IAAI,qBAAqB,CAAC,IAAI,EAAE,sCAAsC,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAAiB,EACjB,QAAkC;IAElC,MAAM,MAAM,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAqC,CAAC;IAC/D,KAAK,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,GAAG,eAAe,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,CAAC;QAClD,MAAM,aAAa,GAAG,MAAM,CAC1B,QAAQ,CAAC,cAAc,EACvB,UAAU,CAAC,aAAa,EACxB,GAAG,IAAI,gBAAgB,CACxB,CAAC;QACF,MAAM,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,WAAW,EAAU,CAAC;QACpF,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE;YACzD,MAAM,QAAQ,GAAG,GAAG,IAAI,UAAU,KAAK,GAAG,CAAC;YAC3C,2EAA2E;YAC3E,oEAAoE;YACpE,MAAM,SAAS,GAAc,EAAE,CAAC;YAChC,MAAM,iBAAiB,GAAc,EAAE,CAAC;YACxC,MAAM,kBAAkB,GAAc,EAAE,CAAC;YACzC,MAAM,MAAM,GACV,UAAU,CAAC,MAAM,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,MAAM;gBAC7D,CAAC,CAAC,UAAU,CAAC,MAAM;gBACnB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,QAAQ,aAAa,CAAC,CAAC;YAC/E,MAAM,YAAY,GAAG,MAAM,CACzB,QAAQ,CAAC,OAAO,EAChB,UAAU,CAAC,YAAY,EACvB,GAAG,QAAQ,eAAe,CAC3B,CAAC;YACF,MAAM,MAAM,GAAG;gBACb,IAAI,EAAE,UAAU,CAAC,IAAI;gBACrB,WAAW,EAAE,UAAU,CAAC,WAAW;gBACnC,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,QAAQ,MAAM,CAAC;gBAC/D,MAAM;gBACN,SAAS;gBACT,iBAAiB;gBACjB,kBAAkB;aACnB,CAAC;YACF,OAAO;gBACL,UAAU;gBACV,IAAI,EAAE,QAAQ;gBACd,SAAS;gBACT,iBAAiB;gBACjB,kBAAkB;gBAClB,IAAI,EAAE,YAAY;oBAChB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,YAAY,EAAE,CAAC;oBAClD,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC;aAChC,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1E,MAAM,YAAY,GAAG,CAAC,GAAG,GAAsB,EAAE,EAAE,SAAiB,EAAa,EAAE,CACjF,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YACxB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,CAAC,KAAK;gBACR,MAAM,IAAI,qBAAqB,CAC7B,GAAG,SAAS,IAAI,KAAK,GAAG,EACxB,gBAAgB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CACzC,CAAC;YACJ,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;QACL,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,KAAK,MAAM,KAAK,IAAI,CAAC,WAAW,EAAE,mBAAmB,EAAE,oBAAoB,CAAU,EAAE,CAAC;gBACtF,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;YACxF,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,IAAI,eAAe,CAAC,CAAC;QAC/F,SAAS,CAAC,GAAG,CACX,EAAE,EACF,cAAc,CAAC;YACb,EAAE;YACF,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;YACzC,OAAO,EAAE,YAAY,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,UAAU,CAAC;YAC5D,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,YAAY,EAAE,GAAG,IAAI,eAAe,CAAC;YACvF,QAAQ,EACN,UAAU,CAAC,QAAQ,CAAC,cAAc,KAAK,SAAS;gBAC9C,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,cAAc,CACZ,YAAY,CAAC,UAAU,CAAC,QAAQ,CAAC,cAAc,EAAE,GAAG,IAAI,0BAA0B,CAAC,EACnF,QAAQ,CACT;SACR,CAAC,CACH,CAAC;IACJ,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import {\n createSteps,\n definePipeline,\n requireOutputs,\n type AnyStep,\n type Pipeline,\n type PipelineExecutionContext,\n type PipelineStepContext,\n type StandardSchemaV1,\n} from \"../core/pipeline.js\";\nimport { validatePipelineDocument, PipelineDocumentError } from \"./project-document.js\";\n\nexport { PipelineDocumentError };\nexport type {\n PipelineDocument,\n PipelineDocumentDefinition,\n PipelineDocumentStep,\n} from \"./project-document.js\";\n\n/** Dependency values are checked at runtime rather than inferred from a project document. */\nexport type PipelineDocumentHandler = (\n inputs: Record<string, unknown>,\n context: PipelineStepContext<object>\n) => unknown;\n\nexport type PipelineDocumentFinalizer = (\n outputs: Record<string, unknown>,\n context: PipelineExecutionContext<object>\n) => unknown;\n\n/** Only explicitly registered functions and schemas can be referenced by a document. */\nexport interface PipelineDocumentRegistry {\n steps: Readonly<Record<string, PipelineDocumentHandler>>;\n finalizers: Readonly<Record<string, PipelineDocumentFinalizer>>;\n optionsSchemas?: Readonly<Record<string, StandardSchemaV1<object, object>>>;\n schemas?: Readonly<Record<string, StandardSchemaV1>>;\n}\n\nfunction resolve<T>(\n registry: Readonly<Record<string, T>> | undefined,\n name: string,\n path: string\n): T {\n if (!registry || !Object.hasOwn(registry, name)) {\n throw new PipelineDocumentError(path, `Unknown registered name ${JSON.stringify(name)}`);\n }\n return registry[name];\n}\n\nfunction handler<T extends PipelineDocumentHandler | PipelineDocumentFinalizer>(\n registry: Readonly<Record<string, T>>,\n name: string,\n path: string\n): T {\n const value = resolve(registry, name, path);\n if (typeof value !== \"function\")\n throw new PipelineDocumentError(path, \"Expected a registered function\");\n return value;\n}\n\nfunction schema<T extends StandardSchemaV1>(\n registry: Readonly<Record<string, T>> | undefined,\n name: string | undefined,\n path: string\n): T | undefined {\n if (name === undefined) return undefined;\n const value = resolve(registry, name, path);\n if (\n !value ||\n value[\"~standard\"]?.version !== 1 ||\n typeof value[\"~standard\"].validate !== \"function\"\n ) {\n throw new PipelineDocumentError(path, \"Expected a Standard Schema v1 schema\");\n }\n return value;\n}\n\n/**\n * Compile parsed YAML or JSON into ordinary pipelines. Does not load modules,\n * invoke handlers or schemas, or perform I/O. Graph validation belongs to core.\n */\nexport function compilePipelineDocument(\n document: unknown,\n registry: PipelineDocumentRegistry\n): ReadonlyMap<string, Pipeline<object, unknown>> {\n const parsed = validatePipelineDocument(document);\n const pipelines = new Map<string, Pipeline<object, unknown>>();\n for (const [id, definition] of Object.entries(parsed.pipelines)) {\n const path = `$.pipelines[${JSON.stringify(id)}]`;\n const optionsSchema = schema(\n registry.optionsSchemas,\n definition.optionsSchema,\n `${path}.optionsSchema`\n );\n const { step } = optionsSchema ? createSteps(optionsSchema) : createSteps<object>();\n const entries = definition.steps.map((definition, index) => {\n const stepPath = `${path}.steps[${index}]`;\n // Populate these arrays after all steps exist, allowing forward references\n // and leaving cycle detection to the existing definition validator.\n const dependsOn: AnyStep[] = [];\n const optionalDependsOn: AnyStep[] = [];\n const skipAfterFailureOf: AnyStep[] = [];\n const dryRun =\n definition.dryRun === undefined || definition.dryRun === \"skip\"\n ? definition.dryRun\n : handler(registry.steps, definition.dryRun.run, `${stepPath}.dryRun.run`);\n const outputSchema = schema(\n registry.schemas,\n definition.outputSchema,\n `${stepPath}.outputSchema`\n );\n const config = {\n name: definition.name,\n description: definition.description,\n run: handler(registry.steps, definition.run, `${stepPath}.run`),\n dryRun,\n dependsOn,\n optionalDependsOn,\n skipAfterFailureOf,\n };\n return {\n definition,\n path: stepPath,\n dependsOn,\n optionalDependsOn,\n skipAfterFailureOf,\n step: outputSchema\n ? step(definition.id, { ...config, outputSchema })\n : step(definition.id, config),\n };\n });\n const byId = new Map(entries.map((entry) => [entry.step.id, entry.step]));\n const resolveSteps = (ids: readonly string[] = [], fieldPath: string): AnyStep[] =>\n ids.map((stepId, index) => {\n const found = byId.get(stepId);\n if (!found)\n throw new PipelineDocumentError(\n `${fieldPath}[${index}]`,\n `Unknown step ${JSON.stringify(stepId)}`\n );\n return found;\n });\n for (const entry of entries) {\n for (const field of [\"dependsOn\", \"optionalDependsOn\", \"skipAfterFailureOf\"] as const) {\n entry[field].push(...resolveSteps(entry.definition[field], `${entry.path}.${field}`));\n }\n }\n const finalize = handler(registry.finalizers, definition.finalize.run, `${path}.finalize.run`);\n pipelines.set(\n id,\n definePipeline({\n id,\n steps: entries.map((entry) => entry.step),\n targets: resolveSteps(definition.targets, `${path}.targets`),\n resultSchema: schema(registry.schemas, definition.resultSchema, `${path}.resultSchema`),\n finalize:\n definition.finalize.requireOutputs === undefined\n ? finalize\n : requireOutputs(\n resolveSteps(definition.finalize.requireOutputs, `${path}.finalize.requireOutputs`),\n finalize\n ),\n })\n );\n }\n return pipelines;\n}\n"]}
1
+ {"version":3,"file":"project-compiler.js","sourceRoot":"","sources":["../../src/project/project-compiler.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createSteps,\n definePipeline,\n requireOutputs,\n type AnyStep,\n type MappedChildProgressOptions,\n type Pipeline,\n type PipelineExecutionContext,\n type PipelineRun,\n type PipelineRunOptions,\n type PipelineStepContext,\n type StandardSchemaV1,\n type StepSkipDecision,\n} from \"../core/pipeline.js\";\nimport { validatePipelineDocument, PipelineDocumentError } from \"./project-document.js\";\n\nexport { PipelineDocumentError };\n\n/** Dependency values are checked at runtime rather than inferred from a project document. */\ntype PipelineDocumentHandler = (\n inputs: Record<string, unknown>,\n context: PipelineStepContext<object>\n) => unknown;\n\ntype PipelineDocumentFinalizer = (\n outputs: Record<string, unknown>,\n context: PipelineExecutionContext<object>\n) => unknown;\n\ntype PipelineDocumentSkipPredicate = (\n inputs: Record<string, unknown>,\n context: PipelineExecutionContext<object>\n) => StepSkipDecision | Promise<StepSkipDecision>;\n\n/** Application-owned wiring for one declarative `fromPipeline` step. */\ninterface PipelineDocumentFromPipelineAdapter {\n mapOptions(\n inputs: Record<string, unknown>,\n context: PipelineExecutionContext<object>\n ): PipelineRunOptions;\n mapResult?(\n value: unknown,\n result: PipelineRun<unknown>,\n context: PipelineStepContext<object>\n ): unknown;\n}\n\n/** Application-owned wiring for one declarative `forEachPipeline` step. */\ninterface PipelineDocumentForEachPipelineAdapter {\n items(\n inputs: Record<string, unknown>,\n context: PipelineExecutionContext<object>\n ): readonly unknown[] | Promise<readonly unknown[]>;\n key(item: unknown, index: number): string;\n concurrency?:\n | number\n | ((inputs: Record<string, unknown>, context: PipelineExecutionContext<object>) => number);\n progress?: MappedChildProgressOptions;\n mapOptions(\n item: unknown,\n index: number,\n inputs: Record<string, unknown>,\n context: PipelineExecutionContext<object>\n ): PipelineRunOptions;\n mapResult?(\n value: unknown,\n result: PipelineRun<unknown>,\n item: unknown,\n index: number,\n context: PipelineStepContext<object>\n ): unknown;\n}\n\n/** Only explicitly registered functions and schemas can be referenced by a document. */\nexport interface PipelineDocumentRegistry {\n steps: Readonly<Record<string, PipelineDocumentHandler>>;\n finalizers: Readonly<Record<string, PipelineDocumentFinalizer>>;\n skipPredicates?: Readonly<Record<string, PipelineDocumentSkipPredicate>>;\n fromPipelineAdapters?: Readonly<Record<string, PipelineDocumentFromPipelineAdapter>>;\n forEachPipelineAdapters?: Readonly<Record<string, PipelineDocumentForEachPipelineAdapter>>;\n optionsSchemas?: Readonly<Record<string, StandardSchemaV1<object, object>>>;\n schemas?: Readonly<Record<string, StandardSchemaV1>>;\n}\n\nfunction resolve<T>(\n registry: Readonly<Record<string, T>> | undefined,\n name: string,\n path: string\n): T {\n if (!registry || !Object.hasOwn(registry, name)) {\n throw new PipelineDocumentError(path, `Unknown registered name ${JSON.stringify(name)}`);\n }\n return registry[name];\n}\n\nfunction handler<T extends PipelineDocumentHandler | PipelineDocumentFinalizer>(\n registry: Readonly<Record<string, T>>,\n name: string,\n path: string\n): T {\n const value = resolve(registry, name, path);\n if (typeof value !== \"function\")\n throw new PipelineDocumentError(path, \"Expected a registered function\");\n return value;\n}\n\nfunction schema<T extends StandardSchemaV1>(\n registry: Readonly<Record<string, T>> | undefined,\n name: string | undefined,\n path: string\n): T | undefined {\n if (name === undefined) return undefined;\n const value = resolve(registry, name, path);\n if (\n !value ||\n value[\"~standard\"]?.version !== 1 ||\n typeof value[\"~standard\"].validate !== \"function\"\n ) {\n throw new PipelineDocumentError(path, \"Expected a Standard Schema v1 schema\");\n }\n return value;\n}\n\nfunction registeredObject<T extends object>(\n registry: Readonly<Record<string, T>> | undefined,\n name: string,\n path: string\n): T {\n const value = resolve(registry, name, path);\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new PipelineDocumentError(path, \"Expected a registered adapter object\");\n }\n return value;\n}\n\nfunction fromPipelineAdapter(\n registry: PipelineDocumentRegistry[\"fromPipelineAdapters\"],\n name: string,\n path: string\n): PipelineDocumentFromPipelineAdapter {\n const adapter = registeredObject(registry, name, path);\n if (typeof adapter.mapOptions !== \"function\") {\n throw new PipelineDocumentError(path, \"Expected an adapter with mapOptions\");\n }\n if (adapter.mapResult !== undefined && typeof adapter.mapResult !== \"function\") {\n throw new PipelineDocumentError(path, \"Expected adapter mapResult to be a function\");\n }\n return { mapOptions: adapter.mapOptions, mapResult: adapter.mapResult };\n}\n\nfunction forEachPipelineAdapter(\n registry: PipelineDocumentRegistry[\"forEachPipelineAdapters\"],\n name: string,\n path: string\n): PipelineDocumentForEachPipelineAdapter {\n const adapter = registeredObject(registry, name, path);\n if (\n typeof adapter.items !== \"function\" ||\n typeof adapter.key !== \"function\" ||\n typeof adapter.mapOptions !== \"function\"\n ) {\n throw new PipelineDocumentError(path, \"Expected an adapter with items, key, and mapOptions\");\n }\n if (\n adapter.concurrency !== undefined &&\n typeof adapter.concurrency !== \"number\" &&\n typeof adapter.concurrency !== \"function\"\n ) {\n throw new PipelineDocumentError(\n path,\n \"Expected adapter concurrency to be a number or function\"\n );\n }\n if (adapter.mapResult !== undefined && typeof adapter.mapResult !== \"function\") {\n throw new PipelineDocumentError(path, \"Expected adapter mapResult to be a function\");\n }\n return {\n items: adapter.items,\n key: adapter.key,\n concurrency: adapter.concurrency,\n progress: adapter.progress,\n mapOptions: adapter.mapOptions,\n mapResult: adapter.mapResult,\n };\n}\n\n/**\n * Compile parsed YAML or JSON into ordinary pipelines. Does not load modules,\n * invoke handlers or schemas, or perform I/O. Graph validation belongs to core.\n */\nexport function compilePipelineDocument(\n document: unknown,\n registry: PipelineDocumentRegistry\n): ReadonlyMap<string, Pipeline<object, unknown>> {\n const parsed = validatePipelineDocument(document);\n const compiled = new Map<string, Pipeline<object, unknown>>();\n const compiling = new Set<string>();\n\n const resolvePipeline = (id: string, path: string): Pipeline<object, unknown> => {\n if (!Object.hasOwn(parsed.pipelines, id)) {\n throw new PipelineDocumentError(path, `Unknown pipeline ${JSON.stringify(id)}`);\n }\n if (compiling.has(id)) {\n throw new PipelineDocumentError(\n path,\n `Pipeline composition cycle through ${JSON.stringify(id)}`\n );\n }\n return compile(id);\n };\n\n const compile = (id: string): Pipeline<object, unknown> => {\n const existing = compiled.get(id);\n if (existing) return existing;\n const definition = parsed.pipelines[id]!;\n const path = `$.pipelines[${JSON.stringify(id)}]`;\n compiling.add(id);\n const optionsSchema = schema(\n registry.optionsSchemas,\n definition.optionsSchema,\n `${path}.optionsSchema`\n );\n const factory = optionsSchema ? createSteps(optionsSchema) : createSteps<object>();\n const entries = definition.steps.map((definition, index) => {\n const stepPath = `${path}.steps[${index}]`;\n // Populate these arrays after all steps exist, allowing forward references\n // and leaving cycle detection to the existing definition validator.\n const dependsOn: AnyStep[] = [];\n const optionalDependsOn: AnyStep[] = [];\n const skipAfterFailureOf: AnyStep[] = [];\n const dryRun =\n definition.dryRun === undefined || definition.dryRun === \"skip\"\n ? definition.dryRun\n : handler(registry.steps, definition.dryRun.run, `${stepPath}.dryRun.run`);\n const outputSchema = schema(\n registry.schemas,\n definition.outputSchema,\n `${stepPath}.outputSchema`\n );\n const common = {\n name: definition.name,\n description: definition.description,\n dependsOn,\n optionalDependsOn,\n skipAfterFailureOf,\n };\n const skip =\n definition.skip === undefined\n ? undefined\n : handler(registry.skipPredicates ?? {}, definition.skip, `${stepPath}.skip`);\n let built: AnyStep<object>;\n if (definition.run !== undefined) {\n const config = {\n ...common,\n dryRun,\n run: handler(registry.steps, definition.run, `${stepPath}.run`),\n skip,\n };\n built = outputSchema\n ? factory.step(definition.id, { ...config, outputSchema })\n : factory.step(definition.id, config);\n } else if (definition.fromPipeline !== undefined) {\n const reference = definition.fromPipeline;\n const adapter = fromPipelineAdapter(\n registry.fromPipelineAdapters,\n reference.adapter,\n `${stepPath}.fromPipeline.adapter`\n );\n const config = {\n ...common,\n dryRun: definition.dryRun,\n pipeline: resolvePipeline(reference.pipeline, `${stepPath}.fromPipeline.pipeline`),\n mapOptions: adapter.mapOptions,\n skip,\n };\n built =\n adapter.mapResult === undefined\n ? factory.fromPipeline(definition.id, config)\n : factory.fromPipeline(definition.id, { ...config, mapResult: adapter.mapResult });\n } else {\n const reference = definition.forEachPipeline;\n const adapter = forEachPipelineAdapter(\n registry.forEachPipelineAdapters,\n reference.adapter,\n `${stepPath}.forEachPipeline.adapter`\n );\n // SAFETY: document pipelines publish unknown values. The fan-out builder\n // still represents a valued skip as the complete unknown result array.\n const fanOutSkip = skip as\n | ((\n inputs: Record<string, unknown>,\n context: PipelineExecutionContext<object>\n ) =>\n | StepSkipDecision<readonly unknown[]>\n | Promise<StepSkipDecision<readonly unknown[]>>)\n | undefined;\n const config = {\n ...common,\n dryRun: definition.dryRun,\n pipeline: resolvePipeline(reference.pipeline, `${stepPath}.forEachPipeline.pipeline`),\n items: adapter.items,\n key: adapter.key,\n concurrency: adapter.concurrency,\n progress: adapter.progress,\n mapOptions: adapter.mapOptions,\n skip: fanOutSkip,\n };\n built =\n adapter.mapResult === undefined\n ? factory.forEachPipeline(definition.id, config)\n : factory.forEachPipeline(definition.id, { ...config, mapResult: adapter.mapResult });\n }\n return {\n definition,\n path: stepPath,\n dependsOn,\n optionalDependsOn,\n skipAfterFailureOf,\n step: built,\n };\n });\n const byId = new Map(entries.map((entry) => [entry.step.id, entry.step]));\n const resolveSteps = (ids: readonly string[] = [], fieldPath: string): AnyStep[] =>\n ids.map((stepId, index) => {\n const found = byId.get(stepId);\n if (!found)\n throw new PipelineDocumentError(\n `${fieldPath}[${index}]`,\n `Unknown step ${JSON.stringify(stepId)}`\n );\n return found;\n });\n for (const entry of entries) {\n for (const field of [\"dependsOn\", \"optionalDependsOn\", \"skipAfterFailureOf\"] as const) {\n entry[field].push(...resolveSteps(entry.definition[field], `${entry.path}.${field}`));\n }\n }\n const finalize = handler(registry.finalizers, definition.finalize.run, `${path}.finalize.run`);\n const pipeline = definePipeline({\n id,\n steps: entries.map((entry) => entry.step),\n targets: resolveSteps(definition.targets, `${path}.targets`),\n resultSchema: schema(registry.schemas, definition.resultSchema, `${path}.resultSchema`),\n finalize:\n definition.finalize.requireOutputs === undefined\n ? finalize\n : requireOutputs(\n resolveSteps(definition.finalize.requireOutputs, `${path}.finalize.requireOutputs`),\n finalize\n ),\n });\n compiled.set(id, pipeline);\n compiling.delete(id);\n return pipeline;\n };\n\n return new Map(Object.keys(parsed.pipelines).map((id) => [id, compile(id)]));\n}\n"]}
@@ -24,19 +24,48 @@ export interface PipelineDocumentDefinition {
24
24
  requireOutputs?: readonly string[];
25
25
  };
26
26
  }
27
- export interface PipelineDocumentStep {
27
+ interface PipelineDocumentStepBase {
28
28
  id: string;
29
- run: string;
30
29
  name?: string;
31
30
  description?: string;
32
31
  dependsOn?: readonly string[];
33
32
  optionalDependsOn?: readonly string[];
34
33
  skipAfterFailureOf?: readonly string[];
34
+ /** Name in `registry.skipPredicates`. */
35
+ skip?: string;
36
+ }
37
+ interface PipelineDocumentRunStep extends PipelineDocumentStepBase {
38
+ run: string;
39
+ fromPipeline?: never;
40
+ forEachPipeline?: never;
35
41
  dryRun?: "skip" | {
36
42
  run: string;
37
43
  };
38
44
  outputSchema?: string;
39
45
  }
46
+ interface PipelineDocumentFromPipelineStep extends PipelineDocumentStepBase {
47
+ run?: never;
48
+ /** Compose one pipeline from this document through a registered adapter. */
49
+ fromPipeline: {
50
+ pipeline: string;
51
+ adapter: string;
52
+ };
53
+ forEachPipeline?: never;
54
+ dryRun?: "skip";
55
+ outputSchema?: never;
56
+ }
57
+ interface PipelineDocumentForEachPipelineStep extends PipelineDocumentStepBase {
58
+ run?: never;
59
+ fromPipeline?: never;
60
+ /** Fan out one document pipeline through a registered adapter. */
61
+ forEachPipeline: {
62
+ pipeline: string;
63
+ adapter: string;
64
+ };
65
+ dryRun?: "skip";
66
+ outputSchema?: never;
67
+ }
68
+ export type PipelineDocumentStep = PipelineDocumentRunStep | PipelineDocumentFromPipelineStep | PipelineDocumentForEachPipelineStep;
40
69
  /** A document shape or reference error; graph errors remain PipelineDefinitionError. */
41
70
  export declare class PipelineDocumentError extends Error {
42
71
  readonly path: string;
@@ -45,3 +74,4 @@ export declare class PipelineDocumentError extends Error {
45
74
  }
46
75
  /** Validate and copy a parsed document without resolving handlers or executing code. */
47
76
  export declare function validatePipelineDocument(value: unknown): PipelineDocument;
77
+ export {};
@@ -40,18 +40,37 @@ function references(value, path) {
40
40
  throw new PipelineDocumentError(path, "Expected an array of names");
41
41
  return value.map((item, index) => text(item, `${path}[${index}]`));
42
42
  }
43
+ function compositionReference(value, path) {
44
+ if (value === undefined)
45
+ return undefined;
46
+ const reference = fields(value, path, ["pipeline", "adapter"]);
47
+ return {
48
+ pipeline: text(reference.pipeline, `${path}.pipeline`),
49
+ adapter: text(reference.adapter, `${path}.adapter`),
50
+ };
51
+ }
43
52
  function parseStep(value, path) {
44
53
  const step = fields(value, path, [
45
54
  "id",
46
55
  "run",
56
+ "fromPipeline",
57
+ "forEachPipeline",
47
58
  "name",
48
59
  "description",
49
60
  "dependsOn",
50
61
  "optionalDependsOn",
51
62
  "skipAfterFailureOf",
63
+ "skip",
52
64
  "dryRun",
53
65
  "outputSchema",
54
66
  ]);
67
+ const run = optionalText(step.run, `${path}.run`);
68
+ const fromPipeline = compositionReference(step.fromPipeline, `${path}.fromPipeline`);
69
+ const forEachPipeline = compositionReference(step.forEachPipeline, `${path}.forEachPipeline`);
70
+ const kinds = [run, fromPipeline, forEachPipeline].filter((kind) => kind !== undefined);
71
+ if (kinds.length !== 1) {
72
+ throw new PipelineDocumentError(path, "Expected exactly one of run, fromPipeline, or forEachPipeline");
73
+ }
55
74
  let dryRun;
56
75
  if (step.dryRun === "skip")
57
76
  dryRun = "skip";
@@ -59,17 +78,33 @@ function parseStep(value, path) {
59
78
  const handler = fields(step.dryRun, `${path}.dryRun`, ["run"]);
60
79
  dryRun = { run: text(handler.run, `${path}.dryRun.run`) };
61
80
  }
62
- return {
81
+ const common = {
63
82
  id: text(step.id, `${path}.id`),
64
- run: text(step.run, `${path}.run`),
65
83
  name: optionalText(step.name, `${path}.name`),
66
84
  description: optionalText(step.description, `${path}.description`),
67
85
  dependsOn: references(step.dependsOn, `${path}.dependsOn`),
68
86
  optionalDependsOn: references(step.optionalDependsOn, `${path}.optionalDependsOn`),
69
87
  skipAfterFailureOf: references(step.skipAfterFailureOf, `${path}.skipAfterFailureOf`),
70
- outputSchema: optionalText(step.outputSchema, `${path}.outputSchema`),
71
- dryRun,
88
+ skip: optionalText(step.skip, `${path}.skip`),
72
89
  };
90
+ if (run !== undefined) {
91
+ return {
92
+ ...common,
93
+ run,
94
+ outputSchema: optionalText(step.outputSchema, `${path}.outputSchema`),
95
+ dryRun,
96
+ };
97
+ }
98
+ if (step.outputSchema !== undefined) {
99
+ throw new PipelineDocumentError(`${path}.outputSchema`, "Only ordinary run steps support outputSchema");
100
+ }
101
+ if (dryRun !== undefined && dryRun !== "skip") {
102
+ throw new PipelineDocumentError(`${path}.dryRun`, "Composed pipeline steps only support dryRun: skip");
103
+ }
104
+ if (fromPipeline !== undefined)
105
+ return { ...common, fromPipeline, dryRun };
106
+ // The exactly-one check above establishes this final variant.
107
+ return { ...common, forEachPipeline: forEachPipeline, dryRun };
73
108
  }
74
109
  function metadata(value) {
75
110
  if (value === undefined)
@@ -1 +1 @@
1
- {"version":3,"file":"project-document.js","sourceRoot":"","sources":["../../src/project/project-document.ts"],"names":[],"mappings":"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","sourcesContent":["/** The serializable version 1 pipeline authoring format for project documents. */\nexport interface PipelineDocument {\n /** Optional editor schema association; never fetched during validation. */\n $schema?: string;\n version: 1;\n metadata?: PipelineDocumentMetadata;\n pipelines: Record<string, PipelineDocumentDefinition>;\n}\n\n/** Optional human-facing document information, never execution policy. */\nexport interface PipelineDocumentMetadata {\n name?: string;\n description?: string;\n authors?: readonly string[];\n /** Author-maintained date in YYYY-MM-DD format. */\n date?: string;\n}\n\nexport interface PipelineDocumentDefinition {\n optionsSchema?: string;\n resultSchema?: string;\n targets?: readonly string[];\n steps: readonly PipelineDocumentStep[];\n finalize: { run: string; requireOutputs?: readonly string[] };\n}\n\nexport interface PipelineDocumentStep {\n id: string;\n run: string;\n name?: string;\n description?: string;\n dependsOn?: readonly string[];\n optionalDependsOn?: readonly string[];\n skipAfterFailureOf?: readonly string[];\n dryRun?: \"skip\" | { run: string };\n outputSchema?: string;\n}\n\n/** A document shape or reference error; graph errors remain PipelineDefinitionError. */\nexport class PipelineDocumentError extends Error {\n readonly code = \"TUBELESS_DOCUMENT_INVALID\";\n\n constructor(\n readonly path: string,\n message: string\n ) {\n super(`${path}: ${message}`);\n this.name = \"PipelineDocumentError\";\n }\n}\n\nfunction record(value: unknown, path: string): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new PipelineDocumentError(path, \"Expected an object\");\n }\n // SAFETY: the check above establishes a non-null, non-array object; values stay unknown.\n return value as Record<string, unknown>;\n}\n\nfunction fields(value: unknown, path: string, allowed: readonly string[]) {\n const object = record(value, path);\n for (const key of Object.keys(object)) {\n if (!allowed.includes(key)) {\n throw new PipelineDocumentError(`${path}.${key}`, \"Unknown field\");\n }\n }\n return object;\n}\n\nfunction text(value: unknown, path: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new PipelineDocumentError(path, \"Expected a non-empty string\");\n }\n return value;\n}\n\nfunction optionalText(value: unknown, path: string): string | undefined {\n return value === undefined ? undefined : text(value, path);\n}\n\nfunction references(value: unknown, path: string): readonly string[] | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value)) throw new PipelineDocumentError(path, \"Expected an array of names\");\n return value.map((item: unknown, index) => text(item, `${path}[${index}]`));\n}\n\nfunction parseStep(value: unknown, path: string): PipelineDocumentStep {\n const step = fields(value, path, [\n \"id\",\n \"run\",\n \"name\",\n \"description\",\n \"dependsOn\",\n \"optionalDependsOn\",\n \"skipAfterFailureOf\",\n \"dryRun\",\n \"outputSchema\",\n ]);\n let dryRun: PipelineDocumentStep[\"dryRun\"];\n if (step.dryRun === \"skip\") dryRun = \"skip\";\n else if (step.dryRun !== undefined) {\n const handler = fields(step.dryRun, `${path}.dryRun`, [\"run\"]);\n dryRun = { run: text(handler.run, `${path}.dryRun.run`) };\n }\n return {\n id: text(step.id, `${path}.id`),\n run: text(step.run, `${path}.run`),\n name: optionalText(step.name, `${path}.name`),\n description: optionalText(step.description, `${path}.description`),\n dependsOn: references(step.dependsOn, `${path}.dependsOn`),\n optionalDependsOn: references(step.optionalDependsOn, `${path}.optionalDependsOn`),\n skipAfterFailureOf: references(step.skipAfterFailureOf, `${path}.skipAfterFailureOf`),\n outputSchema: optionalText(step.outputSchema, `${path}.outputSchema`),\n dryRun,\n };\n}\n\nfunction metadata(value: unknown): PipelineDocumentMetadata | undefined {\n if (value === undefined) return undefined;\n const entry = fields(value, \"$.metadata\", [\"name\", \"description\", \"authors\", \"date\"]);\n const date = optionalText(entry.date, \"$.metadata.date\");\n if (\n date !== undefined &&\n (!/^\\d{4}-\\d{2}-\\d{2}$/.test(date) ||\n !Number.isFinite(Date.parse(date)) ||\n new Date(date).toISOString().slice(0, 10) !== date)\n ) {\n throw new PipelineDocumentError(\"$.metadata.date\", \"Expected a valid YYYY-MM-DD date\");\n }\n return {\n name: optionalText(entry.name, \"$.metadata.name\"),\n description: optionalText(entry.description, \"$.metadata.description\"),\n authors: references(entry.authors, \"$.metadata.authors\"),\n date,\n };\n}\n\n/** Validate and copy a parsed document without resolving handlers or executing code. */\nexport function validatePipelineDocument(value: unknown): PipelineDocument {\n const document = fields(value, \"$\", [\"$schema\", \"version\", \"metadata\", \"pipelines\"]);\n if (document.version !== 1) {\n throw new PipelineDocumentError(\"$.version\", \"Expected document version 1\");\n }\n const pipelines = record(document.pipelines, \"$.pipelines\");\n if (Object.keys(pipelines).length === 0) {\n throw new PipelineDocumentError(\"$.pipelines\", \"Expected at least one pipeline\");\n }\n return {\n $schema: optionalText(document.$schema, \"$.$schema\"),\n version: 1,\n metadata: metadata(document.metadata),\n pipelines: Object.fromEntries(\n Object.entries(pipelines).map(([id, value]) => {\n const path = `$.pipelines[${JSON.stringify(id)}]`;\n text(id, path);\n const pipeline = fields(value, path, [\n \"optionsSchema\",\n \"resultSchema\",\n \"targets\",\n \"steps\",\n \"finalize\",\n ]);\n if (!Array.isArray(pipeline.steps) || pipeline.steps.length === 0) {\n throw new PipelineDocumentError(`${path}.steps`, \"Expected at least one step\");\n }\n const finalize = fields(pipeline.finalize, `${path}.finalize`, [\"run\", \"requireOutputs\"]);\n return [\n id,\n {\n optionsSchema: optionalText(pipeline.optionsSchema, `${path}.optionsSchema`),\n resultSchema: optionalText(pipeline.resultSchema, `${path}.resultSchema`),\n targets: references(pipeline.targets, `${path}.targets`),\n steps: pipeline.steps.map((step: unknown, index) =>\n parseStep(step, `${path}.steps[${index}]`)\n ),\n finalize: {\n run: text(finalize.run, `${path}.finalize.run`),\n requireOutputs: references(\n finalize.requireOutputs,\n `${path}.finalize.requireOutputs`\n ),\n },\n },\n ];\n })\n ),\n };\n}\n"]}
1
+ {"version":3,"file":"project-document.js","sourceRoot":"","sources":["../../src/project/project-document.ts"],"names":[],"mappings":"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","sourcesContent":["/** The serializable version 1 pipeline authoring format for project documents. */\nexport interface PipelineDocument {\n /** Optional editor schema association; never fetched during validation. */\n $schema?: string;\n version: 1;\n metadata?: PipelineDocumentMetadata;\n pipelines: Record<string, PipelineDocumentDefinition>;\n}\n\n/** Optional human-facing document information, never execution policy. */\nexport interface PipelineDocumentMetadata {\n name?: string;\n description?: string;\n authors?: readonly string[];\n /** Author-maintained date in YYYY-MM-DD format. */\n date?: string;\n}\n\nexport interface PipelineDocumentDefinition {\n optionsSchema?: string;\n resultSchema?: string;\n targets?: readonly string[];\n steps: readonly PipelineDocumentStep[];\n finalize: { run: string; requireOutputs?: readonly string[] };\n}\n\ninterface PipelineDocumentStepBase {\n id: string;\n name?: string;\n description?: string;\n dependsOn?: readonly string[];\n optionalDependsOn?: readonly string[];\n skipAfterFailureOf?: readonly string[];\n /** Name in `registry.skipPredicates`. */\n skip?: string;\n}\n\ninterface PipelineDocumentRunStep extends PipelineDocumentStepBase {\n run: string;\n fromPipeline?: never;\n forEachPipeline?: never;\n dryRun?: \"skip\" | { run: string };\n outputSchema?: string;\n}\n\ninterface PipelineDocumentFromPipelineStep extends PipelineDocumentStepBase {\n run?: never;\n /** Compose one pipeline from this document through a registered adapter. */\n fromPipeline: { pipeline: string; adapter: string };\n forEachPipeline?: never;\n dryRun?: \"skip\";\n outputSchema?: never;\n}\n\ninterface PipelineDocumentForEachPipelineStep extends PipelineDocumentStepBase {\n run?: never;\n fromPipeline?: never;\n /** Fan out one document pipeline through a registered adapter. */\n forEachPipeline: { pipeline: string; adapter: string };\n dryRun?: \"skip\";\n outputSchema?: never;\n}\n\nexport type PipelineDocumentStep =\n | PipelineDocumentRunStep\n | PipelineDocumentFromPipelineStep\n | PipelineDocumentForEachPipelineStep;\n\n/** A document shape or reference error; graph errors remain PipelineDefinitionError. */\nexport class PipelineDocumentError extends Error {\n readonly code = \"TUBELESS_DOCUMENT_INVALID\";\n\n constructor(\n readonly path: string,\n message: string\n ) {\n super(`${path}: ${message}`);\n this.name = \"PipelineDocumentError\";\n }\n}\n\nfunction record(value: unknown, path: string): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new PipelineDocumentError(path, \"Expected an object\");\n }\n // SAFETY: the check above establishes a non-null, non-array object; values stay unknown.\n return value as Record<string, unknown>;\n}\n\nfunction fields(value: unknown, path: string, allowed: readonly string[]) {\n const object = record(value, path);\n for (const key of Object.keys(object)) {\n if (!allowed.includes(key)) {\n throw new PipelineDocumentError(`${path}.${key}`, \"Unknown field\");\n }\n }\n return object;\n}\n\nfunction text(value: unknown, path: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new PipelineDocumentError(path, \"Expected a non-empty string\");\n }\n return value;\n}\n\nfunction optionalText(value: unknown, path: string): string | undefined {\n return value === undefined ? undefined : text(value, path);\n}\n\nfunction references(value: unknown, path: string): readonly string[] | undefined {\n if (value === undefined) return undefined;\n if (!Array.isArray(value)) throw new PipelineDocumentError(path, \"Expected an array of names\");\n return value.map((item: unknown, index) => text(item, `${path}[${index}]`));\n}\n\nfunction compositionReference(\n value: unknown,\n path: string\n): { pipeline: string; adapter: string } | undefined {\n if (value === undefined) return undefined;\n const reference = fields(value, path, [\"pipeline\", \"adapter\"]);\n return {\n pipeline: text(reference.pipeline, `${path}.pipeline`),\n adapter: text(reference.adapter, `${path}.adapter`),\n };\n}\n\nfunction parseStep(value: unknown, path: string): PipelineDocumentStep {\n const step = fields(value, path, [\n \"id\",\n \"run\",\n \"fromPipeline\",\n \"forEachPipeline\",\n \"name\",\n \"description\",\n \"dependsOn\",\n \"optionalDependsOn\",\n \"skipAfterFailureOf\",\n \"skip\",\n \"dryRun\",\n \"outputSchema\",\n ]);\n const run = optionalText(step.run, `${path}.run`);\n const fromPipeline = compositionReference(step.fromPipeline, `${path}.fromPipeline`);\n const forEachPipeline = compositionReference(step.forEachPipeline, `${path}.forEachPipeline`);\n const kinds = [run, fromPipeline, forEachPipeline].filter((kind) => kind !== undefined);\n if (kinds.length !== 1) {\n throw new PipelineDocumentError(\n path,\n \"Expected exactly one of run, fromPipeline, or forEachPipeline\"\n );\n }\n let dryRun: PipelineDocumentRunStep[\"dryRun\"];\n if (step.dryRun === \"skip\") dryRun = \"skip\";\n else if (step.dryRun !== undefined) {\n const handler = fields(step.dryRun, `${path}.dryRun`, [\"run\"]);\n dryRun = { run: text(handler.run, `${path}.dryRun.run`) };\n }\n const common = {\n id: text(step.id, `${path}.id`),\n name: optionalText(step.name, `${path}.name`),\n description: optionalText(step.description, `${path}.description`),\n dependsOn: references(step.dependsOn, `${path}.dependsOn`),\n optionalDependsOn: references(step.optionalDependsOn, `${path}.optionalDependsOn`),\n skipAfterFailureOf: references(step.skipAfterFailureOf, `${path}.skipAfterFailureOf`),\n skip: optionalText(step.skip, `${path}.skip`),\n };\n if (run !== undefined) {\n return {\n ...common,\n run,\n outputSchema: optionalText(step.outputSchema, `${path}.outputSchema`),\n dryRun,\n };\n }\n if (step.outputSchema !== undefined) {\n throw new PipelineDocumentError(\n `${path}.outputSchema`,\n \"Only ordinary run steps support outputSchema\"\n );\n }\n if (dryRun !== undefined && dryRun !== \"skip\") {\n throw new PipelineDocumentError(\n `${path}.dryRun`,\n \"Composed pipeline steps only support dryRun: skip\"\n );\n }\n if (fromPipeline !== undefined) return { ...common, fromPipeline, dryRun };\n // The exactly-one check above establishes this final variant.\n return { ...common, forEachPipeline: forEachPipeline!, dryRun };\n}\n\nfunction metadata(value: unknown): PipelineDocumentMetadata | undefined {\n if (value === undefined) return undefined;\n const entry = fields(value, \"$.metadata\", [\"name\", \"description\", \"authors\", \"date\"]);\n const date = optionalText(entry.date, \"$.metadata.date\");\n if (\n date !== undefined &&\n (!/^\\d{4}-\\d{2}-\\d{2}$/.test(date) ||\n !Number.isFinite(Date.parse(date)) ||\n new Date(date).toISOString().slice(0, 10) !== date)\n ) {\n throw new PipelineDocumentError(\"$.metadata.date\", \"Expected a valid YYYY-MM-DD date\");\n }\n return {\n name: optionalText(entry.name, \"$.metadata.name\"),\n description: optionalText(entry.description, \"$.metadata.description\"),\n authors: references(entry.authors, \"$.metadata.authors\"),\n date,\n };\n}\n\n/** Validate and copy a parsed document without resolving handlers or executing code. */\nexport function validatePipelineDocument(value: unknown): PipelineDocument {\n const document = fields(value, \"$\", [\"$schema\", \"version\", \"metadata\", \"pipelines\"]);\n if (document.version !== 1) {\n throw new PipelineDocumentError(\"$.version\", \"Expected document version 1\");\n }\n const pipelines = record(document.pipelines, \"$.pipelines\");\n if (Object.keys(pipelines).length === 0) {\n throw new PipelineDocumentError(\"$.pipelines\", \"Expected at least one pipeline\");\n }\n return {\n $schema: optionalText(document.$schema, \"$.$schema\"),\n version: 1,\n metadata: metadata(document.metadata),\n pipelines: Object.fromEntries(\n Object.entries(pipelines).map(([id, value]) => {\n const path = `$.pipelines[${JSON.stringify(id)}]`;\n text(id, path);\n const pipeline = fields(value, path, [\n \"optionsSchema\",\n \"resultSchema\",\n \"targets\",\n \"steps\",\n \"finalize\",\n ]);\n if (!Array.isArray(pipeline.steps) || pipeline.steps.length === 0) {\n throw new PipelineDocumentError(`${path}.steps`, \"Expected at least one step\");\n }\n const finalize = fields(pipeline.finalize, `${path}.finalize`, [\"run\", \"requireOutputs\"]);\n return [\n id,\n {\n optionsSchema: optionalText(pipeline.optionsSchema, `${path}.optionsSchema`),\n resultSchema: optionalText(pipeline.resultSchema, `${path}.resultSchema`),\n targets: references(pipeline.targets, `${path}.targets`),\n steps: pipeline.steps.map((step: unknown, index) =>\n parseStep(step, `${path}.steps[${index}]`)\n ),\n finalize: {\n run: text(finalize.run, `${path}.finalize.run`),\n requireOutputs: references(\n finalize.requireOutputs,\n `${path}.finalize.requireOutputs`\n ),\n },\n },\n ];\n })\n ),\n };\n}\n"]}
@@ -1,3 +1,3 @@
1
1
  export { definePipelineProject, type PipelineProjectCommandModule, type PipelineProjectManifest, type PipelineProjectManifestInput, } from "./project-manifest.js";
2
- export { validatePipelineDocument, type PipelineDocumentMetadata } from "./project-document.js";
3
- export { compilePipelineDocument, PipelineDocumentError, type PipelineDocument, type PipelineDocumentDefinition, type PipelineDocumentFinalizer, type PipelineDocumentHandler, type PipelineDocumentRegistry, type PipelineDocumentStep, } from "./project-compiler.js";
2
+ export { validatePipelineDocument } from "./project-document.js";
3
+ export { compilePipelineDocument, PipelineDocumentError, type PipelineDocumentRegistry, } from "./project-compiler.js";
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/project/project.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAItB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,wBAAwB,EAAiC,MAAM,uBAAuB,CAAC;AAEhG,OAAO,EACL,uBAAuB,EACvB,qBAAqB,GAOtB,MAAM,uBAAuB,CAAC","sourcesContent":["export {\n definePipelineProject,\n type PipelineProjectCommandModule,\n type PipelineProjectManifest,\n type PipelineProjectManifestInput,\n} from \"./project-manifest.js\";\n\nexport { validatePipelineDocument, type PipelineDocumentMetadata } from \"./project-document.js\";\n\nexport {\n compilePipelineDocument,\n PipelineDocumentError,\n type PipelineDocument,\n type PipelineDocumentDefinition,\n type PipelineDocumentFinalizer,\n type PipelineDocumentHandler,\n type PipelineDocumentRegistry,\n type PipelineDocumentStep,\n} from \"./project-compiler.js\";\n"]}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/project/project.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAItB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,EACL,uBAAuB,EACvB,qBAAqB,GAEtB,MAAM,uBAAuB,CAAC","sourcesContent":["export {\n definePipelineProject,\n type PipelineProjectCommandModule,\n type PipelineProjectManifest,\n type PipelineProjectManifestInput,\n} from \"./project-manifest.js\";\n\nexport { validatePipelineDocument } from \"./project-document.js\";\n\nexport {\n compilePipelineDocument,\n PipelineDocumentError,\n type PipelineDocumentRegistry,\n} from \"./project-compiler.js\";\n"]}
@@ -119,12 +119,8 @@ function renderStep(state, theme, spinner, progressBarWidth) {
119
119
  }
120
120
  }
121
121
  function reporterOutputFd(output) {
122
- const stdoutFd = output === process.stdout ? process.stdout.fd : undefined;
123
- for (const fd of [output.fd, stdoutFd]) {
124
- if (fd !== undefined && Number.isInteger(fd) && fd >= 0)
125
- return fd;
126
- }
127
- return undefined;
122
+ const fd = output.fd ?? (output === process.stdout ? process.stdout.fd : undefined);
123
+ return fd !== undefined && Number.isInteger(fd) && fd >= 0 ? fd : undefined;
128
124
  }
129
125
  function createInteractiveReporter(options, output) {
130
126
  const terminalIsTTY = options.terminal?.isTTY ?? output.isTTY === true;