@qaick/runner 0.32.7 → 0.32.11
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/fixtures/earlier-flow-loop.qaick.json +230 -0
- package/package-dist/fixtures/project-shared-zero.qaick.json +343 -0
- package/package-dist/package.json +4 -4
- package/package-dist/src/runner/RunnerModule.d.ts.map +1 -1
- package/package-dist/src/runner/RunnerModule.js +130 -34
- package/package-dist/src/runner/RunnerModule.js.map +1 -1
- package/package-dist/src/runner/history.d.ts +5 -2
- package/package-dist/src/runner/history.d.ts.map +1 -1
- package/package-dist/src/runner/history.js +11 -3
- package/package-dist/src/runner/history.js.map +1 -1
- package/package-dist/src/runner/studio-execution-document.d.ts.map +1 -1
- package/package-dist/src/runner/studio-execution-document.js +4 -2
- package/package-dist/src/runner/studio-execution-document.js.map +1 -1
- package/package-dist/src/runner/studio-hierarchy-loader.js +1 -1
- package/package-dist/src/runner/studio-hierarchy-loader.js.map +1 -1
- package/package.json +4 -4
|
@@ -20,7 +20,7 @@ import { RUNNER_BUILD_LABEL, RUNNER_UI_VERSION } from "../build-info.js";
|
|
|
20
20
|
import { collectSecretValues, redactExecutionEvent, redactExecutionResult, redactKeyValuePairs } from "./redaction.js";
|
|
21
21
|
import { createResultExport } from "./result-export.js";
|
|
22
22
|
import { inspectDeclaredInputs, inputDrafts, resolvedArguments } from "./invocation-inputs.js";
|
|
23
|
-
import { persistRunHistory, captureHistoryStructure, assertHistoryIdentity } from "./history.js";
|
|
23
|
+
import { persistRunHistory, redactHistoryEntry, captureHistoryStructure, assertHistoryIdentity } from "./history.js";
|
|
24
24
|
import { prepareStudioDocument } from "./studio-execution-document.js";
|
|
25
25
|
const statusGlyph = { pending: '', 'not executed': '', running: '', finished: '✓', failed: '✕', waiting: '◷', skipped: '–', cancelled: '⊘' };
|
|
26
26
|
const HISTORY_KEY = 'qaick-runner.history.v1';
|
|
@@ -173,7 +173,32 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
173
173
|
const [eventFilter, setEventFilter] = useState('all');
|
|
174
174
|
const [eventSearch, setEventSearch] = useState('');
|
|
175
175
|
const [runHistory, setRunHistory] = useState(readRunHistory);
|
|
176
|
+
const historyRef = useRef(runHistory);
|
|
177
|
+
const [historySaveError, setHistorySaveError] = useState();
|
|
178
|
+
function updateHistory(entries) { historyRef.current = entries; setRunHistory(entries); }
|
|
179
|
+
function saveHistoryEntry(entry, secretValues, replace = false) {
|
|
180
|
+
const previous = historyRef.current;
|
|
181
|
+
const index = previous.findIndex(item => item.requestId === entry.requestId);
|
|
182
|
+
if (replace && index < 0)
|
|
183
|
+
return; // Respect deletion/retention while collection was finishing.
|
|
184
|
+
const safe = redactHistoryEntry(entry, secretValues);
|
|
185
|
+
const next = replace ? previous.map((item, position) => position === index ? safe : item) : [safe, ...previous.filter(item => item.requestId !== entry.requestId)];
|
|
186
|
+
try {
|
|
187
|
+
updateHistory(persistRunHistory(localStorage, HISTORY_KEY, next, retentionRef.current, secretValues, entry.requestId));
|
|
188
|
+
setHistorySaveError(undefined);
|
|
189
|
+
if (replace)
|
|
190
|
+
setHistoricalEntry(previous => previous?.requestId === entry.requestId ? safe : previous);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
updateHistory(next);
|
|
194
|
+
if (replace)
|
|
195
|
+
setHistoricalEntry(previous => previous?.requestId === entry.requestId ? safe : previous);
|
|
196
|
+
setHistorySaveError('History could not be saved. Execution evidence remains available in this session. Export JSON before closing Runner.');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
176
199
|
const [historyRetention, setHistoryRetention] = useState(readHistoryRetention);
|
|
200
|
+
const retentionRef = useRef(historyRetention);
|
|
201
|
+
retentionRef.current = historyRetention;
|
|
177
202
|
const [result, setResult] = useState();
|
|
178
203
|
const [workspace, setWorkspace] = useState('execution');
|
|
179
204
|
const [historicalEntry, setHistoricalEntry] = useState();
|
|
@@ -201,12 +226,12 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
201
226
|
const shownRunState = readOnly ? historicalEntry?.state ?? 'ready' : runState;
|
|
202
227
|
const allResults = shownResult?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(shownResult?.steps ?? []);
|
|
203
228
|
const displaySteps = readOnly ? historicalEntry?.structure ? contextDisplaySteps(historicalEntry.structure.context, historyCollapsedSteps) : recordedDisplaySteps(allResults) : liveDisplaySteps;
|
|
204
|
-
const selectedStart = selectedStepStartedAt(displaySteps[shownSelectedStep], { results: allResults, result: shownResult, context: readOnly ? historicalEntry?.structure?.context : preparedDocument?.context, scope: readOnly ? historicalEntry?.structure?.scope : preparedDocument?.selection.root, events: readOnly ? [] : events, requestId: shownRequestId });
|
|
229
|
+
const selectedStart = selectedStepStartedAt(displaySteps[shownSelectedStep], { results: allResults, result: shownResult, context: readOnly ? historicalEntry?.structure?.context : preparedDocument?.context, scope: readOnly ? historicalEntry?.structure?.scope : preparedDocument?.selection.root, events: readOnly ? historicalEntry?.events ?? [] : events, requestId: shownRequestId });
|
|
205
230
|
const guides = hierarchyGuides(displaySteps);
|
|
206
231
|
const hasNestedSteps = displaySteps.some(step => step.depth > 0 || step.hasChildren);
|
|
207
232
|
const displayResults = displaySteps.map(step => resultForOccurrence(allResults, step.structuralAddress, step.occurrence));
|
|
208
233
|
const selectedInvocations = resultsForOccurrence(allResults, displaySteps[shownSelectedStep]?.structuralAddress, displaySteps[shownSelectedStep]?.occurrence);
|
|
209
|
-
const selectedPaths = readableInvocationPaths(displaySteps[shownSelectedStep], { context: readOnly ? historicalEntry?.structure?.context : preparedDocument?.context, invocations: selectedInvocations, allResults, result: shownResult, events: readOnly ? [] : events, requestId: shownRequestId });
|
|
234
|
+
const selectedPaths = readableInvocationPaths(displaySteps[shownSelectedStep], { context: readOnly ? historicalEntry?.structure?.context : preparedDocument?.context, invocations: selectedInvocations, allResults, result: shownResult, events: readOnly ? historicalEntry?.events ?? [] : events, requestId: shownRequestId });
|
|
210
235
|
const selectedError = displayResults[shownSelectedStep]?.error;
|
|
211
236
|
const errorCode = selectedError?.code ?? shownResult?.error?.code;
|
|
212
237
|
const errorMessage = selectedError?.message ?? shownResult?.error?.message;
|
|
@@ -225,7 +250,7 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
225
250
|
switchWorkspace(next);
|
|
226
251
|
document.getElementById(`runner-tab-${next}`)?.focus();
|
|
227
252
|
}
|
|
228
|
-
const executionEvents = events.filter(event => event.requestId ===
|
|
253
|
+
const executionEvents = (readOnly ? historicalEntry?.events ?? [] : events).filter(event => event.requestId === shownRequestId);
|
|
229
254
|
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; });
|
|
230
255
|
function selectRoot(key) { if (activeInputRequest.current)
|
|
231
256
|
return; setSelectedRoot(key); setSelectedStep(0); stepStateCache.current = {}; setStepStates(liveDisplaySteps.map(() => 'pending')); setResult(undefined); setEvents([]); setRequestId(undefined); setRunState('ready'); setControlState('idle'); setLoadError(undefined); }
|
|
@@ -268,34 +293,48 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
268
293
|
return;
|
|
269
294
|
}
|
|
270
295
|
let completed = false;
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
296
|
+
const secretValues = collectSecretValues(inputs, runtimeValues);
|
|
297
|
+
const recordedEvents = [];
|
|
298
|
+
let collectionFinished = false, collectionFailed = false;
|
|
299
|
+
const collectEvents = (async () => {
|
|
300
|
+
try {
|
|
301
|
+
for await (const event of client.events(id)) {
|
|
302
|
+
if (event.requestId !== id)
|
|
303
|
+
throw new Error('Execution event does not match the submitted run.');
|
|
304
|
+
const recorded = structuredClone(redactExecutionEvent(event, secretValues));
|
|
305
|
+
recordedEvents.push(recorded);
|
|
306
|
+
setEvents(previous => [...previous, recorded]);
|
|
282
307
|
if (completed)
|
|
283
308
|
continue;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
309
|
+
if (event.state === 'running') {
|
|
310
|
+
setRunState('running');
|
|
311
|
+
setControlState('running');
|
|
312
|
+
}
|
|
313
|
+
if (event.control) {
|
|
314
|
+
const state = await Promise.resolve(client.controlState(id));
|
|
315
|
+
if (completed)
|
|
316
|
+
continue;
|
|
317
|
+
setControlState(state ?? 'running');
|
|
318
|
+
}
|
|
319
|
+
if ('occurrence' in event && event.occurrence) {
|
|
320
|
+
const nextState = event.state;
|
|
321
|
+
stepStateCache.current[event.occurrence] = nextState;
|
|
322
|
+
setStepStates(previous => previous.map((state, index) => liveDisplaySteps[index]?.occurrence === event.occurrence ? nextState : state));
|
|
323
|
+
}
|
|
324
|
+
else if (event.structuralAddress) {
|
|
325
|
+
const nextState = event.state;
|
|
326
|
+
stepStateCache.current[occurrenceKey(event.structuralAddress)] = nextState;
|
|
327
|
+
setStepStates(previous => previous.map((state, index) => occurrenceKey(liveDisplaySteps[index]?.structuralAddress) === occurrenceKey(event.structuralAddress) ? nextState : state));
|
|
328
|
+
}
|
|
295
329
|
}
|
|
296
330
|
}
|
|
331
|
+
catch {
|
|
332
|
+
collectionFailed = true;
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
collectionFinished = true;
|
|
336
|
+
}
|
|
297
337
|
})();
|
|
298
|
-
const secretValues = collectSecretValues(inputs, runtimeValues);
|
|
299
338
|
const executionResult = redactExecutionResult(await client.result(id), secretValues);
|
|
300
339
|
completed = true;
|
|
301
340
|
try {
|
|
@@ -319,7 +358,13 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
319
358
|
setControlState(executionResult.state === 'cancelled' ? 'stopped' : executionResult.state);
|
|
320
359
|
const finishedAt = executionResult.finishedAt ?? new Date().toISOString();
|
|
321
360
|
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 };
|
|
322
|
-
|
|
361
|
+
const withEvents = () => ({ ...entry, events: [...recordedEvents],
|
|
362
|
+
...(collectionFailed || recordedEvents.at(-1)?.type !== 'run_finished' ? { eventCollectionIncomplete: true } : {}) });
|
|
363
|
+
const captured = collectionFinished;
|
|
364
|
+
saveHistoryEntry(captured ? withEvents() : entry, secretValues);
|
|
365
|
+
await collectEvents;
|
|
366
|
+
if (!captured)
|
|
367
|
+
saveHistoryEntry(withEvents(), secretValues, true);
|
|
323
368
|
}
|
|
324
369
|
async function stop() { if (requestId)
|
|
325
370
|
await client.cancel(requestId); }
|
|
@@ -344,9 +389,59 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
344
389
|
function exportResult(value, packageId, schemaVersion, provenance, executionVersion) { if (!value)
|
|
345
390
|
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); }
|
|
346
391
|
function reloadHistoryEntry(entry) { setHistoricalEntry(entry); setHistorySelectedStep(0); setHistoryCollapsedSteps(new Set()); }
|
|
347
|
-
function clearHistory() {
|
|
348
|
-
|
|
349
|
-
|
|
392
|
+
function clearHistory() {
|
|
393
|
+
try {
|
|
394
|
+
localStorage.removeItem(HISTORY_KEY);
|
|
395
|
+
updateHistory([]);
|
|
396
|
+
setHistorySaveError(undefined);
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
setHistorySaveError('History could not be cleared. Existing evidence remains available.');
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function deleteHistoryEntry(requestIdToDelete) {
|
|
403
|
+
const next = historyRef.current.filter(item => item.requestId !== requestIdToDelete);
|
|
404
|
+
try {
|
|
405
|
+
localStorage.setItem(HISTORY_KEY, JSON.stringify(next));
|
|
406
|
+
updateHistory(next);
|
|
407
|
+
setHistorySaveError(undefined);
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
setHistorySaveError('The History entry could not be deleted. Existing evidence remains available.');
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function changeHistoryRetention(value) {
|
|
414
|
+
const next = historyRef.current.slice(0, value);
|
|
415
|
+
try {
|
|
416
|
+
const previousPreference = localStorage.getItem(HISTORY_RETENTION_KEY);
|
|
417
|
+
localStorage.setItem(HISTORY_RETENTION_KEY, String(value));
|
|
418
|
+
try {
|
|
419
|
+
localStorage.setItem(HISTORY_KEY, JSON.stringify(next));
|
|
420
|
+
}
|
|
421
|
+
catch (error) {
|
|
422
|
+
try {
|
|
423
|
+
if (previousPreference === null)
|
|
424
|
+
localStorage.removeItem(HISTORY_RETENTION_KEY);
|
|
425
|
+
else
|
|
426
|
+
localStorage.setItem(HISTORY_RETENTION_KEY, previousPreference);
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
retentionRef.current = value;
|
|
430
|
+
setHistoryRetention(value);
|
|
431
|
+
setHistorySaveError(`History retention could not be applied. The saved Keep setting could not be restored and is now ${value} runs. Existing evidence remains available.`);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
436
|
+
retentionRef.current = value;
|
|
437
|
+
setHistoryRetention(value);
|
|
438
|
+
updateHistory(next);
|
|
439
|
+
setHistorySaveError(undefined);
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
setHistorySaveError('History retention could not be applied. Existing evidence remains available in this session.');
|
|
443
|
+
}
|
|
444
|
+
}
|
|
350
445
|
function rememberVisibleStates() { liveDisplaySteps.forEach((step, index) => { const state = stepStates[index]; if (state && step.structuralAddress)
|
|
351
446
|
stepStateCache.current[occurrenceKey(step.structuralAddress)] = state; }); }
|
|
352
447
|
function toggleStep(stepId) { if (readOnly) {
|
|
@@ -453,18 +548,18 @@ function RunnerApp({ client, onExit, applicationMenu }) {
|
|
|
453
548
|
about: () => menuActions.current.about(),
|
|
454
549
|
demo: () => menuActions.current.demo(),
|
|
455
550
|
}), [registerMenu]);
|
|
456
|
-
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 ", contextTarget.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)
|
|
551
|
+
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: [historySaveError && _jsx("p", { className: "load-error", role: "alert", children: historySaveError }), 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 ", contextTarget.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)
|
|
457
552
|
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)
|
|
458
553
|
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)
|
|
459
554
|
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)
|
|
460
|
-
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") {
|
|
555
|
+
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 rowStart = selectedStepStartedAt(step, { results: allResults, result: shownResult, context, scope, events: executionEvents, requestId: shownRequestId }); 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") {
|
|
461
556
|
event.preventDefault();
|
|
462
557
|
selectDisplayedStep(Math.min(displaySteps.length - 1, index + 1));
|
|
463
558
|
} if (event.key === "ArrowUp") {
|
|
464
559
|
event.preventDefault();
|
|
465
560
|
selectDisplayedStep(Math.max(0, index - 1));
|
|
466
561
|
} }, onClick: () => selectDisplayedStep(index), onContextMenu: event => { event.preventDefault(); if (readOnly)
|
|
467
|
-
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 })] }, 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 STEP" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[shownSelectedStep] ? `${shownSelectedStep + 1}. ${displaySteps[shownSelectedStep].name}` : 'No step selected' }) }), _jsx("p", { className: "selected-step-start", children: selectedStart ? `Started: ${new Date(selectedStart).toLocaleTimeString()}` : 'Not started' }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { values: ["Target", (readOnly ? historicalEntry?.result.target.name : contextTarget.name) ?? 'Unknown', "Type", (readOnly ? historicalEntry?.result.target.kind : contextTarget.kind) ?? 'Unknown', "Project reference", (readOnly ? historicalEntry?.packageId : selectedTargetId) ?? 'Not recorded', "Execution format", `Execution v${readOnly ? historicalEntry?.executionSchemaVersion ?? 'unknown' : authoringSource ? 8 : executionPackage.schemaVersion}`, "Definitions", shownPackage ? String(shownPackage.definitions.length) : 'Not recorded', "Required inputs", shownPackage ? shownPackage.requiredInputs?.join(', ') || 'None' : !readOnly && preparedDocument ? preparedDocument.selection.requiredInputs?.join(', ') || 'None' : 'Not recorded', "Runtime values", shownPackage ? shownPackage.runtimeRequirements?.join(', ') || 'None' : !readOnly && preparedDocument ? preparedDocument.selection.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: [errorCode && _jsxs(_Fragment, { children: [_jsx("strong", { children: errorCode }), _jsx("br", {})] }), errorMessage, hasErrorDetails && _jsx("pre", { children: JSON.stringify(errorDetails, null, 2) }), !errorCode && !errorMessage && !hasErrorDetails && 'Error details were not recorded.'] }) }), shownResult?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: shownResult.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: selectedPaths.length ? selectedPaths.map(path => _jsxs("div", { className: "notice", children: [selectedInvocations.length > 1 && _jsx("small", { children: "Latest recorded invocation" }), path.names.map((name, index) => _jsx("div", { children: name }, index))] }, path.key)) : _jsx("div", { className: "notice", children: "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." })] }) })] });
|
|
562
|
+
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 })] }, 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: rowStart ? new Date(rowStart).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(ExecutionEventRows, { events: visibleEvents }) }), 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 STEP" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[shownSelectedStep] ? `${shownSelectedStep + 1}. ${displaySteps[shownSelectedStep].name}` : 'No step selected' }) }), _jsx("p", { className: "selected-step-start", children: selectedStart ? `Started: ${new Date(selectedStart).toLocaleTimeString()}` : 'Not started' }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { values: ["Target", (readOnly ? historicalEntry?.result.target.name : contextTarget.name) ?? 'Unknown', "Type", (readOnly ? historicalEntry?.result.target.kind : contextTarget.kind) ?? 'Unknown', "Project reference", (readOnly ? historicalEntry?.packageId : selectedTargetId) ?? 'Not recorded', "Execution format", `Execution v${readOnly ? historicalEntry?.executionSchemaVersion ?? 'unknown' : authoringSource ? 8 : executionPackage.schemaVersion}`, "Definitions", shownPackage ? String(shownPackage.definitions.length) : 'Not recorded', "Required inputs", shownPackage ? shownPackage.requiredInputs?.join(', ') || 'None' : !readOnly && preparedDocument ? preparedDocument.selection.requiredInputs?.join(', ') || 'None' : 'Not recorded', "Runtime values", shownPackage ? shownPackage.runtimeRequirements?.join(', ') || 'None' : !readOnly && preparedDocument ? preparedDocument.selection.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: [errorCode && _jsxs(_Fragment, { children: [_jsx("strong", { children: errorCode }), _jsx("br", {})] }), errorMessage, hasErrorDetails && _jsx("pre", { children: JSON.stringify(errorDetails, null, 2) }), !errorCode && !errorMessage && !hasErrorDetails && 'Error details were not recorded.'] }) }), shownResult?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: shownResult.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: selectedPaths.length ? selectedPaths.map(path => _jsxs("div", { className: "notice", children: [selectedInvocations.length > 1 && _jsx("small", { children: "Latest recorded invocation" }), path.names.map((name, index) => _jsx("div", { children: name }, index))] }, path.key)) : _jsx("div", { className: "notice", children: "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" }), _jsxs("div", { className: "event-log-content", children: [_jsx("p", { className: "empty-state", children: historicalEntry?.events === undefined ? 'Events were not recorded for this run.' : historicalEntry.eventCollectionIncomplete ? 'Event collection was incomplete. Only captured events are shown.' : executionEvents.length === 0 ? 'No execution events were recorded.' : '' }), _jsx("div", { className: "event-log-scroll", role: "region", "aria-label": "Recorded execution events", tabIndex: 0, children: _jsx(ExecutionEventRows, { events: executionEvents }) })] })] }, 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." })] }) })] });
|
|
468
563
|
}
|
|
469
564
|
function recordedDisplaySteps(results) {
|
|
470
565
|
const unique = new Map();
|
|
@@ -567,4 +662,5 @@ function contextDisplaySteps(context, collapsed, document) {
|
|
|
567
662
|
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)))]; }
|
|
568
663
|
return visit(context.root, 0);
|
|
569
664
|
}
|
|
665
|
+
function ExecutionEventRows({ events }) { return _jsx("ul", { className: "event-log-list", children: events.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)) }); }
|
|
570
666
|
//# sourceMappingURL=RunnerModule.js.map
|