@aiteza/n8n-nodes-aiteza 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class AitezaProgress implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AitezaProgress = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ // ---------------------------------------------------------------------------
6
+ // Helpers for reading the AITEZA Trigger's webhook body
7
+ // ---------------------------------------------------------------------------
8
+ function isAitezaTriggerType(type) {
9
+ return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
10
+ }
11
+ function findAitezaTriggerParentName(ef) {
12
+ try {
13
+ const parents = ef.getParentNodes(ef.getNode().name);
14
+ return parents.find((p) => isAitezaTriggerType(p.type) && !p.disabled)?.name;
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ }
20
+ /**
21
+ * Look up a value first on the current input item, then on the original
22
+ * AITEZA Trigger output (so intermediate Set/IF/SplitOut nodes that strip
23
+ * fields don't break callbacks).
24
+ */
25
+ function readFromTrigger(ef, itemIndex, picker) {
26
+ try {
27
+ const items = ef.getInputData();
28
+ const direct = picker(items[itemIndex]?.json);
29
+ if (direct !== undefined && direct !== null && direct !== '')
30
+ return direct;
31
+ }
32
+ catch {
33
+ // ignore
34
+ }
35
+ const triggerName = findAitezaTriggerParentName(ef);
36
+ if (!triggerName)
37
+ return undefined;
38
+ try {
39
+ const proxy = ef.getWorkflowDataProxy(itemIndex);
40
+ const triggerItems = proxy.$items(triggerName) ?? [];
41
+ for (const item of triggerItems) {
42
+ const value = picker(item?.json);
43
+ if (value !== undefined && value !== null && value !== '')
44
+ return value;
45
+ }
46
+ }
47
+ catch {
48
+ // ignore
49
+ }
50
+ return undefined;
51
+ }
52
+ function readTriggerBodyField(ef, itemIndex, field) {
53
+ return readFromTrigger(ef, itemIndex, (json) => {
54
+ const body = json?.body ?? {};
55
+ const fromBody = body[field];
56
+ if (fromBody !== undefined && fromBody !== null && fromBody !== '') {
57
+ return String(fromBody);
58
+ }
59
+ // Also allow callers to put the value at the top level of an item
60
+ // (e.g. after a Set node).
61
+ const fromTop = json?.[field];
62
+ if (fromTop !== undefined && fromTop !== null && fromTop !== '') {
63
+ return String(fromTop);
64
+ }
65
+ return undefined;
66
+ });
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // Node
70
+ // ---------------------------------------------------------------------------
71
+ class AitezaProgress {
72
+ description = {
73
+ displayName: 'AITEZA Progress',
74
+ name: 'aitezaProgress',
75
+ icon: 'file:aiteza.svg',
76
+ group: ['transform'],
77
+ version: 1,
78
+ subtitle: '={{$parameter["step"]}}',
79
+ description: 'Reports workflow progress back to the AITEZA backend that triggered ' +
80
+ 'this workflow. Reads callbackUrl / executionId / callbackToken from ' +
81
+ 'the upstream AITEZA Trigger webhook body and POSTs to ' +
82
+ '{callbackUrl}/api/internal/workflow/{executionId}/progress.',
83
+ defaults: { name: 'AITEZA Progress' },
84
+ inputs: ['main'],
85
+ outputs: ['main'],
86
+ credentials: [],
87
+ properties: [
88
+ {
89
+ displayName: 'Step',
90
+ name: 'step',
91
+ type: 'string',
92
+ default: '',
93
+ required: true,
94
+ placeholder: 'Fetching files',
95
+ description: 'Short, human-readable label for the current step',
96
+ },
97
+ {
98
+ displayName: 'Step Index',
99
+ name: 'stepIndex',
100
+ type: 'number',
101
+ default: 1,
102
+ typeOptions: { minValue: 0 },
103
+ description: '1-based index of the current step',
104
+ },
105
+ {
106
+ displayName: 'Total Steps',
107
+ name: 'totalSteps',
108
+ type: 'number',
109
+ default: 1,
110
+ typeOptions: { minValue: 0 },
111
+ description: 'Total number of steps in this workflow',
112
+ },
113
+ {
114
+ displayName: 'Detail',
115
+ name: 'detail',
116
+ type: 'string',
117
+ default: '',
118
+ typeOptions: { rows: 2 },
119
+ description: 'Optional longer description of what is happening right now',
120
+ },
121
+ {
122
+ displayName: 'Additional Fields (JSON)',
123
+ name: 'additionalFields',
124
+ type: 'json',
125
+ default: '',
126
+ placeholder: '{ "percent": 42 }',
127
+ description: 'Optional JSON object that will be merged into the progress payload (e.g. percent, items processed, etc.)',
128
+ },
129
+ {
130
+ displayName: 'Options',
131
+ name: 'options',
132
+ type: 'collection',
133
+ placeholder: 'Add Option',
134
+ default: {},
135
+ options: [
136
+ {
137
+ displayName: 'Callback URL Override',
138
+ name: 'callbackUrlOverride',
139
+ type: 'string',
140
+ default: '',
141
+ description: 'Override the callback URL from the AITEZA Trigger. Without trailing slash.',
142
+ },
143
+ {
144
+ displayName: 'Execution ID Override',
145
+ name: 'executionIdOverride',
146
+ type: 'string',
147
+ default: '',
148
+ description: 'Override the execution ID from the AITEZA Trigger',
149
+ },
150
+ {
151
+ displayName: 'Callback Token Override',
152
+ name: 'callbackTokenOverride',
153
+ type: 'string',
154
+ typeOptions: { password: true },
155
+ default: '',
156
+ description: 'Override the callback token from the AITEZA Trigger',
157
+ },
158
+ {
159
+ displayName: 'Timeout (ms)',
160
+ name: 'timeout',
161
+ type: 'number',
162
+ default: 5000,
163
+ typeOptions: { minValue: 100 },
164
+ },
165
+ {
166
+ displayName: 'Fail Workflow on Error',
167
+ name: 'failOnError',
168
+ type: 'boolean',
169
+ default: false,
170
+ description: 'Whether to fail the workflow if the progress callback fails. Defaults to false so progress reports never break the main flow.',
171
+ },
172
+ ],
173
+ },
174
+ ],
175
+ };
176
+ async execute() {
177
+ const items = this.getInputData();
178
+ const returnData = [];
179
+ for (let i = 0; i < items.length; i++) {
180
+ const step = this.getNodeParameter('step', i);
181
+ const stepIndex = this.getNodeParameter('stepIndex', i, 0);
182
+ const totalSteps = this.getNodeParameter('totalSteps', i, 0);
183
+ const detail = this.getNodeParameter('detail', i, '');
184
+ const additionalFieldsRaw = this.getNodeParameter('additionalFields', i, '');
185
+ const options = this.getNodeParameter('options', i, {});
186
+ const callbackUrlOverride = options.callbackUrlOverride || '';
187
+ const executionIdOverride = options.executionIdOverride || '';
188
+ const callbackTokenOverride = options.callbackTokenOverride || '';
189
+ const timeout = options.timeout ?? 5000;
190
+ const failOnError = options.failOnError ?? false;
191
+ const callbackUrl = (callbackUrlOverride || readTriggerBodyField(this, i, 'callbackUrl') || '').replace(/\/+$/, '');
192
+ const executionId = executionIdOverride || readTriggerBodyField(this, i, 'executionId') || '';
193
+ const callbackToken = callbackTokenOverride || readTriggerBodyField(this, i, 'callbackToken') || '';
194
+ // Build payload
195
+ const payload = { step };
196
+ if (Number.isFinite(stepIndex) && stepIndex > 0)
197
+ payload.stepIndex = stepIndex;
198
+ if (Number.isFinite(totalSteps) && totalSteps > 0)
199
+ payload.totalSteps = totalSteps;
200
+ if (detail)
201
+ payload.detail = detail;
202
+ if (additionalFieldsRaw) {
203
+ let parsed;
204
+ if (typeof additionalFieldsRaw === 'string') {
205
+ try {
206
+ parsed = JSON.parse(additionalFieldsRaw);
207
+ }
208
+ catch (err) {
209
+ if (failOnError) {
210
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `"Additional Fields" is not valid JSON: ${err.message}`, { itemIndex: i });
211
+ }
212
+ }
213
+ }
214
+ else if (typeof additionalFieldsRaw === 'object') {
215
+ parsed = additionalFieldsRaw;
216
+ }
217
+ if (parsed && typeof parsed === 'object')
218
+ Object.assign(payload, parsed);
219
+ }
220
+ const reportMeta = {
221
+ sent: false,
222
+ callbackUrl,
223
+ executionId,
224
+ payload,
225
+ };
226
+ if (!callbackUrl || !executionId) {
227
+ reportMeta.skipped = true;
228
+ reportMeta.reason = !callbackUrl
229
+ ? 'No callbackUrl on AITEZA Trigger body'
230
+ : 'No executionId on AITEZA Trigger body';
231
+ if (failOnError) {
232
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot send progress: ${reportMeta.reason}`, { itemIndex: i });
233
+ }
234
+ returnData.push({
235
+ json: { ...items[i].json, _progressReport: reportMeta },
236
+ pairedItem: { item: i },
237
+ });
238
+ continue;
239
+ }
240
+ const requestOptions = {
241
+ method: 'POST',
242
+ url: `${callbackUrl}/api/internal/workflow/${encodeURIComponent(executionId)}/progress`,
243
+ headers: {
244
+ 'Content-Type': 'application/json',
245
+ ...(callbackToken ? { 'X-Callback-Token': callbackToken } : {}),
246
+ },
247
+ body: payload,
248
+ json: true,
249
+ timeout,
250
+ };
251
+ try {
252
+ await this.helpers.httpRequest(requestOptions);
253
+ reportMeta.sent = true;
254
+ }
255
+ catch (error) {
256
+ reportMeta.sent = false;
257
+ reportMeta.error =
258
+ error?.message ?? error?.response?.statusText ?? 'Unknown error';
259
+ if (failOnError) {
260
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Progress callback failed: ${reportMeta.error}`, { itemIndex: i });
261
+ }
262
+ }
263
+ returnData.push({
264
+ json: { ...items[i].json, _progressReport: reportMeta },
265
+ pairedItem: { item: i },
266
+ });
267
+ }
268
+ return [returnData];
269
+ }
270
+ }
271
+ exports.AitezaProgress = AitezaProgress;
@@ -9,7 +9,8 @@ class AitezaTrigger {
9
9
  group: ['trigger'],
10
10
  version: 1,
11
11
  description: 'Starts the workflow when an AITEZA event is received via webhook. ' +
12
- 'Passes the caller\'s auth token (and optionally the AITEZA Base URL) to all downstream AITEZA nodes so they act on behalf of the triggering user.',
12
+ 'Forwards the calling user\'s bearer token (from body.user.bearerToken) to downstream AITEZA nodes ' +
13
+ 'so they act on behalf of the triggering user. Falls back to the request\'s Authorization header if no user token is present.',
13
14
  defaults: { name: 'AITEZA Trigger' },
14
15
  inputs: [],
15
16
  outputs: ['main'],
@@ -47,21 +48,18 @@ class AitezaTrigger {
47
48
  {
48
49
  name: 'Immediately',
49
50
  value: 'onReceived',
50
- description: 'Respond immediately with 200 OK as soon as the webhook is received',
51
+ description: 'Respond immediately with 200 OK as soon as the webhook is received. ' +
52
+ 'Use the "AITEZA Workflow Result" node at the end of your workflow to send results back.',
51
53
  },
52
54
  {
53
55
  name: 'When Last Node Finishes',
54
56
  value: 'lastNode',
55
57
  description: 'Respond with data from the last node in the workflow',
56
58
  },
57
- {
58
- name: 'Using \'Respond to Webhook\' Node',
59
- value: 'responseNode',
60
- description: 'Respond using the data from a downstream "Respond to Webhook" node',
61
- },
62
59
  ],
63
- default: 'lastNode',
64
- description: 'When and how to respond to the webhook',
60
+ default: 'onReceived',
61
+ description: 'When and how to respond to the webhook. Use "Immediately" together with the ' +
62
+ '"AITEZA Workflow Result" node to send results asynchronously.',
65
63
  },
