diff --git a/lib/Diagram.js b/lib/Diagram.js index b7e21d3f1..c1bd69ed8 100644 --- a/lib/Diagram.js +++ b/lib/Diagram.js @@ -12,6 +12,16 @@ import CoreModule from './core'; * } & Record } DiagramOptions */ +/** + * @template T + * @typedef {import('didi').FactoryFunction} FactoryFunction + */ + +/** + * @template T + * @typedef {import('didi').ArrayFunc} ArrayFunc + */ + /** * Bootstrap an injector from a list of modules, instantiating a number of default components * @@ -30,9 +40,10 @@ function bootstrap(modules) { /** * Creates an injector from passed options. * + * @template ServiceMap * @param {DiagramOptions} [options] * - * @return {Injector} + * @return {Injector} */ function createInjector(options) { @@ -59,6 +70,7 @@ function createInjector(options) { * * @class * @constructor + * @template [ServiceMap=null] * * @example Creating a plug-in that logs whenever a shape is added to the canvas. * @@ -97,44 +109,17 @@ function createInjector(options) { * ``` * * @param {DiagramOptions} [options] - * @param {Injector} [injector] An (optional) injector to bootstrap the diagram with. + * @param {Injector} [injector] An (optional) injector to bootstrap the diagram with. */ export default function Diagram(options, injector) { - this._injector = injector = injector || createInjector(options); - - // API - - /** - * Resolves a diagram service. - * - * @template T - * - * @param {string} name The name of the service to get. - * @param {boolean} [strict=true] If false, resolve missing services to null. - * - * @return {T|null} - */ - this.get = injector.get; - /** - * Executes a function with its dependencies injected. - * - * @template T - * - * @param {Function} func function to be invoked - * @param {InjectionContext} [context] context of the invocation - * @param {LocalsMap} [locals] locals provided - * - * @return {T|null} + * @type {Injector} */ - this.invoke = injector.invoke; + this._injector = injector || createInjector(options); // init - // indicate via event - - /** * An event indicating that all plug-ins are loaded. * @@ -157,6 +142,82 @@ export default function Diagram(options, injector) { this.get('eventBus').fire('diagram.init'); } +/** + * @overlord + * + * Resolves a diagram service. + * + * @template T + * + * @param {string} name The name of the service to get. + * + * @return {T} + */ +/** + * @overlord + * + * Resolves a diagram service. + * + * @template T + * + * @param {string} name The name of the service to get. + * @param {true} strict If false, resolve missing services to null. + * + * @return {T} + */ +/** + * @overlord + * + * Resolves a diagram service. + * + * @template T + * + * @param {string} name The name of the service to get. + * @param {boolean} strict If false, resolve missing services to null. + * + * @return {T|null} + */ +/** + * Resolves a diagram service. + * + * @template {keyof ServiceMap} Name + * + * @param {Name} name The name of the service to get. + * + * @return {ServiceMap[Name]} + */ +Diagram.prototype.get = function(name, strict) { + return this._injector.get(name, strict); +}; + +/** + * @overlord + * + * Invoke the given function, injecting dependencies. Return the result. + * + * @template T + * + * @param {FactoryFunction} func + * @param {InjectionContext} [context] + * @param {LocalsMap} [locals] + * + * @return {T} + */ +/** + * Invoke the given function, injecting dependencies provided in + * array notation. Return the result. + * + * @template T + * + * @param {ArrayFunc} func function to be invoked + * @param {InjectionContext} [context] context of the invocation + * @param {LocalsMap} [locals] locals provided + * + * @return {T} + */ +Diagram.prototype.invoke = function(func, context, locals) { + return this._injector.invoke(func, context, locals); +}; /** * Destroys the diagram diff --git a/lib/Diagram.spec.ts b/lib/Diagram.spec.ts index a7df77b80..099014d4f 100644 --- a/lib/Diagram.spec.ts +++ b/lib/Diagram.spec.ts @@ -33,4 +33,23 @@ const foo = diagram.invoke((modeling: Modeling, eventBus: EventBus) => { }; }); -foo.bar = false; \ No newline at end of file +foo.bar = false; + +type NoneEvent = {}; + +type EventMap = { + 'diagram.init': NoneEvent +}; + +type ServiceMap = { + 'eventBus': EventBus +}; + +const typedDiagram = new Diagram(); + +const eventBus = typedDiagram.get('eventBus'); + +eventBus.on('diagram.init', (event) => { + + // go forth and react to init (!) +}); diff --git a/lib/core/EventBus.js b/lib/core/EventBus.js index 9b764759a..f949f0eb1 100644 --- a/lib/core/EventBus.js +++ b/lib/core/EventBus.js @@ -118,6 +118,8 @@ var slice = Array.prototype.slice; * var sum = eventBus.fire('sum', 1, 2); * console.log(sum); // 3 * ``` + * + * @template [EventMap=null] */ export default function EventBus() { @@ -131,8 +133,9 @@ export default function EventBus() { this.on('diagram.destroy', 1, this._destroy, this); } - /** + * @overlord + * * Register an event listener for events with the given name. * * The callback will be invoked with `event, ...additionalArguments` @@ -151,6 +154,25 @@ export default function EventBus() { * @param {EventBusEventCallback} callback * @param {any} [that] callback context */ +/** + * Register an event listener for events with the given name. + * + * The callback will be invoked with `event, ...additionalArguments` + * that have been passed to {@link EventBus#fire}. + * + * Returning false from a listener will prevent the events default action + * (if any is specified). To stop an event from being processed further in + * other listeners execute {@link Event#stopPropagation}. + * + * Returning anything but `undefined` from a listener will stop the listener propagation. + * + * @template {keyof EventMap} EventName + * + * @param {EventName} events to subscribe to + * @param {number} [priority=1000] listen priority + * @param {EventBusEventCallback} callback + * @param {any} [that] callback context + */ EventBus.prototype.on = function(events, priority, callback, that) { events = isArray(events) ? events : [ events ]; @@ -188,6 +210,8 @@ EventBus.prototype.on = function(events, priority, callback, that) { }; /** + * @overlord + * * Register an event listener that is called only once. * * @template T @@ -197,6 +221,16 @@ EventBus.prototype.on = function(events, priority, callback, that) { * @param {EventBusEventCallback} callback * @param {any} [that] callback context */ +/** + * Register an event listener that is called only once. + * + * @template {keyof EventMap} EventName + * + * @param {EventName} events to subscribe to + * @param {number} [priority=1000] listen priority + * @param {EventBusEventCallback} callback + * @param {any} [that] callback context + */ EventBus.prototype.once = function(events, priority, callback, that) { var self = this; diff --git a/lib/core/EventBus.spec.ts b/lib/core/EventBus.spec.ts index a2415d1f1..14b13d99d 100644 --- a/lib/core/EventBus.spec.ts +++ b/lib/core/EventBus.spec.ts @@ -59,3 +59,22 @@ eventBus.once('foo', callback, this); eventBus.once('foo', (event, additional1, additional2) => { console.log('foo', event, additional1, additional2); }); + + +type EventMap = { + foo: FooEvent +}; + +const typedEventBus = new EventBus(); + +typedEventBus.on('foo', (event: FooEvent) => { + const { foo } = event; + + console.log(foo); +}); + +typedEventBus.on('foo', (event) => { + const { foo } = event; + + console.log(foo); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 779ed78cd..8934090fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@bpmn-io/diagram-js-ui": "^0.2.2", "clsx": "^2.1.0", - "didi": "^10.0.1", + "didi": "^10.2.0", "inherits-browser": "^0.1.0", "min-dash": "^4.1.0", "min-dom": "^4.1.0", @@ -24,7 +24,7 @@ "@testing-library/preact": "^3.2.3", "babel-loader": "^9.1.3", "babel-plugin-istanbul": "^6.1.1", - "bio-dts": "^0.8.1", + "bio-dts": "^0.10.0", "chai": "^4.4.1", "del-cli": "^5.0.0", "eslint": "^8.56.0", @@ -45,7 +45,7 @@ "puppeteer": "^22.1.0", "sinon": "^17.0.1", "sinon-chai": "^3.7.0", - "typescript": "^5.0.0", + "typescript": "^5.3.3", "webpack": "^5.90.3" }, "engines": { @@ -1483,15 +1483,16 @@ } }, "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "node_modules/assertion-error": { @@ -1503,9 +1504,9 @@ } }, "node_modules/ast-types": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz", - "integrity": "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==", + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", "dev": true, "dependencies": { "tslib": "^2.0.1" @@ -1656,13 +1657,13 @@ } }, "node_modules/bio-dts": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/bio-dts/-/bio-dts-0.8.1.tgz", - "integrity": "sha512-8Dy5NgZKl1mHJS6PUVGi8HYFIwvbCmV4RNnW85i2YtSLWeo2t7iSAaJVcJ6n6xPDKlkqVcW04VYN13QZ94nKAQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/bio-dts/-/bio-dts-0.10.0.tgz", + "integrity": "sha512-XJRBWwJq+iaLIz7AaxCTT6J7tBjtfUi2e4z8DSI8abS05kp/jykEF4FBq3WuMEBSWup6OoncgMhFBdrYIZ9fsQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.21.2", - "recast": "^0.22.0", + "@babel/parser": "^7.23.9", + "recast": "^0.23.4", "tiny-glob": "^0.2.9" }, "bin": { @@ -2527,9 +2528,9 @@ "license": "MIT" }, "node_modules/didi": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/didi/-/didi-10.0.1.tgz", - "integrity": "sha512-rddmyUjpIh8pIu9OVtqQjGkZB4jNOaaRMz9AClSH3WUEOoJctxpFmqjTButmI5na5sSIDpfjABe34wEvEhrjcg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/didi/-/didi-10.2.0.tgz", + "integrity": "sha512-HqlQimiIImFz8amKDomklW8pZ/7JhiGtgru8yOUlN/VE2X9WQ4sMj2pdsuk8IhWQjGVHTmvKXEmmYQaOKLrr5Q==", "engines": { "node": ">= 16" } @@ -2839,12 +2840,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "dev": true - }, "node_modules/escalade": { "version": "3.1.1", "dev": true, @@ -6908,13 +6903,13 @@ } }, "node_modules/recast": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.22.0.tgz", - "integrity": "sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==", + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.4.tgz", + "integrity": "sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==", "dev": true, "dependencies": { "assert": "^2.0.0", - "ast-types": "0.15.2", + "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tslib": "^2.0.1" @@ -8029,16 +8024,16 @@ } }, "node_modules/typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/ua-parser-js": { @@ -9659,15 +9654,16 @@ "dev": true }, "assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, "requires": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "assertion-error": { @@ -9675,9 +9671,9 @@ "dev": true }, "ast-types": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz", - "integrity": "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==", + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", "dev": true, "requires": { "tslib": "^2.0.1" @@ -9783,13 +9779,13 @@ "dev": true }, "bio-dts": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/bio-dts/-/bio-dts-0.8.1.tgz", - "integrity": "sha512-8Dy5NgZKl1mHJS6PUVGi8HYFIwvbCmV4RNnW85i2YtSLWeo2t7iSAaJVcJ6n6xPDKlkqVcW04VYN13QZ94nKAQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/bio-dts/-/bio-dts-0.10.0.tgz", + "integrity": "sha512-XJRBWwJq+iaLIz7AaxCTT6J7tBjtfUi2e4z8DSI8abS05kp/jykEF4FBq3WuMEBSWup6OoncgMhFBdrYIZ9fsQ==", "dev": true, "requires": { - "@babel/parser": "^7.21.2", - "recast": "^0.22.0", + "@babel/parser": "^7.23.9", + "recast": "^0.23.4", "tiny-glob": "^0.2.9" } }, @@ -10376,9 +10372,9 @@ "dev": true }, "didi": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/didi/-/didi-10.0.1.tgz", - "integrity": "sha512-rddmyUjpIh8pIu9OVtqQjGkZB4jNOaaRMz9AClSH3WUEOoJctxpFmqjTButmI5na5sSIDpfjABe34wEvEhrjcg==" + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/didi/-/didi-10.2.0.tgz", + "integrity": "sha512-HqlQimiIImFz8amKDomklW8pZ/7JhiGtgru8yOUlN/VE2X9WQ4sMj2pdsuk8IhWQjGVHTmvKXEmmYQaOKLrr5Q==" }, "diff": { "version": "5.0.0", @@ -10631,12 +10627,6 @@ "is-symbol": "^1.0.2" } }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "dev": true - }, "escalade": { "version": "3.1.1", "dev": true @@ -13408,13 +13398,13 @@ } }, "recast": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.22.0.tgz", - "integrity": "sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==", + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.4.tgz", + "integrity": "sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==", "dev": true, "requires": { "assert": "^2.0.0", - "ast-types": "0.15.2", + "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tslib": "^2.0.1" @@ -14205,9 +14195,9 @@ } }, "typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true }, "ua-parser-js": { diff --git a/package.json b/package.json index a98cd3ca0..1ebf462ad 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@testing-library/preact": "^3.2.3", "babel-loader": "^9.1.3", "babel-plugin-istanbul": "^6.1.1", - "bio-dts": "^0.8.1", + "bio-dts": "^0.10.0", "chai": "^4.4.1", "del-cli": "^5.0.0", "eslint": "^8.56.0", @@ -77,13 +77,13 @@ "puppeteer": "^22.1.0", "sinon": "^17.0.1", "sinon-chai": "^3.7.0", - "typescript": "^5.0.0", + "typescript": "^5.3.3", "webpack": "^5.90.3" }, "dependencies": { "@bpmn-io/diagram-js-ui": "^0.2.2", "clsx": "^2.1.0", - "didi": "^10.0.1", + "didi": "^10.2.0", "inherits-browser": "^0.1.0", "min-dash": "^4.1.0", "min-dom": "^4.1.0",