@midscene/test 1.12.6-beta-20260910093036.0 → 1.12.6

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 (44) hide show
  1. package/dist/es/1~891.mjs +19 -39
  2. package/dist/es/1~891.mjs.map +1 -1
  3. package/dist/es/cli/index.mjs +26 -44
  4. package/dist/es/cli/index.mjs.map +1 -1
  5. package/dist/es/index.mjs +0 -3
  6. package/dist/es/index.mjs.map +1 -1
  7. package/dist/es/midscene/index.mjs +43 -32
  8. package/dist/es/midscene/index.mjs.map +1 -1
  9. package/dist/es/test-cli.mjs +37 -49
  10. package/dist/es/test-cli.mjs.map +1 -1
  11. package/dist/lib/0~458.js +19 -39
  12. package/dist/lib/0~458.js.map +1 -1
  13. package/dist/lib/cli/index.js +26 -44
  14. package/dist/lib/cli/index.js.map +1 -1
  15. package/dist/lib/index.js +0 -3
  16. package/dist/lib/index.js.map +1 -1
  17. package/dist/lib/midscene/index.js +253 -262
  18. package/dist/lib/midscene/index.js.map +1 -1
  19. package/dist/lib/test-cli.js +37 -49
  20. package/dist/lib/test-cli.js.map +1 -1
  21. package/dist/types/cli/index.d.ts +12 -13
  22. package/dist/types/index.d.ts +1 -7
  23. package/dist/types/midscene/index.d.ts +21 -21
  24. package/package.json +8 -48
  25. package/dist/es/android/index.mjs +0 -182
  26. package/dist/es/android/index.mjs.map +0 -1
  27. package/dist/es/harmony/index.mjs +0 -182
  28. package/dist/es/harmony/index.mjs.map +0 -1
  29. package/dist/es/ios/index.mjs +0 -182
  30. package/dist/es/ios/index.mjs.map +0 -1
  31. package/dist/es/playwright/index.mjs +0 -366
  32. package/dist/es/playwright/index.mjs.map +0 -1
  33. package/dist/lib/android/index.js +0 -255
  34. package/dist/lib/android/index.js.map +0 -1
  35. package/dist/lib/harmony/index.js +0 -252
  36. package/dist/lib/harmony/index.js.map +0 -1
  37. package/dist/lib/ios/index.js +0 -255
  38. package/dist/lib/ios/index.js.map +0 -1
  39. package/dist/lib/playwright/index.js +0 -412
  40. package/dist/lib/playwright/index.js.map +0 -1
  41. package/dist/types/android/index.d.ts +0 -140
  42. package/dist/types/harmony/index.d.ts +0 -137
  43. package/dist/types/ios/index.d.ts +0 -143
  44. package/dist/types/playwright/index.d.ts +0 -229
