@q1k-oss/btree-workflows 0.0.1 → 0.0.2

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/README.md +13 -13
  2. package/dist/index.cjs +5011 -0
  3. package/dist/index.d.cts +3320 -0
  4. package/dist/index.d.ts +3320 -0
  5. package/dist/index.js +4879 -0
  6. package/package.json +33 -3
  7. package/.claude/settings.local.json +0 -31
  8. package/CLAUDE.md +0 -181
  9. package/behaviour-tree-workflows-landing/index.html +0 -16
  10. package/behaviour-tree-workflows-landing/package-lock.json +0 -2074
  11. package/behaviour-tree-workflows-landing/package.json +0 -31
  12. package/behaviour-tree-workflows-landing/public/favicon.svg +0 -17
  13. package/behaviour-tree-workflows-landing/src/App.css +0 -103
  14. package/behaviour-tree-workflows-landing/src/App.tsx +0 -176
  15. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.css +0 -89
  16. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.tsx +0 -64
  17. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.css +0 -64
  18. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.tsx +0 -34
  19. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.css +0 -107
  20. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.tsx +0 -85
  21. package/behaviour-tree-workflows-landing/src/components/Header.css +0 -50
  22. package/behaviour-tree-workflows-landing/src/components/Header.tsx +0 -26
  23. package/behaviour-tree-workflows-landing/src/components/StatusBadge.css +0 -45
  24. package/behaviour-tree-workflows-landing/src/components/StatusBadge.tsx +0 -15
  25. package/behaviour-tree-workflows-landing/src/components/Toolbar.css +0 -74
  26. package/behaviour-tree-workflows-landing/src/components/Toolbar.tsx +0 -53
  27. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.css +0 -67
  28. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.tsx +0 -192
  29. package/behaviour-tree-workflows-landing/src/components/YamlEditor.css +0 -18
  30. package/behaviour-tree-workflows-landing/src/components/YamlEditor.tsx +0 -96
  31. package/behaviour-tree-workflows-landing/src/lib/count-nodes.ts +0 -11
  32. package/behaviour-tree-workflows-landing/src/lib/execution-engine.ts +0 -96
  33. package/behaviour-tree-workflows-landing/src/lib/tree-layout.ts +0 -136
  34. package/behaviour-tree-workflows-landing/src/lib/yaml-examples.ts +0 -549
  35. package/behaviour-tree-workflows-landing/src/main.tsx +0 -9
  36. package/behaviour-tree-workflows-landing/src/stubs/activepieces.ts +0 -18
  37. package/behaviour-tree-workflows-landing/src/stubs/fs.ts +0 -24
  38. package/behaviour-tree-workflows-landing/src/stubs/path.ts +0 -16
  39. package/behaviour-tree-workflows-landing/src/stubs/temporal-activity.ts +0 -6
  40. package/behaviour-tree-workflows-landing/src/stubs/temporal-workflow.ts +0 -22
  41. package/behaviour-tree-workflows-landing/tsconfig.json +0 -25
  42. package/behaviour-tree-workflows-landing/vite.config.ts +0 -40
  43. package/demo-google-sheets.ts +0 -181
  44. package/demo-runtime-variables.ts +0 -174
  45. package/demo-template.ts +0 -208
  46. package/docs/ARCHITECTURE_SUMMARY.md +0 -613
  47. package/docs/NODE_REFERENCE.md +0 -504
  48. package/docs/README.md +0 -53
  49. package/docs/custom-nodes-architecture.md +0 -826
  50. package/docs/observability.md +0 -175
  51. package/docs/yaml-specification.md +0 -990
  52. package/examples/temporal/README.md +0 -117
  53. package/examples/temporal/activities.ts +0 -373
  54. package/examples/temporal/client.ts +0 -115
  55. package/examples/temporal/python-worker/activities.py +0 -339
  56. package/examples/temporal/python-worker/requirements.txt +0 -12
  57. package/examples/temporal/python-worker/worker.py +0 -106
  58. package/examples/temporal/worker.ts +0 -66
  59. package/examples/temporal/workflows.ts +0 -6
  60. package/examples/temporal/yaml-workflow-loader.ts +0 -105
  61. package/examples/yaml-test.ts +0 -97
  62. package/examples/yaml-workflows/01-simple-sequence.yaml +0 -25
  63. package/examples/yaml-workflows/02-parallel-timeout.yaml +0 -45
  64. package/examples/yaml-workflows/03-ecommerce-checkout.yaml +0 -94
  65. package/examples/yaml-workflows/04-ai-agent-workflow.yaml +0 -346
  66. package/examples/yaml-workflows/05-order-processing.yaml +0 -146
  67. package/examples/yaml-workflows/06-activity-test.yaml +0 -71
  68. package/examples/yaml-workflows/07-activity-simple-test.yaml +0 -43
  69. package/examples/yaml-workflows/08-file-processing.yaml +0 -141
  70. package/examples/yaml-workflows/09-http-request.yaml +0 -137
  71. package/examples/yaml-workflows/README.md +0 -211
  72. package/src/actions/code-execution.schema.ts +0 -27
  73. package/src/actions/code-execution.ts +0 -218
  74. package/src/actions/generate-file.test.ts +0 -516
  75. package/src/actions/generate-file.ts +0 -166
  76. package/src/actions/http-request.test.ts +0 -784
  77. package/src/actions/http-request.ts +0 -228
  78. package/src/actions/index.ts +0 -20
  79. package/src/actions/parse-file.test.ts +0 -448
  80. package/src/actions/parse-file.ts +0 -139
  81. package/src/actions/python-script.test.ts +0 -439
  82. package/src/actions/python-script.ts +0 -154
  83. package/src/base-node.test.ts +0 -511
  84. package/src/base-node.ts +0 -605
  85. package/src/behavior-tree.test.ts +0 -431
  86. package/src/behavior-tree.ts +0 -283
  87. package/src/blackboard.test.ts +0 -222
  88. package/src/blackboard.ts +0 -192
  89. package/src/composites/conditional.schema.ts +0 -19
  90. package/src/composites/conditional.test.ts +0 -309
  91. package/src/composites/conditional.ts +0 -129
  92. package/src/composites/for-each.schema.ts +0 -23
  93. package/src/composites/for-each.test.ts +0 -254
  94. package/src/composites/for-each.ts +0 -132
  95. package/src/composites/index.ts +0 -15
  96. package/src/composites/memory-sequence.schema.ts +0 -19
  97. package/src/composites/memory-sequence.test.ts +0 -223
  98. package/src/composites/memory-sequence.ts +0 -98
  99. package/src/composites/parallel.schema.ts +0 -28
  100. package/src/composites/parallel.test.ts +0 -502
  101. package/src/composites/parallel.ts +0 -157
  102. package/src/composites/reactive-sequence.schema.ts +0 -19
  103. package/src/composites/reactive-sequence.test.ts +0 -170
  104. package/src/composites/reactive-sequence.ts +0 -85
  105. package/src/composites/recovery.schema.ts +0 -19
  106. package/src/composites/recovery.test.ts +0 -366
  107. package/src/composites/recovery.ts +0 -90
  108. package/src/composites/selector.schema.ts +0 -19
  109. package/src/composites/selector.test.ts +0 -387
  110. package/src/composites/selector.ts +0 -85
  111. package/src/composites/sequence.schema.ts +0 -19
  112. package/src/composites/sequence.test.ts +0 -337
  113. package/src/composites/sequence.ts +0 -72
  114. package/src/composites/sub-tree.schema.ts +0 -21
  115. package/src/composites/sub-tree.test.ts +0 -893
  116. package/src/composites/sub-tree.ts +0 -177
  117. package/src/composites/while.schema.ts +0 -24
  118. package/src/composites/while.test.ts +0 -381
  119. package/src/composites/while.ts +0 -149
  120. package/src/data-store/index.ts +0 -10
  121. package/src/data-store/memory-store.ts +0 -161
  122. package/src/data-store/types.ts +0 -94
  123. package/src/debug/breakpoint.test.ts +0 -47
  124. package/src/debug/breakpoint.ts +0 -30
  125. package/src/debug/index.ts +0 -17
  126. package/src/debug/resume-point.test.ts +0 -49
  127. package/src/debug/resume-point.ts +0 -29
  128. package/src/decorators/delay.schema.ts +0 -21
  129. package/src/decorators/delay.test.ts +0 -261
  130. package/src/decorators/delay.ts +0 -140
  131. package/src/decorators/force-result.schema.ts +0 -32
  132. package/src/decorators/force-result.test.ts +0 -133
  133. package/src/decorators/force-result.ts +0 -63
  134. package/src/decorators/index.ts +0 -13
  135. package/src/decorators/invert.schema.ts +0 -19
  136. package/src/decorators/invert.test.ts +0 -135
  137. package/src/decorators/invert.ts +0 -42
  138. package/src/decorators/keep-running.schema.ts +0 -20
  139. package/src/decorators/keep-running.test.ts +0 -105
  140. package/src/decorators/keep-running.ts +0 -49
  141. package/src/decorators/precondition.schema.ts +0 -19
  142. package/src/decorators/precondition.test.ts +0 -351
  143. package/src/decorators/precondition.ts +0 -139
  144. package/src/decorators/repeat.schema.ts +0 -21
  145. package/src/decorators/repeat.test.ts +0 -187
  146. package/src/decorators/repeat.ts +0 -94
  147. package/src/decorators/run-once.schema.ts +0 -19
  148. package/src/decorators/run-once.test.ts +0 -140
  149. package/src/decorators/run-once.ts +0 -61
  150. package/src/decorators/soft-assert.schema.ts +0 -19
  151. package/src/decorators/soft-assert.test.ts +0 -107
  152. package/src/decorators/soft-assert.ts +0 -68
  153. package/src/decorators/timeout.schema.ts +0 -21
  154. package/src/decorators/timeout.test.ts +0 -274
  155. package/src/decorators/timeout.ts +0 -159
  156. package/src/errors.test.ts +0 -63
  157. package/src/errors.ts +0 -34
  158. package/src/events.test.ts +0 -347
  159. package/src/events.ts +0 -183
  160. package/src/index.ts +0 -80
  161. package/src/integrations/index.ts +0 -30
  162. package/src/integrations/integration-action.test.ts +0 -571
  163. package/src/integrations/integration-action.ts +0 -233
  164. package/src/integrations/piece-executor.ts +0 -320
  165. package/src/observability/execution-tracker.ts +0 -320
  166. package/src/observability/index.ts +0 -23
  167. package/src/observability/sinks.ts +0 -138
  168. package/src/observability/types.ts +0 -130
  169. package/src/registry-utils.ts +0 -147
  170. package/src/registry.test.ts +0 -466
  171. package/src/registry.ts +0 -334
  172. package/src/schemas/base.schema.ts +0 -104
  173. package/src/schemas/index.ts +0 -223
  174. package/src/schemas/integration.test.ts +0 -238
  175. package/src/schemas/tree-definition.schema.ts +0 -170
  176. package/src/schemas/validation.test.ts +0 -146
  177. package/src/schemas/validation.ts +0 -122
  178. package/src/scripting/index.ts +0 -22
  179. package/src/templates/template-loader.test.ts +0 -281
  180. package/src/templates/template-loader.ts +0 -152
  181. package/src/temporal-integration.test.ts +0 -213
  182. package/src/test-nodes.ts +0 -259
  183. package/src/types.ts +0 -503
  184. package/src/utilities/index.ts +0 -17
  185. package/src/utilities/log-message.test.ts +0 -275
  186. package/src/utilities/log-message.ts +0 -134
  187. package/src/utilities/regex-extract.test.ts +0 -138
  188. package/src/utilities/regex-extract.ts +0 -108
  189. package/src/utilities/variable-resolver.test.ts +0 -416
  190. package/src/utilities/variable-resolver.ts +0 -318
  191. package/src/utils/error-handler.test.ts +0 -117
  192. package/src/utils/error-handler.ts +0 -48
  193. package/src/utils/signal-check.test.ts +0 -234
  194. package/src/utils/signal-check.ts +0 -140
  195. package/src/yaml/errors.ts +0 -143
  196. package/src/yaml/index.ts +0 -30
  197. package/src/yaml/loader.ts +0 -39
  198. package/src/yaml/parser.ts +0 -286
  199. package/src/yaml/validation/semantic-validator.ts +0 -196
  200. package/templates/google-sheets/insert-row.yaml +0 -76
  201. package/templates/notification-sender.yaml +0 -33
  202. package/templates/order-validation.yaml +0 -44
  203. package/tsconfig.json +0 -24
  204. package/vitest.config.ts +0 -25
  205. package/workflows/order-processor.yaml +0 -59
  206. package/workflows/process-order-workflow.yaml +0 -142
