@medplum/core 2.0.23 → 2.0.24

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.
Files changed (55) hide show
  1. package/dist/cjs/index.cjs +14080 -13352
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.min.cjs +1 -1
  4. package/dist/esm/access.mjs +142 -0
  5. package/dist/esm/access.mjs.map +1 -0
  6. package/dist/esm/bundle.mjs +3 -3
  7. package/dist/esm/bundle.mjs.map +1 -1
  8. package/dist/esm/client.mjs +49 -25
  9. package/dist/esm/client.mjs.map +1 -1
  10. package/dist/esm/fhirlexer/parse.mjs.map +1 -1
  11. package/dist/esm/fhirmapper/parse.mjs +1 -1
  12. package/dist/esm/fhirmapper/parse.mjs.map +1 -1
  13. package/dist/esm/fhirpath/functions.mjs +2 -2
  14. package/dist/esm/fhirpath/functions.mjs.map +1 -1
  15. package/dist/esm/fhirpath/parse.mjs +2 -1
  16. package/dist/esm/fhirpath/parse.mjs.map +1 -1
  17. package/dist/esm/fhirpath/utils.mjs +4 -5
  18. package/dist/esm/fhirpath/utils.mjs.map +1 -1
  19. package/dist/esm/format.mjs +1 -1
  20. package/dist/esm/format.mjs.map +1 -1
  21. package/dist/esm/hl7.mjs +6 -6
  22. package/dist/esm/hl7.mjs.map +1 -1
  23. package/dist/esm/index.min.mjs +1 -1
  24. package/dist/esm/index.mjs +5 -2
  25. package/dist/esm/index.mjs.map +1 -1
  26. package/dist/esm/outcomes.mjs +38 -14
  27. package/dist/esm/outcomes.mjs.map +1 -1
  28. package/dist/esm/readablepromise.mjs +1 -1
  29. package/dist/esm/readablepromise.mjs.map +1 -1
  30. package/dist/esm/schema.mjs +1 -1
  31. package/dist/esm/schema.mjs.map +1 -1
  32. package/dist/esm/search/details.mjs +14 -16
  33. package/dist/esm/search/details.mjs.map +1 -1
  34. package/dist/esm/search/match.mjs +7 -5
  35. package/dist/esm/search/match.mjs.map +1 -1
  36. package/dist/esm/search/search.mjs +14 -5
  37. package/dist/esm/search/search.mjs.map +1 -1
  38. package/dist/esm/types.mjs +1 -1
  39. package/dist/esm/types.mjs.map +1 -1
  40. package/dist/esm/typeschema/types.mjs +278 -0
  41. package/dist/esm/typeschema/types.mjs.map +1 -0
  42. package/dist/esm/typeschema/validation.mjs +262 -0
  43. package/dist/esm/typeschema/validation.mjs.map +1 -0
  44. package/dist/esm/utils.mjs +3 -3
  45. package/dist/esm/utils.mjs.map +1 -1
  46. package/dist/types/access.d.ts +48 -0
  47. package/dist/types/client.d.ts +15 -20
  48. package/dist/types/fhirlexer/parse.d.ts +5 -4
  49. package/dist/types/fhirpath/functions.d.ts +1 -3
  50. package/dist/types/index.d.ts +3 -0
  51. package/dist/types/outcomes.d.ts +3 -1
  52. package/dist/types/search/search.d.ts +7 -0
  53. package/dist/types/typeschema/types.d.ts +5 -2
  54. package/dist/types/typeschema/validation.d.ts +2 -0
  55. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ export { canReadResourceType, canWriteResource, canWriteResourceType, matchesAccessPolicy, projectAdminResourceTypes, protectedResourceTypes, publicResourceTypes } from './access.mjs';
1
2
  export { decodeBase64, encodeBase64 } from './base64.mjs';
2
3
  export { convertToTransactionBundle } from './bundle.mjs';
3
4
  export { LRUCache } from './cache.mjs';
@@ -14,14 +15,16 @@ export { FhirFilterComparison, FhirFilterConnective, FhirFilterNegation } from '
14
15
  export { formatAddress, formatCodeableConcept, formatCoding, formatDate, formatDateTime, formatFamilyName, formatGivenName, formatHumanName, formatMoney, formatObservationValue, formatPeriod, formatQuantity, formatRange, formatTime, formatTiming, isValidDate } from './format.mjs';
15
16
  export { Hl7Context, Hl7Field, Hl7Message, Hl7Segment, parseHl7Date } from './hl7.mjs';
16
17
  export { parseJWTPayload } from './jwt.mjs';
17
- export { OperationOutcomeError, accepted, allOk, assertOk, badRequest, created, forbidden, getStatus, gone, isGone, isNotFound, isOk, isOperationOutcome, normalizeErrorString, normalizeOperationOutcome, notFound, notModified, operationOutcomeIssueToString, operationOutcomeToString, tooManyRequests, unauthorized, validationError } from './outcomes.mjs';
18
+ export { OperationOutcomeError, accepted, allOk, assertOk, badRequest, created, forbidden, getStatus, gone, isAccepted, isGone, isNotFound, isOk, isOperationOutcome, normalizeErrorString, normalizeOperationOutcome, notFound, notModified, operationOutcomeIssueToString, operationOutcomeToString, serverError, tooManyRequests, unauthorized, validationError } from './outcomes.mjs';
18
19
  export { ReadablePromise } from './readablepromise.mjs';
19
20
  export { FhirSchemaValidator, checkForNull, createStructureIssue, isResourceType, validateResource, validateResourceType } from './schema.mjs';
20
21
  export { SearchParameterType, getExpressionForResourceType, getSearchParameterDetails } from './search/details.mjs';
21
22
  export { matchesSearchRequest } from './search/match.mjs';
22
- export { DEFAULT_SEARCH_COUNT, Operator, formatSearchQuery, parseSearchDefinition, parseSearchRequest, parseSearchUrl } from './search/search.mjs';
23
+ export { DEFAULT_SEARCH_COUNT, Operator, formatSearchQuery, parseCriteriaAsSearchRequest, parseSearchDefinition, parseSearchRequest, parseSearchUrl } from './search/search.mjs';
23
24
  export { streamToBuffer } from './sftp.mjs';
24
25
  export { ClientStorage, MemoryStorage } from './storage.mjs';
25
26
  export { PropertyType, buildTypeName, getElementDefinition, getElementDefinitionTypeName, getPropertyDisplayName, getResourceTypeSchema, getResourceTypes, getSearchParameters, globalSchema, indexSearchParameter, indexSearchParameterBundle, indexStructureDefinition, indexStructureDefinitionBundle, isReference, isResource, isResourceTypeSchema } from './types.mjs';
26
27
  export { arrayBufferToBase64, arrayBufferToHex, calculateAge, calculateAgeString, capitalize, createReference, deepClone, deepEquals, findObservationInterval, findObservationReferenceRange, findResourceByCode, getCodeBySystem, getDateProperty, getDisplayString, getExtension, getExtensionValue, getIdentifier, getImageSrc, getQuestionnaireAnswers, getReferenceString, isEmpty, isLowerCase, isObject, isProfileResource, isStringArray, isUUID, matchesRange, preciseEquals, preciseGreaterThan, preciseGreaterThanOrEquals, preciseLessThan, preciseLessThanOrEquals, preciseRound, resolveId, setCodeBySystem, stringify } from './utils.mjs';
28
+ export { loadDataTypes } from './typeschema/types.mjs';
29
+ export { validateResource as experimentalValidateResource } from './typeschema/validation.mjs';
27
30
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -111,19 +111,22 @@ const tooManyRequests = {
111
111
  },
112
112
  ],
113
113
  };
