@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
@@ -6023,23 +6023,6 @@
6023
6023
  PropertyType["url"] = "url";
6024
6024
  PropertyType["uuid"] = "uuid";
6025
6025
  })(exports.PropertyType || (exports.PropertyType = {}));
6026
- /**
6027
- * Creates a new empty IndexedStructureDefinition.
6028
- * @returns The empty IndexedStructureDefinition.
6029
- * @deprecated Use globalSchema
6030
- */
6031
- function createSchema() {
6032
- return { types: {} };
6033
- }
6034
- function createTypeSchema(typeName, structureDefinition, elementDefinition) {
6035
- return {
6036
- structureDefinition,
6037
- elementDefinition,
6038
- display: typeName,
6039
- description: elementDefinition.definition,
6040
- properties: {},
6041
- };
6042
- }
6043
6026
  /**
6044
6027
  * Indexes a bundle of StructureDefinitions for faster lookup.
6045
6028
  * @param bundle A FHIR bundle StructureDefinition resources.
@@ -6087,8 +6070,16 @@
6087
6070
  }
6088
6071
  const parts = path.split('.');
6089
6072
  const typeName = buildTypeName(parts);
6090
- globalSchema.types[typeName] = createTypeSchema(typeName, structureDefinition, elementDefinition);
6091
- globalSchema.types[typeName].parentType = buildTypeName(parts.slice(0, parts.length - 1));
6073
+ let typeSchema = globalSchema.types[typeName];
6074
+ if (!typeSchema) {
6075
+ globalSchema.types[typeName] = typeSchema = {};
6076
+ }
6077
+ typeSchema.parentType = typeSchema.parentType || buildTypeName(parts.slice(0, parts.length - 1));
6078
+ typeSchema.display = typeSchema.display || typeName;
6079
+ typeSchema.structureDefinition = typeSchema.structureDefinition || structureDefinition;
6080
+ typeSchema.elementDefinition = typeSchema.elementDefinition || elementDefinition;
6081
+ typeSchema.description = typeSchema.description || elementDefinition.definition;
6082
+ typeSchema.properties = typeSchema.properties || {};
6092
6083
  }
6093
6084
  /**
6094
6085
  * Indexes PropertySchema from an ElementDefinition.
@@ -6203,15 +6194,54 @@
6203
6194
  }
6204
6195
  return components.map(capitalize).join('');
6205
6196
  }
6197
+ /**
6198
+ * Returns true if the type schema is a DomainResource.
6199
+ * @param typeSchema The type schema to check.
6200
+ * @returns True if the type schema is a DomainResource.
6201
+ */
6202
+ function isResourceType(typeSchema) {
6203
+ return typeSchema.structureDefinition?.baseDefinition === 'http://hl7.org/fhir/StructureDefinition/DomainResource';
6204
+ }
6205
+ /**
6206
+ * Returns an array of all resource types.
6207
+ * Note that this is based on globalSchema, and will only return resource types that are currently in memory.
6208
+ * @returns An array of all resource types.
6209
+ */
6210
+ function getResourceTypes() {
6211
+ const result = [];
6212
+ for (const [resourceType, typeSchema] of Object.entries(globalSchema.types)) {
6213
+ if (isResourceType(typeSchema)) {
6214
+ result.push(resourceType);
6215
+ }
6216
+ }
6217
+ return result;
6218
+ }
6219
+ /**
6220
+ * Returns the type schema for the resource type.
6221
+ * @param resourceType The resource type.
6222
+ * @returns The type schema for the resource type.
6223
+ */
6224
+ function getResourceTypeSchema(resourceType) {
6225
+ return globalSchema.types[resourceType];
6226
+ }
6227
+ /**
6228
+ * Returns the search parameters for the resource type indexed by search code.
6229
+ * @param resourceType The resource type.
6230
+ * @returns The search parameters for the resource type indexed by search code.
6231
+ */
6232
+ function getSearchParameters(resourceType) {
6233
+ return globalSchema.types[resourceType].searchParams;
6234
+ }
6235
+ /**
6236
+ * Returns a human friendly display name for a FHIR element definition path.
6237
+ * @param path The FHIR element definition path.
6238
+ * @returns The best guess of the display name.
6239
+ */
6206
6240
  function getPropertyDisplayName(path) {
6207
6241
  // Get the property name, which is the remainder after the last period
6208
6242
  // For example, for path "Patient.birthDate"
6209
6243
  // the property name is "birthDate"
6210
6244
  const propertyName = path.replaceAll('[x]', '').split('.').pop();
6211
- // Special case for ID
6212
- if (propertyName === 'id') {
6213
- return 'ID';
6214
- }
6215
6245
  // Split by capital letters
6216
6246
  // Capitalize the first letter of each word
6217
6247
  // Join together with spaces in between
@@ -6220,11 +6250,19 @@
6220
6250
  // the display name is "Birth Date".
6221
6251
  return propertyName
6222
6252
  .split(/(?=[A-Z])/)
6223
- .map(capitalize)
6253
+ .map(capitalizeDisplayWord)
6224
6254
  .join(' ')
6225
6255
  .replace('_', ' ')
6226
6256
  .replace(/\s+/g, ' ');
6227
6257
  }
