Socket
Socket
Sign inDemoInstall

@medplum/core

Package Overview
Dependencies
Maintainers
1
Versions
186
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@medplum/core - npm Package Compare versions

Comparing version 0.2.2 to 0.3.0

1

dist/cache.d.ts

@@ -9,2 +9,3 @@ /**

constructor(max?: number);
clear(): void;
get(key: string): T | undefined;

@@ -11,0 +12,0 @@ set(key: string, val: T): void;

@@ -13,2 +13,5 @@ "use strict";

}
clear() {
this.cache.clear();
}
get(key) {

@@ -15,0 +18,0 @@ const item = this.cache.get(key);

120

dist/client.d.ts

@@ -0,3 +1,3 @@

import { Binary, Bundle, Project, ProjectMembership, Reference, Resource, ValueSet } from '@medplum/fhirtypes';
import { EventTarget } from './eventtarget';
import { Binary, Bundle, Project, Reference, Resource, ValueSet } from './fhir';
import { SearchRequest } from './search';

@@ -9,5 +9,6 @@ import { IndexedStructureDefinition } from './types';

* The client ID.
* Required.
* Optional. Default is to defer to the server to use the default client.
* Use this to use a specific client for SMART-on-FHIR.
*/
clientId: string;
clientId?: string;
/**

@@ -44,8 +45,2 @@ * Base server URL.

/**
* Number of blob URLs to store in the cache.
* Optional. Default value is 100.
* Consider using this for performance of displaying Patient or Practitioner resources.
*/
blobCacheSize?: number;
/**
* Optional fetch implementation.

@@ -66,15 +61,9 @@ * Optional. Default is window.fetch.

}
interface LoginResponse {
project: Project;
profile: ProfileResource;
accessToken: string;
refreshToken: string;
}
export interface RegisterRequest {
firstName: string;
lastName: string;
projectName: string;
email: string;
password: string;
remember?: boolean;
readonly firstName: string;
readonly lastName: string;
readonly projectName: string;
readonly email: string;
readonly password: string;
readonly remember?: boolean;
}

@@ -85,2 +74,30 @@ export interface GoogleCredentialResponse {

}
export interface LoginAuthenticationResponse {
readonly login: string;
readonly code?: string;
readonly memberships?: ProjectMembership[];
}
export interface LoginProfileResponse {
readonly login: string;
readonly scope: string;
}
export interface LoginScopeResponse {
readonly login: string;
readonly code: string;
}
export interface LoginState {
readonly project: Reference<Project>;
readonly profile: Reference<ProfileResource>;
readonly accessToken: string;
readonly refreshToken: string;
}
export interface TokenResponse {
readonly token_type: string;
readonly id_token: string;
readonly access_token: string;
readonly refresh_token: string;
readonly expires_in: number;
readonly project: Reference<Project>;
readonly profile: Reference<ProfileResource>;
}
export declare class MedplumClient extends EventTarget {

@@ -91,3 +108,2 @@ private readonly fetch;

private readonly resourceCache;
private readonly blobUrlCache;
private readonly baseUrl;

@@ -99,3 +115,6 @@ private readonly clientId;

private readonly onUnauthenticated?;
private refreshPromise?;
private activeLogin?;
private profile?;
private loading;
constructor(options: MedplumClientOptions);

@@ -106,21 +125,20 @@ /**

clear(): void;
get(url: string, blob?: boolean): Promise<any>;
get(url: string): Promise<any>;
post(url: string, body: any, contentType?: string): Promise<any>;
put(url: string, body: any, contentType?: string): Promise<any>;
delete(url: string): Promise<any>;
/**
* Tries to register a new user.
* @param request The registration request.
* @returns Promise to the user profile resource.
* @returns Promise to the authentication response.
*/
register(request: RegisterRequest): Promise<ProfileResource>;
register(request: RegisterRequest): Promise<void>;
/**
* Tries to sign in with email and password.
* @param email The user email address.
* @param password The user password.
* @param role The login role.
* @param scope The OAuth2 login scope.
* @param remember Optional flag to "remember" to generate a refresh token and persist in local storage.
* @returns Promise to the user profile resource.
* Initiates a user login flow.
* @param email The email address of the user.
* @param password The password of the user.
* @param remember Optional flag to remember the user.
* @returns Promise to the authentication response.
*/
signIn(email: string, password: string, role: string, scope: string, remember?: boolean): Promise<ProfileResource>;
startLogin(email: string, password: string, remember?: boolean): Promise<LoginAuthenticationResponse>;
/**

@@ -131,13 +149,6 @@ * Tries to sign in with Google authentication.

* @param googleResponse The Google credential response.
* @returns Promise to the user profile resource.
* @returns Promise to the authentication response.
*/
signInWithGoogle(googleResponse: GoogleCredentialResponse): Promise<ProfileResource>;
startGoogleLogin(googleResponse: GoogleCredentialResponse): Promise<LoginAuthenticationResponse>;
/**
* Handles a login response.
* This can be used for both "register" and "signIn".
* @param response The login response.
* @returns The user profile.
*/
private handleLoginResponse;
/**
* Signs out locally.

@@ -175,6 +186,2 @@ * Does not invalidate tokens with the server.

readPatientEverything(id: string): Promise<Bundle>;
readBlob(url: string): Promise<Blob>;
readBlobAsObjectUrl(url: string): Promise<string>;
readCachedBlobAsObjectUrl(url: string): Promise<string>;
readBinary(id: string): Promise<Blob>;
create<T extends Resource>(resource: T): Promise<T>;

@@ -184,9 +191,12 @@ createBinary(data: any, contentType: string): Promise<Binary>;

patch(resourceType: string, id: string, operations: any): Promise<any>;
deleteResource(resourceType: string, id: string): Promise<any>;
graphql(gql: any): Promise<any>;
subscribe(criteria: string, handler: (e: Resource) => void): Promise<EventSource>;
getActiveLogin(): LoginResponse | undefined;
setActiveLogin(login: LoginResponse): void;
getLogins(): LoginResponse[];
addLogin(newLogin: LoginResponse): void;
getActiveLogin(): LoginState | undefined;
setActiveLogin(login: LoginState): Promise<void>;
getLogins(): LoginState[];
private addLogin;
private refreshProfile;
getProfile(): ProfileResource | undefined;
isLoading(): boolean;
/**

@@ -198,3 +208,2 @@ * Makes an HTTP request.

* @param {Object=} body
* @param {boolean=} blob
*/