114
- const accepted = {
115
- resourceType: 'OperationOutcome',
116
- id: ACCEPTED_ID,
117
- issue: [
118
- {
119
- severity: 'information',
120
- code: 'informational',
121
- details: {
122
- text: 'Accepted',
114
+ function accepted(location) {
115
+ return {
116
+ resourceType: 'OperationOutcome',
117
+ id: ACCEPTED_ID,
118
+ issue: [
119
+ {
120
+ severity: 'information',
121
+ code: 'informational',
122
+ details: {
123
+ text: 'Accepted',
124
+ },
125
+ diagnostics: location,
123
126
  },
124
- },
125
- ],
126
- };
127
+ ],
128
+ };
129
+ }
127
130
  function badRequest(details, expression) {
128
131
  return {
129
132
  resourceType: 'OperationOutcome',
@@ -153,11 +156,29 @@ function validationError(details) {
153
156
  ],
154
157
  };
155
158
  }
159
+ function serverError(err) {
160
+ return {
161
+ resourceType: 'OperationOutcome',
162
+ issue: [
163
+ {
164
+ severity: 'error',
165
+ code: 'exception',
166
+ details: {
167
+ text: 'Internal server error',
168
+ },
169
+ diagnostics: err.toString(),
170
+ },
171
+ ],
172
+ };
173
+ }
156
174
  function isOperationOutcome(value) {
157
175
  return typeof value === 'object' && value !== null && value.resourceType === 'OperationOutcome';
158
176
  }
159
177
  function isOk(outcome) {
160
- return outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID;
178
+ return (outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID || outcome.id === ACCEPTED_ID);
179
+ }
180
+ function isAccepted(outcome) {
181
+ return outcome.id === ACCEPTED_ID;
161
182
  }
162
183
  function isNotFound(outcome) {
163
184
  return outcome.id === NOT_FOUND_ID;
@@ -172,6 +193,9 @@ function getStatus(outcome) {
172
193
  else if (outcome.id === CREATED_ID) {
173
194
  return 201;
174
195
  }
196
+ else if (outcome.id === ACCEPTED_ID) {
197
+ return 202;
198
+ }
175
199
  else if (outcome.id === NOT_MODIFIED_ID) {
176
200
  return 304;
177
201
  }
@@ -267,5 +291,5 @@ function operationOutcomeIssueToString(issue) {
267
291
  return issueStr;
268
292
  }
269
293
 
270
- export { OperationOutcomeError, accepted, allOk, assertOk, badRequest, created, forbidden, getStatus, gone, isGone, isNotFound, isOk, isOperationOutcome, normalizeErrorString, normalizeOperationOutcome, notFound, notModified, operationOutcomeIssueToString, operationOutcomeToString, tooManyRequests, unauthorized, validationError };
294
+ export { OperationOutcomeError, accepted, allOk, assertOk, badRequest, created, forbidden, getStatus, gone, isAccepted, isGone, isNotFound, isOk, isOperationOutcome, normalizeErrorString, normalizeOperationOutcome, notFound, notModified, operationOutcomeIssueToString, operationOutcomeToString, serverError, tooManyRequests, unauthorized, validationError };
271
295
  //# sourceMappingURL=outcomes.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"outcomes.mjs","sources":["../../src/outcomes.ts"],"sourcesContent":["import { OperationOutcome, OperationOutcomeIssue } from '@medplum/fhirtypes';\n\nconst OK_ID = 'ok';\nconst CREATED_ID = 'created';\nconst GONE_ID = 'gone';\nconst NOT_MODIFIED_ID = 'not-modified';\nconst NOT_FOUND_ID = 'not-found';\nconst UNAUTHORIZED_ID = 'unauthorized';\nconst FORBIDDEN_ID = 'forbidden';\nconst TOO_MANY_REQUESTS_ID = 'too-many-requests';\nconst ACCEPTED_ID = 'accepted';\n\nexport const allOk: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: OK_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'All OK',\n },\n },\n ],\n};\n\nexport const created: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: CREATED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Created',\n },\n },\n ],\n};\n\nexport const notModified: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: NOT_MODIFIED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Not Modified',\n },\n },\n ],\n};\n\nexport const notFound: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: NOT_FOUND_ID,\n issue: [\n {\n severity: 'error',\n code: 'not-found',\n details: {\n text: 'Not found',\n },\n },\n ],\n};\n\nexport const unauthorized: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: UNAUTHORIZED_ID,\n issue: [\n {\n severity: 'error',\n code: 'login',\n details: {\n text: 'Unauthorized',\n },\n },\n ],\n};\n\nexport const forbidden: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: FORBIDDEN_ID,\n issue: [\n {\n severity: 'error',\n code: 'forbidden',\n details: {\n text: 'Forbidden',\n },\n },\n ],\n};\n\nexport const gone: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: GONE_ID,\n issue: [\n {\n severity: 'error',\n code: 'deleted',\n details: {\n text: 'Gone',\n },\n },\n ],\n};\n\nexport const tooManyRequests: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: TOO_MANY_REQUESTS_ID,\n issue: [\n {\n severity: 'error',\n code: 'throttled',\n details: {\n text: 'Too Many Requests',\n },\n },\n ],\n};\n\nexport const accepted: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: ACCEPTED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Accepted',\n },\n },\n ],\n};\n\nexport function badRequest(details: string, expression?: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'invalid',\n details: {\n text: details,\n },\n expression: expression ? [expression] : undefined,\n },\n ],\n };\n}\n\nexport function validationError(details: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'structure',\n details: {\n text: details,\n },\n },\n ],\n };\n}\n\nexport function isOperationOutcome(value: unknown): value is OperationOutcome {\n return typeof value === 'object' && value !== null && (value as any).resourceType === 'OperationOutcome';\n}\n\nexport function isOk(outcome: OperationOutcome): boolean {\n return outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID;\n}\n\nexport function isNotFound(outcome: OperationOutcome): boolean {\n return outcome.id === NOT_FOUND_ID;\n}\n\nexport function isGone(outcome: OperationOutcome): boolean {\n return outcome.id === GONE_ID;\n}\n\nexport function getStatus(outcome: OperationOutcome): number {\n if (outcome.id === OK_ID) {\n return 200;\n } else if (outcome.id === CREATED_ID) {\n return 201;\n } else if (outcome.id === NOT_MODIFIED_ID) {\n return 304;\n } else if (outcome.id === UNAUTHORIZED_ID) {\n return 401;\n } else if (outcome.id === FORBIDDEN_ID) {\n return 403;\n } else if (outcome.id === NOT_FOUND_ID) {\n return 404;\n } else if (outcome.id === GONE_ID) {\n return 410;\n } else if (outcome.id === TOO_MANY_REQUESTS_ID) {\n return 429;\n } else {\n return 400;\n }\n}\n\n/**\n * Asserts that the operation completed successfully and that the resource is defined.\n * @param outcome The operation outcome.\n * @param resource The resource that may or may not have been returned.\n */\nexport function assertOk<T>(outcome: OperationOutcome, resource: T | undefined): asserts resource is T {\n if (!isOk(outcome) || resource === undefined) {\n throw new OperationOutcomeError(outcome);\n }\n}\n\nexport class OperationOutcomeError extends Error {\n readonly outcome: OperationOutcome;\n\n constructor(outcome: OperationOutcome, cause?: unknown) {\n super(operationOutcomeToString(outcome));\n this.outcome = outcome;\n this.cause = cause;\n }\n}\n\n/**\n * Normalizes an error object into an OperationOutcome.\n * @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.\n * @returns The normalized OperationOutcome.\n */\nexport function normalizeOperationOutcome(error: unknown): OperationOutcome {\n if (error instanceof OperationOutcomeError) {\n return error.outcome;\n }\n if (isOperationOutcome(error)) {\n return error;\n }\n return badRequest(normalizeErrorString(error));\n}\n\n/**\n * Normalizes an error object into a displayable error string.\n * @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.\n * @returns A display string for the error.\n */\nexport function normalizeErrorString(error: unknown): string {\n if (!error) {\n return 'Unknown error';\n }\n if (typeof error === 'string') {\n return error;\n }\n if (error instanceof Error) {\n return error.message;\n }\n if (isOperationOutcome(error)) {\n return operationOutcomeToString(error);\n }\n return JSON.stringify(error);\n}\n\n/**\n * Returns a string represenation of the operation outcome.\n * @param outcome The operation outcome.\n * @returns The string representation of the operation outcome.\n */\nexport function operationOutcomeToString(outcome: OperationOutcome): string {\n const strs = outcome.issue?.map(operationOutcomeIssueToString) ?? [];\n return strs.length > 0 ? strs.join('; ') : 'Unknown error';\n}\n\n/**\n * Returns a string represenation of the operation outcome issue.\n * @param issue The operation outcome issue.\n * @returns The string representation of the operation outcome issue.\n */\nexport function operationOutcomeIssueToString(issue: OperationOutcomeIssue): string {\n let issueStr = issue.details?.text ?? issue.diagnostics ?? 'Unknown error';\n if (issue.expression?.length) {\n issueStr += ` (${issue.expression.join(', ')})`;\n }\n return issueStr;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"outcomes.mjs","sources":["../../src/outcomes.ts"],"sourcesContent":["import { OperationOutcome, OperationOutcomeIssue } from '@medplum/fhirtypes';\n\nconst OK_ID = 'ok';\nconst CREATED_ID = 'created';\nconst GONE_ID = 'gone';\nconst NOT_MODIFIED_ID = 'not-modified';\nconst NOT_FOUND_ID = 'not-found';\nconst UNAUTHORIZED_ID = 'unauthorized';\nconst FORBIDDEN_ID = 'forbidden';\nconst TOO_MANY_REQUESTS_ID = 'too-many-requests';\nconst ACCEPTED_ID = 'accepted';\n\nexport const allOk: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: OK_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'All OK',\n },\n },\n ],\n};\n\nexport const created: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: CREATED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Created',\n },\n },\n ],\n};\n\nexport const notModified: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: NOT_MODIFIED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Not Modified',\n },\n },\n ],\n};\n\nexport const notFound: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: NOT_FOUND_ID,\n issue: [\n {\n severity: 'error',\n code: 'not-found',\n details: {\n text: 'Not found',\n },\n },\n ],\n};\n\nexport const unauthorized: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: UNAUTHORIZED_ID,\n issue: [\n {\n severity: 'error',\n code: 'login',\n details: {\n text: 'Unauthorized',\n },\n },\n ],\n};\n\nexport const forbidden: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: FORBIDDEN_ID,\n issue: [\n {\n severity: 'error',\n code: 'forbidden',\n details: {\n text: 'Forbidden',\n },\n },\n ],\n};\n\nexport const gone: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: GONE_ID,\n issue: [\n {\n severity: 'error',\n code: 'deleted',\n details: {\n text: 'Gone',\n },\n },\n ],\n};\n\nexport const tooManyRequests: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: TOO_MANY_REQUESTS_ID,\n issue: [\n {\n severity: 'error',\n code: 'throttled',\n details: {\n text: 'Too Many Requests',\n },\n },\n ],\n};\n\nexport function accepted(location: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n id: ACCEPTED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Accepted',\n },\n diagnostics: location,\n },\n ],\n };\n}\n\nexport function badRequest(details: string, expression?: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'invalid',\n details: {\n text: details,\n },\n expression: expression ? [expression] : undefined,\n },\n ],\n };\n}\n\nexport function validationError(details: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'structure',\n details: {\n text: details,\n },\n },\n ],\n };\n}\n\nexport function serverError(err: Error): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'exception',\n details: {\n text: 'Internal server error',\n },\n diagnostics: err.toString(),\n },\n ],\n };\n}\n\nexport function isOperationOutcome(value: unknown): value is OperationOutcome {\n return typeof value === 'object' && value !== null && (value as any).resourceType === 'OperationOutcome';\n}\n\nexport function isOk(outcome: OperationOutcome): boolean {\n return (\n outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID || outcome.id === ACCEPTED_ID\n );\n}\n\nexport function isAccepted(outcome: OperationOutcome): boolean {\n return outcome.id === ACCEPTED_ID;\n}\n\nexport function isNotFound(outcome: OperationOutcome): boolean {\n return outcome.id === NOT_FOUND_ID;\n}\n\nexport function isGone(outcome: OperationOutcome): boolean {\n return outcome.id === GONE_ID;\n}\n\nexport function getStatus(outcome: OperationOutcome): number {\n if (outcome.id === OK_ID) {\n return 200;\n } else if (outcome.id === CREATED_ID) {\n return 201;\n } else if (outcome.id === ACCEPTED_ID) {\n return 202;\n } else if (outcome.id === NOT_MODIFIED_ID) {\n return 304;\n } else if (outcome.id === UNAUTHORIZED_ID) {\n return 401;\n } else if (outcome.id === FORBIDDEN_ID) {\n return 403;\n } else if (outcome.id === NOT_FOUND_ID) {\n return 404;\n } else if (outcome.id === GONE_ID) {\n return 410;\n } else if (outcome.id === TOO_MANY_REQUESTS_ID) {\n return 429;\n } else {\n return 400;\n }\n}\n\n/**\n * Asserts that the operation completed successfully and that the resource is defined.\n * @param outcome The operation outcome.\n * @param resource The resource that may or may not have been returned.\n */\nexport function assertOk<T>(outcome: OperationOutcome, resource: T | undefined): asserts resource is T {\n if (!isOk(outcome) || resource === undefined) {\n throw new OperationOutcomeError(outcome);\n }\n}\n\nexport class OperationOutcomeError extends Error {\n readonly outcome: OperationOutcome;\n\n constructor(outcome: OperationOutcome, cause?: unknown) {\n super(operationOutcomeToString(outcome));\n this.outcome = outcome;\n this.cause = cause;\n }\n}\n\n/**\n * Normalizes an error object into an OperationOutcome.\n * @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.\n * @returns The normalized OperationOutcome.\n */\nexport function normalizeOperationOutcome(error: unknown): OperationOutcome {\n if (error instanceof OperationOutcomeError) {\n return error.outcome;\n }\n if (isOperationOutcome(error)) {\n return error;\n }\n return badRequest(normalizeErrorString(error));\n}\n\n/**\n * Normalizes an error object into a displayable error string.\n * @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.\n * @returns A display string for the error.\n */\nexport function normalizeErrorString(error: unknown): string {\n if (!error) {\n return 'Unknown error';\n }\n if (typeof error === 'string') {\n return error;\n }\n if (error instanceof Error) {\n return error.message;\n }\n if (isOperationOutcome(error)) {\n return operationOutcomeToString(error);\n }\n return JSON.stringify(error);\n}\n\n/**\n * Returns a string represenation of the operation outcome.\n * @param outcome The operation outcome.\n * @returns The string representation of the operation outcome.\n */\nexport function operationOutcomeToString(outcome: OperationOutcome): string {\n const strs = outcome.issue?.map(operationOutcomeIssueToString) ?? [];\n return strs.length > 0 ? strs.join('; ') : 'Unknown error';\n}\n\n/**\n * Returns a string represenation of the operation outcome issue.\n * @param issue The operation outcome issue.\n * @returns The string representation of the operation outcome issue.\n */\nexport function operationOutcomeIssueToString(issue: OperationOutcomeIssue): string {\n let issueStr = issue.details?.text ?? issue.diagnostics ?? 'Unknown error';\n if (issue.expression?.length) {\n issueStr += ` (${issue.expression.join(', ')})`;\n }\n return issueStr;\n}\n"],"names":[],"mappings":"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"}
@@ -42,7 +42,7 @@ class ReadablePromise {
42
42
  read() {
43
43
  switch (this.status) {
44
44
  case 'pending':
45
- throw this.suspender;
45
+ throw this.suspender; //eslint-disable-line @typescript-eslint/no-throw-literal
46
46
  case 'error':
47
47
  throw this.error;
48
48
  default:
@@ -1 +1 @@
1
- {"version":3,"file":"readablepromise.mjs","sources":["../../src/readablepromise.ts"],"sourcesContent":["/**\n * The ReadablePromise class wraps a request promise suitable for React Suspense.\n * See: https://blog.logrocket.com/react-suspense-data-fetching/#wrappromise-js\n * See: https://github.com/ovieokeh/suspense-data-fetching/blob/master/lib/api/wrapPromise.js\n */\nexport class ReadablePromise<T> implements Promise<T> {\n readonly [Symbol.toStringTag]: string = 'ReadablePromise';\n private suspender: Promise<T>;\n private status: 'pending' | 'error' | 'success' = 'pending';\n private response: T | undefined;\n private error: Error | undefined;\n\n constructor(requestPromise: Promise<T>) {\n this.suspender = requestPromise.then(\n (res: T) => {\n this.status = 'success';\n this.response = res;\n return res;\n },\n (err: any) => {\n this.status = 'error';\n this.error = err;\n throw err;\n }\n );\n }\n\n /**\n * Returns true if the promise is pending.\n * @returns True if the Promise is pending.\n */\n isPending(): boolean {\n return this.status === 'pending';\n }\n\n /**\n * Returns true if the promise resolved successfully.\n * @returns True if the Promise resolved successfully.\n */\n isOk(): boolean {\n return this.status === 'success';\n }\n\n /**\n * Attempts to read the value of the promise.\n * If the promise is pending, this method will throw a promise.\n * If the promise rejected, this method will throw the rejection reason.\n * If the promise resolved, this method will return the resolved value.\n * @returns The resolved value of the Promise.\n */\n read(): T {\n switch (this.status) {\n case 'pending':\n throw this.suspender;\n case 'error':\n throw this.error;\n default:\n return this.response as T;\n }\n }\n\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then<TResult1 = T, TResult2 = never>(\n onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null\n ): Promise<TResult1 | TResult2> {\n return this.suspender.then(onfulfilled, onrejected);\n }\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch<TResult = never>(\n onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null\n ): Promise<T | TResult> {\n return this.suspender.catch(onrejected);\n }\n\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise<T> {\n return this.suspender.finally(onfinally);\n }\n}\n"],"names":[],"mappings":";AAAA;;;;AAIG;MACU,eAAe,CAAA;AAO1B,IAAA,WAAA,CAAY,cAA0B,EAAA;QAN7B,IAAoB,CAAA,EAAA,CAAA,GAAW,iBAAiB,CAAC;QAElD,IAAM,CAAA,MAAA,GAAoC,SAAS,CAAC;QAK1D,IAAI,CAAC,SAAS,GAAG,cAAc,CAAC,IAAI,CAClC,CAAC,GAAM,KAAI;AACT,YAAA,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;AACxB,YAAA,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;AACpB,YAAA,OAAO,GAAG,CAAC;AACb,SAAC,EACD,CAAC,GAAQ,KAAI;AACX,YAAA,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;AACtB,YAAA,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;AACjB,YAAA,MAAM,GAAG,CAAC;AACZ,SAAC,CACF,CAAC;KACH;AAED;;;AAGG;IACH,SAAS,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;KAClC;AAED;;;AAGG;IACH,IAAI,GAAA;AACF,QAAA,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;KAClC;AAED;;;;;;AAMG;IACH,IAAI,GAAA;QACF,QAAQ,IAAI,CAAC,MAAM;AACjB,YAAA,KAAK,SAAS;gBACZ,MAAM,IAAI,CAAC,SAAS,CAAC;AACvB,YAAA,KAAK,OAAO;gBACV,MAAM,IAAI,CAAC,KAAK,CAAC;AACnB,YAAA;gBACE,OAAO,IAAI,CAAC,QAAa,CAAC;AAC7B,SAAA;KACF;AAED;;;;;AAKG;IACH,IAAI,CACF,WAAiF,EACjF,UAAmF,EAAA;QAEnF,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;KACrD;AAED;;;;AAIG;AACH,IAAA,KAAK,CACH,UAAiF,EAAA;QAEjF,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;KACzC;AAED;;;;;AAKG;AACH,IAAA,OAAO,CAAC,SAA2C,EAAA;QACjD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;KAC1C;AACF,CAAA;AAxFW,EAAA,GAAA,MAAM,CAAC,WAAW;;;;"}
1
+ {"version":3,"file":"readablepromise.mjs","sources":["../../src/readablepromise.ts"],"sourcesContent":["/**\n * The ReadablePromise class wraps a request promise suitable for React Suspense.\n * See: https://blog.logrocket.com/react-suspense-data-fetching/#wrappromise-js\n * See: https://github.com/ovieokeh/suspense-data-fetching/blob/master/lib/api/wrapPromise.js\n */\nexport class ReadablePromise<T> implements Promise<T> {\n readonly [Symbol.toStringTag]: string = 'ReadablePromise';\n private suspender: Promise<T>;\n private status: 'pending' | 'error' | 'success' = 'pending';\n private response: T | undefined;\n private error: Error | undefined;\n\n constructor(requestPromise: Promise<T>) {\n this.suspender = requestPromise.then(\n (res: T) => {\n this.status = 'success';\n this.response = res;\n return res;\n },\n (err: any) => {\n this.status = 'error';\n this.error = err;\n throw err;\n }\n );\n }\n\n /**\n * Returns true if the promise is pending.\n * @returns True if the Promise is pending.\n */\n isPending(): boolean {\n return this.status === 'pending';\n }\n\n /**\n * Returns true if the promise resolved successfully.\n * @returns True if the Promise resolved successfully.\n */\n isOk(): boolean {\n return this.status === 'success';\n }\n\n /**\n * Attempts to read the value of the promise.\n * If the promise is pending, this method will throw a promise.\n * If the promise rejected, this method will throw the rejection reason.\n * If the promise resolved, this method will return the resolved value.\n * @returns The resolved value of the Promise.\n */\n read(): T {\n switch (this.status) {\n case 'pending':\n throw this.suspender; //eslint-disable-line @typescript-eslint/no-throw-literal\n case 'error':\n throw this.error as Error;\n default:\n return this.response as T;\n }\n }\n\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then<TResult1 = T, TResult2 = never>(\n onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null\n ): Promise<TResult1 | TResult2> {\n return this.suspender.then(onfulfilled, onrejected);\n }\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch<TResult = never>(\n onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null\n ): Promise<T | TResult> {\n return this.suspender.catch(onrejected);\n }\n\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise<T> {\n return this.suspender.finally(onfinally);\n }\n}\n"],"names":[],"mappings":";AAAA;;;;AAIG;MACU,eAAe,CAAA;AAO1B,IAAA,WAAA,CAAY,cAA0B,EAAA;QAN7B,IAAoB,CAAA,EAAA,CAAA,GAAW,iBAAiB,CAAC;QAElD,IAAM,CAAA,MAAA,GAAoC,SAAS,CAAC;QAK1D,IAAI,CAAC,SAAS,GAAG,cAAc,CAAC,IAAI,CAClC,CAAC,GAAM,KAAI;AACT,YAAA,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;AACxB,YAAA,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;AACpB,YAAA,OAAO,GAAG,CAAC;AACb,SAAC,EACD,CAAC,GAAQ,KAAI;AACX,YAAA,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;AACtB,YAAA,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;AACjB,YAAA,MAAM,GAAG,CAAC;AACZ,SAAC,CACF,CAAC;KACH;AAED;;;AAGG;IACH,SAAS,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;KAClC;AAED;;;AAGG;IACH,IAAI,GAAA;AACF,QAAA,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;KAClC;AAED;;;;;;AAMG;IACH,IAAI,GAAA;QACF,QAAQ,IAAI,CAAC,MAAM;AACjB,YAAA,KAAK,SAAS;AACZ,gBAAA,MAAM,IAAI,CAAC,SAAS,CAAC;AACvB,YAAA,KAAK,OAAO;gBACV,MAAM,IAAI,CAAC,KAAc,CAAC;AAC5B,YAAA;gBACE,OAAO,IAAI,CAAC,QAAa,CAAC;AAC7B,SAAA;KACF;AAED;;;;;AAKG;IACH,IAAI,CACF,WAAiF,EACjF,UAAmF,EAAA;QAEnF,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;KACrD;AAED;;;;AAIG;AACH,IAAA,KAAK,CACH,UAAiF,EAAA;QAEjF,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;KACzC;AAED;;;;;AAKG;AACH,IAAA,OAAO,CAAC,SAA2C,EAAA;QACjD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;KAC1C;AACF,CAAA;AAxFW,EAAA,GAAA,MAAM,CAAC,WAAW;;;;"}
@@ -261,7 +261,7 @@ class FhirSchemaValidator {
261
261
  return;
262
262
  }
263
263
  // Try to get the regex
264
- const valueDefinition = globalSchema.types[type]?.properties?.['value'];
264
+ const valueDefinition = globalSchema.types[type]?.properties['value'];
265
265
  if (valueDefinition?.type) {
266
266
  const regex = getExtensionValue(valueDefinition.type[0], 'http://hl7.org/fhir/StructureDefinition/regex');
267
267
  if (regex) {
@@ -1 +1 @@
1
- {"version":3,"file":"schema.mjs","sources":["../../src/schema.ts"],"sourcesContent":["import { ElementDefinition, OperationOutcomeIssue, Resource } from '@medplum/fhirtypes';\nimport { getTypedPropertyValue, toTypedValue } from './fhirpath';\nimport { OperationOutcomeError, validationError } from './outcomes';\nimport { globalSchema, PropertyType, TypedValue } from './types';\nimport { capitalize, getExtensionValue, isEmpty, isLowerCase } from './utils';\n\n/*\n * This file provides schema validation utilities for FHIR JSON objects.\n *\n * See: [JSON Representation of Resources](https://hl7.org/fhir/json.html)\n * See: [FHIR Data Types](https://www.hl7.org/fhir/datatypes.html)\n */\n\nconst fhirTypeToJsType: Record<string, string> = {\n base64Binary: 'string',\n boolean: 'boolean',\n canonical: 'string',\n code: 'string',\n date: 'string',\n dateTime: 'string',\n decimal: 'number',\n id: 'string',\n instant: 'string',\n integer: 'number',\n markdown: 'string',\n oid: 'string',\n positiveInt: 'number',\n string: 'string',\n time: 'string',\n unsignedInt: 'number',\n uri: 'string',\n url: 'string',\n uuid: 'string',\n xhtml: 'string',\n 'http://hl7.org/fhirpath/System.String': 'string',\n};\n\nconst baseResourceProperties = new Set<string>([\n // Resource\n 'resourceType',\n 'id',\n 'meta',\n 'implicitRules',\n 'language',\n\n // DomainResource\n 'text',\n 'contained',\n 'extension',\n 'modifierExtension',\n]);\n\n/**\n * Returns true if the given string is a valid FHIR resource type.\n *\n * ```ts\n * isResourceType('Patient'); // true\n * isResourceType('XYZ'); // false\n * ```\n *\n * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.\n *\n * In a server context, you can load all schema definitions:\n *\n * ```ts\n * import { indexStructureDefinitionBundle } from '@medplum/core';\n * import { readJson } from '@medplum/definitions';\n * import { Bundle } from '@medplum/fhirtypes';\n *\n * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);\n * ```\n *\n * In a client context, you can load the schema definitions using MedplumClient:\n *\n * ```ts\n * import { MedplumClient } from '@medplum/core';\n *\n * const medplum = new MedplumClient();\n * await medplum.requestSchema('Patient');\n * ```\n * @param resourceType The candidate resource type string.\n * @returns True if the resource type is a valid FHIR resource type.\n */\nexport function isResourceType(resourceType: string): boolean {\n const typeSchema = globalSchema.types[resourceType];\n return (\n typeSchema &&\n typeSchema.structureDefinition.id === resourceType &&\n typeSchema.structureDefinition.kind === 'resource'\n );\n}\n\n/**\n * Validates that the given string is a valid FHIR resource type.\n * On success, silently returns void.\n * On failure, throws an OperationOutcomeError.\n *\n * ```ts\n * validateResourceType('Patient'); // nothing\n * validateResourceType('XYZ'); // throws OperationOutcomeError\n * ```\n *\n * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.\n *\n * In a server context, you can load all schema definitions:\n *\n * ```ts\n * import { indexStructureDefinitionBundle } from '@medplum/core';\n * import { readJson } from '@medplum/definitions';\n * import { Bundle } from '@medplum/fhirtypes';\n *\n * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);\n * ```\n *\n * In a client context, you can load the schema definitions using MedplumClient:\n *\n * ```ts\n * import { MedplumClient } from '@medplum/core';\n *\n * const medplum = new MedplumClient();\n * await medplum.requestSchema('Patient');\n * ```\n * @param resourceType The candidate resource type string.\n */\nexport function validateResourceType(resourceType: string): void {\n if (!resourceType) {\n throw new OperationOutcomeError(validationError('Resource type is null'));\n }\n if (!isResourceType(resourceType)) {\n throw new OperationOutcomeError(validationError('Unknown resource type'));\n }\n}\n\n/**\n * Validates a candidate FHIR resource object.\n * On success, silently returns void.\n * On failure, throws an OperationOutcomeError with issues for each violation.\n *\n * ```ts\n * validateResource({ resourceType: 'Patient' }); // nothing\n * validateResource({ resourceType: 'XYZ' }); // throws OperationOutcomeError\n * ```\n *\n * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.\n *\n * In a server context, you can load all schema definitions:\n *\n * ```ts\n * import { indexStructureDefinitionBundle } from '@medplum/core';\n * import { readJson } from '@medplum/definitions';\n * import { Bundle } from '@medplum/fhirtypes';\n *\n * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);\n * ```\n *\n * In a client context, you can load the schema definitions using MedplumClient:\n *\n * ```ts\n * import { MedplumClient } from '@medplum/core';\n *\n * const medplum = new MedplumClient();\n * await medplum.requestSchema('Patient');\n * ```\n * @param resource The candidate resource.\n */\nexport function validateResource<T extends Resource>(resource: T): void {\n new FhirSchemaValidator(resource).validate();\n}\n\nexport class FhirSchemaValidator<T extends Resource> {\n private readonly issues: OperationOutcomeIssue[];\n private readonly root: T;\n\n constructor(root: T) {\n this.issues = [];\n this.root = root;\n }\n\n validate(): void {\n const resource = this.root;\n if (!resource) {\n throw new OperationOutcomeError(validationError('Resource is null'));\n }\n\n const resourceType = resource.resourceType;\n if (!resourceType) {\n throw new OperationOutcomeError(validationError('Missing resource type'));\n }\n\n // Check for \"null\" once for the entire object hierarchy\n checkForNull(resource, '', this.issues);\n\n this.validateObject(toTypedValue(resource), resourceType);\n\n if (this.issues.length > 0) {\n throw new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: this.issues,\n });\n }\n }\n\n private validateObject(typedValue: TypedValue, path: string): void {\n const definition = globalSchema.types[typedValue.type];\n if (!definition) {\n throw new OperationOutcomeError(validationError('Unknown type: ' + typedValue.type));\n }\n\n const propertyDefinitions = definition.properties;\n this.checkProperties(path, propertyDefinitions, typedValue);\n this.checkAdditionalProperties(path, typedValue, propertyDefinitions);\n }\n\n private checkProperties(\n path: string,\n propertyDefinitions: Record<string, ElementDefinition>,\n typedValue: TypedValue\n ): void {\n for (const [key, elementDefinition] of Object.entries(propertyDefinitions)) {\n this.checkProperty(path + '.' + key, elementDefinition, typedValue);\n }\n }\n\n private checkProperty(path: string, elementDefinition: ElementDefinition, typedValue: TypedValue): void {\n const propertyName = path.split('.').pop() as string;\n const value = getTypedPropertyValue(typedValue, propertyName);\n\n if (isEmpty(value)) {\n if (elementDefinition.min !== undefined && elementDefinition.min > 0) {\n this.issues.push(createStructureIssue(path, 'Missing required property'));\n }\n return;\n }\n\n if (elementDefinition.max === '*') {\n if (!Array.isArray(value)) {\n this.issues.push(createStructureIssue(path, 'Expected array for property'));\n return;\n }\n for (const item of value) {\n this.checkPropertyValue(path, elementDefinition, item);\n }\n } else {\n if (Array.isArray(value)) {\n this.issues.push(createStructureIssue(path, 'Expected single value for property'));\n return;\n }\n this.checkPropertyValue(path, elementDefinition, value as TypedValue);\n }\n }\n\n private checkPropertyValue(path: string, elementDefinition: ElementDefinition, typedValue: TypedValue): void {\n if (typedValue.value === null) {\n // Null handled separately\n return;\n }\n\n if (isLowerCase(typedValue.type.charAt(0))) {\n this.validatePrimitiveType(elementDefinition, typedValue);\n } else {\n this.validateObject(typedValue, path);\n }\n }\n\n private validatePrimitiveType(elementDefinition: ElementDefinition, typedValue: TypedValue): void {\n const { type, value } = typedValue;\n\n if (value === null) {\n // Null handled separately, so this code should never be reached\n // Leaving this check in place for now, in case we change the null handling\n return;\n }\n\n // First, make sure the value is the correct JS type\n const expectedType = fhirTypeToJsType[typedValue.type];\n if (typeof value !== expectedType) {\n this.createIssue(elementDefinition, 'Invalid type for ' + type);\n return;\n }\n\n // Then, perform additional checks for specialty types\n if (expectedType === 'string') {\n this.validateString(elementDefinition, type as PropertyType, value as string);\n } else if (expectedType === 'number') {\n this.validateNumber(elementDefinition, type as PropertyType, value as number);\n }\n }\n\n private validateString(elementDefinition: ElementDefinition, type: PropertyType, value: string): void {\n if (!value.trim()) {\n this.createIssue(elementDefinition, 'Invalid empty string');\n return;\n }\n\n // Try to get the regex\n const valueDefinition = globalSchema.types[type]?.properties?.['value'];\n if (valueDefinition?.type) {\n const regex = getExtensionValue(valueDefinition.type[0], 'http://hl7.org/fhir/StructureDefinition/regex');\n if (regex) {\n if (!value.match(new RegExp(regex))) {\n this.createIssue(elementDefinition, 'Invalid ' + type + ' format');\n }\n }\n }\n }\n\n private validateNumber(elementDefinition: ElementDefinition, type: PropertyType, value: number): void {\n if (isNaN(value) || !isFinite(value)) {\n this.createIssue(elementDefinition, 'Invalid ' + type + ' value');\n return;\n }\n\n if (isIntegerType(type) && !Number.isInteger(value)) {\n this.createIssue(elementDefinition, 'Number is not an integer');\n }\n\n if (type === PropertyType.positiveInt && value <= 0) {\n this.createIssue(elementDefinition, 'Number is less than or equal to zero');\n }\n\n if (type === PropertyType.unsignedInt && value < 0) {\n this.createIssue(elementDefinition, 'Number is negative');\n }\n }\n\n private checkAdditionalProperties(\n path: string,\n typedValue: TypedValue,\n propertyDefinitions: Record<string, ElementDefinition>\n ): void {\n const object = typedValue.value as Record<string, unknown>;\n for (const key of Object.keys(object)) {\n this.checkAdditionalProperty(path, key, typedValue, propertyDefinitions);\n }\n }\n\n /**\n * Checks if the given property is allowed on the given object.\n * @param path The path of the current object.\n * @param key The key of a property to check.\n * @param typedValue The current object.\n * @param propertyDefinitions The property definitions of the current object.\n */\n private checkAdditionalProperty(\n path: string,\n key: string,\n typedValue: TypedValue,\n propertyDefinitions: Record<string, ElementDefinition>\n ): void {\n if (\n !baseResourceProperties.has(key) &&\n !(key in propertyDefinitions) &&\n !isChoiceOfType(key, typedValue, propertyDefinitions) &&\n !this.checkPrimitiveElement(path, key, typedValue)\n ) {\n const expression = `${path}.${key}`;\n this.issues.push(createStructureIssue(expression, `Invalid additional property \"${expression}\"`));\n }\n }\n\n /**\n * Checks the element for a primitive.\n *\n * FHIR elements with primitive data types are represented in two parts:\n * 1) A JSON property with the name of the element, which has a JSON type of number, boolean, or string\n * 2) a JSON property with _ prepended to the name of the element, which, if present, contains the value's id and/or extensions\n *\n * See: https://hl7.org/fhir/json.html#primitive\n * @param path The path to the property\n * @param key The key in the current typed value.\n * @param typedValue The current typed value.\n * @returns True if the primitive element is valid.\n */\n private checkPrimitiveElement(path: string, key: string, typedValue: TypedValue): boolean {\n // Primitive element starts with underscore\n if (!key.startsWith('_')) {\n return false;\n }\n\n // Validate the non-underscore property exists\n const primitiveKey = key.slice(1);\n if (!(primitiveKey in typedValue.value)) {\n return false;\n }\n\n // Then validate the element\n this.validateObject({ type: 'Element', value: typedValue.value[key] }, path);\n return true;\n }\n\n private createIssue(elementDefinition: ElementDefinition, message: string): void {\n this.issues.push(createStructureIssue(elementDefinition.path as string, message));\n }\n}\n\nfunction isIntegerType(propertyType: PropertyType): boolean {\n return (\n propertyType === PropertyType.integer ||\n propertyType === PropertyType.positiveInt ||\n propertyType === PropertyType.unsignedInt\n );\n}\n\nfunction isChoiceOfType(\n key: string,\n typedValue: TypedValue,\n propertyDefinitions: Record<string, ElementDefinition>\n): boolean {\n for (const propertyName of Object.keys(propertyDefinitions)) {\n if (!propertyName.endsWith('[x]')) {\n continue;\n }\n const basePropertyName = propertyName.replace('[x]', '');\n if (!key.startsWith(basePropertyName)) {\n continue;\n }\n let typedPropertyValue = getTypedPropertyValue(typedValue, propertyName);\n if (!typedPropertyValue) {\n continue;\n }\n if (Array.isArray(typedPropertyValue)) {\n // At present, there are no choice types that are arrays in the FHIR spec\n // Leaving this here to make TypeScript happy, and in case that changes\n typedPropertyValue = typedPropertyValue[0];\n }\n if (typedPropertyValue && key === basePropertyName + capitalize(typedPropertyValue.type)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Recursively checks for null values in an object.\n *\n * Note that \"null\" is a special value in JSON that is not allowed in FHIR.\n * @param value Input value of any type.\n * @param path Path string to the value for OperationOutcome.\n * @param issues Output list of issues.\n */\nexport function checkForNull(value: unknown, path: string, issues: OperationOutcomeIssue[]): void {\n if (value === null) {\n issues.push(createStructureIssue(path, 'Invalid null value'));\n } else if (Array.isArray(value)) {\n checkArrayForNull(value, path, issues);\n } else if (typeof value === 'object') {\n checkObjectForNull(value as Record<string, unknown>, path, issues);\n }\n}\n\nfunction checkArrayForNull(array: unknown[], path: string, issues: OperationOutcomeIssue[]): void {\n for (let i = 0; i < array.length; i++) {\n if (array[i] === undefined) {\n issues.push(createStructureIssue(`${path}[${i}]`, 'Invalid undefined value'));\n } else {\n checkForNull(array[i], `${path}[${i}]`, issues);\n }\n }\n}\n\nfunction checkObjectForNull(obj: Record<string, unknown>, path: string, issues: OperationOutcomeIssue[]): void {\n for (const [key, value] of Object.entries(obj)) {\n checkForNull(value, `${path}${path ? '.' : ''}${key}`, issues);\n }\n}\n\nexport function createStructureIssue(expression: string, details: string): OperationOutcomeIssue {\n return {\n severity: 'error',\n code: 'structure',\n details: {\n text: details,\n },\n expression: [expression],\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.mjs","sources":["../../src/schema.ts"],"sourcesContent":["import { ElementDefinition, OperationOutcomeIssue, Resource } from '@medplum/fhirtypes';\nimport { getTypedPropertyValue, toTypedValue } from './fhirpath';\nimport { OperationOutcomeError, validationError } from './outcomes';\nimport { globalSchema, PropertyType, TypedValue } from './types';\nimport { capitalize, getExtensionValue, isEmpty, isLowerCase } from './utils';\n\n/*\n * This file provides schema validation utilities for FHIR JSON objects.\n *\n * See: [JSON Representation of Resources](https://hl7.org/fhir/json.html)\n * See: [FHIR Data Types](https://www.hl7.org/fhir/datatypes.html)\n */\nconst fhirTypeToJsType: Record<string, string> = {\n base64Binary: 'string',\n boolean: 'boolean',\n canonical: 'string',\n code: 'string',\n date: 'string',\n dateTime: 'string',\n decimal: 'number',\n id: 'string',\n instant: 'string',\n integer: 'number',\n markdown: 'string',\n oid: 'string',\n positiveInt: 'number',\n string: 'string',\n time: 'string',\n unsignedInt: 'number',\n uri: 'string',\n url: 'string',\n uuid: 'string',\n xhtml: 'string',\n 'http://hl7.org/fhirpath/System.String': 'string',\n};\n\nconst baseResourceProperties = new Set<string>([\n // Resource\n 'resourceType',\n 'id',\n 'meta',\n 'implicitRules',\n 'language',\n\n // DomainResource\n 'text',\n 'contained',\n 'extension',\n 'modifierExtension',\n]);\n\n/**\n * Returns true if the given string is a valid FHIR resource type.\n *\n * ```ts\n * isResourceType('Patient'); // true\n * isResourceType('XYZ'); // false\n * ```\n *\n * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.\n *\n * In a server context, you can load all schema definitions:\n *\n * ```ts\n * import { indexStructureDefinitionBundle } from '@medplum/core';\n * import { readJson } from '@medplum/definitions';\n * import { Bundle } from '@medplum/fhirtypes';\n *\n * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);\n * ```\n *\n * In a client context, you can load the schema definitions using MedplumClient:\n *\n * ```ts\n * import { MedplumClient } from '@medplum/core';\n *\n * const medplum = new MedplumClient();\n * await medplum.requestSchema('Patient');\n * ```\n * @param resourceType The candidate resource type string.\n * @returns True if the resource type is a valid FHIR resource type.\n */\nexport function isResourceType(resourceType: string): boolean {\n const typeSchema = globalSchema.types[resourceType];\n return (\n typeSchema &&\n typeSchema.structureDefinition.id === resourceType &&\n typeSchema.structureDefinition.kind === 'resource'\n );\n}\n\n/**\n * Validates that the given string is a valid FHIR resource type.\n * On success, silently returns void.\n * On failure, throws an OperationOutcomeError.\n *\n * ```ts\n * validateResourceType('Patient'); // nothing\n * validateResourceType('XYZ'); // throws OperationOutcomeError\n * ```\n *\n * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.\n *\n * In a server context, you can load all schema definitions:\n *\n * ```ts\n * import { indexStructureDefinitionBundle } from '@medplum/core';\n * import { readJson } from '@medplum/definitions';\n * import { Bundle } from '@medplum/fhirtypes';\n *\n * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);\n * ```\n *\n * In a client context, you can load the schema definitions using MedplumClient:\n *\n * ```ts\n * import { MedplumClient } from '@medplum/core';\n *\n * const medplum = new MedplumClient();\n * await medplum.requestSchema('Patient');\n * ```\n * @param resourceType The candidate resource type string.\n */\nexport function validateResourceType(resourceType: string): void {\n if (!resourceType) {\n throw new OperationOutcomeError(validationError('Resource type is null'));\n }\n if (!isResourceType(resourceType)) {\n throw new OperationOutcomeError(validationError('Unknown resource type'));\n }\n}\n\n/**\n * Validates a candidate FHIR resource object.\n * On success, silently returns void.\n * On failure, throws an OperationOutcomeError with issues for each violation.\n *\n * ```ts\n * validateResource({ resourceType: 'Patient' }); // nothing\n * validateResource({ resourceType: 'XYZ' }); // throws OperationOutcomeError\n * ```\n *\n * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.\n *\n * In a server context, you can load all schema definitions:\n *\n * ```ts\n * import { indexStructureDefinitionBundle } from '@medplum/core';\n * import { readJson } from '@medplum/definitions';\n * import { Bundle } from '@medplum/fhirtypes';\n *\n * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);\n * ```\n *\n * In a client context, you can load the schema definitions using MedplumClient:\n *\n * ```ts\n * import { MedplumClient } from '@medplum/core';\n *\n * const medplum = new MedplumClient();\n * await medplum.requestSchema('Patient');\n * ```\n * @param resource The candidate resource.\n */\nexport function validateResource<T extends Resource>(resource: T): void {\n new FhirSchemaValidator(resource).validate();\n}\n\nexport class FhirSchemaValidator<T extends Resource> {\n private readonly issues: OperationOutcomeIssue[];\n private readonly root: T;\n\n constructor(root: T) {\n this.issues = [];\n this.root = root;\n }\n\n validate(): void {\n const resource = this.root;\n if (!resource) {\n throw new OperationOutcomeError(validationError('Resource is null'));\n }\n\n const resourceType = resource.resourceType;\n if (!resourceType) {\n throw new OperationOutcomeError(validationError('Missing resource type'));\n }\n\n // Check for \"null\" once for the entire object hierarchy\n checkForNull(resource, '', this.issues);\n\n this.validateObject(toTypedValue(resource), resourceType);\n\n if (this.issues.length > 0) {\n throw new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: this.issues,\n });\n }\n }\n\n private validateObject(typedValue: TypedValue, path: string): void {\n const definition = globalSchema.types[typedValue.type];\n if (!definition) {\n throw new OperationOutcomeError(validationError('Unknown type: ' + typedValue.type));\n }\n\n const propertyDefinitions = definition.properties;\n this.checkProperties(path, propertyDefinitions, typedValue);\n this.checkAdditionalProperties(path, typedValue, propertyDefinitions);\n }\n\n private checkProperties(\n path: string,\n propertyDefinitions: Record<string, ElementDefinition>,\n typedValue: TypedValue\n ): void {\n for (const [key, elementDefinition] of Object.entries(propertyDefinitions)) {\n this.checkProperty(path + '.' + key, elementDefinition, typedValue);\n }\n }\n\n private checkProperty(path: string, elementDefinition: ElementDefinition, typedValue: TypedValue): void {\n const propertyName = path.split('.').pop() as string;\n const value = getTypedPropertyValue(typedValue, propertyName);\n\n if (isEmpty(value)) {\n if (elementDefinition.min !== undefined && elementDefinition.min > 0) {\n this.issues.push(createStructureIssue(path, 'Missing required property'));\n }\n return;\n }\n\n if (elementDefinition.max === '*') {\n if (!Array.isArray(value)) {\n this.issues.push(createStructureIssue(path, 'Expected array for property'));\n return;\n }\n for (const item of value) {\n this.checkPropertyValue(path, elementDefinition, item);\n }\n } else {\n if (Array.isArray(value)) {\n this.issues.push(createStructureIssue(path, 'Expected single value for property'));\n return;\n }\n this.checkPropertyValue(path, elementDefinition, value as TypedValue);\n }\n }\n\n private checkPropertyValue(path: string, elementDefinition: ElementDefinition, typedValue: TypedValue): void {\n if (typedValue.value === null) {\n // Null handled separately\n return;\n }\n\n if (isLowerCase(typedValue.type.charAt(0))) {\n this.validatePrimitiveType(elementDefinition, typedValue);\n } else {\n this.validateObject(typedValue, path);\n }\n }\n\n private validatePrimitiveType(elementDefinition: ElementDefinition, typedValue: TypedValue): void {\n const { type, value } = typedValue;\n\n if (value === null) {\n // Null handled separately, so this code should never be reached\n // Leaving this check in place for now, in case we change the null handling\n return;\n }\n\n // First, make sure the value is the correct JS type\n const expectedType = fhirTypeToJsType[typedValue.type];\n if (typeof value !== expectedType) {\n this.createIssue(elementDefinition, 'Invalid type for ' + type);\n return;\n }\n\n // Then, perform additional checks for specialty types\n if (expectedType === 'string') {\n this.validateString(elementDefinition, type as PropertyType, value as string);\n } else if (expectedType === 'number') {\n this.validateNumber(elementDefinition, type as PropertyType, value as number);\n }\n }\n\n private validateString(elementDefinition: ElementDefinition, type: PropertyType, value: string): void {\n if (!value.trim()) {\n this.createIssue(elementDefinition, 'Invalid empty string');\n return;\n }\n\n // Try to get the regex\n const valueDefinition = globalSchema.types[type]?.properties['value'];\n if (valueDefinition?.type) {\n const regex = getExtensionValue(valueDefinition.type[0], 'http://hl7.org/fhir/StructureDefinition/regex');\n if (regex) {\n if (!value.match(new RegExp(regex))) {\n this.createIssue(elementDefinition, 'Invalid ' + type + ' format');\n }\n }\n }\n }\n\n private validateNumber(elementDefinition: ElementDefinition, type: PropertyType, value: number): void {\n if (isNaN(value) || !isFinite(value)) {\n this.createIssue(elementDefinition, 'Invalid ' + type + ' value');\n return;\n }\n\n if (isIntegerType(type) && !Number.isInteger(value)) {\n this.createIssue(elementDefinition, 'Number is not an integer');\n }\n\n if (type === PropertyType.positiveInt && value <= 0) {\n this.createIssue(elementDefinition, 'Number is less than or equal to zero');\n }\n\n if (type === PropertyType.unsignedInt && value < 0) {\n this.createIssue(elementDefinition, 'Number is negative');\n }\n }\n\n private checkAdditionalProperties(\n path: string,\n typedValue: TypedValue,\n propertyDefinitions: Record<string, ElementDefinition>\n ): void {\n const object = typedValue.value as Record<string, unknown>;\n for (const key of Object.keys(object)) {\n this.checkAdditionalProperty(path, key, typedValue, propertyDefinitions);\n }\n }\n\n /**\n * Checks if the given property is allowed on the given object.\n * @param path The path of the current object.\n * @param key The key of a property to check.\n * @param typedValue The current object.\n * @param propertyDefinitions The property definitions of the current object.\n */\n private checkAdditionalProperty(\n path: string,\n key: string,\n typedValue: TypedValue,\n propertyDefinitions: Record<string, ElementDefinition>\n ): void {\n if (\n !baseResourceProperties.has(key) &&\n !(key in propertyDefinitions) &&\n !isChoiceOfType(key, typedValue, propertyDefinitions) &&\n !this.checkPrimitiveElement(path, key, typedValue)\n ) {\n const expression = `${path}.${key}`;\n this.issues.push(createStructureIssue(expression, `Invalid additional property \"${expression}\"`));\n }\n }\n\n /**\n * Checks the element for a primitive.\n *\n * FHIR elements with primitive data types are represented in two parts:\n * 1) A JSON property with the name of the element, which has a JSON type of number, boolean, or string\n * 2) a JSON property with _ prepended to the name of the element, which, if present, contains the value's id and/or extensions\n *\n * See: https://hl7.org/fhir/json.html#primitive\n * @param path The path to the property\n * @param key The key in the current typed value.\n * @param typedValue The current typed value.\n * @returns True if the primitive element is valid.\n */\n private checkPrimitiveElement(path: string, key: string, typedValue: TypedValue): boolean {\n // Primitive element starts with underscore\n if (!key.startsWith('_')) {\n return false;\n }\n\n // Validate the non-underscore property exists\n const primitiveKey = key.slice(1);\n if (!(primitiveKey in typedValue.value)) {\n return false;\n }\n\n // Then validate the element\n this.validateObject({ type: 'Element', value: typedValue.value[key] }, path);\n return true;\n }\n\n private createIssue(elementDefinition: ElementDefinition, message: string): void {\n this.issues.push(createStructureIssue(elementDefinition.path as string, message));\n }\n}\n\nfunction isIntegerType(propertyType: PropertyType): boolean {\n return (\n propertyType === PropertyType.integer ||\n propertyType === PropertyType.positiveInt ||\n propertyType === PropertyType.unsignedInt\n );\n}\n\nfunction isChoiceOfType(\n key: string,\n typedValue: TypedValue,\n propertyDefinitions: Record<string, ElementDefinition>\n): boolean {\n for (const propertyName of Object.keys(propertyDefinitions)) {\n if (!propertyName.endsWith('[x]')) {\n continue;\n }\n const basePropertyName = propertyName.replace('[x]', '');\n if (!key.startsWith(basePropertyName)) {\n continue;\n }\n let typedPropertyValue = getTypedPropertyValue(typedValue, propertyName);\n if (!typedPropertyValue) {\n continue;\n }\n if (Array.isArray(typedPropertyValue)) {\n // At present, there are no choice types that are arrays in the FHIR spec\n // Leaving this here to make TypeScript happy, and in case that changes\n typedPropertyValue = typedPropertyValue[0];\n }\n if (typedPropertyValue && key === basePropertyName + capitalize(typedPropertyValue.type)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Recursively checks for null values in an object.\n *\n * Note that \"null\" is a special value in JSON that is not allowed in FHIR.\n * @param value Input value of any type.\n * @param path Path string to the value for OperationOutcome.\n * @param issues Output list of issues.\n */\nexport function checkForNull(value: unknown, path: string, issues: OperationOutcomeIssue[]): void {\n if (value === null) {\n issues.push(createStructureIssue(path, 'Invalid null value'));\n } else if (Array.isArray(value)) {\n checkArrayForNull(value, path, issues);\n } else if (typeof value === 'object') {\n checkObjectForNull(value as Record<string, unknown>, path, issues);\n }\n}\n\nfunction checkArrayForNull(array: unknown[], path: string, issues: OperationOutcomeIssue[]): void {\n for (let i = 0; i < array.length; i++) {\n if (array[i] === undefined) {\n issues.push(createStructureIssue(`${path}[${i}]`, 'Invalid undefined value'));\n } else {\n checkForNull(array[i], `${path}[${i}]`, issues);\n }\n }\n}\n\nfunction checkObjectForNull(obj: Record<string, unknown>, path: string, issues: OperationOutcomeIssue[]): void {\n for (const [key, value] of Object.entries(obj)) {\n checkForNull(value, `${path}${path ? '.' : ''}${key}`, issues);\n }\n}\n\nexport function createStructureIssue(expression: string, details: string): OperationOutcomeIssue {\n return {\n severity: 'error',\n code: 'structure',\n details: {\n text: details,\n },\n expression: [expression],\n };\n}\n"],"names":[],"mappings":"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"}
@@ -28,7 +28,7 @@ var SearchParameterType;
28
28
  * @returns The search parameter type details.
29
29
  */
30
30
  function getSearchParameterDetails(resourceType, searchParam) {
31
- let result = globalSchema.types[resourceType]?.searchParamsDetails?.[searchParam.code];
31
+ let result = globalSchema.types[resourceType].searchParamsDetails?.[searchParam.code];
32
32
  if (!result) {
33
33
  result = buildSearchParameterDetails(resourceType, searchParam);
34
34
  }
@@ -98,37 +98,35 @@ function convertCodeToColumnName(code) {
98
98
  return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');
99
99
  }
100
100
  function getSearchParameterType(searchParam, propertyType) {
101
- let type = SearchParameterType.TEXT;
102
101
  switch (searchParam.type) {
103
102
  case 'date':
104
103
  if (propertyType === PropertyType.date) {
105
- type = SearchParameterType.DATE;
104
+ return SearchParameterType.DATE;
106
105
  }
107
106
  else {
108
- type = SearchParameterType.DATETIME;
107
+ return SearchParameterType.DATETIME;
109
108
  }
110
- break;
111
109
  case 'number':
112
- type = SearchParameterType.NUMBER;
113
- break;
110
+ return SearchParameterType.NUMBER;
114
111
  case 'quantity':
115
- type = SearchParameterType.QUANTITY;
116
- break;
112
+ return SearchParameterType.QUANTITY;
117
113
  case 'reference':
118
114
  if (propertyType === PropertyType.canonical) {
119
- type = SearchParameterType.CANONICAL;
115
+ return SearchParameterType.CANONICAL;
120
116
  }
121
117
  else {
122
- type = SearchParameterType.REFERENCE;
118
+ return SearchParameterType.REFERENCE;
123
119
  }
124
- break;
125
120
  case 'token':
126
- if (propertyType === 'boolean') {
127
- type = SearchParameterType.BOOLEAN;
121
+ if (propertyType === PropertyType.boolean) {
122
+ return SearchParameterType.BOOLEAN;
128
123
  }
129
- break;
124
+ else {
125
+ return SearchParameterType.TEXT;
126
+ }
127
+ default:
128
+ return SearchParameterType.TEXT;
130
129
  }
131
- return type;
132
130
  }
133
131
  function getExpressionForResourceType(resourceType, expression) {
134
132
  const expressions = expression.split(' | ');
@@ -1 +1 @@
1
- {"version":3,"file":"details.mjs","sources":["../../../src/search/details.ts"],"sourcesContent":["import { ElementDefinition, SearchParameter } from '@medplum/fhirtypes';\nimport { buildTypeName, getElementDefinition, globalSchema, PropertyType } from '../types';\nimport { capitalize } from '../utils';\n\nexport enum SearchParameterType {\n BOOLEAN = 'BOOLEAN',\n NUMBER = 'NUMBER',\n QUANTITY = 'QUANTITY',\n TEXT = 'TEXT',\n REFERENCE = 'REFERENCE',\n CANONICAL = 'CANONICAL',\n DATE = 'DATE',\n DATETIME = 'DATETIME',\n PERIOD = 'PERIOD',\n UUID = 'UUID',\n}\n\nexport interface SearchParameterDetails {\n readonly columnName: string;\n readonly type: SearchParameterType;\n readonly elementDefinition?: ElementDefinition;\n readonly array?: boolean;\n}\n\n/**\n * Returns the type details of a SearchParameter.\n *\n * The SearchParameter resource has a \"type\" parameter, but that is missing some critical information.\n *\n * For example:\n * 1) The \"date\" type includes \"date\", \"datetime\", and \"period\".\n * 2) The \"token\" type includes enums and booleans.\n * 3) Arrays/multiple values are not reflected at all.\n * @param resourceType The root resource type.\n * @param searchParam The search parameter.\n * @returns The search parameter type details.\n */\nexport function getSearchParameterDetails(resourceType: string, searchParam: SearchParameter): SearchParameterDetails {\n let result: SearchParameterDetails | undefined =\n globalSchema.types[resourceType]?.searchParamsDetails?.[searchParam.code as string];\n if (!result) {\n result = buildSearchParameterDetails(resourceType, searchParam);\n }\n return result;\n}\n\nfunction setSearchParameterDetails(resourceType: string, code: string, details: SearchParameterDetails): void {\n const typeSchema = globalSchema.types[resourceType];\n if (!typeSchema.searchParamsDetails) {\n typeSchema.searchParamsDetails = {};\n }\n typeSchema.searchParamsDetails[code] = details;\n}\n\nfunction buildSearchParameterDetails(resourceType: string, searchParam: SearchParameter): SearchParameterDetails {\n const code = searchParam.code as string;\n const columnName = convertCodeToColumnName(code);\n const expression = getExpressionForResourceType(resourceType, searchParam.expression as string)?.split('.');\n if (!expression) {\n // This happens on compound types\n // In the future, explore returning multiple column definitions\n return { columnName, type: SearchParameterType.TEXT };\n }\n\n let baseType = resourceType;\n let elementDefinition = undefined;\n let propertyType = undefined;\n let array = false;\n\n for (let i = 1; i < expression.length; i++) {\n let propertyName = expression[i];\n let hasArrayIndex = false;\n\n const arrayIndexMatch = /\\[\\d+\\]$/.exec(propertyName);\n if (arrayIndexMatch) {\n propertyName = propertyName.substring(0, propertyName.length - arrayIndexMatch[0].length);\n hasArrayIndex = true;\n }\n\n elementDefinition = getElementDefinition(baseType, propertyName);\n if (!elementDefinition) {\n throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);\n }\n\n if (elementDefinition.max !== '0' && elementDefinition.max !== '1' && !hasArrayIndex) {\n array = true;\n }\n\n // \"code\" is only missing when using \"contentReference\"\n // \"contentReference\" is handled above in \"getElementDefinition\"\n propertyType = elementDefinition.type?.[0].code as string;\n\n if (i < expression.length - 1) {\n if (isBackboneElement(propertyType)) {\n baseType = buildTypeName(elementDefinition.path?.split('.') as string[]);\n } else {\n baseType = propertyType;\n }\n }\n }\n\n const type = getSearchParameterType(searchParam, propertyType as PropertyType);\n const result = { columnName, type, elementDefinition, array };\n setSearchParameterDetails(resourceType, code, result);\n return result;\n}\n\nfunction isBackboneElement(propertyType: string): boolean {\n return propertyType === 'Element' || propertyType === 'BackboneElement';\n}\n\n/**\n * Converts a hyphen-delimited code to camelCase string.\n * @param code The search parameter code.\n * @returns The SQL column name.\n */\nfunction convertCodeToColumnName(code: string): string {\n return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');\n}\n\nfunction getSearchParameterType(searchParam: SearchParameter, propertyType: PropertyType): SearchParameterType {\n let type = SearchParameterType.TEXT;\n switch (searchParam.type) {\n case 'date':\n if (propertyType === PropertyType.date) {\n type = SearchParameterType.DATE;\n } else {\n type = SearchParameterType.DATETIME;\n }\n break;\n case 'number':\n type = SearchParameterType.NUMBER;\n break;\n case 'quantity':\n type = SearchParameterType.QUANTITY;\n break;\n case 'reference':\n if (propertyType === PropertyType.canonical) {\n type = SearchParameterType.CANONICAL;\n } else {\n type = SearchParameterType.REFERENCE;\n }\n break;\n case 'token':\n if (propertyType === 'boolean') {\n type = SearchParameterType.BOOLEAN;\n }\n break;\n }\n return type;\n}\n\nexport function getExpressionForResourceType(resourceType: string, expression: string): string | undefined {\n const expressions = expression.split(' | ');\n for (const e of expressions) {\n if (isIgnoredExpression(e)) {\n continue;\n }\n const simplified = simplifyExpression(e);\n if (simplified.startsWith(resourceType + '.')) {\n return simplified;\n }\n }\n return undefined;\n}\n\nfunction isIgnoredExpression(input: string): boolean {\n return input.includes(' as Period') || input.includes(' as SampledDate');\n}\n\nfunction simplifyExpression(input: string): string {\n let result = input.trim();\n\n if (result.startsWith('(') && result.endsWith(')')) {\n result = result.substring(1, result.length - 1);\n }\n\n const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.resolve(', '.where('];\n for (const stopString of stopStrings) {\n if (result.includes(stopString)) {\n result = result.substring(0, result.indexOf(stopString));\n }\n }\n\n return result;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"details.mjs","sources":["../../../src/search/details.ts"],"sourcesContent":["import { ElementDefinition, SearchParameter } from '@medplum/fhirtypes';\nimport { buildTypeName, getElementDefinition, globalSchema, PropertyType } from '../types';\nimport { capitalize } from '../utils';\n\nexport enum SearchParameterType {\n BOOLEAN = 'BOOLEAN',\n NUMBER = 'NUMBER',\n QUANTITY = 'QUANTITY',\n TEXT = 'TEXT',\n REFERENCE = 'REFERENCE',\n CANONICAL = 'CANONICAL',\n DATE = 'DATE',\n DATETIME = 'DATETIME',\n PERIOD = 'PERIOD',\n UUID = 'UUID',\n}\n\nexport interface SearchParameterDetails {\n readonly columnName: string;\n readonly type: SearchParameterType;\n readonly elementDefinition?: ElementDefinition;\n readonly array?: boolean;\n}\n\n/**\n * Returns the type details of a SearchParameter.\n *\n * The SearchParameter resource has a \"type\" parameter, but that is missing some critical information.\n *\n * For example:\n * 1) The \"date\" type includes \"date\", \"datetime\", and \"period\".\n * 2) The \"token\" type includes enums and booleans.\n * 3) Arrays/multiple values are not reflected at all.\n * @param resourceType The root resource type.\n * @param searchParam The search parameter.\n * @returns The search parameter type details.\n */\nexport function getSearchParameterDetails(resourceType: string, searchParam: SearchParameter): SearchParameterDetails {\n let result: SearchParameterDetails | undefined =\n globalSchema.types[resourceType].searchParamsDetails?.[searchParam.code as string];\n if (!result) {\n result = buildSearchParameterDetails(resourceType, searchParam);\n }\n return result;\n}\n\nfunction setSearchParameterDetails(resourceType: string, code: string, details: SearchParameterDetails): void {\n const typeSchema = globalSchema.types[resourceType];\n if (!typeSchema.searchParamsDetails) {\n typeSchema.searchParamsDetails = {};\n }\n typeSchema.searchParamsDetails[code] = details;\n}\n\nfunction buildSearchParameterDetails(resourceType: string, searchParam: SearchParameter): SearchParameterDetails {\n const code = searchParam.code as string;\n const columnName = convertCodeToColumnName(code);\n const expression = getExpressionForResourceType(resourceType, searchParam.expression as string)?.split('.');\n if (!expression) {\n // This happens on compound types\n // In the future, explore returning multiple column definitions\n return { columnName, type: SearchParameterType.TEXT };\n }\n\n let baseType = resourceType;\n let elementDefinition = undefined;\n let propertyType = undefined;\n let array = false;\n\n for (let i = 1; i < expression.length; i++) {\n let propertyName = expression[i];\n let hasArrayIndex = false;\n\n const arrayIndexMatch = /\\[\\d+\\]$/.exec(propertyName);\n if (arrayIndexMatch) {\n propertyName = propertyName.substring(0, propertyName.length - arrayIndexMatch[0].length);\n hasArrayIndex = true;\n }\n\n elementDefinition = getElementDefinition(baseType, propertyName);\n if (!elementDefinition) {\n throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);\n }\n\n if (elementDefinition.max !== '0' && elementDefinition.max !== '1' && !hasArrayIndex) {\n array = true;\n }\n\n // \"code\" is only missing when using \"contentReference\"\n // \"contentReference\" is handled above in \"getElementDefinition\"\n propertyType = elementDefinition.type?.[0].code as string;\n\n if (i < expression.length - 1) {\n if (isBackboneElement(propertyType)) {\n baseType = buildTypeName(elementDefinition.path?.split('.') as string[]);\n } else {\n baseType = propertyType;\n }\n }\n }\n\n const type = getSearchParameterType(searchParam, propertyType as PropertyType);\n const result = { columnName, type, elementDefinition, array };\n setSearchParameterDetails(resourceType, code, result);\n return result;\n}\n\nfunction isBackboneElement(propertyType: string): boolean {\n return propertyType === 'Element' || propertyType === 'BackboneElement';\n}\n\n/**\n * Converts a hyphen-delimited code to camelCase string.\n * @param code The search parameter code.\n * @returns The SQL column name.\n */\nfunction convertCodeToColumnName(code: string): string {\n return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');\n}\n\nfunction getSearchParameterType(searchParam: SearchParameter, propertyType: PropertyType): SearchParameterType {\n switch (searchParam.type) {\n case 'date':\n if (propertyType === PropertyType.date) {\n return SearchParameterType.DATE;\n } else {\n return SearchParameterType.DATETIME;\n }\n case 'number':\n return SearchParameterType.NUMBER;\n case 'quantity':\n return SearchParameterType.QUANTITY;\n case 'reference':\n if (propertyType === PropertyType.canonical) {\n return SearchParameterType.CANONICAL;\n } else {\n return SearchParameterType.REFERENCE;\n }\n case 'token':\n if (propertyType === PropertyType.boolean) {\n return SearchParameterType.BOOLEAN;\n } else {\n return SearchParameterType.TEXT;\n }\n default:\n return SearchParameterType.TEXT;\n }\n}\n\nexport function getExpressionForResourceType(resourceType: string, expression: string): string | undefined {\n const expressions = expression.split(' | ');\n for (const e of expressions) {\n if (isIgnoredExpression(e)) {\n continue;\n }\n const simplified = simplifyExpression(e);\n if (simplified.startsWith(resourceType + '.')) {\n return simplified;\n }\n }\n return undefined;\n}\n\nfunction isIgnoredExpression(input: string): boolean {\n return input.includes(' as Period') || input.includes(' as SampledDate');\n}\n\nfunction simplifyExpression(input: string): string {\n let result = input.trim();\n\n if (result.startsWith('(') && result.endsWith(')')) {\n result = result.substring(1, result.length - 1);\n }\n\n const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.resolve(', '.where('];\n for (const stopString of stopStrings) {\n if (result.includes(stopString)) {\n result = result.substring(0, result.indexOf(stopString));\n }\n }\n\n return result;\n}\n"],"names":[],"mappings":"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"}
@@ -31,7 +31,7 @@ function matchesSearchRequest(resource, searchRequest) {
31
31
  * @returns True if the resource satisfies the search filter.
32
32
  */
33
33
  function matchesSearchFilter(resource, searchRequest, filter) {
34
- const searchParam = globalSchema.types[searchRequest.resourceType]?.searchParams?.[filter.code];
34
+ const searchParam = globalSchema.types[searchRequest.resourceType].searchParams?.[filter.code];
35
35
  switch (searchParam?.type) {
36
36
  case 'reference':
37
37
  return matchesReferenceFilter(resource, filter, searchParam);
@@ -42,10 +42,11 @@ function matchesSearchFilter(resource, searchRequest, filter) {
42
42
  return matchesTokenFilter(resource, filter, searchParam);
43
43
  case 'date':
44
44
  return matchesDateFilter(resource, filter, searchParam);
45
+ default:
46
+ // Unknown search parameter or search parameter type
47
+ // Default fail the check
48
+ return false;
45
49
  }
46
- // Unknown search parameter or search parameter type
47
- // Default fail the check
48
- return false;
49
50
  }
50
51
  function matchesReferenceFilter(resource, filter, searchParam) {
51
52
  const values = evalFhirPath(searchParam.expression, resource);
@@ -153,8 +154,9 @@ function matchesDateValue(resourceValue, operator, filterValue) {
153
154
  case Operator.EQUALS:
154
155
  case Operator.NOT_EQUALS:
155
156
  return resourceValue === filterValue;
157
+ default:
158
+ return false;
156
159
  }
157
- return false;
158
160
  }
159
161
  function isNegated(operator) {
160
162
  return operator === Operator.NOT_EQUALS || operator === Operator.NOT;
@@ -1 +1 @@
1
- {"version":3,"file":"match.mjs","sources":["../../../src/search/match.ts"],"sourcesContent":["import { Reference, Resource, SearchParameter } from '@medplum/fhirtypes';\nimport { evalFhirPath } from '../fhirpath';\nimport { globalSchema } from '../types';\nimport { getSearchParameterDetails, SearchParameterType } from './details';\nimport { Filter, Operator, SearchRequest } from './search';\n\n/**\n * Determines if the resource matches the search request.\n * @param resource The resource that was created or updated.\n * @param searchRequest The subscription criteria as a search request.\n * @returns True if the resource satisfies the search request.\n */\nexport function matchesSearchRequest(resource: Resource, searchRequest: SearchRequest): boolean {\n if (searchRequest.resourceType !== resource.resourceType) {\n return false;\n }\n if (searchRequest.filters) {\n for (const filter of searchRequest.filters) {\n if (!matchesSearchFilter(resource, searchRequest, filter)) {\n return false;\n }\n }\n }\n return true;\n}\n\n/**\n * Determines if the resource matches the search filter.\n * @param resource The resource that was created or updated.\n * @param searchRequest The search request.\n * @param filter One of the filters of a subscription criteria.\n * @returns True if the resource satisfies the search filter.\n */\nfunction matchesSearchFilter(resource: Resource, searchRequest: SearchRequest, filter: Filter): boolean {\n const searchParam = globalSchema.types[searchRequest.resourceType]?.searchParams?.[filter.code];\n switch (searchParam?.type) {\n case 'reference':\n return matchesReferenceFilter(resource, filter, searchParam);\n case 'string':\n case 'uri':\n return matchesStringFilter(resource, filter, searchParam);\n case 'token':\n return matchesTokenFilter(resource, filter, searchParam);\n case 'date':\n return matchesDateFilter(resource, filter, searchParam);\n }\n // Unknown search parameter or search parameter type\n // Default fail the check\n return false;\n}\n\nfunction matchesReferenceFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const values = evalFhirPath(searchParam.expression as string, resource) as (Reference | string)[];\n const negated = isNegated(filter.operator);\n\n if (filter.value === '' && values.length === 0) {\n // If the filter operator is \"equals\", then the filter matches.\n // If the filter operator is \"not equals\", then the filter does not match.\n return filter.operator === Operator.EQUALS;\n }\n\n // Normalize the values array into reference strings\n const references = values.map((value) => (typeof value === 'string' ? value : value.reference));\n\n for (const filterValue of filter.value.split(',')) {\n let match = references.includes(filterValue);\n if (!match && filter.code === '_compartment') {\n // Backwards compability for compartment search parameter\n // In previous versions, the resource type was not required in compartment values\n // So, \"123\" would match \"Patient/123\"\n // We need to maintain this behavior for backwards compatibility\n match = references.some((reference) => reference?.endsWith('/' + filterValue));\n }\n if (match) {\n return !negated;\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesTokenFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const details = getSearchParameterDetails(resource.resourceType, searchParam);\n if (details.type === SearchParameterType.BOOLEAN) {\n return matchesBooleanFilter(resource, filter, searchParam);\n } else {\n return matchesStringFilter(resource, filter, searchParam, true);\n }\n}\n\nfunction matchesBooleanFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const values = evalFhirPath(searchParam.expression as string, resource);\n const expected = filter.value === 'true';\n const result = values.includes(expected);\n return isNegated(filter.operator) ? !result : result;\n}\n\nfunction matchesStringFilter(\n resource: Resource,\n filter: Filter,\n searchParam: SearchParameter,\n asToken?: boolean\n): boolean {\n const resourceValues = evalFhirPath(searchParam.expression as string, resource);\n const filterValues = filter.value.split(',');\n const negated = isNegated(filter.operator);\n for (const resourceValue of resourceValues) {\n for (const filterValue of filterValues) {\n const match = matchesStringValue(resourceValue, filter.operator, filterValue, asToken);\n if (match) {\n return !negated;\n }\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesStringValue(\n resourceValue: unknown,\n operator: Operator,\n filterValue: string,\n asToken?: boolean\n): boolean {\n if (asToken && filterValue.includes('|')) {\n const [system, code] = filterValue.split('|');\n return (\n matchesStringValue(resourceValue, operator, system, false) &&\n (!code || matchesStringValue(resourceValue, operator, code, false))\n );\n }\n let str = '';\n if (resourceValue) {\n if (typeof resourceValue === 'string') {\n str = resourceValue;\n } else if (typeof resourceValue === 'object') {\n str = JSON.stringify(resourceValue);\n }\n }\n return str.toLowerCase().includes(filterValue.toLowerCase());\n}\n\nfunction matchesDateFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const resourceValues = evalFhirPath(searchParam.expression as string, resource);\n const filterValues = filter.value.split(',');\n const negated = isNegated(filter.operator);\n for (const resourceValue of resourceValues) {\n for (const filterValue of filterValues) {\n const match = matchesDateValue(resourceValue as string, filter.operator, filterValue);\n if (match) {\n return !negated;\n }\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesDateValue(resourceValue: string, operator: Operator, filterValue: string): boolean {\n switch (operator) {\n case Operator.STARTS_AFTER:\n case Operator.GREATER_THAN:\n return resourceValue > filterValue;\n case Operator.GREATER_THAN_OR_EQUALS:\n return resourceValue >= filterValue;\n case Operator.ENDS_BEFORE:\n case Operator.LESS_THAN:\n return resourceValue < filterValue;\n case Operator.LESS_THAN_OR_EQUALS:\n return resourceValue <= filterValue;\n case Operator.EQUALS:\n case Operator.NOT_EQUALS:\n return resourceValue === filterValue;\n }\n return false;\n}\n\nfunction isNegated(operator: Operator): boolean {\n return operator === Operator.NOT_EQUALS || operator === Operator.NOT;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"match.mjs","sources":["../../../src/search/match.ts"],"sourcesContent":["import { Reference, Resource, SearchParameter } from '@medplum/fhirtypes';\nimport { evalFhirPath } from '../fhirpath';\nimport { globalSchema } from '../types';\nimport { SearchParameterType, getSearchParameterDetails } from './details';\nimport { Filter, Operator, SearchRequest } from './search';\n\n/**\n * Determines if the resource matches the search request.\n * @param resource The resource that was created or updated.\n * @param searchRequest The subscription criteria as a search request.\n * @returns True if the resource satisfies the search request.\n */\nexport function matchesSearchRequest(resource: Resource, searchRequest: SearchRequest): boolean {\n if (searchRequest.resourceType !== resource.resourceType) {\n return false;\n }\n if (searchRequest.filters) {\n for (const filter of searchRequest.filters) {\n if (!matchesSearchFilter(resource, searchRequest, filter)) {\n return false;\n }\n }\n }\n return true;\n}\n\n/**\n * Determines if the resource matches the search filter.\n * @param resource The resource that was created or updated.\n * @param searchRequest The search request.\n * @param filter One of the filters of a subscription criteria.\n * @returns True if the resource satisfies the search filter.\n */\nfunction matchesSearchFilter(resource: Resource, searchRequest: SearchRequest, filter: Filter): boolean {\n const searchParam = globalSchema.types[searchRequest.resourceType].searchParams?.[filter.code];\n switch (searchParam?.type) {\n case 'reference':\n return matchesReferenceFilter(resource, filter, searchParam);\n case 'string':\n case 'uri':\n return matchesStringFilter(resource, filter, searchParam);\n case 'token':\n return matchesTokenFilter(resource, filter, searchParam);\n case 'date':\n return matchesDateFilter(resource, filter, searchParam);\n default:\n // Unknown search parameter or search parameter type\n // Default fail the check\n return false;\n }\n}\n\nfunction matchesReferenceFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const values = evalFhirPath(searchParam.expression as string, resource) as (Reference | string)[];\n const negated = isNegated(filter.operator);\n\n if (filter.value === '' && values.length === 0) {\n // If the filter operator is \"equals\", then the filter matches.\n // If the filter operator is \"not equals\", then the filter does not match.\n return filter.operator === Operator.EQUALS;\n }\n\n // Normalize the values array into reference strings\n const references = values.map((value) => (typeof value === 'string' ? value : value.reference));\n\n for (const filterValue of filter.value.split(',')) {\n let match = references.includes(filterValue);\n if (!match && filter.code === '_compartment') {\n // Backwards compability for compartment search parameter\n // In previous versions, the resource type was not required in compartment values\n // So, \"123\" would match \"Patient/123\"\n // We need to maintain this behavior for backwards compatibility\n match = references.some((reference) => reference?.endsWith('/' + filterValue));\n }\n if (match) {\n return !negated;\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesTokenFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const details = getSearchParameterDetails(resource.resourceType, searchParam);\n if (details.type === SearchParameterType.BOOLEAN) {\n return matchesBooleanFilter(resource, filter, searchParam);\n } else {\n return matchesStringFilter(resource, filter, searchParam, true);\n }\n}\n\nfunction matchesBooleanFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const values = evalFhirPath(searchParam.expression as string, resource);\n const expected = filter.value === 'true';\n const result = values.includes(expected);\n return isNegated(filter.operator) ? !result : result;\n}\n\nfunction matchesStringFilter(\n resource: Resource,\n filter: Filter,\n searchParam: SearchParameter,\n asToken?: boolean\n): boolean {\n const resourceValues = evalFhirPath(searchParam.expression as string, resource);\n const filterValues = filter.value.split(',');\n const negated = isNegated(filter.operator);\n for (const resourceValue of resourceValues) {\n for (const filterValue of filterValues) {\n const match = matchesStringValue(resourceValue, filter.operator, filterValue, asToken);\n if (match) {\n return !negated;\n }\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesStringValue(\n resourceValue: unknown,\n operator: Operator,\n filterValue: string,\n asToken?: boolean\n): boolean {\n if (asToken && filterValue.includes('|')) {\n const [system, code] = filterValue.split('|');\n return (\n matchesStringValue(resourceValue, operator, system, false) &&\n (!code || matchesStringValue(resourceValue, operator, code, false))\n );\n }\n let str = '';\n if (resourceValue) {\n if (typeof resourceValue === 'string') {\n str = resourceValue;\n } else if (typeof resourceValue === 'object') {\n str = JSON.stringify(resourceValue);\n }\n }\n return str.toLowerCase().includes(filterValue.toLowerCase());\n}\n\nfunction matchesDateFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const resourceValues = evalFhirPath(searchParam.expression as string, resource);\n const filterValues = filter.value.split(',');\n const negated = isNegated(filter.operator);\n for (const resourceValue of resourceValues) {\n for (const filterValue of filterValues) {\n const match = matchesDateValue(resourceValue as string, filter.operator, filterValue);\n if (match) {\n return !negated;\n }\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesDateValue(resourceValue: string, operator: Operator, filterValue: string): boolean {\n switch (operator) {\n case Operator.STARTS_AFTER:\n case Operator.GREATER_THAN:\n return resourceValue > filterValue;\n case Operator.GREATER_THAN_OR_EQUALS:\n return resourceValue >= filterValue;\n case Operator.ENDS_BEFORE:\n case Operator.LESS_THAN:\n return resourceValue < filterValue;\n case Operator.LESS_THAN_OR_EQUALS:\n return resourceValue <= filterValue;\n case Operator.EQUALS:\n case Operator.NOT_EQUALS:\n return resourceValue === filterValue;\n default:\n return false;\n }\n}\n\nfunction isNegated(operator: Operator): boolean {\n return operator === Operator.NOT_EQUALS || operator === Operator.NOT;\n}\n"],"names":[],"mappings":"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"}