tubeless 0.2.13 → 0.2.14

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 (125) hide show
  1. package/dist/cli/cli-pipeline-command.d.ts +7 -2
  2. package/dist/cli/cli-pipeline-command.js +24 -3
  3. package/dist/cli/cli-pipeline-command.js.map +1 -1
  4. package/dist/core/child-execution.js +5 -3
  5. package/dist/core/child-execution.js.map +1 -1
  6. package/dist/core/child-progress.js +2 -0
  7. package/dist/core/child-progress.js.map +1 -1
  8. package/dist/core/pipeline-cache.d.ts +68 -0
  9. package/dist/core/pipeline-cache.js +157 -0
  10. package/dist/core/pipeline-cache.js.map +1 -0
  11. package/dist/core/pipeline-compiler.js +4 -2
  12. package/dist/core/pipeline-compiler.js.map +1 -1
  13. package/dist/core/pipeline-definition-identity.d.ts +3 -1
  14. package/dist/core/pipeline-definition-identity.js +38 -5
  15. package/dist/core/pipeline-definition-identity.js.map +1 -1
  16. package/dist/core/pipeline-definition-validation.d.ts +7 -2
  17. package/dist/core/pipeline-definition-validation.js +22 -2
  18. package/dist/core/pipeline-definition-validation.js.map +1 -1
  19. package/dist/core/pipeline-definition.d.ts +6 -0
  20. package/dist/core/pipeline-definition.js.map +1 -1
  21. package/dist/core/pipeline-execute.js +5 -0
  22. package/dist/core/pipeline-execute.js.map +1 -1
  23. package/dist/core/pipeline-graph.d.ts +2 -1
  24. package/dist/core/pipeline-graph.js +10 -4
  25. package/dist/core/pipeline-graph.js.map +1 -1
  26. package/dist/core/pipeline-mermaid.js +7 -1
  27. package/dist/core/pipeline-mermaid.js.map +1 -1
  28. package/dist/core/pipeline-plan.js +4 -0
  29. package/dist/core/pipeline-plan.js.map +1 -1
  30. package/dist/core/pipeline-query.d.ts +11 -0
  31. package/dist/core/pipeline-query.js +10 -0
  32. package/dist/core/pipeline-query.js.map +1 -0
  33. package/dist/core/pipeline-run-state.d.ts +2 -2
  34. package/dist/core/pipeline-run-state.js.map +1 -1
  35. package/dist/core/pipeline-step-executor.d.ts +2 -0
  36. package/dist/core/pipeline-step-executor.js +24 -9
  37. package/dist/core/pipeline-step-executor.js.map +1 -1
  38. package/dist/core/pipeline-step-metadata.d.ts +1 -0
  39. package/dist/core/pipeline-step-metadata.js +1 -0
  40. package/dist/core/pipeline-step-metadata.js.map +1 -1
  41. package/dist/core/pipeline-steps.d.ts +17 -3
  42. package/dist/core/pipeline-steps.js +11 -3
  43. package/dist/core/pipeline-steps.js.map +1 -1
  44. package/dist/core/pipeline-types.d.ts +20 -7
  45. package/dist/core/pipeline-types.js.map +1 -1
  46. package/dist/core/pipeline.d.ts +4 -0
  47. package/dist/core/pipeline.js +7 -1
  48. package/dist/core/pipeline.js.map +1 -1
  49. package/dist/node/node.d.ts +1 -0
  50. package/dist/node/node.js +1 -0
  51. package/dist/node/node.js.map +1 -1
  52. package/dist/reporter/interactive-reporter.js +3 -4
  53. package/dist/reporter/interactive-reporter.js.map +1 -1
  54. package/dist/reporter/reporter.d.ts +1 -0
  55. package/dist/reporter/reporter.js +4 -2
  56. package/dist/reporter/reporter.js.map +1 -1
  57. package/dist/run-store/cache-artifact.d.ts +7 -0
  58. package/dist/run-store/cache-artifact.js +22 -0
  59. package/dist/run-store/cache-artifact.js.map +1 -0
  60. package/dist/run-store/definition-diff.js +20 -8
  61. package/dist/run-store/definition-diff.js.map +1 -1
  62. package/dist/run-store/run-projection.js +2 -2
  63. package/dist/run-store/run-projection.js.map +1 -1
  64. package/dist/run-store/run-store.d.ts +2 -2
  65. package/dist/run-store/run-store.js.map +1 -1
  66. package/dist/studio/run-store-ui-artifacts.d.ts +5 -0
  67. package/dist/studio/run-store-ui-artifacts.js +18 -0
  68. package/dist/studio/run-store-ui-artifacts.js.map +1 -0
  69. package/dist/studio/run-store-ui-client-browser.js +2 -2
  70. package/dist/studio/run-store-ui-client-transport.js +9 -3
  71. package/dist/studio/run-store-ui-client-transport.js.map +1 -1
  72. package/dist/studio/run-store-ui-client.d.ts +2 -2
  73. package/dist/studio/run-store-ui-client.js +13 -7
  74. package/dist/studio/run-store-ui-client.js.map +1 -1
  75. package/dist/studio/run-store-ui-definitions.js +2 -1
  76. package/dist/studio/run-store-ui-definitions.js.map +1 -1
  77. package/dist/studio/run-store-ui-metadata.d.ts +13 -0
  78. package/dist/studio/run-store-ui-metadata.js +32 -0
  79. package/dist/studio/run-store-ui-metadata.js.map +1 -0
  80. package/dist/studio/run-store-ui-page.js +8 -0
  81. package/dist/studio/run-store-ui-page.js.map +1 -1
  82. package/dist/tracing/artifact-metadata.d.ts +1 -0
  83. package/dist/tracing/artifact-metadata.js +2 -2
  84. package/dist/tracing/artifact-metadata.js.map +1 -1
  85. package/dist/tracing/graph-metadata.d.ts +12 -0
  86. package/dist/tracing/graph-metadata.js +55 -0
  87. package/dist/tracing/graph-metadata.js.map +1 -0
  88. package/dist/tracing/tracing-internal.js +2 -2
  89. package/dist/tracing/tracing-internal.js.map +1 -1
  90. package/dist/tracing/tracing-schema.d.ts +58 -31
  91. package/dist/tracing/tracing-schema.js +26 -8
  92. package/dist/tracing/tracing-schema.js.map +1 -1
  93. package/dist/utilities/cache-duration.d.ts +2 -0
  94. package/dist/utilities/cache-duration.js +39 -0
  95. package/dist/utilities/cache-duration.js.map +1 -0
  96. package/dist/utilities/cache-key.d.ts +2 -0
  97. package/dist/utilities/cache-key.js +63 -0
  98. package/dist/utilities/cache-key.js.map +1 -0
  99. package/dist/utilities/cache-storage.d.ts +6 -0
  100. package/dist/utilities/cache-storage.js +83 -0
  101. package/dist/utilities/cache-storage.js.map +1 -0
  102. package/dist/utilities/canonical-json.d.ts +2 -0
  103. package/dist/utilities/canonical-json.js +12 -0
  104. package/dist/utilities/canonical-json.js.map +1 -0
  105. package/dist/workbench/workbench-commands.js +43 -3
  106. package/dist/workbench/workbench-commands.js.map +1 -1
  107. package/dist/workbench/workbench-history.js +2 -1
  108. package/dist/workbench/workbench-history.js.map +1 -1
  109. package/docs/agent-guide.md +23 -0
  110. package/docs/api-reference.md +98 -85
  111. package/docs/api-report.json +19 -6
  112. package/docs/artifacts.md +6 -2
  113. package/docs/cli.md +14 -0
  114. package/docs/concepts.md +10 -0
  115. package/docs/declarative-pipelines.md +4 -0
  116. package/docs/graph-metadata.md +108 -0
  117. package/docs/llms.txt +2 -0
  118. package/docs/recipes.md +5 -0
  119. package/docs/step-output-cache.md +242 -0
  120. package/docs/studio.md +11 -2
  121. package/examples/graph-metadata.ts +32 -0
  122. package/examples/project/tubeless.project.ts +5 -0
  123. package/examples/step-output-cache.ts +30 -0
  124. package/package.json +1 -1
  125. package/skills/tubeless/SKILL.md +16 -0
