@medplum/core 2.0.3 → 2.0.5

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 (82) hide show
  1. package/dist/cjs/index.cjs +477 -209
  2. package/dist/cjs/index.cjs.map +1 -1
  3. package/dist/cjs/index.min.cjs +1 -1
  4. package/dist/esm/client.mjs +18 -21
  5. package/dist/esm/client.mjs.map +1 -1
  6. package/dist/esm/index.min.mjs +1 -1
  7. package/dist/esm/index.mjs +6 -5
  8. package/dist/esm/index.mjs.map +1 -1
  9. package/dist/esm/outcomes.mjs +17 -2
  10. package/dist/esm/outcomes.mjs.map +1 -1
  11. package/dist/esm/{searchparams.mjs → search/details.mjs} +9 -11
  12. package/dist/esm/search/details.mjs.map +1 -0
  13. package/dist/esm/{match.mjs → search/match.mjs} +16 -11
  14. package/dist/esm/search/match.mjs.map +1 -0
  15. package/dist/esm/search/parse.mjs +218 -0
  16. package/dist/esm/search/parse.mjs.map +1 -0
  17. package/dist/esm/{search.mjs → search/search.mjs} +0 -3
  18. package/dist/esm/search/search.mjs.map +1 -0
  19. package/dist/esm/types.mjs +63 -25
  20. package/dist/esm/types.mjs.map +1 -1
  21. package/dist/{esm → types}/client.d.ts +5 -3
  22. package/dist/{esm → types}/index.d.ts +4 -3
  23. package/dist/{esm → types}/outcomes.d.ts +7 -1
  24. package/dist/types/search/parse.d.ts +17 -0
  25. package/dist/{cjs → types}/types.d.ts +30 -7
  26. package/package.json +4 -4
  27. package/tsconfig.build.json +9 -0
  28. package/dist/cjs/client.d.ts +0 -1216
  29. package/dist/cjs/index.d.ts +0 -13
  30. package/dist/cjs/outcomes.d.ts +0 -31
  31. package/dist/esm/cache.d.ts +0 -34
  32. package/dist/esm/crypto.d.ts +0 -9
  33. package/dist/esm/eventtarget.d.ts +0 -13
  34. package/dist/esm/fhirlexer/index.d.ts +0 -2
  35. package/dist/esm/fhirlexer/parse.d.ts +0 -47
  36. package/dist/esm/fhirlexer/tokenize.d.ts +0 -14
  37. package/dist/esm/fhirmapper/parse.d.ts +0 -7
  38. package/dist/esm/fhirmapper/tokenize.d.ts +0 -2
  39. package/dist/esm/fhirpath/atoms.d.ts +0 -120
  40. package/dist/esm/fhirpath/date.d.ts +0 -1
  41. package/dist/esm/fhirpath/functions.d.ts +0 -6
  42. package/dist/esm/fhirpath/index.d.ts +0 -4
  43. package/dist/esm/fhirpath/parse.d.ts +0 -64
  44. package/dist/esm/fhirpath/tokenize.d.ts +0 -4
  45. package/dist/esm/fhirpath/utils.d.ts +0 -95
  46. package/dist/esm/format.d.ts +0 -118
  47. package/dist/esm/hl7.d.ts +0 -136
  48. package/dist/esm/jwt.d.ts +0 -5
  49. package/dist/esm/match.d.ts +0 -9
  50. package/dist/esm/match.mjs.map +0 -1
  51. package/dist/esm/readablepromise.d.ts +0 -48
  52. package/dist/esm/search.d.ts +0 -66
  53. package/dist/esm/search.mjs.map +0 -1
  54. package/dist/esm/searchparams.d.ts +0 -33
  55. package/dist/esm/searchparams.mjs.map +0 -1
  56. package/dist/esm/storage.d.ts +0 -47
  57. package/dist/esm/types.d.ts +0 -177
  58. package/dist/esm/utils.d.ts +0 -259
  59. /package/dist/{cjs → types}/cache.d.ts +0 -0
  60. /package/dist/{cjs → types}/crypto.d.ts +0 -0
  61. /package/dist/{cjs → types}/eventtarget.d.ts +0 -0
  62. /package/dist/{cjs → types}/fhirlexer/index.d.ts +0 -0
  63. /package/dist/{cjs → types}/fhirlexer/parse.d.ts +0 -0
  64. /package/dist/{cjs → types}/fhirlexer/tokenize.d.ts +0 -0
  65. /package/dist/{cjs → types}/fhirmapper/parse.d.ts +0 -0
  66. /package/dist/{cjs → types}/fhirmapper/tokenize.d.ts +0 -0
  67. /package/dist/{cjs → types}/fhirpath/atoms.d.ts +0 -0
  68. /package/dist/{cjs → types}/fhirpath/date.d.ts +0 -0
  69. /package/dist/{cjs → types}/fhirpath/functions.d.ts +0 -0
  70. /package/dist/{cjs → types}/fhirpath/index.d.ts +0 -0
  71. /package/dist/{cjs → types}/fhirpath/parse.d.ts +0 -0
  72. /package/dist/{cjs → types}/fhirpath/tokenize.d.ts +0 -0
  73. /package/dist/{cjs → types}/fhirpath/utils.d.ts +0 -0
  74. /package/dist/{cjs → types}/format.d.ts +0 -0
  75. /package/dist/{cjs → types}/hl7.d.ts +0 -0
  76. /package/dist/{cjs → types}/jwt.d.ts +0 -0
  77. /package/dist/{cjs → types}/readablepromise.d.ts +0 -0
  78. /package/dist/{cjs/searchparams.d.ts → types/search/details.d.ts} +0 -0
  79. /package/dist/{cjs → types/search}/match.d.ts +0 -0
  80. /package/dist/{cjs → types/search}/search.d.ts +0 -0
  81. /package/dist/{cjs → types}/storage.d.ts +0 -0
  82. /package/dist/{cjs → types}/utils.d.ts +0 -0
