tubeless 0.2.12 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/child-execution.d.ts +0 -23
- package/dist/core/child-execution.js +10 -12
- package/dist/core/child-execution.js.map +1 -1
- package/dist/core/child-progress.js +7 -2
- package/dist/core/child-progress.js.map +1 -1
- package/dist/core/iteration.d.ts +27 -0
- package/dist/core/iteration.js +111 -0
- package/dist/core/iteration.js.map +1 -0
- package/dist/core/lifecycle.d.ts +7 -5
- package/dist/core/lifecycle.js +36 -4
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/pipeline-artifacts.d.ts +11 -0
- package/dist/core/pipeline-artifacts.js +2 -0
- package/dist/core/pipeline-artifacts.js.map +1 -0
- package/dist/core/pipeline-compiler.d.ts +1 -0
- package/dist/core/pipeline-compiler.js +1 -0
- package/dist/core/pipeline-compiler.js.map +1 -1
- package/dist/core/pipeline-definition-identity.d.ts +2 -2
- package/dist/core/pipeline-definition-identity.js +28 -5
- package/dist/core/pipeline-definition-identity.js.map +1 -1
- package/dist/core/pipeline-execute.d.ts +1 -6
- package/dist/core/pipeline-execute.js +28 -215
- package/dist/core/pipeline-execute.js.map +1 -1
- package/dist/core/pipeline-execution-error.d.ts +35 -0
- package/dist/core/pipeline-execution-error.js +195 -0
- package/dist/core/pipeline-execution-error.js.map +1 -0
- package/dist/core/pipeline-identity.d.ts +1 -0
- package/dist/core/pipeline-identity.js +1 -0
- package/dist/core/pipeline-identity.js.map +1 -1
- package/dist/core/pipeline-mermaid.js +4 -1
- package/dist/core/pipeline-mermaid.js.map +1 -1
- package/dist/core/pipeline-run-state.d.ts +4 -4
- package/dist/core/pipeline-run-state.js +10 -16
- package/dist/core/pipeline-run-state.js.map +1 -1
- package/dist/core/pipeline-step-executor.d.ts +2 -0
- package/dist/core/pipeline-step-executor.js +11 -3
- package/dist/core/pipeline-step-executor.js.map +1 -1
- package/dist/core/pipeline-steps.d.ts +20 -0
- package/dist/core/pipeline-steps.js +62 -0
- package/dist/core/pipeline-steps.js.map +1 -1
- package/dist/core/pipeline-types.d.ts +15 -2
- package/dist/core/pipeline-types.js.map +1 -1
- package/dist/core/pipeline.d.ts +4 -1
- package/dist/core/pipeline.js +18 -3
- package/dist/core/pipeline.js.map +1 -1
- package/dist/project/project-compiler.d.ts +3 -31
- package/dist/project/project-compiler.js +11 -180
- package/dist/project/project-compiler.js.map +1 -1
- package/dist/project/project-registry.d.ts +44 -0
- package/dist/project/project-registry.js +107 -0
- package/dist/project/project-registry.js.map +1 -0
- package/dist/project/project-step-graph.d.ts +9 -0
- package/dist/project/project-step-graph.js +99 -0
- package/dist/project/project-step-graph.js.map +1 -0
- package/dist/render/render.js +1 -1
- package/dist/render/render.js.map +1 -1
- package/dist/reporter/interactive-reporter.js +6 -3
- package/dist/reporter/interactive-reporter.js.map +1 -1
- package/dist/reporter/live-ticker-frame.d.ts +21 -0
- package/dist/reporter/live-ticker-frame.js +196 -0
- package/dist/reporter/live-ticker-frame.js.map +1 -0
- package/dist/reporter/live-ticker-protocol.d.ts +41 -0
- package/dist/reporter/live-ticker-protocol.js +59 -0
- package/dist/reporter/live-ticker-protocol.js.map +1 -0
- package/dist/reporter/live-ticker-worker.js +11 -25
- package/dist/reporter/live-ticker-worker.js.map +1 -1
- package/dist/reporter/live-ticker.d.ts +0 -15
- package/dist/reporter/live-ticker.js +11 -216
- package/dist/reporter/live-ticker.js.map +1 -1
- package/dist/reporter/reporter.js +7 -6
- package/dist/reporter/reporter.js.map +1 -1
- package/dist/run-store/definition-projection.d.ts +8 -0
- package/dist/run-store/definition-projection.js +89 -0
- package/dist/run-store/definition-projection.js.map +1 -0
- package/dist/run-store/run-projection.d.ts +15 -0
- package/dist/run-store/run-projection.js +203 -0
- package/dist/run-store/run-projection.js.map +1 -0
- package/dist/run-store/run-store-codec.d.ts +2 -0
- package/dist/run-store/run-store-codec.js +11 -0
- package/dist/run-store/run-store-codec.js.map +1 -1
- package/dist/run-store/run-store-event-decoder.js +3 -0
- package/dist/run-store/run-store-event-decoder.js.map +1 -1
- package/dist/run-store/run-store-reader.d.ts +7 -0
- package/dist/run-store/run-store-reader.js +31 -0
- package/dist/run-store/run-store-reader.js.map +1 -0
- package/dist/run-store/run-store-sqlite.js +23 -7
- package/dist/run-store/run-store-sqlite.js.map +1 -1
- package/dist/run-store/run-store.d.ts +16 -4
- package/dist/run-store/run-store.js +14 -263
- package/dist/run-store/run-store.js.map +1 -1
- package/dist/studio/run-store-ui-api.d.ts +13 -0
- package/dist/studio/run-store-ui-api.js +163 -0
- package/dist/studio/run-store-ui-api.js.map +1 -0
- package/dist/studio/run-store-ui-client-browser.js +2 -2
- package/dist/studio/run-store-ui-client-transport.js +28 -3
- package/dist/studio/run-store-ui-client-transport.js.map +1 -1
- package/dist/studio/run-store-ui-client.d.ts +2 -1
- package/dist/studio/run-store-ui-client.js +15 -9
- package/dist/studio/run-store-ui-client.js.map +1 -1
- package/dist/studio/run-store-ui-http.d.ts +11 -0
- package/dist/studio/run-store-ui-http.js +52 -0
- package/dist/studio/run-store-ui-http.js.map +1 -1
- package/dist/studio/run-store-ui-state.d.ts +1 -1
- package/dist/studio/run-store-ui-state.js +10 -28
- package/dist/studio/run-store-ui-state.js.map +1 -1
- package/dist/studio/run-store-ui.d.ts +5 -11
- package/dist/studio/run-store-ui.js +5 -203
- package/dist/studio/run-store-ui.js.map +1 -1
- package/dist/testing/testing.d.ts +18 -2
- package/dist/testing/testing.js +35 -2
- package/dist/testing/testing.js.map +1 -1
- package/dist/tracing/artifact-metadata.d.ts +28 -0
- package/dist/tracing/artifact-metadata.js +105 -0
- package/dist/tracing/artifact-metadata.js.map +1 -0
- package/dist/tracing/tracing-codec.d.ts +1 -1
- package/dist/tracing/tracing-codec.js +1 -1
- package/dist/tracing/tracing-codec.js.map +1 -1
- package/dist/tracing/tracing-constants.d.ts +1 -1
- package/dist/tracing/tracing-constants.js +1 -1
- package/dist/tracing/tracing-constants.js.map +1 -1
- package/dist/tracing/tracing-contracts.d.ts +3 -1
- package/dist/tracing/tracing-contracts.js.map +1 -1
- package/dist/tracing/tracing-internal.d.ts +2 -0
- package/dist/tracing/tracing-internal.js +36 -13
- package/dist/tracing/tracing-internal.js.map +1 -1
- package/dist/tracing/tracing-schema.d.ts +330 -49
- package/dist/tracing/tracing-schema.js +102 -10
- package/dist/tracing/tracing-schema.js.map +1 -1
- package/dist/workbench/workbench-command-execution.d.ts +6 -0
- package/dist/workbench/workbench-command-execution.js +48 -0
- package/dist/workbench/workbench-command-execution.js.map +1 -0
- package/dist/workbench/workbench-history.js +11 -18
- package/dist/workbench/workbench-history.js.map +1 -1
- package/dist/workbench/workbench-launch-session.d.ts +2 -2
- package/dist/workbench/workbench-launch-session.js.map +1 -1
- package/dist/workbench/workbench-run.d.ts +0 -4
- package/dist/workbench/workbench-run.js +3 -47
- package/dist/workbench/workbench-run.js.map +1 -1
- package/dist/workbench/workbench-studio-launcher.d.ts +27 -0
- package/dist/workbench/workbench-studio-launcher.js +95 -0
- package/dist/workbench/workbench-studio-launcher.js.map +1 -0
- package/dist/workbench/workbench-ui.js +10 -83
- package/dist/workbench/workbench-ui.js.map +1 -1
- package/docs/README.md +13 -7
- package/docs/agent-guide.md +69 -6
- package/docs/airflow.md +187 -0
- package/docs/api-reference.md +87 -76
- package/docs/api-report.json +17 -6
- package/docs/artifacts.md +173 -0
- package/docs/child-pipeline-composition.md +65 -2
- package/docs/comparison.md +20 -0
- package/docs/concepts.md +70 -2
- package/docs/dagster.md +161 -0
- package/docs/inngest.md +163 -0
- package/docs/llms.txt +30 -1
- package/docs/recipes.md +82 -33
- package/docs/remote-step-composition.md +6 -0
- package/docs/step-functions.md +190 -0
- package/docs/studio.md +31 -12
- package/docs/temporal.md +183 -0
- package/examples/airflow/contract.ts +46 -0
- package/examples/airflow/hosted.ts +70 -0
- package/examples/airflow/remote.ts +175 -0
- package/examples/airflow/tubeless_example.py +49 -0
- package/examples/airflow/worker.ts +24 -0
- package/examples/artifact-lineage.ts +70 -0
- package/examples/dagster/definitions.py +59 -0
- package/examples/dagster/hosted.ts +78 -0
- package/examples/dagster/pipeline.ts +62 -0
- package/examples/dagster/test_definitions.py +47 -0
- package/examples/dagster/tsconfig.build.json +10 -0
- package/examples/dagster/worker.ts +22 -0
- package/examples/inngest/client.ts +4 -0
- package/examples/inngest/functions.ts +65 -0
- package/examples/inngest/pipeline.ts +41 -0
- package/examples/inngest/send.ts +11 -0
- package/examples/inngest/server.ts +17 -0
- package/examples/inngest/tsconfig.build.json +10 -0
- package/examples/iteration.ts +52 -0
- package/examples/parameterized-steps.ts +96 -0
- package/examples/project/tubeless.project.ts +46 -0
- package/examples/step-functions/handler.ts +110 -0
- package/examples/step-functions/input.json +1 -0
- package/examples/step-functions/pipeline.ts +41 -0
- package/examples/step-functions/state-machine.asl.json +41 -0
- package/examples/step-functions/template.json +42 -0
- package/examples/step-output-overrides.ts +41 -0
- package/examples/temporal/activities.ts +94 -0
- package/examples/temporal/client.ts +19 -0
- package/examples/temporal/pipeline.ts +41 -0
- package/examples/temporal/tsconfig.build.json +10 -0
- package/examples/temporal/worker.ts +21 -0
- package/examples/temporal/workflows.ts +16 -0
- package/package.json +13 -4
- package/skills/tubeless/SKILL.md +30 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition-projection.js","sourceRoot":"","sources":["../../src/run-store/definition-projection.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PipelineDefinitionSnapshot } from \"../core/pipeline.js\";\nimport type {\n StoredPipelineDefinition,\n StoredPipelineDefinitionStep,\n StoredPipelineEvent,\n StoredPipelineRun,\n} from \"./run-store.js\";\n\nfunction definitionStep(\n event: Extract<StoredPipelineEvent, { name: \"step.planned\" }>\n): StoredPipelineDefinitionStep {\n const step: StoredPipelineDefinitionStep = {\n dependencies: [...event.payload.dependencies],\n dryRun: event.payload.dryRun,\n id: event.stepId,\n optionalDependencies: [...event.payload.optionalDependencies],\n runtimeSkipPossible: event.payload.runtimeSkipPossible,\n skipAfterFailureOf: [...event.payload.skipAfterFailureOf],\n };\n const description = event.payload.description;\n if (description) step.description = description;\n const name = event.payload.name;\n if (name) step.name = name;\n if (event.payload.nestedPipeline) {\n step.nestedPipeline = {\n ...event.payload.nestedPipeline,\n stepIds: [...event.payload.nestedPipeline.stepIds],\n };\n }\n if (event.payload.remote) step.remote = { ...event.payload.remote };\n return step;\n}\n\n/** Own observed definition replacement independently of run execution state. */\nexport class DefinitionProjection {\n #snapshot?: PipelineDefinitionSnapshot;\n #definitionRun: { id: string; startedAtMs: number; startedEventId: number } | undefined;\n #firstSeenAtMs: number;\n #lastSeenAtMs: number;\n readonly #latestSteps = new Map<string, StoredPipelineDefinitionStep>();\n readonly #runStarts = new Map<string, { atMs: number; eventId: number; targetIds: string[] }>();\n #targetIds: string[] = [];\n\n constructor(event: StoredPipelineEvent) {\n this.#firstSeenAtMs = event.timestampMs;\n this.#lastSeenAtMs = event.timestampMs;\n this.append(event);\n }\n\n append(event: StoredPipelineEvent): void {\n this.#firstSeenAtMs = Math.min(this.#firstSeenAtMs, event.timestampMs);\n this.#lastSeenAtMs = Math.max(this.#lastSeenAtMs, event.timestampMs);\n if (event.name === \"pipeline.started\") {\n if (event.payload.definitionSnapshot)\n this.#snapshot = structuredClone(event.payload.definitionSnapshot);\n this.#runStarts.set(event.runId, {\n atMs: event.timestampMs,\n eventId: event.id,\n targetIds: [...event.payload.targetIds],\n });\n }\n if (event.name !== \"step.planned\" || !event.stepId) return;\n const start = this.#runStarts.get(event.runId);\n if (!start) return;\n const current = this.#definitionRun;\n if (\n !current ||\n start.atMs > current.startedAtMs ||\n (start.atMs === current.startedAtMs && start.eventId > current.startedEventId)\n ) {\n this.#definitionRun = {\n id: event.runId,\n startedAtMs: start.atMs,\n startedEventId: start.eventId,\n };\n this.#latestSteps.clear();\n this.#targetIds = start.targetIds;\n }\n if (event.runId === this.#definitionRun?.id) {\n this.#latestSteps.set(event.stepId, definitionStep(event));\n }\n }\n\n snapshot(pipelineRuns: readonly StoredPipelineRun[]): StoredPipelineDefinition {\n const identity = pipelineRuns[0]!.definitionIdentity;\n const snapshot = this.#snapshot;\n return {\n activeRuns: pipelineRuns.filter(({ status }) => status === \"running\").length,\n firstSeenAtMs: this.#firstSeenAtMs,\n lastSeenAtMs: this.#lastSeenAtMs,\n pipelineId: pipelineRuns[0]!.pipelineId,\n ...(identity ? { identity: { ...identity } } : {}),\n ...(snapshot ? { snapshot: structuredClone(snapshot) } : {}),\n runCount: pipelineRuns.length,\n steps: [...this.#latestSteps.values()].map((step) => structuredClone(step)),\n targetIds: [...(snapshot?.targetIds ?? this.#targetIds)],\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StoredPipelineEvent, StoredPipelineRun } from "./run-store.js";
|
|
2
|
+
/** Own the mutable state of one recorded run. */
|
|
3
|
+
export declare class RunProjection {
|
|
4
|
+
#private;
|
|
5
|
+
private readonly options;
|
|
6
|
+
constructor(event: StoredPipelineEvent, options?: {
|
|
7
|
+
readonly retainLogs?: boolean;
|
|
8
|
+
readonly retainArtifacts?: boolean;
|
|
9
|
+
});
|
|
10
|
+
get definitionId(): string | undefined;
|
|
11
|
+
append(event: StoredPipelineEvent): void;
|
|
12
|
+
snapshot(): StoredPipelineRun;
|
|
13
|
+
}
|
|
14
|
+
/** Fold one run's append-only events into a UI-friendly current snapshot. */
|
|
15
|
+
export declare function projectPipelineRun(events: readonly StoredPipelineEvent[]): StoredPipelineRun;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { RUN_MODEL_VERSION } from "../core/pipeline-ids.js";
|
|
2
|
+
import { hasVisibleStepProgress } from "../core/progress.js";
|
|
3
|
+
function terminalStepStatus(event) {
|
|
4
|
+
switch (event.name) {
|
|
5
|
+
case "step.cancelled":
|
|
6
|
+
return "cancelled";
|
|
7
|
+
case "step.complete":
|
|
8
|
+
// Shipped trace name; projected snapshot uses the live success token.
|
|
9
|
+
return "completed";
|
|
10
|
+
case "step.failed":
|
|
11
|
+
return "failed";
|
|
12
|
+
case "step.skipped":
|
|
13
|
+
return "skipped";
|
|
14
|
+
default:
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function attemptStatus(status) {
|
|
19
|
+
return status === "planned" ? "running" : status;
|
|
20
|
+
}
|
|
21
|
+
/** Own the mutable state of one recorded run. */
|
|
22
|
+
export class RunProjection {
|
|
23
|
+
options;
|
|
24
|
+
#completed;
|
|
25
|
+
#eventCount = 0;
|
|
26
|
+
#identity;
|
|
27
|
+
#logCount = 0;
|
|
28
|
+
#logs = [];
|
|
29
|
+
#startedAtMs;
|
|
30
|
+
#startObserved = false;
|
|
31
|
+
#dryRun = false;
|
|
32
|
+
#definitionIdentity;
|
|
33
|
+
#steps = new Map();
|
|
34
|
+
constructor(event, options = {}) {
|
|
35
|
+
this.options = options;
|
|
36
|
+
this.#identity = {
|
|
37
|
+
pipelineId: event.pipelineId,
|
|
38
|
+
runId: event.runId,
|
|
39
|
+
correlationId: event.correlationId,
|
|
40
|
+
parentRunId: event.parentRunId,
|
|
41
|
+
iteration: event.iteration ? { ...event.iteration } : undefined,
|
|
42
|
+
};
|
|
43
|
+
this.#startedAtMs = event.timestampMs;
|
|
44
|
+
this.append(event);
|
|
45
|
+
}
|
|
46
|
+
get definitionId() {
|
|
47
|
+
return this.#definitionIdentity?.definitionId;
|
|
48
|
+
}
|
|
49
|
+
append(event) {
|
|
50
|
+
this.#eventCount += 1;
|
|
51
|
+
if (event.name === "pipeline.started" && !this.#startObserved) {
|
|
52
|
+
this.#startObserved = true;
|
|
53
|
+
this.#startedAtMs = event.timestampMs;
|
|
54
|
+
this.#dryRun = event.payload.dryRun;
|
|
55
|
+
this.#definitionIdentity = event.payload.definitionIdentity
|
|
56
|
+
? { ...event.payload.definitionIdentity }
|
|
57
|
+
: undefined;
|
|
58
|
+
}
|
|
59
|
+
if (event.name === "pipeline.completed") {
|
|
60
|
+
this.#completed = {
|
|
61
|
+
status: event.payload.status,
|
|
62
|
+
timestampMs: event.timestampMs,
|
|
63
|
+
durationMs: event.durationMs,
|
|
64
|
+
error: event.error ? structuredClone(event.error) : undefined,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (event.name === "pipeline.log") {
|
|
68
|
+
this.#logCount += 1;
|
|
69
|
+
if (this.options.retainLogs === false)
|
|
70
|
+
return;
|
|
71
|
+
const log = {
|
|
72
|
+
id: event.id,
|
|
73
|
+
level: event.payload.level,
|
|
74
|
+
message: event.payload.message,
|
|
75
|
+
timestampMs: event.timestampMs,
|
|
76
|
+
};
|
|
77
|
+
if (event.attemptId)
|
|
78
|
+
log.attemptId = event.attemptId;
|
|
79
|
+
if (event.stepId)
|
|
80
|
+
log.stepId = event.stepId;
|
|
81
|
+
this.#logs.push(log);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!event.stepId || !event.name.startsWith("step."))
|
|
85
|
+
return;
|
|
86
|
+
let step = this.#steps.get(event.stepId);
|
|
87
|
+
if (!step) {
|
|
88
|
+
step = { id: event.stepId, status: "planned" };
|
|
89
|
+
this.#steps.set(event.stepId, step);
|
|
90
|
+
}
|
|
91
|
+
if (event.name === "step.planned") {
|
|
92
|
+
step.name = event.payload.name;
|
|
93
|
+
step.description = event.payload.description;
|
|
94
|
+
if (event.payload.nestedPipeline) {
|
|
95
|
+
step.nestedPipeline = {
|
|
96
|
+
...event.payload.nestedPipeline,
|
|
97
|
+
stepIds: [...event.payload.nestedPipeline.stepIds],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (event.payload.remote)
|
|
101
|
+
step.remote = { ...event.payload.remote };
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (event.name === "step.artifact") {
|
|
105
|
+
if (this.options.retainArtifacts === false)
|
|
106
|
+
return;
|
|
107
|
+
(step.artifacts ??= []).push({
|
|
108
|
+
...structuredClone(event.payload),
|
|
109
|
+
attemptId: event.attemptId,
|
|
110
|
+
timestampMs: event.timestampMs,
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if ("outputSource" in event.payload && event.payload.outputSource === "override") {
|
|
115
|
+
step.outputSource = "override";
|
|
116
|
+
}
|
|
117
|
+
if (event.attemptId) {
|
|
118
|
+
let attempt = step.attempt;
|
|
119
|
+
if (!attempt) {
|
|
120
|
+
attempt = {
|
|
121
|
+
attemptId: event.attemptId,
|
|
122
|
+
retries: [],
|
|
123
|
+
startedAtMs: event.timestampMs,
|
|
124
|
+
status: "running",
|
|
125
|
+
};
|
|
126
|
+
step.attempt = attempt;
|
|
127
|
+
}
|
|
128
|
+
if (step.outputSource)
|
|
129
|
+
attempt.outputSource = step.outputSource;
|
|
130
|
+
if (event.name === "step.attempted") {
|
|
131
|
+
attempt.retries.push(event.payload.attempt);
|
|
132
|
+
}
|
|
133
|
+
const terminal = terminalStepStatus(event);
|
|
134
|
+
if (terminal) {
|
|
135
|
+
attempt.status = attemptStatus(terminal);
|
|
136
|
+
attempt.finishedAtMs = event.timestampMs;
|
|
137
|
+
attempt.durationMs = event.durationMs;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (event.name === "step.running") {
|
|
141
|
+
step.status = "running";
|
|
142
|
+
step.startedAtMs ??= event.timestampMs;
|
|
143
|
+
if (event.payload.progress) {
|
|
144
|
+
const progress = {
|
|
145
|
+
...event.payload.progress,
|
|
146
|
+
details: event.payload.progress.details?.map((detail) => ({ ...detail })),
|
|
147
|
+
};
|
|
148
|
+
if (!step.progress || hasVisibleStepProgress(progress))
|
|
149
|
+
step.progress = progress;
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const terminal = terminalStepStatus(event);
|
|
154
|
+
if (terminal) {
|
|
155
|
+
step.status = terminal;
|
|
156
|
+
step.finishedAtMs = event.timestampMs;
|
|
157
|
+
step.durationMs = event.durationMs;
|
|
158
|
+
if (event.durationMs !== undefined)
|
|
159
|
+
step.startedAtMs = event.timestampMs - event.durationMs;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
snapshot() {
|
|
163
|
+
const completed = this.#completed;
|
|
164
|
+
const run = {
|
|
165
|
+
dryRun: this.#dryRun,
|
|
166
|
+
eventCount: this.#eventCount,
|
|
167
|
+
logCount: this.#logCount,
|
|
168
|
+
logs: this.options.retainLogs === false ? [] : this.#logs.map((log) => ({ ...log })),
|
|
169
|
+
pipelineId: this.#identity.pipelineId,
|
|
170
|
+
runId: this.#identity.runId,
|
|
171
|
+
startedAtMs: this.#startedAtMs,
|
|
172
|
+
status: completed?.status ?? "running",
|
|
173
|
+
steps: [...this.#steps.values()].map((step) => structuredClone(step)),
|
|
174
|
+
version: RUN_MODEL_VERSION,
|
|
175
|
+
};
|
|
176
|
+
if (this.#definitionIdentity)
|
|
177
|
+
run.definitionIdentity = { ...this.#definitionIdentity };
|
|
178
|
+
if (this.#identity.correlationId !== undefined)
|
|
179
|
+
run.correlationId = this.#identity.correlationId;
|
|
180
|
+
if (completed?.durationMs !== undefined)
|
|
181
|
+
run.durationMs = completed.durationMs;
|
|
182
|
+
if (completed?.error)
|
|
183
|
+
run.error = structuredClone(completed.error);
|
|
184
|
+
if (completed)
|
|
185
|
+
run.finishedAtMs = completed.timestampMs;
|
|
186
|
+
if (this.#identity.parentRunId)
|
|
187
|
+
run.parentRunId = this.#identity.parentRunId;
|
|
188
|
+
if (this.#identity.iteration)
|
|
189
|
+
run.iteration = { ...this.#identity.iteration };
|
|
190
|
+
return run;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/** Fold one run's append-only events into a UI-friendly current snapshot. */
|
|
194
|
+
export function projectPipelineRun(events) {
|
|
195
|
+
if (events.length === 0)
|
|
196
|
+
throw new Error("Cannot project an empty pipeline run event list.");
|
|
197
|
+
const ordered = [...events].sort((left, right) => left.id - right.id);
|
|
198
|
+
const projection = new RunProjection(ordered[0]);
|
|
199
|
+
for (const event of ordered.slice(1))
|
|
200
|
+
projection.append(event);
|
|
201
|
+
return projection.snapshot();
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=run-projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-projection.js","sourceRoot":"","sources":["../../src/run-store/run-projection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAU7D,SAAS,kBAAkB,CAAC,KAA0B;IACpD,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,gBAAgB;YACnB,OAAO,WAAW,CAAC;QACrB,KAAK,eAAe;YAClB,sEAAsE;YACtE,OAAO,WAAW,CAAC;QACrB,KAAK,aAAa;YAChB,OAAO,QAAQ,CAAC;QAClB,KAAK,cAAc;YACjB,OAAO,SAAS,CAAC;QACnB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,MAAoC;IACzD,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;AACnD,CAAC;AAED,iDAAiD;AACjD,MAAM,OAAO,aAAa;IAwBL,OAAO;IAvB1B,UAAU,CAOI;IACd,WAAW,GAAG,CAAC,CAAC;IACP,SAAS,CAGhB;IACF,SAAS,GAAG,CAAC,CAAC;IACL,KAAK,GAAwB,EAAE,CAAC;IACzC,YAAY,CAAS;IACrB,cAAc,GAAG,KAAK,CAAC;IACvB,OAAO,GAAG,KAAK,CAAC;IAChB,mBAAmB,CAAyC;IACnD,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;IAExD,YACE,KAA0B,EACT,OAAO,GAGpB,EAAE;uBAHW,OAAO;QAKxB,IAAI,CAAC,SAAS,GAAG;YACf,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS;SAChE,CAAC;QACF,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,mBAAmB,EAAE,YAAY,CAAC;IAChD,CAAC;IAED,MAAM,CAAC,KAA0B;QAC/B,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC;QACtB,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC9D,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;YACtC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YACpC,IAAI,CAAC,mBAAmB,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB;gBACzD,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE;gBACzC,CAAC,CAAC,SAAS,CAAC;QAChB,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;YACxC,IAAI,CAAC,UAAU,GAAG;gBAChB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;gBAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;aAC9D,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;YACpB,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,KAAK;gBAAE,OAAO;YAC9C,MAAM,GAAG,GAAsB;gBAC7B,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK;gBAC1B,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO;gBAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC;YACF,IAAI,KAAK,CAAC,SAAS;gBAAE,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;YACrD,IAAI,KAAK,CAAC,MAAM;gBAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC5C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO;QAE7D,IAAI,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAC/B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;YAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;gBACjC,IAAI,CAAC,cAAc,GAAG;oBACpB,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc;oBAC/B,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC;iBACnD,CAAC;YACJ,CAAC;YACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM;gBAAE,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpE,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACnC,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe,KAAK,KAAK;gBAAE,OAAO;YACnD,CAAC,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC;gBAC3B,GAAG,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC;gBACjC,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,IAAI,cAAc,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;YACjF,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC;QACjC,CAAC;QACD,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG;oBACR,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,OAAO,EAAE,EAAE;oBACX,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,MAAM,EAAE,SAAS;iBAClB,CAAC;gBACF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YACzB,CAAC;YACD,IAAI,IAAI,CAAC,YAAY;gBAAE,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;YAChE,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACpC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC9C,CAAC;YACD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,CAAC,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;gBACzC,OAAO,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;gBACzC,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;YACxC,CAAC;QACH,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;YACxB,IAAI,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,CAAC;YACvC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBAC3B,MAAM,QAAQ,GAAmC;oBAC/C,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ;oBACzB,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;iBAC1E,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,sBAAsB,CAAC,QAAQ,CAAC;oBAAE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;YACnF,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;YACvB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;YACtC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;YACnC,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;gBAAE,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,UAAU,CAAC;QAC9F,CAAC;IACH,CAAC;IAED,QAAQ;QACN,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,MAAM,GAAG,GAAsB;YAC7B,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;YACpF,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU;YACrC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;YAC3B,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,SAAS;YACtC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACrE,OAAO,EAAE,iBAAiB;SAC3B,CAAC;QACF,IAAI,IAAI,CAAC,mBAAmB;YAAE,GAAG,CAAC,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACvF,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,KAAK,SAAS;YAC5C,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;QACnD,IAAI,SAAS,EAAE,UAAU,KAAK,SAAS;YAAE,GAAG,CAAC,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC;QAC/E,IAAI,SAAS,EAAE,KAAK;YAAE,GAAG,CAAC,KAAK,GAAG,eAAe,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnE,IAAI,SAAS;YAAE,GAAG,CAAC,YAAY,GAAG,SAAS,CAAC,WAAW,CAAC;QACxD,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW;YAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;QAC7E,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS;YAAE,GAAG,CAAC,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC;QAC9E,OAAO,GAAG,CAAC;IACb,CAAC;CACF;AAED,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,MAAsC;IACvE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAC7F,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,CAAC;IAClD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/D,OAAO,UAAU,CAAC,QAAQ,EAAE,CAAC;AAC/B,CAAC","sourcesContent":["import type { PipelineDefinitionIdentity } from \"../core/pipeline-types.js\";\nimport type { PipelineTraceError } from \"../tracing/tracing-contracts.js\";\nimport { RUN_MODEL_VERSION } from \"../core/pipeline-ids.js\";\nimport { hasVisibleStepProgress } from \"../core/progress.js\";\nimport type {\n StoredPipelineEvent,\n StoredPipelineRun,\n StoredPipelineRunStatus,\n StoredPipelineStep,\n StoredPipelineAttempt,\n StoredPipelineLog,\n} from \"./run-store.js\";\n\nfunction terminalStepStatus(event: StoredPipelineEvent): StoredPipelineStep[\"status\"] | undefined {\n switch (event.name) {\n case \"step.cancelled\":\n return \"cancelled\";\n case \"step.complete\":\n // Shipped trace name; projected snapshot uses the live success token.\n return \"completed\";\n case \"step.failed\":\n return \"failed\";\n case \"step.skipped\":\n return \"skipped\";\n default:\n return undefined;\n }\n}\n\nfunction attemptStatus(status: StoredPipelineStep[\"status\"]): StoredPipelineAttempt[\"status\"] {\n return status === \"planned\" ? \"running\" : status;\n}\n\n/** Own the mutable state of one recorded run. */\nexport class RunProjection {\n #completed:\n | {\n status: StoredPipelineRunStatus;\n timestampMs: number;\n durationMs?: number;\n error?: PipelineTraceError;\n }\n | undefined;\n #eventCount = 0;\n readonly #identity: Pick<\n StoredPipelineEvent,\n \"pipelineId\" | \"runId\" | \"correlationId\" | \"parentRunId\" | \"iteration\"\n >;\n #logCount = 0;\n readonly #logs: StoredPipelineLog[] = [];\n #startedAtMs: number;\n #startObserved = false;\n #dryRun = false;\n #definitionIdentity: PipelineDefinitionIdentity | undefined;\n readonly #steps = new Map<string, StoredPipelineStep>();\n\n constructor(\n event: StoredPipelineEvent,\n private readonly options: {\n readonly retainLogs?: boolean;\n readonly retainArtifacts?: boolean;\n } = {}\n ) {\n this.#identity = {\n pipelineId: event.pipelineId,\n runId: event.runId,\n correlationId: event.correlationId,\n parentRunId: event.parentRunId,\n iteration: event.iteration ? { ...event.iteration } : undefined,\n };\n this.#startedAtMs = event.timestampMs;\n this.append(event);\n }\n\n get definitionId(): string | undefined {\n return this.#definitionIdentity?.definitionId;\n }\n\n append(event: StoredPipelineEvent): void {\n this.#eventCount += 1;\n if (event.name === \"pipeline.started\" && !this.#startObserved) {\n this.#startObserved = true;\n this.#startedAtMs = event.timestampMs;\n this.#dryRun = event.payload.dryRun;\n this.#definitionIdentity = event.payload.definitionIdentity\n ? { ...event.payload.definitionIdentity }\n : undefined;\n }\n if (event.name === \"pipeline.completed\") {\n this.#completed = {\n status: event.payload.status,\n timestampMs: event.timestampMs,\n durationMs: event.durationMs,\n error: event.error ? structuredClone(event.error) : undefined,\n };\n }\n if (event.name === \"pipeline.log\") {\n this.#logCount += 1;\n if (this.options.retainLogs === false) return;\n const log: StoredPipelineLog = {\n id: event.id,\n level: event.payload.level,\n message: event.payload.message,\n timestampMs: event.timestampMs,\n };\n if (event.attemptId) log.attemptId = event.attemptId;\n if (event.stepId) log.stepId = event.stepId;\n this.#logs.push(log);\n return;\n }\n if (!event.stepId || !event.name.startsWith(\"step.\")) return;\n\n let step = this.#steps.get(event.stepId);\n if (!step) {\n step = { id: event.stepId, status: \"planned\" };\n this.#steps.set(event.stepId, step);\n }\n if (event.name === \"step.planned\") {\n step.name = event.payload.name;\n step.description = event.payload.description;\n if (event.payload.nestedPipeline) {\n step.nestedPipeline = {\n ...event.payload.nestedPipeline,\n stepIds: [...event.payload.nestedPipeline.stepIds],\n };\n }\n if (event.payload.remote) step.remote = { ...event.payload.remote };\n return;\n }\n if (event.name === \"step.artifact\") {\n if (this.options.retainArtifacts === false) return;\n (step.artifacts ??= []).push({\n ...structuredClone(event.payload),\n attemptId: event.attemptId,\n timestampMs: event.timestampMs,\n });\n return;\n }\n if (\"outputSource\" in event.payload && event.payload.outputSource === \"override\") {\n step.outputSource = \"override\";\n }\n if (event.attemptId) {\n let attempt = step.attempt;\n if (!attempt) {\n attempt = {\n attemptId: event.attemptId,\n retries: [],\n startedAtMs: event.timestampMs,\n status: \"running\",\n };\n step.attempt = attempt;\n }\n if (step.outputSource) attempt.outputSource = step.outputSource;\n if (event.name === \"step.attempted\") {\n attempt.retries.push(event.payload.attempt);\n }\n const terminal = terminalStepStatus(event);\n if (terminal) {\n attempt.status = attemptStatus(terminal);\n attempt.finishedAtMs = event.timestampMs;\n attempt.durationMs = event.durationMs;\n }\n }\n if (event.name === \"step.running\") {\n step.status = \"running\";\n step.startedAtMs ??= event.timestampMs;\n if (event.payload.progress) {\n const progress: StoredPipelineStep[\"progress\"] = {\n ...event.payload.progress,\n details: event.payload.progress.details?.map((detail) => ({ ...detail })),\n };\n if (!step.progress || hasVisibleStepProgress(progress)) step.progress = progress;\n }\n return;\n }\n const terminal = terminalStepStatus(event);\n if (terminal) {\n step.status = terminal;\n step.finishedAtMs = event.timestampMs;\n step.durationMs = event.durationMs;\n if (event.durationMs !== undefined) step.startedAtMs = event.timestampMs - event.durationMs;\n }\n }\n\n snapshot(): StoredPipelineRun {\n const completed = this.#completed;\n const run: StoredPipelineRun = {\n dryRun: this.#dryRun,\n eventCount: this.#eventCount,\n logCount: this.#logCount,\n logs: this.options.retainLogs === false ? [] : this.#logs.map((log) => ({ ...log })),\n pipelineId: this.#identity.pipelineId,\n runId: this.#identity.runId,\n startedAtMs: this.#startedAtMs,\n status: completed?.status ?? \"running\",\n steps: [...this.#steps.values()].map((step) => structuredClone(step)),\n version: RUN_MODEL_VERSION,\n };\n if (this.#definitionIdentity) run.definitionIdentity = { ...this.#definitionIdentity };\n if (this.#identity.correlationId !== undefined)\n run.correlationId = this.#identity.correlationId;\n if (completed?.durationMs !== undefined) run.durationMs = completed.durationMs;\n if (completed?.error) run.error = structuredClone(completed.error);\n if (completed) run.finishedAtMs = completed.timestampMs;\n if (this.#identity.parentRunId) run.parentRunId = this.#identity.parentRunId;\n if (this.#identity.iteration) run.iteration = { ...this.#identity.iteration };\n return run;\n }\n}\n\n/** Fold one run's append-only events into a UI-friendly current snapshot. */\nexport function projectPipelineRun(events: readonly StoredPipelineEvent[]): StoredPipelineRun {\n if (events.length === 0) throw new Error(\"Cannot project an empty pipeline run event list.\");\n const ordered = [...events].sort((left, right) => left.id - right.id);\n const projection = new RunProjection(ordered[0]!);\n for (const event of ordered.slice(1)) projection.append(event);\n return projection.snapshot();\n}\n"]}
|
|
@@ -2,3 +2,5 @@ export declare function isDefinitionIdentity(value: unknown): boolean;
|
|
|
2
2
|
export declare function isDefinitionSnapshot(value: unknown): boolean;
|
|
3
3
|
/** Validate a projected trace error at the Studio transport boundary. */
|
|
4
4
|
export declare function isPipelineTraceError(value: unknown): boolean;
|
|
5
|
+
/** Validate artifact metadata received in a Studio step projection. */
|
|
6
|
+
export declare function isArtifactRecord(value: unknown): boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { artifactRecordSchema } from "../tracing/artifact-metadata.js";
|
|
1
2
|
import { pipelineDefinitionIdentitySchema, pipelineDefinitionSnapshotSchema, } from "../tracing/tracing-schema.js";
|
|
2
3
|
export function isDefinitionIdentity(value) {
|
|
3
4
|
try {
|
|
@@ -28,4 +29,14 @@ export function isPipelineTraceError(value) {
|
|
|
28
29
|
return false;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
32
|
+
/** Validate artifact metadata received in a Studio step projection. */
|
|
33
|
+
export function isArtifactRecord(value) {
|
|
34
|
+
try {
|
|
35
|
+
artifactRecordSchema.decode(value, "artifact record");
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
31
42
|
//# sourceMappingURL=run-store-codec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-store-codec.js","sourceRoot":"","sources":["../../src/run-store/run-store-codec.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,EAChC,gCAAgC,GACjC,MAAM,8BAA8B,CAAC;AAEtC,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC;QACH,gCAAgC,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC;QACH,gCAAgC,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAEvE,yEAAyE;AACzE,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC;QACH,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["import {\n pipelineDefinitionIdentitySchema,\n pipelineDefinitionSnapshotSchema,\n} from \"../tracing/tracing-schema.js\";\n\nexport function isDefinitionIdentity(value: unknown): boolean {\n try {\n pipelineDefinitionIdentitySchema.decode(value, \"identity\");\n return true;\n } catch {\n return false;\n }\n}\n\nexport function isDefinitionSnapshot(value: unknown): boolean {\n try {\n pipelineDefinitionSnapshotSchema.decode(value, \"snapshot\");\n return true;\n } catch {\n return false;\n }\n}\n\nimport { decodePipelineTraceError } from \"../tracing/tracing-codec.js\";\n\n/** Validate a projected trace error at the Studio transport boundary. */\nexport function isPipelineTraceError(value: unknown): boolean {\n try {\n decodePipelineTraceError(value);\n return true;\n } catch {\n return false;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"run-store-codec.js","sourceRoot":"","sources":["../../src/run-store/run-store-codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EACL,gCAAgC,EAChC,gCAAgC,GACjC,MAAM,8BAA8B,CAAC;AAEtC,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC;QACH,gCAAgC,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC;QACH,gCAAgC,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAEvE,yEAAyE;AACzE,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,CAAC;QACH,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,CAAC;QACH,oBAAoB,CAAC,MAAM,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["import { artifactRecordSchema } from \"../tracing/artifact-metadata.js\";\nimport {\n pipelineDefinitionIdentitySchema,\n pipelineDefinitionSnapshotSchema,\n} from \"../tracing/tracing-schema.js\";\n\nexport function isDefinitionIdentity(value: unknown): boolean {\n try {\n pipelineDefinitionIdentitySchema.decode(value, \"identity\");\n return true;\n } catch {\n return false;\n }\n}\n\nexport function isDefinitionSnapshot(value: unknown): boolean {\n try {\n pipelineDefinitionSnapshotSchema.decode(value, \"snapshot\");\n return true;\n } catch {\n return false;\n }\n}\n\nimport { decodePipelineTraceError } from \"../tracing/tracing-codec.js\";\n\n/** Validate a projected trace error at the Studio transport boundary. */\nexport function isPipelineTraceError(value: unknown): boolean {\n try {\n decodePipelineTraceError(value);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Validate artifact metadata received in a Studio step projection. */\nexport function isArtifactRecord(value: unknown): boolean {\n try {\n artifactRecordSchema.decode(value, \"artifact record\");\n return true;\n } catch {\n return false;\n }\n}\n"]}
|
|
@@ -6,6 +6,9 @@ export function decodeStoredTraceEvent(value) {
|
|
|
6
6
|
if (event.name === "pipeline.started" && event.payload.definitionSnapshot) {
|
|
7
7
|
const snapshot = event.payload.definitionSnapshot;
|
|
8
8
|
const expected = createDefinitionIdentity(snapshot, snapshot.identity.implementationVersion);
|
|
9
|
+
if (snapshot.identity.version !== expected.version) {
|
|
10
|
+
throw new Error("Definition snapshot identity version does not match its contents");
|
|
11
|
+
}
|
|
9
12
|
if (snapshot.identity.structuralFingerprint !== expected.structuralFingerprint) {
|
|
10
13
|
throw new Error("Definition snapshot structural fingerprint does not match its contents");
|
|
11
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-store-event-decoder.js","sourceRoot":"","sources":["../../src/run-store/run-store-event-decoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAGvE,mFAAmF;AACnF,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,MAAM,KAAK,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAC1E,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC;QAClD,MAAM,QAAQ,GAAG,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC;QAC7F,IAAI,QAAQ,CAAC,QAAQ,CAAC,qBAAqB,KAAK,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YAC/E,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC7D,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { createDefinitionIdentity } from \"../core/pipeline-definition-identity.js\";\nimport { decodePipelineTraceEvent } from \"../tracing/tracing-codec.js\";\nimport type { PipelineTraceEvent } from \"../tracing/tracing-contracts.js\";\n\n/** Validate persisted contents before they enter history or Studio projections. */\nexport function decodeStoredTraceEvent(value: unknown): PipelineTraceEvent {\n const event = decodePipelineTraceEvent(value);\n if (event.name === \"pipeline.started\" && event.payload.definitionSnapshot) {\n const snapshot = event.payload.definitionSnapshot;\n const expected = createDefinitionIdentity(snapshot, snapshot.identity.implementationVersion);\n if (snapshot.identity.structuralFingerprint !== expected.structuralFingerprint) {\n throw new Error(\"Definition snapshot structural fingerprint does not match its contents\");\n }\n if (snapshot.identity.definitionId !== expected.definitionId) {\n throw new Error(\"Definition snapshot definition ID does not match its contents\");\n }\n }\n return event;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"run-store-event-decoder.js","sourceRoot":"","sources":["../../src/run-store/run-store-event-decoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAGvE,mFAAmF;AACnF,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,MAAM,KAAK,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;QAC1E,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC;QAClD,MAAM,QAAQ,GAAG,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC;QAC7F,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;YACnD,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;QACtF,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,qBAAqB,KAAK,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YAC/E,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;YAC7D,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { createDefinitionIdentity } from \"../core/pipeline-definition-identity.js\";\nimport { decodePipelineTraceEvent } from \"../tracing/tracing-codec.js\";\nimport type { PipelineTraceEvent } from \"../tracing/tracing-contracts.js\";\n\n/** Validate persisted contents before they enter history or Studio projections. */\nexport function decodeStoredTraceEvent(value: unknown): PipelineTraceEvent {\n const event = decodePipelineTraceEvent(value);\n if (event.name === \"pipeline.started\" && event.payload.definitionSnapshot) {\n const snapshot = event.payload.definitionSnapshot;\n const expected = createDefinitionIdentity(snapshot, snapshot.identity.implementationVersion);\n if (snapshot.identity.version !== expected.version) {\n throw new Error(\"Definition snapshot identity version does not match its contents\");\n }\n if (snapshot.identity.structuralFingerprint !== expected.structuralFingerprint) {\n throw new Error(\"Definition snapshot structural fingerprint does not match its contents\");\n }\n if (snapshot.identity.definitionId !== expected.definitionId) {\n throw new Error(\"Definition snapshot definition ID does not match its contents\");\n }\n }\n return event;\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PipelineRunEventQuery, PipelineRunEventReader, StoredPipelineEvent } from "./run-store.js";
|
|
2
|
+
/**
|
|
3
|
+
* Read an event source to exhaustion with bounded, ordered pages. A reader may
|
|
4
|
+
* impose a smaller page cap than requested; only a page that cannot advance the
|
|
5
|
+
* cursor ends the scan. The caller owns the reader and backpressure between pages.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readPipelineEventPages(reader: Pick<PipelineRunEventReader, "listEvents">, query?: PipelineRunEventQuery): AsyncGenerator<readonly StoredPipelineEvent[]>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read an event source to exhaustion with bounded, ordered pages. A reader may
|
|
3
|
+
* impose a smaller page cap than requested; only a page that cannot advance the
|
|
4
|
+
* cursor ends the scan. The caller owns the reader and backpressure between pages.
|
|
5
|
+
*/
|
|
6
|
+
export async function* readPipelineEventPages(reader, query = {}) {
|
|
7
|
+
const selection = { ...query, limit: query.limit ?? 20_000 };
|
|
8
|
+
let afterId = query.afterId;
|
|
9
|
+
for (;;) {
|
|
10
|
+
const page = await reader.listEvents({ ...selection, afterId });
|
|
11
|
+
const ordered = [...page]
|
|
12
|
+
.filter((event) => afterId === undefined || event.id > afterId)
|
|
13
|
+
.sort((left, right) => left.id - right.id);
|
|
14
|
+
if (ordered.length === 0)
|
|
15
|
+
return;
|
|
16
|
+
const next = [];
|
|
17
|
+
for (const event of ordered) {
|
|
18
|
+
if (afterId !== undefined && event.id <= afterId)
|
|
19
|
+
continue;
|
|
20
|
+
afterId = event.id;
|
|
21
|
+
if (selection.runId !== undefined && event.runId !== selection.runId)
|
|
22
|
+
continue;
|
|
23
|
+
if (selection.pipelineId !== undefined && event.pipelineId !== selection.pipelineId)
|
|
24
|
+
continue;
|
|
25
|
+
next.push(event);
|
|
26
|
+
}
|
|
27
|
+
if (next.length > 0)
|
|
28
|
+
yield next;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=run-store-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-store-reader.js","sourceRoot":"","sources":["../../src/run-store/run-store-reader.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,sBAAsB,CAC3C,MAAkD,EAClD,KAAK,GAA0B,EAAE;IAEjC,MAAM,SAAS,GAAG,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC;IAC7D,IAAI,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC5B,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC;aACtB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC;aAC9D,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;QAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjC,MAAM,IAAI,GAA0B,EAAE,CAAC;QACvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,EAAE,IAAI,OAAO;gBAAE,SAAS;YAC3D,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC;YACnB,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,KAAK;gBAAE,SAAS;YAC/E,IAAI,SAAS,CAAC,UAAU,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,UAAU;gBAAE,SAAS;YAC9F,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,CAAC;IAClC,CAAC;AACH,CAAC","sourcesContent":["import type {\n PipelineRunEventQuery,\n PipelineRunEventReader,\n StoredPipelineEvent,\n} from \"./run-store.js\";\n\n/**\n * Read an event source to exhaustion with bounded, ordered pages. A reader may\n * impose a smaller page cap than requested; only a page that cannot advance the\n * cursor ends the scan. The caller owns the reader and backpressure between pages.\n */\nexport async function* readPipelineEventPages(\n reader: Pick<PipelineRunEventReader, \"listEvents\">,\n query: PipelineRunEventQuery = {}\n): AsyncGenerator<readonly StoredPipelineEvent[]> {\n const selection = { ...query, limit: query.limit ?? 20_000 };\n let afterId = query.afterId;\n for (;;) {\n const page = await reader.listEvents({ ...selection, afterId });\n const ordered = [...page]\n .filter((event) => afterId === undefined || event.id > afterId)\n .sort((left, right) => left.id - right.id);\n if (ordered.length === 0) return;\n const next: StoredPipelineEvent[] = [];\n for (const event of ordered) {\n if (afterId !== undefined && event.id <= afterId) continue;\n afterId = event.id;\n if (selection.runId !== undefined && event.runId !== selection.runId) continue;\n if (selection.pipelineId !== undefined && event.pipelineId !== selection.pipelineId) continue;\n next.push(event);\n }\n if (next.length > 0) yield next;\n }\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { lstat, mkdir, realpath, stat } from "node:fs/promises";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { decodeStoredTraceEvent } from "./run-store-event-decoder.js";
|
|
5
|
-
const RUN_EVENT_STORE_VERSION =
|
|
5
|
+
const RUN_EVENT_STORE_VERSION = 4;
|
|
6
6
|
/** Larger batches mean fewer commits but more tail loss on crash. */
|
|
7
7
|
const EXPORT_BATCH_SIZE = 64;
|
|
8
8
|
function eventRow(event) {
|
|
@@ -21,6 +21,7 @@ function eventRow(event) {
|
|
|
21
21
|
"durationMs" in canonical ? (canonical.durationMs ?? null) : null,
|
|
22
22
|
JSON.stringify(canonical.payload),
|
|
23
23
|
"error" in canonical && canonical.error ? JSON.stringify(canonical.error) : null,
|
|
24
|
+
canonical.iteration ? JSON.stringify(canonical.iteration) : null,
|
|
24
25
|
];
|
|
25
26
|
}
|
|
26
27
|
const RUN_EVENT_STORE_APPEND_ONLY_TRIGGERS = `
|
|
@@ -55,7 +56,8 @@ const RUN_EVENT_STORE_SCHEMA = `
|
|
|
55
56
|
timestamp_ms INTEGER NOT NULL,
|
|
56
57
|
duration_ms REAL,
|
|
57
58
|
payload_json TEXT NOT NULL,
|
|
58
|
-
error_json TEXT
|
|
59
|
+
error_json TEXT,
|
|
60
|
+
iteration_json TEXT
|
|
59
61
|
);
|
|
60
62
|
|
|
61
63
|
CREATE INDEX IF NOT EXISTS pipeline_run_events_run_id_idx
|
|
@@ -218,6 +220,8 @@ function mapRow(row) {
|
|
|
218
220
|
encoded.error = JSON.parse(row.error_json);
|
|
219
221
|
if (row.item_key)
|
|
220
222
|
encoded.itemKey = row.item_key;
|
|
223
|
+
if (row.iteration_json)
|
|
224
|
+
encoded.iteration = JSON.parse(row.iteration_json);
|
|
221
225
|
if (row.parent_run_id)
|
|
222
226
|
encoded.parentRunId = row.parent_run_id;
|
|
223
227
|
if (row.step_id)
|
|
@@ -250,12 +254,20 @@ export async function openSqlitePipelineRunStore(filename, options = {}) {
|
|
|
250
254
|
const database = await openDatabase(resolvedFilename, { create: initialize, readOnly });
|
|
251
255
|
try {
|
|
252
256
|
const version = readStoreVersion(database);
|
|
253
|
-
if (version !== 0 && version !== RUN_EVENT_STORE_VERSION) {
|
|
257
|
+
if (version !== 0 && version !== 3 && version !== RUN_EVENT_STORE_VERSION) {
|
|
254
258
|
throw new Error(`Unsupported pipeline run store schema version ${version}; expected ${RUN_EVENT_STORE_VERSION}.`);
|
|
255
259
|
}
|
|
256
260
|
if (!initialize && version === 0) {
|
|
257
261
|
throw new Error(`${resolvedFilename} is not a pipeline run store.`);
|
|
258
262
|
}
|
|
263
|
+
if (!readOnly && version === 3) {
|
|
264
|
+
database.exec(`
|
|
265
|
+
BEGIN IMMEDIATE;
|
|
266
|
+
ALTER TABLE pipeline_run_events ADD COLUMN iteration_json TEXT;
|
|
267
|
+
PRAGMA user_version = ${RUN_EVENT_STORE_VERSION};
|
|
268
|
+
COMMIT;
|
|
269
|
+
`);
|
|
270
|
+
}
|
|
259
271
|
if (initialize) {
|
|
260
272
|
database.exec(RUN_EVENT_STORE_SCHEMA);
|
|
261
273
|
database.exec(`PRAGMA user_version = ${RUN_EVENT_STORE_VERSION}`);
|
|
@@ -265,15 +277,17 @@ export async function openSqlitePipelineRunStore(filename, options = {}) {
|
|
|
265
277
|
database.close();
|
|
266
278
|
throw error;
|
|
267
279
|
}
|
|
268
|
-
const insert =
|
|
280
|
+
const insert = readOnly
|
|
281
|
+
? undefined
|
|
282
|
+
: statement(database, `INSERT INTO pipeline_run_events (
|
|
269
283
|
version, run_id, correlation_id, parent_run_id, pipeline_id, step_id, attempt_id,
|
|
270
|
-
item_key, event_name, timestamp_ms, duration_ms, payload_json, error_json
|
|
271
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
284
|
+
item_key, event_name, timestamp_ms, duration_ms, payload_json, error_json, iteration_json
|
|
285
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
272
286
|
let closed = false;
|
|
273
287
|
let exportError;
|
|
274
288
|
const pending = [];
|
|
275
289
|
function drain() {
|
|
276
|
-
if (pending.length === 0)
|
|
290
|
+
if (!insert || pending.length === 0)
|
|
277
291
|
return;
|
|
278
292
|
try {
|
|
279
293
|
database.exec("BEGIN IMMEDIATE");
|
|
@@ -295,6 +309,8 @@ export async function openSqlitePipelineRunStore(filename, options = {}) {
|
|
|
295
309
|
export(event) {
|
|
296
310
|
if (closed)
|
|
297
311
|
throw new Error("Cannot append to a closed pipeline run store.");
|
|
312
|
+
if (!insert)
|
|
313
|
+
throw new Error("Cannot append to a read-only pipeline run store.");
|
|
298
314
|
if (exportError)
|
|
299
315
|
throw exportError;
|
|
300
316
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-store-sqlite.js","sourceRoot":"","sources":["../../src/run-store/run-store-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, mkdir, realpath, stat } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { decodeStoredTraceEvent } from \"./run-store-event-decoder.js\";\nimport type { PipelineTraceEvent } from \"../tracing/tracing.js\";\nimport type {\n PipelineRunEventQuery,\n PipelineRunEventStore,\n StoredPipelineEvent,\n} from \"./run-store.js\";\n\ninterface SqliteStatement {\n all(...params: unknown[]): unknown[];\n run(...params: unknown[]): void;\n}\n\ninterface SqliteDatabase {\n close(): void;\n exec(sql: string): void;\n prepare?(sql: string): SqliteStatement;\n query?(sql: string): SqliteStatement;\n}\n\ninterface SqliteOpenOptions {\n create?: boolean;\n readOnly?: boolean;\n readonly?: boolean;\n readwrite?: boolean;\n}\n\ninterface SqliteModule {\n Database?: new (filename: string, options?: SqliteOpenOptions) => SqliteDatabase;\n DatabaseSync?: new (filename: string, options?: SqliteOpenOptions) => SqliteDatabase;\n}\n\ninterface StoredEventRow {\n attempt_id: string | null;\n correlation_id: string | null;\n duration_ms: number | null;\n error_json: string | null;\n event_name: string;\n id: number | bigint;\n item_key: string | null;\n parent_run_id: string | null;\n payload_json: string;\n pipeline_id: string;\n run_id: string;\n step_id: string | null;\n timestamp_ms: number | bigint;\n version: number;\n}\n\nconst RUN_EVENT_STORE_VERSION = 3;\n/** Larger batches mean fewer commits but more tail loss on crash. */\nconst EXPORT_BATCH_SIZE = 64;\n\ntype EventRow = readonly [\n version: number,\n runId: string,\n correlationId: string | null,\n parentRunId: string | null,\n pipelineId: string,\n stepId: string | null,\n attemptId: string | null,\n itemKey: string | null,\n name: string,\n timestampMs: number,\n durationMs: number | null,\n payloadJson: string,\n errorJson: string | null,\n];\n\nfunction eventRow(event: PipelineTraceEvent): EventRow {\n const canonical = decodeStoredTraceEvent(event);\n return [\n canonical.version,\n canonical.runId,\n canonical.correlationId ?? null,\n canonical.parentRunId ?? null,\n canonical.pipelineId,\n \"stepId\" in canonical ? (canonical.stepId ?? null) : null,\n \"attemptId\" in canonical ? (canonical.attemptId ?? null) : null,\n canonical.itemKey ?? null,\n canonical.name,\n canonical.timestampMs,\n \"durationMs\" in canonical ? (canonical.durationMs ?? null) : null,\n JSON.stringify(canonical.payload),\n \"error\" in canonical && canonical.error ? JSON.stringify(canonical.error) : null,\n ];\n}\n\nconst RUN_EVENT_STORE_APPEND_ONLY_TRIGGERS = `\n CREATE TRIGGER IF NOT EXISTS pipeline_run_events_no_update\n BEFORE UPDATE ON pipeline_run_events\n BEGIN\n SELECT RAISE(ABORT, 'pipeline_run_events is append-only');\n END;\n\n CREATE TRIGGER IF NOT EXISTS pipeline_run_events_no_delete\n BEFORE DELETE ON pipeline_run_events\n BEGIN\n SELECT RAISE(ABORT, 'pipeline_run_events is append-only');\n END;\n`;\n\nconst RUN_EVENT_STORE_SCHEMA = `\n PRAGMA journal_mode = WAL;\n PRAGMA synchronous = NORMAL;\n PRAGMA foreign_keys = ON;\n\n CREATE TABLE IF NOT EXISTS pipeline_run_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n version INTEGER NOT NULL,\n run_id TEXT NOT NULL,\n correlation_id TEXT,\n parent_run_id TEXT,\n pipeline_id TEXT NOT NULL,\n step_id TEXT,\n attempt_id TEXT,\n item_key TEXT,\n event_name TEXT NOT NULL,\n timestamp_ms INTEGER NOT NULL,\n duration_ms REAL,\n payload_json TEXT NOT NULL,\n error_json TEXT\n );\n\n CREATE INDEX IF NOT EXISTS pipeline_run_events_run_id_idx\n ON pipeline_run_events(run_id, id);\n CREATE INDEX IF NOT EXISTS pipeline_run_events_pipeline_id_idx\n ON pipeline_run_events(pipeline_id, id DESC);\n CREATE INDEX IF NOT EXISTS pipeline_run_events_timestamp_idx\n ON pipeline_run_events(timestamp_ms DESC);\n\n ${RUN_EVENT_STORE_APPEND_ONLY_TRIGGERS}\n`;\n\n/** SQLite event store plus the explicit all-history maintenance operation. */\nexport interface SqlitePipelineRunStore extends PipelineRunEventStore {\n /** Delete every recorded event and compact the database; individual mutation stays forbidden. */\n clearHistory(): void | Promise<void>;\n}\n\nfunction statement(database: SqliteDatabase, sql: string): SqliteStatement {\n const prepared = database.query?.(sql) ?? database.prepare?.(sql);\n if (!prepared) throw new Error(\"The current SQLite runtime does not support prepared queries.\");\n return prepared;\n}\n\nfunction readStoreVersion(database: SqliteDatabase): number {\n // SAFETY: `PRAGMA user_version` always returns a single row with a\n // `user_version` column, so the first result row matches this shape.\n const versionRow = statement(database, \"PRAGMA user_version\").all()[0] as\n | { user_version?: number | bigint }\n | undefined;\n return Number(versionRow?.user_version ?? 0);\n}\n\nasync function loadSqliteModule(): Promise<SqliteModule> {\n const specifier = \"Bun\" in globalThis ? \"bun:sqlite\" : \"node:sqlite\";\n // SAFETY: The dynamic import resolves to the built-in sqlite module whose\n // runtime shape is the `SqliteModule` interface we depend on.\n return (await import(specifier)) as SqliteModule;\n}\n\nasync function openDatabase(\n filename: string,\n options: { create?: boolean; readOnly?: boolean } = {}\n): Promise<SqliteDatabase> {\n const module = await loadSqliteModule();\n const Database = module.Database ?? module.DatabaseSync;\n if (!Database) throw new Error(\"No synchronous SQLite database implementation is available.\");\n const bunSqlite = \"Bun\" in globalThis;\n const create = options.create !== false;\n const readOnly = options.readOnly === true;\n if (readOnly) {\n await sqliteAssertImmutableInspect(filename);\n const database = openReadableDatabase(\n Database,\n `${pathToFileURL(filename).href}?mode=ro&immutable=1`\n );\n try {\n await sqliteAssertImmutableInspect(filename);\n return database;\n } catch (error) {\n database.close();\n throw error;\n }\n }\n if (!create) {\n if (bunSqlite) {\n return openReadableDatabase(Database, filename, { create: false, readwrite: true });\n }\n // Node's DatabaseSync ignores `{ create: false }` and creates an empty\n // file. `mode=rw` fails closed when the path is missing.\n return openReadableDatabase(Database, `${pathToFileURL(filename).href}?mode=rw`);\n }\n return new Database(filename);\n}\n\nfunction isMissingDirectoryEntry(error: NodeJS.ErrnoException): boolean {\n return error.code === \"ENOENT\";\n}\n\nasync function sqliteSidecarExists(filename: string): Promise<boolean> {\n try {\n await stat(filename);\n return true;\n } catch (error) {\n // SAFETY: Node fs rejects with ErrnoException. Only ENOENT is a missing\n // directory entry; EACCES/EPERM and other inspect failures stay present.\n if (isMissingDirectoryEntry(error as NodeJS.ErrnoException) === false) return true;\n }\n try {\n await lstat(filename);\n return true;\n } catch (error) {\n // SAFETY: Same Node fs ErrnoException contract as the stat() branch.\n return isMissingDirectoryEntry(error as NodeJS.ErrnoException) === false;\n }\n}\n\nasync function sqliteSidecarTargets(filename: string): Promise<readonly string[]> {\n const targets: string[] = [];\n try {\n targets.push(await realpath(filename));\n } catch {\n // Missing or unresolvable; still check the given name.\n }\n if (targets.includes(filename) === false) targets.push(filename);\n return targets;\n}\n\nasync function sqliteDatabaseHasMultipleLinks(filename: string): Promise<boolean> {\n for (const target of await sqliteSidecarTargets(filename)) {\n try {\n if ((await stat(target)).nlink > 1) return true;\n } catch {\n return true;\n }\n }\n return false;\n}\n\nasync function sqliteAssertImmutableInspect(filename: string): Promise<void> {\n const pending = await sqlitePendingTransactionalSidecar(filename);\n if (pending !== undefined) {\n throw new Error(\n `${filename} has a write-ahead log or rollback journal that a read-only open cannot apply without writing a sidecar.`\n );\n }\n if (await sqliteDatabaseHasMultipleLinks(filename)) {\n throw new Error(\n `${filename} has multiple hard links; a read-only open cannot locate a writer's write-ahead log beside another name.`\n );\n }\n}\n\nasync function sqlitePendingTransactionalSidecar(filename: string): Promise<string | undefined> {\n for (const target of await sqliteSidecarTargets(filename)) {\n for (const suffix of [\"-journal\", \"-wal\"] as const) {\n const sidecar = `${target}${suffix}`;\n if ((await sqliteSidecarExists(sidecar)) === false) continue;\n try {\n if ((await stat(sidecar)).size > 0) return sidecar;\n } catch {\n return sidecar;\n }\n }\n }\n return undefined;\n}\n\nfunction openReadableDatabase(\n Database: new (filename: string, options?: SqliteOpenOptions) => SqliteDatabase,\n filename: string,\n options?: SqliteOpenOptions\n): SqliteDatabase {\n const database = options === undefined ? new Database(filename) : new Database(filename, options);\n try {\n statement(database, \"PRAGMA user_version\").all();\n return database;\n } catch (error) {\n database.close();\n throw error;\n }\n}\n\nfunction mapRow(row: StoredEventRow): StoredPipelineEvent {\n const encoded: Record<string, unknown> = {\n name: row.event_name,\n pipelineId: row.pipeline_id,\n runId: row.run_id,\n timestampMs: Number(row.timestamp_ms),\n version: row.version,\n };\n encoded.payload = JSON.parse(row.payload_json);\n if (row.attempt_id) encoded.attemptId = row.attempt_id;\n if (row.correlation_id != null) encoded.correlationId = row.correlation_id;\n if (row.duration_ms !== null) encoded.durationMs = Number(row.duration_ms);\n if (row.error_json) encoded.error = JSON.parse(row.error_json);\n if (row.item_key) encoded.itemKey = row.item_key;\n if (row.parent_run_id) encoded.parentRunId = row.parent_run_id;\n if (row.step_id) encoded.stepId = row.step_id;\n return { ...decodeStoredTraceEvent(encoded), id: Number(row.id) };\n}\n\n/** Options for `openSqlitePipelineRunStore`. */\nexport interface OpenSqlitePipelineRunStoreOptions {\n /**\n * When `false`, reject a path that is not already a versioned run store\n * and do not create parent directories or a database file.\n * Defaults to `true` so `run --store` and `ui` can create the database.\n */\n readonly initialize?: boolean;\n /**\n * Open the file without creating it or writing WAL/sidecars.\n * A leftover empty `-wal` is ignored and the main file is opened\n * immutable. A non-empty `-wal` or `-journal`, a multiply linked\n * database file, or a WAL that appears during the open or a later\n * `listEvents` is refused so pending events are not dropped.\n * `history` uses this so a supplied finished artifact can be inspected\n * without rewriting `-shm`.\n */\n readonly readOnly?: boolean;\n}\n\n/**\n * Open the optional local SQLite trace store used by `tubeless run --store`,\n * `tubeless history`, and `tubeless ui`. The main executor never imports this\n * module. `export()` buffers up to 64 events and commits them in one\n * transaction, plus on `flush()`, same-instance `listEvents`/`clearHistory`,\n * and `close()`. Other connections cannot see the unflushed tail; a crash can\n * lose it. Finished `tubeless run --store` invocations flush at completion.\n */\nexport async function openSqlitePipelineRunStore(\n filename: string,\n options: OpenSqlitePipelineRunStoreOptions = {}\n): Promise<SqlitePipelineRunStore> {\n const readOnly = options.readOnly === true;\n const initialize = options.initialize !== false && !readOnly;\n const resolvedFilename = filename === \":memory:\" ? filename : path.resolve(filename);\n if (resolvedFilename !== \":memory:\" && initialize) {\n await mkdir(path.dirname(resolvedFilename), { recursive: true });\n }\n if (resolvedFilename !== \":memory:\" && !initialize) {\n try {\n await stat(resolvedFilename);\n } catch {\n throw new Error(`${resolvedFilename} is not a pipeline run store.`);\n }\n }\n const database = await openDatabase(resolvedFilename, { create: initialize, readOnly });\n try {\n const version = readStoreVersion(database);\n if (version !== 0 && version !== RUN_EVENT_STORE_VERSION) {\n throw new Error(\n `Unsupported pipeline run store schema version ${version}; expected ${RUN_EVENT_STORE_VERSION}.`\n );\n }\n if (!initialize && version === 0) {\n throw new Error(`${resolvedFilename} is not a pipeline run store.`);\n }\n if (initialize) {\n database.exec(RUN_EVENT_STORE_SCHEMA);\n database.exec(`PRAGMA user_version = ${RUN_EVENT_STORE_VERSION}`);\n }\n } catch (error) {\n database.close();\n throw error;\n }\n const insert = statement(\n database,\n `INSERT INTO pipeline_run_events (\n version, run_id, correlation_id, parent_run_id, pipeline_id, step_id, attempt_id,\n item_key, event_name, timestamp_ms, duration_ms, payload_json, error_json\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n );\n let closed = false;\n let exportError: Error | undefined;\n const pending: EventRow[] = [];\n\n function drain(): void {\n if (pending.length === 0) return;\n try {\n database.exec(\"BEGIN IMMEDIATE\");\n for (const row of pending) insert.run(...row);\n database.exec(\"COMMIT\");\n pending.length = 0;\n } catch (error) {\n try {\n database.exec(\"ROLLBACK\");\n } catch {}\n exportError = error instanceof Error ? error : new Error(String(error));\n throw exportError;\n }\n }\n\n return {\n export(event) {\n if (closed) throw new Error(\"Cannot append to a closed pipeline run store.\");\n if (exportError) throw exportError;\n try {\n pending.push(eventRow(event));\n if (pending.length >= EXPORT_BATCH_SIZE) drain();\n } catch (error) {\n exportError = error instanceof Error ? error : new Error(String(error));\n throw exportError;\n }\n },\n flush() {\n if (exportError) throw exportError;\n drain();\n },\n async listEvents(query: PipelineRunEventQuery = {}) {\n if (closed) throw new Error(\"Cannot query a closed pipeline run store.\");\n drain();\n if (readOnly && resolvedFilename !== \":memory:\") {\n await sqliteAssertImmutableInspect(resolvedFilename);\n }\n const predicates: string[] = [];\n const params: unknown[] = [];\n if (query.afterId !== undefined) {\n predicates.push(\"id > ?\");\n params.push(query.afterId);\n }\n if (query.pipelineId !== undefined) {\n predicates.push(\"pipeline_id = ?\");\n params.push(query.pipelineId);\n }\n if (query.runId !== undefined) {\n predicates.push(\"run_id = ?\");\n params.push(query.runId);\n }\n const limit = Math.max(1, Math.min(100_000, Math.floor(query.limit ?? 20_000)));\n params.push(limit);\n const where = predicates.length > 0 ? `WHERE ${predicates.join(\" AND \")}` : \"\";\n // SAFETY: The SELECT * columns match the StoredEventRow interface exactly\n // (the table schema is owned by this module), so each result row is one.\n const rows = statement(\n database,\n `SELECT * FROM pipeline_run_events ${where} ORDER BY id ASC LIMIT ?`\n ).all(...params) as StoredEventRow[];\n if (readOnly && resolvedFilename !== \":memory:\") {\n await sqliteAssertImmutableInspect(resolvedFilename);\n }\n return rows.map(mapRow);\n },\n clearHistory() {\n if (closed) throw new Error(\"Cannot clear a closed pipeline run store.\");\n drain();\n try {\n database.exec(`\n BEGIN IMMEDIATE;\n DROP TRIGGER IF EXISTS pipeline_run_events_no_update;\n DROP TRIGGER IF EXISTS pipeline_run_events_no_delete;\n DELETE FROM pipeline_run_events;\n ${RUN_EVENT_STORE_APPEND_ONLY_TRIGGERS}\n COMMIT;\n `);\n } catch (error) {\n try {\n database.exec(\"ROLLBACK\");\n } catch {}\n throw error;\n }\n database.exec(\"VACUUM; PRAGMA wal_checkpoint(TRUNCATE);\");\n },\n close() {\n if (closed) return;\n closed = true;\n try {\n if (!exportError) drain();\n } catch {\n // exportError is recorded by drain; close still releases the handle.\n }\n database.close();\n if (exportError) throw exportError;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"run-store-sqlite.js","sourceRoot":"","sources":["../../src/run-store/run-store-sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAmDtE,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAClC,qEAAqE;AACrE,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAmB7B,SAAS,QAAQ,CAAC,KAAyB;IACzC,MAAM,SAAS,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAChD,OAAO;QACL,SAAS,CAAC,OAAO;QACjB,SAAS,CAAC,KAAK;QACf,SAAS,CAAC,aAAa,IAAI,IAAI;QAC/B,SAAS,CAAC,WAAW,IAAI,IAAI;QAC7B,SAAS,CAAC,UAAU;QACpB,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QACzD,WAAW,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC/D,SAAS,CAAC,OAAO,IAAI,IAAI;QACzB,SAAS,CAAC,IAAI;QACd,SAAS,CAAC,WAAW;QACrB,YAAY,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QACjE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC;QACjC,OAAO,IAAI,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;QAChF,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI;KACjE,CAAC;AACJ,CAAC;AAED,MAAM,oCAAoC,GAAG;;;;;;;;;;;;CAY5C,CAAC;AAEF,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8B3B,oCAAoC;CACvC,CAAC;AAQF,SAAS,SAAS,CAAC,QAAwB,EAAE,GAAW;IACtD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;IAClE,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IAChG,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAwB;IAChD,mEAAmE;IACnE,qEAAqE;IACrE,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAExD,CAAC;IACd,OAAO,MAAM,CAAC,UAAU,EAAE,YAAY,IAAI,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,KAAK,UAAU,gBAAgB;IAC7B,MAAM,SAAS,GAAG,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC;IACrE,0EAA0E;IAC1E,8DAA8D;IAC9D,OAAO,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAiB,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,QAAgB,EAChB,OAAO,GAA6C,EAAE;IAEtD,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,YAAY,CAAC;IACxD,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IAC9F,MAAM,SAAS,GAAG,KAAK,IAAI,UAAU,CAAC;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC;IACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,4BAA4B,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,oBAAoB,CACnC,QAAQ,EACR,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,sBAAsB,CACtD,CAAC;QACF,IAAI,CAAC;YACH,MAAM,4BAA4B,CAAC,QAAQ,CAAC,CAAC;YAC7C,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,uEAAuE;QACvE,yDAAyD;QACzD,OAAO,oBAAoB,CAAC,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,uBAAuB,CAAC,KAA4B;IAC3D,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AACjC,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACjD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,wEAAwE;QACxE,yEAAyE;QACzE,IAAI,uBAAuB,CAAC,KAA8B,CAAC,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC;IACrF,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,qEAAqE;QACrE,OAAO,uBAAuB,CAAC,KAA8B,CAAC,KAAK,KAAK,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IAClD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,uDAAuD;IACzD,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,KAAK;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,8BAA8B,CAAC,QAAgB;IAC5D,KAAK,MAAM,MAAM,IAAI,MAAM,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,4BAA4B,CAAC,QAAgB;IAC1D,MAAM,OAAO,GAAG,MAAM,iCAAiC,CAAC,QAAQ,CAAC,CAAC;IAClE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,GAAG,QAAQ,0GAA0G,CACtH,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,8BAA8B,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,GAAG,QAAQ,0GAA0G,CACtH,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,iCAAiC,CAAC,QAAgB;IAC/D,KAAK,MAAM,MAAM,IAAI,MAAM,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1D,KAAK,MAAM,MAAM,IAAI,CAAC,UAAU,EAAE,MAAM,CAAU,EAAE,CAAC;YACnD,MAAM,OAAO,GAAG,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,MAAM,mBAAmB,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK;gBAAE,SAAS;YAC7D,IAAI,CAAC;gBACH,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;oBAAE,OAAO,OAAO,CAAC;YACrD,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,OAAO,CAAC;YACjB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,oBAAoB,CAC3B,QAA+E,EAC/E,QAAgB,EAChB,OAA2B;IAE3B,MAAM,QAAQ,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClG,IAAI,CAAC;QACH,SAAS,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAC,GAAG,EAAE,CAAC;QACjD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,GAAmB;IACjC,MAAM,OAAO,GAA4B;QACvC,IAAI,EAAE,GAAG,CAAC,UAAU;QACpB,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,KAAK,EAAE,GAAG,CAAC,MAAM;QACjB,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC;QACrC,OAAO,EAAE,GAAG,CAAC,OAAO;KACrB,CAAC;IACF,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC/C,IAAI,GAAG,CAAC,UAAU;QAAE,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC,UAAU,CAAC;IACvD,IAAI,GAAG,CAAC,cAAc,IAAI,IAAI;QAAE,OAAO,CAAC,aAAa,GAAG,GAAG,CAAC,cAAc,CAAC;IAC3E,IAAI,GAAG,CAAC,WAAW,KAAK,IAAI;QAAE,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC3E,IAAI,GAAG,CAAC,UAAU;QAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,GAAG,CAAC,QAAQ;QAAE,OAAO,CAAC,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC;IACjD,IAAI,GAAG,CAAC,cAAc;QAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC3E,IAAI,GAAG,CAAC,aAAa;QAAE,OAAO,CAAC,WAAW,GAAG,GAAG,CAAC,aAAa,CAAC;IAC/D,IAAI,GAAG,CAAC,OAAO;QAAE,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC;IAC9C,OAAO,EAAE,GAAG,sBAAsB,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;AACpE,CAAC;AAsBD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,QAAgB,EAChB,OAAO,GAAsC,EAAE;IAE/C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC;IAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC;IAC7D,MAAM,gBAAgB,GAAG,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrF,IAAI,gBAAgB,KAAK,UAAU,IAAI,UAAU,EAAE,CAAC;QAClD,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,gBAAgB,KAAK,UAAU,IAAI,CAAC,UAAU,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,GAAG,gBAAgB,+BAA+B,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,gBAAgB,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;IACxF,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,OAAO,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,OAAO,KAAK,uBAAuB,EAAE,CAAC;YAC1E,MAAM,IAAI,KAAK,CACb,iDAAiD,OAAO,cAAc,uBAAuB,GAAG,CACjG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,UAAU,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,GAAG,gBAAgB,+BAA+B,CAAC,CAAC;QACtE,CAAC;QACD,IAAI,CAAC,QAAQ,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC;;;gCAGY,uBAAuB;;OAEhD,CAAC,CAAC;QACL,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YACtC,QAAQ,CAAC,IAAI,CAAC,yBAAyB,uBAAuB,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,KAAK,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ;QACrB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,SAAS,CACP,QAAQ,EACR;;;4DAGoD,CACrD,CAAC;IACN,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,WAA8B,CAAC;IACnC,MAAM,OAAO,GAAe,EAAE,CAAC;IAE/B,SAAS,KAAK;QACZ,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC5C,IAAI,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACjC,KAAK,MAAM,GAAG,IAAI,OAAO;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;YAC9C,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,WAAW,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACxE,MAAM,WAAW,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM,CAAC,KAAK;YACV,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;YAC7E,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;YACjF,IAAI,WAAW;gBAAE,MAAM,WAAW,CAAC;YACnC,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC9B,IAAI,OAAO,CAAC,MAAM,IAAI,iBAAiB;oBAAE,KAAK,EAAE,CAAC;YACnD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,WAAW,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBACxE,MAAM,WAAW,CAAC;YACpB,CAAC;QACH,CAAC;QACD,KAAK;YACH,IAAI,WAAW;gBAAE,MAAM,WAAW,CAAC;YACnC,KAAK,EAAE,CAAC;QACV,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,KAAK,GAA0B,EAAE;YAChD,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YACzE,KAAK,EAAE,CAAC;YACR,IAAI,QAAQ,IAAI,gBAAgB,KAAK,UAAU,EAAE,CAAC;gBAChD,MAAM,4BAA4B,CAAC,gBAAgB,CAAC,CAAC;YACvD,CAAC;YACD,MAAM,UAAU,GAAa,EAAE,CAAC;YAChC,MAAM,MAAM,GAAc,EAAE,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC;YACD,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBACnC,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;gBACnC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAChC,CAAC;YACD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC9B,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;YAChF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,0EAA0E;YAC1E,yEAAyE;YACzE,MAAM,IAAI,GAAG,SAAS,CACpB,QAAQ,EACR,qCAAqC,KAAK,0BAA0B,CACrE,CAAC,GAAG,CAAC,GAAG,MAAM,CAAqB,CAAC;YACrC,IAAI,QAAQ,IAAI,gBAAgB,KAAK,UAAU,EAAE,CAAC;gBAChD,MAAM,4BAA4B,CAAC,gBAAgB,CAAC,CAAC;YACvD,CAAC;YACD,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;QACD,YAAY;YACV,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YACzE,KAAK,EAAE,CAAC;YACR,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC;;;;;YAKV,oCAAoC;;SAEvC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC;oBACH,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;gBACV,MAAM,KAAK,CAAC;YACd,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;QAC5D,CAAC;QACD,KAAK;YACH,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,CAAC;gBACH,IAAI,CAAC,WAAW;oBAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,qEAAqE;YACvE,CAAC;YACD,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,IAAI,WAAW;gBAAE,MAAM,WAAW,CAAC;QACrC,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { lstat, mkdir, realpath, stat } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { decodeStoredTraceEvent } from \"./run-store-event-decoder.js\";\nimport type { PipelineTraceEvent } from \"../tracing/tracing.js\";\nimport type {\n PipelineRunEventQuery,\n PipelineRunEventStore,\n StoredPipelineEvent,\n} from \"./run-store.js\";\n\ninterface SqliteStatement {\n all(...params: unknown[]): unknown[];\n run(...params: unknown[]): void;\n}\n\ninterface SqliteDatabase {\n close(): void;\n exec(sql: string): void;\n prepare?(sql: string): SqliteStatement;\n query?(sql: string): SqliteStatement;\n}\n\ninterface SqliteOpenOptions {\n create?: boolean;\n readOnly?: boolean;\n readonly?: boolean;\n readwrite?: boolean;\n}\n\ninterface SqliteModule {\n Database?: new (filename: string, options?: SqliteOpenOptions) => SqliteDatabase;\n DatabaseSync?: new (filename: string, options?: SqliteOpenOptions) => SqliteDatabase;\n}\n\ninterface StoredEventRow {\n attempt_id: string | null;\n correlation_id: string | null;\n duration_ms: number | null;\n error_json: string | null;\n event_name: string;\n id: number | bigint;\n item_key: string | null;\n /** Absent when inspecting a legacy version 3 store without migrating it. */\n iteration_json?: string | null;\n parent_run_id: string | null;\n payload_json: string;\n pipeline_id: string;\n run_id: string;\n step_id: string | null;\n timestamp_ms: number | bigint;\n version: number;\n}\n\nconst RUN_EVENT_STORE_VERSION = 4;\n/** Larger batches mean fewer commits but more tail loss on crash. */\nconst EXPORT_BATCH_SIZE = 64;\n\ntype EventRow = readonly [\n version: number,\n runId: string,\n correlationId: string | null,\n parentRunId: string | null,\n pipelineId: string,\n stepId: string | null,\n attemptId: string | null,\n itemKey: string | null,\n name: string,\n timestampMs: number,\n durationMs: number | null,\n payloadJson: string,\n errorJson: string | null,\n iterationJson: string | null,\n];\n\nfunction eventRow(event: PipelineTraceEvent): EventRow {\n const canonical = decodeStoredTraceEvent(event);\n return [\n canonical.version,\n canonical.runId,\n canonical.correlationId ?? null,\n canonical.parentRunId ?? null,\n canonical.pipelineId,\n \"stepId\" in canonical ? (canonical.stepId ?? null) : null,\n \"attemptId\" in canonical ? (canonical.attemptId ?? null) : null,\n canonical.itemKey ?? null,\n canonical.name,\n canonical.timestampMs,\n \"durationMs\" in canonical ? (canonical.durationMs ?? null) : null,\n JSON.stringify(canonical.payload),\n \"error\" in canonical && canonical.error ? JSON.stringify(canonical.error) : null,\n canonical.iteration ? JSON.stringify(canonical.iteration) : null,\n ];\n}\n\nconst RUN_EVENT_STORE_APPEND_ONLY_TRIGGERS = `\n CREATE TRIGGER IF NOT EXISTS pipeline_run_events_no_update\n BEFORE UPDATE ON pipeline_run_events\n BEGIN\n SELECT RAISE(ABORT, 'pipeline_run_events is append-only');\n END;\n\n CREATE TRIGGER IF NOT EXISTS pipeline_run_events_no_delete\n BEFORE DELETE ON pipeline_run_events\n BEGIN\n SELECT RAISE(ABORT, 'pipeline_run_events is append-only');\n END;\n`;\n\nconst RUN_EVENT_STORE_SCHEMA = `\n PRAGMA journal_mode = WAL;\n PRAGMA synchronous = NORMAL;\n PRAGMA foreign_keys = ON;\n\n CREATE TABLE IF NOT EXISTS pipeline_run_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n version INTEGER NOT NULL,\n run_id TEXT NOT NULL,\n correlation_id TEXT,\n parent_run_id TEXT,\n pipeline_id TEXT NOT NULL,\n step_id TEXT,\n attempt_id TEXT,\n item_key TEXT,\n event_name TEXT NOT NULL,\n timestamp_ms INTEGER NOT NULL,\n duration_ms REAL,\n payload_json TEXT NOT NULL,\n error_json TEXT,\n iteration_json TEXT\n );\n\n CREATE INDEX IF NOT EXISTS pipeline_run_events_run_id_idx\n ON pipeline_run_events(run_id, id);\n CREATE INDEX IF NOT EXISTS pipeline_run_events_pipeline_id_idx\n ON pipeline_run_events(pipeline_id, id DESC);\n CREATE INDEX IF NOT EXISTS pipeline_run_events_timestamp_idx\n ON pipeline_run_events(timestamp_ms DESC);\n\n ${RUN_EVENT_STORE_APPEND_ONLY_TRIGGERS}\n`;\n\n/** SQLite event store plus the explicit all-history maintenance operation. */\nexport interface SqlitePipelineRunStore extends PipelineRunEventStore {\n /** Delete every recorded event and compact the database; individual mutation stays forbidden. */\n clearHistory(): void | Promise<void>;\n}\n\nfunction statement(database: SqliteDatabase, sql: string): SqliteStatement {\n const prepared = database.query?.(sql) ?? database.prepare?.(sql);\n if (!prepared) throw new Error(\"The current SQLite runtime does not support prepared queries.\");\n return prepared;\n}\n\nfunction readStoreVersion(database: SqliteDatabase): number {\n // SAFETY: `PRAGMA user_version` always returns a single row with a\n // `user_version` column, so the first result row matches this shape.\n const versionRow = statement(database, \"PRAGMA user_version\").all()[0] as\n | { user_version?: number | bigint }\n | undefined;\n return Number(versionRow?.user_version ?? 0);\n}\n\nasync function loadSqliteModule(): Promise<SqliteModule> {\n const specifier = \"Bun\" in globalThis ? \"bun:sqlite\" : \"node:sqlite\";\n // SAFETY: The dynamic import resolves to the built-in sqlite module whose\n // runtime shape is the `SqliteModule` interface we depend on.\n return (await import(specifier)) as SqliteModule;\n}\n\nasync function openDatabase(\n filename: string,\n options: { create?: boolean; readOnly?: boolean } = {}\n): Promise<SqliteDatabase> {\n const module = await loadSqliteModule();\n const Database = module.Database ?? module.DatabaseSync;\n if (!Database) throw new Error(\"No synchronous SQLite database implementation is available.\");\n const bunSqlite = \"Bun\" in globalThis;\n const create = options.create !== false;\n const readOnly = options.readOnly === true;\n if (readOnly) {\n await sqliteAssertImmutableInspect(filename);\n const database = openReadableDatabase(\n Database,\n `${pathToFileURL(filename).href}?mode=ro&immutable=1`\n );\n try {\n await sqliteAssertImmutableInspect(filename);\n return database;\n } catch (error) {\n database.close();\n throw error;\n }\n }\n if (!create) {\n if (bunSqlite) {\n return openReadableDatabase(Database, filename, { create: false, readwrite: true });\n }\n // Node's DatabaseSync ignores `{ create: false }` and creates an empty\n // file. `mode=rw` fails closed when the path is missing.\n return openReadableDatabase(Database, `${pathToFileURL(filename).href}?mode=rw`);\n }\n return new Database(filename);\n}\n\nfunction isMissingDirectoryEntry(error: NodeJS.ErrnoException): boolean {\n return error.code === \"ENOENT\";\n}\n\nasync function sqliteSidecarExists(filename: string): Promise<boolean> {\n try {\n await stat(filename);\n return true;\n } catch (error) {\n // SAFETY: Node fs rejects with ErrnoException. Only ENOENT is a missing\n // directory entry; EACCES/EPERM and other inspect failures stay present.\n if (isMissingDirectoryEntry(error as NodeJS.ErrnoException) === false) return true;\n }\n try {\n await lstat(filename);\n return true;\n } catch (error) {\n // SAFETY: Same Node fs ErrnoException contract as the stat() branch.\n return isMissingDirectoryEntry(error as NodeJS.ErrnoException) === false;\n }\n}\n\nasync function sqliteSidecarTargets(filename: string): Promise<readonly string[]> {\n const targets: string[] = [];\n try {\n targets.push(await realpath(filename));\n } catch {\n // Missing or unresolvable; still check the given name.\n }\n if (targets.includes(filename) === false) targets.push(filename);\n return targets;\n}\n\nasync function sqliteDatabaseHasMultipleLinks(filename: string): Promise<boolean> {\n for (const target of await sqliteSidecarTargets(filename)) {\n try {\n if ((await stat(target)).nlink > 1) return true;\n } catch {\n return true;\n }\n }\n return false;\n}\n\nasync function sqliteAssertImmutableInspect(filename: string): Promise<void> {\n const pending = await sqlitePendingTransactionalSidecar(filename);\n if (pending !== undefined) {\n throw new Error(\n `${filename} has a write-ahead log or rollback journal that a read-only open cannot apply without writing a sidecar.`\n );\n }\n if (await sqliteDatabaseHasMultipleLinks(filename)) {\n throw new Error(\n `${filename} has multiple hard links; a read-only open cannot locate a writer's write-ahead log beside another name.`\n );\n }\n}\n\nasync function sqlitePendingTransactionalSidecar(filename: string): Promise<string | undefined> {\n for (const target of await sqliteSidecarTargets(filename)) {\n for (const suffix of [\"-journal\", \"-wal\"] as const) {\n const sidecar = `${target}${suffix}`;\n if ((await sqliteSidecarExists(sidecar)) === false) continue;\n try {\n if ((await stat(sidecar)).size > 0) return sidecar;\n } catch {\n return sidecar;\n }\n }\n }\n return undefined;\n}\n\nfunction openReadableDatabase(\n Database: new (filename: string, options?: SqliteOpenOptions) => SqliteDatabase,\n filename: string,\n options?: SqliteOpenOptions\n): SqliteDatabase {\n const database = options === undefined ? new Database(filename) : new Database(filename, options);\n try {\n statement(database, \"PRAGMA user_version\").all();\n return database;\n } catch (error) {\n database.close();\n throw error;\n }\n}\n\nfunction mapRow(row: StoredEventRow): StoredPipelineEvent {\n const encoded: Record<string, unknown> = {\n name: row.event_name,\n pipelineId: row.pipeline_id,\n runId: row.run_id,\n timestampMs: Number(row.timestamp_ms),\n version: row.version,\n };\n encoded.payload = JSON.parse(row.payload_json);\n if (row.attempt_id) encoded.attemptId = row.attempt_id;\n if (row.correlation_id != null) encoded.correlationId = row.correlation_id;\n if (row.duration_ms !== null) encoded.durationMs = Number(row.duration_ms);\n if (row.error_json) encoded.error = JSON.parse(row.error_json);\n if (row.item_key) encoded.itemKey = row.item_key;\n if (row.iteration_json) encoded.iteration = JSON.parse(row.iteration_json);\n if (row.parent_run_id) encoded.parentRunId = row.parent_run_id;\n if (row.step_id) encoded.stepId = row.step_id;\n return { ...decodeStoredTraceEvent(encoded), id: Number(row.id) };\n}\n\n/** Options for `openSqlitePipelineRunStore`. */\nexport interface OpenSqlitePipelineRunStoreOptions {\n /**\n * When `false`, reject a path that is not already a versioned run store\n * and do not create parent directories or a database file.\n * Defaults to `true` so `run --store` and `ui` can create the database.\n */\n readonly initialize?: boolean;\n /**\n * Open the file without creating it or writing WAL/sidecars.\n * A leftover empty `-wal` is ignored and the main file is opened\n * immutable. A non-empty `-wal` or `-journal`, a multiply linked\n * database file, or a WAL that appears during the open or a later\n * `listEvents` is refused so pending events are not dropped.\n * `history` uses this so a supplied finished artifact can be inspected\n * without rewriting `-shm`.\n */\n readonly readOnly?: boolean;\n}\n\n/**\n * Open the optional local SQLite trace store used by `tubeless run --store`,\n * `tubeless history`, and `tubeless ui`. The main executor never imports this\n * module. `export()` buffers up to 64 events and commits them in one\n * transaction, plus on `flush()`, same-instance `listEvents`/`clearHistory`,\n * and `close()`. Other connections cannot see the unflushed tail; a crash can\n * lose it. Finished `tubeless run --store` invocations flush at completion.\n */\nexport async function openSqlitePipelineRunStore(\n filename: string,\n options: OpenSqlitePipelineRunStoreOptions = {}\n): Promise<SqlitePipelineRunStore> {\n const readOnly = options.readOnly === true;\n const initialize = options.initialize !== false && !readOnly;\n const resolvedFilename = filename === \":memory:\" ? filename : path.resolve(filename);\n if (resolvedFilename !== \":memory:\" && initialize) {\n await mkdir(path.dirname(resolvedFilename), { recursive: true });\n }\n if (resolvedFilename !== \":memory:\" && !initialize) {\n try {\n await stat(resolvedFilename);\n } catch {\n throw new Error(`${resolvedFilename} is not a pipeline run store.`);\n }\n }\n const database = await openDatabase(resolvedFilename, { create: initialize, readOnly });\n try {\n const version = readStoreVersion(database);\n if (version !== 0 && version !== 3 && version !== RUN_EVENT_STORE_VERSION) {\n throw new Error(\n `Unsupported pipeline run store schema version ${version}; expected ${RUN_EVENT_STORE_VERSION}.`\n );\n }\n if (!initialize && version === 0) {\n throw new Error(`${resolvedFilename} is not a pipeline run store.`);\n }\n if (!readOnly && version === 3) {\n database.exec(`\n BEGIN IMMEDIATE;\n ALTER TABLE pipeline_run_events ADD COLUMN iteration_json TEXT;\n PRAGMA user_version = ${RUN_EVENT_STORE_VERSION};\n COMMIT;\n `);\n }\n if (initialize) {\n database.exec(RUN_EVENT_STORE_SCHEMA);\n database.exec(`PRAGMA user_version = ${RUN_EVENT_STORE_VERSION}`);\n }\n } catch (error) {\n database.close();\n throw error;\n }\n const insert = readOnly\n ? undefined\n : statement(\n database,\n `INSERT INTO pipeline_run_events (\n version, run_id, correlation_id, parent_run_id, pipeline_id, step_id, attempt_id,\n item_key, event_name, timestamp_ms, duration_ms, payload_json, error_json, iteration_json\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n );\n let closed = false;\n let exportError: Error | undefined;\n const pending: EventRow[] = [];\n\n function drain(): void {\n if (!insert || pending.length === 0) return;\n try {\n database.exec(\"BEGIN IMMEDIATE\");\n for (const row of pending) insert.run(...row);\n database.exec(\"COMMIT\");\n pending.length = 0;\n } catch (error) {\n try {\n database.exec(\"ROLLBACK\");\n } catch {}\n exportError = error instanceof Error ? error : new Error(String(error));\n throw exportError;\n }\n }\n\n return {\n export(event) {\n if (closed) throw new Error(\"Cannot append to a closed pipeline run store.\");\n if (!insert) throw new Error(\"Cannot append to a read-only pipeline run store.\");\n if (exportError) throw exportError;\n try {\n pending.push(eventRow(event));\n if (pending.length >= EXPORT_BATCH_SIZE) drain();\n } catch (error) {\n exportError = error instanceof Error ? error : new Error(String(error));\n throw exportError;\n }\n },\n flush() {\n if (exportError) throw exportError;\n drain();\n },\n async listEvents(query: PipelineRunEventQuery = {}) {\n if (closed) throw new Error(\"Cannot query a closed pipeline run store.\");\n drain();\n if (readOnly && resolvedFilename !== \":memory:\") {\n await sqliteAssertImmutableInspect(resolvedFilename);\n }\n const predicates: string[] = [];\n const params: unknown[] = [];\n if (query.afterId !== undefined) {\n predicates.push(\"id > ?\");\n params.push(query.afterId);\n }\n if (query.pipelineId !== undefined) {\n predicates.push(\"pipeline_id = ?\");\n params.push(query.pipelineId);\n }\n if (query.runId !== undefined) {\n predicates.push(\"run_id = ?\");\n params.push(query.runId);\n }\n const limit = Math.max(1, Math.min(100_000, Math.floor(query.limit ?? 20_000)));\n params.push(limit);\n const where = predicates.length > 0 ? `WHERE ${predicates.join(\" AND \")}` : \"\";\n // SAFETY: The SELECT * columns match the StoredEventRow interface exactly\n // (the table schema is owned by this module), so each result row is one.\n const rows = statement(\n database,\n `SELECT * FROM pipeline_run_events ${where} ORDER BY id ASC LIMIT ?`\n ).all(...params) as StoredEventRow[];\n if (readOnly && resolvedFilename !== \":memory:\") {\n await sqliteAssertImmutableInspect(resolvedFilename);\n }\n return rows.map(mapRow);\n },\n clearHistory() {\n if (closed) throw new Error(\"Cannot clear a closed pipeline run store.\");\n drain();\n try {\n database.exec(`\n BEGIN IMMEDIATE;\n DROP TRIGGER IF EXISTS pipeline_run_events_no_update;\n DROP TRIGGER IF EXISTS pipeline_run_events_no_delete;\n DELETE FROM pipeline_run_events;\n ${RUN_EVENT_STORE_APPEND_ONLY_TRIGGERS}\n COMMIT;\n `);\n } catch (error) {\n try {\n database.exec(\"ROLLBACK\");\n } catch {}\n throw error;\n }\n database.exec(\"VACUUM; PRAGMA wal_checkpoint(TRUNCATE);\");\n },\n close() {\n if (closed) return;\n closed = true;\n try {\n if (!exportError) drain();\n } catch {\n // exportError is recorded by drain; close still releases the handle.\n }\n database.close();\n if (exportError) throw exportError;\n },\n };\n}\n"]}
|