6258
+ const capitalizedWords = new Set(['ID', 'PKCE', 'JWKS', 'URI', 'URL']);
6259
+ function capitalizeDisplayWord(word) {
6260
+ const upper = word.toUpperCase();
6261
+ if (capitalizedWords.has(upper)) {
6262
+ return upper;
6263
+ }
6264
+ return upper.charAt(0) + word.slice(1);
6265
+ }
6228
6266
  /**
6229
6267
  * Returns an element definition by type and property name.
6230
6268
  * Handles content references.
@@ -6259,7 +6297,7 @@
6259
6297
  // PKCE auth based on:
6260
6298
  // https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
6261
6299
  var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _MedplumClient_baseUrl, _MedplumClient_fhirBaseUrl, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_autoBatchTime, _MedplumClient_autoBatchQueue, _MedplumClient_clientId, _MedplumClient_clientSecret, _MedplumClient_autoBatchTimerId, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_getCacheEntry, _MedplumClient_setCacheEntry, _MedplumClient_cacheResource, _MedplumClient_deleteCacheEntry, _MedplumClient_request, _MedplumClient_fetchWithRetry, _MedplumClient_executeAutoBatch, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
6262
- const MEDPLUM_VERSION = "2.0.3-e39c01ab";
6300
+ const MEDPLUM_VERSION = "2.0.4-c4747b4e";
6263
6301
  const DEFAULT_BASE_URL = 'https://api.medplum.com/';
6264
6302
  const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
6265
6303
  const DEFAULT_CACHE_TIME = 60000; // 60 seconds
@@ -6270,7 +6308,7 @@
6270
6308
  /**
6271
6309
  * The MedplumClient class provides a client for the Medplum FHIR server.
6272
6310
  *
6273
- * The client can be used in the browser, in a NodeJS application, or in a Medplum Bot.
6311
+ * The client can be used in the browser, in a Node.js application, or in a Medplum Bot.
6274
6312
  *
6275
6313
  * The client provides helpful methods for common operations such as:
6276
6314
  * 1) Authenticating
@@ -7651,6 +7689,7 @@
7651
7689
  /**
7652
7690
  * Starts a new PKCE flow.
7653
7691
  * These PKCE values are stateful, and must survive redirects and page refreshes.
7692
+ * @category Authentication
7654
7693
  */
7655
7694
  async startPkce() {
7656
7695
  const pkceState = getRandomString();
@@ -7666,6 +7705,7 @@
7666
7705
  * Processes an OAuth authorization code.
7667
7706
  * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
7668
7707
  * @param code The authorization code received by URL parameter.
7708
+ * @category Authentication
7669
7709
  */
7670
7710
  processCode(code) {
7671
7711
  const formBody = new URLSearchParams();
@@ -7673,9 +7713,11 @@
7673
7713
  formBody.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
7674
7714
  formBody.set('code', code);
7675
7715
  formBody.set('redirect_uri', getWindowOrigin());
7676
- const codeVerifier = sessionStorage.getItem('codeVerifier');
7677
- if (codeVerifier) {
7678
- formBody.set('code_verifier', codeVerifier);
7716
+ if (typeof sessionStorage !== 'undefined') {
7717
+ const codeVerifier = sessionStorage.getItem('codeVerifier');
7718
+ if (codeVerifier) {
7719
+ formBody.set('code_verifier', codeVerifier);
7720
+ }
7679
7721
  }
7680
7722
  return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, formBody);
7681
7723
  }
@@ -10914,6 +10956,160 @@
10914
10956
  }