@@ -1353,11 +1353,26 @@
1353
1353
  }
1354
1354
  }
1355
1355
  class OperationOutcomeError extends Error {
1356
- constructor(outcome) {
1356
+ constructor(outcome, cause) {
1357
1357
  super(outcome?.issue?.[0].details?.text);
1358
1358
  this.outcome = outcome;
1359
+ this.cause = cause;
1359
1360
  }
1360
1361
  }
1362
+ /**
1363
+ * Normalizes an error object into an OperationOutcome.
1364
+ * @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.
1365
+ * @returns The normalized OperationOutcome.
1366
+ */
1367
+ function normalizeOperationOutcome(error) {
1368
+ if (error instanceof OperationOutcomeError) {
1369
+ return error.outcome;
1370
+ }
1371
+ if (isOperationOutcome(error)) {
1372
+ return error;
1373
+ }
1374
+ return badRequest(normalizeErrorString(error));
1375
+ }
1361
1376
  /**
1362
1377
  * Normalizes an error object into a displayable error string.
1363
1378
  * @param error The error value which could be a string, Error, OperationOutcome, or other unknown type.
@@ -6023,23 +6038,6 @@
6023
6038
  PropertyType["url"] = "url";
6024
6039
  PropertyType["uuid"] = "uuid";
6025
6040
  })(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
6041
  /**
6044
6042
  * Indexes a bundle of StructureDefinitions for faster lookup.
6045
6043
  * @param bundle A FHIR bundle StructureDefinition resources.
@@ -6087,8 +6085,16 @@
6087
6085
  }
6088
6086
  const parts = path.split('.');
6089
6087
  const typeName = buildTypeName(parts);
6090
- globalSchema.types[typeName] = createTypeSchema(typeName, structureDefinition, elementDefinition);
6091
- globalSchema.types[typeName].parentType = buildTypeName(parts.slice(0, parts.length - 1));
6088
+ let typeSchema = globalSchema.types[typeName];
6089
+ if (!typeSchema) {
6090
+ globalSchema.types[typeName] = typeSchema = {};
6091
+ }
6092
+ typeSchema.parentType = typeSchema.parentType || buildTypeName(parts.slice(0, parts.length - 1));
6093
+ typeSchema.display = typeSchema.display || typeName;
6094
+ typeSchema.structureDefinition = typeSchema.structureDefinition || structureDefinition;
6095
+ typeSchema.elementDefinition = typeSchema.elementDefinition || elementDefinition;
6096
+ typeSchema.description = typeSchema.description || elementDefinition.definition;
6097
+ typeSchema.properties = typeSchema.properties || {};
6092
6098
  }
6093
6099
  /**
6094
6100
  * Indexes PropertySchema from an ElementDefinition.
@@ -6203,15 +6209,54 @@
6203
6209
  }
6204
6210
  return components.map(capitalize).join('');
6205
6211
  }
6212
+ /**
6213
+ * Returns true if the type schema is a DomainResource.
6214
+ * @param typeSchema The type schema to check.
6215
+ * @returns True if the type schema is a DomainResource.
6216
+ */
6217
+ function isResourceType(typeSchema) {
6218
+ return typeSchema.structureDefinition?.baseDefinition === 'http://hl7.org/fhir/StructureDefinition/DomainResource';
6219
+ }
6220
+ /**
6221
+ * Returns an array of all resource types.
6222
+ * Note that this is based on globalSchema, and will only return resource types that are currently in memory.
6223
+ * @returns An array of all resource types.
6224
+ */
6225
+ function getResourceTypes() {
6226
+ const result = [];
6227
+ for (const [resourceType, typeSchema] of Object.entries(globalSchema.types)) {
6228
+ if (isResourceType(typeSchema)) {
6229
+ result.push(resourceType);
6230
+ }
6231
+ }
6232
+ return result;
6233
+ }
6234
+ /**
6235
+ * Returns the type schema for the resource type.
6236
+ * @param resourceType The resource type.
6237
+ * @returns The type schema for the resource type.
6238
+ */
6239
+ function getResourceTypeSchema(resourceType) {
6240
+ return globalSchema.types[resourceType];
6241
+ }
6242
+ /**
6243
+ * Returns the search parameters for the resource type indexed by search code.
6244
+ * @param resourceType The resource type.
6245
+ * @returns The search parameters for the resource type indexed by search code.
6246
+ */
6247
+ function getSearchParameters(resourceType) {
6248
+ return globalSchema.types[resourceType].searchParams;
6249
+ }
6250
+ /**
6251
+ * Returns a human friendly display name for a FHIR element definition path.
6252
+ * @param path The FHIR element definition path.
6253
+ * @returns The best guess of the display name.
6254
+ */
6206
6255
  function getPropertyDisplayName(path) {
6207
6256
  // Get the property name, which is the remainder after the last period
6208
6257
  // For example, for path "Patient.birthDate"
6209
6258
  // the property name is "birthDate"
6210
6259
  const propertyName = path.replaceAll('[x]', '').split('.').pop();
6211
- // Special case for ID
6212
- if (propertyName === 'id') {
6213
- return 'ID';
6214
- }
6215
6260
  // Split by capital letters
6216
6261
  // Capitalize the first letter of each word
6217
6262
  // Join together with spaces in between
@@ -6220,11 +6265,19 @@
6220
6265
  // the display name is "Birth Date".
6221
6266
  return propertyName
6222
6267
  .split(/(?=[A-Z])/)
6223
- .map(capitalize)
6268
+ .map(capitalizeDisplayWord)
6224
6269
  .join(' ')
6225
6270
  .replace('_', ' ')
6226
6271
  .replace(/\s+/g, ' ');
6227
6272
  }
6273
+ const capitalizedWords = new Set(['ID', 'PKCE', 'JWKS', 'URI', 'URL']);
6274
+ function capitalizeDisplayWord(word) {
6275
+ const upper = word.toUpperCase();
6276
+ if (capitalizedWords.has(upper)) {
6277
+ return upper;
6278
+ }
6279
+ return upper.charAt(0) + word.slice(1);
6280
+ }
6228
6281
  /**
6229
6282
  * Returns an element definition by type and property name.
6230
6283
  * Handles content references.
@@ -6259,7 +6312,7 @@
6259
6312
  // PKCE auth based on:
6260
6313
  // https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
6261
6314
  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";
6315
+ const MEDPLUM_VERSION = "2.0.5-886af1d8";
6263
6316
  const DEFAULT_BASE_URL = 'https://api.medplum.com/';
6264
6317
  const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
6265
6318
  const DEFAULT_CACHE_TIME = 60000; // 60 seconds
@@ -6270,7 +6323,7 @@
6270
6323
  /**
6271
6324
  * The MedplumClient class provides a client for the Medplum FHIR server.
6272
6325
  *
6273
- * The client can be used in the browser, in a NodeJS application, or in a Medplum Bot.
6326
+ * The client can be used in the browser, in a Node.js application, or in a Medplum Bot.
6274
6327
  *
6275
6328
  * The client provides helpful methods for common operations such as:
6276
6329
  * 1) Authenticating
@@ -7651,6 +7704,7 @@
7651
7704
  /**
7652
7705
  * Starts a new PKCE flow.
7653
7706
  * These PKCE values are stateful, and must survive redirects and page refreshes.
7707
+ * @category Authentication
7654
7708
  */
7655
7709
  async startPkce() {
7656
7710
  const pkceState = getRandomString();
@@ -7666,6 +7720,7 @@
7666
7720
  * Processes an OAuth authorization code.
7667
7721
  * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
7668
7722
  * @param code The authorization code received by URL parameter.
7723
+ * @category Authentication
7669
7724
  */
7670
7725
  processCode(code) {
7671
7726
  const formBody = new URLSearchParams();
@@ -7673,9 +7728,11 @@
7673
7728
  formBody.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
7674
7729
  formBody.set('code', code);
7675
7730
  formBody.set('redirect_uri', getWindowOrigin());
7676
- const codeVerifier = sessionStorage.getItem('codeVerifier');
7677
- if (codeVerifier) {
7678
- formBody.set('code_verifier', codeVerifier);
7731
+ if (typeof sessionStorage !== 'undefined') {
7732
+ const codeVerifier = sessionStorage.getItem('codeVerifier');
7733
+ if (codeVerifier) {
7734
+ formBody.set('code_verifier', codeVerifier);
7735
+ }
7679
7736
  }
7680
7737
  return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_fetchTokens).call(this, formBody);
7681
7738
  }
@@ -7770,7 +7827,7 @@
7770
7827
  throw err;
7771
7828
  }
7772
7829
  if (response.status >= 400) {
7773
- throw obj;
7830
+ throw new OperationOutcomeError(normalizeOperationOutcome(obj));
7774
7831
  }
7775
7832
  return obj;
7776
7833
  }, _MedplumClient_fetchWithRetry = async function _MedplumClient_fetchWithRetry(url, options) {
@@ -7821,7 +7878,7 @@
7821
7878
  const entry = entries[i];
7822
7879
  const responseEntry = response.entry?.[i];
7823
7880
  if (responseEntry?.response?.outcome && !isOk(responseEntry.response.outcome)) {
7824
- entry.reject(responseEntry.response.outcome);
7881
+ entry.reject(new OperationOutcomeError(responseEntry.response.outcome));
7825
7882
  }
7826
7883
  else {
7827
7884
  entry.resolve(responseEntry?.resource);
@@ -7965,14 +8022,6 @@
7965
8022
  // Silently ignore if this environment does not support storage events
7966
8023
  }
7967
8024
  };
