fhirsmith 0.15.1 → 0.16.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 +18 -0
- package/library/npm-audit.js +69 -2
- package/library/sqlite-session-store.js +133 -0
- package/package.json +18 -28
- package/token/token.js +7 -10
- package/tx/importers/import-rxnorm.module.js +1 -1
- package/tx/importers/import-sct.module.js +1 -1
- package/tx/library/designations.js +1 -1
- package/tx/sct/text-index.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ All notable changes to Health Intersections FHIRsmith will be documented in this
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.16.0] - 2026-10-15
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
-
|
|
12
|
+
- Token: the login session store is now FHIRsmith's own (`library/sqlite-session-store.js`), in place of connect-sqlite3. connect-sqlite3 depends on sqlite3 5, which brought node-gyp 8, tar 6 and @tootallnate/once into every npm install of FHIRsmith (12 advisories, one critical). The `overrides` in package.json kept them out of a clone + `npm ci` (and so out of the Docker image and the repo's own `npm audit`), but npm ignores overrides when installing the published package, so `npm install -g fhirsmith` still got them. Sessions are kept in the same `sessions` table of the token database, in the same layout, so existing logins carry over
|
|
13
|
+
- Dependencies: fhirpath 5 (its ucum-lhc no longer brings csv-parse 4) and node-cron 4 (no longer depends on uuid), for the same reason: the csv-parse and uuid overrides didn't reach npm installs. The csv-parse override is gone
|
|
14
|
+
- Dependencies: natural 8 (uuid 13). Its stemmers are unchanged - every stemmer we use gives the same stem as natural 6 on ~1.6M words (RxNorm, LOINC's translations, and English, French, Spanish, Italian and Portuguese word lists), so existing SNOMED caches and RxNorm databases don't need rebuilding. FHIRsmith now loads only `natural/lib/natural/stemmers`: the package index also loads natural's storage backends, which run dotenv (reading any `.env` in the working directory into the environment, and in natural 8 printing a banner to stdout)
|
|
15
|
+
- Development: jest 30 and jest-junit 17, and `npm run dev` uses Node's own `node --watch` in place of nodemon. With these, `npm audit` is clean for the dev tooling too (jest 29 and nodemon brought braces, and jest's coverage tooling sprintf-js, neither of which has a fixed version). The package.json overrides are down to one, js-yaml 4 for @istanbuljs/load-nyc-config, which only affects the dev install; the rest were no longer doing anything
|
|
16
|
+
- The PR pipeline fails on high/critical advisories anywhere in the dependency tree, on moderate ones in what ships, and audits a fresh install of the packed package (installed the way users install it, so the repo's overrides don't apply)
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- The npm audit self-check looks up the latest FHIRsmith release, and only tells the administrator to upgrade when there is a newer release to upgrade to; a server already on the latest release says so instead
|
|
21
|
+
|
|
22
|
+
### Tx Conformance Statement
|
|
23
|
+
|
|
24
|
+
FHIRsmith v0.16.0 passed all 3630 HL7 terminology service tests (modes tx.fhir.org+omop+general+snomed+mimetypes+icd-11+closure, tests v1.9.6, runner v7.0.1)
|
|
25
|
+
|
|
8
26
|
## [0.15.1] - 2026-10-15
|
|
9
27
|
|
|
10
28
|
### Security
|
package/library/npm-audit.js
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
//
|
|
13
13
|
// Config (all optional), top level of config.json:
|
|
14
14
|
// "npmAudit": { "enabled": true, "intervalHours": 24 }
|
|
15
|
+
//
|
|
16
|
+
// Each check also looks up the latest fhirsmith release on npm, so the advice can say
|
|
17
|
+
// whether upgrading will help. When this server is already on the latest release it won't:
|
|
18
|
+
// npm ignores our package.json "overrides" for anything but a clone + npm ci, so an
|
|
19
|
+
// npm-installed server can carry transitive versions the repo itself has overridden away.
|
|
15
20
|
|
|
16
21
|
const fs = require('fs');
|
|
17
22
|
const path = require('path');
|
|
@@ -19,6 +24,7 @@ const axios = require('axios');
|
|
|
19
24
|
const escape = require('escape-html');
|
|
20
25
|
|
|
21
26
|
const DEFAULT_URL = 'https://registry.npmjs.org/-/npm/v1/security/advisories/bulk';
|
|
27
|
+
const DEFAULT_LATEST_URL = 'https://registry.npmjs.org/fhirsmith/latest';
|
|
22
28
|
const SEVERITIES = ['critical', 'high', 'moderate', 'low', 'info'];
|
|
23
29
|
const HOUR_MS = 60 * 60 * 1000;
|
|
24
30
|
const FIRST_RUN_DELAY_MS = 60 * 1000; // let startup finish first
|
|
@@ -38,11 +44,34 @@ function describeAgo(time, now = Date.now()) {
|
|
|
38
44
|
return `${Math.floor(hours / 24)} days ago`;
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Compare two x.y.z versions numerically (pre-release and build suffixes ignored).
|
|
49
|
+
* Returns <0, 0 or >0, or null if either can't be read
|
|
50
|
+
*/
|
|
51
|
+
function compareVersions(a, b) {
|
|
52
|
+
const parse = v => {
|
|
53
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(String(v || '').trim());
|
|
54
|
+
return m ? m.slice(1).map(Number) : null;
|
|
55
|
+
};
|
|
56
|
+
const x = parse(a);
|
|
57
|
+
const y = parse(b);
|
|
58
|
+
if (!x || !y) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
for (let i = 0; i < 3; i++) {
|
|
62
|
+
if (x[i] !== y[i]) {
|
|
63
|
+
return x[i] - y[i];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
41
69
|
class NpmAudit {
|
|
42
70
|
constructor(config = {}, logger = null, appDir = path.join(__dirname, '..'), stats = null) {
|
|
43
71
|
this.enabled = config.enabled !== false;
|
|
44
72
|
this.intervalHours = config.intervalHours > 0 ? config.intervalHours : 24;
|
|
45
73
|
this.url = config.url || DEFAULT_URL;
|
|
74
|
+
this.latestUrl = config.latestUrl || DEFAULT_LATEST_URL;
|
|
46
75
|
this.timeout = config.timeout || 60000;
|
|
47
76
|
this.logger = logger;
|
|
48
77
|
this.appDir = appDir;
|
|
@@ -55,6 +84,8 @@ class NpmAudit {
|
|
|
55
84
|
this.findings = []; // [{name, installed: [versions], id, severity, title, url, range}]
|
|
56
85
|
this.lastError = null; // message from the last attempt, if it failed
|
|
57
86
|
this.lastAttempt = null;
|
|
87
|
+
this.installedVersion = null; // this server's fhirsmith version, from package.json
|
|
88
|
+
this.latestVersion = null; // the latest fhirsmith release on npm, if it could be found
|
|
58
89
|
}
|
|
59
90
|
|
|
60
91
|
/**
|
|
@@ -93,6 +124,7 @@ class NpmAudit {
|
|
|
93
124
|
const pkg = JSON.parse(fs.readFileSync(path.join(this.appDir, 'package.json'), 'utf8'));
|
|
94
125
|
if (pkg.name && pkg.version) {
|
|
95
126
|
add(pkg.name, pkg.version);
|
|
127
|
+
this.installedVersion = pkg.version;
|
|
96
128
|
}
|
|
97
129
|
} catch (e) {
|
|
98
130
|
// no package.json - nothing to add
|
|
@@ -153,6 +185,39 @@ class NpmAudit {
|
|
|
153
185
|
return true;
|
|
154
186
|
}
|
|
155
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Look up the latest fhirsmith release. A failure only means the advice is less specific,
|
|
190
|
+
* so it is logged and otherwise ignored (the previous answer is kept)
|
|
191
|
+
*/
|
|
192
|
+
async checkLatest() {
|
|
193
|
+
try {
|
|
194
|
+
const response = await axios.get(this.latestUrl, { timeout: this.timeout, headers: { 'Accept': 'application/json' } });
|
|
195
|
+
const version = response.data && response.data.version;
|
|
196
|
+
if (typeof version === 'string' && compareVersions(version, version) === 0) {
|
|
197
|
+
this.latestVersion = version;
|
|
198
|
+
}
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (this.logger) {
|
|
201
|
+
this.logger.info('npm audit: could not look up the latest FHIRsmith release: ' + error.message);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* What the administrator can do about the findings
|
|
208
|
+
*/
|
|
209
|
+
advice() {
|
|
210
|
+
const cmp = compareVersions(this.installedVersion, this.latestVersion);
|
|
211
|
+
if (cmp === null) {
|
|
212
|
+
return 'The server administrator should upgrade to the latest FHIRsmith release.';
|
|
213
|
+
}
|
|
214
|
+
if (cmp < 0) {
|
|
215
|
+
return `The server administrator should upgrade to FHIRsmith ${this.latestVersion} (this server is running ${this.installedVersion}).`;
|
|
216
|
+
}
|
|
217
|
+
return `This server is already running the latest FHIRsmith release (${this.installedVersion}), ` +
|
|
218
|
+
'so these will be fixed by a future FHIRsmith release.';
|
|
219
|
+
}
|
|
220
|
+
|
|
156
221
|
async run() {
|
|
157
222
|
if (this.running) {
|
|
158
223
|
return;
|
|
@@ -190,6 +255,7 @@ class NpmAudit {
|
|
|
190
255
|
this.lastError = null;
|
|
191
256
|
const summary = this.summary();
|
|
192
257
|
if (findings.length > 0) {
|
|
258
|
+
await this.checkLatest();
|
|
193
259
|
if (this.logger) {
|
|
194
260
|
this.logger.warn(`npm audit: ${summary} in ${this.packageCount} packages: ` +
|
|
195
261
|
findings.map(f => `${f.name} (${f.severity}) ${f.url}`).join('; '));
|
|
@@ -278,7 +344,7 @@ class NpmAudit {
|
|
|
278
344
|
return `<div class="alert ${cls}" role="alert"><strong>Security:</strong> npm audit found ` +
|
|
279
345
|
`${escape(this.summary())} in the packages this server uses ` +
|
|
280
346
|
`(checked ${escape(describeAgo(this.checkedAt.getTime(), now))}). ` +
|
|
281
|
-
|
|
347
|
+
`${escape(this.advice())} ` +
|
|
282
348
|
'<a href="/dashboard#npm-audit">Details</a></div>';
|
|
283
349
|
}
|
|
284
350
|
|
|
@@ -303,6 +369,7 @@ class NpmAudit {
|
|
|
303
369
|
}
|
|
304
370
|
html += '</p>';
|
|
305
371
|
if (this.findings.length > 0) {
|
|
372
|
+
html += `<p>${escape(this.advice())}</p>`;
|
|
306
373
|
html += '<table class="grid"><tr><th>Severity</th><th>Package</th><th>Installed</th><th>Vulnerable</th><th>Advisory</th></tr>';
|
|
307
374
|
for (const f of this.findings) {
|
|
308
375
|
const link = /^https:\/\//.test(f.url) ? `<a href="${escape(f.url)}">${escape(f.title || f.url)}</a>` : escape(f.title);
|
|
@@ -315,4 +382,4 @@ class NpmAudit {
|
|
|
315
382
|
}
|
|
316
383
|
}
|
|
317
384
|
|
|
318
|
-
module.exports = { NpmAudit, describeAgo };
|
|
385
|
+
module.exports = { NpmAudit, describeAgo, compareVersions };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// library/sqlite-session-store.js
|
|
2
|
+
// An express-session store kept in a table of an already-open sqlite3 database.
|
|
3
|
+
//
|
|
4
|
+
// This replaces connect-sqlite3, which depends on sqlite3 ^5 and so brought a second, old
|
|
5
|
+
// copy of sqlite3 (and with it node-gyp 8 and tar 6) into every npm install of FHIRsmith.
|
|
6
|
+
// Our package.json "overrides" hid that from npm audit in the repo, but npm ignores
|
|
7
|
+
// overrides for anyone installing the published package.
|
|
8
|
+
//
|
|
9
|
+
// The table layout is the one connect-sqlite3 used - (sid PRIMARY KEY, expired, sess),
|
|
10
|
+
// with expired in epoch ms and sess as JSON - so sessions in an existing database carry over.
|
|
11
|
+
|
|
12
|
+
const session = require('express-session');
|
|
13
|
+
|
|
14
|
+
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
15
|
+
const TABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16
|
+
|
|
17
|
+
class SqliteSessionStore extends session.Store {
|
|
18
|
+
/**
|
|
19
|
+
* @param {object} options
|
|
20
|
+
* @param {object} options.db - an open sqlite3 Database; the caller owns it and closes it
|
|
21
|
+
* @param {string} [options.table='sessions']
|
|
22
|
+
* @param {number} [options.cleanupIntervalMs=one day] - how often expired sessions are deleted
|
|
23
|
+
*/
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
super();
|
|
26
|
+
if (!options.db) {
|
|
27
|
+
throw new Error('SqliteSessionStore needs an open sqlite3 database');
|
|
28
|
+
}
|
|
29
|
+
const table = options.table || 'sessions';
|
|
30
|
+
if (!TABLE_NAME.test(table)) {
|
|
31
|
+
throw new Error('Invalid session table name: ' + table);
|
|
32
|
+
}
|
|
33
|
+
this.db = options.db;
|
|
34
|
+
this.table = table;
|
|
35
|
+
this.timer = null;
|
|
36
|
+
this.ready = this.run(`CREATE TABLE IF NOT EXISTS ${table} (sid PRIMARY KEY, expired, sess)`)
|
|
37
|
+
.then(() => this.cleanup());
|
|
38
|
+
this.ready.catch(() => { /* reported by the first operation that waits on it */ });
|
|
39
|
+
const interval = options.cleanupIntervalMs > 0 ? options.cleanupIntervalMs : ONE_DAY_MS;
|
|
40
|
+
this.timer = setInterval(() => void this.cleanup().catch(() => {}), interval);
|
|
41
|
+
if (this.timer.unref) {
|
|
42
|
+
this.timer.unref();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
run(sql, params = []) {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
this.db.run(sql, params, function (err) {
|
|
49
|
+
if (err) {
|
|
50
|
+
reject(err);
|
|
51
|
+
} else {
|
|
52
|
+
resolve(this);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
query(method, sql, params = []) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
this.db[method](sql, params, (err, result) => err ? reject(err) : resolve(result));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// adapt a promise to express-session's callback style
|
|
65
|
+
done(promise, cb) {
|
|
66
|
+
promise.then(result => cb && cb(null, result), err => cb && cb(err));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
cleanup() {
|
|
70
|
+
return this.run(`DELETE FROM ${this.table} WHERE expired < ?`, [Date.now()]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static expiry(sess) {
|
|
74
|
+
if (sess && sess.cookie) {
|
|
75
|
+
if (sess.cookie.expires) {
|
|
76
|
+
const t = new Date(sess.cookie.expires).getTime();
|
|
77
|
+
if (!isNaN(t)) {
|
|
78
|
+
return t;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (sess.cookie.maxAge) {
|
|
82
|
+
return Date.now() + sess.cookie.maxAge;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return Date.now() + ONE_DAY_MS;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get(sid, cb) {
|
|
89
|
+
this.done(this.ready
|
|
90
|
+
.then(() => this.query('get', `SELECT sess FROM ${this.table} WHERE sid = ? AND expired >= ?`, [sid, Date.now()]))
|
|
91
|
+
.then(row => row ? JSON.parse(row.sess) : null), cb);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
set(sid, sess, cb) {
|
|
95
|
+
this.done(this.ready
|
|
96
|
+
.then(() => this.run(`INSERT OR REPLACE INTO ${this.table} (sid, expired, sess) VALUES (?, ?, ?)`,
|
|
97
|
+
[sid, SqliteSessionStore.expiry(sess), JSON.stringify(sess)]))
|
|
98
|
+
.then(() => undefined), cb);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
touch(sid, sess, cb) {
|
|
102
|
+
this.done(this.ready
|
|
103
|
+
.then(() => this.run(`UPDATE ${this.table} SET expired = ? WHERE sid = ? AND expired >= ?`,
|
|
104
|
+
[SqliteSessionStore.expiry(sess), sid, Date.now()]))
|
|
105
|
+
.then(() => undefined), cb);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
destroy(sid, cb) {
|
|
109
|
+
this.done(this.ready
|
|
110
|
+
.then(() => this.run(`DELETE FROM ${this.table} WHERE sid = ?`, [sid]))
|
|
111
|
+
.then(() => undefined), cb);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
length(cb) {
|
|
115
|
+
this.done(this.ready
|
|
116
|
+
.then(() => this.query('get', `SELECT COUNT(*) AS count FROM ${this.table} WHERE expired >= ?`, [Date.now()]))
|
|
117
|
+
.then(row => row.count), cb);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
clear(cb) {
|
|
121
|
+
this.done(this.ready.then(() => this.run(`DELETE FROM ${this.table}`)).then(() => undefined), cb);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Stop the cleanup timer. The database itself belongs to the caller. */
|
|
125
|
+
close() {
|
|
126
|
+
if (this.timer) {
|
|
127
|
+
clearInterval(this.timer);
|
|
128
|
+
this.timer = null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = { SqliteSessionStore };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fhirsmith",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"txVersion": "1.9.6",
|
|
5
5
|
"description": "A Node.js server that provides a collection of tools to serve the FHIR ecosystem",
|
|
6
6
|
"main": "server.js",
|
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node server.js",
|
|
12
|
-
"dev": "
|
|
12
|
+
"dev": "node --watch server.js",
|
|
13
13
|
"test": "jest",
|
|
14
14
|
"test:watch": "jest --watch",
|
|
15
15
|
"test:coverage": "jest --coverage",
|
|
16
16
|
"test:ci": "jest --ci --coverage --watchAll=false --reporters=default --reporters=jest-junit --verbose",
|
|
17
|
-
"test:shl": "jest --
|
|
18
|
-
"test:vcl": "jest --
|
|
19
|
-
"test:xig": "jest --
|
|
20
|
-
"test:packages": "jest --
|
|
21
|
-
"test:token": "jest --
|
|
22
|
-
"test:integration": "jest --
|
|
23
|
-
"test:unit": "jest --
|
|
17
|
+
"test:shl": "jest --testPathPatterns=shl",
|
|
18
|
+
"test:vcl": "jest --testPathPatterns=vcl",
|
|
19
|
+
"test:xig": "jest --testPathPatterns=xig",
|
|
20
|
+
"test:packages": "jest --testPathPatterns=packages",
|
|
21
|
+
"test:token": "jest --testPathPatterns=token",
|
|
22
|
+
"test:integration": "jest --testPathPatterns=integration",
|
|
23
|
+
"test:unit": "jest --testPathPatterns=unit",
|
|
24
24
|
"lint": "eslint . --ext .js --ignore-path .gitignore",
|
|
25
25
|
"lint:fix": "eslint . --fix --ignore-pattern 'static/assets/js/html5shiv.js'",
|
|
26
26
|
"lint:relaxed": "eslint . --config .eslintrc.relaxed.js",
|
|
@@ -41,21 +41,20 @@
|
|
|
41
41
|
"cli-progress": "^3.12.0",
|
|
42
42
|
"commander": "^14.0.3",
|
|
43
43
|
"commonmark": "^0.31.2",
|
|
44
|
-
"connect-sqlite3": "^0.9.18",
|
|
45
44
|
"cors": "^2.8.6",
|
|
46
45
|
"express": "^5.2.1",
|
|
47
46
|
"express-rate-limit": "^7.4.1",
|
|
48
47
|
"express-session": "^1.19.0",
|
|
49
48
|
"fast-xml-parser": "^5.3.4",
|
|
50
49
|
"fhir-validator-wrapper": "1.4.0",
|
|
51
|
-
"fhirpath": "^
|
|
50
|
+
"fhirpath": "^5.2.1",
|
|
52
51
|
"fs-extra": "^11.3.3",
|
|
53
52
|
"ini": "^6.0.0",
|
|
54
53
|
"inquirer": "^8.2.5",
|
|
55
54
|
"liquidjs": "^10.25.5",
|
|
56
55
|
"lusca": "^1.7.0",
|
|
57
|
-
"natural": "^
|
|
58
|
-
"node-cron": "^
|
|
56
|
+
"natural": "^8.1.1",
|
|
57
|
+
"node-cron": "^4.6.0",
|
|
59
58
|
"pako": "^2.1.0",
|
|
60
59
|
"passport": "^0.7.0",
|
|
61
60
|
"passport-facebook": "^3.0.0",
|
|
@@ -71,18 +70,17 @@
|
|
|
71
70
|
"yaml": "^2.8.2"
|
|
72
71
|
},
|
|
73
72
|
"devDependencies": {
|
|
74
|
-
"@types/jest": "^
|
|
73
|
+
"@types/jest": "^30.0.0",
|
|
75
74
|
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
76
75
|
"@typescript-eslint/parser": "^8.54.0",
|
|
77
76
|
"ajv": "^8.20.0",
|
|
78
77
|
"eslint": "^8.57.1",
|
|
79
78
|
"eslint-plugin-promise": "^7.2.1",
|
|
80
|
-
"jest": "^
|
|
81
|
-
"jest-environment-node": "^
|
|
82
|
-
"jest-junit": "^
|
|
79
|
+
"jest": "^30.5.2",
|
|
80
|
+
"jest-environment-node": "^30.5.2",
|
|
81
|
+
"jest-junit": "^17.0.0",
|
|
83
82
|
"mock-fs": "^5.5.0",
|
|
84
83
|
"nock": "^13.4.0",
|
|
85
|
-
"nodemon": "^3.1.11",
|
|
86
84
|
"supertest": "^7.2.2",
|
|
87
85
|
"tmp": "^0.2.5"
|
|
88
86
|
},
|
|
@@ -107,16 +105,8 @@
|
|
|
107
105
|
"author": "Health Intersections Pty Ltd",
|
|
108
106
|
"license": "BSD-3",
|
|
109
107
|
"overrides": {
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
"inquirer": {
|
|
113
|
-
"lodash": "4.18.1"
|
|
114
|
-
},
|
|
115
|
-
"brace-expansion": "^5.0.8",
|
|
116
|
-
"csv-parse": "^7.0.3",
|
|
117
|
-
"uuid": "^11.1.1",
|
|
118
|
-
"connect-sqlite3": {
|
|
119
|
-
"sqlite3": "$sqlite3"
|
|
108
|
+
"@istanbuljs/load-nyc-config": {
|
|
109
|
+
"js-yaml": "^4.1.0"
|
|
120
110
|
}
|
|
121
111
|
},
|
|
122
112
|
"bin": {
|
package/token/token.js
CHANGED
|
@@ -11,7 +11,6 @@ const crypto = require('crypto');
|
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const session = require('express-session');
|
|
14
|
-
const SQLiteStore = require('connect-sqlite3')(session);
|
|
15
14
|
const passport = require('passport');
|
|
16
15
|
const GoogleStrategy = require('passport-google-oauth20').Strategy;
|
|
17
16
|
const FacebookStrategy = require('passport-facebook').Strategy;
|
|
@@ -19,6 +18,7 @@ const GitHubStrategy = require('passport-github2').Strategy;
|
|
|
19
18
|
const rateLimit = require('express-rate-limit');
|
|
20
19
|
const lusca = require('lusca');
|
|
21
20
|
const folders = require('../library/folder-setup');
|
|
21
|
+
const { SqliteSessionStore } = require('../library/sqlite-session-store');
|
|
22
22
|
const escape = require('escape-html');
|
|
23
23
|
|
|
24
24
|
|
|
@@ -155,17 +155,11 @@ class TokenModule {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
initializeSession() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
? configDb
|
|
161
|
-
: folders.ensureFilePath('token', configDb);
|
|
158
|
+
// sessions live in the module's own database (initializeDatabase has opened it)
|
|
159
|
+
this.sessionStore = new SqliteSessionStore({ db: this.db, table: 'sessions' });
|
|
162
160
|
|
|
163
161
|
const sessionConfig = {
|
|
164
|
-
store:
|
|
165
|
-
dir: path.dirname(sessionDbPath),
|
|
166
|
-
db: path.basename(sessionDbPath),
|
|
167
|
-
table: 'sessions'
|
|
168
|
-
}),
|
|
162
|
+
store: this.sessionStore,
|
|
169
163
|
secret: this.config.sessionSecret || crypto.randomBytes(64).toString('hex'),
|
|
170
164
|
name: 'fhir.token.sid', // Don't use default session name
|
|
171
165
|
resave: false,
|
|
@@ -1287,6 +1281,9 @@ class TokenModule {
|
|
|
1287
1281
|
}
|
|
1288
1282
|
|
|
1289
1283
|
async shutdown() {
|
|
1284
|
+
if (this.sessionStore) {
|
|
1285
|
+
this.sessionStore.close();
|
|
1286
|
+
}
|
|
1290
1287
|
if (this.db) {
|
|
1291
1288
|
await new Promise((resolve) => {
|
|
1292
1289
|
this.db.close((err) => {
|
|
@@ -780,7 +780,7 @@ class RxNormImporter {
|
|
|
780
780
|
if (this.options.verbose) console.log('Generating word stems...');
|
|
781
781
|
|
|
782
782
|
// Simple English stemmer implementation
|
|
783
|
-
var natural = require('natural');
|
|
783
|
+
var natural = require('natural/lib/natural/stemmers'); // just the stemmers: the package index also loads its storage backends, and dotenv with them
|
|
784
784
|
const stems = new Map();
|
|
785
785
|
|
|
786
786
|
// Get all RXNORM concepts
|
|
@@ -4,7 +4,7 @@ const inquirer = require('inquirer');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const readline = require('readline');
|
|
6
6
|
const chalk = require('chalk');
|
|
7
|
-
const natural = require('natural');
|
|
7
|
+
const natural = require('natural/lib/natural/stemmers'); // just the stemmers: the package index also loads its storage backends, and dotenv with them
|
|
8
8
|
|
|
9
9
|
const {
|
|
10
10
|
SnomedStrings, SnomedWords, SnomedStems, SnomedReferences,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { LanguagePartType, Languages, Language, LanguageDefinitions} = require('../../library/languages');
|
|
2
2
|
const {validateParameter, validateOptionalParameter, validateArrayParameter} = require("../../library/utilities");
|
|
3
|
-
const natural = require('natural');
|
|
3
|
+
const natural = require('natural/lib/natural/stemmers'); // just the stemmers: the package index also loads its storage backends, and dotenv with them
|
|
4
4
|
/**
|
|
5
5
|
* Display checking modes for concept designations
|
|
6
6
|
*/
|
package/tx/sct/text-index.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* event loop as it goes, and a search takes milliseconds once it exists.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
const natural = require('natural');
|
|
30
|
+
const natural = require('natural/lib/natural/stemmers'); // just the stemmers: the package index also loads its storage backends, and dotenv with them
|
|
31
31
|
|
|
32
32
|
const stemmer = natural.PorterStemmer;
|
|
33
33
|
|