Skip to content

Commit 4bc689e

Browse files
committed
fix FlowScript type declaration
1 parent 564d1b7 commit 4bc689e

File tree

7 files changed

+123
-42
lines changed

7 files changed

+123
-42
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
# unreleased
44

5+
# 8.3.0
6+
7+
- fix `FlowScripts` type declaration
8+
- throw `FlowResourceError` if failing to get script resource content
9+
510
# 8.2.2
611

712
- bump [0dep/piso@2](https://www.npmjs.com/package/@0dep/piso)

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@onify/flow-extensions",
3-
"version": "8.2.2",
3+
"version": "8.3.0",
44
"description": "Onify Flow extensions",
55
"type": "module",
66
"module": "src/index.js",
@@ -52,7 +52,7 @@
5252
"@babel/register": "^7.23.7",
5353
"bpmn-elements": "^15.0.3",
5454
"bpmn-elements-8-1": "npm:[email protected]",
55-
"bpmn-engine": "^21.1.0",
55+
"bpmn-engine": "^22.0.2",
5656
"bpmn-engine-14": "npm:bpmn-engine@14",
5757
"bpmn-moddle": "^9.0.1",
5858
"c8": "^10.1.2",

test/features/scripts-feature.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,8 @@ Feature('Flow scripts', () => {
389389

390390
Then('an Error is thrown indicating input script failed', async () => {
391391
const err = await error;
392-
expect(err.content.error.message).to.equal('command-definition/bpmn:ScriptTask/task: script resource ./io-sripts.js not found');
392+
expect(err.content.error.message).to.match(/ENOENT/i);
393+
expect(err.content.error.code).to.equal('EFLOW_SCRIPT');
393394
});
394395
});
395396

test/helpers/FlowScripts.js

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { join } from 'path';
2-
import { promises as fs } from 'fs';
3-
import { Script } from 'vm';
1+
import { join } from 'node:path';
2+
import { promises as fs } from 'node:fs';
3+
import { Script } from 'node:vm';
44

55
const kSyntaxError = Symbol.for('syntax error');
66
const kResources = Symbol.for('resources base');
77

8-
class FlowScriptError extends Error {
8+
export class FlowScriptError extends Error {
99
constructor(fromErr) {
1010
super(fromErr.message);
1111
this.name = this.constructor.name;
@@ -23,11 +23,11 @@ class FlowScriptError extends Error {
2323
});
2424
}
2525
toString() {
26-
return '[FlowScriptError] ' + this.message + '\n' + this.stack;
26+
return '[' + this.name + '] ' + this.message + '\n' + this.stack;
2727
}
2828
}
2929

30-
class FlowSyntaxError extends Error {
30+
export class FlowSyntaxError extends Error {
3131
constructor(fromErr) {
3232
super(fromErr.message);
3333
this.name = this.constructor.name;
@@ -45,17 +45,23 @@ class FlowSyntaxError extends Error {
4545
});
4646
}
4747
toString() {
48-
return '[FlowSyntaxError] ' + this.message + '\n' + this.stack;
48+
return '[' + this.name + '] ' + this.message + '\n' + this.stack;
4949
}
5050
}
5151

52-
export { FlowScripts, JavaScript, JavaScriptResource };
52+
export class FlowResourceError extends FlowScriptError {
53+
constructor(fromErr, filename) {
54+
super(fromErr);
55+
this.filename = filename;
56+
this.inner = fromErr;
57+
}
58+
}
5359

54-
function FlowScripts(flowName, resourceBase, runContext, timeout = 60000) {
55-
this._name = flowName;
56-
this._scripts = {};
57-
this._timeout = timeout;
58-
this._runContext = runContext;
60+
export function FlowScripts(flowName, resourceBase, runContext, timeout = 60000) {
61+
this.flowName = flowName;
62+
this.scripts = new Map();
63+
this.timeout = timeout;
64+
this.runContext = runContext;
5965
this[kResources] = resourceBase;
6066
}
6167

@@ -83,22 +89,33 @@ FlowScripts.prototype.register = function register({ id, type, behaviour }) {
8389

8490
language = 'javascript';
8591

86-
const name = this._name;
87-
const filename = `${name}/${type}/${id}`;
92+
const flowName = this.flowName;
93+
const filename = `${flowName}/${type}/${id}`;
8894
if (scriptBody) {
89-
this._scripts[id] = new JavaScript(name, scriptBody, this._runContext, { filename, timeout: this._timeout });
95+
this.scripts.set(id, new JavaScript(flowName, scriptBody, this.runContext, { filename, timeout: this.timeout }));
9096
} else if (resource) {
91-
this._scripts[id] = new JavaScriptResource(name, resource, this[kResources], this._runContext, { filename, timeout: this._timeout });
97+
this.scripts.set(
98+
id,
99+
new JavaScriptResource(flowName, resource, this[kResources], this.runContext, { filename, timeout: this.timeout }),
100+
);
92101
}
93102
};
94103

95104
FlowScripts.prototype.getScript = function getScript(scriptType, { id }) {
96-
return this._scripts[id];
105+
return this.scripts.get(id);
97106
};
98107

99-
function JavaScript(flowName, scriptBody, runContext, options) {
108+
/**
109+
* Java script
110+
* @param {string} flowName
111+
* @param {string|Buffer} scriptBody
112+
* @param {any} [runContext]
113+
* @param {import('node:vm').ScriptOptions} options
114+
*/
115+
export function JavaScript(flowName, scriptBody, runContext, options) {
100116
this.flowName = flowName;
101-
this._runContext = runContext;
117+
this.options = options;
118+
this.runContext = runContext;
102119
this.timeout = options?.timeout;
103120

104121
try {
@@ -124,7 +141,7 @@ JavaScript.prototype.execute = async function execute(executionContext, callback
124141
from: Buffer.from,
125142
},
126143
contextName: this.flowName,
127-
...this._runContext,
144+
...this.runContext,
128145
next,
129146
},
130147
{
@@ -143,28 +160,43 @@ JavaScript.prototype.execute = async function execute(executionContext, callback
143160
}
144161
};
145162

146-
function JavaScriptResource(flowName, resource, resourceBase, runContext, options) {
163+
export function JavaScriptResource(flowName, resource, resourceBase, runContext, options) {
147164
this.flowName = flowName;
148165
this.resource = resource;
166+
this.runContext = runContext;
149167
this.options = options;
150-
this._runContext = runContext;
151-
this[kResources] = resourceBase;
168+
this.timeout = options?.timeout;
169+
this.resourceBase = resourceBase;
152170
}
153171

172+
/**
173+
* Get javascript resource content
174+
* @param {string} resourceBase Resource base
175+
* @param {*} resource Resource name or path
176+
* @returns {Promise<string|Buffer} Resource content
177+
*/
178+
JavaScriptResource.prototype.getResourceContent = function getResourceContent(resourceBase, resource) {
179+
return fs.readFile(join(resourceBase, resource));
180+
};
181+
154182
JavaScriptResource.prototype.execute = async function execute(executionContext, callback) {
155183
let resource;
156184
try {
157185
resource = executionContext.resolveExpression(this.resource);
158-
var scriptBody = await fs.readFile(join(this[kResources], resource)); // eslint-disable-line no-var
186+
var scriptBody = await this.getResourceContent(this.resourceBase, resource); // eslint-disable-line no-var
187+
if (!scriptBody) throw new TypeError(`${this.options.filename}: script resource ${resource || this.resource} is empty`);
159188
} catch (err) {
160189
if (err instanceof SyntaxError) {
161190
return callback(err);
162191
}
163-
const { filename } = this.options;
164-
return callback(new Error(`${filename}: script resource ${resource || this.resource} not found`));
192+
return callback(new FlowResourceError(err, this.options.filename));
193+
}
194+
195+
if (!scriptBody) {
196+
throw new FlowResourceError();
165197
}
166198

167-
const script = new JavaScript(this.flowName, scriptBody, this._runContext, {
199+
const script = new JavaScript(this.flowName, scriptBody, this.runContext, {
168200
...this.options,
169201
filename: `${this.options.filename}/${resource}`,
170202
});

test/helpers/factory.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import fs from 'fs';
2-
import path from 'path';
1+
import fs from 'node:fs';
2+
import path from 'node:path';
33

44
export default {
55
resource,

test/helpers/testHelpers.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { Serializer, TypeResolver } from 'moddle-context-serializer';
1+
import fs from 'node:fs/promises';
2+
import BpmnModdle from 'bpmn-moddle';
3+
import Debug from 'debug';
24
import { Engine } from 'bpmn-engine';
3-
import { extensions, extendFn } from '../../src/index.js';
4-
import { FlowScripts } from './FlowScripts.js';
5-
import { promises as fs } from 'fs';
5+
import { Serializer, TypeResolver } from 'moddle-context-serializer';
66
import * as Elements from 'bpmn-elements';
77
import * as expressions from '@aircall/expression-parser';
8-
import BpmnModdle from 'bpmn-moddle';
9-
import Debug from 'debug';
8+
9+
import { FlowScripts } from './FlowScripts.js';
10+
import { extensions, extendFn } from '../../src/index.js';
1011
import { OnifyTimerEventDefinition as TimerEventDefinition } from '../../src/OnifyTimerEventDefinition.js';
1112

1213
let exts;

types/index.d.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
declare module '@onify/flow-extensions' {
2-
import { SequenceFlow, TimerEventDefinition, ElementBase, IExtension, ContextInstance } from 'bpmn-elements';
2+
import { SequenceFlow, TimerEventDefinition, ElementBase, IExtension, ContextInstance, SequenceFlow } from 'bpmn-elements';
33
import { extendFn as extendFunction } from 'moddle-context-serializer';
44

55
export class OnifySequenceFlow extends SequenceFlow {}
@@ -11,6 +11,48 @@ declare module '@onify/flow-extensions' {
1111
}
1212

1313
declare module '@onify/flow-extensions/FlowScripts' {
14-
import { IScripts } from 'bpmn-elements';
15-
export var FlowScripts: IScripts;
14+
import { IScripts, Script, ExecutionScope } from 'bpmn-elements';
15+
import { SerializableElement } from 'moddle-context-serializer';
16+
import { ScriptOptions } from 'node:vm';
17+
18+
declare type registerArgument = SerializableElement | { id: string; type: string; behavior: any };
19+
20+
export interface FlowScriptOptions extends ScriptOptions {
21+
timeout?: number;
22+
}
23+
24+
export class FlowScripts implements IScripts {
25+
/**
26+
* @param flowName Flow name
27+
* @param resourceBase External resource base
28+
* @param runContext Optional script globals
29+
* @param timeout Optional execution timeout in milliseconds, default 60000
30+
*/
31+
constructor(flowName: string, resourceBase: string, runContext?: any, timeout?: number);
32+
flowName: string;
33+
timeout: number;
34+
/** Registered scripts */
35+
get scripts(): Map<string, Script | JavaScriptResource>;
36+
/** Registered scripts */
37+
register(element: registerArgument): Script | undefined;
38+
getScript(language: string, identifier: { id: string; [x: string]: any }): Script;
39+
}
40+
41+
export class JavaScript implements Script {
42+
constructor(flowName: string, scriptBody: string | Buffer, runContext: any, options?: FlowScriptOptions);
43+
get flowName(): string;
44+
get runContext(): unknown;
45+
get options(): FlowScriptOptions;
46+
execute(executionContext: ExecutionScope, callback: CallableFunction): void;
47+
}
48+
49+
export class JavaScriptResource extends JavaScript {
50+
constructor(flowName: string, resource: string, resourceBase: string, runContext?: unknown, options?: FlowScriptOptions);
51+
/**
52+
* Get javascript resource content
53+
* @param resourceBase Resource base
54+
* @param resource Resolved resource name or path
55+
*/
56+
getResourceContent(resourceBase: string, resource: string): Promise<string | Buffer>;
57+
}
1658
}

0 commit comments

Comments
 (0)