10915
10957
  }
10916
10958
 
10959
+ exports.SearchParameterType = void 0;
10960
+ (function (SearchParameterType) {
10961
+ SearchParameterType["BOOLEAN"] = "BOOLEAN";
10962
+ SearchParameterType["NUMBER"] = "NUMBER";
10963
+ SearchParameterType["QUANTITY"] = "QUANTITY";
10964
+ SearchParameterType["TEXT"] = "TEXT";
10965
+ SearchParameterType["REFERENCE"] = "REFERENCE";
10966
+ SearchParameterType["DATE"] = "DATE";
10967
+ SearchParameterType["DATETIME"] = "DATETIME";
10968
+ SearchParameterType["PERIOD"] = "PERIOD";
10969
+ })(exports.SearchParameterType || (exports.SearchParameterType = {}));
10970
+ /**
10971
+ * Returns the type details of a SearchParameter.
10972
+ *
10973
+ * The SearchParameter resource has a "type" parameter, but that is missing some critical information.
10974
+ *
10975
+ * For example:
10976
+ * 1) The "date" type includes "date", "datetime", and "period".
10977
+ * 2) The "token" type includes enums and booleans.
10978
+ * 3) Arrays/multiple values are not reflected at all.
10979
+ *
10980
+ * @param resourceType The root resource type.
10981
+ * @param searchParam The search parameter.
10982
+ * @returns The search parameter type details.
10983
+ */
10984
+ function getSearchParameterDetails(resourceType, searchParam) {
10985
+ let result = globalSchema.types[resourceType]?.searchParamsDetails?.[searchParam.code];
10986
+ if (!result) {
10987
+ result = buildSearchParamterDetails(resourceType, searchParam);
10988
+ }
10989
+ return result;
10990
+ }
10991
+ function setSearchParamterDetails(resourceType, code, details) {
10992
+ const typeSchema = globalSchema.types[resourceType];
10993
+ if (!typeSchema.searchParamsDetails) {
10994
+ typeSchema.searchParamsDetails = {};
10995
+ }
10996
+ typeSchema.searchParamsDetails[code] = details;
10997
+ }
10998
+ function buildSearchParamterDetails(resourceType, searchParam) {
10999
+ const code = searchParam.code;
11000
+ const columnName = convertCodeToColumnName(code);
11001
+ const expression = getExpressionForResourceType(resourceType, searchParam.expression)?.split('.');
11002
+ if (!expression) {
11003
+ // This happens on compound types
11004
+ // In the future, explore returning multiple column definitions
11005
+ return { columnName, type: exports.SearchParameterType.TEXT };
11006
+ }
11007
+ const defaultType = getSearchParameterType(searchParam);
11008
+ let baseType = resourceType;
11009
+ let elementDefinition = undefined;
11010
+ let propertyType = undefined;
11011
+ let array = false;
11012
+ for (let i = 1; i < expression.length; i++) {
11013
+ const propertyName = expression[i];
11014
+ elementDefinition = getElementDefinition(baseType, propertyName);
11015
+ if (!elementDefinition) {
11016
+ throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);
11017
+ }
11018
+ if (elementDefinition.max === '*') {
11019
+ array = true;
11020
+ }
11021
+ propertyType = elementDefinition.type?.[0].code;
11022
+ if (!propertyType) {
11023
+ // This happens when one of parent properties uses contentReference
11024
+ // In the future, explore following the reference
11025
+ return { columnName, type: defaultType, array };
11026
+ }
11027
+ if (i < expression.length - 1) {
11028
+ if (isBackboneElement(propertyType)) {
11029
+ baseType = buildTypeName(elementDefinition.path?.split('.'));
11030
+ }
11031
+ else {
11032
+ baseType = propertyType;
11033
+ }
11034
+ }
11035
+ }
11036
+ const type = getSearchParameterType(searchParam, propertyType);
11037
+ const result = { columnName, type, elementDefinition, array };
11038
+ setSearchParamterDetails(resourceType, code, result);
11039
+ return result;
11040
+ }
11041
+ function isBackboneElement(propertyType) {
11042
+ return propertyType === 'Element' || propertyType === 'BackboneElement';
11043
+ }
11044
+ /**
11045
+ * Converts a hyphen-delimited code to camelCase string.
11046
+ * @param code The search parameter code.
11047
+ * @returns The SQL column name.
11048
+ */
11049
+ function convertCodeToColumnName(code) {
11050
+ return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');
11051
+ }
11052
+ function getSearchParameterType(searchParam, propertyType) {
11053
+ let type = exports.SearchParameterType.TEXT;
11054
+ switch (searchParam.type) {
11055
+ case 'date':
11056
+ if (propertyType === exports.PropertyType.dateTime || propertyType === exports.PropertyType.instant) {
11057
+ type = exports.SearchParameterType.DATETIME;
11058
+ }
11059
+ else {
11060
+ type = exports.SearchParameterType.DATE;
11061
+ }
11062
+ break;
11063
+ case 'number':
11064
+ type = exports.SearchParameterType.NUMBER;
11065
+ break;
11066
+ case 'quantity':
11067
+ type = exports.SearchParameterType.QUANTITY;
11068
+ break;
11069
+ case 'reference':
11070
+ type = exports.SearchParameterType.REFERENCE;
11071
+ break;
11072
+ case 'token':
11073
+ if (propertyType === 'boolean') {
11074
+ type = exports.SearchParameterType.BOOLEAN;
11075
+ }
11076
+ break;
11077
+ }
11078
+ return type;
11079
+ }
11080
+ function getExpressionForResourceType(resourceType, expression) {
11081
+ const expressions = expression.split(' | ');
11082
+ for (const e of expressions) {
11083
+ if (isIgnoredExpression(e)) {
11084
+ continue;
11085
+ }
11086
+ const simplified = simplifyExpression(e);
11087
+ if (simplified.startsWith(resourceType + '.')) {
11088
+ return simplified;
11089
+ }
11090
+ }
11091
+ return undefined;
11092
+ }
11093
+ function isIgnoredExpression(input) {
11094
+ return input.includes(' as Period') || input.includes(' as SampledDate');
11095
+ }
11096
+ function simplifyExpression(input) {
11097
+ let result = input.trim();
11098
+ if (result.startsWith('(') && result.endsWith(')')) {
11099
+ result = result.substring(1, result.length - 1);
11100
+ }
11101
+ if (result.includes('[0]')) {
11102
+ result = result.replaceAll('[0]', '');
11103
+ }
11104
+ const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.where('];
11105
+ for (const stopString of stopStrings) {
11106
+ if (result.includes(stopString)) {
11107
+ result = result.substring(0, result.indexOf(stopString));
11108
+ }
11109
+ }
11110
+ return result;
11111
+ }
11112
+
10917
11113
  const DEFAULT_SEARCH_COUNT = 20;
10918
11114
  /**
10919
11115
  * Search operators.
@@ -11113,168 +11309,9 @@
11113
11309
  return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
11114
11310
  }
11115
11311
  function formatSortRules(sortRules) {
11116
- if (!sortRules || sortRules.length === 0) {
11117
- return '';
11118
- }
11119
11312
  return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
11120
11313
  }
11121
11314
 
11122
- exports.SearchParameterType = void 0;
11123
- (function (SearchParameterType) {
11124
- SearchParameterType["BOOLEAN"] = "BOOLEAN";
11125
- SearchParameterType["NUMBER"] = "NUMBER";
11126
- SearchParameterType["QUANTITY"] = "QUANTITY";
11127
- SearchParameterType["TEXT"] = "TEXT";
11128
- SearchParameterType["REFERENCE"] = "REFERENCE";
11129
- SearchParameterType["DATE"] = "DATE";
11130
- SearchParameterType["DATETIME"] = "DATETIME";
11131
- SearchParameterType["PERIOD"] = "PERIOD";
11132
- })(exports.SearchParameterType || (exports.SearchParameterType = {}));
11133
- /**
11134
- * Returns the type details of a SearchParameter.
11135
- *
11136
- * The SearchParameter resource has a "type" parameter, but that is missing some critical information.
11137
- *
11138
- * For example:
11139
- * 1) The "date" type includes "date", "datetime", and "period".
11140
- * 2) The "token" type includes enums and booleans.
11141
- * 3) Arrays/multiple values are not reflected at all.
11142
- *
11143
- * @param resourceType The root resource type.
11144
- * @param searchParam The search parameter.
11145
- * @returns The search parameter type details.
11146
- */
11147
- function getSearchParameterDetails(resourceType, searchParam) {
11148
- let result = globalSchema.types[resourceType]?.searchParamsDetails?.[searchParam.code];
11149
- if (!result) {
11150
- result = buildSearchParamterDetails(resourceType, searchParam);
11151
- }
11152
- return result;
11153
- }
11154
- function setSearchParamterDetails(resourceType, code, details) {
11155
- const typeSchema = globalSchema.types[resourceType];
11156
- if (!typeSchema.searchParamsDetails) {
11157
- typeSchema.searchParamsDetails = {};
11158
- }
11159
- typeSchema.searchParamsDetails[code] = details;
11160
- }
11161
- function buildSearchParamterDetails(resourceType, searchParam) {
11162
- if (searchParam.code === '_lastUpdated') {
11163
- return { columnName: 'lastUpdated', type: exports.SearchParameterType.DATETIME };
11164
- }
11165
- const code = searchParam.code;
11166
- const columnName = convertCodeToColumnName(code);
11167
- const expression = getExpressionForResourceType(resourceType, searchParam.expression)?.split('.');
11168
- if (!expression) {
11169
- // This happens on compound types
11170
- // In the future, explore returning multiple column definitions
11171
- return { columnName, type: exports.SearchParameterType.TEXT };
11172
- }
11173
- const defaultType = getSearchParameterType(searchParam);
11174
- let baseType = resourceType;
11175
- let elementDefinition = undefined;
11176
- let propertyType = undefined;
11177
- let array = false;
11178
- for (let i = 1; i < expression.length; i++) {
11179
- const propertyName = expression[i];
11180
- elementDefinition =
11181
- globalSchema.types[baseType]?.properties?.[propertyName] ??
11182
- globalSchema.types[baseType]?.properties?.[propertyName + '[x]'];
11183
- if (!elementDefinition) {
11184
- throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);
11185
- }
11186
- if (elementDefinition.max === '*') {
11187
- array = true;
11188
- }
11189
- propertyType = elementDefinition.type?.[0].code;
11190
- if (!propertyType) {
11191
- // This happens when one of parent properties uses contentReference
11192
- // In the future, explore following the reference
11193
- return { columnName, type: defaultType, array };
11194
- }
11195
- if (i < expression.length - 1) {
11196
- if (propertyType === 'Element' || propertyType === 'BackboneElement') {
11197
- baseType = baseType + capitalize(propertyName);
11198
- }
11199
- else {
11200
- baseType = propertyType;
11201
- }
11202
- }
11203
- }
11204
- const type = getSearchParameterType(searchParam, propertyType);
11205
- const result = { columnName, type, elementDefinition, array };
11206
- setSearchParamterDetails(resourceType, code, result);
11207
- return result;
11208
- }
11209
- /**
11210
- * Converts a hyphen-delimited code to camelCase string.
11211
- * @param code The search parameter code.
11212
- * @returns The SQL column name.
11213
- */
11214
- function convertCodeToColumnName(code) {
11215
- return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');
11216
- }
11217
- function getSearchParameterType(searchParam, propertyType) {
11218
- let type = exports.SearchParameterType.TEXT;
11219
- switch (searchParam.type) {
11220
- case 'date':
11221
- if (propertyType === exports.PropertyType.dateTime || propertyType === exports.PropertyType.instant) {
11222
- type = exports.SearchParameterType.DATETIME;
11223
- }
11224
- else {
11225
- type = exports.SearchParameterType.DATE;
11226
- }
11227
- break;
11228
- case 'number':
11229
- type = exports.SearchParameterType.NUMBER;
11230
- break;
11231
- case 'quantity':
11232
- type = exports.SearchParameterType.QUANTITY;
11233
- break;
11234
- case 'reference':
11235
- type = exports.SearchParameterType.REFERENCE;
11236
- break;
11237
- case 'token':
11238
- if (propertyType === 'boolean') {
11239
- type = exports.SearchParameterType.BOOLEAN;
11240
- }
11241
- break;
11242
- }
11243
- return type;
11244
- }
11245
- function getExpressionForResourceType(resourceType, expression) {
11246
- const expressions = expression.split(' | ');
11247
- for (const e of expressions) {
11248
- if (isIgnoredExpression(e)) {
11249
- continue;
11250
- }
11251
- const simplified = simplifyExpression(e);
11252
- if (simplified.startsWith(resourceType + '.')) {
11253
- return simplified;
11254
- }
11255
- }
11256
- return undefined;
11257
- }
11258
- function isIgnoredExpression(input) {
11259
- return input.includes(' as Period') || input.includes(' as SampledDate');
11260
- }
11261
- function simplifyExpression(input) {
11262
- let result = input.trim();
11263
- if (result.startsWith('(') && result.endsWith(')')) {
11264
- result = result.substring(1, result.length - 1);
11265
- }
11266
- if (result.includes('[0]')) {
11267
- result = result.replaceAll('[0]', '');
11268
- }
11269
- const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.where('];
11270
- for (const stopString of stopStrings) {
11271
- if (result.includes(stopString)) {
11272
- result = result.substring(0, result.indexOf(stopString));
11273
- }
11274
- }
11275
- return result;
11276
- }
11277
-
11278
11315
  /**
11279
11316
  * Determines if the resource matches the search request.
11280
11317
  * @param resource The resource that was created or updated.
@@ -11424,6 +11461,218 @@
11424
11461
  return operator === exports.Operator.NOT_EQUALS || operator === exports.Operator.NOT;
11425
11462
  }
11426
11463
 
11464
+ var _SearchParser_instances, _SearchParser_parseKeyValue, _SearchParser_parseSortRule, _SearchParser_parseParameter, _SearchParser_parsePrefixType, _SearchParser_parseModifierType, _SearchParser_parseReference, _SearchParser_parseQuantity, _SearchParser_parseUnknownParameter;
11465
+ /**
11466
+ * Parses a FHIR search query.
11467
+ * See: https://www.hl7.org/fhir/search.html
11468
+ */
11469
+ /**
11470
+ * For the ordered parameter types of number, date, and quantity,
11471
+ * a prefix to the parameter value may be used to control the nature
11472
+ * of the matching.
11473
+ * See: https://www.hl7.org/fhir/search.html#prefix
11474
+ */
11475
+ const prefixMap = {
11476
+ eq: exports.Operator.EQUALS,
11477
+ ne: exports.Operator.NOT_EQUALS,
11478
+ lt: exports.Operator.LESS_THAN,
11479
+ le: exports.Operator.LESS_THAN_OR_EQUALS,
11480
+ gt: exports.Operator.GREATER_THAN,
11481
+ ge: exports.Operator.GREATER_THAN_OR_EQUALS,
11482
+ sa: exports.Operator.STARTS_AFTER,
11483
+ eb: exports.Operator.ENDS_BEFORE,
11484
+ ap: exports.Operator.APPROXIMATELY,
11485
+ };
11486
+ /**
11487
+ * Parameter names may specify a modifier as a suffix.
11488
+ * The modifiers are separated from the parameter name by a colon.
11489
+ * See: https://www.hl7.org/fhir/search.html#modifiers
11490
+ */
11491
+ const modifierMap = {
11492
+ contains: exports.Operator.CONTAINS,
11493
+ exact: exports.Operator.EXACT,
11494
+ above: exports.Operator.ABOVE,
11495
+ below: exports.Operator.BELOW,
11496
+ text: exports.Operator.TEXT,
11497
+ not: exports.Operator.NOT,
11498
+ in: exports.Operator.IN,
11499
+ 'not-in': exports.Operator.NOT_IN,
11500
+ 'of-type': exports.Operator.OF_TYPE,
11501
+ };
11502
+ /**
11503
+ * Parses a search URL into a search request.
11504
+ * @param resourceType The FHIR resource type.
11505
+ * @param query The collection of query string parameters.
11506
+ * @returns A parsed SearchRequest.
11507
+ */
11508
+ function parseSearchRequest(resourceType, query) {
11509
+ return new SearchParser(resourceType, query);
11510
+ }
11511
+ /**
11512
+ * Parses a search URL into a search request.
11513
+ * @param url The search URL.
11514
+ * @returns A parsed SearchRequest.
11515
+ */
11516
+ function parseSearchUrl(url) {
11517
+ const resourceType = url.pathname.split('/').pop();
11518
+ return new SearchParser(resourceType, Object.fromEntries(url.searchParams.entries()));
11519
+ }
11520
+ class SearchParser {
11521
+ constructor(resourceType, query) {
11522
+ _SearchParser_instances.add(this);
11523
+ this.resourceType = resourceType;
11524
+ this.filters = [];
11525
+ this.sortRules = [];
11526
+ for (const [key, value] of Object.entries(query)) {
11527
+ if (Array.isArray(value)) {
11528
+ value.forEach((element) => __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseKeyValue).call(this, key, element));
11529
+ }
11530
+ else {
11531
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseKeyValue).call(this, key, value ?? '');
11532
+ }
11533
+ }
11534
+ }
11535
+ }
11536
+ _SearchParser_instances = new WeakSet(), _SearchParser_parseKeyValue = function _SearchParser_parseKeyValue(key, value) {
11537
+ let code;
11538
+ let modifier;
11539
+ const colonIndex = key.indexOf(':');
11540
+ if (colonIndex >= 0) {
11541
+ code = key.substring(0, colonIndex);
11542
+ modifier = key.substring(colonIndex + 1);
11543
+ }
11544
+ else {
11545
+ code = key;
11546
+ modifier = '';
11547
+ }
11548
+ switch (code) {
11549
+ case '_sort':
11550
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseSortRule).call(this, value);
11551
+ break;
11552
+ case '_count':
11553
+ this.count = parseInt(value);
11554
+ break;
11555
+ case '_offset':
11556
+ this.offset = parseInt(value);
11557
+ break;
11558
+ case '_total':
11559
+ this.total = value;
11560
+ break;
11561
+ case '_summary':
11562
+ this.total = 'estimate';
11563
+ this.count = 0;
11564
+ break;
11565
+ case '_revinclude':
11566
+ this.revInclude = value;
11567
+ break;
11568
+ default: {
11569
+ const param = globalSchema.types[this.resourceType]?.searchParams?.[code];
11570
+ if (param) {
11571
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseParameter).call(this, param, modifier, value);
11572
+ }
11573
+ else {
11574
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseUnknownParameter).call(this, code, modifier, value);
11575
+ }
11576
+ }
11577
+ }
11578
+ }, _SearchParser_parseSortRule = function _SearchParser_parseSortRule(value) {
11579
+ for (const field of value.split(',')) {
11580
+ let code;
11581
+ let descending = false;
11582
+ if (field.startsWith('-')) {
11583
+ code = field.substring(1);
11584
+ descending = true;
11585
+ }
11586
+ else {
11587
+ code = field;
11588
+ }
11589
+ this.sortRules.push({ code, descending });
11590
+ }
11591
+ }, _SearchParser_parseParameter = function _SearchParser_parseParameter(searchParam, modifier, value) {
11592
+ if (modifier === 'missing') {
11593
+ this.filters.push({
11594
+ code: searchParam.code,
11595
+ operator: exports.Operator.MISSING,
11596
+ value,
11597
+ });
11598
+ return;
11599
+ }
11600
+ switch (searchParam.type) {
11601
+ case 'number':
11602
+ case 'date':
11603
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parsePrefixType).call(this, searchParam, value);
11604
+ break;
11605
+ case 'string':
11606
+ case 'token':
11607
+ case 'uri':
11608
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseModifierType).call(this, searchParam, modifier, value);
11609
+ break;
11610
+ case 'reference':
11611
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseReference).call(this, searchParam, value);
11612
+ break;
11613
+ case 'quantity':
11614
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseQuantity).call(this, searchParam, value);
11615
+ break;
11616
+ }
11617
+ }, _SearchParser_parsePrefixType = function _SearchParser_parsePrefixType(param, input) {
11618
+ const { operator, value } = parsePrefix(input);
11619
+ this.filters.push({
11620
+ code: param.code,
11621
+ operator,
11622
+ value,
11623
+ });
11624
+ }, _SearchParser_parseModifierType = function _SearchParser_parseModifierType(param, modifier, value) {
11625
+ this.filters.push({
11626
+ code: param.code,
11627
+ operator: parseModifier(modifier),
11628
+ value,
11629
+ });
11630
+ }, _SearchParser_parseReference = function _SearchParser_parseReference(param, value) {
11631
+ this.filters.push({
11632
+ code: param.code,
11633
+ operator: exports.Operator.EQUALS,
11634
+ value: value,
11635
+ });
11636
+ }, _SearchParser_parseQuantity = function _SearchParser_parseQuantity(param, input) {
11637
+ const [prefixNumber, unitSystem, unitCode] = input.split('|');
11638
+ const { operator, value } = parsePrefix(prefixNumber);
11639
+ this.filters.push({
11640
+ code: param.code,
11641
+ operator,
11642
+ value,
11643
+ unitSystem,
11644
+ unitCode,
11645
+ });
11646
+ }, _SearchParser_parseUnknownParameter = function _SearchParser_parseUnknownParameter(code, modifier, value) {
11647
+ let operator = exports.Operator.EQUALS;
11648
+ if (modifier) {
11649
+ operator = modifier;
11650
+ }
11651
+ else {
11652
+ for (const prefix of Object.keys(prefixMap)) {
11653
+ if (value.match(new RegExp('^' + prefix + '\\d'))) {
11654
+ operator = prefix;
11655
+ value = value.substring(prefix.length);
11656
+ }
11657
+ }
11658
+ }
11659
+ this.filters.push({
11660
+ code,
11661
+ operator,
11662
+ value,
11663
+ });
11664
+ };
11665
+ function parsePrefix(input) {
11666
+ const prefix = input.substring(0, 2);
11667
+ if (prefix in prefixMap) {
11668
+ return { operator: prefixMap[prefix], value: input.substring(2) };
11669
+ }
11670
+ return { operator: exports.Operator.EQUALS, value: input };
11671
+ }
11672
+ function parseModifier(modifier) {
11673
+ return modifierMap[modifier] || exports.Operator.EQUALS;
11674
+ }
11675
+
11427
11676
  exports.AndAtom = AndAtom;
