@qaick/runner 0.29.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/package-dist/fixtures/qaick-local-http-regression.qaick.json +5002 -0
  2. package/package-dist/fixtures/regression-cancel.qaick.json +314 -0
  3. package/package-dist/fixtures/regression-declared-inputs.qaick.json +281 -0
  4. package/package-dist/fixtures/regression-input-conflict.qaick.json +548 -0
  5. package/package-dist/fixtures/regression-script.qaick.json +58 -0
  6. package/package-dist/fixtures/regression-timeout.qaick.json +314 -0
  7. package/package-dist/fixtures/script-worker-http.qaick.json +226 -0
  8. package/package-dist/package.json +5 -5
  9. package/package-dist/src/runner/RunnerModule.d.ts +11 -2
  10. package/package-dist/src/runner/RunnerModule.d.ts.map +1 -1
  11. package/package-dist/src/runner/RunnerModule.js +77 -52
  12. package/package-dist/src/runner/RunnerModule.js.map +1 -1
  13. package/package-dist/src/runner/action-template-lowering.d.ts +18 -0
  14. package/package-dist/src/runner/action-template-lowering.d.ts.map +1 -0
  15. package/package-dist/src/runner/action-template-lowering.js +83 -0
  16. package/package-dist/src/runner/action-template-lowering.js.map +1 -0
  17. package/package-dist/src/runner/adapters.d.ts +45 -0
  18. package/package-dist/src/runner/adapters.d.ts.map +1 -0
  19. package/package-dist/src/runner/adapters.js +203 -0
  20. package/package-dist/src/runner/adapters.js.map +1 -0
  21. package/package-dist/src/runner/catalog.d.ts +4 -1
  22. package/package-dist/src/runner/catalog.d.ts.map +1 -1
  23. package/package-dist/src/runner/catalog.js +11 -6
  24. package/package-dist/src/runner/catalog.js.map +1 -1
  25. package/package-dist/src/runner/execution-context.d.ts +3 -0
  26. package/package-dist/src/runner/execution-context.d.ts.map +1 -0
  27. package/package-dist/src/runner/execution-context.js +4 -0
  28. package/package-dist/src/runner/execution-context.js.map +1 -0
  29. package/package-dist/src/runner/fixture.d.ts.map +1 -1
  30. package/package-dist/src/runner/fixture.js +14 -27
  31. package/package-dist/src/runner/fixture.js.map +1 -1
  32. package/package-dist/src/runner/invocation-inputs.d.ts +33 -0
  33. package/package-dist/src/runner/invocation-inputs.d.ts.map +1 -0
  34. package/package-dist/src/runner/invocation-inputs.js +85 -0
  35. package/package-dist/src/runner/invocation-inputs.js.map +1 -0
  36. package/package-dist/src/runner/legacy-types.d.ts +81 -0
  37. package/package-dist/src/runner/legacy-types.d.ts.map +1 -0
  38. package/package-dist/src/runner/legacy-types.js +7 -0
  39. package/package-dist/src/runner/legacy-types.js.map +1 -0
  40. package/package-dist/src/runner/loaded-target.d.ts +13 -0
  41. package/package-dist/src/runner/loaded-target.d.ts.map +1 -0
  42. package/package-dist/src/runner/loaded-target.js +23 -0
  43. package/package-dist/src/runner/loaded-target.js.map +1 -0
  44. package/package-dist/src/runner/package-loader.d.ts +3 -2
  45. package/package-dist/src/runner/package-loader.d.ts.map +1 -1
  46. package/package-dist/src/runner/package-loader.js +3 -112
  47. package/package-dist/src/runner/package-loader.js.map +1 -1
  48. package/package-dist/src/runner/result-export.d.ts +7 -2
  49. package/package-dist/src/runner/result-export.d.ts.map +1 -1
  50. package/package-dist/src/runner/result-export.js +3 -2
  51. package/package-dist/src/runner/result-export.js.map +1 -1
  52. package/package-dist/src/runner/runtime-client-adapter.d.ts +25 -0
  53. package/package-dist/src/runner/runtime-client-adapter.d.ts.map +1 -1
  54. package/package-dist/src/runner/runtime-client-adapter.js +44 -2
  55. package/package-dist/src/runner/runtime-client-adapter.js.map +1 -1
  56. package/package-dist/src/runner/studio-artifact-loader.d.ts +3 -5
  57. package/package-dist/src/runner/studio-artifact-loader.d.ts.map +1 -1
  58. package/package-dist/src/runner/studio-artifact-loader.js +4 -335
  59. package/package-dist/src/runner/studio-artifact-loader.js.map +1 -1
  60. package/package-dist/src/runner/studio-hierarchy-loader.d.ts +10 -0
  61. package/package-dist/src/runner/studio-hierarchy-loader.d.ts.map +1 -0
  62. package/package-dist/src/runner/studio-hierarchy-loader.js +271 -0
  63. package/package-dist/src/runner/studio-hierarchy-loader.js.map +1 -0
  64. package/package-dist/src/runner/types.d.ts +12 -0
  65. package/package-dist/src/runner/types.d.ts.map +1 -1
  66. package/package.json +5 -5