@@ -0,0 +1,157 @@
1
+ import { createHash } from "node:crypto";
2
+ import { artifactMetadataSchema, artifactRecordSchema, } from "../tracing/artifact-metadata.js";
3
+ import { isAbortError, throwIfAborted } from "../utilities/abort.js";
4
+ import { cacheDurationMs } from "../utilities/cache-duration.js";
5
+ import { defaultCacheKey } from "../utilities/cache-key.js";
6
+ function validateSettings(settings) {
7
+ if (!settings || typeof settings !== "object" || Array.isArray(settings))
8
+ throw new Error("Cache settings must be an object");
9
+ cacheDurationMs(settings.maxAge);
10
+ if (settings.store !== undefined &&
11
+ (typeof settings.store?.get !== "function" || typeof settings.store?.set !== "function"))
12
+ throw new Error("Cache store requires get and set methods");
13
+ if (settings.codec !== undefined &&
14
+ (typeof settings.codec?.encode !== "function" || typeof settings.codec?.decode !== "function"))
15
+ throw new Error("Cache codec requires encode and decode methods");
16
+ }
17
+ export function validatePipelineCache(settings) {
18
+ if (settings === undefined)
19
+ return;
20
+ validateSettings(settings);
21
+ if (settings.directory !== undefined &&
22
+ (typeof settings.directory !== "string" || !settings.directory.trim()))
23
+ throw new Error("Cache directory must be nonblank");
24
+ }
25
+ export function compileStepCache(source, defaults, implementationVersion) {
26
+ if (source === undefined || source === false)
27
+ return undefined;
28
+ const config = source === true ? {} : source;
29
+ validateSettings(config);
30
+ const version = config.version ?? implementationVersion;
31
+ if (typeof version !== "string" || !version.trim() || version.length > 256)
32
+ throw new Error("Cached steps require a nonblank version or pipeline implementationVersion of at most 256 characters");
33
+ if (config.key !== undefined && typeof config.key !== "function")
34
+ throw new Error("Cache key must be a function");
35
+ if (config.policy !== undefined &&
36
+ typeof config.policy !== "function" &&
37
+ !["use", "recompute", "bypass"].includes(config.policy))
38
+ throw new Error("Invalid cache policy");
39
+ const store = config.store ?? defaults?.store;
40
+ const codec = config.codec ?? defaults?.codec;
41
+ return Object.freeze({
42
+ version,
43
+ directory: defaults?.directory ?? ".cache",
44
+ maxAge: cacheDurationMs(config.maxAge ?? defaults?.maxAge),
45
+ key: config.key?.bind(config),
46
+ policy: typeof config.policy === "function" ? config.policy.bind(config) : config.policy,
47
+ store: store
48
+ ? Object.freeze({ get: store.get.bind(store), set: store.set.bind(store) })
49
+ : undefined,
50
+ codec: codec
51
+ ? Object.freeze({ encode: codec.encode.bind(codec), decode: codec.decode.bind(codec) })
52
+ : undefined,
53
+ });
54
+ }
55
+ async function cacheOperation(operation, context, run) {
56
+ throwIfAborted(context.signal, "Step cache");
57
+ try {
58
+ const result = await run();
59
+ throwIfAborted(context.signal, "Step cache");
60
+ return result;
61
+ }
62
+ catch (cause) {
63
+ throwIfAborted(context.signal, "Step cache");
64
+ if (isAbortError(cause))
65
+ throw cause;
66
+ throw new Error(`Step cache ${operation} failed`, { cause });
67
+ }
68
+ }
69
+ /** One execution path: cache raw handler results, then validate every publication. */
70
+ export async function executeWithStepCache(input) {
71
+ const { cache, context } = input;
72
+ const uncached = async () => input.validate(await input.run());
73
+ if (!cache || context.dryRun)
74
+ return uncached();
75
+ const policy = await cacheOperation("policy", context, () => context.cachePolicy ??
76
+ (typeof cache.policy === "function"
77
+ ? cache.policy(input.inputs, context)
78
+ : (cache.policy ?? "use")));
79
+ if (policy === "bypass")
80
+ return uncached();
81
+ if (policy !== "use" && policy !== "recompute")
82
+ throw new Error("Invalid step cache policy");
83
+ const key = await cacheOperation("key", context, () => cache.key ? cache.key(input.inputs, context) : defaultCacheKey(input.inputs, context.options));
84
+ if (key === null)
85
+ return uncached();
86
+ if (typeof key !== "string" || !key.trim())
87
+ throw new Error("Step cache key must be a nonblank string or null");
88
+ const scopedKey = JSON.stringify([
89
+ "tubeless.step-cache.v1",
90
+ input.pipelineId,
91
+ input.stepId,
92
+ cache.version,
93
+ key,
94
+ ]);
95
+ // Load the optional built-in persistence only when an eligible step needs it.
96
+ const local = !cache.store || !cache.codec ? await import("../utilities/cache-storage.js") : undefined;
97
+ const store = cache.store ??
98
+ local.defaultFileStepCache(context.cwd, cache.directory, input.pipelineId, input.stepId);
99
+ const codec = cache.codec ?? local.v8StepCacheCodec;
100
+ const io = { signal: context.signal };
101
+ const recordArtifact = (operation, createdAtMs, receipt) => {
102
+ const artifact = receipt === undefined
103
+ ? undefined
104
+ : artifactMetadataSchema.decode(receipt, "cache artifact receipt");
105
+ input.onArtifact(artifactRecordSchema.decode({
106
+ operation,
107
+ artifact: {
108
+ ...artifact,
109
+ id: `tubeless:step-cache:${createHash("sha256").update(scopedKey).digest("hex")}`,
110
+ metadata: {
111
+ ...artifact?.metadata,
112
+ tubelessCache: {
113
+ implementationVersion: cache.version,
114
+ createdAtMs,
115
+ ageMs: context.now() - createdAtMs,
116
+ ...(cache.maxAge === undefined ? {} : { maxAgeMs: cache.maxAge }),
117
+ },
118
+ },
119
+ },
120
+ }, "cache artifact record"));
121
+ };
122
+ if (policy === "use" && cache.maxAge !== 0) {
123
+ const entry = await cacheOperation("read", io, async () => {
124
+ const found = await store.get(scopedKey, io);
125
+ if (found === undefined)
126
+ return undefined;
127
+ if (!found || !(found.value instanceof Uint8Array) || !Number.isFinite(found.createdAtMs))
128
+ throw new Error("Invalid cache entry");
129
+ const age = context.now() - found.createdAtMs;
130
+ return cache.maxAge !== undefined && (age < 0 || age >= cache.maxAge) ? undefined : found;
131
+ });
132
+ if (entry !== undefined) {
133
+ input.onHit();
134
+ const raw = await cacheOperation("decode", io, () => codec.decode(Uint8Array.from(entry.value)));
135
+ const value = await input.validate(raw);
136
+ throwIfAborted(context.signal, "Step cache");
137
+ recordArtifact("reuse", entry.createdAtMs, entry.artifact);
138
+ return value;
139
+ }
140
+ }
141
+ const raw = await input.run();
142
+ const bytes = await cacheOperation("encode", io, async () => {
143
+ const encoded = await codec.encode(raw);
144
+ if (!(encoded instanceof Uint8Array))
145
+ throw new Error("Cache codec must encode bytes");
146
+ return Uint8Array.from(encoded);
147
+ });
148
+ const value = await input.validate(raw);
149
+ await cacheOperation("write", io, async () => {
150
+ const createdAtMs = context.now();
151
+ const receipt = await store.set(scopedKey, { value: bytes, createdAtMs }, io);
152
+ // A completed write remains observable even if cancellation follows persistence.
153
+ recordArtifact("write", createdAtMs, receipt);
154
+ });
155
+ return value;
156
+ }
157
+ //# sourceMappingURL=pipeline-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-cache.js","sourceRoot":"","sources":["../../src/core/pipeline-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n artifactMetadataSchema,\n artifactRecordSchema,\n type ArtifactMetadata,\n type ArtifactRecord,\n} from \"../tracing/artifact-metadata.js\";\nimport { isAbortError, throwIfAborted } from \"../utilities/abort.js\";\nimport { cacheDurationMs } from \"../utilities/cache-duration.js\";\nimport { defaultCacheKey } from \"../utilities/cache-key.js\";\nimport type { PipelineExecutionContext } from \"./pipeline-types.js\";\n\n/** Cache I/O is cancellable and independent of pipeline scheduling. */\nexport interface StepCacheContext {\n readonly signal?: AbortSignal;\n}\n\n/** Encoded raw handler result and its creation time; expiration is enforced by core. */\nexport interface StepCacheEntry {\n readonly value: Uint8Array;\n readonly createdAtMs: number;\n /** Optional receipt identifying the stored bytes, refreshed by the adapter on reads. */\n readonly artifact?: ArtifactMetadata;\n}\n\n/** Store timestamped encoded handler results; only undefined means a cache miss. */\nexport interface StepCacheStore {\n get(\n key: string,\n context: StepCacheContext\n ): StepCacheEntry | undefined | Promise<StepCacheEntry | undefined>;\n /** Return an optional receipt after persistence succeeds; core supplies a stable artifact ID. */\n set(\n key: string,\n entry: StepCacheEntry,\n context: StepCacheContext\n ): void | ArtifactMetadata | Promise<void | ArtifactMetadata>;\n}\n\n/** Encode a detached snapshot; decode must produce a fresh handler-result value. */\nexport interface StepCacheCodec {\n encode(value: unknown): Uint8Array | Promise<Uint8Array>;\n decode(value: Uint8Array): unknown | Promise<unknown>;\n}\n\n/** Use reads and writes, recompute replaces entries, bypass performs no cache work. */\nexport type StepCachePolicy = \"use\" | \"recompute\" | \"bypass\";\n\ninterface CacheSettings {\n /** Maximum acceptable age: milliseconds or a fixed duration such as \"30 days\" or \"12h\". */\n readonly maxAge?: string | number;\n readonly store?: StepCacheStore;\n readonly codec?: StepCacheCodec;\n}\n\n/** Defaults for opted-in steps; configuring defaults does not make other steps cacheable. */\nexport interface PipelineCacheOptions extends CacheSettings {\n /** Root directory relative to the run's cwd; defaults to .cache with pipeline/step subdirectories. */\n readonly directory?: string;\n}\n\ntype CachePolicyResolver<TInputs, TOptions extends object> = {\n resolve(\n inputs: TInputs,\n context: PipelineExecutionContext<TOptions>\n ): StepCachePolicy | Promise<StepCachePolicy>;\n}[\"resolve\"];\n\n/** Opt a deterministic ordinary step into caching, with optional overrides of pipeline defaults. */\nexport interface StepCache<TInputs, TOptions extends object = {}> extends CacheSettings {\n /** Explicit implementation identity; defaults to the pipeline's implementationVersion. */\n readonly version?: string;\n /** Override the hash of dependency inputs and validated options; null bypasses this invocation. */\n key?(\n inputs: TInputs,\n context: PipelineExecutionContext<TOptions>\n ): string | null | Promise<string | null>;\n readonly policy?: StepCachePolicy | CachePolicyResolver<TInputs, TOptions>;\n}\n\nexport interface CompiledStepCache<TOptions extends object> extends StepCache<\n Record<string, unknown>,\n TOptions\n> {\n readonly version: string;\n readonly directory: string;\n readonly maxAge?: number;\n}\n\nfunction validateSettings(settings: CacheSettings): void {\n if (!settings || typeof settings !== \"object\" || Array.isArray(settings))\n throw new Error(\"Cache settings must be an object\");\n cacheDurationMs(settings.maxAge);\n if (\n settings.store !== undefined &&\n (typeof settings.store?.get !== \"function\" || typeof settings.store?.set !== \"function\")\n )\n throw new Error(\"Cache store requires get and set methods\");\n if (\n settings.codec !== undefined &&\n (typeof settings.codec?.encode !== \"function\" || typeof settings.codec?.decode !== \"function\")\n )\n throw new Error(\"Cache codec requires encode and decode methods\");\n}\n\nexport function validatePipelineCache(settings: PipelineCacheOptions | undefined): void {\n if (settings === undefined) return;\n validateSettings(settings);\n if (\n settings.directory !== undefined &&\n (typeof settings.directory !== \"string\" || !settings.directory.trim())\n )\n throw new Error(\"Cache directory must be nonblank\");\n}\n\nexport function compileStepCache<TOptions extends object>(\n source: boolean | StepCache<Record<string, unknown>, TOptions> | undefined,\n defaults: PipelineCacheOptions | undefined,\n implementationVersion: string | undefined\n): CompiledStepCache<TOptions> | undefined {\n if (source === undefined || source === false) return undefined;\n const config = source === true ? {} : source;\n validateSettings(config);\n const version = config.version ?? implementationVersion;\n if (typeof version !== \"string\" || !version.trim() || version.length > 256)\n throw new Error(\n \"Cached steps require a nonblank version or pipeline implementationVersion of at most 256 characters\"\n );\n if (config.key !== undefined && typeof config.key !== \"function\")\n throw new Error(\"Cache key must be a function\");\n if (\n config.policy !== undefined &&\n typeof config.policy !== \"function\" &&\n ![\"use\", \"recompute\", \"bypass\"].includes(config.policy)\n )\n throw new Error(\"Invalid cache policy\");\n const store = config.store ?? defaults?.store;\n const codec = config.codec ?? defaults?.codec;\n return Object.freeze({\n version,\n directory: defaults?.directory ?? \".cache\",\n maxAge: cacheDurationMs(config.maxAge ?? defaults?.maxAge),\n key: config.key?.bind(config),\n policy: typeof config.policy === \"function\" ? config.policy.bind(config) : config.policy,\n store: store\n ? Object.freeze({ get: store.get.bind(store), set: store.set.bind(store) })\n : undefined,\n codec: codec\n ? Object.freeze({ encode: codec.encode.bind(codec), decode: codec.decode.bind(codec) })\n : undefined,\n });\n}\n\nasync function cacheOperation<T>(\n operation: string,\n context: StepCacheContext,\n run: () => T | Promise<T>\n): Promise<T> {\n throwIfAborted(context.signal, \"Step cache\");\n try {\n const result = await run();\n throwIfAborted(context.signal, \"Step cache\");\n return result;\n } catch (cause) {\n throwIfAborted(context.signal, \"Step cache\");\n if (isAbortError(cause)) throw cause;\n throw new Error(`Step cache ${operation} failed`, { cause });\n }\n}\n\n/** One execution path: cache raw handler results, then validate every publication. */\nexport async function executeWithStepCache<TOptions extends object>(input: {\n cache: CompiledStepCache<TOptions> | undefined;\n pipelineId: string;\n stepId: string;\n inputs: Record<string, unknown>;\n context: PipelineExecutionContext<TOptions>;\n run(): Promise<unknown>;\n validate(value: unknown): Promise<unknown>;\n onHit(): void;\n onArtifact(record: ArtifactRecord): void;\n}): Promise<unknown> {\n const { cache, context } = input;\n const uncached = async () => input.validate(await input.run());\n if (!cache || context.dryRun) return uncached();\n const policy = await cacheOperation(\n \"policy\",\n context,\n () =>\n context.cachePolicy ??\n (typeof cache.policy === \"function\"\n ? cache.policy(input.inputs, context)\n : (cache.policy ?? \"use\"))\n );\n if (policy === \"bypass\") return uncached();\n if (policy !== \"use\" && policy !== \"recompute\") throw new Error(\"Invalid step cache policy\");\n const key = await cacheOperation(\"key\", context, () =>\n cache.key ? cache.key(input.inputs, context) : defaultCacheKey(input.inputs, context.options)\n );\n if (key === null) return uncached();\n if (typeof key !== \"string\" || !key.trim())\n throw new Error(\"Step cache key must be a nonblank string or null\");\n const scopedKey = JSON.stringify([\n \"tubeless.step-cache.v1\",\n input.pipelineId,\n input.stepId,\n cache.version,\n key,\n ]);\n // Load the optional built-in persistence only when an eligible step needs it.\n const local =\n !cache.store || !cache.codec ? await import(\"../utilities/cache-storage.js\") : undefined;\n const store =\n cache.store ??\n local!.defaultFileStepCache(context.cwd, cache.directory, input.pipelineId, input.stepId);\n const codec = cache.codec ?? local!.v8StepCacheCodec;\n const io = { signal: context.signal };\n const recordArtifact = (\n operation: \"write\" | \"reuse\",\n createdAtMs: number,\n receipt?: ArtifactMetadata | void\n ) => {\n const artifact =\n receipt === undefined\n ? undefined\n : artifactMetadataSchema.decode(receipt, \"cache artifact receipt\");\n input.onArtifact(\n artifactRecordSchema.decode(\n {\n operation,\n artifact: {\n ...artifact,\n id: `tubeless:step-cache:${createHash(\"sha256\").update(scopedKey).digest(\"hex\")}`,\n metadata: {\n ...artifact?.metadata,\n tubelessCache: {\n implementationVersion: cache.version,\n createdAtMs,\n ageMs: context.now() - createdAtMs,\n ...(cache.maxAge === undefined ? {} : { maxAgeMs: cache.maxAge }),\n },\n },\n },\n },\n \"cache artifact record\"\n )\n );\n };\n if (policy === \"use\" && cache.maxAge !== 0) {\n const entry = await cacheOperation(\"read\", io, async () => {\n const found = await store.get(scopedKey, io);\n if (found === undefined) return undefined;\n if (!found || !(found.value instanceof Uint8Array) || !Number.isFinite(found.createdAtMs))\n throw new Error(\"Invalid cache entry\");\n const age = context.now() - found.createdAtMs;\n return cache.maxAge !== undefined && (age < 0 || age >= cache.maxAge) ? undefined : found;\n });\n if (entry !== undefined) {\n input.onHit();\n const raw = await cacheOperation(\"decode\", io, () =>\n codec.decode(Uint8Array.from(entry.value))\n );\n const value = await input.validate(raw);\n throwIfAborted(context.signal, \"Step cache\");\n recordArtifact(\"reuse\", entry.createdAtMs, entry.artifact);\n return value;\n }\n }\n const raw = await input.run();\n const bytes = await cacheOperation(\"encode\", io, async () => {\n const encoded = await codec.encode(raw);\n if (!(encoded instanceof Uint8Array)) throw new Error(\"Cache codec must encode bytes\");\n return Uint8Array.from(encoded);\n });\n const value = await input.validate(raw);\n await cacheOperation(\"write\", io, async () => {\n const createdAtMs = context.now();\n const receipt = await store.set(scopedKey, { value: bytes, createdAtMs }, io);\n // A completed write remains observable even if cancellation follows persistence.\n recordArtifact(\"write\", createdAtMs, receipt);\n });\n return value;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { snapshotPipelineMetadata } from "../tracing/graph-metadata.js";
1
2
  import { compileDefinitionSnapshot } from "./pipeline-definition-identity.js";
