@abyss-project/console 1.0.28 → 1.0.30

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 (74) hide show
  1. package/dist/api/workflow.api.d.ts +2 -1
  2. package/dist/api/workflow.api.js +6 -1
  3. package/dist/expressions/functions/array.d.ts +2 -0
  4. package/dist/expressions/functions/array.js +252 -0
  5. package/dist/expressions/functions/crypto.d.ts +2 -0
  6. package/dist/expressions/functions/crypto.js +101 -0
  7. package/dist/expressions/functions/datetime.d.ts +2 -0
  8. package/dist/expressions/functions/datetime.js +256 -0
  9. package/dist/expressions/functions/index.d.ts +7 -0
  10. package/dist/expressions/functions/index.js +46 -0
  11. package/dist/expressions/functions/math.d.ts +2 -0
  12. package/dist/expressions/functions/math.js +174 -0
  13. package/dist/expressions/functions/string.d.ts +2 -0
  14. package/dist/expressions/functions/string.js +301 -0
  15. package/dist/expressions/functions/utility.d.ts +2 -0
  16. package/dist/expressions/functions/utility.js +230 -0
  17. package/dist/expressions/helpers.d.ts +26 -0
  18. package/dist/expressions/helpers.js +132 -0
  19. package/dist/expressions/index.d.ts +5 -0
  20. package/dist/expressions/index.js +16 -0
  21. package/dist/expressions/mapper.d.ts +9 -0
  22. package/dist/expressions/mapper.js +88 -0
  23. package/dist/expressions/parser.d.ts +3 -0
  24. package/dist/expressions/parser.js +97 -0
  25. package/dist/expressions/pipes/array-pipes.d.ts +2 -0
  26. package/dist/expressions/pipes/array-pipes.js +248 -0
  27. package/dist/expressions/pipes/index.d.ts +8 -0
  28. package/dist/expressions/pipes/index.js +40 -0
  29. package/dist/expressions/pipes/object-pipes.d.ts +2 -0
  30. package/dist/expressions/pipes/object-pipes.js +243 -0
  31. package/dist/expressions/pipes/string-pipes.d.ts +9 -0
  32. package/dist/expressions/pipes/string-pipes.js +178 -0
  33. package/dist/expressions/resolver.d.ts +12 -0
  34. package/dist/expressions/resolver.js +88 -0
  35. package/dist/expressions/types.d.ts +36 -0
  36. package/dist/expressions/types.js +2 -0
  37. package/dist/index.d.ts +5 -0
  38. package/dist/index.js +6 -1
  39. package/dist/types/interface/api/requests/workflow.request.d.ts +10 -0
  40. package/dist/types/interface/api/responses/workflow.response.d.ts +4 -0
  41. package/dist/utils/webhook-trigger.utils.js +10 -6
  42. package/dist/workflow-expressions/functions/array.d.ts +2 -0
  43. package/dist/workflow-expressions/functions/array.js +252 -0
  44. package/dist/workflow-expressions/functions/crypto.d.ts +2 -0
  45. package/dist/workflow-expressions/functions/crypto.js +101 -0
  46. package/dist/workflow-expressions/functions/datetime.d.ts +2 -0
  47. package/dist/workflow-expressions/functions/datetime.js +256 -0
  48. package/dist/workflow-expressions/functions/index.d.ts +7 -0
  49. package/dist/workflow-expressions/functions/index.js +46 -0
  50. package/dist/workflow-expressions/functions/math.d.ts +2 -0
  51. package/dist/workflow-expressions/functions/math.js +174 -0
  52. package/dist/workflow-expressions/functions/string.d.ts +2 -0
  53. package/dist/workflow-expressions/functions/string.js +301 -0
  54. package/dist/workflow-expressions/functions/utility.d.ts +2 -0
  55. package/dist/workflow-expressions/functions/utility.js +230 -0
  56. package/dist/workflow-expressions/helpers.d.ts +26 -0
  57. package/dist/workflow-expressions/helpers.js +130 -0
  58. package/dist/workflow-expressions/index.d.ts +15 -0
  59. package/dist/workflow-expressions/index.js +61 -0
  60. package/dist/workflow-expressions/parser.d.ts +8 -0
  61. package/dist/workflow-expressions/parser.js +456 -0
  62. package/dist/workflow-expressions/pipes/array-pipes.d.ts +2 -0
  63. package/dist/workflow-expressions/pipes/array-pipes.js +248 -0
  64. package/dist/workflow-expressions/pipes/index.d.ts +8 -0
  65. package/dist/workflow-expressions/pipes/index.js +40 -0
  66. package/dist/workflow-expressions/pipes/object-pipes.d.ts +2 -0
  67. package/dist/workflow-expressions/pipes/object-pipes.js +243 -0
  68. package/dist/workflow-expressions/pipes/string-pipes.d.ts +9 -0
  69. package/dist/workflow-expressions/pipes/string-pipes.js +178 -0
  70. package/dist/workflow-expressions/resolver.d.ts +8 -0
  71. package/dist/workflow-expressions/resolver.js +260 -0
  72. package/dist/workflow-expressions/types.d.ts +141 -0
  73. package/dist/workflow-expressions/types.js +33 -0
  74. package/package.json +2 -1
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stringPipes = void 0;
4
+ exports.stringPipes = {
5
+ upper: {
6
+ name: 'upper',
7
+ description: 'Converts to uppercase',
8
+ category: 'string',
9
+ signature: 'upper',
10
+ examples: ['{{trigger.name | upper}}'],
11
+ execute: (value) => String(value).toUpperCase(),
12
+ },
13
+ lower: {
14
+ name: 'lower',
15
+ description: 'Converts to lowercase',
16
+ category: 'string',
17
+ signature: 'lower',
18
+ examples: ['{{trigger.email | lower}}'],
19
+ execute: (value) => String(value).toLowerCase(),
20
+ },
21
+ capitalize: {
22
+ name: 'capitalize',
23
+ description: 'Capitalizes the first letter',
24
+ category: 'string',
25
+ signature: 'capitalize',
26
+ examples: ['{{trigger.name | capitalize}}'],
27
+ execute: (value) => {
28
+ const str = String(value);
29
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
30
+ },
31
+ },
32
+ trim: {
33
+ name: 'trim',
34
+ description: 'Removes whitespace from both ends',
35
+ category: 'string',
36
+ signature: 'trim',
37
+ examples: ['{{trigger.input | trim}}'],
38
+ execute: (value) => String(value).trim(),
39
+ },
40
+ truncate: {
41
+ name: 'truncate',
42
+ description: 'Truncates a string to a specified length',
43
+ category: 'string',
44
+ signature: 'truncate(length)',
45
+ examples: ['{{trigger.description | truncate(100)}}'],
46
+ execute: (value, length) => {
47
+ const str = String(value);
48
+ const len = Number(length);
49
+ return str.length > len ? str.substring(0, len) + '...' : str;
50
+ },
51
+ },
52
+ replace: {
53
+ name: 'replace',
54
+ description: 'Replaces occurrences of a pattern',
55
+ category: 'string',
56
+ signature: 'replace(search, replacement)',
57
+ examples: ['{{trigger.text | replace("old", "new")}}'],
58
+ execute: (value, search, replacement) => {
59
+ const searchStr = String(search);
60
+ const replaceStr = String(replacement);
61
+ return String(value).replace(new RegExp(searchStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), replaceStr);
62
+ },
63
+ },
64
+ split: {
65
+ name: 'split',
66
+ description: 'Splits a string into an array',
67
+ category: 'string',
68
+ signature: 'split(separator)',
69
+ examples: ['{{trigger.tags | split(",")}}'],
70
+ execute: (value, separator) => {
71
+ return String(value).split(String(separator));
72
+ },
73
+ },
74
+ substring: {
75
+ name: 'substring',
76
+ description: 'Extracts a substring',
77
+ category: 'string',
78
+ signature: 'substring(start, end?)',
79
+ examples: ['{{trigger.id | substring(0, 8)}}'],
80
+ execute: (value, start, end) => {
81
+ return String(value).substring(Number(start), end !== undefined ? Number(end) : undefined);
82
+ },
83
+ },
84
+ padStart: {
85
+ name: 'padStart',
86
+ description: 'Pads a string from the start',
87
+ category: 'string',
88
+ signature: 'padStart(length, padString?)',
89
+ examples: ['{{trigger.id | padStart(10, "0")}}'],
90
+ execute: (value, length, padString) => {
91
+ return String(value).padStart(Number(length), padString !== undefined ? String(padString) : ' ');
92
+ },
93
+ },
94
+ padEnd: {
95
+ name: 'padEnd',
96
+ description: 'Pads a string from the end',
97
+ category: 'string',
98
+ signature: 'padEnd(length, padString?)',
99
+ examples: ['{{trigger.code | padEnd(5, "X")}}'],
100
+ execute: (value, length, padString) => {
101
+ return String(value).padEnd(Number(length), padString !== undefined ? String(padString) : ' ');
102
+ },
103
+ },
104
+ repeat: {
105
+ name: 'repeat',
106
+ description: 'Repeats a string n times',
107
+ category: 'string',
108
+ signature: 'repeat(count)',
109
+ examples: ['{{trigger.char | repeat(3)}}'],
110
+ execute: (value, count) => {
111
+ return String(value).repeat(Number(count));
112
+ },
113
+ },
114
+ reverse: {
115
+ name: 'reverse',
116
+ description: 'Reverses a string',
117
+ category: 'string',
118
+ signature: 'reverse',
119
+ examples: ['{{trigger.text | reverse}}'],
120
+ execute: (value) => {
121
+ return String(value).split('').reverse().join('');
122
+ },
123
+ },
124
+ slugify: {
125
+ name: 'slugify',
126
+ description: 'Converts to URL-friendly slug',
127
+ category: 'string',
128
+ signature: 'slugify',
129
+ examples: ['{{trigger.title | slugify}}'],
130
+ execute: (value) => {
131
+ return String(value)
132
+ .toLowerCase()
133
+ .trim()
134
+ .replace(/[^\w\s-]/g, '')
135
+ .replace(/[\s_-]+/g, '-')
136
+ .replace(/^-+|-+$/g, '');
137
+ },
138
+ },
139
+ camelCase: {
140
+ name: 'camelCase',
141
+ description: 'Converts to camelCase',
142
+ category: 'string',
143
+ signature: 'camelCase',
144
+ examples: ['{{trigger.text | camelCase}}'],
145
+ execute: (value) => {
146
+ return String(value)
147
+ .replace(/[^\w\s]/g, '')
148
+ .replace(/\s+(.)/g, (_, char) => char.toUpperCase())
149
+ .replace(/^\w/, (char) => char.toLowerCase());
150
+ },
151
+ },
152
+ kebabCase: {
153
+ name: 'kebabCase',
154
+ description: 'Converts to kebab-case',
155
+ category: 'string',
156
+ signature: 'kebabCase',
157
+ examples: ['{{trigger.text | kebabCase}}'],
158
+ execute: (value) => {
159
+ return String(value)
160
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
161
+ .replace(/[\s_]+/g, '-')
162
+ .toLowerCase();
163
+ },
164
+ },
165
+ snakeCase: {
166
+ name: 'snakeCase',
167
+ description: 'Converts to snake_case',
168
+ category: 'string',
169
+ signature: 'snakeCase',
170
+ examples: ['{{trigger.text | snakeCase}}'],
171
+ execute: (value) => {
172
+ return String(value)
173
+ .replace(/([a-z])([A-Z])/g, '$1_$2')
174
+ .replace(/[\s-]+/g, '_')
175
+ .toLowerCase();
176
+ },
177
+ },
178
+ };
@@ -0,0 +1,12 @@
1
+ export interface ExpressionContext {
2
+ [key: string]: unknown;
3
+ }
4
+ export interface ResolverOptions {
5
+ strict?: boolean;
6
+ functions?: Record<string, (...args: unknown[]) => unknown>;
7
+ allowNested?: boolean;
8
+ maxDepth?: number;
9
+ }
10
+ export declare function resolveExpression(expression: string, context: ExpressionContext, options?: ResolverOptions): unknown;
11
+ export declare function isDynamicExpression(expression: string): boolean;
12
+ export declare function extractPaths(expression: string): string[];
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractPaths = exports.isDynamicExpression = exports.resolveExpression = void 0;
4
+ const parser_1 = require("./parser");
5
+ function resolveExpression(expression, context, options = {}) {
6
+ const opts = {
7
+ strict: false,
8
+ functions: {},
9
+ allowNested: true,
10
+ maxDepth: 10,
11
+ ...options,
12
+ };
13
+ if (!expression) {
14
+ return expression;
15
+ }
16
+ const parsed = (0, parser_1.parseExpression)(expression);
17
+ if (!parsed.isDynamic) {
18
+ return expression;
19
+ }
20
+ if (parsed.tokens.length === 1 && parsed.tokens[0].type === 'expression') {
21
+ return evaluateExpression(parsed.tokens[0].value, context, opts);
22
+ }
23
+ return parsed.tokens
24
+ .map((token) => {
25
+ if (token.type === 'text') {
26
+ return token.value;
27
+ }
28
+ if (token.type === 'expression') {
29
+ const value = evaluateExpression(token.value, context, opts);
30
+ return value === undefined || value === null ? '' : String(value);
31
+ }
32
+ return '';
33
+ })
34
+ .join('');
35
+ }
36
+ exports.resolveExpression = resolveExpression;
37
+ function evaluateExpression(expression, context, options) {
38
+ try {
39
+ const value = resolvePath(expression, context, options);
40
+ return value;
41
+ }
42
+ catch (error) {
43
+ if (options.strict) {
44
+ throw error;
45
+ }
46
+ return undefined;
47
+ }
48
+ }
49
+ function resolvePath(path, context, options, depth = 0) {
50
+ if (depth > options.maxDepth) {
51
+ throw new Error(`Maximum depth exceeded for path: ${path}`);
52
+ }
53
+ const bracketMatch = path.match(/^([^[]+)\[['"]([^'"]+)['"]\](.*)$/);
54
+ if (bracketMatch) {
55
+ const [, base, key, rest] = bracketMatch;
56
+ const baseValue = resolvePath(base, context, options, depth + 1);
57
+ if (baseValue === undefined || baseValue === null) {
58
+ return undefined;
59
+ }
60
+ const value = baseValue[key];
61
+ if (rest && rest.length > 0) {
62
+ return resolvePath(rest.replace(/^\./, ''), { _: value }, options, depth + 1);
63
+ }
64
+ return value;
65
+ }
66
+ const parts = path.split('.');
67
+ let current = context;
68
+ for (const part of parts) {
69
+ if (current === undefined || current === null) {
70
+ return undefined;
71
+ }
72
+ if (typeof current !== 'object') {
73
+ return undefined;
74
+ }
75
+ current = current[part];
76
+ }
77
+ return current;
78
+ }
79
+ function isDynamicExpression(expression) {
80
+ const parsed = (0, parser_1.parseExpression)(expression);
81
+ return parsed.isDynamic;
82
+ }
83
+ exports.isDynamicExpression = isDynamicExpression;
84
+ function extractPaths(expression) {
85
+ const parsed = (0, parser_1.parseExpression)(expression);
86
+ return parsed.references.map((ref) => `${ref.source}.${ref.path}`);
87
+ }
88
+ exports.extractPaths = extractPaths;
@@ -0,0 +1,36 @@
1
+ export interface ResourceMapping<TSource = string> {
2
+ source: TSource;
3
+ displayPath: string;
4
+ savePath: string;
5
+ resourceId: string;
6
+ resourceName: string;
7
+ }
8
+ export type ExpressionToken = {
9
+ type: 'text';
10
+ value: string;
11
+ } | {
12
+ type: 'expression';
13
+ value: string;
14
+ raw: string;
15
+ } | {
16
+ type: 'reference';
17
+ source: string;
18
+ path: string;
19
+ };
20
+ export interface ExpressionParserConfig {
21
+ openDelimiter?: string;
22
+ closeDelimiter?: string;
23
+ trimExpressions?: boolean;
24
+ }
25
+ export interface ParsedExpression {
26
+ tokens: ExpressionToken[];
27
+ isDynamic: boolean;
28
+ references: Array<{
29
+ source: string;
30
+ path: string;
31
+ }>;
32
+ }
33
+ export interface ExpressionMappingOptions {
34
+ strict?: boolean;
35
+ preserveUnknown?: boolean;
36
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/index.d.ts CHANGED
@@ -13,6 +13,10 @@ import * as secretPublicApi from './api/secret-public.api';
13
13
  export * from './utils/cidr.util';
14
14
  export * from './types';
15
15
  export * from './utils';
16
+ import * as GenericExpression from './expressions';
17
+ export { GenericExpression };
18
+ import * as WorkflowExpression from './workflow-expressions';
19
+ export { WorkflowExpression };
16
20
  export declare const API_KEY_APPLICATION_HEADER = "abyss-console-api-key-application";
17
21
  export declare const ADMIN_TOKEN_HEADER = "abyss-admin-token";
18
22
  export declare const HEADER_WEBHOOK_SIGNATURE_NAME = "abyss-console-webhook-signature";
@@ -97,6 +101,7 @@ type AbyssConsoleCoreSDK = {
97
101
  };
98
102
  connection: {
99
103
  list: typeof workflowApi.listWorkflowStepConnections;
104
+ update: typeof workflowApi.updateWorkflowStepConnection;
100
105
  };
101
106
  execution: {
102
107
  paginate: typeof workflowApi.paginateWorkflowExecution;
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29
29
  return (mod && mod.__esModule) ? mod : { "default": mod };
30
30
  };
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.AbyssConsoleCore = exports.WEBHOOK_SIGNATURE_ALGORITHM = exports.HEADER_WEBHOOK_SIGNATURE_NAME = exports.ADMIN_TOKEN_HEADER = exports.API_KEY_APPLICATION_HEADER = void 0;
32
+ exports.AbyssConsoleCore = exports.WEBHOOK_SIGNATURE_ALGORITHM = exports.HEADER_WEBHOOK_SIGNATURE_NAME = exports.ADMIN_TOKEN_HEADER = exports.API_KEY_APPLICATION_HEADER = exports.WorkflowExpression = exports.GenericExpression = void 0;
33
33
  const axios_1 = __importDefault(require("axios"));
34
34
  const axios_retry_1 = __importStar(require("axios-retry"));
35
35
  const domainVerificationApi = __importStar(require("./api/domain-verification.api"));
@@ -46,6 +46,10 @@ const secretPublicApi = __importStar(require("./api/secret-public.api"));
46
46
  __exportStar(require("./utils/cidr.util"), exports);
47
47
  __exportStar(require("./types"), exports);
48
48
  __exportStar(require("./utils"), exports);
49
+ const GenericExpression = __importStar(require("./expressions"));
50
+ exports.GenericExpression = GenericExpression;
51
+ const WorkflowExpression = __importStar(require("./workflow-expressions"));
52
+ exports.WorkflowExpression = WorkflowExpression;
49
53
  const MONITOR_DEFAULT_BASE_URL = 'https://console-api.abyss-project.fr/api/';
50
54
  exports.API_KEY_APPLICATION_HEADER = 'abyss-console-api-key-application';
51
55
  exports.ADMIN_TOKEN_HEADER = 'abyss-admin-token';
@@ -91,6 +95,7 @@ class AbyssConsoleCore {
91
95
  },
92
96
  connection: {
93
97
  list: workflowApi.listWorkflowStepConnections,
98
+ update: workflowApi.updateWorkflowStepConnection,
94
99
  },
95
100
  execution: {
96
101
  paginate: workflowApi.paginateWorkflowExecution,
@@ -101,6 +101,16 @@ export interface IListWorkflowStepConnectionsParams {
101
101
  projectId: string;
102
102
  workflowId: string;
103
103
  }
104
+ export interface IUpdateWorkflowStepConnectionParams {
105
+ projectId: string;
106
+ workflowId: string;
107
+ connectionId: string;
108
+ }
109
+ export interface IUpdateWorkflowStepConnectionBody {
110
+ label?: string | null;
111
+ expression?: string | null;
112
+ order?: number;
113
+ }
104
114
  export interface IPaginateWorkflowExecutionParams {
105
115
  projectId: string;
106
116
  workflowId: string;
@@ -45,6 +45,10 @@ export interface IListWorkflowStepConnectionData {
45
45
  connections: IWorkflowStepConnection[];
46
46
  }
47
47
  export type IListWorkflowStepConnectionResponse = IResponse<IListWorkflowStepConnectionData>;
48
+ export interface IUpdateWorkflowStepConnectionData {
49
+ connection: IWorkflowStepConnection;
50
+ }
51
+ export type IUpdateWorkflowStepConnectionResponse = IResponse<IUpdateWorkflowStepConnectionData>;
48
52
  export type IPaginateWorkflowExecutionResponse = IResponse<BasePaginate<IWorkflowExecution>>;
49
53
  export interface IGetWorkflowExecutionData {
50
54
  workflowExecution: IWorkflowExecution;
@@ -72,7 +72,7 @@ const tokenizeExpression = (expression, context) => {
72
72
  }
73
73
  const path = expression.slice(pos + 2, endIdx).trim();
74
74
  if (path.includes('|')) {
75
- const [refPath, ...pipes] = path.split('|').map(s => s.trim());
75
+ const [refPath, ...pipes] = path.split('|').map((s) => s.trim());
76
76
  let value = (0, exports.getNestedValue)(context, refPath);
77
77
  for (const pipe of pipes) {
78
78
  value = applyPipeOperator(value, pipe);
@@ -174,11 +174,15 @@ const applyPipeOperator = (value, pipe) => {
174
174
  case 'endsWith':
175
175
  return strValue.endsWith(args || '');
176
176
  case 'isEmpty':
177
- return value === null || value === undefined || value === '' ||
178
- (Array.isArray(value) && value.length === 0);
177
+ return (value === null ||
178
+ value === undefined ||
179
+ value === '' ||
180
+ (Array.isArray(value) && value.length === 0));
179
181
  case 'isNotEmpty':
180
- return value !== null && value !== undefined && value !== '' &&
181
- (!Array.isArray(value) || value.length > 0);
182
+ return (value !== null &&
183
+ value !== undefined &&
184
+ value !== '' &&
185
+ (!Array.isArray(value) || value.length > 0));
182
186
  case 'length':
183
187
  return Array.isArray(value) ? value.length : strValue.length;
184
188
  case 'lower':
@@ -200,7 +204,7 @@ const evaluateTokens = (tokens) => {
200
204
  if (tokens.length === 1) {
201
205
  return tokens[0].value;
202
206
  }
203
- const questionIdx = tokens.findIndex(t => t.type === 'operator' && t.value === '?');
207
+ const questionIdx = tokens.findIndex((t) => t.type === 'operator' && t.value === '?');
204
208
  if (questionIdx !== -1) {
205
209
  const colonIdx = tokens.findIndex((t, i) => i > questionIdx && t.type === 'operator' && t.value === ':');
206
210
  if (colonIdx !== -1) {
@@ -0,0 +1,2 @@
1
+ import type { BuiltInFunction } from '../types';
2
+ export declare const arrayFunctions: Record<string, BuiltInFunction>;
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.arrayFunctions = void 0;
4
+ exports.arrayFunctions = {
5
+ length: {
6
+ name: 'length',
7
+ description: 'Returns the length of a string or array',
8
+ category: 'array',
9
+ signature: 'length(value)',
10
+ args: [
11
+ {
12
+ name: 'value',
13
+ type: 'string|array',
14
+ required: true,
15
+ description: 'String or array',
16
+ },
17
+ ],
18
+ returnType: 'number',
19
+ examples: ['{{fn.length(trigger.items)}}'],
20
+ execute: (value) => {
21
+ if (typeof value === 'string') {
22
+ return value.length;
23
+ }
24
+ if (Array.isArray(value)) {
25
+ return value.length;
26
+ }
27
+ return 0;
28
+ },
29
+ },
30
+ first: {
31
+ name: 'first',
32
+ description: 'Returns the first element of an array',
33
+ category: 'array',
34
+ signature: 'first(arr)',
35
+ args: [
36
+ {
37
+ name: 'arr',
38
+ type: 'array',
39
+ required: true,
40
+ description: 'Input array',
41
+ },
42
+ ],
43
+ returnType: 'any',
44
+ examples: ['{{fn.first(trigger.items)}}'],
45
+ execute: (arr) => {
46
+ if (Array.isArray(arr) && arr.length > 0) {
47
+ return arr[0];
48
+ }
49
+ return undefined;
50
+ },
51
+ },
52
+ last: {
53
+ name: 'last',
54
+ description: 'Returns the last element of an array',
55
+ category: 'array',
56
+ signature: 'last(arr)',
57
+ args: [
58
+ {
59
+ name: 'arr',
60
+ type: 'array',
61
+ required: true,
62
+ description: 'Input array',
63
+ },
64
+ ],
65
+ returnType: 'any',
66
+ examples: ['{{fn.last(trigger.items)}}'],
67
+ execute: (arr) => {
68
+ if (Array.isArray(arr) && arr.length > 0) {
69
+ return arr[arr.length - 1];
70
+ }
71
+ return undefined;
72
+ },
73
+ },
74
+ join: {
75
+ name: 'join',
76
+ description: 'Joins array elements into a string',
77
+ category: 'array',
78
+ signature: 'join(arr, separator)',
79
+ args: [
80
+ {
81
+ name: 'arr',
82
+ type: 'array',
83
+ required: true,
84
+ description: 'Input array',
85
+ },
86
+ {
87
+ name: 'separator',
88
+ type: 'string',
89
+ required: false,
90
+ description: 'Separator (default: ",")',
91
+ },
92
+ ],
93
+ returnType: 'string',
94
+ examples: ['{{fn.join(trigger.tags, ", ")}}'],
95
+ execute: (arr, separator) => {
96
+ if (!Array.isArray(arr)) {
97
+ return '';
98
+ }
99
+ return arr.join(separator !== undefined ? String(separator) : ',');
100
+ },
101
+ },
102
+ slice: {
103
+ name: 'slice',
104
+ description: 'Extracts a section of an array',
105
+ category: 'array',
106
+ signature: 'slice(arr, start, end?)',
107
+ args: [
108
+ {
109
+ name: 'arr',
110
+ type: 'array',
111
+ required: true,
112
+ description: 'Input array',
113
+ },
114
+ {
115
+ name: 'start',
116
+ type: 'number',
117
+ required: true,
118
+ description: 'Start index',
119
+ },
120
+ {
121
+ name: 'end',
122
+ type: 'number',
123
+ required: false,
124
+ description: 'End index',
125
+ },
126
+ ],
127
+ returnType: 'array',
128
+ examples: ['{{fn.slice(trigger.items, 0, 3)}}'],
129
+ execute: (arr, start, end) => {
130
+ if (!Array.isArray(arr)) {
131
+ return [];
132
+ }
133
+ return arr.slice(Number(start), end !== undefined ? Number(end) : undefined);
134
+ },
135
+ },
136
+ includes: {
137
+ name: 'includes',
138
+ description: 'Checks if an array includes a value',
139
+ category: 'array',
140
+ signature: 'includes(arr, value)',
141
+ args: [
142
+ {
143
+ name: 'arr',
144
+ type: 'array',
145
+ required: true,
146
+ description: 'Input array',
147
+ },
148
+ {
149
+ name: 'value',
150
+ type: 'any',
151
+ required: true,
152
+ description: 'Value to search for',
153
+ },
154
+ ],
155
+ returnType: 'boolean',
156
+ examples: ['{{fn.includes(trigger.roles, "admin")}}'],
157
+ execute: (arr, value) => {
158
+ if (!Array.isArray(arr)) {
159
+ return false;
160
+ }
161
+ return arr.includes(value);
162
+ },
163
+ },
164
+ reverse: {
165
+ name: 'reverse',
166
+ description: 'Reverses the order of array elements',
167
+ category: 'array',
168
+ signature: 'reverse(arr)',
169
+ args: [
170
+ {
171
+ name: 'arr',
172
+ type: 'array',
173
+ required: true,
174
+ description: 'Input array',
175
+ },
176
+ ],
177
+ returnType: 'array',
178
+ examples: ['{{fn.reverse(trigger.items)}}'],
179
+ execute: (arr) => {
180
+ if (!Array.isArray(arr)) {
181
+ return [];
182
+ }
183
+ return [...arr].reverse();
184
+ },
185
+ },
186
+ unique: {
187
+ name: 'unique',
188
+ description: 'Returns unique elements from an array',
189
+ category: 'array',
190
+ signature: 'unique(arr)',
191
+ args: [
192
+ {
193
+ name: 'arr',
194
+ type: 'array',
195
+ required: true,
196
+ description: 'Input array',
197
+ },
198
+ ],
199
+ returnType: 'array',
200
+ examples: ['{{fn.unique(trigger.tags)}}'],
201
+ execute: (arr) => {
202
+ if (!Array.isArray(arr)) {
203
+ return [];
204
+ }
205
+ return Array.from(new Set(arr));
206
+ },
207
+ },
208
+ flatten: {
209
+ name: 'flatten',
210
+ description: 'Flattens a nested array by one level',
211
+ category: 'array',
212
+ signature: 'flatten(arr)',
213
+ args: [
214
+ {
215
+ name: 'arr',
216
+ type: 'array',
217
+ required: true,
218
+ description: 'Input array',
219
+ },
220
+ ],
221
+ returnType: 'array',
222
+ examples: ['{{fn.flatten([[1, 2], [3, 4]])}}'],
223
+ execute: (arr) => {
224
+ if (!Array.isArray(arr)) {
225
+ return [];
226
+ }
227
+ return arr.flat();
228
+ },
229
+ },
230
+ compact: {
231
+ name: 'compact',
232
+ description: 'Removes falsy values from an array',
233
+ category: 'array',
234
+ signature: 'compact(arr)',
235
+ args: [
236
+ {
237
+ name: 'arr',
238
+ type: 'array',
239
+ required: true,
240
+ description: 'Input array',
241
+ },
242
+ ],
243
+ returnType: 'array',
244
+ examples: ['{{fn.compact([0, 1, false, 2, "", 3])}}'],
245
+ execute: (arr) => {
246
+ if (!Array.isArray(arr)) {
247
+ return [];
248
+ }
249
+ return arr.filter(Boolean);
250
+ },
251
+ },
252
+ };