7968
- /**
7969
- * Returns the current window if available.
7970
- * All access to the current window should use this to support SSR such as Next.js.
7971
- * @returns The current window or undefined if not available.
7972
- */
7973
- function getWindow() {
7974
- return typeof window === 'undefined' ? undefined : window;
7975
- }
7976
8025
  /**
7977
8026
  * Returns the default fetch method.
7978
8027
  * The default fetch is currently only available in browser environments.
@@ -7980,19 +8029,20 @@
7980
8029
  * @returns The default fetch function for the current environment.
7981
8030
  */
7982
8031
  function getDefaultFetch() {
7983
- const window = getWindow();
7984
- if (!window) {
8032
+ if (!globalThis.fetch) {
7985
8033
  throw new Error('Fetch not available in this environment');
7986
8034
  }
7987
- return window.fetch.bind(window);
8035
+ return globalThis.fetch.bind(globalThis);
7988
8036
  }
7989
8037
  /**
7990
8038
  * Returns the base URL for the current page.
7991
8039
  * @category HTTP
7992
8040
  */
7993
8041
  function getWindowOrigin() {
7994
- const window = getWindow();
7995
- return window ? window.location.protocol + '//' + window.location.host + '/' : '';
8042
+ if (typeof window === 'undefined') {
8043
+ return '';
8044
+ }
8045
+ return window.location.protocol + '//' + window.location.host + '/';
7996
8046
  }
7997
8047
  function ensureTrailingSlash(url) {
7998
8048
  if (!url) {
@@ -10914,6 +10964,160 @@
10914
10964
  }
10915
10965
  }
10916
10966
 
10967
+ exports.SearchParameterType = void 0;
10968
+ (function (SearchParameterType) {
10969
+ SearchParameterType["BOOLEAN"] = "BOOLEAN";
10970
+ SearchParameterType["NUMBER"] = "NUMBER";
10971
+ SearchParameterType["QUANTITY"] = "QUANTITY";
10972
+ SearchParameterType["TEXT"] = "TEXT";
10973
+ SearchParameterType["REFERENCE"] = "REFERENCE";
10974
+ SearchParameterType["DATE"] = "DATE";
10975
+ SearchParameterType["DATETIME"] = "DATETIME";
10976
+ SearchParameterType["PERIOD"] = "PERIOD";
10977
+ })(exports.SearchParameterType || (exports.SearchParameterType = {}));
10978
+ /**
10979
+ * Returns the type details of a SearchParameter.
10980
+ *
10981
+ * The SearchParameter resource has a "type" parameter, but that is missing some critical information.
10982
+ *
10983
+ * For example:
10984
+ * 1) The "date" type includes "date", "datetime", and "period".
10985
+ * 2) The "token" type includes enums and booleans.
10986
+ * 3) Arrays/multiple values are not reflected at all.
10987
+ *
10988
+ * @param resourceType The root resource type.
10989
+ * @param searchParam The search parameter.
10990
+ * @returns The search parameter type details.
10991
+ */
10992
+ function getSearchParameterDetails(resourceType, searchParam) {
10993
+ let result = globalSchema.types[resourceType]?.searchParamsDetails?.[searchParam.code];
10994
+ if (!result) {
10995
+ result = buildSearchParamterDetails(resourceType, searchParam);
10996
+ }
10997
+ return result;
10998
+ }
10999
+ function setSearchParamterDetails(resourceType, code, details) {
11000
+ const typeSchema = globalSchema.types[resourceType];
11001
+ if (!typeSchema.searchParamsDetails) {
11002
+ typeSchema.searchParamsDetails = {};
11003
+ }
11004
+ typeSchema.searchParamsDetails[code] = details;
11005
+ }
11006
+ function buildSearchParamterDetails(resourceType, searchParam) {
11007
+ const code = searchParam.code;
11008
+ const columnName = convertCodeToColumnName(code);
11009
+ const expression = getExpressionForResourceType(resourceType, searchParam.expression)?.split('.');
11010
+ if (!expression) {
11011
+ // This happens on compound types
11012
+ // In the future, explore returning multiple column definitions
11013
+ return { columnName, type: exports.SearchParameterType.TEXT };
11014
+ }
11015
+ const defaultType = getSearchParameterType(searchParam);
11016
+ let baseType = resourceType;
11017
+ let elementDefinition = undefined;
11018
+ let propertyType = undefined;
11019
+ let array = false;
11020
+ for (let i = 1; i < expression.length; i++) {
11021
+ const propertyName = expression[i];
11022
+ elementDefinition = getElementDefinition(baseType, propertyName);
11023
+ if (!elementDefinition) {
11024
+ throw new Error(`Element definition not found for ${resourceType} ${searchParam.code}`);
11025
+ }
11026
+ if (elementDefinition.max === '*') {
11027
+ array = true;
11028
+ }
11029
+ propertyType = elementDefinition.type?.[0].code;
11030
+ if (!propertyType) {
11031
+ // This happens when one of parent properties uses contentReference
11032
+ // In the future, explore following the reference
11033
+ return { columnName, type: defaultType, array };
11034
+ }
11035
+ if (i < expression.length - 1) {
11036
+ if (isBackboneElement(propertyType)) {
11037
+ baseType = buildTypeName(elementDefinition.path?.split('.'));
11038
+ }
11039
+ else {
11040
+ baseType = propertyType;
11041
+ }
11042
+ }
11043
+ }
11044
+ const type = getSearchParameterType(searchParam, propertyType);
11045
+ const result = { columnName, type, elementDefinition, array };
11046
+ setSearchParamterDetails(resourceType, code, result);
11047
+ return result;
11048
+ }
11049
+ function isBackboneElement(propertyType) {
11050
+ return propertyType === 'Element' || propertyType === 'BackboneElement';
11051
+ }
11052
+ /**
11053
+ * Converts a hyphen-delimited code to camelCase string.
11054
+ * @param code The search parameter code.
11055
+ * @returns The SQL column name.
11056
+ */
11057
+ function convertCodeToColumnName(code) {
11058
+ return code.split('-').reduce((result, word, index) => result + (index ? capitalize(word) : word), '');
11059
+ }
11060
+ function getSearchParameterType(searchParam, propertyType) {
11061
+ let type = exports.SearchParameterType.TEXT;
11062
+ switch (searchParam.type) {
11063
+ case 'date':
11064
+ if (propertyType === exports.PropertyType.dateTime || propertyType === exports.PropertyType.instant) {
11065
+ type = exports.SearchParameterType.DATETIME;
11066
+ }
11067
+ else {
11068
+ type = exports.SearchParameterType.DATE;
11069
+ }
11070
+ break;
11071
+ case 'number':
11072
+ type = exports.SearchParameterType.NUMBER;
11073
+ break;
11074
+ case 'quantity':
11075
+ type = exports.SearchParameterType.QUANTITY;
11076
+ break;
11077
+ case 'reference':
11078
+ type = exports.SearchParameterType.REFERENCE;
11079
+ break;
11080
+ case 'token':
11081
+ if (propertyType === 'boolean') {
11082
+ type = exports.SearchParameterType.BOOLEAN;
11083
+ }
11084
+ break;
11085
+ }
11086
+ return type;
11087
+ }
11088
+ function getExpressionForResourceType(resourceType, expression) {
11089
+ const expressions = expression.split(' | ');
11090
+ for (const e of expressions) {
11091
+ if (isIgnoredExpression(e)) {
11092
+ continue;
11093
+ }
11094
+ const simplified = simplifyExpression(e);
11095
+ if (simplified.startsWith(resourceType + '.')) {
11096
+ return simplified;
11097
+ }
11098
+ }
11099
+ return undefined;
11100
+ }
11101
+ function isIgnoredExpression(input) {
11102
+ return input.includes(' as Period') || input.includes(' as SampledDate');
11103
+ }
11104
+ function simplifyExpression(input) {
11105
+ let result = input.trim();
11106
+ if (result.startsWith('(') && result.endsWith(')')) {
11107
+ result = result.substring(1, result.length - 1);
11108
+ }
11109
+ if (result.includes('[0]')) {
11110
+ result = result.replaceAll('[0]', '');
11111
+ }
11112
+ const stopStrings = [' != ', ' as ', '.as(', '.exists(', '.where('];
11113
+ for (const stopString of stopStrings) {
11114
+ if (result.includes(stopString)) {
11115
+ result = result.substring(0, result.indexOf(stopString));
11116
+ }
11117
+ }
11118
+ return result;
11119
+ }
11120
+
10917
11121
  const DEFAULT_SEARCH_COUNT = 20;
10918
11122
  /**
10919
11123
  * Search operators.
@@ -11113,168 +11317,9 @@
11113
11317
  return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;
11114
11318
  }
11115
11319
  function formatSortRules(sortRules) {
11116
- if (!sortRules || sortRules.length === 0) {
11117
- return '';
11118
- }
11119
11320
  return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
11120
11321
  }
11121
11322
 
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
11323
  /**
11279
11324
  * Determines if the resource matches the search request.
11280
11325
  * @param resource The resource that was created or updated.
@@ -11349,7 +11394,7 @@
11349
11394
  return matchesBooleanFilter(resource, filter, searchParam);
11350
11395
  }
11351
11396
  else {
11352
- return matchesStringFilter(resource, filter, searchParam);
11397
+ return matchesStringFilter(resource, filter, searchParam, true);
11353
11398
  }
11354
11399
  }
11355
11400
  function matchesBooleanFilter(resource, filter, searchParam) {
@@ -11358,13 +11403,13 @@
11358
11403
  const result = values.includes(expected);
11359
11404
  return isNegated(filter.operator) ? !result : result;
11360
11405
  }
11361
- function matchesStringFilter(resource, filter, searchParam) {
11406
+ function matchesStringFilter(resource, filter, searchParam, asToken) {
11362
11407
  const resourceValues = evalFhirPath(searchParam.expression, resource);
11363
11408
  const filterValues = filter.value.split(',');
11364
11409
  const negated = isNegated(filter.operator);
11365
11410
  for (const resourceValue of resourceValues) {
11366
11411
  for (const filterValue of filterValues) {
11367
- const match = matchesStringValue(resourceValue, filter.operator, filterValue);
11412
+ const match = matchesStringValue(resourceValue, filter.operator, filterValue, asToken);
11368
11413
  if (match) {
11369
11414
  return !negated;
11370
11415
  }
@@ -11374,7 +11419,12 @@
11374
11419
  // If "equals" and no matches, then return false
11375
11420
  return negated;
11376
11421
  }
11377
- function matchesStringValue(resourceValue, operator, filterValue) {
11422
+ function matchesStringValue(resourceValue, operator, filterValue, asToken) {
11423
+ if (asToken && filterValue.includes('|')) {
11424
+ const [system, code] = filterValue.split('|');
11425
+ return (matchesStringValue(resourceValue, operator, system, false) &&
11426
+ (!code || matchesStringValue(resourceValue, operator, code, false)));
11427
+ }
11378
11428
  let str = '';
11379
11429
  if (resourceValue) {
11380
11430
  if (typeof resourceValue === 'string') {
@@ -11424,6 +11474,218 @@
11424
11474
  return operator === exports.Operator.NOT_EQUALS || operator === exports.Operator.NOT;
11425
11475
  }
11426
11476
 
11477
+ var _SearchParser_instances, _SearchParser_parseKeyValue, _SearchParser_parseSortRule, _SearchParser_parseParameter, _SearchParser_parsePrefixType, _SearchParser_parseModifierType, _SearchParser_parseReference, _SearchParser_parseQuantity, _SearchParser_parseUnknownParameter;
11478
+ /**
11479
+ * Parses a FHIR search query.
11480
+ * See: https://www.hl7.org/fhir/search.html
11481
+ */
11482
+ /**
11483
+ * For the ordered parameter types of number, date, and quantity,
11484
+ * a prefix to the parameter value may be used to control the nature
11485
+ * of the matching.
11486
+ * See: https://www.hl7.org/fhir/search.html#prefix
11487
+ */
11488
+ const prefixMap = {
11489
+ eq: exports.Operator.EQUALS,
11490
+ ne: exports.Operator.NOT_EQUALS,
11491
+ lt: exports.Operator.LESS_THAN,
11492
+ le: exports.Operator.LESS_THAN_OR_EQUALS,
11493
+ gt: exports.Operator.GREATER_THAN,
11494
+ ge: exports.Operator.GREATER_THAN_OR_EQUALS,
11495
+ sa: exports.Operator.STARTS_AFTER,
11496
+ eb: exports.Operator.ENDS_BEFORE,
11497
+ ap: exports.Operator.APPROXIMATELY,
11498
+ };
11499
+ /**
11500
+ * Parameter names may specify a modifier as a suffix.
11501
+ * The modifiers are separated from the parameter name by a colon.
11502
+ * See: https://www.hl7.org/fhir/search.html#modifiers
11503
+ */
11504
+ const modifierMap = {
11505
+ contains: exports.Operator.CONTAINS,
11506
+ exact: exports.Operator.EXACT,
11507
+ above: exports.Operator.ABOVE,
11508
+ below: exports.Operator.BELOW,
11509
+ text: exports.Operator.TEXT,
11510
+ not: exports.Operator.NOT,
11511
+ in: exports.Operator.IN,
11512
+ 'not-in': exports.Operator.NOT_IN,
11513
+ 'of-type': exports.Operator.OF_TYPE,
11514
+ };
11515
+ /**
11516
+ * Parses a search URL into a search request.
11517
+ * @param resourceType The FHIR resource type.
11518
+ * @param query The collection of query string parameters.
11519
+ * @returns A parsed SearchRequest.
11520
+ */
11521
+ function parseSearchRequest(resourceType, query) {
11522
+ return new SearchParser(resourceType, query);
11523
+ }
11524
+ /**
11525
+ * Parses a search URL into a search request.
11526
+ * @param url The search URL.
11527
+ * @returns A parsed SearchRequest.
11528
+ */
11529
+ function parseSearchUrl(url) {
11530
+ const resourceType = url.pathname.split('/').pop();
11531
+ return new SearchParser(resourceType, Object.fromEntries(url.searchParams.entries()));
11532
+ }
11533
+ class SearchParser {
11534
+ constructor(resourceType, query) {
11535
+ _SearchParser_instances.add(this);
11536
+ this.resourceType = resourceType;
11537
+ this.filters = [];
11538
+ this.sortRules = [];
11539
+ for (const [key, value] of Object.entries(query)) {
11540
+ if (Array.isArray(value)) {
11541
+ value.forEach((element) => __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseKeyValue).call(this, key, element));
11542
+ }
11543
+ else {
11544
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseKeyValue).call(this, key, value ?? '');
11545
+ }
11546
+ }
11547
+ }
11548
+ }
11549
+ _SearchParser_instances = new WeakSet(), _SearchParser_parseKeyValue = function _SearchParser_parseKeyValue(key, value) {
11550
+ let code;
11551
+ let modifier;
11552
+ const colonIndex = key.indexOf(':');
11553
+ if (colonIndex >= 0) {
11554
+ code = key.substring(0, colonIndex);
11555
+ modifier = key.substring(colonIndex + 1);
11556
+ }
11557
+ else {
11558
+ code = key;
11559
+ modifier = '';
11560
+ }
11561
+ switch (code) {
11562
+ case '_sort':
11563
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseSortRule).call(this, value);
11564
+ break;
11565
+ case '_count':
11566
+ this.count = parseInt(value);
11567
+ break;
11568
+ case '_offset':
11569
+ this.offset = parseInt(value);
11570
+ break;
11571
+ case '_total':
11572
+ this.total = value;
11573
+ break;
11574
+ case '_summary':
11575
+ this.total = 'estimate';
11576
+ this.count = 0;
11577
+ break;
11578
+ case '_revinclude':
11579
+ this.revInclude = value;
11580
+ break;
11581
+ default: {
11582
+ const param = globalSchema.types[this.resourceType]?.searchParams?.[code];
11583
+ if (param) {
11584
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseParameter).call(this, param, modifier, value);
11585
+ }
11586
+ else {
11587
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseUnknownParameter).call(this, code, modifier, value);
11588
+ }
11589
+ }
11590
+ }
11591
+ }, _SearchParser_parseSortRule = function _SearchParser_parseSortRule(value) {
11592
+ for (const field of value.split(',')) {
11593
+ let code;
11594
+ let descending = false;
11595
+ if (field.startsWith('-')) {
11596
+ code = field.substring(1);
11597
+ descending = true;
11598
+ }
11599
+ else {
11600
+ code = field;
11601
+ }
11602
+ this.sortRules.push({ code, descending });
11603
+ }
11604
+ }, _SearchParser_parseParameter = function _SearchParser_parseParameter(searchParam, modifier, value) {
11605
+ if (modifier === 'missing') {
11606
+ this.filters.push({
11607
+ code: searchParam.code,
11608
+ operator: exports.Operator.MISSING,
11609
+ value,
11610
+ });
11611
+ return;
11612
+ }
11613
+ switch (searchParam.type) {
11614
+ case 'number':
11615
+ case 'date':
11616
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parsePrefixType).call(this, searchParam, value);
11617
+ break;
11618
+ case 'string':
11619
+ case 'token':
11620
+ case 'uri':
11621
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseModifierType).call(this, searchParam, modifier, value);
11622
+ break;
11623
+ case 'reference':
11624
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseReference).call(this, searchParam, value);
11625
+ break;
11626
+ case 'quantity':
11627
+ __classPrivateFieldGet(this, _SearchParser_instances, "m", _SearchParser_parseQuantity).call(this, searchParam, value);
11628
+ break;
11629
+ }
11630
+ }, _SearchParser_parsePrefixType = function _SearchParser_parsePrefixType(param, input) {
11631
+ const { operator, value } = parsePrefix(input);
11632
+ this.filters.push({
11633
+ code: param.code,
11634
+ operator,
11635
+ value,
11636
+ });
11637
+ }, _SearchParser_parseModifierType = function _SearchParser_parseModifierType(param, modifier, value) {
11638
+ this.filters.push({
11639
+ code: param.code,
11640
+ operator: parseModifier(modifier),
11641
+ value,
11642
+ });
11643
+ }, _SearchParser_parseReference = function _SearchParser_parseReference(param, value) {
11644
+ this.filters.push({
11645
+ code: param.code,
11646
+ operator: exports.Operator.EQUALS,
11647
+ value: value,
11648
+ });
11649
+ }, _SearchParser_parseQuantity = function _SearchParser_parseQuantity(param, input) {
11650
+ const [prefixNumber, unitSystem, unitCode] = input.split('|');
11651
+ const { operator, value } = parsePrefix(prefixNumber);
11652
+ this.filters.push({
11653
+ code: param.code,
11654
+ operator,
11655
+ value,
11656
+ unitSystem,
11657
+ unitCode,
11658
+ });
11659
+ }, _SearchParser_parseUnknownParameter = function _SearchParser_parseUnknownParameter(code, modifier, value) {
11660
+ let operator = exports.Operator.EQUALS;
11661
+ if (modifier) {
11662
+ operator = modifier;
11663
+ }
11664
+ else {
11665
+ for (const prefix of Object.keys(prefixMap)) {
11666
+ if (value.match(new RegExp('^' + prefix + '\\d'))) {
11667
+ operator = prefix;
11668
+ value = value.substring(prefix.length);
11669
+ }
11670
+ }
11671
+ }
11672
+ this.filters.push({
11673
+ code,
11674
+ operator,
11675
+ value,
11676
+ });
11677
+ };
11678
+ function parsePrefix(input) {
11679
+ const prefix = input.substring(0, 2);
11680
+ if (prefix in prefixMap) {
11681
+ return { operator: prefixMap[prefix], value: input.substring(2) };
11682
+ }
11683
+ return { operator: exports.Operator.EQUALS, value: input };
11684
+ }
11685
+ function parseModifier(modifier) {
11686
+ return modifierMap[modifier] || exports.Operator.EQUALS;
11687
+ }
11688
+
11427
11689
  exports.AndAtom = AndAtom;
