diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e5a499..c8cd1ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ and here ### Changed --> +## Unreleased — Separate runtime and administration APIs (#2) + +- Keep workflow execution operations on `BPMNAPI`. +- Move workflow-definition administration to the explicit `BPMNAdminAPI` surface. +- Add boundary tests preventing runtime clients from acquiring model-management capabilities. + ## Release 2.3.8 -- 2026-3-15 Fix NPM i ## Release 2.3.7 -- 2026-02-04 @@ -428,4 +434,4 @@ improved documentation - new format: return=service(input,executionContext); ### Removed -- server.cron.checktimers(); \ No newline at end of file +- server.cron.checktimers(); diff --git a/api-boundary.test.mjs b/api-boundary.test.mjs new file mode 100644 index 0000000..f60b108 --- /dev/null +++ b/api-boundary.test.mjs @@ -0,0 +1,20 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +import { BPMNAPI, BPMNAdminAPI } from './dist/index.js'; + +const server = {}; + +test('runtime API does not expose model administration', () => { + const api = new BPMNAPI(server); + assert.equal(api.model, undefined); + assert.ok(api.engine); + assert.ok(api.data); +}); + +test('admin API explicitly exposes model administration', () => { + const api = new BPMNAdminAPI(server); + assert.ok(api.model); + assert.ok(api.engine); + assert.ok(api.data); +}); diff --git a/package.json b/package.json index 8d6fcb1..72e6451 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "type": "module", "scripts": { "build": "tsc --build", - "clean": "rimraf dist" + "clean": "rimraf dist", + "test:boundary": "node --test api-boundary.test.mjs" }, "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/API/API.ts b/src/API/API.ts index 23c1e39..59d4822 100644 --- a/src/API/API.ts +++ b/src/API/API.ts @@ -15,26 +15,37 @@ import { * * api.engine to access engine functions (start,invoke..) * api.data to access dataStore functions (find.. delete) - * api.model to access Model functions (list,save,export..) + * + * Model administration is intentionally available only through BPMNAdminAPI. * * * api cli: * >api engine start --name 'Buy Used Car' --data caseId 1003 * >api engine invoke --query name 'Buy Used Car' items.status wait name 'Buy' --data carModel 'Mazda 3' * >api data findItems --query name 'Buy Used Car' items.status wait - * >api model list */ class BPMNAPI { server: IBPMNServer; engine: APIEngine; data: APIData; - model: APIModel; defaultUser: ISecureUser; constructor(server: IBPMNServer) { this.server = server; this.engine = new APIEngine(this); this.data = new APIData(this); + } +} + +/** + * Privileged API used by deployment and model-management tooling. + * Runtime integrations should receive BPMNAPI so they cannot mutate models. + */ +class BPMNAdminAPI extends BPMNAPI { + model: APIModel; + + constructor(server: IBPMNServer) { + super(server); this.model = new APIModel(this); } } @@ -333,6 +344,7 @@ class APIModel extends APIComponent { return await this.server.definitions.findEvents(query, user.modelsOwner); } public async delete(name, user?: ISecureUser) { + user=this.getUser(user); if (await user.canDeleteModel(name)) return await this.server.definitions.deleteModel(name, user.modelsOwner); return false; @@ -345,9 +357,11 @@ class APIModel extends APIComponent { return false; } public async getSource(name, user?: ISecureUser) { + user=this.getUser(user); return await this.server.definitions.getSource(name, user.modelsOwner); } public async load(name, user?: ISecureUser) { + user=this.getUser(user); return await this.server.definitions.load(name, user.modelsOwner); } public async export(query, folder, user?: ISecureUser) { @@ -355,4 +369,4 @@ class APIModel extends APIComponent { } } -export { BPMNAPI, APIEngine, APIData, APIModel } +export { BPMNAPI, BPMNAdminAPI, APIEngine, APIData, APIModel }