@@ -1,23 +1,22 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
3
  import { checkoutNestedPackage, checkoutPackage, checkoutProjectPackage } from './fixture.js';
4
- import { loadExecutionPackage, PackageLoadError } from './package-loader.js';
4
+ import { PackageLoadError } from './package-loader.js';
5
+ import { loadTarget, restoreTarget } from './loaded-target.js';
5
6
  import { PackageCatalog } from './catalog.js';
6
7
  import { RUNNER_CONTRACT_VERSION } from './types.js';
7
8
  import { RunnerWalkthrough } from '../demo/RunnerWalkthrough.js';
8
9
  import { RUNNER_BUILD_LABEL, RUNNER_UI_VERSION } from '../build-info.js';
9
10
  import { collectSecretValues, redactExecutionEvent, redactExecutionResult, redactKeyValuePairs } from './redaction.js';
10
11
  import { createResultExport } from './result-export.js';
12
+ import { inspectDeclaredInputs, inputDrafts, resolvedArguments } from './invocation-inputs.js';
11
13
  import { persistRunHistory } from './history.js';
12
14
  const HISTORY_KEY = 'qaick-runner.history.v1';
13
15
  const HISTORY_RETENTION_KEY = 'qaick-runner.history-retention.v1';
14
16
  const IMPORTED_PACKAGE_KEY = 'qaick-runner.imported-package.v1';
15
17
  const THEME_STORAGE_KEY = 'qaick-studio-theme';
