@qaick/runner 0.32.2 → 0.32.4
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/package-dist/package.json +5 -5
- package/package-dist/src/demo/RunnerWalkthrough.js +4 -4
- package/package-dist/src/demo/RunnerWalkthrough.js.map +1 -1
- package/package-dist/src/runner/RunnerModule.d.ts +5 -3
- package/package-dist/src/runner/RunnerModule.d.ts.map +1 -1
- package/package-dist/src/runner/RunnerModule.js +241 -65
- package/package-dist/src/runner/RunnerModule.js.map +1 -1
- package/package-dist/src/runner/catalog.js +1 -1
- package/package-dist/src/runner/execution-context.d.ts +1 -1
- package/package-dist/src/runner/execution-context.d.ts.map +1 -1
- package/package-dist/src/runner/execution-context.js +3 -3
- package/package-dist/src/runner/execution-context.js.map +1 -1
- package/package-dist/src/runner/hierarchy-presentation.d.ts +20 -0
- package/package-dist/src/runner/hierarchy-presentation.d.ts.map +1 -0
- package/package-dist/src/runner/hierarchy-presentation.js +47 -0
- package/package-dist/src/runner/hierarchy-presentation.js.map +1 -0
- package/package-dist/src/runner/history.d.ts +20 -1
- package/package-dist/src/runner/history.d.ts.map +1 -1
- package/package-dist/src/runner/history.js +14 -0
- package/package-dist/src/runner/history.js.map +1 -1
- package/package-dist/src/runner/invocation-inputs.d.ts +6 -2
- package/package-dist/src/runner/invocation-inputs.d.ts.map +1 -1
- package/package-dist/src/runner/invocation-inputs.js.map +1 -1
- package/package-dist/src/runner/loop-presentation.d.ts +4 -4
- package/package-dist/src/runner/loop-presentation.d.ts.map +1 -1
- package/package-dist/src/runner/loop-presentation.js +4 -4
- package/package-dist/src/runner/loop-presentation.js.map +1 -1
- package/package-dist/src/runner/presentation-state.d.ts +19 -4
- package/package-dist/src/runner/presentation-state.d.ts.map +1 -1
- package/package-dist/src/runner/presentation-state.js +68 -3
- package/package-dist/src/runner/presentation-state.js.map +1 -1
- package/package-dist/src/runner/runtime-client-adapter.d.ts +1 -1
- package/package-dist/src/runner/studio-execution-document.d.ts +4 -0
- package/package-dist/src/runner/studio-execution-document.d.ts.map +1 -0
- package/package-dist/src/runner/studio-execution-document.js +239 -0
- package/package-dist/src/runner/studio-execution-document.js.map +1 -0
- package/package-dist/src/runner/studio-hierarchy-loader.js +2 -2
- package/package-dist/src/runner/types.d.ts +26 -6
- package/package-dist/src/runner/types.d.ts.map +1 -1
- package/package-dist/styles.css +114 -43
- package/package.json +5 -5
|
@@ -3,9 +3,10 @@ import { commandLinks, runnerMenus } from "../shared/application-menu.js";
|
|
|
3
3
|
import { ThemeToggle } from "../ThemeToggle.js";
|
|
4
4
|
import { HeaderHelp } from "./HeaderHelp.js";
|
|
5
5
|
import { ScreenshotButton } from "../ScreenshotButton.js";
|
|
6
|
+
import { StructuralIcon, hierarchyGuides, HierarchyGuides } from "./hierarchy-presentation.js";
|
|
6
7
|
import { invocationBranches } from "./invocation-branches.js";
|
|
7
8
|
import { loopPresentation } from "./loop-presentation.js";
|
|
8
|
-
import {
|
|
9
|
+
import { rowPresentation, scopeIncludes, eventPresentationEvidence } from "./presentation-state.js";
|
|
9
10
|
import { useEffect, useRef, useState } from 'react';
|
|
10
11
|
import { checkoutNestedPackage, checkoutPackage, checkoutProjectPackage } from "./fixture.js";
|
|
11
12
|
import { PackageLoadError } from "./package-loader.js";
|
|
@@ -17,12 +18,30 @@ import { RUNNER_BUILD_LABEL, RUNNER_UI_VERSION } from "../build-info.js";
|
|
|
17
18
|
import { collectSecretValues, redactExecutionEvent, redactExecutionResult, redactKeyValuePairs } from "./redaction.js";
|
|
18
19
|
import { createResultExport } from "./result-export.js";
|
|
19
20
|
import { inspectDeclaredInputs, inputDrafts, resolvedArguments } from "./invocation-inputs.js";
|
|
20
|
-
import { persistRunHistory } from "./history.js";
|
|
21
|
+
import { persistRunHistory, captureHistoryStructure, assertHistoryIdentity } from "./history.js";
|
|
22
|
+
import { prepareStudioDocument } from "./studio-execution-document.js";
|
|
21
23
|
const statusGlyph = { pending: '', 'not executed': '', running: '', finished: '✓', failed: '✕', waiting: '◷', skipped: '–', cancelled: '⊘' };
|
|
22
24
|
const HISTORY_KEY = 'qaick-runner.history.v1';
|
|
23
25
|
const HISTORY_RETENTION_KEY = 'qaick-runner.history-retention.v1';
|
|
24
26
|
const IMPORTED_PACKAGE_KEY = 'qaick-runner.imported-package.v1';
|
|
25
27
|
const THEME_STORAGE_KEY = 'qaick-studio-theme';
|
|
28
|
+
const INSPECTOR_WIDTH_KEY = 'qaick-runner.inspector-width.v1';
|
|
29
|
+
const INSPECTOR_MIN_WIDTH = 280;
|
|
30
|
+
const INSPECTOR_MAX_WIDTH = 640;
|
|
31
|
+
const inspectorWidthWithinBounds = (width) => Math.max(INSPECTOR_MIN_WIDTH, Math.min(INSPECTOR_MAX_WIDTH, width));
|
|
32
|
+
function readInspectorWidth() {
|
|
33
|
+
try {
|
|
34
|
+
const value = Number(localStorage.getItem(INSPECTOR_WIDTH_KEY));
|
|
35
|
+
return Number.isFinite(value) && value > 0 ? inspectorWidthWithinBounds(value) : 320;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return 320;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function saveInspectorWidth(width) { try {
|
|
42
|
+
localStorage.setItem(INSPECTOR_WIDTH_KEY, String(width));
|
|
43
|
+
}
|
|
44
|
+
catch { /* Resizing still works when preferences cannot be stored. */ } }
|
|
26
45
|
function readRunHistory() { try {
|
|
27
46
|
return JSON.parse(localStorage.getItem(HISTORY_KEY) ?? '[]');
|
|
28
47
|
}
|
|
@@ -63,12 +82,68 @@ function standaloneDemoRequested() { return typeof window !== 'undefined' && new
|
|
|
63
82
|
function RunnerApp({ client, onExit, applicationMenu }) {
|
|
64
83
|
const [theme, setTheme] = useState(readTheme);
|
|
65
84
|
const [showAbout, setShowAbout] = useState(false);
|
|
66
|
-
const [
|
|
85
|
+
const [inspectorWidth, setInspectorWidth] = useState(readInspectorWidth);
|
|
86
|
+
const inspectorDrag = useRef(undefined);
|
|
87
|
+
function startInspectorResize(event) {
|
|
88
|
+
if (event.button !== 0)
|
|
89
|
+
return;
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
92
|
+
inspectorDrag.current = { pointerId: event.pointerId, startX: event.clientX, startWidth: inspectorWidth, width: inspectorWidth };
|
|
93
|
+
}
|
|
94
|
+
function moveInspectorResize(event) {
|
|
95
|
+
const drag = inspectorDrag.current;
|
|
96
|
+
if (!drag || drag.pointerId !== event.pointerId)
|
|
97
|
+
return;
|
|
98
|
+
drag.width = inspectorWidthWithinBounds(drag.startWidth + drag.startX - event.clientX);
|
|
99
|
+
setInspectorWidth(drag.width);
|
|
100
|
+
}
|
|
101
|
+
function finishInspectorResize(event) {
|
|
102
|
+
const drag = inspectorDrag.current;
|
|
103
|
+
if (!drag || drag.pointerId !== event.pointerId)
|
|
104
|
+
return;
|
|
105
|
+
inspectorDrag.current = undefined;
|
|
106
|
+
if (event.type === 'pointercancel')
|
|
107
|
+
setInspectorWidth(drag.startWidth);
|
|
108
|
+
else
|
|
109
|
+
saveInspectorWidth(drag.width);
|
|
110
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId))
|
|
111
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
112
|
+
}
|
|
113
|
+
function inspectorResizeKey(event) {
|
|
114
|
+
const delta = event.shiftKey ? 50 : 10;
|
|
115
|
+
const width = event.key === 'Home' ? INSPECTOR_MIN_WIDTH : event.key === 'End' ? INSPECTOR_MAX_WIDTH : event.key === 'ArrowLeft' ? inspectorWidth + delta : event.key === 'ArrowRight' ? inspectorWidth - delta : undefined;
|
|
116
|
+
if (width === undefined)
|
|
117
|
+
return;
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
const next = inspectorWidthWithinBounds(width);
|
|
120
|
+
setInspectorWidth(next);
|
|
121
|
+
saveInspectorWidth(next);
|
|
122
|
+
}
|
|
67
123
|
const [selectedTargetId, setSelectedTargetId] = useState(restoredImportedPackage?.executionPackage?.packageId ?? restoredImportedPackage?.authoringSource?.targets[0]?.packageId ?? checkoutPackage.packageId);
|
|
68
124
|
const selectedTarget = packageCatalog.targets().find(t => t.packageId === selectedTargetId);
|
|
125
|
+
const [authoringSource, setAuthoringSource] = useState(restoredImportedPackage?.authoringSource?.serialized);
|
|
126
|
+
const [selectedRoot, setSelectedRoot] = useState('');
|
|
127
|
+
const [preparedDocument, setPreparedDocument] = useState();
|
|
128
|
+
const [preparing, setPreparing] = useState(Boolean(restoredImportedPackage?.authoringSource));
|
|
129
|
+
const [preparationError, setPreparationError] = useState();
|
|
130
|
+
useEffect(() => { let current = true; if (!authoringSource) {
|
|
131
|
+
setPreparedDocument(undefined);
|
|
132
|
+
setPreparing(false);
|
|
133
|
+
return;
|
|
134
|
+
} setPreparing(true); setPreparationError(undefined); void prepareStudioDocument(authoringSource, selectedRoot ? { kind: 'occurrence', key: selectedRoot } : { kind: 'document' }).then(doc => { if (current) {
|
|
135
|
+
setPreparedDocument(doc);
|
|
136
|
+
setPreparing(false);
|
|
137
|
+
} }).catch(error => { if (current) {
|
|
138
|
+
setPreparationError(error instanceof Error ? error.message : 'Cannot prepare this Project.');
|
|
139
|
+
setPreparing(false);
|
|
140
|
+
} }); return () => { current = false; }; }, [authoringSource, selectedRoot]);
|
|
69
141
|
const [executionPackage, setExecutionPackage] = useState(restoredImportedPackage?.executionPackage ?? checkoutPackage);
|
|
70
142
|
const [inputEdits, setInputEdits] = useState(() => inputDrafts(inspectDeclaredInputs(restoredImportedPackage?.executionPackage ?? checkoutPackage).declarations));
|
|
71
|
-
const {
|
|
143
|
+
const inputContract = preparedDocument ? { target: preparedDocument.target, inputDeclarations: preparedDocument.selection.inputDeclarations, requiredInputs: preparedDocument.selection.requiredInputs, flows: preparedDocument.selection.root.kind === 'document' ? preparedDocument.flows.map(f => ({ ...f, name: preparedDocument.context.nodes.find(n => n.key === f.occurrence).name })) : [] } : executionPackage;
|
|
144
|
+
const { declarations, conflict, unresolved } = inspectDeclaredInputs(inputContract);
|
|
145
|
+
useEffect(() => { if (preparedDocument && !conflict && !unresolved)
|
|
146
|
+
setInputEdits(inputDrafts(declarations)); }, [preparedDocument]);
|
|
72
147
|
const inputBlocker = conflict ?? unresolved;
|
|
73
148
|
let inputError;
|
|
74
149
|
let inputs = {};
|
|
@@ -82,6 +157,8 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
82
157
|
const [timeoutMs, setTimeoutMs] = useState('none');
|
|
83
158
|
const [editingInputs, setEditingInputs] = useState(false);
|
|
84
159
|
const [runState, setRunState] = useState('ready');
|
|
160
|
+
const activeInputRequest = useRef(undefined);
|
|
161
|
+
const inputsLocked = activeInputRequest.current !== undefined;
|
|
85
162
|
const [controlState, setControlState] = useState('idle');
|
|
86
163
|
const [breakpoints, setBreakpoints] = useState(new Set());
|
|
87
164
|
const [contextMenu, setContextMenu] = useState();
|
|
@@ -95,7 +172,14 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
95
172
|
const [runHistory, setRunHistory] = useState(readRunHistory);
|
|
96
173
|
const [historyRetention, setHistoryRetention] = useState(readHistoryRetention);
|
|
97
174
|
const [result, setResult] = useState();
|
|
98
|
-
const [
|
|
175
|
+
const [workspace, setWorkspace] = useState('execution');
|
|
176
|
+
const [historicalEntry, setHistoricalEntry] = useState();
|
|
177
|
+
const [historySelectedStep, setHistorySelectedStep] = useState(0);
|
|
178
|
+
const [historyCollapsedSteps, setHistoryCollapsedSteps] = useState(new Set());
|
|
179
|
+
const [loadedContexts, setLoadedContexts] = useState({});
|
|
180
|
+
const loadedContext = loadedContexts[selectedTargetId];
|
|
181
|
+
const contextTarget = preparedDocument?.target ?? loadedContext?.owner ?? selectedTarget?.target ?? executionPackage.target;
|
|
182
|
+
const readOnly = workspace === 'history';
|
|
99
183
|
const [selectedStep, setSelectedStep] = useState(0);
|
|
100
184
|
const [collapsedSteps, setCollapsedSteps] = useState(new Set());
|
|
101
185
|
const [requestId, setRequestId] = useState();
|
|
@@ -103,43 +187,76 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
103
187
|
const [loadIssues, setLoadIssues] = useState([]);
|
|
104
188
|
const [, setCatalogVersion] = useState(0);
|
|
105
189
|
const packageSteps = executionPackage.target.kind === 'project' ? executionPackage.flows?.flatMap(flow => flow.steps) ?? [] : executionPackage.steps;
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
190
|
+
const legacyDisplaySteps = orderDisplayStepsByResult(flattenDisplaySteps(packageSteps, 0, undefined, undefined, collapsedSteps), result?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(result?.steps ?? []));
|
|
191
|
+
const liveDisplaySteps = preparedDocument ? contextDisplaySteps(preparedDocument.context, collapsedSteps, preparedDocument) : authoringSource ? [] : legacyDisplaySteps;
|
|
192
|
+
const historyPackage = undefined;
|
|
193
|
+
const shownResult = readOnly ? historicalEntry?.result : result;
|
|
194
|
+
const shownPackage = readOnly ? historyPackage : executionPackage;
|
|
195
|
+
const shownPackageSteps = shownPackage ? packageStepsFor(shownPackage) : [];
|
|
196
|
+
const shownSelectedStep = readOnly ? historySelectedStep : selectedStep;
|
|
197
|
+
const shownRequestId = readOnly ? historicalEntry?.requestId : requestId;
|
|
198
|
+
const shownRunState = readOnly ? historicalEntry?.state ?? 'ready' : runState;
|
|
199
|
+
const allResults = shownResult?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(shownResult?.steps ?? []);
|
|
200
|
+
const displaySteps = readOnly ? historicalEntry?.structure ? contextDisplaySteps(historicalEntry.structure.context, historyCollapsedSteps) : recordedDisplaySteps(allResults) : liveDisplaySteps;
|
|
201
|
+
const guides = hierarchyGuides(displaySteps);
|
|
202
|
+
const hasNestedSteps = displaySteps.some(step => step.depth > 0 || step.hasChildren);
|
|
203
|
+
const displayResults = displaySteps.map(step => resultForOccurrence(allResults, step.structuralAddress, step.occurrence));
|
|
204
|
+
const selectedInvocations = resultsForOccurrence(allResults, displaySteps[shownSelectedStep]?.structuralAddress, displaySteps[shownSelectedStep]?.occurrence);
|
|
205
|
+
function selectDisplayedStep(value) { if (readOnly)
|
|
206
|
+
setHistorySelectedStep(value);
|
|
207
|
+
else
|
|
208
|
+
setSelectedStep(value); }
|
|
209
|
+
function switchWorkspace(value) { setContextMenu(undefined); setWorkspace(value); }
|
|
210
|
+
function tabKey(event) {
|
|
211
|
+
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key))
|
|
212
|
+
return;
|
|
213
|
+
event.preventDefault();
|
|
214
|
+
const next = event.key === 'Home' ? 'execution' : event.key === 'End' ? 'history' : workspace === 'execution' ? 'history' : 'execution';
|
|
215
|
+
switchWorkspace(next);
|
|
216
|
+
document.getElementById(`runner-tab-${next}`)?.focus();
|
|
217
|
+
}
|
|
218
|
+
const executionEvents = events.filter(event => event.requestId === requestId);
|
|
219
|
+
const visibleEvents = executionEvents.filter(event => { const categoryMatches = eventFilter === 'all' || eventFilter === 'parallel' && event.parallel || eventFilter === 'steps' && event.stepId || eventFilter === 'lifecycle' && !event.stepId && !event.parallel || eventFilter === 'failures' && (event.state === 'failed' || event.type.includes('error')); const searchMatches = !eventSearch.trim() || `${event.type} ${event.state} ${event.message} ${event.stepName ?? ''}`.toLowerCase().includes(eventSearch.trim().toLowerCase()); return categoryMatches && searchMatches; });
|
|
220
|
+
function selectRoot(key) { if (activeInputRequest.current)
|
|
221
|
+
return; setSelectedRoot(key); setSelectedStep(0); stepStateCache.current = {}; setStepStates(liveDisplaySteps.map(() => 'pending')); setResult(undefined); setEvents([]); setRequestId(undefined); setRunState('ready'); setControlState('idle'); setLoadError(undefined); }
|
|
120
222
|
async function run(dryRun = false) {
|
|
121
|
-
if (inputBlocker || inputError)
|
|
223
|
+
if (activeInputRequest.current || inputBlocker || inputError || preparing || preparationError)
|
|
122
224
|
return;
|
|
123
225
|
let currentPackage;
|
|
124
226
|
try {
|
|
125
|
-
if (
|
|
126
|
-
throw new Error(
|
|
127
|
-
currentPackage = packageCatalog.prepare(executionPackage);
|
|
227
|
+
if (authoringSource && !preparedDocument)
|
|
228
|
+
throw new Error('Project preparation is not ready.');
|
|
229
|
+
currentPackage = preparedDocument ?? packageCatalog.prepare(executionPackage);
|
|
128
230
|
}
|
|
129
231
|
catch (error) {
|
|
130
232
|
setLoadError(error instanceof Error ? error.message : "Source target cannot execute.");
|
|
131
233
|
return;
|
|
132
234
|
}
|
|
133
235
|
const id = crypto.randomUUID();
|
|
134
|
-
|
|
236
|
+
activeInputRequest.current = id;
|
|
135
237
|
setRequestId(id);
|
|
136
238
|
setResult(undefined);
|
|
137
239
|
setEvents([]);
|
|
138
240
|
stepStateCache.current = Object.fromEntries(flattenDisplaySteps(packageSteps).filter(step => step.structuralAddress).map(step => [occurrenceKey(step.structuralAddress), 'pending']));
|
|
139
|
-
setStepStates(
|
|
241
|
+
setStepStates(liveDisplaySteps.map(() => 'pending'));
|
|
140
242
|
setRunState('preflighting');
|
|
141
243
|
setControlState('running');
|
|
142
|
-
|
|
244
|
+
let structure;
|
|
245
|
+
try {
|
|
246
|
+
structure = await captureHistoryStructure(currentPackage);
|
|
247
|
+
const common = { requestId: id, inputs, runtimeValues, options: { ...(timeoutMs === 'none' ? {} : { timeoutMs: Number(timeoutMs) }), dryRun } };
|
|
248
|
+
if (currentPackage.schemaVersion === 8)
|
|
249
|
+
await client.submit({ ...common, executionPackage: currentPackage, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfter: stopAfterStepId } : {}) } });
|
|
250
|
+
else
|
|
251
|
+
await client.submit({ ...common, executionPackage: currentPackage, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfterStepId } : {}) } });
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
activeInputRequest.current = undefined;
|
|
255
|
+
setRunState('failed');
|
|
256
|
+
setControlState('failed');
|
|
257
|
+
setLoadError(error instanceof Error ? error.message : 'Execution admission failed.');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
143
260
|
let completed = false;
|
|
144
261
|
void (async () => {
|
|
145
262
|
for await (const event of client.events(id)) {
|
|
@@ -156,25 +273,42 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
156
273
|
continue;
|
|
157
274
|
setControlState(state ?? 'running');
|
|
158
275
|
}
|
|
159
|
-
if (event.
|
|
276
|
+
if ('occurrence' in event && event.occurrence) {
|
|
277
|
+
const nextState = event.state;
|
|
278
|
+
stepStateCache.current[event.occurrence] = nextState;
|
|
279
|
+
setStepStates(previous => previous.map((state, index) => liveDisplaySteps[index]?.occurrence === event.occurrence ? nextState : state));
|
|
280
|
+
}
|
|
281
|
+
else if (event.structuralAddress) {
|
|
160
282
|
const nextState = event.state;
|
|
161
283
|
stepStateCache.current[occurrenceKey(event.structuralAddress)] = nextState;
|
|
162
|
-
setStepStates(previous => previous.map((state, index) => occurrenceKey(
|
|
284
|
+
setStepStates(previous => previous.map((state, index) => occurrenceKey(liveDisplaySteps[index]?.structuralAddress) === occurrenceKey(event.structuralAddress) ? nextState : state));
|
|
163
285
|
}
|
|
164
286
|
}
|
|
165
287
|
})();
|
|
166
288
|
const secretValues = collectSecretValues(inputs, runtimeValues);
|
|
167
289
|
const executionResult = redactExecutionResult(await client.result(id), secretValues);
|
|
168
290
|
completed = true;
|
|
291
|
+
try {
|
|
292
|
+
assertHistoryIdentity(id, structure, executionResult);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
activeInputRequest.current = undefined;
|
|
296
|
+
setRunState('failed');
|
|
297
|
+
setControlState('failed');
|
|
298
|
+
setLoadError(error.message);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (activeInputRequest.current === id)
|
|
302
|
+
activeInputRequest.current = undefined;
|
|
169
303
|
const finalSteps = flattenResultSteps(executionResult.children?.flatMap(child => child.steps) ?? executionResult.steps);
|
|
170
304
|
const finalStateById = Object.fromEntries(flattenDisplaySteps(packageSteps).filter(step => step.structuralAddress).map(step => [occurrenceKey(step.structuralAddress), stateForOccurrence(finalSteps, step.structuralAddress) ?? 'pending']));
|
|
171
305
|
stepStateCache.current = { ...stepStateCache.current, ...finalStateById };
|
|
172
306
|
setResult(executionResult);
|
|
173
|
-
setStepStates(
|
|
307
|
+
setStepStates(liveDisplaySteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending'));
|
|
174
308
|
setRunState(executionResult.state);
|
|
175
309
|
setControlState(executionResult.state === 'cancelled' ? 'stopped' : executionResult.state);
|
|
176
310
|
const finishedAt = executionResult.finishedAt ?? new Date().toISOString();
|
|
177
|
-
const entry = { requestId: id, startedAt: executionResult.startedAt, finishedAt, target:
|
|
311
|
+
const entry = { historyVersion: 2, structure, requestId: id, startedAt: executionResult.startedAt, finishedAt, target: currentPackage.target.name, packageId: currentPackage.packageId, artifactSchemaVersion: packageCatalog.artifactProvenance(executionPackage.packageId)?.originalVersion, artifactProvenance: packageCatalog.artifactProvenance(executionPackage.packageId), executionSchemaVersion: currentPackage.schemaVersion, state: executionResult.state, durationMs: Math.max(0, new Date(finishedAt).getTime() - new Date(executionResult.startedAt).getTime()), result: executionResult };
|
|
178
312
|
setRunHistory(previous => persistRunHistory(localStorage, HISTORY_KEY, [entry, ...previous.filter(item => item.requestId !== id)], historyRetention, secretValues));
|
|
179
313
|
}
|
|
180
314
|
async function stop() { if (requestId)
|
|
@@ -198,38 +332,53 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
198
332
|
next.add(stepId); breakpointsRef.current = next; return next; }); }
|
|
199
333
|
function toggleStopAfter(stepId) { setStopAfterStepId(previous => previous === stepId ? undefined : stepId); }
|
|
200
334
|
function exportResult(value, packageId, schemaVersion, provenance, executionVersion) { if (!value)
|
|
201
|
-
return; const exportPayload = createResultExport(value, packageId ?? 'unknown', schemaVersion, collectSecretValues(inputs, runtimeValues), provenance, executionVersion); const blob = new Blob([JSON.stringify(exportPayload, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${value.target.name.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}-${value.requestId}.result.json`; link.click(); URL.revokeObjectURL(url); }
|
|
202
|
-
function reloadHistoryEntry(entry) {
|
|
203
|
-
try {
|
|
204
|
-
loadedPackage = packageCatalog.get(entry.packageId);
|
|
205
|
-
setExecutionPackage(loadedPackage);
|
|
206
|
-
setSelectedTargetId(loadedPackage.packageId);
|
|
207
|
-
}
|
|
208
|
-
catch { /* The result remains viewable when its package is no longer loaded. */ }
|
|
209
|
-
} const steps = loadedPackage.target.kind === 'project' ? loadedPackage.flows?.flatMap(flow => flow.steps) ?? [] : loadedPackage.steps; const storedSteps = flattenResultSteps(entry.result.children?.flatMap(child => child.steps) ?? entry.result.steps); const states = new Map(flattenDisplaySteps(steps).map(step => [occurrenceKey(step.structuralAddress), stateForOccurrence(storedSteps, step.structuralAddress) ?? 'pending'])); setRequestId(entry.requestId); setEnvironment(entry.environment); setResult(entry.result); setViewingHistoryEntry(entry.requestId); setEvents([]); setSelectedStep(0); setStepStates(flattenDisplaySteps(steps).map(step => states.get(occurrenceKey(step.structuralAddress)) ?? 'pending')); setRunState(entry.state); document.querySelector('.runner-workspace')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
|
|
335
|
+
return; const storedEntry = runHistory.find(entry => entry.requestId === value.requestId); const exportPayload = storedEntry?.historyVersion === 2 ? { runnerResultVersion: 2, entry: storedEntry } : createResultExport(value, packageId ?? 'unknown', schemaVersion, collectSecretValues(inputs, runtimeValues), provenance, executionVersion); const blob = new Blob([JSON.stringify(exportPayload, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${value.target.name.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}-${value.requestId}.result.json`; link.click(); URL.revokeObjectURL(url); }
|
|
336
|
+
function reloadHistoryEntry(entry) { setHistoricalEntry(entry); setHistorySelectedStep(0); setHistoryCollapsedSteps(new Set()); }
|
|
210
337
|
function clearHistory() { setRunHistory([]); localStorage.removeItem(HISTORY_KEY); }
|
|
211
338
|
function deleteHistoryEntry(requestIdToDelete) { setRunHistory(previous => { const next = previous.filter(item => item.requestId !== requestIdToDelete); localStorage.setItem(HISTORY_KEY, JSON.stringify(next)); return next; }); }
|
|
212
339
|
function changeHistoryRetention(value) { setHistoryRetention(value); localStorage.setItem(HISTORY_RETENTION_KEY, String(value)); setRunHistory(previous => { const next = previous.slice(0, value); localStorage.setItem(HISTORY_KEY, JSON.stringify(next)); return next; }); }
|
|
213
|
-
function rememberVisibleStates() {
|
|
340
|
+
function rememberVisibleStates() { liveDisplaySteps.forEach((step, index) => { const state = stepStates[index]; if (state && step.structuralAddress)
|
|
214
341
|
stepStateCache.current[occurrenceKey(step.structuralAddress)] = state; }); }
|
|
215
|
-
function toggleStep(stepId) {
|
|
342
|
+
function toggleStep(stepId) { if (readOnly) {
|
|
343
|
+
setHistoryCollapsedSteps(previous => { const next = new Set(previous); if (next.has(stepId))
|
|
344
|
+
next.delete(stepId);
|
|
345
|
+
else
|
|
346
|
+
next.add(stepId); return next; });
|
|
347
|
+
return;
|
|
348
|
+
} rememberVisibleStates(); const next = new Set(collapsedSteps); if (next.has(stepId))
|
|
216
349
|
next.delete(stepId);
|
|
217
350
|
else
|
|
218
|
-
next.add(stepId); const nextSteps = flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
|
|
219
|
-
function setAllCollapsed(collapsed) {
|
|
351
|
+
next.add(stepId); const nextSteps = preparedDocument ? contextDisplaySteps(preparedDocument.context, next, preparedDocument) : flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
|
|
352
|
+
function setAllCollapsed(collapsed) { if (readOnly) {
|
|
353
|
+
setHistoryCollapsedSteps(collapsed ? new Set(historicalEntry?.structure?.context.nodes.filter(n => n.children.length).map(n => n.key) ?? containerStepIds(shownPackageSteps)) : new Set());
|
|
354
|
+
return;
|
|
355
|
+
} rememberVisibleStates(); const next = collapsed ? new Set(preparedDocument?.context.nodes.filter(n => n.children.length).map(n => n.key) ?? containerStepIds(packageSteps)) : new Set(); const nextSteps = preparedDocument ? contextDisplaySteps(preparedDocument.context, next, preparedDocument) : flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
|
|
220
356
|
async function loadPackage(file) {
|
|
221
|
-
if (!file)
|
|
357
|
+
if (!file || activeInputRequest.current)
|
|
222
358
|
return;
|
|
223
359
|
try {
|
|
224
|
-
const
|
|
360
|
+
const text = await file.text();
|
|
361
|
+
if (activeInputRequest.current)
|
|
362
|
+
return;
|
|
363
|
+
const target = loadTarget(text);
|
|
225
364
|
const loaded = target.executionPackage;
|
|
365
|
+
const owner = target.authoringSource?.targets.find(item => item.target.kind === 'project')?.target ?? loaded?.target ?? target.authoringSource?.targets[0]?.target;
|
|
366
|
+
setLoadedContexts(previous => ({ ...previous, ...Object.fromEntries([loaded?.packageId, ...(target.authoringSource?.targets ?? []).map(item => item.packageId)].filter((id) => Boolean(id)).map(id => [id, { filename: file.name, owner }])) }));
|
|
226
367
|
const selected = packageCatalog.addLoadedTarget(target);
|
|
227
368
|
setSelectedTargetId(selected);
|
|
369
|
+
setAuthoringSource(target.authoringSource?.serialized);
|
|
370
|
+
setSelectedRoot('');
|
|
371
|
+
setPreparedDocument(undefined);
|
|
372
|
+
setPreparing(Boolean(target.authoringSource));
|
|
373
|
+
setResult(undefined);
|
|
374
|
+
setEvents([]);
|
|
375
|
+
setBreakpoints(new Set());
|
|
376
|
+
breakpointsRef.current = new Set();
|
|
377
|
+
setStopAfterStepId(undefined);
|
|
228
378
|
if (!loaded) {
|
|
229
379
|
localStorage.setItem(IMPORTED_PACKAGE_KEY, JSON.stringify(target));
|
|
230
380
|
setCatalogVersion(v => v + 1);
|
|
231
381
|
setRequestId(undefined);
|
|
232
|
-
setViewingHistoryEntry(undefined);
|
|
233
382
|
setControlState("idle");
|
|
234
383
|
setRunState("ready");
|
|
235
384
|
setLoadError(undefined);
|
|
@@ -252,18 +401,17 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
252
401
|
setEvents([]);
|
|
253
402
|
setResult(undefined);
|
|
254
403
|
setRequestId(undefined);
|
|
255
|
-
setViewingHistoryEntry(undefined);
|
|
256
404
|
setControlState('idle');
|
|
257
405
|
setRunState('ready');
|
|
258
406
|
setLoadError(undefined);
|
|
259
407
|
setLoadIssues([]);
|
|
260
408
|
}
|
|
261
409
|
catch (error) {
|
|
262
|
-
setLoadError(error instanceof PackageLoadError ? `${error.category}: ${error.message}` : 'Unable to load the selected
|
|
410
|
+
setLoadError(error instanceof PackageLoadError ? `${error.category}: ${error.message}` : 'Unable to load the selected Project.');
|
|
263
411
|
setLoadIssues(error instanceof PackageLoadError ? error.issues : []);
|
|
264
412
|
}
|
|
265
413
|
}
|
|
266
|
-
const statusLabel = runState === 'ready' ? 'Ready' :
|
|
414
|
+
const statusLabel = !readOnly && runState === 'running' && controlState === 'paused' ? 'Paused' : shownRunState === 'ready' ? 'Ready' : shownRunState[0].toUpperCase() + shownRunState.slice(1);
|
|
267
415
|
useEffect(() => {
|
|
268
416
|
document.documentElement.dataset.theme = theme;
|
|
269
417
|
document.documentElement.style.colorScheme = theme;
|
|
@@ -276,11 +424,11 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
276
424
|
useEffect(() => { const close = () => setContextMenu(undefined); const keydown = (event) => { if (event.key === 'Escape')
|
|
277
425
|
close(); }; document.addEventListener('click', close); document.addEventListener('keydown', keydown); return () => { document.removeEventListener('click', close); document.removeEventListener('keydown', keydown); }; }, []);
|
|
278
426
|
useEffect(() => { const navigateIssue = (event) => { const target = event.target; const issue = target.closest('.validation-issue'); const stepId = issue?.dataset.stepId; const eventRow = target.closest('.event-row'); const index = stepId ? displaySteps.findIndex(step => step.id === stepId || step.id.endsWith(`.${stepId}`)) : eventRow ? displaySteps.findIndex(step => eventRow.textContent?.includes(step.name)) : -1; if (index >= 0)
|
|
279
|
-
|
|
427
|
+
selectDisplayedStep(index); }; document.addEventListener('click', navigateIssue); return () => document.removeEventListener('click', navigateIssue); }, [displaySteps]);
|
|
280
428
|
const projectFileInput = useRef(null);
|
|
281
429
|
const menuActions = useRef({ enabled: () => false, openProject: async () => { }, about: () => { }, demo: () => { } });
|
|
282
430
|
menuActions.current = {
|
|
283
|
-
enabled: id => runnerMenus.some(menu => menu.items.some(([command]) => command === id)) && (!showAbout || id.startsWith('help.')),
|
|
431
|
+
enabled: id => runnerMenus.some(menu => menu.items.some(([command]) => command === id)) && (!showAbout || id.startsWith('help.')) && (id !== 'project.open' || !inputsLocked),
|
|
284
432
|
openProject: file => loadPackage(new File([file.text], file.name, { type: 'application/json' })),
|
|
285
433
|
about: () => setShowAbout(true),
|
|
286
434
|
// Preserve the current standalone/combined destination exactly.
|
|
@@ -293,16 +441,39 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
293
441
|
about: () => menuActions.current.about(),
|
|
294
442
|
demo: () => menuActions.current.demo(),
|
|
295
443
|
}), [registerMenu]);
|
|
296
|
-
return _jsxs("div", { className: "runner-shell", children: [_jsxs("header", { className: "runner-header", children: [_jsxs("div", { className: "runner-brand", children: [_jsx("span", { className: "runner-brand-mark", children: "QAICK" }), _jsx("span", { className: "runner-brand-product", children: "Runner" }), _jsx("span", { className: "alpha-label", children: RUNNER_UI_VERSION }), onExit && _jsx("button", { className: "about-button", onClick: onExit, children: "Studio" })] }), !applicationMenu?.native && _jsx(HeaderHelp, { enabled: id => menuActions.current.enabled(id), onAbout: () => menuActions.current.about(), onOpenProject: () => projectFileInput.current?.click() }), _jsx("span", { className: "header-spacer" }), _jsx(ScreenshotButton, { className: "about-button" }), _jsx("a", { className: "demo-entry", href: "./examples/qaick-local-http-regression.qaick.json", download: true, title: "Download QAick Local HTTP Regression. Start the local fixture on port 5197, then load this file. Runner executes both Flows. Editing is restricted in Studio.", children: "HTTP example" }), _jsx(ThemeToggle, { theme: theme, onChange: setTheme, className: "runner-theme-toggle" })] }), _jsx("input", { ref: projectFileInput, type: "file", accept: "application/json,.json", hidden: true, "aria-label": "Open Project\u2026", onChange: event => { const file = event.target.files?.[0]; event.target.value = ''; void loadPackage(file); } }), _jsxs("main", { className: "runner-main", children: [selectedTarget?.issues?.length ? _jsxs("section", { className: "runner-workspace runner-authoring-source", children: [_jsx("div", { className: "eyebrow", children: "AUTHORING SOURCE" }), _jsx("h1", { children: selectedTarget.target.name }), _jsx("div", { className: "run-settings", children: _jsxs("label", { children: ["Target", _jsx("select", { value: selectedTargetId, onChange: event => selectTarget(event.target.value), children: packageCatalog.targets().map(t => _jsx("option", { value: t.packageId, children: t.target.name }, t.packageId)) })] }) }), _jsxs("div", { className: "notice", children: [_jsx("strong", { children: "Not executable" }), _jsx("p", { children: "This source has no validated executable structure. Complete and validate it in Studio, then reload the saved Project. Valid sibling Flows remain available in Target." })] }), _jsx("ul", { role: "alert", children: selectedTarget.issues.map((issue, index) => _jsx("li", { children: issue }, index)) }), loadError && _jsx("p", { role: "alert", children: loadError })] }) : _jsxs(_Fragment, { children: [_jsxs("nav", { className: "runner-nav", children: [_jsx("p", { className: "nav-label", children: "Workspace" }), _jsx(NavItem, { active: true, icon: "\u25C8", text: "Execution Package" }), _jsx(NavItem, { icon: "\u25A1", text: "Projects" }), _jsx(NavItem, { icon: "\u2318", text: "Flows" }), _jsx(NavItem, { icon: "\u21B6", text: "Run History" }), _jsxs("div", { className: "package-card", children: [_jsx("small", { children: "Loaded package" }), _jsxs("strong", { children: [executionPackage.packageId, ".qaick.json"] }), _jsxs("small", { children: ["Execution v", executionPackage.schemaVersion, " \u00B7 ", executionPackage.definitions.length, " definitions"] })] })] }), _jsxs("section", { className: "runner-workspace", children: [_jsx("div", { className: "eyebrow", children: "EXECUTION PACKAGE" }), _jsx("h1", { children: executionPackage.target.name }), _jsxs("p", { className: "description", children: ["Execute the selected QAick ", executionPackage.target.kind === 'project' ? 'Project' : 'Flow', " and capture structured evidence."] }), _jsxs("div", { className: "run-controls", children: [_jsxs("div", { className: "run-settings", role: "group", "aria-label": "Execution setup", children: [_jsxs("label", { children: ["Target", _jsx("select", { value: selectedTargetId, onChange: event => selectTarget(event.target.value), children: packageCatalog.targets().map(item => _jsx("option", { value: item.packageId, children: item.target.name }, item.packageId)) })] }), _jsxs("label", { children: ["Environment", _jsxs("select", { value: environment, onChange: event => setEnvironment(event.target.value), children: [_jsx("option", { children: "test" }), _jsx("option", { children: "local" }), _jsx("option", { children: "staging" })] })] }), _jsxs("label", { children: ["Timeout", _jsxs("select", { value: timeoutMs, onChange: event => setTimeoutMs(event.target.value), children: [_jsx("option", { value: "none", children: "None" }), _jsx("option", { value: "1000", children: "1 second" }), _jsx("option", { value: "3000", children: "3 seconds" }), _jsx("option", { value: "10000", children: "10 seconds" })] })] }), _jsxs("label", { children: ["Run configuration", _jsxs("select", { defaultValue: "Default", children: [_jsx("option", { children: "Default" }), _jsx("option", { children: "Debug" })] })] })] }), _jsxs("div", { className: "run-actions", role: "group", "aria-label": "Execution actions", children: [_jsx("button", { className: "button", onClick: () => setEditingInputs(value => !value), children: editingInputs ? 'Done inputs' : 'Edit inputs' }), _jsx("button", { className: "button primary", onClick: () => void run(), disabled: !!inputBlocker || runState === 'preflighting' || runState === 'running', children: "\u25B6\u00A0 Run" }), _jsx("button", { className: "button dry-run", onClick: () => void run(true), disabled: !!inputBlocker || runState === "preflighting" || runState === "running", children: "\u25CE\u00A0 Pre-run validation" }), _jsx("button", { className: "button stop", onClick: () => void stop(), disabled: runState !== 'preflighting' && runState !== 'running', children: "\u25A0\u00A0 Stop" }), _jsx("button", { className: "button", onClick: () => void pause(), disabled: controlState !== 'running', children: "\u2161\u00A0 Pause" }), _jsx("button", { className: `button ${controlState === 'paused' ? 'resume-highlight' : ''}`, onClick: () => void resume(), disabled: controlState !== 'paused', children: "\u25B6\u00A0 Resume" }), _jsx("button", { className: "button", onClick: () => void stepExecution(), disabled: controlState !== 'paused', children: "\u21A6\u00A0 Step" }), breakpoints.size > 0 && _jsxs("span", { className: "debug-session-badge", children: [breakpoints.size, " breakpoint", breakpoints.size === 1 ? "" : "s", " armed"] })] })] }), viewingHistoryEntry && _jsxs("div", { className: "history-view-banner", children: ["Viewing stored results \u00B7 ", viewingHistoryEntry] }), conflict && _jsxs("section", { role: "alert", children: [_jsx("p", { children: conflict.message }), _jsx("ul", { children: conflict.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.declaration.name, " \u00B7 ", entry.declaration.type, " \u00B7 ", entry.declaration.required ? 'required' : 'optional', " \u00B7 default: ", Object.hasOwn(entry.declaration, 'defaultValue') ? JSON.stringify(entry.declaration.defaultValue) : 'none'] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), unresolved && _jsxs("section", { role: "alert", children: [_jsx("p", { children: unresolved.message }), _jsx("ul", { children: unresolved.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.name, " \u00B7 required \u00B7 type unresolved"] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), inputError && _jsx("p", { role: "alert", children: inputError }), loadError && _jsx("p", { className: "load-error", children: loadError }), loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues, title: "Artifact validation" }), result?.issues !== undefined && _jsx(PreRunValidation, { issues: result.issues }), result?.warnings !== undefined && result.warnings.length > 0 && _jsx(PreRunValidation, { issues: result.warnings, title: "Browser execution warnings" }), editingInputs && _jsxs("section", { className: "inputs-panel panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Invocation Inputs" }), _jsx("span", { className: "panel-spacer" }), _jsx("span", { className: "input-hint", children: "Values are sent with the next run" })] }), _jsxs("div", { className: "input-grid", children: [inputBlocker ? _jsx("p", { children: "Invocation input editing is unavailable until requirements are resolved." }) : declarations.map(declaration => { const draft = inputEdits[declaration.name] ?? { supplied: false, text: '' }; const change = (text) => setInputEdits(previous => ({ ...previous, [declaration.name]: { supplied: true, text } })); return _jsxs("div", { children: [_jsxs("label", { children: [_jsx("input", { type: "checkbox", "aria-label": `Supply ${declaration.name}`, checked: draft.supplied, onChange: event => setInputEdits(previous => ({ ...previous, [declaration.name]: { ...draft, supplied: event.target.checked } })) }), "Supply ", declaration.name] }), declaration.type === 'boolean' ? _jsxs("label", { className: "input-field", children: [declaration.name, _jsxs("select", { value: draft.text, onChange: event => change(event.target.value), children: [_jsx("option", { value: "false", children: "false" }), _jsx("option", { value: "true", children: "true" })] })] }) : _jsx(InputField, { label: declaration.name, value: draft.text, onChange: change }), _jsxs("small", { children: [declaration.type, " \u00B7 ", draft.supplied ? 'supplied' : Object.hasOwn(declaration, 'defaultValue') ? 'omitted — declared default applies' : 'omitted', declaration.required ? ' · required' : ''] })] }, declaration.name); }), (executionPackage.runtimeRequirements ?? []).map(name => _jsx(InputField, { label: `Runtime · ${name}`, value: runtimeValues[name] ?? '', onChange: value => setRuntimeValues(previous => ({ ...previous, [name]: value })) }, name))] })] }), _jsxs("section", { className: "panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Live Execution" }), _jsx("span", { className: "panel-spacer" }), _jsxs("span", { className: "execution-menu-label", children: ["Right-click a row to manage its breakpoint \u00B7 Selected: ", displaySteps[selectedStep]?.name ?? 'None'] }), _jsxs("span", { className: runState === 'running' ? 'running' : runState === 'failed' ? 'stopped' : 'run-status', children: ["\u25CF ", statusLabel] })] }), hasNestedSteps && _jsxs("div", { className: "hierarchy-hint", children: [_jsx("strong", { children: "Nested execution" }), _jsx("span", { children: "Use \u2212 / + to collapse or expand nested Operations, Control Blocks, and loops." }), _jsx("button", { className: "hierarchy-action", onClick: () => setAllCollapsed(true), children: "Collapse all" }), _jsx("button", { className: "hierarchy-action", onClick: () => setAllCollapsed(false), children: "Expand all" })] }), _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Execution steps", tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "#" }), _jsx("th", { children: "Step hierarchy" }), _jsx("th", { children: "Status" }), _jsx("th", { children: "Start time" }), _jsx("th", { children: "Duration" })] }) }), _jsx("tbody", { children: displaySteps.map((step, index) => { const isLoop = step.loopRepeat; const loopContexts = isLoop ? loopPresentation(step.structuralAddress, step.loopTotal, step.loopDuration, result ? allResults : eventPresentationEvidence(events), Boolean(result)) : []; const rowEvidence = result ? allResults : eventPresentationEvidence(events); const repeatedMatches = rowEvidence.filter(r => occurrenceKey(r.structuralAddress) === occurrenceKey(step.structuralAddress)); const latestQualifier = step.branchRow ? rowEvidence.filter(r => occurrenceKey(r.structuralAddress) === occurrenceKey(step.branchOwner)).length > 1 : repeatedMatches.length > 1; const displayState = presentationState(step, step.branchRow ? rowEvidence : latestQualifier ? [repeatedMatches.at(-1)] : rowEvidence, Boolean(result), step.branchRow ? 'pending' : stepStates[index] ?? 'pending'); const isLoopContent = Boolean(step.loopId); const isLoopEnd = isLoopContent && displaySteps[index + 1]?.loopId !== step.loopId; return _jsxs("tr", { className: `${selectedStep === index ? 'selected ' : ''}${isLoop ? 'loop-row ' : ''}${isLoopContent ? 'loop-content ' : ''}${isLoopEnd ? 'loop-end ' : ''}${step.depth > 0 ? 'nested-row' : 'parent-row'}`, tabIndex: 0, onKeyDown: event => { if (event.key === "ArrowDown") {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
} }, onClick: () => setSelectedStep(index), onContextMenu: event => { event.preventDefault(); setSelectedStep(index); setContextMenu({ x: event.clientX, y: event.clientY, stepId: step.id }); }, children: [_jsx("td", { children: index + 1 }), _jsxs("td", { children: [_jsx("span", { className: `step-icon ${displayState.replaceAll(' ', '-')}${isLoop ? ' loop-icon' : ''}`, children: statusGlyph[displayState] }), _jsxs("span", { className: "hierarchy-name", style: { paddingLeft: `${step.depth * 18}px` }, children: [step.depth > 0 && _jsx("span", { className: "branch-marker", children: "\u2514" }), step.hasChildren && _jsx("button", { className: "collapse-toggle", "aria-label": `${collapsedSteps.has(step.id) ? 'Expand' : 'Collapse'} ${step.name}`, onClick: event => { event.stopPropagation(); toggleStep(step.id); }, children: collapsedSteps.has(step.id) ? '+' : '−' }), _jsx("span", { children: step.name }), breakpoints.has(step.id) && _jsx("small", { className: "breakpoint-marker", "aria-label": "Breakpoint armed", title: "Breakpoint armed", children: "STOP" }), isLoop && _jsx("small", { className: "kind-label loop-label", children: "\u21BB LOOP" }), step.depth > 0 ? _jsx("small", { className: "kind-label nested-label", children: "NESTED STEP" }) : !isLoop && _jsx("small", { className: "kind-label", children: step.kind.toUpperCase() })] }), selectedStep === index && _jsx("small", { className: "selected-label", children: "SELECTED" })] }), _jsxs("td", { children: [loopContexts.map(context => _jsxs("div", { className: "loop-progress", children: [context.context.length > 0 && _jsxs("small", { children: ["Enclosing iterations: ", context.context.map(s => s.iteration).join(' / ')] }), _jsx("strong", { children: context.label }), _jsx("small", { children: context.state.toUpperCase() })] }, JSON.stringify(context.context))), _jsx("span", { className: `status ${displayState.replaceAll(' ', '-')}`, children: displayState.toUpperCase() }), latestQualifier && _jsx("small", { className: "latest-invocation", children: "Latest invocation" })] }), _jsx("td", { children: displayResults[index] ? new Date(result?.finishedAt ?? '').toLocaleTimeString() : '—' }), _jsx("td", { children: displayResults[index] ? `${displayResults[index].durationMs} ms` : '—' })] }, `${step.id}-${index}`); }) })] }) }), contextMenu && _jsxs("div", { className: "runner-context-menu", style: { left: contextMenu.x, top: contextMenu.y }, onClick: event => event.stopPropagation(), onContextMenu: event => event.preventDefault(), children: [_jsx("strong", { children: "Breakpoint" }), breakpoints.has(contextMenu.stepId) ? _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Remove breakpoint" }) : _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Add breakpoint" })] })] }), _jsxs("section", { className: "panel event-panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Event Log" }), _jsx("span", { className: "panel-spacer" }), _jsx("input", { className: "event-search", "aria-label": "Search event log", placeholder: "Search events", value: eventSearch, onChange: event => setEventSearch(event.target.value) }), _jsxs("select", { className: "event-filter", value: eventFilter, onChange: event => setEventFilter(event.target.value), children: [_jsx("option", { value: "all", children: "All events" }), _jsx("option", { value: "steps", children: "Step events" }), _jsx("option", { value: "parallel", children: "Parallel groups" }), _jsx("option", { value: "lifecycle", children: "Lifecycle" }), _jsx("option", { value: "failures", children: "Failures" })] })] }), _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Execution events", tabIndex: 0, children: _jsx("table", { children: _jsx("tbody", { children: visibleEvents.map(event => _jsxs("tr", { className: "event-row", children: [_jsx("td", { children: new Date(event.timestamp).toLocaleTimeString() }), _jsx("td", { children: event.state.toUpperCase() }), _jsx("td", { children: _jsx("code", { children: event.type.toUpperCase() }) }), _jsxs("td", { children: [event.message, event.parallel && _jsxs("small", { className: "parallel-event", children: [" \u00B7 Parallel ", event.parallel.completed, "/", event.parallel.total, " (", event.parallel.join, ")"] })] })] }, event.sequence)) }) }) }), events.length > 0 && visibleEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No events match the current filter." })] }), _jsx(HistoryPanel, { entries: runHistory, onReload: reloadHistoryEntry, retention: historyRetention, onRetentionChange: changeHistoryRetention, onClear: clearHistory, onDelete: deleteHistoryEntry, onExport: exportResult })] }), _jsxs("aside", { className: "inspector", children: [_jsx("h2", { children: "Selected execution" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[selectedStep]?.name ?? 'No step selected' }) }), _jsxs("p", { className: "run-id", children: ["Run ID: ", requestId ?? 'No run started'] }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { values: ["Target", executionPackage.target.name, "Type", executionPackage.target.kind, "Package", executionPackage.packageId, "Execution format", `Execution v${executionPackage.schemaVersion}`, "Definitions", String(executionPackage.definitions.length), "Required inputs", (executionPackage.requiredInputs ?? []).join(", ") || "None", "Runtime values", (executionPackage.runtimeRequirements ?? []).join(", ") || "None"] }) }), _jsx(InspectorSection, { title: "Run outcome", children: _jsxs("div", { className: "notice", children: [_jsx("strong", { children: result?.state.toUpperCase() ?? 'NOT STARTED' }), _jsx("br", {}), result ? `${result.environment} · ${result.state}` : 'Run the package to inspect its terminal outcome.'] }) }), _jsx(InspectorSection, { title: "Selected step inputs", children: _jsx(KeyValues, { values: Object.entries(displayResults[selectedStep]?.inputs ?? (selectedInvocations.length > 1 ? {} : inputs)).flatMap(([key, value]) => [key, String(value)]) }) }), _jsx(InspectorSection, { title: "Selected step outputs", children: _jsx(KeyValues, { values: displayResults[selectedStep]?.outputs ? Object.entries(displayResults[selectedStep]?.outputs ?? {}).flatMap(([key, value]) => [key, String(value)]) : ['—', selectedInvocations.length > 1 ? `${selectedInvocations.length} recorded invocations; see iteration history` : 'Available after execution'] }) }), (result?.error || displayResults[selectedStep]?.error) && _jsx(InspectorSection, { title: "Error", children: _jsxs("div", { className: "error-notice", children: [_jsx("strong", { children: displayResults[selectedStep]?.error?.code ?? result?.error?.code }), _jsx("br", {}), displayResults[selectedStep]?.error?.message ?? result?.error?.message] }) }), result?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: result.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: _jsx("div", { className: "notice", children: displayResults[selectedStep]?.invocationPath?.join(" › ") ?? "No invocation path available." }) }), _jsx(InspectorSection, { title: "Iteration history", children: _jsx(RecordedInvocations, { results: selectedInvocations, definition: findInspectionDefinition(packageSteps, displaySteps[selectedStep]?.structuralAddress) }, JSON.stringify([requestId, displaySteps[selectedStep]?.structuralAddress])) }), _jsx(InspectorSection, { title: "Execution timing", children: _jsxs("div", { className: "timing-details", children: [_jsxs("span", { children: ["Wall ", result?.executionTiming?.wallDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Active ", result?.executionTiming?.activeDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Paused ", result?.executionTiming?.pausedDurationMs ?? "—", " ms"] })] }) }), _jsx(InspectorSection, { title: "Selected step attempts", children: _jsx("div", { className: "attempt-history", children: displayResults[selectedStep]?.attempts?.map(attempt => _jsxs("div", { className: "iteration-entry", children: [_jsxs("strong", { children: ["Attempt ", attempt.attempt] }), _jsxs("span", { children: [(attempt.kind ?? 'automatic').toUpperCase(), " \u00B7 ", attempt.state.toUpperCase(), " \u00B7 ", attempt.durationMs, " ms"] })] }, `${attempt.attempt}-${attempt.kind}`)) ?? _jsx("div", { className: "notice", children: "No retry attempts recorded." }) }) }), _jsxs(InspectorSection, { title: "Selected step", children: [_jsxs("div", { className: "notice", children: [_jsx("strong", { children: displaySteps[selectedStep]?.name ?? 'No step selected' }), _jsx("br", {}), displayResults[selectedStep] ? displayResults[selectedStep].error ? 'This step failed. See the error details above.' : `Completed in ${displayResults[selectedStep].durationMs} ms.` : selectedInvocations.length > 1 ? `${selectedInvocations.length} invocations recorded. No single invocation selected.` : 'Run the package to inspect execution details.', _jsxs("div", { className: "inspector-actions", children: [_jsx("button", { className: "button", onClick: () => void retrySelected(), disabled: !displayResults[selectedStep] || (!displayResults[selectedStep]?.error && runState !== "completed"), children: "Retry Action" }), _jsx("button", { className: `button ${stopAfterStepId === displaySteps[selectedStep]?.id ? "active" : ""}`, onClick: () => displaySteps[selectedStep] && toggleStopAfter(displaySteps[selectedStep].id), disabled: !displaySteps[selectedStep], children: "Stop After" }), _jsx("button", { className: "button", onClick: () => void runFromSelected(), disabled: !displayResults[selectedStep] || runState === "running", children: "Run From Here" })] })] }), _jsxs("div", { className: "selection-nav", children: [_jsx("button", { className: "button", disabled: selectedStep <= 0, onClick: () => setSelectedStep(index => Math.max(0, index - 1)), children: "\u2039 Previous" }), _jsx("span", { children: displaySteps.length ? `${selectedStep + 1} / ${displaySteps.length}` : '0 / 0' }), _jsx("button", { className: "button", disabled: selectedStep >= displaySteps.length - 1, onClick: () => setSelectedStep(index => Math.min(displaySteps.length - 1, index + 1)), children: "Next \u203A" })] })] })] })] }), " "] }), showAbout && _jsx("div", { className: "about-backdrop", onClick: () => setShowAbout(false), children: _jsxs("section", { className: "about-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "about-title", onClick: event => event.stopPropagation(), children: [_jsx("button", { className: "about-close", onClick: () => setShowAbout(false), "aria-label": "Close About", children: "\u00D7" }), _jsx("div", { className: "runner-logo", children: "\u25B6" }), _jsx("h2", { id: "about-title", children: "QAick Runner" }), _jsx("p", { children: "Standalone execution and evidence viewer for QAICK packages." }), _jsxs("small", { children: [RUNNER_UI_VERSION, " \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
|
|
444
|
+
return _jsxs("div", { className: "runner-shell", children: [_jsxs("header", { className: "runner-header", children: [_jsxs("div", { className: "runner-brand", children: [_jsx("span", { className: "runner-brand-mark", children: "QAICK" }), _jsx("span", { className: "runner-brand-product", children: "Runner" }), _jsx("span", { className: "alpha-label", children: RUNNER_UI_VERSION }), onExit && _jsx("button", { className: "about-button", onClick: onExit, children: "Studio" })] }), !applicationMenu?.native && _jsx(HeaderHelp, { enabled: id => menuActions.current.enabled(id), onAbout: () => menuActions.current.about(), onOpenProject: () => projectFileInput.current?.click() }), _jsx("span", { className: "header-spacer" }), _jsx(ScreenshotButton, { className: "about-button" }), _jsx("a", { className: "demo-entry", href: "./examples/qaick-local-http-regression.qaick.json", download: true, title: "Download QAick Local HTTP Regression. Start the local fixture on port 5197, then load this file. Runner executes both Flows. Editing is restricted in Studio.", children: "HTTP example" }), _jsx(ThemeToggle, { theme: theme, onChange: setTheme, className: "runner-theme-toggle" })] }), _jsx("input", { ref: projectFileInput, type: "file", accept: "application/json,.json", hidden: true, "aria-label": "Open Project\u2026", onChange: event => { const file = event.target.files?.[0]; event.target.value = ''; void loadPackage(file); } }), _jsx("div", { className: "runner-tabs", role: "tablist", "aria-label": "Runner workspace", children: ['execution', 'history'].map(tab => _jsx("button", { id: `runner-tab-${tab}`, role: "tab", "aria-selected": workspace === tab, "aria-controls": `runner-panel-${tab}`, tabIndex: workspace === tab ? 0 : -1, onKeyDown: tabKey, onClick: () => switchWorkspace(tab), children: tab === 'execution' ? 'Execution' : 'History' }, tab)) }), _jsx("div", { className: "runner-pane-scroll", role: "region", "aria-label": "Runner workspace and Inspector", tabIndex: 0, children: _jsxs("main", { style: { gridTemplateColumns: `minmax(560px, 1fr) 8px ${inspectorWidth}px`, minWidth: `${568 + inspectorWidth}px` }, className: "runner-main runner-tabbed-main", id: `runner-panel-${workspace}`, role: "tabpanel", "aria-labelledby": `runner-tab-${workspace}`, children: [_jsxs(_Fragment, { children: [_jsxs("section", { className: "runner-workspace", children: [readOnly && loadError && _jsx("p", { className: "load-error", role: "alert", children: loadError }), readOnly && loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues, title: "Artifact validation" }), readOnly && historicalEntry && !historicalEntry.structure && _jsx("p", { className: "notice", children: "Original Project structure was not recorded for this run. Only recorded execution evidence is available." }), readOnly && _jsx(HistoryPanel, { entries: runHistory, onReload: reloadHistoryEntry, retention: historyRetention, onRetentionChange: changeHistoryRetention, onClear: clearHistory, onDelete: deleteHistoryEntry, onExport: exportResult }), _jsxs("div", { hidden: readOnly, children: [_jsx("h1", { children: contextTarget.name }), _jsxs("p", { className: "description", children: ["Execute the selected QAick ", executionPackage.target.kind === 'project' ? 'Project' : 'Flow', " and capture structured evidence."] }), _jsxs("div", { className: "run-controls", children: [_jsxs("div", { className: "run-settings", role: "group", "aria-label": "Execution setup", children: [_jsxs("label", { children: ["Run only", _jsxs("select", { disabled: inputsLocked || preparing, value: selectedRoot, onChange: event => selectRoot(event.target.value), children: [_jsx("option", { value: "", children: "Entire project" }), preparedDocument && contextDisplaySteps(preparedDocument.context, new Set(), preparedDocument).filter(n => n.kind !== 'body' && n.kind !== 'branch').map(n => _jsxs("option", { value: n.id, children: [' '.repeat(n.depth), n.name] }, n.id))] })] }), _jsxs("label", { children: ["Timeout", _jsxs("select", { disabled: inputsLocked, value: timeoutMs, onChange: event => setTimeoutMs(event.target.value), children: [_jsx("option", { value: "none", children: "None" }), _jsx("option", { value: "1000", children: "1 second" }), _jsx("option", { value: "3000", children: "3 seconds" }), _jsx("option", { value: "10000", children: "10 seconds" })] })] })] }), _jsxs("div", { className: "run-actions", role: "group", "aria-label": "Execution actions", children: [_jsx("button", { className: "button", disabled: inputsLocked, onClick: () => { if (!activeInputRequest.current)
|
|
445
|
+
setEditingInputs(value => !value); }, children: editingInputs ? 'Done inputs' : 'Edit inputs' }), _jsx("button", { className: "button primary", onClick: () => void run(), disabled: !!inputBlocker || preparing || !!preparationError || runState === 'preflighting' || runState === 'running', children: "\u25B6\u00A0 Run" }), _jsx("button", { className: "button dry-run", onClick: () => void run(true), disabled: !!inputBlocker || preparing || !!preparationError || runState === "preflighting" || runState === "running", children: "\u25CE\u00A0 Pre-run validation" }), _jsx("button", { className: "button stop", onClick: () => void stop(), disabled: runState !== 'preflighting' && runState !== 'running', children: "\u25A0\u00A0 Stop" }), _jsx("button", { className: "button", onClick: () => void pause(), disabled: controlState !== 'running', children: "\u2161\u00A0 Pause" }), _jsx("button", { className: `button ${controlState === 'paused' ? 'resume-highlight' : ''}`, onClick: () => void resume(), disabled: controlState !== 'paused', children: "\u25B6\u00A0 Resume" }), _jsx("button", { className: "button", onClick: () => void stepExecution(), disabled: controlState !== 'paused', children: "\u21A6\u00A0 Step" }), breakpoints.size > 0 && _jsxs("span", { className: "debug-session-badge", children: [breakpoints.size, " breakpoint", breakpoints.size === 1 ? "" : "s", " armed"] })] })] }), preparing && _jsx("p", { role: "status", children: "Preparing Project\u2026" }), preparationError && _jsx("p", { role: "alert", children: preparationError }), preparedDocument && !preparedDocument.selection.executable && _jsx("section", { role: "alert", children: preparedDocument.selection.issues.map((i, index) => _jsxs("p", { children: [preparedDocument.context.nodes.find(n => n.key === i.at)?.name, ": ", i.message] }, index)) }), conflict && _jsxs("section", { role: "alert", children: [_jsx("p", { children: conflict.message }), _jsx("ul", { children: conflict.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.declaration.name, " \u00B7 ", entry.declaration.type, " \u00B7 ", entry.declaration.required ? 'required' : 'optional', " \u00B7 default: ", Object.hasOwn(entry.declaration, 'defaultValue') ? JSON.stringify(entry.declaration.defaultValue) : 'none'] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), unresolved && _jsxs("section", { role: "alert", children: [_jsx("p", { children: unresolved.message }), _jsx("ul", { children: unresolved.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.name, " \u00B7 required \u00B7 type unresolved"] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), inputError && _jsx("p", { role: "alert", children: inputError }), loadError && _jsx("p", { className: "load-error", children: loadError }), loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues, title: "Artifact validation" }), result?.issues !== undefined && _jsx(PreRunValidation, { issues: result.issues }), result?.warnings !== undefined && result.warnings.length > 0 && _jsx(PreRunValidation, { issues: result.warnings, title: "Browser execution warnings" }), editingInputs && _jsxs("section", { className: "inputs-panel panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Invocation Inputs" }), _jsx("span", { className: "panel-spacer" }), _jsx("span", { className: "input-hint", children: inputsLocked ? 'Inputs are locked while execution is active.' : 'Values are sent with the next run' })] }), _jsxs("fieldset", { className: "input-grid input-lock-fields", "aria-label": "Invocation input values", disabled: inputsLocked, children: [inputBlocker ? _jsx("p", { children: "Invocation input editing is unavailable until requirements are resolved." }) : declarations.map(declaration => { const draft = inputEdits[declaration.name] ?? { supplied: false, text: '' }; const change = (text) => { if (!activeInputRequest.current)
|
|
446
|
+
setInputEdits(previous => ({ ...previous, [declaration.name]: { supplied: true, text } })); }; return _jsxs("div", { children: [_jsxs("label", { children: [_jsx("input", { type: "checkbox", "aria-label": `Supply ${declaration.name}`, checked: draft.supplied, onChange: event => { if (!activeInputRequest.current)
|
|
447
|
+
setInputEdits(previous => ({ ...previous, [declaration.name]: { ...draft, supplied: event.target.checked } })); } }), "Supply ", declaration.name] }), declaration.type === 'boolean' ? _jsxs("label", { className: "input-field", children: [declaration.name, _jsxs("select", { value: draft.text, onChange: event => change(event.target.value), children: [_jsx("option", { value: "false", children: "false" }), _jsx("option", { value: "true", children: "true" })] })] }) : _jsx(InputField, { label: declaration.name, value: draft.text, onChange: change }), _jsxs("small", { children: [declaration.type, " \u00B7 ", draft.supplied ? 'supplied' : Object.hasOwn(declaration, 'defaultValue') ? 'omitted — declared default applies' : 'omitted', declaration.required ? ' · required' : ''] })] }, declaration.name); }), (executionPackage.runtimeRequirements ?? []).map(name => _jsx(InputField, { label: `Runtime · ${name}`, value: runtimeValues[name] ?? '', onChange: value => { if (!activeInputRequest.current)
|
|
448
|
+
setRuntimeValues(previous => ({ ...previous, [name]: value })); } }, name))] })] })] }), readOnly && historicalEntry && _jsxs("div", { className: "history-view-banner", children: ["Viewing stored results: ", historyRecordName(historicalEntry), " \u00B7 ", historicalEntry.state.toUpperCase(), " \u00B7 ", new Date(historicalEntry.startedAt).toLocaleString()] }), _jsxs("section", { className: "panel", hidden: readOnly && !historicalEntry, children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: readOnly ? 'Recorded execution' : 'Live Execution' }), _jsx("span", { className: "panel-spacer" }), _jsxs("span", { className: "execution-menu-label", hidden: readOnly, children: ["Right-click a row to manage its breakpoint \u00B7 Selected: ", displaySteps[shownSelectedStep]?.name ?? 'None'] }), _jsxs("span", { className: shownRunState === 'running' ? 'running' : shownRunState === 'failed' ? 'stopped' : 'run-status', children: ["\u25CF ", statusLabel] })] }), hasNestedSteps && _jsxs("div", { className: "hierarchy-hint", children: [_jsx("strong", { children: "Nested execution" }), _jsx("span", { children: "Use \u2212 / + to collapse or expand nested Operations, Control Blocks, and loops." }), _jsx("button", { className: "hierarchy-action", onClick: () => setAllCollapsed(true), children: "Collapse all" }), _jsx("button", { className: "hierarchy-action", onClick: () => setAllCollapsed(false), children: "Expand all" })] }), _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Execution steps", tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "#" }), _jsx("th", { children: "Step hierarchy" }), _jsx("th", { children: "Status" }), _jsx("th", { children: "Start time" }), _jsx("th", { children: "Duration" })] }) }), _jsx("tbody", { children: displaySteps.map((step, index) => { const isLoop = step.loopRepeat; const loopContexts = isLoop ? loopPresentation(step.structuralAddress, step.loopTotal, step.loopDuration, shownResult ? allResults : eventPresentationEvidence(events, requestId), Boolean(shownResult), step.occurrence) : []; const rowEvidence = shownResult ? allResults : eventPresentationEvidence(events, requestId); const repeatedMatches = rowEvidence.filter(r => step.occurrence ? r.occurrence === step.occurrence : occurrenceKey(r.structuralAddress) === occurrenceKey(step.structuralAddress)); const latestQualifier = step.branchRow ? rowEvidence.filter(r => occurrenceKey(r.structuralAddress) === occurrenceKey(step.branchOwner)).length > 1 : repeatedMatches.length > 1; const scope = readOnly ? historicalEntry?.structure?.scope : preparedDocument?.selection.root; const context = readOnly ? historicalEntry?.structure?.context : preparedDocument?.context; const outside = Boolean(step.occurrence && scope && context && !scopeIncludes(context, scope, step.occurrence)); const presentation = rowPresentation(step, rowEvidence, Boolean(shownResult), step.branchRow ? 'pending' : readOnly ? 'pending' : stepStates[index] ?? 'pending', { context, scope, result: shownResult }); const displayState = presentation.state; const isLoopContent = Boolean(step.loopId); const isLoopEnd = isLoopContent && displaySteps[index + 1]?.loopId !== step.loopId; return _jsxs("tr", { className: `${outside ? 'outside-scope ' : ''}${shownSelectedStep === index ? 'selected ' : ''}${isLoop ? 'loop-row ' : ''}${isLoopContent ? 'loop-content ' : ''}${isLoopEnd ? 'loop-end ' : ''}${step.depth > 0 ? 'nested-row' : 'parent-row'}`, tabIndex: 0, onKeyDown: event => { if (event.key === "ArrowDown") {
|
|
449
|
+
event.preventDefault();
|
|
450
|
+
selectDisplayedStep(Math.min(displaySteps.length - 1, index + 1));
|
|
451
|
+
} if (event.key === "ArrowUp") {
|
|
452
|
+
event.preventDefault();
|
|
453
|
+
selectDisplayedStep(Math.max(0, index - 1));
|
|
454
|
+
} }, onClick: () => selectDisplayedStep(index), onContextMenu: event => { event.preventDefault(); if (readOnly)
|
|
455
|
+
return; selectDisplayedStep(index); setContextMenu({ x: event.clientX, y: event.clientY, stepId: step.id }); }, children: [_jsx("td", { children: index + 1 }), _jsxs("td", { className: "hierarchy-cell", children: [_jsx(HierarchyGuides, { segments: guides.get(step.id) ?? [] }), _jsxs("span", { className: "hierarchy-name", style: { paddingLeft: `${step.depth * 18}px` }, children: [step.hasChildren && _jsx("button", { className: "collapse-toggle", "aria-label": `${(readOnly ? historyCollapsedSteps : collapsedSteps).has(step.id) ? 'Expand' : 'Collapse'} ${step.name}`, onClick: event => { event.stopPropagation(); toggleStep(step.id); }, children: (readOnly ? historyCollapsedSteps : collapsedSteps).has(step.id) ? '+' : '−' }), !step.hasChildren && _jsx("span", { className: "collapse-spacer", "aria-hidden": "true" }), _jsx(StructuralIcon, { kind: step.structuralKind }), !readOnly && breakpoints.has(step.id) && _jsx("span", { className: "breakpoint-marker", role: "img", "aria-label": "Breakpoint armed", title: "Breakpoint armed" }), _jsx("span", { children: step.name }), isLoop && _jsx("small", { className: "kind-label loop-label", children: "\u21BB LOOP" }), step.structuralKind === 'branch' && _jsx("small", { className: "kind-label", children: "BRANCH" }), (!step.structuralKind || step.structuralKind === 'unknown') && _jsx("small", { className: "kind-label", children: "TYPE NOT RECORDED" })] }), shownSelectedStep === index && _jsx("small", { className: "selected-label", children: "SELECTED" })] }), _jsxs("td", { children: [!outside && loopContexts.map(context => _jsxs("div", { className: "loop-progress", children: [context.context.length > 0 && _jsxs("small", { children: ["Enclosing iterations: ", context.context.map(s => s.iteration).join(' / ')] }), _jsx("strong", { children: context.label }), _jsx("small", { children: context.state.toUpperCase() })] }, JSON.stringify(context.context))), _jsxs("span", { className: "execution-status", children: [_jsx("span", { className: `step-icon execution-status-icon ${displayState.replaceAll(' ', '-')}`, "aria-hidden": "true", children: statusGlyph[displayState] }), _jsx("span", { className: `status ${displayState.replaceAll(' ', '-')}`, children: presentation.label })] }), latestQualifier && _jsx("small", { className: "latest-invocation", children: "Latest invocation" })] }), _jsx("td", { children: displayResults[index] ? new Date(shownResult?.finishedAt ?? '').toLocaleTimeString() : '—' }), _jsx("td", { children: displayResults[index] ? `${displayResults[index].durationMs} ms` : '—' })] }, `${step.id}-${index}`); }) })] }) }), !readOnly && contextMenu && _jsxs("div", { className: "runner-context-menu", style: { left: contextMenu.x, top: contextMenu.y }, onClick: event => event.stopPropagation(), onContextMenu: event => event.preventDefault(), children: [_jsx("strong", { children: "Breakpoint" }), breakpoints.has(contextMenu.stepId) ? _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Remove breakpoint" }) : _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Add breakpoint" })] })] }), !readOnly && _jsxs("details", { className: "panel runner-event-log execution-event-log", children: [_jsx("summary", { children: "Event Log" }), _jsxs("div", { className: "event-log-content", children: [_jsxs("div", { className: "event-log-tools", children: [_jsx("input", { className: "event-search", "aria-label": "Search event log", placeholder: "Search events", value: eventSearch, onChange: event => setEventSearch(event.target.value) }), _jsxs("select", { "aria-label": "Filter execution events", className: "event-filter", value: eventFilter, onChange: event => setEventFilter(event.target.value), children: [_jsx("option", { value: "all", children: "All events" }), _jsx("option", { value: "steps", children: "Step events" }), _jsx("option", { value: "parallel", children: "Parallel groups" }), _jsx("option", { value: "lifecycle", children: "Lifecycle" }), _jsx("option", { value: "failures", children: "Failures" })] })] }), _jsx("div", { className: "event-log-scroll", role: "region", "aria-label": "Execution events", tabIndex: 0, children: _jsx("ul", { className: "event-log-list", children: visibleEvents.map(event => _jsx("li", { children: _jsxs("button", { type: "button", className: "event-row", children: [_jsxs("span", { className: "event-row-meta", children: [_jsx("time", { dateTime: event.timestamp, children: new Date(event.timestamp).toLocaleTimeString() }), _jsx("code", { children: event.type.toUpperCase() }), _jsx("span", { className: "event-state", children: event.state.toUpperCase() })] }), _jsx("span", { className: "event-message", children: event.message }), event.parallel && _jsxs("small", { className: "parallel-event", children: ["Parallel ", event.parallel.completed, "/", event.parallel.total, " (", event.parallel.join, ")"] })] }) }, event.sequence)) }) }), executionEvents.length > 0 && visibleEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No events match the current filter." }), executionEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No execution events yet." })] })] }, JSON.stringify(['execution', requestId ?? 'ready', selectedTargetId]))] }), _jsx("div", { className: "inspector-divider", role: "separator", "aria-label": "Resize Inspector", "aria-orientation": "vertical", "aria-controls": "runner-inspector", "aria-valuemin": INSPECTOR_MIN_WIDTH, "aria-valuemax": INSPECTOR_MAX_WIDTH, "aria-valuenow": inspectorWidth, "aria-valuetext": `${inspectorWidth} pixels`, tabIndex: 0, hidden: readOnly && !historicalEntry, onPointerDown: startInspectorResize, onPointerMove: moveInspectorResize, onPointerUp: finishInspectorResize, onPointerCancel: finishInspectorResize, onLostPointerCapture: finishInspectorResize, onKeyDown: inspectorResizeKey }), _jsxs("aside", { id: "runner-inspector", className: "inspector", hidden: readOnly && !historicalEntry, children: [_jsx("h2", { children: "Selected execution" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[shownSelectedStep]?.name ?? 'No step selected' }) }), _jsxs("p", { className: "run-id", children: ["Run ID: ", shownRequestId ?? 'No run started'] }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { values: ["Target", (readOnly ? historicalEntry?.result.target.name : executionPackage.target.name) ?? 'Unknown', "Type", (readOnly ? historicalEntry?.result.target.kind : executionPackage.target.kind) ?? 'Unknown', "Project reference", (readOnly ? historicalEntry?.packageId : executionPackage.packageId) ?? 'Not recorded', "Execution format", `Execution v${readOnly ? historicalEntry?.executionSchemaVersion ?? 'unknown' : preparedDocument?.schemaVersion ?? executionPackage.schemaVersion}`, "Definitions", shownPackage ? String(shownPackage.definitions.length) : 'Source Project unavailable', "Required inputs", shownPackage ? shownPackage.requiredInputs?.join(', ') || 'None' : 'Not recorded', "Runtime values", shownPackage ? shownPackage.runtimeRequirements?.join(', ') || 'None' : 'Not recorded'] }) }), _jsx(InspectorSection, { title: "Run outcome", children: _jsxs("div", { className: "notice", children: [_jsx("strong", { children: shownResult?.state.toUpperCase() ?? 'NOT STARTED' }), _jsx("br", {}), shownResult ? shownResult.state : 'Run the Project to inspect its terminal outcome.'] }) }), _jsx(InspectorSection, { title: "Selected step inputs", children: _jsx(KeyValues, { values: Object.entries(displayResults[shownSelectedStep]?.inputs ?? (selectedInvocations.length > 1 ? {} : readOnly ? {} : inputs)).flatMap(([key, value]) => [key, String(value)]) }) }), _jsx(InspectorSection, { title: "Selected step outputs", children: _jsx(KeyValues, { values: displayResults[shownSelectedStep]?.outputs ? Object.entries(displayResults[shownSelectedStep]?.outputs ?? {}).flatMap(([key, value]) => [key, String(value)]) : ['—', selectedInvocations.length > 1 ? `${selectedInvocations.length} recorded invocations; see iteration history` : 'Available after execution'] }) }), (shownResult?.error || displayResults[shownSelectedStep]?.error) && _jsx(InspectorSection, { title: "Error", children: _jsxs("div", { className: "error-notice", children: [_jsx("strong", { children: displayResults[shownSelectedStep]?.error?.code ?? shownResult?.error?.code }), _jsx("br", {}), displayResults[shownSelectedStep]?.error?.message ?? shownResult?.error?.message] }) }), shownResult?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: shownResult.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: _jsx("div", { className: "notice", children: displayResults[shownSelectedStep]?.invocationPath?.join(" › ") ?? "No invocation path available." }) }), _jsx(InspectorSection, { title: "Iteration history", children: _jsx(RecordedInvocations, { results: selectedInvocations, definition: findInspectionDefinition(shownPackageSteps, displaySteps[shownSelectedStep]?.structuralAddress) }, JSON.stringify([shownRequestId, displaySteps[shownSelectedStep]?.structuralAddress])) }), _jsx(InspectorSection, { title: "Execution timing", children: _jsxs("div", { className: "timing-details", children: [_jsxs("span", { children: ["Wall ", shownResult?.executionTiming?.wallDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Active ", shownResult?.executionTiming?.activeDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Paused ", shownResult?.executionTiming?.pausedDurationMs ?? "—", " ms"] })] }) }), _jsx(InspectorSection, { title: "Selected step attempts", children: _jsx("div", { className: "attempt-history", children: displayResults[shownSelectedStep]?.attempts?.map(attempt => _jsxs("div", { className: "iteration-entry", children: [_jsxs("strong", { children: ["Attempt ", attempt.attempt] }), _jsxs("span", { children: [(attempt.kind ?? 'automatic').toUpperCase(), " \u00B7 ", attempt.state.toUpperCase(), " \u00B7 ", attempt.durationMs, " ms"] })] }, `${attempt.attempt}-${attempt.kind}`)) ?? _jsx("div", { className: "notice", children: "No retry attempts recorded." }) }) }), readOnly && _jsxs("details", { className: "inspector-section inspector-event-log runner-event-log", children: [_jsx("summary", { children: "Event Log" }), _jsx("div", { className: "event-log-content", children: _jsx("p", { className: "empty-state", children: "Recorded events are unavailable for this run." }) })] }, JSON.stringify(['history', shownRequestId ?? 'ready', historicalEntry?.packageId])), _jsxs(InspectorSection, { title: "Selected step", children: [_jsxs("div", { className: "notice", children: [_jsx("strong", { children: displaySteps[shownSelectedStep]?.name ?? 'No step selected' }), _jsx("br", {}), displayResults[shownSelectedStep] ? displayResults[shownSelectedStep].error ? 'This step failed. See the error details above.' : `Completed in ${displayResults[shownSelectedStep].durationMs} ms.` : selectedInvocations.length > 1 ? `${selectedInvocations.length} invocations recorded. No single invocation selected.` : 'Run the Project to inspect execution details.', _jsxs("div", { className: "inspector-actions", children: [_jsx("button", { className: "button", onClick: () => void retrySelected(), disabled: readOnly || !!preparedDocument || !displayResults[shownSelectedStep] || (!displayResults[shownSelectedStep]?.error && runState !== "completed"), children: "Retry Action" }), _jsx("button", { className: `button ${stopAfterStepId === displaySteps[shownSelectedStep]?.id ? "active" : ""}`, onClick: () => displaySteps[shownSelectedStep] && toggleStopAfter(displaySteps[shownSelectedStep].id), disabled: readOnly || !displaySteps[shownSelectedStep], children: "Stop After" }), _jsx("button", { className: "button", onClick: () => void runFromSelected(), disabled: readOnly || !!preparedDocument || !displayResults[shownSelectedStep] || runState === "running", children: "Run From Here" })] })] }), _jsxs("div", { className: "selection-nav", children: [_jsx("button", { className: "button", disabled: shownSelectedStep <= 0, onClick: () => selectDisplayedStep(index => Math.max(0, index - 1)), children: "\u2039 Previous" }), _jsx("span", { children: displaySteps.length ? `${shownSelectedStep + 1} / ${displaySteps.length}` : '0 / 0' }), _jsx("button", { className: "button", disabled: shownSelectedStep >= displaySteps.length - 1, onClick: () => selectDisplayedStep(index => Math.min(displaySteps.length - 1, index + 1)), children: "Next \u203A" })] })] })] })] }), " "] }) }), showAbout && _jsx("div", { className: "about-backdrop", onClick: () => setShowAbout(false), children: _jsxs("section", { className: "about-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "about-title", onClick: event => event.stopPropagation(), children: [_jsx("button", { className: "about-close", onClick: () => setShowAbout(false), "aria-label": "Close About", children: "\u00D7" }), _jsx("div", { className: "runner-logo", children: "\u25B6" }), _jsx("h2", { id: "about-title", children: "QAick Runner" }), _jsx("p", { children: "Standalone execution and evidence viewer for QAICK Projects." }), _jsxs("small", { children: [RUNNER_UI_VERSION, " \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
|
|
456
|
+
}
|
|
457
|
+
function recordedDisplaySteps(results) {
|
|
458
|
+
const unique = new Map();
|
|
459
|
+
for (const step of results) {
|
|
460
|
+
const key = step.occurrence ?? occurrenceKey(step.structuralAddress);
|
|
461
|
+
if (!unique.has(key))
|
|
462
|
+
unique.set(key, { occurrence: step.occurrence, id: step.occurrence ?? step.id, name: step.name, kind: step.kind ?? 'action', structuralKind: step.kind, structuralAddress: step.structuralAddress, depth: Math.max(0, (step.structuralAddress?.segments.filter(segment => segment.kind === 'step').length ?? 1) - 1), hasChildren: false });
|
|
463
|
+
}
|
|
464
|
+
return [...unique.values()];
|
|
465
|
+
}
|
|
466
|
+
function historyRecordName(entry) { const context = entry.structure?.context; return context?.nodes.find(node => node.key === context.root)?.name ?? entry.target; }
|
|
467
|
+
function HistoryPanel({ entries, retention, onRetentionChange, onClear, onDelete, onExport, onReload }) {
|
|
468
|
+
return _jsxs("section", { className: "panel history-panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Run History" }), _jsx("span", { className: "panel-spacer" }), _jsxs("label", { className: "history-retention", children: ["Keep", _jsxs("select", { "aria-label": "Run history retention", value: retention, onChange: event => onRetentionChange(Number(event.target.value)), children: [_jsx("option", { value: "10", children: "10 runs" }), _jsx("option", { value: "20", children: "20 runs" }), _jsx("option", { value: "50", children: "50 runs" }), _jsx("option", { value: "100", children: "100 runs" })] })] }), _jsx("button", { className: "button", onClick: onClear, disabled: !entries.length, children: "Clear history" })] }), entries.length ? _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Stored runs", tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Project" }), _jsx("th", { children: "Started" }), _jsx("th", { children: "Outcome" }), _jsx("th", { children: "Duration" }), _jsx("th", { children: "Result" })] }) }), _jsx("tbody", { children: entries.map(entry => {
|
|
469
|
+
const context = entry.structure?.context;
|
|
470
|
+
const projectName = historyRecordName(entry);
|
|
471
|
+
const scope = entry.structure?.scope;
|
|
472
|
+
const scopeName = !scope ? 'Scope not recorded' : scope.kind === 'document' ? 'Entire project' : context?.nodes.find(node => node.key === scope.key)?.name ?? 'Unavailable';
|
|
473
|
+
const runKind = entry.result.provenance ? 'Run From Here' : entry.result.steps.some(step => step.attempts?.some(attempt => attempt.kind === 'interactive')) ? 'Interactive retry' : 'Normal run';
|
|
474
|
+
return _jsxs("tr", { children: [_jsxs("td", { children: [_jsx("strong", { className: "history-project-name", children: projectName }), _jsxs("small", { className: "history-scope", children: [scope ? `Run only: ${scopeName}` : scopeName, runKind !== 'Normal run' && ` · ${runKind}`] }), entry.structure && entry.historyVersion !== 2 && _jsxs("small", { className: "history-meta", children: [entry.structure.context.coverage, " \u00B7 hash ", entry.structure.documentHash] })] }), _jsx("td", { children: _jsx("time", { dateTime: entry.startedAt, children: new Date(entry.startedAt).toLocaleString() }) }), _jsx("td", { children: _jsx("span", { className: `status ${entry.state}`, children: entry.state.toUpperCase() }) }), _jsxs("td", { children: [entry.durationMs, " ms"] }), _jsxs("td", { children: [_jsx("button", { className: "button", onClick: () => onReload(entry), children: "View results" }), _jsx("button", { className: "button", onClick: () => onExport(entry.result, entry.packageId, entry.artifactProvenance?.originalVersion, entry.artifactProvenance, entry.executionSchemaVersion), children: "Export JSON" }), _jsx("button", { className: "button history-delete", onClick: () => onDelete(entry.requestId), children: "Delete" })] })] }, entry.requestId);
|
|
475
|
+
}) })] }) }) : _jsx("p", { className: "empty-state", children: "Completed and failed runs will be kept here in this browser." })] });
|
|
303
476
|
}
|
|
304
|
-
function NavItem({ icon, text, active = false }) { const scrollTarget = text === 'Run History' ? '.history-panel' : text === 'Flows' ? '.runner-workspace table' : '.runner-workspace'; return _jsxs("a", { className: `nav-item ${active ? 'active' : ''}`, href: `#${text.toLowerCase().replaceAll(' ', '-')}`, onClick: event => { event.preventDefault(); document.querySelector(scrollTarget)?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, children: [_jsx("span", { children: icon }), text] }); }
|
|
305
|
-
function HistoryPanel({ entries, retention, onRetentionChange, onClear, onDelete, onExport, onReload }) { return _jsxs("section", { className: "panel history-panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Run History" }), _jsx("span", { className: "panel-spacer" }), _jsxs("label", { className: "history-retention", children: ["Keep", _jsxs("select", { "aria-label": "Run history retention", value: retention, onChange: event => onRetentionChange(Number(event.target.value)), children: [_jsx("option", { value: "10", children: "10 runs" }), _jsx("option", { value: "20", children: "20 runs" }), _jsx("option", { value: "50", children: "50 runs" }), _jsx("option", { value: "100", children: "100 runs" })] })] }), _jsx("button", { className: "button", onClick: onClear, disabled: !entries.length, children: "Clear history" })] }), entries.length ? _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Stored runs", tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Target" }), _jsx("th", { children: "Started" }), _jsx("th", { children: "State" }), _jsx("th", { children: "Duration" }), _jsx("th", { children: "Result" })] }) }), _jsx("tbody", { children: entries.map(entry => _jsxs("tr", { children: [_jsxs("td", { children: [entry.target, _jsxs("small", { className: "history-meta", children: [entry.result.provenance ? 'Run From Here' : entry.result.steps.some(step => step.attempts?.some(attempt => attempt.kind === 'interactive')) ? 'Interactive retry' : 'Normal run', " \u00B7 ", entry.packageId ?? 'legacy result', " \u00B7 Artifact v", entry.artifactProvenance?.originalVersion ?? 'unknown', " \u00B7 Execution v", entry.executionSchemaVersion ?? 'unknown', _jsx("br", {}), "hash ", entry.result.artifactHash ?? 'legacy result'] })] }), _jsx("td", { children: new Date(entry.startedAt).toLocaleString() }), _jsx("td", { children: _jsx("span", { className: `status ${entry.state}`, children: entry.state.toUpperCase() }) }), _jsxs("td", { children: [entry.durationMs, " ms"] }), _jsxs("td", { children: [_jsx("button", { className: "button", onClick: () => onReload(entry), children: "View results" }), _jsx("button", { className: "button", onClick: () => onExport(entry.result, entry.packageId, entry.artifactProvenance?.originalVersion, entry.artifactProvenance, entry.executionSchemaVersion), children: "Export JSON" }), _jsx("button", { className: "button history-delete", onClick: () => onDelete(entry.requestId), children: "Delete" })] })] }, entry.requestId)) })] }) }) : _jsx("p", { className: "empty-state", children: "Completed and failed runs will be kept here in this browser." })] }); }
|
|
306
477
|
function InspectorSection({ title, children }) { return _jsxs("section", { className: "inspector-section", children: [_jsx("h3", { children: title }), children] }); }
|
|
307
478
|
function KeyValues({ values }) { return _jsx("div", { className: "key-values", children: redactKeyValuePairs(values).map((value, index) => _jsx("span", { children: value }, `${value}-${index}`)) }); }
|
|
308
479
|
function IterationHistory({ result }) { return _jsxs(_Fragment, { children: [_jsx("div", { className: "iteration-history", children: result?.children?.length ? result.children.map((iteration, index) => _jsxs("div", { className: "iteration-entry", children: [_jsx("strong", { children: iteration.name }), _jsxs("span", { children: [iteration.state.toUpperCase(), " \u00B7 ", iteration.durationMs, " ms"] }), _jsx("small", { children: iteration.children?.map(child => `${child.name}: ${Object.entries(child.outputs).map(([key, value]) => `${key}=${String(value)}`).join(', ') || 'no outputs'}`).join(' · ') || 'No nested output.' })] }, `${iteration.id}-${index}`)) : _jsx("div", { className: "notice", children: "No iteration history available." }) }), result?.terminalReason && _jsxs("div", { className: "timing-details", children: [_jsx("strong", { children: "Terminal reason" }), _jsx("span", { children: result.terminalReason })] }), _jsx(TransportTiming, { timing: result?.transportTiming }), _jsx(BranchExecutionPath, { result: result }), _jsx(ParallelGroupDetails, { result: result }), _jsx(ResultSummary, { result: result })] }); }
|
|
@@ -314,7 +485,7 @@ function ParallelGroupDetails({ result }) { const group = result?.terminalReason
|
|
|
314
485
|
function ResultSummary({ result }) { if (!result)
|
|
315
486
|
return null; const steps = flattenResultSteps([result]); const counts = Object.fromEntries(['finished', 'failed', 'skipped', 'cancelled'].map(state => [state, steps.filter(step => step.state === state).length])); return _jsxs("div", { className: "result-summary", children: [_jsx("strong", { children: "Result summary" }), _jsxs("span", { children: [steps.length, " invocations \u00B7 ", result.durationMs, " ms"] }), _jsxs("small", { children: ["Finished ", counts.finished, " \u00B7 Failed ", counts.failed, " \u00B7 Skipped ", counts.skipped, " \u00B7 Cancelled ", counts.cancelled] })] }); }
|
|
316
487
|
function PreRunValidation({ issues, title = 'Pre-run validation' }) { if (!issues.length)
|
|
317
|
-
return _jsxs("section", { className: "validation-panel validation-pass", children: [_jsx("span", { className: "validation-icon", children: "\u2713" }), _jsxs("div", { children: [_jsxs("strong", { children: [title, " passed"] }), _jsx("small", { children: "No issues found. The next Run can invoke adapters." })] })] }); return _jsxs("section", { className: "validation-panel validation-failed", children: [_jsxs("div", { className: "validation-heading", children: [_jsx("span", { className: "validation-icon", children: "!" }), _jsxs("div", { children: [_jsxs("strong", { children: [title, " found ", issues.length, " issue", issues.length === 1 ? '' : 's'] }), _jsx("small", { children: "Resolve these items before running the
|
|
488
|
+
return _jsxs("section", { className: "validation-panel validation-pass", children: [_jsx("span", { className: "validation-icon", children: "\u2713" }), _jsxs("div", { children: [_jsxs("strong", { children: [title, " passed"] }), _jsx("small", { children: "No issues found. The next Run can invoke adapters." })] })] }); return _jsxs("section", { className: "validation-panel validation-failed", children: [_jsxs("div", { className: "validation-heading", children: [_jsx("span", { className: "validation-icon", children: "!" }), _jsxs("div", { children: [_jsxs("strong", { children: [title, " found ", issues.length, " issue", issues.length === 1 ? '' : 's'] }), _jsx("small", { children: "Resolve these items before running the Project." })] })] }), _jsx("div", { className: "validation-issues", children: issues.map((issue, index) => _jsxs("div", { className: "validation-issue", "data-step-id": issue.stepId, children: [_jsx("span", { className: "issue-icon", children: "\u00D7" }), _jsxs("div", { children: [_jsxs("strong", { children: [diagnosticCategory(issue.code), " \u00B7 ", issue.code] }), _jsx("span", { children: issue.message }), issue.path && _jsxs("small", { children: ["Path: ", issue.path] }), issue.stepId && _jsxs("small", { children: ["Step: ", issue.stepId, " \u00B7 click to inspect"] })] })] }, `${issue.code}-${issue.stepId ?? issue.path ?? 'run'}-${index}`)) })] }); }
|
|
318
489
|
function diagnosticCategory(code) { if (code === 'CONTRACT_INVALID' || code === 'UNSUPPORTED_SCHEMA')
|
|
319
490
|
return 'contract-invalid'; if (code === 'INVALID_BINDING' || code === 'MISSING_INPUT_BINDING' || code === 'MISSING_STEP_BINDING' || code === 'MISSING_STEP_OUTPUT' || code === 'BINDING_FAILED')
|
|
320
491
|
return 'binding-unresolved'; if (code.startsWith('HTTP_') || code === 'ACTION_FAILED' || code === 'MISSING_ACTION_ADAPTER')
|
|
@@ -326,11 +497,11 @@ function flattenDisplaySteps(steps, depth = 0, loopId, parentId, collapsed = new
|
|
|
326
497
|
const isLoop = step.control?.type === 'repeat';
|
|
327
498
|
const currentLoopId = isLoop ? step.id : loopId;
|
|
328
499
|
const branchMode = (step.control?.type === 'branch' || step.control?.type === 'parallel') && step.control.branches?.length;
|
|
329
|
-
const row = { structuralAddress: step.structuralAddress, id: step.id, name: step.name, depth, kind: step.kind ?? (step.children ? 'operation' : 'action'), loopRepeat: isLoop, loopTotal: step.control?.iterations, loopDuration: step.control?.durationMs, loopId: currentLoopId, parentId, hasChildren: Boolean(step.children?.length || branchMode) };
|
|
500
|
+
const row = { structuralAddress: step.structuralAddress, id: step.id, name: step.name, depth, kind: step.kind ?? (step.children ? 'operation' : 'action'), structuralKind: step.kind, loopRepeat: isLoop, loopTotal: step.control?.iterations, loopDuration: step.control?.durationMs, loopId: currentLoopId, parentId, hasChildren: Boolean(step.children?.length || branchMode) };
|
|
330
501
|
if (branchMode && !collapsed.has(step.id)) {
|
|
331
502
|
const branchRows = step.control.branches.flatMap((branch, branchIndex) => {
|
|
332
503
|
const branchId = `${step.id}.${branch.id}`;
|
|
333
|
-
const branchRow = { id: branchId, name: branch.name ?? branch.id, depth: depth + 1, kind: 'control-block', loopId: currentLoopId, parentId: step.id, hasChildren: branch.children.length > 0, branchRow: true, branchOwner: step.structuralAddress, branchPosition: branchIndex + 1 };
|
|
504
|
+
const branchRow = { id: branchId, name: branch.name ?? branch.id, depth: depth + 1, kind: 'control-block', structuralKind: 'branch', loopId: currentLoopId, parentId: step.id, hasChildren: branch.children.length > 0, branchRow: true, branchOwner: step.structuralAddress, branchPosition: branchIndex + 1 };
|
|
334
505
|
return [branchRow, ...(collapsed.has(branchId) ? [] : flattenDisplaySteps(branch.children, depth + 2, currentLoopId, branchId, collapsed))];
|
|
335
506
|
});
|
|
336
507
|
return [row, ...branchRows];
|
|
@@ -340,8 +511,8 @@ function flattenDisplaySteps(steps, depth = 0, loopId, parentId, collapsed = new
|
|
|
340
511
|
}
|
|
341
512
|
export function orderDisplayStepsByResult(steps, _results) { return steps; }
|
|
342
513
|
export const occurrenceKey = (address) => address ? JSON.stringify([address.scope, address.segments]) : '';
|
|
343
|
-
export function resultsForOccurrence(results, address) { return results.filter(r => address && occurrenceKey(r.structuralAddress) === occurrenceKey(address)); }
|
|
344
|
-
export function resultForOccurrence(results, address) { const matches = resultsForOccurrence(results, address); return matches.length === 1 ? matches[0] : undefined; }
|
|
514
|
+
export function resultsForOccurrence(results, address, occurrence) { return results.filter(r => occurrence ? r.occurrence === occurrence : address && occurrenceKey(r.structuralAddress) === occurrenceKey(address)); }
|
|
515
|
+
export function resultForOccurrence(results, address, occurrence) { const matches = resultsForOccurrence(results, address, occurrence); return matches.length === 1 ? matches[0] : undefined; }
|
|
345
516
|
export function stateForOccurrence(results, address) {
|
|
346
517
|
const matches = resultsForOccurrence(results, address);
|
|
347
518
|
if (!matches.length)
|
|
@@ -379,4 +550,9 @@ export function RecordedInvocations({ results, definition }) {
|
|
|
379
550
|
}
|
|
380
551
|
function containerStepIds(steps) { return steps.flatMap(step => { const branchIds = step.control?.branches?.flatMap(branch => [`${step.id}.${branch.id}`, ...containerStepIds(branch.children)]) ?? []; return step.children?.length || branchIds.length ? [step.id, ...branchIds, ...(step.children ? containerStepIds(step.children) : [])] : []; }); }
|
|
381
552
|
function flattenResultSteps(steps) { return steps.flatMap(step => [step, ...(step.children ? flattenResultSteps(step.children) : [])]); }
|
|
553
|
+
function contextDisplaySteps(context, collapsed, document) {
|
|
554
|
+
const byKey = new Map(context.nodes.map(n => [n.key, n]));
|
|
555
|
+
function visit(key, depth, parentId) { const n = byKey.get(key); const visible = !['project', 'body'].includes(n.kind); const payload = document?.nodes.find(p => p.occurrence === key); const row = { parentId, structuralKind: n.kind, id: key, occurrence: key, name: n.name, kind: n.kind === 'project' ? 'flow' : n.kind === 'unknown' ? 'action' : n.kind, depth, structuralAddress: n.originalAddress, hasChildren: n.children.length > 0, loopRepeat: payload?.control?.type === 'repeat', loopTotal: payload?.control?.iterations, loopDuration: payload?.control?.durationMs }; return [...(visible ? [row] : []), ...(collapsed.has(key) ? [] : [...(n.sequence ?? []), ...n.children.filter(child => !n.sequence?.includes(child))].flatMap(child => visit(child, depth + (visible ? 1 : 0), visible ? key : parentId)))]; }
|
|
556
|
+
return visit(context.root, 0);
|
|
557
|
+
}
|
|
382
558
|
//# sourceMappingURL=RunnerModule.js.map
|