11428
11690
  exports.ArithemticOperatorAtom = ArithemticOperatorAtom;
11429
11691
  exports.AsAtom = AsAtom;
@@ -11469,7 +11731,6 @@
11469
11731
  exports.calculateAgeString = calculateAgeString;
11470
11732
  exports.capitalize = capitalize;
11471
11733
  exports.createReference = createReference;
11472
- exports.createSchema = createSchema;
11473
11734
  exports.created = created;
11474
11735
  exports.deepClone = deepClone;
11475
11736
  exports.deepEquals = deepEquals$1;
@@ -11513,7 +11774,10 @@
11513
11774
  exports.getPropertyDisplayName = getPropertyDisplayName;
11514
11775
  exports.getQuestionnaireAnswers = getQuestionnaireAnswers;
11515
11776
  exports.getReferenceString = getReferenceString;
11777
+ exports.getResourceTypeSchema = getResourceTypeSchema;
11778
+ exports.getResourceTypes = getResourceTypes;
11516
11779
  exports.getSearchParameterDetails = getSearchParameterDetails;
11780
+ exports.getSearchParameters = getSearchParameters;
11517
11781
  exports.getStatus = getStatus;
11518
11782
  exports.getTypedPropertyValue = getTypedPropertyValue;
11519
11783
  exports.globalSchema = globalSchema;