16
18
  function readRunHistory() { try {
17
- const stored = localStorage.getItem(HISTORY_KEY);
18
- if (!stored)
19
- return [];
20
- return JSON.parse(stored).map(entry => ({ ...entry, artifactSchemaVersion: entry.artifactSchemaVersion === 6 && entry.packageId?.startsWith('studio-') ? 7 : entry.artifactSchemaVersion }));
19
+ return JSON.parse(localStorage.getItem(HISTORY_KEY) ?? '[]');
21
20
  }
22
21
  catch {
23
22
  return [];
@@ -31,10 +30,7 @@ catch {
31
30
  } }
32
31
  function readImportedPackage() { try {
33
32
  const stored = localStorage.getItem(IMPORTED_PACKAGE_KEY);
34
- if (!stored)
35
- return undefined;
36
- const pack = loadExecutionPackage(stored);
37
- return pack.artifactSchemaVersion ? pack : pack.packageId.startsWith('studio-') ? { ...pack, artifactSchemaVersion: 7 } : pack;
33
+ return stored ? restoreTarget(stored) : undefined;
38
34
  }
39
35
  catch {
40
36
  return undefined;
@@ -51,7 +47,7 @@ packageCatalog.addPackage(checkoutProjectPackage);
51
47
  packageCatalog.addPackage(checkoutNestedPackage);
52
48
  const restoredImportedPackage = readImportedPackage();
53
49
  if (restoredImportedPackage)
54
- packageCatalog.addPackage(restoredImportedPackage);
50
+ packageCatalog.addPackage(restoredImportedPackage.executionPackage, restoredImportedPackage.artifactProvenance);
55
51
  export function RunnerModule({ client, showDemo = standaloneDemoRequested(), onExit }) {
56
52
  return showDemo ? _jsx(RunnerWalkthrough, {}) : _jsx(RunnerApp, { client: client, onExit: onExit });
57
53
  }
@@ -60,8 +56,18 @@ function RunnerApp({ client, onExit }) {
60
56
  const [theme, setTheme] = useState(readTheme);
61
57
  const [showAbout, setShowAbout] = useState(false);
62
58
  const [environment, setEnvironment] = useState('test');
63
- const [executionPackage, setExecutionPackage] = useState(restoredImportedPackage ?? checkoutPackage);
64
- const [inputs, setInputs] = useState({ customerId: 'CUST-001234', orderId: 'ORD-789456', amount: '125.50' });
59
+ const [executionPackage, setExecutionPackage] = useState(restoredImportedPackage?.executionPackage ?? checkoutPackage);
60
+ const [inputEdits, setInputEdits] = useState(() => inputDrafts(inspectDeclaredInputs(restoredImportedPackage?.executionPackage ?? checkoutPackage).declarations));
61
+ const { declarations, conflict, unresolved } = inspectDeclaredInputs(executionPackage);
62
+ const inputBlocker = conflict ?? unresolved;
63
+ let inputError;
64
+ let inputs = {};
65
+ try {
66
+ inputs = resolvedArguments(declarations, inputEdits);
67
+ }
68
+ catch (error) {
69
+ inputError = error instanceof Error ? error.message : 'Invalid invocation inputs';
70
+ }
65
71
  const [runtimeValues, setRuntimeValues] = useState({});
66
72
  const [timeoutMs, setTimeoutMs] = useState('none');
67
73
  const [editingInputs, setEditingInputs] = useState(false);
@@ -72,7 +78,7 @@ function RunnerApp({ client, onExit }) {
72
78
  const breakpointsRef = useRef(new Set());
73
79
  const [stopAfterStepId, setStopAfterStepId] = useState();
74
80
  const [stepStates, setStepStates] = useState(() => packageStepsFor(executionPackage).map(() => 'pending'));
75
- const stepStateCache = useRef(Object.fromEntries(packageStepsFor(executionPackage).map(step => [step.id, 'pending'])));
81
+ const stepStateCache = useRef(Object.fromEntries(packageStepsFor(executionPackage).map(step => [occurrenceKey(step.structuralAddress), 'pending'])));
76
82
  const [loopProgress, setLoopProgress] = useState(() => loopProgressFor(packageStepsFor(executionPackage)));
77
83
  const [loopStartedAt, setLoopStartedAt] = useState({});
78
84
  const [clock, setClock] = useState(() => Date.now());
@@ -93,15 +99,20 @@ function RunnerApp({ client, onExit }) {
93
99
  const allResults = result?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(result?.steps ?? []);
94
100
  const displaySteps = orderDisplayStepsByResult(flattenDisplaySteps(packageSteps, 0, undefined, undefined, collapsedSteps), allResults);
95
101
  const hasNestedSteps = flattenDisplaySteps(packageSteps).some(step => step.depth > 0);
96
- const displayResults = displaySteps.map(step => [...allResults].reverse().find(resultStep => resultStep.id === step.id || step.branchRow && resultStep.invocationPath?.at(-1) === step.id.split('.').at(-1)));
102
+ const displayResults = displaySteps.map(step => resultForOccurrence(allResults, step.structuralAddress));
103
+ const selectedInvocations = resultsForOccurrence(allResults, displaySteps[selectedStep]?.structuralAddress);
97
104
  const visibleEvents = events.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; });
98
105
  async function run(dryRun = false) {
106
+ if (inputBlocker || inputError) {
107
+ setLoadError(inputBlocker?.message ?? inputError);
108
+ return;
109
+ }
99
110
  const id = crypto.randomUUID();
100
111
  setViewingHistoryEntry(undefined);
101
112
  setRequestId(id);
102
113
  setResult(undefined);
103
114
  setEvents([]);
104
- stepStateCache.current = Object.fromEntries(flattenDisplaySteps(packageSteps).map(step => [step.id, 'pending']));
115
+ stepStateCache.current = Object.fromEntries(flattenDisplaySteps(packageSteps).map(step => [occurrenceKey(step.structuralAddress), 'pending']));
105
116
  setStepStates(displaySteps.map(() => 'pending'));
106
117
  setLoopProgress(loopProgressFor(executionPackage.target.kind === 'project' ? executionPackage.flows?.flatMap(flow => flow.steps) ?? [] : executionPackage.steps));
107
118
  setLoopStartedAt({});
@@ -123,10 +134,10 @@ function RunnerApp({ client, onExit }) {
123
134
  setLoopStartedAt(previous => ({ ...previous, [event.iteration.loopId]: previous[event.iteration.loopId] ?? Date.now() }));
124
135
  setClock(Date.now());
125
136
  }
126
- if (event.stepId) {
127
- const nextState = event.type === 'step_finished' ? 'finished' : event.state;
128
- stepStateCache.current[event.stepId] = nextState;
129
- setStepStates(previous => previous.map((state, index) => displaySteps[index]?.id === event.stepId ? nextState : state));
137
+ if (event.structuralAddress) {
138
+ const nextState = event.state;
139
+ stepStateCache.current[occurrenceKey(event.structuralAddress)] = nextState;
140
+ setStepStates(previous => previous.map((state, index) => occurrenceKey(displaySteps[index]?.structuralAddress) === occurrenceKey(event.structuralAddress) ? nextState : state));
130
141
  }
131
142
  }
132
143
  })();
@@ -134,14 +145,14 @@ function RunnerApp({ client, onExit }) {
134
145
  const executionResult = redactExecutionResult(await client.result(id), secretValues);
135
146
  const finalSteps = flattenResultSteps(executionResult.children?.flatMap(child => child.steps) ?? executionResult.steps);
136
147
  const finalControlState = await Promise.resolve(client.controlState(id));
137
- const finalStateById = Object.fromEntries(finalSteps.map(step => [step.id, step.state]));
148
+ const finalStateById = Object.fromEntries(flattenDisplaySteps(packageSteps).map(step => [occurrenceKey(step.structuralAddress), stateForOccurrence(finalSteps, step.structuralAddress) ?? 'pending']));
138
149
  stepStateCache.current = { ...stepStateCache.current, ...finalStateById };
139
150
  setResult(executionResult);
140
- setStepStates(displaySteps.map(step => stepStateCache.current[step.id] ?? 'pending'));
151
+ setStepStates(displaySteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending'));
141
152
  setRunState(executionResult.state);
142
153
  setControlState(finalControlState ?? (executionResult.state === 'cancelled' ? 'stopped' : executionResult.state));
143
154
  const finishedAt = executionResult.finishedAt ?? new Date().toISOString();
144
- const entry = { requestId: id, startedAt: executionResult.startedAt, finishedAt, target: executionPackage.target.name, packageId: executionPackage.packageId, artifactSchemaVersion: executionPackage.artifactSchemaVersion ?? executionPackage.schemaVersion, environment: executionResult.environment, state: executionResult.state, durationMs: Math.max(0, new Date(finishedAt).getTime() - new Date(executionResult.startedAt).getTime()), result: executionResult };
155
+ const entry = { requestId: id, startedAt: executionResult.startedAt, finishedAt, target: executionPackage.target.name, packageId: executionPackage.packageId, artifactSchemaVersion: packageCatalog.artifactProvenance(executionPackage.packageId)?.originalVersion, artifactProvenance: packageCatalog.artifactProvenance(executionPackage.packageId), executionSchemaVersion: executionPackage.schemaVersion, environment: executionResult.environment, state: executionResult.state, durationMs: Math.max(0, new Date(finishedAt).getTime() - new Date(executionResult.startedAt).getTime()), result: executionResult };
145
156
  setRunHistory(previous => persistRunHistory(localStorage, HISTORY_KEY, [entry, ...previous.filter(item => item.requestId !== id)], historyRetention, secretValues));
146
157
  }
147
158
  async function stop() { if (requestId)
@@ -164,41 +175,43 @@ function RunnerApp({ client, onExit }) {
164
175
  else
165
176
  next.add(stepId); breakpointsRef.current = next; return next; }); }
166
177
  function toggleStopAfter(stepId) { setStopAfterStepId(previous => previous === stepId ? undefined : stepId); }
167
- function exportResult(value = result, packageId = executionPackage.packageId, schemaVersion = executionPackage.artifactSchemaVersion ?? executionPackage.schemaVersion) { if (!value)
168
- return; const exportPayload = createResultExport(value, packageId, schemaVersion, collectSecretValues(inputs, runtimeValues)); 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); }
178
+ function exportResult(value, packageId, schemaVersion, provenance, executionVersion) { if (!value)
179
+ 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); }
169
180
  function reloadHistoryEntry(entry) { let loadedPackage = executionPackage; if (entry.packageId) {
170
181
  try {
171
182
  loadedPackage = packageCatalog.get(entry.packageId);
172
183
  setExecutionPackage(loadedPackage);
173
184
  }
174
185
  catch { /* The result remains viewable when its package is no longer loaded. */ }
175
- } 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(storedSteps.map(step => [step.id, step.state])); setRequestId(entry.requestId); setEnvironment(entry.environment); setResult(entry.result); setViewingHistoryEntry(entry.requestId); setEvents([]); setSelectedStep(0); setStepStates(flattenDisplaySteps(steps).map(step => states.get(step.id) ?? 'pending')); setRunState(entry.state); document.querySelector('.runner-workspace')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
186
+ } 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' }); }
176
187
  function clearHistory() { setRunHistory([]); localStorage.removeItem(HISTORY_KEY); }
177
188
  function deleteHistoryEntry(requestIdToDelete) { setRunHistory(previous => { const next = previous.filter(item => item.requestId !== requestIdToDelete); localStorage.setItem(HISTORY_KEY, JSON.stringify(next)); return next; }); }
178
189
  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; }); }
