@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,286 @@
1
+ /**
2
+ * YAML parser for behavior tree definitions
3
+ * Implements 4-stage validation pipeline
4
+ */
5
+
6
+ import * as yaml from "js-yaml";
7
+ import type { Registry } from "../registry.js";
8
+ import type { TreeNode } from "../types.js";
9
+ import type { TreeDefinition } from "../schemas/tree-definition.schema.js";
10
+ import {
11
+ YamlSyntaxError,
12
+ StructureValidationError,
13
+ ValidationErrors,
14
+ ValidationError,
15
+ } from "./errors.js";
16
+ import { treeDefinitionSchema } from "../schemas/tree-definition.schema.js";
17
+ import { semanticValidator } from "./validation/semantic-validator.js";
18
+
19
+ /**
20
+ * Options for YAML loading and validation
21
+ */
22
+ export interface LoadOptions {
23
+ /**
24
+ * Enable validation (default: true)
25
+ */
26
+ validate?: boolean;
27
+
28
+ /**
29
+ * Fail on first error or collect all errors (default: true - fail fast)
30
+ */
31
+ failFast?: boolean;
32
+
33
+ /**
34
+ * Auto-generate IDs for nodes without IDs (default: true)
35
+ */
36
+ autoGenerateIds?: boolean;
37
+ }
38
+
39
+ /**
40
+ * Validation options for validateYaml()
41
+ */
42
+ export interface ValidationOptions {
43
+ /**
44
+ * Collect all errors instead of failing fast (default: false)
45
+ */
46
+ collectAllErrors?: boolean;
47
+
48
+ /**
49
+ * Check semantic rules like references and circular deps (default: true)
50
+ */
51
+ checkReferences?: boolean;
52
+ }
53
+
54
+ /**
55
+ * Validation result
56
+ */
57
+ export interface ValidationResult {
58
+ valid: boolean;
59
+ errors: ValidationError[];
60
+ warnings?: string[];
61
+ }
62
+
63
+ /**
64
+ * Parse YAML string to TreeDefinition
65
+ * Stage 1: YAML Syntax Validation
66
+ *
67
+ * @param yamlString - YAML content to parse
68
+ * @returns Parsed tree definition object
69
+ * @throws YamlSyntaxError if YAML is malformed
70
+ */
71
+ export function parseYaml(yamlString: string): TreeDefinition {
72
+ try {
73
+ const parsed = yaml.load(yamlString);
74
+
75
+ if (!parsed || typeof parsed !== "object") {
76
+ throw new YamlSyntaxError(
77
+ "Invalid YAML: expected an object",
78
+ undefined,
79
+ undefined,
80
+ "Ensure your YAML defines a tree structure with 'type' field",
81
+ );
82
+ }
83
+
84
+ return parsed as TreeDefinition;
85
+ } catch (error) {
86
+ if (error instanceof YamlSyntaxError) {
87
+ throw error;
88
+ }
89
+
90
+ // Convert js-yaml errors to our error format
91
+ if (error instanceof yaml.YAMLException) {
92
+ throw new YamlSyntaxError(
93
+ error.message,
94
+ error.mark?.line,
95
+ error.mark?.column,
96
+ "Check YAML syntax (indentation, colons, quotes)",
97
+ );
98
+ }
99
+
100
+ throw new YamlSyntaxError(
101
+ `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`,
102
+ undefined,
103
+ undefined,
104
+ "Ensure your YAML is well-formed",
105
+ );
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Validate tree definition structure
111
+ * Stage 2: Structure Validation (using Zod schema)
112
+ *
113
+ * @param definition - Parsed tree definition
114
+ * @throws StructureValidationError if structure is invalid
115
+ */
116
+ function validateStructure(definition: unknown): TreeDefinition {
117
+ try {
118
+ return treeDefinitionSchema.parse(definition) as TreeDefinition;
119
+ } catch (error) {
120
+ // Convert Zod errors to StructureValidationError
121
+ throw new StructureValidationError(
122
+ `Invalid tree structure: ${error instanceof Error ? error.message : String(error)}`,
123
+ undefined,
124
+ "Ensure all nodes have 'type' field and children are arrays",
125
+ );
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Load and create tree from YAML string
131
+ *
132
+ * @param yamlString - YAML content defining the tree
133
+ * @param registry - Registry with registered node types
134
+ * @param options - Loading options
135
+ * @returns Created tree node
136
+ * @throws ValidationError if validation fails
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const yaml = `
141
+ * type: Sequence
142
+ * id: my-seq
143
+ * children:
144
+ * - type: PrintAction
145
+ * id: action1
146
+ * `;
147
+ *
148
+ * const tree = loadTreeFromYaml(yaml, registry);
149
+ * ```
150
+ */
151
+ export function loadTreeFromYaml(
152
+ yamlString: string,
153
+ registry: Registry,
154
+ options: LoadOptions = {},
155
+ ): TreeNode {
156
+ const { validate = true, failFast = true, autoGenerateIds = true } = options;
157
+
158
+ // Stage 1: Parse YAML syntax
159
+ const parsed = parseYaml(yamlString);
160
+
161
+ if (validate) {
162
+ // Stage 2: Validate structure
163
+ const definition = validateStructure(parsed);
164
+
165
+ // Stage 3 & 4: Node config and semantic validation happen in Registry.createTree()
166
+ // which calls schema validation for each node
167
+ if (failFast) {
168
+ // Semantic validation
169
+ const semanticErrors = semanticValidator.validate(definition, registry);
170
+ if (semanticErrors.length > 0) {
171
+ if (failFast) {
172
+ throw semanticErrors[0];
173
+ } else {
174
+ throw new ValidationErrors(semanticErrors);
175
+ }
176
+ }
177
+ }
178
+
179
+ // Create tree (this validates node configs via schemas)
180
+ return registry.createTree(definition);
181
+ }
182
+
183
+ // No validation - directly create tree
184
+ return registry.createTree(parsed);
185
+ }
186
+
187
+ /**
188
+ * Validate YAML without creating tree
189
+ * Useful for editors and validators
190
+ *
191
+ * @param yamlString - YAML content to validate
192
+ * @param registry - Registry with registered node types
193
+ * @param options - Validation options
194
+ * @returns Validation result with errors
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * const result = validateYaml(yaml, registry);
199
+ * if (!result.valid) {
200
+ * console.error('Validation errors:', result.errors);
201
+ * }
202
+ * ```
203
+ */
204
+ export function validateYaml(
205
+ yamlString: string,
206
+ registry: Registry,
207
+ options: ValidationOptions = {},
208
+ ): ValidationResult {
209
+ const { collectAllErrors = false, checkReferences = true } = options;
210
+ const errors: ValidationError[] = [];
211
+
212
+ try {
213
+ // Stage 1: Parse YAML
214
+ const parsed = parseYaml(yamlString);
215
+
216
+ // Stage 2: Validate structure
217
+ const definition = validateStructure(parsed);
218
+
219
+ // Stage 4: Semantic validation
220
+ if (checkReferences) {
221
+ const semanticErrors = semanticValidator.validate(definition, registry);
222
+ errors.push(...semanticErrors);
223
+ }
224
+
225
+ // Stage 3: Node config validation (via safe create)
226
+ if (errors.length === 0 || collectAllErrors) {
227
+ const result = registry.safeCreateTree(definition);
228
+ if (!result.success) {
229
+ errors.push(
230
+ new StructureValidationError(
231
+ result.error.message,
232
+ undefined,
233
+ "Check node configurations match their schema requirements",
234
+ ),
235
+ );
236
+ }
237
+ }
238
+
239
+ return {
240
+ valid: errors.length === 0,
241
+ errors,
242
+ };
243
+ } catch (error) {
244
+ if (error instanceof ValidationErrors) {
245
+ return {
246
+ valid: false,
247
+ errors: error.errors,
248
+ };
249
+ }
250
+
251
+ if (error instanceof ValidationError) {
252
+ errors.push(error);
253
+ } else {
254
+ errors.push(
255
+ new YamlSyntaxError(
256
+ error instanceof Error ? error.message : String(error),
257
+ ),
258
+ );
259
+ }
260
+
261
+ return {
262
+ valid: false,
263
+ errors,
264
+ };
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Convert TreeDefinition to YAML string
270
+ *
271
+ * @param definition - Tree definition to convert
272
+ * @returns YAML string representation
273
+ *
274
+ * @example
275
+ * ```typescript
276
+ * const definition = { type: 'Sequence', id: 'root', children: [] };
277
+ * const yamlString = toYaml(definition);
278
+ * ```
279
+ */
280
+ export function toYaml(definition: TreeDefinition): string {
281
+ return yaml.dump(definition, {
282
+ indent: 2,
283
+ lineWidth: 80,
284
+ noRefs: true,
285
+ });
286
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Semantic validation (Stage 4)
3
+ * Validates semantic rules like ID uniqueness, circular references, etc.
4
+ */
5
+
6
+ import type { Registry } from "../../registry.js";
7
+ import type { TreeDefinition } from "../../schemas/tree-definition.schema.js";
8
+ import { SemanticValidationError } from "../errors.js";
9
+
10
+ /**
11
+ * Semantic validator for tree definitions
12
+ * Validates cross-node rules that can't be expressed in schemas
13
+ */
14
+ class SemanticValidator {
15
+ /**
16
+ * Validate semantic rules for a tree definition
17
+ *
18
+ * @param definition - Tree definition to validate
19
+ * @param registry - Registry to check node types and tree references
20
+ * @returns Array of validation errors (empty if valid)
21
+ */
22
+ validate(definition: TreeDefinition, registry: Registry): SemanticValidationError[] {
23
+ const errors: SemanticValidationError[] = [];
24
+
25
+ // Track IDs for uniqueness checking
26
+ const seenIds = new Set<string>();
27
+
28
+ // Track SubTree references for circular dependency detection
29
+ const subTreePath: string[] = [];
30
+
31
+ // Recursive validation
32
+ this.validateNode(definition, registry, seenIds, subTreePath, "", errors);
33
+
34
+ return errors;
35
+ }
36
+
37
+ /**
38
+ * Recursively validate a node and its children
39
+ */
40
+ private validateNode(
41
+ node: TreeDefinition,
42
+ registry: Registry,
43
+ seenIds: Set<string>,
44
+ subTreePath: string[],
45
+ path: string,
46
+ errors: SemanticValidationError[],
47
+ ): void {
48
+ const nodePath = path ? `${path}.${node.id || node.type}` : node.id || node.type;
49
+
50
+ // Validate node type exists in registry
51
+ if (!registry.has(node.type)) {
52
+ errors.push(
53
+ new SemanticValidationError(
54
+ `Unknown node type '${node.type}'`,
55
+ nodePath,
56
+ `Available types: ${registry.getRegisteredTypes().join(", ")}`,
57
+ ),
58
+ );
59
+ return; // Can't continue validation without valid type
60
+ }
61
+
62
+ // Validate ID uniqueness
63
+ if (node.id) {
64
+ if (seenIds.has(node.id)) {
65
+ errors.push(
66
+ new SemanticValidationError(
67
+ `Duplicate ID '${node.id}'`,
68
+ nodePath,
69
+ "Use unique IDs for each node in the tree",
70
+ ),
71
+ );
72
+ } else {
73
+ seenIds.add(node.id);
74
+ }
75
+ }
76
+
77
+ // Validate SubTree references for circular dependencies
78
+ if (node.type === "SubTree") {
79
+ const treeId = (node.props?.treeId as string) || "";
80
+
81
+ if (!treeId) {
82
+ errors.push(
83
+ new SemanticValidationError(
84
+ "SubTree node missing 'treeId' property",
85
+ nodePath,
86
+ "Specify which tree to reference with 'treeId' in props",
87
+ ),
88
+ );
89
+ } else {
90
+ // Check for circular reference
91
+ if (subTreePath.includes(treeId)) {
92
+ errors.push(
93
+ new SemanticValidationError(
94
+ `Circular SubTree reference detected: ${subTreePath.join(" -> ")} -> ${treeId}`,
95
+ nodePath,
96
+ "Remove circular tree references",
97
+ ),
98
+ );
99
+ }
100
+
101
+ // Check if referenced tree exists (if registry has tree lookup)
102
+ if (registry.hasTree && !registry.hasTree(treeId)) {
103
+ errors.push(
104
+ new SemanticValidationError(
105
+ `SubTree references unknown tree '${treeId}'`,
106
+ nodePath,
107
+ `Register the tree '${treeId}' before referencing it`,
108
+ ),
109
+ );
110
+ }
111
+ }
112
+ }
113
+
114
+ // Validate children based on node category
115
+ const metadata = registry.getMetadata(node.type);
116
+ if (metadata) {
117
+ if (metadata.category === "decorator") {
118
+ // Decorators must have exactly 1 child
119
+ const childCount = node.children?.length || 0;
120
+ if (childCount !== 1) {
121
+ errors.push(
122
+ new SemanticValidationError(
123
+ `Decorator '${node.type}' must have exactly 1 child (has ${childCount})`,
124
+ nodePath,
125
+ "Decorators wrap a single child node",
126
+ ),
127
+ );
128
+ }
129
+ }
130
+
131
+ // Some composites have specific child requirements
132
+ if (node.type === "While") {
133
+ const childCount = node.children?.length || 0;
134
+ if (childCount !== 2) {
135
+ errors.push(
136
+ new SemanticValidationError(
137
+ `While node requires exactly 2 children: condition and body (has ${childCount})`,
138
+ nodePath,
139
+ "First child is the condition, second is the body to execute",
140
+ ),
141
+ );
142
+ }
143
+ }
144
+
145
+ if (node.type === "Conditional") {
146
+ const childCount = node.children?.length || 0;
147
+ if (childCount < 2 || childCount > 3) {
148
+ errors.push(
149
+ new SemanticValidationError(
150
+ `Conditional node requires 2-3 children: condition, then, optional else (has ${childCount})`,
151
+ nodePath,
152
+ "First child is condition, second is 'then' branch, third is optional 'else' branch",
153
+ ),
154
+ );
155
+ }
156
+ }
157
+
158
+ if (node.type === "ForEach") {
159
+ const childCount = node.children?.length || 0;
160
+ if (childCount === 0) {
161
+ errors.push(
162
+ new SemanticValidationError(
163
+ "ForEach node requires at least 1 child (the body to execute for each item)",
164
+ nodePath,
165
+ "Add a child node to execute for each item in the collection",
166
+ ),
167
+ );
168
+ }
169
+ }
170
+ }
171
+
172
+ // Recursively validate children
173
+ if (node.children && Array.isArray(node.children)) {
174
+ const newSubTreePath =
175
+ node.type === "SubTree" && node.props?.treeId
176
+ ? [...subTreePath, node.props.treeId as string]
177
+ : subTreePath;
178
+
179
+ node.children.forEach((child, index) => {
180
+ this.validateNode(
181
+ child,
182
+ registry,
183
+ seenIds,
184
+ newSubTreePath,
185
+ `${nodePath}.children[${index}]`,
186
+ errors,
187
+ );
188
+ });
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Singleton semantic validator instance
195
+ */
196
+ export const semanticValidator = new SemanticValidator();
@@ -0,0 +1,76 @@
1
+ # Template: GoogleSheets.InsertRow
2
+ #
3
+ # Friendly interface for inserting rows into Google Sheets.
4
+ # Handles data transformation to Active Pieces format.
5
+ #
6
+ # Expected inputs (set on blackboard before calling):
7
+ # tpl_spreadsheet: string - Spreadsheet ID
8
+ # tpl_sheet: number - Sheet index (default: 0)
9
+ # tpl_values: array|object - Values as array or { "Column": "value" }
10
+ #
11
+ # Output:
12
+ # tpl_result: object - Result from insert operation
13
+
14
+ type: Sequence
15
+ id: google-sheets-insert-row
16
+ children:
17
+ # Step 1: Validate and prepare inputs
18
+ - type: CodeExecution
19
+ id: _prepare-inputs
20
+ props:
21
+ language: javascript
22
+ code: |
23
+ const spreadsheet = getBB('tpl_spreadsheet');
24
+ if (!spreadsheet) {
25
+ throw new Error('Missing required input: tpl_spreadsheet');
26
+ }
27
+
28
+ const sheet = getBB('tpl_sheet');
29
+ const sheetId = (sheet === undefined || sheet === null) ? 0 : Number(sheet);
30
+
31
+ const values = getBB('tpl_values');
32
+ if (!values) {
33
+ throw new Error('Missing required input: tpl_values');
34
+ }
35
+
36
+ // Transform values to Active Pieces format
37
+ let transformed;
38
+ if (Array.isArray(values)) {
39
+ transformed = { values: values };
40
+ } else if (typeof values === 'object') {
41
+ transformed = { values: Object.values(values) };
42
+ }
43
+
44
+ // Store prepared values
45
+ setBB('tpl_spreadsheetId', spreadsheet);
46
+ setBB('tpl_sheetId', sheetId);
47
+ setBB('tpl_transformedValues', transformed);
48
+
49
+ # Step 2: Log what we're doing
50
+ - type: LogMessage
51
+ id: _log-insert
52
+ props:
53
+ message: "Inserting row into sheet ${tpl_spreadsheetId}"
54
+ level: debug
55
+
56
+ # Step 3: Execute the insert
57
+ - type: IntegrationAction
58
+ id: _do-insert
59
+ props:
60
+ provider: google
61
+ action: insert_row
62
+ inputs:
63
+ spreadsheetId: "${bb.tpl_spreadsheetId}"
64
+ sheetId: "${bb.tpl_sheetId}"
65
+ first_row_headers: false
66
+ as_string: true
67
+ values: "${bb.tpl_transformedValues}"
68
+
69
+ # Step 4: Store result for caller
70
+ - type: CodeExecution
71
+ id: _store-result
72
+ props:
73
+ language: javascript
74
+ code: |
75
+ const result = getBB('_do-insert.result');
76
+ setBB('tpl_result', result);
@@ -0,0 +1,33 @@
1
+ # Notification Sender Template
2
+ # Sends notifications through configured channels
3
+ #
4
+ # Inputs (expected on blackboard):
5
+ # - notificationMessage: string
6
+ # - notificationType: string (email, slack, sms)
7
+ # - recipient: string
8
+ #
9
+ # Outputs (set on blackboard):
10
+ # - notificationSent: boolean
11
+ # - notificationId: string (optional)
12
+
13
+ type: Sequence
14
+ id: notification-sender
15
+ name: Notification Sender
16
+
17
+ children:
18
+ - type: LogMessage
19
+ id: log-start
20
+ props:
21
+ message: "Preparing to send notification: ${notificationMessage}"
22
+ level: info
23
+
24
+ - type: LogMessage
25
+ id: log-type
26
+ props:
27
+ message: "Notification type: ${notificationType}, Recipient: ${recipient}"
28
+
29
+ - type: LogMessage
30
+ id: log-complete
31
+ props:
32
+ message: "Notification sent successfully"
33
+ level: info
@@ -0,0 +1,44 @@
1
+ # Order Validation Template
2
+ # Validates order data before processing
3
+ #
4
+ # Inputs (expected on blackboard):
5
+ # - orderId: string
6
+ # - items: array of order items
7
+ # - customerId: string (optional)
8
+ #
9
+ # Outputs (set on blackboard):
10
+ # - validationPassed: boolean
11
+ # - validationErrors: array of error messages
12
+
13
+ type: Sequence
14
+ id: order-validation
15
+ name: Order Validation
16
+
17
+ children:
18
+ - type: LogMessage
19
+ id: start-validation
20
+ props:
21
+ message: "Starting order validation for order: ${orderId}"
22
+ level: info
23
+
24
+ - type: Selector
25
+ id: validate-all
26
+ children:
27
+ - type: Sequence
28
+ id: all-checks
29
+ children:
30
+ - type: LogMessage
31
+ id: check-order-id
32
+ props:
33
+ message: "Validating order ID..."
34
+
35
+ - type: LogMessage
36
+ id: check-items
37
+ props:
38
+ message: "Validating order items..."
39
+
40
+ - type: LogMessage
41
+ id: validation-complete
42
+ props:
43
+ message: "Order validation completed successfully"
44
+ level: info
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "incremental": false,
4
+ "isolatedModules": true,
5
+ "moduleDetection": "force",
6
+ "noUncheckedIndexedAccess": true,
7
+ "resolveJsonModule": true,
8
+ "outDir": "./dist",
9
+ "rootDir": "./",
10
+ "declaration": true,
11
+ "declarationMap": true,
12
+ "lib": ["ES2022"],
13
+ "target": "ES2022",
14
+ "module": "NodeNext",
15
+ "moduleResolution": "NodeNext",
16
+ "strict": true,
17
+ "esModuleInterop": true,
18
+ "skipLibCheck": true,
19
+ "forceConsistentCasingInFileNames": true,
20
+ "types": ["node"]
21
+ },
22
+ "include": ["src/**/*", "examples/**/*"],
23
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
24
+ }
@@ -0,0 +1,25 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ watch: false,
6
+ globals: true,
7
+ environment: "node",
8
+ include: ["src/**/*.{test,spec}.{js,ts}"],
9
+ coverage: {
10
+ provider: "v8",
11
+ reporter: ["text", "json", "html"],
12
+ include: ["src/**/*.ts"],
13
+ exclude: [
14
+ "src/index.ts",
15
+ "src/**/*.test.ts",
16
+ "src/**/*.spec.ts",
17
+ "src/test-nodes.ts", // Test utilities
18
+ "src/scripting/generated/**/*", // Generated files
19
+ ],
20
+ },
21
+ },
22
+ resolve: {
23
+ extensions: [".ts", ".js"],
24
+ },
25
+ });