66
64
  {
67
65
  displayName: 'AITEZA Base URL',
@@ -77,8 +75,17 @@ class AitezaTrigger {
77
75
  const body = this.getBodyData();
78
76
  const headers = this.getHeaderData();
79
77
  const query = this.getQueryData();
78
+ // AITEZA places the calling user's bearer token inside the body (body.user.bearerToken)
79
+ // so that downstream nodes can act on behalf of the user. The request's own
80
+ // Authorization header is typically the service-account token used by AITEZA to
81
+ // call the webhook itself – not what we want for delegated calls.
82
+ const user = body?.user ?? {};
83
+ const userBearer = user.bearerToken ?? '';
80
84
  const authHeader = headers.authorization || headers.Authorization || '';
81
- const authToken = authHeader.replace(/^Bearer\s+/i, '');
85
+ const headerToken = authHeader.replace(/^Bearer\s+/i, '');
86
+ // Prefer the user-context token. Fall back to the header token (e.g. when the
87
+ // webhook is called by something other than AITEZA).
88
+ const authToken = userBearer || headerToken;
82
89
  const baseUrl = this.getNodeParameter('baseUrl', '').replace(/\/+$/, '');
83
90
  const outputData = {
84
91
  body,
@@ -87,6 +94,10 @@ class AitezaTrigger {
87
94
  };
88
95
  if (authToken)
89
96
  outputData._authToken = authToken;
97
+ // Expose the service-account token separately in case a workflow explicitly
98
+ // needs to call AITEZA with elevated/service privileges.
99
+ if (headerToken && headerToken !== authToken)
100
+ outputData._serviceAuthToken = headerToken;
90
101
  if (baseUrl)
91
102
  outputData._baseUrl = baseUrl;
92
103
  return {
@@ -0,0 +1,5 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class AitezaWorkflowResult implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AitezaWorkflowResult = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ // ---------------------------------------------------------------------------
6
+ // Helpers for reading the AITEZA Trigger's webhook body
7
+ // ---------------------------------------------------------------------------
8
+ function isAitezaTriggerType(type) {
9
+ return typeof type === 'string' && /(^|\.)aitezaTrigger$/i.test(type);
10
+ }
11
+ function findAitezaTriggerParentName(ef) {
12
+ try {
13
+ const parents = ef.getParentNodes(ef.getNode().name);
14
+ return parents.find((p) => isAitezaTriggerType(p.type) && !p.disabled)?.name;
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ }
20
+ /**
21
+ * Look up a value first on the current input item, then on the original
22
+ * AITEZA Trigger output (so intermediate Set/IF/SplitOut nodes that strip
23
+ * fields don't break callbacks).
24
+ */
25
+ function readFromTrigger(ef, itemIndex, picker) {
26
+ try {
27
+ const items = ef.getInputData();
28
+ const direct = picker(items[itemIndex]?.json);
29
+ if (direct !== undefined && direct !== null && direct !== '')
30
+ return direct;
31
+ }
32
+ catch {
33
+ // ignore
34
+ }
35
+ const triggerName = findAitezaTriggerParentName(ef);
36
+ if (!triggerName)
37
+ return undefined;
38
+ try {
39
+ const proxy = ef.getWorkflowDataProxy(itemIndex);
40
+ const triggerItems = proxy.$items(triggerName) ?? [];
41
+ for (const item of triggerItems) {
42
+ const value = picker(item?.json);
43
+ if (value !== undefined && value !== null && value !== '')
44
+ return value;
45
+ }
46
+ }
47
+ catch {
48
+ // ignore
49
+ }
50
+ return undefined;
51
+ }
52
+ function readTriggerBodyField(ef, itemIndex, field) {
53
+ return readFromTrigger(ef, itemIndex, (json) => {
54
+ const body = json?.body ?? {};
55
+ const fromBody = body[field];
56
+ if (fromBody !== undefined && fromBody !== null && fromBody !== '') {
57
+ return String(fromBody);
58
+ }
59
+ const fromTop = json?.[field];
60
+ if (fromTop !== undefined && fromTop !== null && fromTop !== '') {
61
+ return String(fromTop);
62
+ }
63
+ return undefined;
64
+ });
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // Node
68
+ // ---------------------------------------------------------------------------
69
+ class AitezaWorkflowResult {
70
+ description = {
71
+ displayName: 'AITEZA Workflow Result',
72
+ name: 'aitezaWorkflowResult',
73
+ icon: 'file:aiteza.svg',
74
+ group: ['output'],
75
+ version: 1,
76
+ subtitle: 'Send result to AITEZA',
77
+ description: 'Sends the final workflow result back to the AITEZA backend. ' +
78
+ 'Reads callbackUrl / executionId / callbackToken from the upstream ' +
79
+ 'AITEZA Trigger webhook body and POSTs to ' +
80
+ '{callbackUrl}/api/internal/workflow/{executionId}/result. ' +
81
+ 'Use this instead of "Respond to Webhook" which is not compatible ' +
82
+ 'with custom trigger nodes.',
83
+ defaults: { name: 'AITEZA Workflow Result' },
84
+ inputs: ['main'],
85
+ outputs: ['main'],
86
+ credentials: [],
87
+ properties: [
88
+ {
89
+ displayName: 'Result Data',
90
+ name: 'resultData',
91
+ type: 'json',
92
+ default: '',
93
+ placeholder: '={{ $json }}',
94
+ description: 'The result payload to send back to AITEZA. Can be a JSON object or an expression that resolves to one. If empty, the entire input item JSON is sent.',
95
+ },
96
+ {
97
+ displayName: 'Status',
98
+ name: 'status',
99
+ type: 'options',
100
+ options: [
101
+ {
102
+ name: 'Success',
103
+ value: 'success',
104
+ },
105
+ {
106
+ name: 'Error',
107
+ value: 'error',
108
+ },
109
+ ],
110
+ default: 'success',
111
+ description: 'The result status to report back to AITEZA',
112
+ },
113
+ {
114
+ displayName: 'Message',
115
+ name: 'message',
116
+ type: 'string',
117
+ default: '',
118
+ placeholder: 'Workflow completed successfully',
119
+ description: 'Optional human-readable message to include with the result',
120
+ },
121
+ {
122
+ displayName: 'Options',
123
+ name: 'options',
124
+ type: 'collection',
125
+ placeholder: 'Add Option',
126
+ default: {},
127
+ options: [
128
+ {
129
+ displayName: 'Callback URL Override',
130
+ name: 'callbackUrlOverride',
131
+ type: 'string',
132
+ default: '',
133
+ description: 'Override the callback URL from the AITEZA Trigger. Without trailing slash.',
134
+ },
135
+ {
136
+ displayName: 'Execution ID Override',
137
+ name: 'executionIdOverride',
138
+ type: 'string',
139
+ default: '',
140
+ description: 'Override the execution ID from the AITEZA Trigger',
141
+ },
142
+ {
143
+ displayName: 'Callback Token Override',
144
+ name: 'callbackTokenOverride',
145
+ type: 'string',
146
+ typeOptions: { password: true },
147
+ default: '',
148
+ description: 'Override the callback token from the AITEZA Trigger',
149
+ },
150
+ {
151
+ displayName: 'Timeout (ms)',
152
+ name: 'timeout',
153
+ type: 'number',
154
+ default: 10000,
155
+ typeOptions: { minValue: 100 },
156
+ },
157
+ {
158
+ displayName: 'Fail Workflow on Error',
159
+ name: 'failOnError',
160
+ type: 'boolean',
161
+ default: true,
162
+ description: 'Whether to fail the workflow if the result callback fails. Defaults to true since the result is typically critical.',
163
+ },
164
+ ],
165
+ },
166
+ ],
167
+ };
168
+ async execute() {
169
+ const items = this.getInputData();
170
+ const returnData = [];
171
+ for (let i = 0; i < items.length; i++) {
172
+ const resultDataRaw = this.getNodeParameter('resultData', i, '');
173
+ const status = this.getNodeParameter('status', i, 'success');
174
+ const message = this.getNodeParameter('message', i, '');
175
+ const options = this.getNodeParameter('options', i, {});
176
+ const callbackUrlOverride = options.callbackUrlOverride || '';
177
+ const executionIdOverride = options.executionIdOverride || '';
178
+ const callbackTokenOverride = options.callbackTokenOverride || '';
179
+ const timeout = options.timeout ?? 10000;
180
+ const failOnError = options.failOnError ?? true;
181
+ const callbackUrl = (callbackUrlOverride || readTriggerBodyField(this, i, 'callbackUrl') || '').replace(/\/+$/, '');
182
+ const executionId = executionIdOverride || readTriggerBodyField(this, i, 'executionId') || '';
183
+ const callbackToken = callbackTokenOverride || readTriggerBodyField(this, i, 'callbackToken') || '';
184
+ // Build result payload
185
+ let resultPayload;
186
+ if (!resultDataRaw || (typeof resultDataRaw === 'string' && resultDataRaw.trim() === '')) {
187
+ // Default: send the entire input item JSON as the result data
188
+ resultPayload = { ...items[i].json };
189
+ }
190
+ else if (typeof resultDataRaw === 'string') {
191
+ try {
192
+ resultPayload = JSON.parse(resultDataRaw);
193
+ }
194
+ catch (err) {
195
+ if (failOnError) {
196
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `"Result Data" is not valid JSON: ${err.message}`, { itemIndex: i });
197
+ }
198
+ resultPayload = { raw: resultDataRaw };
199
+ }
200
+ }
201
+ else {
202
+ resultPayload = resultDataRaw;
203
+ }
204
+ const payload = {
205
+ status,
206
+ data: resultPayload,
207
+ };
208
+ if (message)
209
+ payload.message = message;
210
+ const reportMeta = {
211
+ sent: false,
212
+ callbackUrl,
213
+ executionId,
214
+ payload,
215
+ };
216
+ if (!callbackUrl || !executionId) {
217
+ reportMeta.skipped = true;
218
+ reportMeta.reason = !callbackUrl
219
+ ? 'No callbackUrl on AITEZA Trigger body'
220
+ : 'No executionId on AITEZA Trigger body';
221
+ if (failOnError) {
222
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Cannot send workflow result: ${reportMeta.reason}`, { itemIndex: i });
223
+ }
224
+ returnData.push({
225
+ json: { ...items[i].json, _workflowResult: reportMeta },
226
+ pairedItem: { item: i },
227
+ });
228
+ continue;
229
+ }
230
+ const requestOptions = {
231
+ method: 'POST',
232
+ url: `${callbackUrl}/api/internal/workflow/${encodeURIComponent(executionId)}/result`,
233
+ headers: {
234
+ 'Content-Type': 'application/json',
235
+ ...(callbackToken ? { 'X-Callback-Token': callbackToken } : {}),
236
+ },
237
+ body: payload,
238
+ json: true,
239
+ timeout,
240
+ };
241
+ try {
242
+ await this.helpers.httpRequest(requestOptions);
243
+ reportMeta.sent = true;
244
+ }
245
+ catch (error) {
246
+ reportMeta.sent = false;
247
+ reportMeta.error =
248
+ error?.message ?? error?.response?.statusText ?? 'Unknown error';
249
+ if (failOnError) {
250
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Workflow result callback failed: ${reportMeta.error}`, { itemIndex: i });
251
+ }
252
+ }
253
+ returnData.push({
254
+ json: { ...items[i].json, _workflowResult: reportMeta },
255
+ pairedItem: { item: i },
256
+ });
257
+ }
258
+ return [returnData];
259
+ }
260
+ }
261
+ exports.AitezaWorkflowResult = AitezaWorkflowResult;
@@ -5,6 +5,12 @@ export interface AitezaAuthContext {
5
5
  }
6
6
  export declare function getUpstreamAuthToken(ef: IExecuteFunctions): string | undefined;
7
7
  export declare function getUpstreamBaseUrl(ef: IExecuteFunctions): string | undefined;
8
+ /**
9
+ * Returns true when the token is a JWT with an `exp` claim that is in the past
10
+ * (with a small skew). Returns false when the token is not a parsable JWT or
11
+ * has no `exp` (we assume valid in that case – the server is the source of truth).
12
+ */
13
+ export declare function isJwtExpired(token: string | undefined, skewSeconds?: number): boolean;
8
14
  export declare function aitezaApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject | IDataObject[], qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<any>;
9
15
  export declare function aitezaApiRequestFullResponse(this: IExecuteFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject, extraOpts?: Partial<IHttpRequestOptions>, authCtx?: AitezaAuthContext): Promise<{
10
16
  body: any;