11428
11677
  exports.ArithemticOperatorAtom = ArithemticOperatorAtom;
11429
11678
  exports.AsAtom = AsAtom;
@@ -11469,7 +11718,6 @@
11469
11718
  exports.calculateAgeString = calculateAgeString;
11470
11719
  exports.capitalize = capitalize;
11471
11720
  exports.createReference = createReference;
11472
- exports.createSchema = createSchema;
11473
11721
  exports.created = created;
11474
11722
  exports.deepClone = deepClone;
11475
11723
  exports.deepEquals = deepEquals$1;
@@ -11513,7 +11761,10 @@
11513
11761
  exports.getPropertyDisplayName = getPropertyDisplayName;
11514
11762
  exports.getQuestionnaireAnswers = getQuestionnaireAnswers;
11515
11763
  exports.getReferenceString = getReferenceString;
11764
+ exports.getResourceTypeSchema = getResourceTypeSchema;
11765
+ exports.getResourceTypes = getResourceTypes;
11516
11766
  exports.getSearchParameterDetails = getSearchParameterDetails;
11767
+ exports.getSearchParameters = getSearchParameters;
11517
11768
  exports.getStatus = getStatus;
11518
11769
  exports.getTypedPropertyValue = getTypedPropertyValue;
11519
11770
  exports.globalSchema = globalSchema;
@@ -11534,6 +11785,7 @@
11534
11785
  exports.isProfileResource = isProfileResource;
11535
11786
  exports.isQuantity = isQuantity;
11536
11787
  exports.isQuantityEquivalent = isQuantityEquivalent;
11788
+ exports.isResourceType = isResourceType;
11537
11789
  exports.isStringArray = isStringArray;
11538
11790
  exports.isUUID = isUUID;
11539
11791
  exports.isValidDate = isValidDate;
@@ -11545,6 +11797,8 @@
11545
11797
  exports.parseFhirPath = parseFhirPath;
11546
11798
  exports.parseJWTPayload = parseJWTPayload;
11547
11799
  exports.parseSearchDefinition = parseSearchDefinition;
11800
+ exports.parseSearchRequest = parseSearchRequest;
11801
+ exports.parseSearchUrl = parseSearchUrl;
11548
11802
  exports.preciseEquals = preciseEquals;
11549
11803
  exports.preciseGreaterThan = preciseGreaterThan;
11550
11804
  exports.preciseGreaterThanOrEquals = preciseGreaterThanOrEquals;