callman-core 1.0.6 → 1.2.0

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 (199) hide show
  1. package/dist/index.d.ts +6 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +5 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/script/errors.d.ts +3 -0
  6. package/dist/script/errors.d.ts.map +1 -0
  7. package/dist/script/errors.js +68 -0
  8. package/dist/script/errors.js.map +1 -0
  9. package/dist/script/executor.d.ts +5 -0
  10. package/dist/script/executor.d.ts.map +1 -0
  11. package/dist/script/executor.js +301 -0
  12. package/dist/script/executor.js.map +1 -0
  13. package/dist/script/features/assertions.d.ts +46 -0
  14. package/dist/script/features/assertions.d.ts.map +1 -0
  15. package/dist/script/features/assertions.js +264 -0
  16. package/dist/script/features/assertions.js.map +1 -0
  17. package/dist/script/features/counter.d.ts +11 -0
  18. package/dist/script/features/counter.d.ts.map +1 -0
  19. package/dist/script/features/counter.js +43 -0
  20. package/dist/script/features/counter.js.map +1 -0
  21. package/dist/script/features/cryptojs.d.ts +13 -0
  22. package/dist/script/features/cryptojs.d.ts.map +1 -0
  23. package/dist/script/features/cryptojs.js +63 -0
  24. package/dist/script/features/cryptojs.js.map +1 -0
  25. package/dist/script/features/db.d.ts +36 -0
  26. package/dist/script/features/db.d.ts.map +1 -0
  27. package/dist/script/features/db.js +165 -0
  28. package/dist/script/features/db.js.map +1 -0
  29. package/dist/script/features/environment.d.ts +11 -0
  30. package/dist/script/features/environment.d.ts.map +1 -0
  31. package/dist/script/features/environment.js +61 -0
  32. package/dist/script/features/environment.js.map +1 -0
  33. package/dist/script/features/feature-policy.d.ts +8 -0
  34. package/dist/script/features/feature-policy.d.ts.map +1 -0
  35. package/dist/script/features/feature-policy.js +88 -0
  36. package/dist/script/features/feature-policy.js.map +1 -0
  37. package/dist/script/features/kafka.d.ts +11 -0
  38. package/dist/script/features/kafka.d.ts.map +1 -0
  39. package/dist/script/features/kafka.js +71 -0
  40. package/dist/script/features/kafka.js.map +1 -0
  41. package/dist/script/features/log.d.ts +15 -0
  42. package/dist/script/features/log.d.ts.map +1 -0
  43. package/dist/script/features/log.js +59 -0
  44. package/dist/script/features/log.js.map +1 -0
  45. package/dist/script/features/node.d.ts +14 -0
  46. package/dist/script/features/node.d.ts.map +1 -0
  47. package/dist/script/features/node.js +82 -0
  48. package/dist/script/features/node.js.map +1 -0
  49. package/dist/script/features/random.d.ts +13 -0
  50. package/dist/script/features/random.d.ts.map +1 -0
  51. package/dist/script/features/random.js +111 -0
  52. package/dist/script/features/random.js.map +1 -0
  53. package/dist/script/features/redis.d.ts +10 -0
  54. package/dist/script/features/redis.d.ts.map +1 -0
  55. package/dist/script/features/redis.js +22 -0
  56. package/dist/script/features/redis.js.map +1 -0
  57. package/dist/script/features/request.d.ts +23 -0
  58. package/dist/script/features/request.d.ts.map +1 -0
  59. package/dist/script/features/request.js +105 -0
  60. package/dist/script/features/request.js.map +1 -0
  61. package/dist/script/features/response.d.ts +25 -0
  62. package/dist/script/features/response.d.ts.map +1 -0
  63. package/dist/script/features/response.js +43 -0
  64. package/dist/script/features/response.js.map +1 -0
  65. package/dist/script/features/tests.d.ts +15 -0
  66. package/dist/script/features/tests.d.ts.map +1 -0
  67. package/dist/script/features/tests.js +147 -0
  68. package/dist/script/features/tests.js.map +1 -0
  69. package/dist/script/features/variables.d.ts +6 -0
  70. package/dist/script/features/variables.d.ts.map +1 -0
  71. package/dist/script/features/variables.js +25 -0
  72. package/dist/script/features/variables.js.map +1 -0
  73. package/dist/script/legacy.d.ts +4 -0
  74. package/dist/script/legacy.d.ts.map +1 -0
  75. package/dist/script/legacy.js +128 -0
  76. package/dist/script/legacy.js.map +1 -0
  77. package/dist/script/sandbox.d.ts +16 -0
  78. package/dist/script/sandbox.d.ts.map +1 -0
  79. package/dist/script/sandbox.js +51 -0
  80. package/dist/script/sandbox.js.map +1 -0
  81. package/dist/script/scope.d.ts +23 -0
  82. package/dist/script/scope.d.ts.map +1 -0
  83. package/dist/script/scope.js +172 -0
  84. package/dist/script/scope.js.map +1 -0
  85. package/dist/script/scriptEngine.d.ts +2 -2
  86. package/dist/script/scriptEngine.d.ts.map +1 -1
  87. package/dist/script/scriptEngine.js +7 -105
  88. package/dist/script/scriptEngine.js.map +1 -1
  89. package/dist/script/timeout.d.ts +3 -0
  90. package/dist/script/timeout.d.ts.map +1 -0
  91. package/dist/script/timeout.js +38 -0
  92. package/dist/script/timeout.js.map +1 -0
  93. package/dist/script/types.d.ts +167 -0
  94. package/dist/script/types.d.ts.map +1 -0
  95. package/dist/script/types.js +7 -0
  96. package/dist/script/types.js.map +1 -0
  97. package/dist-cjs/contract/contractValidator.js +236 -0
  98. package/dist-cjs/contract/contractValidator.js.map +1 -0
  99. package/dist-cjs/index.js +47 -0
  100. package/dist-cjs/index.js.map +1 -0
  101. package/dist-cjs/package.json +1 -0
  102. package/dist-cjs/request/executeRequest.js +33 -0
  103. package/dist-cjs/request/executeRequest.js.map +1 -0
  104. package/dist-cjs/runner/requestRunner.js +102 -0
  105. package/dist-cjs/runner/requestRunner.js.map +1 -0
  106. package/dist-cjs/runner/runCollection.js +53 -0
  107. package/dist-cjs/runner/runCollection.js.map +1 -0
  108. package/dist-cjs/scenario-runner/condition.js +1735 -0
  109. package/dist-cjs/scenario-runner/condition.js.map +1 -0
  110. package/dist-cjs/scenario-runner/conditionTypes.js +3 -0
  111. package/dist-cjs/scenario-runner/conditionTypes.js.map +1 -0
  112. package/dist-cjs/scenario-runner/executionPolicyRunner.js +53 -0
  113. package/dist-cjs/scenario-runner/executionPolicyRunner.js.map +1 -0
  114. package/dist-cjs/scenario-runner/graphTraversal.js +27 -0
  115. package/dist-cjs/scenario-runner/graphTraversal.js.map +1 -0
  116. package/dist-cjs/scenario-runner/index.js +39 -0
  117. package/dist-cjs/scenario-runner/index.js.map +1 -0
  118. package/dist-cjs/scenario-runner/nodePolicy.js +127 -0
  119. package/dist-cjs/scenario-runner/nodePolicy.js.map +1 -0
  120. package/dist-cjs/scenario-runner/notification/index.js +18 -0
  121. package/dist-cjs/scenario-runner/notification/index.js.map +1 -0
  122. package/dist-cjs/scenario-runner/notification/providers/provider.interface.js +3 -0
  123. package/dist-cjs/scenario-runner/notification/providers/provider.interface.js.map +1 -0
  124. package/dist-cjs/scenario-runner/notification/providers/slack.provider.js +88 -0
  125. package/dist-cjs/scenario-runner/notification/providers/slack.provider.js.map +1 -0
  126. package/dist-cjs/scenario-runner/notification/template.js +13 -0
  127. package/dist-cjs/scenario-runner/notification/template.js.map +1 -0
  128. package/dist-cjs/scenario-runner/retryExecutor.js +57 -0
  129. package/dist-cjs/scenario-runner/retryExecutor.js.map +1 -0
  130. package/dist-cjs/scenario-runner/runScenario.js +1545 -0
  131. package/dist-cjs/scenario-runner/runScenario.js.map +1 -0
  132. package/dist-cjs/scenario-runner/templateResolver.js +468 -0
  133. package/dist-cjs/scenario-runner/templateResolver.js.map +1 -0
  134. package/dist-cjs/scenario-runner/types.js +3 -0
  135. package/dist-cjs/scenario-runner/types.js.map +1 -0
  136. package/dist-cjs/scenario-runner/waitNodeExecutor.js +71 -0
  137. package/dist-cjs/scenario-runner/waitNodeExecutor.js.map +1 -0
  138. package/dist-cjs/script/errors.js +72 -0
  139. package/dist-cjs/script/errors.js.map +1 -0
  140. package/dist-cjs/script/executor.js +306 -0
  141. package/dist-cjs/script/executor.js.map +1 -0
  142. package/dist-cjs/script/features/assertions.js +269 -0
  143. package/dist-cjs/script/features/assertions.js.map +1 -0
  144. package/dist-cjs/script/features/counter.js +47 -0
  145. package/dist-cjs/script/features/counter.js.map +1 -0
  146. package/dist-cjs/script/features/cryptojs.js +67 -0
  147. package/dist-cjs/script/features/cryptojs.js.map +1 -0
  148. package/dist-cjs/script/features/db.js +169 -0
  149. package/dist-cjs/script/features/db.js.map +1 -0
  150. package/dist-cjs/script/features/environment.js +65 -0
  151. package/dist-cjs/script/features/environment.js.map +1 -0
  152. package/dist-cjs/script/features/feature-policy.js +94 -0
  153. package/dist-cjs/script/features/feature-policy.js.map +1 -0
  154. package/dist-cjs/script/features/kafka.js +77 -0
  155. package/dist-cjs/script/features/kafka.js.map +1 -0
  156. package/dist-cjs/script/features/log.js +64 -0
  157. package/dist-cjs/script/features/log.js.map +1 -0
  158. package/dist-cjs/script/features/node.js +89 -0
  159. package/dist-cjs/script/features/node.js.map +1 -0
  160. package/dist-cjs/script/features/random.js +115 -0
  161. package/dist-cjs/script/features/random.js.map +1 -0
  162. package/dist-cjs/script/features/redis.js +26 -0
  163. package/dist-cjs/script/features/redis.js.map +1 -0
  164. package/dist-cjs/script/features/request.js +109 -0
  165. package/dist-cjs/script/features/request.js.map +1 -0
  166. package/dist-cjs/script/features/response.js +47 -0
  167. package/dist-cjs/script/features/response.js.map +1 -0
  168. package/dist-cjs/script/features/tests.js +151 -0
  169. package/dist-cjs/script/features/tests.js.map +1 -0
  170. package/dist-cjs/script/features/variables.js +29 -0
  171. package/dist-cjs/script/features/variables.js.map +1 -0
  172. package/dist-cjs/script/legacy.js +132 -0
  173. package/dist-cjs/script/legacy.js.map +1 -0
  174. package/dist-cjs/script/pmApi.js +353 -0
  175. package/dist-cjs/script/pmApi.js.map +1 -0
  176. package/dist-cjs/script/sandbox.js +91 -0
  177. package/dist-cjs/script/sandbox.js.map +1 -0
  178. package/dist-cjs/script/scope.js +177 -0
  179. package/dist-cjs/script/scope.js.map +1 -0
  180. package/dist-cjs/script/scriptEngine.js +12 -0
  181. package/dist-cjs/script/scriptEngine.js.map +1 -0
  182. package/dist-cjs/script/timeout.js +43 -0
  183. package/dist-cjs/script/timeout.js.map +1 -0
  184. package/dist-cjs/script/types.js +8 -0
  185. package/dist-cjs/script/types.js.map +1 -0
  186. package/dist-cjs/types/index.js +3 -0
  187. package/dist-cjs/types/index.js.map +1 -0
  188. package/dist-cjs/utils/get.js +52 -0
  189. package/dist-cjs/utils/get.js.map +1 -0
  190. package/dist-cjs/utils/random.js +87 -0
  191. package/dist-cjs/utils/random.js.map +1 -0
  192. package/dist-cjs/variables/envStore.js +24 -0
  193. package/dist-cjs/variables/envStore.js.map +1 -0
  194. package/dist-cjs/variables/resolver.js +95 -0
  195. package/dist-cjs/variables/resolver.js.map +1 -0
  196. package/package.json +19 -3
  197. package/samples/runSample.mjs +0 -128
  198. package/tests/waitNode.test.mjs +0 -311
  199. package/tsconfig.json +0 -18
