@medplum/core 0.9.10 → 0.9.13
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/cody-pdf-test.js +1 -1
- package/dist/cjs/index.js +406 -337
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +1 -1
- package/dist/cjs/index.min.js.map +1 -1
- package/dist/esm/index.js +398 -338
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +1 -1
- package/dist/esm/index.min.js.map +1 -1
- package/dist/types/client.d.ts +61 -31
- package/dist/types/utils.d.ts +71 -1
- package/package.json +2 -2
- package/dist/types/pdf.d.ts +0 -5
- package/document.pdf +0 -0
- package/test.pdf +0 -1
- package/test2.pdf +0 -1
- package/test3.pdf.txt +0 -1
- package/test4.pdf +0 -1
- package/test5.pdf +0 -0
- package/test7-2.pdf +0 -1
- package/test7.pdf +0 -1
- package/test8-2.pdf +0 -0
- package/test8.pdf +0 -0
- package/test9-2.pdf +0 -0
- package/test9-3.pdf +0 -0
- package/test9-4.pdf +0 -0
- package/test9.pdf +0 -0
package/dist/esm/index.js
CHANGED
|
@@ -566,6 +566,165 @@ function capitalize(word) {
|
|
|
566
566
|
function isLowerCase(c) {
|
|
567
567
|
return c === c.toLowerCase();
|
|
568
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* Tries to find a code string for a given system within a given codeable concept.
|
|
571
|
+
* @param concept The codeable concept.
|
|
572
|
+
* @param system The system string.
|
|
573
|
+
* @returns The code if found; otherwise undefined.
|
|
574
|
+
*/
|
|
575
|
+
function getCodeBySystem(concept, system) {
|
|
576
|
+
var _a, _b;
|
|
577
|
+
return (_b = (_a = concept === null || concept === void 0 ? void 0 : concept.coding) === null || _a === void 0 ? void 0 : _a.find((coding) => coding.system === system)) === null || _b === void 0 ? void 0 : _b.code;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Sets a code for a given system within a given codeable concept.
|
|
581
|
+
* @param concept The codeable concept.
|
|
582
|
+
* @param system The system string.
|
|
583
|
+
* @param code The code value.
|
|
584
|
+
*/
|
|
585
|
+
function setCodeBySystem(concept, system, code) {
|
|
586
|
+
var _a, _b;
|
|
587
|
+
if (!concept.coding) {
|
|
588
|
+
concept.coding = [];
|
|
589
|
+
}
|
|
590
|
+
const coding = (_a = concept.coding) === null || _a === void 0 ? void 0 : _a.find((c) => c.system === system);
|
|
591
|
+
if (coding) {
|
|
592
|
+
coding.code = code;
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
(_b = concept.coding) === null || _b === void 0 ? void 0 : _b.push({ system, code });
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Tries to find an observation interval for the given patient and value.
|
|
600
|
+
* @param definition The observation definition.
|
|
601
|
+
* @param patient The patient.
|
|
602
|
+
* @param value The observation value.
|
|
603
|
+
* @returns The observation interval if found; otherwise undefined.
|
|
604
|
+
*/
|
|
605
|
+
function findObservationInterval(definition, patient, value, category) {
|
|
606
|
+
var _a;
|
|
607
|
+
return (_a = definition.qualifiedInterval) === null || _a === void 0 ? void 0 : _a.find((interval) => {
|
|
608
|
+
var _a;
|
|
609
|
+
return observationIntervalMatchesPatient(interval, patient) &&
|
|
610
|
+
observationIntervalMatchesValue(interval, value, (_a = definition.quantitativeDetails) === null || _a === void 0 ? void 0 : _a.decimalPrecision) &&
|
|
611
|
+
(category === undefined || interval.category === category);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Returns true if the patient matches the observation interval.
|
|
616
|
+
* @param interval The observation interval.
|
|
617
|
+
* @param patient The patient.
|
|
618
|
+
* @returns True if the patient matches the observation interval.
|
|
619
|
+
*/
|
|
620
|
+
function observationIntervalMatchesPatient(interval, patient) {
|
|
621
|
+
return observationIntervalMatchesGender(interval, patient) && observationIntervalMatchesAge(interval, patient);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Returns true if the patient gender matches the observation interval.
|
|
625
|
+
* @param interval The observation interval.
|
|
626
|
+
* @param patient The patient.
|
|
627
|
+
* @returns True if the patient gender matches the observation interval.
|
|
628
|
+
*/
|
|
629
|
+
function observationIntervalMatchesGender(interval, patient) {
|
|
630
|
+
return !interval.gender || interval.gender === patient.gender;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Returns true if the patient age matches the observation interval.
|
|
634
|
+
* @param interval The observation interval.
|
|
635
|
+
* @param patient The patient.
|
|
636
|
+
* @returns True if the patient age matches the observation interval.
|
|
637
|
+
*/
|
|
638
|
+
function observationIntervalMatchesAge(interval, patient) {
|
|
639
|
+
return !interval.age || matchesRange(calculateAge(patient.birthDate).years, interval.age);
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Returns true if the value matches the observation interval.
|
|
643
|
+
* @param interval The observation interval.
|
|
644
|
+
* @param value The observation value.
|
|
645
|
+
* @param precision Optional precision in number of digits.
|
|
646
|
+
* @returns True if the value matches the observation interval.
|
|
647
|
+
*/
|
|
648
|
+
function observationIntervalMatchesValue(interval, value, precision) {
|
|
649
|
+
return !!interval.range && matchesRange(value, interval.range, precision);
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Returns true if the value is in the range accounting for precision.
|
|
653
|
+
* @param value The numeric value.
|
|
654
|
+
* @param range The numeric range.
|
|
655
|
+
* @param precision Optional precision in number of digits.
|
|
656
|
+
* @returns True if the value is within the range.
|
|
657
|
+
*/
|
|
658
|
+
function matchesRange(value, range, precision) {
|
|
659
|
+
var _a, _b;
|
|
660
|
+
return ((((_a = range.low) === null || _a === void 0 ? void 0 : _a.value) === undefined || preciseGreaterThanOrEquals(value, range.low.value, precision)) &&
|
|
661
|
+
(((_b = range.high) === null || _b === void 0 ? void 0 : _b.value) === undefined || preciseLessThanOrEquals(value, range.high.value, precision)));
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Returns true if the two numbers are equal to the given precision.
|
|
665
|
+
* @param a The first number.
|
|
666
|
+
* @param b The second number.
|
|
667
|
+
* @param precision Optional precision in number of digits.
|
|
668
|
+
* @returns True if the two numbers are equal to the given precision.
|
|
669
|
+
*/
|
|
670
|
+
function preciseEquals(a, b, precision) {
|
|
671
|
+
if (precision) {
|
|
672
|
+
return Math.abs(a - b) < Math.pow(10, -precision);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
return a === b;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Returns true if the first number is less than the second number to the given precision.
|
|
680
|
+
* @param a The first number.
|
|
681
|
+
* @param b The second number.
|
|
682
|
+
* @param precision Optional precision in number of digits.
|
|
683
|
+
* @returns True if the first number is less than the second number to the given precision.
|
|
684
|
+
*/
|
|
685
|
+
function preciseLessThan(a, b, precision) {
|
|
686
|
+
if (precision) {
|
|
687
|
+
return a < b && Math.abs(a - b) > Math.pow(10, -precision);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
return a < b;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Returns true if the first number is greater than the second number to the given precision.
|
|
695
|
+
* @param a The first number.
|
|
696
|
+
* @param b The second number.
|
|
697
|
+
* @param precision Optional precision in number of digits.
|
|
698
|
+
* @returns True if the first number is greater than the second number to the given precision.
|
|
699
|
+
*/
|
|
700
|
+
function preciseGreaterThan(a, b, precision) {
|
|
701
|
+
if (precision) {
|
|
702
|
+
return a > b && Math.abs(a - b) > Math.pow(10, -precision);
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
return a > b;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Returns true if the first number is less than or equal to the second number to the given precision.
|
|
710
|
+
* @param a The first number.
|
|
711
|
+
* @param b The second number.
|
|
712
|
+
* @param precision Optional precision in number of digits.
|
|
713
|
+
* @returns True if the first number is less than or equal to the second number to the given precision.
|
|
714
|
+
*/
|
|
715
|
+
function preciseLessThanOrEquals(a, b, precision) {
|
|
716
|
+
return preciseLessThan(a, b, precision) || preciseEquals(a, b, precision);
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Returns true if the first number is greater than or equal to the second number to the given precision.
|
|
720
|
+
* @param a The first number.
|
|
721
|
+
* @param b The second number.
|
|
722
|
+
* @param precision Optional precision in number of digits.
|
|
723
|
+
* @returns True if the first number is greater than or equal to the second number to the given precision.
|
|
724
|
+
*/
|
|
725
|
+
function preciseGreaterThanOrEquals(a, b, precision) {
|
|
726
|
+
return preciseGreaterThan(a, b, precision) || preciseEquals(a, b, precision);
|
|
727
|
+
}
|
|
569
728
|
|
|
570
729
|
/**
|
|
571
730
|
* Returns a cryptographically secure random string.
|
|
@@ -804,106 +963,6 @@ class OperationOutcomeError extends Error {
|
|
|
804
963
|
}
|
|
805
964
|
}
|
|
806
965
|
|
|
807
|
-
/*
|
|
808
|
-
* This file attempts a unified "generatePdf" function that works both client-side and server-side.
|
|
809
|
-
* On client-side, it checks for a global "pdfMake" variable.
|
|
810
|
-
* On server-side, it dynamically loads "pdfmake" from the node_modules.
|
|
811
|
-
*/
|
|
812
|
-
function generatePdf(docDefinition, tableLayouts, fonts) {
|
|
813
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
814
|
-
// Setup sane defaults
|
|
815
|
-
// See: https://pdfmake.github.io/docs/0.1/document-definition-object/styling/
|
|
816
|
-
docDefinition.pageSize = docDefinition.pageSize || 'LETTER';
|
|
817
|
-
docDefinition.pageMargins = docDefinition.pageMargins || [60, 60, 60, 60];
|
|
818
|
-
docDefinition.pageOrientation = docDefinition.pageOrientation || 'portrait';
|
|
819
|
-
docDefinition.defaultStyle = docDefinition.defaultStyle || {};
|
|
820
|
-
docDefinition.defaultStyle.font = docDefinition.defaultStyle.font || 'Helvetica';
|
|
821
|
-
docDefinition.defaultStyle.fontSize = docDefinition.defaultStyle.fontSize || 11;
|
|
822
|
-
docDefinition.defaultStyle.lineHeight = docDefinition.defaultStyle.lineHeight || 2.0;
|
|
823
|
-
if (typeof window !== 'undefined' && typeof pdfMake !== 'undefined') {
|
|
824
|
-
return generatePdfClientSide(docDefinition, tableLayouts, fonts);
|
|
825
|
-
}
|
|
826
|
-
if (typeof process !== 'undefined' && typeof require !== 'undefined') {
|
|
827
|
-
return generatePdfServerSide(docDefinition, tableLayouts, fonts);
|
|
828
|
-
}
|
|
829
|
-
throw new Error('Unable to determine PDF environment');
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
function generatePdfServerSide(docDefinition, tableLayouts, fonts) {
|
|
833
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
834
|
-
if (!fonts) {
|
|
835
|
-
fonts = {
|
|
836
|
-
Helvetica: {
|
|
837
|
-
normal: 'Helvetica',
|
|
838
|
-
bold: 'Helvetica-Bold',
|
|
839
|
-
italics: 'Helvetica-Oblique',
|
|
840
|
-
bolditalics: 'Helvetica-BoldOblique',
|
|
841
|
-
},
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
return new Promise((resolve, reject) => {
|
|
845
|
-
try {
|
|
846
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
847
|
-
const PdfPrinter = require('pdfmake');
|
|
848
|
-
const printer = new PdfPrinter(fonts);
|
|
849
|
-
const pdfDoc = printer.createPdfKitDocument(docDefinition, { tableLayouts });
|
|
850
|
-
const chunks = [];
|
|
851
|
-
pdfDoc.on('data', (chunk) => chunks.push(chunk));
|
|
852
|
-
pdfDoc.on('end', () => resolve(concat(chunks)));
|
|
853
|
-
pdfDoc.on('error', reject);
|
|
854
|
-
pdfDoc.end();
|
|
855
|
-
}
|
|
856
|
-
catch (err) {
|
|
857
|
-
reject(err);
|
|
858
|
-
}
|
|
859
|
-
});
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
function generatePdfClientSide(docDefinition, tableLayouts, fonts) {
|
|
863
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
864
|
-
if (!fonts) {
|
|
865
|
-
fonts = {
|
|
866
|
-
Helvetica: {
|
|
867
|
-
normal: 'https://static.medplum.com/fonts/Helvetica.ttf',
|
|
868
|
-
bold: 'https://static.medplum.com/fonts/Helvetica-bold.ttf',
|
|
869
|
-
italics: 'https://static.medplum.com/fonts/Helvetica-italic.ttf',
|
|
870
|
-
bolditalics: 'https://static.medplum.com/fonts/Helvetica-bold-italic.ttf',
|
|
871
|
-
},
|
|
872
|
-
Roboto: {
|
|
873
|
-
normal: 'https://static.medplum.com/fonts/Roboto-Regular.ttf',
|
|
874
|
-
bold: 'https://static.medplum.com/fonts/Roboto-Medium.ttf',
|
|
875
|
-
italics: 'https://static.medplum.com/fonts/Roboto-Italic.ttf',
|
|
876
|
-
bolditalics: 'https://static.medplum.com/fonts/Roboto-MediumItalic.ttf',
|
|
877
|
-
},
|
|
878
|
-
Avenir: {
|
|
879
|
-
normal: 'https://static.medplum.com/fonts/avenir.ttf',
|
|
880
|
-
},
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
return new Promise((resolve) => {
|
|
884
|
-
pdfMake.createPdf(docDefinition, tableLayouts, fonts).getBlob(resolve);
|
|
885
|
-
});
|
|
886
|
-
});
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* Concatenates an array of Uint8Arrays into a single Uint8Array.
|
|
890
|
-
* @param arrays An array of arrays of bytes.
|
|
891
|
-
* @returns A single array of bytes.
|
|
892
|
-
*/
|
|
893
|
-
function concat(arrays) {
|
|
894
|
-
let len = 0;
|
|
895
|
-
for (const array of arrays) {
|
|
896
|
-
len += array.length;
|
|
897
|
-
}
|
|
898
|
-
const result = new Uint8Array(len);
|
|
899
|
-
let index = 0;
|
|
900
|
-
for (const array of arrays) {
|
|
901
|
-
result.set(array, index);
|
|
902
|
-
index += array.length;
|
|
903
|
-
}
|
|
904
|
-
return result;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
966
|
var _ReadablePromise_suspender, _ReadablePromise_status, _ReadablePromise_response, _ReadablePromise_error, _a;
|
|
908
967
|
/**
|
|
909
968
|
* The ReadablePromise class wraps a request promise suitable for React Suspense.
|
|
@@ -980,206 +1039,6 @@ class ReadablePromise {
|
|
|
980
1039
|
}
|
|
981
1040
|
_ReadablePromise_suspender = new WeakMap(), _ReadablePromise_status = new WeakMap(), _ReadablePromise_response = new WeakMap(), _ReadablePromise_error = new WeakMap(), _a = Symbol.toStringTag;
|
|
982
1041
|
|
|
983
|
-
const DEFAULT_SEARCH_COUNT = 20;
|
|
984
|
-
/**
|
|
985
|
-
* Search operators.
|
|
986
|
-
* These operators represent "modifiers" and "prefixes" in FHIR search.
|
|
987
|
-
* See: https://www.hl7.org/fhir/search.html
|
|
988
|
-
*/
|
|
989
|
-
var Operator;
|
|
990
|
-
(function (Operator) {
|
|
991
|
-
Operator["EQUALS"] = "eq";
|
|
992
|
-
Operator["NOT_EQUALS"] = "ne";
|
|
993
|
-
// Numbers
|
|
994
|
-
Operator["GREATER_THAN"] = "gt";
|
|
995
|
-
Operator["LESS_THAN"] = "lt";
|
|
996
|
-
Operator["GREATER_THAN_OR_EQUALS"] = "ge";
|
|
997
|
-
Operator["LESS_THAN_OR_EQUALS"] = "le";
|
|
998
|
-
// Dates
|
|
999
|
-
Operator["STARTS_AFTER"] = "sa";
|
|
1000
|
-
Operator["ENDS_BEFORE"] = "eb";
|
|
1001
|
-
Operator["APPROXIMATELY"] = "ap";
|
|
1002
|
-
// String
|
|
1003
|
-
Operator["CONTAINS"] = "contains";
|
|
1004
|
-
Operator["EXACT"] = "exact";
|
|
1005
|
-
// Token
|
|
1006
|
-
Operator["TEXT"] = "text";
|
|
1007
|
-
Operator["ABOVE"] = "above";
|
|
1008
|
-
Operator["BELOW"] = "below";
|
|
1009
|
-
Operator["IN"] = "in";
|
|
1010
|
-
Operator["NOT_IN"] = "not-in";
|
|
1011
|
-
Operator["OF_TYPE"] = "of-type";
|
|
1012
|
-
})(Operator || (Operator = {}));
|
|
1013
|
-
const MODIFIER_OPERATORS = [
|
|
1014
|
-
Operator.CONTAINS,
|
|
1015
|
-
Operator.EXACT,
|
|
1016
|
-
Operator.TEXT,
|
|
1017
|
-
Operator.ABOVE,
|
|
1018
|
-
Operator.BELOW,
|
|
1019
|
-
Operator.IN,
|
|
1020
|
-
Operator.NOT_IN,
|
|
1021
|
-
Operator.OF_TYPE,
|
|
1022
|
-
];
|
|
1023
|
-
const PREFIX_OPERATORS = [
|
|
1024
|
-
Operator.NOT_EQUALS,
|
|
1025
|
-
Operator.GREATER_THAN,
|
|
1026
|
-
Operator.LESS_THAN,
|
|
1027
|
-
Operator.GREATER_THAN_OR_EQUALS,
|
|
1028
|
-
Operator.LESS_THAN_OR_EQUALS,
|
|
1029
|
-
Operator.STARTS_AFTER,
|
|
1030
|
-
Operator.ENDS_BEFORE,
|
|
1031
|
-
Operator.APPROXIMATELY,
|
|
1032
|
-
];
|
|
1033
|
-
/**
|
|
1034
|
-
* Parses a URL into a SearchRequest.
|
|
1035
|
-
*
|
|
1036
|
-
* See the FHIR search spec: http://hl7.org/fhir/r4/search.html
|
|
1037
|
-
*
|
|
1038
|
-
* @param url The URL to parse.
|
|
1039
|
-
* @returns Parsed search definition.
|
|
1040
|
-
*/
|
|
1041
|
-
function parseSearchDefinition(url) {
|
|
1042
|
-
const location = new URL(url, 'https://example.com/');
|
|
1043
|
-
const resourceType = location.pathname
|
|
1044
|
-
.replace(/(^\/)|(\/$)/g, '') // Remove leading and trailing slashes
|
|
1045
|
-
.split('/')
|
|
1046
|
-
.pop();
|
|
1047
|
-
const params = new URLSearchParams(location.search);
|
|
1048
|
-
let filters = undefined;
|
|
1049
|
-
let sortRules = undefined;
|
|
1050
|
-
let fields = undefined;
|
|
1051
|
-
let offset = undefined;
|
|
1052
|
-
let count = undefined;
|
|
1053
|
-
let total = undefined;
|
|
1054
|
-
params.forEach((value, key) => {
|
|
1055
|
-
if (key === '_fields') {
|
|
1056
|
-
fields = value.split(',');
|
|
1057
|
-
}
|
|
1058
|
-
else if (key === '_offset') {
|
|
1059
|
-
offset = parseInt(value);
|
|
1060
|
-
}
|
|
1061
|
-
else if (key === '_count') {
|
|
1062
|
-
count = parseInt(value);
|
|
1063
|
-
}
|
|
1064
|
-
else if (key === '_total') {
|
|
1065
|
-
total = value;
|
|
1066
|
-
}
|
|
1067
|
-
else if (key === '_sort') {
|
|
1068
|
-
sortRules = sortRules || [];
|
|
1069
|
-
sortRules.push(parseSortRule(value));
|
|
1070
|
-
}
|
|
1071
|
-
else {
|
|
1072
|
-
filters = filters || [];
|
|
1073
|
-
filters.push(parseSearchFilter(key, value));
|
|
1074
|
-
}
|
|
1075
|
-
});
|
|
1076
|
-
return {
|
|
1077
|
-
resourceType,
|
|
1078
|
-
filters,
|
|
1079
|
-
fields,
|
|
1080
|
-
offset,
|
|
1081
|
-
count,
|
|
1082
|
-
total,
|
|
1083
|
-
sortRules,
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1086
|
-
/**
|
|
1087
|
-
* Parses a URL query parameter into a sort rule.
|
|
1088
|
-
*
|
|
1089
|
-
* By default, the sort rule is the field name.
|
|
1090
|
-
*
|
|
1091
|
-
* Sort rules can be reversed into descending order by prefixing the field name with a minus sign.
|
|
1092
|
-
*
|
|
1093
|
-
* See sorting: http://hl7.org/fhir/r4/search.html#_sort
|
|
1094
|
-
*
|
|
1095
|
-
* @param value The URL parameter value.
|
|
1096
|
-
* @returns The parsed sort rule.
|
|
1097
|
-
*/
|
|
1098
|
-
function parseSortRule(value) {
|
|
1099
|
-
if (value.startsWith('-')) {
|
|
1100
|
-
return { code: value.substring(1), descending: true };
|
|
1101
|
-
}
|
|
1102
|
-
else {
|
|
1103
|
-
return { code: value };
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
/**
|
|
1107
|
-
* Parses a URL query parameter into a search filter.
|
|
1108
|
-
*
|
|
1109
|
-
* FHIR search filters can be specified as modifiers or prefixes.
|
|
1110
|
-
*
|
|
1111
|
-
* For string properties, modifiers are appended to the key, e.g. "name:contains=eve".
|
|
1112
|
-
*
|
|
1113
|
-
* For date and numeric properties, prefixes are prepended to the value, e.g. "birthdate=gt2000".
|
|
1114
|
-
*
|
|
1115
|
-
* See the FHIR search spec: http://hl7.org/fhir/r4/search.html
|
|
1116
|
-
*
|
|
1117
|
-
* @param key The URL parameter key.
|
|
1118
|
-
* @param value The URL parameter value.
|
|
1119
|
-
* @returns The parsed search filter.
|
|
1120
|
-
*/
|
|
1121
|
-
function parseSearchFilter(key, value) {
|
|
1122
|
-
let code = key;
|
|
1123
|
-
let operator = Operator.EQUALS;
|
|
1124
|
-
for (const modifier of MODIFIER_OPERATORS) {
|
|
1125
|
-
const modifierIndex = code.indexOf(':' + modifier);
|
|
1126
|
-
if (modifierIndex !== -1) {
|
|
1127
|
-
operator = modifier;
|
|
1128
|
-
code = code.substring(0, modifierIndex);
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
for (const prefix of PREFIX_OPERATORS) {
|
|
1132
|
-
if (value.match(new RegExp('^' + prefix + '\\d'))) {
|
|
1133
|
-
operator = prefix;
|
|
1134
|
-
value = value.substring(prefix.length);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
return { code, operator, value };
|
|
1138
|
-
}
|
|
1139
|
-
/**
|
|
1140
|
-
* Formats a search definition object into a query string.
|
|
1141
|
-
* Note: The return value does not include the resource type.
|
|
1142
|
-
* @param {!SearchRequest} definition The search definition.
|
|
1143
|
-
* @returns Formatted URL.
|
|
1144
|
-
*/
|
|
1145
|
-
function formatSearchQuery(definition) {
|
|
1146
|
-
const params = [];
|
|
1147
|
-
if (definition.fields) {
|
|
1148
|
-
params.push('_fields=' + definition.fields.join(','));
|
|
1149
|
-
}
|
|
1150
|
-
if (definition.filters) {
|
|
1151
|
-
definition.filters.forEach((filter) => params.push(formatFilter(filter)));
|
|
1152
|
-
}
|
|
1153
|
-
if (definition.sortRules && definition.sortRules.length > 0) {
|
|
1154
|
-
params.push(formatSortRules(definition.sortRules));
|
|
1155
|
-
}
|
|
1156
|
-
if (definition.offset !== undefined) {
|
|
1157
|
-
params.push('_offset=' + definition.offset);
|
|
1158
|
-
}
|
|
1159
|
-
if (definition.count !== undefined) {
|
|
1160
|
-
params.push('_count=' + definition.count);
|
|
1161
|
-
}
|
|
1162
|
-
if (definition.total !== undefined) {
|
|
1163
|
-
params.push('_total=' + definition.total);
|
|
1164
|
-
}
|
|
1165
|
-
if (params.length === 0) {
|
|
1166
|
-
return '';
|
|
1167
|
-
}
|
|
1168
|
-
params.sort();
|
|
1169
|
-
return '?' + params.join('&');
|
|
1170
|
-
}
|
|
1171
|
-
function formatFilter(filter) {
|
|
1172
|
-
const modifier = MODIFIER_OPERATORS.includes(filter.operator) ? ':' + filter.operator : '';
|
|
1173
|
-
const prefix = PREFIX_OPERATORS.includes(filter.operator) ? filter.operator : '';
|
|
1174
|
-
return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
|
|
1175
|
-
}
|
|
1176
|
-
function formatSortRules(sortRules) {
|
|
1177
|
-
if (!sortRules || sortRules.length === 0) {
|
|
1178
|
-
return '';
|
|
1179
|
-
}
|
|
1180
|
-
return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
1042
|
var _ClientStorage_storage, _MemoryStorage_data;
|
|
1184
1043
|
/**
|
|
1185
1044
|
* The ClientStorage class is a utility class for storing strings and objects.
|
|
@@ -1468,7 +1327,7 @@ function getPropertyDisplayName(property) {
|
|
|
1468
1327
|
|
|
1469
1328
|
// PKCE auth ased on:
|
|
1470
1329
|
// https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
|
|
1471
|
-
var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_storage, _MedplumClient_schema, _MedplumClient_requestCache, _MedplumClient_baseUrl, _MedplumClient_clientId, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_request, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_startPkce, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
|
|
1330
|
+
var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_schema, _MedplumClient_requestCache, _MedplumClient_baseUrl, _MedplumClient_clientId, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_request, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_startPkce, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
|
|
1472
1331
|
const DEFAULT_BASE_URL = 'https://api.medplum.com/';
|
|
1473
1332
|
const DEFAULT_SCOPE = 'launch/patient openid fhirUser offline_access user/*.*';
|
|
1474
1333
|
const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
|
|
@@ -1518,7 +1377,7 @@ const PATCH_CONTENT_TYPE = 'application/json-patch+json';
|
|
|
1518
1377
|
* Search for a `Patient` by name:
|
|
1519
1378
|
*
|
|
1520
1379
|
* ```typescript
|
|
1521
|
-
* const bundle = await medplum.search('Patient
|
|
1380
|
+
* const bundle = await medplum.search('Patient', 'name=Alice');
|
|
1522
1381
|
* console.log(bundle.total);
|
|
1523
1382
|
* ```
|
|
1524
1383
|
*/
|
|
@@ -1528,6 +1387,7 @@ class MedplumClient extends EventTarget {
|
|
|
1528
1387
|
super();
|
|
1529
1388
|
_MedplumClient_instances.add(this);
|
|
1530
1389
|
_MedplumClient_fetch.set(this, void 0);
|
|
1390
|
+
_MedplumClient_createPdf.set(this, void 0);
|
|
1531
1391
|
_MedplumClient_storage.set(this, void 0);
|
|
1532
1392
|
_MedplumClient_schema.set(this, void 0);
|
|
1533
1393
|
_MedplumClient_requestCache.set(this, void 0);
|
|
@@ -1552,6 +1412,7 @@ class MedplumClient extends EventTarget {
|
|
|
1552
1412
|
}
|
|
1553
1413
|
}
|
|
1554
1414
|
__classPrivateFieldSet(this, _MedplumClient_fetch, (options === null || options === void 0 ? void 0 : options.fetch) || window.fetch.bind(window), "f");
|
|
1415
|
+
__classPrivateFieldSet(this, _MedplumClient_createPdf, options === null || options === void 0 ? void 0 : options.createPdf, "f");
|
|
1555
1416
|
__classPrivateFieldSet(this, _MedplumClient_storage, new ClientStorage(), "f");
|
|
1556
1417
|
__classPrivateFieldSet(this, _MedplumClient_schema, createSchema(), "f");
|
|
1557
1418
|
__classPrivateFieldSet(this, _MedplumClient_requestCache, new LRUCache((_a = options === null || options === void 0 ? void 0 : options.resourceCacheSize) !== null && _a !== void 0 ? _a : DEFAULT_RESOURCE_CACHE_SIZE), "f");
|
|
@@ -1796,12 +1657,11 @@ class MedplumClient extends EventTarget {
|
|
|
1796
1657
|
* @param query The FHIR search query or structured query object.
|
|
1797
1658
|
* @returns The well-formed FHIR URL.
|
|
1798
1659
|
*/
|
|
1799
|
-
fhirSearchUrl(query) {
|
|
1800
|
-
|
|
1801
|
-
|
|
1660
|
+
fhirSearchUrl(resourceType, query) {
|
|
1661
|
+
const url = this.fhirUrl(resourceType);
|
|
1662
|
+
if (query) {
|
|
1663
|
+
url.search = query.toString();
|
|
1802
1664
|
}
|
|
1803
|
-
const url = this.fhirUrl(query.resourceType);
|
|
1804
|
-
url.search = formatSearchQuery(query);
|
|
1805
1665
|
return url;
|
|
1806
1666
|
}
|
|
1807
1667
|
/**
|
|
@@ -1810,21 +1670,7 @@ class MedplumClient extends EventTarget {
|
|
|
1810
1670
|
* Example using a FHIR search string:
|
|
1811
1671
|
*
|
|
1812
1672
|
* ```typescript
|
|
1813
|
-
* const bundle = await client.search('Patient
|
|
1814
|
-
* console.log(bundle);
|
|
1815
|
-
* ```
|
|
1816
|
-
*
|
|
1817
|
-
* Example using a structured search:
|
|
1818
|
-
*
|
|
1819
|
-
* ```typescript
|
|
1820
|
-
* const bundle = await client.search({
|
|
1821
|
-
* resourceType: 'Patient',
|
|
1822
|
-
* filters: [{
|
|
1823
|
-
* code: 'name',
|
|
1824
|
-
* operator: 'eq',
|
|
1825
|
-
* value: 'Alice',
|
|
1826
|
-
* }]
|
|
1827
|
-
* });
|
|
1673
|
+
* const bundle = await client.search('Patient', 'name=Alice');
|
|
1828
1674
|
* console.log(bundle);
|
|
1829
1675
|
* ```
|
|
1830
1676
|
*
|
|
@@ -1858,8 +1704,8 @@ class MedplumClient extends EventTarget {
|
|
|
1858
1704
|
* @param query The search query as either a string or a structured search object.
|
|
1859
1705
|
* @returns Promise to the search result bundle.
|
|
1860
1706
|
*/
|
|
1861
|
-
search(query, options = {}) {
|
|
1862
|
-
return this.get(this.fhirSearchUrl(query), options);
|
|
1707
|
+
search(resourceType, query, options = {}) {
|
|
1708
|
+
return this.get(this.fhirSearchUrl(resourceType, query), options);
|
|
1863
1709
|
}
|
|
1864
1710
|
/**
|
|
1865
1711
|
* Sends a FHIR search request for a single resource.
|
|
@@ -1869,7 +1715,7 @@ class MedplumClient extends EventTarget {
|
|
|
1869
1715
|
* Example using a FHIR search string:
|
|
1870
1716
|
*
|
|
1871
1717
|
* ```typescript
|
|
1872
|
-
* const patient = await client.searchOne('Patient
|
|
1718
|
+
* const patient = await client.searchOne('Patient', 'identifier=123');
|
|
1873
1719
|
* console.log(patient);
|
|
1874
1720
|
* ```
|
|
1875
1721
|
*
|
|
@@ -1880,17 +1726,18 @@ class MedplumClient extends EventTarget {
|
|
|
1880
1726
|
* @param query The search query as either a string or a structured search object.
|
|
1881
1727
|
* @returns Promise to the search result bundle.
|
|
1882
1728
|
*/
|
|
1883
|
-
searchOne(query, options = {}) {
|
|
1884
|
-
const
|
|
1885
|
-
|
|
1886
|
-
|
|
1729
|
+
searchOne(resourceType, query, options = {}) {
|
|
1730
|
+
const url = this.fhirSearchUrl(resourceType, query);
|
|
1731
|
+
url.searchParams.set('_count', '1');
|
|
1732
|
+
url.searchParams.sort();
|
|
1733
|
+
const cacheKey = url.toString() + '-searchOne';
|
|
1887
1734
|
if (!(options === null || options === void 0 ? void 0 : options.cache)) {
|
|
1888
1735
|
const cached = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(cacheKey);
|
|
1889
1736
|
if (cached) {
|
|
1890
1737
|
return cached;
|
|
1891
1738
|
}
|
|
1892
1739
|
}
|
|
1893
|
-
const promise = new ReadablePromise(this.search(
|
|
1740
|
+
const promise = new ReadablePromise(this.search(resourceType, url.searchParams, options).then((b) => { var _a, _b; return (_b = (_a = b.entry) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.resource; }));
|
|
1894
1741
|
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(cacheKey, promise);
|
|
1895
1742
|
return promise;
|
|
1896
1743
|
}
|
|
@@ -1902,7 +1749,7 @@ class MedplumClient extends EventTarget {
|
|
|
1902
1749
|
* Example using a FHIR search string:
|
|
1903
1750
|
*
|
|
1904
1751
|
* ```typescript
|
|
1905
|
-
* const patients = await client.searchResources('Patient
|
|
1752
|
+
* const patients = await client.searchResources('Patient', 'name=Alice');
|
|
1906
1753
|
* console.log(patients);
|
|
1907
1754
|
* ```
|
|
1908
1755
|
*
|
|
@@ -1913,15 +1760,16 @@ class MedplumClient extends EventTarget {
|
|
|
1913
1760
|
* @param query The search query as either a string or a structured search object.
|
|
1914
1761
|
* @returns Promise to the search result bundle.
|
|
1915
1762
|
*/
|
|
1916
|
-
searchResources(query, options = {}) {
|
|
1917
|
-
const
|
|
1763
|
+
searchResources(resourceType, query, options = {}) {
|
|
1764
|
+
const url = this.fhirSearchUrl(resourceType, query);
|
|
1765
|
+
const cacheKey = url.toString() + '-searchResources';
|
|
1918
1766
|
if (!(options === null || options === void 0 ? void 0 : options.cache)) {
|
|
1919
1767
|
const cached = __classPrivateFieldGet(this, _MedplumClient_requestCache, "f").get(cacheKey);
|
|
1920
1768
|
if (cached) {
|
|
1921
1769
|
return cached;
|
|
1922
1770
|
}
|
|
1923
1771
|
}
|
|
1924
|
-
const promise = new ReadablePromise(this.search(query, options).then((b) => { var _a, _b; return (_b = (_a = b.entry) === null || _a === void 0 ? void 0 : _a.map((e) => e.resource)) !== null && _b !== void 0 ? _b : []; }));
|
|
1772
|
+
const promise = new ReadablePromise(this.search(resourceType, query, options).then((b) => { var _a, _b; return (_b = (_a = b.entry) === null || _a === void 0 ? void 0 : _a.map((e) => e.resource)) !== null && _b !== void 0 ? _b : []; }));
|
|
1925
1773
|
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").set(cacheKey, promise);
|
|
1926
1774
|
return promise;
|
|
1927
1775
|
}
|
|
@@ -1956,7 +1804,13 @@ class MedplumClient extends EventTarget {
|
|
|
1956
1804
|
*/
|
|
1957
1805
|
getCachedReference(reference) {
|
|
1958
1806
|
const refString = reference.reference;
|
|
1807
|
+
if (!refString) {
|
|
1808
|
+
return undefined;
|
|
1809
|
+
}
|
|
1959
1810
|
const [resourceType, id] = refString.split('/');
|
|
1811
|
+
if (!resourceType || !id) {
|
|
1812
|
+
return undefined;
|
|
1813
|
+
}
|
|
1960
1814
|
return this.getCached(resourceType, id);
|
|
1961
1815
|
}
|
|
1962
1816
|
/**
|
|
@@ -2002,6 +1856,9 @@ class MedplumClient extends EventTarget {
|
|
|
2002
1856
|
return new ReadablePromise(Promise.reject(new Error('Missing reference')));
|
|
2003
1857
|
}
|
|
2004
1858
|
const [resourceType, id] = refString.split('/');
|
|
1859
|
+
if (!resourceType || !id) {
|
|
1860
|
+
return new ReadablePromise(Promise.reject(new Error('Invalid reference')));
|
|
1861
|
+
}
|
|
2005
1862
|
return this.readResource(resourceType, id);
|
|
2006
1863
|
}
|
|
2007
1864
|
/**
|
|
@@ -2178,7 +2035,7 @@ class MedplumClient extends EventTarget {
|
|
|
2178
2035
|
createResourceIfNoneExist(resource, query) {
|
|
2179
2036
|
var _a;
|
|
2180
2037
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2181
|
-
return (_a = (yield this.searchOne(
|
|
2038
|
+
return ((_a = (yield this.searchOne(resource.resourceType, query))) !== null && _a !== void 0 ? _a : this.createResource(resource));
|
|
2182
2039
|
});
|
|
2183
2040
|
}
|
|
2184
2041
|
/**
|
|
@@ -2234,7 +2091,10 @@ class MedplumClient extends EventTarget {
|
|
|
2234
2091
|
*/
|
|
2235
2092
|
createPdf(docDefinition, filename, tableLayouts, fonts) {
|
|
2236
2093
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2237
|
-
|
|
2094
|
+
if (!__classPrivateFieldGet(this, _MedplumClient_createPdf, "f")) {
|
|
2095
|
+
throw new Error('PDF creation not enabled');
|
|
2096
|
+
}
|
|
2097
|
+
const blob = yield __classPrivateFieldGet(this, _MedplumClient_createPdf, "f").call(this, docDefinition, tableLayouts, fonts);
|
|
2238
2098
|
return this.createBinary(blob, filename, 'application/pdf');
|
|
2239
2099
|
});
|
|
2240
2100
|
}
|
|
@@ -2497,7 +2357,7 @@ class MedplumClient extends EventTarget {
|
|
|
2497
2357
|
});
|
|
2498
2358
|
}
|
|
2499
2359
|
}
|
|
2500
|
-
_MedplumClient_fetch = new WeakMap(), _MedplumClient_storage = new WeakMap(), _MedplumClient_schema = new WeakMap(), _MedplumClient_requestCache = new WeakMap(), _MedplumClient_baseUrl = new WeakMap(), _MedplumClient_clientId = new WeakMap(), _MedplumClient_authorizeUrl = new WeakMap(), _MedplumClient_tokenUrl = new WeakMap(), _MedplumClient_logoutUrl = new WeakMap(), _MedplumClient_onUnauthenticated = new WeakMap(), _MedplumClient_accessToken = new WeakMap(), _MedplumClient_refreshToken = new WeakMap(), _MedplumClient_refreshPromise = new WeakMap(), _MedplumClient_profilePromise = new WeakMap(), _MedplumClient_profile = new WeakMap(), _MedplumClient_config = new WeakMap(), _MedplumClient_instances = new WeakSet(), _MedplumClient_addLogin = function _MedplumClient_addLogin(newLogin) {
|
|
2360
|
+
_MedplumClient_fetch = new WeakMap(), _MedplumClient_createPdf = new WeakMap(), _MedplumClient_storage = new WeakMap(), _MedplumClient_schema = new WeakMap(), _MedplumClient_requestCache = new WeakMap(), _MedplumClient_baseUrl = new WeakMap(), _MedplumClient_clientId = new WeakMap(), _MedplumClient_authorizeUrl = new WeakMap(), _MedplumClient_tokenUrl = new WeakMap(), _MedplumClient_logoutUrl = new WeakMap(), _MedplumClient_onUnauthenticated = new WeakMap(), _MedplumClient_accessToken = new WeakMap(), _MedplumClient_refreshToken = new WeakMap(), _MedplumClient_refreshPromise = new WeakMap(), _MedplumClient_profilePromise = new WeakMap(), _MedplumClient_profile = new WeakMap(), _MedplumClient_config = new WeakMap(), _MedplumClient_instances = new WeakSet(), _MedplumClient_addLogin = function _MedplumClient_addLogin(newLogin) {
|
|
2501
2361
|
const logins = this.getLogins().filter((login) => { var _a, _b; return ((_a = login.profile) === null || _a === void 0 ? void 0 : _a.reference) !== ((_b = newLogin.profile) === null || _b === void 0 ? void 0 : _b.reference); });
|
|
2502
2362
|
logins.push(newLogin);
|
|
2503
2363
|
__classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('logins', logins);
|
|
@@ -5326,6 +5186,206 @@ class Hl7Field {
|
|
|
5326
5186
|
}
|
|
5327
5187
|
}
|
|
5328
5188
|
|
|
5189
|
+
const DEFAULT_SEARCH_COUNT = 20;
|
|
5190
|
+
/**
|
|
5191
|
+
* Search operators.
|
|
5192
|
+
* These operators represent "modifiers" and "prefixes" in FHIR search.
|
|
5193
|
+
* See: https://www.hl7.org/fhir/search.html
|
|
5194
|
+
*/
|
|
5195
|
+
var Operator;
|
|
5196
|
+
(function (Operator) {
|
|
5197
|
+
Operator["EQUALS"] = "eq";
|
|
5198
|
+
Operator["NOT_EQUALS"] = "ne";
|
|
5199
|
+
// Numbers
|
|
5200
|
+
Operator["GREATER_THAN"] = "gt";
|
|
5201
|
+
Operator["LESS_THAN"] = "lt";
|
|
5202
|
+
Operator["GREATER_THAN_OR_EQUALS"] = "ge";
|
|
5203
|
+
Operator["LESS_THAN_OR_EQUALS"] = "le";
|
|
5204
|
+
// Dates
|
|
5205
|
+
Operator["STARTS_AFTER"] = "sa";
|
|
5206
|
+
Operator["ENDS_BEFORE"] = "eb";
|
|
5207
|
+
Operator["APPROXIMATELY"] = "ap";
|
|
5208
|
+
// String
|
|
5209
|
+
Operator["CONTAINS"] = "contains";
|
|
5210
|
+
Operator["EXACT"] = "exact";
|
|
5211
|
+
// Token
|
|
5212
|
+
Operator["TEXT"] = "text";
|
|
5213
|
+
Operator["ABOVE"] = "above";
|
|
5214
|
+
Operator["BELOW"] = "below";
|
|
5215
|
+
Operator["IN"] = "in";
|
|
5216
|
+
Operator["NOT_IN"] = "not-in";
|
|
5217
|
+
Operator["OF_TYPE"] = "of-type";
|
|
5218
|
+
})(Operator || (Operator = {}));
|
|
5219
|
+
const MODIFIER_OPERATORS = [
|
|
5220
|
+
Operator.CONTAINS,
|
|
5221
|
+
Operator.EXACT,
|
|
5222
|
+
Operator.TEXT,
|
|
5223
|
+
Operator.ABOVE,
|
|
5224
|
+
Operator.BELOW,
|
|
5225
|
+
Operator.IN,
|
|
5226
|
+
Operator.NOT_IN,
|
|
5227
|
+
Operator.OF_TYPE,
|
|
5228
|
+
];
|
|
5229
|
+
const PREFIX_OPERATORS = [
|
|
5230
|
+
Operator.NOT_EQUALS,
|
|
5231
|
+
Operator.GREATER_THAN,
|
|
5232
|
+
Operator.LESS_THAN,
|
|
5233
|
+
Operator.GREATER_THAN_OR_EQUALS,
|
|
5234
|
+
Operator.LESS_THAN_OR_EQUALS,
|
|
5235
|
+
Operator.STARTS_AFTER,
|
|
5236
|
+
Operator.ENDS_BEFORE,
|
|
5237
|
+
Operator.APPROXIMATELY,
|
|
5238
|
+
];
|
|
5239
|
+
/**
|
|
5240
|
+
* Parses a URL into a SearchRequest.
|
|
5241
|
+
*
|
|
5242
|
+
* See the FHIR search spec: http://hl7.org/fhir/r4/search.html
|
|
5243
|
+
*
|
|
5244
|
+
* @param url The URL to parse.
|
|
5245
|
+
* @returns Parsed search definition.
|
|
5246
|
+
*/
|
|
5247
|
+
function parseSearchDefinition(url) {
|
|
5248
|
+
const location = new URL(url, 'https://example.com/');
|
|
5249
|
+
const resourceType = location.pathname
|
|
5250
|
+
.replace(/(^\/)|(\/$)/g, '') // Remove leading and trailing slashes
|
|
5251
|
+
.split('/')
|
|
5252
|
+
.pop();
|
|
5253
|
+
const params = new URLSearchParams(location.search);
|
|
5254
|
+
let filters = undefined;
|
|
5255
|
+
let sortRules = undefined;
|
|
5256
|
+
let fields = undefined;
|
|
5257
|
+
let offset = undefined;
|
|
5258
|
+
let count = undefined;
|
|
5259
|
+
let total = undefined;
|
|
5260
|
+
params.forEach((value, key) => {
|
|
5261
|
+
if (key === '_fields') {
|
|
5262
|
+
fields = value.split(',');
|
|
5263
|
+
}
|
|
5264
|
+
else if (key === '_offset') {
|
|
5265
|
+
offset = parseInt(value);
|
|
5266
|
+
}
|
|
5267
|
+
else if (key === '_count') {
|
|
5268
|
+
count = parseInt(value);
|
|
5269
|
+
}
|
|
5270
|
+
else if (key === '_total') {
|
|
5271
|
+
total = value;
|
|
5272
|
+
}
|
|
5273
|
+
else if (key === '_sort') {
|
|
5274
|
+
sortRules = sortRules || [];
|
|
5275
|
+
sortRules.push(parseSortRule(value));
|
|
5276
|
+
}
|
|
5277
|
+
else {
|
|
5278
|
+
filters = filters || [];
|
|
5279
|
+
filters.push(parseSearchFilter(key, value));
|
|
5280
|
+
}
|
|
5281
|
+
});
|
|
5282
|
+
return {
|
|
5283
|
+
resourceType,
|
|
5284
|
+
filters,
|
|
5285
|
+
fields,
|
|
5286
|
+
offset,
|
|
5287
|
+
count,
|
|
5288
|
+
total,
|
|
5289
|
+
sortRules,
|
|
5290
|
+
};
|
|
5291
|
+
}
|
|
5292
|
+
/**
|
|
5293
|
+
* Parses a URL query parameter into a sort rule.
|
|
5294
|
+
*
|
|
5295
|
+
* By default, the sort rule is the field name.
|
|
5296
|
+
*
|
|
5297
|
+
* Sort rules can be reversed into descending order by prefixing the field name with a minus sign.
|
|
5298
|
+
*
|
|
5299
|
+
* See sorting: http://hl7.org/fhir/r4/search.html#_sort
|
|
5300
|
+
*
|
|
5301
|
+
* @param value The URL parameter value.
|
|
5302
|
+
* @returns The parsed sort rule.
|
|
5303
|
+
*/
|
|
5304
|
+
function parseSortRule(value) {
|
|
5305
|
+
if (value.startsWith('-')) {
|
|
5306
|
+
return { code: value.substring(1), descending: true };
|
|
5307
|
+
}
|
|
5308
|
+
else {
|
|
5309
|
+
return { code: value };
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
/**
|
|
5313
|
+
* Parses a URL query parameter into a search filter.
|
|
5314
|
+
*
|
|
5315
|
+
* FHIR search filters can be specified as modifiers or prefixes.
|
|
5316
|
+
*
|
|
5317
|
+
* For string properties, modifiers are appended to the key, e.g. "name:contains=eve".
|
|
5318
|
+
*
|
|
5319
|
+
* For date and numeric properties, prefixes are prepended to the value, e.g. "birthdate=gt2000".
|
|
5320
|
+
*
|
|
5321
|
+
* See the FHIR search spec: http://hl7.org/fhir/r4/search.html
|
|
5322
|
+
*
|
|
5323
|
+
* @param key The URL parameter key.
|
|
5324
|
+
* @param value The URL parameter value.
|
|
5325
|
+
* @returns The parsed search filter.
|
|
5326
|
+
*/
|
|
5327
|
+
function parseSearchFilter(key, value) {
|
|
5328
|
+
let code = key;
|
|
5329
|
+
let operator = Operator.EQUALS;
|
|
5330
|
+
for (const modifier of MODIFIER_OPERATORS) {
|
|
5331
|
+
const modifierIndex = code.indexOf(':' + modifier);
|
|
5332
|
+
if (modifierIndex !== -1) {
|
|
5333
|
+
operator = modifier;
|
|
5334
|
+
code = code.substring(0, modifierIndex);
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
for (const prefix of PREFIX_OPERATORS) {
|
|
5338
|
+
if (value.match(new RegExp('^' + prefix + '\\d'))) {
|
|
5339
|
+
operator = prefix;
|
|
5340
|
+
value = value.substring(prefix.length);
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
return { code, operator, value };
|
|
5344
|
+
}
|
|
5345
|
+
/**
|
|
5346
|
+
* Formats a search definition object into a query string.
|
|
5347
|
+
* Note: The return value does not include the resource type.
|
|
5348
|
+
* @param {!SearchRequest} definition The search definition.
|
|
5349
|
+
* @returns Formatted URL.
|
|
5350
|
+
*/
|
|
5351
|
+
function formatSearchQuery(definition) {
|
|
5352
|
+
const params = [];
|
|
5353
|
+
if (definition.fields) {
|
|
5354
|
+
params.push('_fields=' + definition.fields.join(','));
|
|
5355
|
+
}
|
|
5356
|
+
if (definition.filters) {
|
|
5357
|
+
definition.filters.forEach((filter) => params.push(formatFilter(filter)));
|
|
5358
|
+
}
|
|
5359
|
+
if (definition.sortRules && definition.sortRules.length > 0) {
|
|
5360
|
+
params.push(formatSortRules(definition.sortRules));
|
|
5361
|
+
}
|
|
5362
|
+
if (definition.offset !== undefined) {
|
|
5363
|
+
params.push('_offset=' + definition.offset);
|
|
5364
|
+
}
|
|
5365
|
+
if (definition.count !== undefined) {
|
|
5366
|
+
params.push('_count=' + definition.count);
|
|
5367
|
+
}
|
|
5368
|
+
if (definition.total !== undefined) {
|
|
5369
|
+
params.push('_total=' + definition.total);
|
|
5370
|
+
}
|
|
5371
|
+
if (params.length === 0) {
|
|
5372
|
+
return '';
|
|
5373
|
+
}
|
|
5374
|
+
params.sort();
|
|
5375
|
+
return '?' + params.join('&');
|
|
5376
|
+
}
|
|
5377
|
+
function formatFilter(filter) {
|
|
5378
|
+
const modifier = MODIFIER_OPERATORS.includes(filter.operator) ? ':' + filter.operator : '';
|
|
5379
|
+
const prefix = PREFIX_OPERATORS.includes(filter.operator) ? filter.operator : '';
|
|
5380
|
+
return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
|
|
5381
|
+
}
|
|
5382
|
+
function formatSortRules(sortRules) {
|
|
5383
|
+
if (!sortRules || sortRules.length === 0) {
|
|
5384
|
+
return '';
|
|
5385
|
+
}
|
|
5386
|
+
return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
|
|
5387
|
+
}
|
|
5388
|
+
|
|
5329
5389
|
var SearchParameterType;
|
|
5330
5390
|
(function (SearchParameterType) {
|
|
5331
5391
|
SearchParameterType["BOOLEAN"] = "BOOLEAN";
|
|
@@ -5457,5 +5517,5 @@ function simplifyExpression(input) {
|
|
|
5457
5517
|
return result;
|
|
5458
5518
|
}
|
|
5459
5519
|
|
|
5460
|
-
export { COMPONENT_SEPARATOR, DEFAULT_SEARCH_COUNT, FIELD_SEPARATOR, Hl7Field, Hl7Message, Hl7Segment, LRUCache, MedplumClient, OperationOutcomeError, Operator, PropertyType, ReadablePromise, SEGMENT_SEPARATOR, SearchParameterType, accessDenied, allOk, arrayBufferToBase64, arrayBufferToHex, assertOk, badRequest, buildTypeName, calculateAge, calculateAgeString, capitalize, createReference, createSchema, createTypeSchema, created, deepEquals$1 as deepEquals, evalFhirPath, formatAddress, formatFamilyName, formatGivenName, formatHumanName, formatSearchQuery, getDateProperty, getDisplayString, getExpressionForResourceType, getExtensionValue, getIdentifier, getImageSrc, getPropertyDisplayName, getQuestionnaireAnswers, getReferenceString, getSearchParameterDetails, getStatus, gone, indexSearchParameter, indexStructureDefinition, isGone, isLowerCase, isNotFound, isObject$1 as isObject, isOk, isProfileResource, isStringArray, isUUID, notFound, notModified, parseFhirPath, parseSearchDefinition, resolveId, stringify, tokenize };
|
|
5520
|
+
export { COMPONENT_SEPARATOR, DEFAULT_SEARCH_COUNT, FIELD_SEPARATOR, Hl7Field, Hl7Message, Hl7Segment, LRUCache, MedplumClient, OperationOutcomeError, Operator, PropertyType, ReadablePromise, SEGMENT_SEPARATOR, SearchParameterType, accessDenied, allOk, arrayBufferToBase64, arrayBufferToHex, assertOk, badRequest, buildTypeName, calculateAge, calculateAgeString, capitalize, createReference, createSchema, createTypeSchema, created, deepEquals$1 as deepEquals, evalFhirPath, findObservationInterval, formatAddress, formatFamilyName, formatGivenName, formatHumanName, formatSearchQuery, getCodeBySystem, getDateProperty, getDisplayString, getExpressionForResourceType, getExtensionValue, getIdentifier, getImageSrc, getPropertyDisplayName, getQuestionnaireAnswers, getReferenceString, getSearchParameterDetails, getStatus, gone, indexSearchParameter, indexStructureDefinition, isGone, isLowerCase, isNotFound, isObject$1 as isObject, isOk, isProfileResource, isStringArray, isUUID, matchesRange, notFound, notModified, parseFhirPath, parseSearchDefinition, preciseEquals, preciseGreaterThan, preciseGreaterThanOrEquals, preciseLessThan, preciseLessThanOrEquals, resolveId, setCodeBySystem, stringify, tokenize };
|
|
5461
5521
|
//# sourceMappingURL=index.js.map
|