@medplum/core 2.0.3 → 2.0.4

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 (36) hide show
  1. package/dist/cjs/client.d.ts +5 -3
  2. package/dist/cjs/index.cjs +443 -189
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs/index.d.ts +4 -3
  5. package/dist/cjs/index.min.cjs +1 -1
  6. package/dist/cjs/{searchparams.d.ts → search/details.d.ts} +0 -0
  7. package/dist/cjs/{match.d.ts → search/match.d.ts} +0 -0
  8. package/dist/cjs/search/parse.d.ts +17 -0
  9. package/dist/cjs/{search.d.ts → search/search.d.ts} +0 -0
  10. package/dist/cjs/types.d.ts +30 -7
  11. package/dist/esm/client.d.ts +5 -3
  12. package/dist/esm/client.mjs +9 -5
  13. package/dist/esm/client.mjs.map +1 -1
  14. package/dist/esm/index.d.ts +4 -3
  15. package/dist/esm/index.min.mjs +1 -1
  16. package/dist/esm/index.mjs +5 -4
  17. package/dist/esm/index.mjs.map +1 -1
  18. package/dist/esm/{searchparams.d.ts → search/details.d.ts} +0 -0
  19. package/dist/esm/{searchparams.mjs → search/details.mjs} +9 -11
  20. package/dist/esm/search/details.mjs.map +1 -0
  21. package/dist/esm/{match.d.ts → search/match.d.ts} +0 -0
  22. package/dist/esm/{match.mjs → search/match.mjs} +7 -7
  23. package/dist/esm/search/match.mjs.map +1 -0
  24. package/dist/esm/search/parse.d.ts +17 -0
  25. package/dist/esm/search/parse.mjs +218 -0
  26. package/dist/esm/search/parse.mjs.map +1 -0
  27. package/dist/esm/{search.d.ts → search/search.d.ts} +0 -0
  28. package/dist/esm/{search.mjs → search/search.mjs} +0 -3
  29. package/dist/esm/search/search.mjs.map +1 -0
  30. package/dist/esm/types.d.ts +30 -7
  31. package/dist/esm/types.mjs +63 -25
  32. package/dist/esm/types.mjs.map +1 -1
  33. package/package.json +1 -1
  34. package/dist/esm/match.mjs.map +0 -1
  35. package/dist/esm/search.mjs.map +0 -1
  36. package/dist/esm/searchparams.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
@@ -1,5 +1,5 @@
1
- import { globalSchema, PropertyType } from './types.mjs';
2
- import { capitalize } from './utils.mjs';
1
+ import { globalSchema, getElementDefinition, buildTypeName, PropertyType } from '../types.mjs';
2
+ import { capitalize } from '../utils.mjs';
3
3
 
4
4
  var SearchParameterType;
