@onify/flow-extensions 2.0.0 → 4.0.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ Changelog
2
+ =========
3
+
4
+ # 4.0.0
5
+
6
+ - make sure sub-process extensions are only triggered once. Sub process activities unintentionally trigger formatting since events bubble
7
+ - add script element type to registered scripts
8
+ - restructure and split up index.js to separate files
9
+ - abide to new lint rules
10
+
11
+ # 3.0.0
12
+
13
+ - Support activity script and expression execution listeners
14
+ - Remove default behaviour of saving activity output even if output is not declared. Output **was** saved to `environment.output._<Activity ID>` if no result variable or output was declared. The reason was that there was no easy way of getting output from SubProcess or a multi-instance task. Now SubProcess output can be defined and multi-instance output can be solved with execution listener
15
+
16
+ # 2.0.0
17
+
18
+ - fix resume extension when recovered mid formatting
19
+ - drop node 12 support
20
+
21
+ # 1.1.0
22
+
23
+ - allow ISO8601 repeating interval and duration in timeCycle
24
+
25
+ # 1.0.0
26
+
27
+ - proper routing keys for end formatting
28
+
29
+ # 0.1.0
30
+
31
+ - catch errors when resolving service expression
32
+ - test with bpmn-engine@14
33
+
34
+ # 0.0.2
35
+
36
+ - allow expressions in timeCycle
37
+ - test with bpmn-engine@13
38
+ - error message changed in node 16
package/dist/index.js CHANGED
@@ -5,354 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.extendFn = extendFn;
7
7
  exports.extensions = extensions;