@@ -0,0 +1,1735 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertLegacyConditionOperator = exports.createConditionConfigFromLegacyExpression = exports.filterConditionRuntimeSuggestions = exports.buildConditionRuntimeSuggestions = exports.buildConditionRuntimeRootKeysForNodes = exports.buildConditionRuntimeRootSetFromKeys = exports.isConditionOperatorUnary = exports.getConditionOperatorLabel = exports.buildConditionOutputPayload = exports.updateConditionRuleRightOperand = exports.updateConditionRuleLeftOperand = exports.updateConditionRuleOperator = exports.patchConditionGroup = exports.patchConditionRule = exports.replaceConditionGroup = exports.removeConditionChild = exports.appendGroupToConditionGroup = exports.appendRuleToConditionGroup = exports.updateConditionGroupOperator = exports.getConditionRuntimeRootKeys = exports.evaluateConditionConfig = exports.getExecutableConditionGroup = exports.getConditionValueTypeLabel = exports.getConditionHeadline = exports.getConditionQuickSummary = exports.getConditionGroupCount = exports.getConditionRuleCount = exports.validateConditionConfig = exports.normalizeConditionNodeConfig = exports.parseConditionExpressionSafely = exports.parseConditionExpressionToGroup = exports.serializeConditionConfigToPreview = exports.serializeConditionGroupToPreview = exports.serializeConditionConfigToExpression = exports.serializeConditionGroupToExpression = exports.createDefaultConditionConfig = exports.createConditionGroup = exports.createConditionRule = exports.createConditionOperand = exports.CONDITION_VALUE_TYPES = exports.CONDITION_OPERATOR_META = void 0;
4
+ const createId = (prefix) => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
5
+ ? crypto.randomUUID()
6
+ : `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
7
+ const BUILT_IN_RUNTIME_SUGGESTIONS = [
8
+ {
9
+ description: "Global workflow execution metadata.",
10
+ exampleValue: "success",
11
+ group: "System",
12
+ path: "workflow.__meta.status",
13
+ valueType: "string",
14
+ },
15
+ {
16
+ description: "Whether the workflow completed without failures.",
17
+ exampleValue: "true",
18
+ group: "System",
19
+ path: "workflow.__meta.success",
20
+ valueType: "boolean",
21
+ },
22
+ {
23
+ description: "Whether the workflow has failed.",
24
+ exampleValue: "false",
25
+ group: "System",
26
+ path: "workflow.__meta.failed",
27
+ valueType: "boolean",
28
+ },
29
+ {
30
+ description: "How many nodes were executed successfully.",
31
+ exampleValue: "5",
32
+ group: "System",
33
+ path: "workflow.__meta.successfulNodes",
34
+ valueType: "number",
35
+ },
36
+ {
37
+ description: "How many nodes failed during workflow execution.",
38
+ exampleValue: "1",
39
+ group: "System",
40
+ path: "workflow.__meta.failedNodes",
41
+ valueType: "number",
42
+ },
43
+ {
44
+ description: "How many nodes were skipped during workflow execution.",
45
+ exampleValue: "2",
46
+ group: "System",
47
+ path: "workflow.__meta.skippedNodes",
48
+ valueType: "number",
49
+ },
50
+ {
51
+ description: "Total retry count across the workflow.",
52
+ exampleValue: "3",
53
+ group: "System",
54
+ path: "workflow.__meta.totalRetries",
55
+ valueType: "number",
56
+ },
57
+ {
58
+ description: "Workflow duration in milliseconds.",
59
+ exampleValue: "1240",
60
+ group: "System",
61
+ path: "workflow.__meta.durationMs",
62
+ valueType: "number",
63
+ },
64
+ {
65
+ description: "The currently running node label.",
66
+ exampleValue: "Req1",
67
+ group: "System",
68
+ path: "workflow.__meta.currentNode",
69
+ valueType: "string",
70
+ },
71
+ {
72
+ description: "The most recently completed node label.",
73
+ exampleValue: "Req1",
74
+ group: "System",
75
+ path: "workflow.__meta.lastNode",
76
+ valueType: "string",
77
+ },
78
+ {
79
+ description: "The node label that caused workflow failure.",
80
+ exampleValue: "Kafka1",
81
+ group: "System",
82
+ path: "workflow.__meta.failedNode",
83
+ valueType: "string",
84
+ },
85
+ {
86
+ description: "Latest HTTP status code from the most recent request node.",
87
+ exampleValue: "200",
88
+ group: "Built-in",
89
+ path: "response.status",
90
+ valueType: "number",
91
+ },
92
+ {
93
+ description: "Latest HTTP response body payload.",
94
+ exampleValue: '{"ok":true}',
95
+ group: "Built-in",
96
+ path: "response.body",
97
+ valueType: "string",
98
+ },
99
+ {
100
+ description: "Latest HTTP response text body.",
101
+ exampleValue: "OK",
102
+ group: "Built-in",
103
+ path: "response.text",
104
+ valueType: "string",
105
+ },
106
+ {
107
+ description: "Latest HTTP response headers object.",
108
+ exampleValue: '{"content-type":"application/json"}',
109
+ group: "Built-in",
110
+ path: "response.headers",
111
+ valueType: "string",
112
+ },
113
+ {
114
+ description: "Latest HTTP request duration in milliseconds.",
115
+ exampleValue: "124",
116
+ group: "Built-in",
117
+ path: "response.durationMs",
118
+ valueType: "number",
119
+ },
120
+ {
121
+ description: "Latest database result rows for unlabeled DB access.",
122
+ exampleValue: "[...]",
123
+ group: "Built-in",
124
+ path: "db.result",
125
+ valueType: "array",
126
+ },
127
+ {
128
+ description: "Latest Kafka event payload for unlabeled Kafka access.",
129
+ exampleValue: '{"pin":"1234"}',
130
+ group: "Built-in",
131
+ path: "kafkaEvent",
132
+ valueType: "string",
133
+ },
134
+ {
135
+ description: "Latest Redis result for unlabeled Redis access.",
136
+ exampleValue: "cached-value",
137
+ group: "Built-in",
138
+ path: "redis.result",
139
+ valueType: "string",
140
+ },
141
+ {
142
+ description: "Scenario environment variables.",
143
+ exampleValue: "token",
144
+ group: "Built-in",
145
+ path: "env",
146
+ valueType: "string",
147
+ },
148
+ {
149
+ description: "Global runtime variables.",
150
+ exampleValue: "sharedToken",
151
+ group: "Built-in",
152
+ path: "global",
153
+ valueType: "string",
154
+ },
155
+ ];
156
+ exports.CONDITION_OPERATOR_META = [
157
+ { category: "Equality", label: "Equals", value: "equals" },
158
+ { category: "Equality", label: "Not Equals", value: "not_equals" },
159
+ { category: "Equality", label: "Strict Equals", value: "strict_equals" },
160
+ { category: "Equality", label: "Strict Not Equals", value: "strict_not_equals" },
161
+ { category: "Numeric", label: "Greater Than", value: "greater_than" },
162
+ { category: "Numeric", label: "Less Than", value: "less_than" },
163
+ { category: "Numeric", label: "Greater Or Equal", value: "greater_or_equal" },
164
+ { category: "Numeric", label: "Less Or Equal", value: "less_or_equal" },
165
+ { category: "Numeric", label: "Between", value: "between" },
166
+ { category: "String", label: "Contains", value: "contains" },
167
+ { category: "String", label: "Not Contains", value: "not_contains" },
168
+ { category: "String", label: "Starts With", value: "starts_with" },
169
+ { category: "String", label: "Ends With", value: "ends_with" },
170
+ { category: "String", label: "Regex", value: "regex" },
171
+ { category: "Existence", isUnary: true, label: "Exists", value: "exists" },
172
+ { category: "Existence", isUnary: true, label: "Not Exists", value: "not_exists" },
173
+ { category: "Existence", isUnary: true, label: "Is Empty", value: "is_empty" },
174
+ { category: "Existence", isUnary: true, label: "Not Empty", value: "not_empty" },
175
+ { category: "Existence", isUnary: true, label: "Is Null", value: "is_null" },
176
+ { category: "Existence", isUnary: true, label: "Not Null", value: "not_null" },
177
+ { category: "Collection", label: "In", value: "in" },
178
+ { category: "Collection", label: "Not In", value: "not_in" },
179
+ ];
180
+ const OPERATOR_ALIAS_MAP = [
181
+ { alias: "!==", operator: "strict_not_equals" },
182
+ { alias: "===", operator: "strict_equals" },
183
+ { alias: "!=", operator: "not_equals" },
184
+ { alias: "==", operator: "equals" },
185
+ { alias: ">=", operator: "greater_or_equal" },
186
+ { alias: "<=", operator: "less_or_equal" },
187
+ { alias: ">", operator: "greater_than" },
188
+ { alias: "<", operator: "less_than" },
189
+ { alias: "not_contains", operator: "not_contains" },
190
+ { alias: "not contains", operator: "not_contains" },
191
+ { alias: "contains", operator: "contains" },
192
+ { alias: "starts_with", operator: "starts_with" },
193
+ { alias: "starts with", operator: "starts_with" },
194
+ { alias: "ends_with", operator: "ends_with" },
195
+ { alias: "ends with", operator: "ends_with" },
196
+ { alias: "regex", operator: "regex" },
197
+ { alias: "not_exists", operator: "not_exists" },
198
+ { alias: "not exists", operator: "not_exists" },
199
+ { alias: "exists", operator: "exists" },
200
+ { alias: "is_empty", operator: "is_empty" },
201
+ { alias: "is empty", operator: "is_empty" },
202
+ { alias: "not_empty", operator: "not_empty" },
203
+ { alias: "not empty", operator: "not_empty" },
204
+ { alias: "is_null", operator: "is_null" },
205
+ { alias: "is null", operator: "is_null" },
206
+ { alias: "not_null", operator: "not_null" },
207
+ { alias: "not null", operator: "not_null" },
208
+ { alias: "not_in", operator: "not_in" },
209
+ { alias: "not in", operator: "not_in" },
210
+ { alias: "in", operator: "in" },
211
+ { alias: "between", operator: "between" },
212
+ ];
213
+ const LOGICAL_OPERATOR_ALIAS_MAP = [
214
+ { alias: "&&", operator: "AND" },
215
+ { alias: "AND", operator: "AND" },
216
+ { alias: "||", operator: "OR" },
217
+ { alias: "OR", operator: "OR" },
218
+ ];
219
+ const LEGACY_OPERATOR_MAP = {
220
+ "==": "equals",
221
+ "!=": "not_equals",
222
+ ">": "greater_than",
223
+ "<": "less_than",
224
+ ">=": "greater_or_equal",
225
+ "<=": "less_or_equal",
226
+ contains: "contains",
227
+ };
228
+ const VALUE_TYPE_LABELS = {
229
+ string: "String",
230
+ number: "Number",
231
+ boolean: "Boolean",
232
+ null: "Null",
233
+ array: "Array",
234
+ };
235
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
236
+ const isArrayIndexProperty = (value) => value.trim().length > 0 && Number.isInteger(Number(value));
237
+ const normalizeWhitespace = (value) => value.replace(/\s+/g, " ").trim();
238
+ const normalizeAlias = (value) => value.trim().toUpperCase();
239
+ const isBoundaryCharacter = (value) => value.length === 0 || /[\s()[\]{},"']/.test(value);
240
+ const matchesAliasAtStart = (input, alias) => {
241
+ if (/^[<>=!&|]+$/.test(alias)) {
242
+ return input.startsWith(alias);
243
+ }
244
+ const normalizedAlias = normalizeAlias(alias);
245
+ const candidate = input.slice(0, alias.length);
246
+ if (/[A-Z_ ]/.test(normalizedAlias) && normalizeAlias(candidate) !== normalizedAlias) {
247
+ return false;
248
+ }
249
+ if (!/[A-Z_ ]/.test(normalizedAlias) && candidate !== alias) {
250
+ return false;
251
+ }
252
+ return isBoundaryCharacter(input[alias.length] ?? "");
253
+ };
254
+ const isUnaryOperator = (operator) => operator === "exists" ||
255
+ operator === "not_exists" ||
256
+ operator === "is_empty" ||
257
+ operator === "not_empty" ||
258
+ operator === "is_null" ||
259
+ operator === "not_null";
260
+ const isNumericOperator = (operator) => operator === "greater_than" ||
261
+ operator === "less_than" ||
262
+ operator === "greater_or_equal" ||
263
+ operator === "less_or_equal" ||
264
+ operator === "between";
265
+ const isCollectionOperator = (operator) => operator === "in" || operator === "not_in";
266
+ const getOperatorToken = (operator) => {
267
+ switch (operator) {
268
+ case "equals":
269
+ return "==";
270
+ case "not_equals":
271
+ return "!=";
272
+ case "strict_equals":
273
+ return "===";
274
+ case "strict_not_equals":
275
+ return "!==";
276
+ case "greater_than":
277
+ return ">";
278
+ case "less_than":
279
+ return "<";
280
+ case "greater_or_equal":
281
+ return ">=";
282
+ case "less_or_equal":
283
+ return "<=";
284
+ case "between":
285
+ return "between";
286
+ case "contains":
287
+ return "contains";
288
+ case "not_contains":
289
+ return "not contains";
290
+ case "starts_with":
291
+ return "starts_with";
292
+ case "ends_with":
293
+ return "ends_with";
294
+ case "regex":
295
+ return "regex";
296
+ case "exists":
297
+ return "exists";
298
+ case "not_exists":
299
+ return "not exists";
300
+ case "is_empty":
301
+ return "is empty";
302
+ case "not_empty":
303
+ return "not empty";
304
+ case "is_null":
305
+ return "is null";
306
+ case "not_null":
307
+ return "not null";
308
+ case "in":
309
+ return "in";
310
+ case "not_in":
311
+ return "not in";
312
+ default:
313
+ return "==";
314
+ }
315
+ };
316
+ const formatDisplayValue = (value) => {
317
+ if (typeof value === "string") {
318
+ return value;
319
+ }
320
+ if (typeof value === "number" || typeof value === "boolean") {
321
+ return String(value);
322
+ }
323
+ if (value === null) {
324
+ return "null";
325
+ }
326
+ if (typeof value === "undefined") {
327
+ return "undefined";
328
+ }
329
+ try {
330
+ return JSON.stringify(value);
331
+ }
332
+ catch {
333
+ return String(value);
334
+ }
335
+ };
336
+ const flattenScenarioNodes = (nodes) => nodes.flatMap((node) => node.type === "subscenario"
337
+ ? [node, ...flattenScenarioNodes(node.data.config.nodes)]
338
+ : [node]);
339
+ const createSuggestion = (group, path, description, valueType, exampleValue) => ({
340
+ description,
341
+ ...(exampleValue ? { exampleValue } : {}),
342
+ group,
343
+ path,
344
+ valueType,
345
+ });
346
+ const buildNodeSuggestions = (label, type) => {
347
+ const group = label;
348
+ const metaSuggestions = [
349
+ createSuggestion(group, `${label}.__meta.status`, "Execution status.", "string", "success"),
350
+ createSuggestion(group, `${label}.__meta.success`, "Node completed successfully.", "boolean", "true"),
351
+ createSuggestion(group, `${label}.__meta.failed`, "Node failed during execution.", "boolean", "false"),
352
+ createSuggestion(group, `${label}.__meta.skipped`, "Node was skipped.", "boolean", "false"),
353
+ createSuggestion(group, `${label}.__meta.attempts`, "How many attempts were used.", "number", "1"),
354
+ createSuggestion(group, `${label}.__meta.durationMs`, "Execution duration in milliseconds.", "number", "124"),
355
+ createSuggestion(group, `${label}.__meta.startedAt`, "Execution start timestamp.", "string"),
356
+ createSuggestion(group, `${label}.__meta.endedAt`, "Execution end timestamp.", "string"),
357
+ createSuggestion(group, `${label}.__meta.continueAfterFailure`, "Whether execution continued after failure.", "boolean", "false"),
358
+ createSuggestion(group, `${label}.__meta.error.message`, "Node execution error message.", "string"),
359
+ createSuggestion(group, `${label}.__meta.error.code`, "Node execution error code.", "string"),
360
+ createSuggestion(group, `${label}.__meta.error.type`, "Node execution error type.", "string"),
361
+ ];
362
+ switch (type) {
363
+ case "request":
364
+ return [
365
+ createSuggestion(group, `${label}.status`, "HTTP status code.", "number", "200"),
366
+ createSuggestion(group, `${label}.body`, "Resolved response body payload.", "string"),
367
+ createSuggestion(group, `${label}.data`, "Response data shortcut.", "string"),
368
+ createSuggestion(group, `${label}.headers`, "Response headers object.", "string"),
369
+ createSuggestion(group, `${label}.cookies`, "Response cookies object.", "string"),
370
+ createSuggestion(group, `${label}.durationMs`, "Request duration in milliseconds.", "number", "124"),
371
+ ...metaSuggestions,
372
+ ];
373
+ case "db":
374
+ return [
375
+ createSuggestion(group, `${label}.rows`, "Database query rows.", "array"),
376
+ createSuggestion(group, `${label}.rows[0]`, "First database row.", "string"),
377
+ createSuggestion(group, `${label}.rowCount`, "Total database rows returned.", "number", "1"),
378
+ ...metaSuggestions,
379
+ ];
380
+ case "wait":
381
+ return [
382
+ createSuggestion(group, `${label}.configuredDurationMs`, "Configured wait duration in milliseconds.", "number", "5000"),
383
+ createSuggestion(group, `${label}.actualDurationMs`, "Actual elapsed wait duration in milliseconds.", "number", "5003"),
384
+ createSuggestion(group, `${label}.status`, "Wait node execution status.", "string", "success"),
385
+ ...metaSuggestions,
386
+ ];
387
+ case "kafka":
388
+ return [
389
+ createSuggestion(group, `${label}.meta`, "Kafka metadata object.", "string"),
390
+ createSuggestion(group, `${label}.value`, "Kafka payload value.", "string"),
391
+ createSuggestion(group, `${label}.text`, "Kafka payload text.", "string"),
392
+ createSuggestion(group, `${label}.headers`, "Kafka headers object.", "string"),
393
+ createSuggestion(group, `${label}.key`, "Kafka message key.", "string"),
394
+ createSuggestion(group, `${label}.timestamp`, "Kafka event timestamp.", "string"),
395
+ ...metaSuggestions,
396
+ ];
397
+ case "redis":
398
+ return [
399
+ createSuggestion(group, `${label}`, "Redis command result.", "string"),
400
+ createSuggestion(group, `${label}[0]`, "First Redis result when multiple commands run.", "string"),
401
+ ...metaSuggestions,
402
+ ];
403
+ case "notification":
404
+ return [
405
+ createSuggestion(group, `${label}.delivered`, "Whether the notification was delivered.", "boolean", "true"),
406
+ createSuggestion(group, `${label}.provider`, "Notification provider.", "string", "slack"),
407
+ createSuggestion(group, `${label}.severity`, "Notification severity.", "string", "info"),
408
+ createSuggestion(group, `${label}.channel`, "Notification delivery channel.", "string", "#ops-alerts"),
409
+ createSuggestion(group, `${label}.messagePreview`, "Rendered message preview.", "string"),
410
+ createSuggestion(group, `${label}.responseMeta.status`, "Provider response status.", "number", "200"),
411
+ ...metaSuggestions,
412
+ ];
413
+ case "condition":
414
+ return [
415
+ createSuggestion(group, `${label}.result`, "Condition boolean result.", "boolean", "true"),
416
+ createSuggestion(group, `${label}.preview`, "Human-readable condition preview.", "string"),
417
+ createSuggestion(group, `${label}.expression`, "Executable condition expression.", "string"),
418
+ createSuggestion(group, `${label}.mode`, "Condition editor mode.", "string", "builder"),
419
+ ...metaSuggestions,
420
+ ];
421
+ case "script":
422
+ return [
423
+ createSuggestion(group, `${label}`, "Returned Script Node output.", "string"),
424
+ ...metaSuggestions,
425
+ ];
426
+ case "aggregator":
427
+ return [
428
+ createSuggestion(group, `${label}`, "Aggregator JSON output.", "string"),
429
+ ...metaSuggestions,
430
+ ];
431
+ case "subscenario":
432
+ return [
433
+ createSuggestion(group, `${label}`, "Grouped output from the sub-scenario.", "string"),
434
+ ...metaSuggestions,
435
+ ];
436
+ case "end":
437
+ return metaSuggestions;
438
+ default:
439
+ return [];
440
+ }
441
+ };
442
+ const dedupeSuggestions = (suggestions) => {
443
+ const seen = new Set();
444
+ return suggestions.filter((suggestion) => {
445
+ if (seen.has(suggestion.path)) {
446
+ return false;
447
+ }
448
+ seen.add(suggestion.path);
449
+ return true;
450
+ });
451
+ };
452
+ const getDefaultLiteralValue = (valueType) => {
453
+ switch (valueType) {
454
+ case "number":
455
+ return 0;
456
+ case "boolean":
457
+ return false;
458
+ case "null":
459
+ return null;
460
+ case "array":
461
+ return [];
462
+ default:
463
+ return "";
464
+ }
465
+ };
466
+ const getDefaultRightOperand = (operator) => {
467
+ if (isUnaryOperator(operator)) {
468
+ return undefined;
469
+ }
470
+ if (operator === "between" || isCollectionOperator(operator)) {
471
+ return (0, exports.createConditionOperand)("literal", "array", operator === "between" ? [0, 1] : []);
472
+ }
473
+ return (0, exports.createConditionOperand)("literal", "string", "");
474
+ };
475
+ const parseLegacyConditionOperator = (value) => LEGACY_OPERATOR_MAP[value] ?? "equals";
476
+ const coerceUnknownToBoolean = (value) => {
477
+ if (typeof value === "boolean") {
478
+ return value;
479
+ }
480
+ if (typeof value === "string") {
481
+ const normalized = value.trim().toLowerCase();
482
+ if (normalized === "true") {
483
+ return true;
484
+ }
485
+ if (normalized === "false") {
486
+ return false;
487
+ }
488
+ }
489
+ return null;
490
+ };
491
+ const coerceUnknownToNumber = (value) => {
492
+ if (typeof value === "number" && Number.isFinite(value)) {
493
+ return value;
494
+ }
495
+ if (typeof value === "string" && value.trim()) {
496
+ const parsed = Number(value);
497
+ if (Number.isFinite(parsed)) {
498
+ return parsed;
499
+ }
500
+ }
501
+ return null;
502
+ };
503
+ const coerceOperandValue = (value, valueType) => {
504
+ if (valueType === "null") {
505
+ return value === null ? null : value === "null" ? null : undefined;
506
+ }
507
+ if (valueType === "boolean") {
508
+ return coerceUnknownToBoolean(value) ?? undefined;
509
+ }
510
+ if (valueType === "number") {
511
+ return coerceUnknownToNumber(value) ?? undefined;
512
+ }
513
+ if (valueType === "array") {
514
+ if (Array.isArray(value)) {
515
+ return value;
516
+ }
517
+ if (typeof value === "string") {
518
+ try {
519
+ const parsed = JSON.parse(value);
520
+ return Array.isArray(parsed) ? parsed : undefined;
521
+ }
522
+ catch {
523
+ return undefined;
524
+ }
525
+ }
526
+ return undefined;
527
+ }
528
+ if (typeof value === "string") {
529
+ return value;
530
+ }
531
+ if (value === null || typeof value === "undefined") {
532
+ return "";
533
+ }
534
+ return formatDisplayValue(value);
535
+ };
536
+ const parseLiteralValue = (value, valueType) => {
537
+ const coercedValue = coerceOperandValue(value, valueType);
538
+ if (typeof coercedValue === "undefined") {
539
+ return getDefaultLiteralValue(valueType);
540
+ }
541
+ return coercedValue;
542
+ };
543
+ const parseRuntimePathSegments = (path) => {
544
+ const segments = [];
545
+ let buffer = "";
546
+ let index = 0;
547
+ const pushBuffer = () => {
548
+ const trimmed = buffer.trim();
549
+ if (trimmed) {
550
+ segments.push(trimmed);
551
+ }
552
+ buffer = "";
553
+ };
554
+ while (index < path.length) {
555
+ const character = path[index] ?? "";
556
+ if (character === ".") {
557
+ pushBuffer();
558
+ index += 1;
559
+ continue;
560
+ }
561
+ if (character === "[") {
562
+ pushBuffer();
563
+ const closingIndex = path.indexOf("]", index + 1);
564
+ if (closingIndex === -1) {
565
+ buffer += path.slice(index);
566
+ break;
567
+ }
568
+ const indexValue = path.slice(index + 1, closingIndex).trim();
569
+ const numericIndex = Number(indexValue);
570
+ if (Number.isInteger(numericIndex)) {
571
+ segments.push(numericIndex);
572
+ }
573
+ else if (indexValue) {
574
+ segments.push(indexValue);
575
+ }
576
+ index = closingIndex + 1;
577
+ continue;
578
+ }
579
+ buffer += character;
580
+ index += 1;
581
+ }
582
+ pushBuffer();
583
+ return segments;
584
+ };
585
+ const resolveRuntimePath = (roots, path) => {
586
+ const segments = parseRuntimePathSegments(path);
587
+ if (segments.length === 0) {
588
+ return {
589
+ exists: false,
590
+ value: undefined,
591
+ };
592
+ }
593
+ if (typeof segments[0] === "string" &&
594
+ segments[0] !== "workflow" &&
595
+ segments[1] === "__meta") {
596
+ const label = segments[0];
597
+ const metaRoot = roots.__nodeMeta?.[label];
598
+ if (typeof metaRoot === "undefined") {
599
+ return {
600
+ exists: false,
601
+ value: undefined,
602
+ };
603
+ }
604
+ let currentMetaValue = metaRoot;
605
+ for (const segment of segments.slice(2)) {
606
+ if (typeof segment === "number") {
607
+ if (!Array.isArray(currentMetaValue) || segment < 0 || segment >= currentMetaValue.length) {
608
+ return {
609
+ exists: false,
610
+ value: undefined,
611
+ };
612
+ }
613
+ currentMetaValue = currentMetaValue[segment];
614
+ continue;
615
+ }
616
+ if (Array.isArray(currentMetaValue)) {
617
+ if (Object.prototype.hasOwnProperty.call(currentMetaValue, segment) &&
618
+ !isArrayIndexProperty(segment)) {
619
+ currentMetaValue = currentMetaValue[segment];
620
+ continue;
621
+ }
622
+ return {
623
+ exists: false,
624
+ value: undefined,
625
+ };
626
+ }
627
+ if (!isRecord(currentMetaValue)) {
628
+ return {
629
+ exists: false,
630
+ value: undefined,
631
+ };
632
+ }
633
+ if (!Object.prototype.hasOwnProperty.call(currentMetaValue, segment)) {
634
+ return {
635
+ exists: false,
636
+ value: undefined,
637
+ };
638
+ }
639
+ currentMetaValue = currentMetaValue[segment];
640
+ }
641
+ return {
642
+ exists: true,
643
+ value: currentMetaValue,
644
+ };
645
+ }
646
+ let currentValue = roots;
647
+ for (const segment of segments) {
648
+ if (typeof segment === "number") {
649
+ if (!Array.isArray(currentValue) || segment < 0 || segment >= currentValue.length) {
650
+ return {
651
+ exists: false,
652
+ value: undefined,
653
+ };
654
+ }
655
+ currentValue = currentValue[segment];
656
+ continue;
657
+ }
658
+ if (Array.isArray(currentValue)) {
659
+ if (Object.prototype.hasOwnProperty.call(currentValue, segment) &&
660
+ !isArrayIndexProperty(segment)) {
661
+ currentValue = currentValue[segment];
662
+ continue;
663
+ }
664
+ return {
665
+ exists: false,
666
+ value: undefined,
667
+ };
668
+ }
669
+ if (!isRecord(currentValue)) {
670
+ return {
671
+ exists: false,
672
+ value: undefined,
673
+ };
674
+ }
675
+ if (!Object.prototype.hasOwnProperty.call(currentValue, segment)) {
676
+ return {
677
+ exists: false,
678
+ value: undefined,
679
+ };
680
+ }
681
+ currentValue = currentValue[segment];
682
+ }
683
+ return {
684
+ exists: true,
685
+ value: currentValue,
686
+ };
687
+ };
688
+ const createRuntimeRoots = ({ dbResult, environment, globals, kafkaEvent, nodeMetaByLabel, redisResult, responseRoot, workflowContext, }) => {
689
+ const roots = {
690
+ ...(workflowContext ?? {}),
691
+ __nodeMeta: nodeMetaByLabel ?? {},
692
+ response: responseRoot ?? null,
693
+ lastResponse: responseRoot ?? null,
694
+ env: {
695
+ ...environment,
696
+ dbResult: dbResult ?? null,
697
+ kafkaEvent: kafkaEvent ?? null,
698
+ kafkaevent: kafkaEvent ?? null,
699
+ lastResponse: responseRoot ?? null,
700
+ redisResult: redisResult ?? null,
701
+ },
702
+ global: globals,
703
+ globals,
704
+ db: {
705
+ result: dbResult ?? null,
706
+ },
707
+ kafkaEvent: kafkaEvent ?? null,
708
+ kafkaevent: kafkaEvent ?? null,
709
+ redis: {
710
+ result: redisResult ?? null,
711
+ },
712
+ };
713
+ return roots;
714
+ };
715
+ const serializeLiteralOperand = (operand) => {
716
+ if (operand.valueType === "null") {
717
+ return "null";
718
+ }
719
+ if (operand.valueType === "boolean") {
720
+ return operand.value ? "true" : "false";
721
+ }
722
+ if (operand.valueType === "number") {
723
+ return String(typeof operand.value === "number" ? operand.value : 0);
724
+ }
725
+ if (operand.valueType === "array") {
726
+ try {
727
+ return JSON.stringify(Array.isArray(operand.value) ? operand.value : []);
728
+ }
729
+ catch {
730
+ return "[]";
731
+ }
732
+ }
733
+ return JSON.stringify(String(operand.value ?? ""));
734
+ };
735
+ const serializeOperandForExpression = (operand) => operand.mode === "runtime"
736
+ ? `{{${String(operand.value ?? "").trim()}}}`
737
+ : serializeLiteralOperand(operand);
738
+ const serializeOperandForPreview = (operand) => operand.mode === "runtime"
739
+ ? String(operand.value ?? "").trim() || "runtime.path"
740
+ : serializeLiteralOperand(operand);
741
+ const tokenizeConditionExpression = (expression) => {
742
+ const tokens = [];
743
+ let index = 0;
744
+ const pushToken = (type, value, position = index) => {
745
+ tokens.push({
746
+ type,
747
+ value,
748
+ position,
749
+ });
750
+ };
751
+ while (index < expression.length) {
752
+ const remaining = expression.slice(index);
753
+ if (/^\s+/.test(remaining)) {
754
+ index += remaining.match(/^\s+/)?.[0].length ?? 1;
755
+ continue;
756
+ }
757
+ if (remaining.startsWith("{{")) {
758
+ const closingIndex = expression.indexOf("}}", index + 2);
759
+ if (closingIndex === -1) {
760
+ throw new Error(`Unclosed runtime token at character ${index + 1}.`);
761
+ }
762
+ pushToken("runtime", expression.slice(index + 2, closingIndex).trim(), index);
763
+ index = closingIndex + 2;
764
+ continue;
765
+ }
766
+ if (remaining[0] === "(" || remaining[0] === ")") {
767
+ pushToken("paren", remaining[0] ?? "", index);
768
+ index += 1;
769
+ continue;
770
+ }
771
+ const logicalMatch = LOGICAL_OPERATOR_ALIAS_MAP.find(({ alias }) => matchesAliasAtStart(remaining, alias));
772
+ if (logicalMatch) {
773
+ pushToken("logic", logicalMatch.operator, index);
774
+ index += logicalMatch.alias.length;
775
+ continue;
776
+ }
777
+ const operatorMatch = OPERATOR_ALIAS_MAP.find(({ alias }) => matchesAliasAtStart(remaining, alias));
778
+ if (operatorMatch) {
779
+ pushToken("operator", operatorMatch.operator, index);
780
+ index += operatorMatch.alias.length;
781
+ continue;
782
+ }
783
+ if (remaining[0] === `"` || remaining[0] === `'`) {
784
+ const quote = remaining[0];
785
+ let cursor = index + 1;
786
+ let escaped = false;
787
+ while (cursor < expression.length) {
788
+ const character = expression[cursor] ?? "";
789
+ if (character === "\\" && !escaped) {
790
+ escaped = true;
791
+ cursor += 1;
792
+ continue;
793
+ }
794
+ if (character === quote && !escaped) {
795
+ break;
796
+ }
797
+ escaped = false;
798
+ cursor += 1;
799
+ }
800
+ if (expression[cursor] !== quote) {
801
+ throw new Error(`Unclosed string literal at character ${index + 1}.`);
802
+ }
803
+ pushToken("string", expression.slice(index, cursor + 1), index);
804
+ index = cursor + 1;
805
+ continue;
806
+ }
807
+ if (remaining[0] === "[") {
808
+ let depth = 0;
809
+ let cursor = index;
810
+ let activeQuote = null;
811
+ let escaped = false;
812
+ while (cursor < expression.length) {
813
+ const character = expression[cursor] ?? "";
814
+ if (activeQuote) {
815
+ if (character === "\\" && !escaped) {
816
+ escaped = true;
817
+ cursor += 1;
818
+ continue;
819
+ }
820
+ if (character === activeQuote && !escaped) {
821
+ activeQuote = null;
822
+ }
823
+ escaped = false;
824
+ cursor += 1;
825
+ continue;
826
+ }
827
+ if (character === `"` || character === `'`) {
828
+ activeQuote = character;
829
+ cursor += 1;
830
+ continue;
831
+ }
832
+ if (character === "[") {
833
+ depth += 1;
834
+ }
835
+ if (character === "]") {
836
+ depth -= 1;
837
+ if (depth === 0) {
838
+ cursor += 1;
839
+ break;
840
+ }
841
+ }
842
+ cursor += 1;
843
+ }
844
+ if (depth !== 0) {
845
+ throw new Error(`Unclosed array literal at character ${index + 1}.`);
846
+ }
847
+ pushToken("array", expression.slice(index, cursor), index);
848
+ index = cursor;
849
+ continue;
850
+ }
851
+ const numberMatch = remaining.match(/^-?\d+(?:\.\d+)?/);
852
+ if (numberMatch) {
853
+ pushToken("number", numberMatch[0], index);
854
+ index += numberMatch[0].length;
855
+ continue;
856
+ }
857
+ const booleanOrNullMatch = remaining.match(/^(true|false|null)\b/i);
858
+ if (booleanOrNullMatch) {
859
+ const matched = booleanOrNullMatch[1]?.toLowerCase() ?? "";
860
+ pushToken(matched === "null" ? "null" : "boolean", matched, index);
861
+ index += booleanOrNullMatch[0].length;
862
+ continue;
863
+ }
864
+ throw new Error(`Unexpected token near "${remaining.slice(0, 20)}" at character ${index + 1}.`);
865
+ }
866
+ return tokens;
867
+ };
868
+ const parseCursorToken = (cursor) => cursor.tokens[cursor.index] ?? null;
869
+ const consumeCursorToken = (cursor, expectedType) => {
870
+ const token = parseCursorToken(cursor);
871
+ if (!token) {
872
+ throw new Error("Unexpected end of expression.");
873
+ }
874
+ if (expectedType && token.type !== expectedType) {
875
+ throw new Error(`Expected ${expectedType} at character ${token.position + 1}.`);
876
+ }
877
+ cursor.index += 1;
878
+ return token;
879
+ };
880
+ const mergeGroupChildren = (operator, children) => ({
881
+ id: createId("condition-group"),
882
+ type: "group",
883
+ operator,
884
+ children: children.flatMap((child) => child.type === "group" && child.operator === operator ? child.children : [child]),
885
+ });
886
+ const parseOperandToken = (token) => {
887
+ if (token.type === "runtime") {
888
+ return (0, exports.createConditionOperand)("runtime", "string", token.value.trim());
889
+ }
890
+ if (token.type === "number") {
891
+ return (0, exports.createConditionOperand)("literal", "number", Number(token.value));
892
+ }
893
+ if (token.type === "boolean") {
894
+ return (0, exports.createConditionOperand)("literal", "boolean", token.value === "true");
895
+ }
896
+ if (token.type === "null") {
897
+ return (0, exports.createConditionOperand)("literal", "null", null);
898
+ }
899
+ if (token.type === "array") {
900
+ try {
901
+ return (0, exports.createConditionOperand)("literal", "array", JSON.parse(token.value.replace(/'/g, `"`)));
902
+ }
903
+ catch {
904
+ throw new Error(`Invalid array literal at character ${token.position + 1}.`);
905
+ }
906
+ }
907
+ if (token.type === "string") {
908
+ try {
909
+ const normalizedToken = token.value.startsWith("'") && token.value.endsWith("'")
910
+ ? `"${token.value.slice(1, -1).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`
911
+ : token.value;
912
+ return (0, exports.createConditionOperand)("literal", "string", JSON.parse(normalizedToken));
913
+ }
914
+ catch {
915
+ return (0, exports.createConditionOperand)("literal", "string", token.value.slice(1, -1));
916
+ }
917
+ }
918
+ throw new Error(`Expected operand at character ${token.position + 1}.`);
919
+ };
920
+ const parseConditionRule = (cursor) => {
921
+ const leftToken = consumeCursorToken(cursor);
922
+ if (leftToken.type !== "runtime" &&
923
+ leftToken.type !== "number" &&
924
+ leftToken.type !== "boolean" &&
925
+ leftToken.type !== "null" &&
926
+ leftToken.type !== "array" &&
927
+ leftToken.type !== "string") {
928
+ throw new Error(`Expected operand at character ${leftToken.position + 1}.`);
929
+ }
930
+ const operatorToken = consumeCursorToken(cursor, "operator");
931
+ const operator = operatorToken.value;
932
+ if (isUnaryOperator(operator)) {
933
+ return {
934
+ id: createId("condition-rule"),
935
+ type: "rule",
936
+ left: parseOperandToken(leftToken),
937
+ operator,
938
+ };
939
+ }
940
+ const rightToken = consumeCursorToken(cursor);
941
+ if (rightToken.type !== "runtime" &&
942
+ rightToken.type !== "number" &&
943
+ rightToken.type !== "boolean" &&
944
+ rightToken.type !== "null" &&
945
+ rightToken.type !== "array" &&
946
+ rightToken.type !== "string") {
947
+ throw new Error(`Expected right operand at character ${rightToken.position + 1}.`);
948
+ }
949
+ return {
950
+ id: createId("condition-rule"),
951
+ type: "rule",
952
+ left: parseOperandToken(leftToken),
953
+ operator,
954
+ right: parseOperandToken(rightToken),
955
+ };
956
+ };
957
+ const parsePrimaryGroupNode = (cursor) => {
958
+ const token = parseCursorToken(cursor);
959
+ if (!token) {
960
+ throw new Error("Unexpected end of expression.");
961
+ }
962
+ if (token.type === "paren" && token.value === "(") {
963
+ consumeCursorToken(cursor, "paren");
964
+ const nested = parseOrExpression(cursor);
965
+ const closingToken = consumeCursorToken(cursor, "paren");
966
+ if (closingToken.value !== ")") {
967
+ throw new Error(`Expected closing parenthesis at character ${closingToken.position + 1}.`);
968
+ }
969
+ return nested;
970
+ }
971
+ return parseConditionRule(cursor);
972
+ };
973
+ const parseAndExpression = (cursor) => {
974
+ const children = [parsePrimaryGroupNode(cursor)];
975
+ while (parseCursorToken(cursor)?.type === "logic" && parseCursorToken(cursor)?.value === "AND") {
976
+ consumeCursorToken(cursor, "logic");
977
+ children.push(parsePrimaryGroupNode(cursor));
978
+ }
979
+ return children.length === 1 ? children[0] : mergeGroupChildren("AND", children);
980
+ };
981
+ const parseOrExpression = (cursor) => {
982
+ const children = [parseAndExpression(cursor)];
983
+ while (parseCursorToken(cursor)?.type === "logic" && parseCursorToken(cursor)?.value === "OR") {
984
+ consumeCursorToken(cursor, "logic");
985
+ children.push(parseAndExpression(cursor));
986
+ }
987
+ return children.length === 1 ? children[0] : mergeGroupChildren("OR", children);
988
+ };
989
+ const ensureGroupRoot = (node) => node.type === "group"
990
+ ? node
991
+ : {
992
+ id: createId("condition-root"),
993
+ type: "group",
994
+ operator: "AND",
995
+ children: [node],
996
+ };
997
+ const serializeRuleExpression = (rule, humanReadable) => {
998
+ const left = humanReadable
999
+ ? serializeOperandForPreview(rule.left)
1000
+ : serializeOperandForExpression(rule.left);
1001
+ const operator = getOperatorToken(rule.operator);
1002
+ if (isUnaryOperator(rule.operator)) {
1003
+ return `${left} ${operator}`;
1004
+ }
1005
+ const rightOperand = rule.right ?? getDefaultRightOperand(rule.operator);
1006
+ const right = humanReadable
1007
+ ? serializeOperandForPreview(rightOperand ?? (0, exports.createConditionOperand)("literal", "string", ""))
1008
+ : serializeOperandForExpression(rightOperand ?? (0, exports.createConditionOperand)("literal", "string", ""));
1009
+ return `${left} ${operator} ${right}`;
1010
+ };
1011
+ const serializeGroupExpression = (group, humanReadable, nested = false) => {
1012
+ if (group.children.length === 0) {
1013
+ return humanReadable ? "Empty group" : "()";
1014
+ }
1015
+ const renderedChildren = group.children.map((child) => child.type === "group"
1016
+ ? serializeGroupExpression(child, humanReadable, true)
1017
+ : serializeRuleExpression(child, humanReadable));
1018
+ const joined = renderedChildren.join(` ${group.operator} `);
1019
+ if (!nested && group.children.length === 1) {
1020
+ return joined;
1021
+ }
1022
+ return `(${joined})`;
1023
+ };
1024
+ const collectConditionRuleCount = (group) => group.children.reduce((count, child) => count + (child.type === "rule" ? 1 : collectConditionRuleCount(child)), 0);
1025
+ const collectConditionGroupCount = (group) => 1 +
1026
+ group.children.reduce((count, child) => count + (child.type === "group" ? collectConditionGroupCount(child) : 0), 0);
1027
+ const cloneConditionOperand = (operand) => ({
1028
+ mode: operand.mode,
1029
+ valueType: operand.valueType,
1030
+ value: Array.isArray(operand.value) ? [...operand.value] : operand.value,
1031
+ });
1032
+ const sanitizeConditionOperand = (value, fallbackMode = "literal") => {
1033
+ if (!isRecord(value)) {
1034
+ return (0, exports.createConditionOperand)(fallbackMode);
1035
+ }
1036
+ const mode = value.mode === "runtime" ? "runtime" : "literal";
1037
+ const rawValueType = String(value.valueType ?? "");
1038
+ const valueType = rawValueType === "number" ||
1039
+ rawValueType === "boolean" ||
1040
+ rawValueType === "null" ||
1041
+ rawValueType === "array" ||
1042
+ rawValueType === "string"
1043
+ ? rawValueType
1044
+ : mode === "runtime"
1045
+ ? "string"
1046
+ : "string";
1047
+ return {
1048
+ mode,
1049
+ valueType,
1050
+ value: mode === "runtime"
1051
+ ? String(value.value ?? "").trim()
1052
+ : parseLiteralValue(value.value, valueType),
1053
+ };
1054
+ };
1055
+ const sanitizeConditionRule = (value) => {
1056
+ if (!isRecord(value) || value.type !== "rule") {
1057
+ return null;
1058
+ }
1059
+ const rawOperator = String(value.operator ?? "");
1060
+ const operator = exports.CONDITION_OPERATOR_META.find((entry) => entry.value === rawOperator)?.value;
1061
+ if (!operator) {
1062
+ return null;
1063
+ }
1064
+ return {
1065
+ id: typeof value.id === "string" && value.id.trim() ? value.id : createId("condition-rule"),
1066
+ type: "rule",
1067
+ left: sanitizeConditionOperand(value.left, "runtime"),
1068
+ operator,
1069
+ ...(isUnaryOperator(operator)
1070
+ ? {}
1071
+ : {
1072
+ right: sanitizeConditionOperand(value.right ?? getDefaultRightOperand(operator), "literal"),
1073
+ }),
1074
+ };
1075
+ };
1076
+ const sanitizeConditionGroup = (value) => {
1077
+ if (!isRecord(value) || value.type !== "group") {
1078
+ return null;
1079
+ }
1080
+ const operator = value.operator === "OR" ? "OR" : "AND";
1081
+ const children = Array.isArray(value.children)
1082
+ ? value.children
1083
+ .map((child) => sanitizeConditionGroup(child) ?? sanitizeConditionRule(child))
1084
+ .filter((child) => child !== null)
1085
+ : [];
1086
+ return {
1087
+ id: typeof value.id === "string" && value.id.trim() ? value.id : createId("condition-group"),
1088
+ type: "group",
1089
+ operator,
1090
+ children,
1091
+ };
1092
+ };
1093
+ const buildValidationIssue = (id, message, severity) => ({
1094
+ id,
1095
+ message,
1096
+ severity,
1097
+ });
1098
+ const validateOperand = (operand, id, issues, runtimeRootKeys) => {
1099
+ if (!operand) {
1100
+ issues.push(buildValidationIssue(id, "Operand is missing.", "error"));
1101
+ return;
1102
+ }
1103
+ if (operand.mode === "runtime") {
1104
+ const path = String(operand.value ?? "").trim();
1105
+ if (!path) {
1106
+ issues.push(buildValidationIssue(id, "Runtime path is required.", "error"));
1107
+ return;
1108
+ }
1109
+ const root = parseRuntimePathSegments(path)[0];
1110
+ if (runtimeRootKeys &&
1111
+ typeof root === "string" &&
1112
+ root.trim() &&
1113
+ !runtimeRootKeys.has(root)) {
1114
+ issues.push(buildValidationIssue(id, `Runtime root "${root}" is not available in this workflow context.`, "warning"));
1115
+ }
1116
+ return;
1117
+ }
1118
+ if (operand.valueType === "array") {
1119
+ if (!Array.isArray(operand.value)) {
1120
+ issues.push(buildValidationIssue(id, "Array operand must be a JSON array.", "error"));
1121
+ }
1122
+ return;
1123
+ }
1124
+ if (operand.valueType === "number" && coerceUnknownToNumber(operand.value) === null) {
1125
+ issues.push(buildValidationIssue(id, "Number operand must be numeric.", "error"));
1126
+ }
1127
+ if (operand.valueType === "boolean" && coerceUnknownToBoolean(operand.value) === null) {
1128
+ issues.push(buildValidationIssue(id, "Boolean operand must be true or false.", "error"));
1129
+ }
1130
+ };
1131
+ const validateRule = (rule, issues, runtimeRootKeys) => {
1132
+ validateOperand(rule.left, `${rule.id}:left`, issues, runtimeRootKeys);
1133
+ if (!isUnaryOperator(rule.operator)) {
1134
+ validateOperand(rule.right, `${rule.id}:right`, issues, runtimeRootKeys);
1135
+ }
1136
+ if (rule.operator === "between") {
1137
+ if (!rule.right || rule.right.valueType !== "array" || !Array.isArray(rule.right.value)) {
1138
+ issues.push(buildValidationIssue(rule.id, "Between expects an array literal with exactly two numeric values.", "error"));
1139
+ }
1140
+ else if (rule.right.value.length !== 2 ||
1141
+ rule.right.value.some((value) => coerceUnknownToNumber(value) === null)) {
1142
+ issues.push(buildValidationIssue(rule.id, "Between expects two numeric boundary values.", "error"));
1143
+ }
1144
+ }
1145
+ if (isCollectionOperator(rule.operator)) {
1146
+ if (!rule.right || rule.right.valueType !== "array" || !Array.isArray(rule.right.value)) {
1147
+ issues.push(buildValidationIssue(rule.id, "In operators require an array on the right side.", "error"));
1148
+ }
1149
+ }
1150
+ if (rule.operator === "regex" && rule.right?.mode === "literal") {
1151
+ try {
1152
+ buildConditionRegex(String(rule.right.value ?? ""));
1153
+ }
1154
+ catch (error) {
1155
+ issues.push(buildValidationIssue(rule.id, error instanceof Error ? error.message : "Regex pattern is invalid.", "error"));
1156
+ }
1157
+ }
1158
+ if (isNumericOperator(rule.operator)) {
1159
+ if (rule.left.valueType !== "number") {
1160
+ issues.push(buildValidationIssue(`${rule.id}:numeric-left`, "Left operand should use Number type for numeric comparisons.", "warning"));
1161
+ }
1162
+ if (rule.operator !== "between" &&
1163
+ rule.right &&
1164
+ rule.right.valueType !== "number" &&
1165
+ rule.right.mode !== "runtime") {
1166
+ issues.push(buildValidationIssue(`${rule.id}:numeric-right`, "Right operand should use Number type for numeric comparisons.", "warning"));
1167
+ }
1168
+ }
1169
+ };
1170
+ const validateGroup = (group, issues, runtimeRootKeys) => {
1171
+ if (group.children.length === 0) {
1172
+ issues.push(buildValidationIssue(group.id, "Group must contain at least one rule.", "error"));
1173
+ return;
1174
+ }
1175
+ group.children.forEach((child) => {
1176
+ if (child.type === "group") {
1177
+ validateGroup(child, issues, runtimeRootKeys);
1178
+ return;
1179
+ }
1180
+ validateRule(child, issues, runtimeRootKeys);
1181
+ });
1182
+ };
1183
+ const buildConditionRegex = (value) => {
1184
+ const trimmed = value.trim();
1185
+ const literalMatch = trimmed.match(/^\/(.+)\/([a-z]*)$/i);
1186
+ if (literalMatch) {
1187
+ return new RegExp(literalMatch[1] ?? "", literalMatch[2] ?? "");
1188
+ }
1189
+ return new RegExp(trimmed);
1190
+ };
1191
+ const compareLooseEquality = (left, right) => {
1192
+ if (Array.isArray(left) || Array.isArray(right) || isRecord(left) || isRecord(right)) {
1193
+ try {
1194
+ return JSON.stringify(left) === JSON.stringify(right);
1195
+ }
1196
+ catch {
1197
+ return false;
1198
+ }
1199
+ }
1200
+ if (left === right) {
1201
+ return true;
1202
+ }
1203
+ if (typeof left === "string" && typeof right !== "string") {
1204
+ const coercedLeft = coerceOperandValue(left, typeof right === "number" ? "number" : typeof right === "boolean" ? "boolean" : right === null ? "null" : "string");
1205
+ return typeof coercedLeft === "undefined" ? false : compareLooseEquality(coercedLeft, right);
1206
+ }
1207
+ if (typeof right === "string" && typeof left !== "string") {
1208
+ const coercedRight = coerceOperandValue(right, typeof left === "number" ? "number" : typeof left === "boolean" ? "boolean" : left === null ? "null" : "string");
1209
+ return typeof coercedRight === "undefined" ? false : compareLooseEquality(left, coercedRight);
1210
+ }
1211
+ return formatDisplayValue(left) === formatDisplayValue(right);
1212
+ };
1213
+ const resolveOperand = (operand, runtimeScope) => {
1214
+ if (operand.mode === "literal") {
1215
+ const literalValue = parseLiteralValue(operand.value, operand.valueType);
1216
+ return {
1217
+ displayValue: formatDisplayValue(literalValue),
1218
+ exists: true,
1219
+ operand,
1220
+ path: null,
1221
+ value: literalValue,
1222
+ };
1223
+ }
1224
+ const path = String(operand.value ?? "").trim();
1225
+ const resolution = resolveRuntimePath(createRuntimeRoots(runtimeScope), path);
1226
+ const coercedValue = typeof resolution.value === "undefined"
1227
+ ? undefined
1228
+ : coerceOperandValue(resolution.value, operand.valueType);
1229
+ return {
1230
+ displayValue: formatDisplayValue(typeof coercedValue === "undefined" ? resolution.value : coercedValue),
1231
+ exists: resolution.exists,
1232
+ operand,
1233
+ path,
1234
+ value: typeof coercedValue === "undefined" ? resolution.value : coercedValue,
1235
+ };
1236
+ };
1237
+ const evaluateRule = (rule, runtimeScope) => {
1238
+ const left = resolveOperand(rule.left, runtimeScope);
1239
+ const right = rule.right ? resolveOperand(rule.right, runtimeScope) : undefined;
1240
+ let result = false;
1241
+ switch (rule.operator) {
1242
+ case "equals":
1243
+ result = compareLooseEquality(left.value, right?.value);
1244
+ break;
1245
+ case "not_equals":
1246
+ result = !compareLooseEquality(left.value, right?.value);
1247
+ break;
1248
+ case "strict_equals":
1249
+ result = left.value === right?.value;
1250
+ break;
1251
+ case "strict_not_equals":
1252
+ result = left.value !== right?.value;
1253
+ break;
1254
+ case "greater_than":
1255
+ result =
1256
+ coerceUnknownToNumber(left.value) !== null &&
1257
+ coerceUnknownToNumber(right?.value) !== null &&
1258
+ (coerceUnknownToNumber(left.value) ?? 0) > (coerceUnknownToNumber(right?.value) ?? 0);
1259
+ break;
1260
+ case "less_than":
1261
+ result =
1262
+ coerceUnknownToNumber(left.value) !== null &&
1263
+ coerceUnknownToNumber(right?.value) !== null &&
1264
+ (coerceUnknownToNumber(left.value) ?? 0) < (coerceUnknownToNumber(right?.value) ?? 0);
1265
+ break;
1266
+ case "greater_or_equal":
1267
+ result =
1268
+ coerceUnknownToNumber(left.value) !== null &&
1269
+ coerceUnknownToNumber(right?.value) !== null &&
1270
+ (coerceUnknownToNumber(left.value) ?? 0) >= (coerceUnknownToNumber(right?.value) ?? 0);
1271
+ break;
1272
+ case "less_or_equal":
1273
+ result =
1274
+ coerceUnknownToNumber(left.value) !== null &&
1275
+ coerceUnknownToNumber(right?.value) !== null &&
1276
+ (coerceUnknownToNumber(left.value) ?? 0) <= (coerceUnknownToNumber(right?.value) ?? 0);
1277
+ break;
1278
+ case "between": {
1279
+ const range = Array.isArray(right?.value) ? right?.value : [];
1280
+ const leftNumber = coerceUnknownToNumber(left.value);
1281
+ const lowerBound = coerceUnknownToNumber(range[0]);
1282
+ const upperBound = coerceUnknownToNumber(range[1]);
1283
+ result =
1284
+ leftNumber !== null &&
1285
+ lowerBound !== null &&
1286
+ upperBound !== null &&
1287
+ leftNumber >= lowerBound &&
1288
+ leftNumber <= upperBound;
1289
+ break;
1290
+ }
1291
+ case "contains":
1292
+ result = Array.isArray(left.value)
1293
+ ? left.value.some((entry) => compareLooseEquality(entry, right?.value))
1294
+ : formatDisplayValue(left.value).includes(formatDisplayValue(right?.value));
1295
+ break;
1296
+ case "not_contains":
1297
+ result = Array.isArray(left.value)
1298
+ ? !left.value.some((entry) => compareLooseEquality(entry, right?.value))
1299
+ : !formatDisplayValue(left.value).includes(formatDisplayValue(right?.value));
1300
+ break;
1301
+ case "starts_with":
1302
+ result = formatDisplayValue(left.value).startsWith(formatDisplayValue(right?.value));
1303
+ break;
1304
+ case "ends_with":
1305
+ result = formatDisplayValue(left.value).endsWith(formatDisplayValue(right?.value));
1306
+ break;
1307
+ case "regex":
1308
+ result = buildConditionRegex(formatDisplayValue(right?.value)).test(formatDisplayValue(left.value));
1309
+ break;
1310
+ case "exists":
1311
+ result = left.exists;
1312
+ break;
1313
+ case "not_exists":
1314
+ result = !left.exists;
1315
+ break;
1316
+ case "is_empty":
1317
+ result = left.exists
1318
+ ? left.value === "" ||
1319
+ (Array.isArray(left.value) && left.value.length === 0) ||
1320
+ (isRecord(left.value) && Object.keys(left.value).length === 0)
1321
+ : false;
1322
+ break;
1323
+ case "not_empty":
1324
+ result = left.exists
1325
+ ? !(left.value === "" ||
1326
+ (Array.isArray(left.value) && left.value.length === 0) ||
1327
+ (isRecord(left.value) && Object.keys(left.value).length === 0))
1328
+ : false;
1329
+ break;
1330
+ case "is_null":
1331
+ result = left.value === null;
1332
+ break;
1333
+ case "not_null":
1334
+ result = left.value !== null;
1335
+ break;
1336
+ case "in":
1337
+ result = Array.isArray(right?.value)
1338
+ ? right.value.some((entry) => compareLooseEquality(left.value, entry))
1339
+ : false;
1340
+ break;
1341
+ case "not_in":
1342
+ result = Array.isArray(right?.value)
1343
+ ? !right.value.some((entry) => compareLooseEquality(left.value, entry))
1344
+ : false;
1345
+ break;
1346
+ default:
1347
+ result = false;
1348
+ break;
1349
+ }
1350
+ return {
1351
+ id: rule.id,
1352
+ type: "rule",
1353
+ operator: rule.operator,
1354
+ result,
1355
+ left,
1356
+ ...(right ? { right } : {}),
1357
+ };
1358
+ };
1359
+ const evaluateGroup = (group, runtimeScope) => {
1360
+ const children = group.children.map((child) => child.type === "group"
1361
+ ? evaluateGroup(child, runtimeScope)
1362
+ : evaluateRule(child, runtimeScope));
1363
+ return {
1364
+ id: group.id,
1365
+ type: "group",
1366
+ operator: group.operator,
1367
+ result: group.operator === "AND"
1368
+ ? children.every((child) => child.result)
1369
+ : children.some((child) => child.result),
1370
+ children,
1371
+ };
1372
+ };
1373
+ const replaceChildInGroup = (group, childId, nextChild) => ({
1374
+ ...group,
1375
+ children: group.children.map((child) => {
1376
+ if (child.id === childId) {
1377
+ return nextChild;
1378
+ }
1379
+ if (child.type === "group") {
1380
+ return replaceChildInGroup(child, childId, nextChild);
1381
+ }
1382
+ return child;
1383
+ }),
1384
+ });
1385
+ const appendChildToConditionGroup = (group, groupId, nextChild) => ({
1386
+ ...group,
1387
+ children: group.id === groupId
1388
+ ? [...group.children, nextChild]
1389
+ : group.children.map((child) => child.type === "group"
1390
+ ? appendChildToConditionGroup(child, groupId, nextChild)
1391
+ : child),
1392
+ });
1393
+ const removeChildFromConditionGroup = (group, groupId, childId) => ({
1394
+ ...group,
1395
+ children: group.id === groupId
1396
+ ? group.children.filter((child) => child.id !== childId)
1397
+ : group.children.map((child) => child.type === "group"
1398
+ ? removeChildFromConditionGroup(child, groupId, childId)
1399
+ : child),
1400
+ });
1401
+ const updateConditionGroup = (group, groupId, updater) => {
1402
+ if (group.id === groupId) {
1403
+ return updater(group);
1404
+ }
1405
+ return {
1406
+ ...group,
1407
+ children: group.children.map((child) => child.type === "group" ? updateConditionGroup(child, groupId, updater) : child),
1408
+ };
1409
+ };
1410
+ const updateConditionRule = (group, ruleId, updater) => ({
1411
+ ...group,
1412
+ children: group.children.map((child) => {
1413
+ if (child.type === "rule") {
1414
+ return child.id === ruleId ? updater(child) : child;
1415
+ }
1416
+ return updateConditionRule(child, ruleId, updater);
1417
+ }),
1418
+ });
1419
+ const getConditionQuickSummaryParts = (config) => {
1420
+ const ruleCount = collectConditionRuleCount(config.rootGroup);
1421
+ const groupCount = collectConditionGroupCount(config.rootGroup);
1422
+ return [
1423
+ config.mode === "expert" ? "Expert" : "Builder",
1424
+ `${ruleCount} rule${ruleCount === 1 ? "" : "s"}`,
1425
+ `${groupCount} group${groupCount === 1 ? "" : "s"}`,
1426
+ ];
1427
+ };
1428
+ exports.CONDITION_VALUE_TYPES = [
1429
+ "string",
1430
+ "number",
1431
+ "boolean",
1432
+ "null",
1433
+ "array",
1434
+ ];
1435
+ const createConditionOperand = (mode = "runtime", valueType = mode === "runtime" ? "string" : "string", value) => ({
1436
+ mode,
1437
+ valueType,
1438
+ value: typeof value !== "undefined"
1439
+ ? mode === "runtime"
1440
+ ? String(value ?? "").trim()
1441
+ : parseLiteralValue(value, valueType)
1442
+ : mode === "runtime"
1443
+ ? ""
1444
+ : getDefaultLiteralValue(valueType),
1445
+ });
1446
+ exports.createConditionOperand = createConditionOperand;
1447
+ const createConditionRule = (overrides = {}) => {
1448
+ const operator = overrides.operator ?? "equals";
1449
+ return {
1450
+ id: overrides.id ?? createId("condition-rule"),
1451
+ type: "rule",
1452
+ left: overrides.left ? cloneConditionOperand(overrides.left) : (0, exports.createConditionOperand)("runtime"),
1453
+ operator,
1454
+ ...(isUnaryOperator(operator)
1455
+ ? {}
1456
+ : {
1457
+ right: overrides.right
1458
+ ? cloneConditionOperand(overrides.right)
1459
+ : getDefaultRightOperand(operator),
1460
+ }),
1461
+ };
1462
+ };
1463
+ exports.createConditionRule = createConditionRule;
1464
+ const createConditionGroup = (operator = "AND", children = [(0, exports.createConditionRule)()]) => ({
1465
+ id: createId("condition-group"),
1466
+ type: "group",
1467
+ operator,
1468
+ children,
1469
+ });
1470
+ exports.createConditionGroup = createConditionGroup;
1471
+ const createDefaultConditionConfig = () => {
1472
+ const rootGroup = (0, exports.createConditionGroup)("AND", [
1473
+ (0, exports.createConditionRule)({
1474
+ left: (0, exports.createConditionOperand)("runtime", "number", "response.status"),
1475
+ operator: "equals",
1476
+ right: (0, exports.createConditionOperand)("literal", "number", 200),
1477
+ }),
1478
+ ]);
1479
+ return {
1480
+ mode: "builder",
1481
+ rootGroup,
1482
+ expertExpression: (0, exports.serializeConditionGroupToExpression)(rootGroup),
1483
+ };
1484
+ };
1485
+ exports.createDefaultConditionConfig = createDefaultConditionConfig;
1486
+ const serializeConditionGroupToExpression = (group) => serializeGroupExpression(group, false);
1487
+ exports.serializeConditionGroupToExpression = serializeConditionGroupToExpression;
1488
+ const serializeConditionConfigToExpression = (config) => config.mode === "expert" && config.expertExpression?.trim()
1489
+ ? config.expertExpression.trim()
1490
+ : (0, exports.serializeConditionGroupToExpression)(config.rootGroup);
1491
+ exports.serializeConditionConfigToExpression = serializeConditionConfigToExpression;
1492
+ const serializeConditionGroupToPreview = (group) => serializeGroupExpression(group, true);
1493
+ exports.serializeConditionGroupToPreview = serializeConditionGroupToPreview;
1494
+ const serializeConditionConfigToPreview = (config) => config.mode === "expert" && config.expertExpression?.trim()
1495
+ ? config.expertExpression.trim()
1496
+ : (0, exports.serializeConditionGroupToPreview)(config.rootGroup);
1497
+ exports.serializeConditionConfigToPreview = serializeConditionConfigToPreview;
1498
+ const parseConditionExpressionToGroup = (expression) => {
1499
+ const tokens = tokenizeConditionExpression(expression);
1500
+ const cursor = {
1501
+ index: 0,
1502
+ tokens,
1503
+ };
1504
+ const group = ensureGroupRoot(parseOrExpression(cursor));
1505
+ if (parseCursorToken(cursor)) {
1506
+ throw new Error(`Unexpected token at character ${(parseCursorToken(cursor)?.position ?? 0) + 1}.`);
1507
+ }
1508
+ return group;
1509
+ };
1510
+ exports.parseConditionExpressionToGroup = parseConditionExpressionToGroup;
1511
+ const parseConditionExpressionSafely = (expression) => {
1512
+ try {
1513
+ return {
1514
+ group: (0, exports.parseConditionExpressionToGroup)(expression),
1515
+ issues: [],
1516
+ };
1517
+ }
1518
+ catch (error) {
1519
+ return {
1520
+ group: (0, exports.createDefaultConditionConfig)().rootGroup,
1521
+ issues: [
1522
+ buildValidationIssue("expert:parse", error instanceof Error ? error.message : "Condition expression is invalid.", "error"),
1523
+ ],
1524
+ };
1525
+ }
1526
+ };
1527
+ exports.parseConditionExpressionSafely = parseConditionExpressionSafely;
1528
+ const normalizeConditionNodeConfig = (value) => {
1529
+ if (isRecord(value) && typeof value.expression === "string") {
1530
+ const expression = value.expression.trim();
1531
+ if (!expression) {
1532
+ return (0, exports.createDefaultConditionConfig)();
1533
+ }
1534
+ try {
1535
+ const rootGroup = (0, exports.parseConditionExpressionToGroup)(expression);
1536
+ return {
1537
+ mode: "builder",
1538
+ rootGroup,
1539
+ expertExpression: (0, exports.serializeConditionGroupToExpression)(rootGroup),
1540
+ };
1541
+ }
1542
+ catch {
1543
+ return {
1544
+ mode: "expert",
1545
+ rootGroup: (0, exports.createDefaultConditionConfig)().rootGroup,
1546
+ expertExpression: expression,
1547
+ };
1548
+ }
1549
+ }
1550
+ if (!isRecord(value)) {
1551
+ return (0, exports.createDefaultConditionConfig)();
1552
+ }
1553
+ const mode = value.mode === "expert" ? "expert" : "builder";
1554
+ const rootGroup = sanitizeConditionGroup(value.rootGroup) ?? (0, exports.createDefaultConditionConfig)().rootGroup;
1555
+ const expertExpression = typeof value.expertExpression === "string" ? value.expertExpression : null;
1556
+ return {
1557
+ mode,
1558
+ rootGroup,
1559
+ expertExpression: expertExpression ??
1560
+ (0, exports.serializeConditionGroupToExpression)(rootGroup),
1561
+ };
1562
+ };
1563
+ exports.normalizeConditionNodeConfig = normalizeConditionNodeConfig;
1564
+ const validateConditionConfig = (config, runtimeRootKeys) => {
1565
+ const issues = [];
1566
+ const rootKeySet = runtimeRootKeys ? new Set(runtimeRootKeys) : null;
1567
+ let targetGroup = config.rootGroup;
1568
+ if (config.mode === "expert" && config.expertExpression?.trim()) {
1569
+ const parseResult = (0, exports.parseConditionExpressionSafely)(config.expertExpression);
1570
+ issues.push(...parseResult.issues);
1571
+ if (parseResult.issues.some((issue) => issue.severity === "error")) {
1572
+ return issues;
1573
+ }
1574
+ targetGroup = parseResult.group;
1575
+ }
1576
+ validateGroup(targetGroup, issues, rootKeySet);
1577
+ return issues;
1578
+ };
1579
+ exports.validateConditionConfig = validateConditionConfig;
1580
+ const getConditionRuleCount = (group) => collectConditionRuleCount(group);
1581
+ exports.getConditionRuleCount = getConditionRuleCount;
1582
+ const getConditionGroupCount = (group) => collectConditionGroupCount(group);
1583
+ exports.getConditionGroupCount = getConditionGroupCount;
1584
+ const getConditionQuickSummary = (config) => getConditionQuickSummaryParts(config).join(" • ");
1585
+ exports.getConditionQuickSummary = getConditionQuickSummary;
1586
+ const getConditionHeadline = (config) => {
1587
+ const ruleCount = collectConditionRuleCount(config.rootGroup);
1588
+ const groupCount = collectConditionGroupCount(config.rootGroup);
1589
+ return `IF (${ruleCount} rule${ruleCount === 1 ? "" : "s"}, ${groupCount} group${groupCount === 1 ? "" : "s"})`;
1590
+ };
1591
+ exports.getConditionHeadline = getConditionHeadline;
1592
+ const getConditionValueTypeLabel = (valueType) => VALUE_TYPE_LABELS[valueType];
1593
+ exports.getConditionValueTypeLabel = getConditionValueTypeLabel;
1594
+ const getExecutableConditionGroup = (config) => {
1595
+ if (config.mode === "expert" && config.expertExpression?.trim()) {
1596
+ return (0, exports.parseConditionExpressionToGroup)(config.expertExpression);
1597
+ }
1598
+ return config.rootGroup;
1599
+ };
1600
+ exports.getExecutableConditionGroup = getExecutableConditionGroup;
1601
+ const evaluateConditionConfig = (config, runtimeScope) => {
1602
+ const executableGroup = (0, exports.getExecutableConditionGroup)(config);
1603
+ const tree = evaluateGroup(executableGroup, runtimeScope);
1604
+ return {
1605
+ expression: (0, exports.serializeConditionConfigToExpression)(config),
1606
+ result: tree.result,
1607
+ tree,
1608
+ };
1609
+ };
1610
+ exports.evaluateConditionConfig = evaluateConditionConfig;
1611
+ const getConditionRuntimeRootKeys = (config) => new Set(Object.keys(createRuntimeRoots(config)));
1612
+ exports.getConditionRuntimeRootKeys = getConditionRuntimeRootKeys;
1613
+ const updateConditionGroupOperator = (rootGroup, groupId, operator) => updateConditionGroup(rootGroup, groupId, (group) => ({
1614
+ ...group,
1615
+ operator,
1616
+ }));
1617
+ exports.updateConditionGroupOperator = updateConditionGroupOperator;
1618
+ const appendRuleToConditionGroup = (rootGroup, groupId) => appendChildToConditionGroup(rootGroup, groupId, (0, exports.createConditionRule)());
1619
+ exports.appendRuleToConditionGroup = appendRuleToConditionGroup;
1620
+ const appendGroupToConditionGroup = (rootGroup, groupId) => appendChildToConditionGroup(rootGroup, groupId, (0, exports.createConditionGroup)("AND"));
1621
+ exports.appendGroupToConditionGroup = appendGroupToConditionGroup;
1622
+ const removeConditionChild = (rootGroup, groupId, childId) => removeChildFromConditionGroup(rootGroup, groupId, childId);
1623
+ exports.removeConditionChild = removeConditionChild;
1624
+ const replaceConditionGroup = (rootGroup, nextGroup) => replaceChildInGroup(rootGroup, nextGroup.id, nextGroup);
1625
+ exports.replaceConditionGroup = replaceConditionGroup;
1626
+ const patchConditionRule = (rootGroup, ruleId, updater) => updateConditionRule(rootGroup, ruleId, updater);
1627
+ exports.patchConditionRule = patchConditionRule;
1628
+ const patchConditionGroup = (rootGroup, groupId, updater) => updateConditionGroup(rootGroup, groupId, updater);
1629
+ exports.patchConditionGroup = patchConditionGroup;
1630
+ const updateConditionRuleOperator = (rootGroup, ruleId, operator) => updateConditionRule(rootGroup, ruleId, (rule) => ({
1631
+ ...rule,
1632
+ operator,
1633
+ ...(isUnaryOperator(operator)
1634
+ ? { right: undefined }
1635
+ : {
1636
+ right: rule.right &&
1637
+ ((operator !== "between" && operator !== "in" && operator !== "not_in") ||
1638
+ rule.right.valueType === "array")
1639
+ ? cloneConditionOperand(rule.right)
1640
+ : getDefaultRightOperand(operator),
1641
+ }),
1642
+ }));
1643
+ exports.updateConditionRuleOperator = updateConditionRuleOperator;
1644
+ const updateConditionRuleLeftOperand = (rootGroup, ruleId, operand) => updateConditionRule(rootGroup, ruleId, (rule) => ({
1645
+ ...rule,
1646
+ left: cloneConditionOperand(operand),
1647
+ }));
1648
+ exports.updateConditionRuleLeftOperand = updateConditionRuleLeftOperand;
1649
+ const updateConditionRuleRightOperand = (rootGroup, ruleId, operand) => updateConditionRule(rootGroup, ruleId, (rule) => ({
1650
+ ...rule,
1651
+ right: cloneConditionOperand(operand),
1652
+ }));
1653
+ exports.updateConditionRuleRightOperand = updateConditionRuleRightOperand;
1654
+ const buildConditionOutputPayload = (config, evaluation) => ({
1655
+ debug: evaluation.tree,
1656
+ expression: evaluation.expression,
1657
+ mode: config.mode,
1658
+ preview: (0, exports.serializeConditionConfigToPreview)(config),
1659
+ result: evaluation.result,
1660
+ });
1661
+ exports.buildConditionOutputPayload = buildConditionOutputPayload;
1662
+ const getConditionOperatorLabel = (operator) => exports.CONDITION_OPERATOR_META.find((entry) => entry.value === operator)?.label ?? operator;
1663
+ exports.getConditionOperatorLabel = getConditionOperatorLabel;
1664
+ const isConditionOperatorUnary = (operator) => isUnaryOperator(operator);
1665
+ exports.isConditionOperatorUnary = isConditionOperatorUnary;
1666
+ const buildConditionRuntimeRootSetFromKeys = (keys) => new Set(keys);
1667
+ exports.buildConditionRuntimeRootSetFromKeys = buildConditionRuntimeRootSetFromKeys;
1668
+ const buildConditionRuntimeRootKeysForNodes = (nodes) => {
1669
+ const nodeLabels = flattenScenarioNodes(nodes)
1670
+ .map((node) => node.data.label.trim())
1671
+ .filter(Boolean);
1672
+ return new Set([
1673
+ "workflow",
1674
+ "response",
1675
+ "env",
1676
+ "global",
1677
+ "globals",
1678
+ "db",
1679
+ "kafkaEvent",
1680
+ "kafkaevent",
1681
+ "redis",
1682
+ "lastResponse",
1683
+ ...nodeLabels,
1684
+ ]);
1685
+ };
1686
+ exports.buildConditionRuntimeRootKeysForNodes = buildConditionRuntimeRootKeysForNodes;
1687
+ const buildConditionRuntimeSuggestions = ({ nodes, environmentKeys = [], }) => dedupeSuggestions([
1688
+ ...BUILT_IN_RUNTIME_SUGGESTIONS,
1689
+ ...environmentKeys.map((key) => createSuggestion("Environment", `env.${key}`, `Environment variable "${key}".`, "string")),
1690
+ ...flattenScenarioNodes(nodes).flatMap((node) => {
1691
+ const label = node.data.label.trim();
1692
+ return label ? buildNodeSuggestions(label, node.type) : [];
1693
+ }),
1694
+ ]);
1695
+ exports.buildConditionRuntimeSuggestions = buildConditionRuntimeSuggestions;
1696
+ const filterConditionRuntimeSuggestions = ({ query, suggestions, limit = 40, }) => {
1697
+ const normalizedQuery = query.trim().toLowerCase();
1698
+ const filtered = normalizedQuery
1699
+ ? suggestions.filter((suggestion) => {
1700
+ const haystacks = [
1701
+ suggestion.group.toLowerCase(),
1702
+ suggestion.path.toLowerCase(),
1703
+ suggestion.description.toLowerCase(),
1704
+ ];
1705
+ return haystacks.some((haystack) => haystack.includes(normalizedQuery));
1706
+ })
1707
+ : suggestions;
1708
+ return filtered.slice(0, limit);
1709
+ };
1710
+ exports.filterConditionRuntimeSuggestions = filterConditionRuntimeSuggestions;
1711
+ const createConditionConfigFromLegacyExpression = (expression) => {
1712
+ const normalizedExpression = normalizeWhitespace(expression);
1713
+ if (!normalizedExpression) {
1714
+ return (0, exports.createDefaultConditionConfig)();
1715
+ }
1716
+ try {
1717
+ const rootGroup = (0, exports.parseConditionExpressionToGroup)(normalizedExpression);
1718
+ return {
1719
+ mode: "builder",
1720
+ rootGroup,
1721
+ expertExpression: (0, exports.serializeConditionGroupToExpression)(rootGroup),
1722
+ };
1723
+ }
1724
+ catch {
1725
+ return {
1726
+ mode: "expert",
1727
+ rootGroup: (0, exports.createDefaultConditionConfig)().rootGroup,
1728
+ expertExpression: normalizedExpression,
1729
+ };
1730
+ }
1731
+ };
1732
+ exports.createConditionConfigFromLegacyExpression = createConditionConfigFromLegacyExpression;
1733
+ const convertLegacyConditionOperator = (value) => parseLegacyConditionOperator(value);
1734
+ exports.convertLegacyConditionOperator = convertLegacyConditionOperator;
1735
+ //# sourceMappingURL=condition.js.map