@midscene/core 1.15.6 → 1.16.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 (94) 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 +1 -1
  12. package/dist/es/report-generator.mjs +73 -33
  13. package/dist/es/report-generator.mjs.map +1 -1
  14. package/dist/es/report-html-template.mjs +1 -1
  15. package/dist/es/test-runner/engine/document-runtime.mjs +30 -24
  16. package/dist/es/test-runner/engine/document-runtime.mjs.map +1 -1
  17. package/dist/es/test-runner/engine/project-runtime.mjs +17 -11
  18. package/dist/es/test-runner/engine/project-runtime.mjs.map +1 -1
  19. package/dist/es/test-runner/engine/resource-operations.mjs +33 -8
  20. package/dist/es/test-runner/engine/resource-operations.mjs.map +1 -1
  21. package/dist/es/test-runner/engine/run-collected-case.mjs +1 -0
  22. package/dist/es/test-runner/engine/run-collected-case.mjs.map +1 -1
  23. package/dist/es/test-runner/engine/run-workflow-document.mjs +3 -0
  24. package/dist/es/test-runner/engine/run-workflow-document.mjs.map +1 -1
  25. package/dist/es/test-runner/engine/setup-timeout.mjs +37 -0
  26. package/dist/es/test-runner/engine/setup-timeout.mjs.map +1 -0
  27. package/dist/es/utils.mjs +1 -1
  28. package/dist/es/yaml/execution-session.mjs +2 -1
  29. package/dist/es/yaml/execution-session.mjs.map +1 -1
  30. package/dist/es/yaml/legacy-yaml-report.mjs +1 -1
  31. package/dist/es/yaml/legacy-yaml-report.mjs.map +1 -1
  32. package/dist/es/yaml/player.mjs +17 -3
  33. package/dist/es/yaml/player.mjs.map +1 -1
  34. package/dist/lib/agent/agent-options.js +73 -0
  35. package/dist/lib/agent/agent-options.js.map +1 -0
  36. package/dist/lib/agent/agent-report-manager.js +357 -0
  37. package/dist/lib/agent/agent-report-manager.js.map +1 -0
  38. package/dist/lib/agent/agent.js +110 -226
  39. package/dist/lib/agent/agent.js.map +1 -1
  40. package/dist/lib/agent/cache-config.js +23 -0
  41. package/dist/lib/agent/cache-config.js.map +1 -1
  42. package/dist/lib/agent/tasks.js +3 -1
  43. package/dist/lib/agent/tasks.js.map +1 -1
  44. package/dist/lib/agent/utils.js +1 -1
  45. package/dist/lib/report-generator.js +73 -33
  46. package/dist/lib/report-generator.js.map +1 -1
  47. package/dist/lib/report-html-template.js +1 -1
  48. package/dist/lib/test-runner/engine/document-runtime.js +30 -24
  49. package/dist/lib/test-runner/engine/document-runtime.js.map +1 -1
  50. package/dist/lib/test-runner/engine/project-runtime.js +17 -11
  51. package/dist/lib/test-runner/engine/project-runtime.js.map +1 -1
  52. package/dist/lib/test-runner/engine/resource-operations.js +36 -8
  53. package/dist/lib/test-runner/engine/resource-operations.js.map +1 -1
  54. package/dist/lib/test-runner/engine/run-collected-case.js +1 -0
  55. package/dist/lib/test-runner/engine/run-collected-case.js.map +1 -1
  56. package/dist/lib/test-runner/engine/run-workflow-document.js +3 -0
  57. package/dist/lib/test-runner/engine/run-workflow-document.js.map +1 -1
  58. package/dist/lib/test-runner/engine/setup-timeout.js +71 -0
  59. package/dist/lib/test-runner/engine/setup-timeout.js.map +1 -0
  60. package/dist/lib/utils.js +1 -1
  61. package/dist/lib/yaml/execution-session.js +4 -0
  62. package/dist/lib/yaml/execution-session.js.map +1 -1
  63. package/dist/lib/yaml/legacy-yaml-report.js +1 -1
  64. package/dist/lib/yaml/legacy-yaml-report.js.map +1 -1
  65. package/dist/lib/yaml/player.js +16 -2
  66. package/dist/lib/yaml/player.js.map +1 -1
  67. package/dist/types/agent/agent-options.d.ts +4 -0
  68. package/dist/types/agent/agent-options.d.ts.map +1 -0
  69. package/dist/types/agent/agent-report-manager.d.ts +92 -0
  70. package/dist/types/agent/agent-report-manager.d.ts.map +1 -0
  71. package/dist/types/agent/agent.d.ts +31 -24
  72. package/dist/types/agent/agent.d.ts.map +1 -1
  73. package/dist/types/agent/cache-config.d.ts +10 -0
  74. package/dist/types/agent/cache-config.d.ts.map +1 -1
  75. package/dist/types/agent/tasks.d.ts +1 -0
  76. package/dist/types/agent/tasks.d.ts.map +1 -1
  77. package/dist/types/report-generator.d.ts +5 -1
  78. package/dist/types/report-generator.d.ts.map +1 -1
  79. package/dist/types/test-runner/engine/document-runtime.d.ts.map +1 -1
  80. package/dist/types/test-runner/engine/project-runtime.d.ts.map +1 -1
  81. package/dist/types/test-runner/engine/resource-operations.d.ts +5 -0
  82. package/dist/types/test-runner/engine/resource-operations.d.ts.map +1 -1
  83. package/dist/types/test-runner/engine/run-collected-case.d.ts.map +1 -1
  84. package/dist/types/test-runner/engine/run-workflow-document.d.ts.map +1 -1
  85. package/dist/types/test-runner/engine/setup-timeout.d.ts +3 -0
  86. package/dist/types/test-runner/engine/setup-timeout.d.ts.map +1 -0
  87. package/dist/types/test-runner/engine/types.d.ts +7 -0
  88. package/dist/types/test-runner/engine/types.d.ts.map +1 -1
  89. package/dist/types/test-runner/project.d.ts +22 -0
  90. package/dist/types/test-runner/project.d.ts.map +1 -1
  91. package/dist/types/yaml/execution-session.d.ts +2 -0
  92. package/dist/types/yaml/execution-session.d.ts.map +1 -1
  93. package/dist/types/yaml/player.d.ts.map +1 -1
  94. package/package.json +2 -2
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"test-runner/engine/resource-operations.mjs","sources":["../../../../src/test-runner/engine/resource-operations.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { uuid } from '@midscene/shared/utils';\nimport type {\n TestRunReportCleanup,\n TestRunReportOperationEvent,\n TestRunReportOperationReference,\n} from '../../test-run-report';\nimport { WorkflowError } from '../errors';\nimport { sanitizeReportError } from '../reporting/sanitize';\n\ninterface OperationIdentity {\n scopeId: string;\n stepId?: string;\n node?: string;\n}\ninterface ResourceOperation {\n signal: AbortSignal;\n operationId: string;\n identity?: OperationIdentity;\n settled?: boolean;\n blockers?: Set<ResourceOperation>;\n}\ntype OperationListener = (event: Readonly<TestRunReportOperationEvent>) => void;\n\ninterface CleanupOwner {\n resources: Set<object>;\n signal?: AbortSignal;\n blockers: Set<ResourceOperation>;\n}\n\ninterface ResourceScope {\n operations: Map<object, number>;\n resources?: Set<object>;\n cleanupOwner?: CleanupOwner;\n}\n\ninterface ResourceHandle {\n operations: Map<Promise<unknown>, ResourceOperation>;\n retiring: Set<CleanupOwner>;\n cleanup?: Promise<unknown>;\n cleanupState?: {\n operationId?: string;\n status: 'pending' | 'completed' | 'failed';\n blockers: Set<ResourceOperation>;\n error?: unknown;\n };\n}\n\ninterface ResourceState {\n resources: WeakMap<object, ResourceHandle>;\n scopes: WeakMap<AbortSignal, ResourceScope>;\n parents: WeakMap<AbortSignal, AbortSignal>;\n cleanupOwners: WeakMap<AbortSignal, CleanupOwner>;\n identities: WeakMap<AbortSignal, OperationIdentity>;\n listeners: WeakMap<AbortSignal, Set<OperationListener>>;\n}\n\n// CJS and ESM hosts must agree about ownership of the same Agent instance.\nconst admissionErrorKey = Symbol.for('@midscene/core/resource-admission-error');\nconst stateKey = Symbol.for('@midscene/core/resource-operations/v4');\nconst host = globalThis as typeof globalThis & { [stateKey]?: ResourceState };\nhost[stateKey] ??= {\n resources: new WeakMap(),\n scopes: new WeakMap(),\n parents: new WeakMap(),\n cleanupOwners: new WeakMap(),\n identities: new WeakMap(),\n listeners: new WeakMap(),\n};\nconst { resources, scopes, parents, cleanupOwners, identities, listeners } =\n host[stateKey];\nconst resourceHandle = (resource: object): ResourceHandle => {\n let handle = resources.get(resource);\n if (!handle) {\n handle = {\n operations: new Map(),\n retiring: new Set(),\n };\n resources.set(resource, handle);\n }\n return handle;\n};\n\n/** Actual teardown completion belongs to the owner, never to a thrown error. */\nexport const getResourceCleanupCompletion = (\n owner: object,\n): Promise<unknown> | undefined => resources.get(owner)?.cleanup;\n\nfunction blockingReferences(\n blockers: Iterable<ResourceOperation>,\n): TestRunReportOperationReference[] {\n const references = new Map<string, TestRunReportOperationReference>();\n const visited = new Set<ResourceOperation>();\n const visit = (record: ResourceOperation) => {\n if (visited.has(record)) return;\n visited.add(record);\n if (!record.settled)\n references.set(record.operationId, {\n operationId: record.operationId,\n ...record.identity,\n });\n for (const blocker of record.blockers ?? []) visit(blocker);\n };\n for (const blocker of blockers) visit(blocker);\n return [...references.values()];\n}\n\n/** Read a copy at the publication boundary; never expose the live cleanup state. */\nexport function getResourceCleanupSnapshot(\n owner: object,\n): TestRunReportCleanup {\n const state = resources.get(owner)?.cleanupState;\n return freezeEvent({\n capturedAt: new Date().toISOString(),\n status: state?.status ?? 'not-required',\n ...(state?.operationId ? { operationId: state.operationId } : {}),\n ...(state?.status === 'pending'\n ? { blockingOperations: blockingReferences(state.blockers) }\n : {}),\n ...(state?.error === undefined\n ? {}\n : { error: sanitizeReportError(state.error) }),\n });\n}\n\nexport const RESOURCE_CLEANUP_GRACE_MS = 1000;\nexport const RESOURCE_RECOVERY_GRACE_MS = 30_000;\nconst RESOURCE_TEARDOWN_GRACE_MS = 10_000;\n\nexport function getPendingScopeOperations(\n signal: AbortSignal,\n): TestRunReportOperationReference[] {\n return freezeEvent(\n blockingReferences(\n [...(scopes.get(signal)?.operations.keys() ?? [])].flatMap((resource) => [\n ...(resources.get(resource)?.operations.values() ?? []),\n ]),\n ),\n );\n}\n\n/** Snapshotting must not wait for a timed-out operation to release its resource. */\nexport const hasPendingResourceOperation = (resource: object): boolean =>\n (resources.get(resource)?.operations.size ?? 0) > 0;\n\n/** Resource ancestry is independent of cancellation: cleanup can outlive abort. */\nexport function linkResourceSignal(signal: AbortSignal, parent?: AbortSignal) {\n if (parent && parent !== signal) parents.set(signal, parent);\n}\n\nfunction* ownerSignals(signal?: AbortSignal): Generator<AbortSignal> {\n const seen = new Set<AbortSignal>();\n for (\n let owner = signal;\n owner && !seen.has(owner);\n owner = parents.get(owner)\n ) {\n seen.add(owner);\n yield owner;\n }\n}\n\n/** Each Case/document owns its cleanup, even when cancellation is shared. */\nexport function createResourceScope(parent?: AbortSignal) {\n const controller = new AbortController();\n const scope: ResourceScope = { operations: new Map(), resources: new Set() };\n scopes.set(controller.signal, scope);\n linkResourceSignal(controller.signal, parent);\n const abort = () => controller.abort(parent?.reason);\n if (parent?.aborted) abort();\n else parent?.addEventListener('abort', abort, { once: true });\n return {\n signal: controller.signal,\n dispose() {\n parent?.removeEventListener('abort', abort);\n scope.resources = undefined;\n if (!scope.operations.size) scopes.delete(controller.signal);\n },\n };\n}\n\nexport function assertResourceAvailable(\n resource: object,\n exclusive = false,\n signal?: AbortSignal,\n): boolean {\n const retiringOwners = resources.get(resource)?.retiring;\n const isOwnerCleanup = [...ownerSignals(signal)].some((ownerSignal) => {\n const owner = cleanupOwners.get(ownerSignal);\n return owner !== undefined && retiringOwners?.has(owner);\n });\n if (retiringOwners?.size && !isOwnerCleanup)\n throw new WorkflowError(\n 'A resource cannot execute overlapping Runner operations while its cleanup is pending.',\n { code: 'RESOURCE_CLEANUP_PENDING' },\n );\n const pending = resources.get(resource)?.operations;\n if (exclusive && pending?.size)\n throw new WorkflowError(\n 'The same Agent instance cannot execute overlapping Runner operations.',\n { code: 'RESOURCE_OPERATION_PENDING' },\n );\n if (\n pending &&\n [...pending.values()].some((operation) => operation.signal.aborted)\n )\n throw new WorkflowError(\n 'Cannot reuse an Agent while an aborted YAML action or Runner operation is still running.',\n { code: 'RESOURCE_OPERATION_PENDING' },\n );\n return isOwnerCleanup;\n}\n\n/** Capture subscribers at registration: late settlement stays with its original run. */\nexport function observeResourceOperations(\n signal: AbortSignal,\n listener: OperationListener,\n) {\n const subscribers = listeners.get(signal) ?? new Set<OperationListener>();\n listeners.set(signal, subscribers);\n subscribers.add(listener);\n return () => {\n subscribers.delete(listener);\n if (!subscribers.size) listeners.delete(signal);\n };\n}\n\nexport function setResourceOperationIdentity(\n signal: AbortSignal,\n identity: OperationIdentity,\n) {\n identities.set(signal, Object.freeze({ ...identity }));\n}\n\nfunction freezeEvent<T>(value: T): T {\n if (typeof value === 'object' && value !== null && !Object.isFrozen(value)) {\n for (const child of Object.values(value)) freezeEvent(child);\n Object.freeze(value);\n }\n return value;\n}\n\nexport function recordOperation(\n operation: Promise<unknown>,\n signal: AbortSignal,\n operationKind: TestRunReportOperationEvent['operationKind'],\n) {\n const owners = [...ownerSignals(signal)];\n const identity = owners.map((owner) => identities.get(owner)).find(Boolean);\n const record: ResourceOperation = { signal, operationId: uuid(), identity };\n const subscribers = new Set(\n owners.flatMap((owner) => [...(listeners.get(owner) ?? [])]),\n );\n let settled = false;\n const emit = (\n event: Omit<\n TestRunReportOperationEvent,\n 'operationId' | 'operationKind' | 'at'\n >,\n ) => {\n const frozen = freezeEvent({\n ...identity,\n operationId: record.operationId,\n operationKind,\n at: new Date().toISOString(),\n ...event,\n });\n for (const subscriber of subscribers) {\n try {\n subscriber(frozen);\n } catch (error) {\n getDebug('runner:operations', { console: true })(\n `Operation observer failed: ${String(error)}`,\n );\n }\n }\n };\n const abort = () =>\n emit({\n kind: 'cancel-requested',\n error: sanitizeReportError(signal.reason),\n });\n emit({ kind: 'started' });\n if (signal.aborted) abort();\n else signal.addEventListener('abort', abort, { once: true });\n const finish = (\n status: 'success' | 'failed' | 'not-run',\n error?: unknown,\n ) => {\n settled = true;\n record.settled = true;\n signal.removeEventListener('abort', abort);\n emit({\n kind: 'settled',\n status,\n ...(status !== 'success' ? { error: sanitizeReportError(error) } : {}),\n });\n };\n void operation.then(\n () => finish('success'),\n (error) =>\n finish(isResourceAdmissionError(error) ? 'not-run' : 'failed', error),\n );\n return {\n record,\n runnerReturned(status: 'success' | 'failed' | 'not-run') {\n emit({ kind: 'runner-returned', status, pending: !settled });\n },\n cleanupDeferred(blockers: Iterable<ResourceOperation>) {\n emit({\n kind: 'cleanup-deferred',\n blockingOperations: blockingReferences(blockers),\n });\n },\n };\n}\n\n/** A scheduling decision, distinct from an action that ran and failed. */\nexport class ResourceAdmissionError extends WorkflowError {\n readonly blockedBy?: string;\n readonly [admissionErrorKey] = true;\n constructor(code: string, blockers: readonly ResourceOperation[]) {\n super(\n 'The Agent is occupied by an unfinished operation or pending cleanup; this action was not started.',\n {\n code,\n details: {\n blockingOperations: blockers.map(({ operationId, identity }) => ({\n operationId,\n ...identity,\n })),\n },\n },\n );\n this.blockedBy = blockers.find(\n (blocker) => blocker.identity?.stepId,\n )?.identity?.stepId;\n }\n}\n\nexport function isResourceAdmissionError(\n error: unknown,\n): error is ResourceAdmissionError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as ResourceAdmissionError)[admissionErrorKey] === true\n );\n}\n\nexport function admitResourceOperation(\n resource: object,\n signal: AbortSignal,\n exclusive = true,\n): boolean {\n let isCleanupOwner: boolean;\n try {\n isCleanupOwner = assertResourceAvailable(resource, exclusive, signal);\n } catch (error) {\n if (\n error instanceof WorkflowError &&\n ['RESOURCE_OPERATION_PENDING', 'RESOURCE_CLEANUP_PENDING'].includes(\n error.code,\n )\n )\n throw new ResourceAdmissionError(error.code, [\n ...(resources.get(resource)?.operations.values() ?? []),\n ...[...(resources.get(resource)?.retiring ?? [])].flatMap((owner) => [\n ...owner.blockers,\n ]),\n ]);\n throw error;\n }\n return isCleanupOwner;\n}\n\nconst retainForCleanup = (owner: CleanupOwner, resource: object) => {\n owner.resources.add(resource);\n const handle = resourceHandle(resource);\n for (const operation of handle.operations.values())\n owner.blockers.add(operation);\n handle.retiring.add(owner);\n};\n\n/** Acquisition establishes cleanup ownership even if action admission is rejected. */\nexport function retainResourceForScope(resource: object, signal: AbortSignal) {\n let retained = false;\n for (const ownerSignal of ownerSignals(signal)) {\n const scope = scopes.get(ownerSignal);\n if (!retained && scope?.resources) {\n scope.resources.add(resource);\n retained = true;\n }\n if (scope?.cleanupOwner) retainForCleanup(scope.cleanupOwner, resource);\n }\n}\n\n/** Track actual completion, not the Runner timeout race. */\nexport function trackResourceOperation<T>(\n resource: object,\n operation: Promise<T>,\n signal: AbortSignal,\n kind: TestRunReportOperationEvent['operationKind'] = 'resource',\n existingObservation?: ReturnType<typeof recordOperation>,\n): Promise<T> {\n const observation =\n existingObservation ?? recordOperation(operation, signal, kind);\n const active = resourceHandle(resource).operations;\n active.set(operation, observation.record);\n const ownerScopes: Array<[AbortSignal, ResourceScope]> = [];\n let retainedByScope = false;\n for (const ownerSignal of ownerSignals(signal)) {\n const scope: ResourceScope = scopes.get(ownerSignal) ?? {\n operations: new Map(),\n };\n scopes.set(ownerSignal, scope);\n scope.operations.set(resource, (scope.operations.get(resource) ?? 0) + 1);\n ownerScopes.push([ownerSignal, scope]);\n // Only the closest owner retains idle resources for its teardown. Parents\n // wait on active operations, not every Agent used by completed Cases.\n if (!retainedByScope && scope.resources) {\n scope.resources.add(resource);\n retainedByScope = true;\n }\n const cleanupOwner = cleanupOwners.get(ownerSignal);\n if (cleanupOwner) {\n cleanupOwner.blockers.add(observation.record);\n retainForCleanup(cleanupOwner, resource);\n }\n }\n const finish = () => {\n active.delete(operation);\n for (const [ownerSignal, scope] of ownerScopes) {\n const count = scope.operations.get(resource)! - 1;\n if (count) scope.operations.set(resource, count);\n else scope.operations.delete(resource);\n if (!scope.operations.size && !scope.resources)\n scopes.delete(ownerSignal);\n }\n };\n void operation.then(finish, finish);\n return operation;\n}\n\nexport async function waitForResourceIdle(resource: object): Promise<void> {\n while (resources.get(resource)?.operations?.size)\n await Promise.allSettled([...resources.get(resource)!.operations.keys()]);\n}\n\n/** Wait for this scope's cancelled work before scheduling its next Step. */\nexport async function recoverCancelledScopeResources(\n signal: AbortSignal,\n graceMs = RESOURCE_RECOVERY_GRACE_MS,\n): Promise<number> {\n signal.throwIfAborted();\n const startedAt = Date.now();\n const pendingOperations = () => {\n const pending: Array<{\n operation: Promise<unknown>;\n signal: AbortSignal;\n resource: object;\n record: ResourceOperation;\n }> = [];\n for (const resource of scopes.get(signal)?.operations.keys() ?? []) {\n for (const [operation, tracked] of resources.get(resource)?.operations ??\n []) {\n const ownerSignal = tracked.signal;\n if (\n ownerSignal.aborted &&\n [...ownerSignals(ownerSignal)].includes(signal)\n )\n pending.push({\n operation,\n signal: ownerSignal,\n resource,\n record: tracked,\n });\n }\n }\n return pending;\n };\n let pending = pendingOperations();\n if (!pending.length) return 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort!: () => void;\n const stopped = new Promise<never>((_resolve, reject) => {\n onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n timer = setTimeout(() => {\n const blockingOperations = blockingReferences(\n pending.map(({ record }) => record),\n );\n const blockedByStepId = blockingOperations.every(\n ({ stepId }) => stepId === blockingOperations[0]?.stepId,\n )\n ? blockingOperations[0]?.stepId\n : undefined;\n reject(\n new WorkflowError(\n `The previous operation was still running after ${graceMs / 1000} s. Later steps were not started.`,\n {\n code: 'RESOURCE_RECOVERY_TIMEOUT',\n details: {\n graceMs,\n waitedMs: Date.now() - startedAt,\n resourceCount: new Set(pending.map(({ resource }) => resource))\n .size,\n blockingOperations,\n blockedByStepId,\n },\n cause: pending[0]?.signal.reason,\n },\n ),\n );\n }, graceMs);\n });\n try {\n while (pending.length) {\n await Promise.race([\n Promise.allSettled(pending.map(({ operation }) => operation)),\n stopped,\n ]);\n signal.throwIfAborted();\n pending = pendingOperations();\n }\n return Date.now() - startedAt;\n } finally {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n }\n}\n\nexport class ResourceCleanupDeferredError extends WorkflowError {\n constructor(\n graceMs: number,\n resourceCount: number,\n blockers: readonly ResourceOperation[] = [],\n ) {\n super(\n `Resource cleanup did not finish within ${graceMs}ms. Resources remain quarantined until cleanup settles.`,\n {\n code: 'RESOURCE_CLEANUP_DEFERRED',\n details: {\n graceMs,\n resourceCount,\n blockingOperations: blockers.map(({ operationId, identity }) => ({\n operationId,\n ...identity,\n })),\n },\n },\n );\n }\n}\n\ninterface CleanupOptions {\n /** Stable owner of this cleanup, distinct from borrowed resource identities. */\n owner?: object;\n /** Parent scope observes deferred completion without host-specific forwarding. */\n signal?: AbortSignal;\n graceMs?: number;\n onDeferredError(error: unknown): void;\n}\n\n/** Bound waiting, but keep ownership until the actual cleanup has finished. */\nasync function runResourceCleanup<T>(\n resources: readonly object[],\n cleanup: () => Promise<T>,\n options: CleanupOptions,\n beforeCleanup?: {\n signal: AbortSignal;\n run(signal: AbortSignal): Promise<void>;\n },\n): Promise<T> {\n const owner: CleanupOwner = {\n resources: new Set(),\n blockers: new Set(),\n signal: beforeCleanup?.signal,\n };\n for (const resource of resources) retainForCleanup(owner, resource);\n if (owner.signal) cleanupOwners.set(owner.signal, owner);\n const ownerScope = options.signal ? scopes.get(options.signal) : undefined;\n if (ownerScope) ownerScope.cleanupOwner = owner;\n\n let defer!: () => void;\n const deferred = new Promise<{ deferred: true }>((resolve) => {\n defer = () => resolve({ deferred: true });\n });\n let graceMs = options.graceMs ?? RESOURCE_CLEANUP_GRACE_MS;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const drain = async () => {\n timer = setTimeout(defer, graceMs);\n try {\n // An acquisition may finish late and add another shared resource to its\n // owner. Keep draining that resource before calling any disposer.\n while (true) {\n const pending = [...owner.resources].filter(\n (resource) => resourceHandle(resource).operations.size,\n );\n if (!pending.length) return;\n await Promise.all(pending.map(waitForResourceIdle));\n }\n } finally {\n clearTimeout(timer);\n }\n };\n const completion = (async () => {\n try {\n if (beforeCleanup) {\n // Hooks may use idle resources owned by this cleanup. Busy resources\n // still reject overlapping actions, including cleanup actions.\n await beforeCleanup.run(beforeCleanup.signal);\n }\n // Lifecycle hooks can themselves time out with an action still running.\n // Drain both the original work and any operation started by the hook\n // before disposing the resource.\n await drain();\n graceMs = options.graceMs ?? RESOURCE_TEARDOWN_GRACE_MS;\n timer = setTimeout(defer, graceMs);\n return await cleanup();\n } finally {\n clearTimeout(timer);\n if (owner.signal) cleanupOwners.delete(owner.signal);\n if (ownerScope?.cleanupOwner === owner)\n ownerScope.cleanupOwner = undefined;\n for (const resource of owner.resources) {\n const owners = resourceHandle(resource).retiring;\n owners.delete(owner);\n }\n }\n })();\n const cleanupObservation = options.signal\n ? recordOperation(completion, options.signal, 'cleanup')\n : undefined;\n const ownerKey = options.owner ?? beforeCleanup?.signal ?? resources[0];\n const cleanupState: NonNullable<ResourceHandle['cleanupState']> = {\n status: 'pending',\n operationId: cleanupObservation?.record.operationId,\n blockers: owner.blockers,\n };\n if (cleanupObservation) cleanupObservation.record.blockers = owner.blockers;\n if (ownerKey) {\n resourceHandle(ownerKey).cleanup = completion;\n resourceHandle(ownerKey).cleanupState = cleanupState;\n }\n void completion.then(\n () => {\n cleanupState.status = 'completed';\n },\n (error) => {\n cleanupState.status = 'failed';\n cleanupState.error = error;\n },\n );\n const outcome = await Promise.race([\n completion.then((value) => ({ value })),\n deferred,\n ]);\n if ('deferred' in outcome) {\n cleanupObservation?.cleanupDeferred(owner.blockers);\n void completion.catch((error) => {\n try {\n options.onDeferredError(error);\n } catch {\n // The returned completion still carries the original cleanup failure.\n }\n });\n if (options.signal)\n trackResourceOperation(\n {},\n completion,\n options.signal,\n 'cleanup',\n cleanupObservation,\n );\n throw new ResourceCleanupDeferredError(graceMs, owner.resources.size, [\n ...owner.blockers,\n ]);\n }\n return outcome.value;\n}\n\nexport function cleanupResources<T>(\n resources: readonly object[],\n cleanup: () => Promise<T>,\n options: CleanupOptions,\n): Promise<T> {\n return runResourceCleanup(resources, cleanup, options);\n}\n\nexport async function cleanupScopeResources<T>(\n signal: AbortSignal,\n cleanup: () => Promise<T>,\n options: CleanupOptions & {\n beforeCleanup?(signal: AbortSignal): Promise<void>;\n },\n): Promise<T> {\n const cleanupSignal = new AbortController().signal;\n linkResourceSignal(cleanupSignal, signal);\n const scope = scopes.get(signal);\n return runResourceCleanup(\n [...(scope?.resources ?? []), ...(scope?.operations.keys() ?? [])],\n cleanup,\n { ...options, owner: signal, signal },\n options.beforeCleanup\n ? { signal: cleanupSignal, run: options.beforeCleanup }\n : undefined,\n );\n}\n"],"names":["admissionErrorKey","Symbol","stateKey","host","globalThis","WeakMap","resources","scopes","parents","cleanupOwners","identities","listeners","resourceHandle","resource","handle","Map","Set","getResourceCleanupCompletion","owner","blockingReferences","blockers","references","visited","visit","record","blocker","getResourceCleanupSnapshot","state","freezeEvent","Date","undefined","sanitizeReportError","RESOURCE_CLEANUP_GRACE_MS","RESOURCE_RECOVERY_GRACE_MS","RESOURCE_TEARDOWN_GRACE_MS","getPendingScopeOperations","signal","hasPendingResourceOperation","linkResourceSignal","parent","ownerSignals","seen","createResourceScope","controller","AbortController","scope","abort","assertResourceAvailable","exclusive","retiringOwners","isOwnerCleanup","ownerSignal","WorkflowError","pending","operation","observeResourceOperations","listener","subscribers","setResourceOperationIdentity","identity","Object","value","child","recordOperation","operationKind","owners","Boolean","uuid","settled","emit","event","frozen","subscriber","error","getDebug","String","finish","status","isResourceAdmissionError","_admissionErrorKey","ResourceAdmissionError","code","operationId","admitResourceOperation","isCleanupOwner","retainForCleanup","retainResourceForScope","retained","trackResourceOperation","kind","existingObservation","observation","active","ownerScopes","retainedByScope","cleanupOwner","count","waitForResourceIdle","Promise","recoverCancelledScopeResources","graceMs","startedAt","pendingOperations","tracked","timer","onAbort","stopped","_resolve","reject","setTimeout","blockingOperations","blockedByStepId","stepId","clearTimeout","ResourceCleanupDeferredError","resourceCount","runResourceCleanup","cleanup","options","beforeCleanup","ownerScope","defer","deferred","resolve","drain","completion","cleanupObservation","ownerKey","cleanupState","outcome","cleanupResources","cleanupScopeResources","cleanupSignal"],"mappings":"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"}
1
+ {"version":3,"file":"test-runner/engine/resource-operations.mjs","sources":["../../../../src/test-runner/engine/resource-operations.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { uuid } from '@midscene/shared/utils';\nimport type {\n TestRunReportCleanup,\n TestRunReportOperationEvent,\n TestRunReportOperationReference,\n} from '../../test-run-report';\nimport { WorkflowError } from '../errors';\nimport { sanitizeReportError } from '../reporting/sanitize';\n\ninterface OperationIdentity {\n scopeId: string;\n stepId?: string;\n node?: string;\n}\ninterface ResourceOperation {\n signal: AbortSignal;\n operationId: string;\n identity?: OperationIdentity;\n settled?: boolean;\n blockers?: Set<ResourceOperation>;\n}\ntype OperationListener = (event: Readonly<TestRunReportOperationEvent>) => void;\n\ninterface CleanupOwner {\n resources: Set<object>;\n signal?: AbortSignal;\n blockers: Set<ResourceOperation>;\n}\n\ninterface ResourceScope {\n operations: Map<object, number>;\n resources?: Set<object>;\n cleanupOwner?: CleanupOwner;\n}\n\ninterface ResourceHandle {\n operations: Map<Promise<unknown>, ResourceOperation>;\n retiring: Set<CleanupOwner>;\n cleanup?: Promise<unknown>;\n cleanupState?: {\n operationId?: string;\n status: 'pending' | 'completed' | 'failed';\n blockers: Set<ResourceOperation>;\n error?: unknown;\n };\n}\n\ninterface ResourceState {\n resources: WeakMap<object, ResourceHandle>;\n scopes: WeakMap<AbortSignal, ResourceScope>;\n parents: WeakMap<AbortSignal, AbortSignal>;\n cleanupOwners: WeakMap<AbortSignal, CleanupOwner>;\n identities: WeakMap<AbortSignal, OperationIdentity>;\n listeners: WeakMap<AbortSignal, Set<OperationListener>>;\n}\n\n// CJS and ESM hosts must agree about ownership of the same Agent instance.\nconst admissionErrorKey = Symbol.for('@midscene/core/resource-admission-error');\nconst stateKey = Symbol.for('@midscene/core/resource-operations/v4');\nconst host = globalThis as typeof globalThis & { [stateKey]?: ResourceState };\nhost[stateKey] ??= {\n resources: new WeakMap(),\n scopes: new WeakMap(),\n parents: new WeakMap(),\n cleanupOwners: new WeakMap(),\n identities: new WeakMap(),\n listeners: new WeakMap(),\n};\nconst { resources, scopes, parents, cleanupOwners, identities, listeners } =\n host[stateKey];\nconst resourceHandle = (resource: object): ResourceHandle => {\n let handle = resources.get(resource);\n if (!handle) {\n handle = {\n operations: new Map(),\n retiring: new Set(),\n };\n resources.set(resource, handle);\n }\n return handle;\n};\n\n/** Actual teardown completion belongs to the owner, never to a thrown error. */\nexport const getResourceCleanupCompletion = (\n owner: object,\n): Promise<unknown> | undefined => resources.get(owner)?.cleanup;\n\nfunction blockingReferences(\n blockers: Iterable<ResourceOperation>,\n): TestRunReportOperationReference[] {\n const references = new Map<string, TestRunReportOperationReference>();\n const visited = new Set<ResourceOperation>();\n const visit = (record: ResourceOperation) => {\n if (visited.has(record)) return;\n visited.add(record);\n if (!record.settled)\n references.set(record.operationId, {\n operationId: record.operationId,\n ...record.identity,\n });\n for (const blocker of record.blockers ?? []) visit(blocker);\n };\n for (const blocker of blockers) visit(blocker);\n return [...references.values()];\n}\n\n/** Read a copy at the publication boundary; never expose the live cleanup state. */\nexport function getResourceCleanupSnapshot(\n owner: object,\n): TestRunReportCleanup {\n const state = resources.get(owner)?.cleanupState;\n return freezeEvent({\n capturedAt: new Date().toISOString(),\n status: state?.status ?? 'not-required',\n ...(state?.operationId ? { operationId: state.operationId } : {}),\n ...(state?.status === 'pending'\n ? { blockingOperations: blockingReferences(state.blockers) }\n : {}),\n ...(state?.error === undefined\n ? {}\n : { error: sanitizeReportError(state.error) }),\n });\n}\n\nexport const RESOURCE_CLEANUP_GRACE_MS = 1000;\nexport const RESOURCE_RECOVERY_GRACE_MS = 30_000;\nconst RESOURCE_TEARDOWN_GRACE_MS = 10_000;\n\nexport function getPendingScopeOperations(\n signal: AbortSignal,\n): TestRunReportOperationReference[] {\n return freezeEvent(\n blockingReferences(\n [...(scopes.get(signal)?.operations.keys() ?? [])].flatMap((resource) => [\n ...(resources.get(resource)?.operations.values() ?? []),\n ]),\n ),\n );\n}\n\n/** Snapshotting must not wait for a timed-out operation to release its resource. */\nexport const hasPendingResourceOperation = (resource: object): boolean =>\n (resources.get(resource)?.operations.size ?? 0) > 0;\n\n/** Resource ancestry is independent of cancellation: cleanup can outlive abort. */\nexport function linkResourceSignal(signal: AbortSignal, parent?: AbortSignal) {\n if (parent && parent !== signal) parents.set(signal, parent);\n}\n\nfunction* ownerSignals(signal?: AbortSignal): Generator<AbortSignal> {\n const seen = new Set<AbortSignal>();\n for (\n let owner = signal;\n owner && !seen.has(owner);\n owner = parents.get(owner)\n ) {\n seen.add(owner);\n yield owner;\n }\n}\n\n/** Each Case/document owns its cleanup, even when cancellation is shared. */\nexport function createResourceScope(parent?: AbortSignal) {\n const controller = new AbortController();\n const scope: ResourceScope = { operations: new Map(), resources: new Set() };\n scopes.set(controller.signal, scope);\n linkResourceSignal(controller.signal, parent);\n const abort = () => controller.abort(parent?.reason);\n if (parent?.aborted) abort();\n else parent?.addEventListener('abort', abort, { once: true });\n return {\n signal: controller.signal,\n abort(reason?: unknown) {\n controller.abort(reason);\n },\n dispose() {\n parent?.removeEventListener('abort', abort);\n scope.resources = undefined;\n if (!scope.operations.size) scopes.delete(controller.signal);\n },\n };\n}\n\nexport function assertResourceAvailable(\n resource: object,\n exclusive = false,\n signal?: AbortSignal,\n): boolean {\n const retiringOwners = resources.get(resource)?.retiring;\n const isOwnerCleanup = [...ownerSignals(signal)].some((ownerSignal) => {\n const owner = cleanupOwners.get(ownerSignal);\n return owner !== undefined && retiringOwners?.has(owner);\n });\n if (retiringOwners?.size && !isOwnerCleanup)\n throw new WorkflowError(\n 'A resource cannot execute overlapping Runner operations while its cleanup is pending.',\n { code: 'RESOURCE_CLEANUP_PENDING' },\n );\n const pending = resources.get(resource)?.operations;\n if (exclusive && pending?.size)\n throw new WorkflowError(\n 'The same Agent instance cannot execute overlapping Runner operations.',\n { code: 'RESOURCE_OPERATION_PENDING' },\n );\n if (\n pending &&\n [...pending.values()].some((operation) => operation.signal.aborted)\n )\n throw new WorkflowError(\n 'Cannot reuse an Agent while an aborted YAML action or Runner operation is still running.',\n { code: 'RESOURCE_OPERATION_PENDING' },\n );\n return isOwnerCleanup;\n}\n\n/** Capture subscribers at registration: late settlement stays with its original run. */\nexport function observeResourceOperations(\n signal: AbortSignal,\n listener: OperationListener,\n) {\n const subscribers = listeners.get(signal) ?? new Set<OperationListener>();\n listeners.set(signal, subscribers);\n subscribers.add(listener);\n return () => {\n subscribers.delete(listener);\n if (!subscribers.size) listeners.delete(signal);\n };\n}\n\nexport function setResourceOperationIdentity(\n signal: AbortSignal,\n identity: OperationIdentity,\n) {\n identities.set(signal, Object.freeze({ ...identity }));\n}\n\nfunction freezeEvent<T>(value: T): T {\n if (typeof value === 'object' && value !== null && !Object.isFrozen(value)) {\n for (const child of Object.values(value)) freezeEvent(child);\n Object.freeze(value);\n }\n return value;\n}\n\nexport function recordOperation(\n operation: Promise<unknown>,\n signal: AbortSignal,\n operationKind: TestRunReportOperationEvent['operationKind'],\n) {\n const owners = [...ownerSignals(signal)];\n const identity = owners.map((owner) => identities.get(owner)).find(Boolean);\n const record: ResourceOperation = { signal, operationId: uuid(), identity };\n const subscribers = new Set(\n owners.flatMap((owner) => [...(listeners.get(owner) ?? [])]),\n );\n let settled = false;\n const emit = (\n event: Omit<\n TestRunReportOperationEvent,\n 'operationId' | 'operationKind' | 'at'\n >,\n ) => {\n const frozen = freezeEvent({\n ...identity,\n operationId: record.operationId,\n operationKind,\n at: new Date().toISOString(),\n ...event,\n });\n for (const subscriber of subscribers) {\n try {\n subscriber(frozen);\n } catch (error) {\n getDebug('runner:operations', { console: true })(\n `Operation observer failed: ${String(error)}`,\n );\n }\n }\n };\n const abort = () =>\n emit({\n kind: 'cancel-requested',\n error: sanitizeReportError(signal.reason),\n });\n emit({ kind: 'started' });\n if (signal.aborted) abort();\n else signal.addEventListener('abort', abort, { once: true });\n const finish = (\n status: 'success' | 'failed' | 'not-run',\n error?: unknown,\n ) => {\n settled = true;\n record.settled = true;\n signal.removeEventListener('abort', abort);\n emit({\n kind: 'settled',\n status,\n ...(status !== 'success' ? { error: sanitizeReportError(error) } : {}),\n });\n };\n void operation.then(\n () => finish('success'),\n (error) =>\n finish(isResourceAdmissionError(error) ? 'not-run' : 'failed', error),\n );\n return {\n record,\n runnerReturned(status: 'success' | 'failed' | 'not-run') {\n emit({ kind: 'runner-returned', status, pending: !settled });\n },\n cleanupDeferred(blockers: Iterable<ResourceOperation>) {\n emit({\n kind: 'cleanup-deferred',\n blockingOperations: blockingReferences(blockers),\n });\n },\n };\n}\n\n/** A scheduling decision, distinct from an action that ran and failed. */\nexport class ResourceAdmissionError extends WorkflowError {\n readonly blockedBy?: string;\n readonly [admissionErrorKey] = true;\n constructor(code: string, blockers: readonly ResourceOperation[]) {\n super(\n 'The Agent is occupied by an unfinished operation or pending cleanup; this action was not started.',\n {\n code,\n details: {\n blockingOperations: blockers.map(({ operationId, identity }) => ({\n operationId,\n ...identity,\n })),\n },\n },\n );\n this.blockedBy = blockers.find(\n (blocker) => blocker.identity?.stepId,\n )?.identity?.stepId;\n }\n}\n\nexport function isResourceAdmissionError(\n error: unknown,\n): error is ResourceAdmissionError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as ResourceAdmissionError)[admissionErrorKey] === true\n );\n}\n\nexport function admitResourceOperation(\n resource: object,\n signal: AbortSignal,\n exclusive = true,\n): boolean {\n let isCleanupOwner: boolean;\n try {\n isCleanupOwner = assertResourceAvailable(resource, exclusive, signal);\n } catch (error) {\n if (\n error instanceof WorkflowError &&\n ['RESOURCE_OPERATION_PENDING', 'RESOURCE_CLEANUP_PENDING'].includes(\n error.code,\n )\n )\n throw new ResourceAdmissionError(error.code, [\n ...(resources.get(resource)?.operations.values() ?? []),\n ...[...(resources.get(resource)?.retiring ?? [])].flatMap((owner) => [\n ...owner.blockers,\n ]),\n ]);\n throw error;\n }\n return isCleanupOwner;\n}\n\nconst retainForCleanup = (owner: CleanupOwner, resource: object) => {\n owner.resources.add(resource);\n const handle = resourceHandle(resource);\n for (const operation of handle.operations.values())\n owner.blockers.add(operation);\n handle.retiring.add(owner);\n};\n\n/** Acquisition establishes cleanup ownership even if action admission is rejected. */\nexport function retainResourceForScope(resource: object, signal: AbortSignal) {\n let retained = false;\n for (const ownerSignal of ownerSignals(signal)) {\n const scope = scopes.get(ownerSignal);\n if (!retained && scope?.resources) {\n scope.resources.add(resource);\n retained = true;\n }\n if (scope?.cleanupOwner) retainForCleanup(scope.cleanupOwner, resource);\n }\n}\n\n/** Track actual completion, not the Runner timeout race. */\nexport function trackResourceOperation<T>(\n resource: object,\n operation: Promise<T>,\n signal: AbortSignal,\n kind: TestRunReportOperationEvent['operationKind'] = 'resource',\n existingObservation?: ReturnType<typeof recordOperation>,\n): Promise<T> {\n const observation =\n existingObservation ?? recordOperation(operation, signal, kind);\n const active = resourceHandle(resource).operations;\n active.set(operation, observation.record);\n const ownerScopes: Array<[AbortSignal, ResourceScope]> = [];\n let retainedByScope = false;\n for (const ownerSignal of ownerSignals(signal)) {\n const scope: ResourceScope = scopes.get(ownerSignal) ?? {\n operations: new Map(),\n };\n scopes.set(ownerSignal, scope);\n scope.operations.set(resource, (scope.operations.get(resource) ?? 0) + 1);\n ownerScopes.push([ownerSignal, scope]);\n // Only the closest owner retains idle resources for its teardown. Parents\n // wait on active operations, not every Agent used by completed Cases.\n if (!retainedByScope && scope.resources) {\n scope.resources.add(resource);\n retainedByScope = true;\n }\n const cleanupOwner = cleanupOwners.get(ownerSignal);\n if (cleanupOwner) {\n cleanupOwner.blockers.add(observation.record);\n retainForCleanup(cleanupOwner, resource);\n }\n }\n const finish = () => {\n active.delete(operation);\n for (const [ownerSignal, scope] of ownerScopes) {\n const count = scope.operations.get(resource)! - 1;\n if (count) scope.operations.set(resource, count);\n else scope.operations.delete(resource);\n if (!scope.operations.size && !scope.resources)\n scopes.delete(ownerSignal);\n }\n };\n void operation.then(finish, finish);\n return operation;\n}\n\nexport async function waitForResourceIdle(resource: object): Promise<void> {\n while (resources.get(resource)?.operations?.size)\n await Promise.allSettled([...resources.get(resource)!.operations.keys()]);\n}\n\n/** Wait for actual descendant settlement, including deferred cleanup. */\nexport async function waitForScopeOperations(\n signal: AbortSignal,\n): Promise<void> {\n while (true) {\n const active = [...(scopes.get(signal)?.operations.keys() ?? [])];\n if (!active.length) return;\n await Promise.all(active.map(waitForResourceIdle));\n }\n}\n\n/** Wait for this scope's cancelled work before scheduling its next Step. */\nexport async function recoverCancelledScopeResources(\n signal: AbortSignal,\n graceMs = RESOURCE_RECOVERY_GRACE_MS,\n): Promise<number> {\n signal.throwIfAborted();\n const startedAt = Date.now();\n const pendingOperations = () => {\n const pending: Array<{\n operation: Promise<unknown>;\n signal: AbortSignal;\n resource: object;\n record: ResourceOperation;\n }> = [];\n for (const resource of scopes.get(signal)?.operations.keys() ?? []) {\n for (const [operation, tracked] of resources.get(resource)?.operations ??\n []) {\n const ownerSignal = tracked.signal;\n if (\n ownerSignal.aborted &&\n [...ownerSignals(ownerSignal)].includes(signal)\n )\n pending.push({\n operation,\n signal: ownerSignal,\n resource,\n record: tracked,\n });\n }\n }\n return pending;\n };\n let pending = pendingOperations();\n if (!pending.length) return 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort!: () => void;\n const stopped = new Promise<never>((_resolve, reject) => {\n onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n timer = setTimeout(() => {\n const blockingOperations = blockingReferences(\n pending.map(({ record }) => record),\n );\n const blockedByStepId = blockingOperations.every(\n ({ stepId }) => stepId === blockingOperations[0]?.stepId,\n )\n ? blockingOperations[0]?.stepId\n : undefined;\n reject(\n new WorkflowError(\n `The previous operation was still running after ${graceMs / 1000} s. Later steps were not started.`,\n {\n code: 'RESOURCE_RECOVERY_TIMEOUT',\n details: {\n graceMs,\n waitedMs: Date.now() - startedAt,\n resourceCount: new Set(pending.map(({ resource }) => resource))\n .size,\n blockingOperations,\n blockedByStepId,\n },\n cause: pending[0]?.signal.reason,\n },\n ),\n );\n }, graceMs);\n });\n try {\n while (pending.length) {\n await Promise.race([\n Promise.allSettled(pending.map(({ operation }) => operation)),\n stopped,\n ]);\n signal.throwIfAborted();\n pending = pendingOperations();\n }\n return Date.now() - startedAt;\n } finally {\n clearTimeout(timer);\n signal.removeEventListener('abort', onAbort);\n }\n}\n\nexport class ResourceCleanupDeferredError extends WorkflowError {\n constructor(\n graceMs: number,\n resourceCount: number,\n blockers: readonly ResourceOperation[] = [],\n ) {\n super(\n `Resource cleanup did not finish within ${graceMs}ms. Resources remain quarantined until cleanup settles.`,\n {\n code: 'RESOURCE_CLEANUP_DEFERRED',\n details: {\n graceMs,\n resourceCount,\n blockingOperations: blockers.map(({ operationId, identity }) => ({\n operationId,\n ...identity,\n })),\n },\n },\n );\n }\n}\n\ninterface CleanupOptions {\n /** Stable owner of this cleanup, distinct from borrowed resource identities. */\n owner?: object;\n /** Parent scope observes deferred completion without host-specific forwarding. */\n signal?: AbortSignal;\n graceMs?: number;\n /** Optional total deadline, including hooks, draining and all disposers. */\n totalTimeoutMs?: number;\n onDeferredError(error: unknown): void;\n}\n\n/** Bound waiting, but keep ownership until the actual cleanup has finished. */\nasync function runResourceCleanup<T>(\n resources: readonly object[],\n cleanup: () => Promise<T>,\n options: CleanupOptions,\n beforeCleanup?: {\n signal: AbortSignal;\n abort(reason: unknown): void;\n run(signal: AbortSignal): Promise<void>;\n },\n): Promise<T> {\n const owner: CleanupOwner = {\n resources: new Set(),\n blockers: new Set(),\n signal: beforeCleanup?.signal,\n };\n for (const resource of resources) retainForCleanup(owner, resource);\n if (owner.signal) cleanupOwners.set(owner.signal, owner);\n const ownerScope = options.signal ? scopes.get(options.signal) : undefined;\n if (ownerScope) ownerScope.cleanupOwner = owner;\n\n let defer!: () => void;\n const deferred = new Promise<{ deferred: true }>((resolve) => {\n defer = () => {\n if (options.totalTimeoutMs !== undefined)\n beforeCleanup?.abort(\n new WorkflowError(\n `Resource cleanup timed out after ${options.totalTimeoutMs}ms.`,\n {\n code: 'TEARDOWN_TIMEOUT',\n details: { timeoutMs: options.totalTimeoutMs },\n },\n ),\n );\n resolve({ deferred: true });\n };\n });\n let graceMs =\n options.totalTimeoutMs ?? options.graceMs ?? RESOURCE_CLEANUP_GRACE_MS;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const drain = async () => {\n if (options.totalTimeoutMs === undefined)\n timer = setTimeout(defer, graceMs);\n try {\n // An acquisition may finish late and add another shared resource to its\n // owner. Keep draining that resource before calling any disposer.\n while (true) {\n const pending = [...owner.resources].filter(\n (resource) => resourceHandle(resource).operations.size,\n );\n if (!pending.length) return;\n await Promise.all(pending.map(waitForResourceIdle));\n }\n } finally {\n if (options.totalTimeoutMs === undefined) clearTimeout(timer);\n }\n };\n const completion = (async () => {\n if (options.totalTimeoutMs !== undefined)\n timer = setTimeout(defer, Math.max(0, options.totalTimeoutMs));\n try {\n if (beforeCleanup) {\n // Hooks may use idle resources owned by this cleanup. Busy resources\n // still reject overlapping actions, including cleanup actions.\n await beforeCleanup.run(beforeCleanup.signal);\n }\n // Lifecycle hooks can themselves time out with an action still running.\n // Drain both the original work and any operation started by the hook\n // before disposing the resource.\n await drain();\n if (options.totalTimeoutMs === undefined) {\n graceMs = options.graceMs ?? RESOURCE_TEARDOWN_GRACE_MS;\n timer = setTimeout(defer, graceMs);\n }\n return await cleanup();\n } finally {\n clearTimeout(timer);\n if (owner.signal) cleanupOwners.delete(owner.signal);\n if (ownerScope?.cleanupOwner === owner)\n ownerScope.cleanupOwner = undefined;\n for (const resource of owner.resources) {\n const owners = resourceHandle(resource).retiring;\n owners.delete(owner);\n }\n }\n })();\n const cleanupObservation = options.signal\n ? recordOperation(completion, options.signal, 'cleanup')\n : undefined;\n const ownerKey = options.owner ?? beforeCleanup?.signal ?? resources[0];\n const cleanupState: NonNullable<ResourceHandle['cleanupState']> = {\n status: 'pending',\n operationId: cleanupObservation?.record.operationId,\n blockers: owner.blockers,\n };\n if (cleanupObservation) cleanupObservation.record.blockers = owner.blockers;\n if (ownerKey) {\n resourceHandle(ownerKey).cleanup = completion;\n resourceHandle(ownerKey).cleanupState = cleanupState;\n }\n void completion.then(\n () => {\n cleanupState.status = 'completed';\n },\n (error) => {\n cleanupState.status = 'failed';\n cleanupState.error = error;\n },\n );\n const outcome = await Promise.race([\n completion.then((value) => ({ value })),\n deferred,\n ]);\n if ('deferred' in outcome) {\n cleanupObservation?.cleanupDeferred(owner.blockers);\n void completion.catch((error) => {\n try {\n options.onDeferredError(error);\n } catch {\n // The returned completion still carries the original cleanup failure.\n }\n });\n if (options.signal)\n trackResourceOperation(\n {},\n completion,\n options.signal,\n 'cleanup',\n cleanupObservation,\n );\n throw new ResourceCleanupDeferredError(graceMs, owner.resources.size, [\n ...owner.blockers,\n ]);\n }\n return outcome.value;\n}\n\nexport function cleanupResources<T>(\n resources: readonly object[],\n cleanup: () => Promise<T>,\n options: CleanupOptions,\n): Promise<T> {\n return runResourceCleanup(resources, cleanup, options);\n}\n\nexport async function cleanupScopeResources<T>(\n signal: AbortSignal,\n cleanup: () => Promise<T>,\n options: CleanupOptions & {\n beforeCleanup?(signal: AbortSignal): Promise<void>;\n },\n): Promise<T> {\n const cleanupController = new AbortController();\n const cleanupSignal = cleanupController.signal;\n linkResourceSignal(cleanupSignal, signal);\n const scope = scopes.get(signal);\n return runResourceCleanup(\n [...(scope?.resources ?? []), ...(scope?.operations.keys() ?? [])],\n cleanup,\n { ...options, owner: signal, signal },\n options.beforeCleanup\n ? {\n signal: cleanupSignal,\n abort: (reason) => cleanupController.abort(reason),\n run: options.beforeCleanup,\n }\n : undefined,\n );\n}\n"],"names":["admissionErrorKey","Symbol","stateKey","host","globalThis","WeakMap","resources","scopes","parents","cleanupOwners","identities","listeners","resourceHandle","resource","handle","Map","Set","getResourceCleanupCompletion","owner","blockingReferences","blockers","references","visited","visit","record","blocker","getResourceCleanupSnapshot","state","freezeEvent","Date","undefined","sanitizeReportError","RESOURCE_CLEANUP_GRACE_MS","RESOURCE_RECOVERY_GRACE_MS","RESOURCE_TEARDOWN_GRACE_MS","getPendingScopeOperations","signal","hasPendingResourceOperation","linkResourceSignal","parent","ownerSignals","seen","createResourceScope","controller","AbortController","scope","abort","reason","assertResourceAvailable","exclusive","retiringOwners","isOwnerCleanup","ownerSignal","WorkflowError","pending","operation","observeResourceOperations","listener","subscribers","setResourceOperationIdentity","identity","Object","value","child","recordOperation","operationKind","owners","Boolean","uuid","settled","emit","event","frozen","subscriber","error","getDebug","String","finish","status","isResourceAdmissionError","_admissionErrorKey","ResourceAdmissionError","code","operationId","admitResourceOperation","isCleanupOwner","retainForCleanup","retainResourceForScope","retained","trackResourceOperation","kind","existingObservation","observation","active","ownerScopes","retainedByScope","cleanupOwner","count","waitForResourceIdle","Promise","waitForScopeOperations","recoverCancelledScopeResources","graceMs","startedAt","pendingOperations","tracked","timer","onAbort","stopped","_resolve","reject","setTimeout","blockingOperations","blockedByStepId","stepId","clearTimeout","ResourceCleanupDeferredError","resourceCount","runResourceCleanup","cleanup","options","beforeCleanup","ownerScope","defer","deferred","resolve","drain","completion","Math","cleanupObservation","ownerKey","cleanupState","outcome","cleanupResources","cleanupScopeResources","cleanupController","cleanupSignal"],"mappings":"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"}
@@ -118,6 +118,7 @@ async function runCollectedCase(collectedCase, options) {
118
118
  };
119
119
  try {
120
120
  if (phases.afterEach.length || teardownStack.length) await cleanupScopeResources(scopeSignal, cleanup, {
121
+ totalTimeoutMs: options.teardownTimeoutMs?.(),
121
122
  beforeCleanup: async (cleanupSignal)=>{
122
123
  try {
123
124
  await runPhase('afterEach', afterEach, cleanupSignal);