8
- var _cronParser = _interopRequireDefault(require("cron-parser"));
9
- var _IOProperties = _interopRequireDefault(require("./src/IOProperties"));
10
- var _IOForm = _interopRequireDefault(require("./src/IOForm"));
11
- var _Connector = _interopRequireDefault(require("./src/Connector"));
12
- var _ServiceExpression = _interopRequireDefault(require("./src/ServiceExpression"));
13
- var _IO = require("./src/IO");
14
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
- const iso8601cycle = /^\s*(R\d+\/)?P\w+/i;
16
- class FormatError extends Error {
17
- constructor(elementId, err) {
18
- super(`<${elementId}> ${err.message}`);
19
- this.code = 'EFLOW_FORMAT';
20
- this.output = {
21
- statusCode: 500
22
- };
23
- }
24
- }
8
+ var _OnifyProcessExtensions = require("./src/OnifyProcessExtensions.js");
9
+ var _OnifyElementExtensions = require("./src/OnifyElementExtensions.js");
25
10
  function extensions(element, context) {
26
- if (element.type === 'bpmn:Process') return new OnifyProcessExtensions(element, context);
27
- return new OnifyElementExtensions(element, context);
28
- }
29
- class OnifyProcessExtensions {
30
- constructor(bp, context) {
31
- this.process = bp;
32
- this.context = context;
33
- this.extensions = getExtensions(bp, context);
34
- this._activate();
35
- }
36
- _activate() {
37
- const bp = this.process;
38
- bp.on('process.enter', elementApi => {
39
- try {
40
- const result = this._onEnter(elementApi);
41
- const environment = elementApi.environment;
42
- environment.assignVariables(result);
43
- } catch (err) {
44
- elementApi.broker.publish('event', 'process.error', {
45
- ...elementApi.content,
46
- error: new FormatError(bp.id, err)
47
- }, {
48
- mandatory: true,
49
- type: 'error'
50
- });
51
- }
52
- }, {
53
- consumerTag: '_onify-extension-on-enter'
54
- });
55
- }
56
- _onEnter(elementApi) {
57
- const {
58
- format,
59
- properties
60
- } = this.extensions;
61
- const result = {};
62
- Object.assign(result, format.resolve(elementApi));
63
- Object.assign(elementApi.content, result);
64
- if (properties) {
65
- Object.assign(result, properties.resolve(elementApi));
66
- Object.assign(elementApi.content, result);
67
- }
68
- return result;
69
- }
70
- }
71
- class OnifyElementExtensions {
72
- constructor(activity, context) {
73
- this.activity = activity;
74
- this.context = context;
75
- const {
76
- Service
77
- } = this.extensions = getExtensions(activity, context);
78
- if (Service) {
79
- activity.behaviour.Service = Service;
80
- }
81
- }
82
- activate(message) {
83
- const activity = this.activity;
84
- const broker = activity.broker;
85
- const formatQ = activity.broker.getQueue('format-run-q');
86
- if (message.fields.redelivered && message.fields.routingKey === 'run.start') {
87
- activity.on('start', elementApi => {
88
- this._formatOnEnter(broker, formatQ, elementApi);
89
- }, {
90
- consumerTag: '_onify-extension-on-enter'
91
- });
92
- } else {
93
- activity.on('enter', elementApi => {
94
- this._formatOnEnter(broker, formatQ, elementApi);
95
- }, {
96
- consumerTag: '_onify-extension-on-enter'
97
- });
98
- }
99
- activity.on('activity.execution.completed', async elementApi => {
100
- formatQ.queueMessage({
101
- routingKey: 'run.end.format'
102
- }, {
103
- endRoutingKey: 'run.end.complete'
104
- }, {
105
- persistent: false
106
- });
107
- try {
108
- var format = await this._onExecuted(elementApi);
109
- } catch (err) {
110
- return broker.publish('format', 'run.end.error', {
111
- error: err
112
- }, {
113
- persistent: false
114
- });
115
- }
116
- broker.publish('format', 'run.end.complete', {
117
- ...format
118
- }, {
119
- persistent: false
120
- });
121
- }, {
122
- consumerTag: '_onify-extension-on-executed'
123
- });
124
- }
125
- deactivate() {
126
- const broker = this.activity.broker;
127
- broker.cancel('_onify-extension-on-enter');
128
- broker.cancel('_onify-extension-on-executed');
129
- }
130
- async _formatOnEnter(broker, formatQ, elementApi) {
131
- formatQ.queueMessage({
132
- routingKey: 'run.enter.format'
133
- }, {
134
- endRoutingKey: 'run.enter.complete'
135
- }, {
136
- persistent: false
137
- });
138
- try {
139
- var format = await this._onEnter(elementApi);
140
- } catch (err) {
141
- return broker.publish('format', 'run.enter.error', {
142
- error: err
143
- }, {
144
- persistent: false
145
- });
146
- }
147
- broker.publish('format', 'run.enter.complete', format, {
148
- persistent: false
149
- });
150
- }
151
- async _onEnter(elementApi) {
152
- const {
153
- format,
154
- properties,
155
- io,
156
- form
157
- } = this.extensions;
158
- const result = {};
159
- Object.assign(result, format.resolve(elementApi));
160
- Object.assign(elementApi.content, result);
161
- if (properties) {
162
- Object.assign(result, {
163
- properties: properties.resolve(elementApi)
164
- });
165
- Object.assign(elementApi.content, result);
166
- }
167
- if (io) {
168
- if (io.input?.length) {
169
- const input = await io.getInput(this.activity, elementApi);
170
- Object.assign(result, {
171
- input
172
- });
173
- Object.assign(elementApi.content, result);
174
- }
175
- if (io.output?.length) {
176
- result.assignToOutput = true;
177
- }
178
- }
179
- if (form) {
180
- Object.assign(result, {
181
- form: form.resolve(elementApi)
182
- });
183
- }
184
- return result;
185
- }
186
- async _onExecuted(elementApi) {
187
- const {
188
- properties,
189
- io
190
- } = this.extensions;
191
- const result = {};
192
- if (io?.output.length) {
193
- const output = await io.getOutput(this.activity, elementApi);
194
- Object.assign(result, {
195
- output
196
- });
197
- Object.assign(elementApi.content, {
198
- output
199
- });
200
- }
201
- if (properties) {
202
- const props = properties.resolve(elementApi);
203
- Object.assign(result, {
204
- properties: props
205
- });
206
- Object.assign(elementApi.content, {
207
- properties: props
208
- });
209
- }
210
- const {
211
- output,
212
- assignToOutput,
213
- resultVariable
214
- } = elementApi.content;
215
- if (output === undefined || output === null) return result;
216
- if (assignToOutput && typeof output === 'object') {
217
- Object.assign(this.activity.environment.output, output);
218
- } else if (resultVariable) {
219
- elementApi.environment.output[resultVariable] = output;
220
- }
221
- return result;
222
- }
223
- }
224
- function getExtensions(element, context) {
225
- const result = {
226
- format: element.type === 'bpmn:Process' ? new FormatProcess(element) : new FormatActivity(element)
227
- };
228
- const expression = element.behaviour.expression;
229
- if (expression) result.Service = _ServiceExpression.default;
230
- const extensions = element.behaviour.extensionElements?.values;
231
- if (!extensions) return result;
232
- for (const ext of extensions) {
233
- switch (ext.$type) {
234
- case 'camunda:Properties':
235
- if (ext.values?.length) result.properties = new _IOProperties.default(element, ext);
236
- break;
237
- case 'camunda:InputOutput':
238
- result.io = new _IO.InputOutput(element.id, ext, context);
239
- break;
240
- case 'camunda:FormData':
241
- if (ext.fields?.length) result.form = new _IOForm.default(element, ext);
242
- break;
243
- case 'camunda:Connector':
244
- {
245
- const {
246
- connectorId,
247
- inputOutput
248
- } = ext;
249
- const io = inputOutput && new _IO.InputOutput(`${element.id}/${ext.$type.toLowerCase()}`, inputOutput, context);
250
- result.Service = _Connector.default.bind(_Connector.default, connectorId, io);
251
- break;
252
- }
253
- }
254
- }
255
- return result;
256
- }
257
- class FormatActivity {
258
- constructor(activity) {
259
- this.activity = activity;
260
- this.resultVariable = activity.behaviour.resultVariable || '_' + activity.id;
261
- let timeCycles;
262
- if (activity.eventDefinitions) {
263
- for (const ed of activity.eventDefinitions.filter(e => e.type === 'bpmn:TimerEventDefinition')) {
264
- if (!('timeCycle' in ed)) continue;
265
- timeCycles = timeCycles || [];
266
- timeCycles.push(ed.timeCycle);
267
- }
268
- }
269
- this.timeCycles = timeCycles;
270
- }
271
- resolve(elementApi) {
272
- let user, groups, assigneeValue, description;
273
- const activity = this.activity;
274
- const {
275
- documentation,
276
- candidateUsers,
277
- candidateGroups,
278
- scheduledStart,
279
- assignee
280
- } = activity.behaviour;
281
- if (candidateUsers) user = resolveAndSplit(elementApi, candidateUsers);
282
- if (candidateGroups) groups = resolveAndSplit(elementApi, candidateGroups);
283
- if (assignee) assigneeValue = elementApi.resolveExpression(assignee);
284
- if (documentation) description = documentation[0]?.text;
285
- let expireAt;
286
- let timeCycles = this.timeCycles;
287
- if (timeCycles) {
288
- for (const cycle of timeCycles) {
289
- const cron = elementApi.resolveExpression(cycle);
290
- if (!cron || iso8601cycle.test(cron)) continue;
291
- const expireAtDt = _cronParser.default.parseExpression(cron).next().toDate();
292
- if (!expireAt || expireAtDt < expireAt) expireAt = expireAtDt;
293
- }
294
- }
295
- return {
296
- resultVariable: this.resultVariable,
297
- ...(scheduledStart && activity.parent.type === 'bpmn:Process' && {
298
- scheduledStart
299
- }),
300
- ...(user?.length && {
301
- candidateUsers: user
302
- }),
303
- ...(groups?.length && {
304
- candidateGroups: groups
305
- }),
306
- ...(!elementApi.content.description && description && {
307
- description: elementApi.resolveExpression(description)
308
- }),
309
- ...(expireAt && {
310
- expireAt
311
- }),
312
- ...(assigneeValue && {
313
- assignee: assigneeValue
314
- })
315
- };
316
- }
317
- }
318
- class FormatProcess {
319
- constructor(bp) {
320
- this.process = bp;
321
- }
322
- resolve(elementApi) {
323
- let user, groups, description;
324
- const bp = this.process;
325
- const {
326
- documentation,
327
- candidateStarterUsers,
328
- candidateStarterGroups
329
- } = bp.behaviour;
330
- if (candidateStarterUsers) user = resolveAndSplit(elementApi, candidateStarterUsers);
331
- if (candidateStarterGroups) groups = resolveAndSplit(elementApi, candidateStarterGroups);
332
- if (documentation) description = documentation[0]?.text;
333
- return {
334
- resultVariable: this.resultVariable,
335
- ...(user?.length && {
336
- candidateStarterUsers: user
337
- }),
338
- ...(groups?.length && {
339
- candidateStarterGroups: groups
340
- }),
341
- ...(!elementApi.content.description && description && {
342
- description: elementApi.resolveExpression(description)
343
- })
344
- };
345
- }
346
- }
347
- function resolveAndSplit(elementApi, str) {
348
- if (Array.isArray(str)) return str.filter(Boolean);
349
- if (typeof str !== 'string') return;
350
- const resolved = elementApi.resolveExpression(str);
351
- if (Array.isArray(resolved)) return resolved.filter(Boolean);
352
- if (typeof resolved !== 'string') return;
353
- return resolved.split(',').map(g => g.trim && g.trim().toLowerCase()).filter(Boolean);
11
+ if (element.type === 'bpmn:Process') return new _OnifyProcessExtensions.OnifyProcessExtensions(element, context);
12
+ return new _OnifyElementExtensions.OnifyElementExtensions(element, context);
354
13
  }
