@midscene/core 1.16.0 → 1.16.2

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 (147) hide show
  1. package/dist/es/agent/agent-options.mjs +36 -0
  2. package/dist/es/agent/agent-options.mjs.map +1 -0
  3. package/dist/es/agent/agent-report-manager.mjs +323 -0
  4. package/dist/es/agent/agent-report-manager.mjs.map +1 -0
  5. package/dist/es/agent/agent.mjs +112 -228
  6. package/dist/es/agent/agent.mjs.map +1 -1
  7. package/dist/es/agent/cache-config.mjs +21 -1
  8. package/dist/es/agent/cache-config.mjs.map +1 -1
  9. package/dist/es/agent/tasks.mjs +3 -1
  10. package/dist/es/agent/tasks.mjs.map +1 -1
  11. package/dist/es/agent/utils.mjs +4 -4
  12. package/dist/es/agent/utils.mjs.map +1 -1
  13. package/dist/es/ai-model/models/auto-glm/parser.mjs +1 -1
  14. package/dist/es/ai-model/models/auto-glm/parser.mjs.map +1 -1
  15. package/dist/es/ai-model/models/kimi.mjs +1 -1
  16. package/dist/es/ai-model/models/kimi.mjs.map +1 -1
  17. package/dist/es/ai-model/service-caller/call-ai.mjs +10 -1
  18. package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
  19. package/dist/es/ai-model/service-caller/openai/call-openai.mjs +7 -2
  20. package/dist/es/ai-model/service-caller/openai/call-openai.mjs.map +1 -1
  21. package/dist/es/ai-model/shared/model-locate-result/errors.mjs +1 -1
  22. package/dist/es/ai-model/shared/model-locate-result/errors.mjs.map +1 -1
  23. package/dist/es/ai-model/shared/model-locate-result/parse.mjs +6 -2
  24. package/dist/es/ai-model/shared/model-locate-result/parse.mjs.map +1 -1
  25. package/dist/es/device/index.mjs +9 -8
  26. package/dist/es/device/index.mjs.map +1 -1
  27. package/dist/es/report-generator.mjs +76 -36
  28. package/dist/es/report-generator.mjs.map +1 -1
  29. package/dist/es/report-html-template.mjs +1 -1
  30. package/dist/es/test-run-report.mjs.map +1 -1
  31. package/dist/es/test-runner/engine/document-runtime.mjs +30 -24
  32. package/dist/es/test-runner/engine/document-runtime.mjs.map +1 -1
  33. package/dist/es/test-runner/engine/project-runtime.mjs +17 -11
  34. package/dist/es/test-runner/engine/project-runtime.mjs.map +1 -1
  35. package/dist/es/test-runner/engine/resource-operations.mjs +33 -8
  36. package/dist/es/test-runner/engine/resource-operations.mjs.map +1 -1
  37. package/dist/es/test-runner/engine/run-collected-case.mjs +1 -0
  38. package/dist/es/test-runner/engine/run-collected-case.mjs.map +1 -1
  39. package/dist/es/test-runner/engine/run-workflow-document.mjs +3 -0
  40. package/dist/es/test-runner/engine/run-workflow-document.mjs.map +1 -1
  41. package/dist/es/test-runner/engine/setup-timeout.mjs +37 -0
  42. package/dist/es/test-runner/engine/setup-timeout.mjs.map +1 -0
  43. package/dist/es/test-runner/reporting/merge-report.mjs +3 -1
  44. package/dist/es/test-runner/reporting/merge-report.mjs.map +1 -1
  45. package/dist/es/test-runner/reporting/metrics.mjs +39 -2
  46. package/dist/es/test-runner/reporting/metrics.mjs.map +1 -1
  47. package/dist/es/utils.mjs +1 -1
  48. package/dist/es/yaml/execution-session.mjs +2 -1
  49. package/dist/es/yaml/execution-session.mjs.map +1 -1
  50. package/dist/es/yaml/legacy-yaml-report.mjs +1 -1
  51. package/dist/es/yaml/legacy-yaml-report.mjs.map +1 -1
  52. package/dist/es/yaml/player.mjs +17 -3
  53. package/dist/es/yaml/player.mjs.map +1 -1
  54. package/dist/lib/agent/agent-options.js +73 -0
  55. package/dist/lib/agent/agent-options.js.map +1 -0
  56. package/dist/lib/agent/agent-report-manager.js +357 -0
  57. package/dist/lib/agent/agent-report-manager.js.map +1 -0
  58. package/dist/lib/agent/agent.js +110 -226
  59. package/dist/lib/agent/agent.js.map +1 -1
  60. package/dist/lib/agent/cache-config.js +23 -0
  61. package/dist/lib/agent/cache-config.js.map +1 -1
  62. package/dist/lib/agent/tasks.js +3 -1
  63. package/dist/lib/agent/tasks.js.map +1 -1
  64. package/dist/lib/agent/utils.js +2 -2
  65. package/dist/lib/agent/utils.js.map +1 -1
  66. package/dist/lib/ai-model/models/auto-glm/parser.js +1 -1
  67. package/dist/lib/ai-model/models/auto-glm/parser.js.map +1 -1
  68. package/dist/lib/ai-model/models/kimi.js +1 -1
  69. package/dist/lib/ai-model/models/kimi.js.map +1 -1
  70. package/dist/lib/ai-model/service-caller/call-ai.js +10 -1
  71. package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
  72. package/dist/lib/ai-model/service-caller/openai/call-openai.js +7 -2
  73. package/dist/lib/ai-model/service-caller/openai/call-openai.js.map +1 -1
  74. package/dist/lib/ai-model/shared/model-locate-result/errors.js +1 -1
  75. package/dist/lib/ai-model/shared/model-locate-result/errors.js.map +1 -1
  76. package/dist/lib/ai-model/shared/model-locate-result/parse.js +6 -2
  77. package/dist/lib/ai-model/shared/model-locate-result/parse.js.map +1 -1
  78. package/dist/lib/device/index.js +9 -8
  79. package/dist/lib/device/index.js.map +1 -1
  80. package/dist/lib/report-generator.js +75 -35
  81. package/dist/lib/report-generator.js.map +1 -1
  82. package/dist/lib/report-html-template.js +1 -1
  83. package/dist/lib/test-run-report.js.map +1 -1
  84. package/dist/lib/test-runner/engine/document-runtime.js +30 -24
  85. package/dist/lib/test-runner/engine/document-runtime.js.map +1 -1
  86. package/dist/lib/test-runner/engine/project-runtime.js +17 -11
  87. package/dist/lib/test-runner/engine/project-runtime.js.map +1 -1
  88. package/dist/lib/test-runner/engine/resource-operations.js +36 -8
  89. package/dist/lib/test-runner/engine/resource-operations.js.map +1 -1
  90. package/dist/lib/test-runner/engine/run-collected-case.js +1 -0
  91. package/dist/lib/test-runner/engine/run-collected-case.js.map +1 -1
  92. package/dist/lib/test-runner/engine/run-workflow-document.js +3 -0
  93. package/dist/lib/test-runner/engine/run-workflow-document.js.map +1 -1
  94. package/dist/lib/test-runner/engine/setup-timeout.js +71 -0
  95. package/dist/lib/test-runner/engine/setup-timeout.js.map +1 -0
  96. package/dist/lib/test-runner/reporting/merge-report.js +3 -1
  97. package/dist/lib/test-runner/reporting/merge-report.js.map +1 -1
  98. package/dist/lib/test-runner/reporting/metrics.js +39 -2
  99. package/dist/lib/test-runner/reporting/metrics.js.map +1 -1
  100. package/dist/lib/utils.js +1 -1
  101. package/dist/lib/yaml/execution-session.js +4 -0
  102. package/dist/lib/yaml/execution-session.js.map +1 -1
  103. package/dist/lib/yaml/legacy-yaml-report.js +1 -1
  104. package/dist/lib/yaml/legacy-yaml-report.js.map +1 -1
  105. package/dist/lib/yaml/player.js +16 -2
  106. package/dist/lib/yaml/player.js.map +1 -1
  107. package/dist/types/agent/agent-options.d.ts +4 -0
  108. package/dist/types/agent/agent-options.d.ts.map +1 -0
  109. package/dist/types/agent/agent-report-manager.d.ts +92 -0
  110. package/dist/types/agent/agent-report-manager.d.ts.map +1 -0
  111. package/dist/types/agent/agent.d.ts +31 -24
  112. package/dist/types/agent/agent.d.ts.map +1 -1
  113. package/dist/types/agent/cache-config.d.ts +10 -0
  114. package/dist/types/agent/cache-config.d.ts.map +1 -1
  115. package/dist/types/agent/tasks.d.ts +1 -0
  116. package/dist/types/agent/tasks.d.ts.map +1 -1
  117. package/dist/types/ai-model/models/auto-glm/parser.d.ts.map +1 -1
  118. package/dist/types/ai-model/service-caller/openai/call-openai.d.ts.map +1 -1
  119. package/dist/types/ai-model/shared/model-locate-result/errors.d.ts +1 -1
  120. package/dist/types/ai-model/shared/model-locate-result/errors.d.ts.map +1 -1
  121. package/dist/types/ai-model/shared/model-locate-result/parse.d.ts.map +1 -1
  122. package/dist/types/device/index.d.ts +7 -7
  123. package/dist/types/device/index.d.ts.map +1 -1
  124. package/dist/types/report-generator.d.ts +5 -1
  125. package/dist/types/report-generator.d.ts.map +1 -1
  126. package/dist/types/test-run-report.d.ts +4 -1
  127. package/dist/types/test-run-report.d.ts.map +1 -1
  128. package/dist/types/test-runner/engine/document-runtime.d.ts.map +1 -1
  129. package/dist/types/test-runner/engine/project-runtime.d.ts.map +1 -1
  130. package/dist/types/test-runner/engine/resource-operations.d.ts +5 -0
  131. package/dist/types/test-runner/engine/resource-operations.d.ts.map +1 -1
  132. package/dist/types/test-runner/engine/run-collected-case.d.ts.map +1 -1
  133. package/dist/types/test-runner/engine/run-workflow-document.d.ts.map +1 -1
  134. package/dist/types/test-runner/engine/setup-timeout.d.ts +3 -0
  135. package/dist/types/test-runner/engine/setup-timeout.d.ts.map +1 -0
  136. package/dist/types/test-runner/engine/types.d.ts +7 -0
  137. package/dist/types/test-runner/engine/types.d.ts.map +1 -1
  138. package/dist/types/test-runner/parser/types.d.ts +5 -0
  139. package/dist/types/test-runner/parser/types.d.ts.map +1 -1
  140. package/dist/types/test-runner/project.d.ts +22 -0
  141. package/dist/types/test-runner/project.d.ts.map +1 -1
  142. package/dist/types/test-runner/reporting/merge-report.d.ts.map +1 -1
  143. package/dist/types/test-runner/reporting/metrics.d.ts.map +1 -1
  144. package/dist/types/yaml/execution-session.d.ts +2 -0
  145. package/dist/types/yaml/execution-session.d.ts.map +1 -1
  146. package/dist/types/yaml/player.d.ts.map +1 -1
  147. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"test-run-report.mjs","sources":["../../src/test-run-report.ts"],"sourcesContent":["export const TEST_RUN_REPORT_SCRIPT_TYPE = 'midscene_test_run_dump';\n\nexport type TestRunReportRunStatus = 'success' | 'failed';\nexport type TestRunReportCaseStatus = TestRunReportRunStatus | 'not-run';\nexport type TestRunReportStepPhase =\n | 'beforeAll'\n | 'beforeEach'\n | 'steps'\n | 'afterEach'\n | 'afterAll';\n\n/**\n * A JSON-safe value prepared for display in a Midscene Test report.\n *\n * Paths use JSONPath-like notation rooted at `$`. The path arrays are kept\n * beside the value so the UI never presents redacted or truncated data as if\n * it were complete.\n */\nexport interface TestRunReportValue {\n value: unknown;\n truncatedPaths?: string[];\n redactedPaths?: string[];\n}\n\nexport interface TestRunReportError {\n format?: 'midscene-error';\n name: string;\n message: string;\n stack?: string;\n code?: string;\n details?: TestRunReportValue;\n cause?: TestRunReportError;\n errors?: TestRunReportError[];\n truncated?: boolean;\n}\n\nexport interface TestRunReportOperationReference {\n operationId: string;\n scopeId?: string;\n stepId?: string;\n node?: string;\n}\n\n/** Immutable observation; late completion remains in the operation journal. */\nexport interface TestRunReportCleanup {\n status: 'not-required' | 'completed' | 'pending' | 'failed';\n capturedAt: string;\n operationId?: string;\n blockingOperations?: TestRunReportOperationReference[];\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportRetryDecision {\n scope: 'case' | 'document';\n retryCount: number;\n attemptIndex: number;\n decision: 'retry' | 'stop';\n reason:\n | 'passed'\n | 'attempt-failed'\n | 'budget-exhausted'\n | 'cleanup-pending'\n | 'cleanup-failed'\n | 'operation-pending'\n | 'fatal-error'\n | 'interrupted'\n | 'bail'\n | 'infrastructure-error';\n}\n\nexport interface TestRunReportPhaseResult {\n status: 'success' | 'failed' | 'not-run' | 'not-required';\n plannedStepCount: number;\n executedStepCount: number;\n}\n\nexport interface TestRunReportHostError {\n phase:\n | 'setup'\n | 'execution'\n | 'cleanup'\n | 'report'\n | 'publication'\n | 'observer';\n error: TestRunReportError;\n}\n\nexport interface TestRunReportDiagnostic {\n level: 'warning' | 'error';\n code:\n | 'agent-detail-unresolved'\n | 'agent-detail-unavailable'\n | 'large-report'\n | 'run-infrastructure-error'\n | 'agent-source-unavailable'\n | 'source-without-executions';\n message: string;\n scopeId?: string;\n executionId?: string;\n sourcePaths?: string[];\n stepId?: string;\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportMetrics {\n modelCallCount: number;\n modelTimeMs: number;\n promptTokens: number;\n cachedInputTokens: number;\n completionTokens: number;\n totalTokens: number;\n collection?: {\n status: 'complete' | 'partial' | 'unknown';\n expectedExecutionCount: number;\n capturedExecutionCount: number;\n reasons: string[];\n fields?: Record<\n 'modelCallCount' | 'modelTimeMs' | 'totalTokens',\n 'complete' | 'partial' | 'unknown'\n >;\n };\n}\n\nexport interface TestRunReportSnapshotCollection {\n status: 'complete' | 'partial';\n /** Native dump cutoff before I/O; observation time for partial live sources. */\n capturedAt: string;\n reason?: string;\n}\n\nexport interface TestRunReportAgentDetail {\n reportId: string;\n executionId: string;\n}\n\n/** Operation facts are independent of Agent dump IDs and report snapshots. */\nexport interface TestRunReportOperationEvent {\n operationId: string;\n operationKind: 'node' | 'resource' | 'acquisition' | 'cleanup' | 'readiness';\n kind:\n | 'started'\n | 'cancel-requested'\n | 'settled'\n | 'runner-returned'\n | 'cleanup-deferred';\n at: string;\n stepId?: string;\n scopeId?: string;\n node?: string;\n status?: 'success' | 'failed' | 'not-run';\n /** True only when the actual operation has not settled at Runner return. */\n pending?: boolean;\n error?: TestRunReportError;\n blockingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportPlannedStep {\n id: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n input: TestRunReportValue;\n inputMode: 'template' | 'resolved';\n timeoutMs?: number;\n continueOnError: boolean;\n}\n\nexport interface TestRunReportCasePlan {\n caseId: string;\n name: string;\n caseIndex: number;\n selected: boolean;\n tags: string[];\n onFailure?: 'continue' | 'stop-document';\n beforeEach: TestRunReportPlannedStep[];\n steps: TestRunReportPlannedStep[];\n afterEach: TestRunReportPlannedStep[];\n}\n\n/** A safe snapshot collected before resource acquisition, independent of attempts. */\nexport interface TestRunReportDocumentPlan {\n documentId: string;\n sourcePath: string;\n sourceYaml?: TestRunReportValue;\n retry: { scope: 'case' | 'document'; count: number };\n beforeAll: TestRunReportPlannedStep[];\n cases: TestRunReportCasePlan[];\n afterAll: TestRunReportPlannedStep[];\n}\n\ninterface TestRunReportStepBase {\n id: string;\n planStepId?: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n continuedAfterError: boolean;\n /** Time spent waiting for a cancelled operation before this Step started. */\n waitForPreviousOperationMs?: number;\n input?: TestRunReportValue;\n output?: {\n summary?: string;\n data?: TestRunReportValue;\n };\n error?: TestRunReportError;\n agentDetails?: TestRunReportAgentDetail[];\n agentDetailDiagnostic?: string;\n /** Usage survives even when screenshots or execution detail storage fails. */\n metrics?: TestRunReportMetrics;\n evidenceCollection?: {\n status: 'complete' | 'partial' | 'unknown';\n /** The missing source also prevented independent usage recovery. */\n modelUsageUnavailable?: boolean;\n capturedAt?: string;\n errors?: TestRunReportError[];\n };\n operations?: { capturedAt: string; events: TestRunReportOperationEvent[] };\n}\n\nexport type TestRunReportStep = TestRunReportStepBase &\n (\n | {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n notRunReason?: never;\n blockedBy?: never;\n }\n | {\n status: 'not-run';\n notRunReason: string;\n blockedBy?: string;\n startedAt?: never;\n endedAt?: never;\n durationMs?: never;\n }\n );\n\nexport interface TestRunReportAttempt {\n attemptId: string;\n attemptIndex: number;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n beforeEach: TestRunReportStep[];\n steps: TestRunReportStep[];\n afterEach: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n}\n\nexport interface TestRunReportCase {\n plan?: TestRunReportCasePlan;\n caseId: string;\n name: string;\n caseIndex: number;\n status: TestRunReportCaseStatus;\n notRunReason?: string;\n attempts: TestRunReportAttempt[];\n}\n\nexport interface TestRunReportDocument {\n plan?: TestRunReportDocumentPlan;\n documentId: string;\n /** Logical file identity when separate invocations need distinct UI IDs. */\n logicalDocumentId?: string;\n documentRunId?: string;\n attemptIndex?: number;\n sourcePath: string;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt?: string;\n endedAt?: string;\n durationMs?: number;\n beforeAll: TestRunReportStep[];\n cases: TestRunReportCase[];\n afterAll: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n /** Whole-file invocation history; Cases aggregate their own attempts. */\n attempts?: Omit<TestRunReportDocument, 'cases' | 'attempts'>[];\n}\n\nexport interface TestRunReportCollectionError {\n sourcePath: string;\n error: TestRunReportError;\n}\n\nexport interface TestRunReportProjectLifecycle {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n setupError?: TestRunReportError;\n teardownErrors?: TestRunReportError[];\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportProject {\n plans?: readonly TestRunReportDocumentPlan[];\n projectId: string;\n name: string;\n /** Display identity, also supports legacy custom interface adapters. */\n platform?: string;\n status: TestRunReportRunStatus;\n retry: number;\n lifecycle?: TestRunReportProjectLifecycle;\n documents: TestRunReportDocument[];\n collectionErrors: TestRunReportCollectionError[];\n}\n\nexport interface TestRunReportSummary {\n total: number;\n passed: number;\n failed: number;\n notRun: number;\n filtered: number;\n collectionErrors: number;\n documentFailures: number;\n projectFailures: number;\n}\n\nexport interface TestRunReportDump {\n schemaVersion: 1;\n kind: 'test-runner';\n runId: string;\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n summary: TestRunReportSummary;\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n projects: TestRunReportProject[];\n diagnostics?: TestRunReportDiagnostic[];\n operationLogs?: { path: string; snapshotAt: string }[];\n}\n\nexport interface TestRunReportSource {\n /** Resource/report scope that owns this Midscene Agent source. */\n scopeId: string;\n /** Absolute path identifying the Midscene Agent report. */\n sourcePath: string;\n /** Current Agent dump JSON; when present, HTML is not parsed for execution data. */\n dumpPath?: string;\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface IndexedTestRunReportSource {\n reportId: string;\n scopeId: string;\n sourcePath: string;\n executionIds: string[];\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface TestRunReportSourceIndex {\n sources: IndexedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n /** Usage from readable sources, before applying source-availability diagnostics. */\n capturedMetrics?: TestRunReportMetrics;\n diagnostics?: TestRunReportDiagnostic[];\n}\n\nexport interface AssembleTestRunReportOptions {\n outputDir: string;\n /** File or directory name without an extension. */\n reportFileName: string;\n sources: readonly TestRunReportSource[];\n buildRunnerDump(index: TestRunReportSourceIndex): TestRunReportDump;\n overwrite?: boolean;\n}\n"],"names":["TEST_RUN_REPORT_SCRIPT_TYPE"],"mappings":"AAAO,MAAMA,8BAA8B"}
