@haibun/core 4.0.0-alpha.1 → 4.0.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.
Files changed (297) hide show
  1. package/build/currentVersion.d.ts +1 -1
  2. package/build/currentVersion.js +1 -1
  3. package/build/index.d.ts +1 -1
  4. package/build/index.d.ts.map +1 -1
  5. package/build/index.js +1 -1
  6. package/build/index.js.map +1 -1
  7. package/build/kireji/converter.d.ts +2 -2
  8. package/build/kireji/converter.d.ts.map +1 -1
  9. package/build/kireji/converter.js +12 -9
  10. package/build/kireji/converter.js.map +1 -1
  11. package/build/kireji/withAction.d.ts +16 -4
  12. package/build/kireji/withAction.d.ts.map +1 -1
  13. package/build/kireji/withAction.js +40 -11
  14. package/build/kireji/withAction.js.map +1 -1
  15. package/build/lib/EventLogger.d.ts +5 -5
  16. package/build/lib/EventLogger.d.ts.map +1 -1
  17. package/build/lib/EventLogger.js +39 -37
  18. package/build/lib/EventLogger.js.map +1 -1
  19. package/build/lib/PhaseRunner.d.ts +3 -3
  20. package/build/lib/PhaseRunner.d.ts.map +1 -1
  21. package/build/lib/PhaseRunner.js +7 -5
  22. package/build/lib/PhaseRunner.js.map +1 -1
  23. package/build/lib/astepper.d.ts +90 -13
  24. package/build/lib/astepper.d.ts.map +1 -1
  25. package/build/lib/astepper.js +35 -6
  26. package/build/lib/astepper.js.map +1 -1
  27. package/build/lib/base-prompt-manager.d.ts +1 -1
  28. package/build/lib/base-prompt-manager.d.ts.map +1 -1
  29. package/build/lib/base-prompt-manager.js.map +1 -1
  30. package/build/lib/capture-locator.d.ts +1 -1
  31. package/build/lib/capture-locator.d.ts.map +1 -1
  32. package/build/lib/capture-locator.js +5 -5
  33. package/build/lib/capture-locator.js.map +1 -1
  34. package/build/lib/core/flow-runner.d.ts +8 -7
  35. package/build/lib/core/flow-runner.d.ts.map +1 -1
  36. package/build/lib/core/flow-runner.js +42 -71
  37. package/build/lib/core/flow-runner.js.map +1 -1
  38. package/build/lib/core-domains.d.ts +2 -2
  39. package/build/lib/core-domains.d.ts.map +1 -1
  40. package/build/lib/core-domains.js +17 -18
  41. package/build/lib/core-domains.js.map +1 -1
  42. package/build/lib/defs.d.ts +2 -13
  43. package/build/lib/defs.d.ts.map +1 -1
  44. package/build/lib/defs.js.map +1 -1
  45. package/build/lib/document-content.d.ts +18 -0
  46. package/build/lib/document-content.d.ts.map +1 -0
  47. package/build/lib/document-content.js +158 -0
  48. package/build/lib/document-content.js.map +1 -0
  49. package/build/lib/domains.d.ts +31 -0
  50. package/build/lib/domains.d.ts.map +1 -0
  51. package/build/lib/domains.js +105 -0
  52. package/build/lib/domains.js.map +1 -0
  53. package/build/lib/execution.d.ts +304 -0
  54. package/build/lib/execution.d.ts.map +1 -0
  55. package/build/lib/execution.js +23 -0
  56. package/build/lib/execution.js.map +1 -0
  57. package/build/lib/feature-variables.d.ts +22 -49
  58. package/build/lib/feature-variables.d.ts.map +1 -1
  59. package/build/lib/feature-variables.js +143 -210
  60. package/build/lib/feature-variables.js.map +1 -1
  61. package/build/lib/features.d.ts +2 -2
  62. package/build/lib/features.d.ts.map +1 -1
  63. package/build/lib/features.js +13 -17
  64. package/build/lib/features.js.map +1 -1
  65. package/build/lib/fixme.d.ts.map +1 -1
  66. package/build/lib/host-id.d.ts +44 -0
  67. package/build/lib/host-id.d.ts.map +1 -0
  68. package/build/lib/host-id.js +51 -0
  69. package/build/lib/host-id.js.map +1 -0
  70. package/build/lib/http-observations.d.ts +15 -2
  71. package/build/lib/http-observations.d.ts.map +1 -1
  72. package/build/lib/http-observations.js +40 -23
  73. package/build/lib/http-observations.js.map +1 -1
  74. package/build/lib/hypermedia.d.ts +62 -0
  75. package/build/lib/hypermedia.d.ts.map +1 -0
  76. package/build/lib/hypermedia.js +228 -0
  77. package/build/lib/hypermedia.js.map +1 -0
  78. package/build/lib/namedVars.d.ts +2 -2
  79. package/build/lib/namedVars.d.ts.map +1 -1
  80. package/build/lib/namedVars.js +25 -30
  81. package/build/lib/namedVars.js.map +1 -1
  82. package/build/lib/node-http-events.d.ts +3 -3
  83. package/build/lib/node-http-events.d.ts.map +1 -1
  84. package/build/lib/node-http-events.js +26 -26
  85. package/build/lib/node-http-events.js.map +1 -1
  86. package/build/lib/populateActionArgs.d.ts +4 -4
  87. package/build/lib/populateActionArgs.d.ts.map +1 -1
  88. package/build/lib/populateActionArgs.js +4 -6
  89. package/build/lib/populateActionArgs.js.map +1 -1
  90. package/build/lib/prompter.d.ts.map +1 -1
  91. package/build/lib/prompter.js +10 -7
  92. package/build/lib/prompter.js.map +1 -1
  93. package/build/lib/quad-store.d.ts +34 -10
  94. package/build/lib/quad-store.d.ts.map +1 -1
  95. package/build/lib/quad-store.js +169 -21
  96. package/build/lib/quad-store.js.map +1 -1
  97. package/build/lib/quad-types.d.ts +80 -9
  98. package/build/lib/quad-types.d.ts.map +1 -1
  99. package/build/lib/quad-types.js +46 -2
  100. package/build/lib/quad-types.js.map +1 -1
  101. package/build/lib/readline-prompter.d.ts +1 -1
  102. package/build/lib/readline-prompter.js +4 -4
  103. package/build/lib/remote-stepper-proxy.d.ts +56 -0
  104. package/build/lib/remote-stepper-proxy.d.ts.map +1 -0
  105. package/build/lib/remote-stepper-proxy.js +123 -0
  106. package/build/lib/remote-stepper-proxy.js.map +1 -0
  107. package/build/lib/resolver-features.d.ts +1 -1
  108. package/build/lib/resolver-features.d.ts.map +1 -1
  109. package/build/lib/resolver-features.js +2 -2
  110. package/build/lib/resolver-features.js.map +1 -1
  111. package/build/lib/resources.d.ts +461 -0
  112. package/build/lib/resources.d.ts.map +1 -0
  113. package/build/lib/resources.js +249 -0
  114. package/build/lib/resources.js.map +1 -0
  115. package/build/lib/rpc-client.d.ts +68 -0
  116. package/build/lib/rpc-client.d.ts.map +1 -0
  117. package/build/lib/rpc-client.js +186 -0
  118. package/build/lib/rpc-client.js.map +1 -0
  119. package/build/lib/sse-subscriber.d.ts +57 -0
  120. package/build/lib/sse-subscriber.d.ts.map +1 -0
  121. package/build/lib/sse-subscriber.js +110 -0
  122. package/build/lib/sse-subscriber.js.map +1 -0
  123. package/build/lib/step-dispatch.d.ts +92 -28
  124. package/build/lib/step-dispatch.d.ts.map +1 -1
  125. package/build/lib/step-dispatch.js +331 -75
  126. package/build/lib/step-dispatch.js.map +1 -1
  127. package/build/lib/step-validation.d.ts +3 -3
  128. package/build/lib/step-validation.d.ts.map +1 -1
  129. package/build/lib/step-validation.js +2 -2
  130. package/build/lib/step-validation.js.map +1 -1
  131. package/build/lib/stepper-cycles.d.ts +5 -0
  132. package/build/lib/stepper-cycles.d.ts.map +1 -0
  133. package/build/lib/stepper-cycles.js +31 -0
  134. package/build/lib/stepper-cycles.js.map +1 -0
  135. package/build/lib/stepper-registry.d.ts +6 -3
  136. package/build/lib/stepper-registry.d.ts.map +1 -1
  137. package/build/lib/stepper-registry.js +9 -8
  138. package/build/lib/stepper-registry.js.map +1 -1
  139. package/build/lib/subprocess-runner.d.ts +35 -0
  140. package/build/lib/subprocess-runner.d.ts.map +1 -0
  141. package/build/lib/subprocess-runner.js +60 -0
  142. package/build/lib/subprocess-runner.js.map +1 -0
  143. package/build/lib/subprocess-transport.d.ts +37 -0
  144. package/build/lib/subprocess-transport.d.ts.map +1 -0
  145. package/build/lib/subprocess-transport.js +113 -0
  146. package/build/lib/subprocess-transport.js.map +1 -0
  147. package/build/lib/test/EventCollectorStepper.d.ts +3 -3
  148. package/build/lib/test/EventCollectorStepper.d.ts.map +1 -1
  149. package/build/lib/test/EventCollectorStepper.js +3 -3
  150. package/build/lib/test/EventCollectorStepper.js.map +1 -1
  151. package/build/lib/test/SetTimeStepper.d.ts +3 -3
  152. package/build/lib/test/SetTimeStepper.d.ts.map +1 -1
  153. package/build/lib/test/SetTimeStepper.js +7 -7
  154. package/build/lib/test/SetTimeStepper.js.map +1 -1
  155. package/build/lib/test/TestSteps.d.ts +7 -7
  156. package/build/lib/test/TestSteps.d.ts.map +1 -1
  157. package/build/lib/test/TestSteps.js +7 -7
  158. package/build/lib/test/TestSteps.js.map +1 -1
  159. package/build/lib/test/TestStepsWithOptions.d.ts +6 -6
  160. package/build/lib/test/TestStepsWithOptions.js +7 -7
  161. package/build/lib/test/lib.d.ts +3 -3
  162. package/build/lib/test/lib.d.ts.map +1 -1
  163. package/build/lib/test/lib.js +27 -21
  164. package/build/lib/test/lib.js.map +1 -1
  165. package/build/lib/test/resolvedTestFeatures.d.ts +6 -6
  166. package/build/lib/test/resolvedTestFeatures.d.ts.map +1 -1
  167. package/build/lib/test/resolvedTestFeatures.js +5 -5
  168. package/build/lib/test/subprocess-fixture.d.ts +8 -0
  169. package/build/lib/test/subprocess-fixture.d.ts.map +1 -0
  170. package/build/lib/test/subprocess-fixture.js +27 -0
  171. package/build/lib/test/subprocess-fixture.js.map +1 -0
  172. package/build/lib/ttag.d.ts +7 -1
  173. package/build/lib/ttag.d.ts.map +1 -1
  174. package/build/lib/ttag.js +5 -4
  175. package/build/lib/ttag.js.map +1 -1
  176. package/build/lib/util/dot-path.d.ts +14 -0
  177. package/build/lib/util/dot-path.d.ts.map +1 -0
  178. package/build/lib/util/dot-path.js +51 -0
  179. package/build/lib/util/dot-path.js.map +1 -0
  180. package/build/lib/util/index.d.ts +10 -24
  181. package/build/lib/util/index.d.ts.map +1 -1
  182. package/build/lib/util/index.js +43 -93
  183. package/build/lib/util/index.js.map +1 -1
  184. package/build/lib/util/node/actualURI.d.ts +4 -0
  185. package/build/lib/util/node/actualURI.d.ts.map +1 -0
  186. package/build/lib/util/node/actualURI.js +10 -0
  187. package/build/lib/util/node/actualURI.js.map +1 -0
  188. package/build/lib/util/node/module-loader.d.ts +14 -0
  189. package/build/lib/util/node/module-loader.d.ts.map +1 -0
  190. package/build/lib/util/node/module-loader.js +51 -0
  191. package/build/lib/util/node/module-loader.js.map +1 -0
  192. package/build/lib/util/node/workspace-lib.d.ts +15 -0
  193. package/build/lib/util/node/workspace-lib.d.ts.map +1 -0
  194. package/build/lib/util/node/workspace-lib.js +119 -0
  195. package/build/lib/util/node/workspace-lib.js.map +1 -0
  196. package/build/lib/util/secret-utils.d.ts.map +1 -1
  197. package/build/lib/util/secret-utils.js +2 -2
  198. package/build/lib/util/secret-utils.js.map +1 -1
  199. package/build/lib/vertex-crud.d.ts +22 -0
  200. package/build/lib/vertex-crud.d.ts.map +1 -0
  201. package/build/lib/vertex-crud.js +73 -0
  202. package/build/lib/vertex-crud.js.map +1 -0
  203. package/build/lib/workspace-discovery.d.ts +1 -1
  204. package/build/lib/workspace-discovery.d.ts.map +1 -1
  205. package/build/lib/workspace-discovery.js +10 -10
  206. package/build/lib/workspace-discovery.js.map +1 -1
  207. package/build/lib/zcap-like-authority.d.ts +38 -0
  208. package/build/lib/zcap-like-authority.d.ts.map +1 -0
  209. package/build/lib/zcap-like-authority.js +56 -0
  210. package/build/lib/zcap-like-authority.js.map +1 -0
  211. package/build/monitor/index.d.ts +1 -1
  212. package/build/monitor/index.d.ts.map +1 -1
  213. package/build/monitor/index.js +1 -1
  214. package/build/monitor/index.js.map +1 -1
  215. package/build/phases/Executor.d.ts +31 -7
  216. package/build/phases/Executor.d.ts.map +1 -1
  217. package/build/phases/Executor.js +202 -217
  218. package/build/phases/Executor.js.map +1 -1
  219. package/build/phases/Resolver.d.ts +2 -2
  220. package/build/phases/Resolver.d.ts.map +1 -1
  221. package/build/phases/Resolver.js +35 -30
  222. package/build/phases/Resolver.js.map +1 -1
  223. package/build/phases/collector.d.ts +3 -3
  224. package/build/phases/collector.d.ts.map +1 -1
  225. package/build/phases/collector.js +19 -17
  226. package/build/phases/collector.js.map +1 -1
  227. package/build/run-policy/run-policy-schema.d.ts +3 -5
  228. package/build/run-policy/run-policy-schema.d.ts.map +1 -1
  229. package/build/run-policy/run-policy-schema.js +41 -37
  230. package/build/run-policy/run-policy-schema.js.map +1 -1
  231. package/build/run-policy/run-policy-types.d.ts +1 -1
  232. package/build/run-policy/run-policy-types.d.ts.map +1 -1
  233. package/build/run-policy/run-policy-types.js +19 -16
  234. package/build/run-policy/run-policy-types.js.map +1 -1
  235. package/build/runner.d.ts +4 -4
  236. package/build/runner.d.ts.map +1 -1
  237. package/build/runner.js +15 -13
  238. package/build/runner.js.map +1 -1
  239. package/build/schema/protocol.d.ts +155 -63
  240. package/build/schema/protocol.d.ts.map +1 -1
  241. package/build/schema/protocol.js +177 -148
  242. package/build/schema/protocol.js.map +1 -1
  243. package/build/steps/activities-stepper.d.ts +14 -13
  244. package/build/steps/activities-stepper.d.ts.map +1 -1
  245. package/build/steps/activities-stepper.js +116 -78
  246. package/build/steps/activities-stepper.js.map +1 -1
  247. package/build/steps/conformance.d.ts +5 -5
  248. package/build/steps/conformance.js +4 -4
  249. package/build/steps/console-monitor-stepper.d.ts +2 -2
  250. package/build/steps/console-monitor-stepper.d.ts.map +1 -1
  251. package/build/steps/console-monitor-stepper.js +22 -22
  252. package/build/steps/console-monitor-stepper.js.map +1 -1
  253. package/build/steps/debugger-stepper.d.ts +11 -11
  254. package/build/steps/debugger-stepper.d.ts.map +1 -1
  255. package/build/steps/debugger-stepper.js +65 -64
  256. package/build/steps/debugger-stepper.js.map +1 -1
  257. package/build/steps/finalizer-stepper.d.ts +3 -4
  258. package/build/steps/finalizer-stepper.d.ts.map +1 -1
  259. package/build/steps/finalizer-stepper.js +16 -19
  260. package/build/steps/finalizer-stepper.js.map +1 -1
  261. package/build/steps/haibun.d.ts +27 -20
  262. package/build/steps/haibun.d.ts.map +1 -1
  263. package/build/steps/haibun.js +76 -67
  264. package/build/steps/haibun.js.map +1 -1
  265. package/build/steps/lib/tts.d.ts +1 -1
  266. package/build/steps/lib/tts.d.ts.map +1 -1
  267. package/build/steps/lib/tts.js +28 -28
  268. package/build/steps/lib/tts.js.map +1 -1
  269. package/build/steps/logic-stepper.d.ts +5 -5
  270. package/build/steps/logic-stepper.d.ts.map +1 -1
  271. package/build/steps/logic-stepper.js +76 -76
  272. package/build/steps/logic-stepper.js.map +1 -1
  273. package/build/steps/narrator.d.ts +9 -9
  274. package/build/steps/narrator.d.ts.map +1 -1
  275. package/build/steps/narrator.js +32 -28
  276. package/build/steps/narrator.js.map +1 -1
  277. package/build/steps/resources-stepper.d.ts +47 -0
  278. package/build/steps/resources-stepper.d.ts.map +1 -0
  279. package/build/steps/resources-stepper.js +100 -0
  280. package/build/steps/resources-stepper.js.map +1 -0
  281. package/build/steps/variables-stepper.d.ts +82 -54
  282. package/build/steps/variables-stepper.d.ts.map +1 -1
  283. package/build/steps/variables-stepper.js +264 -216
  284. package/build/steps/variables-stepper.js.map +1 -1
  285. package/build/steps/zcap-like-stepper.d.ts +50 -0
  286. package/build/steps/zcap-like-stepper.d.ts.map +1 -0
  287. package/build/steps/zcap-like-stepper.js +113 -0
  288. package/build/steps/zcap-like-stepper.js.map +1 -0
  289. package/package.json +1 -1
  290. package/build/lib/consts.d.ts +0 -5
  291. package/build/lib/consts.d.ts.map +0 -1
  292. package/build/lib/consts.js +0 -5
  293. package/build/lib/consts.js.map +0 -1
  294. package/build/lib/step-helpers.d.ts +0 -10
  295. package/build/lib/step-helpers.d.ts.map +0 -1
  296. package/build/lib/step-helpers.js +0 -16
  297. package/build/lib/step-helpers.js.map +0 -1
