fhirsmith 0.18.2 → 1.0.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/ACCESSIBILITY.md +119 -0
- package/CHANGELOG.md +149 -0
- package/CONFIGURATION.md +305 -0
- package/LICENSE +29 -0
- package/README.md +29 -122
- package/cluster.js +1220 -0
- package/cluster.md +707 -0
- package/config-template.json +77 -8
- package/extension-tracker/extension-tracker-template.html +5 -8
- package/extension-tracker/extension-tracker.js +0 -2
- package/folder/folder.js +7 -6
- package/ips/analysis.js +560 -0
- package/ips/ips-template.html +5 -8
- package/ips/ips.js +132 -59
- package/ips/nationality.js +120 -0
- package/ips/pipeline.js +74 -21
- package/ips/presenter.js +1 -1
- package/ips/readme.md +132 -25
- package/ips/render.js +466 -77
- package/ips/store.js +79 -22
- package/ips/structure.js +247 -0
- package/ips/terminology.js +1 -1
- package/library/body-limit.js +22 -0
- package/library/cache-generation.js +15 -0
- package/library/cgroup-memory.js +133 -1
- package/library/cluster-counts.js +121 -0
- package/library/cluster-events.js +153 -0
- package/library/cluster-metrics.js +193 -0
- package/library/cluster-status.js +468 -0
- package/library/config-reader.js +96 -0
- package/library/daily-report.js +100 -2
- package/library/html-server.js +27 -1
- package/library/i18nsupport.js +23 -1
- package/library/keep-alive.js +18 -0
- package/library/languages.js +17 -3
- package/library/logger.js +31 -0
- package/library/memory-guard.js +428 -0
- package/library/native-memory.js +52 -0
- package/library/npm-audit.js +51 -25
- package/library/openapi-doc.js +7 -5
- package/library/package-manager.js +8 -0
- package/library/role.js +197 -0
- package/library/server-credentials.js +169 -0
- package/library/sqlite-utilities.js +59 -0
- package/library/ssrf-guard.js +100 -0
- package/library/static-response-stats.js +16 -0
- package/library/version-utilities.js +50 -0
- package/library/worker-count.js +192 -0
- package/nginx.md +30 -11
- package/npmprojector/indexer.js +18 -10
- package/package.json +11 -8
- package/packages/feed-check-page.js +117 -0
- package/packages/feed-check.js +279 -0
- package/packages/npm-check.js +57 -3
- package/packages/npm-signing.js +219 -0
- package/packages/openapi.yaml +207 -1
- package/packages/package-crawler.js +322 -41
- package/packages/packages-template.html +6 -8
- package/packages/packages.js +604 -57
- package/packages/readme.md +113 -1
- package/publisher/publisher-template.html +2 -5
- package/publisher/publisher.js +121 -110
- package/publisher/readme.md +75 -0
- package/registry/crawler.js +84 -108
- package/registry/documentation/configuration.md +34 -0
- package/registry/registry-template.html +5 -8
- package/registry/registry.js +5 -4
- package/root-bare-template.html +7 -4
- package/root-template.html +5 -8
- package/server.js +398 -198
- package/shl/shl.js +38 -37
- package/static/a11y/a11y-check.js +832 -0
- package/static/a11y/axe.min.js +12 -0
- package/static/assets/css/bootstrap-fhir.css +15 -15
- package/static/assets/images/flags/au.svg +18 -0
- package/static/assets/images/flags/unknown.svg +11 -0
- package/static/assets/images/flags/us.svg +63 -0
- package/static/assets/images/icon_audit.png +0 -0
- package/static/assets/images/icon_clinical.png +0 -0
- package/static/assets/images/icon_document.png +0 -0
- package/static/assets/images/icon_external.png +0 -0
- package/static/assets/images/icon_medication.png +0 -0
- package/static/assets/images/icon_organization.png +0 -0
- package/static/assets/images/icon_other.png +0 -0
- package/static/assets/images/icon_person.png +0 -0
- package/static/assets/images/icon_section.png +0 -0
- package/stats-db.js +45 -0
- package/stats.js +197 -10
- package/testing/testing-template.html +5 -8
- package/token/nginx_passport_setup.md +1 -1
- package/token/security_guide.md +1 -1
- package/token/token-template.html +2 -5
- package/token/token.js +40 -36
- package/translations/Messages.properties +19 -0
- package/tx/README.md +154 -4
- package/tx/closure/closure-store.js +126 -3
- package/tx/cm/cm-database.js +69 -57
- package/tx/cm/cm-package.js +29 -1
- package/tx/cs/cs-api.js +284 -19
- package/tx/cs/cs-api.md +41 -4
- package/tx/cs/cs-areacode.js +1 -1
- package/tx/cs/cs-country.js +17 -5
- package/tx/cs/cs-cpt.js +33 -16
- package/tx/cs/cs-cs.js +148 -14
- package/tx/cs/cs-currency.js +1 -1
- package/tx/cs/cs-icd11.js +21 -13
- package/tx/cs/cs-icf.js +22 -3
- package/tx/cs/cs-lang.js +2 -2
- package/tx/cs/cs-loinc.js +185 -66
- package/tx/cs/cs-mimetypes.js +12 -2
- package/tx/cs/cs-ndc.js +40 -25
- package/tx/cs/cs-omop.js +54 -29
- package/tx/cs/cs-rxnorm.js +300 -196
- package/tx/cs/cs-snomed.js +608 -64
- package/tx/cs/cs-ucum.js +14 -4
- package/tx/cs/cs-unii.js +18 -14
- package/tx/cs/cs-virtual.js +210 -0
- package/tx/ecosystem-resolver.js +413 -0
- package/tx/html/cluster-dash-metrics.liquid +26 -0
- package/tx/html/cluster-home-metrics.liquid +31 -0
- package/tx/html/cluster-metrics-script.liquid +251 -0
- package/tx/html/codesystem-operations.liquid +3 -3
- package/tx/html/ecl-panel.liquid +1 -1
- package/tx/html/external-metrics.liquid +17 -0
- package/tx/html/operations-form.liquid +17 -17
- package/tx/html/search-form.liquid +11 -11
- package/tx/html/status-line.liquid +58 -0
- package/tx/html/status-panel.liquid +140 -0
- package/tx/html/status-tasks.liquid +25 -0
- package/tx/html/tx-template.html +2 -5
- package/tx/importers/atc-to-fhir.js +17 -16
- package/tx/importers/export-icd11-codesystem.js +3 -2
- package/tx/importers/import-icd11.js +4 -3
- package/tx/importers/import-loinc.module.js +118 -56
- package/tx/importers/import-ndc.module.js +56 -44
- package/tx/importers/import-rxnorm.module.js +118 -79
- package/tx/importers/import-sct.module.js +57 -58
- package/tx/importers/import-unii.module.js +37 -36
- package/tx/importers/subset-loinc.module.js +49 -48
- package/tx/importers/subset-rxnorm.module.js +29 -28
- package/tx/importers/terminal.js +18 -0
- package/tx/importers/tx-import-base.js +13 -12
- package/tx/importers/tx-import-settings.js +5 -4
- package/tx/importers/tx-import.js +53 -52
- package/tx/library/canonical-resource.js +49 -0
- package/tx/library/codesystem.js +94 -5
- package/tx/library/designations.js +45 -2
- package/tx/library/extensions.js +13 -7
- package/tx/library/implicit-valuesets.js +66 -0
- package/tx/library/renderer.js +77 -21
- package/tx/library/sqlite-utilities.js +2 -0
- package/tx/library/ucum-parsers.js +11 -1
- package/tx/library/wait-for-cache.js +43 -0
- package/tx/library.js +321 -22
- package/tx/openapi-operations.js +6 -4
- package/tx/operation-context.js +460 -99
- package/tx/params.js +25 -0
- package/tx/problems.js +3 -5
- package/tx/provider.js +278 -42
- package/tx/resource-cache-sqlite.js +1050 -0
- package/tx/sct/ecl.js +47 -6
- package/tx/sct/editions.js +39 -0
- package/tx/sct/expressions.js +10 -1
- package/tx/sct/load-config.js +107 -0
- package/tx/sct/mmap.js +109 -0
- package/tx/sct/structures.js +166 -13
- package/tx/sct/text-index.js +5 -1
- package/tx/syndication/syndication-feed.js +2 -2
- package/tx/tx-html.js +44 -8
- package/tx/tx.fhir.org.yml +8 -8
- package/tx/tx.js +282 -80
- package/tx/usage-tracker.js +39 -11
- package/tx/vs/vs-api.js +19 -0
- package/tx/vs/vs-database.js +386 -62
- package/tx/vs/vs-package.js +27 -4
- package/tx/vs/vs-vsac.js +648 -100
- package/tx/workers/batch-validate.js +37 -10
- package/tx/workers/batch.js +6 -1
- package/tx/workers/cache-control.js +15 -4
- package/tx/workers/closure.js +156 -13
- package/tx/workers/compare.js +8 -5
- package/tx/workers/expand.js +1173 -289
- package/tx/workers/metadata.js +35 -0
- package/tx/workers/search.js +53 -1
- package/tx/workers/supplement-filters.js +145 -0
- package/tx/workers/translate.js +169 -4
- package/tx/workers/validate.js +223 -119
- package/tx/workers/worker.js +123 -31
- package/tx/xversion/xv-valueset.js +7 -4
- package/utilities/submit-test-report.js +3 -1
- package/utilities/tx-stress-test.js +482 -0
- package/vcl/readme.md +37 -0
- package/vcl/vcl-parser.js +15 -0
- package/xig/readme.md +52 -0
- package/xig/xig-template.html +5 -8
- package/xig/xig.js +238 -422
- package/changelog-template.md +0 -22
- package/registry/test-runner.js +0 -237
- package/release.sh +0 -80
- package/test-scripts/repro-re2-wasm-leak.js +0 -34
- package/tsconfig.eslint.json +0 -9
- package/tx/html/dash-metrics.liquid +0 -147
- package/tx/html/home-metrics.liquid +0 -225
package/tx/workers/worker.js
CHANGED
|
@@ -174,28 +174,45 @@ class TerminologyWorker {
|
|
|
174
174
|
if (!noVParams) {
|
|
175
175
|
version = this.determineVersionBase(url, version, params);
|
|
176
176
|
}
|
|
177
|
-
let codeSystemResource = null;
|
|
178
|
-
let provider = null;
|
|
179
|
-
const supplements = this.loadSupplements(url, version, statedSupplements);
|
|
180
|
-
|
|
181
177
|
// First check additional resources
|
|
182
|
-
codeSystemResource = this.findInAdditionalResources(url, version, 'CodeSystem', !nullOk);
|
|
178
|
+
const codeSystemResource = this.findInAdditionalResources(url, version, 'CodeSystem', !nullOk);
|
|
183
179
|
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
const build = async (supplements) => {
|
|
181
|
+
let provider = null;
|
|
182
|
+
if (codeSystemResource && codeSystemResource.content === 'complete') {
|
|
186
183
|
// Create provider from complete code system
|
|
187
184
|
provider = await this.provider.createCodeSystemProvider(this.opContext, codeSystemResource, supplements);
|
|
188
185
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
186
|
+
// If no provider from additional resources, try main provider
|
|
187
|
+
if (!provider) {
|
|
188
|
+
provider = await this.provider.getCodeSystemProvider(this.opContext, url, version, supplements);
|
|
189
|
+
}
|
|
190
|
+
// If still no provider but we have a code system with allowed content mode
|
|
191
|
+
if (!provider && codeSystemResource && kinds.includes(codeSystemResource.content)) {
|
|
192
|
+
provider = await this.provider.createCodeSystemProvider(this.opContext, codeSystemResource, supplements);
|
|
193
|
+
}
|
|
194
|
+
// Track for lifecycle cleanup at end of request, at once: what follows can throw (a client
|
|
195
|
+
// that has gone, in loadSupplements). Providers built from a factory open a fresh sqlite
|
|
196
|
+
// connection that needs releasing; resource-built providers and others are no-ops.
|
|
197
|
+
this.opContext.registerProvider(provider);
|
|
198
|
+
return provider;
|
|
199
|
+
};
|
|
195
200
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
201
|
+
const supplements = this.loadSupplements(url, version, statedSupplements);
|
|
202
|
+
let provider = await build(supplements);
|
|
203
|
+
|
|
204
|
+
// The supplements were chosen for the version asked for. When no version was asked for (or
|
|
205
|
+
// only part of one), it's the version the provider turned out to be that decides which apply:
|
|
206
|
+
// a supplement pinned to another version of the code system (supplements = url|2.80 on
|
|
207
|
+
// LOINC 2.83) is not used - so a value set that requires it fails - and one pinned to this
|
|
208
|
+
// version is
|
|
209
|
+
const actual = provider ? provider.version() : null;
|
|
210
|
+
if (actual && actual !== version) {
|
|
211
|
+
const fitting = this.loadSupplements(url, actual, statedSupplements);
|
|
212
|
+
const key = (list) => list.map(cs => cs.vurl || cs.url).sort().join(' ');
|
|
213
|
+
if (key(fitting) !== key(supplements)) {
|
|
214
|
+
provider = await build(fitting);
|
|
215
|
+
}
|
|
199
216
|
}
|
|
200
217
|
|
|
201
218
|
if (!provider && !nullOk) {
|
|
@@ -210,14 +227,8 @@ class TerminologyWorker {
|
|
|
210
227
|
}
|
|
211
228
|
}
|
|
212
229
|
}
|
|
213
|
-
if (provider) {
|
|
214
|
-
|
|
215
|
-
this.checkVersion(url, provider.version(), params, provider.versionAlgorithm(), op);
|
|
216
|
-
}
|
|
217
|
-
// Track for lifecycle cleanup at end of request. Providers built from
|
|
218
|
-
// a factory open a fresh sqlite connection that needs releasing;
|
|
219
|
-
// resource-built providers and others without close() are no-ops.
|
|
220
|
-
this.opContext.registerProvider(provider);
|
|
230
|
+
if (provider && checkVer) {
|
|
231
|
+
this.checkVersion(url, provider.version(), params, provider.versionAlgorithm(), op);
|
|
221
232
|
}
|
|
222
233
|
|
|
223
234
|
return provider;
|
|
@@ -284,10 +295,52 @@ class TerminologyWorker {
|
|
|
284
295
|
* @returns {Array<CodeSystem>} Supplement code systems
|
|
285
296
|
*/
|
|
286
297
|
loadSupplements(url, version = '', statedSupplements) {
|
|
298
|
+
return this.#oneVersionEach(this.#findSupplements(url, version, statedSupplements), statedSupplements);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Only one version of a supplement applies. When more than one is in scope (the server has
|
|
303
|
+
* one, and the request passes another in tx-resource), the one the request names by version
|
|
304
|
+
* (useSupplement url|version, or a value set's supplement extension) wins; failing that, the
|
|
305
|
+
* request's own over the server's; failing that, the latest
|
|
306
|
+
*
|
|
307
|
+
* @param {CodeSystem[]} supplements - server ones first, then the request's
|
|
308
|
+
* @param {Set<string>} statedSupplements
|
|
309
|
+
* @returns {CodeSystem[]}
|
|
310
|
+
*/
|
|
311
|
+
#oneVersionEach(supplements, statedSupplements) {
|
|
312
|
+
const byUrl = new Map();
|
|
313
|
+
for (const cs of supplements) {
|
|
314
|
+
if (!byUrl.has(cs.url)) {
|
|
315
|
+
byUrl.set(cs.url, []);
|
|
316
|
+
}
|
|
317
|
+
byUrl.get(cs.url).push(cs);
|
|
318
|
+
}
|
|
319
|
+
if ([...byUrl.values()].every(list => list.length === 1)) {
|
|
320
|
+
return supplements;
|
|
321
|
+
}
|
|
322
|
+
const result = [];
|
|
323
|
+
for (const list of byUrl.values()) {
|
|
324
|
+
if (list.length === 1) {
|
|
325
|
+
result.push(list[0]);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
let chosen = statedSupplements ? list.find(cs => cs.version && statedSupplements.has(cs.vurl)) : null;
|
|
329
|
+
if (!chosen) {
|
|
330
|
+
chosen = list.find(cs => (this.additionalResources || []).includes(cs));
|
|
331
|
+
}
|
|
332
|
+
if (!chosen) {
|
|
333
|
+
chosen = list.reduce((a, b) => (VersionUtilities.compareVersionsGeneral(b.version, a.version) > 0 ? b : a));
|
|
334
|
+
}
|
|
335
|
+
result.push(chosen);
|
|
336
|
+
}
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
#findSupplements(url, version, statedSupplements) {
|
|
287
341
|
const supplements = [];
|
|
288
342
|
|
|
289
343
|
supplements.push(...this.provider.loadSupplements(url, version, statedSupplements));
|
|
290
|
-
// todo: look in provider for supplements
|
|
291
344
|
|
|
292
345
|
if (!this.additionalResources) {
|
|
293
346
|
return supplements;
|
|
@@ -298,14 +351,16 @@ class TerminologyWorker {
|
|
|
298
351
|
if (resource.resourceType === 'CodeSystem' && resource instanceof CodeSystem) {
|
|
299
352
|
const cs = resource;
|
|
300
353
|
// Check if this code system supplements the target URL
|
|
301
|
-
|
|
354
|
+
// (supplementsUrl rather than jsonObj.supplements: a resource from the shared
|
|
355
|
+
// resource cache answers it without being loaded - see resource-cache-sqlite.js)
|
|
356
|
+
const supplementsUrl = cs.supplementsUrl;
|
|
302
357
|
|
|
303
358
|
if (!supplementsUrl) {
|
|
304
359
|
continue;
|
|
305
360
|
}
|
|
306
361
|
|
|
307
|
-
// we consider either
|
|
308
|
-
if (!(cs.
|
|
362
|
+
// we consider either global supplements or specified supplements
|
|
363
|
+
if (!(cs.isGlobalSupplement() || (statedSupplements && (statedSupplements.has(cs.url) || statedSupplements.has(cs.vurl))))) {
|
|
309
364
|
continue;
|
|
310
365
|
}
|
|
311
366
|
if (this.hasSupplement(cs, supplements)) {
|
|
@@ -325,7 +380,11 @@ class TerminologyWorker {
|
|
|
325
380
|
} else {
|
|
326
381
|
// Version specified, check if it matches the tail of supplements URL
|
|
327
382
|
const supplementsVersion = supplementsUrl.substring(`${url}|`.length);
|
|
328
|
-
|
|
383
|
+
// not every code system's versions are semver (SNOMED CT's are URIs, ICF's are dates):
|
|
384
|
+
// versionMatches throws on those, so match by the version's own algorithm, as
|
|
385
|
+
// CodeSystem.isSupplementFor does for the server's own supplements
|
|
386
|
+
if (supplementsVersion === version || VersionUtilities.versionMatchesByAlgorithm(supplementsVersion, version,
|
|
387
|
+
VersionUtilities.guessVersionAlgorithmFromVersion(version))) {
|
|
329
388
|
supplements.push(cs);
|
|
330
389
|
}
|
|
331
390
|
}
|
|
@@ -420,7 +479,12 @@ class TerminologyWorker {
|
|
|
420
479
|
|
|
421
480
|
// Then try the provider
|
|
422
481
|
if (this.provider && this.provider.findValueSet) {
|
|
423
|
-
|
|
482
|
+
// an implicit value set in the generic form (http://snomed.info/sct?fhir_vs...) is drawn
|
|
483
|
+
// from the code system version the request asks for, if it asks for one
|
|
484
|
+
const systemVersionFor = this.params && this.params.requestedSystemVersion
|
|
485
|
+
? (system) => this.params.requestedSystemVersion(system)
|
|
486
|
+
: null;
|
|
487
|
+
const vs = await this.provider.findValueSet(this.opContext, effectiveUrl, effectiveVersion, systemVersionFor);
|
|
424
488
|
if (vs) {
|
|
425
489
|
return vs;
|
|
426
490
|
}
|
|
@@ -740,7 +804,7 @@ class TerminologyWorker {
|
|
|
740
804
|
if (!this.opContext.resourceCache.isSealed(cacheId)) {
|
|
741
805
|
const toCache = txResources.concat(primaryResources);
|
|
742
806
|
if (toCache.length > 0) {
|
|
743
|
-
this.
|
|
807
|
+
this.addToClientCache(cacheId, toCache);
|
|
744
808
|
}
|
|
745
809
|
this.additionalResources = this.opContext.resourceCache.get(cacheId);
|
|
746
810
|
} else {
|
|
@@ -756,6 +820,34 @@ class TerminologyWorker {
|
|
|
756
820
|
}
|
|
757
821
|
}
|
|
758
822
|
|
|
823
|
+
/**
|
|
824
|
+
* Add resources to the client side cache cacheId. If that would take the cache over
|
|
825
|
+
* cacheMaxConcepts, the cache is dropped and this throws the 404 cache-id-unknown
|
|
826
|
+
* (CACHE_ID_DISCARDED_TOO_BIG), in the request's language: the operation is not performed.
|
|
827
|
+
* @param {string} cacheId
|
|
828
|
+
* @param {Object[]} resources
|
|
829
|
+
*/
|
|
830
|
+
addToClientCache(cacheId, resources) {
|
|
831
|
+
try {
|
|
832
|
+
this.opContext.resourceCache.add(cacheId, resources);
|
|
833
|
+
} catch (e) {
|
|
834
|
+
throw this.translateCacheIssue(e);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* The resource cache raises its limits (CACHE_ID_DISCARDED_TOO_BIG, CACHE_REQUEST_TOO_BIG,
|
|
840
|
+
* CACHE_START_TOO_BIG) in English, with their parameters: put the message in the request's language
|
|
841
|
+
* @param {Error} e
|
|
842
|
+
* @returns {Error} e
|
|
843
|
+
*/
|
|
844
|
+
translateCacheIssue(e) {
|
|
845
|
+
if (e && e.msgId && e.msgParams) {
|
|
846
|
+
e.message = this.i18n.translate(e.msgId, this.opContext.langs, e.msgParams);
|
|
847
|
+
}
|
|
848
|
+
return e;
|
|
849
|
+
}
|
|
850
|
+
|
|
759
851
|
/**
|
|
760
852
|
* Wrap a raw resource in its appropriate class wrapper
|
|
761
853
|
* @param {Object} resource - Raw resource object
|
|
@@ -160,12 +160,15 @@ function valueSetR5ToR4(r5Obj) {
|
|
|
160
160
|
if (exp.property && exp.property.length > 0) {
|
|
161
161
|
exp.extension = exp.extension || [];
|
|
162
162
|
for (let prop of exp.property) {
|
|
163
|
+
// uri is 0..1: a property without one (a provider that doesn't declare its properties)
|
|
164
|
+
// must not produce an empty uri extension, which is not valid
|
|
165
|
+
const ext = [{ url: "code", valueCode: prop.code }];
|
|
166
|
+
if (prop.uri) {
|
|
167
|
+
ext.push({ url: "uri", valueUri: prop.uri });
|
|
168
|
+
}
|
|
163
169
|
exp.extension.push({
|
|
164
170
|
url: "http://hl7.org/fhir/5.0/StructureDefinition/extension-ValueSet.expansion.property",
|
|
165
|
-
extension:
|
|
166
|
-
{ url: "code", valueCode: prop.code },
|
|
167
|
-
{ url: "uri", valueUri: prop.uri }
|
|
168
|
-
]
|
|
171
|
+
extension: ext
|
|
169
172
|
});
|
|
170
173
|
}
|
|
171
174
|
delete exp.property;
|
|
@@ -120,7 +120,9 @@ async function main() {
|
|
|
120
120
|
console.log(`Submitting ${path.basename(opts.file)} (${report.result}, ${Array.isArray(report.test) ? report.test.length : 0} tests) to ${url}`);
|
|
121
121
|
let res;
|
|
122
122
|
try {
|
|
123
|
-
|
|
123
|
+
// sent compact: the file is pretty-printed (about 1.7x the size), and the server limits
|
|
124
|
+
// the size of a report (npm.fhir.org: about 1.5 MB)
|
|
125
|
+
res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(report) });
|
|
124
126
|
} catch (e) {
|
|
125
127
|
console.error(`Could not reach ${url}: ${e.cause ? e.cause.message : e.message}`);
|
|
126
128
|
process.exit(1);
|