@abyss-project/console 1.0.23 → 1.0.26

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.
@@ -1,4 +1,4 @@
1
- import { FilterOperator, SwitchConditionOperator, HttpMethod, WorkflowActionType } from '../enum';
1
+ import { HttpMethod, WorkflowActionType } from '../enum';
2
2
  export interface IUserRegistrationWorkflowTriggerData {
3
3
  userId: string;
4
4
  email: string;
@@ -14,19 +14,13 @@ export interface IWebhookWorkflowTriggerData {
14
14
  }
15
15
  export type WorkflowTriggerData = IUserRegistrationWorkflowTriggerData | IWebhookWorkflowTriggerData | Record<string, any>;
16
16
  export interface IFilterStepConfig {
17
- filters: Array<{
18
- field: string;
19
- operator: FilterOperator;
20
- value: any;
21
- }>;
17
+ expression: string;
22
18
  }
23
19
  export interface ISleepStepConfig {
24
- duration: number;
20
+ duration: string | number;
25
21
  }
26
22
  export interface ISwitchCondition {
27
- field: string;
28
- operator: SwitchConditionOperator;
29
- value: any;
23
+ expression: string;
30
24
  targetStepId: string | null;
31
25
  }
32
26
  export interface ISwitchStepConfig {
@@ -83,6 +83,7 @@ export interface IUpdateWorkflowStepBody {
83
83
  continueOnError?: boolean;
84
84
  afterStepId?: string | null;
85
85
  beforeStepId?: string | null;
86
+ swapStepId?: string;
86
87
  condition?: {
87
88
  field: string;
88
89
  operator: 'equals' | 'not_equals' | 'contains' | 'greater_than' | 'less_than';
@@ -1,15 +1,10 @@
1
- export interface WorkflowCondition {
2
- field: string;
3
- operator: 'equals' | 'not_equals' | 'contains' | 'greater_than' | 'less_than';
4
- value: any;
5
- }
6
1
  export interface IWorkflowStepConnection {
7
2
  id: string;
8
3
  workflowId: string;
9
4
  fromStepId: string | null;
10
5
  toStepId: string | null;
11
6
  label: string | null;
12
- condition: WorkflowCondition | null;
7
+ expression: string | null;
13
8
  order: number;
14
9
  createdAt?: Date;
15
10
  updatedAt?: Date;
@@ -1,9 +1,10 @@
1
1
  import { WorkflowTriggerType, FilterOperator } from '../../enum/workflow.enum';
2
2
  import { IWorkflow } from './workflow.model';
3
3
  export interface ITriggerFilter {
4
- field: string;
5
- operator: FilterOperator;
6
- value: any;
4
+ expression?: string;
5
+ field?: string;
6
+ operator?: FilterOperator;
7
+ value?: any;
7
8
  }
8
9
  export interface ITriggerExecutionStats {
9
10
  total: number;
@@ -1,4 +1,7 @@
1
1
  import { WorkflowTriggerData, FilterOperator, IWorkflowTrigger } from '../types';
2
+ type ExpressionValue = string | number | boolean | null | undefined | unknown[] | Record<string, unknown>;
2
3
  export declare const matchesTriggerFilters: (trigger: IWorkflowTrigger, triggerData: WorkflowTriggerData) => boolean;
4
+ export declare const evaluateExpression: (expression: string, context: Record<string, unknown>) => ExpressionValue;
3
5
  export declare const getNestedValue: (obj: Record<string, unknown>, path: string) => unknown;
4
6
  export declare const evaluateFilter: (value: unknown, operator: FilterOperator, expected: unknown) => boolean;
7
+ export {};
@@ -1,17 +1,298 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.evaluateFilter = exports.getNestedValue = exports.matchesTriggerFilters = void 0;
3
+ exports.evaluateFilter = exports.getNestedValue = exports.evaluateExpression = exports.matchesTriggerFilters = void 0;
4
4
  const types_1 = require("../types");
5
+ const OPERATOR_PRECEDENCE = {
6
+ '||': 1,
7
+ '&&': 2,
8
+ '==': 3,
9
+ '===': 3,
10
+ '!=': 3,
11
+ '!==': 3,
12
+ '<': 4,
13
+ '<=': 4,
14
+ '>': 4,
15
+ '>=': 4,
16
+ '+': 5,
17
+ '-': 5,
18
+ '*': 6,
19
+ '/': 6,
20
+ '%': 6,
21
+ '**': 7,
22
+ };
5
23
  const matchesTriggerFilters = (trigger, triggerData) => {
6
24
  if (!trigger.filters || trigger.filters.length === 0) {
7
25
  return true;
8
26
  }
9
27
  return trigger.filters.every((filter) => {
10
- const value = (0, exports.getNestedValue)(triggerData, filter.field);
11
- return (0, exports.evaluateFilter)(value, filter.operator, filter.value);
28
+ if (filter.expression) {
29
+ try {
30
+ const context = { trigger: triggerData };
31
+ const result = (0, exports.evaluateExpression)(filter.expression, context);
32
+ return Boolean(result);
33
+ }
34
+ catch (error) {
35
+ console.error('Expression evaluation error:', error);
36
+ return false;
37
+ }
38
+ }
39
+ if (filter.field && filter.operator) {
40
+ const value = (0, exports.getNestedValue)(triggerData, filter.field);
41
+ return (0, exports.evaluateFilter)(value, filter.operator, filter.value);
42
+ }
43
+ return true;
12
44
  });
13
45
  };
14
46
  exports.matchesTriggerFilters = matchesTriggerFilters;
47
+ const evaluateExpression = (expression, context) => {
48
+ const trimmed = expression.trim();
49
+ const simpleRefMatch = trimmed.match(/^\{\{([^}]+)\}\}$/);
50
+ if (simpleRefMatch) {
51
+ return (0, exports.getNestedValue)(context, simpleRefMatch[1].trim());
52
+ }
53
+ const tokens = tokenizeExpression(trimmed, context);
54
+ return evaluateTokens(tokens);
55
+ };
56
+ exports.evaluateExpression = evaluateExpression;
57
+ const tokenizeExpression = (expression, context) => {
58
+ const tokens = [];
59
+ let pos = 0;
60
+ const len = expression.length;
61
+ while (pos < len) {
62
+ while (pos < len && /\s/.test(expression[pos])) {
63
+ pos++;
64
+ }
65
+ if (pos >= len)
66
+ break;
67
+ const char = expression[pos];
68
+ if (expression.slice(pos, pos + 2) === '{{') {
69
+ const endIdx = expression.indexOf('}}', pos + 2);
70
+ if (endIdx === -1) {
71
+ throw new Error(`Unclosed expression at position ${pos}`);
72
+ }
73
+ const path = expression.slice(pos + 2, endIdx).trim();
74
+ if (path.includes('|')) {
75
+ const [refPath, ...pipes] = path.split('|').map(s => s.trim());
76
+ let value = (0, exports.getNestedValue)(context, refPath);
77
+ for (const pipe of pipes) {
78
+ value = applyPipeOperator(value, pipe);
79
+ }
80
+ tokens.push({ type: 'reference', value: value });
81
+ }
82
+ else {
83
+ const value = (0, exports.getNestedValue)(context, path);
84
+ tokens.push({ type: 'reference', value: value });
85
+ }
86
+ pos = endIdx + 2;
87
+ continue;
88
+ }
89
+ if (char === '"' || char === "'") {
90
+ const quote = char;
91
+ let str = '';
92
+ pos++;
93
+ while (pos < len && expression[pos] !== quote) {
94
+ if (expression[pos] === '\\' && pos + 1 < len) {
95
+ pos++;
96
+ str += expression[pos];
97
+ }
98
+ else {
99
+ str += expression[pos];
100
+ }
101
+ pos++;
102
+ }
103
+ pos++;
104
+ tokens.push({ type: 'literal', value: str });
105
+ continue;
106
+ }
107
+ if (/[\d.]/.test(char)) {
108
+ let numStr = '';
109
+ while (pos < len && /[\d.eE+-]/.test(expression[pos])) {
110
+ numStr += expression[pos];
111
+ pos++;
112
+ }
113
+ tokens.push({ type: 'literal', value: parseFloat(numStr) });
114
+ continue;
115
+ }
116
+ const restExpr = expression.slice(pos);
117
+ if (restExpr.startsWith('true') && !/\w/.test(restExpr[4] || '')) {
118
+ tokens.push({ type: 'literal', value: true });
119
+ pos += 4;
120
+ continue;
121
+ }
122
+ if (restExpr.startsWith('false') && !/\w/.test(restExpr[5] || '')) {
123
+ tokens.push({ type: 'literal', value: false });
124
+ pos += 5;
125
+ continue;
126
+ }
127
+ if (restExpr.startsWith('null') && !/\w/.test(restExpr[4] || '')) {
128
+ tokens.push({ type: 'literal', value: null });
129
+ pos += 4;
130
+ continue;
131
+ }
132
+ const twoChar = expression.slice(pos, pos + 2);
133
+ const threeChar = expression.slice(pos, pos + 3);
134
+ if (['===', '!=='].includes(threeChar)) {
135
+ tokens.push({ type: 'operator', value: threeChar });
136
+ pos += 3;
137
+ continue;
138
+ }
139
+ if (['==', '!=', '<=', '>=', '&&', '||', '**'].includes(twoChar)) {
140
+ tokens.push({ type: 'operator', value: twoChar });
141
+ pos += 2;
142
+ continue;
143
+ }
144
+ if (['+', '-', '*', '/', '%', '<', '>', '!'].includes(char)) {
145
+ tokens.push({ type: 'operator', value: char });
146
+ pos++;
147
+ continue;
148
+ }
149
+ if (char === '(' || char === ')') {
150
+ tokens.push({ type: 'paren', value: char });
151
+ pos++;
152
+ continue;
153
+ }
154
+ if (char === '?' || char === ':') {
155
+ tokens.push({ type: 'operator', value: char });
156
+ pos++;
157
+ continue;
158
+ }
159
+ pos++;
160
+ }
161
+ return tokens;
162
+ };
163
+ const applyPipeOperator = (value, pipe) => {
164
+ const pipeMatch = pipe.match(/^(\w+)(?:\(([^)]*)\))?$/);
165
+ if (!pipeMatch)
166
+ return value;
167
+ const [, operator, args] = pipeMatch;
168
+ const strValue = String(value !== null && value !== void 0 ? value : '');
169
+ switch (operator) {
170
+ case 'contains':
171
+ return strValue.includes(args || '');
172
+ case 'startsWith':
173
+ return strValue.startsWith(args || '');
174
+ case 'endsWith':
175
+ return strValue.endsWith(args || '');
176
+ case 'isEmpty':
177
+ return value === null || value === undefined || value === '' ||
178
+ (Array.isArray(value) && value.length === 0);
179
+ case 'isNotEmpty':
180
+ return value !== null && value !== undefined && value !== '' &&
181
+ (!Array.isArray(value) || value.length > 0);
182
+ case 'length':
183
+ return Array.isArray(value) ? value.length : strValue.length;
184
+ case 'lower':
185
+ case 'toLowerCase':
186
+ return strValue.toLowerCase();
187
+ case 'upper':
188
+ case 'toUpperCase':
189
+ return strValue.toUpperCase();
190
+ case 'trim':
191
+ return strValue.trim();
192
+ default:
193
+ return value;
194
+ }
195
+ };
196
+ const evaluateTokens = (tokens) => {
197
+ var _a, _b;
198
+ if (tokens.length === 0)
199
+ return null;
200
+ if (tokens.length === 1) {
201
+ return tokens[0].value;
202
+ }
203
+ const questionIdx = tokens.findIndex(t => t.type === 'operator' && t.value === '?');
204
+ if (questionIdx !== -1) {
205
+ const colonIdx = tokens.findIndex((t, i) => i > questionIdx && t.type === 'operator' && t.value === ':');
206
+ if (colonIdx !== -1) {
207
+ const condition = evaluateTokens(tokens.slice(0, questionIdx));
208
+ const trueValue = evaluateTokens(tokens.slice(questionIdx + 1, colonIdx));
209
+ const falseValue = evaluateTokens(tokens.slice(colonIdx + 1));
210
+ return condition ? trueValue : falseValue;
211
+ }
212
+ }
213
+ let lowestPrecedence = Infinity;
214
+ let opIndex = -1;
215
+ let parenDepth = 0;
216
+ for (let i = 0; i < tokens.length; i++) {
217
+ const token = tokens[i];
218
+ if (token.type === 'paren') {
219
+ parenDepth += token.value === '(' ? 1 : -1;
220
+ continue;
221
+ }
222
+ if (parenDepth > 0)
223
+ continue;
224
+ if (token.type === 'operator' && typeof token.value === 'string') {
225
+ const precedence = OPERATOR_PRECEDENCE[token.value];
226
+ if (precedence !== undefined && precedence <= lowestPrecedence) {
227
+ lowestPrecedence = precedence;
228
+ opIndex = i;
229
+ }
230
+ }
231
+ }
232
+ if (opIndex !== -1) {
233
+ const op = tokens[opIndex].value;
234
+ const left = evaluateTokens(tokens.slice(0, opIndex));
235
+ const right = evaluateTokens(tokens.slice(opIndex + 1));
236
+ return applyOperator(op, left, right);
237
+ }
238
+ if (((_a = tokens[0]) === null || _a === void 0 ? void 0 : _a.type) === 'paren' && tokens[0].value === '(') {
239
+ let depth = 1;
240
+ let endIdx = 1;
241
+ while (endIdx < tokens.length && depth > 0) {
242
+ if (tokens[endIdx].type === 'paren') {
243
+ depth += tokens[endIdx].value === '(' ? 1 : -1;
244
+ }
245
+ endIdx++;
246
+ }
247
+ return evaluateTokens(tokens.slice(1, endIdx - 1));
248
+ }
249
+ if (((_b = tokens[0]) === null || _b === void 0 ? void 0 : _b.type) === 'operator' && tokens[0].value === '!') {
250
+ const operand = evaluateTokens(tokens.slice(1));
251
+ return !operand;
252
+ }
253
+ return tokens[0].value;
254
+ };
255
+ const applyOperator = (op, left, right) => {
256
+ switch (op) {
257
+ case '+':
258
+ if (typeof left === 'string' || typeof right === 'string') {
259
+ return String(left !== null && left !== void 0 ? left : '') + String(right !== null && right !== void 0 ? right : '');
260
+ }
261
+ return (Number(left) || 0) + (Number(right) || 0);
262
+ case '-':
263
+ return (Number(left) || 0) - (Number(right) || 0);
264
+ case '*':
265
+ return (Number(left) || 0) * (Number(right) || 0);
266
+ case '/':
267
+ return (Number(left) || 0) / (Number(right) || 1);
268
+ case '%':
269
+ return (Number(left) || 0) % (Number(right) || 1);
270
+ case '**':
271
+ return Math.pow(Number(left) || 0, Number(right) || 0);
272
+ case '==':
273
+ return left == right;
274
+ case '===':
275
+ return left === right;
276
+ case '!=':
277
+ return left != right;
278
+ case '!==':
279
+ return left !== right;
280
+ case '<':
281
+ return (Number(left) || 0) < (Number(right) || 0);
282
+ case '<=':
283
+ return (Number(left) || 0) <= (Number(right) || 0);
284
+ case '>':
285
+ return (Number(left) || 0) > (Number(right) || 0);
286
+ case '>=':
287
+ return (Number(left) || 0) >= (Number(right) || 0);
288
+ case '&&':
289
+ return Boolean(left) && Boolean(right);
290
+ case '||':
291
+ return Boolean(left) || Boolean(right);
292
+ default:
293
+ return null;
294
+ }
295
+ };
15
296
  const getNestedValue = (obj, path) => {
16
297
  return path
17
298
  .split('.')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abyss-project/console",
3
- "version": "1.0.23",
3
+ "version": "1.0.26",
4
4
  "description": "Core package to interact with AbyssConsole",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",