@@ -1 +1 @@
1
- {"version":3,"file":"midscene/index.js","sources":["../../../src/errors.ts","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/node/define-node.ts","../../../src/midscene/index.ts"],"sourcesContent":["import type { z } from 'zod/v4';\n\nexport interface WorkflowErrorOptions {\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\nexport class WorkflowError extends Error {\n readonly code: string;\n readonly details?: unknown;\n\n constructor(message: string, options: WorkflowErrorOptions = {}) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.code = options.code ?? 'WORKFLOW_ERROR';\n this.details = options.details;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n ...(this.details === undefined ? {} : { details: this.details }),\n };\n }\n}\n\nexport class WorkflowParseError extends WorkflowError {\n constructor(message: string, details?: unknown, cause?: unknown) {\n super(message, { code: 'WORKFLOW_PARSE_ERROR', details, cause });\n }\n}\n\nexport class NodeDefinitionError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'NODE_DEFINITION_ERROR', details });\n }\n}\n\nexport class DuplicateNodeError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string) {\n super(`Node \"${node}\" is already registered.`, {\n code: 'DUPLICATE_NODE',\n details: { node },\n });\n this.node = node;\n }\n}\n\nexport class NodeNotFoundError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string) {\n super(`Node \"${node}\" is not registered.`, {\n code: 'NODE_NOT_FOUND',\n details: { node },\n });\n this.node = node;\n }\n}\n\nexport class NodeInputValidationError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'NODE_INPUT_VALIDATION_ERROR', details });\n }\n\n static fromZod(node: string, error: z.ZodError): NodeInputValidationError {\n const issues = error.issues.map((issue) => ({\n code: issue.code,\n path: issue.path.map(String).join('.'),\n message: issue.message,\n }));\n const firstIssue = issues[0];\n const path = firstIssue?.path || '<root>';\n const message = firstIssue?.message ?? 'invalid input';\n return new NodeInputValidationError(\n `Node \"${node}\" input validation failed at \"${path}\": ${message}`,\n { node, issues },\n );\n }\n}\n\nexport class StepTimeoutError extends WorkflowError {\n readonly timeoutMs: number;\n readonly node?: string;\n\n constructor(timeoutMs: number, node?: string) {\n super(\n node\n ? `Node \"${node}\" timed out after ${timeoutMs}ms.`\n : `Step timed out after ${timeoutMs}ms.`,\n {\n code: 'STEP_TIMEOUT',\n details: { timeoutMs, ...(node === undefined ? {} : { node }) },\n },\n );\n this.timeoutMs = timeoutMs;\n this.node = node;\n }\n}\n\nexport class NodeExecutionError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string, cause: unknown) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Node \"${node}\" failed: ${causeMessage}`, {\n code: 'NODE_EXECUTION_ERROR',\n details: { node },\n cause,\n });\n this.node = node;\n }\n}\n\nexport class WorkflowLifecycleError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'WORKFLOW_LIFECYCLE_ERROR', details });\n }\n}\n\nexport class ProjectSetupError extends WorkflowError {\n constructor(cause: unknown, details: { projectName: string }) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Project \"${details.projectName}\" setup failed: ${causeMessage}`, {\n code: 'PROJECT_SETUP_ERROR',\n details,\n cause,\n });\n }\n}\n\nexport class ProjectTeardownError extends WorkflowError {\n constructor(\n cause: unknown,\n details: { projectName: string; registrationIndex: number },\n ) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Project \"${details.projectName}\" teardown failed: ${causeMessage}`, {\n code: 'PROJECT_TEARDOWN_ERROR',\n details,\n cause,\n });\n }\n}\n\nexport class NodeScopeTeardownError extends WorkflowError {\n constructor(\n cause: unknown,\n details: {\n scope: 'case' | 'document';\n scopeId: string;\n node: string;\n registrationIndex: number;\n },\n ) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(\n `${details.scope === 'case' ? 'Case attempt' : 'Workflow document'} node teardown failed for \"${details.node}\": ${causeMessage}`,\n { code: 'NODE_SCOPE_TEARDOWN_ERROR', details, cause },\n );\n }\n}\n\nexport class FatalDeviceError extends WorkflowError {\n constructor(message: string, cause?: unknown) {\n super(message, { code: 'FATAL_DEVICE_ERROR', cause });\n }\n}\n\nexport const isFatalDeviceError = (error: unknown): boolean => {\n if (error instanceof FatalDeviceError) return true;\n if (error instanceof WorkflowError && error.code === 'FATAL_DEVICE_ERROR') {\n return true;\n }\n if (\n error instanceof Error &&\n /device offline|device not found|(?:adb|bdc|device) connection (?:was )?closed/i.test(\n error.message,\n )\n ) {\n return true;\n }\n return error instanceof Error && error.cause !== undefined\n ? isFatalDeviceError(error.cause)\n : false;\n};\n\nexport class CaseExecutionError extends WorkflowError {\n readonly result: import('./engine/types').CaseRunResult;\n\n constructor(result: import('./engine/types').CaseRunResult) {\n super(`Case \"${result.name}\" failed.`, {\n code: 'CASE_EXECUTION_FAILED',\n details: { caseId: result.caseId, runId: result.runId },\n });\n this.result = result;\n }\n}\n\nexport class WorkflowDocumentExecutionError extends WorkflowError {\n readonly result: import('./engine/types').WorkflowDocumentRunResult;\n\n constructor(result: import('./engine/types').WorkflowDocumentRunResult) {\n super(`Workflow document \"${result.sourcePath}\" failed.`, {\n code: 'WORKFLOW_DOCUMENT_EXECUTION_FAILED',\n details: {\n documentId: result.documentId,\n documentRunId: result.documentRunId,\n },\n });\n this.result = result;\n }\n}\n\nexport function normalizeNodeExecutionError(\n error: unknown,\n node: string,\n): WorkflowError {\n return error instanceof WorkflowError\n ? error\n : new NodeExecutionError(node, error);\n}\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { z } from 'zod/v4';\nimport { NodeDefinitionError } from '../errors';\nimport type {\n DefineNodeOptions,\n DefineNodeWithSchemaOptions,\n NodeDefinition,\n NodeDefinitionWithSchema,\n NodeInputSchema,\n} from './types';\n\nconst validateOptionalText = (\n value: unknown,\n field: 'title' | 'description',\n node: string,\n): void => {\n if (\n value !== undefined &&\n (typeof value !== 'string' || value.trim().length === 0)\n ) {\n throw new NodeDefinitionError(\n `Node \"${node}\" ${field} must be a non-empty string.`,\n { node, field },\n );\n }\n};\n\nconst validateInputSchema = (schema: unknown, node: string): void => {\n if (schema === undefined) return;\n if (!(schema instanceof z.ZodObject)) {\n throw new NodeDefinitionError(\n `Node \"${node}\" inputSchema must be a Zod object schema.`,\n { node, field: 'inputSchema' },\n );\n }\n if ('$' in schema.shape) {\n throw new NodeDefinitionError(\n `Node \"${node}\" inputSchema must not declare \"$\" as an input property.`,\n { node, field: 'inputSchema.$' },\n );\n }\n};\n\nconst validateDefinition = (options: {\n name: string;\n title?: unknown;\n description?: unknown;\n stringInputKey?: unknown;\n inputSchema?: unknown;\n execute: unknown;\n}): void => {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError('Node definition must be an object.');\n }\n\n if (typeof options.name !== 'string' || options.name.trim().length === 0) {\n throw new NodeDefinitionError('Node name must be a non-empty string.');\n }\n\n validateOptionalText(options.title, 'title', options.name);\n validateOptionalText(options.description, 'description', options.name);\n if (\n options.stringInputKey !== undefined &&\n options.stringInputKey !== false &&\n (typeof options.stringInputKey !== 'string' ||\n options.stringInputKey.trim().length === 0)\n ) {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" stringInputKey must be a non-empty string or false.`,\n { node: options.name, field: 'stringInputKey' },\n );\n }\n validateInputSchema(options.inputSchema, options.name);\n\n if (\n typeof options.stringInputKey === 'string' &&\n options.inputSchema instanceof z.ZodObject &&\n !(options.stringInputKey in options.inputSchema.shape)\n ) {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" stringInputKey must reference a field in inputSchema.`,\n { node: options.name, field: 'stringInputKey' },\n );\n }\n\n if (typeof options.execute !== 'function') {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" must provide an execute function.`,\n { node: options.name },\n );\n }\n};\n\nexport function defineNode<\n TSchema extends NodeInputSchema,\n TData = unknown,\n TContext = unknown,\n>(\n options: DefineNodeWithSchemaOptions<TSchema, TData, TContext>,\n): NodeDefinitionWithSchema<TSchema, TData, TContext>;\n\nexport function defineNode<\n TInput = unknown,\n TData = unknown,\n TContext = unknown,\n>(\n options: DefineNodeOptions<TInput, TData, TContext>,\n): NodeDefinition<TInput, TData, TContext>;\n\nexport function defineNode(\n options: DefineNodeOptions<any, any, any>,\n): NodeDefinition<any, any, any> {\n validateDefinition(options);\n return options;\n}\n","import {\n type AgentTestRunnerNodeDefinition,\n type AgentTestRunnerNodeProvider,\n type CommonAgentTestRunnerApi,\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n} from '@midscene/core/agent/test-runner';\nimport { getDebug } from '@midscene/shared/logger';\nimport { z } from 'zod/v4';\nimport type { Awaitable } from '../engine/types';\nimport { NodeDefinitionError, NodeExecutionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type { NodeDefinition, NodeExecutionContext } from '../node/types';\n\nexport type MidsceneUIAgent = CommonAgentTestRunnerApi & {\n addDumpUpdateListener?(\n listener: (dump: string, execution?: MidsceneExecutionRef) => void,\n ): () => void;\n};\n\nexport {\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n};\n\nexport interface MidsceneExecutionRef {\n id?: string;\n}\n\nexport interface AgentProvider<TContext> {\n getAgent(\n runId: string,\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n // biome-ignore lint/suspicious/noConfusingVoidType: providers without a report intentionally return void.\n releaseAgent?(runId: string): Awaitable<AgentReleaseResult | void>;\n dispose?(): Awaitable<void>;\n}\n\nexport interface AgentReleaseResult {\n /** Absolute path to the finalized report for this Agent scope. */\n reportPath?: string;\n}\n\nexport const waitInputSchema = z.strictObject({\n duration: z.number().positive().describe('How long to wait.'),\n unit: z\n .enum(['ms', 's', 'min'])\n .default('ms')\n .describe('Duration unit: milliseconds, seconds, or minutes.'),\n});\n\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\nexport type WaitNodeInput = z.infer<typeof waitInputSchema>;\n\nexport interface CreateMidsceneNodesOptions<TContext> {\n getAgent?(\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n agentProvider?: AgentProvider<TContext>;\n /** Agent class that declares the Agent-backed Nodes to register. */\n agentClass: AgentTestRunnerNodeProvider;\n}\n\nconst waitFor = async (durationMs: number, signal: AbortSignal) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error('Wait aborted.');\n }\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal.removeEventListener('abort', abort);\n resolve();\n }, durationMs);\n const abort = () => {\n clearTimeout(timeout);\n reject(signal.reason ?? new Error('Wait aborted.'));\n };\n signal.addEventListener('abort', abort, { once: true });\n });\n};\n\n/** Adapt Agent-owned descriptions to executable Midscene Test Nodes. */\nexport const createAgentTestRunnerNodes = <TContext>(\n definitions: readonly AgentTestRunnerNodeDefinition[],\n getAgent: (\n ctx: NodeExecutionContext<unknown, TContext>,\n ) => Awaitable<unknown>,\n): readonly NodeDefinition<any, any, TContext>[] => {\n const activeAgentCalls = new WeakMap<\n MidsceneUIAgent,\n { scopeId: string; node: string }\n >();\n const runAgentCall = async <T>(\n node: string,\n ctx: NodeExecutionContext<unknown, TContext>,\n agent: MidsceneUIAgent,\n call: () => Promise<T>,\n captureExecutions = true,\n ): Promise<T> => {\n const scopeId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n const activeCall = activeAgentCalls.get(agent);\n if (activeCall) {\n throw new NodeExecutionError(\n node,\n new Error(\n `The same Agent instance cannot execute overlapping Midscene Test Steps. Active scope: ${activeCall.scopeId} (${activeCall.node}); requested scope: ${scopeId} (${node}).`,\n ),\n );\n }\n\n activeAgentCalls.set(agent, { scopeId, node });\n let removeListener: (() => void) | undefined;\n let sawExecutionWithoutId = false;\n const stopListening = () => {\n const remove = removeListener;\n removeListener = undefined;\n remove?.();\n };\n const stopListeningOnAbort = () => stopListening();\n try {\n if (captureExecutions && agent.addDumpUpdateListener) {\n removeListener = agent.addDumpUpdateListener((_dump, execution) => {\n if (!execution) return;\n if (!execution.id) {\n sawExecutionWithoutId = true;\n return;\n }\n ctx.report.addTrace({\n type: 'midscene-execution',\n executionId: execution.id,\n });\n });\n if (ctx.signal.aborted) stopListening();\n else {\n ctx.signal.addEventListener('abort', stopListeningOnAbort, {\n once: true,\n });\n }\n }\n return await call();\n } finally {\n ctx.signal.removeEventListener('abort', stopListeningOnAbort);\n try {\n stopListening();\n } finally {\n activeAgentCalls.delete(agent);\n }\n if (sawExecutionWithoutId) {\n warnReportTrace(\n `Skipped a report trace without a stable execution id for ${node} in scope ${scopeId}.`,\n );\n }\n }\n };\n\n return definitions.map((definition) =>\n defineNode({\n name: definition.name,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(ctx) {\n const agent = await getAgent(ctx);\n const call = async () =>\n definition.execute(agent, ctx.input, { signal: ctx.signal });\n if (typeof agent !== 'object' || agent === null) return call();\n return runAgentCall(\n definition.name,\n ctx,\n agent as MidsceneUIAgent,\n call,\n );\n },\n }),\n );\n};\n\nconst warnReportTrace = getDebug('test-runner:report-trace', {\n console: true,\n});\n\nexport function createMidsceneNodes<TContext>(\n options: CreateMidsceneNodesOptions<TContext>,\n): readonly NodeDefinition<any, any, TContext>[] {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() options must be an object.',\n );\n }\n if (\n typeof options.agentProvider?.getAgent !== 'function' &&\n typeof options.getAgent !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires getAgent or agentProvider.getAgent.',\n );\n }\n if (\n !options.agentClass ||\n typeof options.agentClass.getTestRunnerNodeDefinitions !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().',\n );\n }\n\n const registeredAgentScopes = new Set<string>();\n const getExecutionId = (ctx: NodeExecutionContext<unknown, TContext>) =>\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n const getAgent = async (\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Promise<MidsceneUIAgent> => {\n if (!options.agentProvider) return options.getAgent!(ctx);\n const runId = getExecutionId(ctx);\n if (\n options.agentProvider.releaseAgent &&\n !registeredAgentScopes.has(runId)\n ) {\n registeredAgentScopes.add(runId);\n ctx.onTeardown(async () => {\n try {\n const released = await options.agentProvider!.releaseAgent!(runId);\n return released?.reportPath\n ? { reportPaths: [released.reportPath] }\n : undefined;\n } finally {\n registeredAgentScopes.delete(runId);\n }\n });\n }\n return options.agentProvider.getAgent(runId, ctx);\n };\n\n const agentDefinitions = options.agentClass.getTestRunnerNodeDefinitions();\n\n return [\n ...createAgentTestRunnerNodes(agentDefinitions, getAgent),\n defineNode<typeof waitInputSchema, unknown, TContext>({\n name: 'wait',\n description: 'Wait for a fixed duration while honoring cancellation.',\n stringInputKey: false,\n inputSchema: waitInputSchema,\n async execute(ctx) {\n const multiplier =\n ctx.input.unit === 'min'\n ? 60_000\n : ctx.input.unit === 's'\n ? 1_000\n : 1;\n const durationMs = ctx.input.duration * multiplier;\n await waitFor(durationMs, ctx.signal);\n return { summary: `Waited ${durationMs}ms` };\n },\n }),\n ];\n}\n"],"names":["WorkflowError","Error","undefined","message","options","NodeDefinitionError","details","NodeExecutionError","node","cause","causeMessage","String","__webpack_require__","definition","key","Object","obj","prop","Symbol","validateOptionalText","value","field","validateInputSchema","schema","z","validateDefinition","defineNode","waitInputSchema","waitFor","durationMs","signal","Promise","resolve","reject","timeout","setTimeout","abort","clearTimeout","createAgentTestRunnerNodes","definitions","getAgent","activeAgentCalls","WeakMap","runAgentCall","ctx","agent","call","captureExecutions","scopeId","activeCall","removeListener","sawExecutionWithoutId","stopListening","remove","stopListeningOnAbort","_dump","execution","warnReportTrace","getDebug","createMidsceneNodes","registeredAgentScopes","Set","getExecutionId","runId","released","agentDefinitions","multiplier"],"mappings":"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"}
1
+ {"version":3,"file":"midscene/index.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/errors.ts","../../../src/node/define-node.ts","../../../src/midscene/index.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import type { z } from 'zod/v4';\n\nexport interface WorkflowErrorOptions {\n code?: string;\n details?: unknown;\n cause?: unknown;\n}\n\nexport class WorkflowError extends Error {\n readonly code: string;\n readonly details?: unknown;\n\n constructor(message: string, options: WorkflowErrorOptions = {}) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.code = options.code ?? 'WORKFLOW_ERROR';\n this.details = options.details;\n }\n\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n ...(this.details === undefined ? {} : { details: this.details }),\n };\n }\n}\n\nexport class WorkflowParseError extends WorkflowError {\n constructor(message: string, details?: unknown, cause?: unknown) {\n super(message, { code: 'WORKFLOW_PARSE_ERROR', details, cause });\n }\n}\n\nexport class NodeDefinitionError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'NODE_DEFINITION_ERROR', details });\n }\n}\n\nexport class DuplicateNodeError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string) {\n super(`Node \"${node}\" is already registered.`, {\n code: 'DUPLICATE_NODE',\n details: { node },\n });\n this.node = node;\n }\n}\n\nexport class NodeNotFoundError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string) {\n super(`Node \"${node}\" is not registered.`, {\n code: 'NODE_NOT_FOUND',\n details: { node },\n });\n this.node = node;\n }\n}\n\nexport class NodeInputValidationError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'NODE_INPUT_VALIDATION_ERROR', details });\n }\n\n static fromZod(node: string, error: z.ZodError): NodeInputValidationError {\n const issues = error.issues.map((issue) => ({\n code: issue.code,\n path: issue.path.map(String).join('.'),\n message: issue.message,\n }));\n const firstIssue = issues[0];\n const path = firstIssue?.path || '<root>';\n const message = firstIssue?.message ?? 'invalid input';\n return new NodeInputValidationError(\n `Node \"${node}\" input validation failed at \"${path}\": ${message}`,\n { node, issues },\n );\n }\n}\n\nexport class StepTimeoutError extends WorkflowError {\n readonly timeoutMs: number;\n readonly node?: string;\n\n constructor(timeoutMs: number, node?: string) {\n super(\n node\n ? `Node \"${node}\" timed out after ${timeoutMs}ms.`\n : `Step timed out after ${timeoutMs}ms.`,\n {\n code: 'STEP_TIMEOUT',\n details: { timeoutMs, ...(node === undefined ? {} : { node }) },\n },\n );\n this.timeoutMs = timeoutMs;\n this.node = node;\n }\n}\n\nexport class NodeExecutionError extends WorkflowError {\n readonly node: string;\n\n constructor(node: string, cause: unknown) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Node \"${node}\" failed: ${causeMessage}`, {\n code: 'NODE_EXECUTION_ERROR',\n details: { node },\n cause,\n });\n this.node = node;\n }\n}\n\nexport class WorkflowLifecycleError extends WorkflowError {\n constructor(message: string, details?: unknown) {\n super(message, { code: 'WORKFLOW_LIFECYCLE_ERROR', details });\n }\n}\n\nexport class ProjectSetupError extends WorkflowError {\n constructor(cause: unknown, details: { projectName: string }) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Project \"${details.projectName}\" setup failed: ${causeMessage}`, {\n code: 'PROJECT_SETUP_ERROR',\n details,\n cause,\n });\n }\n}\n\nexport class ProjectTeardownError extends WorkflowError {\n constructor(\n cause: unknown,\n details: { projectName: string; registrationIndex: number },\n ) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(`Project \"${details.projectName}\" teardown failed: ${causeMessage}`, {\n code: 'PROJECT_TEARDOWN_ERROR',\n details,\n cause,\n });\n }\n}\n\nexport class NodeScopeTeardownError extends WorkflowError {\n constructor(\n cause: unknown,\n details: {\n scope: 'case' | 'document';\n scopeId: string;\n node: string;\n registrationIndex: number;\n },\n ) {\n const causeMessage =\n cause instanceof Error ? cause.message : String(cause ?? 'Unknown error');\n super(\n `${details.scope === 'case' ? 'Case attempt' : 'Workflow document'} node teardown failed for \"${details.node}\": ${causeMessage}`,\n { code: 'NODE_SCOPE_TEARDOWN_ERROR', details, cause },\n );\n }\n}\n\nexport class FatalDeviceError extends WorkflowError {\n constructor(message: string, cause?: unknown) {\n super(message, { code: 'FATAL_DEVICE_ERROR', cause });\n }\n}\n\nexport const isFatalDeviceError = (error: unknown): boolean => {\n if (error instanceof FatalDeviceError) return true;\n if (error instanceof WorkflowError && error.code === 'FATAL_DEVICE_ERROR') {\n return true;\n }\n if (\n error instanceof Error &&\n /device offline|device not found|(?:adb|bdc|device) connection (?:was )?closed/i.test(\n error.message,\n )\n ) {\n return true;\n }\n return error instanceof Error && error.cause !== undefined\n ? isFatalDeviceError(error.cause)\n : false;\n};\n\nexport class CaseExecutionError extends WorkflowError {\n readonly result: import('./engine/types').CaseRunResult;\n\n constructor(result: import('./engine/types').CaseRunResult) {\n super(`Case \"${result.name}\" failed.`, {\n code: 'CASE_EXECUTION_FAILED',\n details: { caseId: result.caseId, runId: result.runId },\n });\n this.result = result;\n }\n}\n\nexport class WorkflowDocumentExecutionError extends WorkflowError {\n readonly result: import('./engine/types').WorkflowDocumentRunResult;\n\n constructor(result: import('./engine/types').WorkflowDocumentRunResult) {\n super(`Workflow document \"${result.sourcePath}\" failed.`, {\n code: 'WORKFLOW_DOCUMENT_EXECUTION_FAILED',\n details: {\n documentId: result.documentId,\n documentRunId: result.documentRunId,\n },\n });\n this.result = result;\n }\n}\n\nexport function normalizeNodeExecutionError(\n error: unknown,\n node: string,\n): WorkflowError {\n return error instanceof WorkflowError\n ? error\n : new NodeExecutionError(node, error);\n}\n","import { z } from 'zod/v4';\nimport { NodeDefinitionError } from '../errors';\nimport type {\n DefineNodeOptions,\n DefineNodeWithSchemaOptions,\n NodeDefinition,\n NodeDefinitionWithSchema,\n NodeInputSchema,\n} from './types';\n\nconst validateOptionalText = (\n value: unknown,\n field: 'title' | 'description',\n node: string,\n): void => {\n if (\n value !== undefined &&\n (typeof value !== 'string' || value.trim().length === 0)\n ) {\n throw new NodeDefinitionError(\n `Node \"${node}\" ${field} must be a non-empty string.`,\n { node, field },\n );\n }\n};\n\nconst validateInputSchema = (schema: unknown, node: string): void => {\n if (schema === undefined) return;\n if (!(schema instanceof z.ZodObject)) {\n throw new NodeDefinitionError(\n `Node \"${node}\" inputSchema must be a Zod object schema.`,\n { node, field: 'inputSchema' },\n );\n }\n if ('$' in schema.shape) {\n throw new NodeDefinitionError(\n `Node \"${node}\" inputSchema must not declare \"$\" as an input property.`,\n { node, field: 'inputSchema.$' },\n );\n }\n};\n\nconst validateDefinition = (options: {\n name: string;\n title?: unknown;\n description?: unknown;\n stringInputKey?: unknown;\n inputSchema?: unknown;\n execute: unknown;\n}): void => {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError('Node definition must be an object.');\n }\n\n if (typeof options.name !== 'string' || options.name.trim().length === 0) {\n throw new NodeDefinitionError('Node name must be a non-empty string.');\n }\n\n validateOptionalText(options.title, 'title', options.name);\n validateOptionalText(options.description, 'description', options.name);\n if (\n options.stringInputKey !== undefined &&\n options.stringInputKey !== false &&\n (typeof options.stringInputKey !== 'string' ||\n options.stringInputKey.trim().length === 0)\n ) {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" stringInputKey must be a non-empty string or false.`,\n { node: options.name, field: 'stringInputKey' },\n );\n }\n validateInputSchema(options.inputSchema, options.name);\n\n if (\n typeof options.stringInputKey === 'string' &&\n options.inputSchema instanceof z.ZodObject &&\n !(options.stringInputKey in options.inputSchema.shape)\n ) {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" stringInputKey must reference a field in inputSchema.`,\n { node: options.name, field: 'stringInputKey' },\n );\n }\n\n if (typeof options.execute !== 'function') {\n throw new NodeDefinitionError(\n `Node \"${options.name}\" must provide an execute function.`,\n { node: options.name },\n );\n }\n};\n\nexport function defineNode<\n TSchema extends NodeInputSchema,\n TData = unknown,\n TContext = unknown,\n>(\n options: DefineNodeWithSchemaOptions<TSchema, TData, TContext>,\n): NodeDefinitionWithSchema<TSchema, TData, TContext>;\n\nexport function defineNode<\n TInput = unknown,\n TData = unknown,\n TContext = unknown,\n>(\n options: DefineNodeOptions<TInput, TData, TContext>,\n): NodeDefinition<TInput, TData, TContext>;\n\nexport function defineNode(\n options: DefineNodeOptions<any, any, any>,\n): NodeDefinition<any, any, any> {\n validateDefinition(options);\n return options;\n}\n","import {\n type AgentTestRunnerNodeDefinition,\n type AgentTestRunnerNodeProvider,\n type CommonAgentTestRunnerApi,\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n} from '@midscene/core/agent/test';\nimport { getDebug } from '@midscene/shared/logger';\nimport { z } from 'zod/v4';\nimport type { Awaitable } from '../engine/types';\nimport { NodeDefinitionError, NodeExecutionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type { NodeDefinition, NodeExecutionContext } from '../node/types';\n\nexport type MidsceneUIAgent = CommonAgentTestRunnerApi & {\n addDumpUpdateListener?(\n listener: (dump: string, execution?: MidsceneExecutionRef) => void,\n ): () => void;\n};\n\nexport {\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n};\n\nexport interface MidsceneExecutionRef {\n id?: string;\n}\n\nexport interface AgentProvider<TContext> {\n getAgent(\n runId: string,\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n // biome-ignore lint/suspicious/noConfusingVoidType: providers without a report intentionally return void.\n releaseAgent?(runId: string): Awaitable<AgentReleaseResult | void>;\n dispose?(): Awaitable<void>;\n}\n\nexport interface AgentReleaseResult {\n /** Absolute path to the finalized report for this Agent scope. */\n reportPath?: string;\n}\n\nexport const waitInputSchema = z.strictObject({\n duration: z.number().positive().describe('How long to wait.'),\n unit: z\n .enum(['ms', 's', 'min'])\n .default('ms')\n .describe('Duration unit: milliseconds, seconds, or minutes.'),\n});\n\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\nexport type WaitNodeInput = z.infer<typeof waitInputSchema>;\n\nexport interface CreateMidsceneNodesOptions<TContext> {\n getAgent?(\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n agentProvider?: AgentProvider<TContext>;\n /** Agent class that declares the Agent-backed Nodes to register. */\n agentClass: AgentTestRunnerNodeProvider;\n}\n\nconst waitFor = async (durationMs: number, signal: AbortSignal) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error('Wait aborted.');\n }\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal.removeEventListener('abort', abort);\n resolve();\n }, durationMs);\n const abort = () => {\n clearTimeout(timeout);\n reject(signal.reason ?? new Error('Wait aborted.'));\n };\n signal.addEventListener('abort', abort, { once: true });\n });\n};\n\n/** Adapt Agent-owned descriptions to executable Midscene Test Nodes. */\nexport const createAgentTestRunnerNodes = <TContext>(\n definitions: readonly AgentTestRunnerNodeDefinition[],\n getAgent: (\n execution: NodeExecutionContext<unknown, TContext>,\n ) => Awaitable<unknown>,\n): readonly NodeDefinition<any, any, TContext>[] => {\n const activeAgentCalls = new WeakMap<\n MidsceneUIAgent,\n { scopeId: string; node: string }\n >();\n const runAgentCall = async <T>(\n node: string,\n execution: NodeExecutionContext<unknown, TContext>,\n agent: MidsceneUIAgent,\n call: () => Promise<T>,\n captureExecutions = true,\n ): Promise<T> => {\n execution.signal.throwIfAborted();\n const scopeId =\n execution.scope === 'case'\n ? execution.case.runId\n : execution.document.documentRunId;\n const activeCall = activeAgentCalls.get(agent);\n if (activeCall) {\n throw new NodeExecutionError(\n node,\n new Error(\n `The same Agent instance cannot execute overlapping Midscene Test Steps. Active scope: ${activeCall.scopeId} (${activeCall.node}); requested scope: ${scopeId} (${node}).`,\n ),\n );\n }\n\n const currentCall = { scopeId, node };\n activeAgentCalls.set(agent, currentCall);\n const releaseCall = () => {\n // An aborted call may finish after another Step has acquired this Agent.\n if (activeAgentCalls.get(agent) === currentCall) {\n activeAgentCalls.delete(agent);\n }\n };\n let removeListener: (() => void) | undefined;\n let sawExecutionWithoutId = false;\n const stopListening = () => {\n const remove = removeListener;\n removeListener = undefined;\n remove?.();\n };\n const stopListeningOnAbort = () => {\n try {\n stopListening();\n } finally {\n releaseCall();\n }\n };\n execution.signal.addEventListener('abort', stopListeningOnAbort, {\n once: true,\n });\n try {\n if (captureExecutions && agent.addDumpUpdateListener) {\n removeListener = agent.addDumpUpdateListener((_dump, executionRef) => {\n if (!executionRef) return;\n if (!executionRef.id) {\n sawExecutionWithoutId = true;\n return;\n }\n execution.report.addTrace({\n type: 'midscene-execution',\n executionId: executionRef.id,\n });\n });\n if (execution.signal.aborted) stopListening();\n }\n return await call();\n } finally {\n execution.signal.removeEventListener('abort', stopListeningOnAbort);\n try {\n stopListening();\n } finally {\n releaseCall();\n }\n if (sawExecutionWithoutId) {\n warnReportTrace(\n `Skipped a report trace without a stable execution id for ${node} in scope ${scopeId}.`,\n );\n }\n }\n };\n\n return definitions.map((definition) =>\n defineNode({\n name: definition.name,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(execution) {\n const agent = await getAgent(execution);\n const call = async () =>\n definition.execute(agent, execution.input, {\n signal: execution.signal,\n });\n if (typeof agent !== 'object' || agent === null) return call();\n return runAgentCall(\n definition.name,\n execution,\n agent as MidsceneUIAgent,\n call,\n );\n },\n }),\n );\n};\n\nconst warnReportTrace = getDebug('test-runner:report-trace', {\n console: true,\n});\n\nexport function createMidsceneNodes<TContext>(\n options: CreateMidsceneNodesOptions<TContext>,\n): readonly NodeDefinition<any, any, TContext>[] {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() options must be an object.',\n );\n }\n if (\n typeof options.agentProvider?.getAgent !== 'function' &&\n typeof options.getAgent !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires getAgent or agentProvider.getAgent.',\n );\n }\n if (\n !options.agentClass ||\n typeof options.agentClass.getTestRunnerNodeDefinitions !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().',\n );\n }\n\n const registeredAgentScopes = new Set<string>();\n const getExecutionId = (\n execution: NodeExecutionContext<unknown, TContext>,\n ) =>\n execution.scope === 'case'\n ? execution.case.runId\n : execution.document.documentRunId;\n const getAgent = async (\n execution: NodeExecutionContext<unknown, TContext>,\n ): Promise<MidsceneUIAgent> => {\n if (!options.agentProvider) return options.getAgent!(execution);\n const runId = getExecutionId(execution);\n if (\n options.agentProvider.releaseAgent &&\n !registeredAgentScopes.has(runId)\n ) {\n registeredAgentScopes.add(runId);\n execution.onTeardown(async () => {\n try {\n const released = await options.agentProvider!.releaseAgent!(runId);\n return released?.reportPath\n ? { reportPaths: [released.reportPath] }\n : undefined;\n } finally {\n registeredAgentScopes.delete(runId);\n }\n });\n }\n return options.agentProvider.getAgent(runId, execution);\n };\n\n const agentDefinitions = options.agentClass.getTestRunnerNodeDefinitions();\n\n return [\n ...createAgentTestRunnerNodes(agentDefinitions, getAgent),\n defineNode<typeof waitInputSchema, unknown, TContext>({\n name: 'wait',\n description: 'Wait for a fixed duration while honoring cancellation.',\n stringInputKey: false,\n inputSchema: waitInputSchema,\n async execute(execution) {\n const multiplier =\n execution.input.unit === 'min'\n ? 60_000\n : execution.input.unit === 's'\n ? 1_000\n : 1;\n const durationMs = execution.input.duration * multiplier;\n await waitFor(durationMs, execution.signal);\n return { summary: `Waited ${durationMs}ms` };\n },\n }),\n ];\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","WorkflowError","Error","undefined","message","options","NodeDefinitionError","details","NodeExecutionError","node","cause","causeMessage","String","validateOptionalText","value","field","validateInputSchema","schema","z","validateDefinition","defineNode","waitInputSchema","waitFor","durationMs","signal","Promise","resolve","reject","timeout","setTimeout","abort","clearTimeout","createAgentTestRunnerNodes","definitions","getAgent","activeAgentCalls","WeakMap","runAgentCall","execution","agent","call","captureExecutions","scopeId","activeCall","currentCall","releaseCall","removeListener","sawExecutionWithoutId","stopListening","remove","stopListeningOnAbort","_dump","executionRef","warnReportTrace","getDebug","createMidsceneNodes","registeredAgentScopes","Set","getExecutionId","runId","released","agentDefinitions","multiplier"],"mappings":"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"}
@@ -104,12 +104,12 @@ var __webpack_exports__ = {};
104
104
  sections.push(`**Config file:** ${location.configPath ? `\`${location.configPath}\`` : 'No config file loaded.'}`);
105
105
  for (const project of location.projects){
106
106
  const patterns = project.files.include.map((pattern)=>`\`${pattern}\``).join(', ');
107
- const projectLabel = location.projects.length > 1 ? ` (Execution Project: ${project.name})` : '';
107
+ const projectLabel = location.projects.length > 1 || 'default' !== project.name ? ` (Execution Project: ${project.name})` : '';
108
108
  const excludes = project.files.exclude?.length ? `; excludes: ${project.files.exclude.map((pattern)=>`\`${pattern}\``).join(', ')}` : '';
109
109
  sections.push(`**Case files:** ${patterns}${projectLabel}${excludes}`);
110
110
  }
111
111
  }
112
- sections.push('Files matching `files.exclude` are not selected. Midscene Test always ignores `.git/`, `.midscene/`, `midscene_run/`, and `node_modules/` directories, including nested ones.', 'Call a Node in `cases[].steps`, or in the Workflow Document lifecycle steps `beforeAll`, `beforeEach`, `afterEach`, and `afterAll`. Node registrations are shared across the Test Project; each Execution Project supplies its own runtime environment.');
112
+ sections.push('Files matching `files.exclude` are not selected. Midscene Test always ignores `.git/`, `.midscene/`, `midscene_run/`, and `node_modules/` directories, including nested ones.', 'Call a Node in `cases[].steps`, or in the Workflow Document lifecycle steps `beforeAll`, `beforeEach`, `afterEach`, and `afterAll`. Root Nodes are shared defaults; `projects[].nodes` adds local Nodes and overrides same-named root Nodes only within that Execution Project. Each Execution Project supplies its own runtime environment.');
113
113
  return sections.join('\n\n');
114
114
  };
115
115
  const WORKFLOW_STEP_CONTRACT = `## Workflow Step Contract
