tubeless 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +5 -5
  2. package/dist/core/child-execution.js +8 -1
  3. package/dist/core/child-execution.js.map +1 -1
  4. package/dist/core/lifecycle.js +2 -0
  5. package/dist/core/lifecycle.js.map +1 -1
  6. package/dist/core/pipeline-execute.d.ts +2 -0
  7. package/dist/core/pipeline-execute.js +132 -395
  8. package/dist/core/pipeline-execute.js.map +1 -1
  9. package/dist/core/pipeline-plan.js +96 -34
  10. package/dist/core/pipeline-plan.js.map +1 -1
  11. package/dist/core/pipeline-run-state.d.ts +49 -0
  12. package/dist/core/pipeline-run-state.js +284 -0
  13. package/dist/core/pipeline-run-state.js.map +1 -0
  14. package/dist/core/pipeline-step-executor.d.ts +20 -0
  15. package/dist/core/pipeline-step-executor.js +52 -0
  16. package/dist/core/pipeline-step-executor.js.map +1 -0
  17. package/dist/core/pipeline-types.d.ts +11 -2
  18. package/dist/core/pipeline-types.js.map +1 -1
  19. package/dist/core/pipeline-validation.d.ts +6 -0
  20. package/dist/core/pipeline-validation.js +41 -0
  21. package/dist/core/pipeline-validation.js.map +1 -0
  22. package/dist/run-store/run-store-ndjson.js +2 -295
  23. package/dist/run-store/run-store-ndjson.js.map +1 -1
  24. package/dist/run-store/run-store-sqlite.js +91 -42
  25. package/dist/run-store/run-store-sqlite.js.map +1 -1
  26. package/dist/run-store/run-store.d.ts +3 -2
  27. package/dist/run-store/run-store.js +38 -195
  28. package/dist/run-store/run-store.js.map +1 -1
  29. package/dist/studio/run-store-ui-client-controller.d.ts +9 -0
  30. package/dist/studio/run-store-ui-client-controller.js +479 -0
  31. package/dist/studio/run-store-ui-client-controller.js.map +1 -0
  32. package/dist/studio/run-store-ui-client-form.d.ts +9 -0
  33. package/dist/studio/run-store-ui-client-form.js +44 -0
  34. package/dist/studio/run-store-ui-client-form.js.map +1 -0
  35. package/dist/studio/run-store-ui-client-model.d.ts +40 -0
  36. package/dist/studio/run-store-ui-client-model.js +211 -0
  37. package/dist/studio/run-store-ui-client-model.js.map +1 -0
  38. package/dist/studio/run-store-ui-client-source.js +1 -1
  39. package/dist/studio/run-store-ui-client-transport.d.ts +25 -0
  40. package/dist/studio/run-store-ui-client-transport.js +322 -0
  41. package/dist/studio/run-store-ui-client-transport.js.map +1 -0
  42. package/dist/studio/run-store-ui-client-views.d.ts +24 -0
  43. package/dist/studio/run-store-ui-client-views.js +676 -0
  44. package/dist/studio/run-store-ui-client-views.js.map +1 -0
  45. package/dist/studio/run-store-ui-client.d.ts +2 -14
  46. package/dist/studio/run-store-ui-client.js +7 -1360
  47. package/dist/studio/run-store-ui-client.js.map +1 -1
  48. package/dist/studio/run-store-ui-page.d.ts +1 -1
  49. package/dist/studio/run-store-ui.js +44 -25
  50. package/dist/studio/run-store-ui.js.map +1 -1
  51. package/dist/tracing/tracing-codec.d.ts +6 -0
  52. package/dist/tracing/tracing-codec.js +829 -0
  53. package/dist/tracing/tracing-codec.js.map +1 -0
  54. package/dist/tracing/tracing-constants.d.ts +4 -0
  55. package/dist/tracing/tracing-constants.js +5 -0
  56. package/dist/tracing/tracing-constants.js.map +1 -0
  57. package/dist/tracing/tracing-internal.js +116 -81
  58. package/dist/tracing/tracing-internal.js.map +1 -1
  59. package/dist/tracing/tracing-otel.js +19 -3
  60. package/dist/tracing/tracing-otel.js.map +1 -1
  61. package/dist/tracing/tracing.d.ts +149 -10
  62. package/dist/tracing/tracing.js.map +1 -1
  63. package/dist/workbench/workbench-history.js +8 -1
  64. package/dist/workbench/workbench-history.js.map +1 -1
  65. package/dist/workbench/workbench-ui.js +26 -6
  66. package/dist/workbench/workbench-ui.js.map +1 -1
  67. package/docs/README.md +21 -15
  68. package/docs/agent-guide.md +48 -35
  69. package/docs/agent-skills.md +59 -0
  70. package/docs/api-reference.md +28 -12
  71. package/docs/api-report.json +29 -13
  72. package/docs/child-pipeline-composition.md +142 -193
  73. package/docs/cli.md +83 -61
  74. package/docs/comparison.md +63 -62
  75. package/docs/concepts.md +210 -272
  76. package/docs/getting-started.md +59 -48
  77. package/docs/llms.txt +7 -4
  78. package/docs/recipes.md +21 -18
  79. package/docs/remote-step-composition.md +69 -74
  80. package/docs/studio.md +146 -71
  81. package/examples/catalog/pipelines/enrich.ts +2 -2
  82. package/examples/catalog/tubeless.project.ts +4 -2
  83. package/examples/host-embedding.ts +4 -5
  84. package/examples/remote-steps.ts +8 -5
  85. package/package.json +11 -10
  86. package/skills/tubeless/SKILL.md +83 -0
  87. package/skills/tubeless-make-pipeline/SKILL.md +126 -0
@@ -1,302 +1,9 @@
1
1
  import { open } from "node:fs/promises";
2
2
  import * as path from "node:path";
3
+ import { decodePipelineTraceEvent } from "../tracing/tracing-codec.js";
3
4
  const DEFAULT_MAX_BYTES = 64 * 1_024 * 1_024;
4
5
  const DEFAULT_MAX_EVENT_BYTES = 1 * 1_024 * 1_024;
5
6
  const DEFAULT_MAX_EVENTS = 100_000;