355
14
  function extendFn(behaviour, context) {
15
+ var _behaviour$extensionE;
356
16
  if (behaviour.$type === 'bpmn:StartEvent' && behaviour.eventDefinitions) {
357
17
  const timer = behaviour.eventDefinitions.find(({
358
18
  type,
@@ -362,11 +22,21 @@ function extendFn(behaviour, context) {
362
22
  scheduledStart: timer.behaviour.timeCycle
363
23
  });
364
24
  }
365
- if (!Array.isArray(behaviour.extensionElements?.values)) return;
366
- const inputOutput = behaviour.extensionElements.values.find(el => el.$type === 'camunda:InputOutput');
367
- const connector = behaviour.extensionElements.values.find(el => el.$type === 'camunda:Connector');
368
- if (inputOutput) registerIOScripts(behaviour.id, context, inputOutput.$type, inputOutput);
369
- if (connector) registerIOScripts(behaviour.id, context, connector.$type, connector.inputOutput);
25
+ if (!Array.isArray((_behaviour$extensionE = behaviour.extensionElements) === null || _behaviour$extensionE === void 0 ? void 0 : _behaviour$extensionE.values)) return;
26
+ let listener = 0;
27
+ for (const extension of behaviour.extensionElements.values) {
28
+ switch (extension.$type) {
29
+ case 'camunda:InputOutput':
30
+ registerIOScripts(behaviour.id, context, extension.$type, extension);
31
+ break;
32
+ case 'camunda:Connector':
33
+ registerIOScripts(behaviour.id, context, extension.$type, extension.inputOutput);
34
+ break;
35
+ case 'camunda:ExecutionListener':
36
+ registerListenerScript(behaviour.id, context, extension.$type, extension, listener++);
37
+ break;
38
+ }
39
+ }
370
40
  }
371
41
  function registerIOScripts(parentId, context, type, ioBehaviour) {
372
42
  if (!ioBehaviour) return;
@@ -381,10 +51,12 @@ function registerIOScripts(parentId, context, type, ioBehaviour) {
381
51
  } of inputParameters.concat(outputParameters)) {
382
52
  if (!definition) continue;
383
53
  if (definition.$type !== 'camunda:Script') continue;
384
- const filename = `${parentId}/${type}/${$type}/${name}`;
54
+ const ioType = `${type}/${$type}`;
55
+ const filename = `${parentId}/${ioType}/${name}`;
385
56
  context.addScript(filename, {
386
57
  id: filename,
387
58
  scriptFormat: definition.scriptFormat,
59
+ type: ioType,
388
60
  ...(definition.value && {
389
61
  body: definition.value
390
62
  }),
@@ -393,4 +65,23 @@ function registerIOScripts(parentId, context, type, ioBehaviour) {
393
65
  })
394
66
  });
395
67
  }
68
+ }
69
+ function registerListenerScript(parentId, context, type, listener, pos) {
70
+ const {
71
+ event,
72
+ script
73
+ } = listener;
74
+ if (!script) return;
75
+ const id = `${parentId}/${type}/${event}/${pos}`;
76
+ context.addScript(id, {
77
+ id,
78
+ scriptFormat: script.scriptFormat,
79
+ type,
80
+ ...(script.value && {
81
+ body: script.value
82
+ }),
83
+ ...(script.resource && {
84
+ resource: script.resource
85
+ })
86
+ });
396
87
  }
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = Connector;
7
- var _Errors = require("./Errors");
7
+ var _Errors = require("./Errors.js");
8
8
  function Connector(connectorId, io, activity, executionMessage) {
9
9
  if (!(this instanceof Connector)) return new Connector(connectorId, io, activity, executionMessage);
10
10
  this.connectorId = connectorId;
@@ -30,7 +30,7 @@ Connector.prototype.execute = async function execute(...args) {
30
30
  }
31
31
  async function formatCallback(serviceErr, result) {
32
32
  if (serviceErr) return callback(serviceErr);
33
- if (!io?.output.length) return callback(null, result);
33
+ if (!(io !== null && io !== void 0 && io.output.length)) return callback(null, result);
34
34
  try {
35
35
  const formattedResult = await io.getOutput(activity, {
36
36
  ...executionMessage,
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.NotImplemented = void 0;
6
+ exports.NotImplemented = exports.FormatError = void 0;
7
7
  class NotImplemented extends Error {
8
8
  constructor(serviceId) {
9
9
  super(`${serviceId} service function not found`);
@@ -13,4 +13,14 @@ class NotImplemented extends Error {
13
13
  };
14
14
  }
15
15
  }
16
- exports.NotImplemented = NotImplemented;
16
+ exports.NotImplemented = NotImplemented;
17
+ class FormatError extends Error {
18
+ constructor(elementId, err) {
19
+ super(`<${elementId}> ${err.message}`);
20
+ this.code = 'EFLOW_FORMAT';
21
+ this.output = {
22
+ statusCode: 500
23
+ };
24
+ }
25
+ }
26
+ exports.FormatError = FormatError;
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ class Listener {
8
+ constructor(activity, context, extension) {
9
+ this.activity = activity;
10
+ this.environment = this.activity.environment;
11
+ this.context = context;
12
+ this.extension = extension;
13
+ this.type = extension.$type;
14
+ this.event = extension.event;
15
+ }
16
+ _getScope(message, extend) {
17
+ const environment = this.environment;
18
+ const {
19
+ fields,
20
+ content,
21
+ properties
22
+ } = message;
23
+ const scope = {
24
+ ...extend,
25
+ type: this.type,
26
+ listener: {
27
+ event: this.event
28
+ },
29
+ fields,
30
+ content,
31
+ properties,
32
+ environment,
33
+ logger: this.activity.logger
34
+ };
35
+ const listenerFields = this._getFields(environment, scope);
36
+ if (listenerFields) scope.listener.fields = listenerFields;
37
+ return scope;
38
+ }
39
+ _getFields(environment, scope) {
40
+ const fields = this.extension.fields;
41
+ if (!(fields !== null && fields !== void 0 && fields.length)) return;
42
+ const result = {};
43
+ for (const {
44
+ name,
45
+ expression,
46
+ string
47
+ } of fields) {
48
+ result['' + name] = expression ? environment.resolveExpression(expression, scope) : string;
49
+ }
50
+ return result;
51
+ }
52
+ }
53
+ class ScriptListener extends Listener {
54
+ constructor(activity, context, extension, pos) {
55
+ super(activity, context, extension);
56
+ const id = this.id = `${activity.id}/${extension.script.$type}/${this.event}/${pos}`;
57
+ this._register(context, id, extension.script);
58
+ }
59
+ execute(api) {
60
+ const environment = this.environment;
61
+ const scope = this._getScope(api, {
62
+ id: this.id,
63
+ resolveExpression
64
+ });
65
+ const script = this.environment.scripts.getScript(this.extension.scriptFormat, {
66
+ id: this.id
67
+ });
68
+ return new Promise((resolve, reject) => {
69
+ return script.execute(scope, (err, result) => {
70
+ if (err) return reject(err);
71
+ resolve(result);
72
+ });
73
+ });
74
+ function resolveExpression(expression) {
75
+ return environment.resolveExpression(expression, scope);
76
+ }
77
+ }
78
+ _register(context, id, script) {
79
+ const {
80
+ scriptFormat,
81
+ value,
82
+ resource
83
+ } = script;
84
+ context.environment.scripts.register({
85
+ id,
86
+ type: this.type,
87
+ behaviour: {
88
+ scriptFormat,
89
+ ...(value && {
90
+ script: value
91
+ }),
92
+ ...(resource && {
93
+ resource
94
+ })
95
+ }
96
+ });
97
+ }
98
+ }
99
+ class ExpressionListener extends Listener {
100
+ execute(message) {
101
+ const scope = this._getScope(message);
102
+ return {
103
+ expression: this.environment.resolveExpression(this.extension.expression, scope)
104
+ };
105
+ }
106
+ }
107
+ class ExecutionListeners {
108
+ constructor(activity, context) {
109
+ this.activity = activity;
110
+ this.context = context;
111
+ this.listeners = [];
112
+ this.onStart = false;
113
+ this.onEnd = false;
114
+ }
115
+ get length() {
116
+ return this.listeners.length;
117
+ }
118
+ add(extension, pos) {
119
+ const {
120
+ event,
121
+ script,
122
+ expression
123
+ } = extension;
124
+ if (!script && !expression) return;
125
+ const list = this.listeners;
126
+ if (script && (script.value || script.resource)) list.push(new ScriptListener(this.activity, this.context, extension, pos));else if (expression) list.push(new ExpressionListener(this.activity, this.context, extension));
127
+ if (event === 'start') this.onStart = true;
128
+ if (event === 'end') this.onEnd = true;
129
+ }
130
+ async execute(event, message) {
131
+ const found = this.listeners.filter(l => l.event === event);
132
+ const format = {};
133
+ for (const f of found) {
134
+ const result = await f.execute(message);
135
+ if (result !== null && typeof result === 'object') Object.assign(format, result);
136
+ }
137
+ return format;
138
+ }
139
+ }
140
+ exports.default = ExecutionListeners;
@@ -105,7 +105,7 @@ FlowScripts.prototype.getScript = function getScript(scriptType, {
105
105
  function JavaScript(flowName, scriptBody, runContext, options) {
106
106
  this.flowName = flowName;
107
107
  this._runContext = runContext;
108
- this.timeout = options && options.timeout;
108
+ this.timeout = options === null || options === void 0 ? void 0 : options.timeout;
109
109
  try {
110
110
  this.script = new _vm.Script(scriptBody, options);
111
111
  } catch (err) {
@@ -151,17 +151,22 @@ function JavaScriptResource(flowName, resource, resourceBase, runContext, option
151
151
  this[kResources] = resourceBase;
152
152
  }
153
153
  JavaScriptResource.prototype.execute = async function execute(executionContext, callback) {
154
+ let resource;
154
155
  try {
155
- var scriptBody = await _fs.promises.readFile((0, _path.join)(this[kResources], executionContext.resolveExpression(this.resource))); // eslint-disable-line no-var
156
+ resource = executionContext.resolveExpression(this.resource);
157
+ var scriptBody = await _fs.promises.readFile((0, _path.join)(this[kResources], resource)); // eslint-disable-line no-var
156
158
  } catch (err) {
159
+ if (err instanceof SyntaxError) {
160
+ return callback(err);
161
+ }
157
162
  const {
158
163
  filename
159
164
  } = this.options;
160
- return callback(new Error(`${filename}: script resource ${this.resource} not found`));
165
+ return callback(new Error(`${filename}: script resource ${resource || this.resource} not found`));
161
166
  }
162
167
  const script = new JavaScript(this.flowName, scriptBody, this._runContext, {
163
168
  ...this.options,
164
- filename: this.resource
169
+ filename: `${this.options.filename}/${resource}`
165
170
  });
166
171
  return script.execute(executionContext, callback);
167
172
  };