@@ -569,26 +569,15 @@ A Node may map a string step value to one declared input field. Each Node sectio
569
569
  assertJsonValue(value, label, new Set());
570
570
  return deepFreezeJson(value);
571
571
  };
572
- const platforms = new Set([
573
- 'web',
574
- 'android',
575
- 'ios',
576
- 'harmony',
577
- 'computer'
578
- ]);
579
- const validatePlatform = (value, label)=>{
580
- if ('string' != typeof value || !platforms.has(value)) throw new TypeError(`Midscene config ${label} must be one of web, android, ios, harmony, computer.`);
581
- return value;
582
- };
583
- const validateProjectSetup = (value, projectPlatform, label)=>{
572
+ const validateProjectSetup = (value, label)=>{
584
573
  if (void 0 === value) return;
585
574
  if (!isRecord(value)) throw new TypeError(`Midscene config ${label} must be an object.`);
575
+ rejectUnknownKeys(value, [
576
+ 'name',
577
+ 'setup'
578
+ ], label);
586
579
  if ('string' != typeof value.name || 0 === value.name.trim().length) throw new TypeError(`Midscene config ${label}.name must be non-empty.`);
587
580
  if ('function' != typeof value.setup) throw new TypeError(`Midscene config ${label}.setup must be a function.`);
588
- const supported = void 0 === value.platform ? void 0 : Array.isArray(value.platform) ? value.platform.map((item, index)=>validatePlatform(item, `${label}.platform[${index}]`)) : [
589
- validatePlatform(value.platform, `${label}.platform`)
590
- ];
591
- if (supported && !supported.includes(projectPlatform)) throw new TypeError(`Midscene config ${label} does not support project platform "${projectPlatform}".`);
592
581
  return value;
593
582
  };
594
583
  const validatePositiveInteger = (value, fallback, label)=>{
@@ -601,24 +590,15 @@ A Node may map a string step value to one declared input field. Each Node sectio
601
590
  if (!Number.isInteger(value) || value < 0) throw new TypeError(`Midscene config ${label} must be a non-negative integer.`);
602
591
  return value;
603
592
  };
604
- const validateExecutionProjects = (value, defaultSetup)=>{
593
+ const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
605
594
  if (void 0 === value) {
606
- let platform = 'web';
607
- if (void 0 !== defaultSetup) {
608
- if (!isRecord(defaultSetup)) throw new TypeError('Midscene config setup must be an object.');
609
- if (Array.isArray(defaultSetup.platform)) {
610
- if (1 !== defaultSetup.platform.length) throw new TypeError('Midscene config setup.platform must select exactly one platform when projects is omitted.');
611
- platform = validatePlatform(defaultSetup.platform[0], 'setup.platform[0]');
612
- } else if (void 0 !== defaultSetup.platform) platform = validatePlatform(defaultSetup.platform, 'setup.platform');
613
- }
614
- const setup = validateProjectSetup(defaultSetup, platform, 'setup');
595
+ const setup = validateProjectSetup(defaultSetup, 'setup');
615
596
  return {
616
597
  hasExplicitProjects: false,
617
598
  projects: Object.freeze([
618
599
  Object.freeze({
619
600
  projectId: projectIdFromIndex(0),
620
601
  name: 'default',
621
- platform,
622
602
  ...setup ? {
623
603
  setup
624
604
  } : {},
@@ -627,7 +607,8 @@ A Node may map a string step value to one declared input field. Each Node sectio
627
607
  exclude: []
628
608
  }),
629
609
  retry: 0,
630
- variables: Object.freeze({})
610
+ variables: Object.freeze({}),
611
+ nodes: new NodeRegistry(globalNodes.definitions())
631
612
  })
632
613
  ])
633
614
  };
@@ -640,8 +621,8 @@ A Node may map a string step value to one declared input field. Each Node sectio
640
621
  if (!isRecord(candidate)) throw new TypeError(`Midscene config ${label} must be an object.`);
641
622
  rejectUnknownKeys(candidate, [
642
623
  'name',
643
- 'platform',
644
624
  'setup',
625
+ 'nodes',
645
626
  'files',
646
627
  'tags',
647
628
  'retry',
@@ -650,12 +631,17 @@ A Node may map a string step value to one declared input field. Each Node sectio
650
631
  if ('string' != typeof candidate.name || 0 === candidate.name.trim().length) throw new TypeError(`Midscene config ${label}.name must be non-empty.`);
651
632
  if (names.has(candidate.name)) throw new TypeError(`Midscene config project name "${candidate.name}" must be unique.`);
652
633
  names.add(candidate.name);
653
- const platform = validatePlatform(candidate.platform, `${label}.platform`);
654
634
  const files = validateTestFileSelection(candidate.files, `${label}.files`);
635
+ if (void 0 !== candidate.nodes && !Array.isArray(candidate.nodes)) throw new TypeError(`Midscene config ${label}.nodes must be an array.`);
636
+ const localNodes = new NodeRegistry(candidate.nodes);
637
+ const nodes = new NodeRegistry([
638
+ ...globalNodes.definitions().filter((node)=>!localNodes.has(node.name)),
639
+ ...localNodes.definitions()
640
+ ]);
655
641
  return Object.freeze({
656
642
  projectId: projectIdFromIndex(index),
657
643
  name: candidate.name,
658
- platform,
644
+ nodes,
659
645
  ...files ? {
660
646
  files
661
647
  } : {},
@@ -663,7 +649,7 @@ A Node may map a string step value to one declared input field. Each Node sectio
663
649
  retry: validateNonNegativeInteger(candidate.retry, 0, `${label}.retry`),
664
650
  variables: validateVariables(candidate.variables, `${label}.variables`),
665
651
  ...void 0 === candidate.setup ? {} : {
666
- setup: validateProjectSetup(candidate.setup, platform, `${label}.setup`)
652
+ setup: validateProjectSetup(candidate.setup, `${label}.setup`)
667
653
  }
668
654
  });
669
655
  });
@@ -697,7 +683,8 @@ A Node may map a string step value to one declared input field. Each Node sectio
697
683
  });
