@midscene/test 1.12.4-beta-20260903111111.0 → 1.12.4
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.
- package/dist/es/android/index.mjs +50 -106
- package/dist/es/android/index.mjs.map +1 -1
- package/dist/es/cli/index.mjs +99 -149
- package/dist/es/cli/index.mjs.map +1 -1
- package/dist/es/harmony/index.mjs +119 -0
- package/dist/es/harmony/index.mjs.map +1 -0
- package/dist/es/index.mjs +36 -87
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/ios/index.mjs +51 -101
- package/dist/es/ios/index.mjs.map +1 -1
- package/dist/es/midscene/index.mjs +37 -235
- package/dist/es/midscene/index.mjs.map +1 -1
- package/dist/es/playwright/index.mjs +14 -10
- package/dist/es/playwright/index.mjs.map +1 -1
- package/dist/es/test-cli.mjs +102 -150
- package/dist/es/test-cli.mjs.map +1 -1
- package/dist/lib/android/index.js +151 -178
- package/dist/lib/android/index.js.map +1 -1
- package/dist/lib/cli/index.js +99 -149
- package/dist/lib/cli/index.js.map +1 -1
- package/dist/lib/harmony/index.js +188 -0
- package/dist/lib/harmony/index.js.map +1 -0
- package/dist/lib/index.js +37 -100
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/ios/index.js +152 -173
- package/dist/lib/ios/index.js.map +1 -1
- package/dist/lib/midscene/index.js +264 -412
- package/dist/lib/midscene/index.js.map +1 -1
- package/dist/lib/playwright/index.js +14 -10
- package/dist/lib/playwright/index.js.map +1 -1
- package/dist/lib/test-cli.js +102 -150
- package/dist/lib/test-cli.js.map +1 -1
- package/dist/types/android/index.d.ts +27 -101
- package/dist/types/cli/index.d.ts +9 -32
- package/dist/types/harmony/index.d.ts +124 -0
- package/dist/types/index.d.ts +18 -45
- package/dist/types/ios/index.d.ts +28 -107
- package/dist/types/midscene/index.d.ts +54 -153
- package/dist/types/playwright/index.d.ts +9 -63
- package/package.json +25 -6
package/dist/es/cli/index.mjs
CHANGED
|
@@ -204,7 +204,15 @@ const validateDefinition = (options)=>{
|
|
|
204
204
|
if ('string' != typeof options.name || 0 === options.name.trim().length) throw new NodeDefinitionError('Node name must be a non-empty string.');
|
|
205
205
|
validateOptionalText(options.title, 'title', options.name);
|
|
206
206
|
validateOptionalText(options.description, "description", options.name);
|
|
207
|
+
if (void 0 !== options.stringInputKey && false !== options.stringInputKey && ('string' != typeof options.stringInputKey || 0 === options.stringInputKey.trim().length)) throw new NodeDefinitionError(`Node "${options.name}" stringInputKey must be a non-empty string or false.`, {
|
|
208
|
+
node: options.name,
|
|
209
|
+
field: 'stringInputKey'
|
|
210
|
+
});
|
|
207
211
|
validateInputSchema(options.inputSchema, options.name);
|
|
212
|
+
if ('string' == typeof options.stringInputKey && options.inputSchema instanceof z.ZodObject && !(options.stringInputKey in options.inputSchema.shape)) throw new NodeDefinitionError(`Node "${options.name}" stringInputKey must reference a field in inputSchema.`, {
|
|
213
|
+
node: options.name,
|
|
214
|
+
field: 'stringInputKey'
|
|
215
|
+
});
|
|
208
216
|
if ('function' != typeof options.execute) throw new NodeDefinitionError(`Node "${options.name}" must provide an execute function.`, {
|
|
209
217
|
node: options.name
|
|
210
218
|
});
|
|
@@ -367,10 +375,11 @@ const platforms = new Set([
|
|
|
367
375
|
'web',
|
|
368
376
|
'android',
|
|
369
377
|
'ios',
|
|
378
|
+
'harmony',
|
|
370
379
|
'computer'
|
|
371
380
|
]);
|
|
372
381
|
const validatePlatform = (value, label)=>{
|
|
373
|
-
if ('string' != typeof value || !platforms.has(value)) throw new TypeError(`Midscene config ${label} must be one of web, android, ios, computer.`);
|
|
382
|
+
if ('string' != typeof value || !platforms.has(value)) throw new TypeError(`Midscene config ${label} must be one of web, android, ios, harmony, computer.`);
|
|
374
383
|
return value;
|
|
375
384
|
};
|
|
376
385
|
const validateProjectSetup = (value, projectPlatform, label)=>{
|
|
@@ -661,88 +670,6 @@ const createProjectRuntime = (options)=>{
|
|
|
661
670
|
}
|
|
662
671
|
};
|
|
663
672
|
};
|
|
664
|
-
const supportedMetaKeys = new Set([
|
|
665
|
-
'timeout',
|
|
666
|
-
'continue-on-error'
|
|
667
|
-
]);
|
|
668
|
-
function isMapping(value) {
|
|
669
|
-
return 'object' == typeof value && null !== value && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || null === Object.getPrototypeOf(value));
|
|
670
|
-
}
|
|
671
|
-
function formatStep(index) {
|
|
672
|
-
return `step ${index + 1}`;
|
|
673
|
-
}
|
|
674
|
-
function normalizeMeta(value, index) {
|
|
675
|
-
if (void 0 === value) return {
|
|
676
|
-
continueOnError: false
|
|
677
|
-
};
|
|
678
|
-
if (!isMapping(value)) throw new WorkflowParseError(`${formatStep(index)} "$" must be a mapping.`, {
|
|
679
|
-
index
|
|
680
|
-
});
|
|
681
|
-
for (const key of Object.keys(value))if (!supportedMetaKeys.has(key)) throw new WorkflowParseError(`${formatStep(index)} has unsupported engine metadata "${key}".`, {
|
|
682
|
-
index,
|
|
683
|
-
key
|
|
684
|
-
});
|
|
685
|
-
const timeout = value.timeout;
|
|
686
|
-
if (void 0 !== timeout && ('number' != typeof timeout || !Number.isFinite(timeout) || timeout <= 0)) throw new WorkflowParseError(`${formatStep(index)} "$.timeout" must be a positive number of milliseconds.`, {
|
|
687
|
-
index,
|
|
688
|
-
timeout
|
|
689
|
-
});
|
|
690
|
-
const continueOnError = value['continue-on-error'];
|
|
691
|
-
if (void 0 !== continueOnError && 'boolean' != typeof continueOnError) throw new WorkflowParseError(`${formatStep(index)} "$.continue-on-error" must be a boolean.`, {
|
|
692
|
-
index,
|
|
693
|
-
continueOnError
|
|
694
|
-
});
|
|
695
|
-
return {
|
|
696
|
-
...void 0 === timeout ? {} : {
|
|
697
|
-
timeoutMs: timeout
|
|
698
|
-
},
|
|
699
|
-
continueOnError: continueOnError ?? false
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
function validateCommonNodeInput(input, index) {
|
|
703
|
-
if (void 0 !== input.prompt && 'string' != typeof input.prompt) throw new WorkflowParseError(`${formatStep(index)} "prompt" must be a string.`, {
|
|
704
|
-
index,
|
|
705
|
-
prompt: input.prompt
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
function normalizeStep(value, index = 0) {
|
|
709
|
-
if (!isMapping(value)) throw new WorkflowParseError(`${formatStep(index)} must be a mapping.`, {
|
|
710
|
-
index
|
|
711
|
-
});
|
|
712
|
-
const entries = Object.entries(value);
|
|
713
|
-
if (1 !== entries.length) throw new WorkflowParseError(`${formatStep(index)} must contain exactly one node.`, {
|
|
714
|
-
index,
|
|
715
|
-
nodes: Object.keys(value)
|
|
716
|
-
});
|
|
717
|
-
const [node, rawValue] = entries[0];
|
|
718
|
-
if (0 === node.trim().length) throw new WorkflowParseError(`${formatStep(index)} node name must not be empty.`, {
|
|
719
|
-
index
|
|
720
|
-
});
|
|
721
|
-
if ('string' == typeof rawValue) return {
|
|
722
|
-
node,
|
|
723
|
-
input: {
|
|
724
|
-
prompt: rawValue
|
|
725
|
-
},
|
|
726
|
-
meta: {
|
|
727
|
-
continueOnError: false
|
|
728
|
-
}
|
|
729
|
-
};
|
|
730
|
-
if (!isMapping(rawValue)) throw new WorkflowParseError(`${formatStep(index)} value must be a string shorthand or a mapping.`, {
|
|
731
|
-
index,
|
|
732
|
-
node
|
|
733
|
-
});
|
|
734
|
-
const { $: rawMeta, ...input } = rawValue;
|
|
735
|
-
validateCommonNodeInput(input, index);
|
|
736
|
-
return {
|
|
737
|
-
node,
|
|
738
|
-
input,
|
|
739
|
-
meta: normalizeMeta(rawMeta, index)
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
function normalizeSteps(steps) {
|
|
743
|
-
if (!Array.isArray(steps)) throw new WorkflowParseError('Steps must be an array.');
|
|
744
|
-
return steps.map((step, index)=>normalizeStep(step, index));
|
|
745
|
-
}
|
|
746
673
|
const assertJsonSerializable = (value, label, node)=>{
|
|
747
674
|
const visit = (candidate, path, seen)=>{
|
|
748
675
|
if (null === candidate || 'string' == typeof candidate || 'boolean' == typeof candidate) return;
|
|
@@ -765,45 +692,6 @@ const assertJsonSerializable = (value, label, node)=>{
|
|
|
765
692
|
throw new NodeExecutionError(node, error);
|
|
766
693
|
}
|
|
767
694
|
};
|
|
768
|
-
const cloneAndFreezeJson = (value)=>{
|
|
769
|
-
if (Array.isArray(value)) return Object.freeze(value.map(cloneAndFreezeJson));
|
|
770
|
-
if ('object' == typeof value && null !== value) return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, child])=>[
|
|
771
|
-
key,
|
|
772
|
-
cloneAndFreezeJson(child)
|
|
773
|
-
])));
|
|
774
|
-
return value;
|
|
775
|
-
};
|
|
776
|
-
const toHistoryEntry = (result, scope)=>{
|
|
777
|
-
assertJsonSerializable(result.input, 'input', result.node);
|
|
778
|
-
if (result.output?.data !== void 0) assertJsonSerializable(result.output.data, 'output.data', result.node);
|
|
779
|
-
return Object.freeze({
|
|
780
|
-
scope,
|
|
781
|
-
phase: result.phase,
|
|
782
|
-
stepIndex: result.stepIndex,
|
|
783
|
-
node: result.node,
|
|
784
|
-
input: cloneAndFreezeJson(result.input),
|
|
785
|
-
...'string' == typeof result.input?.prompt ? {
|
|
786
|
-
intent: result.input.prompt
|
|
787
|
-
} : {},
|
|
788
|
-
status: 'success' === result.status ? 'passed' : 'failed',
|
|
789
|
-
...result.output?.summary === void 0 ? {} : {
|
|
790
|
-
summary: result.output.summary
|
|
791
|
-
},
|
|
792
|
-
...result.output?.data === void 0 ? {} : {
|
|
793
|
-
data: cloneAndFreezeJson(result.output.data)
|
|
794
|
-
},
|
|
795
|
-
...result.error ? {
|
|
796
|
-
error: Object.freeze({
|
|
797
|
-
name: result.error.name,
|
|
798
|
-
message: result.error.message
|
|
799
|
-
})
|
|
800
|
-
} : {}
|
|
801
|
-
});
|
|
802
|
-
};
|
|
803
|
-
const createHistory = (prefix, results, scope)=>Object.freeze([
|
|
804
|
-
...prefix,
|
|
805
|
-
...results.map((result)=>toHistoryEntry(result, scope))
|
|
806
|
-
]);
|
|
807
695
|
function validateNodeOutput(output, node) {
|
|
808
696
|
if (void 0 === output) return;
|
|
809
697
|
if ('object' != typeof output || null === output || Array.isArray(output)) throw new NodeExecutionError(node, new TypeError('Node output must be an object or undefined.'));
|
|
@@ -825,7 +713,6 @@ function createStepResultBase(step, startedAt, phase, stepIndex) {
|
|
|
825
713
|
};
|
|
826
714
|
}
|
|
827
715
|
async function executeNode(step, execute, phase, stepIndex, options) {
|
|
828
|
-
validateCommonNodeInput(step.input, stepIndex);
|
|
829
716
|
const startedAt = new Date();
|
|
830
717
|
const abortController = new AbortController();
|
|
831
718
|
const parentSignal = options.parentSignal;
|
|
@@ -909,7 +796,6 @@ async function executeStep(step, node, target, context, execution = {}) {
|
|
|
909
796
|
$: step.meta,
|
|
910
797
|
signal,
|
|
911
798
|
context,
|
|
912
|
-
history: execution.history ?? Object.freeze([]),
|
|
913
799
|
onTeardown: (teardown)=>{
|
|
914
800
|
if (!execution.onTeardown) throw new NodeExecutionError(step.node, new Error('The current execution scope cannot register teardown.'));
|
|
915
801
|
execution.onTeardown(step.node, teardown);
|
|
@@ -946,7 +832,6 @@ function createDocumentRuntime(document, options) {
|
|
|
946
832
|
const startedAt = new Date();
|
|
947
833
|
const beforeAll = [];
|
|
948
834
|
const afterAll = [];
|
|
949
|
-
const completedNodes = [];
|
|
950
835
|
const nodes = {
|
|
951
836
|
beforeAll: document.lifecycle.beforeAll.map((step)=>options.resolveNode(step.node)),
|
|
952
837
|
afterAll: document.lifecycle.afterAll.map((step)=>options.resolveNode(step.node))
|
|
@@ -977,7 +862,7 @@ function createDocumentRuntime(document, options) {
|
|
|
977
862
|
projectId: document.projectId,
|
|
978
863
|
projectName: project.name,
|
|
979
864
|
sourcePath: document.sourcePath,
|
|
980
|
-
status:
|
|
865
|
+
status: beforeAll.some((step)=>'failed' === step.status) || afterAll.some((step)=>'failed' === step.status) || teardownErrors.length > 0 ? 'failed' : 'success',
|
|
981
866
|
startedAt: startedAt.toISOString(),
|
|
982
867
|
endedAt: endedAt.toISOString(),
|
|
983
868
|
durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),
|
|
@@ -1006,10 +891,7 @@ function createDocumentRuntime(document, options) {
|
|
|
1006
891
|
projectName: project.name,
|
|
1007
892
|
sourcePath: document.sourcePath,
|
|
1008
893
|
phase,
|
|
1009
|
-
stepIndex
|
|
1010
|
-
completedNodes: Object.freeze([
|
|
1011
|
-
...completedNodes
|
|
1012
|
-
])
|
|
894
|
+
stepIndex
|
|
1013
895
|
};
|
|
1014
896
|
const stepInfo = {
|
|
1015
897
|
scope: 'document',
|
|
@@ -1022,7 +904,6 @@ function createDocumentRuntime(document, options) {
|
|
|
1022
904
|
scope: 'document',
|
|
1023
905
|
document: documentContext
|
|
1024
906
|
}, context, {
|
|
1025
|
-
history: createHistory([], completedNodes, 'document'),
|
|
1026
907
|
signal: executionSignal,
|
|
1027
908
|
defaultTimeoutMs: options.defaultTimeoutMs,
|
|
1028
909
|
onTeardown (node, teardown) {
|
|
@@ -1045,7 +926,6 @@ function createDocumentRuntime(document, options) {
|
|
|
1045
926
|
});
|
|
1046
927
|
await options.onStepResult?.(stepInfo, result);
|
|
1047
928
|
results.push(result);
|
|
1048
|
-
completedNodes.push(result);
|
|
1049
929
|
if ('failed' === result.status && !result.continuedAfterError) break;
|
|
1050
930
|
}
|
|
1051
931
|
};
|
|
@@ -1053,9 +933,6 @@ function createDocumentRuntime(document, options) {
|
|
|
1053
933
|
get context () {
|
|
1054
934
|
return context;
|
|
1055
935
|
},
|
|
1056
|
-
get history () {
|
|
1057
|
-
return createHistory([], completedNodes, 'document');
|
|
1058
|
-
},
|
|
1059
936
|
get canRunCases () {
|
|
1060
937
|
return started && !signal.aborted && !beforeAll.some((step)=>'failed' === step.status);
|
|
1061
938
|
},
|
|
@@ -1115,7 +992,6 @@ async function runCollectedCase(collectedCase, options) {
|
|
|
1115
992
|
const beforeEach = [];
|
|
1116
993
|
const steps = [];
|
|
1117
994
|
const afterEach = [];
|
|
1118
|
-
const completedNodes = [];
|
|
1119
995
|
const teardownStack = [];
|
|
1120
996
|
let acceptingTeardowns = true;
|
|
1121
997
|
const onTeardown = (node, teardown)=>{
|
|
@@ -1144,13 +1020,7 @@ async function runCollectedCase(collectedCase, options) {
|
|
|
1144
1020
|
sourcePath: collectedCase.sourcePath,
|
|
1145
1021
|
caseIndex: collectedCase.caseIndex,
|
|
1146
1022
|
phase,
|
|
1147
|
-
stepIndex
|
|
1148
|
-
completedSteps: Object.freeze([
|
|
1149
|
-
...steps
|
|
1150
|
-
]),
|
|
1151
|
-
completedNodes: Object.freeze([
|
|
1152
|
-
...completedNodes
|
|
1153
|
-
])
|
|
1023
|
+
stepIndex
|
|
1154
1024
|
};
|
|
1155
1025
|
const stepInfo = {
|
|
1156
1026
|
scope: 'case',
|
|
@@ -1163,14 +1033,12 @@ async function runCollectedCase(collectedCase, options) {
|
|
|
1163
1033
|
scope: 'case',
|
|
1164
1034
|
case: caseContext
|
|
1165
1035
|
}, options.context, {
|
|
1166
|
-
history: createHistory(options.documentHistory ?? [], completedNodes, 'case'),
|
|
1167
1036
|
signal,
|
|
1168
1037
|
defaultTimeoutMs: options.defaultTimeoutMs,
|
|
1169
1038
|
onTeardown
|
|
1170
1039
|
});
|
|
1171
1040
|
await options.onStepResult?.(stepInfo, result);
|
|
1172
1041
|
results.push(result);
|
|
1173
|
-
completedNodes.push(result);
|
|
1174
1042
|
if ('failed' === result.status && !result.continuedAfterError) break;
|
|
1175
1043
|
}
|
|
1176
1044
|
};
|
|
@@ -1210,7 +1078,7 @@ async function runCollectedCase(collectedCase, options) {
|
|
|
1210
1078
|
name: collectedCase.definition.name,
|
|
1211
1079
|
sourcePath: collectedCase.sourcePath,
|
|
1212
1080
|
caseIndex: collectedCase.caseIndex,
|
|
1213
|
-
status:
|
|
1081
|
+
status: beforeEach.some((step)=>'failed' === step.status) || steps.some((step)=>'failed' === step.status) || afterEach.some((step)=>'failed' === step.status) || teardownErrors.length > 0 ? 'failed' : 'success',
|
|
1214
1082
|
beforeEach,
|
|
1215
1083
|
steps,
|
|
1216
1084
|
afterEach,
|
|
@@ -1280,7 +1148,6 @@ async function runWorkflowDocument(document, options) {
|
|
|
1280
1148
|
context: runtime.context,
|
|
1281
1149
|
projectName,
|
|
1282
1150
|
attemptIndex,
|
|
1283
|
-
documentHistory: runtime.history,
|
|
1284
1151
|
signal: options.signal,
|
|
1285
1152
|
defaultTimeoutMs: options.defaultTimeoutMs,
|
|
1286
1153
|
onStepStart: options.onStepStart,
|
|
@@ -1326,6 +1193,89 @@ async function runWorkflowDocument(document, options) {
|
|
|
1326
1193
|
cases
|
|
1327
1194
|
};
|
|
1328
1195
|
}
|
|
1196
|
+
const supportedMetaKeys = new Set([
|
|
1197
|
+
'timeout',
|
|
1198
|
+
'continue-on-error'
|
|
1199
|
+
]);
|
|
1200
|
+
function isMapping(value) {
|
|
1201
|
+
return 'object' == typeof value && null !== value && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || null === Object.getPrototypeOf(value));
|
|
1202
|
+
}
|
|
1203
|
+
function formatStep(index) {
|
|
1204
|
+
return `step ${index + 1}`;
|
|
1205
|
+
}
|
|
1206
|
+
function normalizeMeta(value, index) {
|
|
1207
|
+
if (void 0 === value) return {
|
|
1208
|
+
continueOnError: false
|
|
1209
|
+
};
|
|
1210
|
+
if (!isMapping(value)) throw new WorkflowParseError(`${formatStep(index)} "$" must be a mapping.`, {
|
|
1211
|
+
index
|
|
1212
|
+
});
|
|
1213
|
+
for (const key of Object.keys(value))if (!supportedMetaKeys.has(key)) throw new WorkflowParseError(`${formatStep(index)} has unsupported engine metadata "${key}".`, {
|
|
1214
|
+
index,
|
|
1215
|
+
key
|
|
1216
|
+
});
|
|
1217
|
+
const timeout = value.timeout;
|
|
1218
|
+
if (void 0 !== timeout && ('number' != typeof timeout || !Number.isFinite(timeout) || timeout <= 0)) throw new WorkflowParseError(`${formatStep(index)} "$.timeout" must be a positive number of milliseconds.`, {
|
|
1219
|
+
index,
|
|
1220
|
+
timeout
|
|
1221
|
+
});
|
|
1222
|
+
const continueOnError = value['continue-on-error'];
|
|
1223
|
+
if (void 0 !== continueOnError && 'boolean' != typeof continueOnError) throw new WorkflowParseError(`${formatStep(index)} "$.continue-on-error" must be a boolean.`, {
|
|
1224
|
+
index,
|
|
1225
|
+
continueOnError
|
|
1226
|
+
});
|
|
1227
|
+
return {
|
|
1228
|
+
...void 0 === timeout ? {} : {
|
|
1229
|
+
timeoutMs: timeout
|
|
1230
|
+
},
|
|
1231
|
+
continueOnError: continueOnError ?? false
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function normalizeStep(value, index = 0, resolveNode) {
|
|
1235
|
+
if (!isMapping(value)) throw new WorkflowParseError(`${formatStep(index)} must be a mapping.`, {
|
|
1236
|
+
index
|
|
1237
|
+
});
|
|
1238
|
+
const entries = Object.entries(value);
|
|
1239
|
+
if (1 !== entries.length) throw new WorkflowParseError(`${formatStep(index)} must contain exactly one node.`, {
|
|
1240
|
+
index,
|
|
1241
|
+
nodes: Object.keys(value)
|
|
1242
|
+
});
|
|
1243
|
+
const [node, rawValue] = entries[0];
|
|
1244
|
+
if (0 === node.trim().length) throw new WorkflowParseError(`${formatStep(index)} node name must not be empty.`, {
|
|
1245
|
+
index
|
|
1246
|
+
});
|
|
1247
|
+
if ('string' == typeof rawValue) {
|
|
1248
|
+
const resolvedNode = resolveNode?.(node);
|
|
1249
|
+
const stringInputKey = resolvedNode ? resolvedNode.stringInputKey : 'prompt';
|
|
1250
|
+
if (false === stringInputKey || void 0 === stringInputKey) throw new WorkflowParseError(`${formatStep(index)} node "${node}" does not accept string shorthand.`, {
|
|
1251
|
+
index,
|
|
1252
|
+
node
|
|
1253
|
+
});
|
|
1254
|
+
return {
|
|
1255
|
+
node,
|
|
1256
|
+
input: {
|
|
1257
|
+
[stringInputKey]: rawValue
|
|
1258
|
+
},
|
|
1259
|
+
meta: {
|
|
1260
|
+
continueOnError: false
|
|
1261
|
+
}
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
if (!isMapping(rawValue)) throw new WorkflowParseError(`${formatStep(index)} value must be a string shorthand or a mapping.`, {
|
|
1265
|
+
index,
|
|
1266
|
+
node
|
|
1267
|
+
});
|
|
1268
|
+
const { $: rawMeta, ...input } = rawValue;
|
|
1269
|
+
return {
|
|
1270
|
+
node,
|
|
1271
|
+
input,
|
|
1272
|
+
meta: normalizeMeta(rawMeta, index)
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
function normalizeSteps(steps, resolveNode) {
|
|
1276
|
+
if (!Array.isArray(steps)) throw new WorkflowParseError('Steps must be an array.');
|
|
1277
|
+
return steps.map((step, index)=>normalizeStep(step, index, resolveNode));
|
|
1278
|
+
}
|
|
1329
1279
|
const interpolationExpression = /\$\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}|\$\{([^{}]+)\}/g;
|
|
1330
1280
|
const exactEnvExpression = /^\$\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}$/;
|
|
1331
1281
|
const exactProjectExpression = /^\$\{([^{}]+)\}$/;
|
|
@@ -1429,7 +1379,7 @@ function collectWorkflowDocument(source, options) {
|
|
|
1429
1379
|
if (!Array.isArray(value)) throw new WorkflowParseError(`Workflow document ${phase} must be an array.`, {
|
|
1430
1380
|
phase
|
|
1431
1381
|
});
|
|
1432
|
-
return normalizeSteps(value).map((normalized, stepIndex)=>{
|
|
1382
|
+
return normalizeSteps(value, options.resolveNode).map((normalized, stepIndex)=>{
|
|
1433
1383
|
const resolved = options.resolveNode(normalized.node);
|
|
1434
1384
|
if (!resolved) throw new WorkflowParseError(`Workflow document ${phase} step ${stepIndex + 1} references unknown node "${normalized.node}".`, {
|
|
1435
1385
|
phase,
|
|
@@ -1465,7 +1415,7 @@ function collectWorkflowDocument(source, options) {
|
|
|
1465
1415
|
if (!Array.isArray(tags) || tags.some((tag)=>'string' != typeof tag || 0 === tag.trim().length)) throw new WorkflowParseError(`Case ${caseIndex + 1} tags must be an array of non-empty strings.`, {
|
|
1466
1416
|
caseIndex
|
|
1467
1417
|
});
|
|
1468
|
-
const steps = normalizeSteps(definition.steps).map((normalized, stepIndex)=>{
|
|
1418
|
+
const steps = normalizeSteps(definition.steps, options.resolveNode).map((normalized, stepIndex)=>{
|
|
1469
1419
|
if (!options.resolveNode(normalized.node)) throw new WorkflowParseError(`Case ${caseIndex + 1} step ${stepIndex + 1} references unknown node "${normalized.node}".`, {
|
|
1470
1420
|
caseIndex,
|
|
1471
1421
|
stepIndex,
|