@q1k-oss/btree-workflows 0.0.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.
- package/.claude/settings.local.json +31 -0
- package/CLAUDE.md +181 -0
- package/LICENSE +21 -0
- package/README.md +920 -0
- package/behaviour-tree-workflows-landing/index.html +16 -0
- package/behaviour-tree-workflows-landing/package-lock.json +2074 -0
- package/behaviour-tree-workflows-landing/package.json +31 -0
- package/behaviour-tree-workflows-landing/public/favicon.svg +17 -0
- package/behaviour-tree-workflows-landing/src/App.css +103 -0
- package/behaviour-tree-workflows-landing/src/App.tsx +176 -0
- package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.css +89 -0
- package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.tsx +64 -0
- package/behaviour-tree-workflows-landing/src/components/ExampleSelector.css +64 -0
- package/behaviour-tree-workflows-landing/src/components/ExampleSelector.tsx +34 -0
- package/behaviour-tree-workflows-landing/src/components/ExecutionLog.css +107 -0
- package/behaviour-tree-workflows-landing/src/components/ExecutionLog.tsx +85 -0
- package/behaviour-tree-workflows-landing/src/components/Header.css +50 -0
- package/behaviour-tree-workflows-landing/src/components/Header.tsx +26 -0
- package/behaviour-tree-workflows-landing/src/components/StatusBadge.css +45 -0
- package/behaviour-tree-workflows-landing/src/components/StatusBadge.tsx +15 -0
- package/behaviour-tree-workflows-landing/src/components/Toolbar.css +74 -0
- package/behaviour-tree-workflows-landing/src/components/Toolbar.tsx +53 -0
- package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.css +67 -0
- package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.tsx +192 -0
- package/behaviour-tree-workflows-landing/src/components/YamlEditor.css +18 -0
- package/behaviour-tree-workflows-landing/src/components/YamlEditor.tsx +96 -0
- package/behaviour-tree-workflows-landing/src/lib/count-nodes.ts +11 -0
- package/behaviour-tree-workflows-landing/src/lib/execution-engine.ts +96 -0
- package/behaviour-tree-workflows-landing/src/lib/tree-layout.ts +136 -0
- package/behaviour-tree-workflows-landing/src/lib/yaml-examples.ts +549 -0
- package/behaviour-tree-workflows-landing/src/main.tsx +9 -0
- package/behaviour-tree-workflows-landing/src/stubs/activepieces.ts +18 -0
- package/behaviour-tree-workflows-landing/src/stubs/fs.ts +24 -0
- package/behaviour-tree-workflows-landing/src/stubs/path.ts +16 -0
- package/behaviour-tree-workflows-landing/src/stubs/temporal-activity.ts +6 -0
- package/behaviour-tree-workflows-landing/src/stubs/temporal-workflow.ts +22 -0
- package/behaviour-tree-workflows-landing/tsconfig.json +25 -0
- package/behaviour-tree-workflows-landing/vite.config.ts +40 -0
- package/demo-google-sheets.ts +181 -0
- package/demo-runtime-variables.ts +174 -0
- package/demo-template.ts +208 -0
- package/docs/ARCHITECTURE_SUMMARY.md +613 -0
- package/docs/NODE_REFERENCE.md +504 -0
- package/docs/README.md +53 -0
- package/docs/custom-nodes-architecture.md +826 -0
- package/docs/observability.md +175 -0
- package/docs/yaml-specification.md +990 -0
- package/examples/temporal/README.md +117 -0
- package/examples/temporal/activities.ts +373 -0
- package/examples/temporal/client.ts +115 -0
- package/examples/temporal/python-worker/activities.py +339 -0
- package/examples/temporal/python-worker/requirements.txt +12 -0
- package/examples/temporal/python-worker/worker.py +106 -0
- package/examples/temporal/worker.ts +66 -0
- package/examples/temporal/workflows.ts +6 -0
- package/examples/temporal/yaml-workflow-loader.ts +105 -0
- package/examples/yaml-test.ts +97 -0
- package/examples/yaml-workflows/01-simple-sequence.yaml +25 -0
- package/examples/yaml-workflows/02-parallel-timeout.yaml +45 -0
- package/examples/yaml-workflows/03-ecommerce-checkout.yaml +94 -0
- package/examples/yaml-workflows/04-ai-agent-workflow.yaml +346 -0
- package/examples/yaml-workflows/05-order-processing.yaml +146 -0
- package/examples/yaml-workflows/06-activity-test.yaml +71 -0
- package/examples/yaml-workflows/07-activity-simple-test.yaml +43 -0
- package/examples/yaml-workflows/08-file-processing.yaml +141 -0
- package/examples/yaml-workflows/09-http-request.yaml +137 -0
- package/examples/yaml-workflows/README.md +211 -0
- package/package.json +38 -0
- package/src/actions/code-execution.schema.ts +27 -0
- package/src/actions/code-execution.ts +218 -0
- package/src/actions/generate-file.test.ts +516 -0
- package/src/actions/generate-file.ts +166 -0
- package/src/actions/http-request.test.ts +784 -0
- package/src/actions/http-request.ts +228 -0
- package/src/actions/index.ts +20 -0
- package/src/actions/parse-file.test.ts +448 -0
- package/src/actions/parse-file.ts +139 -0
- package/src/actions/python-script.test.ts +439 -0
- package/src/actions/python-script.ts +154 -0
- package/src/base-node.test.ts +511 -0
- package/src/base-node.ts +605 -0
- package/src/behavior-tree.test.ts +431 -0
- package/src/behavior-tree.ts +283 -0
- package/src/blackboard.test.ts +222 -0
- package/src/blackboard.ts +192 -0
- package/src/composites/conditional.schema.ts +19 -0
- package/src/composites/conditional.test.ts +309 -0
- package/src/composites/conditional.ts +129 -0
- package/src/composites/for-each.schema.ts +23 -0
- package/src/composites/for-each.test.ts +254 -0
- package/src/composites/for-each.ts +132 -0
- package/src/composites/index.ts +15 -0
- package/src/composites/memory-sequence.schema.ts +19 -0
- package/src/composites/memory-sequence.test.ts +223 -0
- package/src/composites/memory-sequence.ts +98 -0
- package/src/composites/parallel.schema.ts +28 -0
- package/src/composites/parallel.test.ts +502 -0
- package/src/composites/parallel.ts +157 -0
- package/src/composites/reactive-sequence.schema.ts +19 -0
- package/src/composites/reactive-sequence.test.ts +170 -0
- package/src/composites/reactive-sequence.ts +85 -0
- package/src/composites/recovery.schema.ts +19 -0
- package/src/composites/recovery.test.ts +366 -0
- package/src/composites/recovery.ts +90 -0
- package/src/composites/selector.schema.ts +19 -0
- package/src/composites/selector.test.ts +387 -0
- package/src/composites/selector.ts +85 -0
- package/src/composites/sequence.schema.ts +19 -0
- package/src/composites/sequence.test.ts +337 -0
- package/src/composites/sequence.ts +72 -0
- package/src/composites/sub-tree.schema.ts +21 -0
- package/src/composites/sub-tree.test.ts +893 -0
- package/src/composites/sub-tree.ts +177 -0
- package/src/composites/while.schema.ts +24 -0
- package/src/composites/while.test.ts +381 -0
- package/src/composites/while.ts +149 -0
- package/src/data-store/index.ts +10 -0
- package/src/data-store/memory-store.ts +161 -0
- package/src/data-store/types.ts +94 -0
- package/src/debug/breakpoint.test.ts +47 -0
- package/src/debug/breakpoint.ts +30 -0
- package/src/debug/index.ts +17 -0
- package/src/debug/resume-point.test.ts +49 -0
- package/src/debug/resume-point.ts +29 -0
- package/src/decorators/delay.schema.ts +21 -0
- package/src/decorators/delay.test.ts +261 -0
- package/src/decorators/delay.ts +140 -0
- package/src/decorators/force-result.schema.ts +32 -0
- package/src/decorators/force-result.test.ts +133 -0
- package/src/decorators/force-result.ts +63 -0
- package/src/decorators/index.ts +13 -0
- package/src/decorators/invert.schema.ts +19 -0
- package/src/decorators/invert.test.ts +135 -0
- package/src/decorators/invert.ts +42 -0
- package/src/decorators/keep-running.schema.ts +20 -0
- package/src/decorators/keep-running.test.ts +105 -0
- package/src/decorators/keep-running.ts +49 -0
- package/src/decorators/precondition.schema.ts +19 -0
- package/src/decorators/precondition.test.ts +351 -0
- package/src/decorators/precondition.ts +139 -0
- package/src/decorators/repeat.schema.ts +21 -0
- package/src/decorators/repeat.test.ts +187 -0
- package/src/decorators/repeat.ts +94 -0
- package/src/decorators/run-once.schema.ts +19 -0
- package/src/decorators/run-once.test.ts +140 -0
- package/src/decorators/run-once.ts +61 -0
- package/src/decorators/soft-assert.schema.ts +19 -0
- package/src/decorators/soft-assert.test.ts +107 -0
- package/src/decorators/soft-assert.ts +68 -0
- package/src/decorators/timeout.schema.ts +21 -0
- package/src/decorators/timeout.test.ts +274 -0
- package/src/decorators/timeout.ts +159 -0
- package/src/errors.test.ts +63 -0
- package/src/errors.ts +34 -0
- package/src/events.test.ts +347 -0
- package/src/events.ts +183 -0
- package/src/index.ts +80 -0
- package/src/integrations/index.ts +30 -0
- package/src/integrations/integration-action.test.ts +571 -0
- package/src/integrations/integration-action.ts +233 -0
- package/src/integrations/piece-executor.ts +320 -0
- package/src/observability/execution-tracker.ts +320 -0
- package/src/observability/index.ts +23 -0
- package/src/observability/sinks.ts +138 -0
- package/src/observability/types.ts +130 -0
- package/src/registry-utils.ts +147 -0
- package/src/registry.test.ts +466 -0
- package/src/registry.ts +334 -0
- package/src/schemas/base.schema.ts +104 -0
- package/src/schemas/index.ts +223 -0
- package/src/schemas/integration.test.ts +238 -0
- package/src/schemas/tree-definition.schema.ts +170 -0
- package/src/schemas/validation.test.ts +146 -0
- package/src/schemas/validation.ts +122 -0
- package/src/scripting/index.ts +22 -0
- package/src/templates/template-loader.test.ts +281 -0
- package/src/templates/template-loader.ts +152 -0
- package/src/temporal-integration.test.ts +213 -0
- package/src/test-nodes.ts +259 -0
- package/src/types.ts +503 -0
- package/src/utilities/index.ts +17 -0
- package/src/utilities/log-message.test.ts +275 -0
- package/src/utilities/log-message.ts +134 -0
- package/src/utilities/regex-extract.test.ts +138 -0
- package/src/utilities/regex-extract.ts +108 -0
- package/src/utilities/variable-resolver.test.ts +416 -0
- package/src/utilities/variable-resolver.ts +318 -0
- package/src/utils/error-handler.test.ts +117 -0
- package/src/utils/error-handler.ts +48 -0
- package/src/utils/signal-check.test.ts +234 -0
- package/src/utils/signal-check.ts +140 -0
- package/src/yaml/errors.ts +143 -0
- package/src/yaml/index.ts +30 -0
- package/src/yaml/loader.ts +39 -0
- package/src/yaml/parser.ts +286 -0
- package/src/yaml/validation/semantic-validator.ts +196 -0
- package/templates/google-sheets/insert-row.yaml +76 -0
- package/templates/notification-sender.yaml +33 -0
- package/templates/order-validation.yaml +44 -0
- package/tsconfig.json +24 -0
- package/vitest.config.ts +25 -0
- package/workflows/order-processor.yaml +59 -0
- package/workflows/process-order-workflow.yaml +142 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Variable Resolver Utility
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { ScopedBlackboard } from "../blackboard.js";
|
|
7
|
+
import {
|
|
8
|
+
resolveString,
|
|
9
|
+
resolveValue,
|
|
10
|
+
hasVariables,
|
|
11
|
+
extractVariables,
|
|
12
|
+
type VariableContext,
|
|
13
|
+
} from "./variable-resolver.js";
|
|
14
|
+
|
|
15
|
+
describe("Variable Resolver", () => {
|
|
16
|
+
// Helper to create a context
|
|
17
|
+
function createContext(overrides: Partial<VariableContext> = {}): VariableContext {
|
|
18
|
+
return {
|
|
19
|
+
blackboard: new ScopedBlackboard(),
|
|
20
|
+
...overrides,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe("resolveString", () => {
|
|
25
|
+
describe("blackboard variables (${bb.key} and ${key})", () => {
|
|
26
|
+
it("should resolve ${bb.key} from blackboard", () => {
|
|
27
|
+
const ctx = createContext();
|
|
28
|
+
ctx.blackboard.set("username", "john");
|
|
29
|
+
|
|
30
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
31
|
+
const result = resolveString("${bb.username}", ctx);
|
|
32
|
+
expect(result).toBe("john");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should resolve ${key} shorthand from blackboard", () => {
|
|
36
|
+
const ctx = createContext();
|
|
37
|
+
ctx.blackboard.set("username", "john");
|
|
38
|
+
|
|
39
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
40
|
+
const result = resolveString("${username}", ctx);
|
|
41
|
+
expect(result).toBe("john");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should resolve nested blackboard values", () => {
|
|
45
|
+
const ctx = createContext();
|
|
46
|
+
ctx.blackboard.set("user", { profile: { name: "Alice" } });
|
|
47
|
+
|
|
48
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
49
|
+
const result = resolveString("${bb.user.profile.name}", ctx);
|
|
50
|
+
expect(result).toBe("Alice");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should preserve type for full match", () => {
|
|
54
|
+
const ctx = createContext();
|
|
55
|
+
const user = { name: "Bob", age: 30 };
|
|
56
|
+
ctx.blackboard.set("user", user);
|
|
57
|
+
|
|
58
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
59
|
+
const result = resolveString("${bb.user}", ctx);
|
|
60
|
+
expect(result).toEqual(user);
|
|
61
|
+
expect(typeof result).toBe("object");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should preserve array type for full match", () => {
|
|
65
|
+
const ctx = createContext();
|
|
66
|
+
const items = ["a", "b", "c"];
|
|
67
|
+
ctx.blackboard.set("items", items);
|
|
68
|
+
|
|
69
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
70
|
+
const result = resolveString("${bb.items}", ctx);
|
|
71
|
+
expect(result).toEqual(items);
|
|
72
|
+
expect(Array.isArray(result)).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should interpolate multiple values in string", () => {
|
|
76
|
+
const ctx = createContext();
|
|
77
|
+
ctx.blackboard.set("name", "Alice");
|
|
78
|
+
ctx.blackboard.set("age", 25);
|
|
79
|
+
|
|
80
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
81
|
+
const result = resolveString("${name} is ${age} years old", ctx);
|
|
82
|
+
expect(result).toBe("Alice is 25 years old");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should JSON stringify objects in interpolation", () => {
|
|
86
|
+
const ctx = createContext();
|
|
87
|
+
ctx.blackboard.set("data", { key: "value" });
|
|
88
|
+
|
|
89
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
90
|
+
const result = resolveString("Data: ${data}", ctx);
|
|
91
|
+
expect(result).toBe('Data: {"key":"value"}');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("input variables (${input.key})", () => {
|
|
96
|
+
it("should resolve ${input.key} from input", () => {
|
|
97
|
+
const ctx = createContext({
|
|
98
|
+
input: { orderId: "ORD-123" },
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
102
|
+
const result = resolveString("${input.orderId}", ctx);
|
|
103
|
+
expect(result).toBe("ORD-123");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should resolve nested input values", () => {
|
|
107
|
+
const ctx = createContext({
|
|
108
|
+
input: { order: { id: "ORD-456", customer: "Jane" } },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
112
|
+
const result = resolveString("${input.order.id}", ctx);
|
|
113
|
+
expect(result).toBe("ORD-456");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should preserve type for full input match", () => {
|
|
117
|
+
const order = { id: "ORD-789", items: ["a", "b"] };
|
|
118
|
+
const ctx = createContext({
|
|
119
|
+
input: { order },
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
123
|
+
const result = resolveString("${input.order}", ctx);
|
|
124
|
+
expect(result).toEqual(order);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should return undefined for missing input", () => {
|
|
128
|
+
const ctx = createContext({
|
|
129
|
+
input: { existing: "value" },
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
133
|
+
const result = resolveString("${input.missing}", ctx, { preserveUndefined: false });
|
|
134
|
+
expect(result).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("environment variables (${env.KEY})", () => {
|
|
139
|
+
it("should resolve ${env.KEY} from process.env", () => {
|
|
140
|
+
const ctx = createContext();
|
|
141
|
+
|
|
142
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
143
|
+
const result = resolveString("${env.NODE_ENV}", ctx, {
|
|
144
|
+
envSource: { NODE_ENV: "test" },
|
|
145
|
+
});
|
|
146
|
+
expect(result).toBe("test");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should use custom env source", () => {
|
|
150
|
+
const ctx = createContext();
|
|
151
|
+
|
|
152
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
153
|
+
const result = resolveString("${env.CUSTOM_VAR}", ctx, {
|
|
154
|
+
envSource: { CUSTOM_VAR: "custom-value" },
|
|
155
|
+
});
|
|
156
|
+
expect(result).toBe("custom-value");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("should preserve placeholder for missing env var", () => {
|
|
160
|
+
const ctx = createContext();
|
|
161
|
+
|
|
162
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
163
|
+
const result = resolveString("${env.MISSING_VAR}", ctx, {
|
|
164
|
+
envSource: {},
|
|
165
|
+
});
|
|
166
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
167
|
+
expect(result).toBe("${env.MISSING_VAR}");
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe("test data variables (${param.key})", () => {
|
|
172
|
+
it("should resolve ${param.key} from testData", () => {
|
|
173
|
+
const testData = new Map<string, unknown>();
|
|
174
|
+
testData.set("testId", "TEST-001");
|
|
175
|
+
|
|
176
|
+
const ctx = createContext({ testData });
|
|
177
|
+
|
|
178
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
179
|
+
const result = resolveString("${param.testId}", ctx);
|
|
180
|
+
expect(result).toBe("TEST-001");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("should resolve nested param values", () => {
|
|
184
|
+
const testData = new Map<string, unknown>();
|
|
185
|
+
testData.set("test", { data: { value: 42 } });
|
|
186
|
+
|
|
187
|
+
const ctx = createContext({ testData });
|
|
188
|
+
|
|
189
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
190
|
+
const result = resolveString("${param.test.data.value}", ctx);
|
|
191
|
+
expect(result).toBe(42);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe("undefined value handling", () => {
|
|
196
|
+
it("should preserve placeholder when preserveUndefined is true (default)", () => {
|
|
197
|
+
const ctx = createContext();
|
|
198
|
+
|
|
199
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
200
|
+
const result = resolveString("${bb.missing}", ctx);
|
|
201
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
202
|
+
expect(result).toBe("${bb.missing}");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("should return undefined when preserveUndefined is false (full match)", () => {
|
|
206
|
+
const ctx = createContext();
|
|
207
|
+
|
|
208
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
209
|
+
const result = resolveString("${bb.missing}", ctx, { preserveUndefined: false });
|
|
210
|
+
expect(result).toBeUndefined();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should return empty string when preserveUndefined is false (interpolation)", () => {
|
|
214
|
+
const ctx = createContext();
|
|
215
|
+
|
|
216
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
217
|
+
const result = resolveString("Value: ${bb.missing}", ctx, { preserveUndefined: false });
|
|
218
|
+
expect(result).toBe("Value: ");
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe("edge cases", () => {
|
|
223
|
+
it("should handle null values", () => {
|
|
224
|
+
const ctx = createContext();
|
|
225
|
+
ctx.blackboard.set("nullVal", null);
|
|
226
|
+
|
|
227
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
228
|
+
const result = resolveString("Value: ${nullVal}", ctx);
|
|
229
|
+
expect(result).toBe("Value: null");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should handle number values", () => {
|
|
233
|
+
const ctx = createContext();
|
|
234
|
+
ctx.blackboard.set("count", 42);
|
|
235
|
+
|
|
236
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
237
|
+
const result = resolveString("Count: ${count}", ctx);
|
|
238
|
+
expect(result).toBe("Count: 42");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("should handle boolean values", () => {
|
|
242
|
+
const ctx = createContext();
|
|
243
|
+
ctx.blackboard.set("active", true);
|
|
244
|
+
|
|
245
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
246
|
+
const result = resolveString("Active: ${active}", ctx);
|
|
247
|
+
expect(result).toBe("Active: true");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("should return string as-is when no variables", () => {
|
|
251
|
+
const ctx = createContext();
|
|
252
|
+
const result = resolveString("Hello World", ctx);
|
|
253
|
+
expect(result).toBe("Hello World");
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("should handle empty string", () => {
|
|
257
|
+
const ctx = createContext();
|
|
258
|
+
const result = resolveString("", ctx);
|
|
259
|
+
expect(result).toBe("");
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
describe("resolveValue", () => {
|
|
265
|
+
it("should resolve string values", () => {
|
|
266
|
+
const ctx = createContext();
|
|
267
|
+
ctx.blackboard.set("name", "Alice");
|
|
268
|
+
|
|
269
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
270
|
+
const result = resolveValue("Hello ${name}", ctx);
|
|
271
|
+
expect(result).toBe("Hello Alice");
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("should resolve values in objects recursively", () => {
|
|
275
|
+
const ctx = createContext();
|
|
276
|
+
ctx.blackboard.set("orderId", "ORD-123");
|
|
277
|
+
ctx.blackboard.set("customer", "Bob");
|
|
278
|
+
|
|
279
|
+
const input = {
|
|
280
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
281
|
+
id: "${orderId}",
|
|
282
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
283
|
+
name: "${customer}",
|
|
284
|
+
nested: {
|
|
285
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
286
|
+
value: "${orderId}",
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const result = resolveValue(input, ctx);
|
|
291
|
+
expect(result).toEqual({
|
|
292
|
+
id: "ORD-123",
|
|
293
|
+
name: "Bob",
|
|
294
|
+
nested: {
|
|
295
|
+
value: "ORD-123",
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should resolve values in arrays", () => {
|
|
301
|
+
const ctx = createContext();
|
|
302
|
+
ctx.blackboard.set("a", "first");
|
|
303
|
+
ctx.blackboard.set("b", "second");
|
|
304
|
+
|
|
305
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
306
|
+
const result = resolveValue(["${a}", "${b}", "static"], ctx);
|
|
307
|
+
expect(result).toEqual(["first", "second", "static"]);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("should pass through primitives unchanged", () => {
|
|
311
|
+
const ctx = createContext();
|
|
312
|
+
|
|
313
|
+
expect(resolveValue(42, ctx)).toBe(42);
|
|
314
|
+
expect(resolveValue(true, ctx)).toBe(true);
|
|
315
|
+
expect(resolveValue(null, ctx)).toBe(null);
|
|
316
|
+
expect(resolveValue(undefined, ctx)).toBe(undefined);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("should handle mixed objects and arrays", () => {
|
|
320
|
+
const ctx = createContext();
|
|
321
|
+
ctx.blackboard.set("val", "resolved");
|
|
322
|
+
|
|
323
|
+
const input = {
|
|
324
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
325
|
+
items: [{ key: "${val}" }, { key: "static" }],
|
|
326
|
+
count: 5,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const result = resolveValue(input, ctx);
|
|
330
|
+
expect(result).toEqual({
|
|
331
|
+
items: [{ key: "resolved" }, { key: "static" }],
|
|
332
|
+
count: 5,
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
describe("hasVariables", () => {
|
|
338
|
+
it("should return true for strings with variables", () => {
|
|
339
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
340
|
+
expect(hasVariables("${bb.key}")).toBe(true);
|
|
341
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
342
|
+
expect(hasVariables("${key}")).toBe(true);
|
|
343
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
344
|
+
expect(hasVariables("${input.val}")).toBe(true);
|
|
345
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
346
|
+
expect(hasVariables("Hello ${name}")).toBe(true);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should return false for strings without variables", () => {
|
|
350
|
+
expect(hasVariables("Hello World")).toBe(false);
|
|
351
|
+
expect(hasVariables("")).toBe(false);
|
|
352
|
+
expect(hasVariables("$notvar")).toBe(false);
|
|
353
|
+
expect(hasVariables("{notvar}")).toBe(false);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe("extractVariables", () => {
|
|
358
|
+
it("should extract single variable", () => {
|
|
359
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
360
|
+
const result = extractVariables("${bb.username}");
|
|
361
|
+
expect(result).toEqual([{ namespace: "bb", key: "username" }]);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it("should extract shorthand variable as blackboard", () => {
|
|
365
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
366
|
+
const result = extractVariables("${username}");
|
|
367
|
+
expect(result).toEqual([{ namespace: "bb", key: "username" }]);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it("should extract multiple variables", () => {
|
|
371
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
372
|
+
const result = extractVariables("${input.a} and ${bb.b} and ${c}");
|
|
373
|
+
expect(result).toEqual([
|
|
374
|
+
{ namespace: "input", key: "a" },
|
|
375
|
+
{ namespace: "bb", key: "b" },
|
|
376
|
+
{ namespace: "bb", key: "c" },
|
|
377
|
+
]);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should extract variables with nested keys", () => {
|
|
381
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
382
|
+
const result = extractVariables("${bb.user.profile.name}");
|
|
383
|
+
expect(result).toEqual([{ namespace: "bb", key: "user.profile.name" }]);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("should return empty array for no variables", () => {
|
|
387
|
+
const result = extractVariables("Hello World");
|
|
388
|
+
expect(result).toEqual([]);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe("backward compatibility", () => {
|
|
393
|
+
it("should work with LogMessage-style ${key} syntax", () => {
|
|
394
|
+
const ctx = createContext();
|
|
395
|
+
ctx.blackboard.set("username", "alice");
|
|
396
|
+
ctx.blackboard.set("count", 10);
|
|
397
|
+
|
|
398
|
+
// This is how LogMessage currently works
|
|
399
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
400
|
+
const result = resolveString("User ${username} has ${count} items", ctx);
|
|
401
|
+
expect(result).toBe("User alice has 10 items");
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it("should work with IntegrationAction-style ${bb.key} syntax", () => {
|
|
405
|
+
const ctx = createContext();
|
|
406
|
+
ctx.blackboard.set("spreadsheetId", "sheet-123");
|
|
407
|
+
ctx.blackboard.set("values", ["a", "b", "c"]);
|
|
408
|
+
|
|
409
|
+
// This is how IntegrationAction currently works
|
|
410
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
411
|
+
expect(resolveString("${bb.spreadsheetId}", ctx)).toBe("sheet-123");
|
|
412
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Testing variable syntax
|
|
413
|
+
expect(resolveString("${bb.values}", ctx)).toEqual(["a", "b", "c"]);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
});
|