@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.
Files changed (203) hide show
  1. package/.claude/settings.local.json +31 -0
  2. package/CLAUDE.md +181 -0
  3. package/LICENSE +21 -0
  4. package/README.md +920 -0
  5. package/behaviour-tree-workflows-landing/index.html +16 -0
  6. package/behaviour-tree-workflows-landing/package-lock.json +2074 -0
  7. package/behaviour-tree-workflows-landing/package.json +31 -0
  8. package/behaviour-tree-workflows-landing/public/favicon.svg +17 -0
  9. package/behaviour-tree-workflows-landing/src/App.css +103 -0
  10. package/behaviour-tree-workflows-landing/src/App.tsx +176 -0
  11. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.css +89 -0
  12. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.tsx +64 -0
  13. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.css +64 -0
  14. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.tsx +34 -0
  15. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.css +107 -0
  16. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.tsx +85 -0
  17. package/behaviour-tree-workflows-landing/src/components/Header.css +50 -0
  18. package/behaviour-tree-workflows-landing/src/components/Header.tsx +26 -0
  19. package/behaviour-tree-workflows-landing/src/components/StatusBadge.css +45 -0
  20. package/behaviour-tree-workflows-landing/src/components/StatusBadge.tsx +15 -0
  21. package/behaviour-tree-workflows-landing/src/components/Toolbar.css +74 -0
  22. package/behaviour-tree-workflows-landing/src/components/Toolbar.tsx +53 -0
  23. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.css +67 -0
  24. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.tsx +192 -0
  25. package/behaviour-tree-workflows-landing/src/components/YamlEditor.css +18 -0
  26. package/behaviour-tree-workflows-landing/src/components/YamlEditor.tsx +96 -0
  27. package/behaviour-tree-workflows-landing/src/lib/count-nodes.ts +11 -0
  28. package/behaviour-tree-workflows-landing/src/lib/execution-engine.ts +96 -0
  29. package/behaviour-tree-workflows-landing/src/lib/tree-layout.ts +136 -0
  30. package/behaviour-tree-workflows-landing/src/lib/yaml-examples.ts +549 -0
  31. package/behaviour-tree-workflows-landing/src/main.tsx +9 -0
  32. package/behaviour-tree-workflows-landing/src/stubs/activepieces.ts +18 -0
  33. package/behaviour-tree-workflows-landing/src/stubs/fs.ts +24 -0
  34. package/behaviour-tree-workflows-landing/src/stubs/path.ts +16 -0
  35. package/behaviour-tree-workflows-landing/src/stubs/temporal-activity.ts +6 -0
  36. package/behaviour-tree-workflows-landing/src/stubs/temporal-workflow.ts +22 -0
  37. package/behaviour-tree-workflows-landing/tsconfig.json +25 -0
  38. package/behaviour-tree-workflows-landing/vite.config.ts +40 -0
  39. package/demo-google-sheets.ts +181 -0
  40. package/demo-runtime-variables.ts +174 -0
  41. package/demo-template.ts +208 -0
  42. package/docs/ARCHITECTURE_SUMMARY.md +613 -0
  43. package/docs/NODE_REFERENCE.md +504 -0
  44. package/docs/README.md +53 -0
  45. package/docs/custom-nodes-architecture.md +826 -0
  46. package/docs/observability.md +175 -0
  47. package/docs/yaml-specification.md +990 -0
  48. package/examples/temporal/README.md +117 -0
  49. package/examples/temporal/activities.ts +373 -0
  50. package/examples/temporal/client.ts +115 -0
  51. package/examples/temporal/python-worker/activities.py +339 -0
  52. package/examples/temporal/python-worker/requirements.txt +12 -0
  53. package/examples/temporal/python-worker/worker.py +106 -0
  54. package/examples/temporal/worker.ts +66 -0
  55. package/examples/temporal/workflows.ts +6 -0
  56. package/examples/temporal/yaml-workflow-loader.ts +105 -0
  57. package/examples/yaml-test.ts +97 -0
  58. package/examples/yaml-workflows/01-simple-sequence.yaml +25 -0
  59. package/examples/yaml-workflows/02-parallel-timeout.yaml +45 -0
  60. package/examples/yaml-workflows/03-ecommerce-checkout.yaml +94 -0
  61. package/examples/yaml-workflows/04-ai-agent-workflow.yaml +346 -0
  62. package/examples/yaml-workflows/05-order-processing.yaml +146 -0
  63. package/examples/yaml-workflows/06-activity-test.yaml +71 -0
  64. package/examples/yaml-workflows/07-activity-simple-test.yaml +43 -0
  65. package/examples/yaml-workflows/08-file-processing.yaml +141 -0
  66. package/examples/yaml-workflows/09-http-request.yaml +137 -0
  67. package/examples/yaml-workflows/README.md +211 -0
  68. package/package.json +38 -0
  69. package/src/actions/code-execution.schema.ts +27 -0
  70. package/src/actions/code-execution.ts +218 -0
  71. package/src/actions/generate-file.test.ts +516 -0
  72. package/src/actions/generate-file.ts +166 -0
  73. package/src/actions/http-request.test.ts +784 -0
  74. package/src/actions/http-request.ts +228 -0
  75. package/src/actions/index.ts +20 -0
  76. package/src/actions/parse-file.test.ts +448 -0
  77. package/src/actions/parse-file.ts +139 -0
  78. package/src/actions/python-script.test.ts +439 -0
  79. package/src/actions/python-script.ts +154 -0
  80. package/src/base-node.test.ts +511 -0
  81. package/src/base-node.ts +605 -0
  82. package/src/behavior-tree.test.ts +431 -0
  83. package/src/behavior-tree.ts +283 -0
  84. package/src/blackboard.test.ts +222 -0
  85. package/src/blackboard.ts +192 -0
  86. package/src/composites/conditional.schema.ts +19 -0
  87. package/src/composites/conditional.test.ts +309 -0
  88. package/src/composites/conditional.ts +129 -0
  89. package/src/composites/for-each.schema.ts +23 -0
  90. package/src/composites/for-each.test.ts +254 -0
  91. package/src/composites/for-each.ts +132 -0
  92. package/src/composites/index.ts +15 -0
  93. package/src/composites/memory-sequence.schema.ts +19 -0
  94. package/src/composites/memory-sequence.test.ts +223 -0
  95. package/src/composites/memory-sequence.ts +98 -0
  96. package/src/composites/parallel.schema.ts +28 -0
  97. package/src/composites/parallel.test.ts +502 -0
  98. package/src/composites/parallel.ts +157 -0
  99. package/src/composites/reactive-sequence.schema.ts +19 -0
  100. package/src/composites/reactive-sequence.test.ts +170 -0
  101. package/src/composites/reactive-sequence.ts +85 -0
  102. package/src/composites/recovery.schema.ts +19 -0
  103. package/src/composites/recovery.test.ts +366 -0
  104. package/src/composites/recovery.ts +90 -0
  105. package/src/composites/selector.schema.ts +19 -0
  106. package/src/composites/selector.test.ts +387 -0
  107. package/src/composites/selector.ts +85 -0
  108. package/src/composites/sequence.schema.ts +19 -0
  109. package/src/composites/sequence.test.ts +337 -0
  110. package/src/composites/sequence.ts +72 -0
  111. package/src/composites/sub-tree.schema.ts +21 -0
  112. package/src/composites/sub-tree.test.ts +893 -0
  113. package/src/composites/sub-tree.ts +177 -0
  114. package/src/composites/while.schema.ts +24 -0
  115. package/src/composites/while.test.ts +381 -0
  116. package/src/composites/while.ts +149 -0
  117. package/src/data-store/index.ts +10 -0
  118. package/src/data-store/memory-store.ts +161 -0
  119. package/src/data-store/types.ts +94 -0
  120. package/src/debug/breakpoint.test.ts +47 -0
  121. package/src/debug/breakpoint.ts +30 -0
  122. package/src/debug/index.ts +17 -0
  123. package/src/debug/resume-point.test.ts +49 -0
  124. package/src/debug/resume-point.ts +29 -0
  125. package/src/decorators/delay.schema.ts +21 -0
  126. package/src/decorators/delay.test.ts +261 -0
  127. package/src/decorators/delay.ts +140 -0
  128. package/src/decorators/force-result.schema.ts +32 -0
  129. package/src/decorators/force-result.test.ts +133 -0
  130. package/src/decorators/force-result.ts +63 -0
  131. package/src/decorators/index.ts +13 -0
  132. package/src/decorators/invert.schema.ts +19 -0
  133. package/src/decorators/invert.test.ts +135 -0
  134. package/src/decorators/invert.ts +42 -0
  135. package/src/decorators/keep-running.schema.ts +20 -0
  136. package/src/decorators/keep-running.test.ts +105 -0
  137. package/src/decorators/keep-running.ts +49 -0
  138. package/src/decorators/precondition.schema.ts +19 -0
  139. package/src/decorators/precondition.test.ts +351 -0
  140. package/src/decorators/precondition.ts +139 -0
  141. package/src/decorators/repeat.schema.ts +21 -0
  142. package/src/decorators/repeat.test.ts +187 -0
  143. package/src/decorators/repeat.ts +94 -0
  144. package/src/decorators/run-once.schema.ts +19 -0
  145. package/src/decorators/run-once.test.ts +140 -0
  146. package/src/decorators/run-once.ts +61 -0
  147. package/src/decorators/soft-assert.schema.ts +19 -0
  148. package/src/decorators/soft-assert.test.ts +107 -0
  149. package/src/decorators/soft-assert.ts +68 -0
  150. package/src/decorators/timeout.schema.ts +21 -0
  151. package/src/decorators/timeout.test.ts +274 -0
  152. package/src/decorators/timeout.ts +159 -0
  153. package/src/errors.test.ts +63 -0
  154. package/src/errors.ts +34 -0
  155. package/src/events.test.ts +347 -0
  156. package/src/events.ts +183 -0
  157. package/src/index.ts +80 -0
  158. package/src/integrations/index.ts +30 -0
  159. package/src/integrations/integration-action.test.ts +571 -0
  160. package/src/integrations/integration-action.ts +233 -0
  161. package/src/integrations/piece-executor.ts +320 -0
  162. package/src/observability/execution-tracker.ts +320 -0
  163. package/src/observability/index.ts +23 -0
  164. package/src/observability/sinks.ts +138 -0
  165. package/src/observability/types.ts +130 -0
  166. package/src/registry-utils.ts +147 -0
  167. package/src/registry.test.ts +466 -0
  168. package/src/registry.ts +334 -0
  169. package/src/schemas/base.schema.ts +104 -0
  170. package/src/schemas/index.ts +223 -0
  171. package/src/schemas/integration.test.ts +238 -0
  172. package/src/schemas/tree-definition.schema.ts +170 -0
  173. package/src/schemas/validation.test.ts +146 -0
  174. package/src/schemas/validation.ts +122 -0
  175. package/src/scripting/index.ts +22 -0
  176. package/src/templates/template-loader.test.ts +281 -0
  177. package/src/templates/template-loader.ts +152 -0
  178. package/src/temporal-integration.test.ts +213 -0
  179. package/src/test-nodes.ts +259 -0
  180. package/src/types.ts +503 -0
  181. package/src/utilities/index.ts +17 -0
  182. package/src/utilities/log-message.test.ts +275 -0
  183. package/src/utilities/log-message.ts +134 -0
  184. package/src/utilities/regex-extract.test.ts +138 -0
  185. package/src/utilities/regex-extract.ts +108 -0
  186. package/src/utilities/variable-resolver.test.ts +416 -0
  187. package/src/utilities/variable-resolver.ts +318 -0
  188. package/src/utils/error-handler.test.ts +117 -0
  189. package/src/utils/error-handler.ts +48 -0
  190. package/src/utils/signal-check.test.ts +234 -0
  191. package/src/utils/signal-check.ts +140 -0
  192. package/src/yaml/errors.ts +143 -0
  193. package/src/yaml/index.ts +30 -0
  194. package/src/yaml/loader.ts +39 -0
  195. package/src/yaml/parser.ts +286 -0
  196. package/src/yaml/validation/semantic-validator.ts +196 -0
  197. package/templates/google-sheets/insert-row.yaml +76 -0
  198. package/templates/notification-sender.yaml +33 -0
  199. package/templates/order-validation.yaml +44 -0
  200. package/tsconfig.json +24 -0
  201. package/vitest.config.ts +25 -0
  202. package/workflows/order-processor.yaml +59 -0
  203. package/workflows/process-order-workflow.yaml +142 -0
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Registry for behavior tree nodes
3
+ * Handles registration and creation of nodes by type
4
+ */
5
+
6
+ import type { BehaviorTree } from "./behavior-tree.js";
7
+ import type {
8
+ NodeConfiguration,
9
+ NodeConstructor,
10
+ NodeMetadata,
11
+ TreeNode,
12
+ } from "./types.js";
13
+ import { z } from "zod";
14
+ import {
15
+ schemaRegistry,
16
+ validateConfiguration,
17
+ treeDefinitionSchema,
18
+ type TreeDefinition,
19
+ } from "./schemas/index.js";
20
+
21
+ /**
22
+ * Entry for a registered BehaviorTree with metadata
23
+ */
24
+ interface TreeEntry {
25
+ tree: BehaviorTree;
26
+ sourceFile: string;
27
+ }
28
+
29
+ export class Registry {
30
+ private nodeTypes: Map<string, NodeConstructor> = new Map();
31
+ private nodeMetadata: Map<string, NodeMetadata> = new Map();
32
+ private behaviorTrees: Map<string, TreeEntry> = new Map();
33
+
34
+ constructor() {
35
+ this.log("Registry created");
36
+ }
37
+
38
+ /**
39
+ * Register a node type with the registry
40
+ */
41
+ register<T extends TreeNode>(
42
+ type: string,
43
+ ctor: NodeConstructor<T>,
44
+ metadata?: Partial<NodeMetadata>,
45
+ ): void {
46
+ if (this.nodeTypes.has(type)) {
47
+ throw new Error(`Node type '${type}' is already registered`);
48
+ }
49
+
50
+ this.nodeTypes.set(type, ctor);
51
+
52
+ // Store metadata
53
+ const fullMetadata: NodeMetadata = {
54
+ type,
55
+ category: metadata?.category || "action",
56
+ description: metadata?.description,
57
+ ports: metadata?.ports || [],
58
+ };
59
+
60
+ this.nodeMetadata.set(type, fullMetadata);
61
+ this.log(`Registered node type: ${type} (${fullMetadata.category})`);
62
+ }
63
+
64
+ /**
65
+ * Create a node instance by type
66
+ * Validates configuration against schema before creating node
67
+ */
68
+ create(type: string, config: NodeConfiguration): TreeNode {
69
+ const Constructor = this.nodeTypes.get(type);
70
+
71
+ if (!Constructor) {
72
+ throw new Error(
73
+ `Unknown node type: '${type}'. Available types: ${this.getRegisteredTypes().join(", ")}`,
74
+ );
75
+ }
76
+
77
+ // Validate configuration using schema registry
78
+ const validatedConfig = validateConfiguration<NodeConfiguration>(
79
+ schemaRegistry.getSchema(type) as z.ZodSchema<NodeConfiguration>,
80
+ config,
81
+ type,
82
+ config.id,
83
+ );
84
+
85
+ this.log(`Creating node of type: ${type} with id: ${validatedConfig.id}`);
86
+ return new Constructor(validatedConfig);
87
+ }
88
+
89
+ /**
90
+ * Check if a node type is registered
91
+ */
92
+ has(type: string): boolean {
93
+ return this.nodeTypes.has(type);
94
+ }
95
+
96
+ /**
97
+ * Get metadata for a node type
98
+ */
99
+ getMetadata(type: string): NodeMetadata | undefined {
100
+ return this.nodeMetadata.get(type);
101
+ }
102
+
103
+ /**
104
+ * Get all registered node types
105
+ */
106
+ getRegisteredTypes(): string[] {
107
+ return Array.from(this.nodeTypes.keys());
108
+ }
109
+
110
+ /**
111
+ * Get registered types by category
112
+ */
113
+ getTypesByCategory(category: NodeMetadata["category"]): string[] {
114
+ const types: string[] = [];
115
+
116
+ for (const [type, metadata] of this.nodeMetadata) {
117
+ if (metadata.category === category) {
118
+ types.push(type);
119
+ }
120
+ }
121
+
122
+ return types;
123
+ }
124
+
125
+ /**
126
+ * Clear all registrations (node types, metadata, and behavior trees)
127
+ */
128
+ clear(): void {
129
+ this.nodeTypes.clear();
130
+ this.nodeMetadata.clear();
131
+ this.behaviorTrees.clear();
132
+ this.log("Registry cleared (nodes and trees)");
133
+ }
134
+
135
+ /**
136
+ * Register a behavior tree instance with source file metadata.
137
+ * Used for reusable trees like elements, step groups, and test cases.
138
+ *
139
+ * @param id Unique identifier for the tree
140
+ * @param tree BehaviorTree instance to register
141
+ * @param sourceFile Path to the source .sigma file
142
+ * @throws Error if a tree with the same ID is already registered
143
+ */
144
+ registerTree(id: string, tree: BehaviorTree, sourceFile: string): void {
145
+ if (this.behaviorTrees.has(id)) {
146
+ throw new Error(`Behavior tree '${id}' is already registered`);
147
+ }
148
+ this.behaviorTrees.set(id, { tree, sourceFile });
149
+ this.log(`Registered behavior tree: ${id}`);
150
+ }
151
+
152
+ /**
153
+ * Unregister a behavior tree by ID.
154
+ * Useful for cleanup in long-running processes or tests.
155
+ *
156
+ * @param id Tree ID to unregister
157
+ * @returns true if tree was found and removed, false otherwise
158
+ */
159
+ unregisterTree(id: string): boolean {
160
+ const deleted = this.behaviorTrees.delete(id);
161
+ if (deleted) {
162
+ this.log(`Unregistered behavior tree: ${id}`);
163
+ }
164
+ return deleted;
165
+ }
166
+
167
+ /**
168
+ * Replace an existing behavior tree registration.
169
+ * If the tree doesn't exist, it will be registered as new.
170
+ *
171
+ * @param id Tree ID to replace
172
+ * @param tree New BehaviorTree instance
173
+ * @param sourceFile Path to the source .sigma file
174
+ */
175
+ replaceTree(id: string, tree: BehaviorTree, sourceFile: string): void {
176
+ const existed = this.behaviorTrees.has(id);
177
+ this.behaviorTrees.set(id, { tree, sourceFile });
178
+ this.log(`${existed ? "Replaced" : "Registered"} behavior tree: ${id}`);
179
+ }
180
+
181
+ /**
182
+ * Get a behavior tree instance by ID
183
+ */
184
+ getTree(id: string): BehaviorTree | undefined {
185
+ return this.behaviorTrees.get(id)?.tree;
186
+ }
187
+
188
+ /**
189
+ * Get the source file path for a behavior tree
190
+ */
191
+ getTreeSourceFile(id: string): string | undefined {
192
+ return this.behaviorTrees.get(id)?.sourceFile;
193
+ }
194
+
195
+ /**
196
+ * Get all trees that belong to a specific source file
197
+ */
198
+ getTreesForFile(filePath: string): Map<string, BehaviorTree> {
199
+ const result = new Map<string, BehaviorTree>();
200
+ for (const [id, entry] of this.behaviorTrees) {
201
+ if (entry.sourceFile === filePath) {
202
+ result.set(id, entry.tree);
203
+ }
204
+ }
205
+ return result;
206
+ }
207
+
208
+ /**
209
+ * Check if a behavior tree is registered
210
+ */
211
+ hasTree(id: string): boolean {
212
+ return this.behaviorTrees.has(id);
213
+ }
214
+
215
+ /**
216
+ * Clone a registered behavior tree for instantiation
217
+ * Returns the cloned BehaviorTree (caller should use getRoot() for TreeNode)
218
+ */
219
+ cloneTree(id: string): BehaviorTree {
220
+ const entry = this.behaviorTrees.get(id);
221
+ if (!entry) {
222
+ throw new Error(
223
+ `Behavior tree '${id}' not found. Available trees: ${this.getAllTreeIds().join(", ") || "none"}`,
224
+ );
225
+ }
226
+ this.log(`Cloning behavior tree: ${id}`);
227
+ return entry.tree.clone();
228
+ }
229
+
230
+ /**
231
+ * Get all registered behavior tree IDs
232
+ */
233
+ getAllTreeIds(): string[] {
234
+ return Array.from(this.behaviorTrees.keys());
235
+ }
236
+
237
+ /**
238
+ * Clear all registered behavior trees
239
+ */
240
+ clearTrees(): void {
241
+ this.behaviorTrees.clear();
242
+ this.log("Cleared all behavior trees");
243
+ }
244
+
245
+ /**
246
+ * Create a tree from a JSON definition
247
+ * Validates tree structure before creating nodes
248
+ */
249
+ createTree(definition: unknown): TreeNode {
250
+ // Validate tree definition structure
251
+ const validatedDef = treeDefinitionSchema.parse(definition) as TreeDefinition;
252
+
253
+ if (!validatedDef.type) {
254
+ throw new Error("Node definition must have a type");
255
+ }
256
+
257
+ // Create the node configuration
258
+ const config: NodeConfiguration = {
259
+ id: validatedDef.id || `${validatedDef.type}_${Date.now()}`,
260
+ name: validatedDef.name || validatedDef.id,
261
+ ...validatedDef.props,
262
+ };
263
+
264
+ // Create node with validated configuration (uses schema validation)
265
+ const node = this.create(validatedDef.type, config);
266
+
267
+ // Handle children for composite/decorator nodes
268
+ if (validatedDef.children && Array.isArray(validatedDef.children)) {
269
+ if ("setChild" in node && typeof node.setChild === "function") {
270
+ // Decorator node - single child
271
+ if (validatedDef.children.length !== 1) {
272
+ throw new Error(
273
+ `Decorator ${validatedDef.type} must have exactly one child`,
274
+ );
275
+ }
276
+ const child = this.createTree(validatedDef.children[0]);
277
+ (node as { setChild: (child: TreeNode) => void }).setChild(child);
278
+ } else if (
279
+ "addChildren" in node &&
280
+ typeof node.addChildren === "function"
281
+ ) {
282
+ // Composite node - multiple children
283
+ const children = validatedDef.children.map((childDef) =>
284
+ this.createTree(childDef),
285
+ );
286
+ (node as { addChildren: (children: TreeNode[]) => void }).addChildren(
287
+ children,
288
+ );
289
+ }
290
+ }
291
+
292
+ return node;
293
+ }
294
+
295
+ /**
296
+ * Safe tree creation that returns success/error result
297
+ * Useful for user-facing tools that need graceful error handling
298
+ *
299
+ * @param definition - Tree definition to create
300
+ * @returns Success result with tree or failure result with error
301
+ *
302
+ * @example
303
+ * ```typescript
304
+ * const result = registry.safeCreateTree({
305
+ * type: 'Sequence',
306
+ * id: 'root',
307
+ * children: [...]
308
+ * });
309
+ *
310
+ * if (result.success) {
311
+ * console.log('Tree created:', result.tree);
312
+ * } else {
313
+ * console.error('Failed:', result.error.message);
314
+ * }
315
+ * ```
316
+ */
317
+ safeCreateTree(
318
+ definition: unknown,
319
+ ): { success: true; tree: TreeNode } | { success: false; error: Error } {
320
+ try {
321
+ const tree = this.createTree(definition);
322
+ return { success: true, tree };
323
+ } catch (error) {
324
+ return {
325
+ success: false,
326
+ error: error instanceof Error ? error : new Error(String(error)),
327
+ };
328
+ }
329
+ }
330
+
331
+ private log(message: string): void {
332
+ console.log(`[Registry] ${message}`);
333
+ }
334
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Base Zod schemas for node configurations
3
+ * Foundation for all node-specific validation schemas
4
+ */
5
+
6
+ import { z } from "zod";
7
+
8
+ /**
9
+ * Base schema for all node configurations
10
+ * Matches NodeConfiguration interface from types.ts
11
+ */
12
+ export const nodeConfigurationSchema = z
13
+ .object({
14
+ id: z.string().min(1, "Node ID cannot be empty"),
15
+ name: z.string().optional(),
16
+ })
17
+ .passthrough(); // Allow additional properties for node-specific configs
18
+
19
+ /**
20
+ * Branded type for validated configurations
21
+ * Ensures runtime type matches compile-time type
22
+ */
23
+ export type ValidatedNodeConfiguration = z.infer<
24
+ typeof nodeConfigurationSchema
25
+ >;
26
+
27
+ /**
28
+ * Helper to create node-specific configuration schemas
29
+ * Extends base schema with node-specific fields
30
+ *
31
+ * @param nodeType - Name of the node type for error messages
32
+ * @param fields - Node-specific field definitions
33
+ * @returns Extended Zod schema
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const timeoutSchema = createNodeSchema('Timeout', {
38
+ * timeoutMs: validations.positiveNumber('timeoutMs'),
39
+ * });
40
+ * ```
41
+ */
42
+ export function createNodeSchema<T extends z.ZodRawShape>(
43
+ nodeType: string,
44
+ fields: T,
45
+ ) {
46
+ return nodeConfigurationSchema
47
+ .extend(fields)
48
+ .describe(`${nodeType} configuration`);
49
+ }
50
+
51
+ /**
52
+ * Common validation helpers
53
+ * Reusable validators for standard patterns
54
+ */
55
+ export const validations = {
56
+ /**
57
+ * Positive number validator (> 0)
58
+ */
59
+ positiveNumber: (fieldName: string) =>
60
+ z.number().positive(`${fieldName} must be positive`),
61
+
62
+ /**
63
+ * Non-negative number validator (>= 0)
64
+ */
65
+ nonNegativeNumber: (fieldName: string) =>
66
+ z.number().nonnegative(`${fieldName} must be non-negative`),
67
+
68
+ /**
69
+ * Positive integer validator (> 0, whole number)
70
+ */
71
+ positiveInteger: (fieldName: string) =>
72
+ z.number().int().positive(`${fieldName} must be a positive integer`),
73
+
74
+ /**
75
+ * Non-negative integer validator (>= 0, whole number)
76
+ */
77
+ nonNegativeInteger: (fieldName: string) =>
78
+ z
79
+ .number()
80
+ .int()
81
+ .nonnegative(`${fieldName} must be a non-negative integer`),
82
+
83
+ /**
84
+ * Blackboard key validator (non-empty string)
85
+ */
86
+ blackboardKey: z.string().min(1, "Blackboard key cannot be empty"),
87
+
88
+ /**
89
+ * Tree ID validator (non-empty string for SubTree references)
90
+ */
91
+ treeId: z.string().min(1, "Tree ID cannot be empty"),
92
+
93
+ /**
94
+ * Duration in milliseconds validator (non-negative)
95
+ */
96
+ durationMs: (fieldName: string = "duration") =>
97
+ z.number().nonnegative(`${fieldName} must be non-negative milliseconds`),
98
+ };
99
+
100
+ /**
101
+ * Infer TypeScript type from schema
102
+ * Helper for type-safe configuration interfaces
103
+ */
104
+ export type InferSchema<T extends z.ZodSchema> = z.infer<T>;
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Schema registry and validation exports
3
+ * Central registry mapping node types to their validation schemas
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import { nodeConfigurationSchema } from "./base.schema.js";
8
+
9
+ // Import decorator schemas
10
+ import { timeoutConfigurationSchema } from "../decorators/timeout.schema.js";
11
+ import { delayConfigurationSchema } from "../decorators/delay.schema.js";
12
+ import { repeatConfigurationSchema } from "../decorators/repeat.schema.js";
13
+ import { invertConfigurationSchema } from "../decorators/invert.schema.js";
14
+ import {
15
+ forceSuccessConfigurationSchema,
16
+ forceFailureConfigurationSchema,
17
+ } from "../decorators/force-result.schema.js";
18
+ import { preconditionConfigurationSchema } from "../decorators/precondition.schema.js";
19
+ import { runOnceConfigurationSchema } from "../decorators/run-once.schema.js";
20
+ import { keepRunningUntilFailureConfigurationSchema } from "../decorators/keep-running.schema.js";
21
+ import { softAssertConfigurationSchema } from "../decorators/soft-assert.schema.js";
22
+
23
+ // Import composite schemas
24
+ import { parallelConfigurationSchema } from "../composites/parallel.schema.js";
25
+ import { forEachConfigurationSchema } from "../composites/for-each.schema.js";
26
+ import { whileConfigurationSchema } from "../composites/while.schema.js";
27
+ import { subTreeConfigurationSchema } from "../composites/sub-tree.schema.js";
28
+ import { sequenceConfigurationSchema } from "../composites/sequence.schema.js";
29
+ import { selectorConfigurationSchema } from "../composites/selector.schema.js";
30
+ import { conditionalConfigurationSchema } from "../composites/conditional.schema.js";
31
+ import { reactiveSequenceConfigurationSchema } from "../composites/reactive-sequence.schema.js";
32
+ import { memorySequenceConfigurationSchema } from "../composites/memory-sequence.schema.js";
33
+ import { recoveryConfigurationSchema } from "../composites/recovery.schema.js";
34
+
35
+ // Action schemas - Script removed, use CodeExecution instead
36
+
37
+ /**
38
+ * Central registry mapping node types to their validation schemas
39
+ *
40
+ * Responsibilities:
41
+ * - Register Zod schemas for each node type
42
+ * - Provide schema lookup by node type
43
+ * - Validate configurations against registered schemas
44
+ * - Fallback to base schema for unregistered types
45
+ *
46
+ * Usage:
47
+ * ```typescript
48
+ * // Register a schema
49
+ * schemaRegistry.register('Timeout', timeoutConfigurationSchema);
50
+ *
51
+ * // Get a schema
52
+ * const schema = schemaRegistry.getSchema('Timeout');
53
+ *
54
+ * // Validate configuration
55
+ * const config = schemaRegistry.validate('Timeout', { id: 'test', timeoutMs: 1000 });
56
+ * ```
57
+ */
58
+ export class SchemaRegistry {
59
+ private schemas = new Map<string, z.ZodSchema>();
60
+
61
+ constructor() {
62
+ this.registerAllSchemas();
63
+ }
64
+
65
+ /**
66
+ * Register all node schemas
67
+ * Called automatically on construction
68
+ */
69
+ private registerAllSchemas(): void {
70
+ // Register decorator schemas
71
+ this.register("Timeout", timeoutConfigurationSchema);
72
+ this.register("Delay", delayConfigurationSchema);
73
+ this.register("Repeat", repeatConfigurationSchema);
74
+ this.register("Invert", invertConfigurationSchema);
75
+ this.register("ForceSuccess", forceSuccessConfigurationSchema);
76
+ this.register("ForceFailure", forceFailureConfigurationSchema);
77
+ this.register("Precondition", preconditionConfigurationSchema);
78
+ this.register("RunOnce", runOnceConfigurationSchema);
79
+ this.register(
80
+ "KeepRunningUntilFailure",
81
+ keepRunningUntilFailureConfigurationSchema,
82
+ );
83
+ this.register("SoftAssert", softAssertConfigurationSchema);
84
+
85
+ // Register composite schemas
86
+ this.register("Parallel", parallelConfigurationSchema);
87
+ this.register("ForEach", forEachConfigurationSchema);
88
+ this.register("While", whileConfigurationSchema);
89
+ this.register("SubTree", subTreeConfigurationSchema);
90
+ this.register("Sequence", sequenceConfigurationSchema);
91
+ this.register("Selector", selectorConfigurationSchema);
92
+ this.register("Conditional", conditionalConfigurationSchema);
93
+ this.register("ReactiveSequence", reactiveSequenceConfigurationSchema);
94
+ this.register("MemorySequence", memorySequenceConfigurationSchema);
95
+ this.register("Recovery", recoveryConfigurationSchema);
96
+
97
+ // Action schemas - Script removed, use CodeExecution instead
98
+ }
99
+
100
+ /**
101
+ * Register a validation schema for a node type
102
+ *
103
+ * @param nodeType - Name of the node type (e.g., 'Timeout', 'Sequence')
104
+ * @param schema - Zod schema for validating this node type's configuration
105
+ * @throws Error if node type is already registered
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * schemaRegistry.register('Timeout', timeoutConfigurationSchema);
110
+ * ```
111
+ */
112
+ register(nodeType: string, schema: z.ZodSchema): void {
113
+ if (this.schemas.has(nodeType)) {
114
+ throw new Error(`Schema for node type '${nodeType}' already registered`);
115
+ }
116
+ this.schemas.set(nodeType, schema);
117
+ }
118
+
119
+ /**
120
+ * Get schema for a node type
121
+ * Returns base schema if no specific schema registered
122
+ *
123
+ * @param nodeType - Name of the node type
124
+ * @returns Zod schema for the node type (or base schema if not found)
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const schema = schemaRegistry.getSchema('Timeout');
129
+ * const validated = schema.parse({ id: 'test', timeoutMs: 1000 });
130
+ * ```
131
+ */
132
+ getSchema(nodeType: string): z.ZodSchema {
133
+ return this.schemas.get(nodeType) || nodeConfigurationSchema;
134
+ }
135
+
136
+ /**
137
+ * Check if a schema is registered for a node type
138
+ *
139
+ * @param nodeType - Name of the node type
140
+ * @returns True if schema is registered, false otherwise
141
+ */
142
+ hasSchema(nodeType: string): boolean {
143
+ return this.schemas.has(nodeType);
144
+ }
145
+
146
+ /**
147
+ * Get all registered node types
148
+ *
149
+ * @returns Array of registered node type names
150
+ */
151
+ getRegisteredTypes(): string[] {
152
+ return Array.from(this.schemas.keys());
153
+ }
154
+
155
+ /**
156
+ * Validate configuration for a specific node type
157
+ * Throws ZodError if validation fails
158
+ *
159
+ * @param nodeType - Name of the node type
160
+ * @param config - Configuration object to validate
161
+ * @returns Validated and parsed configuration
162
+ * @throws ZodError if validation fails
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const config = schemaRegistry.validate('Timeout', {
167
+ * id: 'test',
168
+ * timeoutMs: 1000
169
+ * });
170
+ * ```
171
+ */
172
+ validate<T>(nodeType: string, config: unknown): T {
173
+ const schema = this.getSchema(nodeType);
174
+ return schema.parse(config) as T;
175
+ }
176
+
177
+ /**
178
+ * Safe validation that returns success/error result
179
+ * Does not throw errors
180
+ *
181
+ * @param nodeType - Name of the node type
182
+ * @param config - Configuration object to validate
183
+ * @returns Result with success/error
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * const result = schemaRegistry.safeParse('Timeout', { id: 'test', timeoutMs: -100 });
188
+ * if (result.success) {
189
+ * console.log(result.data);
190
+ * } else {
191
+ * console.error(result.error);
192
+ * }
193
+ * ```
194
+ */
195
+ safeParse(
196
+ nodeType: string,
197
+ config: unknown,
198
+ ):
199
+ | { success: true; data: unknown }
200
+ | { success: false; error: z.ZodError<unknown> } {
201
+ const schema = this.getSchema(nodeType);
202
+ return schema.safeParse(config);
203
+ }
204
+
205
+ /**
206
+ * Clear all registered schemas
207
+ * Useful for testing
208
+ */
209
+ clear(): void {
210
+ this.schemas.clear();
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Global singleton schema registry instance
216
+ * Used by Registry class for validation
217
+ */
218
+ export const schemaRegistry = new SchemaRegistry();
219
+
220
+ // Re-export for convenience
221
+ export * from "./base.schema.js";
222
+ export * from "./validation.js";
223
+ export * from "./tree-definition.schema.js";