@@ -1,431 +0,0 @@
1
- /**
2
- * Tests for BehaviorTree with path-based indexing
3
- */
4
-
5
- import { describe, expect, it } from "vitest";
6
- import { BehaviorTree } from "./behavior-tree.js";
7
- import { ScopedBlackboard } from "./blackboard.js";
8
- import { Selector } from "./composites/selector.js";
9
- import { Sequence } from "./composites/sequence.js";
10
- import { ActionNode, type NodeConfiguration } from "./index.js";
11
- import { Registry } from "./registry.js";
12
- import { FailureNode, SuccessNode } from "./test-nodes.js";
13
- import { type TemporalContext, NodeStatus } from "./types.js";
14
-
15
- // Simple print action for testing
16
- class PrintAction extends ActionNode {
17
- private message: string;
18
-
19
- constructor(config: NodeConfiguration & { message: string }) {
20
- super(config);
21
- this.message = config.message;
22
- }
23
-
24
- async executeTick(_context: TemporalContext): Promise<NodeStatus> {
25
- this.log(`Message: ${this.message}`);
26
- this._status = NodeStatus.SUCCESS;
27
- return NodeStatus.SUCCESS;
28
- }
29
- }
30
-
31
- describe("BehaviorTree", () => {
32
- describe("Construction and Path-based Indexing", () => {
33
- it("should index simple tree with paths", () => {
34
- const seq = new Sequence({ id: "seq" });
35
- const a1 = new PrintAction({ id: "a1", message: "First" });
36
- const a2 = new PrintAction({ id: "a2", message: "Second" });
37
- const a3 = new PrintAction({ id: "a3", message: "Third" });
38
- seq.addChildren([a1, a2, a3]);
39
-
40
- const tree = new BehaviorTree(seq);
41
-
42
- // Root at /
43
- expect(tree.findNodeByPath("/")).toBe(seq);
44
-
45
- // Children at /0, /1, /2
46
- expect(tree.findNodeByPath("/0")).toBe(a1);
47
- expect(tree.findNodeByPath("/1")).toBe(a2);
48
- expect(tree.findNodeByPath("/2")).toBe(a3);
49
- });
50
-
51
- it("should index nested tree structure", () => {
52
- const root = new Sequence({ id: "root" });
53
- const step = new Sequence({ id: "step", name: "step" });
54
- const innerSeq = new Sequence({ id: "inner" });
55
- const action = new PrintAction({ id: "action", message: "Deep" });
56
-
57
- innerSeq.addChild(action);
58
- step.addChild(innerSeq);
59
- root.addChild(step);
60
-
61
- const tree = new BehaviorTree(root);
62
-
63
- expect(tree.findNodeByPath("/")).toBe(root); // root
64
- expect(tree.findNodeByPath("/0")).toBe(step); // root → step
65
- expect(tree.findNodeByPath("/0/0")).toBe(innerSeq); // root → step → seq
66
- expect(tree.findNodeByPath("/0/0/0")).toBe(action); // root → step → seq → action
67
- });
68
-
69
- it("should return root node", () => {
70
- const seq = new Sequence({ id: "seq" });
71
- const tree = new BehaviorTree(seq);
72
-
73
- expect(tree.getRoot()).toBe(seq);
74
- });
75
-
76
- it("should handle empty tree (single root node)", () => {
77
- const root = new Sequence({ id: "root" });
78
- const tree = new BehaviorTree(root);
79
-
80
- expect(tree.findNodeByPath("/")).toBe(root);
81
- expect(tree.findNodeByPath("/0")).toBeNull();
82
- });
83
- });
84
-
85
- describe("Node Lookup by Path", () => {
86
- it("should find nodes by path", () => {
87
- const root = new Sequence({ id: "root" });
88
- const child1 = new PrintAction({ id: "c1", message: "Child 1" });
89
- const child2 = new PrintAction({ id: "c2", message: "Child 2" });
90
- root.addChildren([child1, child2]);
91
-
92
- const tree = new BehaviorTree(root);
93
-
94
- expect(tree.findNodeByPath("/")).toBe(root);
95
- expect(tree.findNodeByPath("/0")).toBe(child1);
96
- expect(tree.findNodeByPath("/1")).toBe(child2);
97
- });
98
-
99
- it("should return null for non-existent path", () => {
100
- const root = new Sequence({ id: "root" });
101
- const tree = new BehaviorTree(root);
102
-
103
- expect(tree.findNodeByPath("/0")).toBeNull();
104
- expect(tree.findNodeByPath("/99")).toBeNull();
105
- expect(tree.findNodeByPath("/invalid")).toBeNull();
106
- });
107
- });
108
-
109
- describe("Node Lookup by ID", () => {
110
- it("should allow lookup by ID when IDs are present", () => {
111
- const seq = new Sequence({ id: "my-seq" });
112
- const action = new PrintAction({ id: "my-action", message: "Test" });
113
- seq.addChild(action);
114
-
115
- const tree = new BehaviorTree(seq);
116
-
117
- expect(tree.findNodeById("my-seq")).toBe(seq);
118
- expect(tree.findNodeById("my-action")).toBe(action);
119
- expect(tree.findNodeById("nonexistent")).toBeNull();
120
- });
121
-
122
- it("should handle nodes without IDs", () => {
123
- const seq = new Sequence({ id: "seq" });
124
- const action1 = new PrintAction({ id: "a1", message: "Test 1" });
125
- const action2 = new PrintAction({ id: "a2", message: "Test 2" });
126
- seq.addChildren([action1, action2]);
127
-
128
- const tree = new BehaviorTree(seq);
129
-
130
- // Can still find by path even without IDs
131
- expect(tree.findNodeByPath("/0")).toBe(action1);
132
- expect(tree.findNodeByPath("/1")).toBe(action2);
133
- });
134
- });
135
-
136
- describe("getNodePath", () => {
137
- it("should return correct path for a node", () => {
138
- const root = new Sequence({ id: "root" });
139
- const step = new Sequence({ id: "step", name: "step" });
140
- const action = new PrintAction({ id: "action", message: "Test" });
141
-
142
- step.addChild(action);
143
- root.addChild(step);
144
-
145
- const tree = new BehaviorTree(root);
146
-
147
- expect(tree.getNodePath(root)).toBe("/");
148
- expect(tree.getNodePath(step)).toBe("/0");
149
- expect(tree.getNodePath(action)).toBe("/0/0");
150
- });
151
-
152
- it("should return null for node not in tree", () => {
153
- const root = new Sequence({ id: "root" });
154
- const tree = new BehaviorTree(root);
155
-
156
- const orphan = new PrintAction({ id: "orphan", message: "Not in tree" });
157
- expect(tree.getNodePath(orphan)).toBeNull();
158
- });
159
- });
160
-
161
- describe("getNodePathById", () => {
162
- it("should return path by node ID", () => {
163
- const root = new Sequence({ id: "root" });
164
- const step = new Sequence({ id: "step-1", name: "step" });
165
- const action = new PrintAction({ id: "action-1", message: "Test" });
166
-
167
- step.addChild(action);
168
- root.addChild(step);
169
-
170
- const tree = new BehaviorTree(root);
171
-
172
- expect(tree.getNodePathById("root")).toBe("/");
173
- expect(tree.getNodePathById("step-1")).toBe("/0");
174
- expect(tree.getNodePathById("action-1")).toBe("/0/0");
175
- });
176
-
177
- it("should return null for nonexistent node ID", () => {
178
- const root = new Sequence({ id: "root" });
179
- const tree = new BehaviorTree(root);
180
-
181
- expect(tree.getNodePathById("nonexistent")).toBeNull();
182
- });
183
-
184
- it("should handle multiple children with nested structure", () => {
185
- const root = new Sequence({ id: "root" });
186
- const child1 = new PrintAction({ id: "child-1", message: "First" });
187
- const child2 = new Sequence({ id: "child-2" });
188
- const grandchild = new PrintAction({
189
- id: "grandchild",
190
- message: "Nested",
191
- });
192
-
193
- child2.addChild(grandchild);
194
- root.addChildren([child1, child2]);
195
-
196
- const tree = new BehaviorTree(root);
197
-
198
- expect(tree.getNodePathById("root")).toBe("/");
199
- expect(tree.getNodePathById("child-1")).toBe("/0");
200
- expect(tree.getNodePathById("child-2")).toBe("/1");
201
- expect(tree.getNodePathById("grandchild")).toBe("/1/0");
202
- });
203
- });
204
-
205
- describe("replaceNodeAtPath - Basic Cases", () => {
206
- it("should replace node at path even without ID", () => {
207
- const seq = new Sequence({ id: "seq" });
208
- const a1 = new PrintAction({ id: "a1", message: "First" });
209
- const a2 = new PrintAction({ id: "a2", message: "Second" });
210
- seq.addChildren([a1, a2]);
211
-
212
- const tree = new BehaviorTree(seq);
213
-
214
- // Replace middle node using path
215
- const newAction = new PrintAction({ id: "new", message: "Replaced" });
216
- tree.replaceNodeAtPath("/1", newAction);
217
-
218
- // Verify replacement
219
- expect(tree.findNodeByPath("/1")).toBe(newAction);
220
- expect(seq.children?.[1]).toBe(newAction);
221
- expect(newAction.parent).toBe(seq);
222
- });
223
-
224
- it("should preserve sibling nodes when replacing", () => {
225
- const seq = new Sequence({ id: "seq" });
226
- const a1 = new PrintAction({ id: "a1", message: "First" });
227
- const a2 = new PrintAction({ id: "a2", message: "Second" });
228
- const a3 = new PrintAction({ id: "a3", message: "Third" });
229
- seq.addChildren([a1, a2, a3]);
230
-
231
- const tree = new BehaviorTree(seq);
232
-
233
- // Replace middle node
234
- const newAction = new PrintAction({ id: "new", message: "New" });
235
- tree.replaceNodeAtPath("/1", newAction);
236
-
237
- // Siblings unchanged
238
- expect(tree.findNodeByPath("/0")).toBe(a1);
239
- expect(tree.findNodeByPath("/2")).toBe(a3);
240
- expect(seq.children?.length).toBe(3);
241
- });
242
-
243
- it("should replace composite and reindex new children", () => {
244
- const root = new Sequence({ id: "root" });
245
- const step1 = new Sequence({ id: "step1", name: "step1" });
246
- const step2 = new Sequence({ id: "step2", name: "step2" });
247
-
248
- step1.addChild(new PrintAction({ id: "old-action", message: "Old" }));
249
- step2.addChild(new PrintAction({ id: "other-action", message: "Other" }));
250
- root.addChildren([step1, step2]);
251
-
252
- const tree = new BehaviorTree(root);
253
-
254
- // Replace step1 with new step
255
- const newStep = new Sequence({ id: "step1-new", name: "step1-new" });
256
- newStep.addChild(new PrintAction({ id: "new-action", message: "New" }));
257
- tree.replaceNodeAtPath("/0", newStep);
258
-
259
- // New children indexed
260
- expect(tree.findNodeByPath("/0/0")?.id).toBe("new-action");
261
- expect(tree.findNodeById("new-action")).toBeDefined();
262
-
263
- // Old children gone
264
- expect(tree.findNodeById("old-action")).toBeNull();
265
-
266
- // Sibling preserved
267
- expect(tree.findNodeByPath("/1")).toBe(step2);
268
- expect(tree.findNodeById("other-action")).toBeDefined();
269
- });
270
-
271
- it("should handle root replacement", () => {
272
- const oldRoot = new Sequence({ id: "old-root" });
273
- oldRoot.addChild(new PrintAction({ id: "child", message: "Test" }));
274
-
275
- const tree = new BehaviorTree(oldRoot);
276
-
277
- const newRoot = new Selector({ id: "new-root" });
278
- newRoot.addChild(new PrintAction({ id: "new-child", message: "New" }));
279
-
280
- tree.replaceNodeAtPath("/", newRoot);
281
-
282
- expect(tree.getRoot()).toBe(newRoot);
283
- expect(tree.findNodeByPath("/")).toBe(newRoot);
284
- expect(tree.findNodeByPath("/0")?.id).toBe("new-child");
285
- expect(newRoot.parent).toBeUndefined();
286
- });
287
-
288
- it("should replace leaf node in deeply nested tree", () => {
289
- const root = new Sequence({ id: "root" });
290
- const step1 = new Sequence({ id: "step1", name: "step1" });
291
- const step2 = new Sequence({ id: "step2", name: "step2" });
292
- const innerSeq = new Sequence({ id: "inner" });
293
- const action = new PrintAction({ id: "deep-action", message: "Deep" });
294
-
295
- innerSeq.addChild(action);
296
- step2.addChild(innerSeq);
297
- root.addChildren([step1, step2]);
298
-
299
- const tree = new BehaviorTree(root);
300
-
301
- // Replace deeply nested action
302
- const newAction = new PrintAction({
303
- id: "new-deep",
304
- message: "New Deep",
305
- });
306
- tree.replaceNodeAtPath("/1/0/0", newAction);
307
-
308
- expect(tree.findNodeByPath("/1/0/0")).toBe(newAction);
309
- expect(tree.findNodeById("new-deep")).toBe(newAction);
310
- expect(tree.findNodeById("deep-action")).toBeNull();
311
- });
312
- });
313
-
314
- describe("replaceNodeAtPath - Edge Cases", () => {
315
- it("should throw error for invalid path", () => {
316
- const tree = new BehaviorTree(new Sequence({ id: "root" }));
317
-
318
- expect(() => {
319
- tree.replaceNodeAtPath(
320
- "/99",
321
- new PrintAction({ id: "test", message: "Test" }),
322
- );
323
- }).toThrow("Node not found at path");
324
- });
325
-
326
- it("should throw error when trying to replace non-existent node", () => {
327
- const root = new Sequence({ id: "root" });
328
- root.addChild(new PrintAction({ id: "c1", message: "Child" }));
329
- const tree = new BehaviorTree(root);
330
-
331
- expect(() => {
332
- tree.replaceNodeAtPath(
333
- "/5",
334
- new PrintAction({ id: "test", message: "Test" }),
335
- );
336
- }).toThrow("Node not found at path");
337
- });
338
- });
339
-
340
- describe("Multiple Replacements", () => {
341
- it("should handle multiple sequential replacements", () => {
342
- const seq = new Sequence({ id: "seq" });
343
- const a1 = new PrintAction({ id: "a1", message: "First" });
344
- const a2 = new PrintAction({ id: "a2", message: "Second" });
345
- const a3 = new PrintAction({ id: "a3", message: "Third" });
346
- seq.addChildren([a1, a2, a3]);
347
-
348
- const tree = new BehaviorTree(seq);
349
-
350
- // First replacement
351
- tree.replaceNodeAtPath(
352
- "/0",
353
- new PrintAction({ id: "new1", message: "New First" }),
354
- );
355
- expect(tree.findNodeById("new1")).toBeDefined();
356
- expect(tree.findNodeById("a1")).toBeNull();
357
-
358
- // Second replacement
359
- tree.replaceNodeAtPath(
360
- "/2",
361
- new PrintAction({ id: "new3", message: "New Third" }),
362
- );
363
- expect(tree.findNodeById("new3")).toBeDefined();
364
- expect(tree.findNodeById("a3")).toBeNull();
365
-
366
- // Middle node unchanged
367
- expect(tree.findNodeByPath("/1")).toBe(a2);
368
- });
369
- });
370
-
371
- describe("parsePathWithTreeId", () => {
372
- it("should parse path with tree ID prefix (#TreeID/path)", () => {
373
- const result = BehaviorTree.parsePathWithTreeId("#SimpleTest/0/1");
374
-
375
- expect(result.treeId).toBe("SimpleTest");
376
- expect(result.nodePath).toBe("/0/1");
377
- });
378
-
379
- it("should parse path with tree ID and root path", () => {
380
- const result = BehaviorTree.parsePathWithTreeId("#MyTree/");
381
-
382
- expect(result.treeId).toBe("MyTree");
383
- expect(result.nodePath).toBe("/");
384
- });
385
-
386
- it("should parse path with tree ID only (no slash)", () => {
387
- const result = BehaviorTree.parsePathWithTreeId("#OnlyTreeId");
388
-
389
- expect(result.treeId).toBe("OnlyTreeId");
390
- expect(result.nodePath).toBe("/");
391
- });
392
-
393
- it("should throw error for path without tree ID prefix", () => {
394
- expect(() => BehaviorTree.parsePathWithTreeId("/0/1/2")).toThrow(
395
- "Invalid path format",
396
- );
397
- });
398
-
399
- it("should throw error for empty tree ID (#/0/1)", () => {
400
- expect(() => BehaviorTree.parsePathWithTreeId("#/0/1")).toThrow(
401
- "tree ID cannot be empty",
402
- );
403
- });
404
-
405
- it("should throw error for # alone", () => {
406
- expect(() => BehaviorTree.parsePathWithTreeId("#")).toThrow(
407
- "tree ID cannot be empty",
408
- );
409
- });
410
-
411
- it("should throw error for whitespace-only tree ID", () => {
412
- expect(() => BehaviorTree.parsePathWithTreeId("# /0")).toThrow(
413
- "tree ID cannot be empty",
414
- );
415
- });
416
-
417
- it("should handle tree ID with special characters", () => {
418
- const result = BehaviorTree.parsePathWithTreeId("#My_Tree-123/0");
419
-
420
- expect(result.treeId).toBe("My_Tree-123");
421
- expect(result.nodePath).toBe("/0");
422
- });
423
-
424
- it("should handle deeply nested paths", () => {
425
- const result = BehaviorTree.parsePathWithTreeId("#Root/0/1/2/3/4");
426
-
427
- expect(result.treeId).toBe("Root");
428
- expect(result.nodePath).toBe("/0/1/2/3/4");
429
- });
430
- });
431
- });