@qaick/executor 0.1.0-alpha.2 → 0.1.0-alpha.3

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.
@@ -0,0 +1,11 @@
1
+ import type { ExecutableInput } from '@qaick/contract';
2
+ /** Shared invocation semantics. Never coerce supplied arguments or rewrite declarations. */
3
+ export declare function resolveDeclaredInputs(declarations?: readonly ExecutableInput[], arguments_?: Record<string, unknown>): {
4
+ inputs: {
5
+ [x: string]: unknown;
6
+ };
7
+ issues: {
8
+ code: string;
9
+ message: string;
10
+ }[];
11
+ };
@@ -0,0 +1,32 @@
1
+ /** Shared invocation semantics. Never coerce supplied arguments or rewrite declarations. */
2
+ export function resolveDeclaredInputs(declarations = [], arguments_ = {}) {
3
+ const inputs = { ...arguments_ };
4
+ const issues = [];
5
+ if (!Array.isArray(declarations))
6
+ return { inputs, issues: [{ code: 'INVALID_INPUT', message: 'Declared inputs must be an array.' }] };
7
+ const seen = new Set();
8
+ for (const declaration of declarations) {
9
+ if (!declaration || typeof declaration.name !== 'string' || !declaration.name || seen.has(declaration.name) || !['string', 'number', 'boolean', 'json'].includes(declaration.type) || typeof declaration.required !== 'boolean') {
10
+ issues.push({ code: 'INVALID_INPUT', message: 'Invalid or duplicate declared input.' });
11
+ continue;
12
+ }
13
+ seen.add(declaration.name);
14
+ const supplied = Object.hasOwn(arguments_, declaration.name);
15
+ const value = supplied ? arguments_[declaration.name] : declaration.defaultValue;
16
+ if (value === undefined) {
17
+ delete inputs[declaration.name];
18
+ if (declaration.required)
19
+ issues.push({ code: 'MISSING_INPUT', message: `Required input ${declaration.name} is missing.` });
20
+ continue;
21
+ }
22
+ const validType = declaration.type === 'number' ? typeof value === 'number' && Number.isFinite(value)
23
+ : declaration.type === 'json' ? typeof value === 'object' && value !== null
24
+ : typeof value === declaration.type;
25
+ if (!validType || (declaration.required && value === '')) {
26
+ issues.push({ code: 'INVALID_INPUT', message: `Input ${declaration.name}: expected ${declaration.type}${declaration.required ? ' (required)' : ''}.` });
27
+ continue;
28
+ }
29
+ inputs[declaration.name] = value;
30
+ }
31
+ return { inputs, issues };
32
+ }
package/dist/executor.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { resolveDeclaredInputs } from './declared-inputs.js';
1
2
  const MAX_TIMEOUT_MS = 300_000;