698
684
  };
699
685
  const validateTestProjectDefinition = (definition)=>{
700
- if (!isRecord(definition) || !('nodes' in definition) || !Array.isArray(definition.nodes)) throw new TypeError('Midscene config must default export an object with a nodes array.');
686
+ if (!isRecord(definition)) throw new TypeError('Midscene config must default export an object.');
687
+ if (void 0 !== definition.nodes && !Array.isArray(definition.nodes)) throw new TypeError('Midscene config nodes must be an array.');
701
688
  if ('setupWorkflow' in definition) throw new TypeError('Midscene config setupWorkflow is no longer supported. Use setup or projects[].setup instead.');
702
689
  if ('setupDocument' in definition) throw new TypeError('Midscene config setupDocument is not supported. Use setup or projects[].setup for resources, and beforeAll/afterAll for document lifecycle steps.');
703
690
  if ('root' in definition) throw new TypeError('Midscene config root is not supported. Pass the Test Project directory to midscene-test instead.');
@@ -710,8 +697,8 @@ A Node may map a string step value to one declared input field. Each Node sectio
710
697
  'output',
711
698
  'nodes'
712
699
  ], 'root');
713
- const resolvedProjects = validateExecutionProjects(definition.projects, definition.setup);
714
700
  const nodes = new NodeRegistry(definition.nodes);