@@ -11534,17 +11798,21 @@
11534
11798
  exports.isProfileResource = isProfileResource;
11535
11799
  exports.isQuantity = isQuantity;
11536
11800
  exports.isQuantityEquivalent = isQuantityEquivalent;
11801
+ exports.isResourceType = isResourceType;
11537
11802
  exports.isStringArray = isStringArray;
11538
11803
  exports.isUUID = isUUID;
11539
11804
  exports.isValidDate = isValidDate;
11540
11805
  exports.matchesRange = matchesRange;
11541
11806
  exports.matchesSearchRequest = matchesSearchRequest;
11542
11807
  exports.normalizeErrorString = normalizeErrorString;
11808
+ exports.normalizeOperationOutcome = normalizeOperationOutcome;
11543
11809
  exports.notFound = notFound;
11544
11810
  exports.notModified = notModified;
11545
11811
  exports.parseFhirPath = parseFhirPath;
11546
11812
  exports.parseJWTPayload = parseJWTPayload;
11547
11813
  exports.parseSearchDefinition = parseSearchDefinition;
11814
+ exports.parseSearchRequest = parseSearchRequest;
11815
+ exports.parseSearchUrl = parseSearchUrl;
11548
11816
  exports.preciseEquals = preciseEquals;
11549
11817
  exports.preciseGreaterThan = preciseGreaterThan;
11550
11818
  exports.preciseGreaterThanOrEquals = preciseGreaterThanOrEquals;