@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.
- package/dist/cjs/client.d.ts +5 -3
- package/dist/cjs/index.cjs +443 -189
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +4 -3
- package/dist/cjs/index.min.cjs +1 -1
- package/dist/cjs/{searchparams.d.ts → search/details.d.ts} +0 -0
- package/dist/cjs/{match.d.ts → search/match.d.ts} +0 -0
- package/dist/cjs/search/parse.d.ts +17 -0
- package/dist/cjs/{search.d.ts → search/search.d.ts} +0 -0
- package/dist/cjs/types.d.ts +30 -7
- package/dist/esm/client.d.ts +5 -3
- package/dist/esm/client.mjs +9 -5
- package/dist/esm/client.mjs.map +1 -1
- package/dist/esm/index.d.ts +4 -3
- package/dist/esm/index.min.mjs +1 -1
- package/dist/esm/index.mjs +5 -4
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/{searchparams.d.ts → search/details.d.ts} +0 -0
- package/dist/esm/{searchparams.mjs → search/details.mjs} +9 -11
- package/dist/esm/search/details.mjs.map +1 -0
- package/dist/esm/{match.d.ts → search/match.d.ts} +0 -0
- package/dist/esm/{match.mjs → search/match.mjs} +7 -7
- package/dist/esm/search/match.mjs.map +1 -0
- package/dist/esm/search/parse.d.ts +17 -0
- package/dist/esm/search/parse.mjs +218 -0
- package/dist/esm/search/parse.mjs.map +1 -0
- package/dist/esm/{search.d.ts → search/search.d.ts} +0 -0
- package/dist/esm/{search.mjs → search/search.mjs} +0 -3
- package/dist/esm/search/search.mjs.map +1 -0
- package/dist/esm/types.d.ts +30 -7
- package/dist/esm/types.mjs +63 -25
- package/dist/esm/types.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/esm/match.mjs.map +0 -1
- package/dist/esm/search.mjs.map +0 -1
- package/dist/esm/searchparams.mjs.map +0 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -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]
|
|
6091
|
-
|
|
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(
|
|
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.
|
|
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
|
|
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
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
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;
|