@@ -1,91 +1,105 @@
1
- import { z } from 'zod';
2
- import { OK, Origin } from '../schema/protocol.js';
3
- import { AStepper } from '../lib/astepper.js';
4
- import { actionOK, actionNotOK, getStepTerm } from '../lib/util/index.js';
5
- import { FeatureVariables, OBSCURED_VALUE } from '../lib/feature-variables.js';
6
- import { sanitizeObjectSecrets } from '../lib/util/secret-utils.js';
7
- import { DOMAIN_STATEMENT, DOMAIN_STRING, normalizeDomainKey, createEnumDomainDefinition, registerDomains } from '../lib/domain-types.js';
8
- const clearVars = (vars) => () => {
9
- vars.getWorld().shared.clear();
10
- return;
1
+ import { z } from "zod";
2
+ import { OK, Origin } from "../schema/protocol.js";
3
+ import { AStepper } from "../lib/astepper.js";
4
+ import { actionOK, actionNotOK, actionOKWithProducts, getStepTerm } from "../lib/util/index.js";
5
+ import { FlowRunner } from "../lib/core/flow-runner.js";
6
+ import { FeatureVariables, OBSCURED_VALUE } from "../lib/feature-variables.js";
7
+ import { sanitizeObjectSecrets } from "../lib/util/secret-utils.js";
8
+ import { DOMAIN_STATEMENT, DOMAIN_STRING, normalizeDomainKey, createEnumDomainDefinition, registerDomains, } from "../lib/domains.js";
9
+ const clearVars = (vars) => async () => {
10
+ await vars.getWorld().shared.getStore().clear();
11
11
  };
12
12
  const cycles = (variablesStepper) => ({
13
13
  startFeature: clearVars(variablesStepper),
14
- startScenario: ({ scopedVars }) => {
15
- variablesStepper.getWorld().shared = new FeatureVariables(variablesStepper.getWorld(), { ...scopedVars.all() });
16
- return Promise.resolve();
14
+ startScenario: async ({ scopedVars }) => {
15
+ variablesStepper.getWorld().shared = new FeatureVariables(variablesStepper.getWorld(), { ...(await scopedVars.all()) });
17
16
  },
18
17
  });
19
18
  class VariablesStepper extends AStepper {
20
- description = 'Set, get, and compare variables; define domains and check membership';
19
+ description = "Set, get, and compare variables; define domains and check membership";
21
20
  cycles = cycles(this);
22
21
  steppers;
22
+ runner;
23
23
  async setWorld(world, steppers) {
24
24
  this.world = world;
25
25
  this.steppers = steppers;
26
+ this.runner = new FlowRunner(world, steppers);
26
27
  await Promise.resolve();
27
28
  }
28
29
  steps = {
29
30
  defineOpenSet: {
30
31
  gwta: `set of {domain: string} as {superdomains: ${DOMAIN_STATEMENT}}`,
31
- handlesUndefined: ['domain'],
32
- action: ({ domain, superdomains }, featureStep) => this.registerSubdomainFromStatement(domain, superdomains, featureStep)
32
+ handlesUndefined: ["domain"],
33
+ action: ({ domain, superdomains }, featureStep) => this.registerSubdomainFromStatement(domain, superdomains, featureStep),
33
34
  },
34
35
  defineOrderedSet: {
35
36
  precludes: [`${VariablesStepper.name}.defineValuesSet`, `${VariablesStepper.name}.defineSet`],
36
- handlesUndefined: ['domain'],
37
+ handlesUndefined: ["domain"],
37
38
  gwta: `ordered set of {domain: string} is {values:${DOMAIN_STATEMENT}}`,
38
- action: ({ domain, values }, featureStep) => this.registerValuesDomainFromStatement(domain, values, featureStep, { ordered: true, label: 'ordered set' })
39
+ action: ({ domain, values }, featureStep) => this.registerValuesDomainFromStatement(domain, values, featureStep, { ordered: true, label: "ordered set" }),
39
40
  },
40
41
  defineValuesSet: {
41
42
  gwta: `set of {domain: string} is {values:${DOMAIN_STATEMENT}}`,
42
- handlesUndefined: ['domain'],
43
- action: ({ domain, values }, featureStep) => this.registerValuesDomainFromStatement(domain, values, featureStep, { ordered: false, label: 'set' })
43
+ handlesUndefined: ["domain"],
44
+ action: ({ domain, values }, featureStep) => this.registerValuesDomainFromStatement(domain, values, featureStep, { ordered: false, label: "set" }),
44
45
  },
45
46
  statementSetValues: {
46
- expose: false,
47
- gwta: '\\[{items: string}\\]',
47
+ exposeMCP: false,
48
+ gwta: "\\[{items: string}\\]",
48
49
  action: () => OK,
49
50
  },
50
51
  composeAs: {
51
- gwta: 'compose {what} as {domain} with {template}',
52
- handlesUndefined: ['what', 'template'],
52
+ gwta: "compose {what} as {domain} with {template}",
53
+ handlesUndefined: ["what", "template"],
53
54
  precludes: [`${VariablesStepper.name}.compose`],
54
- action: ({ domain }, featureStep) => {
55
+ action: async ({ domain }, featureStep) => {
55
56
  const { term } = featureStep.action.stepValuesMap.what;
56
57
  const templateVal = featureStep.action.stepValuesMap.template;
57
58
  if (!templateVal?.term)
58
- return actionNotOK('template not provided');
59
- const result = this.interpolateTemplate(templateVal.term, featureStep);
59
+ return actionNotOK("template not provided");
60
+ const result = await this.interpolateTemplate(templateVal.term, featureStep);
60
61
  if (result.error)
61
62
  return actionNotOK(result.error);
62
63
  return trySetVariable(this.getWorld().shared, { term: String(term), value: result.value, domain, origin: Origin.var, secret: result.secret }, provenanceFromFeatureStep(featureStep));
63
- }
64
+ },
64
65
  },
65
66
  compose: {
66
- gwta: 'compose {what} with {template}',
67
- handlesUndefined: ['what', 'template'],
68
- action: (_, featureStep) => {
67
+ gwta: "compose {what} with {template}",
68
+ handlesUndefined: ["what", "template"],
69
+ action: async (_, featureStep) => {
69
70
  const { term } = featureStep.action.stepValuesMap.what;
70
71
  const templateVal = featureStep.action.stepValuesMap.template;
71
72
  if (!templateVal?.term)
72
- return actionNotOK('template not provided');
73
- const result = this.interpolateTemplate(templateVal.term, featureStep);
73
+ return actionNotOK("template not provided");
74
+ const result = await this.interpolateTemplate(templateVal.term, featureStep);
74
75
  if (result.error)
75
76
  return actionNotOK(result.error);
76
77
  return trySetVariable(this.getWorld().shared, { term: String(term), value: result.value, domain: DOMAIN_STRING, origin: Origin.var, secret: result.secret }, provenanceFromFeatureStep(featureStep));
77
78
  },
78
79
  },
80
+ setFromStatement: {
81
+ gwta: `set {what: string} from {statement: ${DOMAIN_STATEMENT}}`,
82
+ handlesUndefined: ["what"],
83
+ precludes: [`${VariablesStepper.name}.set`],
84
+ action: async ({ statement }, featureStep) => {
85
+ const { term } = featureStep.action.stepValuesMap.what;
86
+ const result = await this.runner.runSteps(statement, { intent: { mode: "authoritative" }, parentStep: featureStep });
87
+ if (!result.ok)
88
+ return actionNotOK(`set from statement failed: ${result.errorMessage}`);
89
+ await this.getWorld().shared.setJSON(String(term), result.products ?? {}, Origin.var, featureStep);
90
+ return actionOK();
91
+ },
92
+ },
79
93
  increment: {
80
- gwta: 'increment {what}',
81
- handlesUndefined: ['what'],
82
- action: (_, featureStep) => {
94
+ gwta: "increment {what}",
95
+ handlesUndefined: ["what"],
96
+ action: async (_, featureStep) => {
83
97
  const { term: rawTerm } = featureStep.action.stepValuesMap.what;
84
- const interpolated = this.interpolateTemplate(rawTerm, featureStep);
98
+ const interpolated = await this.interpolateTemplate(rawTerm, featureStep);
85
99
  if (interpolated.error)
86
100
  return actionNotOK(interpolated.error);
87
101
  const term = interpolated?.value;
88
- const resolved = this.getWorld().shared.resolveVariable({ term, origin: Origin.var }, featureStep);
102
+ const resolved = await this.getWorld().shared.resolveVariable({ term, origin: Origin.var }, featureStep);
89
103
  const presentVal = resolved.value;
90
104
  const effectiveDomain = resolved.domain;
91
105
  if (presentVal === undefined) {
@@ -105,7 +119,7 @@ class VariablesStepper extends AStepper {
105
119
  if (nextVal === presentVal) {
106
120
  return OK;
107
121
  }
108
- this.getWorld().shared.set({ term: String(term), value: nextVal, domain: effectiveDomain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
122
+ await this.getWorld().shared.set({ term: String(term), value: nextVal, domain: effectiveDomain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
109
123
  return OK;
110
124
  }
111
125
  // Fallback: numeric increment
@@ -114,252 +128,267 @@ class VariablesStepper extends AStepper {
114
128
  return actionNotOK(`cannot increment non-numeric variable ${term} with value "${presentVal}"`);
115
129
  }
116
130
  const newNum = numVal + 1;
117
- this.getWorld().shared.set({ term: String(term), value: String(newNum), domain: effectiveDomain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
118
- this.getWorld().eventLogger.log(featureStep, 'info', `incremented ${term} to ${newNum}`, {
131
+ await this.getWorld().shared.set({ term: String(term), value: String(newNum), domain: effectiveDomain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
132
+ this.getWorld().eventLogger.log(featureStep, "info", `incremented ${term} to ${newNum}`, {
119
133
  variable: term,
120
134
  oldValue: presentVal,
121
135
  newValue: newNum,
122
- operation: 'increment'
136
+ operation: "increment",
123
137
  });
124
138
  return OK;
125
- }
139
+ },
126
140
  },
127
141
  showEnv: {
128
- gwta: 'show env',
129
- expose: false,
142
+ gwta: "show env",
143
+ exposeMCP: false,
130
144
  action: () => {
131
- // Obscure secret environment variables (matching /password/i)
132
145
  const envVars = this.world.options.envVariables || {};
133
146
  const shared = this.getWorld().shared;
134
147
  const safeEnv = sanitizeObjectSecrets(envVars, (key) => shared.isSecret(key));
135
- this.getWorld().eventLogger.info(`env: ${JSON.stringify(safeEnv, null, 2)}`, { env: safeEnv });
136
- return Promise.resolve(OK);
137
- }
148
+ const count = Object.keys(safeEnv).length;
149
+ return actionOKWithProducts({ _type: "Environment", _summary: `${count} environment variables`, env: safeEnv });
150
+ },
138
151
  },
139
152
  showVars: {
140
- gwta: 'show vars',
141
- action: () => {
153
+ gwta: "show vars",
154
+ action: async () => {
142
155
  const shared = this.getWorld().shared;
143
- const displayVars = Object.fromEntries(Object.entries(shared.all()).map(([key, variable]) => [key, variable.value]));
156
+ const displayVars = Object.fromEntries(Object.entries(await shared.all()).map(([key, variable]) => [key, variable.value]));
144
157
  const safeVars = sanitizeObjectSecrets(displayVars, (key) => shared.isSecret(key));
145
- this.getWorld().eventLogger.info(`vars: ${JSON.stringify(safeVars, null, 2)}`, { vars: safeVars });
146
- return actionOK();
158
+ const count = Object.keys(safeVars).length;
159
+ return actionOKWithProducts({ _type: "Variables", _summary: `${count} variables`, vars: safeVars });
147
160
  },
148
161
  },
149
162
  set: {
150
- gwta: 'set( empty)? {what: string} to {value: string}',
151
- handlesUndefined: ['what', 'value'],
152
- precludes: ['Haibun.prose'],
153
- action: (args, featureStep) => {
163
+ gwta: "set( empty)? {what: string} to {value: string}",
164
+ handlesUndefined: ["what", "value"],
165
+ precludes: ["Haibun.prose"],
166
+ action: async (_, featureStep) => {
154
167
  const { term: rawTerm, domain, origin } = featureStep.action.stepValuesMap.what;
155
- const parsedValue = this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep, undefined, { secure: true });
168
+ const parsedValue = await this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep, undefined, {
169
+ secure: true,
170
+ });
156
171
  if (parsedValue.value === undefined)
157
172
  return actionNotOK(`Variable ${featureStep.action.stepValuesMap.value.term} not found`);
158
173
  const resolved = { value: String(parsedValue.value) };
159
- const interpolated = this.interpolateTemplate(rawTerm, featureStep);
174
+ const interpolated = await this.interpolateTemplate(rawTerm, featureStep);
160
175
  if (interpolated.error)
161
176
  return actionNotOK(interpolated.error);
162
177
  const term = interpolated?.value;
163
- const skip = shouldSkipEmpty(featureStep, term, this.getWorld().shared);
178
+ const skip = await shouldSkipEmpty(featureStep, term, this.getWorld().shared);
164
179
  if (skip)
165
180
  return skip;
166
181
  // Inherit domain from existing variable if not explicitly specified
167
- const existing = this.getWorld().shared.resolveVariable({ term, origin: Origin.var }, featureStep);
168
- const effectiveDomain = (domain === DOMAIN_STRING && existing?.domain) ? existing.domain : (domain || DOMAIN_STRING);
182
+ const existing = await this.getWorld().shared.resolveVariable({ term, origin: Origin.var }, featureStep);
183
+ const effectiveDomain = domain === DOMAIN_STRING && existing?.domain ? existing.domain : domain || DOMAIN_STRING;
169
184
  const result = trySetVariable(this.getWorld().shared, { term, value: resolved.value, domain: effectiveDomain, origin, secret: interpolated.secret || parsedValue.secret }, provenanceFromFeatureStep(featureStep));
170
185
  return result;
171
- }
186
+ },
172
187
  },
173
188
  setAs: {
174
- gwta: 'set( empty)? {what} as {domain} to {value}',
175
- handlesUndefined: ['what', 'domain', 'value'],
189
+ gwta: "set( empty)? {what} as {domain} to {value}",
190
+ handlesUndefined: ["what", "domain", "value"],
176
191
  precludes: [`${VariablesStepper.name}.set`],
177
- action: ({ value, domain }, featureStep) => {
192
+ action: async ({ domain }, featureStep) => {
178
193
  const readonly = !!featureStep.in.match(/ as read-only /);
179
194
  const { term: rawTerm, origin } = featureStep.action.stepValuesMap.what;
180
- const parsedValue = this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep, undefined, { secure: true });
195
+ const parsedValue = await this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep, undefined, {
196
+ secure: true,
197
+ });
181
198
  if (parsedValue.value === undefined)
182
199
  return actionNotOK(`Variable ${featureStep.action.stepValuesMap.value.term} not found`);
183
200
  const resolved = { value: String(parsedValue.value) };
184
- const interpolated = this.interpolateTemplate(rawTerm, featureStep);
201
+ const interpolated = await this.interpolateTemplate(rawTerm, featureStep);
185
202
  if (interpolated.error)
186
203
  return actionNotOK(interpolated.error);
187
204
  const term = interpolated.value;
188
- const skip = shouldSkipEmpty(featureStep, term, this.getWorld().shared);
205
+ const skip = await shouldSkipEmpty(featureStep, term, this.getWorld().shared);
189
206
  if (skip)
190
207
  return skip;
191
208
  // Fallback for unquoted domain names (e.g. 'as number') that resolve to undefined
192
- let effectiveDomain = domain ?? getStepTerm(featureStep, 'domain');
209
+ let effectiveDomain = domain ?? getStepTerm(featureStep, "domain");
193
210
  if (effectiveDomain) {
194
- if (effectiveDomain.startsWith('read-only ')) {
195
- effectiveDomain = effectiveDomain.replace('read-only ', '');
211
+ if (effectiveDomain.startsWith("read-only ")) {
212
+ effectiveDomain = effectiveDomain.replace("read-only ", "");
196
213
  }
197
214
  if (effectiveDomain.startsWith('"') && effectiveDomain.endsWith('"')) {
198
215
  effectiveDomain = effectiveDomain.slice(1, -1);
199
216
  }
200
217
  }
201
218
  let finalValue = resolved.value;
202
- if (typeof finalValue === 'string' && finalValue.startsWith('"') && finalValue.endsWith('"')) {
219
+ if (typeof finalValue === "string" && finalValue.startsWith('"') && finalValue.endsWith('"')) {
203
220
  finalValue = finalValue.slice(1, -1);
204
221
  }
205
222
  return trySetVariable(this.getWorld().shared, { term, value: finalValue, domain: effectiveDomain, origin, readonly, secret: interpolated.secret || parsedValue.secret }, provenanceFromFeatureStep(featureStep));
206
- }
223
+ },
207
224
  },
208
225
  unset: {
209
- gwta: 'unset {what: string}',
210
- action: ({ what }, featureStep) => {
226
+ gwta: "unset {what: string}",
227
+ action: async (_, featureStep) => {
211
228
  const { term } = featureStep.action.stepValuesMap.what;
212
- this.getWorld().shared.unset(term);
213
- return Promise.resolve(OK);
214
- }
229
+ await this.getWorld().shared.unset(term);
230
+ return OK;
231
+ },
215
232
  },
216
233
  setRandom: {
217
234
  precludes: [`${VariablesStepper.name}.set`],
218
235
  gwta: `set( empty)? {what: string} to {length: number} random characters`,
219
- handlesUndefined: ['what'],
220
- action: ({ length }, featureStep) => {
236
+ handlesUndefined: ["what"],
237
+ action: async ({ length }, featureStep) => {
221
238
  const { term } = featureStep.action.stepValuesMap.what;
222
239
  if (length < 1 || length > 100) {
223
240
  return actionNotOK(`length ${length} must be between 1 and 100`);
224
241
  }
225
- const skip = shouldSkipEmpty(featureStep, term, this.getWorld().shared);
242
+ const skip = await shouldSkipEmpty(featureStep, term, this.getWorld().shared);
226
243
  if (skip)
227
244
  return skip;
228
- let rand = '';
245
+ let rand = "";
229
246
  while (rand.length < length) {
230
- rand += Math.random().toString(36).substring(2, 2 + length);
247
+ rand += Math.random()
248
+ .toString(36)
249
+ .substring(2, 2 + length);
231
250
  }
232
251
  rand = rand.substring(0, length);
233
252
  return trySetVariable(this.getWorld().shared, { term, value: rand, domain: DOMAIN_STRING, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
234
- }
253
+ },
235
254
  },
236
255
  is: {
237
- gwta: 'variable {what} is {value}',
238
- handlesUndefined: ['what', 'value'],
239
- action: (_, featureStep) => {
256
+ gwta: "variable {what} is {value}",
257
+ handlesUndefined: ["what", "value"],
258
+ action: async (_, featureStep) => {
240
259
  const { term: rawTerm } = featureStep.action.stepValuesMap.what;
241
- const interpolated = this.interpolateTemplate(rawTerm, featureStep);
260
+ const interpolated = await this.interpolateTemplate(rawTerm, featureStep);
242
261
  if (interpolated.error)
243
262
  return actionNotOK(interpolated.error);
244
263
  const term = interpolated.value;
245
- const resolved = this.getWorld().shared.resolveVariable({ term, origin: Origin.defined }, featureStep, undefined, { secure: true });
264
+ const resolved = await this.getWorld().shared.resolveVariable({ term, origin: Origin.defined }, featureStep, undefined, {
265
+ secure: true,
266
+ });
246
267
  if (resolved.value === undefined || (resolved.origin !== Origin.var && resolved.origin !== Origin.env)) {
247
268
  return actionNotOK(`${term} is not set`);
248
269
  }
249
- const parsedValue = this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep, undefined, { secure: true });
270
+ const parsedValue = await this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep, undefined, {
271
+ secure: true,
272
+ });
250
273
  if (parsedValue.value === undefined)
251
274
  return actionNotOK(`Variable ${featureStep.action.stepValuesMap.value.term} not found`);
252
275
  const value = String(parsedValue.value);
253
276
  const domainKey = normalizeDomainKey(resolved.domain);
254
- const compareVal = this.getWorld().domains[domainKey].coerce({ term: '_cmp', value, domain: domainKey, origin: Origin.quoted }, featureStep, this.steppers);
255
- return JSON.stringify(resolved.value) === JSON.stringify(compareVal) ? OK : actionNotOK(`${term} is ${JSON.stringify(resolved.value)}, not ${JSON.stringify(compareVal)}`);
256
- }
277
+ const compareVal = this.getWorld().domains[domainKey].coerce({ term: "_cmp", value, domain: domainKey, origin: Origin.quoted }, featureStep, this.steppers);
278
+ return JSON.stringify(resolved.value) === JSON.stringify(compareVal)
279
+ ? OK
280
+ : actionNotOK(`${term} is ${JSON.stringify(resolved.value)}, not ${JSON.stringify(compareVal)}`);
281
+ },
257
282
  },
258
283
  isLessThan: {
259
- gwta: 'variable {what} is less than {value}',
260
- handlesUndefined: ['what', 'value'],
261
- precludes: ['VariablesStepper.is'],
284
+ gwta: "variable {what} is less than {value}",
285
+ handlesUndefined: ["what", "value"],
286
+ precludes: ["VariablesStepper.is"],
262
287
  action: ({ what, value }, featureStep) => {
263
- const term = getStepTerm(featureStep, 'what') ?? what;
264
- return this.compareValues(featureStep, term, value, '<');
265
- }
288
+ const term = getStepTerm(featureStep, "what") ?? what;
289
+ return this.compareValues(featureStep, term, value, "<");
290
+ },
266
291
  },
267
292
  isMoreThan: {
268
- gwta: 'variable {what} is more than {value}',
269
- handlesUndefined: ['what', 'value'],
270
- precludes: ['VariablesStepper.is'],
293
+ gwta: "variable {what} is more than {value}",
294
+ handlesUndefined: ["what", "value"],
295
+ precludes: ["VariablesStepper.is"],
271
296
  action: ({ what, value }, featureStep) => {
272
- const term = getStepTerm(featureStep, 'what') ?? what;
273
- return this.compareValues(featureStep, term, value, '>');
274
- }
297
+ const term = getStepTerm(featureStep, "what") ?? what;
298
+ return this.compareValues(featureStep, term, value, ">");
299
+ },
275
300
  },
276
301
  exists: {
277
- gwta: 'variable {what} exists',
278
- handlesUndefined: ['what'],
279
- action: ({ what }, featureStep) => {
280
- const term = (getStepTerm(featureStep, 'what') ?? what);
281
- const sharedVars = this.getWorld().shared.all();
302
+ gwta: "variable {what} exists",
303
+ handlesUndefined: ["what"],
304
+ action: async ({ what }, featureStep) => {
305
+ const term = (getStepTerm(featureStep, "what") ?? what);
306
+ const sharedVars = await this.getWorld().shared.all();
282
307
  if (sharedVars[term])
283
308
  return OK;
284
309
  const envVars = this.getWorld().options.envVariables || {};
285
310
  return envVars[term] !== undefined ? OK : actionNotOK(`${what} not set`);
286
- }
311
+ },
287
312
  },
288
313
  showVar: {
289
- gwta: 'show var {what}',
290
- handlesUndefined: ['what'],
291
- action: (_, featureStep) => {
292
- const rawTerm = getStepTerm(featureStep, 'what');
314
+ gwta: "show var {what}",
315
+ handlesUndefined: ["what"],
316
+ outputSchema: z.object({ term: z.string(), value: z.unknown(), domain: z.string().optional() }),
317
+ action: async (_, featureStep) => {
318
+ const rawTerm = getStepTerm(featureStep, "what");
293
319
  if (rawTerm === undefined)
294
- return actionNotOK('variable not provided');
295
- const interpolated = this.interpolateTemplate(rawTerm, featureStep);
320
+ return actionNotOK("variable not provided");
321
+ const interpolated = await this.interpolateTemplate(rawTerm, featureStep);
296
322
  if (interpolated.error)
297
323
  return actionNotOK(interpolated.error);
298
- const term = interpolated.value || '';
324
+ const term = interpolated.value || "";
299
325
  const shared = this.getWorld().shared;
300
- const stepValue = shared.resolveVariable({ term, origin: Origin.defined }, featureStep);
326
+ const stepValue = await shared.resolveVariable({ term, origin: Origin.defined }, featureStep);
301
327
  const isSecret = shared.isSecret(term) || stepValue.secret === true;
302
328
  if (stepValue.value === undefined) {
303
329
  this.getWorld().eventLogger.info(`${term} is undefined`);
330
+ return actionOKWithProducts({ term, value: undefined, domain: stepValue.domain });
304
331
  }
305
- else {
306
- const displayValue = isSecret ? { ...stepValue, value: OBSCURED_VALUE } : stepValue;
307
- const provenance = featureStep.action.stepValuesMap.what.provenance?.map((p, i) => ({ [i]: { in: p.in, seq: p.seq.join(','), when: p.when } }));
308
- this.getWorld().eventLogger.info(`${term} is ${JSON.stringify({ ...displayValue, provenance }, null, 2)}`, { variable: term, value: displayValue });
309
- }
310
- return actionOK();
311
- }
332
+ const displayValue = isSecret ? OBSCURED_VALUE : stepValue.value;
333
+ const summary = `${term} = ${typeof displayValue === "object" ? JSON.stringify(displayValue) : String(displayValue)}`;
334
+ return actionOKWithProducts({ _type: "Variable", _summary: summary, term, value: stepValue.value, domain: stepValue.domain });
335
+ },
312
336
  },
313
337
  showDomains: {
314
- gwta: 'show domains',
315
- action: () => {
338
+ gwta: "show domains",
339
+ action: async () => {
316
340
  const domains = this.getWorld().domains;
317
- const allVars = this.getWorld().shared.all();
318
- const summary = {};
341
+ const allVars = await this.getWorld().shared.all();
342
+ const items = [];
343
+ // Collect vertexLabel→name mapping for base type edge targets
344
+ const labelToDomain = new Map();
345
+ for (const [dname, ddef] of Object.entries(domains)) {
346
+ const vl = ddef.topology?.vertexLabel;
347
+ labelToDomain.set(vl || dname, vl || dname);
348
+ }
319
349
  for (const [name, def] of Object.entries(domains)) {
320
350
  let members = 0;
321
351
  for (const variable of Object.values(allVars)) {
322
- if (variable.domain && normalizeDomainKey(variable.domain) === name) {
352
+ if (variable.domain && normalizeDomainKey(variable.domain) === name)
323
353
  members++;
324
- }
325
- }
326
- let type = 'schema';
327
- if (def.values) {
328
- type = def.values;
329
354
  }
330
- else if (def.description) {
331
- type = def.description;
355
+ const description = def.values || def.description || "schema";
356
+ const topology = def.topology;
357
+ const vertexLabel = topology?.vertexLabel;
358
+ const _edges = [];
359
+ // Edges from topology (vertex→vertex relationships like Email→Contact)
360
+ const topologyEdges = topology?.edges;
361
+ if (topologyEdges) {
362
+ for (const [edgeName, edge] of Object.entries(topologyEdges)) {
363
+ if (edge.range && edge.range !== vertexLabel)
364
+ _edges.push({ type: edgeName, targetId: edge.range });
365
+ }
332
366
  }
333
- summary[name] = {
334
- type,
335
- members,
336
- ordered: !!def.comparator
337
- };
367
+ items.push({ name, description, members, ...(vertexLabel ? { vertexLabel } : {}), ...(_edges.length ? { _edges } : {}) });
338
368
  }
339
- this.getWorld().eventLogger.info(`Domains: ${JSON.stringify(summary, null, 2)}`, { domains: summary });
340
- return OK;
341
- }
369
+ return actionOKWithProducts({ _type: "Domain", _summary: `${items.length} domains`, items });
370
+ },
342
371
  },
343
372
  showDomain: {
344
- gwta: 'show domain {name}',
345
- handlesUndefined: ['name'],
346
- action: (_, featureStep) => {
347
- const name = getStepTerm(featureStep, 'name');
373
+ gwta: "show domain {name}",
374
+ handlesUndefined: ["name"],
375
+ action: async (_, featureStep) => {
376
+ const name = getStepTerm(featureStep, "name");
348
377
  const domain = this.getWorld().domains[name];
349
378
  if (!domain) {
350
379
  return actionNotOK(`Domain "${name}" not found`);
351
380
  }
352
381
  const shared = this.getWorld().shared;
353
- const allVars = shared.all();
382
+ const allVars = await shared.all();
354
383
  const members = {};
355
384
  for (const [key, variable] of Object.entries(allVars)) {
356
385
  if (variable.domain && normalizeDomainKey(variable.domain) === name) {
357
386
  members[key] = shared.isSecret(key) ? OBSCURED_VALUE : variable.value;
358
387
  }
359
388
  }
360
- this.getWorld().eventLogger.info(`Domain "${name}": ${JSON.stringify({ ...domain, members }, null, 2)}`, { domain: name, ...domain, members });
361
- return OK;
362
- }
389
+ const memberCount = Object.keys(members).length;
390
+ return actionOKWithProducts({ _type: "Domain", _summary: `${name}: ${memberCount} members`, domain: name, ...domain, members });
391
+ },
363
392
  },
364
393
  // Membership check: value is in domain (enum or member values)
365
394
  // Handles quoted ("value"), braced ({var}), or bare (value) forms
@@ -367,23 +396,22 @@ class VariablesStepper extends AStepper {
367
396
  isIn: {
368
397
  match: /^(.+) is in ([a-zA-Z][a-zA-Z0-9 ]*)$/,
369
398
  fallback: true,
370
- action: (_, featureStep) => {
399
+ action: async (_, featureStep) => {
371
400
  const matchResult = featureStep.in.match(/^(.+) is in ([a-zA-Z][a-zA-Z0-9 ]*)$/);
372
401
  if (!matchResult) {
373
402
  return actionNotOK('Invalid "is in" syntax');
374
403
  }
375
404
  let valueTerm = matchResult[1].trim();
376
405
  // Strip quotes if present
377
- if ((valueTerm.startsWith('"') && valueTerm.endsWith('"')) ||
378
- (valueTerm.startsWith('`') && valueTerm.endsWith('`'))) {
406
+ if ((valueTerm.startsWith('"') && valueTerm.endsWith('"')) || (valueTerm.startsWith("`") && valueTerm.endsWith("`"))) {
379
407
  valueTerm = valueTerm.slice(1, -1);
380
408
  }
381
409
  // Strip braces if present and resolve variable
382
- if (valueTerm.startsWith('{') && valueTerm.endsWith('}')) {
410
+ if (valueTerm.startsWith("{") && valueTerm.endsWith("}")) {
383
411
  valueTerm = valueTerm.slice(1, -1);
384
412
  }
385
413
  // Try to resolve as variable, fall back to literal
386
- const resolvedValue = this.getWorld().shared.get(valueTerm, true);
414
+ const resolvedValue = await this.getWorld().shared.get(valueTerm, true);
387
415
  const actualValue = resolvedValue !== undefined ? String(resolvedValue) : valueTerm;
388
416
  const domainName = matchResult[2].trim();
389
417
  const domainKey = normalizeDomainKey(domainName);
@@ -396,14 +424,12 @@ class VariablesStepper extends AStepper {
396
424
  return OK;
397
425
  }
398
426
  // Check member values
399
- const allVars = this.getWorld().shared.all();
427
+ const allVars = await this.getWorld().shared.all();
400
428
  const memberValues = Object.values(allVars)
401
- .filter(v => v.domain && normalizeDomainKey(v.domain) === domainKey)
402
- .map(v => String(v.value));
403
- return memberValues.includes(actualValue)
404
- ? OK
405
- : actionNotOK(`"${actualValue}" is not in ${domainName}`);
406
- }
429
+ .filter((v) => v.domain && normalizeDomainKey(v.domain) === domainKey)
430
+ .map((v) => String(v.value));
431
+ return memberValues.includes(actualValue) ? OK : actionNotOK(`"${actualValue}" is not in ${domainName}`);
432
+ },
407
433
  },
408
434
  // Pattern matching: glob-style patterns for human-readable matching
409
435
  // Usage: matches {host} with "*.wikipedia.org"
@@ -412,40 +438,40 @@ class VariablesStepper extends AStepper {
412
438
  // Supports * as wildcard (matches any characters)
413
439
  // Variables in pattern are interpolated: "{counter URI}*" resolves to actual value
414
440
  matches: {
415
- gwta: 'matches {value} with {pattern}',
416
- action: ({ value, pattern }, featureStep) => {
441
+ gwta: "matches {value} with {pattern}",
442
+ action: async ({ value, pattern }, featureStep) => {
417
443
  // Interpolate value (e.g. "{request}/url" -> "req-1/url")
418
- const interpolatedValue = this.interpolateTemplate(value, featureStep);
444
+ const interpolatedValue = await this.interpolateTemplate(value, featureStep);
419
445
  if (interpolatedValue.error)
420
446
  return actionNotOK(interpolatedValue.error);
421
447
  const term = interpolatedValue.value;
422
448
  // Resolve value as a variable (e.g., "WebPlaywright/currentURI" -> actual URL)
423
- const resolvedValue = this.getWorld().shared.get(term, true);
449
+ const resolvedValue = await this.getWorld().shared.get(term, true);
424
450
  const actualValue = resolvedValue !== undefined ? String(resolvedValue) : String(term);
425
451
  // Interpolate variables in pattern (e.g., "{counter URI}*" -> "http://localhost:8123/*")
426
- const interpolated = this.interpolateTemplate(pattern, featureStep);
452
+ const interpolated = await this.interpolateTemplate(pattern, featureStep);
427
453
  if (interpolated.error)
428
454
  return actionNotOK(interpolated.error);
429
455
  const actualPattern = interpolated.value;
430
456
  // Convert glob pattern to regex
431
457
  // Escape regex special chars except *, then replace * with .*
432
- const escaped = actualPattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
433
- const regexPattern = escaped.replace(/\*/g, '.*');
458
+ const escaped = actualPattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
459
+ const regexPattern = escaped.replace(/\*/g, ".*");
434
460
  const regex = new RegExp(`^${regexPattern}$`);
435
461
  const isMatch = regex.test(actualValue);
436
- return isMatch
437
- ? OK
438
- : actionNotOK(`"${actualValue}" does not match pattern "${actualPattern}"`);
439
- }
462
+ return isMatch ? OK : actionNotOK(`"${actualValue}" does not match pattern "${actualPattern}"`);
463
+ },
440
464
  },
441
465
  };
442
466
  typedSteps = this.steps;
443
- compareValues(featureStep, rawTerm, value, operator) {
444
- const interpolated = this.interpolateTemplate(rawTerm, featureStep);
467
+ async compareValues(featureStep, rawTerm, value, operator) {
468
+ const interpolated = await this.interpolateTemplate(rawTerm, featureStep);
445
469
  if (interpolated.error)
446
470
  return actionNotOK(interpolated.error);
447
471
  const term = interpolated.value;
448
- const stored = this.getWorld().shared.resolveVariable({ term, origin: Origin.var }, featureStep, this.steppers, { secure: true });
472
+ const stored = await this.getWorld().shared.resolveVariable({ term, origin: Origin.var }, featureStep, this.steppers, {
473
+ secure: true,
474
+ });
449
475
  if (!stored) {
450
476
  return actionNotOK(`${term} is not set`);
451
477
  }
@@ -457,20 +483,20 @@ class VariablesStepper extends AStepper {
457
483
  const left = domainEntry.coerce({ ...stored, domain: domainKey }, featureStep, this.steppers);
458
484
  let rightValue = value;
459
485
  if (rightValue === undefined) {
460
- const parsed = this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep);
486
+ const parsed = await this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep);
461
487
  if (parsed.value === undefined)
462
488
  return actionNotOK(`Variable ${featureStep.action.stepValuesMap.value.term} not found`);
463
489
  rightValue = String(parsed.value);
464
490
  }
465
- if (typeof rightValue === 'string' && rightValue.startsWith('"') && rightValue.endsWith('"')) {
491
+ if (typeof rightValue === "string" && rightValue.startsWith('"') && rightValue.endsWith('"')) {
466
492
  rightValue = rightValue.slice(1, -1);
467
493
  }
468
494
  const right = domainEntry.coerce({ term: `${term}__comparison`, value: rightValue, domain: domainKey, origin: Origin.quoted }, featureStep, this.steppers);
469
495
  const comparison = compareDomainValues(domainEntry, left, right, stored.domain);
470
- if (operator === '>') {
496
+ if (operator === ">") {
471
497
  return comparison > 0 ? OK : actionNotOK(`${term} is ${JSON.stringify(left)}, not ${JSON.stringify(right)}`);
472
498
  }
473
- if (operator === '<') {
499
+ if (operator === "<") {
474
500
  return comparison < 0 ? OK : actionNotOK(`${term} is ${JSON.stringify(left)}, not ${JSON.stringify(right)}`);
475
501
  }
476
502
  return actionNotOK(`Unsupported operator: ${operator}`);
@@ -479,7 +505,7 @@ class VariablesStepper extends AStepper {
479
505
  * Interpolates a template string by replacing {varName} placeholders with variable values.
480
506
  * Returns the interpolated string or an error if a variable is not found.
481
507
  */
482
- interpolateTemplate(template, featureStep) {
508
+ async interpolateTemplate(template, featureStep) {
483
509
  const placeholderRegex = /\{([^}]+)\}/g;
484
510
  let result = template;
485
511
  let match;
@@ -490,7 +516,9 @@ class VariablesStepper extends AStepper {
490
516
  if (this.getWorld().shared.isSecret(varName)) {
491
517
  secret = true;
492
518
  }
493
- const resolved = this.getWorld().shared.resolveVariable({ term: varName, origin: Origin.defined }, featureStep, undefined, { secure: true });
519
+ const resolved = await this.getWorld().shared.resolveVariable({ term: varName, origin: Origin.defined }, featureStep, undefined, {
520
+ secure: true,
521
+ });
494
522
  if (resolved.value === undefined) {
495
523
  return { error: `Variable ${varName} not found` };
496
524
  }
@@ -500,15 +528,15 @@ class VariablesStepper extends AStepper {
500
528
  }
501
529
  registerSubdomainFromStatement(domain, superdomains, featureStep) {
502
530
  try {
503
- const fallback = getStepTerm(featureStep, 'superdomains') ?? featureStep.in;
531
+ const fallback = getStepTerm(featureStep, "superdomains") ?? featureStep.in;
504
532
  const superdomainNames = extractValuesFromFragments(superdomains, fallback);
505
533
  if (!superdomainNames.length) {
506
- throw new Error('Superdomain set must specify at least one superdomain');
534
+ throw new Error("Superdomain set must specify at least one superdomain");
507
535
  }
508
536
  const uniqueNames = Array.from(new Set(superdomainNames));
509
- const effectiveDomain = domain ?? getStepTerm(featureStep, 'domain');
537
+ const effectiveDomain = domain ?? getStepTerm(featureStep, "domain");
510
538
  if (!effectiveDomain)
511
- return actionNotOK('Domain name must be provided');
539
+ return actionNotOK("Domain name must be provided");
512
540
  const domainKey = normalizeDomainKey(effectiveDomain);
513
541
  if (this.getWorld().domains[domainKey]) {
514
542
  return actionNotOK(`Domain "${domainKey}" already exists`);
@@ -523,7 +551,7 @@ class VariablesStepper extends AStepper {
523
551
  });
524
552
  const enumSources = superdomainDefs.filter((entry) => Array.isArray(entry.values) && entry.values.length);
525
553
  const uniqueValues = Array.from(new Set(enumSources.flatMap((entry) => entry.values)));
526
- const description = `Values inherited from ${uniqueNames.join(', ')}`;
554
+ const description = `Values inherited from ${uniqueNames.join(", ")}`;
527
555
  if (enumSources.length === superdomainDefs.length && uniqueValues.length) {
528
556
  const definition = createEnumDomainDefinition({ name: domainKey, values: uniqueValues, description });
529
557
  registerDomains(this.getWorld(), [[definition]]);
@@ -531,7 +559,7 @@ class VariablesStepper extends AStepper {
531
559
  }
532
560
  const schemaList = superdomainDefs.map((entry) => entry.schema);
533
561
  if (!schemaList.length) {
534
- throw new Error('Superdomains did not expose any schema to derive from');
562
+ throw new Error("Superdomains did not expose any schema to derive from");
535
563
  }
536
564
  let mergedSchema = schemaList[0];
537
565
  for (let i = 1; i < schemaList.length; i++) {
@@ -552,15 +580,20 @@ class VariablesStepper extends AStepper {
552
580
  }
553
581
  registerValuesDomainFromStatement(domain, valueFragments, featureStep, options) {
554
582
  try {
555
- const values = extractValuesFromFragments(valueFragments, getStepTerm(featureStep, 'values') ?? featureStep.in);
556
- const effectiveDomain = domain ?? getStepTerm(featureStep, 'domain');
583
+ const values = extractValuesFromFragments(valueFragments, getStepTerm(featureStep, "values") ?? featureStep.in);
584
+ const effectiveDomain = domain ?? getStepTerm(featureStep, "domain");
557
585
  if (!effectiveDomain)
558
- return actionNotOK('Domain name must be provided');
586
+ return actionNotOK("Domain name must be provided");
559
587
  const domainKey = normalizeDomainKey(effectiveDomain);
560
588
  if (this.getWorld().domains[domainKey]) {
561
589
  return actionNotOK(`Domain "${domainKey}" already exists`);
562
590
  }
563
- const definition = createEnumDomainDefinition({ name: domainKey, values, description: options?.description, ordered: options?.ordered });
591
+ const definition = createEnumDomainDefinition({
592
+ name: domainKey,
593
+ values,
594
+ description: options?.description,
595
+ ordered: options?.ordered,
596
+ });
564
597
  registerDomains(this.getWorld(), [[definition]]);
565
598
  return OK;
566
599
  }
@@ -577,53 +610,58 @@ export function provenanceFromFeatureStep(featureStep) {
577
610
  return {
578
611
  in: featureStep.in,
579
612
  seq: featureStep.seqPath,
580
- when: `${featureStep.action.stepperName}.steps.${featureStep.action.actionName}`
613
+ when: `${featureStep.action.stepperName}.steps.${featureStep.action.actionName}`,
581
614
  };
582
615
  }
583
616
  const QUOTED_STRING = /"([^"]+)"/g;
584
617
  const extractValuesFromFragments = (valueFragments, fallback) => {
585
618
  if (valueFragments?.length) {
586
- const innerChunks = valueFragments.map(fragment => {
587
- const raw = fragment.in ?? fragment.action?.stepValuesMap?.items?.term ?? '';
619
+ const innerChunks = valueFragments
620
+ .map((fragment) => {
621
+ const raw = fragment.in ?? fragment.action?.stepValuesMap?.items?.term ?? "";
588
622
  const trimmed = raw.trim();
589
- if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
623
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
590
624
  return trimmed.slice(1, -1).trim();
591
625
  }
592
626
  return trimmed;
593
- }).filter(Boolean);
594
- const inner = innerChunks.join(' ').trim();
627
+ })
628
+ .filter(Boolean);
629
+ const inner = innerChunks.join(" ").trim();
595
630
  if (!inner) {
596
- throw new Error('Set values cannot be empty');
631
+ throw new Error("Set values cannot be empty");
597
632
  }
598
633
  return parseQuotedOrWordList(inner);
599
634
  }
600
635
  if (fallback) {
601
636
  return parseBracketedValues(fallback);
602
637
  }
603
- throw new Error('Set statement missing values');
638
+ throw new Error("Set statement missing values");
604
639
  };
605
640
  const parseBracketedValues = (raw) => {
606
641
  const trimmed = raw.trim();
607
- const start = trimmed.indexOf('[');
608
- const end = trimmed.lastIndexOf(']');
642
+ const start = trimmed.indexOf("[");
643
+ const end = trimmed.lastIndexOf("]");
609
644
  if (start === -1 || end === -1 || end <= start) {
610
- throw new Error('Set values must include [ ]');
645
+ throw new Error("Set values must include [ ]");
611
646
  }
612
647
  const inner = trimmed.substring(start + 1, end).trim();
613
648
  return parseQuotedOrWordList(inner);
614
649
  };
615
650
  const parseQuotedOrWordList = (value) => {
616
- const quoted = [...value.matchAll(QUOTED_STRING)].map(match => match[1].trim()).filter(Boolean);
651
+ const quoted = [...value.matchAll(QUOTED_STRING)].map((match) => match[1].trim()).filter(Boolean);
617
652
  if (quoted.length) {
618
653
  return quoted;
619
654
  }
620
- return value.split(/[\s,]+/).map(token => token.trim()).filter(Boolean);
655
+ return value
656
+ .split(/[\s,]+/)
657
+ .map((token) => token.trim())
658
+ .filter(Boolean);
621
659
  };
622
660
  const compareDomainValues = (domain, left, right, domainName) => {
623
661
  if (domain.comparator) {
624
662
  return domain.comparator(left, right);
625
663
  }
626
- if (typeof left === 'number' && typeof right === 'number') {
664
+ if (typeof left === "number" && typeof right === "number") {
627
665
  return left - right;
628
666
  }
629
667
  if (left instanceof Date && right instanceof Date) {
@@ -633,13 +671,23 @@ const compareDomainValues = (domain, left, right, domainName) => {
633
671
  };
634
672
  // ======== Helpers ========
635
673
  // Returns OK if "set empty" and variable exists
636
- function shouldSkipEmpty(featureStep, term, shared) {
637
- return (featureStep.in.includes('set empty ') && shared.resolveVariable({ term, origin: Origin.var }, featureStep, undefined, { secure: true }).value !== undefined) ? OK : undefined;
674
+ async function shouldSkipEmpty(featureStep, term, shared) {
675
+ if (!featureStep.in.includes("set empty "))
676
+ return undefined;
677
+ const resolved = await shared.resolveVariable({ term, origin: Origin.var }, featureStep, undefined, { secure: true });
678
+ return resolved.value !== undefined ? OK : undefined;
638
679
  }
639
680
  // Wraps shared.set in try/catch
640
- function trySetVariable(shared, opts, provenance) {
681
+ async function trySetVariable(shared, opts, provenance) {
641
682
  try {
642
- shared.set({ term: opts.term, value: opts.value, domain: opts.domain, origin: opts.origin, readonly: opts.readonly, secret: opts.secret }, provenance);
683
+ await shared.set({
684
+ term: opts.term,
685
+ value: opts.value,
686
+ domain: opts.domain,
687
+ origin: opts.origin,
688
+ readonly: opts.readonly,
689
+ secret: opts.secret,
690
+ }, provenance);
643
691
  return OK;
644
692
  }
645
693
  catch (e) {