2
3
  import { validatePipelineDefinition } from "./pipeline-definition-validation.js";
3
4
  import { PipelineDefinitionError } from "./pipeline-errors.js";
@@ -5,13 +6,13 @@ import { requiredFinalizerMetadata } from "./pipeline-finalizer.js";
5
6
  import { compilePipelineGraph } from "./pipeline-graph.js";
6
7
  import { STEP_OPTIONS_SCHEMA } from "./pipeline-step-metadata.js";
7
8
  export function compilePipeline(definition) {
8
- const errors = validatePipelineDefinition(definition);
9
+ const { errors, compiledCaches } = validatePipelineDefinition(definition);
9
10
  if (errors.length > 0) {
10
11
  throw new PipelineDefinitionError(definition.id, errors);
11
12
  }
12
13
  // SAFETY: `steps` is `TSteps extends readonly AnyStep[]`; the cast restores
13
14
  // the `TOptions` generic that the tuple erased, without changing the values.
14
- const { compiledByAuthorStep, orderedSteps, stepGraph } = compilePipelineGraph(definition.steps);
15
+ const { compiledByAuthorStep, orderedSteps, stepGraph } = compilePipelineGraph(definition.steps, compiledCaches);
15
16
  const finalizerMetadata = requiredFinalizerMetadata(definition.finalize);
16
17
  const requiredFinalizerSteps = finalizerMetadata?.steps;
17
18
  const compiledTargets = definition.targets
@@ -30,6 +31,7 @@ export function compilePipeline(definition) {
30
31
  const snapshot = compileDefinitionSnapshot({
31
32
  orderedSteps,
32
33
  stepGraph,
34
+ metadata: snapshotPipelineMetadata(definition.metadata),
33
35
  targetIds: compiledTargets.map((step) => step.id),
34
36
  requiredFinalizerSteps: compiledRequiredFinalizerSteps,
35
37
  resultValidated: definition.resultSchema !== undefined,
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline-compiler.js","sourceRoot":"","sources":["../../src/core/pipeline-compiler.ts"],"names":[],"mappings":"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","sourcesContent":["import { compileDefinitionSnapshot } from \"./pipeline-definition-identity.js\";\nimport type { PipelineDefinition, StepsOptions } from \"./pipeline-definition.js\";\nimport { validatePipelineDefinition } from \"./pipeline-definition-validation.js\";\nimport { PipelineDefinitionError } from \"./pipeline-errors.js\";\nimport { requiredFinalizerMetadata } from \"./pipeline-finalizer.js\";\nimport { compilePipelineGraph, type CompiledStepGraph } from \"./pipeline-graph.js\";\nimport type { AnyStep } from \"./pipeline-steps.js\";\nimport { STEP_OPTIONS_SCHEMA } from \"./pipeline-step-metadata.js\";\nimport type { PipelineDefinitionSnapshot, StandardSchemaV1 } from \"./pipeline-types.js\";\n\nexport interface CompiledPipeline<\n TSteps extends readonly AnyStep[] = readonly AnyStep[],\n TResult = unknown,\n TTargets extends readonly TSteps[number][] = readonly [],\n TResultSchema extends StandardSchemaV1 | undefined = undefined,\n> {\n readonly definition: PipelineDefinitionSnapshot;\n readonly declaredTargets: readonly AnyStep<StepsOptions<TSteps>>[];\n readonly finalize: Extract<\n NonNullable<PipelineDefinition<TSteps, TResult, TTargets, TResultSchema>[\"finalize\"]>,\n (...args: never[]) => unknown\n >;\n readonly id: string;\n readonly optionsSchema: StandardSchemaV1 | undefined;\n readonly orderedSteps: readonly AnyStep<StepsOptions<TSteps>>[];\n readonly compiledByAuthorStep: ReadonlyMap<AnyStep, AnyStep>;\n readonly requiredFinalizerSteps: readonly AnyStep<StepsOptions<TSteps>>[] | undefined;\n readonly stepGraph: ReadonlyMap<AnyStep, CompiledStepGraph>;\n readonly resultSchema: TResultSchema | undefined;\n readonly stepIds: readonly string[];\n readonly targetIds: readonly string[];\n}\n\nexport function compilePipeline<\n TSteps extends readonly AnyStep[],\n TResult,\n TTargets extends readonly TSteps[number][],\n TResultSchema extends StandardSchemaV1 | undefined,\n>(\n definition: PipelineDefinition<TSteps, TResult, TTargets, TResultSchema>\n): CompiledPipeline<TSteps, TResult, TTargets, TResultSchema> {\n const errors = validatePipelineDefinition(definition);\n if (errors.length > 0) {\n throw new PipelineDefinitionError(definition.id, errors);\n }\n type TOptions = StepsOptions<TSteps>;\n // SAFETY: `steps` is `TSteps extends readonly AnyStep[]`; the cast restores\n // the `TOptions` generic that the tuple erased, without changing the values.\n const { compiledByAuthorStep, orderedSteps, stepGraph } = compilePipelineGraph(\n definition.steps as readonly AnyStep<TOptions>[]\n );\n const finalizerMetadata = requiredFinalizerMetadata<TOptions>(definition.finalize);\n const requiredFinalizerSteps = finalizerMetadata?.steps;\n const compiledTargets = definition.targets\n ? definition.targets.map((target) => compiledByAuthorStep.get(target)!)\n : orderedSteps.slice(-1);\n const compiledRequiredFinalizerSteps = requiredFinalizerSteps?.map((step) =>\n compiledByAuthorStep.get(step)!\n );\n const lastStepId = orderedSteps.at(-1)?.id;\n const authoredFinalize = definition.finalize;\n type Finalizer = CompiledPipeline<TSteps, TResult, TTargets, TResultSchema>[\"finalize\"];\n const compiledFinalize = finalizerMetadata\n ? finalizerMetadata.compile(compiledRequiredFinalizerSteps!.map((step) => step.id))\n : typeof authoredFinalize === \"function\"\n ? (outputs: Parameters<Finalizer>[0], context: Parameters<Finalizer>[1]) =>\n authoredFinalize.call(definition, outputs, context)\n : (outputs: Record<string, unknown>) =>\n lastStepId !== undefined && Object.hasOwn(outputs, lastStepId)\n ? outputs[lastStepId]\n : undefined;\n const snapshot = compileDefinitionSnapshot({\n orderedSteps,\n stepGraph,\n targetIds: compiledTargets.map((step) => step.id),\n requiredFinalizerSteps: compiledRequiredFinalizerSteps,\n resultValidated: definition.resultSchema !== undefined,\n implementationVersion: definition.implementationVersion,\n });\n return Object.freeze({\n definition: snapshot,\n declaredTargets: Object.freeze(compiledTargets),\n // Invoke ordinary finalizers on the author's definition so method-style\n // implementations keep `this`. Required finalizers compile the same input\n // and context contract with stable required ids.\n // SAFETY: supplied finalizers preserve their signature; definePipeline checks\n // that an omitted finalizer can use the last step's output as its result.\n finalize: compiledFinalize as Finalizer,\n id: definition.id,\n optionsSchema:\n definition.steps.length === 0\n ? undefined\n : compiledByAuthorStep.get(definition.steps[0]!)?.[STEP_OPTIONS_SCHEMA],\n orderedSteps: Object.freeze([...orderedSteps]),\n compiledByAuthorStep,\n stepGraph,\n requiredFinalizerSteps: compiledRequiredFinalizerSteps\n ? Object.freeze(compiledRequiredFinalizerSteps)\n : undefined,\n resultSchema: definition.resultSchema,\n stepIds: Object.freeze(definition.steps.map((step) => compiledByAuthorStep.get(step)!.id)),\n targetIds: Object.freeze(compiledTargets.map((step) => step.id)),\n });\n}\n"]}
1
+ {"version":3,"file":"pipeline-compiler.js","sourceRoot":"","sources":["../../src/core/pipeline-compiler.ts"],"names":[],"mappings":"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","sourcesContent":["import { snapshotPipelineMetadata } from \"../tracing/graph-metadata.js\";\nimport { compileDefinitionSnapshot } from \"./pipeline-definition-identity.js\";\nimport type { PipelineDefinition, StepsOptions } from \"./pipeline-definition.js\";\nimport { validatePipelineDefinition } from \"./pipeline-definition-validation.js\";\nimport { PipelineDefinitionError } from \"./pipeline-errors.js\";\nimport { requiredFinalizerMetadata } from \"./pipeline-finalizer.js\";\nimport { compilePipelineGraph, type CompiledStepGraph } from \"./pipeline-graph.js\";\nimport type { AnyStep } from \"./pipeline-steps.js\";\nimport { STEP_OPTIONS_SCHEMA } from \"./pipeline-step-metadata.js\";\nimport type { PipelineDefinitionSnapshot, StandardSchemaV1 } from \"./pipeline-types.js\";\n\nexport interface CompiledPipeline<\n TSteps extends readonly AnyStep[] = readonly AnyStep[],\n TResult = unknown,\n TTargets extends readonly TSteps[number][] = readonly [],\n TResultSchema extends StandardSchemaV1 | undefined = undefined,\n> {\n readonly definition: PipelineDefinitionSnapshot;\n readonly declaredTargets: readonly AnyStep<StepsOptions<TSteps>>[];\n readonly finalize: Extract<\n NonNullable<PipelineDefinition<TSteps, TResult, TTargets, TResultSchema>[\"finalize\"]>,\n (...args: never[]) => unknown\n >;\n readonly id: string;\n readonly optionsSchema: StandardSchemaV1 | undefined;\n readonly orderedSteps: readonly AnyStep<StepsOptions<TSteps>>[];\n readonly compiledByAuthorStep: ReadonlyMap<AnyStep, AnyStep>;\n readonly requiredFinalizerSteps: readonly AnyStep<StepsOptions<TSteps>>[] | undefined;\n readonly stepGraph: ReadonlyMap<AnyStep, CompiledStepGraph>;\n readonly resultSchema: TResultSchema | undefined;\n readonly stepIds: readonly string[];\n readonly targetIds: readonly string[];\n}\n\nexport function compilePipeline<\n TSteps extends readonly AnyStep[],\n TResult,\n TTargets extends readonly TSteps[number][],\n TResultSchema extends StandardSchemaV1 | undefined,\n>(\n definition: PipelineDefinition<TSteps, TResult, TTargets, TResultSchema>\n): CompiledPipeline<TSteps, TResult, TTargets, TResultSchema> {\n const { errors, compiledCaches } = validatePipelineDefinition(definition);\n if (errors.length > 0) {\n throw new PipelineDefinitionError(definition.id, errors);\n }\n type TOptions = StepsOptions<TSteps>;\n // SAFETY: `steps` is `TSteps extends readonly AnyStep[]`; the cast restores\n // the `TOptions` generic that the tuple erased, without changing the values.\n const { compiledByAuthorStep, orderedSteps, stepGraph } = compilePipelineGraph(\n definition.steps as readonly AnyStep<TOptions>[],\n compiledCaches\n );\n const finalizerMetadata = requiredFinalizerMetadata<TOptions>(definition.finalize);\n const requiredFinalizerSteps = finalizerMetadata?.steps;\n const compiledTargets = definition.targets\n ? definition.targets.map((target) => compiledByAuthorStep.get(target)!)\n : orderedSteps.slice(-1);\n const compiledRequiredFinalizerSteps = requiredFinalizerSteps?.map((step) =>\n compiledByAuthorStep.get(step)!\n );\n const lastStepId = orderedSteps.at(-1)?.id;\n const authoredFinalize = definition.finalize;\n type Finalizer = CompiledPipeline<TSteps, TResult, TTargets, TResultSchema>[\"finalize\"];\n const compiledFinalize = finalizerMetadata\n ? finalizerMetadata.compile(compiledRequiredFinalizerSteps!.map((step) => step.id))\n : typeof authoredFinalize === \"function\"\n ? (outputs: Parameters<Finalizer>[0], context: Parameters<Finalizer>[1]) =>\n authoredFinalize.call(definition, outputs, context)\n : (outputs: Record<string, unknown>) =>\n lastStepId !== undefined && Object.hasOwn(outputs, lastStepId)\n ? outputs[lastStepId]\n : undefined;\n const snapshot = compileDefinitionSnapshot({\n orderedSteps,\n stepGraph,\n metadata: snapshotPipelineMetadata(definition.metadata),\n targetIds: compiledTargets.map((step) => step.id),\n requiredFinalizerSteps: compiledRequiredFinalizerSteps,\n resultValidated: definition.resultSchema !== undefined,\n implementationVersion: definition.implementationVersion,\n });\n return Object.freeze({\n definition: snapshot,\n declaredTargets: Object.freeze(compiledTargets),\n // Invoke ordinary finalizers on the author's definition so method-style\n // implementations keep `this`. Required finalizers compile the same input\n // and context contract with stable required ids.\n // SAFETY: supplied finalizers preserve their signature; definePipeline checks\n // that an omitted finalizer can use the last step's output as its result.\n finalize: compiledFinalize as Finalizer,\n id: definition.id,\n optionsSchema:\n definition.steps.length === 0\n ? undefined\n : compiledByAuthorStep.get(definition.steps[0]!)?.[STEP_OPTIONS_SCHEMA],\n orderedSteps: Object.freeze([...orderedSteps]),\n compiledByAuthorStep,\n stepGraph,\n requiredFinalizerSteps: compiledRequiredFinalizerSteps\n ? Object.freeze(compiledRequiredFinalizerSteps)\n : undefined,\n resultSchema: definition.resultSchema,\n stepIds: Object.freeze(definition.steps.map((step) => compiledByAuthorStep.get(step)!.id)),\n targetIds: Object.freeze(compiledTargets.map((step) => step.id)),\n });\n}\n"]}
@@ -1,8 +1,10 @@
1
+ import type { PipelineMetadata } from "../tracing/graph-metadata.js";
1
2
  import type { CompiledStepGraph } from "./pipeline-graph.js";
2
3
  import type { AnyStep } from "./pipeline-steps.js";
3
4
  import type { PipelineDefinitionIdentity, PipelineDefinitionSnapshot } from "./pipeline-types.js";
4
5
  /** Canonical snapshot. Execution order matters; dependency and target sets do not. */
5
6
  export declare function compileDefinitionSnapshot(input: {
7
+ metadata?: PipelineMetadata;
6
8
  orderedSteps: readonly AnyStep[];
7
9
  stepGraph: ReadonlyMap<AnyStep, CompiledStepGraph>;
8
10
  targetIds: readonly string[];
@@ -10,5 +12,5 @@ export declare function compileDefinitionSnapshot(input: {
10
12
  resultValidated: boolean;
11
13
  implementationVersion: string | undefined;
12
14
  }): PipelineDefinitionSnapshot;
13
- /** Preserve v1 hashes; extended child semantics use v2, including their ancestors. */
15
+ /** Preserve v1/v2 hashes; metadata and its ancestors use v3. */
14
16
  export declare function createDefinitionIdentity(input: Omit<PipelineDefinitionSnapshot, "identity">, implementationVersion: string | undefined): PipelineDefinitionIdentity;
@@ -1,5 +1,6 @@
1
+ import { canonicalJsonValue } from "../utilities/canonical-json.js";
1
2
  import { createHash } from "node:crypto";
2
- import { STEP_NESTED_PIPELINE, STEP_OPTIONS_SCHEMA, STEP_REMOTE, } from "./pipeline-step-metadata.js";
3
+ import { STEP_CACHE, STEP_NESTED_PIPELINE, STEP_OPTIONS_SCHEMA, STEP_REMOTE, } from "./pipeline-step-metadata.js";
3
4
  function fingerprint(value) {
4
5
  return `sha256:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
5
6
  }
@@ -16,6 +17,7 @@ function iterationFields(nested) {
16
17
  return {};
17
18
  const controls = nested.controls;
18
19
  const normalizedControls = {
20
+ ...(controls?.cache !== undefined ? { cache: controls.cache } : {}),
19
21
  ...(controls?.dryRun !== undefined ? { dryRun: controls.dryRun } : {}),
20
22
  ...(controls?.continueOnError !== undefined
21
23
  ? { continueOnError: controls.continueOnError }
@@ -35,8 +37,10 @@ export function compileDefinitionSnapshot(input) {
35
37
  const graph = input.stepGraph.get(step);
36
38
  const nested = step[STEP_NESTED_PIPELINE];
37
39
  const remote = step[STEP_REMOTE];
40
+ const cache = step[STEP_CACHE];
38
41
  return {
39
42
  id: step.id,
43
+ ...(step.metadata === undefined ? {} : { metadata: step.metadata }),
40
44
  dependencies: graph.dependsOn.map(({ id }) => id).sort(),
41
45
  optionalDependencies: graph.optionalDependsOn.map(({ id }) => id).sort(),
42
46
  skipAfterFailureOf: graph.skipAfterFailureOf.map(({ id }) => id).sort(),
@@ -47,6 +51,17 @@ export function compileDefinitionSnapshot(input) {
47
51
  : "run",
48
52
  runtimeSkipPossible: step.skip !== undefined,
49
53
  outputValidated: step.outputSchema !== undefined,
54
+ ...(cache
55
+ ? {
56
+ cache: {
57
+ version: cache.version,
58
+ ...(cache.maxAge !== undefined ? { maxAgeMs: cache.maxAge } : {}),
59
+ policy: typeof cache.policy === "function"
60
+ ? "dynamic"
61
+ : (cache.policy ?? "use"),
62
+ },
63
+ }
64
+ : {}),
50
65
  ...(nested
51
66
  ? {
52
67
  nestedPipeline: {
@@ -70,6 +85,7 @@ export function compileDefinitionSnapshot(input) {
70
85
  };
71
86
  });
72
87
  const semantics = {
88
+ ...(input.metadata === undefined ? {} : { metadata: input.metadata }),
73
89
  steps,
74
90
  targetIds: [...input.targetIds].sort(),
75
91
  ...(input.requiredFinalizerSteps
@@ -83,22 +99,36 @@ export function compileDefinitionSnapshot(input) {
83
99
  ...semantics,
84
100
  });
85
101
  }
86
- /** Preserve v1 hashes; extended child semantics use v2, including their ancestors. */
102
+ /** Preserve v1/v2 hashes; metadata and its ancestors use v3. */
87
103
  export function createDefinitionIdentity(input, implementationVersion) {
88
- const version = input.steps.some((step) => step.nestedPipeline?.mode === "iterate" || step.nestedPipeline?.identity?.version === 2)
89
- ? 2
90
- : 1;
104
+ const hasMetadata = input.metadata !== undefined ||
105
+ input.steps.some((step) => step.metadata !== undefined || step.nestedPipeline?.identity?.version === 3);
106
+ const version = hasMetadata
107
+ ? 3
108
+ : input.steps.some((step) => step.nestedPipeline?.mode === "iterate" || step.nestedPipeline?.identity?.version === 2)
109
+ ? 2
110
+ : 1;
91
111
  // Child handler versions participate in the combined identity, never the graph fingerprint.
92
112
  const structuralFingerprint = fingerprint({
93
113
  version,
114
+ ...(input.metadata === undefined ? {} : { metadata: canonicalJsonValue(input.metadata) }),
94
115
  steps: input.steps.map((step) => ({
95
116
  id: step.id,
117
+ ...(step.metadata === undefined ? {} : { metadata: canonicalJsonValue(step.metadata) }),
96
118
  dependencies: [...step.dependencies].sort(),
97
119
  optionalDependencies: [...step.optionalDependencies].sort(),
98
120
  skipAfterFailureOf: [...step.skipAfterFailureOf].sort(),
99
121
  dryRun: step.dryRun,
100
122
  runtimeSkipPossible: step.runtimeSkipPossible,
101
123
  outputValidated: step.outputValidated,
124
+ ...(step.cache
125
+ ? {
126
+ cache: {
127
+ policy: step.cache.policy,
128
+ ...(step.cache.maxAgeMs !== undefined ? { maxAgeMs: step.cache.maxAgeMs } : {}),
129
+ },
130
+ }
131
+ : {}),
102
132
  ...(step.nestedPipeline
103
133
  ? {
104
134
  nestedPipeline: {
@@ -144,6 +174,9 @@ export function createDefinitionIdentity(input, implementationVersion) {
144
174
  version,
145
175
  structuralFingerprint,
146
176
  implementationVersion,
177
+ ...(input.steps.some((step) => step.cache)
178
+ ? { cacheVersions: input.steps.map((step) => step.cache?.version ?? null) }
179
+ : {}),
147
180
  children: input.steps.map((step) => {
148
181
  const child = step.nestedPipeline?.identity;
149
182
  if (!child)
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline-definition-identity.js","sourceRoot":"","sources":["../../src/core/pipeline-definition-identity.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { CompiledStepGraph } from \"./pipeline-graph.js\";\nimport type { AnyStep } from \"./pipeline-steps.js\";\nimport {\n STEP_NESTED_PIPELINE,\n STEP_OPTIONS_SCHEMA,\n STEP_REMOTE,\n} from \"./pipeline-step-metadata.js\";\nimport type { PipelineDefinitionIdentity, PipelineDefinitionSnapshot } from \"./pipeline-types.js\";\n\nfunction fingerprint(value: unknown): string {\n return `sha256:${createHash(\"sha256\").update(JSON.stringify(value)).digest(\"hex\")}`;\n}\n\nfunction freezeSnapshot<T>(value: T): T {\n if (value !== null && typeof value === \"object\") {\n for (const child of Object.values(value)) freezeSnapshot(child);\n Object.freeze(value);\n }\n return value;\n}\n\nfunction iterationFields(\n nested: NonNullable<PipelineDefinitionSnapshot[\"steps\"][number][\"nestedPipeline\"]>\n) {\n if (nested.mode !== \"iterate\") return {};\n const controls = nested.controls;\n const normalizedControls = {\n ...(controls?.dryRun !== undefined ? { dryRun: controls.dryRun } : {}),\n ...(controls?.continueOnError !== undefined\n ? { continueOnError: controls.continueOnError }\n : {}),\n ...(controls?.maxConcurrency !== undefined ? { maxConcurrency: controls.maxConcurrency } : {}),\n ...(controls?.targets !== undefined ? { targets: [...controls.targets].sort() } : {}),\n ...(controls?.stepIds !== undefined ? { stepIds: [...controls.stepIds].sort() } : {}),\n };\n return {\n maxIterations: nested.maxIterations,\n ...(Object.keys(normalizedControls).length > 0 ? { controls: normalizedControls } : {}),\n };\n}\n\n/** Canonical snapshot. Execution order matters; dependency and target sets do not. */\nexport function compileDefinitionSnapshot(input: {\n orderedSteps: readonly AnyStep[];\n stepGraph: ReadonlyMap<AnyStep, CompiledStepGraph>;\n targetIds: readonly string[];\n requiredFinalizerSteps: readonly AnyStep[] | undefined;\n resultValidated: boolean;\n implementationVersion: string | undefined;\n}): PipelineDefinitionSnapshot {\n const steps = input.orderedSteps.map((step) => {\n const graph = input.stepGraph.get(step)!;\n const nested = step[STEP_NESTED_PIPELINE];\n const remote = step[STEP_REMOTE];\n return {\n id: step.id,\n dependencies: graph.dependsOn.map(({ id }) => id).sort(),\n optionalDependencies: graph.optionalDependsOn.map(({ id }) => id).sort(),\n skipAfterFailureOf: graph.skipAfterFailureOf.map(({ id }) => id).sort(),\n dryRun:\n step.dryRun === \"skip\"\n ? (\"skip\" as const)\n : step.dryRun\n ? (\"custom\" as const)\n : (\"run\" as const),\n runtimeSkipPossible: step.skip !== undefined,\n outputValidated: step.outputSchema !== undefined,\n ...(nested\n ? {\n nestedPipeline: {\n pipelineId: nested.pipelineId,\n mode: nested.mode,\n ...iterationFields(nested),\n stepIds: [...nested.stepIds],\n ...(nested.identity ? { identity: { ...nested.identity } } : {}),\n ...(nested.concurrency !== undefined ? { concurrency: nested.concurrency } : {}),\n },\n }\n : {}),\n ...(remote\n ? {\n remote: {\n engine: remote.engine,\n ...(remote.target !== undefined ? { target: remote.target } : {}),\n },\n }\n : {}),\n };\n });\n const semantics = {\n steps,\n targetIds: [...input.targetIds].sort(),\n ...(input.requiredFinalizerSteps\n ? { requiredFinalizerStepIds: input.requiredFinalizerSteps.map(({ id }) => id).sort() }\n : {}),\n optionsValidated: input.orderedSteps[0]?.[STEP_OPTIONS_SCHEMA] !== undefined,\n resultValidated: input.resultValidated,\n };\n return freezeSnapshot({\n identity: createDefinitionIdentity(semantics, input.implementationVersion),\n ...semantics,\n });\n}\n\n/** Preserve v1 hashes; extended child semantics use v2, including their ancestors. */\nexport function createDefinitionIdentity(\n input: Omit<PipelineDefinitionSnapshot, \"identity\">,\n implementationVersion: string | undefined\n): PipelineDefinitionIdentity {\n const version = input.steps.some(\n (step) =>\n step.nestedPipeline?.mode === \"iterate\" || step.nestedPipeline?.identity?.version === 2\n )\n ? 2\n : 1;\n // Child handler versions participate in the combined identity, never the graph fingerprint.\n const structuralFingerprint = fingerprint({\n version,\n steps: input.steps.map((step) => ({\n id: step.id,\n dependencies: [...step.dependencies].sort(),\n optionalDependencies: [...step.optionalDependencies].sort(),\n skipAfterFailureOf: [...step.skipAfterFailureOf].sort(),\n dryRun: step.dryRun,\n runtimeSkipPossible: step.runtimeSkipPossible,\n outputValidated: step.outputValidated,\n ...(step.nestedPipeline\n ? {\n nestedPipeline: {\n pipelineId: step.nestedPipeline.pipelineId,\n mode: step.nestedPipeline.mode,\n ...iterationFields(step.nestedPipeline),\n stepIds: [...step.nestedPipeline.stepIds],\n ...(step.nestedPipeline.identity\n ? {\n identity: {\n version: step.nestedPipeline.identity.version,\n structuralFingerprint: step.nestedPipeline.identity.structuralFingerprint,\n },\n }\n : {}),\n ...(step.nestedPipeline.concurrency !== undefined\n ? { concurrency: step.nestedPipeline.concurrency }\n : {}),\n },\n }\n : {}),\n ...(step.remote\n ? {\n remote: {\n engine: step.remote.engine,\n ...(step.remote.target !== undefined ? { target: step.remote.target } : {}),\n },\n }\n : {}),\n })),\n targetIds: [...input.targetIds].sort(),\n ...(input.requiredFinalizerStepIds\n ? { requiredFinalizerStepIds: [...input.requiredFinalizerStepIds].sort() }\n : {}),\n optionsValidated: input.optionsValidated,\n resultValidated: input.resultValidated,\n });\n return {\n version,\n structuralFingerprint,\n ...(implementationVersion !== undefined ? { implementationVersion } : {}),\n definitionId: fingerprint({\n version,\n structuralFingerprint,\n implementationVersion,\n children: input.steps.map((step) => {\n const child = step.nestedPipeline?.identity;\n if (!child) return null;\n // Bind every recorded child identity field, not just its opaque definition ID.\n return {\n version: child.version,\n structuralFingerprint: child.structuralFingerprint,\n ...(child.implementationVersion !== undefined\n ? { implementationVersion: child.implementationVersion }\n : {}),\n definitionId: child.definitionId,\n };\n }),\n }),\n };\n}\n"]}
1
+ {"version":3,"file":"pipeline-definition-identity.js","sourceRoot":"","sources":["../../src/core/pipeline-definition-identity.ts"],"names":[],"mappings":"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","sourcesContent":["import { canonicalJsonValue } from \"../utilities/canonical-json.js\";\nimport type { PipelineMetadata } from \"../tracing/graph-metadata.js\";\nimport { createHash } from \"node:crypto\";\nimport type { CompiledStepGraph } from \"./pipeline-graph.js\";\nimport type { AnyStep } from \"./pipeline-steps.js\";\nimport {\n STEP_CACHE,\n STEP_NESTED_PIPELINE,\n STEP_OPTIONS_SCHEMA,\n STEP_REMOTE,\n} from \"./pipeline-step-metadata.js\";\nimport type { PipelineDefinitionIdentity, PipelineDefinitionSnapshot } from \"./pipeline-types.js\";\n\nfunction fingerprint(value: unknown): string {\n return `sha256:${createHash(\"sha256\").update(JSON.stringify(value)).digest(\"hex\")}`;\n}\n\nfunction freezeSnapshot<T>(value: T): T {\n if (value !== null && typeof value === \"object\") {\n for (const child of Object.values(value)) freezeSnapshot(child);\n Object.freeze(value);\n }\n return value;\n}\n\nfunction iterationFields(\n nested: NonNullable<PipelineDefinitionSnapshot[\"steps\"][number][\"nestedPipeline\"]>\n) {\n if (nested.mode !== \"iterate\") return {};\n const controls = nested.controls;\n const normalizedControls = {\n ...(controls?.cache !== undefined ? { cache: controls.cache } : {}),\n ...(controls?.dryRun !== undefined ? { dryRun: controls.dryRun } : {}),\n ...(controls?.continueOnError !== undefined\n ? { continueOnError: controls.continueOnError }\n : {}),\n ...(controls?.maxConcurrency !== undefined ? { maxConcurrency: controls.maxConcurrency } : {}),\n ...(controls?.targets !== undefined ? { targets: [...controls.targets].sort() } : {}),\n ...(controls?.stepIds !== undefined ? { stepIds: [...controls.stepIds].sort() } : {}),\n };\n return {\n maxIterations: nested.maxIterations,\n ...(Object.keys(normalizedControls).length > 0 ? { controls: normalizedControls } : {}),\n };\n}\n\n/** Canonical snapshot. Execution order matters; dependency and target sets do not. */\nexport function compileDefinitionSnapshot(input: {\n metadata?: PipelineMetadata;\n orderedSteps: readonly AnyStep[];\n stepGraph: ReadonlyMap<AnyStep, CompiledStepGraph>;\n targetIds: readonly string[];\n requiredFinalizerSteps: readonly AnyStep[] | undefined;\n resultValidated: boolean;\n implementationVersion: string | undefined;\n}): PipelineDefinitionSnapshot {\n const steps = input.orderedSteps.map((step) => {\n const graph = input.stepGraph.get(step)!;\n const nested = step[STEP_NESTED_PIPELINE];\n const remote = step[STEP_REMOTE];\n const cache = step[STEP_CACHE];\n return {\n id: step.id,\n ...(step.metadata === undefined ? {} : { metadata: step.metadata }),\n dependencies: graph.dependsOn.map(({ id }) => id).sort(),\n optionalDependencies: graph.optionalDependsOn.map(({ id }) => id).sort(),\n skipAfterFailureOf: graph.skipAfterFailureOf.map(({ id }) => id).sort(),\n dryRun:\n step.dryRun === \"skip\"\n ? (\"skip\" as const)\n : step.dryRun\n ? (\"custom\" as const)\n : (\"run\" as const),\n runtimeSkipPossible: step.skip !== undefined,\n outputValidated: step.outputSchema !== undefined,\n ...(cache\n ? {\n cache: {\n version: cache.version,\n ...(cache.maxAge !== undefined ? { maxAgeMs: cache.maxAge } : {}),\n policy:\n typeof cache.policy === \"function\"\n ? (\"dynamic\" as const)\n : (cache.policy ?? (\"use\" as const)),\n },\n }\n : {}),\n ...(nested\n ? {\n nestedPipeline: {\n pipelineId: nested.pipelineId,\n mode: nested.mode,\n ...iterationFields(nested),\n stepIds: [...nested.stepIds],\n ...(nested.identity ? { identity: { ...nested.identity } } : {}),\n ...(nested.concurrency !== undefined ? { concurrency: nested.concurrency } : {}),\n },\n }\n : {}),\n ...(remote\n ? {\n remote: {\n engine: remote.engine,\n ...(remote.target !== undefined ? { target: remote.target } : {}),\n },\n }\n : {}),\n };\n });\n const semantics = {\n ...(input.metadata === undefined ? {} : { metadata: input.metadata }),\n steps,\n targetIds: [...input.targetIds].sort(),\n ...(input.requiredFinalizerSteps\n ? { requiredFinalizerStepIds: input.requiredFinalizerSteps.map(({ id }) => id).sort() }\n : {}),\n optionsValidated: input.orderedSteps[0]?.[STEP_OPTIONS_SCHEMA] !== undefined,\n resultValidated: input.resultValidated,\n };\n return freezeSnapshot({\n identity: createDefinitionIdentity(semantics, input.implementationVersion),\n ...semantics,\n });\n}\n\n/** Preserve v1/v2 hashes; metadata and its ancestors use v3. */\nexport function createDefinitionIdentity(\n input: Omit<PipelineDefinitionSnapshot, \"identity\">,\n implementationVersion: string | undefined\n): PipelineDefinitionIdentity {\n const hasMetadata =\n input.metadata !== undefined ||\n input.steps.some(\n (step) => step.metadata !== undefined || step.nestedPipeline?.identity?.version === 3\n );\n const version = hasMetadata\n ? 3\n : input.steps.some(\n (step) =>\n step.nestedPipeline?.mode === \"iterate\" || step.nestedPipeline?.identity?.version === 2\n )\n ? 2\n : 1;\n // Child handler versions participate in the combined identity, never the graph fingerprint.\n const structuralFingerprint = fingerprint({\n version,\n ...(input.metadata === undefined ? {} : { metadata: canonicalJsonValue(input.metadata) }),\n steps: input.steps.map((step) => ({\n id: step.id,\n ...(step.metadata === undefined ? {} : { metadata: canonicalJsonValue(step.metadata) }),\n dependencies: [...step.dependencies].sort(),\n optionalDependencies: [...step.optionalDependencies].sort(),\n skipAfterFailureOf: [...step.skipAfterFailureOf].sort(),\n dryRun: step.dryRun,\n runtimeSkipPossible: step.runtimeSkipPossible,\n outputValidated: step.outputValidated,\n ...(step.cache\n ? {\n cache: {\n policy: step.cache.policy,\n ...(step.cache.maxAgeMs !== undefined ? { maxAgeMs: step.cache.maxAgeMs } : {}),\n },\n }\n : {}),\n ...(step.nestedPipeline\n ? {\n nestedPipeline: {\n pipelineId: step.nestedPipeline.pipelineId,\n mode: step.nestedPipeline.mode,\n ...iterationFields(step.nestedPipeline),\n stepIds: [...step.nestedPipeline.stepIds],\n ...(step.nestedPipeline.identity\n ? {\n identity: {\n version: step.nestedPipeline.identity.version,\n structuralFingerprint: step.nestedPipeline.identity.structuralFingerprint,\n },\n }\n : {}),\n ...(step.nestedPipeline.concurrency !== undefined\n ? { concurrency: step.nestedPipeline.concurrency }\n : {}),\n },\n }\n : {}),\n ...(step.remote\n ? {\n remote: {\n engine: step.remote.engine,\n ...(step.remote.target !== undefined ? { target: step.remote.target } : {}),\n },\n }\n : {}),\n })),\n targetIds: [...input.targetIds].sort(),\n ...(input.requiredFinalizerStepIds\n ? { requiredFinalizerStepIds: [...input.requiredFinalizerStepIds].sort() }\n : {}),\n optionsValidated: input.optionsValidated,\n resultValidated: input.resultValidated,\n });\n return {\n version,\n structuralFingerprint,\n ...(implementationVersion !== undefined ? { implementationVersion } : {}),\n definitionId: fingerprint({\n version,\n structuralFingerprint,\n implementationVersion,\n ...(input.steps.some((step) => step.cache)\n ? { cacheVersions: input.steps.map((step) => step.cache?.version ?? null) }\n : {}),\n children: input.steps.map((step) => {\n const child = step.nestedPipeline?.identity;\n if (!child) return null;\n // Bind every recorded child identity field, not just its opaque definition ID.\n return {\n version: child.version,\n structuralFingerprint: child.structuralFingerprint,\n ...(child.implementationVersion !== undefined\n ? { implementationVersion: child.implementationVersion }\n : {}),\n definitionId: child.definitionId,\n };\n }),\n }),\n };\n}\n"]}
@@ -1,4 +1,9 @@
1
- import type { PipelineDefinition } from "./pipeline-definition.js";
1
+ import { type CompiledStepCache } from "./pipeline-cache.js";
2
+ import type { PipelineDefinition, StepsOptions } from "./pipeline-definition.js";
2
3
  import type { AnyStep } from "./pipeline-steps.js";
3
4
  import type { PipelineError, StandardSchemaV1 } from "./pipeline-types.js";
4
- export declare function validatePipelineDefinition<TSteps extends readonly AnyStep[], TResult, TTargets extends readonly TSteps[number][], TResultSchema extends StandardSchemaV1 | undefined>(definition: PipelineDefinition<TSteps, TResult, TTargets, TResultSchema>): PipelineError[];
5
+ /** Validate author declarations and retain cache plans so graph construction never recompiles them. */
6
+ export declare function validatePipelineDefinition<TSteps extends readonly AnyStep[], TResult, TTargets extends readonly TSteps[number][], TResultSchema extends StandardSchemaV1 | undefined>(definition: PipelineDefinition<TSteps, TResult, TTargets, TResultSchema>): {
7
+ errors: PipelineError[];
8
+ compiledCaches: Map<AnyStep<object>, CompiledStepCache<StepsOptions<TSteps>>>;
9
+ };
@@ -1,11 +1,14 @@
1
+ import { compileStepCache, validatePipelineCache, } from "./pipeline-cache.js";
1
2
  import { duplicateValues } from "../utilities/collections.js";
2
3
  import { requiredFinalizerMetadata } from "./pipeline-finalizer.js";
3
4
  import { stepEdges, targetClosure, topologicalSort } from "./pipeline-graph.js";
4
5
  import { pipelineDiagnostic } from "./pipeline-errors.js";
5
- import { PIPELINE_FINALIZE_STEP_ID, STEP_OPTIONS_SCHEMA } from "./pipeline-step-metadata.js";
6
+ import { PIPELINE_FINALIZE_STEP_ID, STEP_OPTIONS_SCHEMA, STEP_NESTED_PIPELINE, STEP_REMOTE, } from "./pipeline-step-metadata.js";
7
+ /** Validate author declarations and retain cache plans so graph construction never recompiles them. */
6
8
  export function validatePipelineDefinition(definition) {
7
9
  const steps = definition.steps;
8
10
  const errors = [];
11
+ const compiledCaches = new Map();
9
12
  if (definition.id.trim().length === 0) {
10
13
  errors.push(pipelineDiagnostic("TUBELESS_DEFINITION_PIPELINE_ID_BLANK", "definition", "definition", "Pipeline id must not be blank"));
11
14
  }
@@ -23,6 +26,12 @@ export function validatePipelineDefinition(definition) {
23
26
  definition.implementationVersion.length > 256)) {
24
27
  errors.push(pipelineDiagnostic("TUBELESS_DEFINITION_IMPLEMENTATION_VERSION_INVALID", "definition", "definition", "Implementation version must be a nonblank string of at most 256 characters"));
25
28
  }
29
+ try {
30
+ validatePipelineCache(definition.cache);
31
+ }
32
+ catch (error) {
33
+ errors.push(pipelineDiagnostic("TUBELESS_DEFINITION_CACHE_INVALID", "definition", "definition", error instanceof Error ? error.message : String(error)));
34
+ }
26
35
  const stepIds = steps.map((step) => step.id);
27
36
  const duplicateStepIds = duplicateValues(stepIds);
28
37
  if (duplicateStepIds.length > 0) {
@@ -33,6 +42,17 @@ export function validatePipelineDefinition(definition) {
33
42
  errors.push(pipelineDiagnostic("TUBELESS_DEFINITION_OPTIONS_SCHEMA_CONFLICT", "definition", "definition", `Pipeline ${definition.id} mixes steps from different options-schema scopes`));
34
43
  }
35
44
  for (const step of steps) {
45
+ try {
46
+ const configuredCache = step.cache;
47
+ if (configuredCache && (step[STEP_NESTED_PIPELINE] || step[STEP_REMOTE]))
48
+ throw new Error("Only ordinary steps can opt into caching");
49
+ const cache = compileStepCache(configuredCache, definition.cache, definition.implementationVersion);
50
+ if (cache)
51
+ compiledCaches.set(step, cache);
52
+ }
53
+ catch (error) {
54
+ errors.push(pipelineDiagnostic("TUBELESS_DEFINITION_STEP_CACHE_INVALID", "definition", "definition", `Pipeline ${definition.id} step ${step.id}: ${error instanceof Error ? error.message : String(error)}`, { stepId: step.id }));
55
+ }
36
56
  if (step.id.trim().length === 0) {
37
57
  errors.push(pipelineDiagnostic("TUBELESS_DEFINITION_STEP_ID_BLANK", "definition", "definition", `Pipeline ${definition.id} has a step with a blank id`, { stepId: step.id }));
38
58
  }
@@ -112,6 +132,6 @@ export function validatePipelineDefinition(definition) {
112
132
  if (errors.length === 0 && !orderedSteps) {
113
133
  errors.push(pipelineDiagnostic("TUBELESS_DEFINITION_DEPENDENCY_CYCLE", "definition", "definition", `Pipeline ${definition.id} has a dependency cycle`));
114
134
  }
115
- return errors;
135
+ return { errors, compiledCaches };
116
136
  }
117
137
  //# sourceMappingURL=pipeline-definition-validation.js.map