179
190
  function rememberVisibleStates() { displaySteps.forEach((step, index) => { const state = stepStates[index]; if (state)
180
- stepStateCache.current[step.id] = state; }); }
191
+ stepStateCache.current[occurrenceKey(step.structuralAddress)] = state; }); }
181
192
  function toggleStep(stepId) { rememberVisibleStates(); const next = new Set(collapsedSteps); if (next.has(stepId))
182
193
  next.delete(stepId);
183
194
  else
184
- next.add(stepId); const nextSteps = flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[step.id] ?? 'pending')); }
185
- function setAllCollapsed(collapsed) { rememberVisibleStates(); const next = collapsed ? new Set(containerStepIds(packageSteps)) : new Set(); const nextSteps = flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[step.id] ?? 'pending')); }
195
+ next.add(stepId); const nextSteps = flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
196
+ function setAllCollapsed(collapsed) { rememberVisibleStates(); const next = collapsed ? new Set(containerStepIds(packageSteps)) : new Set(); const nextSteps = flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
186
197
  async function loadPackage(file) {
187
198
  if (!file)
188
199
  return;
189
200
  try {
190
- const loaded = loadExecutionPackage(await file.text());
191
- packageCatalog.addPackage(loaded);
192
- localStorage.setItem(IMPORTED_PACKAGE_KEY, JSON.stringify(loaded));
201
+ const target = loadTarget(await file.text());
202
+ const loaded = target.executionPackage;
203
+ packageCatalog.addPackage(loaded, target.artifactProvenance);
204
+ localStorage.setItem(IMPORTED_PACKAGE_KEY, JSON.stringify(target));
193
205
  setCatalogVersion(version => version + 1);
194
206
  setExecutionPackage(loaded);
195
207
  setBreakpoints(new Set());
196
208
  breakpointsRef.current = new Set();
197
209
  setStopAfterStepId(undefined);
198
- setInputs(previous => ({ ...previous, ...Object.fromEntries((loaded.requiredInputs ?? []).map(name => [name, previous[name] ?? ''])) }));
210
+ if (!inspectDeclaredInputs(loaded).conflict && !inspectDeclaredInputs(loaded).unresolved)
211
+ setInputEdits(inputDrafts(inspectDeclaredInputs(loaded).declarations));
199
212
  setRuntimeValues(Object.fromEntries((loaded.runtimeRequirements ?? []).map(name => [name, ''])));
200
213
  setCollapsedSteps(new Set());
201
- stepStateCache.current = Object.fromEntries(flattenDisplaySteps(loaded.target.kind === 'project' ? loaded.flows?.flatMap(flow => flow.steps) ?? [] : loaded.steps).map(step => [step.id, 'pending']));
214
+ stepStateCache.current = Object.fromEntries(flattenDisplaySteps(loaded.target.kind === 'project' ? loaded.flows?.flatMap(flow => flow.steps) ?? [] : loaded.steps).map(step => [occurrenceKey(step.structuralAddress), 'pending']));
202
215
  setStepStates((loaded.target.kind === 'project' ? loaded.flows?.flatMap(flow => flattenDisplaySteps(flow.steps)) ?? [] : flattenDisplaySteps(loaded.steps)).map(() => 'pending'));
203
216
  setLoopProgress(loopProgressFor(loaded.target.kind === 'project' ? loaded.flows?.flatMap(flow => flow.steps) ?? [] : loaded.steps));
204
217
  setLoopStartedAt({});
@@ -230,16 +243,17 @@ function RunnerApp({ client, onExit }) {
230
243
  return undefined; const timer = window.setInterval(() => setClock(Date.now()), 250); return () => window.clearInterval(timer); }, [runState]);
231
244
  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)
232
245
  setSelectedStep(index); }; document.addEventListener('click', navigateIssue); return () => document.removeEventListener('click', navigateIssue); }, [displaySteps]);
