@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.
- package/build/currentVersion.d.ts +1 -1
- package/build/currentVersion.js +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/kireji/converter.d.ts +2 -2
- package/build/kireji/converter.d.ts.map +1 -1
- package/build/kireji/converter.js +12 -9
- package/build/kireji/converter.js.map +1 -1
- package/build/kireji/withAction.d.ts +16 -4
- package/build/kireji/withAction.d.ts.map +1 -1
- package/build/kireji/withAction.js +40 -11
- package/build/kireji/withAction.js.map +1 -1
- package/build/lib/EventLogger.d.ts +5 -5
- package/build/lib/EventLogger.d.ts.map +1 -1
- package/build/lib/EventLogger.js +39 -37
- package/build/lib/EventLogger.js.map +1 -1
- package/build/lib/PhaseRunner.d.ts +3 -3
- package/build/lib/PhaseRunner.d.ts.map +1 -1
- package/build/lib/PhaseRunner.js +7 -5
- package/build/lib/PhaseRunner.js.map +1 -1
- package/build/lib/astepper.d.ts +90 -13
- package/build/lib/astepper.d.ts.map +1 -1
- package/build/lib/astepper.js +35 -6
- package/build/lib/astepper.js.map +1 -1
- package/build/lib/base-prompt-manager.d.ts +1 -1
- package/build/lib/base-prompt-manager.d.ts.map +1 -1
- package/build/lib/base-prompt-manager.js.map +1 -1
- package/build/lib/capture-locator.d.ts +1 -1
- package/build/lib/capture-locator.d.ts.map +1 -1
- package/build/lib/capture-locator.js +5 -5
- package/build/lib/capture-locator.js.map +1 -1
- package/build/lib/core/flow-runner.d.ts +8 -7
- package/build/lib/core/flow-runner.d.ts.map +1 -1
- package/build/lib/core/flow-runner.js +42 -71
- package/build/lib/core/flow-runner.js.map +1 -1
- package/build/lib/core-domains.d.ts +2 -2
- package/build/lib/core-domains.d.ts.map +1 -1
- package/build/lib/core-domains.js +17 -18
- package/build/lib/core-domains.js.map +1 -1
- package/build/lib/defs.d.ts +2 -13
- package/build/lib/defs.d.ts.map +1 -1
- package/build/lib/defs.js.map +1 -1
- package/build/lib/document-content.d.ts +18 -0
- package/build/lib/document-content.d.ts.map +1 -0
- package/build/lib/document-content.js +158 -0
- package/build/lib/document-content.js.map +1 -0
- package/build/lib/domains.d.ts +31 -0
- package/build/lib/domains.d.ts.map +1 -0
- package/build/lib/domains.js +105 -0
- package/build/lib/domains.js.map +1 -0
- package/build/lib/execution.d.ts +304 -0
- package/build/lib/execution.d.ts.map +1 -0
- package/build/lib/execution.js +23 -0
- package/build/lib/execution.js.map +1 -0
- package/build/lib/feature-variables.d.ts +22 -49
- package/build/lib/feature-variables.d.ts.map +1 -1
- package/build/lib/feature-variables.js +143 -210
- package/build/lib/feature-variables.js.map +1 -1
- package/build/lib/features.d.ts +2 -2
- package/build/lib/features.d.ts.map +1 -1
- package/build/lib/features.js +13 -17
- package/build/lib/features.js.map +1 -1
- package/build/lib/fixme.d.ts.map +1 -1
- package/build/lib/host-id.d.ts +44 -0
- package/build/lib/host-id.d.ts.map +1 -0
- package/build/lib/host-id.js +51 -0
- package/build/lib/host-id.js.map +1 -0
- package/build/lib/http-observations.d.ts +15 -2
- package/build/lib/http-observations.d.ts.map +1 -1
- package/build/lib/http-observations.js +40 -23
- package/build/lib/http-observations.js.map +1 -1
- package/build/lib/hypermedia.d.ts +62 -0
- package/build/lib/hypermedia.d.ts.map +1 -0
- package/build/lib/hypermedia.js +228 -0
- package/build/lib/hypermedia.js.map +1 -0
- package/build/lib/namedVars.d.ts +2 -2
- package/build/lib/namedVars.d.ts.map +1 -1
- package/build/lib/namedVars.js +25 -30
- package/build/lib/namedVars.js.map +1 -1
- package/build/lib/node-http-events.d.ts +3 -3
- package/build/lib/node-http-events.d.ts.map +1 -1
- package/build/lib/node-http-events.js +26 -26
- package/build/lib/node-http-events.js.map +1 -1
- package/build/lib/populateActionArgs.d.ts +4 -4
- package/build/lib/populateActionArgs.d.ts.map +1 -1
- package/build/lib/populateActionArgs.js +4 -6
- package/build/lib/populateActionArgs.js.map +1 -1
- package/build/lib/prompter.d.ts.map +1 -1
- package/build/lib/prompter.js +10 -7
- package/build/lib/prompter.js.map +1 -1
- package/build/lib/quad-store.d.ts +34 -10
- package/build/lib/quad-store.d.ts.map +1 -1
- package/build/lib/quad-store.js +169 -21
- package/build/lib/quad-store.js.map +1 -1
- package/build/lib/quad-types.d.ts +80 -9
- package/build/lib/quad-types.d.ts.map +1 -1
- package/build/lib/quad-types.js +46 -2
- package/build/lib/quad-types.js.map +1 -1
- package/build/lib/readline-prompter.d.ts +1 -1
- package/build/lib/readline-prompter.js +4 -4
- package/build/lib/remote-stepper-proxy.d.ts +56 -0
- package/build/lib/remote-stepper-proxy.d.ts.map +1 -0
- package/build/lib/remote-stepper-proxy.js +123 -0
- package/build/lib/remote-stepper-proxy.js.map +1 -0
- package/build/lib/resolver-features.d.ts +1 -1
- package/build/lib/resolver-features.d.ts.map +1 -1
- package/build/lib/resolver-features.js +2 -2
- package/build/lib/resolver-features.js.map +1 -1
- package/build/lib/resources.d.ts +461 -0
- package/build/lib/resources.d.ts.map +1 -0
- package/build/lib/resources.js +249 -0
- package/build/lib/resources.js.map +1 -0
- package/build/lib/rpc-client.d.ts +68 -0
- package/build/lib/rpc-client.d.ts.map +1 -0
- package/build/lib/rpc-client.js +186 -0
- package/build/lib/rpc-client.js.map +1 -0
- package/build/lib/sse-subscriber.d.ts +57 -0
- package/build/lib/sse-subscriber.d.ts.map +1 -0
- package/build/lib/sse-subscriber.js +110 -0
- package/build/lib/sse-subscriber.js.map +1 -0
- package/build/lib/step-dispatch.d.ts +92 -28
- package/build/lib/step-dispatch.d.ts.map +1 -1
- package/build/lib/step-dispatch.js +331 -75
- package/build/lib/step-dispatch.js.map +1 -1
- package/build/lib/step-validation.d.ts +3 -3
- package/build/lib/step-validation.d.ts.map +1 -1
- package/build/lib/step-validation.js +2 -2
- package/build/lib/step-validation.js.map +1 -1
- package/build/lib/stepper-cycles.d.ts +5 -0
- package/build/lib/stepper-cycles.d.ts.map +1 -0
- package/build/lib/stepper-cycles.js +31 -0
- package/build/lib/stepper-cycles.js.map +1 -0
- package/build/lib/stepper-registry.d.ts +6 -3
- package/build/lib/stepper-registry.d.ts.map +1 -1
- package/build/lib/stepper-registry.js +9 -8
- package/build/lib/stepper-registry.js.map +1 -1
- package/build/lib/subprocess-runner.d.ts +35 -0
- package/build/lib/subprocess-runner.d.ts.map +1 -0
- package/build/lib/subprocess-runner.js +60 -0
- package/build/lib/subprocess-runner.js.map +1 -0
- package/build/lib/subprocess-transport.d.ts +37 -0
- package/build/lib/subprocess-transport.d.ts.map +1 -0
- package/build/lib/subprocess-transport.js +113 -0
- package/build/lib/subprocess-transport.js.map +1 -0
- package/build/lib/test/EventCollectorStepper.d.ts +3 -3
- package/build/lib/test/EventCollectorStepper.d.ts.map +1 -1
- package/build/lib/test/EventCollectorStepper.js +3 -3
- package/build/lib/test/EventCollectorStepper.js.map +1 -1
- package/build/lib/test/SetTimeStepper.d.ts +3 -3
- package/build/lib/test/SetTimeStepper.d.ts.map +1 -1
- package/build/lib/test/SetTimeStepper.js +7 -7
- package/build/lib/test/SetTimeStepper.js.map +1 -1
- package/build/lib/test/TestSteps.d.ts +7 -7
- package/build/lib/test/TestSteps.d.ts.map +1 -1
- package/build/lib/test/TestSteps.js +7 -7
- package/build/lib/test/TestSteps.js.map +1 -1
- package/build/lib/test/TestStepsWithOptions.d.ts +6 -6
- package/build/lib/test/TestStepsWithOptions.js +7 -7
- package/build/lib/test/lib.d.ts +3 -3
- package/build/lib/test/lib.d.ts.map +1 -1
- package/build/lib/test/lib.js +27 -21
- package/build/lib/test/lib.js.map +1 -1
- package/build/lib/test/resolvedTestFeatures.d.ts +6 -6
- package/build/lib/test/resolvedTestFeatures.d.ts.map +1 -1
- package/build/lib/test/resolvedTestFeatures.js +5 -5
- package/build/lib/test/subprocess-fixture.d.ts +8 -0
- package/build/lib/test/subprocess-fixture.d.ts.map +1 -0
- package/build/lib/test/subprocess-fixture.js +27 -0
- package/build/lib/test/subprocess-fixture.js.map +1 -0
- package/build/lib/ttag.d.ts +7 -1
- package/build/lib/ttag.d.ts.map +1 -1
- package/build/lib/ttag.js +5 -4
- package/build/lib/ttag.js.map +1 -1
- package/build/lib/util/dot-path.d.ts +14 -0
- package/build/lib/util/dot-path.d.ts.map +1 -0
- package/build/lib/util/dot-path.js +51 -0
- package/build/lib/util/dot-path.js.map +1 -0
- package/build/lib/util/index.d.ts +10 -24
- package/build/lib/util/index.d.ts.map +1 -1
- package/build/lib/util/index.js +43 -93
- package/build/lib/util/index.js.map +1 -1
- package/build/lib/util/node/actualURI.d.ts +4 -0
- package/build/lib/util/node/actualURI.d.ts.map +1 -0
- package/build/lib/util/node/actualURI.js +10 -0
- package/build/lib/util/node/actualURI.js.map +1 -0
- package/build/lib/util/node/module-loader.d.ts +14 -0
- package/build/lib/util/node/module-loader.d.ts.map +1 -0
- package/build/lib/util/node/module-loader.js +51 -0
- package/build/lib/util/node/module-loader.js.map +1 -0
- package/build/lib/util/node/workspace-lib.d.ts +15 -0
- package/build/lib/util/node/workspace-lib.d.ts.map +1 -0
- package/build/lib/util/node/workspace-lib.js +119 -0
- package/build/lib/util/node/workspace-lib.js.map +1 -0
- package/build/lib/util/secret-utils.d.ts.map +1 -1
- package/build/lib/util/secret-utils.js +2 -2
- package/build/lib/util/secret-utils.js.map +1 -1
- package/build/lib/vertex-crud.d.ts +22 -0
- package/build/lib/vertex-crud.d.ts.map +1 -0
- package/build/lib/vertex-crud.js +73 -0
- package/build/lib/vertex-crud.js.map +1 -0
- package/build/lib/workspace-discovery.d.ts +1 -1
- package/build/lib/workspace-discovery.d.ts.map +1 -1
- package/build/lib/workspace-discovery.js +10 -10
- package/build/lib/workspace-discovery.js.map +1 -1
- package/build/lib/zcap-like-authority.d.ts +38 -0
- package/build/lib/zcap-like-authority.d.ts.map +1 -0
- package/build/lib/zcap-like-authority.js +56 -0
- package/build/lib/zcap-like-authority.js.map +1 -0
- package/build/monitor/index.d.ts +1 -1
- package/build/monitor/index.d.ts.map +1 -1
- package/build/monitor/index.js +1 -1
- package/build/monitor/index.js.map +1 -1
- package/build/phases/Executor.d.ts +31 -7
- package/build/phases/Executor.d.ts.map +1 -1
- package/build/phases/Executor.js +202 -217
- package/build/phases/Executor.js.map +1 -1
- package/build/phases/Resolver.d.ts +2 -2
- package/build/phases/Resolver.d.ts.map +1 -1
- package/build/phases/Resolver.js +35 -30
- package/build/phases/Resolver.js.map +1 -1
- package/build/phases/collector.d.ts +3 -3
- package/build/phases/collector.d.ts.map +1 -1
- package/build/phases/collector.js +19 -17
- package/build/phases/collector.js.map +1 -1
- package/build/run-policy/run-policy-schema.d.ts +3 -5
- package/build/run-policy/run-policy-schema.d.ts.map +1 -1
- package/build/run-policy/run-policy-schema.js +41 -37
- package/build/run-policy/run-policy-schema.js.map +1 -1
- package/build/run-policy/run-policy-types.d.ts +1 -1
- package/build/run-policy/run-policy-types.d.ts.map +1 -1
- package/build/run-policy/run-policy-types.js +19 -16
- package/build/run-policy/run-policy-types.js.map +1 -1
- package/build/runner.d.ts +4 -4
- package/build/runner.d.ts.map +1 -1
- package/build/runner.js +15 -13
- package/build/runner.js.map +1 -1
- package/build/schema/protocol.d.ts +155 -63
- package/build/schema/protocol.d.ts.map +1 -1
- package/build/schema/protocol.js +177 -148
- package/build/schema/protocol.js.map +1 -1
- package/build/steps/activities-stepper.d.ts +14 -13
- package/build/steps/activities-stepper.d.ts.map +1 -1
- package/build/steps/activities-stepper.js +116 -78
- package/build/steps/activities-stepper.js.map +1 -1
- package/build/steps/conformance.d.ts +5 -5
- package/build/steps/conformance.js +4 -4
- package/build/steps/console-monitor-stepper.d.ts +2 -2
- package/build/steps/console-monitor-stepper.d.ts.map +1 -1
- package/build/steps/console-monitor-stepper.js +22 -22
- package/build/steps/console-monitor-stepper.js.map +1 -1
- package/build/steps/debugger-stepper.d.ts +11 -11
- package/build/steps/debugger-stepper.d.ts.map +1 -1
- package/build/steps/debugger-stepper.js +65 -64
- package/build/steps/debugger-stepper.js.map +1 -1
- package/build/steps/finalizer-stepper.d.ts +3 -4
- package/build/steps/finalizer-stepper.d.ts.map +1 -1
- package/build/steps/finalizer-stepper.js +16 -19
- package/build/steps/finalizer-stepper.js.map +1 -1
- package/build/steps/haibun.d.ts +27 -20
- package/build/steps/haibun.d.ts.map +1 -1
- package/build/steps/haibun.js +76 -67
- package/build/steps/haibun.js.map +1 -1
- package/build/steps/lib/tts.d.ts +1 -1
- package/build/steps/lib/tts.d.ts.map +1 -1
- package/build/steps/lib/tts.js +28 -28
- package/build/steps/lib/tts.js.map +1 -1
- package/build/steps/logic-stepper.d.ts +5 -5
- package/build/steps/logic-stepper.d.ts.map +1 -1
- package/build/steps/logic-stepper.js +76 -76
- package/build/steps/logic-stepper.js.map +1 -1
- package/build/steps/narrator.d.ts +9 -9
- package/build/steps/narrator.d.ts.map +1 -1
- package/build/steps/narrator.js +32 -28
- package/build/steps/narrator.js.map +1 -1
- package/build/steps/resources-stepper.d.ts +47 -0
- package/build/steps/resources-stepper.d.ts.map +1 -0
- package/build/steps/resources-stepper.js +100 -0
- package/build/steps/resources-stepper.js.map +1 -0
- package/build/steps/variables-stepper.d.ts +82 -54
- package/build/steps/variables-stepper.d.ts.map +1 -1
- package/build/steps/variables-stepper.js +264 -216
- package/build/steps/variables-stepper.js.map +1 -1
- package/build/steps/zcap-like-stepper.d.ts +50 -0
- package/build/steps/zcap-like-stepper.d.ts.map +1 -0
- package/build/steps/zcap-like-stepper.js +113 -0
- package/build/steps/zcap-like-stepper.js.map +1 -0
- package/package.json +1 -1
- package/build/lib/consts.d.ts +0 -5
- package/build/lib/consts.d.ts.map +0 -1
- package/build/lib/consts.js +0 -5
- package/build/lib/consts.js.map +0 -1
- package/build/lib/step-helpers.d.ts +0 -10
- package/build/lib/step-helpers.d.ts.map +0 -1
- package/build/lib/step-helpers.js +0 -16
- package/build/lib/step-helpers.js.map +0 -1
|
@@ -1,91 +1,105 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { OK, Origin } from
|
|
3
|
-
import { AStepper } from
|
|
4
|
-
import { actionOK, actionNotOK, getStepTerm } from
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 =
|
|
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: [
|
|
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: [
|
|
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:
|
|
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: [
|
|
43
|
-
action: ({ domain, values }, featureStep) => this.registerValuesDomainFromStatement(domain, values, featureStep, { ordered: false, label:
|
|
43
|
+
handlesUndefined: ["domain"],
|
|
44
|
+
action: ({ domain, values }, featureStep) => this.registerValuesDomainFromStatement(domain, values, featureStep, { ordered: false, label: "set" }),
|
|
44
45
|
},
|
|
45
46
|
statementSetValues: {
|
|
46
|
-
|
|
47
|
-
gwta:
|
|
47
|
+
exposeMCP: false,
|
|
48
|
+
gwta: "\\[{items: string}\\]",
|
|
48
49
|
action: () => OK,
|
|
49
50
|
},
|
|
50
51
|
composeAs: {
|
|
51
|
-
gwta:
|
|
52
|
-
handlesUndefined: [
|
|
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(
|
|
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:
|
|
67
|
-
handlesUndefined: [
|
|
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(
|
|
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:
|
|
81
|
-
handlesUndefined: [
|
|
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,
|
|
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:
|
|
136
|
+
operation: "increment",
|
|
123
137
|
});
|
|
124
138
|
return OK;
|
|
125
|
-
}
|
|
139
|
+
},
|
|
126
140
|
},
|
|
127
141
|
showEnv: {
|
|
128
|
-
gwta:
|
|
129
|
-
|
|
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
|
-
|
|
136
|
-
return
|
|
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:
|
|
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
|
-
|
|
146
|
-
return
|
|
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:
|
|
151
|
-
handlesUndefined: [
|
|
152
|
-
precludes: [
|
|
153
|
-
action: (
|
|
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, {
|
|
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 =
|
|
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:
|
|
175
|
-
handlesUndefined: [
|
|
189
|
+
gwta: "set( empty)? {what} as {domain} to {value}",
|
|
190
|
+
handlesUndefined: ["what", "domain", "value"],
|
|
176
191
|
precludes: [`${VariablesStepper.name}.set`],
|
|
177
|
-
action: ({
|
|
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, {
|
|
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,
|
|
209
|
+
let effectiveDomain = domain ?? getStepTerm(featureStep, "domain");
|
|
193
210
|
if (effectiveDomain) {
|
|
194
|
-
if (effectiveDomain.startsWith(
|
|
195
|
-
effectiveDomain = effectiveDomain.replace(
|
|
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 ===
|
|
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:
|
|
210
|
-
action: (
|
|
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
|
|
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: [
|
|
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()
|
|
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:
|
|
238
|
-
handlesUndefined: [
|
|
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, {
|
|
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, {
|
|
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:
|
|
255
|
-
return JSON.stringify(resolved.value) === 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:
|
|
260
|
-
handlesUndefined: [
|
|
261
|
-
precludes: [
|
|
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,
|
|
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:
|
|
269
|
-
handlesUndefined: [
|
|
270
|
-
precludes: [
|
|
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,
|
|
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:
|
|
278
|
-
handlesUndefined: [
|
|
279
|
-
action: ({ what }, featureStep) => {
|
|
280
|
-
const term = (getStepTerm(featureStep,
|
|
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:
|
|
290
|
-
handlesUndefined: [
|
|
291
|
-
|
|
292
|
-
|
|
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(
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
331
|
-
|
|
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
|
-
|
|
334
|
-
type,
|
|
335
|
-
members,
|
|
336
|
-
ordered: !!def.comparator
|
|
337
|
-
};
|
|
367
|
+
items.push({ name, description, members, ...(vertexLabel ? { vertexLabel } : {}), ...(_edges.length ? { _edges } : {}) });
|
|
338
368
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
369
|
+
return actionOKWithProducts({ _type: "Domain", _summary: `${items.length} domains`, items });
|
|
370
|
+
},
|
|
342
371
|
},
|
|
343
372
|
showDomain: {
|
|
344
|
-
gwta:
|
|
345
|
-
handlesUndefined: [
|
|
346
|
-
action: (_, featureStep) => {
|
|
347
|
-
const name = getStepTerm(featureStep,
|
|
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
|
-
|
|
361
|
-
return
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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, {
|
|
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 ===
|
|
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, {
|
|
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,
|
|
531
|
+
const fallback = getStepTerm(featureStep, "superdomains") ?? featureStep.in;
|
|
504
532
|
const superdomainNames = extractValuesFromFragments(superdomains, fallback);
|
|
505
533
|
if (!superdomainNames.length) {
|
|
506
|
-
throw new Error(
|
|
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,
|
|
537
|
+
const effectiveDomain = domain ?? getStepTerm(featureStep, "domain");
|
|
510
538
|
if (!effectiveDomain)
|
|
511
|
-
return actionNotOK(
|
|
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(
|
|
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,
|
|
556
|
-
const effectiveDomain = domain ?? getStepTerm(featureStep,
|
|
583
|
+
const values = extractValuesFromFragments(valueFragments, getStepTerm(featureStep, "values") ?? featureStep.in);
|
|
584
|
+
const effectiveDomain = domain ?? getStepTerm(featureStep, "domain");
|
|
557
585
|
if (!effectiveDomain)
|
|
558
|
-
return actionNotOK(
|
|
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({
|
|
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
|
|
587
|
-
|
|
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(
|
|
623
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
590
624
|
return trimmed.slice(1, -1).trim();
|
|
591
625
|
}
|
|
592
626
|
return trimmed;
|
|
593
|
-
})
|
|
594
|
-
|
|
627
|
+
})
|
|
628
|
+
.filter(Boolean);
|
|
629
|
+
const inner = innerChunks.join(" ").trim();
|
|
595
630
|
if (!inner) {
|
|
596
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
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
|
|
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 ===
|
|
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
|
-
|
|
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({
|
|
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) {
|