@haibun/core 3.0.5 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/build/currentVersion.d.ts +1 -1
  2. package/build/currentVersion.js +2 -1
  3. package/build/lib/EventLogger.d.ts +30 -0
  4. package/build/lib/EventLogger.d.ts.map +1 -0
  5. package/build/lib/EventLogger.js +110 -0
  6. package/build/lib/EventLogger.js.map +1 -0
  7. package/build/lib/astepper.d.ts +9 -0
  8. package/build/lib/astepper.d.ts.map +1 -1
  9. package/build/lib/astepper.js +8 -0
  10. package/build/lib/astepper.js.map +1 -1
  11. package/build/lib/core/flow-runner.d.ts +25 -0
  12. package/build/lib/core/flow-runner.d.ts.map +1 -0
  13. package/build/lib/core/flow-runner.js +128 -0
  14. package/build/lib/core/flow-runner.js.map +1 -0
  15. package/build/lib/core/protocol.d.ts +58 -0
  16. package/build/lib/core/protocol.d.ts.map +1 -0
  17. package/build/lib/core/protocol.js +18 -0
  18. package/build/lib/core/protocol.js.map +1 -0
  19. package/build/lib/core-domains.d.ts +2 -16
  20. package/build/lib/core-domains.d.ts.map +1 -1
  21. package/build/lib/core-domains.js +46 -27
  22. package/build/lib/core-domains.js.map +1 -1
  23. package/build/lib/defs.d.ts +31 -6
  24. package/build/lib/defs.d.ts.map +1 -1
  25. package/build/lib/defs.js +1 -1
  26. package/build/lib/defs.js.map +1 -1
  27. package/build/lib/domain-types.d.ts +12 -1
  28. package/build/lib/domain-types.d.ts.map +1 -1
  29. package/build/lib/domain-types.js +63 -3
  30. package/build/lib/domain-types.js.map +1 -1
  31. package/build/lib/event-bridge.d.ts +42 -0
  32. package/build/lib/event-bridge.d.ts.map +1 -0
  33. package/build/lib/event-bridge.js +214 -0
  34. package/build/lib/event-bridge.js.map +1 -0
  35. package/build/lib/feature-variables.d.ts +13 -0
  36. package/build/lib/feature-variables.d.ts.map +1 -1
  37. package/build/lib/feature-variables.js +125 -6
  38. package/build/lib/feature-variables.js.map +1 -1
  39. package/build/lib/namedVars.d.ts.map +1 -1
  40. package/build/lib/namedVars.js +18 -10
  41. package/build/lib/namedVars.js.map +1 -1
  42. package/build/lib/populateActionArgs.d.ts +1 -1
  43. package/build/lib/populateActionArgs.d.ts.map +1 -1
  44. package/build/lib/populateActionArgs.js +11 -49
  45. package/build/lib/populateActionArgs.js.map +1 -1
  46. package/build/lib/test/TestSteps.d.ts +1 -0
  47. package/build/lib/test/TestSteps.d.ts.map +1 -1
  48. package/build/lib/test/TestStepsWithOptions.d.ts +1 -0
  49. package/build/lib/test/TestStepsWithOptions.d.ts.map +1 -1
  50. package/build/lib/test/lib.d.ts.map +1 -1
  51. package/build/lib/test/lib.js +2 -0
  52. package/build/lib/test/lib.js.map +1 -1
  53. package/build/lib/util/actualURI.js +1 -1
  54. package/build/lib/util/actualURI.js.map +1 -1
  55. package/build/lib/util/index.d.ts +11 -2
  56. package/build/lib/util/index.d.ts.map +1 -1
  57. package/build/lib/util/index.js +46 -2
  58. package/build/lib/util/index.js.map +1 -1
  59. package/build/lib/util/variables.d.ts +9 -0
  60. package/build/lib/util/variables.d.ts.map +1 -0
  61. package/build/lib/util/variables.js +40 -0
  62. package/build/lib/util/variables.js.map +1 -0
  63. package/build/lib/util/workspace-lib.d.ts.map +1 -1
  64. package/build/lib/util/workspace-lib.js +30 -3
  65. package/build/lib/util/workspace-lib.js.map +1 -1
  66. package/build/monitor/browser-stubs.d.ts +12 -0
  67. package/build/monitor/browser-stubs.d.ts.map +1 -0
  68. package/build/monitor/browser-stubs.js +20 -0
  69. package/build/monitor/browser-stubs.js.map +1 -0
  70. package/build/monitor/constants.d.ts +9 -0
  71. package/build/monitor/constants.d.ts.map +1 -0
  72. package/build/monitor/constants.js +9 -0
  73. package/build/monitor/constants.js.map +1 -0
  74. package/build/monitor/event-view.d.ts +40 -0
  75. package/build/monitor/event-view.d.ts.map +1 -0
  76. package/build/monitor/event-view.js +104 -0
  77. package/build/monitor/event-view.js.map +1 -0
  78. package/build/monitor/filters.d.ts +17 -0
  79. package/build/monitor/filters.d.ts.map +1 -0
  80. package/build/monitor/filters.js +32 -0
  81. package/build/monitor/filters.js.map +1 -0
  82. package/build/monitor/formatters.d.ts +27 -0
  83. package/build/monitor/formatters.d.ts.map +1 -0
  84. package/build/monitor/formatters.js +89 -0
  85. package/build/monitor/formatters.js.map +1 -0
  86. package/build/monitor/index.d.ts +11 -0
  87. package/build/monitor/index.d.ts.map +1 -0
  88. package/build/monitor/index.js +15 -0
  89. package/build/monitor/index.js.map +1 -0
  90. package/build/monitor/jit-serialization.d.ts +9 -0
  91. package/build/monitor/jit-serialization.d.ts.map +1 -0
  92. package/build/monitor/jit-serialization.js +73 -0
  93. package/build/monitor/jit-serialization.js.map +1 -0
  94. package/build/monitor/monitor-types.d.ts +119 -0
  95. package/build/monitor/monitor-types.d.ts.map +1 -0
  96. package/build/monitor/monitor-types.js +43 -0
  97. package/build/monitor/monitor-types.js.map +1 -0
  98. package/build/monitor/speculative-tracker.d.ts +20 -0
  99. package/build/monitor/speculative-tracker.d.ts.map +1 -0
  100. package/build/monitor/speculative-tracker.js +38 -0
  101. package/build/monitor/speculative-tracker.js.map +1 -0
  102. package/build/monitor/state.d.ts +31 -0
  103. package/build/monitor/state.d.ts.map +1 -0
  104. package/build/monitor/state.js +110 -0
  105. package/build/monitor/state.js.map +1 -0
  106. package/build/monitor/timer.d.ts +11 -0
  107. package/build/monitor/timer.d.ts.map +1 -0
  108. package/build/monitor/timer.js +13 -0
  109. package/build/monitor/timer.js.map +1 -0
  110. package/build/monitor/tree-builder.d.ts +25 -0
  111. package/build/monitor/tree-builder.d.ts.map +1 -0
  112. package/build/monitor/tree-builder.js +36 -0
  113. package/build/monitor/tree-builder.js.map +1 -0
  114. package/build/phases/Executor.d.ts.map +1 -1
  115. package/build/phases/Executor.js +93 -22
  116. package/build/phases/Executor.js.map +1 -1
  117. package/build/phases/Resolver.d.ts +2 -1
  118. package/build/phases/Resolver.d.ts.map +1 -1
  119. package/build/phases/Resolver.js +64 -1
  120. package/build/phases/Resolver.js.map +1 -1
  121. package/build/runner.d.ts.map +1 -1
  122. package/build/runner.js +5 -0
  123. package/build/runner.js.map +1 -1
  124. package/build/schema/events.d.ts +426 -0
  125. package/build/schema/events.d.ts.map +1 -0
  126. package/build/schema/events.js +123 -0
  127. package/build/schema/events.js.map +1 -0
  128. package/build/steps/activities-stepper.d.ts +49 -8
  129. package/build/steps/activities-stepper.d.ts.map +1 -1
  130. package/build/steps/activities-stepper.js +277 -141
  131. package/build/steps/activities-stepper.js.map +1 -1
  132. package/build/steps/conformance.d.ts +1 -0
  133. package/build/steps/conformance.d.ts.map +1 -1
  134. package/build/steps/console-monitor-stepper.d.ts +44 -0
  135. package/build/steps/console-monitor-stepper.d.ts.map +1 -0
  136. package/build/steps/console-monitor-stepper.js +107 -0
  137. package/build/steps/console-monitor-stepper.js.map +1 -0
  138. package/build/steps/debugger-stepper.d.ts +5 -3
  139. package/build/steps/debugger-stepper.d.ts.map +1 -1
  140. package/build/steps/debugger-stepper.js +32 -8
  141. package/build/steps/debugger-stepper.js.map +1 -1
  142. package/build/steps/haibun.d.ts +6 -18
  143. package/build/steps/haibun.d.ts.map +1 -1
  144. package/build/steps/haibun.js +33 -54
  145. package/build/steps/haibun.js.map +1 -1
  146. package/build/steps/logic-stepper.d.ts +42 -0
  147. package/build/steps/logic-stepper.d.ts.map +1 -0
  148. package/build/steps/logic-stepper.js +143 -0
  149. package/build/steps/logic-stepper.js.map +1 -0
  150. package/build/steps/narrator.js +1 -1
  151. package/build/steps/narrator.js.map +1 -1
  152. package/build/steps/parse.d.ts +1 -0
  153. package/build/steps/parse.d.ts.map +1 -1
  154. package/build/steps/variables-stepper.d.ts +11 -75
  155. package/build/steps/variables-stepper.d.ts.map +1 -1
  156. package/build/steps/variables-stepper.js +527 -85
  157. package/build/steps/variables-stepper.js.map +1 -1
  158. package/package.json +16 -3
  159. package/build/applyEffectFeatures.d.ts +0 -4
  160. package/build/applyEffectFeatures.d.ts.map +0 -1
  161. package/build/applyEffectFeatures.js +0 -31
  162. package/build/applyEffectFeatures.js.map +0 -1
  163. package/build/jsprolog/converter.d.ts +0 -9
  164. package/build/jsprolog/converter.d.ts.map +0 -1
  165. package/build/jsprolog/converter.js +0 -42
  166. package/build/jsprolog/converter.js.map +0 -1
  167. package/build/jsprolog/stepper-utils.d.ts +0 -8
  168. package/build/jsprolog/stepper-utils.d.ts.map +0 -1
  169. package/build/jsprolog/stepper-utils.js +0 -8
  170. package/build/jsprolog/stepper-utils.js.map +0 -1
  171. package/build/jsprolog/test.jsprolog.d.ts +0 -4
  172. package/build/jsprolog/test.jsprolog.d.ts.map +0 -1
  173. package/build/jsprolog/test.jsprolog.js +0 -19
  174. package/build/jsprolog/test.jsprolog.js.map +0 -1
  175. package/build/jsprolog/test.kireji.d.ts +0 -4
  176. package/build/jsprolog/test.kireji.d.ts.map +0 -1
  177. package/build/jsprolog/test.kireji.js +0 -19
  178. package/build/jsprolog/test.kireji.js.map +0 -1
  179. package/build/jsprolog/withAction.d.ts +0 -32
  180. package/build/jsprolog/withAction.d.ts.map +0 -1
  181. package/build/jsprolog/withAction.js +0 -65
  182. package/build/jsprolog/withAction.js.map +0 -1
  183. package/build/kireji/test.kireji.d.ts +0 -4
  184. package/build/kireji/test.kireji.d.ts.map +0 -1
  185. package/build/kireji/test.kireji.js +0 -19
  186. package/build/kireji/test.kireji.js.map +0 -1
  187. package/build/lib/errors.d.ts +0 -13
  188. package/build/lib/errors.d.ts.map +0 -1
  189. package/build/lib/errors.js +0 -18
  190. package/build/lib/errors.js.map +0 -1
  191. package/build/lib/util/featureStep-executor.d.ts +0 -7
  192. package/build/lib/util/featureStep-executor.d.ts.map +0 -1
  193. package/build/lib/util/featureStep-executor.js +0 -63
  194. package/build/lib/util/featureStep-executor.js.map +0 -1
  195. package/build/steps/conditions-stepper.d.ts +0 -27
  196. package/build/steps/conditions-stepper.d.ts.map +0 -1
  197. package/build/steps/conditions-stepper.js +0 -99
  198. package/build/steps/conditions-stepper.js.map +0 -1
  199. package/build/steps/outcomesRegistry.d.ts +0 -8
  200. package/build/steps/outcomesRegistry.d.ts.map +0 -1
  201. package/build/steps/outcomesRegistry.js +0 -3
  202. package/build/steps/outcomesRegistry.js.map +0 -1
  203. package/build/steps/registeredOutcomesStore.d.ts +0 -12
  204. package/build/steps/registeredOutcomesStore.d.ts.map +0 -1
  205. package/build/steps/registeredOutcomesStore.js +0 -11
  206. package/build/steps/registeredOutcomesStore.js.map +0 -1