5
5
  (function (SearchParameterType) {
@@ -41,9 +41,6 @@ function setSearchParamterDetails(resourceType, code, details) {
41
41
  typeSchema.searchParamsDetails[code] = details;
42
42
  }
43
43
  function buildSearchParamterDetails(resourceType, searchParam) {
44
- if (searchParam.code === '_lastUpdated') {
45
- return { columnName: 'lastUpdated', type: SearchParameterType.DATETIME };
46
- }
47
44
  const code = searchParam.code;
48
45
  const columnName = convertCodeToColumnName(code);
49
46
  const expression = getExpressionForResourceType(resourceType, searchParam.expression)?.split('.');
@@ -59,9 +56,7 @@ function buildSearchParamterDetails(resourceType, searchParam) {
59
56
  let array = false;
60
57
  for (let i = 1; i < expression.length; i++) {
61
58
  const propertyName = expression[i];
62
- elementDefinition =
63
- globalSchema.types[baseType]?.properties?.[propertyName] ??
64
- globalSchema.types[baseType]?.properties?.[propertyName + '[x]'];
59
+ elementDefinition = getElementDefinition(baseType, propertyName);
65
60
  if (!elementDefinition) {
66
61
  throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);
67
62
  }
@@ -75,8 +70,8 @@ function buildSearchParamterDetails(resourceType, searchParam) {
75
70
  return { columnName, type: defaultType, array };
76
71
  }
77
72
  if (i < expression.length - 1) {
78
- if (propertyType === 'Element' || propertyType === 'BackboneElement') {
79
- baseType = baseType + capitalize(propertyName);
73
+ if (isBackboneElement(propertyType)) {
74
+ baseType = buildTypeName(elementDefinition.path?.split('.'));
80
75
  }
81
76
  else {
82
77
  baseType = propertyType;
@@ -88,6 +83,9 @@ function buildSearchParamterDetails(resourceType, searchParam) {
88
83
  setSearchParamterDetails(resourceType, code, result);
89
84
  return result;
90
85
  }
86
+ function isBackboneElement(propertyType) {
87
+ return propertyType === 'Element' || propertyType === 'BackboneElement';
88
+ }
91
89
  /**
92
90
  * Converts a hyphen-delimited code to camelCase string.
93
91
  * @param code The search parameter code.
@@ -158,4 +156,4 @@ function simplifyExpression(input) {
158
156
  }
159
157
 
160
158
  export { SearchParameterType, getExpressionForResourceType, getSearchParameterDetails };
161
- //# sourceMappingURL=searchparams.mjs.map
159
+ //# sourceMappingURL=details.mjs.map
@@ -0,0 +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 DATE = 'DATE',\n DATETIME = 'DATETIME',\n PERIOD = 'PERIOD',\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 *\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 = buildSearchParamterDetails(resourceType, searchParam);\n }\n return result;\n}\n\nfunction setSearchParamterDetails(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 buildSearchParamterDetails(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 const defaultType = getSearchParameterType(searchParam);\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 const propertyName = expression[i];\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 === '*') {\n array = true;\n }\n\n propertyType = elementDefinition.type?.[0].code;\n if (!propertyType) {\n // This happens when one of parent properties uses contentReference\n // In the future, explore following the reference\n return { columnName, type: defaultType, array };\n }\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 setSearchParamterDetails(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.dateTime || propertyType === PropertyType.instant) {\n type = SearchParameterType.DATETIME;\n } else {\n type = SearchParameterType.DATE;\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 type = SearchParameterType.REFERENCE;\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 if (result.includes('[0]')) {\n result = result.replaceAll('[0]', '');\n }\n\n const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.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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"}
File without changes
@@ -1,11 +1,11 @@
1
- import './fhirpath/atoms.mjs';
2
- import { evalFhirPath } from './fhirpath/parse.mjs';
3
- import './fhirlexer/parse.mjs';
4
- import './fhirlexer/tokenize.mjs';
5
- import { globalSchema } from './types.mjs';
6
- import './utils.mjs';
1
+ import '../fhirpath/atoms.mjs';
2
+ import { evalFhirPath } from '../fhirpath/parse.mjs';
3
+ import '../fhirlexer/parse.mjs';
4
+ import '../fhirlexer/tokenize.mjs';
5
+ import { globalSchema } from '../types.mjs';
6
+ import '../utils.mjs';
7
+ import { getSearchParameterDetails, SearchParameterType } from './details.mjs';
7
8
  import { Operator } from './search.mjs';
8
- import { getSearchParameterDetails, SearchParameterType } from './searchparams.mjs';
9
9
 
10
10
  /**
11
11
  * Determines if the resource matches the search request.
@@ -0,0 +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 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 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);\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(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 = matchesStringValue(resourceValue, 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 matchesStringValue(resourceValue: unknown, operator: Operator, filterValue: string): boolean {\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":";;;;;;;;;AAMA;;;;;AAKG;AACa,SAAA,oBAAoB,CAAC,QAAkB,EAAE,aAA4B,EAAA;AACnF,IAAA,IAAI,aAAa,CAAC,YAAY,KAAK,QAAQ,CAAC,YAAY,EAAE;AACxD,QAAA,OAAO,KAAK,CAAC;AACd,KAAA;IACD,IAAI,aAAa,CAAC,OAAO,EAAE;AACzB,QAAA,KAAK,MAAM,MAAM,IAAI,aAAa,CAAC,OAAO,EAAE;YAC1C,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE;AACzD,gBAAA,OAAO,KAAK,CAAC;AACd,aAAA;AACF,SAAA;AACF,KAAA;AACD,IAAA,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;AAKG;AACH,SAAS,mBAAmB,CAAC,QAAkB,EAAE,aAA4B,EAAE,MAAc,EAAA;AAC3F,IAAA,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAChG,QAAQ,WAAW,EAAE,IAAI;AACvB,QAAA,KAAK,WAAW;YACd,OAAO,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC/D,QAAA,KAAK,QAAQ;YACX,OAAO,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC5D,QAAA,KAAK,OAAO;YACV,OAAO,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC3D,QAAA,KAAK,MAAM;YACT,OAAO,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC3D,KAAA;;;AAGD,IAAA,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAkB,EAAE,MAAc,EAAE,WAA4B,EAAA;IAC9F,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,CAAC,UAAoB,EAAE,QAAQ,CAA2B,CAAC;IAClG,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,MAAM,CAAC,KAAK,KAAK,EAAE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;;;AAG9C,QAAA,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,MAAM,CAAC;AAC5C,KAAA;;AAGD,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhG,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;QACjD,IAAI,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC7C,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,EAAE;;;;;AAK5C,YAAA,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,EAAE,QAAQ,CAAC,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC;AAChF,SAAA;AACD,QAAA,IAAI,KAAK,EAAE;YACT,OAAO,CAAC,OAAO,CAAC;AACjB,SAAA;AACF,KAAA;;;AAGD,IAAA,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAkB,EAAE,MAAc,EAAE,WAA4B,EAAA;IAC1F,MAAM,OAAO,GAAG,yBAAyB,CAAC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AAC9E,IAAA,IAAI,OAAO,CAAC,IAAI,KAAK,mBAAmB,CAAC,OAAO,EAAE;QAChD,OAAO,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC5D,KAAA;AAAM,SAAA;QACL,OAAO,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC3D,KAAA;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAkB,EAAE,MAAc,EAAE,WAA4B,EAAA;IAC5F,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,CAAC,UAAoB,EAAE,QAAQ,CAAC,CAAC;AACxE,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACzC,IAAA,OAAO,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;AACvD,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAkB,EAAE,MAAc,EAAE,WAA4B,EAAA;IAC3F,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,CAAC,UAAoB,EAAE,QAAQ,CAAC,CAAC;IAChF,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC3C,IAAA,KAAK,MAAM,aAAa,IAAI,cAAc,EAAE;AAC1C,QAAA,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE;AACtC,YAAA,MAAM,KAAK,GAAG,kBAAkB,CAAC,aAAa,EAAE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AAC9E,YAAA,IAAI,KAAK,EAAE;gBACT,OAAO,CAAC,OAAO,CAAC;AACjB,aAAA;AACF,SAAA;AACF,KAAA;;;AAGD,IAAA,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,kBAAkB,CAAC,aAAsB,EAAE,QAAkB,EAAE,WAAmB,EAAA;IACzF,IAAI,GAAG,GAAG,EAAE,CAAC;AACb,IAAA,IAAI,aAAa,EAAE;AACjB,QAAA,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE;YACrC,GAAG,GAAG,aAAa,CAAC;AACrB,SAAA;AAAM,aAAA,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE;AAC5C,YAAA,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;AACrC,SAAA;AACF,KAAA;AACD,IAAA,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAkB,EAAE,MAAc,EAAE,WAA4B,EAAA;IACzF,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,CAAC,UAAoB,EAAE,QAAQ,CAAC,CAAC;IAChF,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC3C,IAAA,KAAK,MAAM,aAAa,IAAI,cAAc,EAAE;AAC1C,QAAA,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE;AACtC,YAAA,MAAM,KAAK,GAAG,gBAAgB,CAAC,aAAuB,EAAE,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AACtF,YAAA,IAAI,KAAK,EAAE;gBACT,OAAO,CAAC,OAAO,CAAC;AACjB,aAAA;AACF,SAAA;AACF,KAAA;;;AAGD,IAAA,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,gBAAgB,CAAC,aAAqB,EAAE,QAAkB,EAAE,WAAmB,EAAA;AACtF,IAAA,QAAQ,QAAQ;QACd,KAAK,QAAQ,CAAC,YAAY,CAAC;QAC3B,KAAK,QAAQ,CAAC,YAAY;YACxB,OAAO,aAAa,GAAG,WAAW,CAAC;QACrC,KAAK,QAAQ,CAAC,sBAAsB;YAClC,OAAO,aAAa,IAAI,WAAW,CAAC;QACtC,KAAK,QAAQ,CAAC,WAAW,CAAC;QAC1B,KAAK,QAAQ,CAAC,SAAS;YACrB,OAAO,aAAa,GAAG,WAAW,CAAC;QACrC,KAAK,QAAQ,CAAC,mBAAmB;YAC/B,OAAO,aAAa,IAAI,WAAW,CAAC;QACtC,KAAK,QAAQ,CAAC,MAAM,CAAC;QACrB,KAAK,QAAQ,CAAC,UAAU;YACtB,OAAO,aAAa,KAAK,WAAW,CAAC;AACxC,KAAA;AACD,IAAA,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,QAAkB,EAAA;IACnC,OAAO,QAAQ,KAAK,QAAQ,CAAC,UAAU,IAAI,QAAQ,KAAK,QAAQ,CAAC,GAAG,CAAC;AACvE;;;;"}
@@ -0,0 +1,17 @@
1
+ /// <reference types="node" />
2
+ import { ResourceType } from '@medplum/fhirtypes';
3
+ import { URL } from 'url';
4
+ import { SearchRequest } from './search';
5
+ /**
6
+ * Parses a search URL into a search request.
7
+ * @param resourceType The FHIR resource type.
8
+ * @param query The collection of query string parameters.
9
+ * @returns A parsed SearchRequest.
10
+ */
11
+ export declare function parseSearchRequest(resourceType: ResourceType, query: Record<string, string[] | string | undefined>): SearchRequest;
12
+ /**
13
+ * Parses a search URL into a search request.
14
+ * @param url The search URL.
15
+ * @returns A parsed SearchRequest.
16
+ */
17
+ export declare function parseSearchUrl(url: URL): SearchRequest;
@@ -0,0 +1,218 @@
1
+ import { __classPrivateFieldGet } from '../node_modules/tslib/tslib.es6.mjs';
2
+ import { globalSchema } from '../types.mjs';
3
+ import { Operator } from './search.mjs';
4
+
5
+ var _SearchParser_instances, _SearchParser_parseKeyValue, _SearchParser_parseSortRule, _SearchParser_parseParameter, _SearchParser_parsePrefixType, _SearchParser_parseModifierType, _SearchParser_parseReference, _SearchParser_parseQuantity, _SearchParser_parseUnknownParameter;
6
+ /**
7
+ * Parses a FHIR search query.
8
+ * See: https://www.hl7.org/fhir/search.html
9
+ */
10
+ /**
11
+ * For the ordered parameter types of number, date, and quantity,
12
+ * a prefix to the parameter value may be used to control the nature
13
+ * of the matching.
14
+ * See: https://www.hl7.org/fhir/search.html#prefix
15
+ */
16
+ const prefixMap = {
17
+ eq: Operator.EQUALS,
18
+ ne: Operator.NOT_EQUALS,
19
+ lt: Operator.LESS_THAN,
20
+ le: Operator.LESS_THAN_OR_EQUALS,
21
+ gt: Operator.GREATER_THAN,
22
+ ge: Operator.GREATER_THAN_OR_EQUALS,
23
+ sa: Operator.STARTS_AFTER,
24
+ eb: Operator.ENDS_BEFORE,
25
+ ap: Operator.APPROXIMATELY,
26
+ };
27
+ /**
28
+ * Parameter names may specify a modifier as a suffix.
29
+ * The modifiers are separated from the parameter name by a colon.
30
+ * See: https://www.hl7.org/fhir/search.html#modifiers
31
+ */
32
+ const modifierMap = {
33
+ contains: Operator.CONTAINS,
34
+ exact: Operator.EXACT,
35
+ above: Operator.ABOVE,
36
+ below: Operator.BELOW,
37
+ text: Operator.TEXT,
38
+ not: Operator.NOT,
39
+ in: Operator.IN,
40
+ 'not-in': Operator.NOT_IN,
41
+ 'of-type': Operator.OF_TYPE,
42
+ };
43
+ /**
44
+ * Parses a search URL into a search request.
45
+ * @param resourceType The FHIR resource type.
46
+ * @param query The collection of query string parameters.
47
+ * @returns A parsed SearchRequest.
48
+ */
49
+ function parseSearchRequest(resourceType, query) {
50
+ return new SearchParser(resourceType, query);
51
+ }
52
+ /**
53
+ * Parses a search URL into a search request.
54
+ * @param url The search URL.
55
+ * @returns A parsed SearchRequest.
56
+ */
57
+ function parseSearchUrl(url) {
58
+ const resourceType = url.pathname.split('/').pop();
59
+ return new SearchParser(resourceType, Object.fromEntries(url.searchParams.entries()));
60
+ }
61
+ class SearchParser {
62
+ constructor(resourceType, query) {
63
+ _SearchParser_instances.add(this);
64
+ this.resourceType = resourceType;
65
+ this.filters = [];
66
+ this.sortRules = [];
67
+ for (const [key, value] of Object.entries(query)) {
68
+ if (Array.isArray(value)) {
69
+ value.forEach((element) => __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseKeyValue).call(this, key, element));
70
+ }
71
+ else {
72
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseKeyValue).call(this, key, value ?? '');
73
+ }
74
+ }
75
+ }
76
+ }
77
+ _SearchParser_instances = new WeakSet(), _SearchParser_parseKeyValue = function _SearchParser_parseKeyValue(key, value) {
78
+ let code;
79
+ let modifier;
80
+ const colonIndex = key.indexOf(':');
81
+ if (colonIndex >= 0) {
82
+ code = key.substring(0, colonIndex);
83
+ modifier = key.substring(colonIndex + 1);
84
+ }
85
+ else {
86
+ code = key;
87
+ modifier = '';
88
+ }
89
+ switch (code) {
90
+ case '_sort':
91
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseSortRule).call(this, value);
92
+ break;
93
+ case '_count':
94
+ this.count = parseInt(value);
95
+ break;
96
+ case '_offset':
97
+ this.offset = parseInt(value);
98
+ break;
99
+ case '_total':
100
+ this.total = value;
101
+ break;
102
+ case '_summary':
103
+ this.total = 'estimate';
104
+ this.count = 0;
105
+ break;
106
+ case '_revinclude':
107
+ this.revInclude = value;
108
+ break;
109
+ default: {
110
+ const param = globalSchema.types[this.resourceType]?.searchParams?.[code];
111
+ if (param) {
112
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseParameter).call(this, param, modifier, value);
113
+ }
114
+ else {
115
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseUnknownParameter).call(this, code, modifier, value);
116
+ }
117
+ }
118
+ }
119
+ }, _SearchParser_parseSortRule = function _SearchParser_parseSortRule(value) {
120
+ for (const field of value.split(',')) {
121
+ let code;
122
+ let descending = false;
123
+ if (field.startsWith('-')) {
124
+ code = field.substring(1);
125
+ descending = true;
126
+ }
127
+ else {
128
+ code = field;
129
+ }
130
+ this.sortRules.push({ code, descending });
131
+ }
132
+ }, _SearchParser_parseParameter = function _SearchParser_parseParameter(searchParam, modifier, value) {
133
+ if (modifier === 'missing') {
134
+ this.filters.push({
135
+ code: searchParam.code,
136
+ operator: Operator.MISSING,
137
+ value,
138
+ });
139
+ return;
140
+ }
141
+ switch (searchParam.type) {
142
+ case 'number':
143
+ case 'date':
144
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parsePrefixType).call(this, searchParam, value);
145
+ break;
146
+ case 'string':
147
+ case 'token':
148
+ case 'uri':
149
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseModifierType).call(this, searchParam, modifier, value);
150
+ break;
151
+ case 'reference':
152
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseReference).call(this, searchParam, value);
153
+ break;
154
+ case 'quantity':
155
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseQuantity).call(this, searchParam, value);
156
+ break;
157
+ }
158
+ }, _SearchParser_parsePrefixType = function _SearchParser_parsePrefixType(param, input) {
159
+ const { operator, value } = parsePrefix(input);
160
+ this.filters.push({
161
+ code: param.code,
162
+ operator,
163
+ value,
164
+ });
165
+ }, _SearchParser_parseModifierType = function _SearchParser_parseModifierType(param, modifier, value) {
166
+ this.filters.push({
167
+ code: param.code,
168
+ operator: parseModifier(modifier),
169
+ value,
170
+ });
171
+ }, _SearchParser_parseReference = function _SearchParser_parseReference(param, value) {
172
+ this.filters.push({
173
+ code: param.code,
174
+ operator: Operator.EQUALS,
175
+ value: value,
176
+ });
177
+ }, _SearchParser_parseQuantity = function _SearchParser_parseQuantity(param, input) {
178
+ const [prefixNumber, unitSystem, unitCode] = input.split('|');
179
+ const { operator, value } = parsePrefix(prefixNumber);
180
+ this.filters.push({
181
+ code: param.code,
182
+ operator,
183
+ value,
184
+ unitSystem,
185
+ unitCode,
186
+ });
187
+ }, _SearchParser_parseUnknownParameter = function _SearchParser_parseUnknownParameter(code, modifier, value) {
188
+ let operator = Operator.EQUALS;
189
+ if (modifier) {
190
+ operator = modifier;
191
+ }
192
+ else {
193
+ for (const prefix of Object.keys(prefixMap)) {
194
+ if (value.match(new RegExp('^' + prefix + '\\d'))) {
195
+ operator = prefix;
196
+ value = value.substring(prefix.length);
197
+ }
198
+ }
199
+ }
200
+ this.filters.push({
201
+ code,
202
+ operator,
203
+ value,
204
+ });
205
+ };
206
+ function parsePrefix(input) {
207
+ const prefix = input.substring(0, 2);
208
+ if (prefix in prefixMap) {
209
+ return { operator: prefixMap[prefix], value: input.substring(2) };
210
+ }
211
+ return { operator: Operator.EQUALS, value: input };
212
+ }
213
+ function parseModifier(modifier) {
214
+ return modifierMap[modifier] || Operator.EQUALS;
215
+ }
216
+
217
+ export { parseSearchRequest, parseSearchUrl };
218
+ //# sourceMappingURL=parse.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.mjs","sources":["../../../src/search/parse.ts"],"sourcesContent":["import { ResourceType, SearchParameter } from '@medplum/fhirtypes';\nimport { URL } from 'url';\nimport { globalSchema } from '../types';\nimport { Filter, Operator, SearchRequest, SortRule } from './search';\n\n/**\n * Parses a FHIR search query.\n * See: https://www.hl7.org/fhir/search.html\n */\n\n/**\n * For the ordered parameter types of number, date, and quantity,\n * a prefix to the parameter value may be used to control the nature\n * of the matching.\n * See: https://www.hl7.org/fhir/search.html#prefix\n */\nconst prefixMap: Record<string, Operator> = {\n eq: Operator.EQUALS,\n ne: Operator.NOT_EQUALS,\n lt: Operator.LESS_THAN,\n le: Operator.LESS_THAN_OR_EQUALS,\n gt: Operator.GREATER_THAN,\n ge: Operator.GREATER_THAN_OR_EQUALS,\n sa: Operator.STARTS_AFTER,\n eb: Operator.ENDS_BEFORE,\n ap: Operator.APPROXIMATELY,\n};\n\n/**\n * Parameter names may specify a modifier as a suffix.\n * The modifiers are separated from the parameter name by a colon.\n * See: https://www.hl7.org/fhir/search.html#modifiers\n */\nconst modifierMap: Record<string, Operator> = {\n contains: Operator.CONTAINS,\n exact: Operator.EXACT,\n above: Operator.ABOVE,\n below: Operator.BELOW,\n text: Operator.TEXT,\n not: Operator.NOT,\n in: Operator.IN,\n 'not-in': Operator.NOT_IN,\n 'of-type': Operator.OF_TYPE,\n};\n\n/**\n * Parses a search URL into a search request.\n * @param resourceType The FHIR resource type.\n * @param query The collection of query string parameters.\n * @returns A parsed SearchRequest.\n */\nexport function parseSearchRequest(\n resourceType: ResourceType,\n query: Record<string, string[] | string | undefined>\n): SearchRequest {\n return new SearchParser(resourceType, query);\n}\n\n/**\n * Parses a search URL into a search request.\n * @param url The search URL.\n * @returns A parsed SearchRequest.\n */\nexport function parseSearchUrl(url: URL): SearchRequest {\n const resourceType = url.pathname.split('/').pop() as ResourceType;\n return new SearchParser(resourceType, Object.fromEntries(url.searchParams.entries()));\n}\n\nclass SearchParser implements SearchRequest {\n readonly resourceType: ResourceType;\n readonly filters: Filter[];\n readonly sortRules: SortRule[];\n count?: number;\n offset?: number;\n total?: 'none' | 'estimate' | 'accurate';\n revInclude?: string;\n\n constructor(resourceType: ResourceType, query: Record<string, string[] | string | undefined>) {\n this.resourceType = resourceType;\n this.filters = [];\n this.sortRules = [];\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n value.forEach((element) => this.#parseKeyValue(key, element));\n } else {\n this.#parseKeyValue(key, value ?? '');\n }\n }\n }\n\n #parseKeyValue(key: string, value: string): void {\n let code;\n let modifier;\n\n const colonIndex = key.indexOf(':');\n if (colonIndex >= 0) {\n code = key.substring(0, colonIndex);\n modifier = key.substring(colonIndex + 1);\n } else {\n code = key;\n modifier = '';\n }\n\n switch (code) {\n case '_sort':\n this.#parseSortRule(value);\n break;\n\n case '_count':\n this.count = parseInt(value);\n break;\n\n case '_offset':\n this.offset = parseInt(value);\n break;\n\n case '_total':\n this.total = value as 'none' | 'estimate' | 'accurate';\n break;\n\n case '_summary':\n this.total = 'estimate';\n this.count = 0;\n break;\n\n case '_revinclude':\n this.revInclude = value;\n break;\n\n default: {\n const param = globalSchema.types[this.resourceType]?.searchParams?.[code];\n if (param) {\n this.#parseParameter(param, modifier, value);\n } else {\n this.#parseUnknownParameter(code, modifier, value);\n }\n }\n }\n }\n\n #parseSortRule(value: string): void {\n for (const field of value.split(',')) {\n let code;\n let descending = false;\n if (field.startsWith('-')) {\n code = field.substring(1);\n descending = true;\n } else {\n code = field;\n }\n this.sortRules.push({ code, descending });\n }\n }\n\n #parseParameter(searchParam: SearchParameter, modifier: string, value: string): void {\n if (modifier === 'missing') {\n this.filters.push({\n code: searchParam.code as string,\n operator: Operator.MISSING,\n value,\n });\n return;\n }\n switch (searchParam.type) {\n case 'number':\n case 'date':\n this.#parsePrefixType(searchParam, value);\n break;\n case 'string':\n case 'token':\n case 'uri':\n this.#parseModifierType(searchParam, modifier, value);\n break;\n case 'reference':\n this.#parseReference(searchParam, value);\n break;\n case 'quantity':\n this.#parseQuantity(searchParam, value);\n break;\n }\n }\n\n #parsePrefixType(param: SearchParameter, input: string): void {\n const { operator, value } = parsePrefix(input);\n this.filters.push({\n code: param.code as string,\n operator,\n value,\n });\n }\n\n #parseModifierType(param: SearchParameter, modifier: string, value: string): void {\n this.filters.push({\n code: param.code as string,\n operator: parseModifier(modifier),\n value,\n });\n }\n\n #parseReference(param: SearchParameter, value: string): void {\n this.filters.push({\n code: param.code as string,\n operator: Operator.EQUALS,\n value: value,\n });\n }\n\n #parseQuantity(param: SearchParameter, input: string): void {\n const [prefixNumber, unitSystem, unitCode] = input.split('|');\n const { operator, value } = parsePrefix(prefixNumber);\n this.filters.push({\n code: param.code as string,\n operator,\n value,\n unitSystem,\n unitCode,\n });\n }\n\n #parseUnknownParameter(code: string, modifier: string, value: string): void {\n let operator = Operator.EQUALS;\n if (modifier) {\n operator = modifier as Operator;\n } else {\n for (const prefix of Object.keys(prefixMap)) {\n if (value.match(new RegExp('^' + prefix + '\\\\d'))) {\n operator = prefix as Operator;\n value = value.substring(prefix.length);\n }\n }\n }\n\n this.filters.push({\n code,\n operator,\n value,\n });\n }\n}\n\nfunction parsePrefix(input: string): { operator: Operator; value: string } {\n const prefix = input.substring(0, 2);\n if (prefix in prefixMap) {\n return { operator: prefixMap[prefix], value: input.substring(2) };\n }\n return { operator: Operator.EQUALS, value: input };\n}\n\nfunction parseModifier(modifier: string): Operator {\n return modifierMap[modifier] || Operator.EQUALS;\n}\n"],"names":[],"mappings":";;;;;AAKA;;;AAGG;AAEH;;;;;AAKG;AACH,MAAM,SAAS,GAA6B;IAC1C,EAAE,EAAE,QAAQ,CAAC,MAAM;IACnB,EAAE,EAAE,QAAQ,CAAC,UAAU;IACvB,EAAE,EAAE,QAAQ,CAAC,SAAS;IACtB,EAAE,EAAE,QAAQ,CAAC,mBAAmB;IAChC,EAAE,EAAE,QAAQ,CAAC,YAAY;IACzB,EAAE,EAAE,QAAQ,CAAC,sBAAsB;IACnC,EAAE,EAAE,QAAQ,CAAC,YAAY;IACzB,EAAE,EAAE,QAAQ,CAAC,WAAW;IACxB,EAAE,EAAE,QAAQ,CAAC,aAAa;CAC3B,CAAC;AAEF;;;;AAIG;AACH,MAAM,WAAW,GAA6B;IAC5C,QAAQ,EAAE,QAAQ,CAAC,QAAQ;IAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK;IACrB,KAAK,EAAE,QAAQ,CAAC,KAAK;IACrB,KAAK,EAAE,QAAQ,CAAC,KAAK;IACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;IACnB,GAAG,EAAE,QAAQ,CAAC,GAAG;IACjB,EAAE,EAAE,QAAQ,CAAC,EAAE;IACf,QAAQ,EAAE,QAAQ,CAAC,MAAM;IACzB,SAAS,EAAE,QAAQ,CAAC,OAAO;CAC5B,CAAC;AAEF;;;;;AAKG;AACa,SAAA,kBAAkB,CAChC,YAA0B,EAC1B,KAAoD,EAAA;AAEpD,IAAA,OAAO,IAAI,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED;;;;AAIG;AACG,SAAU,cAAc,CAAC,GAAQ,EAAA;AACrC,IAAA,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAkB,CAAC;AACnE,IAAA,OAAO,IAAI,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,YAAY,CAAA;IAShB,WAAY,CAAA,YAA0B,EAAE,KAAoD,EAAA;;AAC1F,QAAA,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;AACjC,QAAA,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;AAClB,QAAA,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;AAEpB,QAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AAChD,YAAA,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AACxB,gBAAA,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,KAAK,sBAAA,CAAA,IAAI,EAAe,uBAAA,EAAA,GAAA,EAAA,2BAAA,CAAA,CAAA,IAAA,CAAnB,IAAI,EAAgB,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/D,aAAA;AAAM,iBAAA;gBACL,sBAAA,CAAA,IAAI,EAAe,uBAAA,EAAA,GAAA,EAAA,2BAAA,CAAA,CAAA,IAAA,CAAnB,IAAI,EAAgB,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;AACvC,aAAA;AACF,SAAA;KACF;AAsJF,CAAA;AApJgB,uBAAA,GAAA,IAAA,OAAA,EAAA,EAAA,2BAAA,GAAA,SAAA,2BAAA,CAAA,GAAW,EAAE,KAAa,EAAA;AACvC,IAAA,IAAI,IAAI,CAAC;AACT,IAAA,IAAI,QAAQ,CAAC;IAEb,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,UAAU,IAAI,CAAC,EAAE;QACnB,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QACpC,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;AAC1C,KAAA;AAAM,SAAA;QACL,IAAI,GAAG,GAAG,CAAC;QACX,QAAQ,GAAG,EAAE,CAAC;AACf,KAAA;AAED,IAAA,QAAQ,IAAI;AACV,QAAA,KAAK,OAAO;AACV,YAAA,sBAAA,CAAA,IAAI,EAAe,uBAAA,EAAA,GAAA,EAAA,2BAAA,CAAA,CAAA,IAAA,CAAnB,IAAI,EAAgB,KAAK,CAAC,CAAC;YAC3B,MAAM;AAER,QAAA,KAAK,QAAQ;AACX,YAAA,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC7B,MAAM;AAER,QAAA,KAAK,SAAS;AACZ,YAAA,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM;AAER,QAAA,KAAK,QAAQ;AACX,YAAA,IAAI,CAAC,KAAK,GAAG,KAAyC,CAAC;YACvD,MAAM;AAER,QAAA,KAAK,UAAU;AACb,YAAA,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;AACxB,YAAA,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;YACf,MAAM;AAER,QAAA,KAAK,aAAa;AAChB,YAAA,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;YACxB,MAAM;AAER,QAAA,SAAS;AACP,YAAA,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC,CAAC;AAC1E,YAAA,IAAI,KAAK,EAAE;gBACT,sBAAA,CAAA,IAAI,EAAgB,uBAAA,EAAA,GAAA,EAAA,4BAAA,CAAA,CAAA,IAAA,CAApB,IAAI,EAAiB,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC9C,aAAA;AAAM,iBAAA;gBACL,sBAAA,CAAA,IAAI,EAAuB,uBAAA,EAAA,GAAA,EAAA,mCAAA,CAAA,CAAA,IAAA,CAA3B,IAAI,EAAwB,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AACpD,aAAA;AACF,SAAA;AACF,KAAA;AACH,CAAC,qEAEc,KAAa,EAAA;IAC1B,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;AACpC,QAAA,IAAI,IAAI,CAAC;QACT,IAAI,UAAU,GAAG,KAAK,CAAC;AACvB,QAAA,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;AACzB,YAAA,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC1B,UAAU,GAAG,IAAI,CAAC;AACnB,SAAA;AAAM,aAAA;YACL,IAAI,GAAG,KAAK,CAAC;AACd,SAAA;QACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;AAC3C,KAAA;AACH,CAAC,EAEe,4BAAA,GAAA,SAAA,4BAAA,CAAA,WAA4B,EAAE,QAAgB,EAAE,KAAa,EAAA;IAC3E,IAAI,QAAQ,KAAK,SAAS,EAAE;AAC1B,QAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;YAChB,IAAI,EAAE,WAAW,CAAC,IAAc;YAChC,QAAQ,EAAE,QAAQ,CAAC,OAAO;YAC1B,KAAK;AACN,SAAA,CAAC,CAAC;QACH,OAAO;AACR,KAAA;IACD,QAAQ,WAAW,CAAC,IAAI;AACtB,QAAA,KAAK,QAAQ,CAAC;AACd,QAAA,KAAK,MAAM;YACT,sBAAA,CAAA,IAAI,8DAAiB,CAArB,IAAA,CAAA,IAAI,EAAkB,WAAW,EAAE,KAAK,CAAC,CAAC;YAC1C,MAAM;AACR,QAAA,KAAK,QAAQ,CAAC;AACd,QAAA,KAAK,OAAO,CAAC;AACb,QAAA,KAAK,KAAK;YACR,sBAAA,CAAA,IAAI,EAAmB,uBAAA,EAAA,GAAA,EAAA,+BAAA,CAAA,CAAA,IAAA,CAAvB,IAAI,EAAoB,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YACtD,MAAM;AACR,QAAA,KAAK,WAAW;YACd,sBAAA,CAAA,IAAI,6DAAgB,CAApB,IAAA,CAAA,IAAI,EAAiB,WAAW,EAAE,KAAK,CAAC,CAAC;YACzC,MAAM;AACR,QAAA,KAAK,UAAU;YACb,sBAAA,CAAA,IAAI,4DAAe,CAAnB,IAAA,CAAA,IAAI,EAAgB,WAAW,EAAE,KAAK,CAAC,CAAC;YACxC,MAAM;AACT,KAAA;AACH,CAAC,EAAA,6BAAA,GAAA,SAAA,6BAAA,CAEgB,KAAsB,EAAE,KAAa,EAAA;IACpD,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;AAC/C,IAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAChB,IAAI,EAAE,KAAK,CAAC,IAAc;QAC1B,QAAQ;QACR,KAAK;AACN,KAAA,CAAC,CAAC;AACL,CAAC,EAEkB,+BAAA,GAAA,SAAA,+BAAA,CAAA,KAAsB,EAAE,QAAgB,EAAE,KAAa,EAAA;AACxE,IAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAChB,IAAI,EAAE,KAAK,CAAC,IAAc;AAC1B,QAAA,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;QACjC,KAAK;AACN,KAAA,CAAC,CAAC;AACL,CAAC,EAAA,4BAAA,GAAA,SAAA,4BAAA,CAEe,KAAsB,EAAE,KAAa,EAAA;AACnD,IAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAChB,IAAI,EAAE,KAAK,CAAC,IAAc;QAC1B,QAAQ,EAAE,QAAQ,CAAC,MAAM;AACzB,QAAA,KAAK,EAAE,KAAK;AACb,KAAA,CAAC,CAAC;AACL,CAAC,EAAA,2BAAA,GAAA,SAAA,2BAAA,CAEc,KAAsB,EAAE,KAAa,EAAA;AAClD,IAAA,MAAM,CAAC,YAAY,EAAE,UAAU,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9D,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;AACtD,IAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAChB,IAAI,EAAE,KAAK,CAAC,IAAc;QAC1B,QAAQ;QACR,KAAK;QACL,UAAU;QACV,QAAQ;AACT,KAAA,CAAC,CAAC;AACL,CAAC,EAEsB,mCAAA,GAAA,SAAA,mCAAA,CAAA,IAAY,EAAE,QAAgB,EAAE,KAAa,EAAA;AAClE,IAAA,IAAI,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC;AAC/B,IAAA,IAAI,QAAQ,EAAE;QACZ,QAAQ,GAAG,QAAoB,CAAC;AACjC,KAAA;AAAM,SAAA;QACL,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE;AAC3C,YAAA,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,GAAG,MAAM,GAAG,KAAK,CAAC,CAAC,EAAE;gBACjD,QAAQ,GAAG,MAAkB,CAAC;gBAC9B,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACxC,aAAA;AACF,SAAA;AACF,KAAA;AAED,IAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAChB,IAAI;QACJ,QAAQ;QACR,KAAK;AACN,KAAA,CAAC,CAAC;AACL,CAAC,CAAA;AAGH,SAAS,WAAW,CAAC,KAAa,EAAA;IAChC,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,IAAI,MAAM,IAAI,SAAS,EAAE;AACvB,QAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;AACnE,KAAA;IACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACrD,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB,EAAA;IACrC,OAAO,WAAW,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC;AAClD;;;;"}
File without changes
@@ -197,9 +197,6 @@ function formatFilter(filter) {
197
197
  return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
198
198
  }