6
- const pipelineRunStatuses = new Set(["cancelled", "completed", "failed"]);
7
- const eventNames = new Set([
8
- "pipeline.completed",
9
- "pipeline.log",
10
- "pipeline.started",
11
- "pipeline.finalize.completed",
12
- "pipeline.finalize.failed",
13
- "pipeline.finalize.started",
14
- "step.attempted",
15
- "step.cancelled",
16
- "step.failed",
17
- "step.planned",
18
- "step.running",
19
- "step.skipped",
20
- "step.complete",
21
- ]);
22
- const errorKinds = new Set([
23
- "cancellation",
24
- "child",
25
- "definition",
26
- "finalization",
27
- "selection",
28
- "step",
29
- "validation",
30
- ]);
31
- const errorPhases = new Set([
32
- "definition",
33
- "execution",
34
- "finalization",
35
- "planning",
36
- ]);
37
- const errorCodes = new Set([
38
- "TUBELESS_CHILD_FAILED",
39
- "TUBELESS_DEFINITION_DEPENDENCY_CONTRADICTORY",
40
- "TUBELESS_DEFINITION_DEPENDENCY_CYCLE",
41
- "TUBELESS_DEFINITION_DEPENDENCY_DUPLICATE",
42
- "TUBELESS_DEFINITION_DEPENDENCY_NOT_IN_STEPS",
43
- "TUBELESS_DEFINITION_DEPENDENCY_SELF_REFERENCE",
44
- "TUBELESS_DEFINITION_FINALIZER_STEP_NOT_IN_STEPS",
45
- "TUBELESS_DEFINITION_PIPELINE_ID_BLANK",
46
- "TUBELESS_DEFINITION_OPTIONS_SCHEMA_CONFLICT",
47
- "TUBELESS_DEFINITION_STEP_ID_BLANK",
48
- "TUBELESS_DEFINITION_STEP_ID_RESERVED",
49
- "TUBELESS_DEFINITION_STEP_IDS_DUPLICATE",
50
- "TUBELESS_DEFINITION_STEP_NAME_BLANK",
51
- "TUBELESS_DEFINITION_TARGET_FINALIZER_MISMATCH",
52
- "TUBELESS_DEFINITION_TARGET_NOT_IN_STEPS",
53
- "TUBELESS_DEFINITION_TARGETS_DUPLICATE",
54
- "TUBELESS_FINALIZATION_CANCELLED",
55
- "TUBELESS_FINALIZATION_FAILED",
56
- "TUBELESS_FINAL_RESULT_VALIDATION_FAILED",
57
- "TUBELESS_OPTIONS_VALIDATION_FAILED",
58
- "TUBELESS_PLANNING_SELECTION_CONFLICT",
59
- "TUBELESS_PLANNING_STEP_SELECTION_DUPLICATE",
60
- "TUBELESS_PLANNING_STEP_SELECTION_EMPTY",
61
- "TUBELESS_PLANNING_STEP_UNKNOWN",
62
- "TUBELESS_PLANNING_TARGET_SELECTION_DUPLICATE",
63
- "TUBELESS_PLANNING_TARGET_SELECTION_EMPTY",
64
- "TUBELESS_PLANNING_TARGET_UNDECLARED",
65
- "TUBELESS_PLANNING_TARGET_UNKNOWN",
66
- "TUBELESS_RUN_CANCELLED",
67
- "TUBELESS_STEP_OUTPUT_VALIDATION_FAILED",
68
- "TUBELESS_STEP_FAILED",
69
- ]);
70
- function isRecord(value) {
71
- return value !== null && typeof value === "object" && !Array.isArray(value);
72
- }
73
- function isEventName(value) {
74
- return eventNames.has(value);
75
- }
76
- function isErrorCode(value) {
77
- return errorCodes.has(value);
78
- }
79
- function isErrorKind(value) {
80
- return errorKinds.has(value);
81
- }
82
- function isErrorPhase(value) {
83
- return errorPhases.has(value);
84
- }
85
- function requiredString(record, key) {
86
- const value = record[key];
87
- if (typeof value !== "string" || value.length === 0) {
88
- throw new Error(`${key} must be a non-empty string`);
89
- }
90
- return value;
91
- }
92
- function optionalString(record, key) {
93
- const value = record[key];
94
- if (value === undefined)
95
- return undefined;
96
- if (typeof value !== "string")
97
- throw new Error(`${key} must be a string`);
98
- return value;
99
- }
100
- function diagnosticMessage(record) {
101
- const message = optionalString(record, "message");
102
- if (message === undefined)
103
- throw new Error("message is required");
104
- return message;
105
- }
106
- function finiteNumber(record, key) {
107
- const value = record[key];
108
- if (typeof value !== "number" || !Number.isFinite(value)) {
109
- throw new Error(`${key} must be a finite number`);
110
- }
111
- return value;
112
- }
113
- function parseAttributes(value) {
114
- if (!isRecord(value))
115
- throw new Error("attributes must be an object");
116
- const attributes = {};
117
- for (const [key, attribute] of Object.entries(value)) {
118
- if (typeof attribute !== "boolean" &&
119
- typeof attribute !== "string" &&
120
- (typeof attribute !== "number" || !Number.isFinite(attribute))) {
121
- throw new Error("attribute values must be booleans, finite numbers, or strings");
122
- }
123
- attributes[key] = attribute;
124
- }
125
- return attributes;
126
- }
127
- function parseCause(value, depth = 0) {
128
- if (!isRecord(value))
129
- throw new Error("error.cause must be an object");
130
- if (depth >= 16)
131
- throw new Error("error.cause exceeds 16 levels");
132
- const cause = { message: diagnosticMessage(value) };
133
- const name = optionalString(value, "name");
134
- if (name !== undefined)
135
- cause.name = name;
136
- const sourceCode = optionalString(value, "sourceCode");
137
- if (sourceCode !== undefined)
138
- cause.sourceCode = sourceCode;
139
- if (value.cause !== undefined)
140
- cause.cause = parseCause(value.cause, depth + 1);
141
- return cause;
142
- }
143
- function parseIssues(value) {
144
- if (!Array.isArray(value))
145
- throw new Error("error.issues must be an array");
146
- return value.map((issue, issueIndex) => {
147
- if (!isRecord(issue))
148
- throw new Error(`error.issues[${issueIndex}] must be an object`);
149
- const parsed = { message: requiredString(issue, "message") };
150
- if (issue.path !== undefined) {
151
- if (!Array.isArray(issue.path) ||
152
- !issue.path.every((part) => typeof part === "string" || (typeof part === "number" && Number.isFinite(part)))) {
153
- throw new Error(`error.issues[${issueIndex}].path must contain strings or finite numbers`);
154
- }
155
- parsed.path = issue.path;
156
- }
157
- return parsed;
158
- });
159
- }
160
- function nonnegativeInteger(record, key) {
161
- const value = finiteNumber(record, key);
162
- if (!Number.isSafeInteger(value) || value < 0) {
163
- throw new Error(`error.fanOut.${key} must be a nonnegative safe integer`);
164
- }
165
- return value;
166
- }
167
- function parseFanOutCause(value, depth = 0) {
168
- if (!isRecord(value))
169
- throw new Error("error.fanOut cause must be an object");
170
- // Core emits eight cause levels followed by a truncation marker.
171
- if (depth > 8)
172
- throw new Error("error.fanOut cause exceeds its depth limit");
173
- const message = optionalString(value, "message");
174
- if (message === undefined)
175
- throw new Error("error.fanOut cause requires message");
176
- const cause = { message };
177
- for (const key of ["name", "sourceCode"]) {
178
- const text = optionalString(value, key);
179
- if (text !== undefined)
180
- cause[key] = text;
181
- }
182
- if ([cause.message, cause.name, cause.sourceCode].some((text) => text !== undefined && text.length > 1024)) {
183
- throw new Error("error.fanOut cause strings exceed 1024 code units");
184
- }
185
- if (value.cause !== undefined)
186
- cause.cause = parseFanOutCause(value.cause, depth + 1);
187
- return cause;
188
- }
189
- function parseFanOut(value) {
190
- if (!isRecord(value))
191
- throw new Error("error.fanOut must be an object");
192
- if (!Array.isArray(value.failures) || value.failures.length > 32) {
193
- throw new Error("error.fanOut.failures must be an array of at most 32 entries");
194
- }
195
- const failureCount = nonnegativeInteger(value, "failureCount");
196
- const omittedFailureCount = nonnegativeInteger(value, "omittedFailureCount");
197
- if (failureCount - value.failures.length !== omittedFailureCount) {
198
- throw new Error("error.fanOut counts do not match failures");
199
- }
200
- let previousIndex = -1;
201
- const failures = value.failures.map((failure) => {
202
- if (!isRecord(failure))
203
- throw new Error("error.fanOut failure must be an object");
204
- const index = nonnegativeInteger(failure, "index");
205
- if (index <= previousIndex)
206
- throw new Error("error.fanOut indices must be in input order");
207
- previousIndex = index;
208
- const key = optionalString(failure, "key");
209
- if (key === undefined || key.length > 1024) {
210
- throw new Error("error.fanOut key must be a string of at most 1024 code units");
211
- }
212
- if (typeof failure.keyTruncated !== "boolean" || typeof failure.cancelled !== "boolean") {
213
- throw new Error("error.fanOut keyTruncated and cancelled must be booleans");
214
- }
215
- return {
216
- index,
217
- key,
218
- keyTruncated: failure.keyTruncated,
219
- cancelled: failure.cancelled,
220
- error: parseFanOutCause(failure.error),
221
- };
222
- });
223
- const diagnostics = { failures, failureCount, omittedFailureCount };
224
- if (value.schedulerError !== undefined)
225
- diagnostics.schedulerError = parseFanOutCause(value.schedulerError);
226
- return diagnostics;
227
- }
228
- function parseError(value) {
229
- if (!isRecord(value))
230
- throw new Error("error must be an object");
231
- const code = requiredString(value, "code");
232
- const kind = requiredString(value, "kind");
233
- const phase = requiredString(value, "phase");
234
- if (!isErrorCode(code))
235
- throw new Error("error.code is unsupported");
236
- if (!isErrorKind(kind))
237
- throw new Error("error.kind is unsupported");
238
- if (!isErrorPhase(phase))
239
- throw new Error("error.phase is unsupported");
240
- const error = {
241
- code,
242
- kind,
243
- message: diagnosticMessage(value),
244
- phase,
245
- };
246
- if (value.cause !== undefined)
247
- error.cause = parseCause(value.cause);
248
- if (value.fanOut !== undefined)
249
- error.fanOut = parseFanOut(value.fanOut);
250
- if (value.issues !== undefined)
251
- error.issues = parseIssues(value.issues);
252
- const sourceCode = optionalString(value, "sourceCode");
253
- if (sourceCode !== undefined)
254
- error.sourceCode = sourceCode;
255
- const stack = optionalString(value, "stack");
256
- if (stack !== undefined)
257
- error.stack = stack;
258
- return error;
259
- }
260
- function parseEvent(value) {
261
- if (!isRecord(value))
262
- throw new Error("event must be an object");
263
- if (value.version !== 1)
264
- throw new Error("version must be 1");
265
- const name = requiredString(value, "name");
266
- if (!isEventName(name))
267
- throw new Error("name is unsupported");
268
- const event = {
269
- attributes: parseAttributes(value.attributes),
270
- name,
271
- pipelineId: requiredString(value, "pipelineId"),
272
- runId: requiredString(value, "runId"),
273
- timestampMs: finiteNumber(value, "timestampMs"),
274
- version: 1,
275
- };
276
- const attemptId = optionalString(value, "attemptId");
277
- if (attemptId !== undefined)
278
- event.attemptId = attemptId;
279
- if (value.durationMs !== undefined)
280
- event.durationMs = finiteNumber(value, "durationMs");
281
- if (value.error !== undefined)
282
- event.error = parseError(value.error);
283
- const itemKey = optionalString(value, "itemKey");
284
- if (itemKey !== undefined)
285
- event.itemKey = itemKey;
286
- const parentRunId = optionalString(value, "parentRunId");
287
- if (parentRunId !== undefined)
288
- event.parentRunId = parentRunId;
289
- const stepId = optionalString(value, "stepId");
290
- if (stepId !== undefined)
291
- event.stepId = stepId;
292
- if (event.name === "pipeline.completed") {
293
- const status = event.attributes.status;
294
- if (typeof status !== "string" || !pipelineRunStatuses.has(status)) {
295
- throw new Error("attributes.status must be cancelled, completed, or failed for pipeline.completed");
296
- }
297
- }
298
- return event;
299
- }
300
7
  function positiveInteger(value, fallback, name) {
301
8
  const resolved = value ?? fallback;
302
9
  if (!Number.isSafeInteger(resolved) || resolved <= 0) {
@@ -375,7 +82,7 @@ export async function openNdjsonPipelineRunStore(filename, options = {}) {
375
82
  throw new Error(`${resolvedFilename} line ${lineNumber} is not valid JSON.`);
376
83
  }
377
84
  try {
378
- const event = parseEvent(parsed);
85
+ const event = decodePipelineTraceEvent(parsed);
379
86
  events.push({ ...event, id: events.length });
380
87
  }
381
88
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"run-store-ndjson.js","sourceRoot":"","sources":["../../src/run-store/run-store-ndjson.ts"],"names":[],"mappings":"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","sourcesContent":["import { open } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type {\n PipelineErrorCause,\n PipelineErrorCode,\n PipelineErrorKind,\n PipelineErrorPhase,\n PipelineFanOutDiagnostics,\n PipelineValidationIssue,\n} from \"../core/pipeline.js\";\nimport type {\n PipelineRunEventQuery,\n PipelineRunEventReader,\n StoredPipelineEvent,\n} from \"./run-store.js\";\nimport type {\n PipelineTraceAttributes,\n PipelineTraceError,\n PipelineTraceEvent,\n PipelineTraceEventName,\n} from \"../tracing/tracing.js\";\n\nconst DEFAULT_MAX_BYTES = 64 * 1_024 * 1_024;\nconst DEFAULT_MAX_EVENT_BYTES = 1 * 1_024 * 1_024;\nconst DEFAULT_MAX_EVENTS = 100_000;\nconst pipelineRunStatuses: ReadonlySet<string> = new Set([\"cancelled\", \"completed\", \"failed\"]);\n\nconst eventNames: ReadonlySet<string> = new Set([\n \"pipeline.completed\",\n \"pipeline.log\",\n \"pipeline.started\",\n \"pipeline.finalize.completed\",\n \"pipeline.finalize.failed\",\n \"pipeline.finalize.started\",\n \"step.attempted\",\n \"step.cancelled\",\n \"step.failed\",\n \"step.planned\",\n \"step.running\",\n \"step.skipped\",\n \"step.complete\",\n]);\nconst errorKinds: ReadonlySet<string> = new Set([\n \"cancellation\",\n \"child\",\n \"definition\",\n \"finalization\",\n \"selection\",\n \"step\",\n \"validation\",\n]);\nconst errorPhases: ReadonlySet<string> = new Set([\n \"definition\",\n \"execution\",\n \"finalization\",\n \"planning\",\n]);\nconst errorCodes: ReadonlySet<string> = new Set([\n \"TUBELESS_CHILD_FAILED\",\n \"TUBELESS_DEFINITION_DEPENDENCY_CONTRADICTORY\",\n \"TUBELESS_DEFINITION_DEPENDENCY_CYCLE\",\n \"TUBELESS_DEFINITION_DEPENDENCY_DUPLICATE\",\n \"TUBELESS_DEFINITION_DEPENDENCY_NOT_IN_STEPS\",\n \"TUBELESS_DEFINITION_DEPENDENCY_SELF_REFERENCE\",\n \"TUBELESS_DEFINITION_FINALIZER_STEP_NOT_IN_STEPS\",\n \"TUBELESS_DEFINITION_PIPELINE_ID_BLANK\",\n \"TUBELESS_DEFINITION_OPTIONS_SCHEMA_CONFLICT\",\n \"TUBELESS_DEFINITION_STEP_ID_BLANK\",\n \"TUBELESS_DEFINITION_STEP_ID_RESERVED\",\n \"TUBELESS_DEFINITION_STEP_IDS_DUPLICATE\",\n \"TUBELESS_DEFINITION_STEP_NAME_BLANK\",\n \"TUBELESS_DEFINITION_TARGET_FINALIZER_MISMATCH\",\n \"TUBELESS_DEFINITION_TARGET_NOT_IN_STEPS\",\n \"TUBELESS_DEFINITION_TARGETS_DUPLICATE\",\n \"TUBELESS_FINALIZATION_CANCELLED\",\n \"TUBELESS_FINALIZATION_FAILED\",\n \"TUBELESS_FINAL_RESULT_VALIDATION_FAILED\",\n \"TUBELESS_OPTIONS_VALIDATION_FAILED\",\n \"TUBELESS_PLANNING_SELECTION_CONFLICT\",\n \"TUBELESS_PLANNING_STEP_SELECTION_DUPLICATE\",\n \"TUBELESS_PLANNING_STEP_SELECTION_EMPTY\",\n \"TUBELESS_PLANNING_STEP_UNKNOWN\",\n \"TUBELESS_PLANNING_TARGET_SELECTION_DUPLICATE\",\n \"TUBELESS_PLANNING_TARGET_SELECTION_EMPTY\",\n \"TUBELESS_PLANNING_TARGET_UNDECLARED\",\n \"TUBELESS_PLANNING_TARGET_UNKNOWN\",\n \"TUBELESS_RUN_CANCELLED\",\n \"TUBELESS_STEP_OUTPUT_VALIDATION_FAILED\",\n \"TUBELESS_STEP_FAILED\",\n]);\n\n/** Bounds applied while opening an untrusted NDJSON trace artifact. */\nexport interface OpenNdjsonPipelineRunStoreOptions {\n /** Maximum artifact size. Defaults to 64 MiB. */\n readonly maxBytes?: number;\n /** Maximum size of one event line. Defaults to 1 MiB. */\n readonly maxEventBytes?: number;\n /** Maximum non-blank event lines. Defaults to 100,000. */\n readonly maxEvents?: number;\n}\n\n/** A read-only, in-memory view of one validated NDJSON trace artifact. */\nexport interface NdjsonPipelineRunStore extends PipelineRunEventReader {}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isEventName(value: string): value is PipelineTraceEventName {\n return eventNames.has(value);\n}\n\nfunction isErrorCode(value: string): value is PipelineErrorCode {\n return errorCodes.has(value);\n}\n\nfunction isErrorKind(value: string): value is PipelineErrorKind {\n return errorKinds.has(value);\n}\n\nfunction isErrorPhase(value: string): value is PipelineErrorPhase {\n return errorPhases.has(value);\n}\n\nfunction requiredString(record: Record<string, unknown>, key: string): string {\n const value = record[key];\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(`${key} must be a non-empty string`);\n }\n return value;\n}\n\nfunction optionalString(record: Record<string, unknown>, key: string): string | undefined {\n const value = record[key];\n if (value === undefined) return undefined;\n if (typeof value !== \"string\") throw new Error(`${key} must be a string`);\n return value;\n}\n\nfunction diagnosticMessage(record: Record<string, unknown>): string {\n const message = optionalString(record, \"message\");\n if (message === undefined) throw new Error(\"message is required\");\n return message;\n}\n\nfunction finiteNumber(record: Record<string, unknown>, key: string): number {\n const value = record[key];\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new Error(`${key} must be a finite number`);\n }\n return value;\n}\n\nfunction parseAttributes(value: unknown): PipelineTraceAttributes {\n if (!isRecord(value)) throw new Error(\"attributes must be an object\");\n const attributes: Record<string, boolean | number | string> = {};\n for (const [key, attribute] of Object.entries(value)) {\n if (\n typeof attribute !== \"boolean\" &&\n typeof attribute !== \"string\" &&\n (typeof attribute !== \"number\" || !Number.isFinite(attribute))\n ) {\n throw new Error(\"attribute values must be booleans, finite numbers, or strings\");\n }\n attributes[key] = attribute;\n }\n return attributes;\n}\n\nfunction parseCause(value: unknown, depth = 0): PipelineErrorCause {\n if (!isRecord(value)) throw new Error(\"error.cause must be an object\");\n if (depth >= 16) throw new Error(\"error.cause exceeds 16 levels\");\n const cause: PipelineErrorCause = { message: diagnosticMessage(value) };\n const name = optionalString(value, \"name\");\n if (name !== undefined) cause.name = name;\n const sourceCode = optionalString(value, \"sourceCode\");\n if (sourceCode !== undefined) cause.sourceCode = sourceCode;\n if (value.cause !== undefined) cause.cause = parseCause(value.cause, depth + 1);\n return cause;\n}\n\nfunction parseIssues(value: unknown): readonly PipelineValidationIssue[] {\n if (!Array.isArray(value)) throw new Error(\"error.issues must be an array\");\n return value.map((issue, issueIndex) => {\n if (!isRecord(issue)) throw new Error(`error.issues[${issueIndex}] must be an object`);\n const parsed: PipelineValidationIssue = { message: requiredString(issue, \"message\") };\n if (issue.path !== undefined) {\n if (\n !Array.isArray(issue.path) ||\n !issue.path.every(\n (part) => typeof part === \"string\" || (typeof part === \"number\" && Number.isFinite(part))\n )\n ) {\n throw new Error(`error.issues[${issueIndex}].path must contain strings or finite numbers`);\n }\n parsed.path = issue.path;\n }\n return parsed;\n });\n}\n\nfunction nonnegativeInteger(record: Record<string, unknown>, key: string): number {\n const value = finiteNumber(record, key);\n if (!Number.isSafeInteger(value) || value < 0) {\n throw new Error(`error.fanOut.${key} must be a nonnegative safe integer`);\n }\n return value;\n}\n\nfunction parseFanOutCause(value: unknown, depth = 0): PipelineErrorCause {\n if (!isRecord(value)) throw new Error(\"error.fanOut cause must be an object\");\n // Core emits eight cause levels followed by a truncation marker.\n if (depth > 8) throw new Error(\"error.fanOut cause exceeds its depth limit\");\n const message = optionalString(value, \"message\");\n if (message === undefined) throw new Error(\"error.fanOut cause requires message\");\n const cause: PipelineErrorCause = { message };\n for (const key of [\"name\", \"sourceCode\"] as const) {\n const text = optionalString(value, key);\n if (text !== undefined) cause[key] = text;\n }\n if (\n [cause.message, cause.name, cause.sourceCode].some(\n (text) => text !== undefined && text.length > 1024\n )\n ) {\n throw new Error(\"error.fanOut cause strings exceed 1024 code units\");\n }\n if (value.cause !== undefined) cause.cause = parseFanOutCause(value.cause, depth + 1);\n return cause;\n}\n\nfunction parseFanOut(value: unknown): PipelineFanOutDiagnostics {\n if (!isRecord(value)) throw new Error(\"error.fanOut must be an object\");\n if (!Array.isArray(value.failures) || value.failures.length > 32) {\n throw new Error(\"error.fanOut.failures must be an array of at most 32 entries\");\n }\n const failureCount = nonnegativeInteger(value, \"failureCount\");\n const omittedFailureCount = nonnegativeInteger(value, \"omittedFailureCount\");\n if (failureCount - value.failures.length !== omittedFailureCount) {\n throw new Error(\"error.fanOut counts do not match failures\");\n }\n let previousIndex = -1;\n const failures = value.failures.map((failure) => {\n if (!isRecord(failure)) throw new Error(\"error.fanOut failure must be an object\");\n const index = nonnegativeInteger(failure, \"index\");\n if (index <= previousIndex) throw new Error(\"error.fanOut indices must be in input order\");\n previousIndex = index;\n const key = optionalString(failure, \"key\");\n if (key === undefined || key.length > 1024) {\n throw new Error(\"error.fanOut key must be a string of at most 1024 code units\");\n }\n if (typeof failure.keyTruncated !== \"boolean\" || typeof failure.cancelled !== \"boolean\") {\n throw new Error(\"error.fanOut keyTruncated and cancelled must be booleans\");\n }\n return {\n index,\n key,\n keyTruncated: failure.keyTruncated,\n cancelled: failure.cancelled,\n error: parseFanOutCause(failure.error),\n };\n });\n const diagnostics: PipelineFanOutDiagnostics = { failures, failureCount, omittedFailureCount };\n if (value.schedulerError !== undefined)\n diagnostics.schedulerError = parseFanOutCause(value.schedulerError);\n return diagnostics;\n}\n\nfunction parseError(value: unknown): PipelineTraceError {\n if (!isRecord(value)) throw new Error(\"error must be an object\");\n const code = requiredString(value, \"code\");\n const kind = requiredString(value, \"kind\");\n const phase = requiredString(value, \"phase\");\n if (!isErrorCode(code)) throw new Error(\"error.code is unsupported\");\n if (!isErrorKind(kind)) throw new Error(\"error.kind is unsupported\");\n if (!isErrorPhase(phase)) throw new Error(\"error.phase is unsupported\");\n const error: PipelineTraceError = {\n code,\n kind,\n message: diagnosticMessage(value),\n phase,\n };\n if (value.cause !== undefined) error.cause = parseCause(value.cause);\n if (value.fanOut !== undefined) error.fanOut = parseFanOut(value.fanOut);\n if (value.issues !== undefined) error.issues = parseIssues(value.issues);\n const sourceCode = optionalString(value, \"sourceCode\");\n if (sourceCode !== undefined) error.sourceCode = sourceCode;\n const stack = optionalString(value, \"stack\");\n if (stack !== undefined) error.stack = stack;\n return error;\n}\n\nfunction parseEvent(value: unknown): PipelineTraceEvent {\n if (!isRecord(value)) throw new Error(\"event must be an object\");\n if (value.version !== 1) throw new Error(\"version must be 1\");\n const name = requiredString(value, \"name\");\n if (!isEventName(name)) throw new Error(\"name is unsupported\");\n const event: PipelineTraceEvent = {\n attributes: parseAttributes(value.attributes),\n name,\n pipelineId: requiredString(value, \"pipelineId\"),\n runId: requiredString(value, \"runId\"),\n timestampMs: finiteNumber(value, \"timestampMs\"),\n version: 1,\n };\n const attemptId = optionalString(value, \"attemptId\");\n if (attemptId !== undefined) event.attemptId = attemptId;\n if (value.durationMs !== undefined) event.durationMs = finiteNumber(value, \"durationMs\");\n if (value.error !== undefined) event.error = parseError(value.error);\n const itemKey = optionalString(value, \"itemKey\");\n if (itemKey !== undefined) event.itemKey = itemKey;\n const parentRunId = optionalString(value, \"parentRunId\");\n if (parentRunId !== undefined) event.parentRunId = parentRunId;\n const stepId = optionalString(value, \"stepId\");\n if (stepId !== undefined) event.stepId = stepId;\n if (event.name === \"pipeline.completed\") {\n const status = event.attributes.status;\n if (typeof status !== \"string\" || !pipelineRunStatuses.has(status)) {\n throw new Error(\n \"attributes.status must be cancelled, completed, or failed for pipeline.completed\"\n );\n }\n }\n return event;\n}\n\nfunction positiveInteger(value: number | undefined, fallback: number, name: string): number {\n const resolved = value ?? fallback;\n if (!Number.isSafeInteger(resolved) || resolved <= 0) {\n throw new Error(`${name} must be a positive safe integer.`);\n }\n return resolved;\n}\n\n/**\n * Open and validate an NDJSON trace without modifying it. Events receive\n * zero-based, store-local ids in file order. The file is fully read and closed\n * during this call, so later filesystem changes cannot alter the returned view.\n */\nexport async function openNdjsonPipelineRunStore(\n filename: string,\n options: OpenNdjsonPipelineRunStoreOptions = {}\n): Promise<NdjsonPipelineRunStore> {\n const resolvedFilename = path.resolve(filename);\n const maxBytes = positiveInteger(options.maxBytes, DEFAULT_MAX_BYTES, \"maxBytes\");\n const maxEventBytes = positiveInteger(\n options.maxEventBytes,\n DEFAULT_MAX_EVENT_BYTES,\n \"maxEventBytes\"\n );\n const maxEvents = positiveInteger(options.maxEvents, DEFAULT_MAX_EVENTS, \"maxEvents\");\n const handle = await open(resolvedFilename, \"r\");\n let contents: Buffer;\n try {\n const fileStat = await handle.stat();\n if (!fileStat.isFile()) throw new Error(`${resolvedFilename} is not a regular file.`);\n if (fileStat.size > maxBytes) {\n throw new Error(`${resolvedFilename} exceeds the ${maxBytes}-byte NDJSON trace limit.`);\n }\n const chunks: Buffer[] = [];\n let byteLength = 0;\n for await (const chunk of handle.createReadStream({ autoClose: false })) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n byteLength += buffer.byteLength;\n if (byteLength > maxBytes) {\n throw new Error(`${resolvedFilename} exceeds the ${maxBytes}-byte NDJSON trace limit.`);\n }\n chunks.push(buffer);\n }\n contents = Buffer.concat(chunks, byteLength);\n } finally {\n await handle.close();\n }\n\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n const events: StoredPipelineEvent[] = [];\n let lineNumber = 0;\n let lineStart = 0;\n for (let cursor = 0; cursor <= contents.length; cursor += 1) {\n if (cursor < contents.length && contents[cursor] !== 0x0a) continue;\n lineNumber += 1;\n let lineEnd = cursor;\n if (lineEnd > lineStart && contents[lineEnd - 1] === 0x0d) lineEnd -= 1;\n const byteLength = lineEnd - lineStart;\n if (byteLength > maxEventBytes) {\n throw new Error(\n `${resolvedFilename} line ${lineNumber} exceeds the ${maxEventBytes}-byte event limit.`\n );\n }\n const line = contents.subarray(lineStart, lineEnd);\n lineStart = cursor + 1;\n if (line.every((byte) => byte === 0x20 || byte === 0x09)) continue;\n if (events.length >= maxEvents) {\n throw new Error(`${resolvedFilename} exceeds the ${maxEvents}-event NDJSON trace limit.`);\n }\n let decoded: string;\n try {\n decoded = decoder.decode(line);\n } catch {\n throw new Error(`${resolvedFilename} line ${lineNumber} is not valid UTF-8.`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n throw new Error(`${resolvedFilename} line ${lineNumber} is not valid JSON.`);\n }\n try {\n const event = parseEvent(parsed);\n events.push({ ...event, id: events.length });\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"event is invalid\";\n throw new Error(`${resolvedFilename} line ${lineNumber} is invalid: ${detail}.`);\n }\n }\n\n let closed = false;\n return {\n close() {\n closed = true;\n },\n async listEvents(query: PipelineRunEventQuery = {}) {\n if (closed) throw new Error(\"Cannot query a closed NDJSON pipeline run store.\");\n const limit = Math.max(1, Math.min(100_000, Math.floor(query.limit ?? 20_000)));\n return events\n .filter(\n (event) =>\n (query.afterId === undefined || event.id > query.afterId) &&\n (query.pipelineId === undefined || event.pipelineId === query.pipelineId) &&\n (query.runId === undefined || event.runId === query.runId)\n )\n .slice(0, limit)\n .map((event) => structuredClone(event));\n },\n };\n}\n"]}
1
+ {"version":3,"file":"run-store-ndjson.js","sourceRoot":"","sources":["../../src/run-store/run-store-ndjson.ts"],"names":[],"mappings":"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","sourcesContent":["import { open } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type {\n PipelineRunEventQuery,\n PipelineRunEventReader,\n StoredPipelineEvent,\n} from \"./run-store.js\";\nimport { decodePipelineTraceEvent } from \"../tracing/tracing-codec.js\";\n\nconst DEFAULT_MAX_BYTES = 64 * 1_024 * 1_024;\nconst DEFAULT_MAX_EVENT_BYTES = 1 * 1_024 * 1_024;\nconst DEFAULT_MAX_EVENTS = 100_000;\n\n/** Bounds applied while opening an untrusted NDJSON trace artifact. */\nexport interface OpenNdjsonPipelineRunStoreOptions {\n /** Maximum artifact size. Defaults to 64 MiB. */\n readonly maxBytes?: number;\n /** Maximum size of one event line. Defaults to 1 MiB. */\n readonly maxEventBytes?: number;\n /** Maximum non-blank event lines. Defaults to 100,000. */\n readonly maxEvents?: number;\n}\n\n/** A read-only, in-memory view of one validated NDJSON trace artifact. */\nexport interface NdjsonPipelineRunStore extends PipelineRunEventReader {}\n\nfunction positiveInteger(value: number | undefined, fallback: number, name: string): number {\n const resolved = value ?? fallback;\n if (!Number.isSafeInteger(resolved) || resolved <= 0) {\n throw new Error(`${name} must be a positive safe integer.`);\n }\n return resolved;\n}\n\n/**\n * Open and validate an NDJSON trace without modifying it. Events receive\n * zero-based, store-local ids in file order. The file is fully read and closed\n * during this call, so later filesystem changes cannot alter the returned view.\n */\nexport async function openNdjsonPipelineRunStore(\n filename: string,\n options: OpenNdjsonPipelineRunStoreOptions = {}\n): Promise<NdjsonPipelineRunStore> {\n const resolvedFilename = path.resolve(filename);\n const maxBytes = positiveInteger(options.maxBytes, DEFAULT_MAX_BYTES, \"maxBytes\");\n const maxEventBytes = positiveInteger(\n options.maxEventBytes,\n DEFAULT_MAX_EVENT_BYTES,\n \"maxEventBytes\"\n );\n const maxEvents = positiveInteger(options.maxEvents, DEFAULT_MAX_EVENTS, \"maxEvents\");\n const handle = await open(resolvedFilename, \"r\");\n let contents: Buffer;\n try {\n const fileStat = await handle.stat();\n if (!fileStat.isFile()) throw new Error(`${resolvedFilename} is not a regular file.`);\n if (fileStat.size > maxBytes) {\n throw new Error(`${resolvedFilename} exceeds the ${maxBytes}-byte NDJSON trace limit.`);\n }\n const chunks: Buffer[] = [];\n let byteLength = 0;\n for await (const chunk of handle.createReadStream({ autoClose: false })) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n byteLength += buffer.byteLength;\n if (byteLength > maxBytes) {\n throw new Error(`${resolvedFilename} exceeds the ${maxBytes}-byte NDJSON trace limit.`);\n }\n chunks.push(buffer);\n }\n contents = Buffer.concat(chunks, byteLength);\n } finally {\n await handle.close();\n }\n\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n const events: StoredPipelineEvent[] = [];\n let lineNumber = 0;\n let lineStart = 0;\n for (let cursor = 0; cursor <= contents.length; cursor += 1) {\n if (cursor < contents.length && contents[cursor] !== 0x0a) continue;\n lineNumber += 1;\n let lineEnd = cursor;\n if (lineEnd > lineStart && contents[lineEnd - 1] === 0x0d) lineEnd -= 1;\n const byteLength = lineEnd - lineStart;\n if (byteLength > maxEventBytes) {\n throw new Error(\n `${resolvedFilename} line ${lineNumber} exceeds the ${maxEventBytes}-byte event limit.`\n );\n }\n const line = contents.subarray(lineStart, lineEnd);\n lineStart = cursor + 1;\n if (line.every((byte) => byte === 0x20 || byte === 0x09)) continue;\n if (events.length >= maxEvents) {\n throw new Error(`${resolvedFilename} exceeds the ${maxEvents}-event NDJSON trace limit.`);\n }\n let decoded: string;\n try {\n decoded = decoder.decode(line);\n } catch {\n throw new Error(`${resolvedFilename} line ${lineNumber} is not valid UTF-8.`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n throw new Error(`${resolvedFilename} line ${lineNumber} is not valid JSON.`);\n }\n try {\n const event = decodePipelineTraceEvent(parsed);\n events.push({ ...event, id: events.length });\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"event is invalid\";\n throw new Error(`${resolvedFilename} line ${lineNumber} is invalid: ${detail}.`);\n }\n }\n\n let closed = false;\n return {\n close() {\n closed = true;\n },\n async listEvents(query: PipelineRunEventQuery = {}) {\n if (closed) throw new Error(\"Cannot query a closed NDJSON pipeline run store.\");\n const limit = Math.max(1, Math.min(100_000, Math.floor(query.limit ?? 20_000)));\n return events\n .filter(\n (event) =>\n (query.afterId === undefined || event.id > query.afterId) &&\n (query.pipelineId === undefined || event.pipelineId === query.pipelineId) &&\n (query.runId === undefined || event.runId === query.runId)\n )\n .slice(0, limit)\n .map((event) => structuredClone(event));\n },\n };\n}\n"]}
@@ -1,23 +1,27 @@
1
1
  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
- const RUN_EVENT_STORE_VERSION = 1;
4
+ import { decodePipelineTraceEvent } from "../tracing/tracing-codec.js";
5
+ const RUN_EVENT_STORE_VERSION = 2;
6
+ const LEGACY_RUN_EVENT_STORE_VERSION = 1;
5
7
  /** Larger batches mean fewer commits but more tail loss on crash. */
6
8
  const EXPORT_BATCH_SIZE = 64;
7
9
  function eventRow(event) {
10
+ const canonical = decodePipelineTraceEvent(event);
8
11
  return [
9
- event.version,
10
- event.runId,
11
- event.parentRunId ?? null,
12
- event.pipelineId,
13
- event.stepId ?? null,
14
- event.attemptId ?? null,
15
- event.itemKey ?? null,
16
- event.name,
17
- event.timestampMs,
18
- event.durationMs ?? null,
19
- JSON.stringify(event.attributes),
20
- event.error ? JSON.stringify(event.error) : null,
12
+ canonical.version,
13
+ canonical.runId,
14
+ canonical.correlationId ?? null,
15
+ canonical.parentRunId ?? null,
16
+ canonical.pipelineId,
17
+ "stepId" in canonical ? (canonical.stepId ?? null) : null,
18
+ "attemptId" in canonical ? (canonical.attemptId ?? null) : null,
19
+ canonical.itemKey ?? null,
20
+ canonical.name,
21
+ canonical.timestampMs,
22
+ "durationMs" in canonical ? (canonical.durationMs ?? null) : null,
23
+ JSON.stringify(canonical.payload),
24
+ "error" in canonical && canonical.error ? JSON.stringify(canonical.error) : null,
21
25
  ];
22
26
  }
23
27
  const RUN_EVENT_STORE_APPEND_ONLY_TRIGGERS = `
@@ -42,6 +46,7 @@ const RUN_EVENT_STORE_SCHEMA = `
42
46
  id INTEGER PRIMARY KEY AUTOINCREMENT,
43
47
  version INTEGER NOT NULL,
44
48
  run_id TEXT NOT NULL,
49
+ correlation_id TEXT,
45
50
  parent_run_id TEXT,
46
51
  pipeline_id TEXT NOT NULL,
47
52
  step_id TEXT,
@@ -69,6 +74,12 @@ function statement(database, sql) {
69
74
  throw new Error("The current SQLite runtime does not support prepared queries.");
70
75
  return prepared;
71
76
  }
77
+ function readStoreVersion(database) {
78
+ // SAFETY: `PRAGMA user_version` always returns a single row with a
79
+ // `user_version` column, so the first result row matches this shape.
80
+ const versionRow = statement(database, "PRAGMA user_version").all()[0];
81
+ return Number(versionRow?.user_version ?? 0);
82
+ }
72
83
  async function loadSqliteModule() {
73
84
  const specifier = "Bun" in globalThis ? "bun:sqlite" : "node:sqlite";
74
85
  // SAFETY: The dynamic import resolves to the built-in sqlite module whose
@@ -190,33 +201,29 @@ function openReadableDatabase(Database, filename, options) {
190
201
  }
191
202
  }
192
203
  function mapRow(row) {
193
- const event = {
194
- // SAFETY: attributes_json was written by this store via JSON.stringify of a
195
- // PipelineTraceEvent["attributes"] value, so parsing yields the same shape.
196
- attributes: JSON.parse(row.attributes_json),
197
- id: Number(row.id),
204
+ const encoded = {
198
205
  name: row.event_name,
199
206
  pipelineId: row.pipeline_id,
200
207
  runId: row.run_id,
201
208
  timestampMs: Number(row.timestamp_ms),
202
209
  version: row.version,
203
210
  };
211
+ encoded[row.version === 1 ? "attributes" : "payload"] = JSON.parse(row.attributes_json);
204
212
  if (row.attempt_id)
205
- event.attemptId = row.attempt_id;
213
+ encoded.attemptId = row.attempt_id;
214
+ if (row.correlation_id != null)
215
+ encoded.correlationId = row.correlation_id;
206
216
  if (row.duration_ms !== null)
207
- event.durationMs = Number(row.duration_ms);
208
- if (row.error_json) {
209
- // SAFETY: error_json was written by this store via JSON.stringify of a
210
- // NonNullable<PipelineTraceEvent["error"]> value, so parsing yields the same shape.
211
- event.error = JSON.parse(row.error_json);
212
- }
217
+ encoded.durationMs = Number(row.duration_ms);
218
+ if (row.error_json)
219
+ encoded.error = JSON.parse(row.error_json);
213
220
  if (row.item_key)
214
- event.itemKey = row.item_key;
221
+ encoded.itemKey = row.item_key;
215
222
  if (row.parent_run_id)
216
- event.parentRunId = row.parent_run_id;
223
+ encoded.parentRunId = row.parent_run_id;
217
224
  if (row.step_id)
218
- event.stepId = row.step_id;
219
- return event;
225
+ encoded.stepId = row.step_id;
226
+ return { ...decodePipelineTraceEvent(encoded), id: Number(row.id) };
220
227
  }
221
228
  /**
222
229
  * Open the optional local SQLite trace store used by `tubeless run --store`,
@@ -242,30 +249,66 @@ export async function openSqlitePipelineRunStore(filename, options = {}) {
242
249
  }
243
250
  }
244
251
  const database = await openDatabase(resolvedFilename, { create: initialize, readOnly });
252
+ let storeVersion = 0;
245
253
  try {
246
- // SAFETY: `PRAGMA user_version` always returns a single row with a
247
- // `user_version` column, so the first result row matches this shape.
248
- const versionRow = statement(database, "PRAGMA user_version").all()[0];
249
- const version = Number(versionRow?.user_version ?? 0);
250
- if (version !== 0 && version !== RUN_EVENT_STORE_VERSION) {
254
+ const version = readStoreVersion(database);
255
+ storeVersion = version;
256
+ if (version !== 0 &&
257
+ version !== LEGACY_RUN_EVENT_STORE_VERSION &&
258
+ version !== RUN_EVENT_STORE_VERSION) {
251
259
  throw new Error(`Unsupported pipeline run store schema version ${version}; expected ${RUN_EVENT_STORE_VERSION}.`);
252
260
  }
253
- if (!initialize && version !== RUN_EVENT_STORE_VERSION) {
261
+ if (!initialize && version === 0) {
254
262
  throw new Error(`${resolvedFilename} is not a pipeline run store.`);
255
263
  }
256
- if (initialize) {
264
+ if (!readOnly && version === LEGACY_RUN_EVENT_STORE_VERSION) {
265
+ database.exec("PRAGMA busy_timeout = 5000");
266
+ database.exec("BEGIN IMMEDIATE");
267
+ try {
268
+ const lockedVersion = readStoreVersion(database);
269
+ if (lockedVersion === LEGACY_RUN_EVENT_STORE_VERSION) {
270
+ database.exec("ALTER TABLE pipeline_run_events ADD COLUMN correlation_id TEXT");
271
+ database.exec(`PRAGMA user_version = ${RUN_EVENT_STORE_VERSION}`);
272
+ storeVersion = RUN_EVENT_STORE_VERSION;
273
+ }
274
+ else if (lockedVersion === RUN_EVENT_STORE_VERSION) {
275
+ storeVersion = lockedVersion;
276
+ }
277
+ else {
278
+ throw new Error(`Unsupported pipeline run store schema version ${lockedVersion}; expected ${RUN_EVENT_STORE_VERSION}.`);
279
+ }
280
+ database.exec("COMMIT");
281
+ }
282
+ catch (error) {
283
+ try {
284
+ database.exec("ROLLBACK");
285
+ }
286
+ catch {
287
+ // Preserve the migration failure when SQLite already ended the transaction.
288
+ }
289
+ throw error;
290
+ }
291
+ }
292
+ else if (initialize) {
257
293
  database.exec(RUN_EVENT_STORE_SCHEMA);
258
294
  database.exec(`PRAGMA user_version = ${RUN_EVENT_STORE_VERSION}`);
295
+ storeVersion = RUN_EVENT_STORE_VERSION;
259
296
  }
260
297
  }
261
298
  catch (error) {
262
299
  database.close();
263
300
  throw error;
264
301
  }
265
- const insert = statement(database, `INSERT INTO pipeline_run_events (
266
- version, run_id, parent_run_id, pipeline_id, step_id, attempt_id, item_key,
267
- event_name, timestamp_ms, duration_ms, attributes_json, error_json
268
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
302
+ const legacyReadOnly = readOnly && storeVersion === LEGACY_RUN_EVENT_STORE_VERSION;
303
+ const insert = statement(database, legacyReadOnly
304
+ ? `INSERT INTO pipeline_run_events (
305
+ version, run_id, parent_run_id, pipeline_id, step_id, attempt_id, item_key,
306
+ event_name, timestamp_ms, duration_ms, attributes_json, error_json
307
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
308
+ : `INSERT INTO pipeline_run_events (
309
+ version, run_id, correlation_id, parent_run_id, pipeline_id, step_id, attempt_id,
310
+ item_key, event_name, timestamp_ms, duration_ms, attributes_json, error_json
311
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
269
312
  let closed = false;
270
313
  let exportError;
271
314
  const pending = [];
@@ -274,8 +317,14 @@ export async function openSqlitePipelineRunStore(filename, options = {}) {
274
317
  return;
275
318
  try {
276
319
  database.exec("BEGIN IMMEDIATE");
277
- for (const row of pending)
278
- insert.run(...row);
320
+ for (const row of pending) {
321
+ if (legacyReadOnly) {
322
+ const [version, runId, , ...rest] = row;
323
+ insert.run(version, runId, ...rest);
324
+ }
325
+ else
326
+ insert.run(...row);
327
+ }
279
328
  database.exec("COMMIT");
280
329
  pending.length = 0;
281
330
  }