@@ -1,8 +1,9 @@
1
+ import { z } from 'zod';
1
2
  import { OK, Origin } from '../lib/defs.js';
2
3
  import { AStepper } from '../lib/astepper.js';
3
- import { actionNotOK, actionOK } from '../lib/util/index.js';
4
+ import { actionOK, actionNotOK, getStepTerm, isLiteralValue } from '../lib/util/index.js';
4
5
  import { FeatureVariables } from '../lib/feature-variables.js';
5
- import { DOMAIN_STRING } from '../lib/domain-types.js';
6
+ import { DOMAIN_STATEMENT, DOMAIN_STRING, normalizeDomainKey, createEnumDomainDefinition, registerDomains } from '../lib/domain-types.js';
6
7
  import { EExecutionMessageType } from '../lib/interfaces/logger.js';
7
8
  const clearVars = (vars) => () => {
8
9
  vars.getWorld().shared.clear();
@@ -10,8 +11,8 @@ const clearVars = (vars) => () => {
10
11
  };
11
12
  const cycles = (variablesStepper) => ({
12
13
  startFeature: clearVars(variablesStepper),
13
- startScenario: ({ featureVars }) => {
14
- variablesStepper.getWorld().shared = new FeatureVariables(variablesStepper.getWorld(), { ...featureVars.all() });
14
+ startScenario: ({ scopedVars }) => {
15
+ variablesStepper.getWorld().shared = new FeatureVariables(variablesStepper.getWorld(), { ...scopedVars.all() });
15
16
  return Promise.resolve();
16
17
  },
17
18
  });
@@ -23,65 +24,107 @@ class VariablesStepper extends AStepper {
23
24
  this.steppers = steppers;
24
25
  await Promise.resolve();
25
26
  }
26
- checkIsSet(what) {
27
- return this.getVarValue(what) !== undefined;
28
- }
29
- // FIXME provide explicit mapping to more carefully handle env, etc.
30
- getVarValue(what) {
31
- const envVal = this.getWorld().options.envVariables[what];
32
- if (envVal !== undefined) {
33
- return envVal;
34
- }
35
- return this.getWorld().shared.get(what);
36
- }
37
- isSet(what) {
38
- if (this.checkIsSet(what)) {
39
- return OK;
40
- }
41
- return actionNotOK(`${what} not set`);
42
- }
43
27
  steps = {
44
- combineAs: {
45
- gwta: 'combine {p1} and {p2} as {domain} to {what}',
46
- precludes: [`${VariablesStepper.name}.combine`],
47
- action: ({ p1, p2, domain }, featureStep) => {
28
+ defineOpenSet: {
29
+ gwta: `set of {domain: string} as {superdomains: ${DOMAIN_STATEMENT}}`,
30
+ handlesUndefined: ['domain'],
31
+ action: ({ domain, superdomains }, featureStep) => this.registerSubdomainFromStatement(domain, superdomains, featureStep)
32
+ },
33
+ defineOrderedSet: {
34
+ precludes: [`${VariablesStepper.name}.defineValuesSet`, `${VariablesStepper.name}.defineSet`],
35
+ handlesUndefined: ['domain'],
36
+ gwta: `ordered set of {domain: string} is {values:${DOMAIN_STATEMENT}}`,
37
+ action: ({ domain, values }, featureStep) => this.registerValuesDomainFromStatement(domain, values, featureStep, { ordered: true, label: 'ordered set' })
38
+ },
39
+ defineValuesSet: {
40
+ gwta: `set of {domain: string} is {values:${DOMAIN_STATEMENT}}`,
41
+ handlesUndefined: ['domain'],
42
+ action: ({ domain, values }, featureStep) => this.registerValuesDomainFromStatement(domain, values, featureStep, { ordered: false, label: 'set' })
43
+ },
44
+ statementSetValues: {
45
+ expose: false,
46
+ gwta: '\\[{items: string}\\]',
47
+ action: () => OK,
48
+ },
49
+ composeAs: {
50
+ gwta: 'compose {what} as {domain} with {template}',
51
+ handlesUndefined: ['what', 'template'],
52
+ precludes: [`${VariablesStepper.name}.compose`],
53
+ action: ({ domain }, featureStep) => {
48
54
  const { term } = featureStep.action.stepValuesMap.what;
49
- this.getWorld().shared.set({ term: String(term), value: `${p1}${p2}`, domain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
50
- return Promise.resolve(OK);
55
+ const templateVal = featureStep.action.stepValuesMap.template;
56
+ if (!templateVal?.term)
57
+ return actionNotOK('template not provided');
58
+ const result = this.interpolateTemplate(templateVal.term, featureStep);
59
+ if (result.error)
60
+ return actionNotOK(result.error);
61
+ return trySetVariable(this.getWorld().shared, { term: String(term), value: result.value, domain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
51
62
  }
52
63
  },
53
- combine: {
54
- gwta: 'combine {p1} and {p2} to {what}',
55
- action: ({ p1, p2 }, featureStep) => {
64
+ compose: {
65
+ gwta: 'compose {what} with {template}',
66
+ handlesUndefined: ['what', 'template'],
67
+ action: (_, featureStep) => {
56
68
  const { term } = featureStep.action.stepValuesMap.what;
57
- this.getWorld().shared.set({ term: String(term), value: `${p1}${p2}`, domain: DOMAIN_STRING, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
58
- return Promise.resolve(OK);
69
+ const templateVal = featureStep.action.stepValuesMap.template;
70
+ if (!templateVal?.term)
71
+ return actionNotOK('template not provided');
72
+ const result = this.interpolateTemplate(templateVal.term, featureStep);
73
+ if (result.error)
74
+ return actionNotOK(result.error);
75
+ return trySetVariable(this.getWorld().shared, { term: String(term), value: result.value, domain: DOMAIN_STRING, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
59
76
  }
60
77
  },
61
78
  increment: {
62
79
  gwta: 'increment {what}',
63
- action: ({ what }, featureStep) => {
64
- void what; // used for type checking
65
- const { term, domain } = featureStep.action.stepValuesMap.what;
66
- const presentVal = this.getVarValue(term);
67
- let newVal;
80
+ handlesUndefined: ['what'],
81
+ action: (_, featureStep) => {
82
+ const { term: rawTerm, domain } = featureStep.action.stepValuesMap.what;
83
+ const interpolated = this.interpolateTemplate(rawTerm, featureStep);
84
+ if (interpolated.error)
85
+ return actionNotOK(interpolated.error);
86
+ const term = interpolated.value;
87
+ const resolved = this.getWorld().shared.resolveVariable({ term, origin: Origin.var, domain }, featureStep);
88
+ const presentVal = resolved.value;
89
+ const effectiveDomain = resolved.domain;
68
90
  if (presentVal === undefined) {
69
- newVal = 0;
91
+ return actionNotOK(`${term} not set`);
70
92
  }
71
- else {
72
- const numVal = Number(presentVal);
73
- if (isNaN(numVal)) {
74
- return actionNotOK(`cannot increment non-numeric variable ${term} with value "${presentVal}"`);
93
+ // If domain is an ordered enum, advance to the next enum value
94
+ const domainKey = effectiveDomain ? normalizeDomainKey(effectiveDomain) : undefined;
95
+ const registered = domainKey ? this.getWorld().domains[domainKey] : undefined;
96
+ if (registered?.comparator && Array.isArray(registered.values) && registered.values.length) {
97
+ const enumValues = registered.values;
98
+ const idx = enumValues.indexOf(String(presentVal));
99
+ if (idx === -1) {
100
+ return actionNotOK(`${term} has value "${presentVal}" which is not in domain values`);
101
+ }
102
+ const nextIdx = Math.min(enumValues.length - 1, idx + 1);
103
+ const nextVal = enumValues[nextIdx];
104
+ if (nextVal === presentVal) {
105
+ return OK;
75
106
  }
76
- newVal = numVal + 1;
107
+ this.getWorld().shared.set({ term: String(term), value: nextVal, domain: effectiveDomain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
108
+ return OK;
109
+ }
110
+ // Fallback: numeric increment
111
+ const numVal = Number(presentVal);
112
+ if (isNaN(numVal)) {
113
+ return actionNotOK(`cannot increment non-numeric variable ${term} with value "${presentVal}"`);
77
114
  }
78
- this.getWorld().shared.set({ term: String(term), value: newVal, domain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
79
- const messageContext = {
115
+ const newNum = numVal + 1;
116
+ this.getWorld().shared.set({ term: String(term), value: String(newNum), domain: effectiveDomain, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
117
+ this.getWorld().logger.info(`incremented ${term} to ${newNum}`, {
80
118
  incident: EExecutionMessageType.ACTION,
81
- incidentDetails: { json: { incremented: { [term]: newVal } } },
82
- };
83
- this.getWorld().logger.info(`incremented ${term} to ${newVal}`, messageContext);
84
- return Promise.resolve(OK);
119
+ incidentDetails: { json: { incremented: { [term]: newNum } } },
120
+ });
121
+ this.getWorld().eventLogger.log(featureStep, 'info', `incremented ${term} to ${newNum}`, {
122
+ variable: term,
123
+ oldValue: presentVal,
124
+ newValue: newNum,
125
+ operation: 'increment'
126
+ });
127
+ return OK;
85
128
  }
86
129
  },
87
130
  showEnv: {
@@ -96,34 +139,76 @@ class VariablesStepper extends AStepper {
96
139
  showVars: {
97
140
  gwta: 'show vars',
98
141
  action: () => {
99
- console.info('vars', this.getWorld().shared.all());
100
- return Promise.resolve(actionOK({ artifact: { artifactType: 'json', json: { vars: this.getWorld().shared.all() } } }));
142
+ const displayVars = Object.fromEntries(Object.entries(this.getWorld().shared.all()).map(([k, v]) => [k, v.value]));
143
+ console.info('vars', displayVars);
144
+ return actionOK({ artifact: { artifactType: 'json', json: { vars: displayVars } } });
101
145
  },
102
146
  },
103
147
  set: {
104
148
  gwta: 'set( empty)? {what: string} to {value: string}',
149
+ handlesUndefined: ['what', 'value'],
105
150
  precludes: ['Haibun.prose'],
106
151
  action: (args, featureStep) => {
107
- const emptyOnly = !!featureStep.in.match(/set empty /);
108
- const { term, domain, origin } = featureStep.action.stepValuesMap.what;
109
- if (emptyOnly && this.getWorld().shared.get(term) !== undefined) {
110
- return OK;
152
+ const { term: rawTerm, domain, origin } = featureStep.action.stepValuesMap.what;
153
+ const parsedValue = this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep);
154
+ if (parsedValue.value === undefined)
155
+ return actionNotOK(`Variable ${featureStep.action.stepValuesMap.value.term} not found`);
156
+ const resolved = { value: String(parsedValue.value) };
157
+ const interpolated = this.interpolateTemplate(rawTerm, featureStep);
158
+ if (interpolated.error)
159
+ return actionNotOK(interpolated.error);
160
+ const term = interpolated.value;
161
+ const skip = shouldSkipEmpty(featureStep, term, this.getWorld().shared);
162
+ if (skip)
163
+ return skip;
164
+ // Inherit domain from existing variable if not explicitly specified
165
+ const existing = this.getWorld().shared.resolveVariable({ term, origin: Origin.var }, featureStep);
166
+ const effectiveDomain = (domain === DOMAIN_STRING && existing?.domain) ? existing.domain : (domain || DOMAIN_STRING);
167
+ const result = trySetVariable(this.getWorld().shared, { term, value: resolved.value, domain: effectiveDomain, origin }, provenanceFromFeatureStep(featureStep));
168
+ if (result.ok) {
169
+ this.getWorld().eventLogger.log(featureStep, 'info', `set ${term} to ${resolved.value}`, {
170
+ variable: term,
171
+ newValue: resolved.value,
172
+ domain: effectiveDomain,
173
+ operation: 'set'
174
+ });
111
175
  }
112
- this.getWorld().shared.set({ term: String(term), value: args.value, domain, origin }, provenanceFromFeatureStep(featureStep));
113
- return Promise.resolve(OK);
176
+ return result;
114
177
  }
115
178
  },
116
179
  setAs: {
117
- gwta: 'set( empty)? {what: string} as {domain: string} to {value: string}',
180
+ gwta: 'set( empty)? {what} as {domain} to {value}',
181
+ handlesUndefined: ['what', 'domain', 'value'],
118
182
  precludes: [`${VariablesStepper.name}.set`],
119
183
  action: ({ value, domain }, featureStep) => {
120
- const emptyOnly = !!featureStep.in.match(/set empty /);
121
- const { term, origin } = featureStep.action.stepValuesMap.what;
122
- if (emptyOnly && this.getWorld().shared.get(term) !== undefined) {
123
- return OK;
184
+ const readonly = !!featureStep.in.match(/ as read-only /);
185
+ const { term: rawTerm, origin } = featureStep.action.stepValuesMap.what;
186
+ const parsedValue = this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep);
187
+ if (parsedValue.value === undefined)
188
+ return actionNotOK(`Variable ${featureStep.action.stepValuesMap.value.term} not found`);
189
+ const resolved = { value: String(parsedValue.value) };
190
+ const interpolated = this.interpolateTemplate(rawTerm, featureStep);
191
+ if (interpolated.error)
192
+ return actionNotOK(interpolated.error);
193
+ const term = interpolated.value;
194
+ const skip = shouldSkipEmpty(featureStep, term, this.getWorld().shared);
195
+ if (skip)
196
+ return skip;
197
+ // Fallback for unquoted domain names (e.g. 'as number') that resolve to undefined
198
+ let effectiveDomain = domain ?? getStepTerm(featureStep, 'domain');
199
+ if (effectiveDomain) {
200
+ if (effectiveDomain.startsWith('read-only ')) {
201
+ effectiveDomain = effectiveDomain.replace('read-only ', '');
202
+ }
203
+ if (effectiveDomain.startsWith('"') && effectiveDomain.endsWith('"')) {
204
+ effectiveDomain = effectiveDomain.slice(1, -1);
205
+ }
124
206
  }
125
- this.getWorld().shared.set({ term: String(term), value: value, domain, origin }, provenanceFromFeatureStep(featureStep));
126
- return Promise.resolve(OK);
207
+ let finalValue = resolved.value;
208
+ if (typeof finalValue === 'string' && finalValue.startsWith('"') && finalValue.endsWith('"')) {
209
+ finalValue = finalValue.slice(1, -1);
210
+ }
211
+ return trySetVariable(this.getWorld().shared, { term, value: finalValue, domain: effectiveDomain, origin, readonly }, provenanceFromFeatureStep(featureStep));
127
212
  }
128
213
  },
129
214
  unset: {
@@ -137,58 +222,334 @@ class VariablesStepper extends AStepper {
137
222
  setRandom: {
138
223
  precludes: [`${VariablesStepper.name}.set`],
139
224
  gwta: `set( empty)? {what: string} to {length: number} random characters`,
225
+ handlesUndefined: ['what'],
140
226
  action: ({ length }, featureStep) => {
141
- const emptyOnly = !!featureStep.in.match(/set empty /);
142
227
  const { term } = featureStep.action.stepValuesMap.what;
143
228
  if (length < 1 || length > 100) {
144
229
  return actionNotOK(`length ${length} must be between 1 and 100`);
145
230
  }
146
- if (emptyOnly && this.getWorld().shared.get(term) !== undefined) {
147
- return OK;
148
- }
231
+ const skip = shouldSkipEmpty(featureStep, term, this.getWorld().shared);
232
+ if (skip)
233
+ return skip;
149
234
  let rand = '';
150
235
  while (rand.length < length) {
151
236
  rand += Math.random().toString(36).substring(2, 2 + length);
152
237
  }
153
238
  rand = rand.substring(0, length);
154
- this.getWorld().shared.set({ term: String(term), value: rand, domain: DOMAIN_STRING, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
155
- return Promise.resolve(OK);
239
+ return trySetVariable(this.getWorld().shared, { term, value: rand, domain: DOMAIN_STRING, origin: Origin.var }, provenanceFromFeatureStep(featureStep));
156
240
  }
157
241
  },
158
242
  is: {
159
243
  gwta: 'variable {what} is {value}',
244
+ handlesUndefined: ['what', 'value'],
245
+ action: (args, featureStep) => {
246
+ const { term: rawTerm } = featureStep.action.stepValuesMap.what;
247
+ const interpolated = this.interpolateTemplate(rawTerm, featureStep);
248
+ if (interpolated.error)
249
+ return actionNotOK(interpolated.error);
250
+ const term = interpolated.value;
251
+ const resolved = this.getWorld().shared.resolveVariable({ term, origin: Origin.defined }, featureStep);
252
+ if (resolved.value === undefined) {
253
+ return actionNotOK(`${term} is not set`);
254
+ }
255
+ const storedVal = resolved.value;
256
+ const parsedValue = this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep);
257
+ if (parsedValue.value === undefined)
258
+ return actionNotOK(`Variable ${featureStep.action.stepValuesMap.value.term} not found`);
259
+ const value = String(parsedValue.value);
260
+ const domainKey = normalizeDomainKey(resolved.domain);
261
+ const compareVal = this.getWorld().domains[domainKey].coerce({ term: '_cmp', value, domain: domainKey, origin: Origin.quoted }, featureStep, this.steppers);
262
+ return JSON.stringify(storedVal) === JSON.stringify(compareVal) ? OK : actionNotOK(`${term} is ${JSON.stringify(storedVal)}, not ${JSON.stringify(compareVal)}`);
263
+ }
264
+ },
265
+ isLessThan: {
266
+ gwta: 'variable {what} is less than {value}',
267
+ handlesUndefined: ['what', 'value'],
268
+ precludes: ['VariablesStepper.is'],
160
269
  action: ({ what, value }, featureStep) => {
161
- void what; // used for type checking
162
- const { term, domain } = featureStep.action.stepValuesMap.what;
163
- const val = this.getVarValue(term);
164
- const asDomain = this.getWorld().domains[domain].coerce({ domain, value, term, origin: 'quoted' });
165
- return JSON.stringify(val) === JSON.stringify(asDomain) ? OK : actionNotOK(`${term} is ${JSON.stringify(val)}, not ${JSON.stringify(value)}`);
270
+ const term = getStepTerm(featureStep, 'what') ?? what;
271
+ return this.compareValues(featureStep, term, value, '<');
166
272
  }
167
273
  },
168
- isSet: {
274
+ isMoreThan: {
275
+ gwta: 'variable {what} is more than {value}',
276
+ handlesUndefined: ['what', 'value'],
169
277
  precludes: ['VariablesStepper.is'],
170
- gwta: 'variable {what: string} is set',
278
+ action: ({ what, value }, featureStep) => {
279
+ const term = getStepTerm(featureStep, 'what') ?? what;
280
+ return this.compareValues(featureStep, term, value, '>');
281
+ }
282
+ },
283
+ exists: {
284
+ gwta: 'variable {what} exists',
285
+ handlesUndefined: ['what'],
171
286
  action: ({ what }, featureStep) => {
172
- // Use term from stepValuesMap when available (normal execution), fall back to what for kireji
173
- const term = featureStep?.action?.stepValuesMap?.what?.term ?? what;
174
- return this.isSet(term);
287
+ const term = (getStepTerm(featureStep, 'what') ?? what);
288
+ const resolved = this.getWorld().shared.resolveVariable({ term, origin: Origin.defined }, featureStep);
289
+ return resolved && (resolved.origin === Origin.var || resolved.origin === Origin.env) ? OK : actionNotOK(`${what} not set`);
175
290
  }
176
291
  },
177
292
  showVar: {
178
293
  gwta: 'show var {what}',
179
- action: (args, featureStep) => {
180
- const { term } = featureStep.action.stepValuesMap.what;
181
- const stepValue = this.getWorld().shared.all()[term];
182
- if (!stepValue) {
294
+ handlesUndefined: ['what'],
295
+ action: (_, featureStep) => {
296
+ const rawTerm = getStepTerm(featureStep, 'what');
297
+ if (rawTerm === undefined)
298
+ return actionNotOK('variable not provided');
299
+ const interpolated = this.interpolateTemplate(rawTerm, featureStep);
300
+ if (interpolated.error)
301
+ return actionNotOK(interpolated.error);
302
+ const term = interpolated.value;
303
+ const stepValue = this.getWorld().shared.resolveVariable({ term, origin: Origin.defined }, featureStep);
304
+ if (stepValue.value === undefined) {
183
305
  this.getWorld().logger.info(`is undefined`);
184
306
  }
185
307
  else {
186
- this.getWorld().logger.info(`is ${JSON.stringify({ ...stepValue, provenance: stepValue.provenance.map((p, i) => ({ [i]: { in: p.in, seq: p.seq.join(','), when: p.when } })) }, null, 2)}`);
308
+ const provenance = featureStep.action.stepValuesMap.what.provenance?.map((p, i) => ({ [i]: { in: p.in, seq: p.seq.join(','), when: p.when } }));
309
+ this.getWorld().logger.info(`is ${JSON.stringify({ ...stepValue, provenance }, null, 2)}`);
187
310
  }
188
311
  return actionOK({ artifact: { artifactType: 'json', json: { json: stepValue } } });
189
312
  }
190
313
  },
314
+ showDomains: {
315
+ gwta: 'show domains',
316
+ action: () => {
317
+ const domains = this.getWorld().domains;
318
+ const allVars = this.getWorld().shared.all();
319
+ const summary = {};
320
+ for (const [name, def] of Object.entries(domains)) {
321
+ // Count variables in this domain
322
+ let members = 0;
323
+ for (const variable of Object.values(allVars)) {
324
+ if (variable.domain && normalizeDomainKey(variable.domain) === name) {
325
+ members++;
326
+ }
327
+ }
328
+ // Determine type from schema or values
329
+ let type = 'schema';
330
+ if (def.values) {
331
+ type = def.values;
332
+ }
333
+ else if (def.description) {
334
+ type = def.description;
335
+ }
336
+ summary[name] = {
337
+ type,
338
+ members,
339
+ ordered: !!def.comparator
340
+ };
341
+ }
342
+ this.getWorld().logger.info(`Domains: ${JSON.stringify(summary, null, 2)}`);
343
+ return OK;
344
+ }
345
+ },
346
+ showDomain: {
347
+ gwta: 'show domain {name}',
348
+ handlesUndefined: ['name'],
349
+ action: (_, featureStep) => {
350
+ const name = getStepTerm(featureStep, 'name');
351
+ const domain = this.getWorld().domains[name];
352
+ if (!domain) {
353
+ return actionNotOK(`Domain "${name}" not found`);
354
+ }
355
+ const allVars = this.getWorld().shared.all();
356
+ const members = {};
357
+ for (const [key, variable] of Object.entries(allVars)) {
358
+ if (variable.domain && normalizeDomainKey(variable.domain) === name) {
359
+ members[key] = variable.value;
360
+ }
361
+ }
362
+ this.getWorld().logger.info(`Domain "${name}": ${JSON.stringify({ ...domain, members }, null, 2)}`);
363
+ return OK;
364
+ }
365
+ },
366
+ // Membership check: value is in domain (enum or member values)
367
+ // Handles quoted ("value"), braced ({var}), or bare (value) forms
368
+ // fallback: true lets quantifiers take precedence when both match
369
+ isIn: {
370
+ match: /^(.+) is in ([a-zA-Z][a-zA-Z0-9 ]*)$/,
371
+ fallback: true,
372
+ action: (_, featureStep) => {
373
+ const matchResult = featureStep.in.match(/^(.+) is in ([a-zA-Z][a-zA-Z0-9 ]*)$/);
374
+ if (!matchResult) {
375
+ return actionNotOK('Invalid "is in" syntax');
376
+ }
377
+ let valueTerm = matchResult[1].trim();
378
+ // Strip quotes if present
379
+ if ((valueTerm.startsWith('"') && valueTerm.endsWith('"')) ||
380
+ (valueTerm.startsWith('`') && valueTerm.endsWith('`'))) {
381
+ valueTerm = valueTerm.slice(1, -1);
382
+ }
383
+ // Strip braces if present and resolve variable
384
+ if (valueTerm.startsWith('{') && valueTerm.endsWith('}')) {
385
+ valueTerm = valueTerm.slice(1, -1);
386
+ }
387
+ // Try to resolve as variable, fall back to literal
388
+ const resolved = this.getWorld().shared.resolveVariable({ term: valueTerm, origin: Origin.defined });
389
+ const actualValue = resolved?.value !== undefined ? String(resolved.value) : valueTerm;
390
+ const domainName = matchResult[2].trim();
391
+ const domainKey = normalizeDomainKey(domainName);
392
+ const domainDef = this.getWorld().domains[domainKey];
393
+ if (!domainDef) {
394
+ return actionNotOK(`Domain "${domainName}" is not defined`);
395
+ }
396
+ // Check enum values first
397
+ if (Array.isArray(domainDef.values) && domainDef.values.includes(actualValue)) {
398
+ return OK;
399
+ }
400
+ // Check member values
401
+ const allVars = this.getWorld().shared.all();
402
+ const memberValues = Object.values(allVars)
403
+ .filter(v => v.domain && normalizeDomainKey(v.domain) === domainKey)
404
+ .map(v => String(v.value));
405
+ return memberValues.includes(actualValue)
406
+ ? OK
407
+ : actionNotOK(`"${actualValue}" is not in ${domainName}`);
408
+ }
409
+ },
410
+ // Starts with: predicate for prefix checking
411
+ // Starts with literal 'that' for unambiguous parsing in compositions
412
+ // Usage: that {page} starts with {prefix}
413
+ startsWith: {
414
+ gwta: 'that {value} starts with {prefix}',
415
+ action: ({ value, prefix }) => {
416
+ const actualValue = String(value);
417
+ const actualPrefix = String(prefix);
418
+ return actualValue.startsWith(actualPrefix)
419
+ ? OK
420
+ : actionNotOK(`"${actualValue}" does not start with "${actualPrefix}"`);
421
+ }
422
+ },
191
423
  };
424
+ compareValues(featureStep, rawTerm, value, operator) {
425
+ const interpolated = this.interpolateTemplate(rawTerm, featureStep);
426
+ if (interpolated.error)
427
+ return actionNotOK(interpolated.error);
428
+ const term = interpolated.value;
429
+ const stored = this.getWorld().shared.resolveVariable({ term, origin: Origin.var }, featureStep);
430
+ if (!stored) {
431
+ return actionNotOK(`${term} is not set`);
432
+ }
433
+ const domainKey = normalizeDomainKey(stored.domain);
434
+ const domainEntry = this.getWorld().domains[domainKey];
435
+ if (!domainEntry) {
436
+ throw new Error(`No domain coercer found for domain "${domainKey}"`);
437
+ }
438
+ const left = domainEntry.coerce({ ...stored, domain: domainKey }, featureStep, this.steppers);
439
+ let rightValue = value;
440
+ if (rightValue === undefined) {
441
+ const parsed = this.getWorld().shared.resolveVariable(featureStep.action.stepValuesMap.value, featureStep);
442
+ if (parsed.value === undefined)
443
+ return actionNotOK(`Variable ${featureStep.action.stepValuesMap.value.term} not found`);
444
+ rightValue = String(parsed.value);
445
+ }
446
+ if (typeof rightValue === 'string' && rightValue.startsWith('"') && rightValue.endsWith('"')) {
447
+ rightValue = rightValue.slice(1, -1);
448
+ }
449
+ const right = domainEntry.coerce({ term: `${term}__comparison`, value: rightValue, domain: domainKey, origin: Origin.quoted }, featureStep, this.steppers);
450
+ const comparison = compareDomainValues(domainEntry, left, right, stored.domain);
451
+ if (operator === '>') {
452
+ return comparison > 0 ? OK : actionNotOK(`${term} is ${JSON.stringify(left)}, not ${JSON.stringify(right)}`);
453
+ }
454
+ if (operator === '<') {
455
+ return comparison < 0 ? OK : actionNotOK(`${term} is ${JSON.stringify(left)}, not ${JSON.stringify(right)}`);
456
+ }
457
+ return actionNotOK(`Unsupported operator: ${operator}`);
458
+ }
459
+ /**
460
+ * Interpolates a template string by replacing {varName} placeholders with variable values.
461
+ * Returns the interpolated string or an error if a variable is not found.
462
+ */
463
+ interpolateTemplate(template, featureStep) {
464
+ const placeholderRegex = /\{([^}]+)\}/g;
465
+ let result = template;
466
+ let match;
467
+ while ((match = placeholderRegex.exec(template)) !== null) {
468
+ const varName = match[1];
469
+ // Origin.defined checks runtimeArgs -> env -> literal fallback?
470
+ // We want strict. If resolves to literal, that's fine if user quoted it?
471
+ // But {unknown} resolves to undefined if not found?
472
+ // In feature-variables.ts, resolveVariable defaults to input.term if isLiteralValue.
473
+ // {unknown} is NOT literal value (braces).
474
+ // So resolveVariable returns undefined.
475
+ const resolved = this.getWorld().shared.resolveVariable({ term: varName, origin: Origin.defined }, featureStep);
476
+ if (resolved.value === undefined) {
477
+ return { error: `Variable ${varName} not found` };
478
+ }
479
+ result = result.replace(match[0], String(resolved.value));
480
+ }
481
+ return { value: result };
482
+ }
483
+ registerSubdomainFromStatement(domain, superdomains, featureStep) {
484
+ try {
485
+ const fallback = getStepTerm(featureStep, 'superdomains') ?? featureStep.in;
486
+ const superdomainNames = extractValuesFromFragments(superdomains, fallback);
487
+ if (!superdomainNames.length) {
488
+ throw new Error('Superdomain set must specify at least one superdomain');
489
+ }
490
+ const uniqueNames = Array.from(new Set(superdomainNames));
491
+ const effectiveDomain = domain ?? getStepTerm(featureStep, 'domain');
492
+ if (!effectiveDomain)
493
+ return actionNotOK('Domain name must be provided');
494
+ const domainKey = normalizeDomainKey(effectiveDomain);
495
+ if (this.getWorld().domains[domainKey]) {
496
+ return actionNotOK(`Domain "${domainKey}" already exists`);
497
+ }
498
+ const superdomainDefs = uniqueNames.map((name) => {
499
+ const normalized = normalizeDomainKey(name);
500
+ const registered = this.getWorld().domains[normalized];
501
+ if (!registered) {
502
+ throw new Error(`Superdomain "${name}" not registered`);
503
+ }
504
+ return registered;
505
+ });
506
+ const enumSources = superdomainDefs.filter((entry) => Array.isArray(entry.values) && entry.values.length);
507
+ const uniqueValues = Array.from(new Set(enumSources.flatMap((entry) => entry.values)));
508
+ const description = `Values inherited from ${uniqueNames.join(', ')}`;
509
+ if (enumSources.length === superdomainDefs.length && uniqueValues.length) {
510
+ const definition = createEnumDomainDefinition({ name: domainKey, values: uniqueValues, description });
511
+ registerDomains(this.getWorld(), [[definition]]);
512
+ return OK;
513
+ }
514
+ const schemaList = superdomainDefs.map((entry) => entry.schema);
515
+ if (!schemaList.length) {
516
+ throw new Error('Superdomains did not expose any schema to derive from');
517
+ }
518
+ let mergedSchema = schemaList[0];
519
+ for (let i = 1; i < schemaList.length; i++) {
520
+ mergedSchema = z.union([mergedSchema, schemaList[i]]);
521
+ }
522
+ const definition = {
523
+ selectors: [domainKey],
524
+ schema: mergedSchema,
525
+ coerce: (proto) => mergedSchema.parse(proto.value),
526
+ description,
527
+ };
528
+ registerDomains(this.getWorld(), [[definition]]);
529
+ return OK;
530
+ }
531
+ catch (error) {
532
+ return actionNotOK(error instanceof Error ? error.message : String(error));
533
+ }
534
+ }
535
+ registerValuesDomainFromStatement(domain, valueFragments, featureStep, options) {
536
+ try {
537
+ const values = extractValuesFromFragments(valueFragments, getStepTerm(featureStep, 'values') ?? featureStep.in);
538
+ const effectiveDomain = domain ?? getStepTerm(featureStep, 'domain');
539
+ if (!effectiveDomain)
540
+ return actionNotOK('Domain name must be provided');
541
+ const domainKey = normalizeDomainKey(effectiveDomain);
542
+ if (this.getWorld().domains[domainKey]) {
543
+ return actionNotOK(`Domain "${domainKey}" already exists`);
544
+ }
545
+ const definition = createEnumDomainDefinition({ name: domainKey, values, description: options?.description, ordered: options?.ordered });
546
+ registerDomains(this.getWorld(), [[definition]]);
547
+ return OK;
548
+ }
549
+ catch (error) {
550
+ return actionNotOK(error instanceof Error ? error.message : String(error));
551
+ }
552
+ }
192
553
  }
193
554
  export default VariablesStepper;
194
555
  export const didNotOverwrite = (what, present, value) => ({
@@ -201,4 +562,85 @@ export function provenanceFromFeatureStep(featureStep) {
201
562
  when: `${featureStep.action.stepperName}.steps.${featureStep.action.actionName}`
202
563
  };
203
564
  }
565
+ const QUOTED_STRING = /"([^"]+)"/g;
566
+ const extractValuesFromFragments = (valueFragments, fallback) => {
567
+ if (valueFragments?.length) {
568
+ const innerChunks = valueFragments.map(fragment => {
569
+ const raw = fragment.in ?? fragment.action?.stepValuesMap?.items?.term ?? '';
570
+ const trimmed = raw.trim();
571
+ if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
572
+ return trimmed.slice(1, -1).trim();
573
+ }
574
+ return trimmed;
575
+ }).filter(Boolean);
576
+ const inner = innerChunks.join(' ').trim();
577
+ if (!inner) {
578
+ throw new Error('Set values cannot be empty');
579
+ }
580
+ return parseQuotedOrWordList(inner);
581
+ }
582
+ if (fallback) {
583
+ return parseBracketedValues(fallback);
584
+ }
585
+ throw new Error('Set statement missing values');
586
+ };
587
+ const parseBracketedValues = (raw) => {
588
+ const trimmed = raw.trim();
589
+ const start = trimmed.indexOf('[');
590
+ const end = trimmed.lastIndexOf(']');
591
+ if (start === -1 || end === -1 || end <= start) {
592
+ throw new Error('Set values must include [ ]');
593
+ }
594
+ const inner = trimmed.substring(start + 1, end).trim();
595
+ return parseQuotedOrWordList(inner);
596
+ };
597
+ const parseQuotedOrWordList = (value) => {
598
+ const quoted = [...value.matchAll(QUOTED_STRING)].map(match => match[1].trim()).filter(Boolean);
599
+ if (quoted.length) {
600
+ return quoted;
601
+ }
602
+ return value.split(/[\s,]+/).map(token => token.trim()).filter(Boolean);
603
+ };
604
+ const compareDomainValues = (domain, left, right, domainName) => {
605
+ if (domain.comparator) {
606
+ return domain.comparator(left, right);
607
+ }
608
+ if (typeof left === 'number' && typeof right === 'number') {
609
+ return left - right;
610
+ }
611
+ if (left instanceof Date && right instanceof Date) {
612
+ return left.getTime() - right.getTime();
613
+ }
614
+ throw new Error(`Domain ${domainName} does not support magnitude comparison`);
615
+ };
616
+ const renderComparable = (value) => {
617
+ if (value instanceof Date) {
618
+ return value.toISOString();
619
+ }
620
+ return JSON.stringify(value);
621
+ };
622
+ // ======== Helpers ========
623
+ // Resolves undefined value to literal if it looks like a string
624
+ function resolveValueOrLiteral(valMap, value) {
625
+ if (valMap && valMap.origin === Origin.defined && value === undefined) {
626
+ return isLiteralValue(valMap.term)
627
+ ? { value: String(valMap.term) }
628
+ : { value: undefined, error: `Variable ${valMap.term} is not defined` };
629
+ }
630
+ return { value };
631
+ }
632
+ // Returns OK if "set empty" and variable exists
633
+ function shouldSkipEmpty(featureStep, term, shared) {
634
+ return (featureStep.in.includes('set empty ') && shared.get(term) !== undefined) ? OK : undefined;
635
+ }
636
+ // Wraps shared.set in try/catch
637
+ function trySetVariable(shared, opts, provenance) {
638
+ try {
639
+ shared.set({ term: String(opts.term), value: opts.value, domain: opts.domain, origin: opts.origin, readonly: opts.readonly }, provenance);
640
+ return OK;
641
+ }
642
+ catch (e) {
643
+ return actionNotOK(e.message);
644
+ }
645
+ }
204
646
  //# sourceMappingURL=variables-stepper.js.map