1
+ {"version":3,"file":"test-run-report.mjs","sources":["../../src/test-run-report.ts"],"sourcesContent":["import type { ReportModelUsageSummary } from './report-stats';\n\nexport const TEST_RUN_REPORT_SCRIPT_TYPE = 'midscene_test_run_dump';\n\nexport type TestRunReportRunStatus = 'success' | 'failed';\nexport type TestRunReportCaseStatus = TestRunReportRunStatus | 'not-run';\nexport type TestRunReportStepPhase =\n | 'beforeAll'\n | 'beforeEach'\n | 'steps'\n | 'afterEach'\n | 'afterAll';\n\n/**\n * A JSON-safe value prepared for display in a Midscene Test report.\n *\n * Paths use JSONPath-like notation rooted at `$`. The path arrays are kept\n * beside the value so the UI never presents redacted or truncated data as if\n * it were complete.\n */\nexport interface TestRunReportValue {\n value: unknown;\n truncatedPaths?: string[];\n redactedPaths?: string[];\n}\n\nexport interface TestRunReportError {\n format?: 'midscene-error';\n name: string;\n message: string;\n stack?: string;\n code?: string;\n details?: TestRunReportValue;\n cause?: TestRunReportError;\n errors?: TestRunReportError[];\n truncated?: boolean;\n}\n\nexport interface TestRunReportOperationReference {\n operationId: string;\n scopeId?: string;\n stepId?: string;\n node?: string;\n}\n\n/** Immutable observation; late completion remains in the operation journal. */\nexport interface TestRunReportCleanup {\n status: 'not-required' | 'completed' | 'pending' | 'failed';\n capturedAt: string;\n operationId?: string;\n blockingOperations?: TestRunReportOperationReference[];\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportRetryDecision {\n scope: 'case' | 'document';\n retryCount: number;\n attemptIndex: number;\n decision: 'retry' | 'stop';\n reason:\n | 'passed'\n | 'attempt-failed'\n | 'budget-exhausted'\n | 'cleanup-pending'\n | 'cleanup-failed'\n | 'operation-pending'\n | 'fatal-error'\n | 'interrupted'\n | 'bail'\n | 'infrastructure-error';\n}\n\nexport interface TestRunReportPhaseResult {\n status: 'success' | 'failed' | 'not-run' | 'not-required';\n plannedStepCount: number;\n executedStepCount: number;\n}\n\nexport interface TestRunReportHostError {\n phase:\n | 'setup'\n | 'execution'\n | 'cleanup'\n | 'report'\n | 'publication'\n | 'observer';\n error: TestRunReportError;\n}\n\nexport interface TestRunReportDiagnostic {\n level: 'warning' | 'error';\n code:\n | 'agent-detail-unresolved'\n | 'agent-detail-unavailable'\n | 'large-report'\n | 'run-infrastructure-error'\n | 'agent-source-unavailable'\n | 'source-without-executions';\n message: string;\n scopeId?: string;\n executionId?: string;\n sourcePaths?: string[];\n stepId?: string;\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportMetrics {\n /** Actual models observed in usage records, independent of configured defaults. */\n models?: ReportModelUsageSummary[];\n modelCallCount: number;\n modelTimeMs: number;\n promptTokens: number;\n cachedInputTokens: number;\n completionTokens: number;\n totalTokens: number;\n collection?: {\n status: 'complete' | 'partial' | 'unknown';\n expectedExecutionCount: number;\n capturedExecutionCount: number;\n reasons: string[];\n fields?: Record<\n 'modelCallCount' | 'modelTimeMs' | 'totalTokens',\n 'complete' | 'partial' | 'unknown'\n > &\n Partial<\n Record<\n 'promptTokens' | 'completionTokens',\n 'complete' | 'partial' | 'unknown'\n >\n >;\n };\n}\n\nexport interface TestRunReportSnapshotCollection {\n status: 'complete' | 'partial';\n /** Native dump cutoff before I/O; observation time for partial live sources. */\n capturedAt: string;\n reason?: string;\n}\n\nexport interface TestRunReportAgentDetail {\n reportId: string;\n executionId: string;\n}\n\n/** Operation facts are independent of Agent dump IDs and report snapshots. */\nexport interface TestRunReportOperationEvent {\n operationId: string;\n operationKind: 'node' | 'resource' | 'acquisition' | 'cleanup' | 'readiness';\n kind:\n | 'started'\n | 'cancel-requested'\n | 'settled'\n | 'runner-returned'\n | 'cleanup-deferred';\n at: string;\n stepId?: string;\n scopeId?: string;\n node?: string;\n status?: 'success' | 'failed' | 'not-run';\n /** True only when the actual operation has not settled at Runner return. */\n pending?: boolean;\n error?: TestRunReportError;\n blockingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportPlannedStep {\n id: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n input: TestRunReportValue;\n inputMode: 'template' | 'resolved';\n timeoutMs?: number;\n continueOnError: boolean;\n}\n\nexport interface TestRunReportCasePlan {\n caseId: string;\n name: string;\n caseIndex: number;\n selected: boolean;\n tags: string[];\n onFailure?: 'continue' | 'stop-document';\n beforeEach: TestRunReportPlannedStep[];\n steps: TestRunReportPlannedStep[];\n afterEach: TestRunReportPlannedStep[];\n}\n\n/** A safe snapshot collected before resource acquisition, independent of attempts. */\nexport interface TestRunReportDocumentPlan {\n documentId: string;\n sourcePath: string;\n sourceYaml?: TestRunReportValue;\n retry: { scope: 'case' | 'document'; count: number };\n beforeAll: TestRunReportPlannedStep[];\n cases: TestRunReportCasePlan[];\n afterAll: TestRunReportPlannedStep[];\n}\n\ninterface TestRunReportStepBase {\n id: string;\n planStepId?: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n continuedAfterError: boolean;\n /** Time spent waiting for a cancelled operation before this Step started. */\n waitForPreviousOperationMs?: number;\n input?: TestRunReportValue;\n output?: {\n summary?: string;\n data?: TestRunReportValue;\n };\n error?: TestRunReportError;\n agentDetails?: TestRunReportAgentDetail[];\n agentDetailDiagnostic?: string;\n /** Usage survives even when screenshots or execution detail storage fails. */\n metrics?: TestRunReportMetrics;\n evidenceCollection?: {\n status: 'complete' | 'partial' | 'unknown';\n /** The missing source also prevented independent usage recovery. */\n modelUsageUnavailable?: boolean;\n capturedAt?: string;\n errors?: TestRunReportError[];\n };\n operations?: { capturedAt: string; events: TestRunReportOperationEvent[] };\n}\n\nexport type TestRunReportStep = TestRunReportStepBase &\n (\n | {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n notRunReason?: never;\n blockedBy?: never;\n }\n | {\n status: 'not-run';\n notRunReason: string;\n blockedBy?: string;\n startedAt?: never;\n endedAt?: never;\n durationMs?: never;\n }\n );\n\nexport interface TestRunReportAttempt {\n attemptId: string;\n attemptIndex: number;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n beforeEach: TestRunReportStep[];\n steps: TestRunReportStep[];\n afterEach: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n}\n\nexport interface TestRunReportCase {\n plan?: TestRunReportCasePlan;\n caseId: string;\n name: string;\n caseIndex: number;\n status: TestRunReportCaseStatus;\n notRunReason?: string;\n attempts: TestRunReportAttempt[];\n}\n\nexport interface TestRunReportDocument {\n plan?: TestRunReportDocumentPlan;\n documentId: string;\n /** Logical file identity when separate invocations need distinct UI IDs. */\n logicalDocumentId?: string;\n documentRunId?: string;\n attemptIndex?: number;\n sourcePath: string;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt?: string;\n endedAt?: string;\n durationMs?: number;\n beforeAll: TestRunReportStep[];\n cases: TestRunReportCase[];\n afterAll: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n /** Whole-file invocation history; Cases aggregate their own attempts. */\n attempts?: Omit<TestRunReportDocument, 'cases' | 'attempts'>[];\n}\n\nexport interface TestRunReportCollectionError {\n sourcePath: string;\n error: TestRunReportError;\n}\n\nexport interface TestRunReportProjectLifecycle {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n setupError?: TestRunReportError;\n teardownErrors?: TestRunReportError[];\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportProject {\n plans?: readonly TestRunReportDocumentPlan[];\n projectId: string;\n name: string;\n /** Display identity, also supports legacy custom interface adapters. */\n platform?: string;\n status: TestRunReportRunStatus;\n retry: number;\n lifecycle?: TestRunReportProjectLifecycle;\n documents: TestRunReportDocument[];\n collectionErrors: TestRunReportCollectionError[];\n}\n\nexport interface TestRunReportSummary {\n total: number;\n passed: number;\n failed: number;\n notRun: number;\n filtered: number;\n collectionErrors: number;\n documentFailures: number;\n projectFailures: number;\n}\n\nexport interface TestRunReportDump {\n schemaVersion: 1;\n kind: 'test-runner';\n runId: string;\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n summary: TestRunReportSummary;\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n projects: TestRunReportProject[];\n diagnostics?: TestRunReportDiagnostic[];\n operationLogs?: { path: string; snapshotAt: string }[];\n}\n\nexport interface TestRunReportSource {\n /** Resource/report scope that owns this Midscene Agent source. */\n scopeId: string;\n /** Absolute path identifying the Midscene Agent report. */\n sourcePath: string;\n /** Current Agent dump JSON; when present, HTML is not parsed for execution data. */\n dumpPath?: string;\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface IndexedTestRunReportSource {\n reportId: string;\n scopeId: string;\n sourcePath: string;\n executionIds: string[];\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface TestRunReportSourceIndex {\n sources: IndexedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n /** Usage from readable sources, before applying source-availability diagnostics. */\n capturedMetrics?: TestRunReportMetrics;\n diagnostics?: TestRunReportDiagnostic[];\n}\n\nexport interface AssembleTestRunReportOptions {\n outputDir: string;\n /** File or directory name without an extension. */\n reportFileName: string;\n sources: readonly TestRunReportSource[];\n buildRunnerDump(index: TestRunReportSourceIndex): TestRunReportDump;\n overwrite?: boolean;\n}\n"],"names":["TEST_RUN_REPORT_SCRIPT_TYPE"],"mappings":"AAEO,MAAMA,8BAA8B"}
@@ -7,6 +7,7 @@ import { WorkflowExecutionFailure, asExecutionError } from "./execution-failure.
7
7
  import { ResourceCleanupDeferredError, cleanupScopeResources, createResourceScope, getPendingScopeOperations, getResourceCleanupSnapshot, recoverCancelledScopeResources, setResourceOperationIdentity } from "./resource-operations.mjs";