2
3
  export function createExecutor(configuration = {}) {
3
4
  return new DefaultExecutor(configuration);
@@ -180,7 +181,9 @@ export class DefaultExecutor {
180
181
  timeout = setTimeout(() => { run.timedOut = true; run.abort.abort(); }, timeoutMs);
181
182
  this.emit(run, 'run_started', 'created', 'Execution accepted.');
182
183
  this.emit(run, 'preflight_started', 'preflighting', 'Validating execution package.');
183
- const issues = preflight(run.request.executionPackage, run.request.inputs, run.request.runtimeValues ?? {});
184
+ const arguments_ = resolveDeclaredInputs(run.request.executionPackage.inputDeclarations, run.request.inputs);
185
+ run.request = { ...run.request, inputs: arguments_.inputs };
186
+ const issues = [...arguments_.issues, ...preflight(run.request.executionPackage, arguments_.inputs, run.request.runtimeValues ?? {})];
184
187
  if (issues.length > 0 || run.request.options?.dryRun) {
185
188
  const result = this.baseResult(run, startedAt, issues.length > 0 ? 'failed' : 'completed', undefined, issues);
186
189
  if (timeout)
@@ -211,7 +214,11 @@ export class DefaultExecutor {
211
214
  for (const flow of pkg.flows ?? []) {
212
215
  if (run.cancelled || run.abort.signal.aborted)
213
216
  throw new Error('Execution cancelled.');
214
- const child = await this.executeFlow(run, flow.steps, inputs, [flow.id], startedAt, { kind: 'flow', id: flow.id, name: flow.name });
217
+ const arguments_ = resolveDeclaredInputs(flow.inputDeclarations, inputs);
218
+ const issues = [...arguments_.issues, ...preflight({ ...pkg, target: { kind: 'flow', id: flow.id, name: flow.name }, requiredInputs: flow.requiredInputs }, arguments_.inputs, run.request.runtimeValues ?? {})];
219
+ if (issues.length)
220
+ return this.baseResult(run, startedAt, 'failed', undefined, issues, children);
221
+ const child = await this.executeFlow(run, flow.steps, arguments_.inputs, [flow.id], startedAt, { kind: 'flow', id: flow.id, name: flow.name });
215
222
  children.push(child);
216
223
  if (child.state !== 'completed')
217
224
  return this.baseResult(run, startedAt, child.state, child.error, undefined, children);
@@ -434,8 +441,9 @@ function preflight(pkg, inputs, runtimeValues) {
434
441
  const issues = [];
435
442
  if (!pkg.packageId || !pkg.target?.id)
436
443
  issues.push({ code: 'INVALID_PACKAGE', message: 'Execution package identity is incomplete.' });
444
+ const flowInputNames = new Set(pkg.target.kind === 'project' ? (pkg.flows ?? []).flatMap(flow => Array.isArray(flow.inputDeclarations) ? flow.inputDeclarations.flatMap(input => typeof input?.name === 'string' ? [input.name] : []) : []) : []);
437
445
  for (const name of pkg.requiredInputs ?? [])
438
- if (inputs[name] === undefined)
446
+ if (inputs[name] === undefined && !flowInputNames.has(name))
439
447
  issues.push({ code: 'MISSING_INPUT', message: `Required input '${name}' is missing.` });
440
448
  for (const name of pkg.runtimeRequirements ?? [])
441
449
  if (runtimeValues[name] === undefined)
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export * from './types.js';
2
2
  export * from './executor.js';
3
3
  export * from './in-memory-action-executor.js';
4
4
  export * from './migrated-executor.js';
5
+ export { resolveDeclaredInputs } from './declared-inputs.js';
package/dist/index.js CHANGED
@@ -2,3 +2,4 @@ export * from './types.js';
2
2
  export * from './executor.js';
3
3
  export * from './in-memory-action-executor.js';
4
4
  export * from './migrated-executor.js';
5
+ export { resolveDeclaredInputs } from './declared-inputs.js';
@@ -90,7 +90,7 @@ export class MigratedProjectRunnerEngine {
90
90
  }
91
91
  const childRequestId = `${request.requestId}:${flow.id}`;
92
92
  run.activeRequestId = childRequestId;
93
- const childPackage = { ...request.executionPackage, target: { kind: 'flow', id: flow.id, name: flow.name }, steps: flow.steps, flows: undefined, requiredInputs: flow.requiredInputs };
93
+ const childPackage = { ...request.executionPackage, target: { kind: 'flow', id: flow.id, name: flow.name }, steps: flow.steps, flows: undefined, requiredInputs: flow.requiredInputs, inputDeclarations: flow.inputDeclarations };
94
94
  await this.flowEngine.submit({ requestId: childRequestId, executionPackage: childPackage, inputs, environment: request.environment, runtimeValues: request.runtimeValues, options: request.options, debugSession: request.debugSession });
95
95
  for await (const event of this.flowEngine.events(childRequestId))
96
96
  this.emit(run, event.type, event.state, `[${flow.name}] ${event.message}`, event.stepId ? { stepId: event.stepId, stepName: event.stepName } : undefined, event.parallel, event.control);
@@ -1,3 +1,4 @@
1
+ import { resolveDeclaredInputs } from './declared-inputs.js';
1
2
  import { InMemoryActionAdapter } from './migrated-adapter.js';
2
3
  import { hashExecutionPackage } from './migrated-artifact-hash.js';
3
4
  import { RUNNER_PACKAGE_SCHEMA_VERSION } from './migrated-types.js';
@@ -119,7 +120,7 @@ export class MigratedRunnerEngine {
119
120
  return new Promise(resolve => { const previous = run.resultResolve; run.resultResolve = result => { previous(result); resolve(result); }; });
120
121
  }
121
122
  async execute(run, resultPromise) {
122
- const { request } = run;
123
+ let { request } = run;
123
124
  const startedAt = new Date().toISOString();
124
125
  run.controlState = 'running';
125
126
  this.emit(run, 'run_started', 'created', 'Run accepted.');
@@ -133,7 +134,10 @@ export class MigratedRunnerEngine {
133
134
  await resultPromise;
134
135
  return;
135
136
  }
136
- const validationIssues = this.validateAll(request.executionPackage, request.inputs, request.runtimeValues ?? {});
137
+ const arguments_ = resolveDeclaredInputs(request.executionPackage.inputDeclarations, request.inputs);
138
+ run.request = { ...request, inputs: arguments_.inputs };
139
+ request = run.request;
140
+ const validationIssues = [...arguments_.issues, ...this.validateAll(request.executionPackage, arguments_.inputs, request.runtimeValues ?? {})];
137
141
  const validationWarnings = this.validateBrowserWarnings(request.executionPackage);
138
142
  if (request.options?.dryRun) {
139
143
  const dryRunResult = { requestId: request.requestId, state: validationIssues.length ? 'failed' : 'completed', target: request.executionPackage.target, environment: request.environment, startedAt, finishedAt: new Date().toISOString(), steps: [], terminalReason: validationIssues.length ? 'failed' : 'completed', issues: validationIssues, warnings: validationWarnings, ...(validationIssues.length ? { error: { code: 'DRY_RUN_ISSUES', message: `${validationIssues.length} issue${validationIssues.length === 1 ? '' : 's'} found during dry run.` } } : {}) };
@@ -1,7 +1,7 @@
1
1
  /** Compatibility import for Runner modules. The canonical contract is the published @qaick/contract package. */
2
2
  export * from '@qaick/contract';
3
3
  /** The exact published contract package consumed by this Runner release. */
4
- export declare const RUNNER_CONTRACT_VERSION: "0.2.10";
4
+ export declare const RUNNER_CONTRACT_VERSION: "0.2.12";
5
5
  export type ExecutionControlState = 'idle' | 'running' | 'pause-requested' | 'paused' | 'resuming' | 'stopped' | 'failed' | 'completed';
6
6
  export interface RunnerDebugSession {
7
7
  breakpoints: string[];
@@ -1,4 +1,4 @@
1
1
  /** Compatibility import for Runner modules. The canonical contract is the published @qaick/contract package. */
2
2
  export * from '@qaick/contract';
3
3
  /** The exact published contract package consumed by this Runner release. */
4
- export const RUNNER_CONTRACT_VERSION = '0.2.10';
4
+ export const RUNNER_CONTRACT_VERSION = '0.2.12';
package/package.json CHANGED
@@ -1,10 +1,18 @@
1
1
  {
2
2
  "name": "@qaick/executor",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "Host-neutral QAick action execution primitives.",
5
- "files": ["dist", "README.md"],
6
- "engines": { "node": ">=20" },
7
- "publishConfig": { "access": "public", "tag": "alpha" },
5
+ "files": [
6
+ "dist",
7
+ "README.md"
8
+ ],
9
+ "engines": {
10
+ "node": ">=20"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public",
14
+ "tag": "alpha"
15
+ },
8
16
  "repository": {
9
17
  "type": "git",
10
18
  "url": "git+https://github.com/vladmus/QAICK_Executor.git"
@@ -26,7 +34,7 @@
26
34
  "prepublishOnly": "npm run typecheck && npm test && npm run build"
27
35
  },
28
36
  "dependencies": {
29
- "@qaick/contract": "0.2.11"
37
+ "@qaick/contract": "0.2.12"
30
38
  },
31
39
  "devDependencies": {
32
40
  "@types/node": "^24.13.3",