199
199
  function formatSortRules(sortRules) {
200
- if (!sortRules || sortRules.length === 0) {
201
- return '';
202
- }
203
200
  return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
204
201
  }
205
202
 
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.mjs","sources":["../../../src/search/search.ts"],"sourcesContent":["import { ResourceType } from '@medplum/fhirtypes';\n\nexport const DEFAULT_SEARCH_COUNT = 20;\n\nexport interface SearchRequest {\n readonly resourceType: ResourceType;\n filters?: Filter[];\n sortRules?: SortRule[];\n offset?: number;\n count?: number;\n fields?: string[];\n name?: string;\n total?: 'none' | 'estimate' | 'accurate';\n revInclude?: string;\n}\n\nexport interface Filter {\n code: string;\n operator: Operator;\n value: string;\n unitSystem?: string;\n unitCode?: string;\n}\n\nexport interface SortRule {\n code: string;\n descending?: boolean;\n}\n\n/**\n * Search operators.\n * These operators represent \"modifiers\" and \"prefixes\" in FHIR search.\n * See: https://www.hl7.org/fhir/search.html\n */\nexport enum Operator {\n EQUALS = 'eq',\n NOT_EQUALS = 'ne',\n\n // Numbers\n GREATER_THAN = 'gt',\n LESS_THAN = 'lt',\n GREATER_THAN_OR_EQUALS = 'ge',\n LESS_THAN_OR_EQUALS = 'le',\n\n // Dates\n STARTS_AFTER = 'sa',\n ENDS_BEFORE = 'eb',\n APPROXIMATELY = 'ap',\n\n // String\n CONTAINS = 'contains',\n EXACT = 'exact',\n\n // Token\n TEXT = 'text',\n NOT = 'not',\n ABOVE = 'above',\n BELOW = 'below',\n IN = 'in',\n NOT_IN = 'not-in',\n OF_TYPE = 'of-type',\n\n // All\n MISSING = 'missing',\n}\n\nconst MODIFIER_OPERATORS: Operator[] = [\n Operator.CONTAINS,\n Operator.EXACT,\n Operator.TEXT,\n Operator.NOT,\n Operator.ABOVE,\n Operator.BELOW,\n Operator.IN,\n Operator.NOT_IN,\n Operator.OF_TYPE,\n Operator.MISSING,\n];\n\nconst PREFIX_OPERATORS: Operator[] = [\n Operator.NOT_EQUALS,\n Operator.GREATER_THAN,\n Operator.LESS_THAN,\n Operator.GREATER_THAN_OR_EQUALS,\n Operator.LESS_THAN_OR_EQUALS,\n Operator.STARTS_AFTER,\n Operator.ENDS_BEFORE,\n Operator.APPROXIMATELY,\n];\n\n/**\n * Parses a URL into a SearchRequest.\n *\n * See the FHIR search spec: http://hl7.org/fhir/r4/search.html\n *\n * @param url The URL to parse.\n * @returns Parsed search definition.\n */\nexport function parseSearchDefinition(url: string): SearchRequest {\n const location = new URL(url, 'https://example.com/');\n const resourceType = location.pathname\n .replace(/(^\\/)|(\\/$)/g, '') // Remove leading and trailing slashes\n .split('/')\n .pop() as ResourceType;\n const params = new URLSearchParams(location.search);\n let filters: Filter[] | undefined = undefined;\n let sortRules: SortRule[] | undefined = undefined;\n let fields: string[] | undefined = undefined;\n let offset = undefined;\n let count = undefined;\n let total = undefined;\n\n params.forEach((value, key) => {\n if (key === '_fields') {\n fields = value.split(',');\n } else if (key === '_offset') {\n offset = parseInt(value);\n } else if (key === '_count') {\n count = parseInt(value);\n } else if (key === '_total') {\n total = value;\n } else if (key === '_sort') {\n sortRules = sortRules || [];\n sortRules.push(parseSortRule(value));\n } else {\n filters = filters || [];\n filters.push(parseSearchFilter(key, value));\n }\n });\n\n return {\n resourceType,\n filters,\n fields,\n offset,\n count,\n total,\n sortRules,\n };\n}\n\n/**\n * Parses a URL query parameter into a sort rule.\n *\n * By default, the sort rule is the field name.\n *\n * Sort rules can be reversed into descending order by prefixing the field name with a minus sign.\n *\n * See sorting: http://hl7.org/fhir/r4/search.html#_sort\n *\n * @param value The URL parameter value.\n * @returns The parsed sort rule.\n */\nfunction parseSortRule(value: string): SortRule {\n if (value.startsWith('-')) {\n return { code: value.substring(1), descending: true };\n } else {\n return { code: value };\n }\n}\n\n/**\n * Parses a URL query parameter into a search filter.\n *\n * FHIR search filters can be specified as modifiers or prefixes.\n *\n * For string properties, modifiers are appended to the key, e.g. \"name:contains=eve\".\n *\n * For date and numeric properties, prefixes are prepended to the value, e.g. \"birthdate=gt2000\".\n *\n * See the FHIR search spec: http://hl7.org/fhir/r4/search.html\n *\n * @param key The URL parameter key.\n * @param value The URL parameter value.\n * @returns The parsed search filter.\n */\nfunction parseSearchFilter(key: string, value: string): Filter {\n let code = key;\n let operator = Operator.EQUALS;\n\n for (const modifier of MODIFIER_OPERATORS) {\n const modifierIndex = code.indexOf(':' + modifier);\n if (modifierIndex !== -1) {\n operator = modifier;\n code = code.substring(0, modifierIndex);\n }\n }\n\n for (const prefix of PREFIX_OPERATORS) {\n if (value.match(new RegExp('^' + prefix + '\\\\d'))) {\n operator = prefix;\n value = value.substring(prefix.length);\n }\n }\n\n return { code, operator, value };\n}\n\n/**\n * Formats a search definition object into a query string.\n * Note: The return value does not include the resource type.\n * @param {!SearchRequest} definition The search definition.\n * @returns Formatted URL.\n */\nexport function formatSearchQuery(definition: SearchRequest): string {\n const params: string[] = [];\n\n if (definition.fields) {\n params.push('_fields=' + definition.fields.join(','));\n }\n\n if (definition.filters) {\n definition.filters.forEach((filter) => params.push(formatFilter(filter)));\n }\n\n if (definition.sortRules && definition.sortRules.length > 0) {\n params.push(formatSortRules(definition.sortRules));\n }\n\n if (definition.offset !== undefined) {\n params.push('_offset=' + definition.offset);\n }\n\n if (definition.count !== undefined) {\n params.push('_count=' + definition.count);\n }\n\n if (definition.total !== undefined) {\n params.push('_total=' + definition.total);\n }\n\n if (params.length === 0) {\n return '';\n }\n\n params.sort();\n return '?' + params.join('&');\n}\n\nfunction formatFilter(filter: Filter): string {\n const modifier = MODIFIER_OPERATORS.includes(filter.operator) ? ':' + filter.operator : '';\n const prefix = PREFIX_OPERATORS.includes(filter.operator) ? filter.operator : '';\n return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;\n}\n\nfunction formatSortRules(sortRules: SortRule[]): string {\n return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { Bundle, ElementDefinition, SearchParameter, StructureDefinition } from '@medplum/fhirtypes';
2
- import { SearchParameterDetails } from './searchparams';
2
+ import { SearchParameterDetails } from './search/details';
3
3
  export interface TypedValue {
4
4
  readonly type: string;
5
5
  readonly value: any;
@@ -124,12 +124,6 @@ export interface TypeSchema {
124
124
  description?: string;
125
125
  parentType?: string;
126
126
  }
127
- /**
128
- * Creates a new empty IndexedStructureDefinition.
129
- * @returns The empty IndexedStructureDefinition.
130
- * @deprecated Use globalSchema
131
- */
132
- export declare function createSchema(): IndexedStructureDefinition;
133
127
  /**
134
128
  * Indexes a bundle of StructureDefinitions for faster lookup.
135
129
  * @param bundle A FHIR bundle StructureDefinition resources.
@@ -162,6 +156,35 @@ export declare function indexSearchParameter(searchParam: SearchParameter): void
162
156
  */
163
157
  export declare function getElementDefinitionTypeName(elementDefinition: ElementDefinition): string;
164
158
  export declare function buildTypeName(components: string[]): string;
159
+ /**
160
+ * Returns true if the type schema is a DomainResource.
161
+ * @param typeSchema The type schema to check.
162
+ * @returns True if the type schema is a DomainResource.
163
+ */
164
+ export declare function isResourceType(typeSchema: TypeSchema): boolean;
165
+ /**
166
+ * Returns an array of all resource types.
167
+ * Note that this is based on globalSchema, and will only return resource types that are currently in memory.
168
+ * @returns An array of all resource types.
169
+ */
170
+ export declare function getResourceTypes(): string[];
171
+ /**
172
+ * Returns the type schema for the resource type.
173
+ * @param resourceType The resource type.
174
+ * @returns The type schema for the resource type.
175
+ */
176
+ export declare function getResourceTypeSchema(resourceType: string): TypeSchema;
177
+ /**
178
+ * Returns the search parameters for the resource type indexed by search code.
179
+ * @param resourceType The resource type.
180
+ * @returns The search parameters for the resource type indexed by search code.
181
+ */
182
+ export declare function getSearchParameters(resourceType: string): Record<string, SearchParameter> | undefined;
183
+ /**
184
+ * Returns a human friendly display name for a FHIR element definition path.
185
+ * @param path The FHIR element definition path.
186
+ * @returns The best guess of the display name.
187
+ */
165
188
  export declare function getPropertyDisplayName(path: string): string;
166
189
  /**
167
190
  * Returns an element definition by type and property name.
@@ -68,23 +68,6 @@ var PropertyType;
68
68
  PropertyType["url"] = "url";
69
69
  PropertyType["uuid"] = "uuid";
70
70
  })(PropertyType || (PropertyType = {}));
71
- /**
72
- * Creates a new empty IndexedStructureDefinition.
73
- * @returns The empty IndexedStructureDefinition.
74
- * @deprecated Use globalSchema
75
- */
76
- function createSchema() {
77
- return { types: {} };
78
- }
79
- function createTypeSchema(typeName, structureDefinition, elementDefinition) {
80
- return {
81
- structureDefinition,
82
- elementDefinition,
83
- display: typeName,
84
- description: elementDefinition.definition,
85
- properties: {},
86
- };
87
- }
88
71
  /**
89
72
  * Indexes a bundle of StructureDefinitions for faster lookup.
90
73
  * @param bundle A FHIR bundle StructureDefinition resources.
@@ -132,8 +115,16 @@ function indexType(structureDefinition, elementDefinition) {
132
115
  }
133
116
  const parts = path.split('.');
134
117
  const typeName = buildTypeName(parts);
135
- globalSchema.types[typeName] = createTypeSchema(typeName, structureDefinition, elementDefinition);
136
- globalSchema.types[typeName].parentType = buildTypeName(parts.slice(0, parts.length - 1));
118
+ let typeSchema = globalSchema.types[typeName];
119
+ if (!typeSchema) {
120
+ globalSchema.types[typeName] = typeSchema = {};
121
+ }
122
+ typeSchema.parentType = typeSchema.parentType || buildTypeName(parts.slice(0, parts.length - 1));
123
+ typeSchema.display = typeSchema.display || typeName;
124
+ typeSchema.structureDefinition = typeSchema.structureDefinition || structureDefinition;
125
+ typeSchema.elementDefinition = typeSchema.elementDefinition || elementDefinition;
126
+ typeSchema.description = typeSchema.description || elementDefinition.definition;
127
+ typeSchema.properties = typeSchema.properties || {};
137
128
  }
138
129
  /**
139
130
  * Indexes PropertySchema from an ElementDefinition.
@@ -248,15 +239,54 @@ function buildTypeName(components) {
248
239
  }
249
240
  return components.map(capitalize).join('');
250
241
  }
242
+ /**
243
+ * Returns true if the type schema is a DomainResource.
244
+ * @param typeSchema The type schema to check.
245
+ * @returns True if the type schema is a DomainResource.
246
+ */
247
+ function isResourceType(typeSchema) {
248
+ return typeSchema.structureDefinition?.baseDefinition === 'http://hl7.org/fhir/StructureDefinition/DomainResource';
249
+ }
250
+ /**
251
+ * Returns an array of all resource types.
252
+ * Note that this is based on globalSchema, and will only return resource types that are currently in memory.
253
+ * @returns An array of all resource types.
254
+ */
255
+ function getResourceTypes() {
256
+ const result = [];
257
+ for (const [resourceType, typeSchema] of Object.entries(globalSchema.types)) {
258
+ if (isResourceType(typeSchema)) {
259
+ result.push(resourceType);
260
+ }
261
+ }
262
+ return result;
263
+ }
264
+ /**
265
+ * Returns the type schema for the resource type.
266
+ * @param resourceType The resource type.
267
+ * @returns The type schema for the resource type.
268
+ */
269
+ function getResourceTypeSchema(resourceType) {
270
+ return globalSchema.types[resourceType];
271
+ }
272
+ /**
273
+ * Returns the search parameters for the resource type indexed by search code.
274
+ * @param resourceType The resource type.
275
+ * @returns The search parameters for the resource type indexed by search code.
276
+ */
277
+ function getSearchParameters(resourceType) {
278
+ return globalSchema.types[resourceType].searchParams;
279
+ }
280
+ /**
281
+ * Returns a human friendly display name for a FHIR element definition path.
282
+ * @param path The FHIR element definition path.
283
+ * @returns The best guess of the display name.
284
+ */
251
285
  function getPropertyDisplayName(path) {
252
286
  // Get the property name, which is the remainder after the last period
253
287
  // For example, for path "Patient.birthDate"
254
288
  // the property name is "birthDate"
255
289
  const propertyName = path.replaceAll('[x]', '').split('.').pop();
256
- // Special case for ID
257
- if (propertyName === 'id') {
258
- return 'ID';
259
- }
260
290
  // Split by capital letters
261
291
  // Capitalize the first letter of each word
262
292
  // Join together with spaces in between
@@ -265,11 +295,19 @@ function getPropertyDisplayName(path) {
265
295
  // the display name is "Birth Date".
266
296
  return propertyName
267
297
  .split(/(?=[A-Z])/)
268
- .map(capitalize)
298
+ .map(capitalizeDisplayWord)
269
299
  .join(' ')
270
300
  .replace('_', ' ')
271
301
  .replace(/\s+/g, ' ');
272
302
  }
303
+ const capitalizedWords = new Set(['ID', 'PKCE', 'JWKS', 'URI', 'URL']);
304
+ function capitalizeDisplayWord(word) {
305
+ const upper = word.toUpperCase();
306
+ if (capitalizedWords.has(upper)) {
307
+ return upper;
308
+ }
309
+ return upper.charAt(0) + word.slice(1);
310
+ }
273
311
  /**
274
312
  * Returns an element definition by type and property name.
275
313
  * Handles content references.
@@ -301,5 +339,5 @@ function getElementDefinition(typeName, propertyName) {
301
339
  */
302
340
  const globalSchema = baseSchema;
303
341
 
304
- export { PropertyType, buildTypeName, createSchema, getElementDefinition, getElementDefinitionTypeName, getPropertyDisplayName, globalSchema, indexSearchParameter, indexSearchParameterBundle, indexStructureDefinition, indexStructureDefinitionBundle };
342
+ export { PropertyType, buildTypeName, getElementDefinition, getElementDefinitionTypeName, getPropertyDisplayName, getResourceTypeSchema, getResourceTypes, getSearchParameters, globalSchema, indexSearchParameter, indexSearchParameterBundle, indexStructureDefinition, indexStructureDefinitionBundle, isResourceType };
305
343
  //# sourceMappingURL=types.mjs.map