8
8
  import { phaseResult } from "./result-metadata.mjs";
9
9
  import { reportPathsFromTeardown } from "./scope-teardown.mjs";
10
+ import { runSetupWithTimeout } from "./setup-timeout.mjs";
10
11
  function createDocumentRuntime(document, options) {
11
12
  const documentRunId = options.createDocumentRunId?.() ?? uuid();
12
13
  const plan = options.plan ?? buildTestRunReportPlan(document, {
@@ -172,36 +173,40 @@ function createDocumentRuntime(document, options) {
172
173
  if (!signal.aborted && options.documentSetup) {
173
174
  acceptingSetupTeardowns = true;
174
175
  try {
175
- context = await options.documentSetup.setup({
176
- project,
177
- projectContext: options.projectContext,
178
- document: {
179
- documentId: document.documentId,
180
- documentRunId,
181
- sourcePath: document.sourcePath,
182
- attemptIndex: options.documentAttemptIndex ?? 0
183
- },
184
- signal,
185
- onTeardown (teardown) {
186
- if (!acceptingSetupTeardowns || 'function' != typeof teardown) throw new WorkflowLifecycleError('Document setup onTeardown() requires a function registered during setup.', {
187
- documentId: document.documentId,
188
- documentRunId
189
- });
190
- nodeTeardownStack.push({
191
- registrationIndex: nodeTeardownStack.length,
192
- node: options.documentSetup.name,
193
- teardown,
194
- host: true
176
+ await runSetupWithTimeout(async ()=>{
177
+ try {
178
+ context = await options.documentSetup.setup({
179
+ project,
180
+ projectContext: options.projectContext,
181
+ document: {
182
+ documentId: document.documentId,
183
+ documentRunId,
184
+ sourcePath: document.sourcePath,
185
+ attemptIndex: options.documentAttemptIndex ?? 0
186
+ },
187
+ signal,
188
+ onTeardown (teardown) {
189
+ if (!acceptingSetupTeardowns || 'function' != typeof teardown) throw new WorkflowLifecycleError('Document setup onTeardown() requires a function registered during setup.', {
190
+ documentId: document.documentId,
191
+ documentRunId
192
+ });
193
+ nodeTeardownStack.push({
194
+ registrationIndex: nodeTeardownStack.length,
195
+ node: options.documentSetup.name,
196
+ teardown,
197
+ host: true
198
+ });
199
+ }
195
200
  });
201
+ } finally{
202
+ acceptingSetupTeardowns = false;
196
203
  }
197
- });
204
+ }, signal, resourceScope.abort, options.setupTimeoutMs);
198
205
  } catch (error) {
199
206
  hostErrors.push({
200
207
  phase: 'setup',
201
208
  error: asExecutionError(error)
202
209
  });
203
- } finally{
204
- acceptingSetupTeardowns = false;
205
210
  }
206
211
  }
207
212
  try {
@@ -258,7 +263,8 @@ function createDocumentRuntime(document, options) {
258
263
  }
259
264
  };
260
265
  try {
261
- if (document.lifecycle.afterAll.length || nodeTeardownStack.length) await cleanupScopeResources(signal, cleanup, {
266
+ if (document.lifecycle.afterAll.length || nodeTeardownStack.length || void 0 !== options.setupTimeoutMs && getPendingScopeOperations(signal).length) await cleanupScopeResources(signal, cleanup, {
267
+ totalTimeoutMs: options.teardownTimeoutMs?.(),
262
268
  beforeCleanup: async (cleanupSignal)=>{
263
269
  try {
264
270
  if (!hostErrors.some((error)=>'setup' === error.phase)) await runPhase('afterAll', afterAll, cleanupSignal);
@@ -1 +1 @@
1
- {"version":3,"file":"test-runner/engine/document-runtime.mjs","sources":["../../../../src/test-runner/engine/document-runtime.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { uuid as randomUUID } from '@midscene/shared/utils';\nimport {\n NodeScopeTeardownError,\n type WorkflowError,\n WorkflowLifecycleError,\n} from '../errors';\nimport type { CollectedWorkflowDocument } from '../parser/types';\nimport { buildTestRunReportPlan } from '../reporting/test-plan';\nimport { executeStep } from './execute-step';\nimport {\n WorkflowExecutionFailure,\n asExecutionError,\n} from './execution-failure';\nimport {\n ResourceCleanupDeferredError,\n cleanupScopeResources,\n createResourceScope,\n getPendingScopeOperations,\n getResourceCleanupSnapshot,\n recoverCancelledScopeResources,\n setResourceOperationIdentity,\n} from './resource-operations';\nimport { phaseResult } from './result-metadata';\nimport { reportPathsFromTeardown } from './scope-teardown';\nimport type {\n CreateDocumentRuntimeOptions,\n DocumentNodePhase,\n NodeDocumentContext,\n NodeScopeTeardown,\n StepRunResult,\n WorkflowDocumentRunResult,\n WorkflowDocumentRuntime,\n WorkflowHostError,\n} from './types';\n\nexport function createDocumentRuntime<TContext = undefined>(\n document: CollectedWorkflowDocument,\n options: CreateDocumentRuntimeOptions<TContext>,\n): WorkflowDocumentRuntime<TContext> {\n const documentRunId = options.createDocumentRunId?.() ?? randomUUID();\n const plan =\n options.plan ??\n buildTestRunReportPlan(document, {\n defaultTimeoutMs: options.defaultTimeoutMs,\n retry: { scope: 'case', count: options.project?.retry ?? 0 },\n });\n const startedAt = new Date();\n const beforeAll: StepRunResult[] = [];\n const afterAll: StepRunResult[] = [];\n const nodes = {\n beforeAll: document.lifecycle.beforeAll.map((step) =>\n options.resolveNode(step.node),\n ),\n afterAll: document.lifecycle.afterAll.map((step) =>\n options.resolveNode(step.node),\n ),\n };\n const project =\n options.project ??\n Object.freeze({\n projectId: document.projectId,\n name: document.projectId,\n retry: 0,\n });\n let context = options.projectContext as TContext;\n const resourceScope = createResourceScope(options.signal);\n const signal = resourceScope.signal;\n setResourceOperationIdentity(signal, { scopeId: documentRunId });\n const nodeTeardownStack: Array<{\n registrationIndex: number;\n node: string;\n teardown: NodeScopeTeardown;\n host?: boolean;\n }> = [];\n let acceptingNodeTeardowns = true;\n let started = false;\n let finishedResult: WorkflowDocumentRunResult | undefined;\n let finishPromise: Promise<WorkflowDocumentRunResult> | undefined;\n const reportPaths = new Set<string>();\n const reportSources: import('./types').WorkflowReportSource[] = [];\n const capturedSources = new Map<\n string,\n import('./types').WorkflowReportSource\n >();\n const executionErrors: unknown[] = [];\n const hostErrors: WorkflowHostError[] = [];\n let acceptingSetupTeardowns = false;\n\n const createResult = (\n teardownErrors: WorkflowError[] = [],\n ): WorkflowDocumentRunResult => {\n const endedAt = new Date();\n const cleanup = getResourceCleanupSnapshot(signal);\n const pendingOperations = getPendingScopeOperations(signal);\n const phaseResults = {\n beforeAll: phaseResult(document.lifecycle.beforeAll.length, beforeAll),\n afterAll: phaseResult(document.lifecycle.afterAll.length, afterAll),\n };\n return {\n plan,\n documentId: document.documentId,\n documentRunId,\n ...(options.documentAttemptIndex === undefined\n ? {}\n : { attemptIndex: options.documentAttemptIndex }),\n projectId: document.projectId,\n projectName: project.name,\n sourcePath: document.sourcePath,\n status:\n executionErrors.length > 0 ||\n hostErrors.length > 0 ||\n beforeAll.some((step) => step.status !== 'success') ||\n afterAll.some((step) => step.status !== 'success') ||\n cleanup.status === 'pending' ||\n cleanup.status === 'failed' ||\n pendingOperations.length > 0 ||\n teardownErrors.length > 0\n ? 'failed'\n : 'success',\n executionStatus: Object.values(phaseResults).some(\n (phase) => phase.status === 'failed',\n )\n ? 'failed'\n : Object.values(phaseResults).some(\n (phase) => phase.status === 'not-run',\n ) ||\n hostErrors.some(({ phase }) => phase === 'setup') ||\n (signal.aborted && !beforeAll.length && !afterAll.length)\n ? 'not-run'\n : 'success',\n phases: phaseResults,\n cleanup,\n ...(pendingOperations.length ? { pendingOperations } : {}),\n startedAt: startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),\n beforeAll: [...beforeAll],\n afterAll: [...afterAll],\n ...(executionErrors.length\n ? {\n executionErrors: executionErrors\n .filter(\n (error) => !hostErrors.some((host) => host.error === error),\n )\n .map(asExecutionError),\n }\n : {}),\n ...(hostErrors.length ? { hostErrors: [...hostErrors] } : {}),\n ...(teardownErrors.length > 0\n ? { teardownErrors: [...teardownErrors] }\n : {}),\n ...(reportPaths.size > 0 ? { reportPaths: [...reportPaths] } : {}),\n ...(capturedSources.size || reportSources.length\n ? { reportSources: [...capturedSources.values(), ...reportSources] }\n : {}),\n };\n };\n\n const runPhase = async (\n phase: DocumentNodePhase,\n results: StepRunResult[],\n executionSignal = signal,\n ): Promise<void> => {\n for (const [stepIndex, step] of document.lifecycle[phase].entries()) {\n const waitForPreviousOperationMs =\n stepIndex > 0\n ? await recoverCancelledScopeResources(executionSignal)\n : 0;\n const documentContext: NodeDocumentContext = {\n documentId: document.documentId,\n documentRunId,\n projectId: document.projectId,\n projectName: project.name,\n sourcePath: document.sourcePath,\n phase,\n stepIndex,\n };\n const stepInfo = {\n scope: 'document' as const,\n node: step.node,\n stepCount: document.lifecycle[phase].length,\n document: documentContext,\n };\n await options.onStepStart?.(stepInfo);\n const result = await executeStep(\n step,\n nodes[phase][stepIndex],\n { scope: 'document', document: documentContext },\n context,\n {\n signal: executionSignal,\n defaultTimeoutMs: options.defaultTimeoutMs,\n onTeardown(node, teardown) {\n if (!acceptingNodeTeardowns) {\n throw new WorkflowLifecycleError(\n 'Node teardown can only be registered while a workflow document is running.',\n { documentId: document.documentId, documentRunId, node },\n );\n }\n if (typeof teardown !== 'function') {\n throw new WorkflowLifecycleError(\n 'Node onTeardown() requires a teardown function.',\n { documentId: document.documentId, documentRunId, node },\n );\n }\n nodeTeardownStack.push({\n registrationIndex: nodeTeardownStack.length,\n node,\n teardown,\n });\n },\n },\n );\n if (waitForPreviousOperationMs > 0)\n result.waitForPreviousOperationMs = waitForPreviousOperationMs;\n results.push(result);\n for (const source of result.report?.sources ?? [])\n capturedSources.set(\n `${source.sourcePath}\\u0000${source.dumpPath ?? ''}`,\n source,\n );\n await options.onStepResult?.(stepInfo, result);\n if (result.status !== 'success' && !result.continuedAfterError) break;\n }\n };\n\n return {\n signal,\n get context() {\n return context;\n },\n get canRunCases() {\n return (\n started &&\n executionErrors.length === 0 &&\n hostErrors.length === 0 &&\n !signal.aborted &&\n !beforeAll.some((step) => step.status !== 'success')\n );\n },\n async start() {\n if (started) {\n throw new WorkflowLifecycleError(\n 'Workflow document runtime has already started.',\n { documentId: document.documentId, documentRunId },\n );\n }\n started = true;\n if (!signal.aborted && options.documentSetup) {\n acceptingSetupTeardowns = true;\n try {\n context = await options.documentSetup.setup({\n project,\n projectContext: options.projectContext,\n document: {\n documentId: document.documentId,\n documentRunId,\n sourcePath: document.sourcePath,\n attemptIndex: options.documentAttemptIndex ?? 0,\n },\n signal,\n onTeardown(teardown) {\n if (!acceptingSetupTeardowns || typeof teardown !== 'function')\n throw new WorkflowLifecycleError(\n 'Document setup onTeardown() requires a function registered during setup.',\n { documentId: document.documentId, documentRunId },\n );\n nodeTeardownStack.push({\n registrationIndex: nodeTeardownStack.length,\n node: options.documentSetup!.name,\n teardown,\n host: true,\n });\n },\n });\n } catch (error) {\n hostErrors.push({ phase: 'setup', error: asExecutionError(error) });\n } finally {\n acceptingSetupTeardowns = false;\n }\n }\n try {\n if (!signal.aborted && !hostErrors.length)\n await runPhase('beforeAll', beforeAll);\n } catch (error) {\n executionErrors.push(error);\n throw new WorkflowExecutionFailure(createResult(), [\n ...executionErrors,\n ]);\n }\n return createResult();\n },\n async finish() {\n if (finishPromise) return finishPromise;\n if (!started) {\n throw new WorkflowLifecycleError(\n 'Workflow document runtime must start before it can finish.',\n { documentId: document.documentId, documentRunId },\n );\n }\n finishPromise = (async () => {\n const teardownErrors: WorkflowError[] = [];\n const cleanupExecutionErrors: unknown[] = [];\n let deferredCleanup = false;\n const cleanup = async () => {\n const priorTeardownErrors = teardownErrors.length;\n acceptingNodeTeardowns = false;\n for (const {\n registrationIndex,\n node,\n teardown,\n host,\n } of nodeTeardownStack.reverse()) {\n try {\n const released = await teardown();\n reportSources.push(...(released?.reportSources ?? []));\n for (const path of reportPathsFromTeardown(released)) {\n reportPaths.add(path);\n }\n } catch (error) {\n if (host) {\n const failure = asExecutionError(error);\n hostErrors.push({\n phase: 'cleanup',\n error: failure,\n });\n executionErrors.push(failure);\n cleanupExecutionErrors.push(error);\n continue;\n }\n teardownErrors.push(\n new NodeScopeTeardownError(error, {\n scope: 'document',\n scopeId: documentRunId,\n node,\n registrationIndex,\n }),\n );\n }\n }\n {\n const errors = [\n ...cleanupExecutionErrors,\n ...teardownErrors.slice(priorTeardownErrors),\n ];\n if (errors.length)\n throw new AggregateError(errors, 'Document cleanup failed');\n }\n };\n try {\n if (document.lifecycle.afterAll.length || nodeTeardownStack.length)\n await cleanupScopeResources(signal, cleanup, {\n beforeCleanup: async (cleanupSignal) => {\n try {\n if (!hostErrors.some((error) => error.phase === 'setup'))\n await runPhase('afterAll', afterAll, cleanupSignal);\n } catch (error) {\n executionErrors.push(error);\n cleanupExecutionErrors.push(error);\n }\n },\n onDeferredError: (error) =>\n getDebug('runner:cleanup', { console: true })(\n `Deferred document cleanup failed: ${String(error)}`,\n ),\n });\n else acceptingNodeTeardowns = false;\n } catch (error) {\n deferredCleanup = error instanceof ResourceCleanupDeferredError;\n if (!deferredCleanup) acceptingNodeTeardowns = false;\n if (\n !deferredCleanup &&\n !teardownErrors.length &&\n !cleanupExecutionErrors.length\n )\n teardownErrors.push(\n new NodeScopeTeardownError(error, {\n scope: 'document',\n scopeId: documentRunId,\n node: 'resource-drain',\n registrationIndex: -1,\n }),\n );\n }\n finishedResult = createResult(teardownErrors);\n try {\n await options.onResult?.(finishedResult);\n } catch (error) {\n executionErrors.push(error);\n finishedResult = createResult(teardownErrors);\n }\n resourceScope.dispose();\n if (executionErrors.length)\n throw new WorkflowExecutionFailure(finishedResult, [\n ...executionErrors,\n ]);\n return finishedResult;\n })();\n return finishPromise;\n },\n };\n}\n"],"names":["createDocumentRuntime","document","options","documentRunId","randomUUID","plan","buildTestRunReportPlan","startedAt","Date","beforeAll","afterAll","nodes","step","project","Object","context","resourceScope","createResourceScope","signal","setResourceOperationIdentity","nodeTeardownStack","acceptingNodeTeardowns","started","finishedResult","finishPromise","reportPaths","Set","reportSources","capturedSources","Map","executionErrors","hostErrors","acceptingSetupTeardowns","createResult","teardownErrors","endedAt","cleanup","getResourceCleanupSnapshot","pendingOperations","getPendingScopeOperations","phaseResults","phaseResult","undefined","phase","Math","error","host","asExecutionError","runPhase","results","executionSignal","stepIndex","waitForPreviousOperationMs","recoverCancelledScopeResources","documentContext","stepInfo","result","executeStep","node","teardown","WorkflowLifecycleError","source","WorkflowExecutionFailure","cleanupExecutionErrors","deferredCleanup","priorTeardownErrors","registrationIndex","released","path","reportPathsFromTeardown","failure","NodeScopeTeardownError","errors","AggregateError","cleanupScopeResources","cleanupSignal","getDebug","String","ResourceCleanupDeferredError"],"mappings":"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"}
1
+ {"version":3,"file":"test-runner/engine/document-runtime.mjs","sources":["../../../../src/test-runner/engine/document-runtime.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { uuid as randomUUID } from '@midscene/shared/utils';\nimport {\n NodeScopeTeardownError,\n type WorkflowError,\n WorkflowLifecycleError,\n} from '../errors';\nimport type { CollectedWorkflowDocument } from '../parser/types';\nimport { buildTestRunReportPlan } from '../reporting/test-plan';\nimport { executeStep } from './execute-step';\nimport {\n WorkflowExecutionFailure,\n asExecutionError,\n} from './execution-failure';\nimport {\n ResourceCleanupDeferredError,\n cleanupScopeResources,\n createResourceScope,\n getPendingScopeOperations,\n getResourceCleanupSnapshot,\n recoverCancelledScopeResources,\n setResourceOperationIdentity,\n} from './resource-operations';\nimport { phaseResult } from './result-metadata';\nimport { reportPathsFromTeardown } from './scope-teardown';\nimport { runSetupWithTimeout } from './setup-timeout';\nimport type {\n CreateDocumentRuntimeOptions,\n DocumentNodePhase,\n NodeDocumentContext,\n NodeScopeTeardown,\n StepRunResult,\n WorkflowDocumentRunResult,\n WorkflowDocumentRuntime,\n WorkflowHostError,\n} from './types';\n\nexport function createDocumentRuntime<TContext = undefined>(\n document: CollectedWorkflowDocument,\n options: CreateDocumentRuntimeOptions<TContext>,\n): WorkflowDocumentRuntime<TContext> {\n const documentRunId = options.createDocumentRunId?.() ?? randomUUID();\n const plan =\n options.plan ??\n buildTestRunReportPlan(document, {\n defaultTimeoutMs: options.defaultTimeoutMs,\n retry: { scope: 'case', count: options.project?.retry ?? 0 },\n });\n const startedAt = new Date();\n const beforeAll: StepRunResult[] = [];\n const afterAll: StepRunResult[] = [];\n const nodes = {\n beforeAll: document.lifecycle.beforeAll.map((step) =>\n options.resolveNode(step.node),\n ),\n afterAll: document.lifecycle.afterAll.map((step) =>\n options.resolveNode(step.node),\n ),\n };\n const project =\n options.project ??\n Object.freeze({\n projectId: document.projectId,\n name: document.projectId,\n retry: 0,\n });\n let context = options.projectContext as TContext;\n const resourceScope = createResourceScope(options.signal);\n const signal = resourceScope.signal;\n setResourceOperationIdentity(signal, { scopeId: documentRunId });\n const nodeTeardownStack: Array<{\n registrationIndex: number;\n node: string;\n teardown: NodeScopeTeardown;\n host?: boolean;\n }> = [];\n let acceptingNodeTeardowns = true;\n let started = false;\n let finishedResult: WorkflowDocumentRunResult | undefined;\n let finishPromise: Promise<WorkflowDocumentRunResult> | undefined;\n const reportPaths = new Set<string>();\n const reportSources: import('./types').WorkflowReportSource[] = [];\n const capturedSources = new Map<\n string,\n import('./types').WorkflowReportSource\n >();\n const executionErrors: unknown[] = [];\n const hostErrors: WorkflowHostError[] = [];\n let acceptingSetupTeardowns = false;\n\n const createResult = (\n teardownErrors: WorkflowError[] = [],\n ): WorkflowDocumentRunResult => {\n const endedAt = new Date();\n const cleanup = getResourceCleanupSnapshot(signal);\n const pendingOperations = getPendingScopeOperations(signal);\n const phaseResults = {\n beforeAll: phaseResult(document.lifecycle.beforeAll.length, beforeAll),\n afterAll: phaseResult(document.lifecycle.afterAll.length, afterAll),\n };\n return {\n plan,\n documentId: document.documentId,\n documentRunId,\n ...(options.documentAttemptIndex === undefined\n ? {}\n : { attemptIndex: options.documentAttemptIndex }),\n projectId: document.projectId,\n projectName: project.name,\n sourcePath: document.sourcePath,\n status:\n executionErrors.length > 0 ||\n hostErrors.length > 0 ||\n beforeAll.some((step) => step.status !== 'success') ||\n afterAll.some((step) => step.status !== 'success') ||\n cleanup.status === 'pending' ||\n cleanup.status === 'failed' ||\n pendingOperations.length > 0 ||\n teardownErrors.length > 0\n ? 'failed'\n : 'success',\n executionStatus: Object.values(phaseResults).some(\n (phase) => phase.status === 'failed',\n )\n ? 'failed'\n : Object.values(phaseResults).some(\n (phase) => phase.status === 'not-run',\n ) ||\n hostErrors.some(({ phase }) => phase === 'setup') ||\n (signal.aborted && !beforeAll.length && !afterAll.length)\n ? 'not-run'\n : 'success',\n phases: phaseResults,\n cleanup,\n ...(pendingOperations.length ? { pendingOperations } : {}),\n startedAt: startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),\n beforeAll: [...beforeAll],\n afterAll: [...afterAll],\n ...(executionErrors.length\n ? {\n executionErrors: executionErrors\n .filter(\n (error) => !hostErrors.some((host) => host.error === error),\n )\n .map(asExecutionError),\n }\n : {}),\n ...(hostErrors.length ? { hostErrors: [...hostErrors] } : {}),\n ...(teardownErrors.length > 0\n ? { teardownErrors: [...teardownErrors] }\n : {}),\n ...(reportPaths.size > 0 ? { reportPaths: [...reportPaths] } : {}),\n ...(capturedSources.size || reportSources.length\n ? { reportSources: [...capturedSources.values(), ...reportSources] }\n : {}),\n };\n };\n\n const runPhase = async (\n phase: DocumentNodePhase,\n results: StepRunResult[],\n executionSignal = signal,\n ): Promise<void> => {\n for (const [stepIndex, step] of document.lifecycle[phase].entries()) {\n const waitForPreviousOperationMs =\n stepIndex > 0\n ? await recoverCancelledScopeResources(executionSignal)\n : 0;\n const documentContext: NodeDocumentContext = {\n documentId: document.documentId,\n documentRunId,\n projectId: document.projectId,\n projectName: project.name,\n sourcePath: document.sourcePath,\n phase,\n stepIndex,\n };\n const stepInfo = {\n scope: 'document' as const,\n node: step.node,\n stepCount: document.lifecycle[phase].length,\n document: documentContext,\n };\n await options.onStepStart?.(stepInfo);\n const result = await executeStep(\n step,\n nodes[phase][stepIndex],\n { scope: 'document', document: documentContext },\n context,\n {\n signal: executionSignal,\n defaultTimeoutMs: options.defaultTimeoutMs,\n onTeardown(node, teardown) {\n if (!acceptingNodeTeardowns) {\n throw new WorkflowLifecycleError(\n 'Node teardown can only be registered while a workflow document is running.',\n { documentId: document.documentId, documentRunId, node },\n );\n }\n if (typeof teardown !== 'function') {\n throw new WorkflowLifecycleError(\n 'Node onTeardown() requires a teardown function.',\n { documentId: document.documentId, documentRunId, node },\n );\n }\n nodeTeardownStack.push({\n registrationIndex: nodeTeardownStack.length,\n node,\n teardown,\n });\n },\n },\n );\n if (waitForPreviousOperationMs > 0)\n result.waitForPreviousOperationMs = waitForPreviousOperationMs;\n results.push(result);\n for (const source of result.report?.sources ?? [])\n capturedSources.set(\n `${source.sourcePath}\\u0000${source.dumpPath ?? ''}`,\n source,\n );\n await options.onStepResult?.(stepInfo, result);\n if (result.status !== 'success' && !result.continuedAfterError) break;\n }\n };\n\n return {\n signal,\n get context() {\n return context;\n },\n get canRunCases() {\n return (\n started &&\n executionErrors.length === 0 &&\n hostErrors.length === 0 &&\n !signal.aborted &&\n !beforeAll.some((step) => step.status !== 'success')\n );\n },\n async start() {\n if (started) {\n throw new WorkflowLifecycleError(\n 'Workflow document runtime has already started.',\n { documentId: document.documentId, documentRunId },\n );\n }\n started = true;\n if (!signal.aborted && options.documentSetup) {\n acceptingSetupTeardowns = true;\n try {\n await runSetupWithTimeout(\n async () => {\n try {\n context = await options.documentSetup!.setup({\n project,\n projectContext: options.projectContext,\n document: {\n documentId: document.documentId,\n documentRunId,\n sourcePath: document.sourcePath,\n attemptIndex: options.documentAttemptIndex ?? 0,\n },\n signal,\n onTeardown(teardown) {\n if (\n !acceptingSetupTeardowns ||\n typeof teardown !== 'function'\n )\n throw new WorkflowLifecycleError(\n 'Document setup onTeardown() requires a function registered during setup.',\n { documentId: document.documentId, documentRunId },\n );\n nodeTeardownStack.push({\n registrationIndex: nodeTeardownStack.length,\n node: options.documentSetup!.name,\n teardown,\n host: true,\n });\n },\n });\n } finally {\n acceptingSetupTeardowns = false;\n }\n },\n signal,\n resourceScope.abort,\n options.setupTimeoutMs,\n );\n } catch (error) {\n hostErrors.push({ phase: 'setup', error: asExecutionError(error) });\n }\n }\n try {\n if (!signal.aborted && !hostErrors.length)\n await runPhase('beforeAll', beforeAll);\n } catch (error) {\n executionErrors.push(error);\n throw new WorkflowExecutionFailure(createResult(), [\n ...executionErrors,\n ]);\n }\n return createResult();\n },\n async finish() {\n if (finishPromise) return finishPromise;\n if (!started) {\n throw new WorkflowLifecycleError(\n 'Workflow document runtime must start before it can finish.',\n { documentId: document.documentId, documentRunId },\n );\n }\n finishPromise = (async () => {\n const teardownErrors: WorkflowError[] = [];\n const cleanupExecutionErrors: unknown[] = [];\n let deferredCleanup = false;\n const cleanup = async () => {\n const priorTeardownErrors = teardownErrors.length;\n acceptingNodeTeardowns = false;\n for (const {\n registrationIndex,\n node,\n teardown,\n host,\n } of nodeTeardownStack.reverse()) {\n try {\n const released = await teardown();\n reportSources.push(...(released?.reportSources ?? []));\n for (const path of reportPathsFromTeardown(released)) {\n reportPaths.add(path);\n }\n } catch (error) {\n if (host) {\n const failure = asExecutionError(error);\n hostErrors.push({\n phase: 'cleanup',\n error: failure,\n });\n executionErrors.push(failure);\n cleanupExecutionErrors.push(error);\n continue;\n }\n teardownErrors.push(\n new NodeScopeTeardownError(error, {\n scope: 'document',\n scopeId: documentRunId,\n node,\n registrationIndex,\n }),\n );\n }\n }\n {\n const errors = [\n ...cleanupExecutionErrors,\n ...teardownErrors.slice(priorTeardownErrors),\n ];\n if (errors.length)\n throw new AggregateError(errors, 'Document cleanup failed');\n }\n };\n try {\n if (\n document.lifecycle.afterAll.length ||\n nodeTeardownStack.length ||\n (options.setupTimeoutMs !== undefined &&\n getPendingScopeOperations(signal).length)\n )\n await cleanupScopeResources(signal, cleanup, {\n totalTimeoutMs: options.teardownTimeoutMs?.(),\n beforeCleanup: async (cleanupSignal) => {\n try {\n if (!hostErrors.some((error) => error.phase === 'setup'))\n await runPhase('afterAll', afterAll, cleanupSignal);\n } catch (error) {\n executionErrors.push(error);\n cleanupExecutionErrors.push(error);\n }\n },\n onDeferredError: (error) =>\n getDebug('runner:cleanup', { console: true })(\n `Deferred document cleanup failed: ${String(error)}`,\n ),\n });\n else acceptingNodeTeardowns = false;\n } catch (error) {\n deferredCleanup = error instanceof ResourceCleanupDeferredError;\n if (!deferredCleanup) acceptingNodeTeardowns = false;\n if (\n !deferredCleanup &&\n !teardownErrors.length &&\n !cleanupExecutionErrors.length\n )\n teardownErrors.push(\n new NodeScopeTeardownError(error, {\n scope: 'document',\n scopeId: documentRunId,\n node: 'resource-drain',\n registrationIndex: -1,\n }),\n );\n }\n finishedResult = createResult(teardownErrors);\n try {\n await options.onResult?.(finishedResult);\n } catch (error) {\n executionErrors.push(error);\n finishedResult = createResult(teardownErrors);\n }\n resourceScope.dispose();\n if (executionErrors.length)\n throw new WorkflowExecutionFailure(finishedResult, [\n ...executionErrors,\n ]);\n return finishedResult;\n })();\n return finishPromise;\n },\n };\n}\n"],"names":["createDocumentRuntime","document","options","documentRunId","randomUUID","plan","buildTestRunReportPlan","startedAt","Date","beforeAll","afterAll","nodes","step","project","Object","context","resourceScope","createResourceScope","signal","setResourceOperationIdentity","nodeTeardownStack","acceptingNodeTeardowns","started","finishedResult","finishPromise","reportPaths","Set","reportSources","capturedSources","Map","executionErrors","hostErrors","acceptingSetupTeardowns","createResult","teardownErrors","endedAt","cleanup","getResourceCleanupSnapshot","pendingOperations","getPendingScopeOperations","phaseResults","phaseResult","undefined","phase","Math","error","host","asExecutionError","runPhase","results","executionSignal","stepIndex","waitForPreviousOperationMs","recoverCancelledScopeResources","documentContext","stepInfo","result","executeStep","node","teardown","WorkflowLifecycleError","source","runSetupWithTimeout","WorkflowExecutionFailure","cleanupExecutionErrors","deferredCleanup","priorTeardownErrors","registrationIndex","released","path","reportPathsFromTeardown","failure","NodeScopeTeardownError","errors","AggregateError","cleanupScopeResources","cleanupSignal","getDebug","String","ResourceCleanupDeferredError"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { getDebug } from "@midscene/shared/logger";
2
2
  import { ProjectSetupError, ProjectTeardownError, WorkflowLifecycleError } from "../errors.mjs";
3
3
  import { ResourceCleanupDeferredError, cleanupScopeResources, getPendingScopeOperations, getResourceCleanupSnapshot, linkResourceSignal, setResourceOperationIdentity } from "./resource-operations.mjs";
4
+ import { runSetupWithTimeout } from "./setup-timeout.mjs";
4
5
  const createProjectRuntime = (options)=>{
5
6
  const startedAt = new Date();
6
7
  const controller = new AbortController();
@@ -77,20 +78,24 @@ const createProjectRuntime = (options)=>{
77
78
  return createResult('failed');
78
79
  }
79
80
  if (options.setup) try {
80
- context = await options.setup.setup({
81
- project: options.project,
82
- env: Object.freeze({
83
- ...process.env
84
- }),
85
- signal: controller.signal,
86
- onTeardown
87
- });
81
+ await runSetupWithTimeout(async ()=>{
82
+ try {
83
+ context = await options.setup.setup({
84
+ project: options.project,
85
+ env: Object.freeze({
86
+ ...process.env
87
+ }),
88
+ signal: controller.signal,
89
+ onTeardown
90
+ });
91
+ } finally{
92
+ acceptingTeardowns = false;
93
+ }
94
+ }, controller.signal, (reason)=>controller.abort(reason), options.setupTimeoutMs);
88
95
  } catch (error) {
89
96
  setupError = new ProjectSetupError(error, {
90
97
  projectName: options.project.name
91
98
  });
92
- } finally{
93
- acceptingTeardowns = false;
94
99
  }
95
100
  return createResult(setupError ? 'failed' : 'success');
96
101
  },
@@ -123,7 +128,8 @@ const createProjectRuntime = (options)=>{
123
128
  if (teardownErrors.length > priorErrors) throw new AggregateError(teardownErrors.slice(priorErrors), 'Project cleanup failed');
124
129
  };
125
130
  try {
126
- if (teardownStack.length) await cleanupScopeResources(controller.signal, cleanup, {
131
+ if (teardownStack.length || void 0 !== options.setupTimeoutMs && getPendingScopeOperations(controller.signal).length) await cleanupScopeResources(controller.signal, cleanup, {
132
+ totalTimeoutMs: options.teardownTimeoutMs?.(),
127
133
  onDeferredError: (error)=>getDebug('runner:cleanup', {
128
134
  console: true
129
135
  })(`Deferred Project cleanup failed: ${String(error)}`)
@@ -1 +1 @@
1
- {"version":3,"file":"test-runner/engine/project-runtime.mjs","sources":["../../../../src/test-runner/engine/project-runtime.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport {\n ProjectSetupError,\n ProjectTeardownError,\n WorkflowLifecycleError,\n} from '../errors';\nimport {\n ResourceCleanupDeferredError,\n cleanupScopeResources,\n getPendingScopeOperations,\n getResourceCleanupSnapshot,\n linkResourceSignal,\n setResourceOperationIdentity,\n} from './resource-operations';\nimport type {\n ProjectRuntime,\n ProjectRuntimeOptions,\n ProjectRuntimeResult,\n} from './types';\n\nexport const createProjectRuntime = <TProjectContext = unknown>(\n options: ProjectRuntimeOptions<TProjectContext>,\n): ProjectRuntime<TProjectContext> => {\n const startedAt = new Date();\n const controller = new AbortController();\n const parentSignal = options.signal;\n linkResourceSignal(controller.signal, parentSignal);\n setResourceOperationIdentity(controller.signal, {\n scopeId: options.project.projectId,\n });\n const abortFromParent = () => controller.abort(parentSignal?.reason);\n if (parentSignal?.aborted) abortFromParent();\n else parentSignal?.addEventListener('abort', abortFromParent, { once: true });\n\n const teardownStack: Array<{\n registrationIndex: number;\n teardown: Parameters<\n Parameters<NonNullable<typeof options.setup>['setup']>[0]['onTeardown']\n >[0];\n }> = [];\n let context: TProjectContext | undefined;\n let setupError: ProjectSetupError | undefined;\n let started = false;\n let acceptingTeardowns = options.setup !== undefined;\n let finishedResult: ProjectRuntimeResult<TProjectContext> | undefined;\n let finishPromise: Promise<ProjectRuntimeResult<TProjectContext>> | undefined;\n\n const createResult = (\n requestedStatus: 'success' | 'failed',\n teardownErrors: readonly ProjectTeardownError[] = [],\n ): ProjectRuntimeResult<TProjectContext> => {\n const endedAt = new Date();\n const cleanup = getResourceCleanupSnapshot(controller.signal);\n const pendingOperations = getPendingScopeOperations(controller.signal);\n return {\n projectName: options.project.name,\n status:\n requestedStatus === 'failed' ||\n setupError ||\n teardownErrors.length > 0 ||\n cleanup.status === 'pending' ||\n cleanup.status === 'failed' ||\n pendingOperations.length > 0\n ? 'failed'\n : 'success',\n ...(setupError ? { setupError } : {}),\n ...(teardownErrors.length > 0\n ? { teardownErrors: [...teardownErrors] }\n : {}),\n startedAt: startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),\n cleanup,\n ...(pendingOperations.length ? { pendingOperations } : {}),\n };\n };\n\n const onTeardown = (\n teardown: (typeof teardownStack)[number]['teardown'],\n ): void => {\n if (!acceptingTeardowns) {\n throw new WorkflowLifecycleError(\n 'onTeardown() can only be called while Project setup is running.',\n { projectName: options.project.name },\n );\n }\n if (typeof teardown !== 'function') {\n throw new WorkflowLifecycleError(\n 'Project onTeardown() requires a teardown function.',\n { projectName: options.project.name },\n );\n }\n teardownStack.push({\n registrationIndex: teardownStack.length,\n teardown,\n });\n };\n\n return {\n get context() {\n return context;\n },\n get signal() {\n return controller.signal;\n },\n get canRun() {\n return started && !setupError && !controller.signal.aborted;\n },\n async start() {\n if (started) {\n throw new WorkflowLifecycleError(\n 'Project runtime has already started.',\n {\n projectName: options.project.name,\n },\n );\n }\n started = true;\n if (controller.signal.aborted) {\n acceptingTeardowns = false;\n return createResult('failed');\n }\n if (options.setup) {\n try {\n context = await options.setup.setup({\n project: options.project,\n env: Object.freeze({ ...process.env }),\n signal: controller.signal,\n onTeardown,\n });\n } catch (error) {\n setupError = new ProjectSetupError(error, {\n projectName: options.project.name,\n });\n } finally {\n acceptingTeardowns = false;\n }\n }\n return createResult(setupError ? 'failed' : 'success');\n },\n async finish(requestedStatus = 'success') {\n if (finishPromise) return finishPromise;\n if (!started) {\n throw new WorkflowLifecycleError(\n 'Project runtime must start before it can finish.',\n { projectName: options.project.name },\n );\n }\n finishPromise = (async () => {\n const statusBeforeTeardown = createResult(requestedStatus).status;\n const teardownErrors: ProjectTeardownError[] = [];\n let deferredCleanup = false;\n const cleanup = async () => {\n const priorErrors = teardownErrors.length;\n for (const {\n registrationIndex,\n teardown,\n } of teardownStack.reverse()) {\n try {\n await teardown({\n project: options.project,\n context,\n status: statusBeforeTeardown,\n ...(setupError ? { setupError } : {}),\n });\n } catch (error) {\n teardownErrors.push(\n new ProjectTeardownError(error, {\n projectName: options.project.name,\n registrationIndex,\n }),\n );\n }\n }\n if (teardownErrors.length > priorErrors)\n throw new AggregateError(\n teardownErrors.slice(priorErrors),\n 'Project cleanup failed',\n );\n };\n try {\n if (teardownStack.length)\n await cleanupScopeResources(controller.signal, cleanup, {\n onDeferredError: (error) =>\n getDebug('runner:cleanup', { console: true })(\n `Deferred Project cleanup failed: ${String(error)}`,\n ),\n });\n } catch (error) {\n deferredCleanup = error instanceof ResourceCleanupDeferredError;\n if (!deferredCleanup && !teardownErrors.length)\n teardownErrors.push(\n new ProjectTeardownError(error, {\n projectName: options.project.name,\n registrationIndex: -1,\n }),\n );\n }\n parentSignal?.removeEventListener('abort', abortFromParent);\n finishedResult = createResult(requestedStatus, teardownErrors);\n return finishedResult;\n })();\n return finishPromise;\n },\n abort(reason) {\n controller.abort(reason);\n },\n };\n};\n"],"names":["createProjectRuntime","options","startedAt","Date","controller","AbortController","parentSignal","linkResourceSignal","setResourceOperationIdentity","abortFromParent","teardownStack","context","setupError","started","acceptingTeardowns","undefined","finishedResult","finishPromise","createResult","requestedStatus","teardownErrors","endedAt","cleanup","getResourceCleanupSnapshot","pendingOperations","getPendingScopeOperations","Math","onTeardown","teardown","WorkflowLifecycleError","Object","process","error","ProjectSetupError","statusBeforeTeardown","deferredCleanup","priorErrors","registrationIndex","ProjectTeardownError","AggregateError","cleanupScopeResources","getDebug","String","ResourceCleanupDeferredError","reason"],"mappings":"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"}
1
+ {"version":3,"file":"test-runner/engine/project-runtime.mjs","sources":["../../../../src/test-runner/engine/project-runtime.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport {\n ProjectSetupError,\n ProjectTeardownError,\n WorkflowLifecycleError,\n} from '../errors';\nimport {\n ResourceCleanupDeferredError,\n cleanupScopeResources,\n getPendingScopeOperations,\n getResourceCleanupSnapshot,\n linkResourceSignal,\n setResourceOperationIdentity,\n} from './resource-operations';\nimport { runSetupWithTimeout } from './setup-timeout';\nimport type {\n ProjectRuntime,\n ProjectRuntimeOptions,\n ProjectRuntimeResult,\n} from './types';\n\nexport const createProjectRuntime = <TProjectContext = unknown>(\n options: ProjectRuntimeOptions<TProjectContext>,\n): ProjectRuntime<TProjectContext> => {\n const startedAt = new Date();\n const controller = new AbortController();\n const parentSignal = options.signal;\n linkResourceSignal(controller.signal, parentSignal);\n setResourceOperationIdentity(controller.signal, {\n scopeId: options.project.projectId,\n });\n const abortFromParent = () => controller.abort(parentSignal?.reason);\n if (parentSignal?.aborted) abortFromParent();\n else parentSignal?.addEventListener('abort', abortFromParent, { once: true });\n\n const teardownStack: Array<{\n registrationIndex: number;\n teardown: Parameters<\n Parameters<NonNullable<typeof options.setup>['setup']>[0]['onTeardown']\n >[0];\n }> = [];\n let context: TProjectContext | undefined;\n let setupError: ProjectSetupError | undefined;\n let started = false;\n let acceptingTeardowns = options.setup !== undefined;\n let finishedResult: ProjectRuntimeResult<TProjectContext> | undefined;\n let finishPromise: Promise<ProjectRuntimeResult<TProjectContext>> | undefined;\n\n const createResult = (\n requestedStatus: 'success' | 'failed',\n teardownErrors: readonly ProjectTeardownError[] = [],\n ): ProjectRuntimeResult<TProjectContext> => {\n const endedAt = new Date();\n const cleanup = getResourceCleanupSnapshot(controller.signal);\n const pendingOperations = getPendingScopeOperations(controller.signal);\n return {\n projectName: options.project.name,\n status:\n requestedStatus === 'failed' ||\n setupError ||\n teardownErrors.length > 0 ||\n cleanup.status === 'pending' ||\n cleanup.status === 'failed' ||\n pendingOperations.length > 0\n ? 'failed'\n : 'success',\n ...(setupError ? { setupError } : {}),\n ...(teardownErrors.length > 0\n ? { teardownErrors: [...teardownErrors] }\n : {}),\n startedAt: startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),\n cleanup,\n ...(pendingOperations.length ? { pendingOperations } : {}),\n };\n };\n\n const onTeardown = (\n teardown: (typeof teardownStack)[number]['teardown'],\n ): void => {\n if (!acceptingTeardowns) {\n throw new WorkflowLifecycleError(\n 'onTeardown() can only be called while Project setup is running.',\n { projectName: options.project.name },\n );\n }\n if (typeof teardown !== 'function') {\n throw new WorkflowLifecycleError(\n 'Project onTeardown() requires a teardown function.',\n { projectName: options.project.name },\n );\n }\n teardownStack.push({\n registrationIndex: teardownStack.length,\n teardown,\n });\n };\n\n return {\n get context() {\n return context;\n },\n get signal() {\n return controller.signal;\n },\n get canRun() {\n return started && !setupError && !controller.signal.aborted;\n },\n async start() {\n if (started) {\n throw new WorkflowLifecycleError(\n 'Project runtime has already started.',\n {\n projectName: options.project.name,\n },\n );\n }\n started = true;\n if (controller.signal.aborted) {\n acceptingTeardowns = false;\n return createResult('failed');\n }\n if (options.setup) {\n try {\n await runSetupWithTimeout(\n async () => {\n try {\n context = await options.setup!.setup({\n project: options.project,\n env: Object.freeze({ ...process.env }),\n signal: controller.signal,\n onTeardown,\n });\n } finally {\n acceptingTeardowns = false;\n }\n },\n controller.signal,\n (reason) => controller.abort(reason),\n options.setupTimeoutMs,\n );\n } catch (error) {\n setupError = new ProjectSetupError(error, {\n projectName: options.project.name,\n });\n }\n }\n return createResult(setupError ? 'failed' : 'success');\n },\n async finish(requestedStatus = 'success') {\n if (finishPromise) return finishPromise;\n if (!started) {\n throw new WorkflowLifecycleError(\n 'Project runtime must start before it can finish.',\n { projectName: options.project.name },\n );\n }\n finishPromise = (async () => {\n const statusBeforeTeardown = createResult(requestedStatus).status;\n const teardownErrors: ProjectTeardownError[] = [];\n let deferredCleanup = false;\n const cleanup = async () => {\n const priorErrors = teardownErrors.length;\n for (const {\n registrationIndex,\n teardown,\n } of teardownStack.reverse()) {\n try {\n await teardown({\n project: options.project,\n context,\n status: statusBeforeTeardown,\n ...(setupError ? { setupError } : {}),\n });\n } catch (error) {\n teardownErrors.push(\n new ProjectTeardownError(error, {\n projectName: options.project.name,\n registrationIndex,\n }),\n );\n }\n }\n if (teardownErrors.length > priorErrors)\n throw new AggregateError(\n teardownErrors.slice(priorErrors),\n 'Project cleanup failed',\n );\n };\n try {\n if (\n teardownStack.length ||\n (options.setupTimeoutMs !== undefined &&\n getPendingScopeOperations(controller.signal).length)\n )\n await cleanupScopeResources(controller.signal, cleanup, {\n totalTimeoutMs: options.teardownTimeoutMs?.(),\n onDeferredError: (error) =>\n getDebug('runner:cleanup', { console: true })(\n `Deferred Project cleanup failed: ${String(error)}`,\n ),\n });\n } catch (error) {\n deferredCleanup = error instanceof ResourceCleanupDeferredError;\n if (!deferredCleanup && !teardownErrors.length)\n teardownErrors.push(\n new ProjectTeardownError(error, {\n projectName: options.project.name,\n registrationIndex: -1,\n }),\n );\n }\n parentSignal?.removeEventListener('abort', abortFromParent);\n finishedResult = createResult(requestedStatus, teardownErrors);\n return finishedResult;\n })();\n return finishPromise;\n },\n abort(reason) {\n controller.abort(reason);\n },\n };\n};\n"],"names":["createProjectRuntime","options","startedAt","Date","controller","AbortController","parentSignal","linkResourceSignal","setResourceOperationIdentity","abortFromParent","teardownStack","context","setupError","started","acceptingTeardowns","undefined","finishedResult","finishPromise","createResult","requestedStatus","teardownErrors","endedAt","cleanup","getResourceCleanupSnapshot","pendingOperations","getPendingScopeOperations","Math","onTeardown","teardown","WorkflowLifecycleError","runSetupWithTimeout","Object","process","reason","error","ProjectSetupError","statusBeforeTeardown","deferredCleanup","priorErrors","registrationIndex","ProjectTeardownError","AggregateError","cleanupScopeResources","getDebug","String","ResourceCleanupDeferredError"],"mappings":"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"}
@@ -105,6 +105,9 @@ function createResourceScope(parent) {
105
105
  });
106
106
  return {
107
107
  signal: controller.signal,
108
+ abort (reason) {
109
+ controller.abort(reason);
110
+ },
108
111
  dispose () {
109
112
  parent?.removeEventListener('abort', abort);
110
113
  scope.resources = void 0;
@@ -324,6 +327,15 @@ async function waitForResourceIdle(resource) {
324
327
  ...resource_operations_resources.get(resource).operations.keys()
325
328
  ]);
326
329
  }
330
+ async function waitForScopeOperations(signal) {
331
+ while(true){
332
+ const active = [
333
+ ...scopes.get(signal)?.operations.keys() ?? []
334
+ ];
335
+ if (!active.length) return;
336
+ await Promise.all(active.map(waitForResourceIdle));
337
+ }
338
+ }
327
339
  async function recoverCancelledScopeResources(signal, graceMs = RESOURCE_RECOVERY_GRACE_MS) {
328
340
  signal.throwIfAborted();
329
341
  const startedAt = Date.now();
@@ -409,14 +421,22 @@ async function runResourceCleanup(resources, cleanup, options, beforeCleanup) {
409
421
  if (ownerScope) ownerScope.cleanupOwner = owner;
410
422
  let defer;
411
423
  const deferred = new Promise((resolve)=>{
412
- defer = ()=>resolve({
424
+ defer = ()=>{
425
+ if (void 0 !== options.totalTimeoutMs) beforeCleanup?.abort(new WorkflowError(`Resource cleanup timed out after ${options.totalTimeoutMs}ms.`, {
426
+ code: 'TEARDOWN_TIMEOUT',
427
+ details: {
428
+ timeoutMs: options.totalTimeoutMs
429
+ }
430
+ }));
431
+ resolve({
413
432
  deferred: true
414
433
  });
434
+ };
415
435
  });
416
- let graceMs = options.graceMs ?? RESOURCE_CLEANUP_GRACE_MS;
436
+ let graceMs = options.totalTimeoutMs ?? options.graceMs ?? RESOURCE_CLEANUP_GRACE_MS;
417
437
  let timer;
418
438
  const drain = async ()=>{
419
- timer = setTimeout(defer, graceMs);
439
+ if (void 0 === options.totalTimeoutMs) timer = setTimeout(defer, graceMs);
420
440
  try {
421
441
  while(true){
422
442
  const pending = [
@@ -426,15 +446,18 @@ async function runResourceCleanup(resources, cleanup, options, beforeCleanup) {
426
446
  await Promise.all(pending.map(waitForResourceIdle));
427
447
  }
428
448
  } finally{
429
- clearTimeout(timer);
449
+ if (void 0 === options.totalTimeoutMs) clearTimeout(timer);
430
450
  }
431
451
  };
432
452
  const completion = (async ()=>{
453
+ if (void 0 !== options.totalTimeoutMs) timer = setTimeout(defer, Math.max(0, options.totalTimeoutMs));
433
454
  try {
434
455
  if (beforeCleanup) await beforeCleanup.run(beforeCleanup.signal);
435
456
  await drain();
436
- graceMs = options.graceMs ?? RESOURCE_TEARDOWN_GRACE_MS;
437
- timer = setTimeout(defer, graceMs);
457
+ if (void 0 === options.totalTimeoutMs) {
458
+ graceMs = options.graceMs ?? RESOURCE_TEARDOWN_GRACE_MS;
459
+ timer = setTimeout(defer, graceMs);
460
+ }
438
461
  return await cleanup();
439
462
  } finally{
440
463
  clearTimeout(timer);
@@ -488,7 +511,8 @@ function cleanupResources(resources, cleanup, options) {
488
511
  return runResourceCleanup(resources, cleanup, options);
489
512
  }
490
513
  async function cleanupScopeResources(signal, cleanup, options) {
491
- const cleanupSignal = new AbortController().signal;
514
+ const cleanupController = new AbortController();
515
+ const cleanupSignal = cleanupController.signal;
492
516
  linkResourceSignal(cleanupSignal, signal);
493
517
  const scope = scopes.get(signal);
494
518
  return runResourceCleanup([
@@ -500,9 +524,10 @@ async function cleanupScopeResources(signal, cleanup, options) {
500
524
  signal
501
525
  }, options.beforeCleanup ? {
502
526
  signal: cleanupSignal,
527
+ abort: (reason)=>cleanupController.abort(reason),
503
528
  run: options.beforeCleanup
504
529
  } : void 0);
505
530
  }
506
- export { RESOURCE_CLEANUP_GRACE_MS, RESOURCE_RECOVERY_GRACE_MS, ResourceAdmissionError, ResourceCleanupDeferredError, admitResourceOperation, assertResourceAvailable, cleanupResources, cleanupScopeResources, createResourceScope, getPendingScopeOperations, getResourceCleanupCompletion, getResourceCleanupSnapshot, hasPendingResourceOperation, isResourceAdmissionError, linkResourceSignal, observeResourceOperations, recordOperation, recoverCancelledScopeResources, retainResourceForScope, setResourceOperationIdentity, trackResourceOperation, waitForResourceIdle };
531
+ export { RESOURCE_CLEANUP_GRACE_MS, RESOURCE_RECOVERY_GRACE_MS, ResourceAdmissionError, ResourceCleanupDeferredError, admitResourceOperation, assertResourceAvailable, cleanupResources, cleanupScopeResources, createResourceScope, getPendingScopeOperations, getResourceCleanupCompletion, getResourceCleanupSnapshot, hasPendingResourceOperation, isResourceAdmissionError, linkResourceSignal, observeResourceOperations, recordOperation, recoverCancelledScopeResources, retainResourceForScope, setResourceOperationIdentity, trackResourceOperation, waitForResourceIdle, waitForScopeOperations };
507
532
 
508
533
  //# sourceMappingURL=resource-operations.mjs.map