diff --git a/package-lock.json b/package-lock.json index bd18479..b483a13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,40 @@ { "name": "cql-worker", - "version": "1.2.0", + "version": "1.1.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cql-worker", - "version": "1.2.0", + "version": "1.1.4", "license": "Apache-2.0", "dependencies": { - "cql-exec-fhir": "^2.0.0", - "cql-execution": "^2.2.0" + "cql-exec-fhir": "^2.0.2", + "cql-exec-vsac": "git@github.com:PuraJuniper/cql-exec-vsac.git#2307581c2577a5321beba2ea7a6d7f7f03f80826", + "cql-execution": "^2.4.0" + } + }, + "../cql-exec-vsac": { + "version": "1.2.2", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.2.0", + "mkdirp": "^1.0.4", + "node-fetch": "^2.6.7", + "xml2js": "^0.4.23" + }, + "devDependencies": { + "chai": "^4.3.4", + "chai-as-promised": "^7.1.1", + "cql-execution": ">=1.2.0", + "eslint": "^8.3.0", + "fs-extra": "^8.1.0", + "mocha": "9.2.0", + "nock": "^13.0.4" + }, + "peerDependencies": { + "cql-execution": ">=1.2.0" } }, "node_modules/@lhncbc/ucum-lhc": { @@ -45,6 +69,21 @@ "cql-execution": ">=1.3.0" } }, + "node_modules/cql-exec-vsac": { + "version": "1.2.2", + "resolved": "git+ssh://git@github.com/PuraJuniper/cql-exec-vsac.git#2307581c2577a5321beba2ea7a6d7f7f03f80826", + "integrity": "sha512-W1wZ8HzyeacMg2HwpxUEB19W3u6DI2Q+0Btjf3sbaAWy4A5X7ol3ZPEcWVwPHZ4OcBPDY1Vmnq+Lde1HmTuuRg==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.2.0", + "mkdirp": "^1.0.4", + "node-fetch": "^2.6.7", + "xml2js": "^0.4.23" + }, + "peerDependencies": { + "cql-execution": ">=1.2.0" + } + }, "node_modules/cql-execution": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/cql-execution/-/cql-execution-2.4.0.tgz", @@ -67,6 +106,22 @@ "lodash.get": "~4.4.2" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/emitter-component": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz", @@ -133,6 +188,41 @@ "node": "*" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -192,11 +282,30 @@ "readable-stream": "^2.1.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -256,6 +365,17 @@ "xml2js": "~0.4.23" } }, + "cql-exec-vsac": { + "version": "git+ssh://git@github.com/PuraJuniper/cql-exec-vsac.git#2307581c2577a5321beba2ea7a6d7f7f03f80826", + "integrity": "sha512-W1wZ8HzyeacMg2HwpxUEB19W3u6DI2Q+0Btjf3sbaAWy4A5X7ol3ZPEcWVwPHZ4OcBPDY1Vmnq+Lde1HmTuuRg==", + "from": "cql-exec-vsac@git@github.com:PuraJuniper/cql-exec-vsac.git#2307581c2577a5321beba2ea7a6d7f7f03f80826", + "requires": { + "debug": "^4.2.0", + "mkdirp": "^1.0.4", + "node-fetch": "^2.6.7", + "xml2js": "^0.4.23" + } + }, "cql-execution": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/cql-execution/-/cql-execution-2.4.0.tgz", @@ -278,6 +398,14 @@ "lodash.get": "~4.4.2" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, "emitter-component": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz", @@ -335,6 +463,24 @@ "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz", "integrity": "sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==" }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -394,11 +540,30 @@ "readable-stream": "^2.1.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", diff --git a/package.json b/package.json index 1d27afb..d475f9a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "license": "Apache-2.0", "dependencies": { "cql-exec-fhir": "^2.0.2", + "cql-exec-vsac": "git@github.com:PuraJuniper/cql-exec-vsac.git#2307581c2577a5321beba2ea7a6d7f7f03f80826", "cql-execution": "^2.4.0" } } diff --git a/src/CqlProcessor.js b/src/CqlProcessor.js index 05e6250..96f0bcb 100644 --- a/src/CqlProcessor.js +++ b/src/CqlProcessor.js @@ -1,6 +1,7 @@ import cql from 'cql-execution'; import fhir from 'cql-exec-fhir'; import fhirHelpersJson from './FHIRHelpers-4.0.1.json.js'; +import { CodeService } from 'cql-exec-vsac'; /** * Executes logical expression written in the Clinical Quality Language (CQL) against @@ -21,10 +22,16 @@ export default class CqlProcessor { ...elmJsonDependencies }); this.library = new cql.Library(elmJson, this.repository); - this.codeService = new cql.CodeService(valueSetJson); + this.codeService = new CodeService(undefined, false); this.executor = new cql.Executor(this.library, this.codeService, parameters); } + async initValueSets() { + if (this.library.source.library.valueSets !== undefined) { + await this.codeService.ensureValueSetsInLibraryWithAPIKey(this.library, true, undefined, false); + } + } + /** * Load a patient bundle into the CQL Processor * @param {object} patientBundle - A bundle of FHIR resources for the patient @@ -43,9 +50,10 @@ export default class CqlProcessor { * @param {string} expr - The name of an expression from elmJson * @returns {object} results - The results from executing the expression */ - evaluateExpression(expr) { + async evaluateExpression(expr) { // Only try to evaluate an expression if we have a patient bundle loaded. if (this.patientSource._bundles && this.patientSource._bundles.length > 0) { + await this.initValueSets(); let results = this.executor.exec_expression(expr, this.patientSource); this.patientSource._index = 0; // HACK: rewind the patient source return results.patientResults[this.patientID][expr]; diff --git a/src/cql.worker.js b/src/cql.worker.js index 695db68..e755fcc 100644 --- a/src/cql.worker.js +++ b/src/cql.worker.js @@ -26,23 +26,24 @@ onmessage = function(rx) { if ((expression = rx.data.expression) != null) { let tx; if (processor.patientSource._bundles.length > 0) { - let result = processor.evaluateExpression(expression); - tx = { - expression: expression, - result: result - }; + processor.evaluateExpression(expression).then(v => { + this.postMessage({ + expression: expression, + result: v + }); // send the result back + }); } else { // If we don't have a bundle just send the expression back. tx = { expression: expression, result: 'WAITING_FOR_PATIENT_BUNDLE' } + this.postMessage(tx); // send the result back } - this.postMessage(tx); // send the result back } else if ((patientBundle = rx.data.patientBundle) != null) { // If the message contains a patient bundle, load it. processor.loadBundle(patientBundle); - } else if ((elmJson = rx.data.elmJson) != null && (valueSetJson = rx.data.valueSetJson) != null) { // TODO: Allow empty value sets and check elm dependencies + } else if ((elmJson = rx.data.elmJson) != null) { // TODO: Allow empty value sets and check elm dependencies // If the message contains translated CQL (ELM JSON), use it to create a new // CQL Processor object. parameters = rx.data.parameters;