233
- 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" })] }), _jsx("span", { className: "header-spacer" }), _jsx("a", { className: "demo-entry", href: "?demo", children: "View demo" }), _jsx("button", { className: "about-button", onClick: () => setShowAbout(true), children: "About" }), _jsxs("label", { className: "theme-picker", children: ["Theme", _jsxs("select", { value: theme, onChange: event => setTheme(event.target.value), children: [_jsx("option", { value: "light", children: "Light" }), _jsx("option", { value: "dark", children: "Dark" })] })] })] }), _jsxs("main", { className: "runner-main", 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: ["Schema ", 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("label", { children: ["Target", _jsx("select", { value: executionPackage.packageId, onChange: event => { const next = packageCatalog.get(event.target.value); setExecutionPackage(next); setBreakpoints(new Set()); breakpointsRef.current = new Set(); setStopAfterStepId(undefined); setLoadError(undefined); setLoadIssues([]); setRuntimeValues(Object.fromEntries((next.runtimeRequirements ?? []).map(name => [name, '']))); setStepStates((next.target.kind === 'project' ? next.flows?.flatMap(flow => flattenDisplaySteps(flow.steps)) ?? [] : flattenDisplaySteps(next.steps)).map(() => 'pending')); setLoopProgress(loopProgressFor(next.target.kind === 'project' ? next.flows?.flatMap(flow => flow.steps) ?? [] : next.steps)); setSelectedStep(0); setResult(undefined); setEvents([]); setRunState('ready'); }, 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("label", { className: "file-button", children: ["Load package", _jsx("input", { type: "file", accept: "application/json,.json", onChange: event => void loadPackage(event.target.files?.[0]) })] }), _jsx("button", { className: "button", onClick: () => setEditingInputs(value => !value), children: editingInputs ? 'Done inputs' : 'Edit inputs' }), _jsx("button", { className: "button primary", onClick: () => void run(), disabled: runState === 'preflighting' || runState === 'running', children: "\u25B6\u00A0 Run" }), _jsx("button", { className: "button dry-run", onClick: () => void run(true), disabled: 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] }), 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: [_jsx(InputField, { label: "customerId", value: String(inputs.customerId ?? ''), onChange: value => setInputs(previous => ({ ...previous, customerId: value })) }), _jsx(InputField, { label: "orderId", value: String(inputs.orderId ?? ''), onChange: value => setInputs(previous => ({ ...previous, orderId: value })) }), _jsx(InputField, { label: "amount", value: String(inputs.amount ?? ''), onChange: value => setInputs(previous => ({ ...previous, amount: value })) }), (executionPackage.requiredInputs ?? []).filter(name => !['customerId', 'orderId', 'amount'].includes(name)).map(name => _jsx(InputField, { label: name, value: String(inputs[name] ?? ''), onChange: value => setInputs(previous => ({ ...previous, [name]: value })) }, 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" })] }), _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 progress = step.loopTotal ? loopProgress[step.id] ?? { current: 0, total: step.loopTotal } : undefined; const percent = progress ? Math.round(progress.current / progress.total * 100) : 0; const isLoop = Boolean(step.loopTotal); const displayState = displayResults[index]?.state ?? stepStates[index] ?? 'pending'; const isLoopContent = Boolean(step.loopId); const isLoopEnd = isLoopContent && displaySteps[index + 1]?.loopId !== step.loopId; const elapsed = step.loopId && loopStartedAt[step.loopId] ? formatElapsed(clock - loopStartedAt[step.loopId]) : undefined; 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") {
246
+ 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" })] }), _jsx("span", { className: "header-spacer" }), _jsx("a", { className: "demo-entry", href: "./examples/qaick-local-http-regression.qaick.json" with { type: '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("a", { className: "demo-entry", href: "?demo", children: "View demo" }), _jsx("button", { className: "about-button", onClick: () => setShowAbout(true), children: "About" }), _jsxs("label", { className: "theme-picker", children: ["Theme", _jsxs("select", { value: theme, onChange: event => setTheme(event.target.value), children: [_jsx("option", { value: "light", children: "Light" }), _jsx("option", { value: "dark", children: "Dark" })] })] })] }), _jsxs("main", { className: "runner-main", 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("label", { children: ["Target", _jsx("select", { value: executionPackage.packageId, onChange: event => { const next = packageCatalog.get(event.target.value); setExecutionPackage(next); if (!inspectDeclaredInputs(next).conflict && !inspectDeclaredInputs(next).unresolved)
247
+ setInputEdits(inputDrafts(inspectDeclaredInputs(next).declarations)); setBreakpoints(new Set()); breakpointsRef.current = new Set(); setStopAfterStepId(undefined); setLoadError(undefined); setLoadIssues([]); setRuntimeValues(Object.fromEntries((next.runtimeRequirements ?? []).map(name => [name, '']))); setStepStates((next.target.kind === 'project' ? next.flows?.flatMap(flow => flattenDisplaySteps(flow.steps)) ?? [] : flattenDisplaySteps(next.steps)).map(() => 'pending')); setLoopProgress(loopProgressFor(next.target.kind === 'project' ? next.flows?.flatMap(flow => flow.steps) ?? [] : next.steps)); setSelectedStep(0); setResult(undefined); setEvents([]); setRunState('ready'); }, 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("label", { className: "file-button", children: ["Load package", _jsx("input", { type: "file", accept: "application/json,.json", onChange: event => void loadPackage(event.target.files?.[0]) })] }), _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)) })] }), 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); }), inputError && _jsx("p", { role: "alert", children: inputError }), (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" })] }), _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 progress = step.loopTotal ? loopProgress[step.id] ?? { current: 0, total: step.loopTotal } : undefined; const percent = progress ? Math.round(progress.current / progress.total * 100) : 0; const isLoop = Boolean(step.loopTotal); const displayState = displayResults[index]?.state ?? stepStates[index] ?? 'pending'; const isLoopContent = Boolean(step.loopId); const isLoopEnd = isLoopContent && displaySteps[index + 1]?.loopId !== step.loopId; const elapsed = step.loopId && loopStartedAt[step.loopId] ? formatElapsed(clock - loopStartedAt[step.loopId]) : undefined; 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") {
234
248
  event.preventDefault();
235
249
  setSelectedStep(Math.min(displaySteps.length - 1, index + 1));
236
250
  } if (event.key === "ArrowUp") {
237
251
  event.preventDefault();
238
252
  setSelectedStep(Math.max(0, index - 1));
239
- } }, 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 ${isLoop ? 'loop-icon' : displayState}`, children: isLoop ? '↻' : displayState === 'finished' ? '✓' : displayState === 'waiting' ? '◷' : displayState === 'skipped' ? '–' : '' }), _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: "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: [progress && _jsxs("div", { className: "loop-progress", children: [_jsxs("strong", { children: ["REPEAT LOOP \u00B7 ITERATION ", progress.current, " OF ", progress.total] }), _jsx("span", { className: "loop-bar", children: _jsx("i", { style: { width: `${percent}%` } }) }), _jsx("small", { children: elapsed ? `Elapsed ${elapsed}` : 'Waiting to start' })] }), _jsx("span", { className: `status ${displayState}`, children: isLoop ? 'LOOP' : displayState.toUpperCase() })] }), _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("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, "Artifact schema", String(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?.terminalReason?.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 ?? 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)]) : ['—', '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(IterationHistory, { result: displayResults[selectedStep] }) }), _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.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.` : '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." })] }) })] });
253
+ } }, 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 ${isLoop ? 'loop-icon' : displayState}`, children: isLoop ? '↻' : displayState === 'finished' ? '✓' : displayState === 'waiting' ? '◷' : displayState === 'skipped' ? '–' : '' }), _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: "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: [progress && _jsxs("div", { className: "loop-progress", children: [_jsxs("strong", { children: ["REPEAT LOOP \u00B7 ITERATION ", progress.current, " OF ", progress.total] }), _jsx("span", { className: "loop-bar", children: _jsx("i", { style: { width: `${percent}%` } }) }), _jsx("small", { children: elapsed ? `Elapsed ${elapsed}` : 'Waiting to start' })] }), _jsx("span", { className: `status ${displayState}`, children: isLoop ? 'LOOP' : displayState.toUpperCase() })] }), _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("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 }) }), _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." })] }) })] });
240
254
  }
241
255
  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] }); }
242
- 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 ? _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 schema ", entry.artifactSchemaVersion ?? '—', _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.artifactSchemaVersion), 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." })] }); }
256
+ 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 ? _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." })] }); }
243
257
  function InspectorSection({ title, children }) { return _jsxs("section", { className: "inspector-section", children: [_jsx("h3", { children: title }), children] }); }
244
258
  function KeyValues({ values }) { return _jsx("div", { className: "key-values", children: redactKeyValuePairs(values).map((value, index) => _jsx("span", { children: value }, `${value}-${index}`)) }); }
245
259
  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 })] }); }
@@ -263,7 +277,7 @@ function flattenDisplaySteps(steps, depth = 0, loopId, parentId, collapsed = new
263
277
  const isLoop = step.control?.type === 'repeat';
264
278
  const currentLoopId = isLoop ? step.id : loopId;
265
279
  const branchMode = (step.control?.type === 'branch' || step.control?.type === 'parallel') && step.control.branches?.length;
266
- const row = { id: step.id, name: step.name, depth, kind: step.kind ?? (step.children ? 'operation' : 'action'), loopTotal: isLoop ? Math.max(1, Math.min(step.control?.iterations ?? 1, 32)) : undefined, loopId: currentLoopId, parentId, hasChildren: Boolean(step.children?.length || branchMode) };
280
+ const row = { structuralAddress: step.structuralAddress, id: step.id, name: step.name, depth, kind: step.kind ?? (step.children ? 'operation' : 'action'), loopTotal: isLoop ? Math.max(1, Math.min(step.control?.iterations ?? 1, 32)) : undefined, loopId: currentLoopId, parentId, hasChildren: Boolean(step.children?.length || branchMode) };
267
281
  if (branchMode && !collapsed.has(step.id)) {
268
282
  const branchRows = step.control.branches.flatMap(branch => {
269
283
  const branchId = `${step.id}.${branch.id}`;
@@ -275,21 +289,32 @@ function flattenDisplaySteps(steps, depth = 0, loopId, parentId, collapsed = new
275
289
  return [row, ...(step.children && !collapsed.has(step.id) ? flattenDisplaySteps(step.children, depth + 1, currentLoopId, step.id, collapsed) : [])];
276
290
  });
277
291
  }
278
- export function orderDisplayStepsByResult(steps, results) {
279
- if (!results.length)
280
- return steps;
281
- const resultOrder = new Map(results.map((step, index) => [step.id, index]));
282
- const groups = [];
283
- for (const step of steps) {
284
- if (step.depth === 0)
285
- groups.push([]);
286
- if (groups.length)
287
- groups[groups.length - 1].push(step);
288
- }
289
- return groups
290
- .map((group, index) => ({ group, index, order: resultOrder.get(group[0]?.id) ?? Number.MAX_SAFE_INTEGER }))
291
- .sort((left, right) => left.order - right.order || left.index - right.index)
292
- .flatMap(item => item.group);
292
+ export function orderDisplayStepsByResult(steps, _results) { return steps; }
293
+ export const occurrenceKey = (address) => address ? JSON.stringify([address.scope, address.segments]) : '';
294
+ export function resultsForOccurrence(results, address) { return results.filter(r => address && occurrenceKey(r.structuralAddress) === occurrenceKey(address)); }
295
+ export function resultForOccurrence(results, address) { const matches = resultsForOccurrence(results, address); return matches.length === 1 ? matches[0] : undefined; }
296
+ export function stateForOccurrence(results, address) {
297
+ const matches = resultsForOccurrence(results, address);
298
+ if (!matches.length)
299
+ return undefined;
300
+ if (matches.some(r => r.state === 'failed'))
301
+ return 'failed';
302
+ if (matches.some(r => r.state === 'cancelled'))
303
+ return 'cancelled';
304
+ if (matches.every(r => r.state === 'skipped'))
305
+ return 'skipped';
306
+ if (matches.every(r => r.state === 'finished' || r.state === 'skipped'))
307
+ return 'finished';
308
+ if (matches.some(r => r.state === 'running'))
309
+ return 'running';
310
+ if (matches.some(r => r.state === 'waiting'))
311
+ return 'waiting';
312
+ return 'pending';
313
+ }
314
+ export function RecordedInvocations({ results }) {
315
+ if (results.length < 2)
316
+ return _jsx(IterationHistory, { result: results[0] });
317
+ return _jsxs("div", { className: "iteration-history", children: [_jsxs("p", { children: [results.length, " recorded invocations; no single value selected."] }), results.map((entry, index) => _jsxs("details", { children: [_jsxs("summary", { children: ["Invocation ", index + 1, " \u00B7 ", entry.state.toUpperCase(), " \u00B7 ", entry.durationMs, " ms"] }), _jsx("pre", { children: JSON.stringify(entry.iterations ?? [], null, 2) }), _jsx("strong", { children: "Resolved inputs" }), _jsx(KeyValues, { values: Object.entries(entry.inputs).flatMap(([name, value]) => [name, JSON.stringify(value)]) }), _jsx("strong", { children: "Outputs" }), _jsx(KeyValues, { values: Object.entries(entry.outputs).flatMap(([name, value]) => [name, JSON.stringify(value)]) }), _jsx(IterationHistory, { result: entry })] }, JSON.stringify([entry.structuralAddress, entry.iterations, index])))] });
293
318
  }
294
319
  function formatElapsed(milliseconds) { const seconds = Math.max(0, Math.floor(milliseconds / 1000)); return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`; }
295
320
  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) : [])] : []; }); }