701
+ const resolvedProjects = validateExecutionProjects(definition.projects, definition.setup, nodes);
715
702
  return {
716
703
  ...resolvedProjects,
717
704
  test: validateTestOptions(definition.test),
@@ -779,7 +766,6 @@ A Node may map a string step value to one declared input field. Each Node sectio
779
766
  const endedAt = new Date();
780
767
  return {
781
768
  projectName: options.project.name,
782
- platform: options.project.platform,
783
769
  status: 'failed' === requestedStatus || setupError || teardownErrors.length > 0 ? 'failed' : 'success',
784
770
  ...setupError ? {
785
771
  setupError
@@ -1080,7 +1066,6 @@ A Node may map a string step value to one declared input field. Each Node sectio
1080
1066
  const project = options.project ?? Object.freeze({
1081
1067
  projectId: document.projectId,
1082
1068
  name: document.projectId,
1083
- platform: 'web',
1084
1069
  tags: Object.freeze({
1085
1070
  include: [],
1086
1071
  exclude: []
@@ -2015,7 +2000,6 @@ A Node may map a string step value to one declared input field. Each Node sectio
2015
2000
  return {
2016
2001
  projectId: project.projectId,
2017
2002
  name: project.name,
2018
- platform: project.platform,
2019
2003
  status: project.status,
2020
2004
  retry: project.retry,
2021
2005
  ...project.lifecycle ? {
@@ -2166,7 +2150,6 @@ A Node may map a string step value to one declared input field. Each Node sectio
2166
2150
  projects: result.projects.map((project)=>({
2167
2151
  projectId: project.projectId,
2168
2152
  name: project.name,
2169
- platform: project.platform,
2170
2153
  status: project.status,
2171
2154
  retry: project.retry,
2172
2155
  fileSelection: project.fileSelection,
@@ -2377,7 +2360,7 @@ A Node may map a string step value to one declared input field. Each Node sectio
2377
2360
  if (unknown) throw new Error(`Unknown Midscene project: ${unknown}`);
2378
2361
  return projects.filter((project)=>requested.has(project.name));
2379
2362
  };
2380
- const prepareProject = (project, projectRoot, resolveNode, runDir)=>{
2363
+ const prepareProject = (project, projectRoot, runDir)=>{
2381
2364
  const fileSelection = project.files ?? DEFAULT_TEST_FILE_SELECTION;
2382
2365
  const files = discoverTestFiles(projectRoot, fileSelection);
2383
2366
  const sources = files.map((absolutePath)=>({
@@ -2399,7 +2382,7 @@ A Node may map a string step value to one declared input field. Each Node sectio
2399
2382
  }
2400
2383
  for (const source of sources)try {
2401
2384
  const collected = collectWorkflowDocument(source, {
2402
- resolveNode,
2385
+ resolveNode: project.nodes.get.bind(project.nodes),
2403
2386
  variables: project.variables,
2404
2387
  env: process.env
2405
2388
  });
@@ -2443,7 +2426,7 @@ A Node may map a string step value to one declared input field. Each Node sectio
2443
2426
  });
2444
2427
  (0, external_node_fs_.mkdirSync)(runDir);
2445
2428
  const selectedProjects = selectProjects(definition.projects, options.projectNames);
2446
- const preparedProjects = selectedProjects.map((project)=>prepareProject(project, projectRoot, definition.resolveNode, runDir));
2429
+ const preparedProjects = selectedProjects.map((project)=>prepareProject(project, projectRoot, runDir));
2447
2430
  const progress = options.onProgress ?? (()=>{});
2448
2431
  const totalDocuments = preparedProjects.reduce((total, prepared)=>total + prepared.documents.length, 0);
2449
2432
  const totalCases = preparedProjects.reduce((total, prepared)=>total + prepared.selectedCaseCount, 0);
@@ -2479,7 +2462,7 @@ A Node may map a string step value to one declared input field. Each Node sectio
2479
2462
  };
2480
2463
  const announceProject = (prepared, projectIndex)=>{
2481
2464
  const { project } = prepared;
2482
- runInfrastructureCallback(()=>progress(`[project ${projectIndex + 1}/${preparedProjects.length}] ${project.name} (${project.platform})`));
2465
+ runInfrastructureCallback(()=>progress(`[project ${projectIndex + 1}/${preparedProjects.length}] ${project.name}`));
2483
2466
  };
2484
2467
  const buildProjectResult = (prepared, cases, documents, lifecycle)=>{
2485
2468
  const { project } = prepared;
@@ -2487,7 +2470,6 @@ A Node may map a string step value to one declared input field. Each Node sectio
2487
2470
  return {
2488
2471
  projectId: project.projectId,
2489
2472
  name: project.name,
2490
- platform: project.platform,
2491
2473
  status: projectFailed ? 'failed' : 'success',
2492
2474
  retry: project.retry,
2493
2475
  fileSelection: prepared.fileSelection,
@@ -2536,7 +2518,7 @@ A Node may map a string step value to one declared input field. Each Node sectio
2536
2518
  }
2537
2519
  projectProgress(` [document ${documentIndex + 1}/${prepared.documents.length}] ${document.sourcePath}`);
2538
2520
  const execution = await runWorkflowDocument(document, {
2539
- resolveNode: definition.nodes.require.bind(definition.nodes),
2521
+ resolveNode: project.nodes.require.bind(project.nodes),
2540
2522
  project,
2541
2523
  projectContext: runtime.context,
2542
2524
  retry: project.retry,
@@ -2692,7 +2674,7 @@ A Node may map a string step value to one declared input field. Each Node sectio
2692
2674
  } else throw new Error(`Unknown option: ${arg}`);
2693
2675
  }
2694
2676
  if ('nodes' === command && resultDir) throw new Error('--result-dir is not supported by nodes.');
2695
- if ('nodes' === command && projectNames.length > 0) throw new Error('--project is not supported by nodes.');
2677
+ if ('nodes' === command && projectNames.length > 1) throw new Error('nodes accepts only one --project name.');
2696
2678
  return {
2697
2679
  ...command ? {
2698
2680
  command
@@ -2723,10 +2705,15 @@ A Node may map a string step value to one declared input field. Each Node sectio
2723
2705
  const configPath = options.configPath ? (0, external_node_path_.resolve)(configSearchRoot, options.configPath) : discoverTestConfig(configSearchRoot);
2724
2706
  if (options.configPath && (!configPath || !(0, external_node_fs_.existsSync)(configPath))) throw new Error(`Midscene config does not exist: ${configPath}`);
2725
2707
  const project = await loadTestProject(configPath);
2726
- const nodes = sortNodesForReference(project.nodes.definitions());
2708
+ const projectName = options.projectNames?.[0];
2709
+ const selectedProjects = projectName ? project.projects.filter((candidate)=>candidate.name === projectName) : project.projects;
2710
+ if (0 === selectedProjects.length) throw new Error(`Unknown Midscene project: ${projectName}`);
2711
+ const registry = selectedProjects[0].nodes;
2712
+ if (selectedProjects.some((candidate)=>candidate.nodes.names().length !== registry.names().length || candidate.nodes.definitions().some((node)=>registry.get(node.name) !== node))) throw new Error('Projects have different Nodes. Use nodes --project <name> to select one.');
2713
+ const nodes = sortNodesForReference(registry.definitions());
2727
2714
  const document = renderNodeReference(nodes, {
2728
2715
  configPath: configPath ? (0, external_node_path_.relative)(configSearchRoot, configPath).split(external_node_path_.sep).join('/') : void 0,
2729
- projects: project.projects.map((executionProject)=>({
2716
+ projects: selectedProjects.map((executionProject)=>({
2730
2717
  name: executionProject.name,
2731
2718
  files: executionProject.files ?? DEFAULT_TEST_FILE_SELECTION
2732
2719
  }))
@@ -2734,6 +2721,7 @@ A Node may map a string step value to one declared input field. Each Node sectio
2734
2721
  for (const warning of document.warnings)io.error(`midscene-test nodes: ${warning}`);
2735
2722
  const referencePath = (0, external_node_path_.resolve)(configSearchRoot, 'midscene-node-reference.md');
2736
2723
  (0, external_node_fs_.writeFileSync)(referencePath, document.markdown);
2724
+ if (projectName) io.log(`Execution Project: ${projectName}`);
2737
2725
  io.log(`Registered Nodes (${nodes.length}):`);
2738
2726
  if (0 === nodes.length) io.log('No nodes are registered by the current Test Project.');
2739
2727
  for (const node of nodes)io.log(`- ${node.name}: ${node.description?.trim() || "Description not declared."}`);