@@ -210,6 +219,10 @@ private request;

* @param body The body of the original request.
* @param blob Optional blob flag of the original request.
*/
private handleUnauthenticated;
/**
* Starts a new PKCE flow.
* These PKCE values are stateful, and must survive redirects and page refreshes.
*/
private startPkce;
/**
* Redirects the user to the login screen for authorization.

@@ -225,3 +238,3 @@ * Clears all auth state including local storage and session storage.

*/
private processCode;
processCode(code: string): Promise<ProfileResource>;
/**

@@ -246,2 +259,1 @@ * Tries to refresh the auth tokens.

}
export {};

@@ -25,4 +25,4 @@ "use strict";

const DEFAULT_BASE_URL = 'https://api.medplum.com/';
const DEFAULT_SCOPE = 'launch/patient openid fhirUser offline_access user/*.*';
const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
const DEFAULT_BLOB_CACHE_SIZE = 100;
const JSON_CONTENT_TYPE = 'application/json';

@@ -33,3 +33,3 @@ const FHIR_CONTENT_TYPE = 'application/fhir+json';

constructor(options) {
var _a, _b;
var _a, _b, _c;
super();

@@ -44,5 +44,2 @@ if (options.baseUrl) {

}
if (!options.clientId) {
throw new Error('Client ID cannot be empty');
}
this.fetch = options.fetch || window.fetch.bind(window);

@@ -52,5 +49,4 @@ this.storage = new storage_1.ClientStorage();

this.resourceCache = new cache_1.LRUCache((_a = options.resourceCacheSize) !== null && _a !== void 0 ? _a : DEFAULT_RESOURCE_CACHE_SIZE);
this.blobUrlCache = new cache_1.LRUCache((_b = options.blobCacheSize) !== null && _b !== void 0 ? _b : DEFAULT_BLOB_CACHE_SIZE);
this.baseUrl = options.baseUrl || DEFAULT_BASE_URL;
this.clientId = options.clientId;
this.clientId = options.clientId || '';
this.authorizeUrl = options.authorizeUrl || this.baseUrl + 'oauth2/authorize';

@@ -60,2 +56,8 @@ this.tokenUrl = options.tokenUrl || this.baseUrl + 'oauth2/token';

this.onUnauthenticated = options.onUnauthenticated;
this.activeLogin = this.storage.getObject('activeLogin');
if (!((_c = (_b = this.activeLogin) === null || _b === void 0 ? void 0 : _b.profile) === null || _c === void 0 ? void 0 : _c.reference)) {
this.clear();
}
this.loading = false;
this.refreshProfile().catch(console.log);
}

@@ -67,7 +69,10 @@ /**

this.storage.clear();
this.schema.clear();
this.resourceCache.clear();
this.activeLogin = undefined;
this.profile = undefined;
this.dispatchEvent({ type: 'change' });
}
get(url, blob) {
return this.request('GET', url, undefined, undefined, blob);
get(url) {
return this.request('GET', url);
}

@@ -80,29 +85,36 @@ post(url, body, contentType) {

}
delete(url) {
return this.request('DELETE', url);
}
/**
* Tries to register a new user.
* @param request The registration request.
* @returns Promise to the user profile resource.
* @returns Promise to the authentication response.
*/
register(request) {
return this.post('auth/register', request)
.then((response) => this.handleLoginResponse(response));
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.post('auth/register', request);
yield this.setActiveLogin(response);
});
}
/**
* Tries to sign in with email and password.
* @param email The user email address.
* @param password The user password.
* @param role The login role.
* @param scope The OAuth2 login scope.
* @param remember Optional flag to "remember" to generate a refresh token and persist in local storage.
* @returns Promise to the user profile resource.
* Initiates a user login flow.
* @param email The email address of the user.
* @param password The password of the user.
* @param remember Optional flag to remember the user.
* @returns Promise to the authentication response.
*/
signIn(email, password, role, scope, remember) {
return this.post('auth/login', {
clientId: this.clientId,
email,
password,
role,
scope,
remember: !!remember
}).then((response) => this.handleLoginResponse(response));
startLogin(email, password, remember) {
return __awaiter(this, void 0, void 0, function* () {
yield this.startPkce();
return this.post('auth/login', {
clientId: this.clientId,
scope: DEFAULT_SCOPE,
codeChallengeMethod: 'S256',
codeChallenge: this.storage.getString('codeChallenge'),
email,
password,
remember: !!remember,
});
});
}

@@ -114,19 +126,11 @@ /**

* @param googleResponse The Google credential response.
* @returns Promise to the user profile resource.
* @returns Promise to the authentication response.
*/
signInWithGoogle(googleResponse) {
return this.post('auth/google', googleResponse)
.then((loginResponse) => this.handleLoginResponse(loginResponse));
startGoogleLogin(googleResponse) {
return __awaiter(this, void 0, void 0, function* () {
yield this.startPkce();
return this.post('auth/google', googleResponse);
});
}
/**
* Handles a login response.
* This can be used for both "register" and "signIn".
* @param response The login response.
* @returns The user profile.
*/
handleLoginResponse(response) {
this.setActiveLogin(response);
return response.profile;
}
/**
* Signs out locally.

@@ -164,3 +168,3 @@ * Does not invalidate tokens with the server.

const builder = [this.baseUrl, 'fhir/R4'];
path.forEach(p => builder.push('/', encodeURIComponent(p)));
path.forEach((p) => builder.push('/', encodeURIComponent(p)));
return builder.join('');

@@ -190,4 +194,3 @@ }

const cacheKey = resourceType + '/' + id;
const promise = this.get(this.fhirUrl(resourceType, id))
.then((resource) => {
const promise = this.get(this.fhirUrl(resourceType, id)).then((resource) => {
this.resourceCache.set(cacheKey, resource);

@@ -243,7 +246,9 @@ return resource;

count: 100,
filters: [{
filters: [
{
code: 'base',
operator: search_1.Operator.EQUALS,
value: resourceType
}]
value: resourceType,
},
],
}))

@@ -254,3 +259,3 @@ .then((result) => {

typeDef.types[resourceType].searchParams = entries
.map(e => e.resource)
.map((e) => e.resource)
.sort((a, b) => { var _a, _b; return (_b = (_a = a.name) === null || _a === void 0 ? void 0 : _a.localeCompare(b.name)) !== null && _b !== void 0 ? _b : 0; });

@@ -268,22 +273,2 @@ }

}
readBlob(url) {
return this.get(url, true);
}
readBlobAsObjectUrl(url) {
const promise = this.readBlob(url)
.then(imageBlob => {
const imageUrl = URL.createObjectURL(imageBlob);
this.blobUrlCache.set(url, imageUrl);
return imageUrl;
});
this.blobUrlCache.set(url, promise);
return promise;
}
readCachedBlobAsObjectUrl(url) {
const cached = this.blobUrlCache.get(url);
return cached ? Promise.resolve(cached) : this.readBlobAsObjectUrl(url);
}
readBinary(id) {
return this.readBlob(this.fhirUrl('Binary', id));
}
create(resource) {

@@ -310,2 +295,5 @@ if (!resource.resourceType) {

}
deleteResource(resourceType, id) {
return this.delete(this.fhirUrl(resourceType, id));
}
graphql(gql) {

@@ -320,7 +308,7 @@ return this.post(this.fhirUrl('$graphql'), gql, JSON_CONTENT_TYPE);

channel: {
type: 'sse'
}
type: 'sse',
},
}).then((sub) => {
const eventSource = new EventSource(this.baseUrl + 'sse?subscription=' + encodeURIComponent(sub.id), {
withCredentials: true
withCredentials: true,
});

@@ -334,12 +322,13 @@ eventSource.onmessage = (e) => {

getActiveLogin() {
if (!this.activeLogin) {
this.activeLogin = this.storage.getObject('activeLogin');
}
return this.activeLogin;
}
setActiveLogin(login) {
this.activeLogin = login;
this.storage.setObject('activeLogin', login);
this.addLogin(login);
this.dispatchEvent({ type: 'change' });
return __awaiter(this, void 0, void 0, function* () {
this.activeLogin = login;
this.storage.setObject('activeLogin', login);
this.addLogin(login);
this.resourceCache.clear();
this.refreshPromise = undefined;
yield this.refreshProfile();
});
}

@@ -351,10 +340,25 @@ getLogins() {

addLogin(newLogin) {
const logins = this.getLogins().filter(login => { var _a, _b; return ((_a = login.profile) === null || _a === void 0 ? void 0 : _a.id) !== ((_b = newLogin.profile) === null || _b === void 0 ? void 0 : _b.id); });
const logins = this.getLogins().filter((login) => { var _a, _b; return ((_a = login.profile) === null || _a === void 0 ? void 0 : _a.reference) !== ((_b = newLogin.profile) === null || _b === void 0 ? void 0 : _b.reference); });
logins.push(newLogin);
this.storage.setObject('logins', logins);
}
getProfile() {
refreshProfile() {
var _a;
return (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.profile;
return __awaiter(this, void 0, void 0, function* () {
const reference = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.profile;
if (reference === null || reference === void 0 ? void 0 : reference.reference) {
this.loading = true;
this.profile = yield this.readCachedReference(reference);
this.loading = false;
this.dispatchEvent({ type: 'change' });
}
return this.profile;
});
}
getProfile() {
return this.profile;
}
isLoading() {
return this.loading;
}
/**

@@ -366,7 +370,9 @@ * Makes an HTTP request.

* @param {Object=} body
* @param {boolean=} blob
*/
request(method, url, contentType, body, blob) {
request(method, url, contentType, body) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (this.refreshPromise) {
yield this.refreshPromise;
}
if (!url.startsWith('http')) {

@@ -376,3 +382,3 @@ url = this.baseUrl + url;

const headers = {
'Content-Type': contentType || FHIR_CONTENT_TYPE
'Content-Type': contentType || FHIR_CONTENT_TYPE,
};

@@ -387,3 +393,3 @@ const accessToken = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.accessToken;

credentials: 'include',
headers
headers,
};

@@ -401,9 +407,9 @@ if (body) {

// Refresh and try again
return this.handleUnauthenticated(method, url, contentType, body, blob);
return this.handleUnauthenticated(method, url, contentType, body);
}
if (response.status === 304) {
// No change
if (response.status === 204 || response.status === 304) {
// No content or change
return undefined;
}
const obj = blob ? yield response.blob() : yield response.json();
const obj = yield response.json();
if (obj.resourceType === 'OperationOutcome' && !(0, outcomes_1.isOk)(obj)) {

@@ -423,9 +429,8 @@ return Promise.reject(new outcomes_1.OperationOutcomeError(obj));

* @param body The body of the original request.
* @param blob Optional blob flag of the original request.
*/
handleUnauthenticated(method, url, contentType, body, blob) {
handleUnauthenticated(method, url, contentType, body) {
return __awaiter(this, void 0, void 0, function* () {
return this.refresh()
.then(() => this.request(method, url, contentType, body, blob))
.catch(error => {
.then(() => this.request(method, url, contentType, body))
.catch((error) => {
this.clear();

@@ -440,2 +445,17 @@ if (this.onUnauthenticated) {

/**
* Starts a new PKCE flow.
* These PKCE values are stateful, and must survive redirects and page refreshes.
*/
startPkce() {
return __awaiter(this, void 0, void 0, function* () {
const pkceState = (0, crypto_1.getRandomString)();
this.storage.setString('pkceState', pkceState);
const codeVerifier = (0, crypto_1.getRandomString)();
this.storage.setString('codeVerifier', codeVerifier);
const arrayHash = yield (0, crypto_1.encryptSHA256)(codeVerifier);
const codeChallenge = (0, utils_1.arrayBufferToBase64)(arrayHash).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
this.storage.setString('codeChallenge', codeChallenge);
});
}
/**
* Redirects the user to the login screen for authorization.

@@ -450,19 +470,16 @@ * Clears all auth state including local storage and session storage.

}
this.clear();
const pkceState = (0, crypto_1.getRandomString)();
this.storage.setString('pkceState', pkceState);
const codeVerifier = (0, crypto_1.getRandomString)();
this.storage.setString('codeVerifier', codeVerifier);
const arrayHash = yield (0, crypto_1.encryptSHA256)(codeVerifier);
const codeChallenge = (0, utils_1.arrayBufferToBase64)(arrayHash);
this.storage.setString('codeChallenge', codeChallenge);
const scope = 'launch/patient openid fhirUser offline_access user/*.*';
this.startPkce();
window.location.assign(this.authorizeUrl +
'?response_type=code' +
'&state=' + encodeURIComponent(pkceState) +
'&client_id=' + encodeURIComponent(this.clientId) +
'&redirect_uri=' + encodeURIComponent(getBaseUrl()) +
'&scope=' + encodeURIComponent(scope) +
'&state=' +
encodeURIComponent(this.storage.getString('pkceState')) +
'&client_id=' +
encodeURIComponent(this.clientId) +
'&redirect_uri=' +
encodeURIComponent(getBaseUrl()) +
'&scope=' +
encodeURIComponent(DEFAULT_SCOPE) +
'&code_challenge_method=S256' +
'&code_challenge=' + encodeURIComponent(codeChallenge));
'&code_challenge=' +
encodeURIComponent(this.storage.getString('codeChallenge')));
});

@@ -487,6 +504,9 @@ }

return this.fetchTokens('grant_type=authorization_code' +
'&client_id=' + encodeURIComponent(this.clientId) +
'&code_verifier=' + encodeURIComponent(codeVerifier) +
'&redirect_uri=' + encodeURIComponent(getBaseUrl()) +
'&code=' + encodeURIComponent(code));
(this.clientId ? '&client_id=' + encodeURIComponent(this.clientId) : '') +
'&code_verifier=' +
encodeURIComponent(codeVerifier) +
'&redirect_uri=' +
encodeURIComponent(getBaseUrl()) +
'&code=' +
encodeURIComponent(code));
}

@@ -500,2 +520,5 @@ /**

return __awaiter(this, void 0, void 0, function* () {
if (this.refreshPromise) {
return this.refreshPromise;
}
const refreshToken = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.refreshToken;

@@ -506,5 +529,8 @@ if (!refreshToken) {

}
yield this.fetchTokens('grant_type=refresh_token' +
'&client_id=' + encodeURIComponent(this.clientId) +
'&refresh_token=' + encodeURIComponent(refreshToken));
this.refreshPromise = this.fetchTokens('grant_type=refresh_token' +
'&client_id=' +
encodeURIComponent(this.clientId) +
'&refresh_token=' +
encodeURIComponent(refreshToken));
yield this.refreshPromise;
});

@@ -520,3 +546,3 @@ }

if (!this.tokenUrl) {
throw new Error('Missing token URL');
return Promise.reject('Missing token URL');
}

@@ -526,5 +552,5 @@ return this.fetch(this.tokenUrl, {

headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formBody
body: formBody,
})
.then(response => {
.then((response) => {
if (!response.ok) {

@@ -535,4 +561,4 @@ return Promise.reject('Failed to fetch tokens');

})
.then(tokens => this.verifyTokens(tokens))
.then(() => { var _a; return (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.profile; });
.then((tokens) => this.verifyTokens(tokens))
.then(() => this.profile);
});

@@ -556,7 +582,12 @@ }

// Verify app_client_id
if (tokenPayload.client_id !== this.clientId) {
if (this.clientId && tokenPayload.client_id !== this.clientId) {
this.clear();
return Promise.reject('Token was not issued for this audience');
}
this.setActiveLogin(Object.assign(Object.assign({}, this.getActiveLogin()), { accessToken: token, refreshToken: tokens.refresh_token }));
yield this.setActiveLogin({
accessToken: token,
refreshToken: tokens.refresh_token,
project: tokens.project,
profile: tokens.profile,
});
});

@@ -563,0 +594,0 @@ }

@@ -32,3 +32,3 @@ "use strict";

if (array) {
array.forEach(listener => listener.call(this, event));
array.forEach((listener) => listener.call(this, event));
}

@@ -35,0 +35,0 @@ return !event.defaultPrevented;

@@ -1,2 +0,2 @@

import { Address, HumanName } from './fhir';
import { Address, HumanName } from '@medplum/fhirtypes';
export interface AddressFormatOptions {

@@ -3,0 +3,0 @@ all?: boolean;

export * from './client';
export * from './fhir';
export * from './fhirpath';
export * from './format';

@@ -5,0 +3,0 @@ export * from './outcomes';

@@ -14,4 +14,2 @@ "use strict";

__exportStar(require("./client"), exports);
__exportStar(require("./fhir"), exports);
__exportStar(require("./fhirpath"), exports);
__exportStar(require("./format"), exports);

@@ -18,0 +16,0 @@ __exportStar(require("./outcomes"), exports);

@@ -1,2 +0,2 @@

import { OperationOutcome } from './fhir';
import { OperationOutcome } from '@medplum/fhirtypes';
export declare const allOk: OperationOutcome;

@@ -3,0 +3,0 @@ export declare const created: OperationOutcome;

@@ -13,9 +13,11 @@ "use strict";

id: OK_ID,
issue: [{
issue: [
{
severity: 'information',
code: 'information',
details: {
text: 'All OK'
}
}]
text: 'All OK',
},
},
],
};

@@ -25,9 +27,11 @@ exports.created = {

id: CREATED_ID,
issue: [{
issue: [
{
severity: 'information',
code: 'information',
details: {
text: 'Created'
}
}]
text: 'Created',
},
},
],
};

@@ -37,9 +41,11 @@ exports.notModified = {

id: NOT_MODIFIED_ID,
issue: [{
issue: [
{
severity: 'information',
code: 'information',
details: {
text: 'Not Modified'
}
}]
text: 'Not Modified',
},
},
],
};

@@ -49,9 +55,11 @@ exports.notFound = {

id: NOT_FOUND_ID,
issue: [{
issue: [
{
severity: 'error',
code: 'not-found',
details: {
text: 'Not found'
}
}]
text: 'Not found',
},
},
],
};

@@ -61,9 +69,11 @@ exports.gone = {

id: GONE_ID,
issue: [{
issue: [
{
severity: 'error',
code: 'gone',
details: {
text: 'Gone'
}
}]
text: 'Gone',
},
},
],
};

@@ -73,9 +83,11 @@ exports.accessDenied = {

id: ACCESS_DENIED,
issue: [{
issue: [
{
severity: 'error',
code: 'access-denied',
details: {
text: 'Access Denied'
}
}]
text: 'Access Denied',
},
},
],
};

@@ -85,10 +97,12 @@ function badRequest(details, expression) {

resourceType: 'OperationOutcome',
issue: [{
issue: [
{
severity: 'error',
code: 'invalid',
details: {
text: details
text: details,
},
expression: (expression ? [expression] : undefined)
}]
expression: expression ? [expression] : undefined,
},
],
};

@@ -95,0 +109,0 @@ }

@@ -68,3 +68,3 @@ "use strict";

operator: Operator.EQUALS,
value: value
value: value,
});

@@ -79,3 +79,3 @@ }

count,
sortRules
sortRules,
};

@@ -96,3 +96,3 @@ }

if (definition.filters) {
definition.filters.forEach(filter => {
definition.filters.forEach((filter) => {
params.push(filter.code + '=' + filter.value);

@@ -121,4 +121,4 @@ });

}
return '_sort=' + sortRules.map(sr => sr.descending ? '-' + sr.code : sr.code).join(',');
return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');
}
//# sourceMappingURL=search.js.map

@@ -1,2 +0,2 @@

import { SearchParameter } from './fhir';
import { SearchParameter } from '@medplum/fhirtypes';
import { IndexedStructureDefinition } from './types';

@@ -3,0 +3,0 @@ export declare enum SearchParameterType {

@@ -79,4 +79,3 @@ "use strict";

function convertCodeToColumnName(code) {
return code.split('-')
.reduce((result, word, index) => result + (index ? (0, utils_1.capitalize)(word) : word), '');
return code.split('-').reduce((result, word, index) => result + (index ? (0, utils_1.capitalize)(word) : word), '');
}

@@ -83,0 +82,0 @@ function getSearchParameterType(searchParam, propertyType) {

@@ -1,2 +0,2 @@

import { ElementDefinition, SearchParameter, StructureDefinition } from './fhir';
import { ElementDefinition, SearchParameter, StructureDefinition } from '@medplum/fhirtypes';
/**

@@ -3,0 +3,0 @@ * List of property types.

@@ -87,3 +87,3 @@ "use strict";

output = {
types: {}
types: {},
};

@@ -94,3 +94,3 @@ }

description: structureDefinition.description,
properties: {}
properties: {},
};

@@ -100,7 +100,7 @@ const elements = (_a = structureDefinition.snapshot) === null || _a === void 0 ? void 0 : _a.element;

// Filter out any elements missing path or type
const filtered = elements.filter(e => e.path !== typeName && e.path); // && e.type && e.type.length > 0);
const filtered = elements.filter((e) => e.path !== typeName && e.path); // && e.type && e.type.length > 0);
// First pass, build types
filtered.forEach(element => indexType(output, element));
filtered.forEach((element) => indexType(output, element));
// Second pass, build properties
filtered.forEach(element => indexProperty(output, element));
filtered.forEach((element) => indexProperty(output, element));
}

@@ -131,3 +131,3 @@ return output;

parentType: buildTypeName(parts.slice(0, parts.length - 1)),
properties: {}
properties: {},
};

@@ -134,0 +134,0 @@ }

@@ -1,2 +0,2 @@

import { Patient, Practitioner, Reference, RelatedPerson, Resource } from './fhir';
import { Patient, Practitioner, Reference, RelatedPerson, Resource } from '@medplum/fhirtypes';
export declare type ProfileResource = Patient | Practitioner | RelatedPerson;

@@ -3,0 +3,0 @@ /**

@@ -13,3 +13,3 @@ "use strict";

const display = getDisplayString(resource);
return (display === reference) ? { reference } : { reference, display };
return display === reference ? { reference } : { reference, display };
}

@@ -32,5 +32,5 @@ exports.createReference = createReference;

function isProfileResource(resource) {
return resource.resourceType === 'Patient' ||
return (resource.resourceType === 'Patient' ||
resource.resourceType === 'Practitioner' ||
resource.resourceType === 'RelatedPerson';
resource.resourceType === 'RelatedPerson');
}

@@ -56,2 +56,7 @@ exports.isProfileResource = isProfileResource;

}
if (resource.resourceType === 'User') {
if (resource.email) {
return resource.email;
}
}
const simpleName = resource.name;

@@ -142,3 +147,3 @@ if (simpleName && typeof simpleName === 'string') {

function stringifyReplacer(k, v) {
return (k === '__key' || isEmpty(v)) ? undefined : v;
return k === '__key' || isEmpty(v) ? undefined : v;
}

@@ -169,4 +174,4 @@ /**

if (path === 'meta') {
keys1 = keys1.filter(k => k !== 'versionId' && k !== 'lastUpdated');
keys2 = keys2.filter(k => k !== 'versionId' && k !== 'lastUpdated');
keys1 = keys1.filter((k) => k !== 'versionId' && k !== 'lastUpdated' && k !== 'author');
keys2 = keys2.filter((k) => k !== 'versionId' && k !== 'lastUpdated' && k !== 'author');
}

@@ -173,0 +178,0 @@ if (keys1.length !== keys2.length) {

{
"name": "@medplum/core",
"version": "0.2.2",
"version": "0.3.0",
"description": "Medplum TS/JS Library",

@@ -16,8 +16,7 @@ "author": "Medplum <hello@medplum.com>",

"build": "npm run clean && tsc",
"test": "jest",
"lint": "eslint src/ --ext .ts,.tsx"
"test": "jest"
},
"files": [
"dist"
],
"devDependencies": {
"@medplum/fhirtypes": "0.3.0"
},
"main": "dist/index.js",

@@ -24,0 +23,0 @@ "types": "dist/index.d.ts",

# Medplum
Medplum is a healthcare platform that helps you quickly develop high-quality compliant applications. Medplum includes a FHIR server, React component library, and developer app.
Medplum is a healthcare platform that helps you quickly develop high-quality compliant applications. Medplum includes a FHIR server, React component library, and developer app.

@@ -11,7 +11,7 @@ # Medplum JS Client Library

* FHIR validation and operations
* FHIR client to create, read, update, delete, patch, and search
* WebSockets for realtime communication
* Evaluation of [FhirPath](https://hl7.org/fhirpath/N1/index.html)
* No external dependencies
- FHIR validation and operations
- FHIR client to create, read, update, delete, patch, and search
- WebSockets for realtime communication
- Evaluation of [FhirPath](https://hl7.org/fhirpath/N1/index.html)
- No external dependencies

@@ -30,4 +30,4 @@ ## Installation

const medplum = new MedplumClient({
baseUrl: 'https://www.example.com/fhir/R4/',
clientId: 'MY_CLIENT_ID'
baseUrl: 'https://www.example.com/fhir/R4/',
clientId: 'MY_CLIENT_ID',
});

@@ -41,3 +41,3 @@ ```

```typescript
medplum.signInWithRedirect().then(user => console.log(user));
medplum.signInWithRedirect().then((user) => console.log(user));
```

@@ -57,3 +57,3 @@

```typescript
medplum.signIn(email, password, role, scope).then(user => console.log(user));
medplum.signIn(email, password, role, scope).then((user) => console.log(user));
```

@@ -66,4 +66,4 @@

```typescript
medplum.search('Patient?given=eve').then(bundle => {
bundle.entry.forEach(entry => console.log(entry.resource));
medplum.search('Patient?given=eve').then((bundle) => {
bundle.entry.forEach((entry) => console.log(entry.resource));
});

@@ -75,12 +75,16 @@ ```

```typescript
medplum.search({
resourceType: 'Patient',
filters: [{
code: 'given',
operator: Operator.EQUALS,
value: 'eve'
}]
}).then(bundle => {
bundle.entry.forEach(entry => console.log(entry.resource));
});
medplum
.search({
resourceType: 'Patient',
filters: [
{
code: 'given',
operator: Operator.EQUALS,
value: 'eve',
},
],
})
.then((bundle) => {
bundle.entry.forEach((entry) => console.log(entry.resource));
});
```

@@ -96,7 +100,7 @@

subject: {
reference: 'Patient/123'
reference: 'Patient/123',
},
valueQuantity: {
// ...
}
},
// ...

@@ -128,2 +132,2 @@ });

Apache 2.0. Copyright &copy; Medplum 2021
Apache 2.0. Copyright &copy; Medplum 2021

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc