@medplum/core 2.0.24 → 2.0.25

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 (81) hide show
  1. package/dist/cjs/index.cjs +28 -14114
  2. package/dist/cjs/index.cjs.map +7 -1
  3. package/dist/esm/index.mjs +31 -29
  4. package/dist/esm/index.mjs.map +7 -1
  5. package/dist/types/utils.d.ts +14 -0
  6. package/package.json +2 -2
  7. package/dist/cjs/index.min.cjs +0 -1
  8. package/dist/esm/access.mjs +0 -142
  9. package/dist/esm/access.mjs.map +0 -1
  10. package/dist/esm/base-schema.json.mjs +0 -4408
  11. package/dist/esm/base-schema.json.mjs.map +0 -1
  12. package/dist/esm/base64.mjs +0 -33
  13. package/dist/esm/base64.mjs.map +0 -1
  14. package/dist/esm/bundle.mjs +0 -36
  15. package/dist/esm/bundle.mjs.map +0 -1
  16. package/dist/esm/cache.mjs +0 -64
  17. package/dist/esm/cache.mjs.map +0 -1
  18. package/dist/esm/client.mjs +0 -2168
  19. package/dist/esm/client.mjs.map +0 -1
  20. package/dist/esm/crypto.mjs +0 -22
  21. package/dist/esm/crypto.mjs.map +0 -1
  22. package/dist/esm/eventtarget.mjs +0 -36
  23. package/dist/esm/eventtarget.mjs.map +0 -1
  24. package/dist/esm/fhirlexer/parse.mjs +0 -122
  25. package/dist/esm/fhirlexer/parse.mjs.map +0 -1
  26. package/dist/esm/fhirlexer/tokenize.mjs +0 -231
  27. package/dist/esm/fhirlexer/tokenize.mjs.map +0 -1
  28. package/dist/esm/fhirmapper/parse.mjs +0 -329
  29. package/dist/esm/fhirmapper/parse.mjs.map +0 -1
  30. package/dist/esm/fhirmapper/tokenize.mjs +0 -13
  31. package/dist/esm/fhirmapper/tokenize.mjs.map +0 -1
  32. package/dist/esm/fhirpath/atoms.mjs +0 -347
  33. package/dist/esm/fhirpath/atoms.mjs.map +0 -1
  34. package/dist/esm/fhirpath/date.mjs +0 -24
  35. package/dist/esm/fhirpath/date.mjs.map +0 -1
  36. package/dist/esm/fhirpath/functions.mjs +0 -1626
  37. package/dist/esm/fhirpath/functions.mjs.map +0 -1
  38. package/dist/esm/fhirpath/parse.mjs +0 -145
  39. package/dist/esm/fhirpath/parse.mjs.map +0 -1
  40. package/dist/esm/fhirpath/tokenize.mjs +0 -10
  41. package/dist/esm/fhirpath/tokenize.mjs.map +0 -1
  42. package/dist/esm/fhirpath/utils.mjs +0 -377
  43. package/dist/esm/fhirpath/utils.mjs.map +0 -1
  44. package/dist/esm/filter/parse.mjs +0 -101
  45. package/dist/esm/filter/parse.mjs.map +0 -1
  46. package/dist/esm/filter/tokenize.mjs +0 -16
  47. package/dist/esm/filter/tokenize.mjs.map +0 -1
  48. package/dist/esm/filter/types.mjs +0 -34
  49. package/dist/esm/filter/types.mjs.map +0 -1
  50. package/dist/esm/format.mjs +0 -390
  51. package/dist/esm/format.mjs.map +0 -1
  52. package/dist/esm/hl7.mjs +0 -242
  53. package/dist/esm/hl7.mjs.map +0 -1
  54. package/dist/esm/index.min.mjs +0 -1
  55. package/dist/esm/jwt.mjs +0 -30
  56. package/dist/esm/jwt.mjs.map +0 -1
  57. package/dist/esm/node_modules/tslib/package.json +0 -1
  58. package/dist/esm/outcomes.mjs +0 -295
  59. package/dist/esm/outcomes.mjs.map +0 -1
  60. package/dist/esm/readablepromise.mjs +0 -82
  61. package/dist/esm/readablepromise.mjs.map +0 -1
  62. package/dist/esm/schema.mjs +0 -417
  63. package/dist/esm/schema.mjs.map +0 -1
  64. package/dist/esm/search/details.mjs +0 -162
  65. package/dist/esm/search/details.mjs.map +0 -1
  66. package/dist/esm/search/match.mjs +0 -166
  67. package/dist/esm/search/match.mjs.map +0 -1
  68. package/dist/esm/search/search.mjs +0 -378
  69. package/dist/esm/search/search.mjs.map +0 -1
  70. package/dist/esm/sftp.mjs +0 -24
  71. package/dist/esm/sftp.mjs.map +0 -1
  72. package/dist/esm/storage.mjs +0 -95
  73. package/dist/esm/storage.mjs.map +0 -1
  74. package/dist/esm/types.mjs +0 -370
  75. package/dist/esm/types.mjs.map +0 -1
  76. package/dist/esm/typeschema/types.mjs +0 -278
  77. package/dist/esm/typeschema/types.mjs.map +0 -1
  78. package/dist/esm/typeschema/validation.mjs +0 -262
  79. package/dist/esm/typeschema/validation.mjs.map +0 -1
  80. package/dist/esm/utils.mjs +0 -632
  81. package/dist/esm/utils.mjs.map +0 -1
@@ -1,417 +0,0 @@
1
- import { globalSchema, PropertyType } from './types.mjs';
2
- import { isEmpty, isLowerCase, getExtensionValue, capitalize } from './utils.mjs';
3
- import { toTypedValue, getTypedPropertyValue } from './fhirpath/utils.mjs';
4
- import './fhirpath/parse.mjs';
5
- import { OperationOutcomeError, validationError } from './outcomes.mjs';
6
-
7
- /*
8
- * This file provides schema validation utilities for FHIR JSON objects.
9
- *
10
- * See: [JSON Representation of Resources](https://hl7.org/fhir/json.html)
11
- * See: [FHIR Data Types](https://www.hl7.org/fhir/datatypes.html)
12
- */
13
- const fhirTypeToJsType = {
14
- base64Binary: 'string',
15
- boolean: 'boolean',
16
- canonical: 'string',
17
- code: 'string',
18
- date: 'string',
19
- dateTime: 'string',
20
- decimal: 'number',
21
- id: 'string',
22
- instant: 'string',
23
- integer: 'number',
24
- markdown: 'string',
25
- oid: 'string',
26
- positiveInt: 'number',
27
- string: 'string',
28
- time: 'string',
29
- unsignedInt: 'number',
30
- uri: 'string',
31
- url: 'string',
32
- uuid: 'string',
33
- xhtml: 'string',
34
- 'http://hl7.org/fhirpath/System.String': 'string',
35
- };
36
- const baseResourceProperties = new Set([
37
- // Resource
38
- 'resourceType',
39
- 'id',
40
- 'meta',
41
- 'implicitRules',
42
- 'language',
43
- // DomainResource
44
- 'text',
45
- 'contained',
46
- 'extension',
47
- 'modifierExtension',
48
- ]);
49
- /**
50
- * Returns true if the given string is a valid FHIR resource type.
51
- *
52
- * ```ts
53
- * isResourceType('Patient'); // true
54
- * isResourceType('XYZ'); // false
55
- * ```
56
- *
57
- * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.
58
- *
59
- * In a server context, you can load all schema definitions:
60
- *
61
- * ```ts
62
- * import { indexStructureDefinitionBundle } from '@medplum/core';
63
- * import { readJson } from '@medplum/definitions';
64
- * import { Bundle } from '@medplum/fhirtypes';
65
- *
66
- * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);
67
- * ```
68
- *
69
- * In a client context, you can load the schema definitions using MedplumClient:
70
- *
71
- * ```ts
72
- * import { MedplumClient } from '@medplum/core';
73
- *
74
- * const medplum = new MedplumClient();
75
- * await medplum.requestSchema('Patient');
76
- * ```
77
- * @param resourceType The candidate resource type string.
78
- * @returns True if the resource type is a valid FHIR resource type.
79
- */
80
- function isResourceType(resourceType) {
81
- const typeSchema = globalSchema.types[resourceType];
82
- return (typeSchema &&
83
- typeSchema.structureDefinition.id === resourceType &&
84
- typeSchema.structureDefinition.kind === 'resource');
85
- }
86
- /**
87
- * Validates that the given string is a valid FHIR resource type.
88
- * On success, silently returns void.
89
- * On failure, throws an OperationOutcomeError.
90
- *
91
- * ```ts
92
- * validateResourceType('Patient'); // nothing
93
- * validateResourceType('XYZ'); // throws OperationOutcomeError
94
- * ```
95
- *
96
- * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.
97
- *
98
- * In a server context, you can load all schema definitions:
99
- *
100
- * ```ts
101
- * import { indexStructureDefinitionBundle } from '@medplum/core';
102
- * import { readJson } from '@medplum/definitions';
103
- * import { Bundle } from '@medplum/fhirtypes';
104
- *
105
- * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);
106
- * ```
107
- *
108
- * In a client context, you can load the schema definitions using MedplumClient:
109
- *
110
- * ```ts
111
- * import { MedplumClient } from '@medplum/core';
112
- *
113
- * const medplum = new MedplumClient();
114
- * await medplum.requestSchema('Patient');
115
- * ```
116
- * @param resourceType The candidate resource type string.
117
- */
118
- function validateResourceType(resourceType) {
119
- if (!resourceType) {
120
- throw new OperationOutcomeError(validationError('Resource type is null'));
121
- }
122
- if (!isResourceType(resourceType)) {
123
- throw new OperationOutcomeError(validationError('Unknown resource type'));
124
- }
125
- }
126
- /**
127
- * Validates a candidate FHIR resource object.
128
- * On success, silently returns void.
129
- * On failure, throws an OperationOutcomeError with issues for each violation.
130
- *
131
- * ```ts
132
- * validateResource({ resourceType: 'Patient' }); // nothing
133
- * validateResource({ resourceType: 'XYZ' }); // throws OperationOutcomeError
134
- * ```
135
- *
136
- * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.
137
- *
138
- * In a server context, you can load all schema definitions:
139
- *
140
- * ```ts
141
- * import { indexStructureDefinitionBundle } from '@medplum/core';
142
- * import { readJson } from '@medplum/definitions';
143
- * import { Bundle } from '@medplum/fhirtypes';
144
- *
145
- * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);
146
- * ```
147
- *
148
- * In a client context, you can load the schema definitions using MedplumClient:
149
- *
150
- * ```ts
151
- * import { MedplumClient } from '@medplum/core';
152
- *
153
- * const medplum = new MedplumClient();
154
- * await medplum.requestSchema('Patient');
155
- * ```
156
- * @param resource The candidate resource.
157
- */
158
- function validateResource(resource) {
159
- new FhirSchemaValidator(resource).validate();
160
- }
161
- class FhirSchemaValidator {
162
- constructor(root) {
163
- this.issues = [];
164
- this.root = root;
165
- }
166
- validate() {
167
- const resource = this.root;
168
- if (!resource) {
169
- throw new OperationOutcomeError(validationError('Resource is null'));
170
- }
171
- const resourceType = resource.resourceType;
172
- if (!resourceType) {
173
- throw new OperationOutcomeError(validationError('Missing resource type'));
174
- }
175
- // Check for "null" once for the entire object hierarchy
176
- checkForNull(resource, '', this.issues);
177
- this.validateObject(toTypedValue(resource), resourceType);
178
- if (this.issues.length > 0) {
179
- throw new OperationOutcomeError({
180
- resourceType: 'OperationOutcome',
181
- issue: this.issues,
182
- });
183
- }
184
- }
185
- validateObject(typedValue, path) {
186
- const definition = globalSchema.types[typedValue.type];
187
- if (!definition) {
188
- throw new OperationOutcomeError(validationError('Unknown type: ' + typedValue.type));
189
- }
190
- const propertyDefinitions = definition.properties;
191
- this.checkProperties(path, propertyDefinitions, typedValue);
192
- this.checkAdditionalProperties(path, typedValue, propertyDefinitions);
193
- }
194
- checkProperties(path, propertyDefinitions, typedValue) {
195
- for (const [key, elementDefinition] of Object.entries(propertyDefinitions)) {
196
- this.checkProperty(path + '.' + key, elementDefinition, typedValue);
197
- }
198
- }
199
- checkProperty(path, elementDefinition, typedValue) {
200
- const propertyName = path.split('.').pop();
201
- const value = getTypedPropertyValue(typedValue, propertyName);
202
- if (isEmpty(value)) {
203
- if (elementDefinition.min !== undefined && elementDefinition.min > 0) {
204
- this.issues.push(createStructureIssue(path, 'Missing required property'));
205
- }
206
- return;
207
- }
208
- if (elementDefinition.max === '*') {
209
- if (!Array.isArray(value)) {
210
- this.issues.push(createStructureIssue(path, 'Expected array for property'));
211
- return;
212
- }
213
- for (const item of value) {
214
- this.checkPropertyValue(path, elementDefinition, item);
215
- }
216
- }
217
- else {
218
- if (Array.isArray(value)) {
219
- this.issues.push(createStructureIssue(path, 'Expected single value for property'));
220
- return;
221
- }
222
- this.checkPropertyValue(path, elementDefinition, value);
223
- }
224
- }
225
- checkPropertyValue(path, elementDefinition, typedValue) {
226
- if (typedValue.value === null) {
227
- // Null handled separately
228
- return;
229
- }
230
- if (isLowerCase(typedValue.type.charAt(0))) {
231
- this.validatePrimitiveType(elementDefinition, typedValue);
232
- }
233
- else {
234
- this.validateObject(typedValue, path);
235
- }
236
- }
237
- validatePrimitiveType(elementDefinition, typedValue) {
238
- const { type, value } = typedValue;
239
- if (value === null) {
240
- // Null handled separately, so this code should never be reached
241
- // Leaving this check in place for now, in case we change the null handling
242
- return;
243
- }
244
- // First, make sure the value is the correct JS type
245
- const expectedType = fhirTypeToJsType[typedValue.type];
246
- if (typeof value !== expectedType) {
247
- this.createIssue(elementDefinition, 'Invalid type for ' + type);
248
- return;
249
- }
250
- // Then, perform additional checks for specialty types
251
- if (expectedType === 'string') {
252
- this.validateString(elementDefinition, type, value);
253
- }
254
- else if (expectedType === 'number') {
255
- this.validateNumber(elementDefinition, type, value);
256
- }
257
- }
258
- validateString(elementDefinition, type, value) {
259
- if (!value.trim()) {
260
- this.createIssue(elementDefinition, 'Invalid empty string');
261
- return;
262
- }
263
- // Try to get the regex
264
- const valueDefinition = globalSchema.types[type]?.properties['value'];
265
- if (valueDefinition?.type) {
266
- const regex = getExtensionValue(valueDefinition.type[0], 'http://hl7.org/fhir/StructureDefinition/regex');
267
- if (regex) {
268
- if (!value.match(new RegExp(regex))) {
269
- this.createIssue(elementDefinition, 'Invalid ' + type + ' format');
270
- }
271
- }
272
- }
273
- }
274
- validateNumber(elementDefinition, type, value) {
275
- if (isNaN(value) || !isFinite(value)) {
276
- this.createIssue(elementDefinition, 'Invalid ' + type + ' value');
277
- return;
278
- }
279
- if (isIntegerType(type) && !Number.isInteger(value)) {
280
- this.createIssue(elementDefinition, 'Number is not an integer');
281
- }
282
- if (type === PropertyType.positiveInt && value <= 0) {
283
- this.createIssue(elementDefinition, 'Number is less than or equal to zero');
284
- }
285
- if (type === PropertyType.unsignedInt && value < 0) {
286
- this.createIssue(elementDefinition, 'Number is negative');
287
- }
288
- }
289
- checkAdditionalProperties(path, typedValue, propertyDefinitions) {
290
- const object = typedValue.value;
291
- for (const key of Object.keys(object)) {
292
- this.checkAdditionalProperty(path, key, typedValue, propertyDefinitions);
293
- }
294
- }
295
- /**
296
- * Checks if the given property is allowed on the given object.
297
- * @param path The path of the current object.
298
- * @param key The key of a property to check.
299
- * @param typedValue The current object.
300
- * @param propertyDefinitions The property definitions of the current object.
301
- */
302
- checkAdditionalProperty(path, key, typedValue, propertyDefinitions) {
303
- if (!baseResourceProperties.has(key) &&
304
- !(key in propertyDefinitions) &&
305
- !isChoiceOfType(key, typedValue, propertyDefinitions) &&
306
- !this.checkPrimitiveElement(path, key, typedValue)) {
307
- const expression = `${path}.${key}`;
308
- this.issues.push(createStructureIssue(expression, `Invalid additional property "${expression}"`));
309
- }
310
- }
311
- /**
312
- * Checks the element for a primitive.
313
- *
314
- * FHIR elements with primitive data types are represented in two parts:
315
- * 1) A JSON property with the name of the element, which has a JSON type of number, boolean, or string
316
- * 2) a JSON property with _ prepended to the name of the element, which, if present, contains the value's id and/or extensions
317
- *
318
- * See: https://hl7.org/fhir/json.html#primitive
319
- * @param path The path to the property
320
- * @param key The key in the current typed value.
321
- * @param typedValue The current typed value.
322
- * @returns True if the primitive element is valid.
323
- */
324
- checkPrimitiveElement(path, key, typedValue) {
325
- // Primitive element starts with underscore
326
- if (!key.startsWith('_')) {
327
- return false;
328
- }
329
- // Validate the non-underscore property exists
330
- const primitiveKey = key.slice(1);
331
- if (!(primitiveKey in typedValue.value)) {
332
- return false;
333
- }
334
- // Then validate the element
335
- this.validateObject({ type: 'Element', value: typedValue.value[key] }, path);
336
- return true;
337
- }
338
- createIssue(elementDefinition, message) {
339
- this.issues.push(createStructureIssue(elementDefinition.path, message));
340
- }
341
- }
342
- function isIntegerType(propertyType) {
343
- return (propertyType === PropertyType.integer ||
344
- propertyType === PropertyType.positiveInt ||
345
- propertyType === PropertyType.unsignedInt);
346
- }
347
- function isChoiceOfType(key, typedValue, propertyDefinitions) {
348
- for (const propertyName of Object.keys(propertyDefinitions)) {
349
- if (!propertyName.endsWith('[x]')) {
350
- continue;
351
- }
352
- const basePropertyName = propertyName.replace('[x]', '');
353
- if (!key.startsWith(basePropertyName)) {
354
- continue;
355
- }
356
- let typedPropertyValue = getTypedPropertyValue(typedValue, propertyName);
357
- if (!typedPropertyValue) {
358
- continue;
359
- }
360
- if (Array.isArray(typedPropertyValue)) {
361
- // At present, there are no choice types that are arrays in the FHIR spec
362
- // Leaving this here to make TypeScript happy, and in case that changes
363
- typedPropertyValue = typedPropertyValue[0];
364
- }
365
- if (typedPropertyValue && key === basePropertyName + capitalize(typedPropertyValue.type)) {
366
- return true;
367
- }
368
- }
369
- return false;
370
- }
371
- /**
372
- * Recursively checks for null values in an object.
373
- *
374
- * Note that "null" is a special value in JSON that is not allowed in FHIR.
375
- * @param value Input value of any type.
376
- * @param path Path string to the value for OperationOutcome.
377
- * @param issues Output list of issues.
378
- */
379
- function checkForNull(value, path, issues) {
380
- if (value === null) {
381
- issues.push(createStructureIssue(path, 'Invalid null value'));
382
- }
383
- else if (Array.isArray(value)) {
384
- checkArrayForNull(value, path, issues);
385
- }
386
- else if (typeof value === 'object') {
387
- checkObjectForNull(value, path, issues);
388
- }
389
- }
390
- function checkArrayForNull(array, path, issues) {
391
- for (let i = 0; i < array.length; i++) {
392
- if (array[i] === undefined) {
393
- issues.push(createStructureIssue(`${path}[${i}]`, 'Invalid undefined value'));
394
- }
395
- else {
396
- checkForNull(array[i], `${path}[${i}]`, issues);
397
- }
398
- }
399
- }
400
- function checkObjectForNull(obj, path, issues) {
401
- for (const [key, value] of Object.entries(obj)) {
402
- checkForNull(value, `${path}${path ? '.' : ''}${key}`, issues);
403
- }
404
- }
405
- function createStructureIssue(expression, details) {
406
- return {
407
- severity: 'error',
408
- code: 'structure',
409
- details: {
410
- text: details,
411
- },
412
- expression: [expression],
413
- };
414
- }
415
-
416
- export { FhirSchemaValidator, checkForNull, createStructureIssue, isResourceType, validateResource, validateResourceType };
417
- //# sourceMappingURL=schema.mjs.map
@@ -1 +0,0 @@
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":";;;;;;AAMA;;;;;AAKG;AACH,MAAM,gBAAgB,GAA2B;AAC/C,IAAA,YAAY,EAAE,QAAQ;AACtB,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,SAAS,EAAE,QAAQ;AACnB,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,QAAQ,EAAE,QAAQ;AAClB,IAAA,OAAO,EAAE,QAAQ;AACjB,IAAA,EAAE,EAAE,QAAQ;AACZ,IAAA,OAAO,EAAE,QAAQ;AACjB,IAAA,OAAO,EAAE,QAAQ;AACjB,IAAA,QAAQ,EAAE,QAAQ;AAClB,IAAA,GAAG,EAAE,QAAQ;AACb,IAAA,WAAW,EAAE,QAAQ;AACrB,IAAA,MAAM,EAAE,QAAQ;AAChB,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,WAAW,EAAE,QAAQ;AACrB,IAAA,GAAG,EAAE,QAAQ;AACb,IAAA,GAAG,EAAE,QAAQ;AACb,IAAA,IAAI,EAAE,QAAQ;AACd,IAAA,KAAK,EAAE,QAAQ;AACf,IAAA,uCAAuC,EAAE,QAAQ;CAClD,CAAC;AAEF,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAS;;IAE7C,cAAc;IACd,IAAI;IACJ,MAAM;IACN,eAAe;IACf,UAAU;;IAGV,MAAM;IACN,WAAW;IACX,WAAW;IACX,mBAAmB;AACpB,CAAA,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BG;AACG,SAAU,cAAc,CAAC,YAAoB,EAAA;IACjD,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;AACpD,IAAA,QACE,UAAU;AACV,QAAA,UAAU,CAAC,mBAAmB,CAAC,EAAE,KAAK,YAAY;AAClD,QAAA,UAAU,CAAC,mBAAmB,CAAC,IAAI,KAAK,UAAU,EAClD;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BG;AACG,SAAU,oBAAoB,CAAC,YAAoB,EAAA;IACvD,IAAI,CAAC,YAAY,EAAE;QACjB,MAAM,IAAI,qBAAqB,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAC3E,KAAA;AACD,IAAA,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,EAAE;QACjC,MAAM,IAAI,qBAAqB,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAC3E,KAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BG;AACG,SAAU,gBAAgB,CAAqB,QAAW,EAAA;AAC9D,IAAA,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC/C,CAAC;MAEY,mBAAmB,CAAA;AAI9B,IAAA,WAAA,CAAY,IAAO,EAAA;AACjB,QAAA,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;AACjB,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;KAClB;IAED,QAAQ,GAAA;AACN,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,QAAQ,EAAE;YACb,MAAM,IAAI,qBAAqB,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAAC,CAAC;AACtE,SAAA;AAED,QAAA,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC;QAC3C,IAAI,CAAC,YAAY,EAAE;YACjB,MAAM,IAAI,qBAAqB,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAC3E,SAAA;;QAGD,YAAY,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAExC,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC;AAE1D,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;YAC1B,MAAM,IAAI,qBAAqB,CAAC;AAC9B,gBAAA,YAAY,EAAE,kBAAkB;gBAChC,KAAK,EAAE,IAAI,CAAC,MAAM;AACnB,aAAA,CAAC,CAAC;AACJ,SAAA;KACF;IAEO,cAAc,CAAC,UAAsB,EAAE,IAAY,EAAA;QACzD,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,EAAE;AACf,YAAA,MAAM,IAAI,qBAAqB,CAAC,eAAe,CAAC,gBAAgB,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AACtF,SAAA;AAED,QAAA,MAAM,mBAAmB,GAAG,UAAU,CAAC,UAAU,CAAC;QAClD,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,mBAAmB,EAAE,UAAU,CAAC,CAAC;QAC5D,IAAI,CAAC,yBAAyB,CAAC,IAAI,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAC;KACvE;AAEO,IAAA,eAAe,CACrB,IAAY,EACZ,mBAAsD,EACtD,UAAsB,EAAA;AAEtB,QAAA,KAAK,MAAM,CAAC,GAAG,EAAE,iBAAiB,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE;AAC1E,YAAA,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,EAAE,iBAAiB,EAAE,UAAU,CAAC,CAAC;AACrE,SAAA;KACF;AAEO,IAAA,aAAa,CAAC,IAAY,EAAE,iBAAoC,EAAE,UAAsB,EAAA;QAC9F,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAY,CAAC;QACrD,MAAM,KAAK,GAAG,qBAAqB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAE9D,QAAA,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE;YAClB,IAAI,iBAAiB,CAAC,GAAG,KAAK,SAAS,IAAI,iBAAiB,CAAC,GAAG,GAAG,CAAC,EAAE;AACpE,gBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC,CAAC;AAC3E,aAAA;YACD,OAAO;AACR,SAAA;AAED,QAAA,IAAI,iBAAiB,CAAC,GAAG,KAAK,GAAG,EAAE;AACjC,YAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AACzB,gBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,6BAA6B,CAAC,CAAC,CAAC;gBAC5E,OAAO;AACR,aAAA;AACD,YAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;gBACxB,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;AACxD,aAAA;AACF,SAAA;AAAM,aAAA;AACL,YAAA,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AACxB,gBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,oCAAoC,CAAC,CAAC,CAAC;gBACnF,OAAO;AACR,aAAA;YACD,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAmB,CAAC,CAAC;AACvE,SAAA;KACF;AAEO,IAAA,kBAAkB,CAAC,IAAY,EAAE,iBAAoC,EAAE,UAAsB,EAAA;AACnG,QAAA,IAAI,UAAU,CAAC,KAAK,KAAK,IAAI,EAAE;;YAE7B,OAAO;AACR,SAAA;QAED,IAAI,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;AAC1C,YAAA,IAAI,CAAC,qBAAqB,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;AAC3D,SAAA;AAAM,aAAA;AACL,YAAA,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACvC,SAAA;KACF;IAEO,qBAAqB,CAAC,iBAAoC,EAAE,UAAsB,EAAA;AACxF,QAAA,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC;QAEnC,IAAI,KAAK,KAAK,IAAI,EAAE;;;YAGlB,OAAO;AACR,SAAA;;QAGD,MAAM,YAAY,GAAG,gBAAgB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACvD,QAAA,IAAI,OAAO,KAAK,KAAK,YAAY,EAAE;YACjC,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,mBAAmB,GAAG,IAAI,CAAC,CAAC;YAChE,OAAO;AACR,SAAA;;QAGD,IAAI,YAAY,KAAK,QAAQ,EAAE;YAC7B,IAAI,CAAC,cAAc,CAAC,iBAAiB,EAAE,IAAoB,EAAE,KAAe,CAAC,CAAC;AAC/E,SAAA;aAAM,IAAI,YAAY,KAAK,QAAQ,EAAE;YACpC,IAAI,CAAC,cAAc,CAAC,iBAAiB,EAAE,IAAoB,EAAE,KAAe,CAAC,CAAC;AAC/E,SAAA;KACF;AAEO,IAAA,cAAc,CAAC,iBAAoC,EAAE,IAAkB,EAAE,KAAa,EAAA;AAC5F,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE;AACjB,YAAA,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,CAAC;YAC5D,OAAO;AACR,SAAA;;AAGD,QAAA,MAAM,eAAe,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;QACtE,IAAI,eAAe,EAAE,IAAI,EAAE;AACzB,YAAA,MAAM,KAAK,GAAG,iBAAiB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,+CAA+C,CAAC,CAAC;AAC1G,YAAA,IAAI,KAAK,EAAE;gBACT,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE;oBACnC,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AACpE,iBAAA;AACF,aAAA;AACF,SAAA;KACF;AAEO,IAAA,cAAc,CAAC,iBAAoC,EAAE,IAAkB,EAAE,KAAa,EAAA;QAC5F,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;YACpC,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,UAAU,GAAG,IAAI,GAAG,QAAQ,CAAC,CAAC;YAClE,OAAO;AACR,SAAA;AAED,QAAA,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;AACnD,YAAA,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,0BAA0B,CAAC,CAAC;AACjE,SAAA;QAED,IAAI,IAAI,KAAK,YAAY,CAAC,WAAW,IAAI,KAAK,IAAI,CAAC,EAAE;AACnD,YAAA,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,sCAAsC,CAAC,CAAC;AAC7E,SAAA;QAED,IAAI,IAAI,KAAK,YAAY,CAAC,WAAW,IAAI,KAAK,GAAG,CAAC,EAAE;AAClD,YAAA,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;AAC3D,SAAA;KACF;AAEO,IAAA,yBAAyB,CAC/B,IAAY,EACZ,UAAsB,EACtB,mBAAsD,EAAA;AAEtD,QAAA,MAAM,MAAM,GAAG,UAAU,CAAC,KAAgC,CAAC;QAC3D,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;YACrC,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAC;AAC1E,SAAA;KACF;AAED;;;;;;AAMG;AACK,IAAA,uBAAuB,CAC7B,IAAY,EACZ,GAAW,EACX,UAAsB,EACtB,mBAAsD,EAAA;AAEtD,QAAA,IACE,CAAC,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC;AAChC,YAAA,EAAE,GAAG,IAAI,mBAAmB,CAAC;AAC7B,YAAA,CAAC,cAAc,CAAC,GAAG,EAAE,UAAU,EAAE,mBAAmB,CAAC;YACrD,CAAC,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,UAAU,CAAC,EAClD;AACA,YAAA,MAAM,UAAU,GAAG,CAAA,EAAG,IAAI,CAAI,CAAA,EAAA,GAAG,EAAE,CAAC;AACpC,YAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,UAAU,EAAE,CAAgC,6BAAA,EAAA,UAAU,CAAG,CAAA,CAAA,CAAC,CAAC,CAAC;AACnG,SAAA;KACF;AAED;;;;;;;;;;;;AAYG;AACK,IAAA,qBAAqB,CAAC,IAAY,EAAE,GAAW,EAAE,UAAsB,EAAA;;AAE7E,QAAA,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;AACxB,YAAA,OAAO,KAAK,CAAC;AACd,SAAA;;QAGD,MAAM,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,EAAE,YAAY,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE;AACvC,YAAA,OAAO,KAAK,CAAC;AACd,SAAA;;QAGD,IAAI,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;AAC7E,QAAA,OAAO,IAAI,CAAC;KACb;IAEO,WAAW,CAAC,iBAAoC,EAAE,OAAe,EAAA;AACvE,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,IAAc,EAAE,OAAO,CAAC,CAAC,CAAC;KACnF;AACF,CAAA;AAED,SAAS,aAAa,CAAC,YAA0B,EAAA;AAC/C,IAAA,QACE,YAAY,KAAK,YAAY,CAAC,OAAO;QACrC,YAAY,KAAK,YAAY,CAAC,WAAW;AACzC,QAAA,YAAY,KAAK,YAAY,CAAC,WAAW,EACzC;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,GAAW,EACX,UAAsB,EACtB,mBAAsD,EAAA;IAEtD,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE;AAC3D,QAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;YACjC,SAAS;AACV,SAAA;QACD,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACzD,QAAA,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE;YACrC,SAAS;AACV,SAAA;QACD,IAAI,kBAAkB,GAAG,qBAAqB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACzE,IAAI,CAAC,kBAAkB,EAAE;YACvB,SAAS;AACV,SAAA;AACD,QAAA,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE;;;AAGrC,YAAA,kBAAkB,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;AAC5C,SAAA;AACD,QAAA,IAAI,kBAAkB,IAAI,GAAG,KAAK,gBAAgB,GAAG,UAAU,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE;AACxF,YAAA,OAAO,IAAI,CAAC;AACb,SAAA;AACF,KAAA;AACD,IAAA,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;AAOG;SACa,YAAY,CAAC,KAAc,EAAE,IAAY,EAAE,MAA+B,EAAA;IACxF,IAAI,KAAK,KAAK,IAAI,EAAE;QAClB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAC/D,KAAA;AAAM,SAAA,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AAC/B,QAAA,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACxC,KAAA;AAAM,SAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AACpC,QAAA,kBAAkB,CAAC,KAAgC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACpE,KAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAgB,EAAE,IAAY,EAAE,MAA+B,EAAA;AACxF,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,QAAA,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE;AAC1B,YAAA,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAG,EAAA,IAAI,CAAI,CAAA,EAAA,CAAC,CAAG,CAAA,CAAA,EAAE,yBAAyB,CAAC,CAAC,CAAC;AAC/E,SAAA;AAAM,aAAA;AACL,YAAA,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,CAAG,EAAE,MAAM,CAAC,CAAC;AACjD,SAAA;AACF,KAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,GAA4B,EAAE,IAAY,EAAE,MAA+B,EAAA;AACrG,IAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC9C,YAAY,CAAC,KAAK,EAAE,CAAA,EAAG,IAAI,CAAG,EAAA,IAAI,GAAG,GAAG,GAAG,EAAE,CAAA,EAAG,GAAG,CAAE,CAAA,EAAE,MAAM,CAAC,CAAC;AAChE,KAAA;AACH,CAAC;AAEe,SAAA,oBAAoB,CAAC,UAAkB,EAAE,OAAe,EAAA;IACtE,OAAO;AACL,QAAA,QAAQ,EAAE,OAAO;AACjB,QAAA,IAAI,EAAE,WAAW;AACjB,QAAA,OAAO,EAAE;AACP,YAAA,IAAI,EAAE,OAAO;AACd,SAAA;QACD,UAAU,EAAE,CAAC,UAAU,CAAC;KACzB,CAAC;AACJ;;;;"}
@@ -1,162 +0,0 @@
1
- import { globalSchema, getElementDefinition, buildTypeName, PropertyType } from '../types.mjs';
2
- import { capitalize } from '../utils.mjs';
3
-
4
- var SearchParameterType;
5
- (function (SearchParameterType) {
6
- SearchParameterType["BOOLEAN"] = "BOOLEAN";
7
- SearchParameterType["NUMBER"] = "NUMBER";
8
- SearchParameterType["QUANTITY"] = "QUANTITY";
9
- SearchParameterType["TEXT"] = "TEXT";
10
- SearchParameterType["REFERENCE"] = "REFERENCE";
11
- SearchParameterType["CANONICAL"] = "CANONICAL";
12
- SearchParameterType["DATE"] = "DATE";
13
- SearchParameterType["DATETIME"] = "DATETIME";
14
- SearchParameterType["PERIOD"] = "PERIOD";
15
- SearchParameterType["UUID"] = "UUID";
16
- })(SearchParameterType || (SearchParameterType = {}));
17
- /**
18
- * Returns the type details of a SearchParameter.
19
- *
20
- * The SearchParameter resource has a "type" parameter, but that is missing some critical information.
21
- *
22
- * For example:
23
- * 1) The "date" type includes "date", "datetime", and "period".
24
- * 2) The "token" type includes enums and booleans.
25
- * 3) Arrays/multiple values are not reflected at all.
26
- * @param resourceType The root resource type.
27
- * @param searchParam The search parameter.
28
- * @returns The search parameter type details.
29
- */
30
- function getSearchParameterDetails(resourceType, searchParam) {
31
- let result = globalSchema.types[resourceType].searchParamsDetails?.[searchParam.code];
32
- if (!result) {
33
- result = buildSearchParameterDetails(resourceType, searchParam);
34
- }
35
- return result;
36
- }
37
- function setSearchParameterDetails(resourceType, code, details) {
38
- const typeSchema = globalSchema.types[resourceType];
39
- if (!typeSchema.searchParamsDetails) {
40
- typeSchema.searchParamsDetails = {};
41
- }
42
- typeSchema.searchParamsDetails[code] = details;
43
- }
44
- function buildSearchParameterDetails(resourceType, searchParam) {
45
- const code = searchParam.code;
46
- const columnName = convertCodeToColumnName(code);
47
- const expression = getExpressionForResourceType(resourceType, searchParam.expression)?.split('.');
48
- if (!expression) {
49
- // This happens on compound types
50
- // In the future, explore returning multiple column definitions
51
- return { columnName, type: SearchParameterType.TEXT };
52
- }
53
- let baseType = resourceType;
54
- let elementDefinition = undefined;
55
- let propertyType = undefined;
56
- let array = false;
57
- for (let i = 1; i < expression.length; i++) {
58
- let propertyName = expression[i];
59
- let hasArrayIndex = false;
60
- const arrayIndexMatch = /\[\d+\]$/.exec(propertyName);
61
- if (arrayIndexMatch) {
62
- propertyName = propertyName.substring(0, propertyName.length - arrayIndexMatch[0].length);
63
- hasArrayIndex = true;
64
- }
65
- elementDefinition = getElementDefinition(baseType, propertyName);
66
- if (!elementDefinition) {
67
- throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);
68
- }
69
- if (elementDefinition.max !== '0' && elementDefinition.max !== '1' && !hasArrayIndex) {
70
- array = true;
71
- }
72
- // "code" is only missing when using "contentReference"
73
- // "contentReference" is handled above in "getElementDefinition"
74
- propertyType = elementDefinition.type?.[0].code;
75
- if (i < expression.length - 1) {
76
- if (isBackboneElement(propertyType)) {
77
- baseType = buildTypeName(elementDefinition.path?.split('.'));
78
- }
79
- else {
80
- baseType = propertyType;
81
- }
82
- }
83
- }
84
- const type = getSearchParameterType(searchParam, propertyType);
85
- const result = { columnName, type, elementDefinition, array };
86
- setSearchParameterDetails(resourceType, code, result);
87
- return result;
88
- }
89
- function isBackboneElement(propertyType) {
90
- return propertyType === 'Element' || propertyType === 'BackboneElement';
91
- }
92
- /**
93
- * Converts a hyphen-delimited code to camelCase string.
94
- * @param code The search parameter code.
95
- * @returns The SQL column name.
96
- */
97
- function convertCodeToColumnName(code) {
98
- return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');
99
- }
100
- function getSearchParameterType(searchParam, propertyType) {
101
- switch (searchParam.type) {
102
- case 'date':
103
- if (propertyType === PropertyType.date) {
104
- return SearchParameterType.DATE;
105
- }
106
- else {
107
- return SearchParameterType.DATETIME;
108
- }
109
- case 'number':
110
- return SearchParameterType.NUMBER;
111
- case 'quantity':
112
- return SearchParameterType.QUANTITY;
113
- case 'reference':
114
- if (propertyType === PropertyType.canonical) {
115
- return SearchParameterType.CANONICAL;
116
- }
117
- else {
118
- return SearchParameterType.REFERENCE;
119
- }
120
- case 'token':
121
- if (propertyType === PropertyType.boolean) {
122
- return SearchParameterType.BOOLEAN;
123
- }
124
- else {
125
- return SearchParameterType.TEXT;
126
- }
127
- default:
128
- return SearchParameterType.TEXT;
129
- }
130
- }
131
- function getExpressionForResourceType(resourceType, expression) {
132
- const expressions = expression.split(' | ');
133
- for (const e of expressions) {
134
- if (isIgnoredExpression(e)) {
135
- continue;
136
- }
137
- const simplified = simplifyExpression(e);
138
- if (simplified.startsWith(resourceType + '.')) {
139
- return simplified;
140
- }
141
- }
142
- return undefined;
143
- }
144
- function isIgnoredExpression(input) {
145
- return input.includes(' as Period') || input.includes(' as SampledDate');
146
- }
147
- function simplifyExpression(input) {
148
- let result = input.trim();
149
- if (result.startsWith('(') && result.endsWith(')')) {
150
- result = result.substring(1, result.length - 1);
151
- }
152
- const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.resolve(', '.where('];
153
- for (const stopString of stopStrings) {
154
- if (result.includes(stopString)) {
155
- result = result.substring(0, result.indexOf(stopString));
156
- }
157
- }
158
- return result;
159
- }
160
-
161
- export { SearchParameterType, getExpressionForResourceType, getSearchParameterDetails };
162
- //# sourceMappingURL=details.mjs.map
@@ -1 +0,0 @@
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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"}