@medplum/ccda 3.2.33
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/README.md +11 -0
- package/dist/cjs/index.cjs +12 -0
- package/dist/cjs/index.cjs.map +7 -0
- package/dist/cjs/index.d.ts +667 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/index.d.ts +667 -0
- package/dist/esm/index.mjs +12 -0
- package/dist/esm/index.mjs.map +7 -0
- package/dist/esm/package.json +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts", "../../../core/src/fhirlexer/parse.ts", "../../../core/src/constants.ts", "../../../core/src/outcomes.ts", "../../../core/src/base-schema.ts", "../../../core/src/base-schema.json", "../../../core/src/typeschema/types.ts", "../../../core/src/typeschema/crawler.ts", "../../../core/src/typeschema/validation.ts", "../../../core/src/fhirpath/utils.ts", "../../../core/src/utils.ts", "../../../core/src/format.ts", "../../../core/src/types.ts", "../../../core/src/fhirpath/date.ts", "../../../core/src/fhirpath/functions.ts", "../../../core/src/fhirpath/atoms.ts", "../../../core/src/fhirlexer/tokenize.ts", "../../../core/src/fhirpath/tokenize.ts", "../../../core/src/fhirpath/parse.ts", "../../../core/src/search/details.ts", "../../../core/src/search/search.ts", "../../../core/src/search/match.ts", "../../../core/src/access.ts", "../../../core/src/base64.ts", "../../../core/src/crypto.ts", "../../../core/src/bundle.ts", "../../../core/src/cache.ts", "../../../core/src/contenttype.ts", "../../../core/src/eventtarget.ts", "../../../core/src/fhircast/index.ts", "../../../core/src/jwt.ts", "../../../core/src/keyvalue.ts", "../../../core/src/readablepromise.ts", "../../../core/src/storage.ts", "../../../core/src/websockets/reconnecting-websocket.ts", "../../../core/src/subscriptions/index.ts", "../../../core/src/client.ts", "../../../core/src/config.ts", "../../../core/src/elements-context.ts", "../../../core/src/typeschema/slices.ts", "../../../core/src/schema-crawler.ts", "../../../core/src/default-values.ts", "../../../core/src/fhirmapper/conceptmaptranslate.ts", "../../../core/src/fhirmapper/tokenize.ts", "../../../core/src/fhirmapper/parse.ts", "../../../core/src/fhirmapper/transform.ts", "../../../core/src/filebuilder.ts", "../../../core/src/filter/tokenize.ts", "../../../core/src/filter/types.ts", "../../../core/src/filter/parse.ts", "../../../core/src/hl7.ts", "../../../core/src/logger.ts", "../../../core/src/schema.ts", "../../../core/src/sftp.ts", "../../../core/src/sql-on-fhir/eval.ts", "../../../core/src/version-utils.ts", "../../src/datetime.ts", "../../src/oids.ts", "../../src/systems.ts", "../../src/xml.ts", "../../src/ccda-to-fhir.ts", "../../src/templates.ts", "../../src/fhir-to-ccda.ts"],
|
|
4
|
+
"sourcesContent": ["export * from './ccda-to-fhir';\nexport * from './datetime';\nexport * from './fhir-to-ccda';\nexport * from './systems';\nexport * from './templates';\nexport * from './types';\nexport * from './xml';\n", "import { TypedValue } from '../types';\nimport { Token } from './tokenize';\n\nexport interface AtomContext {\n parent?: AtomContext;\n variables: Record<string, TypedValue>;\n}\nexport interface Atom {\n eval(context: AtomContext, input: TypedValue[]): TypedValue[];\n\n toString(): string;\n}\n\nexport abstract class PrefixOperatorAtom implements Atom {\n constructor(\n public readonly operator: string,\n public readonly child: Atom\n ) {}\n\n abstract eval(context: AtomContext, input: TypedValue[]): TypedValue[];\n\n toString(): string {\n return `${this.operator}(${this.child.toString()})`;\n }\n}\n\nexport abstract class InfixOperatorAtom implements Atom {\n constructor(\n public readonly operator: string,\n public readonly left: Atom,\n public readonly right: Atom\n ) {}\n\n abstract eval(context: AtomContext, input: TypedValue[]): TypedValue[];\n\n toString(): string {\n return `${this.left.toString()} ${this.operator} ${this.right.toString()}`;\n }\n}\n\nexport interface PrefixParselet {\n parse(parser: Parser, token: Token): Atom;\n}\n\nexport interface InfixParselet {\n precedence: number;\n parse?(parser: Parser, left: Atom, token: Token): Atom;\n}\n\nexport class ParserBuilder {\n private readonly prefixParselets: Record<string, PrefixParselet> = {};\n private readonly infixParselets: Record<string, InfixParselet> = {};\n\n public registerInfix(tokenType: string, parselet: InfixParselet): this {\n this.infixParselets[tokenType] = parselet;\n return this;\n }\n\n public registerPrefix(tokenType: string, parselet: PrefixParselet): this {\n this.prefixParselets[tokenType] = parselet;\n return this;\n }\n\n public prefix(tokenType: string, precedence: number, builder: (token: Token, right: Atom) => Atom): this {\n return this.registerPrefix(tokenType, {\n parse(parser, token) {\n const right = parser.consumeAndParse(precedence);\n return builder(token, right);\n },\n });\n }\n\n public infixLeft(\n tokenType: string,\n precedence: number,\n builder: (left: Atom, token: Token, right: Atom) => Atom\n ): this {\n return this.registerInfix(tokenType, {\n parse(parser, left, token) {\n const right = parser.consumeAndParse(precedence);\n return builder(left, token, right);\n },\n precedence,\n });\n }\n\n public construct(input: Token[]): Parser {\n return new Parser(input, this.prefixParselets, this.infixParselets);\n }\n}\n\nexport class Parser {\n private tokens: Token[];\n private prefixParselets: Record<string, PrefixParselet>;\n private infixParselets: Record<string, InfixParselet>;\n\n constructor(\n tokens: Token[],\n prefixParselets: Record<string, PrefixParselet>,\n infixParselets: Record<string, InfixParselet>\n ) {\n this.tokens = tokens;\n this.prefixParselets = prefixParselets;\n this.infixParselets = infixParselets;\n }\n\n hasMore(): boolean {\n return this.tokens.length > 0;\n }\n\n match(expected: string): boolean {\n const token = this.peek();\n if (token?.id !== expected) {\n return false;\n }\n\n this.consume();\n return true;\n }\n\n consumeAndParse(precedence = Infinity): Atom {\n const token = this.consume();\n const prefix = this.prefixParselets[token.id];\n if (!prefix) {\n throw Error(\n `Parse error at \"${token.value}\" (line ${token.line}, column ${token.column}). No matching prefix parselet.`\n );\n }\n\n let left = prefix.parse(this, token);\n\n while (precedence > this.getPrecedence()) {\n const next = this.consume();\n const infix = this.getInfixParselet(next) as InfixParselet;\n left = (infix.parse as (parser: Parser, left: Atom, token: Token) => Atom)(this, left, next);\n }\n\n return left;\n }\n\n getPrecedence(): number {\n const nextToken = this.peek();\n if (!nextToken) {\n return Infinity;\n }\n const parser = this.getInfixParselet(nextToken);\n if (parser) {\n return parser.precedence;\n }\n return Infinity;\n }\n\n consume(expectedId?: string, expectedValue?: string): Token {\n if (!this.tokens.length) {\n throw Error('Cant consume unknown more tokens.');\n }\n if (expectedId && this.peek()?.id !== expectedId) {\n const actual = this.peek() as Token;\n throw Error(\n `Expected ${expectedId} but got \"${actual.id}\" (${actual.value}) at line ${actual.line} column ${actual.column}.`\n );\n }\n if (expectedValue && this.peek()?.value !== expectedValue) {\n const actual = this.peek() as Token;\n throw Error(\n `Expected \"${expectedValue}\" but got \"${actual.value}\" at line ${actual.line} column ${actual.column}.`\n );\n }\n return this.tokens.shift() as Token;\n }\n\n peek(): Token | undefined {\n return this.tokens.length > 0 ? this.tokens[0] : undefined;\n }\n\n removeComments(): void {\n this.tokens = this.tokens.filter((t) => t.id !== 'Comment');\n }\n\n getInfixParselet(token: Token): InfixParselet | undefined {\n return this.infixParselets[token.id === 'Symbol' ? token.value : token.id];\n }\n}\n", "// Common terminology systems, taken from https://terminology.hl7.org/external_terminologies.html\nexport const UCUM = 'http://unitsofmeasure.org';\nexport const LOINC = 'http://loinc.org';\nexport const SNOMED = 'http://snomed.info/sct';\nexport const RXNORM = 'http://www.nlm.nih.gov/research/umls/rxnorm';\nexport const CPT = 'http://www.ama-assn.org/go/cpt';\nexport const ICD10 = 'http://hl7.org/fhir/sid/icd-10';\nexport const NDC = 'http://hl7.org/fhir/sid/ndc';\n\n// common http-based origins useful for avoiding false-positives about preferring https over http,\n// e.g. https://rules.sonarsource.com/javascript/type/Security%20Hotspot/RSPEC-5332/\nexport const HTTP_HL7_ORG = 'http://hl7.org';\nexport const HTTP_TERMINOLOGY_HL7_ORG = 'http://terminology.hl7.org';\n", "import { OperationOutcome, OperationOutcomeIssue } from '@medplum/fhirtypes';\nimport { Constraint } from './typeschema/types';\n\nconst OK_ID = 'ok';\nconst CREATED_ID = 'created';\nconst GONE_ID = 'gone';\nconst NOT_MODIFIED_ID = 'not-modified';\nconst FOUND_ID = 'found';\nconst NOT_FOUND_ID = 'not-found';\nconst CONFLICT_ID = 'conflict';\nconst UNAUTHORIZED_ID = 'unauthorized';\nconst FORBIDDEN_ID = 'forbidden';\nconst PRECONDITION_FAILED_ID = 'precondition-failed';\nconst MULTIPLE_MATCHES_ID = 'multiple-matches';\nconst TOO_MANY_REQUESTS_ID = 'too-many-requests';\nconst ACCEPTED_ID = 'accepted';\nconst SERVER_TIMEOUT_ID = 'server-timeout';\n\nexport const allOk: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: OK_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'All OK',\n },\n },\n ],\n};\n\nexport const created: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: CREATED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Created',\n },\n },\n ],\n};\n\nexport const notModified: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: NOT_MODIFIED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Not Modified',\n },\n },\n ],\n};\n\nexport const notFound: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: NOT_FOUND_ID,\n issue: [\n {\n severity: 'error',\n code: 'not-found',\n details: {\n text: 'Not found',\n },\n },\n ],\n};\n\nexport const unauthorized: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: UNAUTHORIZED_ID,\n issue: [\n {\n severity: 'error',\n code: 'login',\n details: {\n text: 'Unauthorized',\n },\n },\n ],\n};\n\nexport const unauthorizedTokenExpired: OperationOutcome = {\n ...unauthorized,\n issue: [\n ...unauthorized.issue,\n {\n severity: 'error',\n code: 'expired',\n details: {\n text: 'Token expired',\n },\n },\n ],\n};\n\nexport const unauthorizedTokenAudience: OperationOutcome = {\n ...unauthorized,\n issue: [\n ...unauthorized.issue,\n {\n severity: 'error',\n code: 'invalid',\n details: {\n text: 'Token not issued for this audience',\n },\n },\n ],\n};\n\nexport const forbidden: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: FORBIDDEN_ID,\n issue: [\n {\n severity: 'error',\n code: 'forbidden',\n details: {\n text: 'Forbidden',\n },\n },\n ],\n};\n\nexport const gone: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: GONE_ID,\n issue: [\n {\n severity: 'error',\n code: 'deleted',\n details: {\n text: 'Gone',\n },\n },\n ],\n};\n\nexport const preconditionFailed: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: PRECONDITION_FAILED_ID,\n issue: [\n {\n severity: 'error',\n code: 'processing',\n details: {\n text: 'Precondition Failed',\n },\n },\n ],\n};\n\nexport const multipleMatches: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: MULTIPLE_MATCHES_ID,\n issue: [\n {\n severity: 'error',\n code: 'multiple-matches',\n details: {\n text: 'Multiple resources found matching condition',\n },\n },\n ],\n};\n\nexport const tooManyRequests: OperationOutcome = {\n resourceType: 'OperationOutcome',\n id: TOO_MANY_REQUESTS_ID,\n issue: [\n {\n severity: 'error',\n code: 'throttled',\n details: {\n text: 'Too Many Requests',\n },\n },\n ],\n};\n\nexport function accepted(location: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n id: ACCEPTED_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n text: 'Accepted',\n },\n diagnostics: location,\n },\n ],\n };\n}\n\nexport function badRequest(details: string, expression?: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'invalid',\n details: {\n text: details,\n },\n ...(expression ? { expression: [expression] } : undefined),\n },\n ],\n };\n}\n\nexport function conflict(details: string, code?: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n id: CONFLICT_ID,\n issue: [\n {\n severity: 'error',\n code: 'conflict',\n details: {\n coding: code ? [{ code }] : undefined,\n text: details,\n },\n },\n ],\n };\n}\n\nexport function validationError(details: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'structure',\n details: {\n text: details,\n },\n },\n ],\n };\n}\n\nexport function serverError(err: Error): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n issue: [\n {\n severity: 'error',\n code: 'exception',\n details: {\n text: 'Internal server error',\n },\n diagnostics: err.toString(),\n },\n ],\n };\n}\n\nexport function serverTimeout(msg?: string): OperationOutcome {\n return {\n resourceType: 'OperationOutcome',\n id: SERVER_TIMEOUT_ID,\n issue: [\n {\n severity: 'error',\n code: 'timeout',\n details: {\n text: msg ?? 'Server timeout',\n },\n },\n ],\n };\n}\n\nexport function redirect(url: URL): OperationOutcome {\n const urlStr = url.toString();\n return {\n resourceType: 'OperationOutcome',\n id: FOUND_ID,\n issue: [\n {\n severity: 'information',\n code: 'informational',\n details: {\n coding: [{ system: 'urn:ietf:rfc:3986', code: urlStr }],\n text: 'Redirect to ' + urlStr,\n },\n },\n ],\n };\n}\n\nexport function isOperationOutcome(value: unknown): value is OperationOutcome {\n return typeof value === 'object' && value !== null && (value as any).resourceType === 'OperationOutcome';\n}\n\nexport function isOk(outcome: OperationOutcome): boolean {\n return (\n outcome.id === OK_ID || outcome.id === CREATED_ID || outcome.id === NOT_MODIFIED_ID || outcome.id === ACCEPTED_ID\n );\n}\n\nexport function isCreated(outcome: OperationOutcome): boolean {\n return outcome.id === CREATED_ID;\n}\n\nexport function isAccepted(outcome: OperationOutcome): boolean {\n return outcome.id === ACCEPTED_ID;\n}\n\nexport function isRedirect(outcome: OperationOutcome): boolean {\n return outcome.id === FOUND_ID;\n}\n\nexport function isNotFound(outcome: OperationOutcome): boolean {\n return outcome.id === NOT_FOUND_ID;\n}\n\nexport function isConflict(outcome: OperationOutcome): boolean {\n return outcome.id === CONFLICT_ID;\n}\n\nexport function isGone(outcome: OperationOutcome): boolean {\n return outcome.id === GONE_ID;\n}\n\nexport function isUnauthenticated(outcome: OperationOutcome): boolean {\n return outcome.id === UNAUTHORIZED_ID;\n}\n\nexport function getStatus(outcome: OperationOutcome): number {\n switch (outcome.id) {\n case OK_ID:\n return 200;\n case CREATED_ID:\n return 201;\n case ACCEPTED_ID:\n return 202;\n case FOUND_ID:\n return 302;\n case NOT_MODIFIED_ID:\n return 304;\n case UNAUTHORIZED_ID:\n return 401;\n case FORBIDDEN_ID:\n return 403;\n case NOT_FOUND_ID:\n return 404;\n case CONFLICT_ID:\n return 409;\n case GONE_ID:\n return 410;\n case PRECONDITION_FAILED_ID:\n case MULTIPLE_MATCHES_ID:\n return 412;\n case TOO_MANY_REQUESTS_ID:\n return 429;\n case SERVER_TIMEOUT_ID:\n return 504;\n default:\n return outcome.issue?.[0]?.code === 'exception' ? 500 : 400;\n }\n}\n\n/**\n * Asserts that the operation completed successfully and that the resource is defined.\n * @param outcome - The operation outcome.\n * @param resource - The resource that may or may not have been returned.\n */\nexport function assertOk<T>(outcome: OperationOutcome, resource: T | undefined): asserts resource is T {\n if (!isOk(outcome) || resource === undefined) {\n throw new OperationOutcomeError(outcome);\n }\n}\n\nexport class OperationOutcomeError extends Error {\n readonly outcome: OperationOutcome;\n\n constructor(outcome: OperationOutcome, cause?: unknown) {\n super(operationOutcomeToString(outcome));\n this.outcome = outcome;\n this.cause = cause;\n }\n}\n\n/**\n * Normalizes an error object into an OperationOutcome.\n * @param error - The error value which could be a string, Error, OperationOutcome, or other unknown type.\n * @returns The normalized OperationOutcome.\n */\nexport function normalizeOperationOutcome(error: unknown): OperationOutcome {\n if (error instanceof OperationOutcomeError) {\n return error.outcome;\n }\n if (isOperationOutcome(error)) {\n return error;\n }\n return badRequest(normalizeErrorString(error));\n}\n\n/**\n * Normalizes an error object into a displayable error string.\n * @param error - The error value which could be a string, Error, OperationOutcome, or other unknown type.\n * @returns A display string for the error.\n */\nexport function normalizeErrorString(error: unknown): string {\n if (!error) {\n return 'Unknown error';\n }\n if (typeof error === 'string') {\n return error;\n }\n if (error instanceof Error) {\n return error.message;\n }\n if (isOperationOutcome(error)) {\n return operationOutcomeToString(error);\n }\n if (typeof error === 'object' && 'code' in error && typeof error.code === 'string') {\n return error.code;\n }\n return JSON.stringify(error);\n}\n\n/**\n * Returns a string represenation of the operation outcome.\n * @param outcome - The operation outcome.\n * @returns The string representation of the operation outcome.\n */\nexport function operationOutcomeToString(outcome: OperationOutcome): string {\n const strs = outcome.issue?.map(operationOutcomeIssueToString) ?? [];\n return strs.length > 0 ? strs.join('; ') : 'Unknown error';\n}\n\n/**\n * Returns a string represenation of the operation outcome issue.\n * @param issue - The operation outcome issue.\n * @returns The string representation of the operation outcome issue.\n */\nexport function operationOutcomeIssueToString(issue: OperationOutcomeIssue): string {\n let issueStr;\n if (issue.details?.text) {\n if (issue.diagnostics) {\n issueStr = `${issue.details.text} (${issue.diagnostics})`;\n } else {\n issueStr = issue.details.text;\n }\n } else if (issue.diagnostics) {\n issueStr = issue.diagnostics;\n } else {\n issueStr = 'Unknown error';\n }\n if (issue.expression?.length) {\n issueStr += ` (${issue.expression.join(', ')})`;\n }\n return issueStr;\n}\n\nexport type IssueSeverity = 'error' | 'fatal' | 'warning' | 'information';\nexport type IssueType = 'structure' | 'invariant' | 'processing';\n\nexport function createOperationOutcomeIssue(\n severity: IssueSeverity,\n code: IssueType,\n message: string,\n path: string,\n data?: Record<string, any>\n): OperationOutcomeIssue {\n const issue: OperationOutcomeIssue = {\n severity,\n code,\n details: {\n text: message,\n },\n expression: [path],\n };\n if (data) {\n issue.diagnostics = JSON.stringify(data);\n }\n return issue;\n}\n\nexport function createStructureIssue(expression: string, details: string): OperationOutcomeIssue {\n return createOperationOutcomeIssue('error', 'structure', details, expression);\n}\n\nexport function createConstraintIssue(expression: string, constraint: Constraint): OperationOutcomeIssue {\n return createOperationOutcomeIssue(\n 'error',\n 'invariant',\n `Constraint ${constraint.key} not met: ${constraint.description}`,\n expression,\n {\n fhirpath: constraint.expression,\n }\n );\n}\n\nexport function createProcessingIssue(\n expression: string,\n message: string,\n err: Error,\n data?: Record<string, any>\n): OperationOutcomeIssue {\n return createOperationOutcomeIssue('error', 'processing', message, expression, { ...data, error: err });\n}\n", "import { InternalSchemaElement, InternalTypeSchema } from './typeschema/types';\n\nexport type BaseSchema = Record<string, { elements: Record<string, Partial<InternalSchemaElement>> }>;\n\nconst normalizedTypes: Record<string, string> = {\n 'http://hl7.org/fhirpath/System.String': 'string',\n};\n\nexport function compressElement(element: InternalSchemaElement): Partial<InternalSchemaElement> {\n // For each property, only keep \"min\", \"max\", and \"type\"\n // Only keep \"min\" if not 0\n // Only keep \"max\" if not 1\n const outputPropertySchema: Partial<InternalSchemaElement> = {};\n if (element.min !== 0) {\n outputPropertySchema.min = element.min;\n }\n\n if (element.max !== 1 && Number.isFinite(element.max)) {\n outputPropertySchema.max = element.max;\n } else if (element.max === Number.POSITIVE_INFINITY) {\n outputPropertySchema.max = Number.MAX_SAFE_INTEGER;\n }\n\n outputPropertySchema.type = element.type?.map((t) => ({\n ...t,\n extension: undefined,\n code: normalizedTypes[t.code] ?? t.code,\n }));\n return outputPropertySchema;\n}\n\nexport function inflateElement(path: string, partial: Partial<InternalSchemaElement>): InternalSchemaElement {\n const max = partial.max && partial.max === Number.MAX_SAFE_INTEGER ? Number.POSITIVE_INFINITY : partial.max;\n return {\n path,\n description: '',\n type: partial.type ?? [],\n min: partial.min ?? 0,\n max: max ?? 1,\n isArray: !!max && max > 1,\n constraints: [],\n };\n}\n\nexport type DataTypesMap = { [type: string]: InternalTypeSchema };\n\nexport function inflateBaseSchema(base: BaseSchema): DataTypesMap {\n const output: DataTypesMap = Object.create(null);\n for (const [key, schema] of Object.entries(base)) {\n output[key] = {\n name: key,\n type: key,\n path: key,\n elements: Object.fromEntries(\n Object.entries(schema.elements).map(([property, partial]) => [property, inflateElement(property, partial)])\n ),\n constraints: [],\n innerTypes: [],\n };\n }\n return output;\n}\n", "{\n \"Element\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n }\n }\n },\n \"BackboneElement\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n }\n }\n },\n \"Address\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"use\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"type\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"text\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"line\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"city\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"district\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"state\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"postalCode\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"country\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"period\": {\n \"type\": [\n {\n \"code\": \"Period\"\n }\n ]\n }\n }\n },\n \"Age\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"comparator\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"unit\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"Annotation\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"author[x]\": {\n \"type\": [\n {\n \"code\": \"Reference\",\n \"targetProfile\": [\n \"http://hl7.org/fhir/StructureDefinition/Practitioner\",\n \"http://hl7.org/fhir/StructureDefinition/Patient\",\n \"http://hl7.org/fhir/StructureDefinition/RelatedPerson\",\n \"http://hl7.org/fhir/StructureDefinition/Organization\"\n ]\n },\n {\n \"code\": \"string\"\n }\n ]\n },\n \"time\": {\n \"type\": [\n {\n \"code\": \"dateTime\"\n }\n ]\n },\n \"text\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"markdown\"\n }\n ]\n }\n }\n },\n \"Attachment\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"contentType\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"language\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"data\": {\n \"type\": [\n {\n \"code\": \"base64Binary\"\n }\n ]\n },\n \"url\": {\n \"type\": [\n {\n \"code\": \"url\"\n }\n ]\n },\n \"size\": {\n \"type\": [\n {\n \"code\": \"unsignedInt\"\n }\n ]\n },\n \"hash\": {\n \"type\": [\n {\n \"code\": \"base64Binary\"\n }\n ]\n },\n \"title\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"creation\": {\n \"type\": [\n {\n \"code\": \"dateTime\"\n }\n ]\n }\n }\n },\n \"CodeableConcept\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"coding\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Coding\"\n }\n ]\n },\n \"text\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n }\n }\n },\n \"Coding\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"version\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"display\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"userSelected\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n }\n ]\n }\n }\n },\n \"ContactDetail\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"name\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"telecom\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"ContactPoint\"\n }\n ]\n }\n }\n },\n \"ContactPoint\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"use\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"rank\": {\n \"type\": [\n {\n \"code\": \"positiveInt\"\n }\n ]\n },\n \"period\": {\n \"type\": [\n {\n \"code\": \"Period\"\n }\n ]\n }\n }\n },\n \"Contributor\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"type\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"name\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"contact\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"ContactDetail\"\n }\n ]\n }\n }\n },\n \"Count\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"comparator\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"unit\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"DataRequirement\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"type\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"profile\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/StructureDefinition\"]\n }\n ]\n },\n \"subject[x]\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n },\n {\n \"code\": \"Reference\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/Group\"]\n }\n ]\n },\n \"mustSupport\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"codeFilter\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"DataRequirementCodeFilter\"\n }\n ]\n },\n \"dateFilter\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"DataRequirementDateFilter\"\n }\n ]\n },\n \"limit\": {\n \"type\": [\n {\n \"code\": \"positiveInt\"\n }\n ]\n },\n \"sort\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"DataRequirementSort\"\n }\n ]\n }\n }\n },\n \"DataRequirementCodeFilter\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"path\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"searchParam\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"valueSet\": {\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/ValueSet\"]\n }\n ]\n },\n \"code\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Coding\"\n }\n ]\n }\n }\n },\n \"DataRequirementDateFilter\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"path\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"searchParam\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"value[x]\": {\n \"type\": [\n {\n \"code\": \"dateTime\"\n },\n {\n \"code\": \"Period\"\n },\n {\n \"code\": \"Duration\"\n }\n ]\n }\n }\n },\n \"DataRequirementSort\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"path\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"direction\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"Distance\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"comparator\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"unit\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"Dosage\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"sequence\": {\n \"type\": [\n {\n \"code\": \"integer\"\n }\n ]\n },\n \"text\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"additionalInstruction\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"patientInstruction\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"timing\": {\n \"type\": [\n {\n \"code\": \"Timing\"\n }\n ]\n },\n \"asNeeded[x]\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n },\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"site\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"route\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"method\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"doseAndRate\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"DosageDoseAndRate\"\n }\n ]\n },\n \"maxDosePerPeriod\": {\n \"type\": [\n {\n \"code\": \"Ratio\"\n }\n ]\n },\n \"maxDosePerAdministration\": {\n \"type\": [\n {\n \"code\": \"Quantity\",\n \"profile\": [\"http://hl7.org/fhir/StructureDefinition/SimpleQuantity\"]\n }\n ]\n },\n \"maxDosePerLifetime\": {\n \"type\": [\n {\n \"code\": \"Quantity\",\n \"profile\": [\"http://hl7.org/fhir/StructureDefinition/SimpleQuantity\"]\n }\n ]\n }\n }\n },\n \"DosageDoseAndRate\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"type\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"dose[x]\": {\n \"type\": [\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Quantity\",\n \"profile\": [\"http://hl7.org/fhir/StructureDefinition/SimpleQuantity\"]\n }\n ]\n },\n \"rate[x]\": {\n \"type\": [\n {\n \"code\": \"Ratio\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Quantity\",\n \"profile\": [\"http://hl7.org/fhir/StructureDefinition/SimpleQuantity\"]\n }\n ]\n }\n }\n },\n \"Duration\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"comparator\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"unit\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"ElementDefinition\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"path\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"representation\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"sliceName\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"sliceIsConstraining\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n }\n ]\n },\n \"label\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"code\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Coding\"\n }\n ]\n },\n \"slicing\": {\n \"type\": [\n {\n \"code\": \"ElementDefinitionSlicing\"\n }\n ]\n },\n \"short\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"definition\": {\n \"type\": [\n {\n \"code\": \"markdown\"\n }\n ]\n },\n \"comment\": {\n \"type\": [\n {\n \"code\": \"markdown\"\n }\n ]\n },\n \"requirements\": {\n \"type\": [\n {\n \"code\": \"markdown\"\n }\n ]\n },\n \"alias\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"min\": {\n \"type\": [\n {\n \"code\": \"unsignedInt\"\n }\n ]\n },\n \"max\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"base\": {\n \"type\": [\n {\n \"code\": \"ElementDefinitionBase\"\n }\n ]\n },\n \"contentReference\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"type\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"ElementDefinitionType\"\n }\n ]\n },\n \"defaultValue[x]\": {\n \"type\": [\n {\n \"code\": \"base64Binary\"\n },\n {\n \"code\": \"boolean\"\n },\n {\n \"code\": \"canonical\"\n },\n {\n \"code\": \"code\"\n },\n {\n \"code\": \"date\"\n },\n {\n \"code\": \"dateTime\"\n },\n {\n \"code\": \"decimal\"\n },\n {\n \"code\": \"id\"\n },\n {\n \"code\": \"instant\"\n },\n {\n \"code\": \"integer\"\n },\n {\n \"code\": \"markdown\"\n },\n {\n \"code\": \"oid\"\n },\n {\n \"code\": \"positiveInt\"\n },\n {\n \"code\": \"string\"\n },\n {\n \"code\": \"time\"\n },\n {\n \"code\": \"unsignedInt\"\n },\n {\n \"code\": \"uri\"\n },\n {\n \"code\": \"url\"\n },\n {\n \"code\": \"uuid\"\n },\n {\n \"code\": \"Address\"\n },\n {\n \"code\": \"Age\"\n },\n {\n \"code\": \"Annotation\"\n },\n {\n \"code\": \"Attachment\"\n },\n {\n \"code\": \"CodeableConcept\"\n },\n {\n \"code\": \"Coding\"\n },\n {\n \"code\": \"ContactPoint\"\n },\n {\n \"code\": \"Count\"\n },\n {\n \"code\": \"Distance\"\n },\n {\n \"code\": \"Duration\"\n },\n {\n \"code\": \"HumanName\"\n },\n {\n \"code\": \"Identifier\"\n },\n {\n \"code\": \"Money\"\n },\n {\n \"code\": \"Period\"\n },\n {\n \"code\": \"Quantity\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Ratio\"\n },\n {\n \"code\": \"Reference\"\n },\n {\n \"code\": \"SampledData\"\n },\n {\n \"code\": \"Signature\"\n },\n {\n \"code\": \"Timing\"\n },\n {\n \"code\": \"ContactDetail\"\n },\n {\n \"code\": \"Contributor\"\n },\n {\n \"code\": \"DataRequirement\"\n },\n {\n \"code\": \"Expression\"\n },\n {\n \"code\": \"ParameterDefinition\"\n },\n {\n \"code\": \"RelatedArtifact\"\n },\n {\n \"code\": \"TriggerDefinition\"\n },\n {\n \"code\": \"UsageContext\"\n },\n {\n \"code\": \"Dosage\"\n },\n {\n \"code\": \"Meta\"\n }\n ]\n },\n \"meaningWhenMissing\": {\n \"type\": [\n {\n \"code\": \"markdown\"\n }\n ]\n },\n \"orderMeaning\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"fixed[x]\": {\n \"type\": [\n {\n \"code\": \"base64Binary\"\n },\n {\n \"code\": \"boolean\"\n },\n {\n \"code\": \"canonical\"\n },\n {\n \"code\": \"code\"\n },\n {\n \"code\": \"date\"\n },\n {\n \"code\": \"dateTime\"\n },\n {\n \"code\": \"decimal\"\n },\n {\n \"code\": \"id\"\n },\n {\n \"code\": \"instant\"\n },\n {\n \"code\": \"integer\"\n },\n {\n \"code\": \"markdown\"\n },\n {\n \"code\": \"oid\"\n },\n {\n \"code\": \"positiveInt\"\n },\n {\n \"code\": \"string\"\n },\n {\n \"code\": \"time\"\n },\n {\n \"code\": \"unsignedInt\"\n },\n {\n \"code\": \"uri\"\n },\n {\n \"code\": \"url\"\n },\n {\n \"code\": \"uuid\"\n },\n {\n \"code\": \"Address\"\n },\n {\n \"code\": \"Age\"\n },\n {\n \"code\": \"Annotation\"\n },\n {\n \"code\": \"Attachment\"\n },\n {\n \"code\": \"CodeableConcept\"\n },\n {\n \"code\": \"Coding\"\n },\n {\n \"code\": \"ContactPoint\"\n },\n {\n \"code\": \"Count\"\n },\n {\n \"code\": \"Distance\"\n },\n {\n \"code\": \"Duration\"\n },\n {\n \"code\": \"HumanName\"\n },\n {\n \"code\": \"Identifier\"\n },\n {\n \"code\": \"Money\"\n },\n {\n \"code\": \"Period\"\n },\n {\n \"code\": \"Quantity\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Ratio\"\n },\n {\n \"code\": \"Reference\"\n },\n {\n \"code\": \"SampledData\"\n },\n {\n \"code\": \"Signature\"\n },\n {\n \"code\": \"Timing\"\n },\n {\n \"code\": \"ContactDetail\"\n },\n {\n \"code\": \"Contributor\"\n },\n {\n \"code\": \"DataRequirement\"\n },\n {\n \"code\": \"Expression\"\n },\n {\n \"code\": \"ParameterDefinition\"\n },\n {\n \"code\": \"RelatedArtifact\"\n },\n {\n \"code\": \"TriggerDefinition\"\n },\n {\n \"code\": \"UsageContext\"\n },\n {\n \"code\": \"Dosage\"\n },\n {\n \"code\": \"Meta\"\n }\n ]\n },\n \"pattern[x]\": {\n \"type\": [\n {\n \"code\": \"base64Binary\"\n },\n {\n \"code\": \"boolean\"\n },\n {\n \"code\": \"canonical\"\n },\n {\n \"code\": \"code\"\n },\n {\n \"code\": \"date\"\n },\n {\n \"code\": \"dateTime\"\n },\n {\n \"code\": \"decimal\"\n },\n {\n \"code\": \"id\"\n },\n {\n \"code\": \"instant\"\n },\n {\n \"code\": \"integer\"\n },\n {\n \"code\": \"markdown\"\n },\n {\n \"code\": \"oid\"\n },\n {\n \"code\": \"positiveInt\"\n },\n {\n \"code\": \"string\"\n },\n {\n \"code\": \"time\"\n },\n {\n \"code\": \"unsignedInt\"\n },\n {\n \"code\": \"uri\"\n },\n {\n \"code\": \"url\"\n },\n {\n \"code\": \"uuid\"\n },\n {\n \"code\": \"Address\"\n },\n {\n \"code\": \"Age\"\n },\n {\n \"code\": \"Annotation\"\n },\n {\n \"code\": \"Attachment\"\n },\n {\n \"code\": \"CodeableConcept\"\n },\n {\n \"code\": \"Coding\"\n },\n {\n \"code\": \"ContactPoint\"\n },\n {\n \"code\": \"Count\"\n },\n {\n \"code\": \"Distance\"\n },\n {\n \"code\": \"Duration\"\n },\n {\n \"code\": \"HumanName\"\n },\n {\n \"code\": \"Identifier\"\n },\n {\n \"code\": \"Money\"\n },\n {\n \"code\": \"Period\"\n },\n {\n \"code\": \"Quantity\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Ratio\"\n },\n {\n \"code\": \"Reference\"\n },\n {\n \"code\": \"SampledData\"\n },\n {\n \"code\": \"Signature\"\n },\n {\n \"code\": \"Timing\"\n },\n {\n \"code\": \"ContactDetail\"\n },\n {\n \"code\": \"Contributor\"\n },\n {\n \"code\": \"DataRequirement\"\n },\n {\n \"code\": \"Expression\"\n },\n {\n \"code\": \"ParameterDefinition\"\n },\n {\n \"code\": \"RelatedArtifact\"\n },\n {\n \"code\": \"TriggerDefinition\"\n },\n {\n \"code\": \"UsageContext\"\n },\n {\n \"code\": \"Dosage\"\n },\n {\n \"code\": \"Meta\"\n }\n ]\n },\n \"example\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"ElementDefinitionExample\"\n }\n ]\n },\n \"minValue[x]\": {\n \"type\": [\n {\n \"code\": \"date\"\n },\n {\n \"code\": \"dateTime\"\n },\n {\n \"code\": \"instant\"\n },\n {\n \"code\": \"time\"\n },\n {\n \"code\": \"decimal\"\n },\n {\n \"code\": \"integer\"\n },\n {\n \"code\": \"positiveInt\"\n },\n {\n \"code\": \"unsignedInt\"\n },\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"maxValue[x]\": {\n \"type\": [\n {\n \"code\": \"date\"\n },\n {\n \"code\": \"dateTime\"\n },\n {\n \"code\": \"instant\"\n },\n {\n \"code\": \"time\"\n },\n {\n \"code\": \"decimal\"\n },\n {\n \"code\": \"integer\"\n },\n {\n \"code\": \"positiveInt\"\n },\n {\n \"code\": \"unsignedInt\"\n },\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"maxLength\": {\n \"type\": [\n {\n \"code\": \"integer\"\n }\n ]\n },\n \"condition\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"id\"\n }\n ]\n },\n \"constraint\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"ElementDefinitionConstraint\"\n }\n ]\n },\n \"mustSupport\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n }\n ]\n },\n \"isModifier\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n }\n ]\n },\n \"isModifierReason\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"isSummary\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n }\n ]\n },\n \"binding\": {\n \"type\": [\n {\n \"code\": \"ElementDefinitionBinding\"\n }\n ]\n },\n \"mapping\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"ElementDefinitionMapping\"\n }\n ]\n }\n }\n },\n \"ElementDefinitionSlicingDiscriminator\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"type\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"path\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n }\n }\n },\n \"ElementDefinitionSlicing\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"discriminator\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"ElementDefinitionSlicingDiscriminator\"\n }\n ]\n },\n \"description\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"ordered\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n }\n ]\n },\n \"rules\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"ElementDefinitionBase\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"path\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"min\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"unsignedInt\"\n }\n ]\n },\n \"max\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n }\n }\n },\n \"ElementDefinitionType\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"code\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"profile\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\n \"http://hl7.org/fhir/StructureDefinition/StructureDefinition\",\n \"http://hl7.org/fhir/StructureDefinition/ImplementationGuide\"\n ]\n }\n ]\n },\n \"targetProfile\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\n \"http://hl7.org/fhir/StructureDefinition/StructureDefinition\",\n \"http://hl7.org/fhir/StructureDefinition/ImplementationGuide\"\n ]\n }\n ]\n },\n \"aggregation\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"versioning\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"ElementDefinitionExample\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"label\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"value[x]\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"base64Binary\"\n },\n {\n \"code\": \"boolean\"\n },\n {\n \"code\": \"canonical\"\n },\n {\n \"code\": \"code\"\n },\n {\n \"code\": \"date\"\n },\n {\n \"code\": \"dateTime\"\n },\n {\n \"code\": \"decimal\"\n },\n {\n \"code\": \"id\"\n },\n {\n \"code\": \"instant\"\n },\n {\n \"code\": \"integer\"\n },\n {\n \"code\": \"markdown\"\n },\n {\n \"code\": \"oid\"\n },\n {\n \"code\": \"positiveInt\"\n },\n {\n \"code\": \"string\"\n },\n {\n \"code\": \"time\"\n },\n {\n \"code\": \"unsignedInt\"\n },\n {\n \"code\": \"uri\"\n },\n {\n \"code\": \"url\"\n },\n {\n \"code\": \"uuid\"\n },\n {\n \"code\": \"Address\"\n },\n {\n \"code\": \"Age\"\n },\n {\n \"code\": \"Annotation\"\n },\n {\n \"code\": \"Attachment\"\n },\n {\n \"code\": \"CodeableConcept\"\n },\n {\n \"code\": \"Coding\"\n },\n {\n \"code\": \"ContactPoint\"\n },\n {\n \"code\": \"Count\"\n },\n {\n \"code\": \"Distance\"\n },\n {\n \"code\": \"Duration\"\n },\n {\n \"code\": \"HumanName\"\n },\n {\n \"code\": \"Identifier\"\n },\n {\n \"code\": \"Money\"\n },\n {\n \"code\": \"Period\"\n },\n {\n \"code\": \"Quantity\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Ratio\"\n },\n {\n \"code\": \"Reference\"\n },\n {\n \"code\": \"SampledData\"\n },\n {\n \"code\": \"Signature\"\n },\n {\n \"code\": \"Timing\"\n },\n {\n \"code\": \"ContactDetail\"\n },\n {\n \"code\": \"Contributor\"\n },\n {\n \"code\": \"DataRequirement\"\n },\n {\n \"code\": \"Expression\"\n },\n {\n \"code\": \"ParameterDefinition\"\n },\n {\n \"code\": \"RelatedArtifact\"\n },\n {\n \"code\": \"TriggerDefinition\"\n },\n {\n \"code\": \"UsageContext\"\n },\n {\n \"code\": \"Dosage\"\n },\n {\n \"code\": \"Meta\"\n }\n ]\n }\n }\n },\n \"ElementDefinitionConstraint\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"key\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"id\"\n }\n ]\n },\n \"requirements\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"severity\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"human\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"expression\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"xpath\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"source\": {\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/StructureDefinition\"]\n }\n ]\n }\n }\n },\n \"ElementDefinitionBinding\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"strength\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"description\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"valueSet\": {\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/ValueSet\"]\n }\n ]\n }\n }\n },\n \"ElementDefinitionMapping\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"identity\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"id\"\n }\n ]\n },\n \"language\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"map\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"comment\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n }\n }\n },\n \"Expression\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"description\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"name\": {\n \"type\": [\n {\n \"code\": \"id\"\n }\n ]\n },\n \"language\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"expression\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"reference\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n }\n }\n },\n \"Extension\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"url\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"value[x]\": {\n \"type\": [\n {\n \"code\": \"base64Binary\"\n },\n {\n \"code\": \"boolean\"\n },\n {\n \"code\": \"canonical\"\n },\n {\n \"code\": \"code\"\n },\n {\n \"code\": \"date\"\n },\n {\n \"code\": \"dateTime\"\n },\n {\n \"code\": \"decimal\"\n },\n {\n \"code\": \"id\"\n },\n {\n \"code\": \"instant\"\n },\n {\n \"code\": \"integer\"\n },\n {\n \"code\": \"markdown\"\n },\n {\n \"code\": \"oid\"\n },\n {\n \"code\": \"positiveInt\"\n },\n {\n \"code\": \"string\"\n },\n {\n \"code\": \"time\"\n },\n {\n \"code\": \"unsignedInt\"\n },\n {\n \"code\": \"uri\"\n },\n {\n \"code\": \"url\"\n },\n {\n \"code\": \"uuid\"\n },\n {\n \"code\": \"Address\"\n },\n {\n \"code\": \"Age\"\n },\n {\n \"code\": \"Annotation\"\n },\n {\n \"code\": \"Attachment\"\n },\n {\n \"code\": \"CodeableConcept\"\n },\n {\n \"code\": \"Coding\"\n },\n {\n \"code\": \"ContactPoint\"\n },\n {\n \"code\": \"Count\"\n },\n {\n \"code\": \"Distance\"\n },\n {\n \"code\": \"Duration\"\n },\n {\n \"code\": \"HumanName\"\n },\n {\n \"code\": \"Identifier\"\n },\n {\n \"code\": \"Money\"\n },\n {\n \"code\": \"Period\"\n },\n {\n \"code\": \"Quantity\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Ratio\"\n },\n {\n \"code\": \"Reference\"\n },\n {\n \"code\": \"SampledData\"\n },\n {\n \"code\": \"Signature\"\n },\n {\n \"code\": \"Timing\"\n },\n {\n \"code\": \"ContactDetail\"\n },\n {\n \"code\": \"Contributor\"\n },\n {\n \"code\": \"DataRequirement\"\n },\n {\n \"code\": \"Expression\"\n },\n {\n \"code\": \"ParameterDefinition\"\n },\n {\n \"code\": \"RelatedArtifact\"\n },\n {\n \"code\": \"TriggerDefinition\"\n },\n {\n \"code\": \"UsageContext\"\n },\n {\n \"code\": \"Dosage\"\n },\n {\n \"code\": \"Meta\"\n }\n ]\n }\n }\n },\n \"HumanName\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"use\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"text\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"family\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"given\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"prefix\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"suffix\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"period\": {\n \"type\": [\n {\n \"code\": \"Period\"\n }\n ]\n }\n }\n },\n \"Identifier\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"use\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"type\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"period\": {\n \"type\": [\n {\n \"code\": \"Period\"\n }\n ]\n },\n \"assigner\": {\n \"type\": [\n {\n \"code\": \"Reference\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/Organization\"]\n }\n ]\n }\n }\n },\n \"MarketingStatus\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"country\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"jurisdiction\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"status\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"dateRange\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"Period\"\n }\n ]\n },\n \"restoreDate\": {\n \"type\": [\n {\n \"code\": \"dateTime\"\n }\n ]\n }\n }\n },\n \"Meta\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"versionId\": {\n \"type\": [\n {\n \"code\": \"id\"\n }\n ]\n },\n \"lastUpdated\": {\n \"type\": [\n {\n \"code\": \"instant\"\n }\n ]\n },\n \"source\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"profile\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/StructureDefinition\"]\n }\n ]\n },\n \"security\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Coding\"\n }\n ]\n },\n \"tag\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Coding\"\n }\n ]\n },\n \"project\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"author\": {\n \"type\": [\n {\n \"code\": \"Reference\"\n }\n ]\n },\n \"onBehalfOf\": {\n \"type\": [\n {\n \"code\": \"Reference\"\n }\n ]\n },\n \"account\": {\n \"type\": [\n {\n \"code\": \"Reference\"\n }\n ]\n },\n \"accounts\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Reference\"\n }\n ]\n },\n \"compartment\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Reference\"\n }\n ]\n }\n }\n },\n \"Money\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"currency\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"Narrative\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"status\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"div\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"xhtml\"\n }\n ]\n }\n }\n },\n \"ParameterDefinition\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"name\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"use\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"min\": {\n \"type\": [\n {\n \"code\": \"integer\"\n }\n ]\n },\n \"max\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"documentation\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"type\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"profile\": {\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/StructureDefinition\"]\n }\n ]\n }\n }\n },\n \"Period\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"start\": {\n \"type\": [\n {\n \"code\": \"dateTime\"\n }\n ]\n },\n \"end\": {\n \"type\": [\n {\n \"code\": \"dateTime\"\n }\n ]\n }\n }\n },\n \"Population\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"age[x]\": {\n \"type\": [\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"gender\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"race\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"physiologicalCondition\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n }\n }\n },\n \"ProdCharacteristic\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"height\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"width\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"depth\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"weight\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"nominalVolume\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"externalDiameter\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"shape\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"color\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"imprint\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"image\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Attachment\"\n }\n ]\n },\n \"scoring\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n }\n }\n },\n \"ProductShelfLife\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"identifier\": {\n \"type\": [\n {\n \"code\": \"Identifier\"\n }\n ]\n },\n \"type\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"period\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"specialPrecautionsForStorage\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n }\n }\n },\n \"Quantity\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"comparator\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"unit\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"Range\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"low\": {\n \"type\": [\n {\n \"code\": \"Quantity\",\n \"profile\": [\"http://hl7.org/fhir/StructureDefinition/SimpleQuantity\"]\n }\n ]\n },\n \"high\": {\n \"type\": [\n {\n \"code\": \"Quantity\",\n \"profile\": [\"http://hl7.org/fhir/StructureDefinition/SimpleQuantity\"]\n }\n ]\n }\n }\n },\n \"Ratio\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"numerator\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"denominator\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n }\n }\n },\n \"Reference\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"reference\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"type\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"identifier\": {\n \"type\": [\n {\n \"code\": \"Identifier\"\n }\n ]\n },\n \"display\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n }\n }\n },\n \"RelatedArtifact\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"type\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"label\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"display\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"citation\": {\n \"type\": [\n {\n \"code\": \"markdown\"\n }\n ]\n },\n \"url\": {\n \"type\": [\n {\n \"code\": \"url\"\n }\n ]\n },\n \"document\": {\n \"type\": [\n {\n \"code\": \"Attachment\"\n }\n ]\n },\n \"resource\": {\n \"type\": [\n {\n \"code\": \"canonical\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/Resource\"]\n }\n ]\n }\n }\n },\n \"SampledData\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"origin\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"Quantity\",\n \"profile\": [\"http://hl7.org/fhir/StructureDefinition/SimpleQuantity\"]\n }\n ]\n },\n \"period\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"factor\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"lowerLimit\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"upperLimit\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"dimensions\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"positiveInt\"\n }\n ]\n },\n \"data\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n }\n }\n },\n \"Signature\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"type\": {\n \"min\": 1,\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Coding\"\n }\n ]\n },\n \"when\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"instant\"\n }\n ]\n },\n \"who\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"Reference\",\n \"targetProfile\": [\n \"http://hl7.org/fhir/StructureDefinition/Practitioner\",\n \"http://hl7.org/fhir/StructureDefinition/PractitionerRole\",\n \"http://hl7.org/fhir/StructureDefinition/RelatedPerson\",\n \"http://hl7.org/fhir/StructureDefinition/Patient\",\n \"http://hl7.org/fhir/StructureDefinition/Device\",\n \"http://hl7.org/fhir/StructureDefinition/Organization\"\n ]\n }\n ]\n },\n \"onBehalfOf\": {\n \"type\": [\n {\n \"code\": \"Reference\",\n \"targetProfile\": [\n \"http://hl7.org/fhir/StructureDefinition/Practitioner\",\n \"http://hl7.org/fhir/StructureDefinition/PractitionerRole\",\n \"http://hl7.org/fhir/StructureDefinition/RelatedPerson\",\n \"http://hl7.org/fhir/StructureDefinition/Patient\",\n \"http://hl7.org/fhir/StructureDefinition/Device\",\n \"http://hl7.org/fhir/StructureDefinition/Organization\"\n ]\n }\n ]\n },\n \"targetFormat\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"sigFormat\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"data\": {\n \"type\": [\n {\n \"code\": \"base64Binary\"\n }\n ]\n }\n }\n },\n \"SubstanceAmount\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"amount[x]\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"string\"\n }\n ]\n },\n \"amountType\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n },\n \"amountText\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"referenceRange\": {\n \"type\": [\n {\n \"code\": \"SubstanceAmountReferenceRange\"\n }\n ]\n }\n }\n },\n \"SubstanceAmountReferenceRange\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"lowLimit\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n },\n \"highLimit\": {\n \"type\": [\n {\n \"code\": \"Quantity\"\n }\n ]\n }\n }\n },\n \"Timing\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"modifierExtension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"event\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"dateTime\"\n }\n ]\n },\n \"repeat\": {\n \"type\": [\n {\n \"code\": \"TimingRepeat\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n }\n ]\n }\n }\n },\n \"TimingRepeat\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"bounds[x]\": {\n \"type\": [\n {\n \"code\": \"Duration\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Period\"\n }\n ]\n },\n \"count\": {\n \"type\": [\n {\n \"code\": \"positiveInt\"\n }\n ]\n },\n \"countMax\": {\n \"type\": [\n {\n \"code\": \"positiveInt\"\n }\n ]\n },\n \"duration\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"durationMax\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"durationUnit\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"frequency\": {\n \"type\": [\n {\n \"code\": \"positiveInt\"\n }\n ]\n },\n \"frequencyMax\": {\n \"type\": [\n {\n \"code\": \"positiveInt\"\n }\n ]\n },\n \"period\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"periodMax\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"periodUnit\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"dayOfWeek\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"timeOfDay\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"time\"\n }\n ]\n },\n \"when\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"offset\": {\n \"type\": [\n {\n \"code\": \"unsignedInt\"\n }\n ]\n }\n }\n },\n \"TriggerDefinition\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"type\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"name\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"timing[x]\": {\n \"type\": [\n {\n \"code\": \"Timing\"\n },\n {\n \"code\": \"Reference\",\n \"targetProfile\": [\"http://hl7.org/fhir/StructureDefinition/Schedule\"]\n },\n {\n \"code\": \"date\"\n },\n {\n \"code\": \"dateTime\"\n }\n ]\n },\n \"data\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"DataRequirement\"\n }\n ]\n },\n \"condition\": {\n \"type\": [\n {\n \"code\": \"Expression\"\n }\n ]\n }\n }\n },\n \"UsageContext\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"code\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"Coding\"\n }\n ]\n },\n \"value[x]\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"CodeableConcept\"\n },\n {\n \"code\": \"Quantity\"\n },\n {\n \"code\": \"Range\"\n },\n {\n \"code\": \"Reference\",\n \"targetProfile\": [\n \"http://hl7.org/fhir/StructureDefinition/PlanDefinition\",\n \"http://hl7.org/fhir/StructureDefinition/ResearchStudy\",\n \"http://hl7.org/fhir/StructureDefinition/InsurancePlan\",\n \"http://hl7.org/fhir/StructureDefinition/HealthcareService\",\n \"http://hl7.org/fhir/StructureDefinition/Group\",\n \"http://hl7.org/fhir/StructureDefinition/Location\",\n \"http://hl7.org/fhir/StructureDefinition/Organization\"\n ]\n }\n ]\n }\n }\n },\n \"MoneyQuantity\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"comparator\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"unit\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"SimpleQuantity\": {\n \"elements\": {\n \"id\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"extension\": {\n \"max\": 9007199254740991,\n \"type\": [\n {\n \"code\": \"Extension\"\n }\n ]\n },\n \"value\": {\n \"type\": [\n {\n \"code\": \"decimal\"\n }\n ]\n },\n \"comparator\": {\n \"max\": 0,\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"unit\": {\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"system\": {\n \"type\": [\n {\n \"code\": \"uri\"\n }\n ]\n },\n \"code\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n }\n }\n },\n \"IdentityProvider\": {\n \"elements\": {\n \"authorizeUrl\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"tokenUrl\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"tokenAuthMethod\": {\n \"type\": [\n {\n \"code\": \"code\"\n }\n ]\n },\n \"userInfoUrl\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"clientId\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"clientSecret\": {\n \"min\": 1,\n \"type\": [\n {\n \"code\": \"string\"\n }\n ]\n },\n \"usePkce\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n }\n ]\n },\n \"useSubject\": {\n \"type\": [\n {\n \"code\": \"boolean\"\n }\n ]\n }\n }\n }\n}\n", "import {\n Bundle,\n Coding,\n ElementDefinition,\n ElementDefinitionBinding,\n Resource,\n StructureDefinition,\n} from '@medplum/fhirtypes';\nimport { DataTypesMap, inflateBaseSchema } from '../base-schema';\nimport baseSchema from '../base-schema.json';\nimport { getTypedPropertyValue } from '../fhirpath/utils';\nimport { OperationOutcomeError, badRequest } from '../outcomes';\nimport { TypedValue, getElementDefinitionTypeName, indexDefaultSearchParameters, isResourceTypeSchema } from '../types';\nimport { capitalize, getExtension, isEmpty } from '../utils';\n\n/**\n * Internal representation of a non-primitive FHIR type, suitable for use in resource validation\n */\nexport interface InternalTypeSchema {\n name: string;\n type: string;\n path: string;\n title?: string;\n url?: string;\n version?: string;\n kind?: string;\n description?: string;\n elements: Record<string, InternalSchemaElement>;\n constraints?: Constraint[];\n parentType?: InternalTypeSchema;\n innerTypes: InternalTypeSchema[];\n summaryProperties?: Set<string>;\n mandatoryProperties?: Set<string>;\n}\n\nexport interface InternalSchemaElement {\n description: string;\n path: string;\n min: number;\n max: number;\n isArray?: boolean;\n constraints?: Constraint[];\n type: ElementType[];\n slicing?: SlicingRules;\n fixed?: TypedValue;\n pattern?: TypedValue;\n binding?: ElementDefinitionBinding;\n}\n\nexport interface ElementType {\n code: string;\n targetProfile?: string[];\n profile?: string[];\n}\n\nexport interface Constraint {\n key: string;\n severity: 'error' | 'warning';\n expression: string;\n description: string;\n}\n\nexport interface SlicingRules {\n discriminator: SliceDiscriminator[];\n ordered: boolean;\n rule?: 'open' | 'closed' | 'openAtEnd';\n slices: SliceDefinition[];\n}\n\nexport interface SliceDefinition extends Omit<InternalSchemaElement, 'slicing'> {\n name: string;\n definition?: string;\n elements: Record<string, InternalSchemaElement>;\n}\n\nexport interface SliceDiscriminator {\n path: string;\n type: string;\n}\n\n/**\n * Parses a StructureDefinition resource into an internal schema better suited for\n * programmatic validation and usage in internal systems\n * @param sd - The StructureDefinition resource to parse\n * @returns The parsed schema for the given resource type\n * @experimental\n */\nexport function parseStructureDefinition(sd: StructureDefinition): InternalTypeSchema {\n return new StructureDefinitionParser(sd).parse();\n}\n\nconst DATA_TYPES: DataTypesMap = inflateBaseSchema(baseSchema);\n\n// profiles are referenced by URL instead of name\nconst PROFILE_SCHEMAS_BY_URL: { [profileUrl: string]: InternalTypeSchema } = Object.create(null);\n\n// Since profiles alter the schemas of their elements, a mapping of type names to schemas\n// is maintained per profile URL\nconst PROFILE_DATA_TYPES: { [profileUrl: string]: DataTypesMap } = Object.create(null);\n\n// Special case names for StructureDefinitions that are technically \"profiles\", but are used as base types.\n// This is for backwards compatibility with R4 StructureDefinitions that are used as base types.\n// MoneyQuantity and SimpleQuantity are technically \"profiles\" on Quantity, but we allow them to be used as base types.\n// ViewDefinition is a special case for SQL-on-FHIR.\n// We can add more types here in the future as necessary, when we want them to be used as base types.\n// For example, exporting new types in \"@medplum/fhirtypes\".\nconst TYPE_SPECIAL_CASES: { [url: string]: string } = {\n 'http://hl7.org/fhir/StructureDefinition/MoneyQuantity': 'MoneyQuantity',\n 'http://hl7.org/fhir/StructureDefinition/SimpleQuantity': 'SimpleQuantity',\n 'http://hl7.org/fhir/uv/sql-on-fhir/StructureDefinition/ViewDefinition': 'ViewDefinition',\n};\n\nfunction getDataTypesMap(profileUrl: string): DataTypesMap {\n let dataTypes: DataTypesMap;\n dataTypes = PROFILE_DATA_TYPES[profileUrl];\n if (!dataTypes) {\n dataTypes = PROFILE_DATA_TYPES[profileUrl] = Object.create(null);\n }\n return dataTypes;\n}\n\n/**\n * Parses and indexes structure definitions\n * @param bundle - Bundle or array of structure definitions to be parsed and indexed\n */\nexport function indexStructureDefinitionBundle(bundle: StructureDefinition[] | Bundle): void {\n const maybeSds = Array.isArray(bundle) ? bundle : (bundle.entry?.map((e) => e.resource) ?? []);\n const sds: StructureDefinition[] = maybeSds.filter((r) => r?.resourceType === 'StructureDefinition');\n indexDefaultSearchParameters(sds);\n for (const sd of sds) {\n loadDataType(sd);\n }\n}\n\nexport function loadDataType(sd: StructureDefinition): void {\n if (!sd?.name) {\n throw new Error(`Failed loading StructureDefinition from bundle`);\n }\n if (sd.resourceType !== 'StructureDefinition') {\n return;\n }\n const schema = parseStructureDefinition(sd);\n const specialCase = TYPE_SPECIAL_CASES[sd.url];\n let dataTypes: DataTypesMap;\n let typeName: string;\n\n if (specialCase) {\n // Special cases by \"name\"\n // These are StructureDefinitions that are technically \"profiles\", but are used as base types\n dataTypes = DATA_TYPES;\n typeName = specialCase;\n } else if (\n // By default, only index by \"type\" for \"official\" FHIR types\n sd.url === `http://hl7.org/fhir/StructureDefinition/${sd.type}` ||\n sd.url === `https://medplum.com/fhir/StructureDefinition/${sd.type}` ||\n sd.type?.startsWith('http://') ||\n sd.type?.startsWith('https://')\n ) {\n dataTypes = DATA_TYPES;\n typeName = sd.type;\n } else {\n dataTypes = getDataTypesMap(sd.url);\n typeName = sd.type;\n }\n\n dataTypes[typeName] = schema;\n\n for (const inner of schema.innerTypes) {\n inner.parentType = schema;\n dataTypes[inner.name] = inner;\n }\n\n PROFILE_SCHEMAS_BY_URL[sd.url] = schema;\n}\n\nexport function getAllDataTypes(): DataTypesMap {\n return DATA_TYPES;\n}\n\nexport function isDataTypeLoaded(type: string): boolean {\n return !!DATA_TYPES[type];\n}\n\nexport function tryGetDataType(type: string, profileUrl?: string): InternalTypeSchema | undefined {\n if (profileUrl) {\n const profileType = getDataTypesMap(profileUrl)[type];\n if (profileType) {\n return profileType;\n }\n }\n // Fallback to base schema if no result found in profileUrl namespace\n return DATA_TYPES[type];\n}\n\nexport function getDataType(type: string, profileUrl?: string): InternalTypeSchema {\n const schema = tryGetDataType(type, profileUrl);\n if (!schema) {\n throw new OperationOutcomeError(badRequest('Unknown data type: ' + type));\n }\n return schema;\n}\n\n/**\n * Returns true if the given string is a valid FHIR resource type.\n *\n * @example\n * ```ts\n * isResourceType('Patient'); // true\n * isResourceType('XYZ'); // false\n * ```\n *\n * @param resourceType - The candidate resource type string.\n * @returns True if the resource type is a valid FHIR resource type.\n */\nexport function isResourceType(resourceType: string): boolean {\n const typeSchema = DATA_TYPES[resourceType];\n return typeSchema && isResourceTypeSchema(typeSchema);\n}\n\nexport function isProfileLoaded(profileUrl: string): boolean {\n return !!PROFILE_SCHEMAS_BY_URL[profileUrl];\n}\n\nexport function tryGetProfile(profileUrl: string): InternalTypeSchema | undefined {\n return PROFILE_SCHEMAS_BY_URL[profileUrl];\n}\n\ninterface BackboneContext {\n type: InternalTypeSchema;\n path: string;\n parent?: BackboneContext;\n}\n\n/**\n * @experimental\n */\nclass StructureDefinitionParser {\n private readonly root: ElementDefinition;\n private readonly elements: ElementDefinition[];\n private readonly elementIndex: Record<string, ElementDefinition>;\n private index: number;\n private readonly resourceSchema: InternalTypeSchema;\n private slicingContext: { field: SlicingRules; current?: SliceDefinition; path: string } | undefined;\n private innerTypes: InternalTypeSchema[];\n private backboneContext: BackboneContext | undefined;\n\n /**\n * @param sd - The StructureDefinition to parse\n * @throws Throws when the StructureDefinition does not have a populated `snapshot` field\n */\n constructor(sd: StructureDefinition) {\n if (!sd.snapshot?.element || sd.snapshot.element.length === 0) {\n throw new Error(`No snapshot defined for StructureDefinition '${sd.name}'`);\n }\n\n this.root = sd.snapshot.element[0];\n this.elements = sd.snapshot.element.slice(1);\n this.elementIndex = Object.create(null);\n this.index = 0;\n this.resourceSchema = {\n name: sd.name as string,\n path: this.root.path,\n title: sd.title,\n type: sd.type,\n url: sd.url as string,\n version: sd.version,\n kind: sd.kind,\n description: getDescription(sd),\n elements: {},\n constraints: this.parseElementDefinition(this.root).constraints,\n innerTypes: [],\n summaryProperties: new Set(),\n mandatoryProperties: new Set(),\n };\n this.innerTypes = [];\n }\n\n parse(): InternalTypeSchema {\n let element = this.next();\n while (element) {\n if (element.sliceName) {\n // Start of slice: this ElementDefinition defines the top-level element of a slice value\n this.parseSliceStart(element);\n } else if (element.id?.includes(':')) {\n // Slice element, part of some slice definition\n if (this.slicingContext?.current) {\n const path = elementPath(element, this.slicingContext.path);\n this.slicingContext.current.elements[path] = this.parseElementDefinition(element);\n }\n } else {\n // Normal field definition\n const field = this.parseElementDefinition(element);\n this.checkFieldEnter(element, field);\n\n // Record field in schema\n let parentContext: BackboneContext | undefined = this.backboneContext;\n while (parentContext) {\n if (element.path?.startsWith(parentContext.path + '.')) {\n parentContext.type.elements[elementPath(element, parentContext.path)] = field;\n break;\n }\n parentContext = parentContext.parent;\n }\n\n if (!parentContext) {\n // Within R4 StructureDefinitions, there are 2 cases where StructureDefinition.name !== ElementDefinition.path.\n // For SimpleQuantity and MoneyQuantity, the names are the names, but the root ElementDefinition.path is Quantity.\n // We need to use StructureDefinition.name for the type name, and ElementDefinition.path for the path.\n const path = elementPath(element, this.root.path);\n if (element.isSummary) {\n this.resourceSchema.summaryProperties?.add(path.replace('[x]', ''));\n }\n if (field.min > 0) {\n this.resourceSchema.mandatoryProperties?.add(path.replace('[x]', ''));\n }\n this.resourceSchema.elements[path] = field;\n }\n\n // Clean up contextual book-keeping\n this.checkFieldExit(element);\n }\n\n element = this.next();\n }\n\n // Wrap up if the StructureDefinition ends on a slice or backbone element\n this.checkFieldExit();\n if (this.innerTypes.length > 0) {\n this.resourceSchema.innerTypes = this.innerTypes;\n }\n\n return this.resourceSchema;\n }\n\n private checkFieldEnter(element: ElementDefinition, field: InternalSchemaElement): void {\n if (this.isInnerType(element)) {\n this.enterInnerType(element);\n }\n if (this.slicingContext && !pathsCompatible(this.slicingContext.path, element?.path as string)) {\n // Path must be compatible with the sliced field path (i.e. have it as a prefix) to be a part of the\n // same slice group; otherwise, that group is finished and this is the start of a new field\n this.slicingContext = undefined;\n }\n if (element.slicing && !this.slicingContext) {\n this.enterSlice(element, field);\n }\n }\n\n private enterInnerType(element: ElementDefinition): void {\n while (this.backboneContext && !pathsCompatible(this.backboneContext?.path, element.path)) {\n // Starting new inner type, unwind type stack to this property's parent\n this.innerTypes.push(this.backboneContext.type);\n this.backboneContext = this.backboneContext.parent;\n }\n const typeName = getElementDefinitionTypeName(element);\n this.backboneContext = {\n type: {\n name: typeName,\n type: typeName,\n path: element.path,\n title: element.label,\n description: element.definition,\n elements: {},\n constraints: this.parseElementDefinition(element).constraints,\n innerTypes: [],\n },\n path: element.path,\n parent: pathsCompatible(this.backboneContext?.path, element.path)\n ? this.backboneContext\n : this.backboneContext?.parent,\n };\n }\n\n private enterSlice(element: ElementDefinition, field: InternalSchemaElement): void {\n if (hasDefaultExtensionSlice(element) && !this.peek()?.sliceName) {\n // Extensions are always sliced by URL; don't start slicing context if no slices follow\n return;\n }\n field.slicing = {\n discriminator: (element.slicing?.discriminator ?? []).map((d) => {\n if (d.type !== 'value' && d.type !== 'pattern' && d.type !== 'type') {\n throw new Error(`Unsupported slicing discriminator type: ${d.type}`);\n }\n return {\n path: d.path as string,\n type: d.type as string,\n };\n }),\n slices: [],\n ordered: element.slicing?.ordered ?? false,\n rule: element.slicing?.rules,\n };\n this.slicingContext = { field: field.slicing, path: element.path ?? '' };\n }\n\n private checkFieldExit(element: ElementDefinition | undefined = undefined): void {\n if (this.backboneContext && !pathsCompatible(this.backboneContext.path, element?.path)) {\n // Leaving BackboneElement child fields\n if (this.backboneContext.parent) {\n do {\n this.innerTypes.push(this.backboneContext.type);\n this.backboneContext = this.backboneContext.parent;\n } while (this.backboneContext && !pathsCompatible(this.backboneContext.path, element?.path));\n } else {\n this.innerTypes.push(this.backboneContext.type);\n this.backboneContext = undefined;\n }\n }\n }\n\n private next(): ElementDefinition | undefined {\n const element = this.peek();\n if (element) {\n this.index++;\n return element;\n }\n return undefined;\n }\n\n private peek(): ElementDefinition | undefined {\n const element = this.elements[this.index];\n if (element) {\n this.elementIndex[element.path ?? ''] = element;\n if (element.contentReference) {\n const contentRefPath = element.contentReference.slice(element.contentReference.indexOf('#') + 1);\n const ref = this.elementIndex[contentRefPath];\n if (!ref) {\n return undefined;\n }\n return {\n ...ref,\n id: element.id,\n path: element.path,\n min: element.min ?? ref.min,\n max: element.max ?? ref.max,\n base: {\n path: ref.base?.path ?? contentRefPath,\n min: element.base?.min ?? ref.base?.min ?? (ref.min as number),\n max: element.base?.max ?? ref.base?.max ?? (ref.max as string),\n },\n contentReference: element.contentReference,\n definition: element.definition,\n };\n }\n return element;\n }\n return undefined;\n }\n\n private isInnerType(current: ElementDefinition): boolean {\n const next = this.peek();\n return !!(\n pathsCompatible(current?.path, next?.path) &&\n current.type?.some((t) => ['BackboneElement', 'Element'].includes(t.code as string))\n );\n }\n\n private parseSliceStart(element: ElementDefinition): void {\n if (!this.slicingContext) {\n throw new Error(`Invalid slice start before discriminator: ${element.sliceName} (${element.id})`);\n }\n\n this.slicingContext.current = {\n ...this.parseElementDefinition(element),\n name: element.sliceName ?? '',\n definition: element.definition,\n elements: {},\n };\n this.slicingContext.field.slices.push(this.slicingContext.current);\n }\n\n private parseElementDefinitionType(ed: ElementDefinition): ElementType[] {\n return (ed.type ?? []).map((type) => {\n let code: string | undefined;\n\n if (type.code === 'BackboneElement' || type.code === 'Element') {\n code = getElementDefinitionTypeName(ed);\n }\n\n if (!code) {\n // This is a low-level extension that we optimize handling for\n code = getExtension(type, 'http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type')?.valueUrl;\n }\n\n if (!code) {\n code = type.code ?? '';\n }\n\n return {\n code,\n targetProfile: type.targetProfile,\n profile: type.profile,\n };\n });\n }\n\n private parseElementDefinition(ed: ElementDefinition): InternalSchemaElement {\n const max = parseCardinality(ed.max as string);\n const baseMax = ed.base?.max ? parseCardinality(ed.base.max) : max;\n const typedElementDef = { type: 'ElementDefinition', value: ed };\n return {\n description: ed.definition || '',\n path: ed.path || ed.base?.path || '',\n min: ed.min ?? 0,\n max: max,\n isArray: baseMax > 1,\n constraints: (ed.constraint ?? []).map((c) => ({\n key: c.key ?? '',\n severity: c.severity ?? 'error',\n expression: c.expression ?? '',\n description: c.human ?? '',\n })),\n type: this.parseElementDefinitionType(ed),\n fixed: firstValue(getTypedPropertyValue(typedElementDef, 'fixed[x]')),\n pattern: firstValue(getTypedPropertyValue(typedElementDef, 'pattern[x]')),\n binding: ed.binding,\n };\n }\n}\n\n/**\n * Construct the subset of a resource containing a minimum set of fields. The returned resource is not guaranteed\n * to contain only the provided properties, and may contain others (e.g. `resourceType` and `id`)\n *\n * @param resource - The resource to subset\n * @param properties - The minimum properties to include in the subset\n * @returns The modified resource, containing the listed properties and possibly other mandatory ones\n */\nexport function subsetResource<T extends Resource>(resource: T | undefined, properties: string[]): T | undefined {\n if (!resource) {\n return undefined;\n }\n const extraProperties = [];\n for (const property of properties) {\n extraProperties.push('_' + property);\n const choiceTypeField = DATA_TYPES[resource.resourceType].elements[property + '[x]'];\n if (choiceTypeField) {\n extraProperties.push(...choiceTypeField.type.map((t) => property + capitalize(t.code)));\n }\n }\n for (const property of Object.getOwnPropertyNames(resource)) {\n if (\n !properties.includes(property) &&\n !extraProperties.includes(property) &&\n !mandatorySubsetProperties.includes(property)\n ) {\n Object.defineProperty(resource, property, {\n enumerable: false,\n writable: false,\n value: undefined,\n });\n }\n }\n resource.meta = { ...resource.meta, tag: resource.meta?.tag ? resource.meta.tag.concat(subsetTag) : [subsetTag] };\n return resource;\n}\nconst subsetTag: Coding = {\n system: 'http://hl7.org/fhir/v3/ObservationValue',\n code: 'SUBSETTED',\n};\nconst mandatorySubsetProperties = ['resourceType', 'id', 'meta'];\n\nfunction parseCardinality(c: string): number {\n return c === '*' ? Number.POSITIVE_INFINITY : Number.parseInt(c, 10);\n}\n\nfunction elementPath(element: ElementDefinition, prefix = ''): string {\n return trimPrefix(element.path, prefix);\n}\n\nfunction trimPrefix(str: string | undefined, prefix: string): string {\n if (!str) {\n return '';\n }\n if (prefix && str.startsWith(prefix)) {\n return str.substring(prefix.length + 1);\n }\n return str;\n}\n\n/**\n * Tests whether two element paths are compatible, i.e. whether the child path is nested under the parent.\n * @param parent - The expected parent path, which should be a prefix of the child path.\n * @param child - The child path to test for compatibility with the parent path.\n * @returns True if the given path is a child of the parent.\n */\nfunction pathsCompatible(parent: string | undefined, child: string | undefined): boolean {\n if (!parent || !child) {\n return false;\n }\n return child.startsWith(parent + '.') || child === parent;\n}\n\nfunction firstValue(obj: TypedValue | TypedValue[] | undefined): TypedValue | undefined {\n if (Array.isArray(obj) && obj.length > 0) {\n return obj[0];\n } else if (!isEmpty(obj)) {\n return obj as TypedValue;\n } else {\n return undefined;\n }\n}\n\nfunction hasDefaultExtensionSlice(element: ElementDefinition): boolean {\n const discriminators = element.slicing?.discriminator;\n return Boolean(\n element.type?.some((t) => t.code === 'Extension') &&\n discriminators?.length === 1 &&\n discriminators[0].type === 'value' &&\n discriminators[0].path === 'url'\n );\n}\n\nfunction getDescription(sd: StructureDefinition): string | undefined {\n let result = sd.description;\n\n // Many description strings start with an unwanted prefix \"Base StructureDefinition for X Type: \"\n // For example:\n // Base StructureDefinition for Age Type: A duration of time during which an organism (or a process) has existed.\n // If the description starts with the name of the resource type, remove it.\n if (result?.startsWith(`Base StructureDefinition for ${sd.name} Type: `)) {\n result = result.substring(`Base StructureDefinition for ${sd.name} Type: `.length);\n }\n\n return result;\n}\n", "import { Resource } from '@medplum/fhirtypes';\nimport { getTypedPropertyValue, GetTypedPropertyValueOptions, toTypedValue } from '../fhirpath/utils';\nimport { isResource, TypedValue } from '../types';\nimport { arrayify } from '../utils';\nimport { getDataType, InternalTypeSchema } from './types';\nimport { isPrimitiveType } from './validation';\n\nexport interface CrawlerVisitor {\n onEnterObject?: (path: string, value: TypedValueWithPath, schema: InternalTypeSchema) => void;\n onExitObject?: (path: string, value: TypedValueWithPath, schema: InternalTypeSchema) => void;\n onEnterResource?: (path: string, value: TypedValueWithPath, schema: InternalTypeSchema) => void;\n onExitResource?: (path: string, value: TypedValueWithPath, schema: InternalTypeSchema) => void;\n visitProperty: (\n parent: TypedValueWithPath,\n key: string,\n path: string,\n propertyValues: (TypedValueWithPath | TypedValueWithPath[])[],\n schema: InternalTypeSchema\n ) => void;\n}\n\n/** @deprecated - Use CrawlerVisitor instead */\nexport type ResourceVisitor = CrawlerVisitor;\n\nexport interface AsyncCrawlerVisitor {\n onEnterObject?: (path: string, value: TypedValueWithPath, schema: InternalTypeSchema) => Promise<void>;\n onExitObject?: (path: string, value: TypedValueWithPath, schema: InternalTypeSchema) => Promise<void>;\n onEnterResource?: (path: string, value: TypedValueWithPath, schema: InternalTypeSchema) => Promise<void>;\n onExitResource?: (path: string, value: TypedValueWithPath, schema: InternalTypeSchema) => Promise<void>;\n visitPropertyAsync: (\n parent: TypedValueWithPath,\n key: string,\n path: string,\n value: TypedValueWithPath | TypedValueWithPath[],\n schema: InternalTypeSchema\n ) => Promise<void>;\n}\n\n/** @deprecated - Use AsyncCrawlerVisitor instead */\nexport type AsyncResourceVisitor = AsyncCrawlerVisitor;\n\nfunction isSchema(obj: InternalTypeSchema | CrawlerOptions): obj is InternalTypeSchema {\n return 'elements' in obj;\n}\n\nfunction isAsync(visitor: CrawlerVisitor | AsyncCrawlerVisitor): visitor is AsyncCrawlerVisitor {\n return Boolean((visitor as AsyncCrawlerVisitor).visitPropertyAsync);\n}\n\n/**\n * Crawls the resource synchronously.\n * @param resource - The resource to crawl.\n * @param visitor - The visitor functions to apply while crawling.\n * @param schema - The schema to use for the resource.\n * @param initialPath - The path within the resource form which to start crawling.\n * @deprecated - Use crawlTypedValue instead\n */\nexport function crawlResource(\n resource: Resource,\n visitor: CrawlerVisitor,\n schema?: InternalTypeSchema,\n initialPath?: string\n): void;\n/**\n * Crawls the resource asynchronously.\n * @param resource - The resource to crawl.\n * @param visitor - The visitor functions to apply while crawling.\n * @param options - Options for how to crawl the resource.\n * @returns void\n * @deprecated - Use crawlTypedValueAsync instead\n */\nexport function crawlResource(resource: Resource, visitor: AsyncCrawlerVisitor, options: CrawlerOptions): Promise<void>;\n/**\n * Crawls the resource synchronously.\n * @param resource - The resource to crawl.\n * @param visitor - The visitor functions to apply while crawling.\n * @param options - Options for how to crawl the resource.\n * @deprecated - Use crawlTypedValue instead\n */\nexport function crawlResource(resource: Resource, visitor: CrawlerVisitor, options?: CrawlerOptions): void;\n\n/**\n * Crawls the resource synchronously.\n * @param resource - The resource to crawl.\n * @param visitor - The visitor functions to apply while crawling.\n * @param schema - The schema to use for the resource.\n * @param initialPath - The path within the resource form which to start crawling.\n * @returns Promise\n * @deprecated - Use crawlTypedValue or crawlTypedValueAsync instead\n */\nexport function crawlResource(\n resource: Resource,\n visitor: CrawlerVisitor | AsyncCrawlerVisitor,\n schema?: InternalTypeSchema | CrawlerOptions,\n initialPath?: string\n): Promise<void> | void {\n let options: CrawlerOptions | undefined;\n if (schema && isSchema(schema)) {\n options = { schema, initialPath };\n } else {\n options = schema;\n }\n\n if (isAsync(visitor)) {\n return crawlTypedValueAsync(toTypedValue(resource), visitor, options);\n } else {\n return crawlTypedValue(toTypedValue(resource), visitor, options);\n }\n}\n\n/**\n * Crawls the resource asynchronously.\n * @param resource - The resource to crawl.\n * @param visitor - The visitor functions to apply while crawling.\n * @param options - Options for how to crawl the resource.\n * @returns Promise\n * @deprecated - Use crawlTypedValueAsync instead\n */\nexport async function crawlResourceAsync(\n resource: Resource,\n visitor: AsyncCrawlerVisitor,\n options: CrawlerOptions\n): Promise<void> {\n return crawlTypedValueAsync(toTypedValue(resource), visitor, options);\n}\n\nexport interface CrawlerOptions {\n skipMissingProperties?: boolean;\n schema?: InternalTypeSchema;\n initialPath?: string;\n}\n\n/** @deprecated - Use CrawlerOptions instead */\nexport type ResourceCrawlerOptions = CrawlerOptions;\n\n/**\n * Crawls the typed value synchronously.\n * @param typedValue - The typed value to crawl.\n * @param visitor - The visitor functions to apply while crawling.\n * @param options - Options for how to crawl the typed value.\n */\nexport function crawlTypedValue(typedValue: TypedValue, visitor: CrawlerVisitor, options?: CrawlerOptions): void {\n new Crawler(typedValue, visitor, options).crawl();\n}\n\n/**\n * Crawls the typed value asynchronously.\n * @param typedValue - The typed value to crawl.\n * @param visitor - The visitor functions to apply while crawling.\n * @param options - Options for how to crawl the typed value.\n * @returns Promise to crawl the typed value.\n */\nexport function crawlTypedValueAsync(\n typedValue: TypedValue,\n visitor: AsyncCrawlerVisitor,\n options?: CrawlerOptions\n): Promise<void> {\n return new AsyncCrawler(typedValue, visitor, options).crawl();\n}\n\nclass Crawler {\n private readonly root: TypedValue;\n private readonly visitor: CrawlerVisitor;\n private readonly schema: InternalTypeSchema;\n private readonly initialPath: string;\n private readonly excludeMissingProperties?: boolean;\n\n constructor(root: TypedValue, visitor: CrawlerVisitor, options?: CrawlerOptions) {\n this.root = root;\n this.visitor = visitor;\n\n this.schema = options?.schema ?? getDataType(root.type);\n this.initialPath = options?.initialPath ?? this.schema.path;\n this.excludeMissingProperties = options?.skipMissingProperties;\n }\n\n crawl(): void {\n this.crawlObject({ ...this.root, path: this.initialPath }, this.schema, this.initialPath);\n }\n\n private crawlObject(obj: TypedValueWithPath, schema: InternalTypeSchema, path: string): void {\n const objIsResource = isResource(obj.value);\n\n if (objIsResource && this.visitor.onEnterResource) {\n this.visitor.onEnterResource(path, obj, schema);\n }\n\n if (this.visitor.onEnterObject) {\n this.visitor.onEnterObject(path, obj, schema);\n }\n\n if (this.excludeMissingProperties) {\n for (const key of Object.keys(obj.value)) {\n this.crawlProperty(obj, key, schema, `${path}.${key}`);\n }\n } else {\n for (const key of Object.keys(schema.elements)) {\n this.crawlProperty(obj, key, schema, `${path}.${key}`);\n }\n }\n\n if (this.visitor.onExitObject) {\n this.visitor.onExitObject(path, obj, schema);\n }\n\n if (objIsResource && this.visitor.onExitResource) {\n this.visitor.onExitResource(path, obj, schema);\n }\n }\n\n private crawlProperty(parent: TypedValueWithPath, key: string, schema: InternalTypeSchema, path: string): void {\n const propertyValues = getNestedProperty(parent, key, { withPath: true });\n if (this.visitor.visitProperty) {\n this.visitor.visitProperty(parent, key, path, propertyValues, schema);\n }\n\n for (const propertyValue of propertyValues) {\n if (propertyValue) {\n for (const value of arrayify(propertyValue) as TypedValueWithPath[]) {\n this.crawlPropertyValue(value, path);\n }\n }\n }\n }\n\n private crawlPropertyValue(value: TypedValueWithPath, path: string): void {\n if (!isPrimitiveType(value.type)) {\n // Recursively crawl as the expected data type\n const type = getDataType(value.type);\n this.crawlObject(value, type, path);\n }\n }\n}\n\nclass AsyncCrawler {\n private readonly root: TypedValue;\n private readonly visitor: AsyncCrawlerVisitor;\n private readonly schema: InternalTypeSchema;\n private readonly initialPath: string;\n private readonly excludeMissingProperties?: boolean;\n\n constructor(root: TypedValue, visitor: AsyncCrawlerVisitor, options?: CrawlerOptions) {\n this.root = root;\n this.visitor = visitor;\n\n this.schema = options?.schema ?? getDataType(root.type);\n this.initialPath = options?.initialPath ?? this.schema.path;\n this.excludeMissingProperties = options?.skipMissingProperties;\n }\n\n async crawl(): Promise<void> {\n return this.crawlObject({ ...this.root, path: this.initialPath }, this.schema, this.initialPath);\n }\n\n private async crawlObject(obj: TypedValueWithPath, schema: InternalTypeSchema, path: string): Promise<void> {\n const objIsResource = isResource(obj.value);\n\n if (objIsResource && this.visitor.onEnterResource) {\n await this.visitor.onEnterResource(path, obj, schema);\n }\n\n if (this.visitor.onEnterObject) {\n await this.visitor.onEnterObject(path, obj, schema);\n }\n\n if (this.excludeMissingProperties && obj.value) {\n for (const key of Object.keys(obj.value)) {\n await this.crawlProperty(obj, key, schema, `${path}.${key}`);\n }\n } else {\n for (const key of Object.keys(schema.elements)) {\n await this.crawlProperty(obj, key, schema, `${path}.${key}`);\n }\n }\n\n if (this.visitor.onExitObject) {\n await this.visitor.onExitObject(path, obj, schema);\n }\n\n if (objIsResource && this.visitor.onExitResource) {\n await this.visitor.onExitResource(path, obj, schema);\n }\n }\n\n private async crawlProperty(\n parent: TypedValueWithPath,\n key: string,\n schema: InternalTypeSchema,\n path: string\n ): Promise<void> {\n const propertyValues = getNestedProperty(parent, key, { withPath: true });\n if (this.visitor.visitPropertyAsync) {\n for (const propertyValue of propertyValues) {\n await this.visitor.visitPropertyAsync(parent, key, path, propertyValue, schema);\n }\n }\n\n for (const propertyValue of propertyValues) {\n if (propertyValue) {\n for (const value of arrayify(propertyValue) as TypedValueWithPath[]) {\n await this.crawlPropertyValue(value, path);\n }\n }\n }\n }\n\n private async crawlPropertyValue(value: TypedValueWithPath, path: string): Promise<void> {\n if (!isPrimitiveType(value.type)) {\n // Recursively crawl as the expected data type\n const type = getDataType(value.type);\n await this.crawlObject(value, type, path);\n }\n }\n}\n\nexport function getNestedProperty(\n value: TypedValueWithPath | undefined,\n key: string,\n options: { profileUrl?: string; withPath: true }\n): (TypedValueWithPath | TypedValueWithPath[])[];\nexport function getNestedProperty(\n value: TypedValue | undefined,\n key: string,\n options?: { profileUrl?: string; withPath?: false }\n): (TypedValue | TypedValue[] | undefined)[];\nexport function getNestedProperty(\n value: TypedValue | undefined,\n key: string,\n options?: { profileUrl?: string; withPath?: boolean }\n): (TypedValue | TypedValue[] | undefined)[] {\n if (value === undefined) {\n return [undefined];\n }\n\n if (key === '$this') {\n return [value];\n }\n\n const propertyGetter = options?.withPath ? getTypedPropertyValueWithPath : getTypedPropertyValue;\n\n const [firstProp, ...nestedProps] = key.split('.');\n let propertyValues = [propertyGetter(value, firstProp, options)];\n for (const prop of nestedProps) {\n const next = [];\n for (const current of propertyValues) {\n if (Array.isArray(current)) {\n for (const element of current) {\n next.push(propertyGetter(element, prop, options));\n }\n } else if (options?.withPath && current && current.value !== undefined) {\n next.push(propertyGetter(current, prop, options));\n } else if (!options?.withPath && current !== undefined) {\n next.push(propertyGetter(current, prop, options));\n }\n }\n propertyValues = next;\n }\n return propertyValues;\n}\n\nexport function getTypedPropertyValueWithPath(\n input: TypedValue | TypedValueWithPath,\n path: string,\n options?: GetTypedPropertyValueOptions\n): TypedValueWithPath[] | TypedValueWithPath {\n const parentPath = (input as TypedValueWithPath).path;\n return withPath(getTypedPropertyValue(input, path, options), parentPath, path);\n}\n\nexport type TypedValueWithPath = TypedValue & { path: string };\n\nfunction withPath(\n tv: TypedValue | TypedValue[] | undefined,\n parentPath: string | undefined,\n key: string\n): TypedValueWithPath | TypedValueWithPath[] {\n const parentPrefix = parentPath ? parentPath + '.' : '';\n\n if (tv === undefined) {\n return { type: 'undefined', value: undefined, path: `${parentPrefix}${key}` };\n }\n\n if (Array.isArray(tv)) {\n return tv.map((v, idx) => ({\n ...v,\n path: `${parentPrefix}${key}[${idx}]`,\n }));\n }\n\n return { ...tv, path: `${parentPrefix}${key}` };\n}\n", "import { OperationOutcomeIssue, Resource, StructureDefinition } from '@medplum/fhirtypes';\nimport { HTTP_HL7_ORG, UCUM } from '../constants';\nimport { evalFhirPathTyped } from '../fhirpath/parse';\nimport { getTypedPropertyValue, toTypedValue } from '../fhirpath/utils';\nimport {\n OperationOutcomeError,\n createConstraintIssue,\n createOperationOutcomeIssue,\n createProcessingIssue,\n createStructureIssue,\n validationError,\n} from '../outcomes';\nimport { PropertyType, TypedValue, isReference, isResource } from '../types';\nimport { arrayify, deepEquals, deepIncludes, isEmpty } from '../utils';\nimport { CrawlerVisitor, TypedValueWithPath, crawlTypedValue, getNestedProperty } from './crawler';\nimport {\n Constraint,\n InternalSchemaElement,\n InternalTypeSchema,\n SliceDefinition,\n SliceDiscriminator,\n SlicingRules,\n getDataType,\n parseStructureDefinition,\n} from './types';\n\n/*\n * This file provides schema validation utilities for FHIR JSON objects.\n *\n * See: [JSON Representation of Resources](https://hl7.org/fhir/json.html)\n * See: [FHIR Data Types](https://www.hl7.org/fhir/datatypes.html)\n */\nexport const fhirTypeToJsType = {\n base64Binary: 'string',\n boolean: 'boolean',\n canonical: 'string',\n code: 'string',\n date: 'string',\n dateTime: 'string',\n decimal: 'number',\n id: 'string',\n instant: 'string',\n integer: 'number',\n integer64: 'string',\n markdown: 'string',\n oid: 'string',\n positiveInt: 'number',\n string: 'string',\n time: 'string',\n unsignedInt: 'number',\n uri: 'string',\n url: 'string',\n uuid: 'string',\n xhtml: 'string',\n 'http://hl7.org/fhirpath/System.String': 'string', // Not actually a FHIR type, but included in some StructureDefinition resources\n} as const satisfies Record<string, 'string' | 'boolean' | 'number'>;\n\n/**\n * Returns true if the type code is a primitive type.\n * @param code - The type code to check.\n * @returns True if the type code is a primitive type.\n */\nexport function isPrimitiveType(code: string): boolean {\n return code === 'undefined' || code in fhirTypeToJsType;\n}\n\n/*\n * This file provides schema validation utilities for FHIR JSON objects.\n *\n * See: [JSON Representation of Resources](https://hl7.org/fhir/json.html)\n * See: [FHIR Data Types](https://www.hl7.org/fhir/datatypes.html)\n */\nexport const validationRegexes: Record<string, RegExp> = {\n base64Binary: /^([A-Za-z\\d+/]{4})*([A-Za-z\\d+/]{2}==|[A-Za-z\\d+/]{3}=)?$/,\n canonical: /^\\S*$/,\n code: /^[^\\s]+( [^\\s]+)*$/,\n date: /^(\\d(\\d(\\d[1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2]\\d|3[0-1]))?)?$/,\n dateTime:\n /^(\\d(\\d(\\d[1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2]\\d|3[0-1])(T([01]\\d|2[0-3])(:[0-5]\\d:([0-5]\\d|60)(\\.\\d{1,9})?)?)?)?(Z|[+-]((0\\d|1[0-3]):[0-5]\\d|14:00)?)?)?$/,\n id: /^[A-Za-z0-9\\-.]{1,64}$/,\n instant:\n /^(\\d(\\d(\\d[1-9]|[1-9]0)|[1-9]00)|[1-9]000)-(0[1-9]|1[0-2])-(0[1-9]|[1-2]\\d|3[0-1])T([01]\\d|2[0-3]):[0-5]\\d:([0-5]\\d|60)(\\.\\d{1,9})?(Z|[+-]((0\\d|1[0-3]):[0-5]\\d|14:00))$/,\n markdown: /^[\\s\\S]+$/,\n oid: /^urn:oid:[0-2](\\.(0|[1-9]\\d*))+$/,\n string: /^[\\s\\S]+$/,\n time: /^([01]\\d|2[0-3]):[0-5]\\d:([0-5]\\d|60)(\\.\\d{1,9})?$/,\n uri: /^\\S*$/,\n url: /^\\S*$/,\n uuid: /^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,\n xhtml: /.*/,\n} as const;\n\n/**\n * List of constraint keys that aren't to be checked in an expression.\n */\nconst skippedConstraintKeys: Record<string, boolean> = {\n 'ele-1': true,\n 'dom-3': true, // If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource (requries \"descendants()\")\n 'org-1': true, // The organization SHALL at least have a name or an identifier, and possibly more than one (back compat)\n 'sdf-19': true, // FHIR Specification models only use FHIR defined types\n};\n\nexport interface ValidatorOptions {\n profile?: StructureDefinition;\n}\n\nexport function validateResource(resource: Resource, options?: ValidatorOptions): OperationOutcomeIssue[] {\n if (!resource.resourceType) {\n throw new OperationOutcomeError(validationError('Missing resource type'));\n }\n return new ResourceValidator(toTypedValue(resource), options).validate();\n}\n\nexport function validateTypedValue(typedValue: TypedValue, options?: ValidatorOptions): OperationOutcomeIssue[] {\n return new ResourceValidator(typedValue, options).validate();\n}\n\nclass ResourceValidator implements CrawlerVisitor {\n private issues: OperationOutcomeIssue[];\n private root: TypedValue;\n private currentResource: Resource[];\n private readonly schema: InternalTypeSchema;\n\n constructor(typedValue: TypedValue, options?: ValidatorOptions) {\n this.issues = [];\n this.root = typedValue;\n this.currentResource = [];\n if (isResource(typedValue.value)) {\n this.currentResource.push(typedValue.value);\n }\n if (!options?.profile) {\n this.schema = getDataType(typedValue.type);\n } else {\n this.schema = parseStructureDefinition(options.profile);\n }\n }\n\n validate(): OperationOutcomeIssue[] {\n // Check root constraints\n this.constraintsCheck({ ...this.root, path: this.schema.path }, this.schema);\n\n checkObjectForNull(this.root.value as unknown as Record<string, unknown>, this.schema.path, this.issues);\n\n crawlTypedValue(this.root, this, { schema: this.schema, initialPath: this.schema.path });\n\n const issues = this.issues;\n\n let foundError = false;\n for (const issue of issues) {\n if (issue.severity === 'error') {\n foundError = true;\n }\n }\n\n if (foundError) {\n throw new OperationOutcomeError({\n resourceType: 'OperationOutcome',\n issue: issues,\n });\n }\n\n return issues;\n }\n\n onExitObject(_path: string, obj: TypedValueWithPath, schema: InternalTypeSchema): void {\n //@TODO(mattwiller 2023-06-05): Detect extraneous properties in a single pass by keeping track of all keys that\n // were correctly matched to resource properties as elements are validated above\n this.checkAdditionalProperties(obj, schema.elements, obj.path);\n }\n\n onEnterResource(_path: string, obj: TypedValueWithPath): void {\n this.currentResource.push(obj.value);\n }\n\n onExitResource(): void {\n this.currentResource.pop();\n }\n\n visitProperty(\n _parent: TypedValueWithPath,\n key: string,\n path: string,\n propertyValues: (TypedValueWithPath | TypedValueWithPath[])[],\n schema: InternalTypeSchema\n ): void {\n const element = schema.elements[key];\n if (!element) {\n throw new Error(`Missing element validation schema for ${key}`);\n }\n\n for (const value of propertyValues) {\n if (!this.checkPresence(value, element, path)) {\n return;\n }\n // Check cardinality\n let values: TypedValueWithPath[];\n if (element.isArray) {\n if (!Array.isArray(value)) {\n this.issues.push(createStructureIssue(path, 'Expected array of values for property'));\n return;\n }\n values = value;\n } else {\n if (Array.isArray(value)) {\n this.issues.push(createStructureIssue(path, 'Expected single value for property'));\n return;\n }\n values = [value];\n }\n\n if (values.length < element.min || values.length > element.max) {\n this.issues.push(\n createStructureIssue(\n element.path,\n `Invalid number of values: expected ${element.min}..${\n Number.isFinite(element.max) ? element.max : '*'\n }, but found ${values.length}`\n )\n );\n }\n\n if (!matchesSpecifiedValue(value, element)) {\n this.issues.push(createStructureIssue(path, 'Value did not match expected pattern'));\n }\n\n const sliceCounts: Record<string, number> | undefined = element.slicing\n ? Object.fromEntries(element.slicing.slices.map((s) => [s.name, 0]))\n : undefined;\n for (const value of values) {\n this.constraintsCheck(value, element);\n this.referenceTypeCheck(value, element);\n this.checkPropertyValue(value);\n\n const sliceName = checkSliceElement(value, element.slicing);\n if (sliceName && sliceCounts) {\n sliceCounts[sliceName] += 1;\n }\n }\n\n this.validateSlices(element.slicing?.slices, sliceCounts, path);\n }\n }\n\n private checkPresence(\n value: TypedValueWithPath | TypedValueWithPath[],\n field: InternalSchemaElement,\n path: string\n ): boolean {\n if (!Array.isArray(value) && value.value === undefined) {\n if (field.min > 0) {\n this.issues.push(createStructureIssue(value.path, 'Missing required property'));\n }\n return false;\n }\n\n if (isEmpty(value)) {\n this.issues.push(createStructureIssue(path, 'Invalid empty value'));\n return false;\n }\n\n return true;\n }\n\n private checkPropertyValue(value: TypedValueWithPath): void {\n if (isPrimitiveType(value.type)) {\n this.validatePrimitiveType(value);\n }\n }\n\n private validateSlices(\n slices: SliceDefinition[] | undefined,\n counts: Record<string, number> | undefined,\n path: string\n ): void {\n if (!slices || !counts) {\n return;\n }\n for (const slice of slices) {\n const sliceCardinality = counts[slice.name];\n if (sliceCardinality < slice.min || sliceCardinality > slice.max) {\n this.issues.push(\n createStructureIssue(\n path,\n `Incorrect number of values provided for slice '${slice.name}': expected ${slice.min}..${\n Number.isFinite(slice.max) ? slice.max : '*'\n }, but found ${sliceCardinality}`\n )\n );\n }\n }\n }\n\n private checkAdditionalProperties(\n parent: TypedValueWithPath,\n properties: Record<string, InternalSchemaElement>,\n path: string\n ): void {\n const object = parent.value as Record<string, unknown> | undefined;\n if (!object) {\n return;\n }\n const choiceOfTypeElements: Record<string, string> = {};\n for (const key of Object.keys(object)) {\n if (key === 'resourceType') {\n continue; // Skip special resource type discriminator property in JSON\n }\n const choiceOfTypeElementName = isChoiceOfType(parent, key, properties);\n if (choiceOfTypeElementName) {\n // check that the type of the primitive extension matches the type of the property\n let relatedElementName: string;\n let requiredRelatedElementName: string;\n if (choiceOfTypeElementName.startsWith('_')) {\n relatedElementName = choiceOfTypeElementName.slice(1);\n requiredRelatedElementName = key.slice(1);\n } else {\n relatedElementName = '_' + choiceOfTypeElementName;\n requiredRelatedElementName = '_' + key;\n }\n\n if (\n relatedElementName in choiceOfTypeElements &&\n choiceOfTypeElements[relatedElementName] !== requiredRelatedElementName\n ) {\n this.issues.push(\n createOperationOutcomeIssue(\n 'warning',\n 'structure',\n `Type of primitive extension does not match the type of property \"${choiceOfTypeElementName.startsWith('_') ? choiceOfTypeElementName.slice(1) : choiceOfTypeElementName}\"`,\n choiceOfTypeElementName\n )\n );\n }\n\n if (choiceOfTypeElements[choiceOfTypeElementName]) {\n // Found a duplicate choice of type property\n // TODO: This should be an error, but it's currently a warning to avoid breaking existing code\n // Warnings are logged, but do not cause validation to fail\n this.issues.push(\n createOperationOutcomeIssue(\n 'warning',\n 'structure',\n `Conflicting choice of type properties: \"${key}\", \"${choiceOfTypeElements[choiceOfTypeElementName]}\"`,\n key\n )\n );\n }\n choiceOfTypeElements[choiceOfTypeElementName] = key;\n continue;\n }\n if (!(key in properties) && !(key.startsWith('_') && key.slice(1) in properties)) {\n this.issues.push(createStructureIssue(`${path}.${key}`, `Invalid additional property \"${key}\"`));\n }\n }\n }\n\n private constraintsCheck(value: TypedValueWithPath, field: InternalTypeSchema | InternalSchemaElement): void {\n const constraints = field.constraints;\n if (!constraints) {\n return;\n }\n for (const constraint of constraints) {\n if (constraint.severity === 'error' && !(constraint.key in skippedConstraintKeys)) {\n const expression = this.isExpressionTrue(constraint, value);\n if (!expression) {\n this.issues.push(createConstraintIssue(value.path, constraint));\n return;\n }\n }\n }\n }\n\n private referenceTypeCheck(value: TypedValueWithPath, field: InternalSchemaElement): void {\n if (value.type !== 'Reference') {\n return;\n }\n\n const reference = value.value;\n if (!isReference(reference)) {\n // Silently ignore unrecognized reference types\n return;\n }\n\n const referenceResourceType = reference.reference.split('/')[0];\n if (!referenceResourceType) {\n // Silently ignore empty references - that will get picked up by constraint validation\n return;\n }\n\n const targetProfiles = field.type.find((t) => t.code === 'Reference')?.targetProfile;\n if (!targetProfiles) {\n // No required target profiles\n return;\n }\n\n const hl7BaseUrl = HTTP_HL7_ORG + '/fhir/StructureDefinition/';\n const hl7AllResourcesUrl = hl7BaseUrl + 'Resource';\n const hl7ResourceTypeUrl = hl7BaseUrl + referenceResourceType;\n\n const medplumBaseUrl = 'https://medplum.com/fhir/StructureDefinition/';\n const medplumResourceTypeUrl = medplumBaseUrl + referenceResourceType;\n\n for (const targetProfile of targetProfiles) {\n if (\n targetProfile === hl7AllResourcesUrl ||\n targetProfile === hl7ResourceTypeUrl ||\n targetProfile === medplumResourceTypeUrl\n ) {\n // Found a matching profile\n return;\n }\n\n if (!targetProfile.startsWith(hl7BaseUrl) && !targetProfile.startsWith(medplumBaseUrl)) {\n // This is an unrecognized target profile string\n // For example, it could be US-Core or a custom profile definition\n // Example: http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient\n // And therefore we cannot validate\n return;\n }\n }\n\n // All of the target profiles were recognized formats\n // and we did not find a match\n // TODO: This should be an error, but it's currently a warning to avoid breaking existing code\n // Warnings are logged, but do not cause validation to fail\n this.issues.push(\n createOperationOutcomeIssue(\n 'warning',\n 'structure',\n `Invalid reference: got \"${referenceResourceType}\", expected \"${targetProfiles.join('\", \"')}\"`,\n value.path\n )\n );\n }\n\n private isExpressionTrue(constraint: Constraint, value: TypedValueWithPath): boolean {\n const variables: Record<string, TypedValue> = {\n '%context': value,\n '%ucum': toTypedValue(UCUM),\n };\n\n if (this.currentResource.length > 0) {\n variables['%resource'] = toTypedValue(this.currentResource[this.currentResource.length - 1]);\n }\n\n if (isResource(this.root.value)) {\n variables['%rootResource'] = this.root;\n }\n\n try {\n const evalValues = evalFhirPathTyped(constraint.expression, [value], variables);\n\n return evalValues.length === 1 && evalValues[0].value === true;\n } catch (e: any) {\n this.issues.push(\n createProcessingIssue(value.path, 'Error evaluating invariant expression', e, {\n fhirpath: constraint.expression,\n })\n );\n return false;\n }\n }\n\n private validatePrimitiveType(typedValue: TypedValueWithPath): void {\n const [primitiveValue, extensionElement] = unpackPrimitiveElement(typedValue);\n const path = typedValue.path;\n\n if (primitiveValue) {\n const { type, value } = primitiveValue;\n // First, make sure the value is the correct JS type\n if (!(type in fhirTypeToJsType)) {\n this.issues.push(createStructureIssue(path, `Invalid JSON type: ${type} is not a valid FHIR type`));\n return;\n }\n const expectedType = fhirTypeToJsType[type as keyof typeof fhirTypeToJsType];\n // biome-ignore lint/suspicious/useValidTypeof: expected value ensured to be one of: 'string' | 'boolean' | 'number'\n if (typeof value !== expectedType) {\n if (value !== null) {\n this.issues.push(\n createStructureIssue(path, `Invalid JSON type: expected ${expectedType}, but got ${typeof value}`)\n );\n }\n return;\n }\n // Then, perform additional checks for specialty types\n if (expectedType === 'string') {\n this.validateString(value as string, type, path);\n } else if (expectedType === 'number') {\n this.validateNumber(value as number, type, path);\n }\n }\n if (extensionElement) {\n crawlTypedValue(extensionElement, this, { schema: getDataType('Element'), initialPath: path });\n }\n }\n\n private validateString(str: string, type: string, path: string): void {\n if (!str.trim()) {\n this.issues.push(createStructureIssue(path, 'String must contain non-whitespace content'));\n return;\n }\n\n const regex = validationRegexes[type];\n if (regex && !regex.exec(str)) {\n this.issues.push(createStructureIssue(path, 'Invalid ' + type + ' format'));\n }\n }\n\n private validateNumber(n: number, type: string, path: string): void {\n if (Number.isNaN(n) || !Number.isFinite(n)) {\n this.issues.push(createStructureIssue(path, 'Invalid numeric value'));\n } else if (isIntegerType(type) && !Number.isInteger(n)) {\n this.issues.push(createStructureIssue(path, 'Expected number to be an integer'));\n } else if (type === PropertyType.positiveInt && n <= 0) {\n this.issues.push(createStructureIssue(path, 'Expected number to be positive'));\n } else if (type === PropertyType.unsignedInt && n < 0) {\n this.issues.push(createStructureIssue(path, 'Expected number to be non-negative'));\n }\n }\n}\n\nfunction isIntegerType(propertyType: string): boolean {\n return (\n propertyType === PropertyType.integer ||\n propertyType === PropertyType.positiveInt ||\n propertyType === PropertyType.unsignedInt\n );\n}\n\n/**\n * Returns the choice-of-type element name if the key is a choice of type property.\n * Returns undefined if the key is not a choice of type property.\n * @param typedValue - The value to check.\n * @param key - The object key to check. This is different than the element name, which could contain \"[x]\".\n * @param propertyDefinitions - The property definitions for the object..\n * @returns The element name if a choice of type property is present, otherwise undefined.\n */\nfunction isChoiceOfType(\n typedValue: TypedValue,\n key: string,\n propertyDefinitions: Record<string, InternalSchemaElement>\n): string | undefined {\n let prefix = '';\n if (key.startsWith('_')) {\n key = key.slice(1);\n prefix = '_';\n }\n const parts = key.split(/(?=[A-Z])/g); // Split before capital letters\n let testProperty = '';\n for (const part of parts) {\n testProperty += part;\n const elementName = testProperty + '[x]';\n if (propertyDefinitions[elementName]) {\n const typedPropertyValue = getTypedPropertyValue(typedValue, testProperty);\n return typedPropertyValue ? prefix + elementName : undefined;\n }\n }\n return undefined;\n}\n\nfunction checkObjectForNull(obj: Record<string, unknown>, path: string, issues: OperationOutcomeIssue[]): void {\n for (const [key, value] of Object.entries(obj)) {\n const propertyPath = `${path}.${key}`;\n const partnerKey = key.startsWith('_') ? key.slice(1) : `_${key}`;\n if (value === null) {\n issues.push(createStructureIssue(propertyPath, 'Invalid null value'));\n } else if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n if (value[i] === undefined) {\n issues.push(createStructureIssue(`${propertyPath}[${i}]`, 'Invalid undefined value'));\n } else if (value[i] === null && !(obj[partnerKey] as any)?.[i]) {\n // This tests for the one case where `null` is allowed in FHIR JSON, where an array of primitive values\n // has extensions for some but not all values\n issues.push(createStructureIssue(`${propertyPath}[${i}]`, 'Invalid null value'));\n } else if (value[i]) {\n checkObjectForNull(value[i], `${propertyPath}[${i}]`, issues);\n }\n }\n } else if (typeof value === 'object') {\n checkObjectForNull(value as Record<string, unknown>, propertyPath, issues);\n }\n }\n}\n\nfunction matchesSpecifiedValue(value: TypedValue | TypedValue[], element: InternalSchemaElement): boolean {\n // It is possible that `value` has additional keys beyond `type` and `value` (e.g. `expression` if a\n // `TypedValueWithExpression` is being used), so ensure that only `type` and `value` are considered for comparison.\n const typeAndValue = Array.isArray(value)\n ? value.map((v) => ({ type: v.type, value: v.value }))\n : { type: value.type, value: value.value };\n\n if (element.pattern && !deepIncludes(typeAndValue, element.pattern)) {\n return false;\n }\n if (element.fixed && !deepEquals(typeAndValue, element.fixed)) {\n return false;\n }\n return true;\n}\n\nexport function matchDiscriminant(\n value: TypedValue | TypedValue[] | undefined,\n discriminator: SliceDiscriminator,\n slice: SliceDefinition,\n elements?: Record<string, InternalSchemaElement>\n): boolean {\n if (Array.isArray(value)) {\n // Only single values can match\n return false;\n }\n\n let sliceElement: InternalSchemaElement | undefined;\n if (discriminator.path === '$this') {\n sliceElement = slice;\n } else {\n sliceElement = (elements ?? slice.elements)[discriminator.path];\n }\n\n const sliceType = slice.type;\n switch (discriminator.type) {\n case 'value':\n case 'pattern':\n if (!value || !sliceElement) {\n return false;\n }\n if (sliceElement.pattern) {\n return deepIncludes(value, sliceElement.pattern);\n }\n if (sliceElement.fixed) {\n return deepEquals(value, sliceElement.fixed);\n }\n\n if (sliceElement.binding?.strength === 'required' && sliceElement.binding.valueSet) {\n // This cannot be implemented correctly without asynchronous validation, so make it permissive for now.\n // Ideally this should check something like value.value.coding.some((code) => isValidCode(sliceElement.binding.valueSet, code))\n // where isValidCode is a function that checks if the code is included in the expansion of the ValueSet\n return true;\n }\n break;\n case 'type':\n if (!value || !sliceType?.length) {\n return false;\n }\n return sliceType.some((t) => t.code === value.type);\n // Other discriminator types are not yet supported, see http://hl7.org/fhir/R4/profiling.html#discriminator\n }\n // Default to no match\n return false;\n}\n\nfunction checkSliceElement(value: TypedValue, slicingRules: SlicingRules | undefined): string | undefined {\n if (!slicingRules) {\n return undefined;\n }\n for (const slice of slicingRules.slices) {\n if (\n slicingRules.discriminator.every((discriminator) =>\n arrayify(getNestedProperty(value, discriminator.path))?.some((v) => matchDiscriminant(v, discriminator, slice))\n )\n ) {\n return slice.name;\n }\n }\n return undefined;\n}\n\nfunction unpackPrimitiveElement(v: TypedValue): [TypedValue | undefined, TypedValue | undefined] {\n if (typeof v.value !== 'object' || !v.value) {\n return [v, undefined];\n }\n const primitiveValue = v.value.valueOf();\n if (primitiveValue === v.value) {\n return [undefined, { type: 'Element', value: v.value }];\n }\n const primitiveKeys = new Set(Object.keys(primitiveValue));\n const extensionEntries = Object.entries(v.value).filter(([k, _]) => !primitiveKeys.has(k));\n const extensionElement = extensionEntries.length > 0 ? Object.fromEntries(extensionEntries) : undefined;\n return [\n { type: v.type, value: primitiveValue },\n { type: 'Element', value: extensionElement },\n ];\n}\n", "import { Coding, Extension, Period, Quantity } from '@medplum/fhirtypes';\nimport { PropertyType, TypedValue, getElementDefinition, isResource } from '../types';\nimport { InternalSchemaElement } from '../typeschema/types';\nimport { validationRegexes } from '../typeschema/validation';\nimport { capitalize, isCodeableConcept, isCoding, isEmpty } from '../utils';\n\n/**\n * Returns a single element array with a typed boolean value.\n * @param value - The primitive boolean value.\n * @returns Single element array with a typed boolean value.\n */\nexport function booleanToTypedValue(value: boolean): [TypedValue] {\n return [{ type: PropertyType.boolean, value }];\n}\n\n/**\n * Returns a \"best guess\" TypedValue for a given value.\n * @param value - The unknown value to check.\n * @returns A \"best guess\" TypedValue for the given value.\n */\nexport function toTypedValue(value: unknown): TypedValue {\n if (value === null || value === undefined) {\n return { type: 'undefined', value: undefined };\n } else if (Number.isSafeInteger(value)) {\n return { type: PropertyType.integer, value };\n } else if (typeof value === 'number') {\n return { type: PropertyType.decimal, value };\n } else if (typeof value === 'boolean') {\n return { type: PropertyType.boolean, value };\n } else if (typeof value === 'string') {\n return { type: PropertyType.string, value };\n } else if (isQuantity(value)) {\n return { type: PropertyType.Quantity, value };\n } else if (isResource(value)) {\n return { type: value.resourceType, value };\n } else if (isCodeableConcept(value)) {\n return { type: PropertyType.CodeableConcept, value };\n } else if (isCoding(value)) {\n return { type: PropertyType.Coding, value };\n } else {\n return { type: PropertyType.BackboneElement, value };\n }\n}\n\n/**\n * Converts unknown object into a JavaScript boolean.\n * Note that this is different than the FHIRPath \"toBoolean\",\n * which has particular semantics around arrays, empty arrays, and type conversions.\n * @param obj - Any value or array of values.\n * @returns The converted boolean value according to FHIRPath rules.\n */\nexport function toJsBoolean(obj: TypedValue[]): boolean {\n return obj.length === 0 ? false : !!obj[0].value;\n}\n\nexport function singleton(collection: TypedValue[], type?: string): TypedValue | undefined {\n if (collection.length === 0) {\n return undefined;\n } else if (collection.length === 1 && (!type || collection[0].type === type)) {\n return collection[0];\n } else {\n throw new Error(`Expected singleton of type ${type}, but found ${JSON.stringify(collection)}`);\n }\n}\n\nexport interface GetTypedPropertyValueOptions {\n /** (optional) URL of a resource profile for type resolution */\n profileUrl?: string;\n}\n\n/**\n * Returns the value of the property and the property type.\n * Some property definitions support multiple types.\n * For example, \"Observation.value[x]\" can be \"valueString\", \"valueInteger\", \"valueQuantity\", etc.\n * According to the spec, there can only be one property for a given element definition.\n * This function returns the value and the type.\n * @param input - The base context (FHIR resource or backbone element).\n * @param path - The property path.\n * @param options - (optional) Additional options\n * @returns The value of the property and the property type.\n */\nexport function getTypedPropertyValue(\n input: TypedValue,\n path: string,\n options?: GetTypedPropertyValueOptions\n): TypedValue[] | TypedValue | undefined {\n if (!input.value) {\n return undefined;\n }\n\n const elementDefinition = getElementDefinition(input.type, path, options?.profileUrl);\n if (elementDefinition) {\n return getTypedPropertyValueWithSchema(input, path, elementDefinition);\n }\n\n return getTypedPropertyValueWithoutSchema(input, path);\n}\n\n/**\n * Returns the value of the property and the property type using a type schema.\n * @param typedValue - The base context (FHIR resource or backbone element).\n * @param path - The property path.\n * @param element - The property element definition.\n * @returns The value of the property and the property type.\n */\nexport function getTypedPropertyValueWithSchema(\n typedValue: TypedValue,\n path: string,\n element: InternalSchemaElement\n): TypedValue[] | TypedValue | undefined {\n // Consider the following cases of the inputs:\n\n // \"path\" input types:\n // 1. Simple path, e.g., \"name\"\n // 2. Choice-of-type without type, e.g., \"value[x]\"\n // 3. Choice-of-type with type, e.g., \"valueBoolean\"\n\n // \"element\" can be either:\n // 1. Full ElementDefinition from a well-formed StructureDefinition\n // 2. Partial ElementDefinition from base-schema.json\n\n // \"types\" input types:\n // 1. Simple single type, e.g., \"string\"\n // 2. Choice-of-type with full array of types, e.g., [\"string\", \"integer\", \"Quantity\"]\n // 3. Choice-of-type with single array of types, e.g., [\"Quantity\"]\n\n // Note that FHIR Profiles can define a single type for a choice-of-type element.\n // e.g. https://build.fhir.org/ig/HL7/US-Core/StructureDefinition-us-core-birthsex.html\n // Therefore, cannot only check for endsWith('[x]') since FHIRPath uses this code path\n // with a path of 'value' and expects Choice of Types treatment\n\n const value = typedValue.value;\n const types = element.type;\n if (!types || types.length === 0) {\n return undefined;\n }\n\n // The path parameter can be in both \"value[x]\" form and \"valueBoolean\" form.\n // So we need to use the element path to find the type.\n let resultValue: any = undefined;\n let resultType = 'undefined';\n let primitiveExtension: Extension[] | undefined = undefined;\n\n const lastPathSegmentIndex = element.path.lastIndexOf('.');\n const lastPathSegment = element.path.substring(lastPathSegmentIndex + 1);\n for (const type of types) {\n const candidatePath = lastPathSegment.replace('[x]', capitalize(type.code));\n resultValue = value[candidatePath];\n primitiveExtension = value['_' + candidatePath];\n if (resultValue !== undefined || primitiveExtension !== undefined) {\n resultType = type.code;\n break;\n }\n }\n\n // When checking for primitive extensions, we must use the \"resolved\" path.\n // In the case of [x] choice-of-type, the type must be resolved to a single type.\n if (primitiveExtension) {\n if (Array.isArray(resultValue)) {\n // Slice to avoid mutating the array in the input value\n resultValue = resultValue.slice();\n for (let i = 0; i < Math.max(resultValue.length, primitiveExtension.length); i++) {\n resultValue[i] = assignPrimitiveExtension(resultValue[i], primitiveExtension[i]);\n }\n } else {\n resultValue = assignPrimitiveExtension(resultValue, primitiveExtension);\n }\n }\n\n if (isEmpty(resultValue)) {\n return undefined;\n }\n\n if (resultType === 'Element' || resultType === 'BackboneElement') {\n resultType = element.type[0].code;\n }\n\n if (Array.isArray(resultValue)) {\n return resultValue.map((element) => toTypedValueWithType(element, resultType));\n } else {\n return toTypedValueWithType(resultValue, resultType);\n }\n}\n\nfunction toTypedValueWithType(value: any, type: string): TypedValue {\n if (type === 'Resource' && isResource(value)) {\n type = value.resourceType;\n }\n return { type, value };\n}\n\n/**\n * Returns the value of the property and the property type using a type schema.\n * Note that because the type schema is not available, this function may be inaccurate.\n * In some cases, that is the desired behavior.\n * @param typedValue - The base context (FHIR resource or backbone element).\n * @param path - The property path.\n * @returns The value of the property and the property type.\n */\nexport function getTypedPropertyValueWithoutSchema(\n typedValue: TypedValue,\n path: string\n): TypedValue[] | TypedValue | undefined {\n const input = typedValue.value;\n if (!input || typeof input !== 'object') {\n return undefined;\n }\n\n let result: TypedValue[] | TypedValue | undefined = undefined;\n\n if (path in input) {\n const propertyValue = (input as { [key: string]: unknown })[path];\n if (Array.isArray(propertyValue)) {\n result = propertyValue.map(toTypedValue);\n } else {\n result = toTypedValue(propertyValue);\n }\n } else {\n // Only support property names that would be valid types\n // Examples:\n // value + valueString = ok, because \"string\" is valid\n // value + valueDecimal = ok, because \"decimal\" is valid\n // id + identifier = not ok, because \"entifier\" is not a valid type\n // resource + resourceType = not ok, because \"type\" is not a valid type\n const trimmedPath = path.endsWith('[x]') ? path.substring(0, path.length - 3) : path;\n for (const propertyType of Object.values(PropertyType)) {\n const propertyName = trimmedPath + capitalize(propertyType);\n if (propertyName in input) {\n const propertyValue = (input as { [key: string]: unknown })[propertyName];\n if (Array.isArray(propertyValue)) {\n result = propertyValue.map((v) => ({ type: propertyType, value: v }));\n } else {\n result = { type: propertyType, value: propertyValue };\n }\n break;\n }\n }\n }\n\n if (Array.isArray(result)) {\n if (result.length === 0 || isEmpty(result[0])) {\n return undefined;\n }\n } else if (isEmpty(result)) {\n return undefined;\n }\n\n return result;\n}\n\n/**\n * Removes duplicates in array using FHIRPath equality rules.\n * @param arr - The input array.\n * @returns The result array with duplicates removed.\n */\nexport function removeDuplicates(arr: TypedValue[]): TypedValue[] {\n const result: TypedValue[] = [];\n for (const i of arr) {\n let found = false;\n for (const j of result) {\n if (toJsBoolean(fhirPathEquals(i, j))) {\n found = true;\n break;\n }\n }\n if (!found) {\n result.push(i);\n }\n }\n return result;\n}\n\n/**\n * Returns a negated FHIRPath boolean expression.\n * @param input - The input array.\n * @returns The negated type value array.\n */\nexport function fhirPathNot(input: TypedValue[]): TypedValue[] {\n return booleanToTypedValue(!toJsBoolean(input));\n}\n\n/**\n * Determines if two arrays are equal according to FHIRPath equality rules.\n * @param x - The first array.\n * @param y - The second array.\n * @returns FHIRPath true if the arrays are equal.\n */\nexport function fhirPathArrayEquals(x: TypedValue[], y: TypedValue[]): TypedValue[] {\n if (x.length === 0 || y.length === 0) {\n return [];\n }\n if (x.length !== y.length) {\n return booleanToTypedValue(false);\n }\n return booleanToTypedValue(x.every((val, index) => toJsBoolean(fhirPathEquals(val, y[index]))));\n}\n\n/**\n * Determines if two arrays are not equal according to FHIRPath equality rules.\n * @param x - The first array.\n * @param y - The second array.\n * @returns FHIRPath true if the arrays are not equal.\n */\nexport function fhirPathArrayNotEquals(x: TypedValue[], y: TypedValue[]): TypedValue[] {\n if (x.length === 0 || y.length === 0) {\n return [];\n }\n if (x.length !== y.length) {\n return booleanToTypedValue(true);\n }\n return booleanToTypedValue(x.some((val, index) => !toJsBoolean(fhirPathEquals(val, y[index]))));\n}\n\n/**\n * Determines if two values are equal according to FHIRPath equality rules.\n * @param x - The first value.\n * @param y - The second value.\n * @returns True if equal.\n */\nexport function fhirPathEquals(x: TypedValue, y: TypedValue): TypedValue[] {\n const xValue = x.value?.valueOf();\n const yValue = y.value?.valueOf();\n if (typeof xValue === 'number' && typeof yValue === 'number') {\n return booleanToTypedValue(Math.abs(xValue - yValue) < 1e-8);\n }\n if (isQuantity(xValue) && isQuantity(yValue)) {\n return booleanToTypedValue(isQuantityEquivalent(xValue, yValue));\n }\n if (typeof xValue === 'object' && typeof yValue === 'object') {\n return booleanToTypedValue(deepEquals(x, y));\n }\n return booleanToTypedValue(xValue === yValue);\n}\n\n/**\n * Determines if two arrays are equivalent according to FHIRPath equality rules.\n * @param x - The first array.\n * @param y - The second array.\n * @returns FHIRPath true if the arrays are equivalent.\n */\nexport function fhirPathArrayEquivalent(x: TypedValue[], y: TypedValue[]): TypedValue[] {\n if (x.length === 0 && y.length === 0) {\n return booleanToTypedValue(true);\n }\n if (x.length !== y.length) {\n return booleanToTypedValue(false);\n }\n x.sort(fhirPathEquivalentCompare);\n y.sort(fhirPathEquivalentCompare);\n return booleanToTypedValue(x.every((val, index) => toJsBoolean(fhirPathEquivalent(val, y[index]))));\n}\n\n/**\n * Determines if two values are equivalent according to FHIRPath equality rules.\n * @param x - The first value.\n * @param y - The second value.\n * @returns True if equivalent.\n */\nexport function fhirPathEquivalent(x: TypedValue, y: TypedValue): TypedValue[] {\n const { type: xType, value: xValueRaw } = x;\n const { type: yType, value: yValueRaw } = y;\n const xValue = xValueRaw?.valueOf();\n const yValue = yValueRaw?.valueOf();\n\n if (typeof xValue === 'number' && typeof yValue === 'number') {\n // Use more generous threshold than equality\n // Decimal: values must be equal, comparison is done on values rounded to the precision of the least precise operand.\n // Trailing zeroes after the decimal are ignored in determining precision.\n return booleanToTypedValue(Math.abs(xValue - yValue) < 0.01);\n }\n if (isQuantity(xValue) && isQuantity(yValue)) {\n return booleanToTypedValue(isQuantityEquivalent(xValue, yValue));\n }\n\n if (xType === 'Coding' && yType === 'Coding') {\n if (typeof xValue !== 'object' || typeof yValue !== 'object') {\n return booleanToTypedValue(false);\n }\n // \"In addition, for Coding values, equivalence is defined based on the code and system elements only.\n // The version, display, and userSelected elements are ignored for the purposes of determining Coding equivalence.\"\n // Source: https://hl7.org/fhir/fhirpath.html#changes\n\n // We need to check if both `code` and `system` are equivalent.\n // If both have undefined `system` fields, If so, then the two's `system` values must be compared.\n // Essentially they must both be `undefined` or both the same.\n return booleanToTypedValue(\n (xValue as Coding).code === (yValue as Coding).code && (xValue as Coding).system === (yValue as Coding).system\n );\n }\n\n if (typeof xValue === 'object' && typeof yValue === 'object') {\n return booleanToTypedValue(deepEquals({ ...xValue, id: undefined }, { ...yValue, id: undefined }));\n }\n if (typeof xValue === 'string' && typeof yValue === 'string') {\n // String: the strings must be the same, ignoring case and locale, and normalizing whitespace\n // (see String Equivalence for more details).\n return booleanToTypedValue(xValue.toLowerCase() === yValue.toLowerCase());\n }\n return booleanToTypedValue(xValue === yValue);\n}\n\n/**\n * Returns the sort order of two values for FHIRPath array equivalence.\n * @param x - The first value.\n * @param y - The second value.\n * @returns The sort order of the values.\n */\nfunction fhirPathEquivalentCompare(x: TypedValue, y: TypedValue): number {\n const xValue = x.value?.valueOf();\n const yValue = y.value?.valueOf();\n if (typeof xValue === 'number' && typeof yValue === 'number') {\n return xValue - yValue;\n }\n if (typeof xValue === 'string' && typeof yValue === 'string') {\n return xValue.localeCompare(yValue);\n }\n return 0;\n}\n\n/**\n * Determines if the typed value is the desired type.\n * @param typedValue - The typed value to check.\n * @param desiredType - The desired type name.\n * @returns True if the typed value is of the desired type.\n */\nexport function fhirPathIs(typedValue: TypedValue, desiredType: string): boolean {\n const { value } = typedValue;\n if (value === undefined || value === null) {\n return false;\n }\n\n switch (desiredType) {\n case 'Boolean':\n return typeof value === 'boolean';\n case 'Decimal':\n case 'Integer':\n return typeof value === 'number';\n case 'Date':\n return isDateString(value);\n case 'DateTime':\n return isDateTimeString(value);\n case 'Time':\n return typeof value === 'string' && !!/^T\\d/.exec(value);\n case 'Period':\n return isPeriod(value);\n case 'Quantity':\n return isQuantity(value);\n default:\n return typedValue.type === desiredType || (typeof value === 'object' && value?.resourceType === desiredType);\n }\n}\n\n/**\n * Returns true if the input value is a YYYY-MM-DD date string.\n * @param input - Unknown input value.\n * @returns True if the input is a date string.\n */\nexport function isDateString(input: unknown): input is string {\n return typeof input === 'string' && !!validationRegexes.date.exec(input);\n}\n\n/**\n * Returns true if the input value is a YYYY-MM-DDThh:mm:ss.sssZ date/time string.\n * @param input - Unknown input value.\n * @returns True if the input is a date/time string.\n */\nexport function isDateTimeString(input: unknown): input is string {\n return typeof input === 'string' && !!validationRegexes.dateTime.exec(input);\n}\n\n/**\n * Determines if the input is a Period object.\n * This is heuristic based, as we do not have strong typing at runtime.\n * @param input - The input value.\n * @returns True if the input is a period.\n */\nexport function isPeriod(input: unknown): input is Period {\n return !!(\n input &&\n typeof input === 'object' &&\n (('start' in input && isDateTimeString(input.start)) || ('end' in input && isDateTimeString(input.end)))\n );\n}\n\n/**\n * Tries to convert an unknown input value to a Period object.\n * @param input - Unknown input value.\n * @returns A Period object or undefined.\n */\nexport function toPeriod(input: unknown): Period | undefined {\n if (!input) {\n return undefined;\n }\n\n if (isDateString(input)) {\n return {\n start: dateStringToInstantString(input, '0000-00-00T00:00:00.000Z'),\n end: dateStringToInstantString(input, 'xxxx-12-31T23:59:59.999Z'),\n };\n }\n\n if (isDateTimeString(input)) {\n return { start: input, end: input };\n }\n\n if (isPeriod(input)) {\n return input;\n }\n\n return undefined;\n}\n\nfunction dateStringToInstantString(input: string, fill: string): string {\n // Input can be any subset of YYYY-MM-DDThh:mm:ss.sssZ\n return input + fill.substring(input.length);\n}\n\n/**\n * Determines if the input is a Quantity object.\n * This is heuristic based, as we do not have strong typing at runtime.\n * @param input - The input value.\n * @returns True if the input is a quantity.\n */\nexport function isQuantity(input: unknown): input is Quantity {\n return !!(input && typeof input === 'object' && 'value' in input && typeof (input as Quantity).value === 'number');\n}\n\nexport function isQuantityEquivalent(x: Quantity, y: Quantity): boolean {\n return (\n Math.abs((x.value as number) - (y.value as number)) < 0.01 &&\n (x.unit === y.unit || x.code === y.code || x.unit === y.code || x.code === y.unit)\n );\n}\n\n/**\n * Resource equality.\n * See: https://dmitripavlutin.com/how-to-compare-objects-in-javascript/#4-deep-equality\n * @param object1 - The first object.\n * @param object2 - The second object.\n * @returns True if the objects are equal.\n */\nfunction deepEquals<T1 extends object, T2 extends object>(object1: T1, object2: T2): boolean {\n const keys1 = Object.keys(object1) as (keyof T1)[];\n const keys2 = Object.keys(object2) as (keyof T2)[];\n if (keys1.length !== keys2.length) {\n return false;\n }\n for (const key of keys1) {\n const val1 = object1[key] as unknown;\n const val2 = object2[key as unknown as keyof T2] as unknown;\n if (isObject(val1) && isObject(val2)) {\n if (!deepEquals(val1, val2)) {\n return false;\n }\n } else if (val1 !== val2) {\n return false;\n }\n }\n return true;\n}\n\nfunction isObject(obj: unknown): obj is object {\n return obj !== null && typeof obj === 'object';\n}\n\nfunction assignPrimitiveExtension(target: any, primitiveExtension: any): any {\n if (primitiveExtension) {\n if (typeof primitiveExtension !== 'object') {\n throw new Error('Primitive extension must be an object');\n }\n return safeAssign(target ?? {}, primitiveExtension);\n }\n return target;\n}\n\n/**\n * For primitive string, number, boolean, the return value will be the corresponding\n * `String`, `Number`, or `Boolean` version of the type.\n * @param target - The value to have `source` properties assigned to.\n * @param source - An object to be assigned to `target`.\n * @returns The `target` value with the properties of `source` assigned to it.\n */\nfunction safeAssign(target: any, source: any): any {\n delete source.__proto__; //eslint-disable-line no-proto\n delete source.constructor;\n return Object.assign(target, source);\n}\n", "import {\n Attachment,\n Bundle,\n CodeableConcept,\n Coding,\n Device,\n Extension,\n ExtensionValue,\n Identifier,\n ObservationDefinition,\n ObservationDefinitionQualifiedInterval,\n Patient,\n Practitioner,\n QuestionnaireResponse,\n QuestionnaireResponseItem,\n QuestionnaireResponseItemAnswer,\n Range,\n Reference,\n RelatedPerson,\n Resource,\n} from '@medplum/fhirtypes';\nimport { getTypedPropertyValue } from './fhirpath/utils';\nimport { formatCodeableConcept, formatHumanName } from './format';\nimport { OperationOutcomeError, validationError } from './outcomes';\nimport { isReference } from './types';\n\n/**\n * QueryTypes defines the different ways to specify FHIR search parameters.\n *\n * Can be any valid input to the URLSearchParams() constructor.\n *\n * TypeScript definitions for URLSearchParams do not match runtime behavior.\n * The official spec only accepts string values.\n * Web browsers and Node.js automatically coerce values to strings.\n * See: https://github.com/microsoft/TypeScript/issues/32951\n */\nexport type QueryTypes =\n | URLSearchParams\n | string[][]\n | Record<string, string | number | boolean | undefined>\n | string\n | undefined;\n\nexport type ProfileResource = Patient | Practitioner | RelatedPerson;\n\n/**\n * Allowed values for `code_challenge_method` in a PKCE exchange.\n */\nexport type CodeChallengeMethod = 'plain' | 'S256';\n\nexport interface Code {\n code?: CodeableConcept;\n}\n\nexport type ResourceWithCode = Resource & Code;\n\n/**\n * Creates a reference resource.\n * @param resource - The FHIR resource.\n * @returns A reference resource.\n */\nexport function createReference<T extends Resource>(resource: T): Reference<T> & { reference: string } {\n const reference = getReferenceString(resource);\n const display = getDisplayString(resource);\n return display === reference ? { reference } : { reference, display };\n}\n\n/**\n * Returns a reference string for a resource.\n * @param input - The FHIR resource or reference.\n * @returns A reference string of the form resourceType/id.\n */\nexport function getReferenceString(input: Reference | Resource): string {\n if (isReference(input)) {\n return input.reference;\n }\n return `${(input as Resource).resourceType}/${input.id}`;\n}\n\n/**\n * Returns the ID portion of a reference.\n * @param input - A FHIR reference or resource.\n * @returns The ID portion of a reference.\n */\nexport function resolveId(input: Reference | Resource | undefined): string | undefined {\n if (!input) {\n return undefined;\n }\n if (isReference(input)) {\n return input.reference.split('/')[1];\n }\n return input.id;\n}\n\n/**\n * Parses a reference and returns a tuple of [ResourceType, ID].\n * @param reference - A reference to a FHIR resource.\n * @returns A tuple containing the `ResourceType` and the ID of the resource or `undefined` when `undefined` or an invalid reference is passed.\n */\nexport function parseReference<T extends Resource>(reference: Reference<T> | undefined): [T['resourceType'], string] {\n if (reference?.reference === undefined) {\n throw new OperationOutcomeError(validationError('Reference missing reference property.'));\n }\n const [type, id] = reference.reference.split('/') as [T['resourceType'] | '', string];\n if (type === '' || id === '' || id === undefined) {\n throw new OperationOutcomeError(validationError('Unable to parse reference string.'));\n }\n return [type, id];\n}\n\n/**\n * Returns true if the resource is a \"ProfileResource\".\n * @param resource - The FHIR resource.\n * @returns True if the resource is a \"ProfileResource\".\n */\nexport function isProfileResource(resource: Resource): resource is ProfileResource {\n return (\n resource.resourceType === 'Patient' ||\n resource.resourceType === 'Practitioner' ||\n resource.resourceType === 'RelatedPerson'\n );\n}\n\n/**\n * Returns a display string for the resource.\n * @param resource - The input resource.\n * @returns Human friendly display string.\n */\nexport function getDisplayString(resource: Resource): string {\n if (isProfileResource(resource)) {\n const profileName = getProfileResourceDisplayString(resource);\n if (profileName) {\n return profileName;\n }\n }\n if (resource.resourceType === 'Device') {\n const deviceName = getDeviceDisplayString(resource);\n if (deviceName) {\n return deviceName;\n }\n }\n if (resource.resourceType === 'MedicationRequest' && resource.medicationCodeableConcept) {\n return formatCodeableConcept(resource.medicationCodeableConcept);\n }\n if (resource.resourceType === 'Subscription' && resource.criteria) {\n return resource.criteria;\n }\n if (resource.resourceType === 'User' && resource.email) {\n return resource.email;\n }\n if ('name' in resource && resource.name && typeof resource.name === 'string') {\n return resource.name;\n }\n if ('code' in resource && resource.code) {\n let code = resource.code;\n if (Array.isArray(code)) {\n code = code[0];\n }\n if (isCodeableConcept(code)) {\n return formatCodeableConcept(code);\n }\n if (isTextObject(code)) {\n return code.text;\n }\n }\n return getReferenceString(resource);\n}\n\n/**\n * Returns a display string for a profile resource if one is found.\n * @param resource - The profile resource.\n * @returns The display name if one is found.\n */\nfunction getProfileResourceDisplayString(resource: ProfileResource): string | undefined {\n const names = resource.name;\n if (names && names.length > 0) {\n return formatHumanName(names[0]);\n }\n return undefined;\n}\n\n/**\n * Returns a display string for a device resource if one is found.\n * @param device - The device resource.\n * @returns The display name if one is found.\n */\nfunction getDeviceDisplayString(device: Device): string | undefined {\n const names = device.deviceName;\n if (names && names.length > 0) {\n return names[0].name;\n }\n return undefined;\n}\n\n/**\n * Returns an image URL for the resource, if one is available.\n * @param resource - The input resource.\n * @returns The image URL for the resource or undefined.\n */\nexport function getImageSrc(resource: Resource): string | undefined {\n if (!('photo' in resource)) {\n return undefined;\n }\n\n const photo = resource.photo;\n if (!photo) {\n return undefined;\n }\n\n if (Array.isArray(photo)) {\n for (const p of photo) {\n const url = getPhotoImageSrc(p);\n if (url) {\n return url;\n }\n }\n } else {\n return getPhotoImageSrc(photo);\n }\n\n return undefined;\n}\n\nfunction getPhotoImageSrc(photo: Attachment): string | undefined {\n if (photo.url && photo.contentType?.startsWith('image/')) {\n return photo.url;\n }\n return undefined;\n}\n\n/**\n * Returns a Date property as a Date.\n * When working with JSON objects, Dates are often serialized as ISO-8601 strings.\n * When that happens, we need to safely convert to a proper Date object.\n * @param date - The date property value, which could be a string or a Date object.\n * @returns A Date object.\n */\nexport function getDateProperty(date: string | undefined): Date | undefined {\n return date ? new Date(date) : undefined;\n}\n\n/**\n * Calculates the age in years from the birth date.\n * @param birthDateStr - The birth date or start date in ISO-8601 format YYYY-MM-DD.\n * @param endDateStr - Optional end date in ISO-8601 format YYYY-MM-DD. Default value is today.\n * @returns The age in years, months, and days.\n */\nexport function calculateAge(\n birthDateStr: string,\n endDateStr?: string\n): { years: number; months: number; days: number } {\n const startDate = new Date(birthDateStr);\n startDate.setUTCHours(0, 0, 0, 0);\n\n const endDate = endDateStr ? new Date(endDateStr) : new Date();\n endDate.setUTCHours(0, 0, 0, 0);\n\n const startYear = startDate.getUTCFullYear();\n const startMonth = startDate.getUTCMonth();\n const startDay = startDate.getUTCDate();\n\n const endYear = endDate.getUTCFullYear();\n const endMonth = endDate.getUTCMonth();\n const endDay = endDate.getUTCDate();\n\n let years = endYear - startYear;\n if (endMonth < startMonth || (endMonth === startMonth && endDay < startDay)) {\n years--;\n }\n\n let months = endYear * 12 + endMonth - (startYear * 12 + startMonth);\n if (endDay < startDay) {\n months--;\n }\n\n const days = Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));\n\n return { years, months, days };\n}\n\n/**\n * Calculates the age string for display using the age appropriate units.\n * If the age is greater than or equal to 2 years, then the age is displayed in years.\n * If the age is greater than or equal to 1 month, then the age is displayed in months.\n * Otherwise, the age is displayed in days.\n * @param birthDateStr - The birth date or start date in ISO-8601 format YYYY-MM-DD.\n * @param endDateStr - Optional end date in ISO-8601 format YYYY-MM-DD. Default value is today.\n * @returns The age string.\n */\nexport function calculateAgeString(birthDateStr: string, endDateStr?: string): string | undefined {\n const { years, months, days } = calculateAge(birthDateStr, endDateStr);\n if (years >= 2) {\n return years.toString().padStart(3, '0') + 'Y';\n } else if (months >= 1) {\n return months.toString().padStart(3, '0') + 'M';\n } else {\n return days.toString().padStart(3, '0') + 'D';\n }\n}\n\n/**\n * Returns all questionnaire answers as a map by link ID.\n * @param response - The questionnaire response resource.\n * @returns Questionnaire answers mapped by link ID.\n */\nexport function getQuestionnaireAnswers(\n response: QuestionnaireResponse\n): Record<string, QuestionnaireResponseItemAnswer> {\n const result: Record<string, QuestionnaireResponseItemAnswer> = {};\n buildQuestionnaireAnswerItems(response.item, result);\n return result;\n}\n\nfunction buildQuestionnaireAnswerItems(\n items: QuestionnaireResponseItem[] | undefined,\n result: Record<string, QuestionnaireResponseItemAnswer>\n): void {\n if (items) {\n for (const item of items) {\n if (item.linkId && item.answer && item.answer.length > 0) {\n result[item.linkId] = item.answer[0];\n }\n buildQuestionnaireAnswerItems(item.item, result);\n }\n }\n}\n\n/**\n * Returns an array of questionnaire answers as a map by link ID.\n * @param response - The questionnaire response resource.\n * @returns Questionnaire answer arrays mapped by link ID.\n */\nexport function getAllQuestionnaireAnswers(\n response: QuestionnaireResponse\n): Record<string, QuestionnaireResponseItemAnswer[]> {\n const result: Record<string, QuestionnaireResponseItemAnswer[]> = {};\n buildAllQuestionnaireAnswerItems(response.item, result);\n return result;\n}\n\n/**\n * Recursively builds the questionnaire answer items map.\n * @param items - The current questionnaire response items.\n * @param result - The cumulative result map of answers.\n */\nfunction buildAllQuestionnaireAnswerItems(\n items: QuestionnaireResponseItem[] | undefined,\n result: Record<string, QuestionnaireResponseItemAnswer[]>\n): void {\n if (items) {\n for (const item of items) {\n if (item.linkId && item.answer && item.answer.length > 0) {\n if (result[item.linkId]) {\n result[item.linkId] = [...result[item.linkId], ...item.answer];\n } else {\n result[item.linkId] = item.answer;\n }\n }\n buildAllQuestionnaireAnswerItems(item.item, result);\n }\n }\n}\n\n/**\n * Returns the resource identifier for the given system.\n *\n * If multiple identifiers exist with the same system, the first one is returned.\n *\n * If the system is not found, then returns undefined.\n * @param resource - The resource to check.\n * @param system - The identifier system.\n * @returns The identifier value if found; otherwise undefined.\n */\nexport function getIdentifier(resource: Resource, system: string): string | undefined {\n const identifiers = (resource as any).identifier;\n if (!identifiers) {\n return undefined;\n }\n const array = Array.isArray(identifiers) ? identifiers : [identifiers];\n for (const identifier of array) {\n if (identifier.system === system) {\n return identifier.value;\n }\n }\n return undefined;\n}\n\n/**\n * Sets a resource identifier for the given system.\n *\n * Note that this method is only available on resources that have an \"identifier\" property,\n * and that property must be an array of Identifier objects,\n * which is not true for all FHIR resources.\n *\n * If the identifier already exists, then the value is updated.\n *\n * Otherwise a new identifier is added.\n *\n * @param resource - The resource to add the identifier to.\n * @param system - The identifier system.\n * @param value - The identifier value.\n */\nexport function setIdentifier(resource: Resource & { identifier?: Identifier[] }, system: string, value: string): void {\n const identifiers = resource.identifier;\n if (!identifiers) {\n resource.identifier = [{ system, value }];\n return;\n }\n for (const identifier of identifiers) {\n if (identifier.system === system) {\n identifier.value = value;\n return;\n }\n }\n identifiers.push({ system, value });\n}\n\n/**\n * Returns an extension value by extension URLs.\n * @param resource - The base resource.\n * @param urls - Array of extension URLs. Each entry represents a nested extension.\n * @returns The extension value if found; undefined otherwise.\n */\nexport function getExtensionValue(resource: any, ...urls: string[]): ExtensionValue | undefined {\n const extension = getExtension(resource, ...urls);\n if (!extension) {\n return undefined;\n }\n\n const typedValue = getTypedPropertyValue({ type: 'Extension', value: extension }, 'value[x]');\n if (!typedValue) {\n return undefined;\n }\n\n return Array.isArray(typedValue) ? typedValue[0].value : typedValue.value;\n}\n\n/**\n * Returns an extension by extension URLs.\n * @param resource - The base resource.\n * @param urls - Array of extension URLs. Each entry represents a nested extension.\n * @returns The extension object if found; undefined otherwise.\n */\nexport function getExtension(resource: any, ...urls: string[]): Extension | undefined {\n // Let curr be the current resource or extension. Extensions can be nested.\n let curr: any = resource;\n\n // For each of the urls, try to find a matching nested extension.\n for (let i = 0; i < urls.length && curr; i++) {\n curr = (curr?.extension as Extension[] | undefined)?.find((e) => e.url === urls[i]);\n }\n\n return curr;\n}\n\n/**\n * FHIR JSON stringify.\n * Removes properties with empty string values.\n * Removes objects with zero properties.\n * See: https://www.hl7.org/fhir/json.html\n * @param value - The input value.\n * @param pretty - Optional flag to pretty-print the JSON.\n * @returns The resulting JSON string.\n */\nexport function stringify(value: any, pretty?: boolean): string {\n return JSON.stringify(value, stringifyReplacer, pretty ? 2 : undefined) ?? '';\n}\n\n/**\n * Evaluates JSON key/value pairs for FHIR JSON stringify.\n * Removes properties with empty string values.\n * Removes objects with zero properties.\n * @param k - Property key.\n * @param v - Property value.\n * @returns The replaced value.\n */\nfunction stringifyReplacer(k: string, v: any): any {\n return !isArrayKey(k) && isEmpty(v) ? undefined : v;\n}\n\n/**\n * Returns true if the key is an array key.\n * @param k - The property key.\n * @returns True if the key is an array key.\n */\nfunction isArrayKey(k: string): boolean {\n return !!/\\d+$/.exec(k);\n}\n\n/**\n * Returns true if the value is empty (null, undefined, empty string, or empty object).\n * @param v - Any value.\n * @returns True if the value is an empty string or an empty object.\n */\nexport function isEmpty(v: unknown): boolean {\n if (v === null || v === undefined) {\n return true;\n }\n\n const t = typeof v;\n if (t === 'string' || t === 'object') {\n return !isPopulated(v);\n }\n\n return false;\n}\n\nexport type CanBePopulated = { length: number } | object;\n/**\n * Returns true if the value is a non-empty string, an object with a length property greater than zero, or a non-empty object\n * @param arg - Any value\n * @returns True if the value is a non-empty string, an object with a length property greater than zero, or a non-empty object\n */\nexport function isPopulated<T extends { length: number } | object>(arg: CanBePopulated | undefined | null): arg is T {\n if (arg === null || arg === undefined) {\n return false;\n }\n const t = typeof arg;\n\n return (\n (t === 'string' && arg !== '') ||\n (t === 'object' && (('length' in arg && arg.length > 0) || Object.keys(arg).length > 0))\n );\n}\n\n/**\n * Resource equality.\n * Ignores meta.versionId and meta.lastUpdated.\n * @param object1 - The first object.\n * @param object2 - The second object.\n * @param path - Optional path string.\n * @returns True if the objects are equal.\n */\nexport function deepEquals(object1: unknown, object2: unknown, path?: string): boolean {\n if (object1 === object2) {\n return true;\n }\n if (isEmpty(object1) && isEmpty(object2)) {\n return true;\n }\n if (isEmpty(object1) || isEmpty(object2)) {\n return false;\n }\n if (Array.isArray(object1) && Array.isArray(object2)) {\n return deepEqualsArray(object1, object2);\n }\n if (Array.isArray(object1) || Array.isArray(object2)) {\n return false;\n }\n if (isObject(object1) && isObject(object2)) {\n return deepEqualsObject(object1, object2, path);\n }\n if (isObject(object1) || isObject(object2)) {\n return false;\n }\n return false;\n}\n\nfunction deepEqualsArray(array1: unknown[], array2: unknown[]): boolean {\n if (array1.length !== array2.length) {\n return false;\n }\n for (let i = 0; i < array1.length; i++) {\n if (!deepEquals(array1[i], array2[i])) {\n return false;\n }\n }\n return true;\n}\n\nfunction deepEqualsObject(\n object1: Record<string, unknown>,\n object2: Record<string, unknown>,\n path: string | undefined\n): boolean {\n const keySet = new Set<string>();\n Object.keys(object1).forEach((k) => keySet.add(k));\n Object.keys(object2).forEach((k) => keySet.add(k));\n if (path === 'meta') {\n keySet.delete('versionId');\n keySet.delete('lastUpdated');\n keySet.delete('author');\n }\n for (const key of keySet) {\n const val1 = object1[key];\n const val2 = object2[key];\n if (!deepEquals(val1, val2, key)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Checks if value includes all fields and values of pattern.\n * It doesn't matter if value has extra fields, values, etc.\n * @param value - The object being tested against pattern.\n * @param pattern - The object pattern/shape checked to exist within value.\n * @returns True if value includes all fields and values of pattern.\n */\nexport function deepIncludes(value: any, pattern: any): boolean {\n if (isEmpty(value)) {\n return true;\n }\n if (isEmpty(pattern)) {\n return false;\n }\n if (Array.isArray(value) && Array.isArray(pattern)) {\n return deepIncludesArray(value, pattern);\n }\n if (Array.isArray(value) || Array.isArray(pattern)) {\n return false;\n }\n if (isObject(value) && isObject(pattern)) {\n return deepIncludesObject(value, pattern);\n } else if (isObject(value) || isObject(pattern)) {\n return false;\n }\n return value === pattern;\n}\n\nfunction deepIncludesArray(value: any[], pattern: any[]): boolean {\n return pattern.every((patternVal) => value.some((valueVal) => deepIncludes(valueVal, patternVal)));\n}\n\nfunction deepIncludesObject(value: { [key: string]: unknown }, pattern: { [key: string]: unknown }): boolean {\n return Object.entries(pattern).every(\n ([patternKey, patternVal]) => patternKey in value && deepIncludes(value[patternKey], patternVal)\n );\n}\n\n/**\n * Creates a deep clone of the input value.\n *\n * Limitations:\n * - Only supports JSON primitives and arrays.\n * - Does not support Functions, lambdas, etc.\n * - Does not support circular references.\n *\n * See: https://web.dev/structured-clone/\n * See: https://stackoverflow.com/questions/40488190/how-is-structured-clone-algorithm-different-from-deep-copy\n * @param input - The input to clone.\n * @returns A deep clone of the input.\n */\nexport function deepClone<T>(input: T): T {\n return input === undefined ? input : (JSON.parse(JSON.stringify(input)) as T);\n}\n\n/**\n * Returns true if the input string is a UUID.\n * @param input - The input string.\n * @returns True if the input string matches the UUID format.\n */\nexport function isUUID(input: string): input is string {\n return !!/^\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}$/i.exec(input);\n}\n\n/**\n * Returns true if the input is an object.\n * @param obj - The candidate object.\n * @returns True if the input is a non-null non-undefined object.\n */\nexport function isObject(obj: unknown): obj is Record<string, unknown> {\n return obj !== null && typeof obj === 'object';\n}\n\n/**\n * Returns true if the input array is an array of strings.\n * @param arr - Input array.\n * @returns True if the input array is an array of strings.\n */\nexport function isStringArray(arr: any[]): arr is string[] {\n return arr.every(isString);\n}\n\n/**\n * Returns true if the input value is a string.\n * @param value - The candidate value.\n * @returns True if the input value is a string.\n */\nexport function isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\n/**\n * Returns true if the input value is a Coding object.\n * This is a heuristic check based on the presence of the \"code\" property.\n * @param value - The candidate value.\n * @returns True if the input value is a Coding.\n */\nexport function isCoding(value: unknown): value is Coding & { code: string } {\n return isObject(value) && 'code' in value && typeof value.code === 'string';\n}\n\n/**\n * Returns true if the input value is a CodeableConcept object.\n * This is a heuristic check based on the presence of the \"coding\" property.\n * @param value - The candidate value.\n * @returns True if the input value is a CodeableConcept.\n */\nexport function isCodeableConcept(value: unknown): value is CodeableConcept & { coding: Coding[] } {\n return isObject(value) && 'coding' in value && Array.isArray(value.coding) && value.coding.every(isCoding);\n}\n\n/**\n * Returns true if the input value is an object with a string text property.\n * This is a heuristic check based on the presence of the \"text\" property.\n * @param value - The candidate value.\n * @returns True if the input value is a text object.\n */\nexport function isTextObject(value: unknown): value is { text: string } {\n return isObject(value) && 'text' in value && typeof value.text === 'string';\n}\n\n// Precompute hex octets\n// See: https://stackoverflow.com/a/55200387\nconst byteToHex: string[] = [];\nfor (let n = 0; n < 256; n++) {\n byteToHex.push(n.toString(16).padStart(2, '0'));\n}\n\n/**\n * Converts an ArrayBuffer to hex string.\n * See: https://stackoverflow.com/a/55200387\n * @param arrayBuffer - The input array buffer.\n * @returns The resulting hex string.\n */\nexport function arrayBufferToHex(arrayBuffer: ArrayBufferLike | ArrayBufferView): string {\n const buffer = normalizeArrayBufferView(arrayBuffer);\n const bytes = new Uint8Array(buffer);\n const result: string[] = new Array(bytes.length);\n for (let i = 0; i < bytes.length; i++) {\n result[i] = byteToHex[bytes[i]];\n }\n return result.join('');\n}\n\n/**\n * Converts an ArrayBuffer to a base-64 encoded string.\n * @param arrayBuffer - The input array buffer.\n * @returns The base-64 encoded string.\n */\nexport function arrayBufferToBase64(arrayBuffer: ArrayBufferLike | ArrayBufferView): string {\n const buffer = normalizeArrayBufferView(arrayBuffer);\n const bytes = new Uint8Array(buffer);\n const result: string[] = new Array(bytes.length);\n for (let i = 0; i < bytes.length; i++) {\n result[i] = String.fromCharCode(bytes[i]);\n }\n return window.btoa(result.join(''));\n}\n\n/**\n * Normalizes an `ArrayBufferLike` (eg. an `ArrayBuffer`) to a raw `ArrayBufferLike` (without a view). If the passed buffer is a view, it gives the raw `ArrayBufferLike`.\n *\n * This is useful in cases where you need to operate on the raw bytes of an `ArrayBuffer` where a `TypedArray` (eg. `Uint32Array`) might be passed in.\n * This ensures that you will always operate on the raw bytes rather than accidentally truncating the input by operating on the elements of the view.\n *\n * @param typedArrayOrBuffer - The `ArrayBufferLike` (either `TypedArray` or raw `ArrayBuffer`) to normalize to raw `ArrayBuffer`.\n * @returns The raw `ArrayBuffer` without a view.\n */\nexport function normalizeArrayBufferView(typedArrayOrBuffer: ArrayBufferLike | ArrayBufferView): ArrayBufferLike {\n return ArrayBuffer.isView(typedArrayOrBuffer) ? typedArrayOrBuffer.buffer : typedArrayOrBuffer;\n}\n\nexport function capitalize(word: string): string {\n if (!word) {\n return '';\n }\n return word.charAt(0).toUpperCase() + word.substring(1);\n}\n\nexport function isLowerCase(c: string): boolean {\n return c === c.toLowerCase() && c !== c.toUpperCase();\n}\n\nexport function isComplexTypeCode(code: string): boolean {\n return code.length > 0 && code.startsWith(code[0].toUpperCase());\n}\n\n/**\n * Returns the difference between two paths which is often suitable to use as a key in a `Record<string, InternalSchemaElement>`\n * @param parentPath - The parent path that will be removed from `path`.\n * @param path - The element path that should be a child of `parentPath`.\n * @returns - The difference between `path` and `parentPath` or `undefined` if `path` is not a child of `parentPath`.\n */\nexport function getPathDifference(parentPath: string, path: string): string | undefined {\n const parentPathPrefix = parentPath + '.';\n if (path.startsWith(parentPathPrefix)) {\n return path.slice(parentPathPrefix.length);\n }\n return undefined;\n}\n\n/**\n * Tries to find a code string for a given system within a given codeable concept.\n * @param concept - The codeable concept.\n * @param system - The system string.\n * @returns The code if found; otherwise undefined.\n */\nexport function getCodeBySystem(concept: CodeableConcept, system: string): string | undefined {\n return concept.coding?.find((coding) => coding.system === system)?.code;\n}\n\n/**\n * Sets a code for a given system within a given codeable concept.\n * @param concept - The codeable concept.\n * @param system - The system string.\n * @param code - The code value.\n */\nexport function setCodeBySystem(concept: CodeableConcept, system: string, code: string): void {\n if (!concept.coding) {\n concept.coding = [];\n }\n const coding = concept.coding.find((c) => c.system === system);\n if (coding) {\n coding.code = code;\n } else {\n concept.coding.push({ system, code });\n }\n}\n\n/**\n * Tries to find an observation interval for the given patient and value.\n * @param definition - The observation definition.\n * @param patient - The patient.\n * @param value - The observation value.\n * @param category - Optional interval category restriction.\n * @returns The observation interval if found; otherwise undefined.\n */\nexport function findObservationInterval(\n definition: ObservationDefinition,\n patient: Patient,\n value: number,\n category?: 'reference' | 'critical' | 'absolute'\n): ObservationDefinitionQualifiedInterval | undefined {\n return definition.qualifiedInterval?.find(\n (interval) =>\n observationIntervalMatchesPatient(interval, patient) &&\n observationIntervalMatchesValue(interval, value, definition.quantitativeDetails?.decimalPrecision) &&\n (category === undefined || interval.category === category)\n );\n}\n\n/**\n * Tries to find an observation reference range for the given patient and condition names.\n * @param definition - The observation definition.\n * @param patient - The patient.\n * @param names - The condition names.\n * @returns The observation interval if found; otherwise undefined.\n */\nexport function findObservationReferenceRange(\n definition: ObservationDefinition,\n patient: Patient,\n names: string[]\n): ObservationDefinitionQualifiedInterval | undefined {\n return definition.qualifiedInterval?.find(\n (interval) => observationIntervalMatchesPatient(interval, patient) && names.includes(interval.condition as string)\n );\n}\n\n/**\n * Returns true if the patient matches the observation interval.\n * @param interval - The observation interval.\n * @param patient - The patient.\n * @returns True if the patient matches the observation interval.\n */\nfunction observationIntervalMatchesPatient(\n interval: ObservationDefinitionQualifiedInterval,\n patient: Patient\n): boolean {\n return observationIntervalMatchesGender(interval, patient) && observationIntervalMatchesAge(interval, patient);\n}\n\n/**\n * Returns true if the patient gender matches the observation interval.\n * @param interval - The observation interval.\n * @param patient - The patient.\n * @returns True if the patient gender matches the observation interval.\n */\nfunction observationIntervalMatchesGender(interval: ObservationDefinitionQualifiedInterval, patient: Patient): boolean {\n return !interval.gender || interval.gender === patient.gender;\n}\n\n/**\n * Returns true if the patient age matches the observation interval.\n * @param interval - The observation interval.\n * @param patient - The patient.\n * @returns True if the patient age matches the observation interval.\n */\nfunction observationIntervalMatchesAge(interval: ObservationDefinitionQualifiedInterval, patient: Patient): boolean {\n return !interval.age || matchesRange(calculateAge(patient.birthDate as string).years, interval.age);\n}\n\n/**\n * Returns true if the value matches the observation interval.\n * @param interval - The observation interval.\n * @param value - The observation value.\n * @param precision - Optional precision in number of digits.\n * @returns True if the value matches the observation interval.\n */\nfunction observationIntervalMatchesValue(\n interval: ObservationDefinitionQualifiedInterval,\n value: number,\n precision?: number\n): boolean {\n return !!interval.range && matchesRange(value, interval.range, precision);\n}\n\n/**\n * Returns true if the value is in the range accounting for precision.\n * @param value - The numeric value.\n * @param range - The numeric range.\n * @param precision - Optional precision in number of digits.\n * @returns True if the value is within the range.\n */\nexport function matchesRange(value: number, range: Range, precision?: number): boolean {\n return (\n (range.low?.value === undefined || preciseGreaterThanOrEquals(value, range.low.value, precision)) &&\n (range.high?.value === undefined || preciseLessThanOrEquals(value, range.high.value, precision))\n );\n}\n\n/**\n * Returns the input number rounded to the specified number of digits.\n * @param a - The input number.\n * @param precision - The precision in number of digits.\n * @returns The number rounded to the specified number of digits.\n */\nexport function preciseRound(a: number, precision: number): number {\n return parseFloat(a.toFixed(precision));\n}\n\n/**\n * Returns true if the two numbers are equal to the given precision.\n * @param a - The first number.\n * @param b - The second number.\n * @param precision - Optional precision in number of digits.\n * @returns True if the two numbers are equal to the given precision.\n */\nexport function preciseEquals(a: number, b: number, precision?: number): boolean {\n return toPreciseInteger(a, precision) === toPreciseInteger(b, precision);\n}\n\n/**\n * Returns true if the first number is less than the second number to the given precision.\n * @param a - The first number.\n * @param b - The second number.\n * @param precision - Optional precision in number of digits.\n * @returns True if the first number is less than the second number to the given precision.\n */\nexport function preciseLessThan(a: number, b: number, precision?: number): boolean {\n return toPreciseInteger(a, precision) < toPreciseInteger(b, precision);\n}\n\n/**\n * Returns true if the first number is greater than the second number to the given precision.\n * @param a - The first number.\n * @param b - The second number.\n * @param precision - Optional precision in number of digits.\n * @returns True if the first number is greater than the second number to the given precision.\n */\nexport function preciseGreaterThan(a: number, b: number, precision?: number): boolean {\n return toPreciseInteger(a, precision) > toPreciseInteger(b, precision);\n}\n\n/**\n * Returns true if the first number is less than or equal to the second number to the given precision.\n * @param a - The first number.\n * @param b - The second number.\n * @param precision - Optional precision in number of digits.\n * @returns True if the first number is less than or equal to the second number to the given precision.\n */\nexport function preciseLessThanOrEquals(a: number, b: number, precision?: number): boolean {\n return toPreciseInteger(a, precision) <= toPreciseInteger(b, precision);\n}\n\n/**\n * Returns true if the first number is greater than or equal to the second number to the given precision.\n * @param a - The first number.\n * @param b - The second number.\n * @param precision - Optional precision in number of digits.\n * @returns True if the first number is greater than or equal to the second number to the given precision.\n */\nexport function preciseGreaterThanOrEquals(a: number, b: number, precision?: number): boolean {\n return toPreciseInteger(a, precision) >= toPreciseInteger(b, precision);\n}\n\n/**\n * Returns an integer representation of the number with the given precision.\n * For example, if precision is 2, then 1.2345 will be returned as 123.\n * @param a - The number.\n * @param precision - Optional precision in number of digits.\n * @returns The integer with the given precision.\n */\nfunction toPreciseInteger(a: number, precision?: number): number {\n if (precision === undefined) {\n return a;\n }\n return Math.round(a * Math.pow(10, precision));\n}\n\n/**\n * Finds the first resource in the input array that matches the specified code and system.\n * @param resources - The array of resources to search.\n * @param code - The code to search for.\n * @param system - The system to search for.\n * @returns The first resource in the input array that matches the specified code and system, or undefined if no such resource is found.\n */\nexport function findResourceByCode(\n resources: ResourceWithCode[],\n code: CodeableConcept | string,\n system: string\n): ResourceWithCode | undefined {\n return resources.find((r) =>\n typeof code === 'string'\n ? getCodeBySystem(r.code || {}, system) === code\n : getCodeBySystem(r.code || {}, system) === getCodeBySystem(code, system)\n );\n}\n\nexport function arrayify<T>(value: T | T[] | undefined): T[] | undefined {\n if (value === undefined) {\n return undefined;\n } else if (Array.isArray(value)) {\n return value;\n } else {\n return [value];\n }\n}\n\nexport function singularize<T>(value: T | T[] | undefined): T | undefined {\n if (Array.isArray(value)) {\n return value[0];\n } else {\n return value;\n }\n}\n\n/**\n * Sleeps for the specified number of milliseconds.\n * @param ms - Time delay in milliseconds\n * @returns A promise that resolves after the specified number of milliseconds.\n */\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/**\n * Splits a string into an array of strings using the specified delimiter.\n * Unlike the built-in split function, this function will split the string into a maximum of exactly n parts.\n * Trailing empty strings are included in the result.\n * @param str - The string to split.\n * @param delim - The delimiter.\n * @param n - The maximum number of parts to split the string into.\n * @returns The resulting array of strings.\n */\nexport function splitN(str: string, delim: string, n: number): string[] {\n const result: string[] = [];\n for (let i = 0; i < n - 1; i++) {\n const delimIndex = str.indexOf(delim);\n if (delimIndex < 0) {\n break;\n } else {\n result.push(str.slice(0, delimIndex));\n str = str.slice(delimIndex + delim.length);\n }\n }\n result.push(str);\n return result;\n}\n\n/**\n * Memoizes the result of a parameterless function\n * @param fn - The function to be wrapped\n * @returns The result of the first invocation of the wrapped function\n */\nexport function lazy<T>(fn: () => T): () => T {\n let result: T;\n let executed = false;\n\n return function (): T {\n if (!executed) {\n result = fn();\n executed = true;\n }\n return result;\n };\n}\n\nexport function append<T>(array: T[] | undefined, value: T): T[] {\n if (!array) {\n return [value];\n }\n array.push(value);\n return array;\n}\n\n/**\n * Sorts an array of strings in place using the localeCompare method.\n *\n * This method will mutate the input array.\n *\n * @param array - The array of strings to sort.\n * @returns The sorted array of strings.\n */\nexport function sortStringArray(array: string[]): string[] {\n return array.sort((a, b) => a.localeCompare(b));\n}\n\n/**\n * Ensures the given URL has a trailing slash.\n * @param url - The URL to ensure has a trailing slash.\n * @returns The URL with a trailing slash.\n */\nexport function ensureTrailingSlash(url: string): string {\n return url.endsWith('/') ? url : url + '/';\n}\n\n/**\n * Ensures the given URL has no leading slash.\n * @param url - The URL to ensure has no leading slash.\n * @returns The URL string with no slash.\n */\nexport function ensureNoLeadingSlash(url: string): string {\n return url.startsWith('/') ? url.slice(1) : url;\n}\n\n/**\n * Concatenates the given base URL and URL.\n *\n * If the URL is absolute, it is returned as-is.\n *\n * @param baseUrl - The base URL.\n * @param path - The URL to concat. Can be relative or absolute.\n * @returns The concatenated URL.\n */\nexport function concatUrls(baseUrl: string | URL, path: string): string {\n return new URL(ensureNoLeadingSlash(path), ensureTrailingSlash(baseUrl.toString())).toString();\n}\n\n/**\n * Concatenates a given base URL and path, ensuring the URL has the appropriate `ws://` or `wss://` protocol instead of `http://` or `https://`.\n *\n * @param baseUrl - The base URL.\n * @param path - The URL to concat. Can be relative or absolute.\n * @returns The concatenated WebSocket URL.\n */\nexport function getWebSocketUrl(baseUrl: URL | string, path: string): string {\n return concatUrls(baseUrl, path).toString().replace('http://', 'ws://').replace('https://', 'wss://');\n}\n\n/**\n * Converts the given `query` to a string.\n *\n * @param query - The query to convert. The type can be any member of `QueryTypes`.\n * @returns The query as a string.\n */\nexport function getQueryString(query: QueryTypes): string {\n if (typeof query === 'object' && !Array.isArray(query) && !(query instanceof URLSearchParams)) {\n query = Object.fromEntries(Object.entries(query).filter((entry) => entry[1] !== undefined));\n }\n // @ts-expect-error Technically `Record<string, string, number, boolean>` is not valid to pass into `URLSearchParams` constructor since `boolean` and `number`\n // are not considered to be valid values based on the WebIDL definition from WhatWG. The current runtime behavior relies on implementation-specific coercion to string under the hood.\n // Source: https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams:~:text=6.2.%20URLSearchParams,)%20init%20%3D%20%22%22)%3B\n return new URLSearchParams(query).toString();\n}\n\nexport const VALID_HOSTNAME_REGEX =\n /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-_]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-_]*[A-Za-z0-9])$/;\n\n/**\n * Tests whether a given input is a valid hostname.\n *\n * __NOTE: Does not validate that the input is a valid domain name, only a valid hostname.__\n *\n * @param input - The input to test.\n * @returns True if `input` is a valid hostname, otherwise returns false.\n *\n * ### Valid matches:\n * - foo\n * - foo.com\n * - foo.bar.com\n * - foo.org\n * - foo.bar.co.uk\n * - localhost\n * - LOCALHOST\n * - foo-bar-baz\n * - foo_bar\n * - foobar123\n *\n * ### Invalid matches:\n * - foo.com/bar\n * - https://foo.com\n * - foo_-bar_-\n * - foo | rm -rf /\n */\nexport function isValidHostname(input: string): boolean {\n return VALID_HOSTNAME_REGEX.test(input);\n}\n\n/**\n * Adds the supplied profileUrl to the resource.meta.profile if it is not already\n * specified\n * @param resource - A FHIR resource\n * @param profileUrl - The profile URL to add\n * @returns The resource\n */\nexport function addProfileToResource<T extends Resource = Resource>(resource: T, profileUrl: string): T {\n if (!resource?.meta?.profile?.includes(profileUrl)) {\n resource.meta = resource.meta ?? {};\n resource.meta.profile = resource.meta.profile ?? [];\n resource.meta.profile.push(profileUrl);\n }\n return resource;\n}\n\n/**\n * Returns a Map of resources from a bundle, using the specified identifier system as the key.\n * @param resourceBundle - The bundle of resources.\n * @param identifierSystem - The identifier system to use for keys.\n * @returns Map of resources keyed by identifier value for the specified system.\n */\nexport function mapByIdentifier<T extends Resource = Resource>(\n resourceBundle: Bundle<T>,\n identifierSystem: string\n): Map<string, T> {\n const resourceMap = new Map<string, T>(\n resourceBundle.entry\n ?.filter((e) => !!e.resource)\n .map((e) => [getIdentifier(e.resource as Resource, identifierSystem) as string, e.resource as T])\n .filter(([i]) => i !== undefined) as [string, T][]\n );\n return resourceMap;\n}\n\n/**\n * Removes the supplied profileUrl from the resource.meta.profile if it is present\n * @param resource - A FHIR resource\n * @param profileUrl - The profile URL to remove\n * @returns The resource\n */\nexport function removeProfileFromResource<T extends Resource = Resource>(resource: T, profileUrl: string): T {\n if (resource?.meta?.profile?.includes(profileUrl)) {\n const index = resource.meta.profile.indexOf(profileUrl);\n resource.meta.profile.splice(index, 1);\n }\n return resource;\n}\n\nexport function flatMapFilter<T, U>(arr: T[] | undefined, fn: (value: T, idx: number) => U | undefined): U[] {\n const result: U[] = [];\n if (!arr) {\n return result;\n }\n\n for (let i = 0; i < arr.length; i++) {\n const resultValue = fn(arr[i], i);\n if (Array.isArray(resultValue)) {\n result.push(...resultValue.flat());\n } else if (resultValue !== undefined) {\n result.push(resultValue);\n }\n }\n return result;\n}\n", "import {\n Address,\n CodeableConcept,\n Coding,\n HumanName,\n Money,\n Observation,\n ObservationComponent,\n Period,\n Quantity,\n Range,\n Reference,\n Timing,\n TimingRepeat,\n} from '@medplum/fhirtypes';\nimport { TypedValue } from './types';\nimport { capitalize, stringify } from './utils';\n\nexport interface AddressFormatOptions {\n all?: boolean;\n use?: boolean;\n lineSeparator?: string;\n}\n\nexport interface HumanNameFormatOptions {\n all?: boolean;\n prefix?: boolean;\n suffix?: boolean;\n use?: boolean;\n}\n\n/**\n * Converts a typed value to a string.\n * @param typedValue - The typed value to convert to a string.\n * @returns The string representation of the typed value.\n */\nexport function typedValueToString(typedValue: TypedValue | undefined): string {\n if (!typedValue) {\n return '';\n }\n switch (typedValue.type) {\n case 'Address':\n return formatAddress(typedValue.value);\n case 'CodeableConcept':\n return formatCodeableConcept(typedValue.value);\n case 'Coding':\n return formatCoding(typedValue.value);\n case 'ContactPoint':\n return typedValue.value.value;\n case 'HumanName':\n return formatHumanName(typedValue.value);\n case 'Quantity':\n return formatQuantity(typedValue.value);\n case 'Reference':\n return formatReferenceString(typedValue.value);\n default:\n return typedValue.value.toString();\n }\n}\n\n/**\n * Formats a FHIR Reference as a string.\n * @param value - The reference to format.\n * @returns The formatted reference string.\n */\nexport function formatReferenceString(value: Reference | undefined): string {\n if (!value) {\n return '';\n }\n return value.display ?? value.reference ?? stringify(value);\n}\n\n/**\n * Formats a FHIR Address as a string.\n * @param address - The address to format.\n * @param options - Optional address format options.\n * @returns The formatted address string.\n */\nexport function formatAddress(address: Address, options?: AddressFormatOptions): string {\n const builder = [];\n\n if (address.line) {\n builder.push(...address.line);\n }\n\n if (address.city || address.state || address.postalCode) {\n const cityStateZip = [];\n if (address.city) {\n cityStateZip.push(address.city);\n }\n if (address.state) {\n cityStateZip.push(address.state);\n }\n if (address.postalCode) {\n cityStateZip.push(address.postalCode);\n }\n builder.push(cityStateZip.join(', '));\n }\n\n if (address.use && (options?.all || options?.use)) {\n builder.push('[' + address.use + ']');\n }\n\n return builder.join(options?.lineSeparator ?? ', ').trim();\n}\n\n/**\n * Formats a FHIR HumanName as a string.\n * @param name - The name to format.\n * @param options - Optional name format options.\n * @returns The formatted name string.\n */\nexport function formatHumanName(name: HumanName, options?: HumanNameFormatOptions): string {\n const builder = [];\n\n if (name.prefix && options?.prefix !== false) {\n builder.push(...name.prefix);\n }\n\n if (name.given) {\n builder.push(...name.given);\n }\n\n if (name.family) {\n builder.push(name.family);\n }\n\n if (name.suffix && options?.suffix !== false) {\n builder.push(...name.suffix);\n }\n\n if (name.use && (options?.all || options?.use)) {\n builder.push('[' + name.use + ']');\n }\n\n if (builder.length === 0) {\n const textStr = ensureString(name.text);\n if (textStr) {\n return textStr;\n }\n }\n\n return builder.join(' ').trim();\n}\n\n/**\n * Formats the given name portion of a FHIR HumanName element.\n * @param name - The name to format.\n * @returns The formatted given name string.\n */\nexport function formatGivenName(name: HumanName): string {\n const builder: string[] = [];\n if (name.given) {\n builder.push(...name.given);\n }\n return builder.join(' ').trim();\n}\n\n/**\n * Formats the family name portion of a FHIR HumanName element.\n * @param name - The name to format.\n * @returns The formatted family name string.\n */\nexport function formatFamilyName(name: HumanName): string {\n return ensureString(name.family) ?? '';\n}\n\n/**\n * Returns true if the given date object is a valid date.\n * Dates can be invalid if created by parsing an invalid string.\n * @param date - A date object.\n * @returns Returns true if the date is a valid date.\n */\nexport function isValidDate(date: Date): boolean {\n return date instanceof Date && !isNaN(date.getTime());\n}\n\n/**\n * Formats a FHIR date string as a human readable string.\n * Handles missing values and invalid dates.\n * @param date - The date to format.\n * @param locales - Optional locales.\n * @param options - Optional date format options.\n * @returns The formatted date string.\n */\nexport function formatDate(\n date: string | undefined,\n locales?: Intl.LocalesArgument,\n options?: Intl.DateTimeFormatOptions\n): string {\n if (!date) {\n return '';\n }\n const d = new Date(date);\n if (!isValidDate(d)) {\n return '';\n }\n d.setUTCHours(0, 0, 0, 0);\n return d.toLocaleDateString(locales, { timeZone: 'UTC', ...options });\n}\n\n/**\n * Formats a FHIR time string as a human readable string.\n * Handles missing values and invalid dates.\n * @param time - The date to format.\n * @param locales - Optional locales.\n * @param options - Optional time format options.\n * @returns The formatted time string.\n */\nexport function formatTime(\n time: string | undefined,\n locales?: Intl.LocalesArgument,\n options?: Intl.DateTimeFormatOptions\n): string {\n if (!time) {\n return '';\n }\n const d = new Date('2000-01-01T' + time + 'Z');\n if (!isValidDate(d)) {\n return '';\n }\n return d.toLocaleTimeString(locales, options);\n}\n\n/**\n * Formats a FHIR dateTime string as a human readable string.\n * Handles missing values and invalid dates.\n * @param dateTime - The dateTime to format.\n * @param locales - Optional locales.\n * @param options - Optional dateTime format options.\n * @returns The formatted dateTime string.\n */\nexport function formatDateTime(\n dateTime: string | undefined,\n locales?: Intl.LocalesArgument,\n options?: Intl.DateTimeFormatOptions\n): string {\n if (!dateTime) {\n return '';\n }\n const d = new Date(dateTime);\n if (!isValidDate(d)) {\n return '';\n }\n return d.toLocaleString(locales, options);\n}\n\n/**\n * Formats a FHIR Period as a human readable string.\n * @param period - The period to format.\n * @param locales - Optional locales.\n * @param options - Optional period format options.\n * @returns The formatted period string.\n */\nexport function formatPeriod(\n period: Period | undefined,\n locales?: Intl.LocalesArgument,\n options?: Intl.DateTimeFormatOptions\n): string {\n if (!period || (!period.start && !period.end)) {\n return '';\n }\n return formatDateTime(period.start, locales, options) + ' - ' + formatDateTime(period.end, locales, options);\n}\n\nconst unitAdverbForm: Record<string, string> = {\n s: 'every second',\n min: 'every minute',\n h: 'hourly',\n d: 'daily',\n wk: 'weekly',\n mo: 'monthly',\n a: 'annually',\n};\n\nconst singularUnits: Record<string, string> = {\n s: 'second',\n min: 'minute',\n h: 'hour',\n d: 'day',\n wk: 'week',\n mo: 'month',\n a: 'year',\n};\n\nconst pluralUnits: Record<string, string> = {\n s: 'seconds',\n min: 'minutes',\n h: 'hours',\n d: 'days',\n wk: 'weeks',\n mo: 'months',\n a: 'years',\n};\n\n/**\n * Formats a FHIR Timing as a human readable string.\n * @param timing - The timing to format.\n * @returns The formatted timing string.\n */\nexport function formatTiming(timing: Timing | undefined): string {\n if (!timing) {\n return '';\n }\n\n const builder: string[] = [];\n formatTimingRepeat(builder, timing.repeat);\n\n if (timing.event) {\n builder.push(timing.event.map((d) => formatDateTime(d)).join(', '));\n }\n\n return capitalize(builder.join(' ').trim());\n}\n\n/**\n * Formats a FHIR Timing repeat element as a human readable string.\n * @param builder - The output string builder.\n * @param repeat - The timing repeat element.\n */\nfunction formatTimingRepeat(builder: string[], repeat: TimingRepeat | undefined): void {\n if (!repeat?.periodUnit) {\n // Period unit is the only required field\n return;\n }\n\n const frequency = repeat.frequency ?? 1;\n const period = repeat.period ?? 1;\n const periodUnit = repeat.periodUnit;\n\n if (frequency === 1 && period === 1) {\n builder.push(unitAdverbForm[periodUnit]);\n } else {\n if (frequency === 1) {\n builder.push('once');\n } else {\n builder.push(frequency + ' times');\n }\n\n if (period === 1) {\n builder.push('per ' + singularUnits[periodUnit]);\n } else {\n builder.push('per ' + period + ' ' + pluralUnits[periodUnit]);\n }\n }\n\n if (repeat.dayOfWeek) {\n builder.push('on ' + repeat.dayOfWeek.map(capitalize).join(', '));\n }\n\n if (repeat.timeOfDay) {\n builder.push('at ' + repeat.timeOfDay.map((t) => formatTime(t)).join(', '));\n }\n}\n\n/**\n * Returns a human-readable string for a FHIR Range datatype, taking into account one-sided ranges\n * @param range - A FHIR Range element\n * @param precision - Number of decimal places to display in the rendered quantity values\n * @param exclusive - If true, one-sided ranges will be rendered with the `>` or `<` bounds rather than `>=` or `<=`\n * @returns A human-readable string representation of the Range\n */\nexport function formatRange(range: Range | undefined, precision?: number, exclusive = false): string {\n if (exclusive && precision === undefined) {\n throw new Error('Precision must be specified for exclusive ranges');\n }\n\n // Extract high and low range endpoints, explicitly ignoring any comparator\n // since Range uses SimpleQuantity variants (see http://www.hl7.org/fhir/datatypes.html#Range)\n const low = range?.low && { ...range.low, comparator: undefined };\n const high = range?.high && { ...range.high, comparator: undefined };\n if (low?.value === undefined && high?.value === undefined) {\n return '';\n }\n\n if (low?.value !== undefined && high?.value === undefined) {\n // Lower bound only\n if (exclusive && precision !== undefined) {\n low.value = preciseDecrement(low.value, precision);\n return `> ${formatQuantity(low, precision)}`;\n }\n return `>= ${formatQuantity(low, precision)}`;\n } else if (low?.value === undefined && high?.value !== undefined) {\n // Upper bound only\n if (exclusive && precision !== undefined) {\n high.value = preciseIncrement(high.value, precision);\n return `< ${formatQuantity(high, precision)}`;\n }\n return `<= ${formatQuantity(high, precision)}`;\n } else {\n // Double-sided range\n if (low?.unit === high?.unit) {\n delete low?.unit; // Format like \"X - Y units\" instead of \"X units - Y units\"\n }\n return `${formatQuantity(low, precision)} - ${formatQuantity(high, precision)}`;\n }\n}\n\n/**\n * Returns a human-readable string for a FHIR Quantity datatype, taking into account units and comparators\n * @param quantity - A FHIR Quantity element\n * @param precision - Number of decimal places to display in the rendered quantity values\n * @returns A human-readable string representation of the Quantity\n */\nexport function formatQuantity(quantity: Quantity | undefined, precision?: number): string {\n if (!quantity) {\n return '';\n }\n\n const result = [];\n\n if (quantity.comparator) {\n result.push(quantity.comparator);\n result.push(' ');\n }\n\n if (quantity.value !== undefined) {\n if (precision !== undefined) {\n result.push(quantity.value.toFixed(precision));\n } else {\n result.push(quantity.value);\n }\n }\n\n if (quantity.unit) {\n if (quantity.unit !== '%' && result[result.length - 1] !== ' ') {\n result.push(' ');\n }\n result.push(quantity.unit);\n }\n\n return result.join('').trim();\n}\n\nexport function formatMoney(money: Money | undefined): string {\n if (money?.value === undefined) {\n return '';\n }\n\n return money.value.toLocaleString(undefined, {\n style: 'currency',\n currency: money.currency ?? 'USD',\n currencyDisplay: 'narrowSymbol',\n });\n}\n\n/**\n * Formats a CodeableConcept element as a string.\n * @param codeableConcept - A FHIR CodeableConcept element\n * @returns The codeable concept as a string.\n */\nexport function formatCodeableConcept(codeableConcept: CodeableConcept | undefined): string {\n if (!codeableConcept) {\n return '';\n }\n const textStr = ensureString(codeableConcept.text);\n if (textStr) {\n return textStr;\n }\n if (codeableConcept.coding) {\n return codeableConcept.coding.map((c) => formatCoding(c)).join(', ');\n }\n return '';\n}\n\n/**\n * Formats a Coding element as a string.\n * @param coding - A FHIR Coding element\n * @param includeCode - If true, includes both the code and display if available\n * @returns The coding as a string.\n */\nexport function formatCoding(coding: Coding | undefined, includeCode?: boolean): string {\n const display = ensureString(coding?.display);\n if (display) {\n const code = includeCode ? ensureString(coding?.code) : undefined;\n return `${display}${code ? ' (' + code + ')' : ''}`;\n }\n\n return ensureString(coding?.code) ?? '';\n}\n\n/**\n * Formats a FHIR Observation resource value as a string.\n * @param obs - A FHIR Observation resource.\n * @returns A human-readable string representation of the Observation.\n */\nexport function formatObservationValue(obs: Observation | ObservationComponent | undefined): string {\n if (!obs) {\n return '';\n }\n\n const result = [];\n\n if (obs.valueQuantity) {\n result.push(formatQuantity(obs.valueQuantity));\n } else if (obs.valueCodeableConcept) {\n result.push(formatCodeableConcept(obs.valueCodeableConcept));\n } else {\n const valueString = ensureString(obs.valueString);\n if (valueString) {\n result.push(valueString);\n }\n }\n\n if ('component' in obs) {\n result.push((obs.component as ObservationComponent[]).map((c) => formatObservationValue(c)).join(' / '));\n }\n\n return result.join(' / ').trim();\n}\n\n/**\n * Ensures the input is a string.\n * While the TypeScript type definitions for FHIR resources are strict, the actual input data can be malformed.\n * We use this method to protect against runtime errors.\n * @param input - The input to ensure is a string.\n * @returns The input as a string, or undefined if not a string.\n */\nfunction ensureString(input: unknown): string | undefined {\n return typeof input === 'string' ? input : undefined;\n}\n\n/**\n * Returns the input number increased by the `n` units of the specified precision\n * @param a - The input number.\n * @param precision - The precision in number of digits.\n * @param n - (default 1) The number of units to add.\n * @returns The result of the increment.\n */\nfunction preciseIncrement(a: number, precision: number, n = 1): number {\n return (toPreciseInteger(a, precision) + n) * Math.pow(10, -precision);\n}\n\n/**\n * Returns the input number decreased by the `n` units of the specified precision\n * @param a - The input number.\n * @param precision - The precision in number of digits.\n * @param n - (default 1) The number of units to subtract.\n * @returns The result of the decrement.\n */\nfunction preciseDecrement(a: number, precision: number, n = 1): number {\n return (toPreciseInteger(a, precision) - n) * Math.pow(10, -precision);\n}\n\n/**\n * Returns an integer representation of the number with the given precision.\n * For example, if precision is 2, then 1.2345 will be returned as 123.\n * @param a - The number.\n * @param precision - Optional precision in number of digits.\n * @returns The integer with the given precision.\n */\nfunction toPreciseInteger(a: number, precision?: number): number {\n if (precision === undefined) {\n return a;\n }\n return Math.round(a * Math.pow(10, precision));\n}\n", "import {\n Bundle,\n CodeableConcept,\n Coding,\n ElementDefinition,\n Reference,\n Resource,\n ResourceType,\n SearchParameter,\n StructureDefinition,\n} from '@medplum/fhirtypes';\nimport { formatHumanName } from './format';\nimport { SearchParameterDetails } from './search/details';\nimport { InternalSchemaElement, InternalTypeSchema, getAllDataTypes, tryGetDataType } from './typeschema/types';\nimport { capitalize, createReference } from './utils';\n\nexport type TypeName<T> = T extends string\n ? 'string'\n : T extends number\n ? 'number'\n : T extends boolean\n ? 'boolean'\n : T extends undefined\n ? 'undefined'\n : 'object';\n\nexport interface TypedValue {\n readonly type: string;\n readonly value: any;\n}\n\n/**\n * List of property types.\n * http://www.hl7.org/fhir/valueset-defined-types.html\n * The list here includes additions found from StructureDefinition resources.\n */\nexport const PropertyType = {\n Address: 'Address',\n Age: 'Age',\n Annotation: 'Annotation',\n Attachment: 'Attachment',\n BackboneElement: 'BackboneElement',\n CodeableConcept: 'CodeableConcept',\n Coding: 'Coding',\n ContactDetail: 'ContactDetail',\n ContactPoint: 'ContactPoint',\n Contributor: 'Contributor',\n Count: 'Count',\n DataRequirement: 'DataRequirement',\n Distance: 'Distance',\n Dosage: 'Dosage',\n Duration: 'Duration',\n Expression: 'Expression',\n Extension: 'Extension',\n HumanName: 'HumanName',\n Identifier: 'Identifier',\n MarketingStatus: 'MarketingStatus',\n Meta: 'Meta',\n Money: 'Money',\n Narrative: 'Narrative',\n ParameterDefinition: 'ParameterDefinition',\n Period: 'Period',\n Population: 'Population',\n ProdCharacteristic: 'ProdCharacteristic',\n ProductShelfLife: 'ProductShelfLife',\n Quantity: 'Quantity',\n Range: 'Range',\n Ratio: 'Ratio',\n Reference: 'Reference',\n RelatedArtifact: 'RelatedArtifact',\n SampledData: 'SampledData',\n Signature: 'Signature',\n SubstanceAmount: 'SubstanceAmount',\n SystemString: 'http://hl7.org/fhirpath/System.String',\n Timing: 'Timing',\n TriggerDefinition: 'TriggerDefinition',\n UsageContext: 'UsageContext',\n base64Binary: 'base64Binary',\n boolean: 'boolean',\n canonical: 'canonical',\n code: 'code',\n date: 'date',\n dateTime: 'dateTime',\n decimal: 'decimal',\n id: 'id',\n instant: 'instant',\n integer: 'integer',\n markdown: 'markdown',\n oid: 'oid',\n positiveInt: 'positiveInt',\n string: 'string',\n time: 'time',\n unsignedInt: 'unsignedInt',\n uri: 'uri',\n url: 'url',\n uuid: 'uuid',\n} as const;\n\n/**\n * An IndexedStructureDefinition is a lookup-optimized version of a StructureDefinition.\n *\n * StructureDefinition resources contain schema information for other resource types.\n * These schemas can be used to automatically generate user interface elements for\n * resources.\n *\n * However, a StructureDefinition resource is not optimized for realtime lookups. All\n * resource types, sub types, and property definitions are stored in a flat array of\n * ElementDefinition objects. Therefore, to lookup the schema for a property (i.e., \"Patient.name\")\n * requires a linear scan of all ElementDefinition objects\n *\n * A StructureDefinition resource contains information about one or more types.\n * For example, the \"Patient\" StructureDefinition includes \"Patient\", \"Patient_Contact\",\n * \"Patient_Communication\", and \"Patient_Link\". This is inefficient.\n *\n * Instead, we create an indexed version of the StructureDefinition, called IndexedStructureDefinition.\n * In an IndexedStructureDefinition, retrieving a property definition is a hashtable lookup.\n *\n * The hierarchy is:\n * IndexedStructureDefinition - top level for one resource type\n * TypeSchema - one per resource type and all contained BackboneElements\n * PropertySchema - one per property/field\n */\nexport interface IndexedStructureDefinition {\n types: Record<string, TypeInfo>;\n}\n\n/**\n * An indexed TypeSchema.\n *\n * Example: The IndexedStructureDefinition for \"Patient\" would include the following TypeSchemas:\n * 1) Patient\n * 2) Patient_Contact\n * 3) Patient_Communication\n * 4) Patient_Link\n */\nexport interface TypeInfo {\n searchParams?: Record<string, SearchParameter>;\n searchParamsDetails?: Record<string, SearchParameterDetails>;\n}\n\n/**\n * Indexes a bundle of SearchParameter resources for faster lookup.\n * @param bundle - A FHIR bundle SearchParameter resources.\n * @see {@link IndexedStructureDefinition} for more details on indexed StructureDefinitions.\n */\nexport function indexSearchParameterBundle(bundle: Bundle<SearchParameter>): void {\n for (const entry of bundle.entry ?? []) {\n const resource = entry.resource as SearchParameter;\n if (resource.resourceType === 'SearchParameter') {\n indexSearchParameter(resource);\n }\n }\n}\n\nexport function indexDefaultSearchParameters(bundle: StructureDefinition[] | Bundle): void {\n const maybeSDs = Array.isArray(bundle) ? bundle : (bundle.entry?.map((e) => e.resource) ?? []);\n for (const sd of maybeSDs) {\n if (sd?.resourceType === 'StructureDefinition' && sd.kind === 'resource') {\n getOrInitTypeSchema(sd.type);\n }\n }\n}\n\nfunction getOrInitTypeSchema(resourceType: string): TypeInfo {\n let typeSchema = globalSchema.types[resourceType];\n if (!typeSchema) {\n typeSchema = {\n searchParamsDetails: {},\n } as TypeInfo;\n globalSchema.types[resourceType] = typeSchema;\n }\n\n if (!typeSchema.searchParams) {\n typeSchema.searchParams = {\n _id: {\n base: [resourceType],\n code: '_id',\n type: 'token',\n expression: resourceType + '.id',\n } as SearchParameter,\n _lastUpdated: {\n base: [resourceType],\n code: '_lastUpdated',\n type: 'date',\n expression: resourceType + '.meta.lastUpdated',\n } as SearchParameter,\n _compartment: {\n base: [resourceType],\n code: '_compartment',\n type: 'reference',\n expression: resourceType + '.meta.compartment',\n } as SearchParameter,\n _profile: {\n base: [resourceType],\n code: '_profile',\n type: 'uri',\n expression: resourceType + '.meta.profile',\n } as SearchParameter,\n _security: {\n base: [resourceType],\n code: '_security',\n type: 'token',\n expression: resourceType + '.meta.security',\n } as SearchParameter,\n _source: {\n base: [resourceType],\n code: '_source',\n type: 'uri',\n expression: resourceType + '.meta.source',\n } as SearchParameter,\n _tag: {\n base: [resourceType],\n code: '_tag',\n type: 'token',\n expression: resourceType + '.meta.tag',\n } as SearchParameter,\n };\n }\n\n return typeSchema;\n}\n\n/**\n * Indexes a SearchParameter resource for fast lookup.\n * Indexes by SearchParameter.code, which is the query string parameter name.\n * @param searchParam - The SearchParameter resource.\n * @see {@link IndexedStructureDefinition} for more details on indexed StructureDefinitions.\n */\nexport function indexSearchParameter(searchParam: SearchParameter): void {\n for (const resourceType of searchParam.base ?? []) {\n const typeSchema = getOrInitTypeSchema(resourceType);\n\n if (!typeSchema.searchParams) {\n typeSchema.searchParams = {};\n }\n\n typeSchema.searchParams[searchParam.code as string] = searchParam;\n }\n}\n\n/**\n * Returns the type name for an ElementDefinition.\n * @param elementDefinition - The element definition.\n * @returns The Medplum type name.\n */\nexport function getElementDefinitionTypeName(elementDefinition: ElementDefinition): string {\n const code = elementDefinition.type?.[0]?.code as string;\n return code === 'BackboneElement' || code === 'Element'\n ? buildTypeName((elementDefinition.base?.path ?? elementDefinition.path)?.split('.') as string[])\n : code;\n}\n\nexport function buildTypeName(components: string[]): string {\n if (components.length === 1) {\n return components[0];\n }\n return components.map(capitalize).join('');\n}\n\n/**\n * Returns true if the type schema is a non-abstract FHIR resource.\n * @param typeSchema - The type schema to check.\n * @returns True if the type schema is a non-abstract FHIR resource.\n */\nexport function isResourceTypeSchema(typeSchema: InternalTypeSchema): boolean {\n return typeSchema.kind === 'resource' && typeSchema.name !== 'Resource' && typeSchema.name !== 'DomainResource';\n}\n\n/**\n * Returns an array of all resource types.\n * Note that this is based on globalSchema, and will only return resource types that are currently in memory.\n * @returns An array of all resource types.\n */\nexport function getResourceTypes(): ResourceType[] {\n return Object.values(getAllDataTypes())\n .filter(isResourceTypeSchema)\n .map((schema) => schema.name as ResourceType);\n}\n\n/**\n * Returns the search parameters for the resource type indexed by search code.\n * @param resourceType - The resource type.\n * @returns The search parameters for the resource type indexed by search code.\n */\nexport function getSearchParameters(resourceType: string): Record<string, SearchParameter> | undefined {\n return globalSchema.types[resourceType]?.searchParams;\n}\n\n/**\n * Returns a search parameter for a resource type by search code.\n * @param resourceType - The FHIR resource type.\n * @param code - The search parameter code.\n * @returns The search parameter if found, otherwise undefined.\n */\nexport function getSearchParameter(resourceType: string, code: string): SearchParameter | undefined {\n return globalSchema.types[resourceType]?.searchParams?.[code];\n}\n\n/**\n * Returns a human friendly display name for a FHIR element definition path.\n * @param path - The FHIR element definition path.\n * @returns The best guess of the display name.\n */\nexport function getPathDisplayName(path: string): string {\n // Get the property name, which is the remainder after the last period\n // For example, for path \"Patient.birthDate\"\n // the property name is \"birthDate\"\n const propertyName = path.replaceAll('[x]', '').split('.').pop() as string;\n\n return getPropertyDisplayName(propertyName);\n}\n\n/**\n * Returns a human friendly display name for a FHIR element property or slice name\n * @param propertyName - The FHIR element property or slice name\n * @returns The best guess of the display name.\n */\nexport function getPropertyDisplayName(propertyName: string): string {\n let words: string[];\n // CodeQL flags the regex below for potential ReDoS (Regex Denial of Service), so limit input size\n // https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS\n if (propertyName.length < 100) {\n /*\n Split into words looking for acronyms and camelCase\n\n [A-Z]+(?![a-z])\n This part of the regular expression matches a sequence of one or more uppercase letters ([A-Z]+)\n but only if they are not followed by a lowercase letter. The (?![a-z]) is a negative lookahead assertion,\n meaning it checks for the absence of a lowercase letter ([a-z]) following the uppercase letters but does\n not include it in the match. This effectively captures acronyms or any series of consecutive uppercase letters.\n\n [A-Z]?[a-z]+\n This part matches a single, optional, uppercase letter followed by one or more lowercase letters ([a-z]+).\n This pattern is suitable for matching words in camelCase format, where a word begins with a lowercase letter\n but can optionally start with an uppercase letter (like in the middle of camelCase).\n\n \\d+\n Matches a sequence of one or more digits into their own word\n */\n words = propertyName.match(/[A-Z]+(?![a-z])|[A-Z]?[a-z]+|\\d+/g) ?? [];\n } else {\n // fallback to splitting on capital letters\n words = propertyName.split(/(?=[A-Z])/);\n }\n\n // Capitalize the first letter of each word\n // Join together with spaces in between\n // Then normalize whitespace to single space character\n // For example, for property name \"birthDate\",\n // the display name is \"Birth Date\".\n return words.map(capitalizeDisplayWord).join(' ').replace('_', ' ').replace(/\\s+/g, ' ');\n}\n\nconst capitalizedWords = new Set(['ID', 'IP', 'PKCE', 'JWKS', 'URI', 'URL', 'OMB', 'UDI']);\n\nfunction capitalizeDisplayWord(word: string): string {\n const upper = word.toUpperCase();\n if (word === upper) {\n return word;\n }\n if (capitalizedWords.has(upper)) {\n return upper;\n }\n return upper.charAt(0) + word.slice(1);\n}\n\n/**\n * Returns an element definition by type and property name.\n * @param typeName - The type name.\n * @param propertyName - The property name.\n * @param profileUrl - (optional) The URL of the current resource profile\n * @returns The element definition if found.\n */\nexport function getElementDefinition(\n typeName: string,\n propertyName: string,\n profileUrl?: string\n): InternalSchemaElement | undefined {\n const typeSchema = tryGetDataType(typeName, profileUrl);\n if (!typeSchema) {\n return undefined;\n }\n return getElementDefinitionFromElements(typeSchema.elements, propertyName);\n}\n\n/**\n * Returns an element definition from mapping of elements by property name.\n * @param elements - A mapping of property names to element definitions\n * @param propertyName - The property name of interest\n * @returns The element definition if found.\n */\nexport function getElementDefinitionFromElements(\n elements: InternalTypeSchema['elements'],\n propertyName: string\n): InternalSchemaElement | undefined {\n // Always try to match the exact property name first\n const simpleMatch = elements[propertyName] ?? elements[propertyName + '[x]'];\n if (simpleMatch) {\n return simpleMatch;\n }\n\n // The propertyName can be a \"choice of type\" property, such as \"value[x]\", but in resolved form \"valueString\".\n // So we need to iterate through all the elements and find the one that matches.\n // Try to split on each capital letter, and see if that matches an element.\n for (let i = 0; i < propertyName.length; i++) {\n const c = propertyName[i];\n if (c >= 'A' && c <= 'Z') {\n const testProperty = propertyName.slice(0, i) + '[x]';\n const element = elements[testProperty];\n if (element) {\n return element;\n }\n }\n }\n\n // Otherwise, no matches.\n return undefined;\n}\n\n/**\n * Typeguard to validate that an object is a FHIR resource\n * @param value - The object to check\n * @returns True if the input is of type 'object' and contains property 'resourceType'\n */\nexport function isResource(value: unknown): value is Resource {\n return !!(value && typeof value === 'object' && 'resourceType' in value);\n}\n\n/**\n * Typeguard to validate that an object is a FHIR resource\n * @param value - The object to check\n * @returns True if the input is of type 'object' and contains property 'reference'\n */\nexport function isReference(value: unknown): value is Reference & { reference: string } {\n return !!(value && typeof value === 'object' && 'reference' in value && typeof value.reference === 'string');\n}\n\n/**\n * Global schema singleton.\n */\nexport const globalSchema: IndexedStructureDefinition = { types: {} };\n\n/**\n * Output the string representation of a value, suitable for use as part of a search query.\n * @param v - The value to format as a string\n * @returns The stringified value\n */\nexport function stringifyTypedValue(v: TypedValue): string {\n switch (v.type) {\n case PropertyType.uuid:\n case PropertyType.uri:\n case PropertyType.url:\n case PropertyType.string:\n case PropertyType.oid:\n case PropertyType.markdown:\n case PropertyType.id:\n case PropertyType.code:\n case PropertyType.canonical:\n case PropertyType.base64Binary:\n case PropertyType.SystemString:\n case PropertyType.date:\n case PropertyType.dateTime:\n case PropertyType.instant:\n // many types are represented as string primitives\n return v.value as string;\n case PropertyType.Identifier:\n return `${v.value.system ?? ''}|${v.value.value}`;\n case PropertyType.Coding:\n return stringifyCoding(v.value);\n case PropertyType.CodeableConcept:\n return (v.value as CodeableConcept).coding?.map(stringifyCoding).join(',') ?? v.value.text;\n case PropertyType.HumanName:\n if (v.value.text) {\n return v.value.text;\n }\n return formatHumanName(v.value);\n case PropertyType.unsignedInt:\n case PropertyType.positiveInt:\n case PropertyType.integer:\n case PropertyType.decimal:\n return (v.value as number).toString();\n case PropertyType.boolean:\n return v.value ? 'true' : 'false';\n case PropertyType.Extension:\n return v.value.url;\n case PropertyType.ContactPoint:\n return v.value.value;\n case PropertyType.Quantity:\n case PropertyType.Age:\n case PropertyType.Count:\n case PropertyType.Duration:\n return `${v.value.value}|${v.value.system ?? ''}|${v.value.code ?? v.value.unit ?? ''}`;\n case PropertyType.Reference:\n return v.value.reference;\n default:\n if (isResource(v.value)) {\n return createReference(v.value).reference;\n }\n return JSON.stringify(v);\n }\n}\n\nfunction stringifyCoding(coding: Coding | undefined): string {\n if (!coding) {\n return '';\n }\n return `${coding.system ?? ''}|${coding.code}`;\n}\n", "export function parseDateString(str: string): string {\n if (str.startsWith('T')) {\n // If a time string,\n // then normalize to full length.\n return str + 'T00:00:00.000Z'.substring(str.length);\n }\n\n if (str.length <= 10) {\n // If a local date (i.e., \"2021-01-01\"),\n // then return as-is.\n return str;\n }\n\n try {\n // Try to normalize to UTC\n return new Date(str).toISOString();\n } catch (_err) {\n // Fallback to original input\n // This happens on unsupported time formats such as \"2021-01-01T12\"\n return str;\n }\n}\n", "import { Reference, Resource } from '@medplum/fhirtypes';\nimport { Atom, AtomContext } from '../fhirlexer/parse';\nimport { PropertyType, TypedValue, isResource } from '../types';\nimport { calculateAge, getExtension, isEmpty, resolveId } from '../utils';\nimport { DotAtom, SymbolAtom } from './atoms';\nimport { parseDateString } from './date';\nimport { booleanToTypedValue, fhirPathIs, isQuantity, removeDuplicates, toJsBoolean, toTypedValue } from './utils';\n\n/*\n * Collection of FHIRPath\n * See: https://hl7.org/fhirpath/#functions\n */\n\nexport type FhirPathFunction = (context: AtomContext, input: TypedValue[], ...args: Atom[]) => TypedValue[];\n\n/**\n * Temporary placholder for unimplemented methods.\n * @returns Empty array.\n */\nconst stub: FhirPathFunction = (): [] => [];\n\nexport const functions: Record<string, FhirPathFunction> = {\n /*\n * 5.1 Existence\n * See: https://hl7.org/fhirpath/#existence\n */\n\n /**\n * Returns true if the input collection is empty ({ }) and false otherwise.\n *\n * See: https://hl7.org/fhirpath/#empty-boolean\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the input collection is empty ({ }) and false otherwise.\n */\n empty: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return booleanToTypedValue(input.length === 0 || input.every((e) => isEmpty(e.value)));\n },\n\n /**\n * Returns true if the input collection is not empty ({ }) and false otherwise.\n *\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the input collection is not empty ({ }) and false otherwise.\n */\n hasValue: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return booleanToTypedValue(input.length !== 0);\n },\n\n /**\n * Returns true if the collection has unknown elements, and false otherwise.\n * This is the opposite of empty(), and as such is a shorthand for empty().not().\n * If the input collection is empty ({ }), the result is false.\n *\n * The function can also take an optional criteria to be applied to the collection\n * prior to the determination of the exists. In this case, the function is shorthand\n * for where(criteria).exists().\n *\n * See: https://hl7.org/fhirpath/#existscriteria-expression-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param criteria - The evaluation criteria.\n * @returns True if the collection has unknown elements, and false otherwise.\n */\n exists: (context: AtomContext, input: TypedValue[], criteria?: Atom): TypedValue[] => {\n if (criteria) {\n return booleanToTypedValue(input.filter((e) => toJsBoolean(criteria.eval(context, [e]))).length > 0);\n } else {\n return booleanToTypedValue(input.length > 0 && input.every((e) => !isEmpty(e.value)));\n }\n },\n\n /**\n * Returns true if for every element in the input collection, criteria evaluates to true.\n * Otherwise, the result is false.\n *\n * If the input collection is empty ({ }), the result is true.\n *\n * See: https://hl7.org/fhirpath/#allcriteria-expression-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param criteria - The evaluation criteria.\n * @returns True if for every element in the input collection, criteria evaluates to true.\n */\n all: (context: AtomContext, input: TypedValue[], criteria: Atom): TypedValue[] => {\n return booleanToTypedValue(input.every((e) => toJsBoolean(criteria.eval(context, [e]))));\n },\n\n /**\n * Takes a collection of Boolean values and returns true if all the items are true.\n * If unknown items are false, the result is false.\n * If the input is empty ({ }), the result is true.\n *\n * See: https://hl7.org/fhirpath/#alltrue-boolean\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns True if all the items are true.\n */\n allTrue: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n for (const value of input) {\n if (!value.value) {\n return booleanToTypedValue(false);\n }\n }\n return booleanToTypedValue(true);\n },\n\n /**\n * Takes a collection of Boolean values and returns true if unknown of the items are true.\n * If all the items are false, or if the input is empty ({ }), the result is false.\n *\n * See: https://hl7.org/fhirpath/#anytrue-boolean\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns True if unknown of the items are true.\n */\n anyTrue: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n for (const value of input) {\n if (value.value) {\n return booleanToTypedValue(true);\n }\n }\n return booleanToTypedValue(false);\n },\n\n /**\n * Takes a collection of Boolean values and returns true if all the items are false.\n * If unknown items are true, the result is false.\n * If the input is empty ({ }), the result is true.\n *\n * See: https://hl7.org/fhirpath/#allfalse-boolean\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns True if all the items are false.\n */\n allFalse: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n for (const value of input) {\n if (value.value) {\n return booleanToTypedValue(false);\n }\n }\n return booleanToTypedValue(true);\n },\n\n /**\n * Takes a collection of Boolean values and returns true if unknown of the items are false.\n * If all the items are true, or if the input is empty ({ }), the result is false.\n *\n * See: https://hl7.org/fhirpath/#anyfalse-boolean\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns True if for every element in the input collection, criteria evaluates to true.\n */\n anyFalse: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n for (const value of input) {\n if (!value.value) {\n return booleanToTypedValue(true);\n }\n }\n return booleanToTypedValue(false);\n },\n\n /**\n * Returns true if all items in the input collection are members of the collection passed\n * as the other argument. Membership is determined using the = (Equals) (=) operation.\n *\n * Conceptually, this function is evaluated by testing each element in the input collection\n * for membership in the other collection, with a default of true. This means that if the\n * input collection is empty ({ }), the result is true, otherwise if the other collection\n * is empty ({ }), the result is false.\n *\n * See: http://hl7.org/fhirpath/#subsetofother-collection-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param other - The atom representing the collection of elements.\n * @returns True if all items in the input collection are members of the other collection.\n */\n subsetOf: (context: AtomContext, input: TypedValue[], other: Atom): TypedValue[] => {\n if (input.length === 0) {\n return booleanToTypedValue(true);\n }\n\n const otherArray = other.eval(context, getRootInput(context));\n if (otherArray.length === 0) {\n return booleanToTypedValue(false);\n }\n\n return booleanToTypedValue(input.every((e) => otherArray.some((o) => o.value === e.value)));\n },\n\n /**\n * Returns true if all items in the collection passed as the other argument are members of\n * the input collection. Membership is determined using the = (Equals) (=) operation.\n *\n * Conceptually, this function is evaluated by testing each element in the other collection\n * for membership in the input collection, with a default of true. This means that if the\n * other collection is empty ({ }), the result is true, otherwise if the input collection\n * is empty ({ }), the result is false.\n *\n * See: http://hl7.org/fhirpath/#supersetofother-collection-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param other - The atom representing the collection of elements.\n * @returns True if all items in the other collection are members of the input collection.\n */\n supersetOf: (context: AtomContext, input: TypedValue[], other: Atom): TypedValue[] => {\n const otherArray = other.eval(context, getRootInput(context));\n if (otherArray.length === 0) {\n return booleanToTypedValue(true);\n }\n\n if (input.length === 0) {\n return booleanToTypedValue(false);\n }\n\n return booleanToTypedValue(otherArray.every((e) => input.some((o) => o.value === e.value)));\n },\n\n /**\n * Returns the integer count of the number of items in the input collection.\n * Returns 0 when the input collection is empty.\n *\n * See: https://hl7.org/fhirpath/#count-integer\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The integer count of the number of items in the input collection.\n */\n count: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return [{ type: PropertyType.integer, value: input.length }];\n },\n\n /**\n * Returns a collection containing only the unique items in the input collection.\n * To determine whether two items are the same, the = (Equals) (=) operator is used,\n * as defined below.\n *\n * If the input collection is empty ({ }), the result is empty.\n *\n * Note that the order of elements in the input collection is not guaranteed to be\n * preserved in the result.\n *\n * See: https://hl7.org/fhirpath/#distinct-collection\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The integer count of the number of items in the input collection.\n */\n distinct: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n const result: TypedValue[] = [];\n for (const value of input) {\n if (!result.some((e) => e.value === value.value)) {\n result.push(value);\n }\n }\n return result;\n },\n\n /**\n * Returns true if all the items in the input collection are distinct.\n * To determine whether two items are distinct, the = (Equals) (=) operator is used,\n * as defined below.\n *\n * See: https://hl7.org/fhirpath/#isdistinct-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns The integer count of the number of items in the input collection.\n */\n isDistinct: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return booleanToTypedValue(input.length === functions.distinct(context, input).length);\n },\n\n /*\n * 5.2 Filtering and projection\n */\n\n /**\n * Returns a collection containing only those elements in the input collection\n * for which the stated criteria expression evaluates to true.\n * Elements for which the expression evaluates to false or empty ({ }) are not\n * included in the result.\n *\n * If the input collection is empty ({ }), the result is empty.\n *\n * If the result of evaluating the condition is other than a single boolean value,\n * the evaluation will end and signal an error to the calling environment,\n * consistent with singleton evaluation of collections behavior.\n *\n * See: https://hl7.org/fhirpath/#wherecriteria-expression-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param criteria - The condition atom.\n * @returns A collection containing only those elements in the input collection for which the stated criteria expression evaluates to true.\n */\n where: (context: AtomContext, input: TypedValue[], criteria: Atom): TypedValue[] => {\n return input.filter((e) => toJsBoolean(criteria.eval(context, [e])));\n },\n\n /**\n * Evaluates the projection expression for each item in the input collection.\n * The result of each evaluation is added to the output collection. If the\n * evaluation results in a collection with multiple items, all items are added\n * to the output collection (collections resulting from evaluation of projection\n * are flattened). This means that if the evaluation for an element results in\n * the empty collection ({ }), no element is added to the result, and that if\n * the input collection is empty ({ }), the result is empty as well.\n *\n * See: http://hl7.org/fhirpath/#selectprojection-expression-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param criteria - The condition atom.\n * @returns A collection containing only those elements in the input collection for which the stated criteria expression evaluates to true.\n */\n select: (context: AtomContext, input: TypedValue[], criteria: Atom): TypedValue[] => {\n return input.map((e) => criteria.eval({ parent: context, variables: { $this: e } }, [e])).flat();\n },\n\n /**\n * A version of select that will repeat the projection and add it to the output\n * collection, as long as the projection yields new items (as determined by\n * the = (Equals) (=) operator).\n *\n * See: http://hl7.org/fhirpath/#repeatprojection-expression-collection\n */\n repeat: stub,\n\n /**\n * Returns a collection that contains all items in the input collection that\n * are of the given type or a subclass thereof. If the input collection is\n * empty ({ }), the result is empty. The type argument is an identifier that\n * must resolve to the name of a type in a model\n *\n * See: http://hl7.org/fhirpath/#oftypetype-type-specifier-collection\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @param criteria - The condition atom.\n * @returns A collection containing only those elements in the input collection that are of the given type or a subclass thereof.\n */\n ofType: (_context: AtomContext, input: TypedValue[], criteria: Atom): TypedValue[] => {\n return input.filter((e) => e.type === (criteria as SymbolAtom).name);\n },\n\n /*\n * 5.3 Subsetting\n */\n\n /**\n * Will return the single item in the input if there is just one item.\n * If the input collection is empty ({ }), the result is empty.\n * If there are multiple items, an error is signaled to the evaluation environment.\n * This function is useful for ensuring that an error is returned if an assumption\n * about cardinality is violated at run-time.\n *\n * See: https://hl7.org/fhirpath/#single-collection\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The single item in the input if there is just one item.\n */\n single: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length > 1) {\n throw new Error('Expected input length one for single()');\n }\n return input.length === 0 ? [] : input.slice(0, 1);\n },\n\n /**\n * Returns a collection containing only the first item in the input collection.\n * This function is equivalent to item[0], so it will return an empty collection if the input collection has no items.\n *\n * See: https://hl7.org/fhirpath/#first-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing only the first item in the input collection.\n */\n first: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return input.length === 0 ? [] : input.slice(0, 1);\n },\n\n /**\n * Returns a collection containing only the last item in the input collection.\n * Will return an empty collection if the input collection has no items.\n *\n * See: https://hl7.org/fhirpath/#last-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing only the last item in the input collection.\n */\n last: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return input.length === 0 ? [] : input.slice(input.length - 1, input.length);\n },\n\n /**\n * Returns a collection containing all but the first item in the input collection.\n * Will return an empty collection if the input collection has no items, or only one item.\n *\n * See: https://hl7.org/fhirpath/#tail-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing all but the first item in the input collection.\n */\n tail: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return input.length === 0 ? [] : input.slice(1, input.length);\n },\n\n /**\n * Returns a collection containing all but the first num items in the input collection.\n * Will return an empty collection if there are no items remaining after the\n * indicated number of items have been skipped, or if the input collection is empty.\n * If num is less than or equal to zero, the input collection is simply returned.\n *\n * See: https://hl7.org/fhirpath/#skipnum-integer-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param num - The atom representing the number of elements to skip.\n * @returns A collection containing all but the first item in the input collection.\n */\n skip: (context: AtomContext, input: TypedValue[], num: Atom): TypedValue[] => {\n const numValue = num.eval(context, input)[0]?.value;\n if (typeof numValue !== 'number') {\n throw new Error('Expected a number for skip(num)');\n }\n if (numValue >= input.length) {\n return [];\n }\n if (numValue <= 0) {\n return input;\n }\n return input.slice(numValue, input.length);\n },\n\n /**\n * Returns a collection containing the first num items in the input collection,\n * or less if there are less than num items.\n * If num is less than or equal to 0, or if the input collection is empty ({ }),\n * take returns an empty collection.\n *\n * See: https://hl7.org/fhirpath/#takenum-integer-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param num - The atom representing the number of elements to take.\n * @returns A collection containing the first num items in the input collection.\n */\n take: (context: AtomContext, input: TypedValue[], num: Atom): TypedValue[] => {\n const numValue = num.eval(context, input)[0]?.value;\n if (typeof numValue !== 'number') {\n throw new Error('Expected a number for take(num)');\n }\n if (numValue >= input.length) {\n return input;\n }\n if (numValue <= 0) {\n return [];\n }\n return input.slice(0, numValue);\n },\n\n /**\n * Returns the set of elements that are in both collections.\n * Duplicate items will be eliminated by this function.\n * Order of items is not guaranteed to be preserved in the result of this function.\n *\n * See: http://hl7.org/fhirpath/#intersectother-collection-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param other - The atom representing the collection of elements to intersect.\n * @returns A collection containing the elements that are in both collections.\n */\n intersect: (context: AtomContext, input: TypedValue[], other: Atom): TypedValue[] => {\n if (!other) {\n return input;\n }\n const otherArray = other.eval(context, getRootInput(context));\n const result: TypedValue[] = [];\n for (const value of input) {\n if (!result.some((e) => e.value === value.value) && otherArray.some((e) => e.value === value.value)) {\n result.push(value);\n }\n }\n return result;\n },\n\n /**\n * Returns the set of elements that are not in the other collection.\n * Duplicate items will not be eliminated by this function, and order will be preserved.\n *\n * e.g. (1 | 2 | 3).exclude(2) returns (1 | 3).\n *\n * See: http://hl7.org/fhirpath/#excludeother-collection-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param other - The atom representing the collection of elements to exclude.\n * @returns A collection containing the elements that are in the input collection but not the other collection.\n */\n exclude: (context: AtomContext, input: TypedValue[], other: Atom): TypedValue[] => {\n if (!other) {\n return input;\n }\n const otherArray = other.eval(context, getRootInput(context));\n const result: TypedValue[] = [];\n for (const value of input) {\n if (!otherArray.some((e) => e.value === value.value)) {\n result.push(value);\n }\n }\n return result;\n },\n\n /*\n * 5.4. Combining\n *\n * See: https://hl7.org/fhirpath/#combining\n */\n\n /**\n * Merge the two collections into a single collection,\n * eliminating unknown duplicate values (using = (Equals) (=) to determine equality).\n * There is no expectation of order in the resulting collection.\n *\n * In other words, this function returns the distinct list of elements from both inputs.\n *\n * See: http://hl7.org/fhirpath/#unionother-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param other - The atom representing the collection of elements to merge.\n * @returns A collection containing the elements that represent the union of both collections.\n */\n union: (context: AtomContext, input: TypedValue[], other: Atom): TypedValue[] => {\n if (!other) {\n return input;\n }\n const otherArray = other.eval(context, getRootInput(context));\n return removeDuplicates([...input, ...otherArray]);\n },\n\n /**\n * Merge the input and other collections into a single collection\n * without eliminating duplicate values. Combining an empty collection\n * with a non-empty collection will return the non-empty collection.\n *\n * There is no expectation of order in the resulting collection.\n *\n * See: http://hl7.org/fhirpath/#combineother-collection-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param other - The atom representing the collection of elements to merge.\n * @returns A collection containing the elements that represent the combination of both collections including duplicates.\n */\n combine: (context: AtomContext, input: TypedValue[], other: Atom): TypedValue[] => {\n if (!other) {\n return input;\n }\n const otherArray = other.eval(context, getRootInput(context));\n return [...input, ...otherArray];\n },\n\n /**\n * Returns true if the input is a value HTML element.\n *\n * See: https://hl7.org/fhir/fhirpath.html#variables\n * @param _context - The evaluation context.\n * @param _input - The input collection.\n * @param _other - The atom representing the collection of elements to validate the html.\n * @returns A collection of boolean values\n */\n htmlChecks: (_context: AtomContext, _input: TypedValue[], _other: Atom): TypedValue[] => {\n return [toTypedValue(true)];\n },\n\n /*\n * 5.5. Conversion\n *\n * See: https://hl7.org/fhirpath/#conversion\n */\n\n /**\n * The iif function in FHIRPath is an immediate if,\n * also known as a conditional operator (such as C\u2019s ? : operator).\n *\n * The criterion expression is expected to evaluate to a Boolean.\n *\n * If criterion is true, the function returns the value of the true-result argument.\n *\n * If criterion is false or an empty collection, the function returns otherwise-result,\n * unless the optional otherwise-result is not given, in which case the function returns an empty collection.\n *\n * Note that short-circuit behavior is expected in this function. In other words,\n * true-result should only be evaluated if the criterion evaluates to true,\n * and otherwise-result should only be evaluated otherwise. For implementations,\n * this means delaying evaluation of the arguments.\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param criterion - The atom representing the conditional.\n * @param trueResult - The atom to be used if the conditional evaluates to true.\n * @param otherwiseResult - Optional atom to be used if the conditional evaluates to false.\n * @returns The result of the iif function.\n */\n iif: (\n context: AtomContext,\n input: TypedValue[],\n criterion: Atom,\n trueResult: Atom,\n otherwiseResult?: Atom\n ): TypedValue[] => {\n const evalResult = criterion.eval(context, input);\n if (evalResult.length > 1 || (evalResult.length === 1 && typeof evalResult[0].value !== 'boolean')) {\n throw new Error('Expected criterion to evaluate to a Boolean');\n }\n\n if (toJsBoolean(evalResult)) {\n return trueResult.eval(context, input);\n }\n\n if (otherwiseResult) {\n return otherwiseResult.eval(context, input);\n }\n\n return [];\n },\n\n /**\n * Converts an input collection to a boolean.\n *\n * If the input collection contains a single item, this function will return a single boolean if:\n * 1) the item is a Boolean\n * 2) the item is an Integer and is equal to one of the possible integer representations of Boolean values\n * 3) the item is a Decimal that is equal to one of the possible decimal representations of Boolean values\n * 4) the item is a String that is equal to one of the possible string representations of Boolean values\n *\n * If the item is not one the above types, or the item is a String, Integer, or Decimal, but is not equal to one of the possible values convertible to a Boolean, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#toboolean-boolean\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The input converted to boolean value.\n */\n toBoolean: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (typeof value === 'boolean') {\n return [{ type: PropertyType.boolean, value }];\n }\n if (typeof value === 'number') {\n if (value === 0 || value === 1) {\n return booleanToTypedValue(!!value);\n }\n }\n if (typeof value === 'string') {\n const lowerStr = value.toLowerCase();\n if (['true', 't', 'yes', 'y', '1', '1.0'].includes(lowerStr)) {\n return booleanToTypedValue(true);\n }\n if (['false', 'f', 'no', 'n', '0', '0.0'].includes(lowerStr)) {\n return booleanToTypedValue(false);\n }\n }\n return [];\n },\n\n /**\n * If the input collection contains a single item, this function will return true if:\n * 1) the item is a Boolean\n * 2) the item is an Integer that is equal to one of the possible integer representations of Boolean values\n * 3) the item is a Decimal that is equal to one of the possible decimal representations of Boolean values\n * 4) the item is a String that is equal to one of the possible string representations of Boolean values\n *\n * If the item is not one of the above types, or the item is a String, Integer, or Decimal, but is not equal to one of the possible values convertible to a Boolean, the result is false.\n *\n * Possible values for Integer, Decimal, and String are described in the toBoolean() function.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: http://hl7.org/fhirpath/#convertstoboolean-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the input can be converted to boolean.\n */\n convertsToBoolean: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n return booleanToTypedValue(functions.toBoolean(context, input).length === 1);\n },\n\n /**\n * Returns the integer representation of the input.\n *\n * If the input collection contains a single item, this function will return a single integer if:\n * 1) the item is an Integer\n * 2) the item is a String and is convertible to an integer\n * 3) the item is a Boolean, where true results in a 1 and false results in a 0.\n *\n * If the item is not one the above types, the result is empty.\n *\n * If the item is a String, but the string is not convertible to an integer (using the regex format (\\\\+|-)?\\d+), the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#tointeger-integer\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The string representation of the input.\n */\n toInteger: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (typeof value === 'number') {\n return [{ type: PropertyType.integer, value }];\n }\n if (typeof value === 'string' && /^[+-]?\\d+$/.exec(value)) {\n return [{ type: PropertyType.integer, value: parseInt(value, 10) }];\n }\n if (typeof value === 'boolean') {\n return [{ type: PropertyType.integer, value: value ? 1 : 0 }];\n }\n return [];\n },\n\n /**\n * Returns true if the input can be converted to string.\n *\n * If the input collection contains a single item, this function will return true if:\n * 1) the item is an Integer\n * 2) the item is a String and is convertible to an Integer\n * 3) the item is a Boolean\n * 4) If the item is not one of the above types, or the item is a String, but is not convertible to an Integer (using the regex format (\\\\+|-)?\\d+), the result is false.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#convertstointeger-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the input can be converted to an integer.\n */\n convertsToInteger: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n return booleanToTypedValue(functions.toInteger(context, input).length === 1);\n },\n\n /**\n * If the input collection contains a single item, this function will return a single date if:\n * 1) the item is a Date\n * 2) the item is a DateTime\n * 3) the item is a String and is convertible to a Date\n *\n * If the item is not one of the above types, the result is empty.\n *\n * If the item is a String, but the string is not convertible to a Date (using the format YYYY-MM-DD), the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#todate-date\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The value converted to a date if possible; otherwise empty array.\n */\n toDate: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (typeof value === 'string' && /^\\d{4}(-\\d{2}(-\\d{2})?)?/.exec(value)) {\n return [{ type: PropertyType.date, value: parseDateString(value) }];\n }\n return [];\n },\n\n /**\n * If the input collection contains a single item, this function will return true if:\n * 1) the item is a Date\n * 2) the item is a DateTime\n * 3) the item is a String and is convertible to a Date\n *\n * If the item is not one of the above types, or is not convertible to a Date (using the format YYYY-MM-DD), the result is false.\n *\n * If the item contains a partial date (e.g. '2012-01'), the result is a partial date.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#convertstodate-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the item can be converted to a date.\n */\n convertsToDate: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n return booleanToTypedValue(functions.toDate(context, input).length === 1);\n },\n\n /**\n * If the input collection contains a single item, this function will return a single datetime if:\n * 1) the item is a DateTime\n * 2) the item is a Date, in which case the result is a DateTime with the year, month, and day of the Date, and the time components empty (not set to zero)\n * 3) the item is a String and is convertible to a DateTime\n *\n * If the item is not one of the above types, the result is empty.\n *\n * If the item is a String, but the string is not convertible to a DateTime (using the format YYYY-MM-DDThh:mm:ss.fff(+|-)hh:mm), the result is empty.\n *\n * If the item contains a partial datetime (e.g. '2012-01-01T10:00'), the result is a partial datetime.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#todatetime-datetime\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The value converted to a datetime if possible; otherwise empty array.\n */\n toDateTime: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (typeof value === 'string' && /^\\d{4}(-\\d{2}(-\\d{2})?)?/.exec(value)) {\n return [{ type: PropertyType.dateTime, value: parseDateString(value) }];\n }\n return [];\n },\n\n /**\n * If the input collection contains a single item, this function will return true if:\n * 1) the item is a DateTime\n * 2) the item is a Date\n * 3) the item is a String and is convertible to a DateTime\n *\n * If the item is not one of the above types, or is not convertible to a DateTime (using the format YYYY-MM-DDThh:mm:ss.fff(+|-)hh:mm), the result is false.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#convertstodatetime-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the item can be converted to a dateTime.\n */\n convertsToDateTime: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n return booleanToTypedValue(functions.toDateTime(context, input).length === 1);\n },\n\n /**\n * If the input collection contains a single item, this function will return a single decimal if:\n * 1) the item is an Integer or Decimal\n * 2) the item is a String and is convertible to a Decimal\n * 3) the item is a Boolean, where true results in a 1.0 and false results in a 0.0.\n * 4) If the item is not one of the above types, the result is empty.\n *\n * If the item is a String, but the string is not convertible to a Decimal (using the regex format (\\\\+|-)?\\d+(\\.\\d+)?), the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#decimal-conversion-functions\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The value converted to a decimal if possible; otherwise empty array.\n */\n toDecimal: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (typeof value === 'number') {\n return [{ type: PropertyType.decimal, value }];\n }\n if (typeof value === 'string' && /^-?\\d{1,9}(\\.\\d{1,9})?$/.exec(value)) {\n return [{ type: PropertyType.decimal, value: parseFloat(value) }];\n }\n if (typeof value === 'boolean') {\n return [{ type: PropertyType.decimal, value: value ? 1 : 0 }];\n }\n return [];\n },\n\n /**\n * If the input collection contains a single item, this function will true if:\n * 1) the item is an Integer or Decimal\n * 2) the item is a String and is convertible to a Decimal\n * 3) the item is a Boolean\n *\n * If the item is not one of the above types, or is not convertible to a Decimal (using the regex format (\\\\+|-)?\\d+(\\.\\d+)?), the result is false.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#convertstodecimal-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns The value converted to a decimal if possible; otherwise empty array.\n */\n convertsToDecimal: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n return booleanToTypedValue(functions.toDecimal(context, input).length === 1);\n },\n\n /**\n * If the input collection contains a single item, this function will return a single quantity if:\n * 1) the item is an Integer, or Decimal, where the resulting quantity will have the default unit ('1')\n * 2) the item is a Quantity\n * 3) the item is a String and is convertible to a Quantity\n * 4) the item is a Boolean, where true results in the quantity 1.0 '1', and false results in the quantity 0.0 '1'\n *\n * If the item is not one of the above types, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#quantity-conversion-functions\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The value converted to a quantity if possible; otherwise empty array.\n */\n toQuantity: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (isQuantity(value)) {\n return [{ type: PropertyType.Quantity, value }];\n }\n if (typeof value === 'number') {\n return [{ type: PropertyType.Quantity, value: { value, unit: '1' } }];\n }\n if (typeof value === 'string' && /^-?\\d{1,9}(\\.\\d{1,9})?/.exec(value)) {\n return [{ type: PropertyType.Quantity, value: { value: parseFloat(value), unit: '1' } }];\n }\n if (typeof value === 'boolean') {\n return [{ type: PropertyType.Quantity, value: { value: value ? 1 : 0, unit: '1' } }];\n }\n return [];\n },\n\n /**\n * If the input collection contains a single item, this function will return true if:\n * 1) the item is an Integer, Decimal, or Quantity\n * 2) the item is a String that is convertible to a Quantity\n * 3) the item is a Boolean\n *\n * If the item is not one of the above types, or is not convertible to a Quantity using the following regex format:\n *\n * (?'value'(\\+|-)?\\d+(\\.\\d+)?)\\s*('(?'unit'[^']+)'|(?'time'[a-zA-Z]+))?\n *\n * then the result is false.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the unit argument is provided, it must be the string representation of a UCUM code (or a FHIRPath calendar duration keyword), and is used to determine whether the input quantity can be converted to the given unit, according to the unit conversion rules specified by UCUM. If the input quantity can be converted, the result is true, otherwise, the result is false.\n *\n * See: https://hl7.org/fhirpath/#convertstoquantityunit-string-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the item can be converted to a quantity.\n */\n convertsToQuantity: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n return booleanToTypedValue(functions.toQuantity(context, input).length === 1);\n },\n\n /**\n * Returns the string representation of the input.\n *\n * If the input collection contains a single item, this function will return a single String if:\n *\n * 1) the item in the input collection is a String\n * 2) the item in the input collection is an Integer, Decimal, Date, Time, DateTime, or Quantity the output will contain its String representation\n * 3) the item is a Boolean, where true results in 'true' and false in 'false'.\n *\n * If the item is not one of the above types, the result is false.\n *\n * See: https://hl7.org/fhirpath/#tostring-string\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The string representation of the input.\n */\n toString: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (value === null || value === undefined) {\n return [];\n }\n if (isQuantity(value)) {\n return [{ type: PropertyType.string, value: `${value.value} '${value.unit}'` }];\n }\n return [{ type: PropertyType.string, value: (value as boolean | number | string).toString() }];\n },\n\n /**\n * Returns true if the input can be converted to string.\n *\n * If the input collection contains a single item, this function will return true if:\n * 1) the item is a String\n * 2) the item is an Integer, Decimal, Date, Time, or DateTime\n * 3) the item is a Boolean\n * 4) the item is a Quantity\n *\n * If the item is not one of the above types, the result is false.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#tostring-string\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the item can be converted to a string\n */\n convertsToString: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n return booleanToTypedValue((functions.toString as unknown as FhirPathFunction)(context, input).length === 1);\n },\n\n /**\n * If the input collection contains a single item, this function will return a single time if:\n * 1) the item is a Time\n * 2) the item is a String and is convertible to a Time\n *\n * If the item is not one of the above types, the result is empty.\n *\n * If the item is a String, but the string is not convertible to a Time (using the format hh:mm:ss.fff(+|-)hh:mm), the result is empty.\n *\n * If the item contains a partial time (e.g. '10:00'), the result is a partial time.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#totime-time\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The value converted to a time if possible; otherwise empty array.\n */\n toTime: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (typeof value === 'string') {\n const match = /^T?(\\d{2}(:\\d{2}(:\\d{2})?)?)/.exec(value);\n if (match) {\n return [{ type: PropertyType.time, value: parseDateString('T' + match[1]) }];\n }\n }\n return [];\n },\n\n /**\n * If the input collection contains a single item, this function will return true if:\n * 1) the item is a Time\n * 2) the item is a String and is convertible to a Time\n *\n * If the item is not one of the above types, or is not convertible to a Time (using the format hh:mm:ss.fff(+|-)hh:mm), the result is false.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * If the input collection is empty, the result is empty.\n *\n * See: https://hl7.org/fhirpath/#convertstotime-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the item can be converted to a time.\n */\n convertsToTime: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n if (input.length === 0) {\n return [];\n }\n return booleanToTypedValue(functions.toTime(context, input).length === 1);\n },\n\n /*\n * 5.6. String Manipulation.\n *\n * See: https://hl7.org/fhirpath/#string-manipulation\n */\n\n /**\n * Returns the 0-based index of the first position substring is found in the input string, or -1 if it is not found.\n *\n * If substring is an empty string (''), the function returns 0.\n *\n * If the input or substring is empty ({ }), the result is empty ({ }).\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#indexofsubstring-string-integer\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param substringAtom - The substring to search for.\n * @returns The index of the substring.\n */\n indexOf: (context: AtomContext, input: TypedValue[], substringAtom: Atom): TypedValue[] => {\n return applyStringFunc((str, substring) => str.indexOf(substring as string), context, input, substringAtom);\n },\n\n /**\n * Returns the part of the string starting at position start (zero-based). If length is given, will return at most length number of characters from the input string.\n *\n * If start lies outside the length of the string, the function returns empty ({ }). If there are less remaining characters in the string than indicated by length, the function returns just the remaining characters.\n *\n * If the input or start is empty, the result is empty.\n *\n * If an empty length is provided, the behavior is the same as if length had not been provided.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param startAtom - The start index atom.\n * @param lengthAtom - Optional length atom.\n * @returns The substring.\n */\n substring: (context: AtomContext, input: TypedValue[], startAtom: Atom, lengthAtom?: Atom): TypedValue[] => {\n return applyStringFunc(\n (str, start, length) => {\n const startIndex = start as number;\n const endIndex = length ? startIndex + (length as number) : str.length;\n return startIndex < 0 || startIndex >= str.length ? undefined : str.substring(startIndex, endIndex);\n },\n context,\n input,\n startAtom,\n lengthAtom\n );\n },\n\n /**\n * Returns true when the input string starts with the given prefix.\n *\n * If prefix is the empty string (''), the result is true.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#startswithprefix-string-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param prefixAtom - The prefix substring to test.\n * @returns True if the input string starts with the given prefix string.\n */\n startsWith: (context: AtomContext, input: TypedValue[], prefixAtom: Atom): TypedValue[] => {\n return applyStringFunc((str, prefix) => str.startsWith(prefix as string), context, input, prefixAtom);\n },\n\n /**\n * Returns true when the input string ends with the given suffix.\n *\n * If suffix is the empty string (''), the result is true.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#endswithsuffix-string-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param suffixAtom - The suffix substring to test.\n * @returns True if the input string ends with the given suffix string.\n */\n endsWith: (context: AtomContext, input: TypedValue[], suffixAtom: Atom): TypedValue[] => {\n return applyStringFunc((str, suffix) => str.endsWith(suffix as string), context, input, suffixAtom);\n },\n\n /**\n * Returns true when the given substring is a substring of the input string.\n *\n * If substring is the empty string (''), the result is true.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#containssubstring-string-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param substringAtom - The substring to test.\n * @returns True if the input string contains the given substring.\n */\n contains: (context: AtomContext, input: TypedValue[], substringAtom: Atom): TypedValue[] => {\n return applyStringFunc((str, substring) => str.includes(substring as string), context, input, substringAtom);\n },\n\n /**\n * Returns the input string with all characters converted to upper case.\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#upper-string\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns The string converted to upper case.\n */\n upper: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyStringFunc((str) => str.toUpperCase(), context, input);\n },\n\n /**\n * Returns the input string with all characters converted to lower case.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#lower-string\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns The string converted to lower case.\n */\n lower: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyStringFunc((str) => str.toLowerCase(), context, input);\n },\n\n /**\n * Returns the input string with all instances of pattern replaced with substitution. If the substitution is the empty string (''),\n * instances of pattern are removed from the result. If pattern is the empty string (''), every character in the input string is\n * surrounded by the substitution, e.g. 'abc'.replace('','x') becomes 'xaxbxcx'.\n *\n * If the input collection, pattern, or substitution are empty, the result is empty ({ }).\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#replacepattern-string-substitution-string-string\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param patternAtom - The pattern to search for.\n * @param substitionAtom - The substition to replace with.\n * @returns The string with all instances of the search pattern replaced with the substitution string.\n */\n replace: (context: AtomContext, input: TypedValue[], patternAtom: Atom, substitionAtom: Atom): TypedValue[] => {\n return applyStringFunc(\n (str, pattern, substition) => str.replaceAll(pattern as string, substition as string),\n context,\n input,\n patternAtom,\n substitionAtom\n );\n },\n\n /**\n * Returns true when the value matches the given regular expression. Regular expressions should function consistently, regardless of any culture- and locale-specific settings in the environment, should be case-sensitive, use 'single line' mode and allow Unicode characters.\n *\n * If the input collection or regex are empty, the result is empty ({ }).\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#matchesregex-string-boolean\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param regexAtom - The regular expression atom.\n * @returns True if the input string matches the given regular expression.\n */\n matches: (context: AtomContext, input: TypedValue[], regexAtom: Atom): TypedValue[] => {\n return applyStringFunc((str, regex) => !!new RegExp(regex as string).exec(str), context, input, regexAtom);\n },\n\n /**\n * Matches the input using the regular expression in regex and replaces each match with the substitution string. The substitution may refer to identified match groups in the regular expression.\n *\n * If the input collection, regex, or substitution are empty, the result is empty ({ }).\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#replacematchesregex-string-substitution-string-string\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param regexAtom - The regular expression atom.\n * @param substitionAtom - The substition to replace with.\n * @returns The string with all instances of the search pattern replaced with the substitution string.\n */\n replaceMatches: (context: AtomContext, input: TypedValue[], regexAtom: Atom, substitionAtom: Atom): TypedValue[] => {\n return applyStringFunc(\n (str, pattern, substition) => str.replaceAll(pattern as string, substition as string),\n context,\n input,\n regexAtom,\n substitionAtom\n );\n },\n\n /**\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns The index of the substring.\n */\n length: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyStringFunc((str) => str.length, context, input);\n },\n\n /**\n * Returns the list of characters in the input string. If the input collection is empty ({ }), the result is empty.\n *\n * See: https://hl7.org/fhirpath/#tochars-collection\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns Array of characters.\n */\n toChars: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyStringFunc((str) => (str ? str.split('') : undefined), context, input);\n },\n\n /*\n * Additional string functions\n * See: https://build.fhir.org/ig/HL7/FHIRPath/#additional-string-functions\n * STU Note: the contents of this section are Standard for Trial Use (STU)\n */\n\n encode: stub,\n decode: stub,\n escape: stub,\n unescape: stub,\n trim: stub,\n split: stub,\n\n /**\n * The join function takes a collection of strings and joins them into a single string, optionally using the given separator.\n *\n * If the input is empty, the result is empty.\n *\n * If no separator is specified, the strings are directly concatenated.\n *\n * See: https://build.fhir.org/ig/HL7/FHIRPath/#joinseparator-string--string\n *\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param separatorAtom - Optional separator atom.\n * @returns The joined string.\n */\n join: (context: AtomContext, input: TypedValue[], separatorAtom: Atom): TypedValue[] => {\n const separator = separatorAtom?.eval(context, getRootInput(context))[0]?.value ?? '';\n if (typeof separator !== 'string') {\n throw new Error('Separator must be a string.');\n }\n return [{ type: PropertyType.string, value: input.map((i) => i.value?.toString() ?? '').join(separator) }];\n },\n\n /*\n * 5.7. Math\n */\n\n /**\n * Returns the absolute value of the input. When taking the absolute value of a quantity, the unit is unchanged.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#abs-integer-decimal-quantity\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing the result.\n */\n abs: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyMathFunc(Math.abs, context, input);\n },\n\n /**\n * Returns the first integer greater than or equal to the input.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#ceiling-integer\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing the result.\n */\n ceiling: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyMathFunc(Math.ceil, context, input);\n },\n\n /**\n * Returns e raised to the power of the input.\n *\n * If the input collection contains an Integer, it will be implicitly converted to a Decimal and the result will be a Decimal.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#exp-decimal\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing the result.\n */\n exp: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyMathFunc(Math.exp, context, input);\n },\n\n /**\n * Returns the first integer less than or equal to the input.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#floor-integer\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing the result.\n */\n floor: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyMathFunc(Math.floor, context, input);\n },\n\n /**\n * Returns the natural logarithm of the input (i.e. the logarithm base e).\n *\n * When used with an Integer, it will be implicitly converted to a Decimal.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#ln-decimal\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing the result.\n */\n ln: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyMathFunc(Math.log, context, input);\n },\n\n /**\n * Returns the logarithm base base of the input number.\n *\n * When used with Integers, the arguments will be implicitly converted to Decimal.\n *\n * If base is empty, the result is empty.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#logbase-decimal-decimal\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param baseAtom - The logarithm base.\n * @returns A collection containing the result.\n */\n log: (context: AtomContext, input: TypedValue[], baseAtom: Atom): TypedValue[] => {\n return applyMathFunc((value, base) => Math.log(value) / Math.log(base as number), context, input, baseAtom);\n },\n\n /**\n * Raises a number to the exponent power. If this function is used with Integers, the result is an Integer. If the function is used with Decimals, the result is a Decimal. If the function is used with a mixture of Integer and Decimal, the Integer is implicitly converted to a Decimal and the result is a Decimal.\n *\n * If the power cannot be represented (such as the -1 raised to the 0.5), the result is empty.\n *\n * If the input is empty, or exponent is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#powerexponent-integer-decimal-integer-decimal\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param expAtom - The exponent power.\n * @returns A collection containing the result.\n */\n power: (context: AtomContext, input: TypedValue[], expAtom: Atom): TypedValue[] => {\n return applyMathFunc(Math.pow as (x: number, ...args: unknown[]) => number, context, input, expAtom);\n },\n\n /**\n * Rounds the decimal to the nearest whole number using a traditional round (i.e. 0.5 or higher will round to 1). If specified, the precision argument determines the decimal place at which the rounding will occur. If not specified, the rounding will default to 0 decimal places.\n *\n * If specified, the number of digits of precision must be >= 0 or the evaluation will end and signal an error to the calling environment.\n *\n * If the input collection contains a single item of type Integer, it will be implicitly converted to a Decimal.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#roundprecision-integer-decimal\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing the result.\n */\n round: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyMathFunc(Math.round, context, input);\n },\n\n /**\n * Returns the square root of the input number as a Decimal.\n *\n * If the square root cannot be represented (such as the square root of -1), the result is empty.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * Note that this function is equivalent to raising a number of the power of 0.5 using the power() function.\n *\n * See: https://hl7.org/fhirpath/#sqrt-decimal\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing the result.\n */\n sqrt: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyMathFunc(Math.sqrt, context, input);\n },\n\n /**\n * Returns the integer portion of the input.\n *\n * If the input collection is empty, the result is empty.\n *\n * If the input collection contains multiple items, the evaluation of the expression will end and signal an error to the calling environment.\n *\n * See: https://hl7.org/fhirpath/#truncate-integer\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns A collection containing the result.\n */\n truncate: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return applyMathFunc((x) => x | 0, context, input);\n },\n\n /*\n * 5.8. Tree navigation\n */\n\n children: stub,\n\n descendants: stub,\n\n /*\n * 5.9. Utility functions\n */\n\n /**\n * Adds a String representation of the input collection to the diagnostic log,\n * using the name argument as the name in the log. This log should be made available\n * to the user in some appropriate fashion. Does not change the input, so returns\n * the input collection as output.\n *\n * If the projection argument is used, the trace would log the result of evaluating\n * the project expression on the input, but still return the input to the trace\n * function unchanged.\n *\n * See: https://hl7.org/fhirpath/#tracename-string-projection-expression-collection\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @param _nameAtom - The log name.\n * @returns The input collection.\n */\n trace: (_context: AtomContext, input: TypedValue[], _nameAtom: Atom): TypedValue[] => {\n return input;\n },\n\n /**\n * Returns the current date and time, including timezone offset.\n *\n * See: https://hl7.org/fhirpath/#now-datetime\n * @returns The current dateTime.\n */\n now: (): TypedValue[] => {\n return [{ type: PropertyType.dateTime, value: new Date().toISOString() }];\n },\n\n /**\n * Returns the current time.\n *\n * See: https://hl7.org/fhirpath/#timeofday-time\n * @returns The current time string.\n */\n timeOfDay: (): TypedValue[] => {\n return [{ type: PropertyType.time, value: new Date().toISOString().substring(11) }];\n },\n\n /**\n * Returns the current date.\n *\n * See: https://hl7.org/fhirpath/#today-date\n * @returns The current date string.\n */\n today: (): TypedValue[] => {\n return [{ type: PropertyType.date, value: new Date().toISOString().substring(0, 10) }];\n },\n\n /**\n * Calculates the difference between two dates or date/times.\n *\n * This is not part of the official FHIRPath spec.\n *\n * IBM FHIR issue: https://github.com/IBM/FHIR/issues/1014\n * IBM FHIR PR: https://github.com/IBM/FHIR/pull/1023\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param startAtom - The start date/time.\n * @param endAtom - The end date/time.\n * @param unitsAtom - Which units to return (\"years\", \"months\", or \"days\").\n * @returns The Quantity of time between the two dates.\n */\n between: (\n context: AtomContext,\n input: TypedValue[],\n startAtom: Atom,\n endAtom: Atom,\n unitsAtom: Atom\n ): TypedValue[] => {\n const startDate = functions.toDateTime(context, startAtom.eval(context, input));\n if (startDate.length === 0) {\n throw new Error('Invalid start date');\n }\n const endDate = functions.toDateTime(context, endAtom.eval(context, input));\n if (endDate.length === 0) {\n throw new Error('Invalid end date');\n }\n const unit = unitsAtom.eval(context, input)[0]?.value as string;\n if (unit !== 'years' && unit !== 'months' && unit !== 'days') {\n throw new Error('Invalid units');\n }\n const age = calculateAge(startDate[0].value, endDate[0].value);\n return [{ type: PropertyType.Quantity, value: { value: age[unit], unit } }];\n },\n\n /*\n * 6.3 Types\n */\n\n /**\n * The is() function is supported for backwards compatibility with previous\n * implementations of FHIRPath. Just as with the is keyword, the type argument\n * is an identifier that must resolve to the name of a type in a model.\n *\n * For implementations with compile-time typing, this requires special-case\n * handling when processing the argument to treat it as a type specifier rather\n * than an identifier expression:\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @param typeAtom - The desired type.\n * @returns True if the input element is of the desired type.\n */\n is: (_context: AtomContext, input: TypedValue[], typeAtom: Atom): TypedValue[] => {\n let typeName = '';\n if (typeAtom instanceof SymbolAtom) {\n typeName = typeAtom.name;\n } else if (typeAtom instanceof DotAtom) {\n typeName = (typeAtom.left as SymbolAtom).name + '.' + (typeAtom.right as SymbolAtom).name;\n }\n if (!typeName) {\n return [];\n }\n return input.map((value) => ({ type: PropertyType.boolean, value: fhirPathIs(value, typeName) }));\n },\n\n /*\n * 6.5 Boolean logic\n */\n\n /**\n * 6.5.3. not() : Boolean\n *\n * Returns true if the input collection evaluates to false, and false if it evaluates to true. Otherwise, the result is empty ({ }):\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @returns True if the input evaluates to false.\n */\n not: (context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return functions.toBoolean(context, input).map((value) => ({ type: PropertyType.boolean, value: !value.value }));\n },\n\n /*\n * Additional functions\n * See: https://hl7.org/fhir/fhirpath.html#functions\n */\n\n /**\n * For each item in the collection, if it is a string that is a uri (or canonical or url), locate the target of the reference, and add it to the resulting collection. If the item does not resolve to a resource, the item is ignored and nothing is added to the output collection.\n * The items in the collection may also represent a Reference, in which case the Reference.reference is resolved.\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The resolved resource.\n */\n resolve: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return input\n .map((e) => {\n const value = e.value;\n let refStr: string | undefined;\n if (typeof value === 'string') {\n refStr = value;\n } else if (typeof value === 'object') {\n const ref = value as Reference;\n if (ref.resource) {\n return toTypedValue(ref.resource);\n }\n if (ref.reference) {\n refStr = ref.reference;\n } else if (ref.type && ref.identifier) {\n refStr = `${ref.type}?identifier=${ref.identifier.system}|${ref.identifier.value}`;\n }\n }\n if (refStr?.includes('?')) {\n const [resourceType] = refStr.split('?');\n return { type: resourceType, value: { resourceType } };\n }\n if (refStr?.includes('/')) {\n const [resourceType, id] = refStr.split('/');\n return { type: resourceType, value: { resourceType, id } };\n }\n return { type: PropertyType.BackboneElement, value: undefined };\n })\n .filter((e) => !!e.value);\n },\n\n /**\n * The as operator can be used to treat a value as a specific type.\n * @param _context - The evaluation context.\n * @param input - The input value.\n * @returns The value as the specific type.\n */\n as: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return input;\n },\n\n /*\n * 12. Formal Specifications\n */\n\n /**\n * Returns the type of the input.\n *\n * 12.2. Model Information\n *\n * The model information returned by the reflection function type() is specified as an\n * XML Schema document (xsd) and included in this specification at the following link:\n * https://hl7.org/fhirpath/modelinfo.xsd\n *\n * See: https://hl7.org/fhirpath/#model-information\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The type of the input value.\n */\n type: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n return input.map(({ value }) => {\n if (typeof value === 'boolean') {\n return { type: PropertyType.BackboneElement, value: { namespace: 'System', name: 'Boolean' } };\n }\n if (typeof value === 'number') {\n return { type: PropertyType.BackboneElement, value: { namespace: 'System', name: 'Integer' } };\n }\n if (isResource(value)) {\n return {\n type: PropertyType.BackboneElement,\n value: { namespace: 'FHIR', name: value.resourceType },\n };\n }\n return { type: PropertyType.BackboneElement, value: null };\n });\n },\n\n conformsTo: (context: AtomContext, input: TypedValue[], systemAtom: Atom): TypedValue[] => {\n const system = systemAtom.eval(context, input)[0].value as string;\n if (!system.startsWith('http://hl7.org/fhir/StructureDefinition/')) {\n throw new Error('Expected a StructureDefinition URL');\n }\n const expectedResourceType = system.replace('http://hl7.org/fhir/StructureDefinition/', '');\n return input.map((value) => ({\n type: PropertyType.boolean,\n value: value.value?.resourceType === expectedResourceType,\n }));\n },\n\n /*\n * SQL-on-FHIR utilities\n */\n\n /**\n * Returns an opaque value to be used as the primary key for the row associated with the resource.\n *\n * In many cases the value may just be the resource id, but exceptions are described below.\n * This function is used in tandem with getReferenceKey(), which returns an equal value from references that point to this resource.\n *\n * The returned KeyType is implementation dependent, but must be a FHIR primitive type that can be used for efficient joins in the\n * system\u2019s underlying data storage. Integers, strings, UUIDs, and other primitive types are appropriate.\n *\n * See: https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/StructureDefinition-ViewDefinition.html#getresourcekey--keytype\n *\n * @param _context - The evaluation context.\n * @param input - The input collection.\n * @returns The resource key.\n */\n getResourceKey: (_context: AtomContext, input: TypedValue[]): TypedValue[] => {\n const resource = input[0].value as Resource;\n if (!resource?.id) {\n return [];\n }\n return [{ type: PropertyType.id, value: resource.id }];\n },\n\n /**\n * Returns an opaque value that represents the database key of the row being referenced.\n *\n * The value returned must be equal to the getResourceKey() value returned on the resource itself.\n *\n * Users may pass an optional resource type (e.g., Patient or Observation) to indicate the expected type that the reference should point to.\n * The getReferenceKey() will return an empty collection (effectively null since FHIRPath always returns collections) if the reference is not of the expected type.\n * For example, Observation.subject.getReferenceKey(Patient) would return a row key if the subject is a Patient, or the empty collection (i.e., {}) if it is not.\n *\n * The returned KeyType is implementation dependent, but must be a FHIR primitive type that can be used for efficient joins in the systems underlying data storage.\n * Integers, strings, UUIDs, and other primitive types are appropriate.\n *\n * See: https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/StructureDefinition-ViewDefinition.html#getreferencekeyresource-type-specifier--keytype\n *\n * @param context - The evaluation context.\n * @param input - The input collection.\n * @param typeAtom - Optional expected resource type.\n * @returns The reference key.\n */\n getReferenceKey: (context: AtomContext, input: TypedValue[], typeAtom: Atom): TypedValue[] => {\n const reference = input[0].value as Reference;\n if (!reference?.reference) {\n return [];\n }\n\n let typeName = '';\n if (typeAtom instanceof SymbolAtom) {\n typeName = typeAtom.name;\n }\n if (typeName && !reference.reference.startsWith(typeName + '/')) {\n return [];\n }\n\n return [{ type: PropertyType.id, value: resolveId(reference) }];\n },\n\n extension: (context: AtomContext, input: TypedValue[], urlAtom: Atom): TypedValue[] => {\n const url = urlAtom.eval(context, input)[0].value as string;\n const resource = input?.[0]?.value;\n if (resource) {\n const extension = getExtension(resource, url);\n if (extension) {\n return [{ type: PropertyType.Extension, value: extension }];\n }\n }\n return [];\n },\n};\n\n/*\n * Helper utilities\n */\n\nfunction applyStringFunc<T>(\n func: (str: string, ...args: unknown[]) => T | undefined,\n context: AtomContext,\n input: TypedValue[],\n ...argsAtoms: (Atom | undefined)[]\n): TypedValue[] {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n if (typeof value !== 'string') {\n throw new Error('String function cannot be called with non-string');\n }\n const result = func(value, ...argsAtoms.map((atom) => atom?.eval(context, input)[0]?.value));\n if (result === undefined) {\n return [];\n }\n if (Array.isArray(result)) {\n return result.map(toTypedValue);\n }\n return [toTypedValue(result)];\n}\n\nfunction applyMathFunc(\n func: (x: number, ...args: unknown[]) => number,\n context: AtomContext,\n input: TypedValue[],\n ...argsAtoms: Atom[]\n): TypedValue[] {\n if (input.length === 0) {\n return [];\n }\n const [{ value }] = validateInput(input, 1);\n const quantity = isQuantity(value);\n const numberInput = quantity ? value.value : value;\n if (typeof numberInput !== 'number') {\n throw new Error('Math function cannot be called with non-number');\n }\n const result = func(numberInput, ...argsAtoms.map((atom) => atom.eval(context, input)[0]?.value));\n const type = quantity ? PropertyType.Quantity : input[0].type;\n const returnValue = quantity ? { ...value, value: result } : result;\n return [{ type, value: returnValue }];\n}\n\nfunction validateInput(input: TypedValue[], count: number): TypedValue[] {\n if (input.length !== count) {\n throw new Error(`Expected ${count} arguments`);\n }\n for (const element of input) {\n if (element === null || element === undefined) {\n throw new Error('Expected non-null argument');\n }\n }\n return input;\n}\n\nfunction getRootInput(context: AtomContext): [TypedValue] {\n let last = context;\n while (last.parent?.variables.$this) {\n last = last.parent;\n }\n return [last.variables.$this];\n}\n", "import { Atom, AtomContext, InfixOperatorAtom, PrefixOperatorAtom } from '../fhirlexer/parse';\nimport { PropertyType, TypedValue, isResource } from '../types';\nimport { functions } from './functions';\nimport {\n booleanToTypedValue,\n fhirPathArrayEquals,\n fhirPathArrayEquivalent,\n fhirPathArrayNotEquals,\n fhirPathIs,\n fhirPathNot,\n getTypedPropertyValue,\n isQuantity,\n removeDuplicates,\n singleton,\n toTypedValue,\n} from './utils';\n\nexport class FhirPathAtom implements Atom {\n constructor(\n public readonly original: string,\n public readonly child: Atom\n ) {}\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n try {\n if (input.length > 0) {\n const result = [];\n for (const e of input) {\n result.push(this.child.eval({ parent: context, variables: { $this: e } }, [e]));\n }\n return result.flat();\n } else {\n return this.child.eval(context, []);\n }\n } catch (error) {\n throw new Error(`FhirPathError on \"${this.original}\": ${error}`, { cause: error });\n }\n }\n\n toString(): string {\n return this.child.toString();\n }\n}\n\nexport class LiteralAtom implements Atom {\n constructor(public readonly value: TypedValue) {}\n eval(): TypedValue[] {\n return [this.value];\n }\n\n toString(): string {\n const value = this.value.value;\n if (typeof value === 'string') {\n return `'${value}'`;\n }\n return value.toString();\n }\n}\n\nexport class SymbolAtom implements Atom {\n constructor(public readonly name: string) {}\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n if (this.name === '$this') {\n return input;\n }\n const variableValue = this.getVariable(context);\n if (variableValue) {\n return [variableValue];\n }\n if (this.name.startsWith('%')) {\n throw new Error(`Undefined variable ${this.name}`);\n }\n return input.flatMap((e) => this.evalValue(e)).filter((e) => e?.value !== undefined) as TypedValue[];\n }\n\n private getVariable(context: AtomContext): TypedValue | undefined {\n const value = context.variables[this.name];\n if (value !== undefined) {\n return value;\n }\n\n if (context.parent) {\n return this.getVariable(context.parent);\n }\n\n return undefined;\n }\n\n private evalValue(typedValue: TypedValue): TypedValue[] | TypedValue | undefined {\n const input = typedValue.value;\n if (!input || typeof input !== 'object') {\n return undefined;\n }\n\n if (isResource(input) && input.resourceType === this.name) {\n return typedValue;\n }\n\n return getTypedPropertyValue(typedValue, this.name);\n }\n\n toString(): string {\n return this.name;\n }\n}\n\nexport class EmptySetAtom implements Atom {\n eval(): [] {\n return [];\n }\n\n toString(): string {\n return '{}';\n }\n}\n\nexport class UnaryOperatorAtom extends PrefixOperatorAtom {\n constructor(\n operator: string,\n child: Atom,\n public readonly impl: (x: TypedValue[]) => TypedValue[]\n ) {\n super(operator, child);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n return this.impl(this.child.eval(context, input));\n }\n\n toString(): string {\n return this.operator + this.child.toString();\n }\n}\n\nexport class AsAtom extends InfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('as', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n return functions.ofType(context, this.left.eval(context, input), this.right);\n }\n}\n\nexport abstract class BooleanInfixOperatorAtom extends InfixOperatorAtom {\n abstract eval(context: AtomContext, input: TypedValue[]): TypedValue[];\n}\n\nexport class ArithemticOperatorAtom extends BooleanInfixOperatorAtom {\n constructor(\n operator: string,\n left: Atom,\n right: Atom,\n public readonly impl: (x: number, y: number) => number | boolean\n ) {\n super(operator, left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftEvalResult = this.left.eval(context, input);\n if (leftEvalResult.length !== 1) {\n return [];\n }\n const rightEvalResult = this.right.eval(context, input);\n if (rightEvalResult.length !== 1) {\n return [];\n }\n const leftValue = leftEvalResult[0].value;\n const rightValue = rightEvalResult[0].value;\n const leftNumber = isQuantity(leftValue) ? leftValue.value : leftValue;\n const rightNumber = isQuantity(rightValue) ? rightValue.value : rightValue;\n const result = this.impl(leftNumber, rightNumber);\n if (typeof result === 'boolean') {\n return booleanToTypedValue(result);\n } else if (isQuantity(leftValue)) {\n return [{ type: PropertyType.Quantity, value: { ...leftValue, value: result } }];\n } else {\n return [toTypedValue(result)];\n }\n }\n}\n\nexport class ConcatAtom extends InfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('&', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftValue = this.left.eval(context, input);\n const rightValue = this.right.eval(context, input);\n const result = [...leftValue, ...rightValue];\n if (result.length > 0 && result.every((e) => typeof e.value === 'string')) {\n return [{ type: PropertyType.string, value: result.map((e) => e.value as string).join('') }];\n }\n return result;\n }\n}\n\nexport class ContainsAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('contains', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftValue = this.left.eval(context, input);\n const rightValue = this.right.eval(context, input);\n return booleanToTypedValue(leftValue.some((e) => e.value === rightValue[0].value));\n }\n}\n\nexport class InAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('in', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const left = singleton(this.left.eval(context, input));\n const right = this.right.eval(context, input);\n if (!left) {\n return [];\n }\n return booleanToTypedValue(right.some((e) => e.value === left.value));\n }\n}\n\nexport class DotAtom extends InfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('.', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n return this.right.eval(context, this.left.eval(context, input));\n }\n\n toString(): string {\n return `${this.left.toString()}.${this.right.toString()}`;\n }\n}\n\nexport class UnionAtom extends InfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('|', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftResult = this.left.eval(context, input);\n const rightResult = this.right.eval(context, input);\n return removeDuplicates([...leftResult, ...rightResult]);\n }\n}\n\nexport class EqualsAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('=', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftValue = this.left.eval(context, input);\n const rightValue = this.right.eval(context, input);\n return fhirPathArrayEquals(leftValue, rightValue);\n }\n}\n\nexport class NotEqualsAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('!=', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftValue = this.left.eval(context, input);\n const rightValue = this.right.eval(context, input);\n return fhirPathArrayNotEquals(leftValue, rightValue);\n }\n}\n\nexport class EquivalentAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('~', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftValue = this.left.eval(context, input);\n const rightValue = this.right.eval(context, input);\n return fhirPathArrayEquivalent(leftValue, rightValue);\n }\n}\n\nexport class NotEquivalentAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('!~', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftValue = this.left.eval(context, input);\n const rightValue = this.right.eval(context, input);\n return fhirPathNot(fhirPathArrayEquivalent(leftValue, rightValue));\n }\n}\n\nexport class IsAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('is', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const leftValue = this.left.eval(context, input);\n if (leftValue.length !== 1) {\n return [];\n }\n const typeName = (this.right as SymbolAtom).name;\n return booleanToTypedValue(fhirPathIs(leftValue[0], typeName));\n }\n}\n\n/**\n * 6.5.1. and\n * Returns true if both operands evaluate to true,\n * false if either operand evaluates to false,\n * and the empty collection otherwise.\n */\nexport class AndAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('and', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const left = singleton(this.left.eval(context, input), 'boolean');\n const right = singleton(this.right.eval(context, input), 'boolean');\n if (left?.value === true && right?.value === true) {\n return booleanToTypedValue(true);\n }\n if (left?.value === false || right?.value === false) {\n return booleanToTypedValue(false);\n }\n return [];\n }\n}\n\n/**\n * 6.5.2. or\n * Returns false if both operands evaluate to false,\n * true if either operand evaluates to true,\n * and empty (`{ }`) otherwise:\n */\nexport class OrAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('or', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const left = singleton(this.left.eval(context, input), 'boolean');\n const right = singleton(this.right.eval(context, input), 'boolean');\n if (left?.value === false && right?.value === false) {\n return booleanToTypedValue(false);\n } else if (left?.value || right?.value) {\n return booleanToTypedValue(true);\n } else {\n return [];\n }\n }\n}\n\n/**\n * 6.5.4. xor\n * Returns true if exactly one of the operands evaluates to true,\n * false if either both operands evaluate to true or both operands evaluate to false,\n * and the empty collection otherwise.\n */\nexport class XorAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('xor', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const left = singleton(this.left.eval(context, input), 'boolean');\n const right = singleton(this.right.eval(context, input), 'boolean');\n if (!left || !right) {\n return [];\n }\n return booleanToTypedValue(left.value !== right.value);\n }\n}\n\n/**\n * 6.5.5. implies\n * Returns true if left is true and right is true,\n * true left is false and right true, false or empty\n * true left is empty\n */\nexport class ImpliesAtom extends BooleanInfixOperatorAtom {\n constructor(left: Atom, right: Atom) {\n super('implies', left, right);\n }\n\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const left = singleton(this.left.eval(context, input), 'boolean');\n const right = singleton(this.right.eval(context, input), 'boolean');\n if (right?.value === true || left?.value === false) {\n return booleanToTypedValue(true);\n } else if (!left || !right) {\n return [];\n }\n return booleanToTypedValue(false);\n }\n}\n\nexport class FunctionAtom implements Atom {\n constructor(\n public readonly name: string,\n public readonly args: Atom[]\n ) {}\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const impl = functions[this.name];\n if (!impl) {\n throw new Error('Unrecognized function: ' + this.name);\n }\n return impl(context, input, ...this.args);\n }\n\n toString(): string {\n return `${this.name}(${this.args.map((arg) => arg.toString()).join(', ')})`;\n }\n}\n\nexport class IndexerAtom implements Atom {\n constructor(\n public readonly left: Atom,\n public readonly expr: Atom\n ) {}\n eval(context: AtomContext, input: TypedValue[]): TypedValue[] {\n const evalResult = this.expr.eval(context, input);\n if (evalResult.length !== 1) {\n return [];\n }\n const index = evalResult[0].value;\n if (typeof index !== 'number') {\n throw new Error(`Invalid indexer expression: should return integer}`);\n }\n const leftResult = this.left.eval(context, input);\n if (!(index in leftResult)) {\n return [];\n }\n return [leftResult[index]];\n }\n\n toString(): string {\n return `${this.left.toString()}[${this.expr.toString()}]`;\n }\n}\n", "export interface Marker {\n index: number;\n line: number;\n column: number;\n}\n\nexport interface Token extends Marker {\n id: string;\n value: string;\n}\n\nconst STANDARD_UNITS = [\n 'year',\n 'years',\n 'month',\n 'months',\n 'week',\n 'weeks',\n 'day',\n 'days',\n 'hour',\n 'hours',\n 'minute',\n 'minutes',\n 'second',\n 'seconds',\n 'millisecond',\n 'milliseconds',\n];\n\nexport interface TokenizerOptions {\n dateTimeLiterals?: boolean;\n symbolRegex?: RegExp;\n}\n\nexport class Tokenizer {\n private readonly str: string;\n private readonly keywords: string[];\n private readonly operators: string[];\n private readonly dateTimeLiterals: boolean;\n private readonly symbolRegex: RegExp;\n private readonly result: Token[] = [];\n private readonly pos: Marker = { index: 0, line: 1, column: 0 };\n private readonly markStack: Marker[] = [];\n\n constructor(str: string, keywords: string[], operators: string[], options?: TokenizerOptions) {\n this.str = str;\n this.keywords = keywords;\n this.operators = operators;\n this.dateTimeLiterals = !!options?.dateTimeLiterals;\n this.symbolRegex = options?.symbolRegex ?? /[$\\w%]/;\n }\n\n tokenize(): Token[] {\n while (this.pos.index < this.str.length) {\n const token = this.consumeToken();\n if (token) {\n this.result.push(token);\n }\n }\n\n return this.result;\n }\n\n private prevToken(): Token | undefined {\n return this.result.slice(-1)[0];\n }\n\n private peekToken(): Token | undefined {\n this.mark();\n const token = this.consumeToken();\n this.reset();\n return token;\n }\n\n private consumeToken(): Token | undefined {\n this.consumeWhitespace();\n\n const c = this.curr();\n if (!c) {\n return undefined;\n }\n\n this.mark();\n\n const next = this.peek();\n\n if (c === '/' && next === '*') {\n return this.consumeMultiLineComment();\n }\n\n if (c === '/' && next === '/') {\n return this.consumeSingleLineComment();\n }\n\n if (c === \"'\" || c === '\"') {\n return this.consumeString(c);\n }\n\n if (c === '`') {\n return this.consumeBacktickSymbol();\n }\n\n if (c === '@') {\n return this.consumeDateTime();\n }\n\n if (/\\d/.exec(c)) {\n return this.consumeNumber();\n }\n\n if (/\\w/.exec(c)) {\n return this.consumeSymbol();\n }\n\n if ((c === '$' || c === '%') && /\\w/.exec(next)) {\n return this.consumeSymbol();\n }\n\n return this.consumeOperator();\n }\n\n private consumeWhitespace(): void {\n this.consumeWhile(() => /\\s/.exec(this.curr()));\n }\n\n private consumeMultiLineComment(): Token {\n const start = this.pos.index;\n this.consumeWhile(() => this.curr() !== '*' || this.peek() !== '/');\n this.advance();\n this.advance();\n return this.buildToken('Comment', this.str.substring(start, this.pos.index));\n }\n\n private consumeSingleLineComment(): Token {\n return this.buildToken(\n 'Comment',\n this.consumeWhile(() => this.curr() !== '\\n')\n );\n }\n\n private consumeString(endChar: string): Token {\n this.advance();\n const result = this.buildToken(\n 'String',\n this.consumeWhile(() => this.prev() === '\\\\' || this.curr() !== endChar)\n );\n this.advance();\n return result;\n }\n\n private consumeBacktickSymbol(): Token {\n this.advance();\n const result = this.buildToken(\n 'Symbol',\n this.consumeWhile(() => this.curr() !== '`')\n );\n this.advance();\n return result;\n }\n\n private consumeDateTime(): Token {\n this.advance(); // Consume \"@\"\n\n const start = this.pos.index;\n this.consumeWhile(() => /[\\d-]/.exec(this.curr()));\n\n let foundTime = false;\n let foundTimeZone = false;\n\n if (this.curr() === 'T') {\n foundTime = true;\n this.advance();\n this.consumeWhile(() => /[\\d:]/.exec(this.curr()));\n\n if (this.curr() === '.' && /\\d/.exec(this.peek())) {\n this.advance();\n this.consumeWhile(() => /\\d/.exec(this.curr()));\n }\n\n if (this.curr() === 'Z') {\n foundTimeZone = true;\n this.advance();\n } else if (this.curr() === '+' || this.curr() === '-') {\n foundTimeZone = true;\n this.advance();\n this.consumeWhile(() => /[\\d:]/.exec(this.curr()));\n }\n }\n\n if (this.pos.index === start) {\n throw new Error('Invalid DateTime literal');\n }\n\n let value = this.str.substring(start, this.pos.index);\n if (value.endsWith('T')) {\n // The date/time string ended with a \"T\", which is valid FHIRPath, but not valid ISO8601.\n // Strip the \"T\" and treat as a date.\n value = value.substring(0, value.length - 1);\n } else if (!value.startsWith('T') && foundTime && !foundTimeZone) {\n // FHIRPath spec says timezone is optional: https://build.fhir.org/ig/HL7/FHIRPath/#datetime\n // The FHIRPath test suite expects the timezone to be \"Z\" if not specified.\n // See: https://github.com/HL7/FHIRPath/blob/master/tests/r4/tests-fhir-r4.xml\n value += 'Z';\n }\n return this.buildToken('DateTime', value);\n }\n\n private consumeNumber(): Token {\n const start = this.pos.index;\n let id = 'Number';\n this.consumeWhile(() => /\\d/.exec(this.curr()));\n\n if (this.curr() === '.' && /\\d/.exec(this.peek())) {\n this.advance();\n this.consumeWhile(() => /\\d/.exec(this.curr()));\n }\n\n if (this.curr() === '-' && this.dateTimeLiterals) {\n // Rewind to one character before the start, and then treat as dateTime literal.\n this.pos.index = start - 1;\n return this.consumeDateTime();\n }\n\n if (this.curr() === ' ') {\n if (isUnitToken(this.peekToken())) {\n id = 'Quantity';\n this.consumeToken();\n }\n }\n\n return this.buildToken(id, this.str.substring(start, this.pos.index));\n }\n\n private consumeSymbol(): Token {\n const value = this.consumeWhile(() => this.symbolRegex.exec(this.curr()));\n if (this.prevToken()?.value !== '.' && this.keywords.includes(value)) {\n return this.buildToken(value, value);\n }\n return this.buildToken('Symbol', value);\n }\n\n private consumeOperator(): Token {\n const c = this.curr();\n const next = this.peek();\n const twoCharOp = c + next;\n\n if (this.operators.includes(twoCharOp)) {\n this.advance();\n this.advance();\n return this.buildToken(twoCharOp, twoCharOp);\n }\n\n this.advance();\n return this.buildToken(c, c);\n }\n\n private consumeWhile(condition: () => unknown): string {\n const start = this.pos.index;\n\n while (this.pos.index < this.str.length && condition()) {\n this.advance();\n }\n\n return this.str.substring(start, this.pos.index);\n }\n\n private curr(): string {\n return this.str[this.pos.index];\n }\n\n private prev(): string {\n return this.str[this.pos.index - 1] ?? '';\n }\n\n private peek(): string {\n return this.str[this.pos.index + 1] ?? '';\n }\n\n private mark(): void {\n this.markStack.push({ ...this.pos });\n }\n\n private reset(): void {\n const mark = this.markStack.pop();\n if (!mark) {\n throw new Error('No mark to reset to');\n }\n this.pos.index = mark.index;\n this.pos.line = mark.line;\n this.pos.column = mark.column;\n }\n\n private advance(): void {\n this.pos.index++;\n if (this.curr() === '\\n') {\n this.pos.line++;\n this.pos.column = 0;\n } else {\n this.pos.column++;\n }\n }\n\n private buildToken(id: string, value: string): Token {\n const mark = this.markStack.pop();\n if (!mark) {\n throw new Error('No mark for token');\n }\n return {\n id,\n value,\n ...mark,\n };\n }\n}\n\nfunction isUnitToken(token: Token | undefined): boolean {\n if (token) {\n if (token.id === 'String') {\n return true;\n }\n\n if (token.id === 'Symbol' && STANDARD_UNITS.includes(token.value)) {\n return true;\n }\n }\n\n return false;\n}\n", "import { Token, Tokenizer } from '../fhirlexer/tokenize';\n\nexport const FHIRPATH_KEYWORDS = ['true', 'false'];\nexport const FHIRPATH_OPERATORS = ['!=', '!~', '<=', '>=', '{}', '->'];\n\nexport function tokenize(str: string): Token[] {\n return new Tokenizer(str, FHIRPATH_KEYWORDS, FHIRPATH_OPERATORS).tokenize();\n}\n", "import { Quantity } from '@medplum/fhirtypes';\nimport { Atom, InfixParselet, Parser, ParserBuilder, PrefixParselet } from '../fhirlexer/parse';\nimport { PropertyType, TypedValue } from '../types';\nimport {\n AndAtom,\n ArithemticOperatorAtom,\n AsAtom,\n ConcatAtom,\n ContainsAtom,\n DotAtom,\n EmptySetAtom,\n EqualsAtom,\n EquivalentAtom,\n FhirPathAtom,\n FunctionAtom,\n ImpliesAtom,\n InAtom,\n IndexerAtom,\n IsAtom,\n LiteralAtom,\n NotEqualsAtom,\n NotEquivalentAtom,\n OrAtom,\n SymbolAtom,\n UnaryOperatorAtom,\n UnionAtom,\n XorAtom,\n} from './atoms';\nimport { parseDateString } from './date';\nimport { tokenize } from './tokenize';\nimport { toTypedValue } from './utils';\n\n/**\n * Operator precedence\n * See: https://hl7.org/fhirpath/#operator-precedence\n */\nexport const OperatorPrecedence = {\n FunctionCall: 0,\n Dot: 1,\n Indexer: 2,\n UnaryAdd: 3,\n UnarySubtract: 3,\n Multiply: 4,\n Divide: 4,\n IntegerDivide: 4,\n Modulo: 4,\n Add: 5,\n Subtract: 5,\n Ampersand: 5,\n Is: 6,\n As: 6,\n Union: 7,\n GreaterThan: 8,\n GreaterThanOrEquals: 8,\n LessThan: 8,\n LessThanOrEquals: 8,\n Equals: 9,\n Equivalent: 9,\n NotEquals: 9,\n NotEquivalent: 9,\n In: 10,\n Contains: 10,\n And: 11,\n Xor: 12,\n Or: 12,\n Implies: 13,\n Arrow: 100,\n Semicolon: 200,\n};\n\nconst PARENTHESES_PARSELET: PrefixParselet = {\n parse(parser: Parser) {\n const expr = parser.consumeAndParse();\n if (!parser.match(')')) {\n throw new Error('Parse error: expected `)` got `' + parser.peek()?.value + '`');\n }\n return expr;\n },\n};\n\nconst INDEXER_PARSELET: InfixParselet = {\n parse(parser: Parser, left: Atom) {\n const expr = parser.consumeAndParse();\n if (!parser.match(']')) {\n throw new Error('Parse error: expected `]`');\n }\n return new IndexerAtom(left, expr);\n },\n\n precedence: OperatorPrecedence.Indexer,\n};\n\nconst FUNCTION_CALL_PARSELET: InfixParselet = {\n parse(parser: Parser, left: Atom) {\n if (!(left instanceof SymbolAtom)) {\n throw new Error('Unexpected parentheses');\n }\n\n const args = [];\n while (!parser.match(')')) {\n args.push(parser.consumeAndParse());\n parser.match(',');\n }\n\n return new FunctionAtom(left.name, args); //, functions[left.name]);\n },\n precedence: OperatorPrecedence.FunctionCall,\n};\n\nfunction parseQuantity(str: string): Quantity {\n const parts = str.split(' ');\n const value = parseFloat(parts[0]);\n let unit = parts[1];\n if (unit?.startsWith(\"'\") && unit.endsWith(\"'\")) {\n unit = unit.substring(1, unit.length - 1);\n } else {\n unit = '{' + unit + '}';\n }\n return { value, unit };\n}\n\nexport function initFhirPathParserBuilder(): ParserBuilder {\n return new ParserBuilder()\n .registerPrefix('String', {\n parse: (_, token) => new LiteralAtom({ type: PropertyType.string, value: token.value }),\n })\n .registerPrefix('DateTime', {\n parse: (_, token) => new LiteralAtom({ type: PropertyType.dateTime, value: parseDateString(token.value) }),\n })\n .registerPrefix('Quantity', {\n parse: (_, token) => new LiteralAtom({ type: PropertyType.Quantity, value: parseQuantity(token.value) }),\n })\n .registerPrefix('Number', {\n parse: (_, token) =>\n new LiteralAtom({\n type: token.value.includes('.') ? PropertyType.decimal : PropertyType.integer,\n value: parseFloat(token.value),\n }),\n })\n .registerPrefix('true', { parse: () => new LiteralAtom({ type: PropertyType.boolean, value: true }) })\n .registerPrefix('false', { parse: () => new LiteralAtom({ type: PropertyType.boolean, value: false }) })\n .registerPrefix('Symbol', { parse: (_, token) => new SymbolAtom(token.value) })\n .registerPrefix('{}', { parse: () => new EmptySetAtom() })\n .registerPrefix('(', PARENTHESES_PARSELET)\n .registerInfix('[', INDEXER_PARSELET)\n .registerInfix('(', FUNCTION_CALL_PARSELET)\n .prefix('+', OperatorPrecedence.UnaryAdd, (_, right) => new UnaryOperatorAtom('+', right, (x) => x))\n .prefix(\n '-',\n OperatorPrecedence.UnarySubtract,\n (_, right) => new ArithemticOperatorAtom('-', right, right, (_, y) => -y)\n )\n .infixLeft('.', OperatorPrecedence.Dot, (left, _, right) => new DotAtom(left, right))\n .infixLeft(\n '/',\n OperatorPrecedence.Divide,\n (left, _, right) => new ArithemticOperatorAtom('/', left, right, (x, y) => x / y)\n )\n .infixLeft(\n '*',\n OperatorPrecedence.Multiply,\n (left, _, right) => new ArithemticOperatorAtom('*', left, right, (x, y) => x * y)\n )\n .infixLeft(\n '+',\n OperatorPrecedence.Add,\n (left, _, right) => new ArithemticOperatorAtom('+', left, right, (x, y) => x + y)\n )\n .infixLeft(\n '-',\n OperatorPrecedence.Subtract,\n (left, _, right) => new ArithemticOperatorAtom('-', left, right, (x, y) => x - y)\n )\n .infixLeft('|', OperatorPrecedence.Union, (left, _, right) => new UnionAtom(left, right))\n .infixLeft('=', OperatorPrecedence.Equals, (left, _, right) => new EqualsAtom(left, right))\n .infixLeft('!=', OperatorPrecedence.NotEquals, (left, _, right) => new NotEqualsAtom(left, right))\n .infixLeft('~', OperatorPrecedence.Equivalent, (left, _, right) => new EquivalentAtom(left, right))\n .infixLeft('!~', OperatorPrecedence.NotEquivalent, (left, _, right) => new NotEquivalentAtom(left, right))\n .infixLeft(\n '<',\n OperatorPrecedence.LessThan,\n (left, _, right) => new ArithemticOperatorAtom('<', left, right, (x, y) => x < y)\n )\n .infixLeft(\n '<=',\n OperatorPrecedence.LessThanOrEquals,\n (left, _, right) => new ArithemticOperatorAtom('<=', left, right, (x, y) => x <= y)\n )\n .infixLeft(\n '>',\n OperatorPrecedence.GreaterThan,\n (left, _, right) => new ArithemticOperatorAtom('>', left, right, (x, y) => x > y)\n )\n .infixLeft(\n '>=',\n OperatorPrecedence.GreaterThanOrEquals,\n (left, _, right) => new ArithemticOperatorAtom('>=', left, right, (x, y) => x >= y)\n )\n .infixLeft('&', OperatorPrecedence.Ampersand, (left, _, right) => new ConcatAtom(left, right))\n .infixLeft('and', OperatorPrecedence.And, (left, _, right) => new AndAtom(left, right))\n .infixLeft('as', OperatorPrecedence.As, (left, _, right) => new AsAtom(left, right))\n .infixLeft('contains', OperatorPrecedence.Contains, (left, _, right) => new ContainsAtom(left, right))\n .infixLeft(\n 'div',\n OperatorPrecedence.Divide,\n (left, _, right) => new ArithemticOperatorAtom('div', left, right, (x, y) => (x / y) | 0)\n )\n .infixLeft('in', OperatorPrecedence.In, (left, _, right) => new InAtom(left, right))\n .infixLeft('is', OperatorPrecedence.Is, (left, _, right) => new IsAtom(left, right))\n .infixLeft(\n 'mod',\n OperatorPrecedence.Modulo,\n (left, _, right) => new ArithemticOperatorAtom('mod', left, right, (x, y) => x % y)\n )\n .infixLeft('or', OperatorPrecedence.Or, (left, _, right) => new OrAtom(left, right))\n .infixLeft('xor', OperatorPrecedence.Xor, (left, _, right) => new XorAtom(left, right))\n .infixLeft('implies', OperatorPrecedence.Implies, (left, _, right) => new ImpliesAtom(left, right));\n}\n\nconst fhirPathParserBuilder = initFhirPathParserBuilder();\n\n/**\n * Parses a FHIRPath expression into an AST.\n * The result can be used to evaluate the expression against a resource or other object.\n * This method is useful if you know that you will evaluate the same expression many times\n * against different resources.\n * @param input - The FHIRPath expression to parse.\n * @returns The AST representing the expression.\n */\nexport function parseFhirPath(input: string): FhirPathAtom {\n return new FhirPathAtom(input, fhirPathParserBuilder.construct(tokenize(input)).consumeAndParse());\n}\n\n/**\n * Evaluates a FHIRPath expression against a resource or other object.\n * @param expression - The FHIRPath expression to parse.\n * @param input - The resource or object to evaluate the expression against.\n * @returns The result of the FHIRPath expression against the resource or object.\n */\nexport function evalFhirPath(expression: string, input: unknown): unknown[] {\n // eval requires a TypedValue array\n // As a convenience, we can accept array or non-array, and TypedValue or unknown value\n const array = Array.isArray(input) ? input : [input];\n for (let i = 0; i < array.length; i++) {\n const el = array[i];\n if (!(typeof el === 'object' && 'type' in el && 'value' in el)) {\n array[i] = toTypedValue(array[i]);\n }\n }\n return evalFhirPathTyped(expression, array).map((e) => e.value);\n}\n\n/**\n * Evaluates a FHIRPath expression against a resource or other object.\n * @param expression - The FHIRPath expression to parse.\n * @param input - The resource or object to evaluate the expression against.\n * @param variables - A map of variables for eval input.\n * @returns The result of the FHIRPath expression against the resource or object.\n */\nexport function evalFhirPathTyped(\n expression: string,\n input: TypedValue[],\n variables: Record<string, TypedValue> = {}\n): TypedValue[] {\n const ast = parseFhirPath(expression);\n return ast.eval({ variables }, input).map((v) => ({\n type: v.type,\n value: v.value?.valueOf(),\n }));\n}\n", "import { ElementDefinitionType, SearchParameter } from '@medplum/fhirtypes';\nimport { Atom } from '../fhirlexer/parse';\nimport {\n AsAtom,\n BooleanInfixOperatorAtom,\n DotAtom,\n FunctionAtom,\n IndexerAtom,\n IsAtom,\n UnionAtom,\n} from '../fhirpath/atoms';\nimport { parseFhirPath } from '../fhirpath/parse';\nimport { PropertyType, getElementDefinition, globalSchema } from '../types';\nimport { InternalSchemaElement } from '../typeschema/types';\nimport { lazy } from '../utils';\n\nexport enum SearchParameterType {\n BOOLEAN = 'BOOLEAN',\n NUMBER = 'NUMBER',\n QUANTITY = 'QUANTITY',\n TEXT = 'TEXT',\n REFERENCE = 'REFERENCE',\n CANONICAL = 'CANONICAL',\n DATE = 'DATE',\n DATETIME = 'DATETIME',\n PERIOD = 'PERIOD',\n UUID = 'UUID',\n}\n\nexport interface SearchParameterDetails {\n readonly type: SearchParameterType;\n readonly elementDefinitions?: InternalSchemaElement[];\n readonly array?: boolean;\n}\n\ninterface SearchParameterDetailsBuilder {\n elementDefinitions: InternalSchemaElement[];\n propertyTypes: Set<string>;\n array: boolean;\n}\n\n/**\n * Returns the type details of a SearchParameter.\n *\n * The SearchParameter resource has a \"type\" parameter, but that is missing some critical information.\n *\n * For example:\n * 1) The \"date\" type includes \"date\", \"datetime\", and \"period\".\n * 2) The \"token\" type includes enums and booleans.\n * 3) Arrays/multiple values are not reflected at all.\n * @param resourceType - The root resource type.\n * @param searchParam - The search parameter.\n * @returns The search parameter type details.\n */\nexport function getSearchParameterDetails(resourceType: string, searchParam: SearchParameter): SearchParameterDetails {\n let result: SearchParameterDetails | undefined =\n globalSchema.types[resourceType]?.searchParamsDetails?.[searchParam.code as string];\n if (!result) {\n result = buildSearchParameterDetails(resourceType, searchParam);\n }\n return result;\n}\n\nfunction setSearchParameterDetails(resourceType: string, code: string, details: SearchParameterDetails): void {\n let typeSchema = globalSchema.types[resourceType];\n if (!typeSchema) {\n typeSchema = {};\n globalSchema.types[resourceType] = typeSchema;\n }\n if (!typeSchema.searchParamsDetails) {\n typeSchema.searchParamsDetails = {};\n }\n typeSchema.searchParamsDetails[code] = details;\n}\n\nfunction buildSearchParameterDetails(resourceType: string, searchParam: SearchParameter): SearchParameterDetails {\n const code = searchParam.code as string;\n const expressions = getExpressionsForResourceType(resourceType, searchParam.expression as string);\n\n const builder: SearchParameterDetailsBuilder = {\n elementDefinitions: [],\n propertyTypes: new Set(),\n array: false,\n };\n\n for (const expression of expressions) {\n const atomArray = flattenAtom(expression);\n const flattenedExpression = lazy(() => atomArray.join('.'));\n\n if (atomArray.length === 1 && atomArray[0] instanceof BooleanInfixOperatorAtom) {\n builder.propertyTypes.add('boolean');\n } else if (searchParam.code.endsWith(':identifier')) {\n // This is a derived \"identifier\" search parameter\n // See `deriveIdentifierSearchParameter`\n builder.propertyTypes.add('Identifier');\n } else if (\n // To support US Core Patient search parameters without needing profile-aware logic,\n // assume expressions for `Extension.value[x].code` and `Extension.value[x].coding.code`\n // are of type `code`. Otherwise, crawling the Extension.value[x] element definition without\n // access to the type narrowing specified in the profiles would be inconclusive.\n flattenedExpression().endsWith('extension.value.code') ||\n flattenedExpression().endsWith('extension.value.coding.code')\n ) {\n builder.array = true;\n builder.propertyTypes.add('code');\n } else {\n crawlSearchParameterDetails(builder, atomArray, resourceType, 1);\n }\n\n // To support US Core \"us-core-condition-asserted-date\" search parameter without\n // needing profile-aware logic, ensure extensions with a dateTime value are not\n // treated as arrays since Mepdlum search functionality does not yet support datetime arrays.\n // This would be the result if the http://hl7.org/fhir/StructureDefinition/condition-assertedDate\n // extension were parsed since it specifies a cardinality of 0..1.\n if (flattenedExpression().endsWith('extension.valueDateTime')) {\n builder.array = false;\n }\n }\n\n const result: SearchParameterDetails = {\n type: getSearchParameterType(searchParam, builder.propertyTypes),\n elementDefinitions: builder.elementDefinitions,\n array: builder.array,\n };\n setSearchParameterDetails(resourceType, code, result);\n return result;\n}\n\nfunction crawlSearchParameterDetails(\n details: SearchParameterDetailsBuilder,\n atoms: Atom[],\n baseType: string,\n index: number\n): void {\n const currAtom = atoms[index];\n\n if (currAtom instanceof AsAtom) {\n details.propertyTypes.add(currAtom.right.toString());\n return;\n }\n\n if (currAtom instanceof FunctionAtom) {\n handleFunctionAtom(details, currAtom);\n return;\n }\n\n const propertyName = currAtom.toString();\n const elementDefinition = getElementDefinition(baseType, propertyName);\n if (!elementDefinition) {\n throw new Error(`Element definition not found for ${baseType} ${propertyName}`);\n }\n\n let hasArrayIndex = false;\n let nextIndex = index + 1;\n if (nextIndex < atoms.length && atoms[nextIndex] instanceof IndexerAtom) {\n hasArrayIndex = true;\n nextIndex++;\n }\n\n if (elementDefinition.isArray && !hasArrayIndex) {\n details.array = true;\n }\n\n if (nextIndex >= atoms.length) {\n // This is the final atom in the expression\n // So we can collect the ElementDefinition and property types\n details.elementDefinitions.push(elementDefinition);\n for (const elementDefinitionType of elementDefinition.type as ElementDefinitionType[]) {\n details.propertyTypes.add(elementDefinitionType.code as string);\n }\n return;\n }\n\n // This is in the middle of the expression, so we need to keep crawling.\n // \"code\" is only missing when using \"contentReference\"\n // \"contentReference\" is handled whe parsing StructureDefinition into InternalTypeSchema\n for (const elementDefinitionType of elementDefinition.type as ElementDefinitionType[]) {\n let propertyType = elementDefinitionType.code as string;\n if (isBackboneElement(propertyType)) {\n propertyType = elementDefinition.type[0].code;\n }\n crawlSearchParameterDetails(details, atoms, propertyType, nextIndex);\n }\n}\n\nfunction handleFunctionAtom(builder: SearchParameterDetailsBuilder, functionAtom: FunctionAtom): void {\n if (functionAtom.name === 'as') {\n builder.propertyTypes.add(functionAtom.args[0].toString());\n return;\n }\n\n if (functionAtom.name === 'ofType') {\n builder.propertyTypes.add(functionAtom.args[0].toString());\n return;\n }\n\n if (functionAtom.name === 'resolve') {\n // Handle .resolve().resourceType\n builder.propertyTypes.add('string');\n return;\n }\n\n if (functionAtom.name === 'where' && functionAtom.args[0] instanceof IsAtom) {\n // Common pattern: \"where(resolve() is Patient)\"\n // Use the type information\n builder.propertyTypes.add(functionAtom.args[0].right.toString());\n return;\n }\n\n throw new Error(`Unhandled FHIRPath function: ${functionAtom.name}`);\n}\n\nfunction isBackboneElement(propertyType: string): boolean {\n return propertyType === 'Element' || propertyType === 'BackboneElement';\n}\n\nfunction getSearchParameterType(searchParam: SearchParameter, propertyTypes: Set<string>): SearchParameterType {\n switch (searchParam.type) {\n case 'date':\n if (propertyTypes.size === 1 && propertyTypes.has(PropertyType.date)) {\n return SearchParameterType.DATE;\n } else {\n return SearchParameterType.DATETIME;\n }\n case 'number':\n return SearchParameterType.NUMBER;\n case 'quantity':\n return SearchParameterType.QUANTITY;\n case 'reference':\n if (propertyTypes.has(PropertyType.canonical)) {\n return SearchParameterType.CANONICAL;\n } else {\n return SearchParameterType.REFERENCE;\n }\n case 'token':\n if (propertyTypes.size === 1 && propertyTypes.has(PropertyType.boolean)) {\n return SearchParameterType.BOOLEAN;\n } else {\n return SearchParameterType.TEXT;\n }\n default:\n return SearchParameterType.TEXT;\n }\n}\n\nexport function getExpressionsForResourceType(resourceType: string, expression: string): Atom[] {\n const result: Atom[] = [];\n const fhirPathExpression = parseFhirPath(expression);\n buildExpressionsForResourceType(resourceType, fhirPathExpression.child, result);\n return result;\n}\n\nexport function getExpressionForResourceType(resourceType: string, expression: string): string | undefined {\n const atoms = getExpressionsForResourceType(resourceType, expression);\n if (atoms.length === 0) {\n return undefined;\n }\n return atoms.map((atom) => atom.toString()).join(' | ');\n}\n\nfunction buildExpressionsForResourceType(resourceType: string, atom: Atom, result: Atom[]): void {\n if (atom instanceof UnionAtom) {\n buildExpressionsForResourceType(resourceType, atom.left, result);\n buildExpressionsForResourceType(resourceType, atom.right, result);\n } else {\n const str = atom.toString();\n if (str.startsWith(resourceType + '.')) {\n result.push(atom);\n }\n }\n}\n\nfunction flattenAtom(atom: Atom): Atom[] {\n if (atom instanceof AsAtom || atom instanceof IndexerAtom) {\n return [flattenAtom(atom.left), atom].flat();\n }\n if (atom instanceof BooleanInfixOperatorAtom) {\n return [atom];\n }\n if (atom instanceof DotAtom) {\n return [flattenAtom(atom.left), flattenAtom(atom.right)].flat();\n }\n if (atom instanceof FunctionAtom) {\n if (atom.name === 'where' && !(atom.args[0] instanceof IsAtom)) {\n // Remove all \"where\" functions other than \"where(x as type)\"\n return [];\n }\n if (atom.name === 'last') {\n // Remove all \"last\" functions\n return [];\n }\n }\n return [atom];\n}\n", "import { Resource, ResourceType, SearchParameter } from '@medplum/fhirtypes';\nimport { evalFhirPathTyped } from '../fhirpath/parse';\nimport { OperationOutcomeError, badRequest } from '../outcomes';\nimport { TypedValue, globalSchema, stringifyTypedValue } from '../types';\nimport { append, sortStringArray } from '../utils';\nimport { isDateTimeString } from '../fhirpath/utils';\n\nexport const DEFAULT_SEARCH_COUNT = 20;\nexport const DEFAULT_MAX_SEARCH_COUNT = 1000;\n\nexport interface SearchRequest<T extends Resource = Resource> {\n readonly resourceType: T['resourceType'];\n filters?: Filter[];\n sortRules?: SortRule[];\n cursor?: string;\n offset?: number;\n count?: number;\n fields?: string[];\n name?: string;\n total?: 'none' | 'estimate' | 'accurate';\n include?: IncludeTarget[];\n revInclude?: IncludeTarget[];\n summary?: 'true' | 'text' | 'data';\n format?: string;\n pretty?: boolean;\n types?: T['resourceType'][];\n}\n\nexport interface Filter {\n code: string;\n operator: Operator;\n value: string;\n}\n\nexport interface SortRule {\n code: string;\n descending?: boolean;\n}\n\nexport interface IncludeTarget {\n resourceType: string;\n searchParam: string;\n targetType?: string;\n modifier?: 'iterate';\n}\n\n/**\n * Search operators.\n * These operators represent \"modifiers\" and \"prefixes\" in FHIR search.\n * See: https://www.hl7.org/fhir/search.html\n */\nexport enum Operator {\n EQUALS = 'eq',\n NOT_EQUALS = 'ne',\n\n // Numbers\n GREATER_THAN = 'gt',\n LESS_THAN = 'lt',\n GREATER_THAN_OR_EQUALS = 'ge',\n LESS_THAN_OR_EQUALS = 'le',\n\n // Dates\n STARTS_AFTER = 'sa',\n ENDS_BEFORE = 'eb',\n APPROXIMATELY = 'ap',\n\n // String\n CONTAINS = 'contains',\n STARTS_WITH = 'sw',\n EXACT = 'exact',\n\n // Token\n TEXT = 'text',\n NOT = 'not',\n ABOVE = 'above',\n BELOW = 'below',\n IN = 'in',\n NOT_IN = 'not-in',\n OF_TYPE = 'of-type',\n\n // All\n MISSING = 'missing',\n PRESENT = 'present',\n\n // Reference\n IDENTIFIER = 'identifier',\n\n // _include and _revinclude\n ITERATE = 'iterate',\n}\n\n/**\n * Parameter names may specify a modifier as a suffix.\n * The modifiers are separated from the parameter name by a colon.\n * See: https://www.hl7.org/fhir/search.html#modifiers\n */\nconst MODIFIER_OPERATORS: Record<string, Operator> = {\n contains: Operator.CONTAINS,\n exact: Operator.EXACT,\n above: Operator.ABOVE,\n below: Operator.BELOW,\n text: Operator.TEXT,\n not: Operator.NOT,\n in: Operator.IN,\n 'not-in': Operator.NOT_IN,\n 'of-type': Operator.OF_TYPE,\n missing: Operator.MISSING,\n identifier: Operator.IDENTIFIER,\n iterate: Operator.ITERATE,\n};\n\n/**\n * For the ordered parameter types of number, date, and quantity,\n * a prefix to the parameter value may be used to control the nature\n * of the matching.\n * See: https://www.hl7.org/fhir/search.html#prefix\n */\nconst PREFIX_OPERATORS: Record<string, Operator> = {\n eq: Operator.EQUALS,\n ne: Operator.NOT_EQUALS,\n lt: Operator.LESS_THAN,\n le: Operator.LESS_THAN_OR_EQUALS,\n gt: Operator.GREATER_THAN,\n ge: Operator.GREATER_THAN_OR_EQUALS,\n sa: Operator.STARTS_AFTER,\n eb: Operator.ENDS_BEFORE,\n ap: Operator.APPROXIMATELY,\n sw: Operator.STARTS_WITH,\n};\n\n/**\n * Parses a search URL into a search request.\n * @param url - The original search URL or the FHIR resource type.\n * @param query - Optional collection of additional query string parameters.\n * @returns A parsed SearchRequest.\n */\nexport function parseSearchRequest<T extends Resource = Resource>(\n url: T['resourceType'] | URL | string,\n query?: Record<string, string[] | string | undefined>\n): SearchRequest<T> {\n if (!url) {\n throw new Error('Invalid search URL');\n }\n\n // Parse the input into path and search parameters\n let pathname = '';\n let searchParams: URLSearchParams | undefined = undefined;\n if (typeof url === 'string') {\n if (url.includes('?')) {\n const [path, search] = url.split('?');\n pathname = path;\n searchParams = new URLSearchParams(search);\n } else {\n pathname = url;\n }\n } else if (typeof url === 'object') {\n pathname = url.pathname;\n searchParams = url.searchParams;\n }\n\n // Next, parse out the resource type from the URL\n // By convention, the resource type is the last non-empty part of the path\n let resourceType: ResourceType;\n if (pathname.includes('/')) {\n resourceType = pathname.split('/').filter(Boolean).pop() as ResourceType;\n } else {\n resourceType = pathname as ResourceType;\n }\n\n // Next, parse out the search parameters\n // First, we convert the URLSearchParams to an array of key-value pairs\n const queryArray: [string, string][] = [];\n if (searchParams) {\n queryArray.push(...searchParams.entries());\n }\n\n // Next, we merge in the query object\n // This is an optional set of additional query parameters\n // which should be added to the URL\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n for (const v of value) {\n queryArray.push([key, v]);\n }\n } else {\n queryArray.push([key, value ?? '']);\n }\n }\n }\n\n // Finally we can move on to the actual parsing\n return parseSearchImpl(resourceType, queryArray);\n}\n\n/**\n * Parses a search URL into a search request.\n * @param url - The search URL.\n * @returns A parsed SearchRequest.\n * @deprecated Use parseSearchRequest instead.\n */\nexport function parseSearchUrl<T extends Resource = Resource>(url: URL): SearchRequest<T> {\n return parseSearchRequest<T>(url);\n}\n\n/**\n * Parses a URL string into a SearchRequest.\n * @param url - The URL to parse.\n * @returns Parsed search definition.\n * @deprecated Use parseSearchRequest instead.\n */\nexport function parseSearchDefinition<T extends Resource = Resource>(url: string): SearchRequest<T> {\n return parseSearchRequest<T>(url);\n}\n\n/**\n * Parses a FHIR criteria string into a SearchRequest.\n * FHIR criteria strings are found on resources such as Subscription.\n * @param criteria - The FHIR criteria string.\n * @returns Parsed search definition.\n * @deprecated Use parseSearchRequest instead.\n */\nexport function parseCriteriaAsSearchRequest<T extends Resource = Resource>(criteria: string): SearchRequest<T> {\n return parseSearchRequest<T>(criteria);\n}\n\nfunction parseSearchImpl<T extends Resource = Resource>(\n resourceType: T['resourceType'],\n query: Iterable<[string, string]>\n): SearchRequest<T> {\n const searchRequest: SearchRequest<T> = {\n resourceType,\n };\n\n for (const [key, value] of query) {\n parseKeyValue(searchRequest, key, value);\n }\n\n return searchRequest;\n}\n\nfunction parseKeyValue(searchRequest: SearchRequest, key: string, value: string): void {\n let code: string;\n let modifier: string;\n\n const colonIndex = key.indexOf(':');\n if (colonIndex >= 0) {\n code = key.substring(0, colonIndex);\n modifier = key.substring(colonIndex + 1);\n } else {\n code = key;\n modifier = '';\n }\n\n // Ignore the '_' parameter\n // This is added by React Native when `no-cache` strategy is used to bust the cache presumably\n if (code === '_') {\n return;\n }\n\n if (code === '_has' || key.includes('.')) {\n searchRequest.filters = append(searchRequest.filters, { code: key, operator: Operator.EQUALS, value });\n return;\n }\n\n switch (code) {\n case '_sort':\n parseSortRule(searchRequest, value);\n break;\n\n case '_cursor':\n searchRequest.cursor = value;\n break;\n\n case '_count':\n searchRequest.count = parseInt(value, 10);\n break;\n\n case '_offset':\n searchRequest.offset = parseInt(value, 10);\n break;\n\n case '_total':\n searchRequest.total = value as 'none' | 'estimate' | 'accurate';\n break;\n\n case '_summary':\n if (value === 'count') {\n searchRequest.total = 'accurate';\n searchRequest.count = 0;\n } else if (value === 'true' || value === 'data' || value === 'text') {\n searchRequest.summary = value;\n }\n break;\n\n case '_include': {\n const target = parseIncludeTarget(value);\n if (modifier === 'iterate') {\n target.modifier = Operator.ITERATE;\n }\n searchRequest.include = append(searchRequest.include, target);\n break;\n }\n\n case '_revinclude': {\n const target = parseIncludeTarget(value);\n if (modifier === 'iterate') {\n target.modifier = Operator.ITERATE;\n }\n searchRequest.revInclude = append(searchRequest.revInclude, target);\n break;\n }\n\n case '_fields':\n case '_elements':\n searchRequest.fields = value.split(',');\n break;\n\n case '_type':\n searchRequest.types = value.split(',') as Resource['resourceType'][];\n break;\n\n case '_format':\n searchRequest.format = value;\n break;\n\n case '_pretty':\n searchRequest.pretty = value === 'true';\n break;\n\n default: {\n const param = globalSchema.types[searchRequest.resourceType]?.searchParams?.[code];\n if (param) {\n searchRequest.filters = append(searchRequest.filters, parseParameter(param, modifier, value));\n } else {\n searchRequest.filters = append(searchRequest.filters, parseUnknownParameter(code, modifier, value));\n }\n }\n }\n}\n\nfunction parseSortRule(searchRequest: SearchRequest, value: string): void {\n for (const field of value.split(',')) {\n let code: string;\n let descending = false;\n if (field.startsWith('-')) {\n code = field.substring(1);\n descending = true;\n } else {\n code = field;\n }\n if (!searchRequest.sortRules) {\n searchRequest.sortRules = [];\n }\n searchRequest.sortRules.push({ code, descending });\n }\n}\n\nconst presenceOperators: Operator[] = [Operator.MISSING, Operator.PRESENT];\nexport function parseParameter(searchParam: SearchParameter, modifier: string, value: string): Filter {\n if (presenceOperators.includes(modifier as Operator)) {\n return {\n code: searchParam.code,\n operator: modifier as Operator,\n value,\n };\n }\n\n switch (searchParam.type) {\n // Ordered types that can have a prefix modifier on the value\n case 'number':\n case 'date':\n case 'quantity': {\n const { operator, value: searchValue } = parsePrefix(value);\n if (!isValidSearchValue(searchParam, searchValue)) {\n throw new OperationOutcomeError(\n badRequest(`Invalid format for ${searchParam.type} search parameter: ${searchValue}`)\n );\n }\n return { code: searchParam.code, operator, value: searchValue };\n }\n\n // Lookup types that support a variety of modifiers on the search parameter\n case 'reference':\n case 'string':\n case 'token':\n case 'uri':\n if (!isValidSearchValue(searchParam, value)) {\n throw new OperationOutcomeError(\n badRequest(`Invalid format for ${searchParam.type} search parameter: ${value}`)\n );\n }\n return { code: searchParam.code, operator: parseModifier(modifier), value };\n\n default:\n throw new Error('Unrecognized search parameter type: ' + searchParam.type);\n }\n}\n\nfunction parseUnknownParameter(code: string, modifier: string, value: string): Filter {\n let operator = Operator.EQUALS;\n if (modifier) {\n operator = modifier as Operator;\n } else if (value.length >= 2) {\n const prefix = value.substring(0, 2);\n if (prefix in PREFIX_OPERATORS) {\n if (value.length === 2 || value.at(2)?.match(/\\d/)) {\n operator = prefix as Operator;\n value = value.substring(prefix.length);\n }\n }\n }\n return { code, operator, value };\n}\n\nfunction parsePrefix(input: string): { operator: Operator; value: string } {\n const prefix = input.substring(0, 2);\n const prefixOperator = PREFIX_OPERATORS[prefix];\n if (prefixOperator) {\n return { operator: prefixOperator, value: input.substring(2) };\n }\n return { operator: Operator.EQUALS, value: input };\n}\n\nfunction parseModifier(modifier: string): Operator {\n return MODIFIER_OPERATORS[modifier] ?? Operator.EQUALS;\n}\n\nfunction parseIncludeTarget(input: string): IncludeTarget {\n const parts = input.split(':');\n\n if (parts.includes('*')) {\n throw new OperationOutcomeError(badRequest(`'*' is not supported as a value for search inclusion parameters`));\n }\n\n if (parts.length === 1) {\n // Full wildcard, not currently supported\n throw new OperationOutcomeError(\n badRequest(`Invalid include value '${input}': must be of the form ResourceType:search-parameter`)\n );\n } else if (parts.length === 2) {\n return {\n resourceType: parts[0],\n searchParam: parts[1],\n };\n } else if (parts.length === 3) {\n return {\n resourceType: parts[0],\n searchParam: parts[1],\n targetType: parts[2],\n };\n } else {\n throw new OperationOutcomeError(badRequest(`Invalid include value '${input}'`));\n }\n}\n\nfunction isValidSearchValue(searchParam: SearchParameter, searchValue: string): boolean {\n switch (searchParam.type) {\n case 'date':\n return isDateTimeString(searchValue);\n default:\n return true;\n }\n}\n\nconst subexpressionPattern = /{{([^{}]+)}}/g;\n\n/**\n * Parses an extended FHIR search criteria string (i.e. application/x-fhir-query).\n *\n * @example Evaluating a FHIRPath subexpression\n *\n * ```typescript\n * const query = 'Patient?name={{ %patient.name }}';\n * const variables = { patient: { name: 'John Doe' } };\n * const request = parseXFhirQuery(query, variables);\n * console.log(request.filters[0].value); // \"John Doe\"\n * ```\n *\n * @see https://hl7.org/fhir/fhir-xquery.html\n * @param query - The X-Fhir-Query string to parse\n * @param variables - Values to pass into embedded FHIRPath expressions\n * @returns The parsed search request\n */\nexport function parseXFhirQuery(query: string, variables: Record<string, TypedValue>): SearchRequest {\n query = query.replaceAll(subexpressionPattern, (_, expr) => {\n const replacement = evalFhirPathTyped(expr, [], variables);\n if (replacement.length !== 1) {\n return '';\n }\n return stringifyTypedValue(replacement[0]);\n });\n return parseSearchRequest(query);\n}\n\n/**\n * Formats a search definition object into a query string.\n * Note: The return value does not include the resource type.\n * @param definition - The search definition.\n * @returns Formatted URL.\n */\nexport function formatSearchQuery(definition: SearchRequest): string {\n const params: string[] = [];\n\n if (definition.fields) {\n params.push('_fields=' + definition.fields.join(','));\n }\n\n if (definition.filters) {\n definition.filters.forEach((filter) => params.push(formatFilter(filter)));\n }\n\n if (definition.sortRules && definition.sortRules.length > 0) {\n params.push(formatSortRules(definition.sortRules));\n }\n\n if (definition.cursor !== undefined) {\n params.push('_cursor=' + encodeURIComponent(definition.cursor));\n }\n\n if (definition.offset !== undefined && definition.offset !== 0) {\n params.push('_offset=' + definition.offset);\n }\n\n if (definition.count !== undefined) {\n params.push('_count=' + definition.count);\n }\n\n if (definition.total !== undefined) {\n params.push('_total=' + definition.total);\n }\n\n if (definition.include) {\n definition.include.forEach((target) => params.push(formatIncludeTarget('_include', target)));\n }\n\n if (definition.revInclude) {\n definition.revInclude.forEach((target) => params.push(formatIncludeTarget('_revinclude', target)));\n }\n\n if (params.length === 0) {\n return '';\n }\n\n sortStringArray(params);\n return '?' + params.join('&');\n}\n\nfunction formatFilter(filter: Filter): string {\n const modifier = filter.operator in MODIFIER_OPERATORS ? ':' + filter.operator : '';\n const prefix = filter.operator !== Operator.EQUALS && filter.operator in PREFIX_OPERATORS ? filter.operator : '';\n return `${filter.code}${modifier}=${prefix}${encodeURIComponent(filter.value)}`;\n}\n\nfunction formatSortRules(sortRules: SortRule[]): string {\n return '_sort=' + sortRules.map((sr) => (sr.descending ? '-' + sr.code : sr.code)).join(',');\n}\n\nfunction formatIncludeTarget(kind: '_include' | '_revinclude', target: IncludeTarget): string {\n return (\n kind +\n (target.modifier ? ':' + target.modifier : '') +\n '=' +\n target.resourceType +\n ':' +\n target.searchParam +\n (target.targetType ? ':' + target.targetType : '')\n );\n}\n\n/**\n * Splits a FHIR search value on commas.\n * Respects backslash escape.\n *\n * See: https://hl7.org/fhir/r4/search.html#escaping\n *\n * @param input - The FHIR search value to split.\n * @returns The individual search values.\n */\nexport function splitSearchOnComma(input: string): string[] {\n const result: string[] = [];\n let current = '';\n let escaped = false;\n\n for (const c of input) {\n if (escaped) {\n current += c;\n escaped = false;\n } else if (c === '\\\\') {\n escaped = true;\n } else if (c === ',') {\n result.push(current);\n current = '';\n } else {\n current += c;\n }\n }\n\n // Push the last segment\n result.push(current);\n return result;\n}\n", "import { CodeableConcept, Coding, Identifier, Reference, Resource, SearchParameter } from '@medplum/fhirtypes';\nimport { evalFhirPath } from '../fhirpath/parse';\nimport { PropertyType, globalSchema } from '../types';\nimport { SearchParameterType, getSearchParameterDetails } from './details';\nimport { Filter, Operator, SearchRequest, splitSearchOnComma } from './search';\n\n/**\n * Determines if the resource matches the search request.\n * @param resource - The resource that was created or updated.\n * @param searchRequest - The subscription criteria as a search request.\n * @returns True if the resource satisfies the search request.\n */\nexport function matchesSearchRequest(resource: Resource, searchRequest: SearchRequest): boolean {\n if (searchRequest.resourceType !== resource.resourceType) {\n return false;\n }\n if (searchRequest.filters) {\n for (const filter of searchRequest.filters) {\n if (!matchesSearchFilter(resource, searchRequest, filter)) {\n return false;\n }\n }\n }\n return true;\n}\n\n/**\n * Determines if the resource matches the search filter.\n * @param resource - The resource that was created or updated.\n * @param searchRequest - The search request.\n * @param filter - One of the filters of a subscription criteria.\n * @returns True if the resource satisfies the search filter.\n */\nfunction matchesSearchFilter(resource: Resource, searchRequest: SearchRequest, filter: Filter): boolean {\n const searchParam = globalSchema.types[searchRequest.resourceType]?.searchParams?.[filter.code];\n if (!searchParam) {\n return false;\n }\n if (filter.operator === Operator.MISSING || filter.operator === Operator.PRESENT) {\n return matchesMissingOrPresent(resource, filter, searchParam);\n }\n switch (searchParam.type) {\n case 'reference':\n return matchesReferenceFilter(resource, filter, searchParam);\n case 'string':\n case 'uri':\n return matchesStringFilter(resource, filter, searchParam);\n case 'token':\n return matchesTokenFilter(resource, filter, searchParam);\n case 'date':\n return matchesDateFilter(resource, filter, searchParam);\n default:\n // Unknown search parameter or search parameter type\n // Default fail the check\n return false;\n }\n}\n\nfunction matchesMissingOrPresent(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const values = evalFhirPath(searchParam.expression as string, resource);\n const exists = values.length > 0;\n const desired =\n (filter.operator === Operator.MISSING && filter.value === 'false') ||\n (filter.operator === Operator.PRESENT && filter.value === 'true');\n return desired === exists;\n}\n\nfunction matchesReferenceFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const values = evalFhirPath(searchParam.expression as string, resource) as (Reference | string)[];\n const negated = isNegated(filter.operator);\n\n if (filter.value === '' && values.length === 0) {\n // If the filter operator is \"equals\", then the filter matches.\n // If the filter operator is \"not equals\", then the filter does not match.\n return filter.operator === Operator.EQUALS;\n }\n\n // Normalize the values array into reference strings\n const references = values.map((value) => (typeof value === 'string' ? value : value.reference));\n\n for (const filterValue of splitSearchOnComma(filter.value)) {\n let match = references.includes(filterValue);\n if (!match && filter.code === '_compartment') {\n // Backwards compability for compartment search parameter\n // In previous versions, the resource type was not required in compartment values\n // So, \"123\" would match \"Patient/123\"\n // We need to maintain this behavior for backwards compatibility\n match = references.some((reference) => reference?.endsWith('/' + filterValue));\n }\n if (match) {\n return !negated;\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesTokenFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const details = getSearchParameterDetails(resource.resourceType, searchParam);\n if (details.type === SearchParameterType.BOOLEAN) {\n return matchesBooleanFilter(resource, filter, searchParam);\n } else {\n return matchesStringFilter(resource, filter, searchParam, true);\n }\n}\n\nfunction matchesBooleanFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const values = evalFhirPath(searchParam.expression as string, resource);\n const expected = filter.value === 'true';\n const result = values.includes(expected);\n return isNegated(filter.operator) ? !result : result;\n}\n\nfunction matchesStringFilter(\n resource: Resource,\n filter: Filter,\n searchParam: SearchParameter,\n asToken?: boolean\n): boolean {\n const details = getSearchParameterDetails(resource.resourceType, searchParam);\n const searchParamElementType = details.elementDefinitions?.[0]?.type?.[0]?.code;\n const resourceValues = evalFhirPath(searchParam.expression as string, resource);\n const filterValues = splitSearchOnComma(filter.value);\n const negated = isNegated(filter.operator);\n for (const resourceValue of resourceValues) {\n for (const filterValue of filterValues) {\n let match;\n if (searchParamElementType === PropertyType.Identifier) {\n match = matchesTokenIdentifierValue(resourceValue as Identifier, filter.operator, filterValue);\n } else if (searchParamElementType === PropertyType.CodeableConcept) {\n match = matchesTokenCodeableConceptValue(resourceValue as Coding, filter.operator, filterValue);\n } else {\n match = matchesStringValue(resourceValue, filter.operator, filterValue, asToken);\n }\n if (match) {\n return !negated;\n }\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesStringValue(\n resourceValue: unknown,\n operator: Operator,\n filterValue: string,\n asToken?: boolean\n): boolean {\n if (asToken && filterValue.includes('|')) {\n const [system, code] = filterValue.split('|');\n return (\n matchesStringValue(resourceValue, operator, system, false) &&\n (!code || matchesStringValue(resourceValue, operator, code, false))\n );\n }\n let str = '';\n if (resourceValue) {\n if (typeof resourceValue === 'string') {\n str = resourceValue;\n } else if (typeof resourceValue === 'object') {\n str = JSON.stringify(resourceValue);\n }\n }\n return str.toLowerCase().includes(filterValue.toLowerCase());\n}\n\nfunction matchesTokenIdentifierValue(resourceValue: Identifier, operator: Operator, filterValue: string): boolean {\n if (filterValue.includes('|')) {\n const [system, value] = filterValue.split('|').map((s) => s.toLowerCase());\n if (!system && !value) {\n return false;\n } else if (!system) {\n // [parameter]=|[code]: the value of [code] matches a Coding.code or Identifier.value, and the Coding/Identifier has no system property\n return !resourceValue.system && resourceValue.value?.toLowerCase() === value;\n }\n\n // [parameter]=[system]|: any element where the value of [system] matches the system property of the Identifier or Coding\n // [parameter]=[system]|[code]: the value of [code] matches a Coding.code or Identifier.value, and the value of [system] matches the system property of the Identifier or Coding\n return resourceValue.system?.toLowerCase() === system && (!value || resourceValue.value?.toLowerCase() === value);\n }\n\n // [parameter]=[code]: the value of [code] matches a Coding.code or Identifier.value irrespective of the value of the system property\n return resourceValue.value?.toLowerCase() === filterValue.toLowerCase();\n}\n\nfunction matchesTokenCodeableConceptValue(\n resourceValue: CodeableConcept,\n _operator: Operator,\n filterValue: string\n): boolean {\n if (filterValue.includes('|')) {\n const [system, code] = filterValue.split('|').map((s) => s.toLowerCase());\n if (!system && !code) {\n return false;\n } else if (!system) {\n // [parameter]=|[code]: the value of [code] matches a Coding.code or Identifier.value, and the Coding/Identifier has no system property\n return resourceValue.coding?.some((coding) => !coding.system && coding.code?.toLowerCase() === code) ?? false;\n }\n\n // [parameter]=[system]|: any element where the value of [system] matches the system property of the Identifier or Coding\n // [parameter]=[system]|[code]: the value of [code] matches a Coding.code or Identifier.value, and the value of [system] matches the system property of the Identifier or Coding\n return (\n resourceValue.coding?.some(\n (coding) => coding.system?.toLowerCase() === system && (!code || coding.code?.toLowerCase() === code)\n ) ?? false\n );\n }\n\n // [parameter]=[code]: the value of [code] matches a Coding.code or Identifier.value irrespective of the value of the system property\n return (\n resourceValue.text?.toLowerCase() === filterValue.toLowerCase() ||\n (resourceValue.coding?.some((coding) => coding.code?.toLowerCase() === filterValue.toLowerCase()) ?? false)\n );\n}\n\nfunction matchesDateFilter(resource: Resource, filter: Filter, searchParam: SearchParameter): boolean {\n const resourceValues = evalFhirPath(searchParam.expression as string, resource);\n const filterValues = splitSearchOnComma(filter.value);\n const negated = isNegated(filter.operator);\n for (const resourceValue of resourceValues) {\n for (const filterValue of filterValues) {\n const match = matchesDateValue(resourceValue as string, filter.operator, filterValue);\n if (match) {\n return !negated;\n }\n }\n }\n // If \"not equals\" and no matches, then return true\n // If \"equals\" and no matches, then return false\n return negated;\n}\n\nfunction matchesDateValue(resourceValue: string, operator: Operator, filterValue: string): boolean {\n switch (operator) {\n case Operator.STARTS_AFTER:\n case Operator.GREATER_THAN:\n return resourceValue > filterValue;\n case Operator.GREATER_THAN_OR_EQUALS:\n return resourceValue >= filterValue;\n case Operator.ENDS_BEFORE:\n case Operator.LESS_THAN:\n return resourceValue < filterValue;\n case Operator.LESS_THAN_OR_EQUALS:\n return resourceValue <= filterValue;\n case Operator.EQUALS:\n case Operator.NOT_EQUALS:\n return resourceValue === filterValue;\n default:\n return false;\n }\n}\n\nfunction isNegated(operator: Operator): boolean {\n return operator === Operator.NOT_EQUALS || operator === Operator.NOT;\n}\n", "import { AccessPolicy, AccessPolicyResource, Resource, ResourceType } from '@medplum/fhirtypes';\nimport { matchesSearchRequest } from './search/match';\nimport { parseSearchRequest } from './search/search';\n\nconst universalAccessPolicy: AccessPolicyResource = {\n resourceType: '*',\n};\n\n/**\n * Protected resource types are in the \"medplum\" project.\n * Reading and writing is limited to the system account.\n */\nexport const protectedResourceTypes = ['DomainConfiguration', 'JsonWebKey', 'Login'];\n\n/**\n * Project admin resource types are special resources that are only\n * accessible to project administrators.\n */\nexport const projectAdminResourceTypes = [\n 'PasswordChangeRequest',\n 'UserSecurityRequest',\n 'Project',\n 'ProjectMembership',\n 'User',\n];\n\n/**\n * Interactions with a resource that can be controlled via an access policy.\n *\n * Codes taken from http://hl7.org/fhir/codesystem-restful-interaction.html\n */\nexport enum AccessPolicyInteraction {\n READ = 'read',\n VREAD = 'vread',\n UPDATE = 'update',\n PATCH = 'patch',\n DELETE = 'delete',\n HISTORY = 'history',\n HISTORY_INSTANCE = 'history-instance',\n HISTORY_TYPE = 'history-type',\n HISTORY_SYSTEM = 'history-system',\n CREATE = 'create',\n SEARCH = 'search',\n SEARCH_TYPE = 'search-type',\n SEARCH_SYSTEM = 'search-system',\n SEARCH_COMPARTMENT = 'search-compartment',\n CAPABILITIES = 'capabilities',\n TRANSACTION = 'transaction',\n BATCH = 'batch',\n OPERATION = 'operation',\n}\nconst resourceReadInteractions = [\n AccessPolicyInteraction.READ,\n AccessPolicyInteraction.VREAD,\n AccessPolicyInteraction.HISTORY,\n AccessPolicyInteraction.HISTORY_INSTANCE,\n];\n\n/**\n * Determines if the current user can read the specified resource type.\n * @param accessPolicy - The access policy.\n * @param resourceType - The resource type.\n * @returns True if the current user can read the specified resource type.\n */\nexport function canReadResourceType(accessPolicy: AccessPolicy, resourceType: ResourceType): boolean {\n if (accessPolicy.resource) {\n for (const resourcePolicy of accessPolicy.resource) {\n if (matchesAccessPolicyResourceType(resourcePolicy.resourceType, resourceType)) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Determines if the current user can write the specified resource type.\n * This is a preliminary check before evaluating a write operation in depth.\n * If a user cannot write a resource type at all, then don't bother looking up previous versions.\n * @param accessPolicy - The access policy.\n * @param resourceType - The resource type.\n * @returns True if the current user can write the specified resource type.\n */\nexport function canWriteResourceType(accessPolicy: AccessPolicy, resourceType: ResourceType): boolean {\n if (protectedResourceTypes.includes(resourceType)) {\n return false;\n }\n if (accessPolicy.resource) {\n for (const resourcePolicy of accessPolicy.resource) {\n if (matchesAccessPolicyResourceType(resourcePolicy.resourceType, resourceType) && !resourcePolicy.readonly) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Determines if the current user can write the specified resource.\n * This is a more in-depth check after building the candidate result of a write operation.\n * @param accessPolicy - The access policy.\n * @param resource - The resource.\n * @returns True if the current user can write the specified resource type.\n */\nexport function canWriteResource(accessPolicy: AccessPolicy, resource: Resource): boolean {\n const resourceType = resource.resourceType;\n if (!canWriteResourceType(accessPolicy, resourceType)) {\n return false;\n }\n return matchesAccessPolicy(accessPolicy, resource, false);\n}\n\n/**\n * Returns true if the resource satisfies the current access policy.\n * @param accessPolicy - The access policy.\n * @param resource - The resource.\n * @param readonlyMode - True if the resource is being read.\n * @returns True if the resource matches the access policy.\n * @deprecated Use satisfiedAccessPolicy() instead.\n */\nexport function matchesAccessPolicy(accessPolicy: AccessPolicy, resource: Resource, readonlyMode: boolean): boolean {\n if (accessPolicy.resource) {\n for (const resourcePolicy of accessPolicy.resource) {\n if (\n matchesAccessPolicyResourcePolicy(\n resource,\n readonlyMode ? AccessPolicyInteraction.READ : AccessPolicyInteraction.UPDATE,\n resourcePolicy\n )\n ) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Checks that there is an access policy permitting the given resource interaction, returning the matching policy object.\n * @param resource - The resource being acted upon.\n * @param interaction - The interaction being performed on the resource.\n * @param accessPolicy - The relevant access policy for the current user.\n * @returns The satisfied access policy, or undefined if the access policy does not permit the given interaction.\n */\nexport function satisfiedAccessPolicy(\n resource: Resource,\n interaction: AccessPolicyInteraction,\n accessPolicy: AccessPolicy | undefined\n): AccessPolicyResource | undefined {\n if (!accessPolicy) {\n return universalAccessPolicy;\n }\n if (accessPolicy.resource) {\n for (const resourcePolicy of accessPolicy.resource) {\n if (matchesAccessPolicyResourcePolicy(resource, interaction, resourcePolicy)) {\n return resourcePolicy;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Returns true if the resource satisfies the specified access policy resource policy.\n * @param resource - The resource.\n * @param interaction - The interaction being performed on the resource.\n * @param resourcePolicy - One per-resource policy section from the access policy.\n * @returns True if the resource matches the access policy.\n */\nfunction matchesAccessPolicyResourcePolicy(\n resource: Resource,\n interaction: AccessPolicyInteraction,\n resourcePolicy: AccessPolicyResource\n): boolean {\n const resourceType = resource.resourceType;\n if (!matchesAccessPolicyResourceType(resourcePolicy.resourceType, resourceType)) {\n return false;\n }\n if (resourcePolicy.readonly && !resourceReadInteractions.includes(interaction)) {\n return false;\n }\n if (\n resourcePolicy.compartment &&\n !resource.meta?.compartment?.find((c) => c.reference === resourcePolicy.compartment?.reference)\n ) {\n // Deprecated - to be removed\n return false;\n }\n if (resourcePolicy.criteria && !matchesSearchRequest(resource, parseSearchRequest(resourcePolicy.criteria))) {\n return false;\n }\n return true;\n}\n\n/**\n * Returns true if the resource type matches the access policy resource type.\n * @param accessPolicyResourceType - The resource type from the access policy.\n * @param resourceType - The candidate resource resource type.\n * @returns True if the resource type matches the access policy resource type.\n */\nfunction matchesAccessPolicyResourceType(\n accessPolicyResourceType: string | undefined,\n resourceType: ResourceType\n): boolean {\n if (accessPolicyResourceType === resourceType) {\n return true;\n }\n if (accessPolicyResourceType === '*' && !projectAdminResourceTypes.includes(resourceType)) {\n // Project admin resource types are not allowed to be wildcarded\n // Project admin resource types must be explicitly included\n return true;\n }\n return false;\n}\n", "/**\n * Decodes a base64 string.\n * Handles both browser and Node environments.\n * Supports Unicode characters.\n * @param data - The base-64 encoded input string.\n * @returns The decoded string.\n */\nexport function decodeBase64(data: string): string {\n if (typeof window !== 'undefined') {\n const binaryString = window.atob(data);\n const bytes = Uint8Array.from(binaryString, (c) => c.charCodeAt(0));\n return new window.TextDecoder().decode(bytes);\n }\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(data, 'base64').toString('utf-8');\n }\n throw new Error('Unable to decode base64');\n}\n\n/**\n * Encodes a base64 string.\n * Handles both browser and Node environments.\n * Supports Unicode characters.\n * @param data - The unencoded input string.\n * @returns The base-64 encoded string.\n */\nexport function encodeBase64(data: string): string {\n if (typeof window !== 'undefined') {\n const utf8Bytes = new window.TextEncoder().encode(data);\n // utf8Bytes is a Uint8Array, but String.fromCharCode expects a sequence of numbers.\n const binaryString = String.fromCharCode.apply(null, utf8Bytes as unknown as number[]);\n return window.btoa(binaryString);\n }\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(data, 'utf8').toString('base64');\n }\n throw new Error('Unable to encode base64');\n}\n", "import { arrayBufferToHex } from './utils';\n\n/**\n * Returns a cryptographically secure random string.\n * @returns A cryptographically secure random string.\n */\nexport function getRandomString(): string {\n const randomItems = new Uint32Array(28);\n crypto.getRandomValues(randomItems);\n return arrayBufferToHex(randomItems.buffer);\n}\n\n/**\n * Encrypts a string with SHA256 encryption.\n * @param str - The unencrypted input string.\n * @returns The encrypted value in an ArrayBuffer.\n */\nexport async function encryptSHA256(str: string): Promise<ArrayBuffer> {\n return crypto.subtle.digest('SHA-256', new TextEncoder().encode(str));\n}\n\n/**\n * Cross platform random UUID generator\n * Note that this is not intended for production use, but rather for testing\n * This should be replaced when crypto.randomUUID is fully supported\n * See: https://stackoverflow.com/revisions/2117523/28\n * @returns A random UUID.\n */\nexport function generateId(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n", "import { Bundle, BundleEntry, BundleEntryRequest, Resource } from '@medplum/fhirtypes';\nimport { generateId } from './crypto';\nimport { isReference } from './types';\nimport { deepClone } from './utils';\n\n/**\n * More on Bundles can be found here\n * http://hl7.org/fhir/R4/bundle.html\n */\n\n/**\n * Takes a bundle and creates a Transaction Type bundle\n * @param bundle - The Bundle object that we'll receive from the search query\n * @returns transaction type bundle\n */\nexport function convertToTransactionBundle(bundle: Bundle): Bundle {\n const idToUuid: Record<string, string> = {};\n bundle = deepClone(bundle);\n for (const entry of bundle.entry || []) {\n if (entry.resource?.meta !== undefined) {\n delete entry.resource.meta.author;\n delete entry.resource.meta.compartment;\n delete entry.resource.meta.lastUpdated;\n delete entry.resource.meta.project;\n delete entry.resource.meta.versionId;\n }\n const id = entry.resource?.id;\n if (id) {\n idToUuid[id] = generateId();\n\n entry.fullUrl = 'urn:uuid:' + idToUuid[id];\n delete entry.resource?.id;\n }\n }\n const input = bundle.entry;\n const jsonString = JSON.stringify(\n {\n resourceType: 'Bundle',\n type: 'transaction',\n entry: input?.map((entry: any) => ({\n fullUrl: entry.fullUrl,\n request: { method: 'POST', url: entry.resource.resourceType },\n resource: entry.resource,\n })),\n },\n (key, value) => referenceReplacer(key, value, idToUuid),\n 2\n );\n return reorderBundle(JSON.parse(jsonString) as Bundle);\n}\n\nfunction referenceReplacer(key: string, value: string, idToUuid: Record<string, string>): string {\n if (key === 'reference' && typeof value === 'string') {\n let id;\n if (value.includes('/')) {\n id = value.split('/')[1];\n } else if (value.startsWith('urn:uuid:')) {\n id = value.slice(9);\n } else if (value.startsWith('#')) {\n id = value.slice(1);\n }\n if (id) {\n const replacement = idToUuid[id];\n if (replacement) {\n return 'urn:uuid:' + replacement;\n }\n }\n }\n return value;\n}\n\n/**\n * Topologically sorts a `batch` or `transaction` bundle to improve reference resolution.\n * The bundle is sorted such that a resource is created _before_ references to that resource appear in the bundle.\n *\n * In the event of cycles, this function will first create a POST request for each resource in the cycle, and then will\n * append a PUT request to the bundle. This ensures that each resources in the cycle is visited twice, and all\n * references can be resolved\n * @param bundle - Input bundle with type `batch` or `transaction`\n * @returns Bundle of the same type, with Bundle.entry reordered\n */\nexport function reorderBundle(bundle: Bundle): Bundle {\n const adjacencyList = buildAdjacencyList(bundle);\n const { sorted: sortedFullUrls, cycles } = topologicalSortWithCycles(adjacencyList);\n\n const entryMap: Record<string, BundleEntry> = {};\n\n for (const entry of bundle.entry ?? []) {\n if (entry.fullUrl) {\n entryMap[entry.fullUrl] = entry;\n }\n }\n\n const reorderedEntries = sortedFullUrls.map((fullUrl) => entryMap[fullUrl]);\n\n // Handle cycles by appending additional entries with a method of 'PUT'\n for (const cycle of cycles) {\n for (const fullUrl of cycle) {\n const originalEntry = entryMap[fullUrl];\n const putEntry: BundleEntry = {\n ...originalEntry,\n request: {\n ...(originalEntry.request as BundleEntryRequest),\n method: 'PUT',\n },\n };\n reorderedEntries.push(putEntry);\n }\n }\n\n return { ...bundle, entry: reorderedEntries };\n}\n\ntype AdjacencyList = Record<string, string[]>;\n\nenum VertexState {\n NotVisited,\n Visiting,\n Visited,\n}\n\nfunction topologicalSortWithCycles(graph: AdjacencyList): { sorted: string[]; cycles: string[][] } {\n const sorted: string[] = [];\n const state: Record<string, VertexState> = {};\n const cycles: string[][] = [];\n\n // Initialize all vertices to NotVisited state\n for (const vertex of Object.keys(graph)) {\n state[vertex] = VertexState.NotVisited;\n }\n\n function visit(vertex: string, path: string[]): boolean {\n // If this vertex is already visited, return true\n if (state[vertex] === VertexState.Visited) {\n return true;\n }\n\n // If this vertex is currently being visited, we have a cycle\n if (state[vertex] === VertexState.Visiting) {\n const cycleStartIndex = path.lastIndexOf(vertex);\n if (cycleStartIndex !== -1) {\n cycles.push(path.slice(cycleStartIndex));\n }\n return true; // return true for vertices that are part of cycles\n }\n\n // Mark the vertex as visiting and add it to the path\n state[vertex] = VertexState.Visiting;\n path.push(vertex);\n\n // Visit all neighbors\n let hasCycle = false;\n for (const neighbor of graph[vertex]) {\n if (!visit(neighbor, path)) {\n hasCycle = true;\n }\n }\n\n // Mark the vertex as visited, remove it from the path, and add it to the sorted list\n state[vertex] = VertexState.Visited;\n path.pop();\n sorted.unshift(vertex);\n\n return !hasCycle;\n }\n\n for (const vertex in graph) {\n if (state[vertex] === VertexState.NotVisited) {\n const path: string[] = [];\n visit(vertex, path);\n }\n }\n\n return { sorted, cycles };\n}\n\nfunction findReferences(resource: any, callback: (reference: string) => void): void {\n for (const key in resource) {\n if (resource[key] && typeof resource[key] === 'object') {\n const value = resource[key];\n\n if (isReference(value)) {\n const reference = value.reference;\n if (reference.startsWith('urn:uuid:')) {\n callback(reference);\n }\n } else {\n findReferences(value, callback);\n }\n }\n }\n}\n\nfunction buildAdjacencyList(bundle: Bundle): AdjacencyList {\n const adjacencyList: AdjacencyList = {};\n\n // Initialize adjacency list with empty arrays for each entry's fullUrl\n for (const entry of bundle.entry || []) {\n if (entry.fullUrl) {\n adjacencyList[entry.fullUrl] = [];\n }\n }\n\n for (const entry of bundle.entry || []) {\n const fullUrl = entry.fullUrl;\n\n if (entry.resource) {\n findReferences(entry.resource, (reference: string) => {\n // Add an incoming reference to the adjacency list\n if (adjacencyList[reference]) {\n adjacencyList[reference].push(fullUrl as string);\n }\n });\n }\n }\n\n return adjacencyList;\n}\n\n/**\n * Converts a resource with contained resources to a transaction bundle.\n * This function is useful when creating a resource that contains other resources.\n * Handles local references and topological sorting.\n * @param resource - The input resource which may or may not include contained resources.\n * @returns A bundle with the input resource and all contained resources.\n */\nexport function convertContainedResourcesToBundle(resource: Resource & { contained?: Resource[] }): Bundle {\n // Create a clone so we don't modify the original resource\n resource = deepClone(resource);\n\n // Create the simple naive bundle\n const simpleBundle = {\n resourceType: 'Bundle',\n type: 'transaction',\n entry: [{ resource }],\n } satisfies Bundle;\n\n // Move all contained resources to the bundle\n if (resource.contained) {\n for (const contained of resource.contained) {\n simpleBundle.entry.push({ resource: contained });\n }\n resource.contained = undefined;\n }\n\n // Make sure that all resources have an ID\n // This is required for convertToTransactionBundle\n for (const entry of simpleBundle.entry) {\n if (entry.resource && !entry.resource.id) {\n entry.resource.id = generateId();\n }\n }\n\n // Convert to a transaction bundle\n // This adds fullUrl and request properties to each entry\n // and reorders the bundle to ensure that contained resources are created before they are referenced.\n return convertToTransactionBundle(simpleBundle);\n}\n", "/**\n * LRU cache (least recently used)\n * Source: https://stackoverflow.com/a/46432113\n */\nexport class LRUCache<T> {\n private readonly max: number;\n private readonly cache: Map<string, T>;\n\n constructor(max = 10) {\n this.max = max;\n this.cache = new Map();\n }\n\n /**\n * Deletes all values from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Returns the value for the given key.\n * @param key - The key to retrieve.\n * @returns The value if found; undefined otherwise.\n */\n get(key: string): T | undefined {\n const item = this.cache.get(key);\n if (item) {\n this.cache.delete(key);\n this.cache.set(key, item);\n }\n return item;\n }\n\n /**\n * Sets the value for the given key.\n * @param key - The key to set.\n * @param val - The value to set.\n */\n set(key: string, val: T): void {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.max) {\n this.cache.delete(this.first());\n }\n this.cache.set(key, val);\n }\n\n /**\n * Deletes the value for the given key.\n * @param key - The key to delete.\n */\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n /**\n * Returns the list of all keys in the cache.\n * @returns The array of keys in the cache.\n */\n keys(): IterableIterator<string> {\n return this.cache.keys();\n }\n\n private first(): string {\n // This works because the Map class maintains ordered keys.\n return this.cache.keys().next().value as string;\n }\n}\n", "/**\n * Content type constants.\n */\nexport const ContentType = {\n CSS: 'text/css',\n DICOM: 'application/dicom',\n FAVICON: 'image/vnd.microsoft.icon',\n FHIR_JSON: 'application/fhir+json',\n FORM_URL_ENCODED: 'application/x-www-form-urlencoded',\n HL7_V2: 'x-application/hl7-v2+er7',\n HTML: 'text/html',\n JAVASCRIPT: 'text/javascript',\n JSON: 'application/json',\n JSON_PATCH: 'application/json-patch+json',\n PNG: 'image/png',\n SCIM_JSON: 'application/scim+json',\n SVG: 'image/svg+xml',\n TEXT: 'text/plain',\n TYPESCRIPT: 'text/typescript',\n PING: 'x-application/ping',\n XML: 'text/xml',\n // See: https://www.iana.org/assignments/media-types/application/cda+xml\n CDA_XML: 'application/cda+xml',\n} as const;\n", "/*\n * Based on: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget\n */\n\nexport interface Event {\n readonly type: string;\n readonly defaultPrevented?: boolean;\n}\n\nexport type EventListener = (e: Event) => void;\n\nexport class EventTarget {\n private readonly listeners: Record<string, EventListener[]>;\n\n constructor() {\n this.listeners = {};\n }\n\n addEventListener(type: string, callback: EventListener): void {\n if (!this.listeners[type]) {\n this.listeners[type] = [];\n }\n this.listeners[type].push(callback);\n }\n\n removeEventListener(type: string, callback: EventListener): void {\n const array = this.listeners[type];\n if (!array) {\n return;\n }\n for (let i = 0; i < array.length; i++) {\n if (array[i] === callback) {\n array.splice(i, 1);\n return;\n }\n }\n }\n\n dispatchEvent(event: Event): boolean {\n const array = this.listeners[event.type];\n if (array) {\n for (const listener of array) {\n listener.call(this, event);\n }\n }\n return !event.defaultPrevented;\n }\n\n removeAllListeners(): void {\n // @ts-expect-error Normally listeners is read-only. In this case we are dumping all listeners\n this.listeners = {};\n }\n}\n\nexport class TypedEventTarget<TEvents extends Record<string, Event>> {\n private emitter = new EventTarget();\n\n dispatchEvent<TEventType extends keyof TEvents & string>(event: TEvents[TEventType]): void {\n this.emitter.dispatchEvent(event);\n }\n\n addEventListener<TEventType extends keyof TEvents & string>(\n type: TEventType,\n handler: (event: TEvents[TEventType]) => void\n ): void {\n this.emitter.addEventListener(type, handler as any);\n }\n\n removeEventListener<TEventType extends keyof TEvents & string>(\n type: TEventType,\n handler: (event: TEvents[TEventType]) => void\n ): void {\n this.emitter.removeEventListener(type, handler as any);\n }\n\n removeAllListeners(): void {\n this.emitter.removeAllListeners();\n }\n}\n", "import {\n Bundle,\n DiagnosticReport,\n Encounter,\n ImagingStudy,\n OperationOutcome,\n Patient,\n Reference,\n Resource,\n} from '@medplum/fhirtypes';\nimport { generateId } from '../crypto';\nimport { TypedEventTarget } from '../eventtarget';\nimport { OperationOutcomeError, validationError } from '../outcomes';\nimport { isReference } from '../types';\n\n// We currently try to satisfy both STU2 and STU3. Where STU3 removes a resource / key from STU2, we leave it in as a valid key but don't require it.\n\nexport const FHIRCAST_EVENT_NAMES = {\n 'Patient-open': 'Patient-open',\n 'Patient-close': 'Patient-close',\n 'ImagingStudy-open': 'ImagingStudy-open',\n 'ImagingStudy-close': 'ImagingStudy-close',\n 'Encounter-open': 'Encounter-open',\n 'Encounter-close': 'Encounter-close',\n 'DiagnosticReport-open': 'DiagnosticReport-open',\n 'DiagnosticReport-close': 'DiagnosticReport-close',\n 'DiagnosticReport-select': 'DiagnosticReport-select',\n 'DiagnosticReport-update': 'DiagnosticReport-update',\n syncerror: 'syncerror',\n} as const;\n\nexport const FHIRCAST_RESOURCE_TYPES = [\n 'Patient',\n 'Encounter',\n 'ImagingStudy',\n 'DiagnosticReport',\n 'OperationOutcome',\n 'Bundle',\n] as const;\n\nexport const FHIRCAST_EVENT_VERSION_REQUIRED = ['DiagnosticReport-update'] as const;\nexport type FhircastEventVersionRequired = (typeof FHIRCAST_EVENT_VERSION_REQUIRED)[number];\nexport type FhircastEventVersionOptional = Exclude<FhircastEventName, FhircastEventVersionRequired>;\nexport function isContextVersionRequired(event: string): event is FhircastEventVersionRequired {\n return (FHIRCAST_EVENT_VERSION_REQUIRED as readonly string[]).includes(event);\n}\nexport function assertContextVersionOptional(event: string): asserts event is FhircastEventVersionOptional {\n if ((FHIRCAST_EVENT_VERSION_REQUIRED as readonly string[]).includes(event)) {\n throw new OperationOutcomeError(validationError(`'context.version' is required for '${event}'.`));\n }\n}\n\nexport type FhircastEventName = keyof typeof FHIRCAST_EVENT_NAMES;\nexport type FhircastResourceEventName = Exclude<FhircastEventName, 'syncerror'>;\nexport type FhircastResourceType = (typeof FHIRCAST_RESOURCE_TYPES)[number];\nexport type FhircastAnchorResourceType = 'Patient' | 'ImagingStudy' | 'Encounter' | 'DiagnosticReport';\n\nexport type FhircastEventContextDetails = {\n resourceType: FhircastResourceType | '*';\n optional?: boolean; // NOTE: optional here is only referring to the schema, the spec often mentions that these are required if available as references for a given anchor resource\n manyAllowed?: boolean;\n array?: boolean;\n reference?: boolean;\n};\n\n// Key value pairs of { [FhircastEventName]: [required_resource1, required_resource2] }\nexport const FHIRCAST_EVENT_RESOURCES = {\n 'Patient-open': {\n patient: { resourceType: 'Patient' },\n /* STU2 only! `encounter` key removed in STU3 */\n encounter: { resourceType: 'Encounter', optional: true },\n },\n 'Patient-close': {\n patient: { resourceType: 'Patient' },\n /* STU2 only! `encounter` key removed in STU3 */\n encounter: { resourceType: 'Encounter', optional: true },\n },\n 'ImagingStudy-open': {\n study: { resourceType: 'ImagingStudy' },\n encounter: { resourceType: 'Encounter', optional: true },\n patient: { resourceType: 'Patient', optional: true },\n },\n 'ImagingStudy-close': {\n study: { resourceType: 'ImagingStudy' },\n encounter: { resourceType: 'Encounter', optional: true },\n patient: { resourceType: 'Patient', optional: true },\n },\n 'Encounter-open': {\n encounter: { resourceType: 'Encounter' },\n patient: { resourceType: 'Patient' },\n },\n 'Encounter-close': {\n encounter: { resourceType: 'Encounter' },\n patient: { resourceType: 'Patient' },\n },\n 'DiagnosticReport-open': {\n report: { resourceType: 'DiagnosticReport' },\n encounter: { resourceType: 'Encounter', optional: true },\n study: { resourceType: 'ImagingStudy', optional: true, manyAllowed: true },\n patient: { resourceType: 'Patient' },\n },\n 'DiagnosticReport-close': {\n report: { resourceType: 'DiagnosticReport' },\n encounter: { resourceType: 'Encounter', optional: true },\n study: { resourceType: 'ImagingStudy', optional: true, manyAllowed: true },\n patient: { resourceType: 'Patient' },\n },\n 'DiagnosticReport-select': {\n // Most event contexts contain a full resource, but `DiagnosticReport-select` context elements are actually references\n // See: https://build.fhir.org/ig/HL7/fhircast-docs/3-6-4-DiagnosticReport-select.html\n report: { resourceType: 'DiagnosticReport', reference: true },\n patient: { resourceType: 'Patient', optional: true, reference: true },\n select: { resourceType: '*', array: true, reference: true },\n },\n 'DiagnosticReport-update': {\n // `report` and `patient` are also references for `DiagnosticReport-update`:\n // See: https://build.fhir.org/ig/HL7/fhircast-docs/3-6-3-DiagnosticReport-update.html\n report: { resourceType: 'DiagnosticReport', reference: true },\n patient: { resourceType: 'Patient', optional: true, reference: true },\n updates: { resourceType: 'Bundle' },\n },\n syncerror: {\n operationoutcome: { resourceType: 'OperationOutcome' },\n },\n} as const satisfies Record<FhircastEventName, Record<string, FhircastEventContextDetails>>;\n\n/**\n * Checks if a `ResourceType` can be used in a `FHIRcast` context.\n *\n * @param resourceType - A `ResourceType` to test.\n * @returns `true` if this is a resource type associated with `FHIRcast` contexts, otherwise returns `false`.\n */\nexport function isFhircastResourceType(resourceType: FhircastResourceType): boolean {\n return FHIRCAST_RESOURCE_TYPES.includes(resourceType);\n}\n\n/**\n * A `FHIRcast` subscription request.\n *\n * Can be passed to `MedplumClient.fhircastConnect` or `MedplumClient.fhircastUnsubscribe` to either open a `FHIRcast` connection, or unsubscribe from the subscription.\n */\nexport type SubscriptionRequest = {\n channelType: 'websocket';\n mode: 'subscribe' | 'unsubscribe';\n events: FhircastEventName[];\n topic: string;\n endpoint: string;\n};\n\nexport type FhircastPatientContext = { key: 'patient'; resource: Patient };\nexport type FhircastEncounterContext = { key: 'encounter'; resource: Encounter };\nexport type FhircastStudyContext = { key: 'study'; resource: ImagingStudy };\nexport type FhircastReportContext = { key: 'report'; resource: DiagnosticReport };\nexport type FhircastReportReferenceContext = { key: 'report'; reference: Reference<DiagnosticReport> };\nexport type FhircastPatientReferenceContext = { key: 'patient'; reference: Reference<Patient> };\nexport type FhircastUpdatesContext = { key: 'updates'; resource: Bundle };\nexport type FhircastSelectContext = { key: 'select'; reference: Reference[] };\nexport type FhircastOperationOutcomeContext = { key: 'operationoutcome'; resource: OperationOutcome };\n\n// These are all the contexts that contain a `resource` key\nexport type FhircastResourceContext =\n | FhircastPatientContext\n | FhircastEncounterContext\n | FhircastStudyContext\n | FhircastReportContext\n | FhircastUpdatesContext\n | FhircastOperationOutcomeContext;\n\n// The reference contexts related to `*-select` and `*-update` events, which contain a `reference` key\nexport type FhircastSingleReferenceContext = FhircastReportReferenceContext | FhircastPatientReferenceContext;\n// Multi-reference contexts contain a `reference` key with an array of references as the value, currently only the `select` context\nexport type FhircastMultiReferenceContext = FhircastSelectContext;\n\nexport type FhircastPatientOpenContext = FhircastPatientContext | FhircastEncounterContext;\nexport type FhircastPatientCloseContext = FhircastPatientOpenContext;\nexport type FhircastImagingStudyOpenContext = FhircastStudyContext | FhircastEncounterContext | FhircastPatientContext;\nexport type FhircastImagingStudyCloseContext = FhircastImagingStudyOpenContext;\nexport type FhircastEncounterOpenContext = FhircastEncounterContext | FhircastPatientContext;\nexport type FhircastEncounterCloseContext = FhircastEncounterOpenContext;\nexport type FhircastDiagnosticReportOpenContext =\n | FhircastReportContext\n | FhircastEncounterContext\n | FhircastStudyContext\n | FhircastPatientContext;\nexport type FhircastDiagnosticReportCloseContext = FhircastDiagnosticReportOpenContext;\nexport type FhircastDiagnosticReportUpdateContext =\n | FhircastReportReferenceContext\n | FhircastPatientReferenceContext\n | FhircastUpdatesContext;\nexport type FhircastDiagnosticReportSelectContext =\n | FhircastReportReferenceContext\n | FhircastPatientReferenceContext\n | FhircastSelectContext;\nexport type FhircastSyncErrorContext = FhircastOperationOutcomeContext;\n\n// This is the one key that only exists within a GetCurrentContext\n// Specifically related to the `DiagnosticReport-update` event in a `DiagnosticReport` context\n// See the FHIRcast docs regarding content sharing: https://build.fhir.org/ig/HL7/fhircast-docs/2-10-ContentSharing.html#updating-attributes-of-context-resources-and-addingremoving-context-resources\n// And the GetCurrentContext page that mentions the specifications of this key: https://build.fhir.org/ig/HL7/fhircast-docs/2-9-GetCurrentContext.html#context\nexport type FhircastHubContentContext = {\n key: 'content';\n resource: Bundle;\n};\n\n// Type utility to get keys for a specific event\nexport type FhircastEventKeys<EventName extends FhircastEventName> = keyof (typeof FHIRCAST_EVENT_RESOURCES)[EventName];\n\n// Type utility to extract the resource type from an event and key\nexport type FhircastContextResourceType<\n EventName extends FhircastEventName,\n K extends FhircastEventKeys<EventName>,\n> = (typeof FHIRCAST_EVENT_RESOURCES)[EventName][K] extends { resourceType: infer R } ? R : never;\n\nexport type FhircastEventContext<EventName extends FhircastEventName = FhircastResourceEventName> =\n EventName extends 'Patient-open'\n ? FhircastPatientOpenContext\n : EventName extends 'Patient-close'\n ? FhircastPatientCloseContext\n : EventName extends 'ImagingStudy-open'\n ? FhircastImagingStudyOpenContext\n : EventName extends 'ImagingStudy-close'\n ? FhircastImagingStudyCloseContext\n : EventName extends 'Encounter-open'\n ? FhircastEncounterOpenContext\n : EventName extends 'Encounter-close'\n ? FhircastEncounterCloseContext\n : EventName extends 'DiagnosticReport-open'\n ? FhircastDiagnosticReportOpenContext\n : EventName extends 'DiagnosticReport-close'\n ? FhircastDiagnosticReportCloseContext\n : EventName extends 'DiagnosticReport-update'\n ? FhircastDiagnosticReportUpdateContext\n : EventName extends 'DiagnosticReport-select'\n ? FhircastDiagnosticReportSelectContext\n : EventName extends 'syncerror'\n ? FhircastSyncErrorContext\n : never;\n\nexport type AnchorResourceOpenEvent<T extends FhircastAnchorResourceType> = T extends FhircastAnchorResourceType\n ? `${T}-open`\n : never;\n\nexport type CurrentContext<T extends FhircastAnchorResourceType | '' = FhircastAnchorResourceType | ''> = T extends ''\n ? { 'context.type': ''; context: never[] }\n : T extends 'DiagnosticReport'\n ? {\n 'context.type': 'DiagnosticReport';\n 'context.versionId': string;\n context: (FhircastEventContext<'DiagnosticReport-open'> | FhircastHubContentContext)[];\n }\n : T extends 'Patient' | 'Encounter' | 'ImagingStudy'\n ? {\n 'context.type': T;\n 'context.versionId': string;\n context: FhircastEventContext<AnchorResourceOpenEvent<T>>;\n }\n : never;\n\nexport type PendingSubscriptionRequest = Omit<SubscriptionRequest, 'endpoint'>;\n\nexport type FhircastEventPayload<EventName extends FhircastEventName = FhircastEventName> = {\n 'hub.topic': string;\n 'hub.event': EventName;\n context: FhircastEventContext<EventName>[];\n 'context.versionId'?: string;\n 'context.priorVersionId'?: string;\n};\n\nexport type FhircastMessagePayload<EventName extends FhircastEventName = FhircastEventName> = {\n timestamp: string;\n id: string;\n event: FhircastEventPayload<EventName>;\n};\n\nexport function isCompletedSubscriptionRequest(\n subscriptionRequest: SubscriptionRequest | PendingSubscriptionRequest\n): subscriptionRequest is SubscriptionRequest {\n return !!(subscriptionRequest as SubscriptionRequest).endpoint;\n}\n\n/**\n * Creates a serialized url-encoded payload for a `FHIRcast` subscription from a `SubscriptionRequest` object that can be directly used in an HTTP request to the Hub.\n *\n * @param subscriptionRequest - An object representing a subscription request.\n * @returns A serialized subscription in url-encoded form.\n */\nexport function serializeFhircastSubscriptionRequest(\n subscriptionRequest: SubscriptionRequest | PendingSubscriptionRequest\n): string {\n if (!validateFhircastSubscriptionRequest(subscriptionRequest)) {\n throw new OperationOutcomeError(\n validationError('subscriptionRequest must be an object conforming to SubscriptionRequest type.')\n );\n }\n\n const { channelType, mode, topic, events } = subscriptionRequest;\n\n const formattedSubRequest = {\n 'hub.channel.type': channelType,\n 'hub.mode': mode,\n 'hub.topic': topic,\n 'hub.events': events.join(','),\n } as Record<string, string>;\n\n if (isCompletedSubscriptionRequest(subscriptionRequest)) {\n formattedSubRequest.endpoint = subscriptionRequest.endpoint;\n }\n return new URLSearchParams(formattedSubRequest).toString();\n}\n\n/**\n * Validates that a `SubscriptionRequest`.\n *\n * @param subscriptionRequest - The `SubscriptionRequest` to validate.\n * @returns A `boolean` indicating whether or not the `SubscriptionRequest` is valid.\n */\nexport function validateFhircastSubscriptionRequest(\n subscriptionRequest: SubscriptionRequest | PendingSubscriptionRequest\n): boolean {\n if (typeof subscriptionRequest !== 'object') {\n return false;\n }\n const { channelType, mode, topic, events } = subscriptionRequest;\n if (!(channelType && mode && topic && events)) {\n return false;\n }\n if (typeof topic !== 'string') {\n return false;\n }\n if (typeof events !== 'object' || !Array.isArray(events) || events.length < 1) {\n return false;\n }\n if (channelType !== 'websocket') {\n return false;\n }\n if (mode !== 'subscribe' && mode !== 'unsubscribe') {\n return false;\n }\n for (const event of events) {\n if (!FHIRCAST_EVENT_NAMES[event]) {\n return false;\n }\n }\n if (\n isCompletedSubscriptionRequest(subscriptionRequest) &&\n !(typeof subscriptionRequest.endpoint === 'string' && subscriptionRequest.endpoint.startsWith('ws'))\n ) {\n return false;\n }\n return true;\n}\n\n/**\n * Throws if the context resource type is invalid. Intended as a helper for `validateFhircastContexts` only.\n *\n * @param event - The `FHIRcast` event name associated with the provided contexts.\n * @param resource - The `FHIRcast` event context resource to validate for given key.\n * @param i - The index of the current context in the context list.\n * @param keySchema - Schema for given key for FHIRcast event.\n */\nfunction validateSingleResourceContext(\n event: FhircastEventName,\n resource: Resource,\n i: number,\n keySchema: FhircastEventContextDetails\n): void {\n if (typeof resource !== 'object') {\n throw new OperationOutcomeError(\n validationError(\n `context[${i}] is invalid. Context must contain a single valid FHIR resource! Resource is not an object.`\n )\n );\n }\n if (!(resource.id && typeof resource.id === 'string')) {\n throw new OperationOutcomeError(\n validationError(`context[${i}] is invalid. Resource must contain a valid string ID.`)\n );\n }\n if (!resource.resourceType) {\n throw new OperationOutcomeError(\n validationError(`context[${i}] is invalid. Resource must contain a resource type. No resource type found.`)\n );\n }\n const expectedResourceType = keySchema.resourceType;\n // Make sure that resource is a valid type for this event if expected is not wildcard\n if (expectedResourceType !== '*') {\n if (!isFhircastResourceType(resource.resourceType as FhircastResourceType)) {\n throw new OperationOutcomeError(\n validationError(\n `context[${i}] is invalid. Resource must contain a valid FHIRcast resource type. Resource type is not a known resource type.`\n )\n );\n }\n if (expectedResourceType && resource.resourceType !== expectedResourceType) {\n throw new OperationOutcomeError(\n validationError(\n `context[${i}] is invalid. context[${i}] for the '${event}' event should contain resource of type ${expectedResourceType}.`\n )\n );\n }\n }\n}\n\n/**\n * Throws if the context is invalid. Intended as a helper for `validateFhircastContexts` only.\n *\n * @param event - The `FHIRcast` event name associated with the provided contexts.\n * @param context - The `FHIRcast` event contexts to validate.\n * @param i - The index of the current context in the context list.\n * @param keySchema - Schema for given key for FHIRcast event.\n * @param keysSeen - Set of keys seen so far. Used to prevent duplicate keys.\n */\nfunction validateFhircastContext<EventName extends FhircastEventName = FhircastEventName>(\n event: EventName,\n context: FhircastEventContext<EventName>,\n i: number,\n keySchema: FhircastEventContextDetails,\n keysSeen: Map<FhircastEventContext<EventName>['key'], number>\n): void {\n keysSeen.set(context.key, (keysSeen.get(context.key) ?? 0) + 1);\n\n if (keySchema.reference) {\n if (keySchema.array) {\n // Validate multi reference (namely `DiagnosticReport-select`.select)\n if (\n !(\n typeof (context as FhircastMultiReferenceContext).reference === 'object' &&\n Array.isArray((context as FhircastMultiReferenceContext).reference)\n )\n ) {\n throw new OperationOutcomeError(\n validationError(`context[${i}] is invalid. Expected key '${context.key}' to be an array of references.`)\n );\n }\n for (const reference of (context as FhircastMultiReferenceContext).reference) {\n if (!isReference(reference)) {\n throw new OperationOutcomeError(\n validationError(\n `context[${i}] is invalid. Expected key '${context.key}' to be an array of valid references.`\n )\n );\n }\n }\n } else if (!isReference((context as FhircastSingleReferenceContext).reference)) {\n // Validate single reference\n throw new OperationOutcomeError(\n validationError(`context[${i}] is invalid. Expected key '${context.key}' to be a reference.`)\n );\n }\n } else {\n validateSingleResourceContext(event, (context as FhircastResourceContext).resource, i, keySchema);\n }\n}\n\n/**\n * Throws if any context in the given array of contexts is invalid.\n *\n * @param event - The `FHIRcast` event name associated with the provided contexts.\n * @param contexts - The `FHIRcast` event contexts to validate.\n */\nfunction validateFhircastContexts<EventName extends FhircastEventName>(\n event: EventName,\n contexts: FhircastEventContext<EventName>[]\n): void {\n const keysSeen = new Map<FhircastEventContext['key'], number>();\n const eventSchema = FHIRCAST_EVENT_RESOURCES[event] as Record<\n FhircastEventContext['key'],\n FhircastEventContextDetails\n >;\n for (let i = 0; i < contexts.length; i++) {\n const key = contexts[i].key as FhircastEventContext['key'];\n if (!eventSchema[key]) {\n throw new OperationOutcomeError(\n validationError(`Key '${key}' not found for event '${event}'. Make sure to add only valid keys.`)\n );\n }\n validateFhircastContext(event, contexts[i], i, eventSchema[key], keysSeen);\n }\n // Iterate each key, if conditions for keys are not met as confirmed by `keysSeen` map, throw an error\n for (const [key, details] of Object.entries(eventSchema) as [\n FhircastEventContext['key'],\n FhircastEventContextDetails,\n ][]) {\n // If not optional and not keysSeen.has(key), throw\n if (!(details.optional || keysSeen.has(key))) {\n throw new OperationOutcomeError(\n validationError(`Missing required key '${key}' on context for '${event}' event.`)\n );\n }\n // If not multiple allowed and keySeen.get(key) > 1, throw\n if (!details.manyAllowed && (keysSeen.get(key) || 0) > 1) {\n throw new OperationOutcomeError(\n validationError(\n `${keysSeen.get(\n key\n )} context entries with key '${key}' found for the '${event}' event when schema only allows for 1.`\n )\n );\n }\n }\n}\n\n/**\n * Creates a serializable JSON payload for the `FHIRcast` protocol\n *\n * @param topic - The topic that this message will be published on. Usually a UUID.\n * @param event - The event name, ie. \"Patient-open\" or \"Patient-close\".\n * @param context - The updated context, containing new versions of resources related to this event.\n * @param versionId - The current `versionId` of the anchor context. For example, in `DiagnosticReport-update`, it's the `versionId` of the `DiagnosticReport`.\n * @returns A serializable `FhircastMessagePayload`.\n */\nexport function createFhircastMessagePayload<EventName extends FhircastEventVersionOptional>(\n topic: string,\n event: EventName,\n context: FhircastEventContext<EventName> | FhircastEventContext<EventName>[],\n versionId?: never\n): FhircastMessagePayload<EventName>;\n\nexport function createFhircastMessagePayload<EventName extends FhircastEventVersionRequired>(\n topic: string,\n event: EventName,\n context: FhircastEventContext<EventName> | FhircastEventContext<EventName>[],\n versionId: string\n): FhircastMessagePayload<EventName>;\n\nexport function createFhircastMessagePayload<\n EventName extends FhircastEventVersionOptional | FhircastEventVersionRequired,\n>(\n topic: string,\n event: EventName,\n context: FhircastEventContext<EventName> | FhircastEventContext<EventName>[],\n versionId?: string\n): FhircastMessagePayload<EventName> {\n if (!(topic && typeof topic === 'string')) {\n throw new OperationOutcomeError(validationError('Must provide a topic.'));\n }\n if (!FHIRCAST_EVENT_NAMES[event]) {\n throw new OperationOutcomeError(\n validationError(\n `Must provide a valid FHIRcast event name. Supported events: ${Object.keys(FHIRCAST_EVENT_NAMES).join(', ')}`\n )\n );\n }\n if (typeof context !== 'object') {\n throw new OperationOutcomeError(validationError('context must be a context object or array of context objects.'));\n }\n if ((FHIRCAST_EVENT_VERSION_REQUIRED as readonly string[]).includes(event) && !versionId) {\n throw new OperationOutcomeError(validationError(`The '${event}' event must contain a 'context.versionId'.`));\n }\n const normalizedContexts = Array.isArray(context) ? context : [context];\n // This will throw if any context in the array is invalid\n validateFhircastContexts(event, normalizedContexts);\n return {\n timestamp: new Date().toISOString(),\n id: generateId(),\n event: {\n 'hub.topic': topic,\n 'hub.event': event,\n context: normalizedContexts,\n ...(versionId ? { 'context.versionId': versionId } : {}),\n },\n };\n}\n\nexport type FhircastConnectEvent = { type: 'connect' };\nexport type FhircastMessageEvent = { type: 'message'; payload: FhircastMessagePayload };\nexport type FhircastDisconnectEvent = { type: 'disconnect' };\n\nexport type FhircastSubscriptionEventMap = {\n connect: FhircastConnectEvent;\n message: FhircastMessageEvent;\n disconnect: FhircastDisconnectEvent;\n};\n\n/**\n * A class representing a `FHIRcast` connection.\n *\n * `FhircastConnection` extends `EventTarget` and emits 3 lifecycle events:\n * 1. `connect` - An event to signal when a WebSocket connection has been opened. Fired as soon as a WebSocket emits `open`.\n * 2. `message` - Contains a `payload` field containing a `FHIRcast` message payload exactly as it comes in over WebSockets.\n * 3. `disconnect` - An event to signal when a WebSocket connection has been closed. Fired as soon as a WebSocket emits `close`.\n *\n * To close the connection, call `connection.disconnect()` and listen to the `disconnect` event to know when the connection has been disconnected.\n */\nexport class FhircastConnection extends TypedEventTarget<FhircastSubscriptionEventMap> {\n subRequest: SubscriptionRequest;\n private websocket: WebSocket;\n\n /**\n * Creates a new `FhircastConnection`.\n * @param subRequest - The subscription request to initialize the connection from.\n */\n constructor(subRequest: SubscriptionRequest) {\n super();\n this.subRequest = subRequest;\n if (!subRequest.endpoint) {\n throw new OperationOutcomeError(validationError('Subscription request should contain an endpoint.'));\n }\n if (!validateFhircastSubscriptionRequest(subRequest)) {\n throw new OperationOutcomeError(validationError('Subscription request failed validation.'));\n }\n const websocket = new WebSocket(subRequest.endpoint);\n websocket.addEventListener('open', () => {\n this.dispatchEvent({ type: 'connect' });\n\n websocket.addEventListener('message', (event: MessageEvent) => {\n const message = JSON.parse(event.data) as Record<string, string | object>;\n\n // This is a check for `subscription request confirmations`, we just discard these for now\n if (message['hub.topic']) {\n return;\n }\n\n const fhircastMessage = message as unknown as FhircastMessagePayload;\n // Don't bubble up heartbeats, they are just noise\n if (fhircastMessage.event['hub.event'] === ('heartbeat' as unknown as FhircastEventName)) {\n return;\n }\n this.dispatchEvent({ type: 'message', payload: fhircastMessage });\n\n websocket.send(\n JSON.stringify({\n id: message?.id,\n timestamp: new Date().toISOString(),\n })\n );\n });\n\n websocket.addEventListener('close', () => {\n this.dispatchEvent({ type: 'disconnect' });\n });\n });\n this.websocket = websocket;\n }\n\n disconnect(): void {\n this.websocket.close();\n }\n}\n", "import { decodeBase64 } from './base64';\n\n/**\n * Decodes a section of a JWT.\n * See: https://tools.ietf.org/html/rfc7519\n * @param payload - The JWT payload string.\n * @returns Collection of key value claims in the JWT payload.\n */\nfunction decodePayload(payload: string): Record<string, number | string> {\n const cleanedPayload = payload.replace(/-/g, '+').replace(/_/g, '/');\n const decodedPayload = decodeBase64(cleanedPayload);\n const uriEncodedPayload = Array.from(decodedPayload).reduce((acc, char) => {\n const uriEncodedChar = ('00' + char.charCodeAt(0).toString(16)).slice(-2);\n return `${acc}%${uriEncodedChar}`;\n }, '');\n const jsonPayload = decodeURIComponent(uriEncodedPayload);\n return JSON.parse(jsonPayload);\n}\n\n/**\n * Returns true if the token is a JWT.\n * @param token - The potential JWT token.\n * @returns True if the token is a JWT.\n */\nexport function isJwt(token: string): boolean {\n return token.split('.').length === 3;\n}\n\n/**\n * Parses the JWT payload.\n * @param token - JWT token.\n * @returns Collection of key value claims in the JWT payload.\n */\nexport function parseJWTPayload(token: string): Record<string, number | string> {\n const [_header, payload, _signature] = token.split('.');\n return decodePayload(payload);\n}\n\n/**\n * Returns true if the access token was issued by a Medplum server.\n * @param accessToken - An access token of unknown origin.\n * @returns True if the access token was issued by a Medplum server.\n */\nexport function isMedplumAccessToken(accessToken: string): boolean {\n try {\n const payload = parseJWTPayload(accessToken);\n return typeof payload.login_id === 'string';\n } catch (_err) {\n return false;\n }\n}\n\n/**\n * Returns the JWT expiration time in number of milliseconds elapsed since the epoch.\n * @param token - The JWT token.\n * @returns The JWT expiration time in number of milliseconds elapsed since the epoch if available, undefined if unknown.\n */\nexport function tryGetJwtExpiration(token: string): number | undefined {\n try {\n const payload = parseJWTPayload(token);\n const exp = payload.exp;\n if (typeof exp === 'number') {\n return exp * 1000;\n }\n return undefined;\n } catch (_err) {\n return undefined;\n }\n}\n", "import { MedplumClient } from './client';\nimport { ContentType } from './contenttype';\n\nexport class MedplumKeyValueClient {\n constructor(readonly medplum: MedplumClient) {}\n\n /**\n * Gets the value for the given key from the keyvalue store.\n * @param key - The key to get the value for.\n * @returns The value for the given key.\n */\n async get(key: string): Promise<string | undefined> {\n return this.medplum.get(`keyvalue/v1/${key}`);\n }\n\n /**\n * Sets the value for the given key in the keyvalue store.\n * @param key - The key to set the value for.\n * @param value - The value to set.\n */\n async set(key: string, value: string): Promise<void> {\n await this.medplum.put(`keyvalue/v1/${key}`, value, ContentType.TEXT);\n }\n\n /**\n * Deletes the value for the given key from the keyvalue store.\n * @param key - The key to delete the value for.\n */\n async delete(key: string): Promise<void> {\n await this.medplum.delete(`keyvalue/v1/${key}`);\n }\n}\n", "/**\n * The ReadablePromise class wraps a request promise suitable for React Suspense.\n * See: https://blog.logrocket.com/react-suspense-data-fetching/#wrappromise-js\n * See: https://github.com/ovieokeh/suspense-data-fetching/blob/master/lib/api/wrapPromise.js\n */\nexport class ReadablePromise<T> implements Promise<T> {\n readonly [Symbol.toStringTag]: string = 'ReadablePromise';\n private suspender: Promise<T>;\n private status: 'pending' | 'error' | 'success' = 'pending';\n private response: T | undefined;\n private error: Error | undefined;\n\n constructor(requestPromise: Promise<T>) {\n this.suspender = requestPromise.then(\n (res: T) => {\n this.status = 'success';\n this.response = res;\n return res;\n },\n (err: any) => {\n this.status = 'error';\n this.error = err;\n throw err;\n }\n );\n }\n\n /**\n * Returns true if the promise is pending.\n * @returns True if the Promise is pending.\n */\n isPending(): boolean {\n return this.status === 'pending';\n }\n\n /**\n * Returns true if the promise resolved successfully.\n * @returns True if the Promise resolved successfully.\n */\n isOk(): boolean {\n return this.status === 'success';\n }\n\n /**\n * Attempts to read the value of the promise.\n * If the promise is pending, this method will throw a promise.\n * If the promise rejected, this method will throw the rejection reason.\n * If the promise resolved, this method will return the resolved value.\n * @returns The resolved value of the Promise.\n */\n read(): T {\n switch (this.status) {\n case 'pending':\n throw this.suspender;\n case 'error':\n throw this.error;\n default:\n return this.response as T;\n }\n }\n\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled - The callback to execute when the Promise is resolved.\n * @param onrejected - The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then<TResult1 = T, TResult2 = never>(\n onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.suspender.then(onfulfilled, onrejected);\n }\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected - The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<T | TResult> {\n return this.suspender.catch(onrejected);\n }\n\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally - The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | null): Promise<T> {\n return this.suspender.finally(onfinally);\n }\n}\n", "import { stringify } from './utils';\n\nexport interface IClientStorage {\n getInitPromise?(): Promise<void>;\n clear(): void;\n getString(key: string): string | undefined;\n setString(key: string, value: string | undefined): void;\n getObject<T>(key: string): T | undefined;\n setObject<T>(key: string, value: T): void;\n}\n\n/**\n * The ClientStorage class is a utility class for storing strings and objects.\n *\n * When using MedplumClient in the browser, it will be backed by browser localStorage.\n *\n * When Using MedplumClient in the server, it will be backed by the MemoryStorage class. For example, the Medplum CLI uses `FileSystemStorage`.\n */\nexport class ClientStorage implements IClientStorage {\n private readonly storage: Storage;\n\n constructor(storage?: Storage) {\n this.storage = storage ?? (typeof localStorage !== 'undefined' ? localStorage : new MemoryStorage());\n }\n\n clear(): void {\n this.storage.clear();\n }\n\n getString(key: string): string | undefined {\n return this.storage.getItem(key) ?? undefined;\n }\n\n setString(key: string, value: string | undefined): void {\n if (value) {\n this.storage.setItem(key, value);\n } else {\n this.storage.removeItem(key);\n }\n }\n\n getObject<T>(key: string): T | undefined {\n const str = this.getString(key);\n return str ? (JSON.parse(str) as T) : undefined;\n }\n\n setObject<T>(key: string, value: T): void {\n this.setString(key, value ? stringify(value) : undefined);\n }\n}\n\n/**\n * The MemoryStorage class is a minimal in-memory implementation of the Storage interface.\n */\nexport class MemoryStorage implements Storage {\n private data: Map<string, string>;\n\n constructor() {\n this.data = new Map<string, string>();\n }\n\n /**\n * Returns the number of key/value pairs.\n * @returns The number of key/value pairs.\n */\n get length(): number {\n return this.data.size;\n }\n\n /**\n * Removes all key/value pairs, if there are any.\n */\n clear(): void {\n this.data.clear();\n }\n\n /**\n * Returns the current value associated with the given key, or null if the given key does not exist.\n * @param key - The specified storage key.\n * @returns The current value associated with the given key, or null if the given key does not exist.\n */\n getItem(key: string): string | null {\n return this.data.get(key) ?? null;\n }\n\n /**\n * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.\n * @param key - The storage key.\n * @param value - The new value.\n */\n setItem(key: string, value: string | null): void {\n if (value) {\n this.data.set(key, value);\n } else {\n this.data.delete(key);\n }\n }\n\n /**\n * Removes the key/value pair with the given key, if a key/value pair with the given key exists.\n * @param key - The storage key.\n */\n removeItem(key: string): void {\n this.data.delete(key);\n }\n\n /**\n * Returns the name of the nth key, or null if n is greater than or equal to the number of key/value pairs.\n * @param index - The numeric index.\n * @returns The nth key.\n */\n key(index: number): string | null {\n return Array.from(this.data.keys())[index];\n }\n}\n\n/**\n * The MockAsyncClientStorage class is a mock implementation of the ClientStorage class.\n * This can be used for testing async initialization of the MedplumClient.\n */\nexport class MockAsyncClientStorage extends ClientStorage implements IClientStorage {\n private initialized: boolean;\n private initPromise: Promise<void>;\n private initResolve: () => void = () => undefined;\n\n constructor() {\n super();\n this.initialized = false;\n this.initPromise = new Promise((resolve) => {\n this.initResolve = resolve;\n });\n }\n\n setInitialized(): void {\n if (!this.initialized) {\n this.initResolve();\n this.initialized = true;\n }\n }\n\n getInitPromise(): Promise<void> {\n return this.initPromise;\n }\n\n get isInitialized(): boolean {\n return this.initialized;\n }\n}\n", "import { TypedEventTarget } from '../eventtarget';\n\n/*!\n * Reconnecting WebSocket\n * by Pedro Ladaria <pedro.ladaria@gmail.com>\n * https://github.com/pladaria/reconnecting-websocket\n * License MIT\n *\n * Copy of \"partysocket\" from Partykit team, a fork of the original \"Reconnecting WebSocket\"\n * https://github.com/partykit/partykit/blob/main/packages/partysocket\n */\n\nexport interface IReconnectingWebSocket extends TypedEventTarget<WebSocketEventMap> {\n readyState: number;\n close(code?: number, reason?: string): void;\n send(message: string): void;\n reconnect(code?: number, reason?: string): void;\n}\n\nexport interface IReconnectingWebSocketCtor {\n new (url: string, protocols?: ProtocolsProvider, options?: Options): IReconnectingWebSocket;\n}\n\nexport interface ErrorEvent extends globalThis.Event {\n message: string;\n error: Error;\n}\n\nexport interface CloseEvent extends globalThis.Event {\n code: number;\n reason: string;\n wasClean: boolean;\n}\n\nexport type WebSocketEventMap = {\n close: CloseEvent;\n error: ErrorEvent;\n message: MessageEvent;\n open: Event;\n};\n\n/**\n * This map exists separately from `WebSocketEventMap`, which is the actual event map used for the `ReconnectingWebSocket` class itself,\n * due to slight difference in the type between the events as we use them, and the events as they exist as global interfaces. We need the global interfaces\n * to be generic enough to satisfy conformant implementations that don't exactly match the events we export and use in `ReconnectingWebSocket` itself.\n */\nexport type IWebSocketEventMap = {\n close: globalThis.CloseEvent;\n error: globalThis.ErrorEvent;\n message: globalThis.MessageEvent;\n open: Event;\n};\n\n/**\n * Generic interface that an implementation of `WebSocket` must satisfy to be used with `ReconnectingWebSocket`.\n * This is a slightly modified fork of the `WebSocket` global type used in Node.\n *\n * The main key difference is making all the `onclose`, `onerror`, etc. functions have `any[]` args, making `data` in `send()` of type `any`, and making `binaryType` of type string,\n * though the particular implementation should narrow each of these implementation-specific types.\n */\nexport interface IWebSocket {\n binaryType: string;\n\n readonly bufferedAmount: number;\n readonly extensions: string;\n\n onclose: ((...args: any[]) => any) | null;\n onerror: ((...args: any[]) => any) | null;\n onmessage: ((...args: any[]) => any) | null;\n onopen: ((...args: any[]) => any) | null;\n\n readonly protocol: string;\n readonly readyState: number;\n readonly url: string;\n\n close(code?: number, reason?: string): void;\n send(data: any): void;\n\n readonly CLOSED: number;\n readonly CLOSING: number;\n readonly CONNECTING: number;\n readonly OPEN: number;\n\n addEventListener<K extends keyof WebSocketEventMap>(\n type: K,\n listener: (ev: WebSocketEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions\n ): void;\n removeEventListener<K extends keyof WebSocketEventMap>(\n type: K,\n listener: (ev: WebSocketEventMap[K]) => any,\n options?: boolean | EventListenerOptions\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions\n ): void;\n}\n\nconst Events = {\n Event: (typeof globalThis.Event !== 'undefined' ? globalThis.Event : undefined) as\n | typeof globalThis.Event\n | undefined,\n ErrorEvent: undefined as any,\n CloseEvent: undefined as any,\n};\n\nlet eventsInitialized = false;\n\nfunction lazyInitEvents(): void {\n if (typeof globalThis.Event === 'undefined') {\n throw new Error('Unable to lazy init events for ReconnectingWebSocket. globalThis.Event is not defined yet');\n }\n\n Events.Event = globalThis.Event;\n\n Events.ErrorEvent = class ErrorEvent extends Event implements ErrorEvent {\n public message: string;\n public error: Error;\n constructor(error: Error, target: any) {\n super('error', target);\n this.message = error.message;\n this.error = error;\n }\n } as unknown as typeof globalThis.ErrorEvent;\n\n Events.CloseEvent = class CloseEvent extends Event implements CloseEvent {\n public code: number;\n public reason: string;\n public wasClean = true;\n // eslint-disable-next-line default-param-last\n constructor(code = 1000, reason = '', target: any) {\n super('close', target);\n this.code = code;\n this.reason = reason;\n }\n } as unknown as typeof globalThis.CloseEvent;\n}\n\nexport function assert(condition: unknown, msg?: string): asserts condition {\n if (!condition) {\n throw new Error(msg);\n }\n}\n\nfunction cloneEvent(e: Event): Event {\n return new (e as any).constructor(e.type, e);\n}\n\nexport type Options<WS extends IWebSocket = WebSocket> = {\n WebSocket?: any;\n binaryType?: WS['binaryType'];\n maxReconnectionDelay?: number;\n minReconnectionDelay?: number;\n reconnectionDelayGrowFactor?: number;\n minUptime?: number;\n connectionTimeout?: number;\n maxRetries?: number;\n maxEnqueuedMessages?: number;\n startClosed?: boolean;\n debug?: boolean;\n debugLogger?: (...args: any[]) => void;\n};\n\nconst DEFAULT = {\n maxReconnectionDelay: 10000,\n minReconnectionDelay: 1000 + Math.random() * 4000,\n minUptime: 5000,\n reconnectionDelayGrowFactor: 1.3,\n connectionTimeout: 4000,\n maxRetries: Infinity,\n maxEnqueuedMessages: Infinity,\n startClosed: false,\n debug: false,\n};\n\nexport type ProtocolsProvider = null | string | string[];\n\nexport type Message = string | ArrayBuffer | Blob | ArrayBufferView;\n\nlet didWarnAboutMissingWebSocket = false;\n\nexport class ReconnectingWebSocket<WS extends IWebSocket = WebSocket>\n extends TypedEventTarget<WebSocketEventMap>\n implements IReconnectingWebSocket\n{\n private _ws: IWebSocket | undefined;\n private _retryCount = -1;\n private _uptimeTimeout: ReturnType<typeof setTimeout> | undefined;\n private _connectTimeout: ReturnType<typeof setTimeout> | undefined;\n private _shouldReconnect = true;\n private _connectLock = false;\n private _binaryType: WS['binaryType'];\n private _closeCalled = false;\n private _messageQueue: Message[] = [];\n\n private _debugLogger = console.log.bind(console);\n\n protected _url: string;\n protected _protocols?: ProtocolsProvider;\n protected _options: Options<WS>;\n\n constructor(url: string, protocols?: ProtocolsProvider, options: Options<WS> = {}) {\n // Initialize all events if they haven't been created yet\n if (!eventsInitialized) {\n lazyInitEvents();\n eventsInitialized = true;\n }\n\n super();\n this._url = url;\n this._protocols = protocols;\n this._options = options;\n if (this._options.startClosed) {\n this._shouldReconnect = false;\n }\n if (this._options.binaryType) {\n this._binaryType = this._options.binaryType;\n } else {\n this._binaryType = 'blob';\n }\n if (this._options.debugLogger) {\n this._debugLogger = this._options.debugLogger;\n }\n this._connect();\n }\n\n static get CONNECTING(): number {\n return 0;\n }\n static get OPEN(): number {\n return 1;\n }\n static get CLOSING(): number {\n return 2;\n }\n static get CLOSED(): number {\n return 3;\n }\n\n get CONNECTING(): number {\n return ReconnectingWebSocket.CONNECTING;\n }\n get OPEN(): number {\n return ReconnectingWebSocket.OPEN;\n }\n get CLOSING(): number {\n return ReconnectingWebSocket.CLOSING;\n }\n get CLOSED(): number {\n return ReconnectingWebSocket.CLOSED;\n }\n\n get binaryType(): WS['binaryType'] {\n return this._ws ? this._ws.binaryType : this._binaryType;\n }\n\n set binaryType(value: WS['binaryType']) {\n this._binaryType = value;\n if (this._ws) {\n this._ws.binaryType = value;\n }\n }\n\n /**\n * @returns The number or connection retries.\n */\n get retryCount(): number {\n return Math.max(this._retryCount, 0);\n }\n\n /**\n * @returns The number of bytes of data that have been queued using calls to send() but not yet\n * transmitted to the network. This value resets to zero once all queued data has been sent.\n * This value does not reset to zero when the connection is closed; if you keep calling send(),\n * this will continue to climb. Read only\n *\n */\n get bufferedAmount(): number {\n const bytes = this._messageQueue.reduce((acc, message) => {\n if (typeof message === 'string') {\n acc += message.length; // not byte size\n } else if (message instanceof Blob) {\n acc += message.size;\n } else {\n acc += message.byteLength;\n }\n return acc;\n }, 0);\n return bytes + (this._ws?.bufferedAmount ?? 0);\n }\n\n /**\n * @returns The extensions selected by the server. This is currently only the empty string or a list of\n * extensions as negotiated by the connection\n */\n get extensions(): string {\n return this._ws?.extensions ?? '';\n }\n\n /**\n * @returns A string indicating the name of the sub-protocol the server selected;\n * this will be one of the strings specified in the protocols parameter when creating the\n * WebSocket object.\n */\n get protocol(): string {\n return this._ws?.protocol ?? '';\n }\n\n /**\n * @returns The current state of the connection; this is one of the Ready state constants.\n */\n get readyState(): number {\n if (this._ws) {\n return this._ws.readyState;\n }\n return this._options.startClosed ? ReconnectingWebSocket.CLOSED : ReconnectingWebSocket.CONNECTING;\n }\n\n /**\n * @returns The URL as resolved by the constructor.\n */\n get url(): string {\n return this._ws ? this._ws.url : '';\n }\n\n /**\n * @returns Whether the websocket object is now in reconnectable state.\n */\n get shouldReconnect(): boolean {\n return this._shouldReconnect;\n }\n\n /**\n * An event listener to be called when the WebSocket connection's readyState changes to CLOSED\n */\n public onclose: ((event: CloseEvent) => void) | null = null;\n\n /**\n * An event listener to be called when an error occurs\n */\n public onerror: ((event: ErrorEvent) => void) | null = null;\n\n /**\n * An event listener to be called when a message is received from the server\n */\n public onmessage: ((event: MessageEvent) => void) | null = null;\n\n /**\n * An event listener to be called when the WebSocket connection's readyState changes to OPEN;\n * this indicates that the connection is ready to send and receive data\n */\n public onopen: ((event: Event) => void) | null = null;\n\n /**\n * Closes the WebSocket connection or connection attempt, if any. If the connection is already\n * CLOSED, this method does nothing\n * @param code - The code to close with. Default is 1000.\n * @param reason - An optional reason for closing the connection.\n */\n // eslint-disable-next-line default-param-last\n public close(code = 1000, reason?: string): void {\n this._closeCalled = true;\n this._shouldReconnect = false;\n this._clearTimeouts();\n if (!this._ws) {\n this._debug('close enqueued: no ws instance');\n return;\n }\n if (this._ws.readyState === this.CLOSED) {\n this._debug('close: already closed');\n return;\n }\n this._ws.close(code, reason);\n }\n\n /**\n * Closes the WebSocket connection or connection attempt and connects again.\n * Resets retry counter;\n * @param code - The code to disconnect with. Default is 1000.\n * @param reason - An optional reason for disconnecting the connection.\n */\n public reconnect(code?: number, reason?: string): void {\n this._shouldReconnect = true;\n this._closeCalled = false;\n this._retryCount = -1;\n if (!this._ws || this._ws.readyState === this.CLOSED) {\n this._connect();\n } else {\n this._disconnect(code, reason);\n this._connect();\n }\n }\n\n /**\n * Enqueue specified data to be transmitted to the server over the WebSocket connection\n * @param data - The data to enqueue.\n */\n public send(data: Message): void {\n if (this._ws && this._ws.readyState === this.OPEN) {\n this._debug('send', data);\n this._ws.send(data);\n } else {\n const { maxEnqueuedMessages = DEFAULT.maxEnqueuedMessages } = this._options;\n if (this._messageQueue.length < maxEnqueuedMessages) {\n this._debug('enqueue', data);\n this._messageQueue.push(data);\n }\n }\n }\n\n private _debug(...args: unknown[]): void {\n if (this._options.debug) {\n this._debugLogger('RWS>', ...args);\n }\n }\n\n private _getNextDelay(): number {\n const {\n reconnectionDelayGrowFactor = DEFAULT.reconnectionDelayGrowFactor,\n minReconnectionDelay = DEFAULT.minReconnectionDelay,\n maxReconnectionDelay = DEFAULT.maxReconnectionDelay,\n } = this._options;\n let delay = 0;\n if (this._retryCount > 0) {\n delay = minReconnectionDelay * Math.pow(reconnectionDelayGrowFactor, this._retryCount - 1);\n if (delay > maxReconnectionDelay) {\n delay = maxReconnectionDelay;\n }\n }\n this._debug('next delay', delay);\n return delay;\n }\n\n private _wait(): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, this._getNextDelay());\n });\n }\n\n private _connect(): void {\n if (this._connectLock || !this._shouldReconnect) {\n return;\n }\n this._connectLock = true;\n\n const { maxRetries = DEFAULT.maxRetries, connectionTimeout = DEFAULT.connectionTimeout } = this._options;\n\n if (this._retryCount >= maxRetries) {\n this._debug('max retries reached', this._retryCount, '>=', maxRetries);\n return;\n }\n\n this._retryCount++;\n\n this._debug('connect', this._retryCount);\n this._removeListeners();\n\n this._wait()\n .then(() => {\n // close could be called before creating the ws\n if (this._closeCalled) {\n this._connectLock = false;\n return;\n }\n if (!this._options.WebSocket && typeof WebSocket === 'undefined' && !didWarnAboutMissingWebSocket) {\n console.error('\u203C\uFE0F No WebSocket implementation available. You should define options.WebSocket.');\n didWarnAboutMissingWebSocket = true;\n }\n const WS: typeof WebSocket = this._options.WebSocket || WebSocket;\n this._debug('connect', { url: this._url, protocols: this._protocols });\n this._ws = this._protocols ? new WS(this._url, this._protocols) : new WS(this._url);\n\n this._ws.binaryType = this._binaryType;\n this._connectLock = false;\n this._addListeners();\n\n this._connectTimeout = setTimeout(() => this._handleTimeout(), connectionTimeout);\n })\n // via https://github.com/pladaria/reconnecting-websocket/pull/166\n .catch((err) => {\n this._connectLock = false;\n this._handleError(new Events.ErrorEvent(Error(err.message), this));\n });\n }\n\n private _handleTimeout(): void {\n this._debug('timeout event');\n this._handleError(new Events.ErrorEvent(Error('TIMEOUT'), this));\n }\n\n // eslint-disable-next-line default-param-last\n private _disconnect(code = 1000, reason?: string): void {\n this._clearTimeouts();\n if (!this._ws) {\n return;\n }\n this._removeListeners();\n try {\n this._ws.close(code, reason);\n this._handleClose(new Events.CloseEvent(code, reason, this));\n } catch (_error) {\n // ignore\n }\n }\n\n private _acceptOpen(): void {\n this._debug('accept open');\n this._retryCount = 0;\n }\n\n private _handleOpen = (event: Event): void => {\n this._debug('open event');\n const { minUptime = DEFAULT.minUptime } = this._options;\n\n clearTimeout(this._connectTimeout);\n this._uptimeTimeout = setTimeout(() => this._acceptOpen(), minUptime);\n\n assert(this._ws, 'WebSocket is not defined');\n\n this._ws.binaryType = this._binaryType;\n\n // send enqueued messages (messages sent before websocket open event)\n this._messageQueue.forEach((message) => this._ws?.send(message));\n this._messageQueue = [];\n\n if (this.onopen) {\n this.onopen(event);\n }\n this.dispatchEvent(cloneEvent(event));\n };\n\n private _handleMessage = (event: MessageEvent): void => {\n this._debug('message event');\n\n if (this.onmessage) {\n this.onmessage(event);\n }\n this.dispatchEvent(cloneEvent(event));\n };\n\n private _handleError = (event: ErrorEvent): void => {\n this._debug('error event', event.message);\n this._disconnect(undefined, event.message === 'TIMEOUT' ? 'timeout' : undefined);\n\n if (this.onerror) {\n this.onerror(event);\n }\n this._debug('exec error listeners');\n this.dispatchEvent(cloneEvent(event));\n\n this._connect();\n };\n\n private _handleClose = (event: CloseEvent): void => {\n this._debug('close event');\n this._clearTimeouts();\n\n if (this._shouldReconnect) {\n this._connect();\n }\n\n if (this.onclose) {\n this.onclose(event);\n }\n this.dispatchEvent(cloneEvent(event));\n };\n\n private _removeListeners(): void {\n if (!this._ws) {\n return;\n }\n this._debug('removeListeners');\n this._ws.removeEventListener('open', this._handleOpen);\n this._ws.removeEventListener('close', this._handleClose);\n this._ws.removeEventListener('message', this._handleMessage);\n this._ws.removeEventListener('error', this._handleError);\n }\n\n private _addListeners(): void {\n if (!this._ws) {\n return;\n }\n this._debug('addListeners');\n this._ws.addEventListener('open', this._handleOpen);\n this._ws.addEventListener('close', this._handleClose);\n this._ws.addEventListener('message', this._handleMessage);\n this._ws.addEventListener('error', this._handleError);\n }\n\n private _clearTimeouts(): void {\n clearTimeout(this._connectTimeout);\n clearTimeout(this._uptimeTimeout);\n }\n}\n", "import { Bundle, Parameters, Resource, Subscription, SubscriptionStatus } from '@medplum/fhirtypes';\nimport { MedplumClient } from '../client';\nimport { TypedEventTarget } from '../eventtarget';\nimport { evalFhirPathTyped } from '../fhirpath/parse';\nimport { toTypedValue } from '../fhirpath/utils';\nimport { Logger } from '../logger';\nimport { normalizeErrorString, OperationOutcomeError, serverError, validationError } from '../outcomes';\nimport { matchesSearchRequest } from '../search/match';\nimport { parseSearchRequest } from '../search/search';\nimport { deepEquals, getExtension, getReferenceString, ProfileResource, resolveId } from '../utils';\nimport {\n IReconnectingWebSocket,\n IReconnectingWebSocketCtor,\n ReconnectingWebSocket,\n} from '../websockets/reconnecting-websocket';\n\nconst DEFAULT_PING_INTERVAL_MS = 5_000;\n\nexport type SubscriptionEventMap = {\n connect: { type: 'connect'; payload: { subscriptionId: string } };\n disconnect: { type: 'disconnect'; payload: { subscriptionId: string } };\n error: { type: 'error'; payload: Error };\n message: { type: 'message'; payload: Bundle };\n open: { type: 'open' };\n close: { type: 'close' };\n heartbeat: { type: 'heartbeat'; payload: Bundle };\n};\n\n/**\n * An `EventTarget` that emits events when new subscription notifications come in over WebSockets.\n *\n * -----\n *\n * ### Events emitted:\n *\n * - `connect` - A new subscription is connected to the `SubscriptionManager` and `message` events for this subscription can be expected.\n * - `disconnect` - The specified subscription is no longer being monitored by the `SubscriptionManager`.\n * - `error` - An error has occurred.\n * - `message` - A message containing a notification `Bundle` has been received.\n * - `open` - The WebSocket has been opened.\n * - `close` - The WebSocket has been closed.\n * - `heartbeat` - A `heartbeat` message has been received.\n */\nexport class SubscriptionEmitter extends TypedEventTarget<SubscriptionEventMap> {\n private criteria: Set<string>;\n constructor(...criteria: string[]) {\n super();\n this.criteria = new Set(criteria);\n }\n getCriteria(): Set<string> {\n return this.criteria;\n }\n /**\n * @internal\n * @param criteria - The criteria to add to this `SubscriptionEmitter`.\n */\n _addCriteria(criteria: string): void {\n this.criteria.add(criteria);\n }\n /**\n * @internal\n * @param criteria - The criteria to remove from this `SubscriptionEmitter`.\n */\n _removeCriteria(criteria: string): void {\n this.criteria.delete(criteria);\n }\n}\n\nclass CriteriaEntry {\n readonly criteria: string;\n readonly emitter: SubscriptionEmitter;\n refCount: number;\n readonly subscriptionProps?: Partial<Subscription>;\n subscriptionId?: string;\n token?: string;\n connecting = false;\n\n constructor(criteria: string, subscriptionProps?: Partial<Subscription>) {\n this.criteria = criteria;\n this.emitter = new SubscriptionEmitter(criteria);\n this.refCount = 1;\n this.subscriptionProps = subscriptionProps\n ? {\n ...subscriptionProps,\n }\n : undefined;\n }\n\n clearAttachedSubscription(): void {\n this.subscriptionId = undefined;\n this.token = undefined;\n }\n}\n\ntype CriteriaMapEntry = { bareCriteria?: CriteriaEntry; criteriaWithProps: CriteriaEntry[] };\n\nexport interface SubManagerOptions {\n ReconnectingWebSocket?: IReconnectingWebSocketCtor;\n pingIntervalMs?: number;\n debug?: boolean;\n debugLogger?: (...args: any[]) => void;\n}\n\nexport class SubscriptionManager {\n private readonly medplum: MedplumClient;\n private ws: IReconnectingWebSocket;\n private masterSubEmitter?: SubscriptionEmitter;\n private criteriaEntries: Map<string, CriteriaMapEntry>; // Map<criteriaStr, CriteriaMapEntry>\n private criteriaEntriesBySubscriptionId: Map<string, CriteriaEntry>; // Map<subscriptionId, CriteriaEntry>\n private wsClosed: boolean;\n private pingTimer: ReturnType<typeof setInterval> | undefined = undefined;\n private pingIntervalMs: number;\n private waitingForPong = false;\n private currentProfile: ProfileResource | undefined;\n\n constructor(medplum: MedplumClient, wsUrl: URL | string, options?: SubManagerOptions) {\n if (!(medplum instanceof MedplumClient)) {\n throw new OperationOutcomeError(validationError('First arg of constructor should be a `MedplumClient`'));\n }\n let url: string;\n try {\n url = new URL(wsUrl).toString();\n } catch (_err) {\n throw new OperationOutcomeError(validationError('Not a valid URL'));\n }\n const ws = options?.ReconnectingWebSocket\n ? new options.ReconnectingWebSocket(url, undefined, { debug: options?.debug, debugLogger: options?.debugLogger })\n : new ReconnectingWebSocket(url, undefined, { debug: options?.debug, debugLogger: options?.debugLogger });\n\n this.medplum = medplum;\n this.ws = ws;\n this.masterSubEmitter = new SubscriptionEmitter();\n this.criteriaEntries = new Map<string, CriteriaMapEntry>();\n this.criteriaEntriesBySubscriptionId = new Map<string, CriteriaEntry>();\n this.wsClosed = false;\n this.pingIntervalMs = options?.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;\n this.currentProfile = medplum.getProfile();\n\n this.setupListeners();\n }\n\n private setupListeners(): void {\n const ws = this.ws;\n\n ws.addEventListener('message', (event) => {\n try {\n const parsedData = JSON.parse(event.data) as { type: 'pong' } | Bundle;\n if (parsedData.type === 'pong') {\n this.waitingForPong = false;\n return;\n }\n const bundle = parsedData;\n // Get criteria for event\n const status = bundle?.entry?.[0]?.resource as SubscriptionStatus;\n\n // Handle heartbeat\n if (status.type === 'heartbeat') {\n this.masterSubEmitter?.dispatchEvent({ type: 'heartbeat', payload: bundle });\n return;\n }\n\n // Handle handshake\n if (status.type === 'handshake') {\n const subscriptionId = resolveId(status.subscription) as string;\n const connectEvent = {\n type: 'connect',\n payload: { subscriptionId },\n } as const;\n this.masterSubEmitter?.dispatchEvent(connectEvent);\n const criteriaEntry = this.criteriaEntriesBySubscriptionId.get(subscriptionId);\n if (!criteriaEntry) {\n console.warn('Received handshake for criteria the SubscriptionManager is not listening for yet');\n return;\n }\n criteriaEntry.connecting = false;\n criteriaEntry.emitter.dispatchEvent({ ...connectEvent });\n return;\n }\n\n this.masterSubEmitter?.dispatchEvent({ type: 'message', payload: bundle });\n const criteriaEntry = this.criteriaEntriesBySubscriptionId.get(resolveId(status.subscription) as string);\n if (!criteriaEntry) {\n console.warn('Received notification for criteria the SubscriptionManager is not listening for');\n return;\n }\n // Emit event for criteria\n criteriaEntry.emitter.dispatchEvent({ type: 'message', payload: bundle });\n } catch (err: unknown) {\n console.error(err);\n const errorEvent = { type: 'error', payload: err as Error } as SubscriptionEventMap['error'];\n this.masterSubEmitter?.dispatchEvent(errorEvent);\n for (const emitter of this.getAllCriteriaEmitters()) {\n emitter.dispatchEvent({ ...errorEvent });\n }\n }\n });\n\n ws.addEventListener('error', () => {\n const errorEvent = {\n type: 'error',\n payload: new OperationOutcomeError(serverError(new Error('WebSocket error'))),\n } as SubscriptionEventMap['error'];\n this.masterSubEmitter?.dispatchEvent(errorEvent);\n for (const emitter of this.getAllCriteriaEmitters()) {\n emitter.dispatchEvent({ ...errorEvent });\n }\n });\n\n ws.addEventListener('close', () => {\n const closeEvent = { type: 'close' } as SubscriptionEventMap['close'];\n this.masterSubEmitter?.dispatchEvent(closeEvent);\n for (const emitter of this.getAllCriteriaEmitters()) {\n emitter.dispatchEvent({ ...closeEvent });\n }\n\n if (this.pingTimer) {\n clearInterval(this.pingTimer);\n this.pingTimer = undefined;\n this.waitingForPong = false;\n }\n\n if (this.wsClosed) {\n this.criteriaEntries.clear();\n this.criteriaEntriesBySubscriptionId.clear();\n this.masterSubEmitter?.removeAllListeners();\n }\n });\n\n ws.addEventListener('open', () => {\n const openEvent = { type: 'open' } as SubscriptionEventMap['open'];\n this.masterSubEmitter?.dispatchEvent(openEvent);\n for (const emitter of this.getAllCriteriaEmitters()) {\n emitter.dispatchEvent({ ...openEvent });\n }\n // We do this after dispatching the events so listeners can check if this is the initial open or not\n // We are reconnecting\n // So we refresh all current subscriptions\n this.refreshAllSubscriptions().catch(console.error);\n\n if (!this.pingTimer) {\n this.pingTimer = setInterval(() => {\n if (this.waitingForPong) {\n this.waitingForPong = false;\n ws.reconnect();\n return;\n }\n ws.send(JSON.stringify({ type: 'ping' }));\n this.waitingForPong = true;\n }, this.pingIntervalMs);\n }\n });\n\n this.medplum.addEventListener('change', () => {\n const nextProfile = this.medplum.getProfile();\n if (this.currentProfile && nextProfile === undefined) {\n this.ws.close();\n } else if (nextProfile && this.currentProfile?.id !== nextProfile.id) {\n this.ws.reconnect();\n }\n this.currentProfile = nextProfile;\n });\n }\n\n private emitError(criteriaEntry: CriteriaEntry, error: Error): void {\n const errorEvent = { type: 'error', payload: error } as SubscriptionEventMap['error'];\n this.masterSubEmitter?.dispatchEvent(errorEvent);\n criteriaEntry.emitter.dispatchEvent({ ...errorEvent });\n }\n\n private maybeEmitDisconnect(criteriaEntry: CriteriaEntry): void {\n const { subscriptionId } = criteriaEntry;\n if (subscriptionId) {\n const disconnectEvent = {\n type: 'disconnect',\n payload: { subscriptionId },\n } as SubscriptionEventMap['disconnect'];\n // Emit disconnect on master\n this.masterSubEmitter?.dispatchEvent(disconnectEvent);\n // Emit disconnect on criteria emitter\n criteriaEntry.emitter.dispatchEvent({ ...disconnectEvent });\n } else {\n console.warn('Called disconnect for `CriteriaEntry` before `subscriptionId` was present.');\n }\n }\n\n private async getTokenForCriteria(criteriaEntry: CriteriaEntry): Promise<[string, string]> {\n let subscriptionId = criteriaEntry?.subscriptionId;\n if (!subscriptionId) {\n // Make a new subscription\n const subscription = await this.medplum.createResource<Subscription>({\n ...criteriaEntry.subscriptionProps,\n resourceType: 'Subscription',\n status: 'active',\n reason: `WebSocket subscription for ${getReferenceString(this.medplum.getProfile() as ProfileResource)}`,\n channel: { type: 'websocket' },\n criteria: criteriaEntry.criteria,\n });\n subscriptionId = subscription.id as string;\n }\n\n // Get binding token\n const { parameter } = (await this.medplum.get(\n `fhir/R4/Subscription/${subscriptionId}/$get-ws-binding-token`\n )) as Parameters;\n const token = parameter?.find((param) => param.name === 'token')?.valueString;\n const url = parameter?.find((param) => param.name === 'websocket-url')?.valueUrl;\n\n if (!token) {\n throw new OperationOutcomeError(validationError('Failed to get token'));\n }\n if (!url) {\n throw new OperationOutcomeError(validationError('Failed to get URL from $get-ws-binding-token'));\n }\n\n return [subscriptionId, token];\n }\n\n private maybeGetCriteriaEntry(\n criteria: string,\n subscriptionProps?: Partial<Subscription>\n ): CriteriaEntry | undefined {\n const entries = this.criteriaEntries.get(criteria);\n if (!entries) {\n return undefined;\n }\n if (!subscriptionProps) {\n return entries.bareCriteria;\n }\n for (const entry of entries.criteriaWithProps) {\n if (deepEquals(subscriptionProps, entry.subscriptionProps)) {\n return entry;\n }\n }\n return undefined;\n }\n\n private getAllCriteriaEmitters(): SubscriptionEmitter[] {\n const emitters = [];\n for (const mapEntry of this.criteriaEntries.values()) {\n if (mapEntry.bareCriteria) {\n emitters.push(mapEntry.bareCriteria.emitter);\n }\n for (const entry of mapEntry.criteriaWithProps) {\n emitters.push(entry.emitter);\n }\n }\n return emitters;\n }\n\n private addCriteriaEntry(criteriaEntry: CriteriaEntry): void {\n const { criteria, subscriptionProps } = criteriaEntry;\n let mapEntry: CriteriaMapEntry;\n if (!this.criteriaEntries.has(criteria)) {\n mapEntry = { criteriaWithProps: [] as CriteriaEntry[] };\n this.criteriaEntries.set(criteria, mapEntry);\n } else {\n mapEntry = this.criteriaEntries.get(criteria) as CriteriaMapEntry;\n }\n // We can assume because this will be \"guarded\" by `maybeGetCriteriaEntry()`,\n // that we don't need to check if a matching `CriteriaEntry` exists\n // We just need to put the given one into the right spot\n if (!subscriptionProps) {\n mapEntry.bareCriteria = criteriaEntry;\n } else {\n mapEntry.criteriaWithProps.push(criteriaEntry);\n }\n }\n\n private removeCriteriaEntry(criteriaEntry: CriteriaEntry): void {\n const { criteria, subscriptionProps, subscriptionId, token } = criteriaEntry;\n if (!this.criteriaEntries.has(criteria)) {\n return;\n }\n const mapEntry = this.criteriaEntries.get(criteria) as CriteriaMapEntry;\n if (!subscriptionProps) {\n mapEntry.bareCriteria = undefined;\n } else {\n mapEntry.criteriaWithProps = mapEntry.criteriaWithProps.filter((otherEntry): boolean => {\n const otherProps = otherEntry.subscriptionProps as Partial<Subscription>;\n return !deepEquals(subscriptionProps, otherProps);\n });\n }\n if (!mapEntry.bareCriteria && mapEntry.criteriaWithProps.length === 0) {\n this.criteriaEntries.delete(criteria);\n this.masterSubEmitter?._removeCriteria(criteria);\n }\n if (subscriptionId) {\n this.criteriaEntriesBySubscriptionId.delete(subscriptionId);\n }\n if (token && this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify({ type: 'unbind-from-token', payload: { token } }));\n }\n }\n\n private async subscribeToCriteria(criteriaEntry: CriteriaEntry): Promise<void> {\n // We check to see if the WebSocket is open first, since if it's not, we will automatically refresh this later when it opens\n if (this.ws.readyState !== WebSocket.OPEN || criteriaEntry.connecting) {\n return;\n }\n // Set connecting flag to true so other incoming subscription requests to this criteria don't try to subscribe also\n criteriaEntry.connecting = true;\n try {\n const [subscriptionId, token] = await this.getTokenForCriteria(criteriaEntry);\n criteriaEntry.subscriptionId = subscriptionId;\n criteriaEntry.token = token;\n this.criteriaEntriesBySubscriptionId.set(subscriptionId, criteriaEntry);\n // Send binding message\n this.ws.send(JSON.stringify({ type: 'bind-with-token', payload: { token } }));\n } catch (err: unknown) {\n console.error(normalizeErrorString(err));\n this.emitError(criteriaEntry, err as Error);\n this.removeCriteriaEntry(criteriaEntry);\n }\n }\n\n private async refreshAllSubscriptions(): Promise<void> {\n this.criteriaEntriesBySubscriptionId.clear();\n for (const mapEntry of this.criteriaEntries.values()) {\n for (const criteriaEntry of [\n ...(mapEntry.bareCriteria ? [mapEntry.bareCriteria] : []),\n ...mapEntry.criteriaWithProps,\n ]) {\n criteriaEntry.clearAttachedSubscription();\n await this.subscribeToCriteria(criteriaEntry);\n }\n }\n }\n\n addCriteria(criteria: string, subscriptionProps?: Partial<Subscription>): SubscriptionEmitter {\n if (this.masterSubEmitter) {\n this.masterSubEmitter._addCriteria(criteria);\n }\n\n const criteriaEntry = this.maybeGetCriteriaEntry(criteria, subscriptionProps);\n if (criteriaEntry) {\n criteriaEntry.refCount += 1;\n return criteriaEntry.emitter;\n }\n\n const newCriteriaEntry = new CriteriaEntry(criteria, subscriptionProps);\n this.addCriteriaEntry(newCriteriaEntry);\n\n this.subscribeToCriteria(newCriteriaEntry).catch(console.error);\n\n return newCriteriaEntry.emitter;\n }\n\n removeCriteria(criteria: string, subscriptionProps?: Partial<Subscription>): void {\n const criteriaEntry = this.maybeGetCriteriaEntry(criteria, subscriptionProps);\n if (!criteriaEntry) {\n console.warn('Criteria not known to `SubscriptionManager`. Possibly called remove too many times.');\n return;\n }\n\n criteriaEntry.refCount -= 1;\n if (criteriaEntry.refCount > 0) {\n return;\n }\n\n // If actually removing (refcount === 0)\n this.maybeEmitDisconnect(criteriaEntry);\n this.removeCriteriaEntry(criteriaEntry);\n }\n\n getWebSocket(): IReconnectingWebSocket {\n return this.ws;\n }\n\n closeWebSocket(): void {\n if (this.wsClosed) {\n return;\n }\n this.wsClosed = true;\n this.ws.close();\n }\n\n reconnectWebSocket(): void {\n this.ws.reconnect();\n this.wsClosed = false;\n }\n\n getCriteriaCount(): number {\n return this.getAllCriteriaEmitters().length;\n }\n\n getMasterEmitter(): SubscriptionEmitter {\n if (!this.masterSubEmitter) {\n this.masterSubEmitter = new SubscriptionEmitter(...Array.from(this.criteriaEntries.keys()));\n }\n return this.masterSubEmitter;\n }\n}\n\nexport type BackgroundJobInteraction = 'create' | 'update' | 'delete';\n\nexport interface BackgroundJobContext {\n interaction: BackgroundJobInteraction;\n}\n\nexport type ResourceMatchesSubscriptionCriteria = {\n resource: Resource;\n subscription: Subscription;\n context: BackgroundJobContext;\n logger?: Logger;\n getPreviousResource: (currentResource: Resource) => Promise<Resource | undefined>;\n};\n\nexport async function resourceMatchesSubscriptionCriteria({\n resource,\n subscription,\n context,\n getPreviousResource,\n logger,\n}: ResourceMatchesSubscriptionCriteria): Promise<boolean> {\n if (subscription.meta?.account && resource.meta?.account?.reference !== subscription.meta.account.reference) {\n logger?.debug('Ignore resource in different account compartment');\n return false;\n }\n\n if (!matchesChannelType(subscription, logger)) {\n logger?.debug(`Ignore subscription without recognized channel type`);\n return false;\n }\n\n const subscriptionCriteria = subscription.criteria;\n if (!subscriptionCriteria) {\n logger?.debug(`Ignore rest hook missing criteria`);\n return false;\n }\n\n const searchRequest = parseSearchRequest(subscriptionCriteria);\n if (resource.resourceType !== searchRequest.resourceType) {\n logger?.debug(\n `Ignore rest hook for different resourceType (wanted \"${searchRequest.resourceType}\", received \"${resource.resourceType}\")`\n );\n return false;\n }\n\n const fhirPathCriteria = await isFhirCriteriaMet(subscription, resource, getPreviousResource);\n if (!fhirPathCriteria) {\n logger?.debug(`Ignore rest hook for criteria returning false`);\n return false;\n }\n\n const supportedInteractionExtension = getExtension(\n subscription,\n 'https://medplum.com/fhir/StructureDefinition/subscription-supported-interaction'\n );\n if (supportedInteractionExtension && supportedInteractionExtension.valueCode !== context.interaction) {\n logger?.debug(\n `Ignore rest hook for different interaction (wanted \"${supportedInteractionExtension.valueCode}\", received \"${context.interaction}\")`\n );\n return false;\n }\n\n return matchesSearchRequest(resource, searchRequest);\n}\n\n/**\n * Returns true if the subscription channel type is ok to execute.\n * @param subscription - The subscription resource.\n * @param logger - The logger.\n * @returns True if the subscription channel type is ok to execute.\n */\nfunction matchesChannelType(subscription: Subscription, logger?: Logger): boolean {\n const channelType = subscription.channel?.type;\n\n if (channelType === 'rest-hook') {\n const url = subscription.channel?.endpoint;\n if (!url) {\n logger?.debug(`Ignore rest-hook missing URL`);\n return false;\n }\n\n return true;\n }\n\n if (channelType === 'websocket') {\n return true;\n }\n\n return false;\n}\n\nexport async function isFhirCriteriaMet(\n subscription: Subscription,\n currentResource: Resource,\n getPreviousResource: (currentResource: Resource) => Promise<Resource | undefined>\n): Promise<boolean> {\n const criteria = getExtension(\n subscription,\n 'https://medplum.com/fhir/StructureDefinition/fhir-path-criteria-expression'\n );\n if (!criteria?.valueString) {\n return true;\n }\n const previous = await getPreviousResource(currentResource);\n const evalInput = {\n '%current': toTypedValue(currentResource),\n '%previous': toTypedValue(previous ?? {}),\n };\n const evalValue = evalFhirPathTyped(criteria.valueString, [toTypedValue(currentResource)], evalInput);\n return evalValue?.[0]?.value === true;\n}\n", "// PKCE auth based on:\n// https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/\n\nimport {\n AccessPolicy,\n Agent,\n Attachment,\n Binary,\n Bot,\n BulkDataExport,\n Bundle,\n BundleEntry,\n BundleLink,\n Communication,\n Device,\n Encounter,\n ExtractResource,\n Identifier,\n Media,\n OperationOutcome,\n Patient,\n Project,\n ProjectMembership,\n ProjectMembershipAccess,\n ProjectSetting,\n Reference,\n Resource,\n ResourceType,\n SearchParameter,\n StructureDefinition,\n Subscription,\n UserConfiguration,\n ValueSet,\n} from '@medplum/fhirtypes';\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n/** @ts-ignore */\nimport type { CustomTableLayout, TDocumentDefinitions, TFontDictionary } from 'pdfmake/interfaces';\nimport { encodeBase64 } from './base64';\nimport { LRUCache } from './cache';\nimport { ContentType } from './contenttype';\nimport { encryptSHA256, getRandomString } from './crypto';\nimport { TypedEventTarget } from './eventtarget';\nimport {\n CurrentContext,\n FhircastConnection,\n FhircastEventContext,\n FhircastEventName,\n FhircastEventVersionOptional,\n FhircastEventVersionRequired,\n PendingSubscriptionRequest,\n SubscriptionRequest,\n assertContextVersionOptional,\n createFhircastMessagePayload,\n isContextVersionRequired,\n serializeFhircastSubscriptionRequest,\n validateFhircastSubscriptionRequest,\n} from './fhircast';\nimport { Hl7Message } from './hl7';\nimport { isJwt, isMedplumAccessToken, parseJWTPayload, tryGetJwtExpiration } from './jwt';\nimport { MedplumKeyValueClient } from './keyvalue';\nimport {\n OperationOutcomeError,\n badRequest,\n isOk,\n isOperationOutcome,\n normalizeOperationOutcome,\n notFound,\n unauthorized,\n unauthorizedTokenAudience,\n unauthorizedTokenExpired,\n validationError,\n} from './outcomes';\nimport { ReadablePromise } from './readablepromise';\nimport { ClientStorage, IClientStorage } from './storage';\nimport { SubscriptionEmitter, SubscriptionManager } from './subscriptions';\nimport { indexSearchParameter } from './types';\nimport { indexStructureDefinitionBundle, isDataTypeLoaded, isProfileLoaded, loadDataType } from './typeschema/types';\nimport {\n CodeChallengeMethod,\n ProfileResource,\n QueryTypes,\n arrayBufferToBase64,\n concatUrls,\n createReference,\n ensureTrailingSlash,\n getQueryString,\n getReferenceString,\n getWebSocketUrl,\n isObject,\n resolveId,\n sleep,\n sortStringArray,\n} from './utils';\n\nexport const MEDPLUM_VERSION: string = import.meta.env.MEDPLUM_VERSION ?? '';\nexport const MEDPLUM_CLI_CLIENT_ID = 'medplum-cli';\nexport const DEFAULT_ACCEPT = ContentType.FHIR_JSON + ', */*; q=0.1';\n\nconst DEFAULT_BASE_URL = 'https://api.medplum.com/';\nconst DEFAULT_RESOURCE_CACHE_SIZE = 1000;\nconst DEFAULT_BROWSER_CACHE_TIME = 60000; // 60 seconds\nconst DEFAULT_NODE_CACHE_TIME = 0;\nconst DEFAULT_REFRESH_GRACE_PERIOD = 300000; // 5 minutes\nconst BINARY_URL_PREFIX = 'Binary/';\n\nconst system: Device = {\n resourceType: 'Device',\n id: 'system',\n deviceName: [{ type: 'model-name', name: 'System' }],\n};\n\n/**\n * The MedplumClientOptions interface defines configuration options for MedplumClient.\n *\n * All configuration settings are optional.\n */\nexport interface MedplumClientOptions {\n /**\n * Base server URL.\n *\n * Default value is https://api.medplum.com/\n *\n * Use this to point to a custom Medplum deployment.\n */\n baseUrl?: string;\n\n /**\n * OAuth2 authorize URL.\n *\n * Default value is baseUrl + \"/oauth2/authorize\".\n *\n * Can be specified as absolute URL or relative to baseUrl.\n *\n * Use this if you want to use a separate OAuth server.\n */\n authorizeUrl?: string;\n\n /**\n * FHIR URL path.\n *\n * Default value is \"fhir/R4/\".\n *\n * Can be specified as absolute URL or relative to baseUrl.\n *\n * Use this if you want to use a different path when connecting to a FHIR server.\n */\n fhirUrlPath?: string;\n\n /**\n * OAuth2 token URL.\n *\n * Default value is baseUrl + \"/oauth2/token\".\n *\n * Can be specified as absolute URL or relative to baseUrl.\n *\n * Use this if you want to use a separate OAuth server.\n */\n tokenUrl?: string;\n\n /**\n * OAuth2 logout URL.\n *\n * Default value is baseUrl + \"/oauth2/logout\".\n *\n * Can be specified as absolute URL or relative to baseUrl.\n *\n * Use this if you want to use a separate OAuth server.\n */\n logoutUrl?: string;\n\n /**\n * FHIRcast Hub URL.\n *\n * Default value is `fhircast/STU3`.\n *\n * Can be specified as absolute URL or relative to `baseUrl`.\n *\n * Use this if you want to use a different path when connecting to a FHIRcast hub.\n */\n fhircastHubUrl?: string;\n\n /**\n * The client ID.\n *\n * Client ID can be used for SMART-on-FHIR customization.\n */\n clientId?: string;\n\n /**\n * The client secret.\n *\n * Client secret can be used for FHIR Oauth Client Credential flows\n */\n clientSecret?: string;\n\n /**\n * The OAuth Access Token.\n *\n * Access Token used to connect to make request to FHIR servers\n */\n accessToken?: string;\n\n /**\n * Number of resources to store in the cache.\n *\n * Default value is 1000.\n *\n * Consider using this for performance of displaying Patient or Practitioner resources.\n */\n resourceCacheSize?: number;\n\n /**\n * The length of time in milliseconds to cache resources.\n *\n * Default value is 60000 (60 seconds).\n *\n * Cache time of zero disables all caching.\n *\n * For any individual request, the cache behavior can be overridden by setting the cache property on request options.\n *\n * See: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache\n */\n cacheTime?: number;\n\n /**\n * The length of time in milliseconds to delay requests for auto batching.\n *\n * Auto batching attempts to group multiple requests together into a single batch request.\n *\n * Default value is 0, which disables auto batching.\n */\n autoBatchTime?: number;\n\n /**\n * The refresh grace period in milliseconds.\n *\n * This is the amount of time before the access token expires that the client will attempt to refresh the token.\n *\n * Default value is 300000 (5 minutes).\n */\n refreshGracePeriod?: number;\n\n /**\n * Fetch implementation.\n *\n * Default is window.fetch (if available).\n *\n * For Node.js applications, consider the 'node-fetch' package.\n */\n fetch?: FetchLike;\n\n /**\n * Storage implementation.\n *\n * Default is window.localStorage (if available), this is the common implementation for use in the browser, or an in-memory storage implementation. If using Medplum on a server it may be useful to provide a custom storage implementation, for example using redis, a database or a file based storage. Medplum CLI is an an example of `FileSystemStorage`, for reference.\n */\n storage?: IClientStorage;\n\n /**\n * Create PDF implementation.\n *\n * Default is none, and PDF generation is disabled.\n *\n * @example\n * In browser environments, import the client-side pdfmake library.\n *\n * ```html\n * <script src=\"pdfmake.min.js\"></script>\n * <script>\n * async function createPdf(docDefinition, tableLayouts, fonts) {\n * return new Promise((resolve) => {\n * pdfMake.createPdf(docDefinition, tableLayouts, fonts).getBlob(resolve);\n * });\n * }\n * </script>\n * ```\n *\n * @example\n * In Node.js applications:\n *\n * ```ts\n * import type { CustomTableLayout, TDocumentDefinitions, TFontDictionary } from 'pdfmake/interfaces';\n * function createPdf(\n * docDefinition: TDocumentDefinitions,\n * tableLayouts?: { [name: string]: CustomTableLayout },\n * fonts?: TFontDictionary\n * ): Promise<Buffer> {\n * return new Promise((resolve, reject) => {\n * const printer = new PdfPrinter(fonts ?? {});\n * const pdfDoc = printer.createPdfKitDocument(docDefinition, { tableLayouts });\n * const chunks: Uint8Array[] = [];\n * pdfDoc.on('data', (chunk: Uint8Array) => chunks.push(chunk));\n * pdfDoc.on('end', () => resolve(Buffer.concat(chunks)));\n * pdfDoc.on('error', reject);\n * pdfDoc.end();\n * });\n * }\n * ```\n */\n createPdf?: CreatePdfFunction;\n\n /**\n * Callback for when the client is unauthenticated.\n *\n * Default is do nothing.\n *\n * For client side applications, consider redirecting to a sign in page.\n */\n onUnauthenticated?: () => void;\n\n /**\n * The default redirect behavior.\n *\n * The default behavior is to not follow redirects.\n *\n * Use \"follow\" to automatically follow redirects.\n */\n redirect?: RequestRedirect;\n\n /**\n * When the verbose flag is set, the client will log all requests and responses to the console.\n */\n verbose?: boolean;\n\n /**\n * Optional flag to enable or disable Medplum extended mode.\n *\n * Medplum extended mode includes a few non-standard FHIR properties such as meta.author and meta.project.\n *\n * Default is true.\n */\n extendedMode?: boolean;\n\n /**\n * Default headers to include in all requests.\n * This can be used to set custom headers such as Cookies or Authorization headers.\n */\n defaultHeaders?: Record<string, string>;\n}\n\nexport interface MedplumRequestOptions extends RequestInit {\n /**\n * Optional flag to follow \"Location\" or \"Content-Location\" URL on successful HTTP 200 \"OK\" responses.\n */\n followRedirectOnOk?: boolean;\n\n /**\n * Optional flag to follow \"Location\" or \"Content-Location\" URL on successful HTTP 201 \"Created\" responses.\n */\n followRedirectOnCreated?: boolean;\n\n /**\n * Optional flag to poll the status URL on successful HTTP 202 \"Accepted\" responses.\n */\n pollStatusOnAccepted?: boolean;\n\n /**\n * Optional polling time interval in milliseconds.\n * Default value is 1000 (1 second).\n */\n pollStatusPeriod?: number;\n\n /**\n * Optional max number of retries that should be made in the case of a failed request. Default is `2`.\n */\n maxRetries?: number;\n\n /**\n * Optional flag to disable auto-batching for this specific request.\n * Only applies when the client is configured with auto-batching enabled.\n */\n disableAutoBatch?: boolean;\n}\n\nexport type FetchLike = (url: string, options?: any) => Promise<any>;\n\n/**\n * ResourceArray is an array of resources with a bundle property.\n * The bundle property is a FHIR Bundle containing the search results.\n * This is useful for retrieving bundle metadata such as total, offset, and next link.\n */\nexport type ResourceArray<T extends Resource = Resource> = T[] & { bundle: Bundle<T> };\n\nexport interface CreatePdfFunction {\n (\n docDefinition: TDocumentDefinitions,\n tableLayouts?: Record<string, CustomTableLayout>,\n fonts?: TFontDictionary\n ): Promise<any>;\n}\n\nexport interface BaseLoginRequest {\n readonly projectId?: string;\n readonly clientId?: string;\n readonly resourceType?: string;\n readonly scope?: string;\n readonly nonce?: string;\n readonly codeChallenge?: string;\n readonly codeChallengeMethod?: CodeChallengeMethod;\n readonly googleClientId?: string;\n readonly launch?: string;\n readonly redirectUri?: string;\n}\n\nexport interface EmailPasswordLoginRequest extends BaseLoginRequest {\n readonly email: string;\n readonly password: string;\n /** @deprecated Use scope of \"offline\" or \"offline_access\" instead. */\n readonly remember?: boolean;\n}\n\nexport interface NewUserRequest {\n readonly firstName: string;\n readonly lastName: string;\n readonly email: string;\n readonly password: string;\n readonly recaptchaToken: string;\n readonly recaptchaSiteKey?: string;\n readonly remember?: boolean;\n readonly projectId?: string;\n readonly clientId?: string;\n}\n\nexport interface NewProjectRequest {\n readonly login: string;\n readonly projectName: string;\n}\n\nexport interface NewPatientRequest {\n readonly login: string;\n readonly projectId: string;\n}\n\nexport interface GoogleCredentialResponse {\n readonly clientId: string;\n readonly credential: string;\n}\n\nexport interface GoogleLoginRequest extends BaseLoginRequest {\n readonly googleClientId: string;\n readonly googleCredential: string;\n readonly createUser?: boolean;\n}\n\nexport interface LoginAuthenticationResponse {\n readonly login: string;\n readonly mfaRequired?: boolean;\n readonly code?: string;\n readonly memberships?: ProjectMembership[];\n}\n\nexport interface LoginProfileResponse {\n readonly login: string;\n readonly scope: string;\n}\n\nexport interface LoginScopeResponse {\n readonly login: string;\n readonly code: string;\n}\n\nexport interface LoginState {\n readonly project: Reference<Project>;\n readonly profile: Reference<ProfileResource>;\n readonly accessToken: string;\n readonly refreshToken: string;\n}\n\nexport interface TokenResponse {\n readonly token_type: string;\n readonly id_token: string;\n readonly access_token: string;\n readonly refresh_token: string;\n readonly expires_in: number;\n readonly project: Reference<Project>;\n readonly profile: Reference<ProfileResource>;\n}\n\nexport interface BotEvent<T = Resource | Hl7Message | string | Record<string, any>> {\n readonly bot: Reference<Bot>;\n readonly contentType: string;\n readonly input: T;\n readonly secrets: Record<string, ProjectSetting>;\n readonly traceId?: string;\n}\n\nexport interface InviteRequest {\n resourceType: 'Patient' | 'Practitioner' | 'RelatedPerson';\n firstName: string;\n lastName: string;\n email?: string;\n externalId?: string;\n password?: string;\n sendEmail?: boolean;\n membership?: Partial<ProjectMembership>;\n upsert?: boolean;\n /** @deprecated Use membership.accessPolicy instead. */\n accessPolicy?: Reference<AccessPolicy>;\n /** @deprecated Use membership.access instead. */\n access?: ProjectMembershipAccess[];\n /** @deprecated Use membership.admin instead. */\n admin?: boolean;\n}\n\n/**\n * JSONPatch patch operation.\n * Compatible with fast-json-patch and rfc6902 Operation.\n */\nexport interface PatchOperation {\n readonly op: 'add' | 'remove' | 'replace' | 'copy' | 'move' | 'test';\n readonly path: string;\n readonly value?: any;\n}\n\n/**\n * Source for a FHIR Binary.\n */\nexport type BinarySource = string | File | Blob | Uint8Array;\n\n/**\n * Binary upload options.\n */\nexport interface CreateBinaryOptions {\n /**\n * The binary data to upload.\n */\n readonly data: BinarySource;\n\n /**\n * Content type for the binary.\n */\n readonly contentType: string;\n\n /**\n * Optional filename for the binary.\n */\n readonly filename?: string;\n\n /**\n * Optional security context for the binary.\n */\n readonly securityContext?: Reference;\n\n /**\n * Optional fetch options. **NOTE:** only `requestOptions.signal` is respected when `onProgress` is also provided.\n */\n readonly onProgress?: (e: ProgressEvent) => void;\n}\n\nexport interface CreateMediaOptions extends CreateBinaryOptions {\n /**\n * Optional additional fields for the Media resource.\n */\n readonly additionalFields?: Partial<Media>;\n}\n\n/**\n * PDF upload options.\n */\nexport interface CreatePdfOptions extends Omit<CreateBinaryOptions, 'data' | 'contentType'> {\n /**\n * The PDF document definition. See https://pdfmake.github.io/docs/0.1/document-definition-object/\n */\n readonly docDefinition: TDocumentDefinitions;\n\n /**\n * Optional pdfmake custom table layout.\n */\n readonly tableLayouts?: Record<string, CustomTableLayout>;\n\n /**\n * Optional pdfmake custom font dictionary.\n */\n readonly fonts?: TFontDictionary;\n}\n\n/**\n * Email address definition.\n * Compatible with nodemailer Mail.Address.\n */\nexport interface MailAddress {\n readonly name: string;\n readonly address: string;\n}\n\n/**\n * Email destination definition.\n */\nexport type MailDestination = string | MailAddress | string[] | MailAddress[];\n\n/**\n * Email attachment definition.\n * Compatible with nodemailer Mail.Options.\n */\nexport interface MailAttachment {\n /** String, Buffer or a Stream contents for the attachment */\n readonly content?: string;\n /** path to a file or an URL (data uris are allowed as well) if you want to stream the file instead of including it (better for larger attachments) */\n readonly path?: string;\n /** filename to be reported as the name of the attached file, use of unicode is allowed. If you do not want to use a filename, set this value as false, otherwise a filename is generated automatically */\n readonly filename?: string | false;\n /** optional content type for the attachment, if not set will be derived from the filename property */\n readonly contentType?: string;\n}\n\n/**\n * Email message definition.\n * Compatible with nodemailer Mail.Options.\n */\nexport interface MailOptions {\n /** The e-mail address of the sender. All e-mail addresses can be plain `sender@server.com` or formatted `Sender Name <sender@server.com>` */\n readonly from?: string | MailAddress;\n /** An e-mail address that will appear on the Sender: field */\n readonly sender?: string | MailAddress;\n /** Comma separated list or an array of recipients e-mail addresses that will appear on the To: field */\n readonly to?: MailDestination;\n /** Comma separated list or an array of recipients e-mail addresses that will appear on the Cc: field */\n readonly cc?: MailDestination;\n /** Comma separated list or an array of recipients e-mail addresses that will appear on the Bcc: field */\n readonly bcc?: MailDestination;\n /** An e-mail address that will appear on the Reply-To: field */\n readonly replyTo?: string | MailAddress;\n /** The subject of the e-mail */\n readonly subject?: string;\n /** The plaintext version of the message */\n readonly text?: string;\n /** The HTML version of the message */\n readonly html?: string;\n /** An array of attachment objects */\n readonly attachments?: MailAttachment[];\n}\n\ninterface SchemaGraphQLResponse {\n readonly data: {\n readonly StructureDefinitionList: StructureDefinition[];\n readonly SearchParameterList: SearchParameter[];\n };\n}\n\ninterface RequestCacheEntry {\n readonly requestTime: number;\n readonly value: ReadablePromise<any>;\n}\n\ninterface AutoBatchEntry<T = any> {\n readonly method: 'GET';\n readonly url: string;\n readonly options: MedplumRequestOptions;\n readonly resolve: (value: T) => void;\n readonly reject: (reason: any) => void;\n}\n\ninterface RequestState {\n statusUrl?: string;\n pollCount?: number;\n}\n\n/**\n * OAuth 2.0 Grant Type Identifiers\n * Standard identifiers: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#name-grant-types\n * JWT bearer extension: https://datatracker.ietf.org/doc/html/rfc7523\n * Token exchange extension: https://datatracker.ietf.org/doc/html/rfc8693\n */\nexport enum OAuthGrantType {\n ClientCredentials = 'client_credentials',\n AuthorizationCode = 'authorization_code',\n RefreshToken = 'refresh_token',\n JwtBearer = 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n TokenExchange = 'urn:ietf:params:oauth:grant-type:token-exchange',\n}\n\n/**\n * OAuth 2.0 Token Type Identifiers\n * See: https://datatracker.ietf.org/doc/html/rfc8693#name-token-type-identifiers\n */\nexport enum OAuthTokenType {\n /** Indicates that the token is an OAuth 2.0 access token issued by the given authorization server. */\n AccessToken = 'urn:ietf:params:oauth:token-type:access_token',\n /** Indicates that the token is an OAuth 2.0 refresh token issued by the given authorization server. */\n RefreshToken = 'urn:ietf:params:oauth:token-type:refresh_token',\n /** Indicates that the token is an ID Token as defined in Section 2 of [OpenID.Core]. */\n IdToken = 'urn:ietf:params:oauth:token-type:id_token',\n /** Indicates that the token is a base64url-encoded SAML 1.1 [OASIS.saml-core-1.1] assertion. */\n Saml1Token = 'urn:ietf:params:oauth:token-type:saml1',\n /** Indicates that the token is a base64url-encoded SAML 2.0 [OASIS.saml-core-2.0-os] assertion. */\n Saml2Token = 'urn:ietf:params:oauth:token-type:saml2',\n}\n\n/**\n * OAuth 2.0 Client Authentication Methods\n * See: https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication\n */\nexport enum OAuthTokenAuthMethod {\n ClientSecretBasic = 'client_secret_basic',\n ClientSecretPost = 'client_secret_post',\n ClientSecretJwt = 'client_secret_jwt',\n PrivateKeyJwt = 'private_key_jwt',\n None = 'none',\n}\n\n/**\n * OAuth 2.0 Client Authentication Methods\n * See: https://datatracker.ietf.org/doc/html/rfc7523#section-2.2\n */\nexport enum OAuthClientAssertionType {\n /** Using JWTs for Client Authentication */\n JwtBearer = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',\n}\n\ninterface SessionDetails {\n project: Project;\n membership: ProjectMembership;\n profile: ProfileResource;\n config: UserConfiguration;\n accessPolicy: AccessPolicy;\n}\n\n/**\n * ValueSet $expand operation parameters.\n * See: https://hl7.org/fhir/r4/valueset-operation-expand.html\n */\nexport interface ValueSetExpandParams {\n url?: string;\n filter?: string;\n date?: string;\n offset?: number;\n count?: number;\n}\n\nexport interface RequestProfileSchemaOptions {\n /** (optional) Whether to include nested profiles, e.g. from extensions. Defaults to false. */\n expandProfile?: boolean;\n}\n\n/**\n * This map enumerates all the lifecycle events that `MedplumClient` emits and what the shape of the `Event` is.\n */\nexport type MedplumClientEventMap = {\n change: { type: 'change' };\n offline: { type: 'offline' };\n profileRefreshing: { type: 'profileRefreshing' };\n profileRefreshed: { type: 'profileRefreshed' };\n storageInitialized: { type: 'storageInitialized' };\n storageInitFailed: { type: 'storageInitFailed'; payload: { error: Error } };\n};\n\n/**\n * The MedplumClient class provides a client for the Medplum FHIR server.\n *\n * The client can be used in the browser, in a Node.js application, or in a Medplum Bot.\n *\n * The client provides helpful methods for common operations such as:\n * 1. Authenticating\n * 2. Creating resources\n * 3. Reading resources\n * 4. Updating resources\n * 5. Deleting resources\n * 6. Searching\n * 7. Making GraphQL queries\n *\n * The client can also be used to integrate with other FHIR servers. For an example, see the Epic Connection Demo Bot [here](https://github.com/medplum/medplum/tree/main/examples/medplum-demo-bots/src/epic).\n *\n * @example\n * Here is a quick example of how to use the client:\n *\n * ```typescript\n * import { MedplumClient } from '@medplum/core';\n * const medplum = new MedplumClient();\n * ```\n *\n * @example\n * Create a `Patient`:\n *\n * ```typescript\n * const patient = await medplum.createResource({\n * resourceType: 'Patient',\n * name: [{\n * given: ['Alice'],\n * family: 'Smith'\n * }]\n * });\n * ```\n *\n * @example\n * Read a `Patient` by ID:\n *\n * ```typescript\n * const patient = await medplum.readResource('Patient', '123');\n * console.log(patient.name[0].given[0]);\n * ```\n *\n * @example\n * Search for a `Patient` by name:\n *\n * ```typescript\n * const bundle = await medplum.search('Patient', 'name=Alice');\n * console.log(bundle.total);\n * ```\n *\n * <head>\n * <meta name=\"algolia:pageRank\" content=\"100\" />\n * </head>\n */\nexport class MedplumClient extends TypedEventTarget<MedplumClientEventMap> {\n private readonly options: MedplumClientOptions;\n private readonly fetch: FetchLike;\n private readonly createPdfImpl?: CreatePdfFunction;\n private readonly storage: IClientStorage;\n private readonly requestCache: LRUCache<RequestCacheEntry> | undefined;\n private readonly cacheTime: number;\n private readonly baseUrl: string;\n private readonly fhirBaseUrl: string;\n private readonly authorizeUrl: string;\n private readonly tokenUrl: string;\n private readonly logoutUrl: string;\n private readonly fhircastHubUrl: string;\n private readonly defaultHeaders: Record<string, string>;\n private readonly onUnauthenticated?: () => void;\n private readonly autoBatchTime: number;\n private readonly autoBatchQueue: AutoBatchEntry[] | undefined;\n private readonly refreshGracePeriod: number;\n private subscriptionManager?: SubscriptionManager;\n private medplumServer?: boolean;\n private clientId?: string;\n private clientSecret?: string;\n private autoBatchTimerId?: any;\n private accessToken?: string;\n private accessTokenExpires?: number;\n private refreshToken?: string;\n private refreshPromise?: Promise<any>;\n private profilePromise?: Promise<any>;\n private sessionDetails?: SessionDetails;\n private basicAuth?: string;\n private initPromise: Promise<void>;\n private initComplete = true;\n private keyValueClient?: MedplumKeyValueClient;\n\n constructor(options?: MedplumClientOptions) {\n super();\n\n if (options?.baseUrl) {\n if (!options.baseUrl.startsWith('http')) {\n throw new Error('Base URL must start with http or https');\n }\n }\n\n this.options = options ?? {};\n this.fetch = options?.fetch ?? getDefaultFetch();\n this.storage = options?.storage ?? new ClientStorage();\n this.createPdfImpl = options?.createPdf;\n this.baseUrl = ensureTrailingSlash(options?.baseUrl ?? DEFAULT_BASE_URL);\n this.fhirBaseUrl = concatUrls(this.baseUrl, options?.fhirUrlPath ?? 'fhir/R4');\n this.authorizeUrl = concatUrls(this.baseUrl, options?.authorizeUrl ?? 'oauth2/authorize');\n this.tokenUrl = concatUrls(this.baseUrl, options?.tokenUrl ?? 'oauth2/token');\n this.logoutUrl = concatUrls(this.baseUrl, options?.logoutUrl ?? 'oauth2/logout');\n this.fhircastHubUrl = concatUrls(this.baseUrl, options?.fhircastHubUrl ?? 'fhircast/STU3');\n this.clientId = options?.clientId ?? '';\n this.clientSecret = options?.clientSecret ?? '';\n this.defaultHeaders = options?.defaultHeaders ?? {};\n this.onUnauthenticated = options?.onUnauthenticated;\n this.refreshGracePeriod = options?.refreshGracePeriod ?? DEFAULT_REFRESH_GRACE_PERIOD;\n\n this.cacheTime =\n options?.cacheTime ?? (typeof window === 'undefined' ? DEFAULT_NODE_CACHE_TIME : DEFAULT_BROWSER_CACHE_TIME);\n if (this.cacheTime > 0) {\n this.requestCache = new LRUCache(options?.resourceCacheSize ?? DEFAULT_RESOURCE_CACHE_SIZE);\n } else {\n this.requestCache = undefined;\n }\n\n if (options?.autoBatchTime) {\n this.autoBatchTime = options.autoBatchTime;\n this.autoBatchQueue = [];\n } else {\n this.autoBatchTime = 0;\n this.autoBatchQueue = undefined;\n }\n\n if (options?.accessToken) {\n this.setAccessToken(options.accessToken);\n }\n\n if (this.storage.getInitPromise === undefined) {\n if (!options?.accessToken) {\n this.attemptResumeActiveLogin().catch(console.error);\n }\n this.initPromise = Promise.resolve();\n this.dispatchEvent({ type: 'storageInitialized' });\n } else {\n this.initComplete = false;\n this.initPromise = this.storage.getInitPromise();\n this.initPromise\n .then(() => {\n if (!options?.accessToken) {\n this.attemptResumeActiveLogin().catch(console.error);\n }\n this.initComplete = true;\n this.dispatchEvent({ type: 'storageInitialized' });\n })\n .catch((err: Error) => {\n console.error(err);\n this.initComplete = true;\n this.dispatchEvent({ type: 'storageInitFailed', payload: { error: err } });\n });\n }\n\n this.setupStorageListener();\n }\n\n /**\n * @returns Whether the client has been fully initialized or not. Should always be true unless a custom asynchronous `ClientStorage` was passed into the constructor.\n */\n get isInitialized(): boolean {\n return this.initComplete;\n }\n\n /**\n * Gets a Promise that resolves when async initialization is complete. This is particularly useful for waiting for an async `ClientStorage` and/or authentication to finish.\n * @returns A Promise that resolves when any async initialization of the client is finished.\n */\n getInitPromise(): Promise<void> {\n return this.initPromise;\n }\n\n private async attemptResumeActiveLogin(): Promise<void> {\n const activeLogin = this.getActiveLogin();\n if (!activeLogin) {\n return;\n }\n this.setAccessToken(activeLogin.accessToken, activeLogin.refreshToken);\n await this.refreshProfile();\n }\n\n /**\n * Returns the current base URL for all API requests.\n * By default, this is set to `https://api.medplum.com/`.\n * This can be overridden by setting the `baseUrl` option when creating the client.\n * @category HTTP\n * @returns The current base URL for all API requests.\n */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n /**\n * Returns the current authorize URL.\n * By default, this is set to `https://api.medplum.com/oauth2/authorize`.\n * This can be overridden by setting the `authorizeUrl` option when creating the client.\n * @category HTTP\n * @returns The current authorize URL.\n */\n getAuthorizeUrl(): string {\n return this.authorizeUrl;\n }\n\n /**\n * Returns the current token URL.\n * By default, this is set to `https://api.medplum.com/oauth2/token`.\n * This can be overridden by setting the `tokenUrl` option when creating the client.\n * @category HTTP\n * @returns The current token URL.\n */\n getTokenUrl(): string {\n return this.tokenUrl;\n }\n\n /**\n * Returns the current logout URL.\n * By default, this is set to `https://api.medplum.com/oauth2/logout`.\n * This can be overridden by setting the `logoutUrl` option when creating the client.\n * @category HTTP\n * @returns The current logout URL.\n */\n getLogoutUrl(): string {\n return this.logoutUrl;\n }\n\n /**\n * Returns the current FHIRcast Hub URL.\n * By default, this is set to `https://api.medplum.com/fhircast/STU3`.\n * This can be overridden by setting the `logoutUrl` option when creating the client.\n * @category HTTP\n * @returns The current FHIRcast Hub URL.\n */\n getFhircastHubUrl(): string {\n return this.fhircastHubUrl;\n }\n\n /**\n * Returns default headers to include in all requests.\n * This can be used to set custom headers such as Cookies or Authorization headers.\n * @category HTTP\n * @returns Default headers to include in all requests.\n */\n getDefaultHeaders(): Record<string, string> {\n return this.defaultHeaders;\n }\n\n /**\n * Clears all auth state including local storage and session storage.\n * @category Authentication\n */\n clear(): void {\n this.storage.clear();\n if (typeof window !== 'undefined') {\n sessionStorage.clear();\n }\n this.clearActiveLogin();\n }\n\n /**\n * Clears the active login from local storage.\n * Does not clear all local storage (such as other logins).\n * @category Authentication\n */\n clearActiveLogin(): void {\n this.storage.setString('activeLogin', undefined);\n this.requestCache?.clear();\n this.accessToken = undefined;\n this.refreshToken = undefined;\n this.refreshPromise = undefined;\n this.accessTokenExpires = undefined;\n this.sessionDetails = undefined;\n this.medplumServer = undefined;\n this.dispatchEvent({ type: 'change' });\n }\n\n /**\n * Invalidates any cached values or cached requests for the given URL.\n * @category Caching\n * @param url - The URL to invalidate.\n */\n invalidateUrl(url: URL | string): void {\n url = url.toString();\n this.requestCache?.delete(url);\n }\n\n /**\n * Invalidates all cached values and flushes the cache.\n * @category Caching\n */\n invalidateAll(): void {\n this.requestCache?.clear();\n }\n\n /**\n * Invalidates all cached search results or cached requests for the given resourceType.\n * @category Caching\n * @param resourceType - The resource type to invalidate.\n */\n invalidateSearches<K extends ResourceType>(resourceType: K): void {\n const url = concatUrls(this.fhirBaseUrl, resourceType);\n if (this.requestCache) {\n for (const key of this.requestCache.keys()) {\n if (key.endsWith(url) || key.includes(url + '?')) {\n this.requestCache.delete(key);\n }\n }\n }\n }\n\n /**\n * Makes an HTTP GET request to the specified URL.\n *\n * This is a lower level method for custom requests.\n * For common operations, we recommend using higher level methods\n * such as `readResource()`, `search()`, etc.\n * @category HTTP\n * @param url - The target URL.\n * @param options - Optional fetch options.\n * @returns Promise to the response content.\n */\n get<T = any>(url: URL | string, options: MedplumRequestOptions = {}): ReadablePromise<T> {\n url = url.toString();\n const cached = this.getCacheEntry(url, options);\n if (cached) {\n return cached.value;\n }\n\n let promise: Promise<T>;\n\n if (url.startsWith(this.fhirBaseUrl) && this.autoBatchQueue && !options.disableAutoBatch) {\n promise = new Promise<T>((resolve, reject) => {\n (this.autoBatchQueue as AutoBatchEntry[]).push({\n method: 'GET',\n url: (url as string).replace(this.fhirBaseUrl, ''),\n options,\n resolve,\n reject,\n });\n if (!this.autoBatchTimerId) {\n this.autoBatchTimerId = setTimeout(() => this.executeAutoBatch(), this.autoBatchTime);\n }\n });\n } else {\n promise = this.request<T>('GET', url, options);\n }\n\n const readablePromise = new ReadablePromise(promise);\n this.setCacheEntry(url, readablePromise);\n return readablePromise;\n }\n\n /**\n * Makes an HTTP POST request to the specified URL.\n *\n * This is a lower level method for custom requests.\n * For common operations, we recommend using higher level methods\n * such as `createResource()`.\n * @category HTTP\n * @param url - The target URL.\n * @param body - The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.\n * @param contentType - The content type to be included in the \"Content-Type\" header.\n * @param options - Optional fetch options.\n * @returns Promise to the response content.\n */\n post(url: URL | string, body?: any, contentType?: string, options: MedplumRequestOptions = {}): Promise<any> {\n url = url.toString();\n this.setRequestBody(options, body);\n if (contentType) {\n this.setRequestContentType(options, contentType);\n }\n this.invalidateUrl(url);\n return this.request('POST', url, options);\n }\n\n /**\n * Makes an HTTP PUT request to the specified URL.\n *\n * This is a lower level method for custom requests.\n * For common operations, we recommend using higher level methods\n * such as `updateResource()`.\n * @category HTTP\n * @param url - The target URL.\n * @param body - The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.\n * @param contentType - The content type to be included in the \"Content-Type\" header.\n * @param options - Optional fetch options.\n * @returns Promise to the response content.\n */\n put(url: URL | string, body: any, contentType?: string, options: MedplumRequestOptions = {}): Promise<any> {\n url = url.toString();\n this.setRequestBody(options, body);\n if (contentType) {\n this.setRequestContentType(options, contentType);\n }\n this.invalidateUrl(url);\n return this.request('PUT', url, options);\n }\n\n /**\n * Makes an HTTP PATCH request to the specified URL.\n *\n * This is a lower level method for custom requests.\n * For common operations, we recommend using higher level methods\n * such as `patchResource()`.\n * @category HTTP\n * @param url - The target URL.\n * @param operations - Array of JSONPatch operations.\n * @param options - Optional fetch options.\n * @returns Promise to the response content.\n */\n patch(url: URL | string, operations: PatchOperation[], options: MedplumRequestOptions = {}): Promise<any> {\n url = url.toString();\n this.setRequestBody(options, operations);\n this.setRequestContentType(options, ContentType.JSON_PATCH);\n this.invalidateUrl(url);\n return this.request('PATCH', url, options);\n }\n\n /**\n * Makes an HTTP DELETE request to the specified URL.\n *\n *\n * This is a lower level method for custom requests.\n * For common operations, we recommend using higher level methods\n * such as `deleteResource()`.\n * @category HTTP\n * @param url - The target URL.\n * @param options - Optional fetch options.\n * @returns Promise to the response content.\n */\n delete(url: URL | string, options?: MedplumRequestOptions): Promise<any> {\n url = url.toString();\n this.invalidateUrl(url);\n return this.request('DELETE', url, options);\n }\n\n /**\n * Initiates a new user flow.\n *\n * This method is part of the two different user registration flows:\n * 1) New Practitioner and new Project\n * 2) New Patient registration\n * @category Authentication\n * @param newUserRequest - Register request including email and password.\n * @param options - Optional fetch options.\n * @returns Promise to the authentication response.\n */\n async startNewUser(\n newUserRequest: NewUserRequest,\n options?: MedplumRequestOptions\n ): Promise<LoginAuthenticationResponse> {\n const { codeChallengeMethod, codeChallenge } = await this.startPkce();\n return this.post(\n 'auth/newuser',\n {\n ...newUserRequest,\n clientId: newUserRequest.clientId ?? this.clientId,\n codeChallengeMethod,\n codeChallenge,\n },\n undefined,\n options\n ) as Promise<LoginAuthenticationResponse>;\n }\n\n /**\n * Initiates a new project flow.\n *\n * This requires a partial login from `startNewUser` or `startNewGoogleUser`.\n * @param newProjectRequest - Register request including email and password.\n * @param options - Optional fetch options.\n * @returns Promise to the authentication response.\n */\n async startNewProject(\n newProjectRequest: NewProjectRequest,\n options?: MedplumRequestOptions\n ): Promise<LoginAuthenticationResponse> {\n return this.post('auth/newproject', newProjectRequest, undefined, options) as Promise<LoginAuthenticationResponse>;\n }\n\n /**\n * Initiates a new patient flow.\n *\n * This requires a partial login from `startNewUser` or `startNewGoogleUser`.\n * @param newPatientRequest - Register request including email and password.\n * @param options - Optional fetch options.\n * @returns Promise to the authentication response.\n */\n async startNewPatient(\n newPatientRequest: NewPatientRequest,\n options?: MedplumRequestOptions\n ): Promise<LoginAuthenticationResponse> {\n return this.post('auth/newpatient', newPatientRequest, undefined, options) as Promise<LoginAuthenticationResponse>;\n }\n\n /**\n * Initiates a user login flow.\n * @category Authentication\n * @param loginRequest - Login request including email and password.\n * @param options - Optional fetch options.\n * @returns Promise to the authentication response.\n */\n async startLogin(\n loginRequest: EmailPasswordLoginRequest,\n options?: MedplumRequestOptions\n ): Promise<LoginAuthenticationResponse> {\n return this.post(\n 'auth/login',\n {\n ...(await this.ensureCodeChallenge(loginRequest)),\n clientId: loginRequest.clientId ?? this.clientId,\n scope: loginRequest.scope,\n },\n undefined,\n options\n ) as Promise<LoginAuthenticationResponse>;\n }\n\n /**\n * Tries to sign in with Google authentication.\n * The response parameter is the result of a Google authentication.\n * See: https://developers.google.com/identity/gsi/web/guides/handle-credential-responses-js-functions\n * @category Authentication\n * @param loginRequest - Login request including Google credential response.\n * @param options - Optional fetch options.\n * @returns Promise to the authentication response.\n */\n async startGoogleLogin(\n loginRequest: GoogleLoginRequest,\n options?: MedplumRequestOptions\n ): Promise<LoginAuthenticationResponse> {\n return this.post(\n 'auth/google',\n {\n ...(await this.ensureCodeChallenge(loginRequest)),\n clientId: loginRequest.clientId ?? this.clientId,\n scope: loginRequest.scope,\n },\n undefined,\n options\n ) as Promise<LoginAuthenticationResponse>;\n }\n\n /**\n * Returns the PKCE code challenge and method.\n * If the login request already includes a code challenge, it is returned.\n * Otherwise, a new PKCE code challenge is generated.\n * @category Authentication\n * @param loginRequest - The original login request.\n * @returns The PKCE code challenge and method.\n */\n async ensureCodeChallenge<T extends BaseLoginRequest>(loginRequest: T): Promise<T> {\n if (loginRequest.codeChallenge) {\n return loginRequest;\n }\n return { ...loginRequest, ...(await this.startPkce()) };\n }\n\n /**\n * Signs out the client.\n * This revokes the current token and clears token from the local cache.\n * @category Authentication\n */\n async signOut(): Promise<void> {\n await this.post(this.logoutUrl, {});\n this.clear();\n }\n\n /**\n * Tries to sign in the user.\n * Returns true if the user is signed in.\n * This may result in navigating away to the sign in page.\n * @category Authentication\n * @param loginParams - Optional login parameters.\n * @returns The user profile resource if available.\n */\n async signInWithRedirect(loginParams?: Partial<BaseLoginRequest>): Promise<ProfileResource | undefined> {\n const urlParams = new URLSearchParams(window.location.search);\n const code = urlParams.get('code');\n if (!code) {\n await this.requestAuthorization(loginParams);\n return undefined;\n }\n return this.processCode(code);\n }\n\n /**\n * Tries to sign out the user.\n * See: https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html\n * @category Authentication\n */\n signOutWithRedirect(): void {\n window.location.assign(this.logoutUrl);\n }\n\n /**\n * Initiates sign in with an external identity provider.\n * @param authorizeUrl - The external authorization URL.\n * @param clientId - The external client ID.\n * @param redirectUri - The external identity provider redirect URI.\n * @param baseLogin - The Medplum login request.\n * @param pkceEnabled - Whether `PKCE` should be enabled for this external auth request. Defaults to `true`.\n * @category Authentication\n */\n async signInWithExternalAuth(\n authorizeUrl: string,\n clientId: string,\n redirectUri: string,\n baseLogin: BaseLoginRequest,\n pkceEnabled = true\n ): Promise<void> {\n let loginRequest = baseLogin;\n if (pkceEnabled) {\n loginRequest = await this.ensureCodeChallenge(baseLogin);\n }\n window.location.assign(\n this.getExternalAuthRedirectUri(authorizeUrl, clientId, redirectUri, loginRequest, pkceEnabled)\n );\n }\n\n /**\n * Exchange an external access token for a Medplum access token.\n * @param token - The access token that was generated by the external identity provider.\n * @param clientId - The ID of the `ClientApplication` in your Medplum project that will be making the exchange request.\n * @returns The user profile resource.\n * @category Authentication\n */\n async exchangeExternalAccessToken(token: string, clientId?: string): Promise<ProfileResource> {\n clientId = clientId ?? this.clientId;\n if (!clientId) {\n throw new Error('MedplumClient is missing clientId');\n }\n\n const formBody = new URLSearchParams();\n formBody.set('grant_type', OAuthGrantType.TokenExchange);\n formBody.set('subject_token_type', OAuthTokenType.AccessToken);\n formBody.set('client_id', clientId);\n formBody.set('subject_token', token);\n return this.fetchTokens(formBody);\n }\n\n /**\n * Builds the external identity provider redirect URI.\n * @param authorizeUrl - The external authorization URL.\n * @param clientId - The external client ID.\n * @param redirectUri - The external identity provider redirect URI.\n * @param loginRequest - The Medplum login request.\n * @param pkceEnabled - Whether `PKCE` should be enabled for this external auth request. Defaults to `true`.\n * @returns The external identity provider redirect URI.\n * @category Authentication\n */\n getExternalAuthRedirectUri(\n authorizeUrl: string,\n clientId: string,\n redirectUri: string,\n loginRequest: BaseLoginRequest,\n pkceEnabled = true\n ): string {\n const url = new URL(authorizeUrl);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', clientId);\n url.searchParams.set('redirect_uri', redirectUri);\n url.searchParams.set('scope', loginRequest.scope ?? 'openid profile email');\n url.searchParams.set('state', JSON.stringify(loginRequest));\n\n if (pkceEnabled) {\n const { codeChallenge, codeChallengeMethod } = loginRequest;\n if (!codeChallengeMethod) {\n throw new Error('`LoginRequest` for external auth must include a `codeChallengeMethod`.');\n }\n if (!codeChallenge) {\n throw new Error('`LoginRequest` for external auth must include a `codeChallenge`.');\n }\n url.searchParams.set('code_challenge_method', codeChallengeMethod);\n url.searchParams.set('code_challenge', codeChallenge);\n }\n\n return url.toString();\n }\n\n /**\n * Builds a FHIR URL from a collection of URL path components.\n * For example, `fhirUrl('Patient', '123')` returns `fhir/R4/Patient/123`.\n * @category HTTP\n * @param path - The path component of the URL.\n * @returns The well-formed FHIR URL.\n */\n fhirUrl(...path: string[]): URL {\n return new URL(concatUrls(this.fhirBaseUrl, path.join('/')));\n }\n\n /**\n * Builds a FHIR search URL from a search query or structured query object.\n * @category HTTP\n * @category Search\n * @param resourceType - The FHIR resource type.\n * @param query - The FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.\n * @returns The well-formed FHIR URL.\n */\n fhirSearchUrl(resourceType: ResourceType, query: QueryTypes): URL {\n const url = this.fhirUrl(resourceType);\n if (query) {\n url.search = getQueryString(query);\n }\n return url;\n }\n\n /**\n * Sends a FHIR search request.\n *\n * @example\n * Example using a FHIR search string:\n *\n * ```typescript\n * const bundle = await client.search('Patient', 'name=Alice');\n * console.log(bundle);\n * ```\n *\n * @example\n * The return value is a FHIR bundle:\n *\n * ```json\n * {\n * \"resourceType\": \"Bundle\",\n * \"type\": \"searchset\",\n * \"entry\": [\n * {\n * \"resource\": {\n * \"resourceType\": \"Patient\",\n * \"name\": [\n * {\n * \"given\": [\n * \"George\"\n * ],\n * \"family\": \"Washington\"\n * }\n * ],\n * }\n * }\n * ]\n * }\n * ```\n *\n * @example\n * To query the count of a search, use the summary feature like so:\n *\n * ```typescript\n * const patients = medplum.search('Patient', '_summary=count');\n * ```\n *\n * See FHIR search for full details: https://www.hl7.org/fhir/search.html\n * @category Search\n * @param resourceType - The FHIR resource type.\n * @param query - Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.\n * @param options - Optional fetch options.\n * @returns Promise to the search result bundle.\n */\n search<K extends ResourceType>(\n resourceType: K,\n query?: QueryTypes,\n options?: MedplumRequestOptions\n ): ReadablePromise<Bundle<ExtractResource<K>>> {\n const url = this.fhirSearchUrl(resourceType, query);\n const cacheKey = 'search-' + url.toString();\n const cached = this.getCacheEntry(cacheKey, options);\n if (cached) {\n return cached.value;\n }\n const promise = this.getBundle<ExtractResource<K>>(url, options);\n this.setCacheEntry(cacheKey, promise);\n return promise;\n }\n\n /**\n * Sends a FHIR search request for a single resource.\n *\n * This is a convenience method for `search()` that returns the first resource rather than a `Bundle`.\n *\n * @example\n * Example using a FHIR search string:\n *\n * ```typescript\n * const patient = await client.searchOne('Patient', 'identifier=123');\n * console.log(patient);\n * ```\n *\n * The return value is the resource, if available; otherwise, undefined.\n *\n * See FHIR search for full details: https://www.hl7.org/fhir/search.html\n * @category Search\n * @param resourceType - The FHIR resource type.\n * @param query - Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.\n * @param options - Optional fetch options.\n * @returns Promise to the first search result.\n */\n searchOne<K extends ResourceType>(\n resourceType: K,\n query?: QueryTypes,\n options?: MedplumRequestOptions\n ): ReadablePromise<ExtractResource<K> | undefined> {\n const url = this.fhirSearchUrl(resourceType, query);\n url.searchParams.set('_count', '1');\n url.searchParams.sort();\n const cacheKey = 'searchOne-' + url.toString();\n const cached = this.getCacheEntry(cacheKey, options);\n if (cached) {\n return cached.value;\n }\n const promise = new ReadablePromise(\n this.search<K>(resourceType, url.searchParams, options).then((b) => b.entry?.[0]?.resource)\n );\n this.setCacheEntry(cacheKey, promise);\n return promise;\n }\n\n /**\n * Sends a FHIR search request for an array of resources.\n *\n * This is a convenience method for `search()` that returns the resources as an array rather than a `Bundle`.\n *\n * @example\n * Example using a FHIR search string:\n *\n * ```typescript\n * const patients = await client.searchResources('Patient', 'name=Alice');\n * console.log(patients);\n * ```\n *\n * The return value is an array of resources.\n *\n * See FHIR search for full details: https://www.hl7.org/fhir/search.html\n * @category Search\n * @param resourceType - The FHIR resource type.\n * @param query - Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.\n * @param options - Optional fetch options.\n * @returns Promise to the array of search results.\n */\n searchResources<K extends ResourceType>(\n resourceType: K,\n query?: QueryTypes,\n options?: MedplumRequestOptions\n ): ReadablePromise<ResourceArray<ExtractResource<K>>> {\n const url = this.fhirSearchUrl(resourceType, query);\n const cacheKey = 'searchResources-' + url.toString();\n const cached = this.getCacheEntry(cacheKey, options);\n if (cached) {\n return cached.value;\n }\n const promise = new ReadablePromise(this.search<K>(resourceType, query, options).then(bundleToResourceArray));\n this.setCacheEntry(cacheKey, promise);\n return promise;\n }\n\n /**\n * Creates an\n * [async generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator)\n * over a series of FHIR search requests for paginated search results. Each iteration of the generator yields\n * the array of resources on each page. Searches using _offset based pagination are limited to 10,000 records.\n * For larger result sets, _cursor based pagination should be used instead.\n * See: https://www.medplum.com/docs/search/paginated-search#cursor-based-pagination\n *\n * @example\n *\n * ```typescript\n * for await (const page of medplum.searchResourcePages('Patient', { _count: 10 })) {\n * for (const patient of page) {\n * console.log(`Processing Patient resource with ID: ${patient.id}`);\n * }\n * }\n * ```\n *\n * @category Search\n * @param resourceType - The FHIR resource type.\n * @param query - Optional FHIR search query or structured query object. Can be any valid input to the URLSearchParams() constructor.\n * @param options - Optional fetch options.\n * @yields An async generator, where each result is an array of resources for each page.\n */\n async *searchResourcePages<K extends ResourceType>(\n resourceType: K,\n query?: QueryTypes,\n options?: MedplumRequestOptions\n ): AsyncGenerator<ResourceArray<ExtractResource<K>>> {\n let url: URL | undefined = this.fhirSearchUrl(resourceType, query);\n\n while (url) {\n const searchParams: URLSearchParams = new URL(url).searchParams;\n const bundle = await this.search(resourceType, searchParams, options);\n const nextLink: BundleLink | undefined = bundle.link?.find((link) => link.relation === 'next');\n if (!bundle.entry?.length && !nextLink) {\n break;\n }\n\n yield bundleToResourceArray(bundle);\n url = nextLink?.url ? new URL(nextLink.url) : undefined;\n }\n }\n\n /**\n * Searches a ValueSet resource using the \"expand\" operation.\n * See: https://www.hl7.org/fhir/operation-valueset-expand.html\n * @category Search\n * @param system - The ValueSet system url.\n * @param filter - The search string.\n * @param options - Optional fetch options.\n * @returns Promise to expanded ValueSet.\n * @deprecated Use `valueSetExpand()` instead.\n */\n searchValueSet(system: string, filter: string, options?: MedplumRequestOptions): ReadablePromise<ValueSet> {\n return this.valueSetExpand({ url: system, filter }, options);\n }\n\n /**\n * Searches a ValueSet resource using the \"expand\" operation.\n * See: https://www.hl7.org/fhir/operation-valueset-expand.html\n * @category Search\n * @param params - The ValueSet expand parameters.\n * @param options - Optional fetch options.\n * @returns Promise to expanded ValueSet.\n */\n valueSetExpand(params: ValueSetExpandParams, options?: MedplumRequestOptions): ReadablePromise<ValueSet> {\n const url = this.fhirUrl('ValueSet', '$expand');\n url.search = new URLSearchParams(params as Record<string, string>).toString();\n return this.get(url.toString(), options);\n }\n\n /**\n * Returns a cached resource if it is available.\n * @category Caching\n * @param resourceType - The FHIR resource type.\n * @param id - The FHIR resource ID.\n * @returns The resource if it is available in the cache; undefined otherwise.\n */\n getCached<K extends ResourceType>(resourceType: K, id: string): ExtractResource<K> | undefined {\n const cached = this.requestCache?.get(this.fhirUrl(resourceType, id).toString())?.value;\n return cached?.isOk() ? (cached.read() as ExtractResource<K>) : undefined;\n }\n\n /**\n * Returns a cached resource if it is available.\n * @category Caching\n * @param reference - The FHIR reference.\n * @returns The resource if it is available in the cache; undefined otherwise.\n */\n getCachedReference<T extends Resource>(reference: Reference<T>): T | undefined {\n const refString = reference.reference as string;\n if (!refString) {\n return undefined;\n }\n if (refString === 'system') {\n return system as T;\n }\n const [resourceType, id] = refString.split('/');\n if (!resourceType || !id) {\n return undefined;\n }\n return this.getCached(resourceType as ResourceType, id) as T | undefined;\n }\n\n /**\n * Reads a resource by resource type and ID.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const patient = await medplum.readResource('Patient', '123');\n * console.log(patient);\n * ```\n *\n * See the FHIR \"read\" operation for full details: https://www.hl7.org/fhir/http.html#read\n * @category Read\n * @param resourceType - The FHIR resource type.\n * @param id - The resource ID.\n * @param options - Optional fetch options.\n * @returns The resource if available.\n */\n readResource<K extends ResourceType>(\n resourceType: K,\n id: string,\n options?: MedplumRequestOptions\n ): ReadablePromise<ExtractResource<K>> {\n if (!id) {\n throw new Error('The \"id\" parameter cannot be null, undefined, or an empty string.');\n }\n return this.get<ExtractResource<K>>(this.fhirUrl(resourceType, id), options);\n }\n\n /**\n * Reads a resource by `Reference`.\n *\n * This is a convenience method for `readResource()` that accepts a `Reference` object.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const serviceRequest = await medplum.readResource('ServiceRequest', '123');\n * const patient = await medplum.readReference(serviceRequest.subject);\n * console.log(patient);\n * ```\n *\n * See the FHIR \"read\" operation for full details: https://www.hl7.org/fhir/http.html#read\n * @category Read\n * @param reference - The FHIR reference object.\n * @param options - Optional fetch options.\n * @returns The resource if available.\n */\n readReference<T extends Resource>(reference: Reference<T>, options?: MedplumRequestOptions): ReadablePromise<T> {\n const refString = reference.reference;\n if (!refString) {\n return new ReadablePromise(Promise.reject(new Error('Missing reference')));\n }\n if (refString === 'system') {\n return new ReadablePromise(Promise.resolve(system as unknown as T));\n }\n const [resourceType, id] = refString.split('/');\n if (!resourceType || !id) {\n return new ReadablePromise(Promise.reject(new Error('Invalid reference')));\n }\n return this.readResource(resourceType as ResourceType, id, options) as ReadablePromise<T>;\n }\n\n /**\n * Requests the schema for a resource type.\n * If the schema is already cached, the promise is resolved immediately.\n * @category Schema\n * @param resourceType - The FHIR resource type.\n * @returns Promise to a schema with the requested resource type.\n */\n requestSchema(resourceType: string): Promise<void> {\n if (isDataTypeLoaded(resourceType)) {\n return Promise.resolve();\n }\n\n const cacheKey = resourceType + '-requestSchema';\n const cached = this.getCacheEntry(cacheKey, undefined);\n if (cached) {\n return cached.value;\n }\n\n const promise = new ReadablePromise<void>(\n (async () => {\n const query = `{\n StructureDefinitionList(name: \"${resourceType}\") {\n resourceType,\n name,\n kind,\n description,\n type,\n url,\n snapshot {\n element {\n id,\n path,\n definition,\n min,\n max,\n base {\n path,\n min,\n max\n },\n contentReference,\n type {\n code,\n profile,\n targetProfile\n },\n binding {\n strength,\n valueSet\n }\n }\n }\n }\n SearchParameterList(base: \"${resourceType}\", _count: 100) {\n base,\n code,\n type,\n expression,\n target\n }\n }`.replace(/\\s+/g, ' ');\n\n const response = (await this.graphql(query)) as SchemaGraphQLResponse;\n\n indexStructureDefinitionBundle(response.data.StructureDefinitionList.filter((sd) => sd.name === resourceType));\n\n for (const searchParameter of response.data.SearchParameterList) {\n indexSearchParameter(searchParameter);\n }\n })()\n );\n this.setCacheEntry(cacheKey, promise);\n return promise;\n }\n\n /**\n * Requests the schema for a profile.\n * If the schema is already cached, the promise is resolved immediately.\n * @category Schema\n * @param profileUrl - The FHIR URL of the profile\n * @param options - (optional) Additional options\n * @returns Promise for schema request.\n */\n requestProfileSchema(profileUrl: string, options?: RequestProfileSchemaOptions): Promise<void> {\n if (!options?.expandProfile && isProfileLoaded(profileUrl)) {\n return Promise.resolve();\n }\n\n const cacheKey = profileUrl + '-requestSchema' + (options?.expandProfile ? '-nested' : '');\n const cached = this.getCacheEntry(cacheKey, undefined);\n if (cached) {\n return cached.value;\n }\n\n const promise = new ReadablePromise<void>(\n (async () => {\n if (options?.expandProfile) {\n const url = this.fhirUrl('StructureDefinition', '$expand-profile');\n url.search = new URLSearchParams({ url: profileUrl }).toString();\n const sdBundle = (await this.post(url.toString(), {})) as Bundle<StructureDefinition>;\n indexStructureDefinitionBundle(sdBundle);\n } else {\n // Just sort by lastUpdated. Ideally, it would also be based on a logical sort of version\n // See https://hl7.org/fhir/references.html#canonical-matching for more discussion\n const sd = await this.searchOne('StructureDefinition', {\n url: profileUrl,\n _sort: '-_lastUpdated',\n });\n\n if (!sd) {\n console.warn(`No StructureDefinition found for ${profileUrl}!`);\n return;\n }\n\n loadDataType(sd);\n }\n })()\n );\n this.setCacheEntry(cacheKey, promise);\n return promise;\n }\n\n /**\n * Reads resource history by resource type and ID.\n *\n * The return value is a bundle of all versions of the resource.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const history = await medplum.readHistory('Patient', '123');\n * console.log(history);\n * ```\n *\n * See the FHIR \"history\" operation for full details: https://www.hl7.org/fhir/http.html#history\n * @category Read\n * @param resourceType - The FHIR resource type.\n * @param id - The resource ID.\n * @param options - Optional fetch options.\n * @returns Promise to the resource history.\n */\n readHistory<K extends ResourceType>(\n resourceType: K,\n id: string,\n options?: MedplumRequestOptions\n ): ReadablePromise<Bundle<ExtractResource<K>>> {\n return this.get(this.fhirUrl(resourceType, id, '_history'), options);\n }\n\n /**\n * Reads a specific version of a resource by resource type, ID, and version ID.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const version = await medplum.readVersion('Patient', '123', '456');\n * console.log(version);\n * ```\n *\n * See the FHIR \"vread\" operation for full details: https://www.hl7.org/fhir/http.html#vread\n * @category Read\n * @param resourceType - The FHIR resource type.\n * @param id - The resource ID.\n * @param vid - The version ID.\n * @param options - Optional fetch options.\n * @returns The resource if available.\n */\n readVersion<K extends ResourceType>(\n resourceType: K,\n id: string,\n vid: string,\n options?: MedplumRequestOptions\n ): ReadablePromise<ExtractResource<K>> {\n return this.get(this.fhirUrl(resourceType, id, '_history', vid), options);\n }\n\n /**\n * Executes the Patient \"everything\" operation for a patient.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const bundle = await medplum.readPatientEverything('123');\n * console.log(bundle);\n * ```\n *\n * See the FHIR \"patient-everything\" operation for full details: https://hl7.org/fhir/operation-patient-everything.html\n * @category Read\n * @param id - The Patient Id\n * @param options - Optional fetch options.\n * @returns A Bundle of all Resources related to the Patient\n */\n readPatientEverything(id: string, options?: MedplumRequestOptions): ReadablePromise<Bundle> {\n return this.getBundle(this.fhirUrl('Patient', id, '$everything'), options);\n }\n\n /**\n * Executes the Patient \"summary\" operation for a patient.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const bundle = await medplum.readPatientSummary('123');\n * console.log(bundle);\n * ```\n *\n * See International Patient Summary Implementation Guide: https://build.fhir.org/ig/HL7/fhir-ips/index.html\n *\n * See Patient summary operation: https://build.fhir.org/ig/HL7/fhir-ips/OperationDefinition-summary.html\n *\n * @param id - The Patient ID.\n * @param options - Optional fetch options.\n * @returns A patient summary bundle, organized into the patient summary sections.\n */\n readPatientSummary(id: string, options?: MedplumRequestOptions): ReadablePromise<Bundle> {\n return this.getBundle(this.fhirUrl('Patient', id, '$summary'), options);\n }\n\n /**\n * Creates a new FHIR resource.\n *\n * The return value is the newly created resource, including the ID and meta.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.createResource({\n * resourceType: 'Patient',\n * name: [{\n * family: 'Smith',\n * given: ['John']\n * }]\n * });\n * console.log(result.id);\n * ```\n *\n * See the FHIR \"create\" operation for full details: https://www.hl7.org/fhir/http.html#create\n * @category Create\n * @param resource - The FHIR resource to create.\n * @param options - Optional fetch options.\n * @returns The result of the create operation.\n */\n createResource<T extends Resource>(resource: T, options?: MedplumRequestOptions): Promise<T> {\n if (!resource.resourceType) {\n throw new Error('Missing resourceType');\n }\n this.invalidateSearches(resource.resourceType);\n return this.post(this.fhirUrl(resource.resourceType), resource, undefined, options);\n }\n\n /**\n * Conditionally create a new FHIR resource only if some equivalent resource does not already exist on the server.\n *\n * The return value is the existing resource or the newly created resource, including the ID and meta.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.createResourceIfNoneExist(\n * {\n * resourceType: 'Patient',\n * identifier: [{\n * system: 'http://example.com/mrn',\n * value: '123'\n * }]\n * name: [{\n * family: 'Smith',\n * given: ['John']\n * }]\n * },\n * 'identifier=123'\n * );\n * console.log(result.id);\n * ```\n *\n * This method is syntactic sugar for:\n *\n * ```typescript\n * return searchOne(resourceType, query) ?? createResource(resource);\n * ```\n *\n * The query parameter only contains the search parameters (what would be in the URL following the \"?\").\n *\n * See the FHIR \"conditional create\" operation for full details: https://www.hl7.org/fhir/http.html#ccreate\n * @category Create\n * @param resource - The FHIR resource to create.\n * @param query - The search query for an equivalent resource (should not include resource type or \"?\").\n * @param options - Optional fetch options.\n * @returns The result of the create operation.\n */\n async createResourceIfNoneExist<T extends Resource>(\n resource: T,\n query: string,\n options?: MedplumRequestOptions\n ): Promise<T> {\n const url = this.fhirUrl(resource.resourceType);\n if (!options) {\n options = { headers: { 'If-None-Exist': query } };\n } else if (!options.headers) {\n options.headers = { 'If-None-Exist': query };\n } else if (Array.isArray(options.headers)) {\n options.headers.push(['If-None-Exist', query]);\n } else if (options.headers instanceof Headers) {\n options.headers.set('If-None-Exist', query);\n } else {\n options.headers['If-None-Exist'] = query;\n }\n\n const result = await this.post(url, resource, undefined, options);\n this.cacheResource(result);\n this.invalidateUrl(this.fhirUrl(resource.resourceType, resource.id as string, '_history'));\n this.invalidateSearches(resource.resourceType);\n return result;\n }\n\n /**\n * Upsert a resource: update it in place if it exists, otherwise create it. This is done in a single, transactional\n * request to guarantee data consistency.\n * @param resource - The resource to update or create.\n * @param query - A FHIR search query to uniquely identify the resource if it already exists.\n * @param options - Optional fetch options.\n * @returns The updated/created resource.\n */\n async upsertResource<T extends Resource>(\n resource: T,\n query: QueryTypes,\n options?: MedplumRequestOptions\n ): Promise<T> {\n // Build conditional update URL, e.g. `PUT /ResourceType?search-param=value`\n const url = this.fhirSearchUrl(resource.resourceType, query);\n\n let result = await this.put(url, resource, undefined, options);\n if (!result) {\n // On 304 not modified, result will be undefined\n // Return the user input instead\n result = resource;\n }\n this.cacheResource(result);\n this.invalidateUrl(this.fhirUrl(resource.resourceType, resource.id as string, '_history'));\n this.invalidateSearches(resource.resourceType);\n return result;\n }\n\n /**\n * Creates a FHIR `Attachment` with the provided data content.\n *\n * This is a convenience method for creating a `Binary` resource and then creating an `Attachment` element.\n *\n * The `data` parameter can be a string or a `File` object.\n *\n * A `File` object often comes from a `<input type=\"file\">` element.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.createAttachment(myFile, 'test.jpg', 'image/jpeg');\n * console.log(result);\n * ```\n *\n * See the FHIR \"create\" operation for full details: https://www.hl7.org/fhir/http.html#create\n * @category Create\n * @param createBinaryOptions -The binary options. See `CreateBinaryOptions` for full details.\n * @param requestOptions - Optional fetch options. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @returns The result of the create operation.\n */\n createAttachment(\n createBinaryOptions: CreateBinaryOptions,\n requestOptions?: MedplumRequestOptions\n ): Promise<Attachment>;\n\n /**\n * @category Create\n * @param data - The binary data to upload.\n * @param filename - Optional filename for the binary.\n * @param contentType - Content type for the binary.\n * @param onProgress - Optional callback for progress events. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @param options - Optional fetch options. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @returns The result of the create operation.\n * @deprecated Use `createAttachment` with `CreateBinaryOptions` instead. To be removed in Medplum 4.0.\n */\n createAttachment(\n data: BinarySource,\n filename: string | undefined,\n contentType: string,\n onProgress?: (e: ProgressEvent) => void,\n options?: MedplumRequestOptions\n ): Promise<Attachment>;\n\n async createAttachment(\n arg1: BinarySource | CreateBinaryOptions,\n arg2: string | undefined | MedplumRequestOptions,\n arg3?: string,\n arg4?: (e: ProgressEvent) => void,\n arg5?: MedplumRequestOptions\n ): Promise<Attachment> {\n let createBinaryOptions = normalizeCreateBinaryOptions(arg1, arg2, arg3, arg4);\n\n if (createBinaryOptions.contentType === ContentType.XML) {\n const fileData = createBinaryOptions.data;\n let fileStr: string;\n\n if (fileData instanceof Blob) {\n fileStr = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (!reader.result) {\n reject(new Error('Failed to load file'));\n return;\n }\n resolve(reader.result as string);\n };\n reader.readAsText(fileData, 'utf-8');\n });\n } else if (ArrayBuffer.isView(fileData)) {\n fileStr = new TextDecoder().decode(fileData);\n } else {\n fileStr = fileData;\n }\n\n // Both of the above strings are required to be within a valid C-CDA document\n // The root element in a CDA document should be a \"ClinicalDocument\"\n // \"urn:hl7-org:v3\" is a required namespace to be referenced by all valid C-CDA documents as well\n if (fileStr.includes('<ClinicalDocument') && fileStr.includes('urn:hl7-org:v3')) {\n createBinaryOptions = { ...createBinaryOptions, contentType: ContentType.CDA_XML };\n }\n }\n\n const requestOptions = arg5 ?? (typeof arg2 === 'object' ? arg2 : {});\n const binary = await this.createBinary(createBinaryOptions, requestOptions);\n return {\n contentType: createBinaryOptions.contentType,\n url: binary.url,\n title: createBinaryOptions.filename,\n };\n }\n\n /**\n * Creates a FHIR `Binary` resource with the provided data content.\n *\n * The return value is the newly created resource, including the ID and meta.\n *\n * The `data` parameter can be a string or a `File` object.\n *\n * A `File` object often comes from a `<input type=\"file\">` element.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.createBinary(myFile, 'test.jpg', 'image/jpeg');\n * console.log(result.id);\n * ```\n *\n * See the FHIR \"create\" operation for full details: https://www.hl7.org/fhir/http.html#create\n *\n * @category Create\n * @param createBinaryOptions -The binary options. See `CreateBinaryOptions` for full details.\n * @param requestOptions - Optional fetch options. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @returns The result of the create operation.\n */\n createBinary(createBinaryOptions: CreateBinaryOptions, requestOptions?: MedplumRequestOptions): Promise<Binary>;\n\n /**\n * @category Create\n * @param data - The binary data to upload.\n * @param filename - Optional filename for the binary.\n * @param contentType - Content type for the binary.\n * @param onProgress - Optional callback for progress events. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @param options - Optional fetch options. **NOTE:** only `options.signal` is respected when `onProgress` is also provided.\n * @returns The result of the create operation.\n * @deprecated Use `createBinary` with `CreateBinaryOptions` instead. To be removed in Medplum 4.0.\n */\n createBinary(\n data: BinarySource,\n filename: string | undefined,\n contentType: string,\n onProgress?: (e: ProgressEvent) => void,\n options?: MedplumRequestOptions\n ): Promise<Binary>;\n\n createBinary(\n arg1: BinarySource | CreateBinaryOptions,\n arg2: string | undefined | MedplumRequestOptions,\n arg3?: string,\n arg4?: (e: ProgressEvent) => void,\n arg5?: MedplumRequestOptions\n ): Promise<Binary> {\n const createBinaryOptions = normalizeCreateBinaryOptions(arg1, arg2, arg3, arg4);\n const requestOptions = arg5 ?? (typeof arg2 === 'object' ? arg2 : {});\n\n const { data, contentType, filename, securityContext, onProgress } = createBinaryOptions;\n\n const url = this.fhirUrl('Binary');\n if (filename) {\n url.searchParams.set('_filename', filename);\n }\n\n if (securityContext?.reference) {\n this.setRequestHeader(requestOptions, 'X-Security-Context', securityContext.reference);\n }\n\n if (onProgress) {\n return this.uploadwithProgress(url, data, contentType, onProgress, requestOptions);\n }\n return this.post(url, data, contentType, requestOptions);\n }\n\n uploadwithProgress(\n url: URL,\n data: BinarySource,\n contentType: string,\n onProgress: (e: ProgressEvent) => void,\n options?: MedplumRequestOptions\n ): Promise<any> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n // Ensure the 'abort' event listener is removed from the signal to prevent memory leaks,\n // especially in scenarios where there is a long-lived signal across multiple requests.\n const handleSignalAbort = (): void => xhr.abort();\n options?.signal?.addEventListener('abort', handleSignalAbort);\n const sendResult = (result: any): void => {\n options?.signal?.removeEventListener('abort', handleSignalAbort);\n\n if (result instanceof Error) {\n reject(result);\n } else {\n resolve(result);\n }\n };\n\n xhr.responseType = 'json';\n xhr.onabort = () => sendResult(new DOMException('Request aborted', 'AbortError'));\n xhr.onerror = () => sendResult(new Error('Request error'));\n\n if (onProgress) {\n xhr.upload.onprogress = (e) => onProgress(e);\n xhr.upload.onload = (e) => onProgress(e);\n }\n\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n sendResult(xhr.response);\n } else {\n sendResult(new OperationOutcomeError(normalizeOperationOutcome(xhr.response || xhr.statusText)));\n }\n };\n\n xhr.open('POST', url);\n xhr.withCredentials = true;\n xhr.setRequestHeader('Authorization', 'Bearer ' + this.accessToken);\n xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, max-age=0');\n xhr.setRequestHeader('Content-Type', contentType);\n\n if (this.options.extendedMode !== false) {\n xhr.setRequestHeader('X-Medplum', 'extended');\n }\n\n if (options?.headers) {\n const headers = options.headers as Record<string, string>;\n for (const [key, value] of Object.entries(headers)) {\n xhr.setRequestHeader(key, value);\n }\n }\n\n xhr.send(data);\n });\n }\n\n /**\n * Creates a PDF as a FHIR `Binary` resource based on pdfmake document definition.\n *\n * The return value is the newly created resource, including the ID and meta.\n *\n * The `docDefinition` parameter is a pdfmake document definition.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.createPdf({\n * content: ['Hello world']\n * });\n * console.log(result.id);\n * ```\n *\n * See the pdfmake document definition for full details: https://pdfmake.github.io/docs/0.1/document-definition-object/\n * @category Media\n * @param createPdfOptions - The PDF creation options. See `CreatePdfOptions` for full details.\n * @param requestOptions - Optional fetch options.\n * @returns The result of the create operation.\n */\n createPdf(createPdfOptions: CreatePdfOptions, requestOptions?: MedplumRequestOptions): Promise<Binary>;\n\n /**\n * @category Media\n * @param docDefinition - The PDF document definition.\n * @param filename - Optional filename for the PDF binary resource.\n * @param tableLayouts - Optional pdfmake custom table layout.\n * @param fonts - Optional pdfmake custom font dictionary.\n * @returns The result of the create operation.\n * @deprecated Use `createPdf` with `CreatePdfOptions` instead. To be removed in Medplum 4.0.\n */\n createPdf(\n docDefinition: TDocumentDefinitions,\n filename: string | undefined,\n tableLayouts?: Record<string, CustomTableLayout>,\n fonts?: TFontDictionary\n ): Promise<Binary>;\n\n async createPdf(\n arg1: TDocumentDefinitions | CreatePdfOptions,\n arg2?: string | MedplumRequestOptions,\n arg3?: Record<string, CustomTableLayout>,\n arg4?: TFontDictionary\n ): Promise<Binary> {\n if (!this.createPdfImpl) {\n throw new Error('PDF creation not enabled');\n }\n const createPdfOptions = normalizeCreatePdfOptions(arg1, arg2, arg3, arg4);\n const requestOptions = typeof arg2 === 'object' ? arg2 : {};\n const { docDefinition, tableLayouts, fonts, ...rest } = createPdfOptions;\n const blob = await this.createPdfImpl(docDefinition, tableLayouts, fonts);\n const createBinaryOptions = { ...rest, data: blob, contentType: 'application/pdf' };\n return this.createBinary(createBinaryOptions, requestOptions);\n }\n\n /**\n * Creates a FHIR `Communication` resource with the provided data content.\n *\n * This is a convenience method to handle common cases where a `Communication` resource is created with a `payload`.\n * @category Create\n * @param resource - The FHIR resource to comment on.\n * @param text - The text of the comment.\n * @param options - Optional fetch options.\n * @returns The result of the create operation.\n */\n createComment(resource: Resource, text: string, options?: MedplumRequestOptions): Promise<Communication> {\n const profile = this.getProfile();\n let encounter: Reference<Encounter> | undefined = undefined;\n let subject: Reference<Patient> | undefined = undefined;\n\n if (resource.resourceType === 'Encounter') {\n encounter = createReference(resource);\n subject = resource.subject as Reference<Patient> | undefined;\n }\n\n if (resource.resourceType === 'ServiceRequest') {\n encounter = resource.encounter;\n subject = resource.subject as Reference<Patient> | undefined;\n }\n\n if (resource.resourceType === 'Patient') {\n subject = createReference(resource);\n }\n\n return this.createResource<Communication>(\n {\n resourceType: 'Communication',\n status: 'completed',\n basedOn: [createReference(resource)],\n encounter,\n subject,\n sender: profile ? createReference(profile) : undefined,\n sent: new Date().toISOString(),\n payload: [{ contentString: text }],\n },\n options\n );\n }\n\n /**\n * Updates a FHIR resource.\n *\n * The return value is the updated resource, including the ID and meta.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.updateResource({\n * resourceType: 'Patient',\n * id: '123',\n * name: [{\n * family: 'Smith',\n * given: ['John']\n * }]\n * });\n * console.log(result.meta.versionId);\n * ```\n *\n * See the FHIR \"update\" operation for full details: https://www.hl7.org/fhir/http.html#update\n * @category Write\n * @param resource - The FHIR resource to update.\n * @param options - Optional fetch options.\n * @returns The result of the update operation.\n */\n async updateResource<T extends Resource>(resource: T, options?: MedplumRequestOptions): Promise<T> {\n if (!resource.resourceType) {\n throw new Error('Missing resourceType');\n }\n if (!resource.id) {\n throw new Error('Missing id');\n }\n let result = await this.put(this.fhirUrl(resource.resourceType, resource.id), resource, undefined, options);\n if (!result) {\n // On 304 not modified, result will be undefined\n // Return the user input instead\n result = resource;\n }\n this.cacheResource(result);\n this.invalidateUrl(this.fhirUrl(resource.resourceType, resource.id, '_history'));\n this.invalidateSearches(resource.resourceType);\n return result;\n }\n\n /**\n * Updates a FHIR resource using JSONPatch operations.\n *\n * The return value is the updated resource, including the ID and meta.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.patchResource('Patient', '123', [\n * {op: 'replace', path: '/name/0/family', value: 'Smith'},\n * ]);\n * console.log(result.meta.versionId);\n * ```\n *\n * See the FHIR \"update\" operation for full details: https://www.hl7.org/fhir/http.html#patch\n *\n * See the JSONPatch specification for full details: https://tools.ietf.org/html/rfc6902\n * @category Write\n * @param resourceType - The FHIR resource type.\n * @param id - The resource ID.\n * @param operations - The JSONPatch operations.\n * @param options - Optional fetch options.\n * @returns The result of the patch operations.\n */\n async patchResource<K extends ResourceType>(\n resourceType: K,\n id: string,\n operations: PatchOperation[],\n options?: MedplumRequestOptions\n ): Promise<ExtractResource<K>> {\n const result = await this.patch(this.fhirUrl(resourceType, id), operations, options);\n this.cacheResource(result);\n this.invalidateUrl(this.fhirUrl(resourceType, id, '_history'));\n this.invalidateSearches(resourceType);\n return result;\n }\n\n /**\n * Deletes a FHIR resource by resource type and ID.\n *\n * @example\n * Example:\n *\n * ```typescript\n * await medplum.deleteResource('Patient', '123');\n * ```\n *\n * See the FHIR \"delete\" operation for full details: https://www.hl7.org/fhir/http.html#delete\n * @category Delete\n * @param resourceType - The FHIR resource type.\n * @param id - The resource ID.\n * @param options - Optional fetch options.\n * @returns The result of the delete operation.\n */\n deleteResource(resourceType: ResourceType, id: string, options?: MedplumRequestOptions): Promise<any> {\n this.deleteCacheEntry(this.fhirUrl(resourceType, id).toString());\n this.invalidateSearches(resourceType);\n return this.delete(this.fhirUrl(resourceType, id), options);\n }\n\n /**\n * Executes the validate operation with the provided resource.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.validateResource({\n * resourceType: 'Patient',\n * name: [{ given: ['Alice'], family: 'Smith' }],\n * });\n * ```\n *\n * See the FHIR \"$validate\" operation for full details: https://www.hl7.org/fhir/resource-operation-validate.html\n * @param resource - The FHIR resource.\n * @param options - Optional fetch options.\n * @returns The validate operation outcome.\n */\n validateResource<T extends Resource>(resource: T, options?: MedplumRequestOptions): Promise<OperationOutcome> {\n return this.post(this.fhirUrl(resource.resourceType, '$validate'), resource, undefined, options);\n }\n\n /**\n * Executes a bot by ID or Identifier.\n * @param idOrIdentifier - The Bot ID or Identifier.\n * @param body - The content body. Strings and `File` objects are passed directly. Other objects are converted to JSON.\n * @param contentType - The content type to be included in the \"Content-Type\" header.\n * @param options - Optional fetch options.\n * @returns The Bot return value.\n */\n executeBot(\n idOrIdentifier: string | Identifier,\n body: any,\n contentType?: string,\n options?: MedplumRequestOptions\n ): Promise<any> {\n let url: URL;\n if (typeof idOrIdentifier === 'string') {\n const id = idOrIdentifier;\n url = this.fhirUrl('Bot', id, '$execute');\n } else {\n const identifier = idOrIdentifier;\n url = this.fhirUrl('Bot', '$execute');\n url.searchParams.set('identifier', identifier.system + '|' + identifier.value);\n }\n return this.post(url, body, contentType, options);\n }\n\n /**\n * Executes a batch or transaction of FHIR operations.\n *\n * @example\n * Example:\n *\n * ```typescript\n * await medplum.executeBatch({\n * \"resourceType\": \"Bundle\",\n * \"type\": \"transaction\",\n * \"entry\": [\n * {\n * \"fullUrl\": \"urn:uuid:61ebe359-bfdc-4613-8bf2-c5e300945f0a\",\n * \"resource\": {\n * \"resourceType\": \"Patient\",\n * \"name\": [{ \"use\": \"official\", \"given\": [\"Alice\"], \"family\": \"Smith\" }],\n * \"gender\": \"female\",\n * \"birthDate\": \"1974-12-25\"\n * },\n * \"request\": {\n * \"method\": \"POST\",\n * \"url\": \"Patient\"\n * }\n * },\n * {\n * \"fullUrl\": \"urn:uuid:88f151c0-a954-468a-88bd-5ae15c08e059\",\n * \"resource\": {\n * \"resourceType\": \"Patient\",\n * \"identifier\": [{ \"system\": \"http:/example.org/fhir/ids\", \"value\": \"234234\" }],\n * \"name\": [{ \"use\": \"official\", \"given\": [\"Bob\"], \"family\": \"Jones\" }],\n * \"gender\": \"male\",\n * \"birthDate\": \"1974-12-25\"\n * },\n * \"request\": {\n * \"method\": \"POST\",\n * \"url\": \"Patient\",\n * \"ifNoneExist\": \"identifier=http:/example.org/fhir/ids|234234\"\n * }\n * }\n * ]\n * });\n * ```\n *\n * See The FHIR \"batch/transaction\" section for full details: https://hl7.org/fhir/http.html#transaction\n * @category Batch\n * @param bundle - The FHIR batch/transaction bundle.\n * @param options - Optional fetch options.\n * @returns The FHIR batch/transaction response bundle.\n */\n executeBatch(bundle: Bundle, options?: MedplumRequestOptions): Promise<Bundle> {\n return this.post(this.fhirBaseUrl, bundle, undefined, options);\n }\n\n /**\n * Sends an email using the Medplum Email API.\n *\n * Builds the email using nodemailer MailComposer.\n *\n * Examples:\n *\n * @example\n * Send a simple text email:\n *\n * ```typescript\n * await medplum.sendEmail({\n * to: 'alice@example.com',\n * cc: 'bob@example.com',\n * subject: 'Hello',\n * text: 'Hello Alice',\n * });\n * ```\n *\n * @example\n * Send an email with a `Binary` attachment:\n *\n * ```typescript\n * await medplum.sendEmail({\n * to: 'alice@example.com',\n * subject: 'Email with attachment',\n * text: 'See the attached report',\n * attachments: [{\n * filename: 'report.pdf',\n * path: \"Binary/\" + binary.id\n * }]\n * });\n * ```\n *\n * See options here: https://nodemailer.com/extras/mailcomposer/\n * @category Media\n * @param email - The MailComposer options.\n * @param options - Optional fetch options.\n * @returns Promise to the operation outcome.\n */\n sendEmail(email: MailOptions, options?: MedplumRequestOptions): Promise<OperationOutcome> {\n return this.post('email/v1/send', email, ContentType.JSON, options);\n }\n\n /**\n * Executes a GraphQL query.\n *\n * @example\n * Example:\n *\n * ```typescript\n * const result = await medplum.graphql(`{\n * Patient(id: \"123\") {\n * resourceType\n * id\n * name {\n * given\n * family\n * }\n * }\n * }`);\n * ```\n *\n * @example\n * Advanced queries such as named operations and variable substitution are supported:\n *\n * ```typescript\n * const result = await medplum.graphql(\n * `query GetPatientById($patientId: ID!) {\n * Patient(id: $patientId) {\n * resourceType\n * id\n * name {\n * given\n * family\n * }\n * }\n * }`,\n * 'GetPatientById',\n * { patientId: '123' }\n * );\n * ```\n *\n * See the GraphQL documentation for more details: https://graphql.org/learn/\n *\n * See the FHIR GraphQL documentation for FHIR specific details: https://www.hl7.org/fhir/graphql.html\n * @category Read\n * @param query - The GraphQL query.\n * @param operationName - Optional GraphQL operation name.\n * @param variables - Optional GraphQL variables.\n * @param options - Optional fetch options.\n * @returns The GraphQL result.\n */\n graphql(\n query: string,\n operationName?: string | null,\n variables?: any,\n options?: MedplumRequestOptions\n ): Promise<any> {\n return this.post(this.fhirUrl('$graphql'), { query, operationName, variables }, ContentType.JSON, options);\n }\n\n /**\n * Executes the $graph operation on this resource to fetch a Bundle of resources linked to the target resource\n * according to a graph definition\n * @category Read\n * @param resourceType - The FHIR resource type.\n * @param id - The resource ID.\n * @param graphName - `name` parameter of the GraphDefinition\n * @param options - Optional fetch options.\n * @returns A Bundle\n */\n readResourceGraph<K extends ResourceType>(\n resourceType: K,\n id: string,\n graphName: string,\n options?: MedplumRequestOptions\n ): ReadablePromise<Bundle> {\n return this.get<Bundle>(`${this.fhirUrl(resourceType, id)}/$graph?graph=${graphName}`, options);\n }\n\n /**\n * Pushes a message to an agent.\n *\n * @param agent - The agent to push to.\n * @param destination - The destination device.\n * @param body - The message body.\n * @param contentType - Optional message content type.\n * @param waitForResponse - Optional wait for response flag.\n * @param options - Optional fetch options.\n * @returns Promise to the result. If waiting for response, the result is the response body. Otherwise, it is an operation outcome.\n */\n pushToAgent(\n agent: Agent | Reference<Agent>,\n destination: Device | Reference<Device> | string,\n body: any,\n contentType?: string,\n waitForResponse?: boolean,\n options?: MedplumRequestOptions\n ): Promise<any> {\n return this.post(\n this.fhirUrl('Agent', resolveId(agent) as string, '$push'),\n {\n destination: typeof destination === 'string' ? destination : getReferenceString(destination),\n body,\n contentType,\n waitForResponse,\n },\n ContentType.FHIR_JSON,\n options\n );\n }\n\n /**\n * @category Authentication\n * @returns The Login State\n */\n getActiveLogin(): LoginState | undefined {\n return this.storage.getObject('activeLogin');\n }\n\n /**\n * Sets the active login.\n * @param login - The new active login state.\n * @category Authentication\n */\n async setActiveLogin(login: LoginState): Promise<void> {\n if (!this.sessionDetails?.profile || getReferenceString(this.sessionDetails.profile) !== login.profile?.reference) {\n this.clearActiveLogin();\n }\n this.setAccessToken(login.accessToken, login.refreshToken);\n this.storage.setObject('activeLogin', login);\n this.addLogin(login);\n this.refreshPromise = undefined;\n await this.refreshProfile();\n }\n\n /**\n * Returns the current access token.\n * @returns The current access token.\n * @category Authentication\n */\n getAccessToken(): string | undefined {\n return this.accessToken;\n }\n\n /**\n * Returns whether the client has a valid access token or not.\n * @param gracePeriod - Optional grace period in milliseconds. If not specified, uses the client configured grace period (default 5 minutes).\n * @returns Boolean indicating whether or not the client is authenticated.\n *\n * **NOTE: Does not check whether the auth token has been revoked server-side.**\n */\n isAuthenticated(gracePeriod?: number): boolean {\n return (\n this.accessTokenExpires !== undefined &&\n Date.now() < this.accessTokenExpires - (gracePeriod ?? this.refreshGracePeriod)\n );\n }\n\n /**\n * Sets the current access token.\n * @param accessToken - The new access token.\n * @param refreshToken - Optional refresh token.\n * @category Authentication\n */\n setAccessToken(accessToken: string, refreshToken?: string): void {\n this.accessToken = accessToken;\n this.refreshToken = refreshToken;\n this.accessTokenExpires = tryGetJwtExpiration(accessToken);\n this.medplumServer = isMedplumAccessToken(accessToken);\n }\n\n /**\n * Returns the list of available logins.\n * @returns The list of available logins.\n * @category Authentication\n */\n getLogins(): LoginState[] {\n return this.storage.getObject<LoginState[]>('logins') ?? [];\n }\n\n private addLogin(newLogin: LoginState): void {\n const logins = this.getLogins().filter((login) => login.profile?.reference !== newLogin.profile?.reference);\n logins.push(newLogin);\n this.storage.setObject('logins', logins);\n }\n\n private async refreshProfile(): Promise<ProfileResource | undefined> {\n if (!this.medplumServer) {\n return Promise.resolve(undefined);\n }\n\n this.profilePromise = new Promise((resolve, reject) => {\n this.get('auth/me', { cache: 'no-cache' })\n .then((result: SessionDetails) => {\n this.profilePromise = undefined;\n const profileChanged = this.sessionDetails?.profile?.id !== result.profile.id;\n this.sessionDetails = result;\n if (profileChanged) {\n this.dispatchEvent({ type: 'change' });\n }\n resolve(result.profile);\n this.dispatchEvent({ type: 'profileRefreshed' });\n })\n .catch(reject);\n });\n\n this.dispatchEvent({ type: 'profileRefreshing' });\n return this.profilePromise;\n }\n\n /**\n * Returns true if the client is waiting for initial authentication.\n * @returns True if the client is waiting for initial authentication.\n * @category Authentication\n */\n isLoading(): boolean {\n return !this.isInitialized || (Boolean(this.profilePromise) && !this.sessionDetails?.profile);\n }\n\n /**\n * Returns true if the current user is authenticated as a super admin.\n * @returns True if the current user is authenticated as a super admin.\n * @category Authentication\n */\n isSuperAdmin(): boolean {\n return !!this.sessionDetails?.project.superAdmin;\n }\n\n /**\n * Returns true if the current user is authenticated as a project admin.\n * @returns True if the current user is authenticated as a project admin.\n * @category Authentication\n */\n isProjectAdmin(): boolean {\n return !!this.sessionDetails?.membership.admin;\n }\n\n /**\n * Returns the current project if available.\n * @returns The current project if available.\n * @category User Profile\n */\n getProject(): Project | undefined {\n return this.sessionDetails?.project;\n }\n\n /**\n * Returns the current project membership if available.\n * @returns The current project membership if available.\n * @category User Profile\n */\n getProjectMembership(): ProjectMembership | undefined {\n return this.sessionDetails?.membership;\n }\n\n /**\n * Returns the current user profile resource if available.\n * This method does not wait for loading promises.\n * @returns The current user profile resource if available.\n * @category User Profile\n */\n getProfile(): ProfileResource | undefined {\n return this.sessionDetails?.profile;\n }\n\n /**\n * Returns the current user profile resource, retrieving form the server if necessary.\n * This method waits for loading promises.\n * @returns The current user profile resource.\n * @category User Profile\n */\n async getProfileAsync(): Promise<ProfileResource | undefined> {\n if (this.profilePromise) {\n return this.profilePromise;\n }\n if (this.sessionDetails) {\n return this.sessionDetails.profile;\n }\n return this.refreshProfile();\n }\n\n /**\n * Returns the current user configuration if available.\n * @returns The current user configuration if available.\n * @category User Profile\n */\n getUserConfiguration(): UserConfiguration | undefined {\n return this.sessionDetails?.config;\n }\n\n /**\n * Returns the current user access policy if available.\n * @returns The current user access policy if available.\n * @category User Profile\n */\n getAccessPolicy(): AccessPolicy | undefined {\n return this.sessionDetails?.accessPolicy;\n }\n\n /**\n * Downloads the URL as a blob. Can accept binary URLs in the form of `Binary/{id}` as well.\n * @category Read\n * @param url - The URL to request. Can be a standard URL or one in the form of `Binary/{id}`.\n * @param options - Optional fetch request init options.\n * @returns Promise to the response body as a blob.\n */\n async download(url: URL | string, options: MedplumRequestOptions = {}): Promise<Blob> {\n if (this.refreshPromise) {\n await this.refreshPromise;\n }\n const urlString = url.toString();\n if (urlString.startsWith(BINARY_URL_PREFIX)) {\n url = this.fhirUrl(urlString);\n }\n\n let headers = options.headers as Record<string, string> | undefined;\n if (!headers) {\n headers = {};\n options.headers = headers;\n }\n\n if (!headers['Accept']) {\n headers['Accept'] = '*/*';\n }\n\n this.addFetchOptionsDefaults(options);\n const response = await this.fetchWithRetry(url.toString(), options);\n return response.blob();\n }\n\n /**\n * Creates a FHIR Media resource with the provided data content.\n *\n * @category Create\n * @param createMediaOptions - The media creation options. See `CreateMediaOptions` for full details.\n * @param requestOptions - Optional fetch options.\n * @returns The new media resource.\n */\n async createMedia(createMediaOptions: CreateMediaOptions, requestOptions?: MedplumRequestOptions): Promise<Media> {\n const { additionalFields, ...createBinaryOptions } = createMediaOptions;\n\n // First, create the media:\n const media = await this.createResource({\n resourceType: 'Media',\n status: 'preparation',\n content: {\n contentType: createMediaOptions.contentType,\n },\n ...additionalFields,\n });\n\n // If the caller did not specify a security context, use the media reference:\n if (!createBinaryOptions.securityContext) {\n createBinaryOptions.securityContext = createReference(media);\n }\n\n // Next, upload the binary:\n const content = await this.createAttachment(createBinaryOptions, requestOptions);\n\n // Update the media with the binary content:\n return this.updateResource({\n ...media,\n status: 'completed',\n content,\n });\n }\n\n /**\n * Upload media to the server and create a Media instance for the uploaded content.\n * @param contents - The contents of the media file, as a string, Uint8Array, File, or Blob.\n * @param contentType - The media type of the content.\n * @param filename - Optional filename for the binary, or extended upload options (see `BinaryUploadOptions`).\n * @param additionalFields - Additional fields for Media.\n * @param options - Optional fetch options.\n * @returns Promise that resolves to the created Media\n * @deprecated Use `createMedia` with `CreateMediaOptions` instead. To be removed in Medplum 4.0.\n */\n async uploadMedia(\n contents: string | Uint8Array | File | Blob,\n contentType: string,\n filename: string | undefined,\n additionalFields?: Partial<Media>,\n options?: MedplumRequestOptions\n ): Promise<Media> {\n return this.createMedia(\n {\n data: contents,\n contentType,\n filename,\n additionalFields,\n },\n options\n );\n }\n\n /**\n * Performs Bulk Data Export operation request flow. See The FHIR \"Bulk Data Export\" for full details: https://build.fhir.org/ig/HL7/bulk-data/export.html#bulk-data-export\n * @param exportLevel - Optional export level. Defaults to system level export. 'Group/:id' - Group of Patients, 'Patient' - All Patients.\n * @param resourceTypes - A string of comma-delimited FHIR resource types.\n * @param since - Resources will be included in the response if their state has changed after the supplied time (e.g. if Resource.meta.lastUpdated is later than the supplied _since time).\n * @param options - Optional fetch options.\n * @returns Bulk Data Response containing links to Bulk Data files. See \"Response - Complete Status\" for full details: https://build.fhir.org/ig/HL7/bulk-data/export.html#response---complete-status\n */\n async bulkExport(\n //eslint-disable-next-line default-param-last\n exportLevel = '',\n resourceTypes?: string,\n since?: string,\n options?: MedplumRequestOptions\n ): Promise<Partial<BulkDataExport>> {\n const fhirPath = exportLevel ? `${exportLevel}/` : exportLevel;\n const url = this.fhirUrl(`${fhirPath}$export`);\n\n if (resourceTypes) {\n url.searchParams.set('_type', resourceTypes);\n }\n if (since) {\n url.searchParams.set('_since', since);\n }\n\n return this.startAsyncRequest<Partial<BulkDataExport>>(url.toString(), options);\n }\n\n /**\n * Starts an async request following the FHIR \"Asynchronous Request Pattern\".\n * See: https://hl7.org/fhir/r4/async.html\n * @param url - The URL to request.\n * @param options - Optional fetch options.\n * @returns The response body.\n */\n async startAsyncRequest<T>(url: string, options: MedplumRequestOptions = {}): Promise<T> {\n this.addFetchOptionsDefaults(options);\n\n const headers = options.headers as Record<string, string>;\n headers['Prefer'] = 'respond-async';\n\n return this.request('POST', url, options);\n }\n\n /**\n * Returns the key value client.\n * @returns The key value client.\n */\n get keyValue(): MedplumKeyValueClient {\n if (!this.keyValueClient) {\n this.keyValueClient = new MedplumKeyValueClient(this);\n }\n return this.keyValueClient;\n }\n\n //\n // Private helpers\n //\n\n /**\n * Internal helper method to get a bundle from a URL.\n * In addition to returning the bundle, it also caches all of the resources in the bundle.\n * This should be used by any method that returns a bundle of resources to be cached.\n * @param url - The bundle URL.\n * @param options - Optional fetch options.\n * @returns Promise to the bundle.\n */\n private getBundle<T extends Resource = Resource>(\n url: URL,\n options?: MedplumRequestOptions\n ): ReadablePromise<Bundle<T>> {\n return new ReadablePromise(\n (async () => {\n const bundle = await this.get<Bundle<T>>(url, options);\n if (bundle.entry) {\n for (const entry of bundle.entry) {\n this.cacheResource(entry.resource);\n }\n }\n return bundle;\n })()\n );\n }\n\n /**\n * Returns the cache entry if available and not expired.\n * @param key - The cache key to retrieve.\n * @param options - Optional fetch options for cache settings.\n * @returns The cached entry if found.\n */\n private getCacheEntry(key: string, options: MedplumRequestOptions | undefined): RequestCacheEntry | undefined {\n if (!this.requestCache || options?.cache === 'no-cache' || options?.cache === 'reload') {\n return undefined;\n }\n const entry = this.requestCache.get(key);\n if (!entry || entry.requestTime + this.cacheTime < Date.now()) {\n return undefined;\n }\n return entry;\n }\n\n /**\n * Adds a readable promise to the cache.\n * @param key - The cache key to store.\n * @param value - The readable promise to store.\n */\n private setCacheEntry(key: string, value: ReadablePromise<any>): void {\n if (this.requestCache) {\n this.requestCache.set(key, { requestTime: Date.now(), value });\n }\n }\n\n /**\n * Adds a concrete value as the cache entry for the given resource.\n * This is used in cases where the resource is loaded indirectly.\n * For example, when a resource is loaded as part of a Bundle.\n * @param resource - The resource to cache.\n */\n private cacheResource(resource: Resource | undefined): void {\n if (resource?.id && !resource.meta?.tag?.some((t) => t.code === 'SUBSETTED')) {\n this.setCacheEntry(\n this.fhirUrl(resource.resourceType, resource.id).toString(),\n new ReadablePromise(Promise.resolve(resource))\n );\n }\n }\n\n /**\n * Deletes a cache entry.\n * @param key - The cache key to delete.\n */\n private deleteCacheEntry(key: string): void {\n if (this.requestCache) {\n this.requestCache.delete(key);\n }\n }\n\n /**\n * Makes an HTTP request.\n * @param method - The HTTP method (GET, POST, etc).\n * @param url - The target URL.\n * @param options - Optional fetch request init options.\n * @param state - Optional request state.\n * @returns The JSON content body if available.\n */\n private async request<T>(\n method: string,\n url: string,\n options: MedplumRequestOptions = {},\n state: RequestState = {}\n ): Promise<T> {\n await this.refreshIfExpired();\n\n options.method = method;\n this.addFetchOptionsDefaults(options);\n\n const response = await this.fetchWithRetry(url, options);\n\n if (response.status === 401) {\n // Refresh and try again\n return this.handleUnauthenticated(method, url, options);\n }\n\n if (response.status === 204 || response.status === 304) {\n // No content or change\n return undefined as unknown as T;\n }\n\n const contentType = response.headers.get('content-type');\n const isJson = contentType?.includes('json');\n\n if (response.status === 404 && !isJson) {\n // Special case for non-JSON 404 responses\n // In the common case, the 404 response will include an OperationOutcome in JSON with additional details.\n // In the non-JSON case, we can't parse the response, so we'll just throw a generic \"Not Found\" error.\n throw new OperationOutcomeError(notFound);\n }\n\n const body = await this.parseBody(response, isJson);\n\n if (\n (response.status === 200 && options.followRedirectOnOk) ||\n (response.status === 201 && options.followRedirectOnCreated)\n ) {\n const contentLocation = await tryGetContentLocation(response, body);\n if (contentLocation) {\n return this.request('GET', contentLocation, { ...options, body: undefined });\n }\n }\n\n if (response.status === 202 && options.pollStatusOnAccepted) {\n const contentLocation = await tryGetContentLocation(response, body);\n const statusUrl = contentLocation ?? state.statusUrl;\n if (statusUrl) {\n return this.pollStatus(statusUrl, options, state);\n }\n }\n\n if (response.status >= 400) {\n throw new OperationOutcomeError(normalizeOperationOutcome(body));\n }\n\n return body as T;\n }\n\n private async parseBody(\n response: Response,\n isJson: boolean | undefined\n ): Promise<Record<string, any> | string | undefined> {\n let body: Record<string, string> | string | undefined = undefined;\n // If there is no content length, don't attempt to parse the body\n if (response.headers.get('content-length') === '0') {\n return undefined;\n }\n if (isJson) {\n try {\n body = await response.json();\n } catch (err) {\n console.error('Error parsing response', response.status, err);\n throw err;\n }\n } else {\n body = await response.text();\n }\n return body;\n }\n\n private async fetchWithRetry(url: string, options: MedplumRequestOptions): Promise<Response> {\n if (!url.startsWith('http')) {\n url = concatUrls(this.baseUrl, url);\n }\n\n // Previously default for maxRetries was 3, but we will interpret maxRetries literally and not count first attempt\n // Default of 2 matches old behavior with the new semantics\n const maxRetries = options?.maxRetries ?? 2;\n const retryDelay = 200;\n\n // We use <= since we want to retry maxRetries times and first retry is when attemptNum === 1\n for (let attemptNum = 0; attemptNum <= maxRetries; attemptNum++) {\n try {\n if (this.options.verbose) {\n this.logRequest(url, options);\n }\n const response = (await this.fetch(url, options)) as Response;\n if (this.options.verbose) {\n this.logResponse(response);\n }\n // Handle non-500 response and max retries exceeded\n // We return immediately for non-500 or 500 that has exceeded max retries\n if (response.status < 500 || attemptNum === maxRetries) {\n return response;\n }\n } catch (err) {\n // This is for the 1st retry to avoid multiple notifications\n if ((err as Error).message === 'Failed to fetch' && attemptNum === 0) {\n this.dispatchEvent({ type: 'offline' });\n }\n\n // If we got an abort error or exceeded retries, then throw immediately\n if ((err as Error).name === 'AbortError' || attemptNum === maxRetries) {\n throw err;\n }\n }\n\n await sleep(retryDelay);\n }\n\n throw new Error('Unreachable');\n }\n\n private logRequest(url: string, options: MedplumRequestOptions): void {\n console.log(`> ${options.method} ${url}`);\n if (options.headers) {\n const headers = options.headers as Record<string, string>;\n for (const key of sortStringArray(Object.keys(headers))) {\n console.log(`> ${key}: ${headers[key]}`);\n }\n }\n }\n\n private logResponse(response: Response): void {\n console.log(`< ${response.status} ${response.statusText}`);\n if (response.headers) {\n response.headers.forEach((value, key) => console.log(`< ${key}: ${value}`));\n }\n }\n\n private async pollStatus<T>(statusUrl: string, options: MedplumRequestOptions, state: RequestState): Promise<T> {\n const statusOptions: MedplumRequestOptions = { ...options, method: 'GET', body: undefined, redirect: 'follow' };\n if (state.pollCount === undefined) {\n // First request - try request immediately\n if (options.headers && typeof options.headers === 'object' && 'Prefer' in options.headers) {\n statusOptions.headers = { ...options.headers };\n delete statusOptions.headers.Prefer;\n }\n state.statusUrl = statusUrl;\n state.pollCount = 1;\n } else {\n // Subsequent requests - wait and retry\n const retryDelay = options.pollStatusPeriod ?? 1000;\n await sleep(retryDelay);\n state.pollCount++;\n }\n return this.request('GET', statusUrl, statusOptions, state);\n }\n\n /**\n * Executes a batch of requests that were automatically batched together.\n */\n private async executeAutoBatch(): Promise<void> {\n // Get the current queue\n if (this.autoBatchQueue === undefined) {\n return;\n }\n\n const entries = [...this.autoBatchQueue];\n\n // Clear the queue\n this.autoBatchQueue.length = 0;\n\n // Clear the timer\n this.autoBatchTimerId = undefined;\n\n // If there is only one request in the batch, just execute it\n if (entries.length === 1) {\n const entry = entries[0];\n try {\n entry.resolve(await this.request(entry.method, concatUrls(this.fhirBaseUrl, entry.url), entry.options));\n } catch (err) {\n entry.reject(new OperationOutcomeError(normalizeOperationOutcome(err)));\n }\n return;\n }\n\n // Build the batch request\n const batch: Bundle = {\n resourceType: 'Bundle',\n type: 'batch',\n entry: entries.map(\n (e): BundleEntry => ({\n request: {\n method: e.method,\n url: e.url,\n },\n resource: e.options.body ? (JSON.parse(e.options.body as string) as Resource) : undefined,\n })\n ),\n };\n\n // Execute the batch request\n const response = (await this.post(this.fhirBaseUrl, batch)) as Bundle;\n\n // Process the response\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const responseEntry = response.entry?.[i];\n if (responseEntry?.response?.outcome && !isOk(responseEntry.response.outcome)) {\n entry.reject(new OperationOutcomeError(responseEntry.response.outcome));\n } else {\n entry.resolve(responseEntry?.resource);\n }\n }\n }\n\n /**\n * Adds default options to the fetch options.\n * @param options - The options to add defaults to.\n */\n private addFetchOptionsDefaults(options: MedplumRequestOptions): void {\n // Apply default headers\n Object.entries(this.defaultHeaders).forEach(([name, value]) => {\n this.setRequestHeader(options, name, value);\n });\n\n this.setRequestHeader(options, 'Accept', DEFAULT_ACCEPT, true);\n\n if (this.options.extendedMode !== false) {\n this.setRequestHeader(options, 'X-Medplum', 'extended');\n }\n\n if (options.body) {\n this.setRequestHeader(options, 'Content-Type', ContentType.FHIR_JSON, true);\n }\n\n if (this.accessToken) {\n this.setRequestHeader(options, 'Authorization', 'Bearer ' + this.accessToken);\n } else if (this.basicAuth) {\n this.setRequestHeader(options, 'Authorization', 'Basic ' + this.basicAuth);\n }\n\n if (!options.cache) {\n options.cache = 'no-cache';\n }\n\n if (!options.credentials) {\n options.credentials = 'include';\n }\n }\n\n /**\n * Sets the \"Content-Type\" header on fetch options.\n * @param options - The fetch options.\n * @param contentType - The new content type to set.\n */\n private setRequestContentType(options: MedplumRequestOptions, contentType: string): void {\n this.setRequestHeader(options, 'Content-Type', contentType);\n }\n\n /**\n * Sets a header on fetch options.\n * @param options - The fetch options.\n * @param key - The header key.\n * @param value - The header value.\n * @param ifNoneExist - Optional flag to only set the header if it doesn't already exist.\n */\n private setRequestHeader(options: MedplumRequestOptions, key: string, value: string, ifNoneExist = false): void {\n if (!options.headers) {\n options.headers = {};\n }\n const headers = options.headers as Record<string, string>;\n if (ifNoneExist && headers[key]) {\n return;\n }\n headers[key] = value;\n }\n\n /**\n * Sets the body on fetch options.\n * @param options - The fetch options.\n * @param data - The new content body.\n */\n private setRequestBody(options: MedplumRequestOptions, data: any): void {\n if (\n typeof data === 'string' ||\n (typeof Blob !== 'undefined' && (data instanceof Blob || data?.constructor.name === 'Blob')) ||\n (typeof File !== 'undefined' && (data instanceof File || data?.constructor.name === 'File')) ||\n (typeof Uint8Array !== 'undefined' && (data instanceof Uint8Array || data?.constructor.name === 'Uint8Array'))\n ) {\n options.body = data;\n } else if (data) {\n options.body = JSON.stringify(data);\n }\n }\n\n /**\n * Handles an unauthenticated response from the server.\n * First, tries to refresh the access token and retry the request.\n * Otherwise, calls unauthenticated callbacks and rejects.\n * @param method - The HTTP method of the original request.\n * @param url - The URL of the original request.\n * @param options - Optional fetch request init options.\n * @returns The result of the retry.\n */\n private handleUnauthenticated(method: string, url: string, options: MedplumRequestOptions): Promise<any> {\n if (this.refresh()) {\n return this.request(method, url, options);\n }\n this.clear();\n if (this.onUnauthenticated) {\n this.onUnauthenticated();\n }\n return Promise.reject(new OperationOutcomeError(unauthorized));\n }\n\n /**\n * Starts a new PKCE flow.\n * These PKCE values are stateful, and must survive redirects and page refreshes.\n * @category Authentication\n * @returns The PKCE code challenge details.\n */\n async startPkce(): Promise<{ codeChallengeMethod: CodeChallengeMethod; codeChallenge: string }> {\n const pkceState = getRandomString();\n sessionStorage.setItem('pkceState', pkceState);\n\n const codeVerifier = getRandomString().slice(0, 128);\n sessionStorage.setItem('codeVerifier', codeVerifier);\n\n const arrayHash = await encryptSHA256(codeVerifier);\n const codeChallenge = arrayBufferToBase64(arrayHash).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');\n sessionStorage.setItem('codeChallenge', codeChallenge);\n\n return { codeChallengeMethod: 'S256', codeChallenge };\n }\n\n /**\n * Redirects the user to the login screen for authorization.\n * Clears all auth state including local storage and session storage.\n * @param loginParams - The authorization login parameters.\n * @see https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint\n */\n private async requestAuthorization(loginParams?: Partial<BaseLoginRequest>): Promise<void> {\n const loginRequest = await this.ensureCodeChallenge(loginParams ?? {});\n const url = new URL(this.authorizeUrl);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', sessionStorage.getItem('pkceState') as string);\n url.searchParams.set('client_id', loginRequest.clientId ?? (this.clientId as string));\n url.searchParams.set('redirect_uri', loginRequest.redirectUri ?? getWindowOrigin());\n url.searchParams.set('code_challenge_method', loginRequest.codeChallengeMethod as string);\n url.searchParams.set('code_challenge', loginRequest.codeChallenge as string);\n url.searchParams.set('scope', loginRequest.scope ?? 'openid profile');\n window.location.assign(url.toString());\n }\n\n /**\n * Processes an OAuth authorization code.\n * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest\n * @param code - The authorization code received by URL parameter.\n * @param loginParams - Optional login parameters.\n * @returns The user profile resource.\n * @category Authentication\n */\n processCode(code: string, loginParams?: Partial<BaseLoginRequest>): Promise<ProfileResource> {\n const formBody = new URLSearchParams();\n formBody.set('grant_type', OAuthGrantType.AuthorizationCode);\n formBody.set('code', code);\n formBody.set('client_id', loginParams?.clientId ?? (this.clientId as string));\n formBody.set('redirect_uri', loginParams?.redirectUri ?? getWindowOrigin());\n\n if (typeof sessionStorage !== 'undefined') {\n const codeVerifier = sessionStorage.getItem('codeVerifier');\n if (codeVerifier) {\n formBody.set('code_verifier', codeVerifier);\n }\n }\n\n return this.fetchTokens(formBody);\n }\n\n /**\n * Refreshes the access token using the refresh token if available.\n * @param gracePeriod - Optional grace period in milliseconds. If not specified, uses the client configured grace period (default 5 minutes).\n * @returns Promise to refresh the access token.\n */\n refreshIfExpired(gracePeriod?: number): Promise<void> {\n // If (1) not already refreshing, (2) we have an access token, and (3) the access token is expired,\n // then start a refresh.\n if (!this.refreshPromise && this.accessTokenExpires !== undefined && !this.isAuthenticated(gracePeriod)) {\n // The result of the `refresh()` function is cached in `this.refreshPromise`,\n // so we can safely ignore the return value here.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.refresh();\n }\n return this.refreshPromise ?? Promise.resolve();\n }\n\n /**\n * Tries to refresh the auth tokens.\n * @returns The refresh promise if available; otherwise undefined.\n * @see https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens\n */\n private refresh(): Promise<void> | undefined {\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n if (this.refreshToken) {\n const formBody = new URLSearchParams();\n formBody.set('grant_type', OAuthGrantType.RefreshToken);\n formBody.set('client_id', this.clientId as string);\n formBody.set('refresh_token', this.refreshToken);\n this.refreshPromise = this.fetchTokens(formBody);\n return this.refreshPromise;\n }\n\n if (this.clientId && this.clientSecret) {\n this.refreshPromise = this.startClientLogin(this.clientId, this.clientSecret);\n return this.refreshPromise;\n }\n\n return undefined;\n }\n\n /**\n * Starts a new OAuth2 client credentials flow.\n *\n * @example\n * ```typescript\n * await medplum.startClientLogin(import.meta.env.MEDPLUM_CLIENT_ID, import.meta.env.MEDPLUM_CLIENT_SECRET)\n * // Example Search\n * await medplum.searchResources('Patient')\n * ```\n *\n * See: https://datatracker.ietf.org/doc/html/rfc6749#section-4.4\n *\n * @category Authentication\n * @param clientId - The client ID.\n * @param clientSecret - The client secret.\n * @returns Promise that resolves to the client profile.\n */\n async startClientLogin(clientId: string, clientSecret: string): Promise<ProfileResource> {\n this.clientId = clientId;\n this.clientSecret = clientSecret;\n\n const formBody = new URLSearchParams();\n formBody.set('grant_type', OAuthGrantType.ClientCredentials);\n formBody.set('client_id', clientId);\n formBody.set('client_secret', clientSecret);\n return this.fetchTokens(formBody);\n }\n\n /**\n * Starts a new OAuth2 JWT bearer flow.\n *\n * @example\n * ```typescript\n * await medplum.startJwtBearerLogin(import.meta.env.MEDPLUM_CLIENT_ID, import.meta.env.MEDPLUM_JWT_BEARER_ASSERTION, 'openid profile');\n * // Example Search\n * await medplum.searchResources('Patient')\n * ```\n *\n * See: https://datatracker.ietf.org/doc/html/rfc7523#section-2.1\n *\n * @category Authentication\n * @param clientId - The client ID.\n * @param assertion - The JWT assertion.\n * @param scope - The OAuth scope.\n * @returns Promise that resolves to the client profile.\n */\n async startJwtBearerLogin(clientId: string, assertion: string, scope: string): Promise<ProfileResource> {\n this.clientId = clientId;\n\n const formBody = new URLSearchParams();\n formBody.set('grant_type', OAuthGrantType.JwtBearer);\n formBody.set('client_id', clientId);\n formBody.set('assertion', assertion);\n formBody.set('scope', scope);\n return this.fetchTokens(formBody);\n }\n\n /**\n * Starts a new OAuth2 JWT assertion flow.\n *\n * See: https://datatracker.ietf.org/doc/html/rfc7523#section-2.2\n *\n * @category Authentication\n * @param jwt - The JWT assertion.\n * @returns Promise that resolves to the client profile.\n */\n async startJwtAssertionLogin(jwt: string): Promise<ProfileResource> {\n const formBody = new URLSearchParams();\n formBody.append('grant_type', OAuthGrantType.ClientCredentials);\n formBody.append('client_assertion_type', OAuthClientAssertionType.JwtBearer);\n formBody.append('client_assertion', jwt);\n return this.fetchTokens(formBody);\n }\n\n /**\n * Sets the client ID and secret for basic auth.\n *\n * @example\n * ```typescript\n * medplum.setBasicAuth(import.meta.env.MEDPLUM_CLIENT_ID, import.meta.env.MEDPLUM_CLIENT_SECRET);\n * // Example Search\n * await medplum.searchResources('Patient');\n * ```\n *\n * @category Authentication\n * @param clientId - The client ID.\n * @param clientSecret - The client secret.\n */\n setBasicAuth(clientId: string, clientSecret: string): void {\n this.clientId = clientId;\n this.clientSecret = clientSecret;\n this.basicAuth = encodeBase64(clientId + ':' + clientSecret);\n }\n\n /**\n * Subscribes to a specified topic, listening for a list of specified events.\n *\n * Once you have the `SubscriptionRequest` returned from this method, you can call `fhircastConnect(subscriptionRequest)` to connect to the subscription stream.\n *\n * @category FHIRcast\n * @param topic - The topic to publish to. Usually a UUID.\n * @param events - An array of event names to listen for.\n * @returns A `Promise` that resolves once the request completes, or rejects if it fails.\n */\n async fhircastSubscribe(topic: string, events: FhircastEventName[]): Promise<SubscriptionRequest> {\n if (!(typeof topic === 'string' && topic !== '')) {\n throw new OperationOutcomeError(validationError('Invalid topic provided. Topic must be a valid string.'));\n }\n if (!(typeof events === 'object' && Array.isArray(events) && events.length > 0)) {\n throw new OperationOutcomeError(\n validationError(\n 'Invalid events provided. Events must be an array of event names containing at least one event.'\n )\n );\n }\n\n const subRequest = {\n channelType: 'websocket',\n mode: 'subscribe',\n topic,\n events,\n } as PendingSubscriptionRequest;\n\n const body = (await this.post(\n this.fhircastHubUrl,\n serializeFhircastSubscriptionRequest(subRequest),\n ContentType.FORM_URL_ENCODED\n )) as { 'hub.channel.endpoint': string };\n\n const endpoint = body['hub.channel.endpoint'];\n if (!endpoint) {\n throw new Error('Invalid response!');\n }\n\n // Add endpoint to subscription request before returning\n (subRequest as SubscriptionRequest).endpoint = endpoint;\n return subRequest as SubscriptionRequest;\n }\n\n /**\n * Unsubscribes from the specified topic.\n *\n * @category FHIRcast\n * @param subRequest - A `SubscriptionRequest` representing a subscription to cancel. Mode will be set to `unsubscribe` automatically.\n * @returns A `Promise` that resolves when request to unsubscribe is completed.\n */\n async fhircastUnsubscribe(subRequest: SubscriptionRequest): Promise<void> {\n if (!validateFhircastSubscriptionRequest(subRequest)) {\n throw new OperationOutcomeError(\n validationError('Invalid topic or subscriptionRequest. SubscriptionRequest must be an object.')\n );\n }\n if (!(subRequest.endpoint && typeof subRequest.endpoint === 'string' && subRequest.endpoint.startsWith('ws'))) {\n throw new OperationOutcomeError(\n validationError('Provided subscription request must have an endpoint in order to unsubscribe.')\n );\n }\n\n // Turn subRequest -> unsubRequest\n subRequest.mode = 'unsubscribe';\n // Send unsub request\n await this.post(\n this.fhircastHubUrl,\n serializeFhircastSubscriptionRequest(subRequest),\n ContentType.FORM_URL_ENCODED\n );\n }\n\n /**\n * Connects to a `FHIRcast` session.\n *\n * @category FHIRcast\n * @param subRequest - The `SubscriptionRequest` to use for connecting.\n * @returns A `FhircastConnection` which emits lifecycle events for the `FHIRcast` WebSocket connection.\n */\n fhircastConnect(subRequest: SubscriptionRequest): FhircastConnection {\n return new FhircastConnection(subRequest);\n }\n\n /**\n * Publishes a new context to a given topic for a specified event type.\n *\n * @category FHIRcast\n * @param topic - The topic to publish to. Usually a UUID.\n * @param event - The name of the event to publish an updated context for, ie. `Patient-open`.\n * @param context - The updated context containing resources relevant to this event.\n * @param versionId - The `versionId` of the `anchor context` of the given event. Used for `DiagnosticReport-update` event.\n * @returns A `Promise` that resolves once the request completes, or rejects if it fails.\n */\n async fhircastPublish<EventName extends FhircastEventVersionOptional>(\n topic: string,\n event: EventName,\n context: FhircastEventContext<EventName> | FhircastEventContext<EventName>[],\n versionId?: never\n ): Promise<Record<string, any>>;\n\n async fhircastPublish<RequiredVersionEvent extends FhircastEventVersionRequired>(\n topic: string,\n event: RequiredVersionEvent,\n context: FhircastEventContext<RequiredVersionEvent> | FhircastEventContext<RequiredVersionEvent>[],\n versionId: string\n ): Promise<Record<string, any>>;\n\n async fhircastPublish<EventName extends FhircastEventVersionRequired | FhircastEventVersionOptional>(\n topic: string,\n event: EventName,\n context: FhircastEventContext<EventName> | FhircastEventContext<EventName>[],\n versionId?: string\n ): Promise<Record<string, any>> {\n if (isContextVersionRequired(event)) {\n return this.post(\n this.fhircastHubUrl,\n createFhircastMessagePayload<typeof event>(topic, event, context, versionId as string),\n ContentType.JSON\n );\n }\n assertContextVersionOptional(event);\n return this.post(\n this.fhircastHubUrl,\n createFhircastMessagePayload<typeof event>(topic, event, context),\n ContentType.JSON\n );\n }\n\n /**\n * Gets the current context of the given FHIRcast `topic`.\n *\n * @category FHIRcast\n * @param topic - The topic to get the current context for. Usually a UUID.\n * @returns A Promise which resolves to the `CurrentContext` for the given topic.\n */\n async fhircastGetContext(topic: string): Promise<CurrentContext> {\n return this.get(`${this.fhircastHubUrl}/${topic}`);\n }\n\n /**\n * Invite a user to a project.\n * @param projectId - The project ID.\n * @param body - The InviteRequest.\n * @returns Promise that returns a project membership or an operation outcome.\n */\n async invite(projectId: string, body: InviteRequest): Promise<ProjectMembership | OperationOutcome> {\n return this.post('admin/projects/' + projectId + '/invite', body);\n }\n\n /**\n * Makes a POST request to the tokens endpoint.\n * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint\n * @param formBody - Token parameters in URL encoded format.\n * @returns The user profile resource.\n */\n private async fetchTokens(formBody: URLSearchParams): Promise<ProfileResource> {\n const options: MedplumRequestOptions = {\n method: 'POST',\n headers: { 'Content-Type': ContentType.FORM_URL_ENCODED },\n body: formBody.toString(),\n credentials: 'include',\n };\n const headers = options.headers as Record<string, string>;\n Object.assign(headers, this.defaultHeaders);\n\n if (this.basicAuth) {\n headers['Authorization'] = `Basic ${this.basicAuth}`;\n }\n\n let response: Response;\n try {\n response = await this.fetchWithRetry(this.tokenUrl, options);\n } catch (err) {\n this.refreshPromise = undefined;\n throw err;\n }\n\n if (!response.ok) {\n this.clearActiveLogin();\n try {\n const error = await response.json();\n throw new OperationOutcomeError(badRequest(error.error_description));\n } catch (err) {\n throw new OperationOutcomeError(badRequest('Failed to fetch tokens'), err);\n }\n }\n const tokens = await response.json();\n await this.verifyTokens(tokens);\n return this.getProfile() as ProfileResource;\n }\n\n /**\n * Verifies the tokens received from the auth server.\n * Validates the JWT against the JWKS.\n * See: https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint\n * @param tokens - The token response.\n * @returns Promise to complete.\n */\n private async verifyTokens(tokens: TokenResponse): Promise<void> {\n const token = tokens.access_token;\n\n if (isJwt(token)) {\n // Verify token has not expired\n const tokenPayload = parseJWTPayload(token);\n\n if (Date.now() >= (tokenPayload.exp as number) * 1000) {\n this.clearActiveLogin();\n throw new OperationOutcomeError(unauthorizedTokenExpired);\n }\n\n // Verify app_client_id\n if (tokenPayload.cid) {\n if (tokenPayload.cid !== this.clientId) {\n this.clearActiveLogin();\n throw new OperationOutcomeError(unauthorizedTokenAudience);\n }\n } else if (this.clientId && tokenPayload.client_id !== this.clientId) {\n this.clearActiveLogin();\n throw new OperationOutcomeError(unauthorizedTokenAudience);\n }\n }\n\n return this.setActiveLogin({\n accessToken: token,\n refreshToken: tokens.refresh_token,\n project: tokens.project,\n profile: tokens.profile,\n });\n }\n\n private checkSessionDetailsMatchLogin(login?: LoginState): boolean {\n // We only need to validate if we already have session details\n if (!(this.sessionDetails && login)) {\n return true;\n }\n // Make sure sessionDetails.profile.id matches the ID in the profile reference we are checking against\n // Otherwise return false if no profile reference in login\n return login.profile?.reference?.endsWith(this.sessionDetails.profile.id as string) ?? false;\n }\n\n /**\n * Sets up a listener for window storage events.\n * This synchronizes state across browser windows and browser tabs.\n */\n private setupStorageListener(): void {\n try {\n window.addEventListener('storage', (e: StorageEvent) => {\n // Storage events fire when different tabs make changes.\n // On storage clear (key === null) or profile change (key === 'activeLogin', and profile in 'activeLogin' is different)\n // Refresh the page to ensure the active login is up to date.\n if (e.key === null) {\n window.location.reload();\n } else if (e.key === 'activeLogin') {\n const oldState = (e.oldValue ? JSON.parse(e.oldValue) : undefined) as LoginState | undefined;\n const newState = (e.newValue ? JSON.parse(e.newValue) : undefined) as LoginState | undefined;\n if (\n oldState?.profile.reference !== newState?.profile.reference ||\n !this.checkSessionDetailsMatchLogin(newState)\n ) {\n window.location.reload();\n } else if (newState) {\n this.setAccessToken(newState.accessToken, newState.refreshToken);\n } else {\n // Theoretically this should never be called, but we might want to keep it here just in case\n this.clear();\n }\n }\n });\n } catch (_err) {\n // Silently ignore if this environment does not support storage events\n }\n }\n\n /**\n * Gets the `SubscriptionManager` for WebSocket subscriptions.\n *\n * @category Subscriptions\n * @returns the `SubscriptionManager` for this client.\n */\n getSubscriptionManager(): SubscriptionManager {\n if (!this.subscriptionManager) {\n this.subscriptionManager = new SubscriptionManager(this, getWebSocketUrl(this.baseUrl, '/ws/subscriptions-r4'));\n }\n return this.subscriptionManager;\n }\n\n /**\n * Subscribes to a given criteria, listening to notifications over WebSockets.\n *\n * This uses Medplum's `WebSocket Subscriptions` under the hood.\n *\n * A `SubscriptionEmitter` is returned from this function, which can be used to listen for updates to resources described by the given criteria.\n *\n * When subscribing to the same criteria multiple times, the same `SubscriptionEmitter` will be returned, and a reference count will be incremented.\n *\n * -----\n * @example\n * ```ts\n * const emitter = medplum.subscribeToCriteria('Communication');\n *\n * emitter.addEventListener('message', (bundle: Bundle) => {\n * // Called when a `Communication` resource is created or modified\n * console.log(bundle?.entry?.[1]?.resource); // Logs the `Communication` resource that was updated\n * });\n * ```\n *\n * @category Subscriptions\n * @param criteria - The criteria to subscribe to.\n * @param subscriptionProps - Optional properties to add to the created `Subscription` resource.\n * @returns a `SubscriptionEmitter` that emits `Bundle` resources containing changes to resources based on the given criteria.\n */\n subscribeToCriteria(criteria: string, subscriptionProps?: Partial<Subscription>): SubscriptionEmitter {\n return this.getSubscriptionManager().addCriteria(criteria, subscriptionProps);\n }\n\n /**\n * Unsubscribes from the given criteria.\n *\n * When called the same amount of times as proceeding calls to `subscribeToCriteria` on a given `criteria`,\n * the criteria is fully removed from the `SubscriptionManager`.\n *\n * @category Subscriptions\n * @param criteria - The criteria to unsubscribe from.\n * @param subscriptionProps - The optional properties that `subscribeToCriteria` was called with.\n */\n unsubscribeFromCriteria(criteria: string, subscriptionProps?: Partial<Subscription>): void {\n if (!this.subscriptionManager) {\n return;\n }\n this.subscriptionManager.removeCriteria(criteria, subscriptionProps);\n if (this.subscriptionManager.getCriteriaCount() === 0) {\n this.subscriptionManager.closeWebSocket();\n }\n }\n\n /**\n * Get the master `SubscriptionEmitter` for the `SubscriptionManager`.\n *\n * The master `SubscriptionEmitter` gets messages for all subscribed `criteria` as well as WebSocket errors, `connect` and `disconnect` events, and the `close` event.\n *\n * It can also be used to listen for `heartbeat` messages.\n *\n *------\n * @example\n * ### Listening for `heartbeat`:\n * ```ts\n * const masterEmitter = medplum.getMasterSubscriptionEmitter();\n *\n * masterEmitter.addEventListener('heartbeat', (bundle: Bundle<SubscriptionStatus>) => {\n * console.log(bundle?.entry?.[0]?.resource); // A `SubscriptionStatus` of type `heartbeat`\n * });\n *\n * ```\n * @category Subscriptions\n * @returns the master `SubscriptionEmitter` from the `SubscriptionManager`.\n */\n getMasterSubscriptionEmitter(): SubscriptionEmitter {\n return this.getSubscriptionManager().getMasterEmitter();\n }\n}\n\n/**\n * Returns the default fetch method.\n * The default fetch is currently only available in browser environments.\n * If you want to use SSR such as Next.js, you should pass a custom fetch function.\n * @returns The default fetch function for the current environment.\n */\nfunction getDefaultFetch(): FetchLike {\n if (!globalThis.fetch) {\n throw new Error('Fetch not available in this environment');\n }\n return globalThis.fetch.bind(globalThis);\n}\n\n/**\n * Returns the base URL for the current page.\n * @returns The window origin string.\n * @category HTTP\n */\nfunction getWindowOrigin(): string {\n if (typeof window === 'undefined') {\n return '';\n }\n return window.location.protocol + '//' + window.location.host + '/';\n}\n\n/**\n * Attempts to retrieve the content location from the given HTTP response.\n *\n * This function prioritizes the \"Content-Location\" HTTP header as the\n * most authoritative source for the content location. If this header is\n * not present, it falls back to the \"Location\" HTTP header.\n *\n * Note that the FHIR spec does not follow the traditional HTTP semantics of \"Content-Location\" and \"Location\".\n * \"Content-Location\" is not typically used with HTTP 202 responses because the content itself isn't available at the time of the response.\n * However, the FHIR spec explicitly recommends it:\n *\n * 3.2.6.1.2 Kick-off Request\n * 3.2.6.1.2.0.3 Response - Success\n * HTTP Status Code of 202 Accepted\n * Content-Location header with the absolute URL of an endpoint for subsequent status requests (polling location)\n *\n * Source: https://hl7.org/fhir/async-bulk.html\n *\n * In cases where neither of these headers are available (for instance,\n * due to CORS restrictions), it attempts to retrieve the content location\n * from the 'diagnostics' field of the first issue in an OperationOutcome object\n * present in the response body. If all attempts fail, the function returns 'undefined'.\n *\n * @async\n * @param response - The HTTP response object from which to extract the content location.\n * @param body - The response body.\n * @returns A Promise that resolves to the content location string if it is found, or 'undefined' if the content location cannot be determined from the response.\n */\nasync function tryGetContentLocation(\n response: Response,\n body: Record<string, string> | string | undefined\n): Promise<string | undefined> {\n // Accepted content location can come from multiple sources\n // The authoritative source is the \"Content-Location\" HTTP header.\n const contentLocation = response.headers.get('content-location');\n if (contentLocation) {\n return contentLocation;\n }\n\n // The next best source is the \"Location\" HTTP header.\n const location = response.headers.get('location');\n if (location) {\n return location;\n }\n\n // However, \"Content-Location\" may not be available due to CORS limitations.\n // In this case, we use the OperationOutcome.diagnostics field.\n if (isOperationOutcome(body) && body.issue?.[0]?.diagnostics) {\n return body.issue[0].diagnostics;\n }\n\n // If all else fails, return undefined.\n return undefined;\n}\n\n/**\n * Converts a FHIR resource bundle to a resource array.\n * The bundle is attached to the array as a property named \"bundle\".\n * @param bundle - A FHIR resource bundle.\n * @returns The resource array with the bundle attached.\n */\nfunction bundleToResourceArray<T extends Resource>(bundle: Bundle<T>): ResourceArray<T> {\n const array = bundle.entry?.map((e) => e.resource as T) ?? [];\n return Object.assign(array, { bundle });\n}\n\nfunction isCreateBinaryOptions(input: unknown): input is CreateBinaryOptions {\n return isObject(input) && 'data' in input && 'contentType' in input;\n}\n\n// This function can be deleted after Medplum 4.0 and we remove the legacy createBinary method\nexport function normalizeCreateBinaryOptions(\n arg1: BinarySource | CreateBinaryOptions,\n arg2: string | undefined | MedplumRequestOptions,\n arg3?: string,\n arg4?: (e: ProgressEvent) => void\n): CreateBinaryOptions {\n if (isCreateBinaryOptions(arg1)) {\n return arg1;\n }\n return {\n data: arg1,\n filename: arg2 as string | undefined,\n contentType: arg3 as string,\n onProgress: arg4,\n };\n}\n\nfunction isCreatePdfOptions(input: unknown): input is CreatePdfOptions {\n return isObject(input) && 'docDefinition' in input;\n}\n\n// This function can be deleted after Medplum 4.0 and we remove the legacy createPdf method\nexport function normalizeCreatePdfOptions(\n arg1: TDocumentDefinitions | CreatePdfOptions,\n arg2: string | undefined | MedplumRequestOptions,\n arg3: Record<string, CustomTableLayout> | undefined,\n arg4: TFontDictionary | undefined\n): CreatePdfOptions {\n if (isCreatePdfOptions(arg1)) {\n return arg1;\n }\n return {\n docDefinition: arg1,\n filename: arg2 as string,\n tableLayouts: arg3,\n fonts: arg4,\n };\n}\n", "import { TypeName } from './types';\n\nexport const ExternalSecretSystems = {\n aws_ssm_parameter_store: 'aws_ssm_parameter_store',\n} as const;\n\nexport type ExternalSecretSystem = keyof typeof ExternalSecretSystems;\nexport type ExternalSecretPrimitive = string | boolean | number;\nexport type ExternalSecretPrimitiveType = 'string' | 'boolean' | 'number';\nexport type ExternalSecret<T extends ExternalSecretPrimitive = ExternalSecretPrimitive> = {\n system: ExternalSecretSystem;\n key: string;\n type: TypeName<T>;\n};\nexport type ValueOrExternalSecret<T extends ExternalSecretPrimitive> = T | ExternalSecret<T>;\nexport type StringMap = { [key: string]: string };\n\nexport interface MedplumSourceInfraConfig {\n name: ValueOrExternalSecret<string>;\n stackName: ValueOrExternalSecret<string>;\n accountNumber: ValueOrExternalSecret<string>;\n region: string;\n domainName: ValueOrExternalSecret<string>;\n vpcId: ValueOrExternalSecret<string>;\n apiPort: ValueOrExternalSecret<number>;\n apiDomainName: ValueOrExternalSecret<string>;\n apiSslCertArn: ValueOrExternalSecret<string>;\n apiInternetFacing?: ValueOrExternalSecret<boolean>;\n apiWafIpSetArn: ValueOrExternalSecret<string>;\n appDomainName: ValueOrExternalSecret<string>;\n appSslCertArn: ValueOrExternalSecret<string>;\n appApiProxy?: ValueOrExternalSecret<boolean>;\n appWafIpSetArn: ValueOrExternalSecret<string>;\n appLoggingBucket?: ValueOrExternalSecret<string>;\n appLoggingPrefix?: ValueOrExternalSecret<string>;\n storageBucketName: ValueOrExternalSecret<string>;\n storageDomainName: ValueOrExternalSecret<string>;\n storageSslCertArn: ValueOrExternalSecret<string>;\n signingKeyId: ValueOrExternalSecret<string>;\n storagePublicKey: ValueOrExternalSecret<string>;\n storageWafIpSetArn: ValueOrExternalSecret<string>;\n storageLoggingBucket?: ValueOrExternalSecret<string>;\n storageLoggingPrefix?: ValueOrExternalSecret<string>;\n baseUrl: ValueOrExternalSecret<string>;\n maxAzs: ValueOrExternalSecret<number>;\n rdsInstances: ValueOrExternalSecret<number>;\n rdsInstanceType: ValueOrExternalSecret<string>;\n rdsInstanceVersion?: ValueOrExternalSecret<string>;\n rdsSecretsArn?: ValueOrExternalSecret<string>;\n rdsReaderInstanceType?: ValueOrExternalSecret<string>;\n rdsProxyEnabled?: ValueOrExternalSecret<boolean>;\n rdsClusterParameters?: StringMap;\n cacheNodeType?: ValueOrExternalSecret<string>;\n cacheSecurityGroupId?: ValueOrExternalSecret<string>;\n desiredServerCount: ValueOrExternalSecret<number>;\n serverImage: ValueOrExternalSecret<string>;\n serverMemory: ValueOrExternalSecret<number>;\n serverCpu: ValueOrExternalSecret<number>;\n loadBalancerSecurityGroupId?: ValueOrExternalSecret<string>;\n loadBalancerLoggingBucket?: ValueOrExternalSecret<string>;\n loadBalancerLoggingPrefix?: ValueOrExternalSecret<string>;\n clamscanEnabled: ValueOrExternalSecret<boolean>;\n clamscanLoggingBucket: ValueOrExternalSecret<string>;\n clamscanLoggingPrefix: ValueOrExternalSecret<string>;\n skipDns?: ValueOrExternalSecret<boolean>;\n hostedZoneName?: ValueOrExternalSecret<string>;\n additionalContainers?: {\n name: ValueOrExternalSecret<string>;\n image: ValueOrExternalSecret<string>;\n cpu?: ValueOrExternalSecret<number>;\n memory?: ValueOrExternalSecret<number>;\n essential?: ValueOrExternalSecret<boolean>;\n command?: ValueOrExternalSecret<string>[];\n environment?: {\n [key: string]: ValueOrExternalSecret<string>;\n };\n }[];\n containerInsights?: ValueOrExternalSecret<boolean>;\n cloudTrailAlarms?: {\n logGroupName: ValueOrExternalSecret<string>;\n logGroupCreate?: ValueOrExternalSecret<boolean>;\n snsTopicArn?: ValueOrExternalSecret<string>;\n snsTopicName?: ValueOrExternalSecret<string>;\n };\n fargateAutoScaling?: {\n minCapacity: ValueOrExternalSecret<number>;\n maxCapacity: ValueOrExternalSecret<number>;\n targetUtilizationPercent: ValueOrExternalSecret<number>;\n scaleInCooldown: ValueOrExternalSecret<number>;\n scaleOutCooldown: ValueOrExternalSecret<number>;\n };\n environment?: StringMap;\n\n rdsIdsMajorVersionSuffix?: boolean;\n rdsPersistentParameterGroups?: boolean;\n}\n\nexport interface MedplumInfraConfig {\n name: string;\n stackName: string;\n accountNumber: string;\n region: string;\n domainName: string;\n vpcId: string;\n apiPort: number;\n apiDomainName: string;\n apiSslCertArn: string;\n apiInternetFacing?: boolean;\n apiWafIpSetArn?: string;\n appDomainName: string;\n appSslCertArn: string;\n appApiProxy?: boolean;\n appWafIpSetArn?: string;\n appLoggingBucket?: string;\n appLoggingPrefix?: string;\n storageBucketName: string;\n storageDomainName: string;\n storageSslCertArn: string;\n signingKeyId: string;\n storagePublicKey: string;\n storageWafIpSetArn?: string;\n storageLoggingBucket?: string;\n storageLoggingPrefix?: string;\n baseUrl: string;\n maxAzs: number;\n rdsInstances: number;\n rdsInstanceType: string;\n rdsInstanceVersion?: string;\n rdsClusterParameters?: StringMap;\n rdsSecretsArn?: string;\n rdsReaderInstanceType?: string;\n rdsProxyEnabled?: boolean;\n cacheNodeType?: string;\n cacheSecurityGroupId?: string;\n desiredServerCount: number;\n serverImage: string;\n serverMemory: number;\n serverCpu: number;\n loadBalancerSecurityGroupId?: string;\n loadBalancerLoggingBucket?: string;\n loadBalancerLoggingPrefix?: string;\n clamscanEnabled: boolean;\n clamscanLoggingBucket: string;\n clamscanLoggingPrefix: string;\n skipDns?: boolean;\n hostedZoneName?: string;\n additionalContainers?: {\n name: string;\n image: string;\n cpu?: number;\n memory?: number;\n essential?: boolean;\n command?: string[];\n environment?: {\n [key: string]: string;\n };\n }[];\n containerInsights?: boolean;\n cloudTrailAlarms?: {\n logGroupName: string;\n logGroupCreate?: boolean;\n snsTopicArn?: string;\n snsTopicName?: string;\n };\n fargateAutoScaling?: {\n minCapacity: number;\n maxCapacity: number;\n targetUtilizationPercent: number;\n scaleInCooldown: number;\n scaleOutCooldown: number;\n };\n environment?: StringMap;\n\n rdsIdsMajorVersionSuffix?: boolean;\n rdsPersistentParameterGroups?: boolean;\n}\n", "import { AccessPolicyResource } from '@medplum/fhirtypes';\nimport { InternalSchemaElement } from './typeschema/types';\nimport { getPathDifference, splitN } from './utils';\n\nexport interface ExtendedInternalSchemaElement extends InternalSchemaElement {\n readonly?: boolean;\n}\n\nexport type ExtendedElementProperties = { readonly: boolean; hidden: boolean };\n\n/*\nThroughout ElementsContext and the ResourceForm components, we use the following terminology:\n\"path\" refers to the FHIR path to an element including the resourceType, e.g. Patient.name.family\n\"key\" is a contextually relative path to an element not prefixed by the resourceType, e.g. name.family,\n*/\n\n/**\n * Information for the set of elements at a given path within in a resource. This mostly exists to\n * normalize access to elements regardless of whether they are from a profile, extension, or slice.\n */\nexport type ElementsContextType = {\n /** The FHIR path from the root resource to which the keys of `elements` are relative. */\n path: string;\n /**\n * The mapping of keys to `ExtendedInternalSchemaElement` at the current `path` relative to the\n * root resource. `elements` originate from either `InternalTypeSchema.elements` or\n * `SliceDefinition.elements` when the elements context is created within a slice.\n */\n elements: Record<string, ExtendedInternalSchemaElement>;\n /**\n * Similar mapping as `elements`, but with keys being the full path from the root resource rather\n * than relative to `path`, in other words, the keys of the Record are `${path}.${key}`.\n */\n elementsByPath: Record<string, ExtendedInternalSchemaElement>;\n /** The URL, if any, of the resource profile or extension from which the `elements` collection originated. */\n profileUrl: string | undefined;\n /** Whether debug logging is enabled */\n debugMode: boolean;\n /** The `AccessPolicyResource` provided, if any, used to determine hidden and readonly elements. */\n accessPolicyResource?: AccessPolicyResource;\n /**\n * Used to get an `ExtendedElementProperties` object for an element at a given path. This\n * is primarily useful when working with elements not included in `InternalTypeSchema.elements`\n * as is the case for nested elements that have not been modified by a profile or extension,\n * e.g. Patient.name.family.\n *\n * This function does not attempt to determine if the input `path` is actually an element in the\n * resource. When a syntactically correct path to a nonexistent element, e.g. Patient.foobar, is provided,\n * a `ExtendedElementProperties` object with default values is returned.\n *\n * @param path - The full path to an element in the resource, e.g. Patient.name.family\n * @returns An `ExtendedElementProperties` object with `readonly` and `hidden` properties for the\n * element at `path`, or `undefined` if the input path is malformed.\n */\n getExtendedProps(path: string): ExtendedElementProperties | undefined;\n /** `true` if this is a default/placeholder `ElementsContextType` */\n isDefaultContext?: boolean;\n};\n\nexport function buildElementsContext({\n parentContext,\n path,\n elements,\n profileUrl,\n debugMode,\n accessPolicyResource,\n}: {\n /** The most recent `ElementsContextType` in which this context is being built. */\n parentContext: ElementsContextType | undefined;\n /** The FHIR path from the root resource to which the keys of `elements` are relative. */\n path: string;\n /**\n * The mapping of keys to `InternalSchemaElement` at the current `path` relative to the\n * root resource. This should be either `InternalTypeSchema.elements` or `SliceDefinition.elements`.\n */\n elements: Record<string, InternalSchemaElement>;\n /** The URL, if any, of the resource profile or extension from which the `elements` collection originated. */\n profileUrl?: string;\n /** Whether debug logging is enabled */\n debugMode?: boolean;\n accessPolicyResource?: AccessPolicyResource;\n}): ElementsContextType | undefined {\n if (path === parentContext?.path) {\n return undefined;\n }\n\n debugMode ??= parentContext?.debugMode ?? false;\n accessPolicyResource ??= parentContext?.accessPolicyResource;\n\n let mergedElements: Record<string, ExtendedInternalSchemaElement> = mergeElementsForContext(\n path,\n elements,\n parentContext,\n Boolean(debugMode)\n );\n\n const keyPrefix = splitN(path, '.', 2)[1] as string | undefined;\n mergedElements = removeHiddenFields(mergedElements, accessPolicyResource, keyPrefix);\n mergedElements = markReadonlyFields(mergedElements, accessPolicyResource, keyPrefix);\n\n const elementsByPath: Record<string, ExtendedInternalSchemaElement> = Object.create(null);\n for (const [key, property] of Object.entries(mergedElements)) {\n elementsByPath[path + '.' + key] = property;\n }\n\n /*\n Since AccessPolicyResource.readonlyFields and hiddenFields are always relative to the root resource, we propagate\n a memoized `getExtendedProps` from the outermost ElementsContext\n */\n let getExtendedProps: (path: string) => ExtendedElementProperties | undefined;\n if (parentContext && !parentContext.isDefaultContext) {\n getExtendedProps = parentContext.getExtendedProps;\n } else {\n const memoizedExtendedProps: Record<string, ExtendedElementProperties> = Object.create(null);\n getExtendedProps = (path: string): ExtendedElementProperties | undefined => {\n const key = splitN(path, '.', 2)[1] as string | undefined;\n if (!key) {\n return undefined;\n }\n\n if (!memoizedExtendedProps[key]) {\n const hidden = matchesKeyPrefixes(key, accessPolicyResource?.hiddenFields);\n memoizedExtendedProps[key] = {\n hidden,\n // hidden implies readonly even if it's not explicitly marked as such\n readonly: hidden || matchesKeyPrefixes(key, accessPolicyResource?.readonlyFields),\n };\n }\n return memoizedExtendedProps[key];\n };\n }\n\n return {\n path: path,\n elements: mergedElements,\n elementsByPath,\n profileUrl: profileUrl ?? parentContext?.profileUrl,\n debugMode,\n getExtendedProps,\n accessPolicyResource,\n };\n}\n\nfunction mergeElementsForContext(\n path: string,\n elements: Record<string, InternalSchemaElement>,\n parentContext: ElementsContextType | undefined,\n debugMode: boolean\n): Record<string, InternalSchemaElement> {\n const result: Record<string, InternalSchemaElement> = Object.create(null);\n\n if (parentContext) {\n for (const [elementPath, element] of Object.entries(parentContext.elementsByPath)) {\n const key = getPathDifference(path, elementPath);\n if (key !== undefined) {\n result[key] = element;\n }\n }\n }\n\n let usedNewElements = false;\n if (elements) {\n for (const [key, element] of Object.entries(elements)) {\n if (!(key in result)) {\n result[key] = element;\n usedNewElements = true;\n }\n }\n }\n\n // if no new elements are used, the ElementsContext is unnecessary.\n // We could add another guard against unnecessary contexts if usedNewElements is false,\n // but unnecessary contexts **should** already be taken care before\n // this is ever hit. Leaving the debug logging in for now.\n if (debugMode) {\n console.assert(usedNewElements, 'Unnecessary ElementsContext; not using any newly provided elements');\n }\n return result;\n}\n\nfunction removeHiddenFields(\n elements: Record<string, InternalSchemaElement>,\n accessPolicyResource: AccessPolicyResource | undefined,\n keyPrefix?: string\n): Record<string, InternalSchemaElement> {\n if (!accessPolicyResource?.hiddenFields?.length) {\n return elements;\n }\n\n const prefix = keyPrefix ? keyPrefix + '.' : '';\n return Object.fromEntries(\n Object.entries(elements).filter(([key]) => !matchesKeyPrefixes(prefix + key, accessPolicyResource.hiddenFields))\n );\n}\n\nfunction markReadonlyFields(\n elements: Record<string, InternalSchemaElement>,\n accessPolicyResource: AccessPolicyResource | undefined,\n keyPrefix?: string\n): Record<string, ExtendedInternalSchemaElement> {\n if (!accessPolicyResource?.readonlyFields?.length) {\n return elements;\n }\n\n const result: Record<string, ExtendedInternalSchemaElement> = Object.create(null);\n\n const prefix = keyPrefix ? keyPrefix + '.' : '';\n for (const [key, element] of Object.entries(elements)) {\n const isReadonly = matchesKeyPrefixes(prefix + key, accessPolicyResource.readonlyFields);\n if (isReadonly) {\n // shallow-clone `element` to avoid modifying the in-memory DATA_TYPES cache access via `getDataType`\n result[key] = { ...element, readonly: true };\n } else {\n result[key] = element;\n }\n }\n\n return result;\n}\n\nfunction matchesKeyPrefixes(key: string, prefixes: string[] | undefined): boolean {\n // It might be a performance win to convert prefixes to a set, but the\n // cardinality of prefixes, i.e. hidden/readonly fields, is expected to be small (< 10)\n // such that the memory overhead of a set is not worth the performance gain.\n\n if (!prefixes?.length) {\n return false;\n }\n\n const keyParts = key.split('.');\n for (let i = 1; i <= keyParts.length; i++) {\n const key = keyParts.slice(0, i).join('.');\n if (prefixes.includes(key)) {\n return true;\n }\n }\n return false;\n}\n", "import { TypedValue } from '../types';\nimport { getNestedProperty } from './crawler';\nimport { InternalTypeSchema, SliceDefinition, SliceDiscriminator } from './types';\nimport { matchDiscriminant } from './validation';\n\nexport type SliceDefinitionWithTypes = SliceDefinition & {\n type: NonNullable<SliceDefinition['type']>;\n typeSchema?: InternalTypeSchema;\n};\n\nexport function isSliceDefinitionWithTypes(slice: SliceDefinition): slice is SliceDefinitionWithTypes {\n return slice.type !== undefined && slice.type.length > 0;\n}\n\nfunction isDiscriminatorComponentMatch(\n typedValue: TypedValue,\n discriminator: SliceDiscriminator,\n slice: SliceDefinitionWithTypes,\n profileUrl: string | undefined\n): boolean {\n const nestedProp = getNestedProperty(typedValue, discriminator.path, { profileUrl });\n\n if (nestedProp) {\n const elements = slice.typeSchema?.elements ?? slice.elements;\n return nestedProp.some((v: any) => matchDiscriminant(v, discriminator, slice, elements)) ?? false;\n }\n\n console.assert(false, 'getNestedProperty[%s] in isDiscriminatorComponentMatch missed', discriminator.path);\n return false;\n}\n\nexport function getValueSliceName(\n value: any,\n slices: SliceDefinitionWithTypes[],\n discriminators: SliceDiscriminator[],\n profileUrl: string | undefined\n): string | undefined {\n if (!value) {\n return undefined;\n }\n\n for (const slice of slices) {\n const typedValue: TypedValue = {\n value,\n type: slice.typeSchema?.type ?? slice.type?.[0].code,\n };\n if (\n discriminators.every((d) =>\n isDiscriminatorComponentMatch(typedValue, d, slice, slice.typeSchema?.url ?? profileUrl)\n )\n ) {\n return slice.name;\n }\n }\n return undefined;\n}\n", "import { ElementsContextType, buildElementsContext } from './elements-context';\nimport { SliceDefinitionWithTypes, isSliceDefinitionWithTypes } from './typeschema/slices';\nimport {\n InternalSchemaElement,\n InternalTypeSchema,\n SliceDefinition,\n SlicingRules,\n tryGetProfile,\n} from './typeschema/types';\nimport { isPopulated } from './utils';\n\nexport type VisitorSlicingRules = Omit<SlicingRules, 'slices'> & {\n slices: SliceDefinitionWithTypes[];\n};\n\nexport interface SchemaVisitor {\n /**\n * Called when entering a schema. This is called once for the root profile and once for each\n * extension with a profile associated with it.\n * @param schema - The schema being entered.\n */\n onEnterSchema?: (schema: InternalTypeSchema) => void;\n /**\n * Called when exiting a schema. See `onEnterSchema` for more information.\n * @param schema - The schema being exited.\n */\n onExitSchema?: (schema: InternalTypeSchema) => void;\n\n /**\n * Called when entering an element. This is called for every element in the schema in a\n * tree-like fashion. If the element has slices, the slices are crawled after `onEnterElement`\n * but before `onExitElement`.\n *\n * @example\n * Example of tree-like method invocation ordering:\n * '''typescript\n * onEnterElement('Patient.name')\n * onEnterElement('Patient.name.given')\n * onExitElement('Patient.name.given')\n * onEnterElement('Patient.name.family')\n * onExitElement('Patient.name.family')\n * onExitElement('Patient.name')\n * '''\n *\n *\n * @param path - The full path of the element being entered, even if within an extension. e.g The\n * path of the ombCategory extension within the US Core Race extension will be\n * 'Patient.extension.extension.value[x]' rather than 'Extension.extension.value[x]'. The latter is\n * accessible on the element parameter.\n * @param element - The element being entered.\n * @param elementsContext - The context of the elements currently being crawled.\n */\n onEnterElement?: (path: string, element: InternalSchemaElement, elementsContext: ElementsContextType) => void;\n\n /**\n * Called when exiting an element. See `onEnterElement` for more information.\n * @param path - The full path of the element being exited.\n * @param element - The element being exited.\n * @param elementsContext - The context of the elements currently being crawled.\n */\n onExitElement?: (path: string, element: InternalSchemaElement, elementsContext: ElementsContextType) => void;\n\n /**\n * Called when entering a slice. Called for every slice in a given sliced element. `onEnterElement` and `onExitElement`\n * will be called in a tree-like fashion for elements within the slice followed by `onExitSlice`.\n *\n * @example\n * Example of a sliced element being crawled with some elements excluded for brevity:\n * '''typescript\n * onEnterElement ('Observation.component')\n *\n * // systolic\n * onEnterSlice ('Observation.component', systolicSlice, slicingRules)\n * onEnterElement ('Observation.component.code')\n * onExitElement ('Observation.component.code')\n * onEnterElement ('Observation.component.value[x]')\n * onEnterElement ('Observation.component.value[x].code')\n * onExitElement ('Observation.component.value[x].code')\n * onEnterElement ('Observation.component.value[x].system')\n * onExitElement ('Observation.component.value[x].system')\n * onExitElement ('Observation.component.value[x]')\n * onExitSlice ('Observation.component', systolicSlice, slicingRules)\n *\n * // similar set of invocations for diastolic slice\n *\n * onExitElement ('Observation.component')\n * '''\n *\n * @param path - The full path of the sliced element being entered. See `onEnterElement` for more information.\n * @param slice - The slice being entered.\n * @param slicing - The slicing rules related to the slice being entered.\n */\n onEnterSlice?: (path: string, slice: SliceDefinitionWithTypes, slicing: VisitorSlicingRules) => void;\n\n /**\n * Called when exiting a slice. See `onEnterSlice` for more information.\n * @param path - The full path of the sliced element being exited. See `onEnterElement` for more information.\n * @param slice - The slice being exited.\n * @param slicing - The slicing rules related to the slice.\n */\n onExitSlice?: (path: string, slice: SliceDefinitionWithTypes, slicing: VisitorSlicingRules) => void;\n}\n\nexport class SchemaCrawler {\n private readonly rootSchema: InternalTypeSchema & { type: string };\n private readonly visitor: SchemaVisitor;\n private readonly elementsContextStack: ElementsContextType[];\n private sliceAllowList: SliceDefinition[] | undefined;\n\n constructor(schema: InternalTypeSchema, visitor: SchemaVisitor, elements?: InternalTypeSchema['elements']) {\n if (schema.type === undefined) {\n throw new Error('schema must include a type');\n }\n this.rootSchema = schema as InternalTypeSchema & { type: string };\n\n const rootContext = buildElementsContext({\n parentContext: undefined,\n path: this.rootSchema.type,\n elements: elements ?? this.rootSchema.elements,\n profileUrl: this.rootSchema.name === this.rootSchema.type ? undefined : this.rootSchema.url,\n });\n if (rootContext === undefined) {\n throw new Error('Could not create root elements context');\n }\n\n this.elementsContextStack = [rootContext];\n this.visitor = visitor;\n }\n\n private get elementsContext(): ElementsContextType {\n return this.elementsContextStack[this.elementsContextStack.length - 1];\n }\n\n crawlElement(element: InternalSchemaElement, key: string, path: string): void {\n if (this.visitor.onEnterSchema) {\n this.visitor.onEnterSchema(this.rootSchema);\n }\n\n const allowedElements = Object.fromEntries(\n Object.entries(this.elementsContext.elements).filter(([elementKey]) => {\n return elementKey.startsWith(key);\n })\n );\n\n this.crawlElementsImpl(allowedElements, path);\n\n if (this.visitor.onExitSchema) {\n this.visitor.onExitSchema(this.rootSchema);\n }\n }\n\n crawlSlice(key: string, slice: SliceDefinition, slicing: SlicingRules): void {\n const visitorSlicing = this.prepareSlices(slicing.slices, slicing);\n\n if (!isPopulated(visitorSlicing.slices)) {\n throw new Error(`cannot crawl slice ${slice.name} since it has no type information`);\n }\n\n if (this.visitor.onEnterSchema) {\n this.visitor.onEnterSchema(this.rootSchema);\n }\n\n this.sliceAllowList = [slice];\n\n this.crawlSliceImpl(visitorSlicing.slices[0], slice.path, visitorSlicing);\n this.sliceAllowList = undefined;\n\n if (this.visitor.onExitSchema) {\n this.visitor.onExitSchema(this.rootSchema);\n }\n }\n\n crawlResource(): void {\n if (this.visitor.onEnterSchema) {\n this.visitor.onEnterSchema(this.rootSchema);\n }\n\n this.crawlElementsImpl(this.rootSchema.elements, this.rootSchema.type);\n\n if (this.visitor.onExitSchema) {\n this.visitor.onExitSchema(this.rootSchema);\n }\n }\n\n private crawlElementsImpl(elements: InternalTypeSchema['elements'], path: string): void {\n const elementTree = createElementTree(elements);\n for (const node of elementTree) {\n this.crawlElementNode(node, path);\n }\n }\n\n private crawlElementNode(node: ElementNode, path: string): void {\n const nodePath = path + '.' + node.key;\n if (this.visitor.onEnterElement) {\n this.visitor.onEnterElement(nodePath, node.element, this.elementsContext);\n }\n\n for (const child of node.children) {\n this.crawlElementNode(child, path);\n }\n\n if (isPopulated(node.element?.slicing?.slices)) {\n this.crawlSlicingImpl(node.element.slicing, nodePath);\n }\n\n if (this.visitor.onExitElement) {\n this.visitor.onExitElement(nodePath, node.element, this.elementsContext);\n }\n }\n\n private prepareSlices(slices: SliceDefinition[], slicing: SlicingRules): VisitorSlicingRules {\n const slicesToVisit: SliceDefinitionWithTypes[] = [];\n for (const slice of slices) {\n if (!isSliceDefinitionWithTypes(slice)) {\n continue;\n }\n const profileUrl = slice.type.find((t) => isPopulated(t.profile))?.profile?.[0];\n if (isPopulated(profileUrl)) {\n const schema = tryGetProfile(profileUrl);\n if (schema) {\n slice.typeSchema = schema;\n }\n }\n slicesToVisit.push(slice);\n }\n\n const visitorSlicing = { ...slicing, slices: slicesToVisit } as VisitorSlicingRules;\n return visitorSlicing;\n }\n\n private crawlSlicingImpl(slicing: SlicingRules, path: string): void {\n const visitorSlicing = this.prepareSlices(slicing.slices, slicing);\n\n for (const slice of visitorSlicing.slices) {\n if (this.sliceAllowList === undefined || this.sliceAllowList.includes(slice)) {\n this.crawlSliceImpl(slice, path, visitorSlicing);\n }\n }\n }\n\n private crawlSliceImpl(slice: SliceDefinitionWithTypes, path: string, slicing: VisitorSlicingRules): void {\n const sliceSchema = slice.typeSchema;\n if (sliceSchema) {\n if (this.visitor.onEnterSchema) {\n this.visitor.onEnterSchema(sliceSchema);\n }\n }\n\n if (this.visitor.onEnterSlice) {\n this.visitor.onEnterSlice(path, slice, slicing);\n }\n\n let elementsContext: ElementsContextType | undefined;\n\n const sliceElements = sliceSchema?.elements ?? slice.elements;\n if (isPopulated(sliceElements)) {\n elementsContext = buildElementsContext({\n path,\n parentContext: this.elementsContext,\n elements: sliceElements,\n });\n }\n if (elementsContext) {\n this.elementsContextStack.push(elementsContext);\n }\n\n this.crawlElementsImpl(sliceElements, path);\n\n if (elementsContext) {\n this.elementsContextStack.pop();\n }\n\n if (this.visitor.onExitSlice) {\n this.visitor.onExitSlice(path, slice, slicing);\n }\n\n if (sliceSchema) {\n if (this.visitor.onExitSchema) {\n this.visitor.onExitSchema(sliceSchema);\n }\n }\n }\n}\n\ntype ElementNode = {\n key: string;\n element: InternalSchemaElement;\n children: ElementNode[];\n};\n\n/**\n * Creates a tree of InternalSchemaElements nested by their key hierarchy:\n *\n * @param elements -\n * @returns The list of root nodes of the tree\n */\nfunction createElementTree(elements: Record<string, InternalSchemaElement>): ElementNode[] {\n const rootNodes: ElementNode[] = [];\n\n function isChildKey(parentKey: string, childKey: string): boolean {\n return childKey.startsWith(parentKey + '.');\n }\n\n function addNode(currentNode: ElementNode, newNode: ElementNode): void {\n for (const child of currentNode.children) {\n // If the new node is a child of an existing child, recurse deeper\n if (isChildKey(child.key, newNode.key)) {\n addNode(child, newNode);\n return;\n }\n }\n // Otherwise, add it here\n currentNode.children.push(newNode);\n }\n\n const elementEntries = Object.entries(elements);\n /*\n By sorting beforehand, we guarantee that no false root nodes are created.\n e.g. if 'a.b' were to be added to the tree before 'a', 'a.b' would be made a\n root node when it should be a child of 'a'.\n */\n elementEntries.sort((a, b) => a[0].localeCompare(b[0]));\n\n for (const [key, element] of elementEntries) {\n const newNode: ElementNode = { key, element, children: [] };\n\n let added = false;\n for (const rootNode of rootNodes) {\n if (isChildKey(rootNode.key, key)) {\n addNode(rootNode, newNode);\n added = true;\n break;\n }\n }\n\n // If the string is not a child of any existing node, add it as a new root\n if (!added) {\n rootNodes.push(newNode);\n }\n }\n\n return rootNodes;\n}\n", "import { Resource } from '@medplum/fhirtypes';\nimport { SchemaCrawler, SchemaVisitor, VisitorSlicingRules } from './schema-crawler';\nimport { SliceDefinitionWithTypes, getValueSliceName } from './typeschema/slices';\nimport { InternalSchemaElement, InternalTypeSchema, SliceDefinition, SlicingRules } from './typeschema/types';\nimport { capitalize, deepClone, getPathDifference, isComplexTypeCode, isEmpty, isObject, isPopulated } from './utils';\nimport { ElementsContextType } from './elements-context';\n\n/**\n * Used when an array entry, typically an empty one, needs to be assigned\n * to a given slice even though it doesn't match the slice's discriminator.\n */\nconst SLICE_NAME_KEY = '__sliceName';\n\n/**\n * Adds default values to `resource` based on the supplied `schema`. Default values includes all required fixed and pattern\n * values specified on elements in the schema. If an element has a fixed/pattern value but is optional, i.e.\n * `element.min === 0`, the default value is not added.\n *\n * @param resource - The resource to which default values should be added.\n * @param schema - The schema to use for adding default values.\n * @returns A clone of `resource` with default values added.\n */\nexport function applyDefaultValuesToResource(resource: Resource, schema: InternalTypeSchema): Resource {\n const visitor = new DefaultValueVisitor(resource, resource.resourceType, 'resource');\n const crawler = new SchemaCrawler(schema, visitor);\n crawler.crawlResource();\n return visitor.getDefaultValue();\n}\n\n/**\n * Adds default values to `existingValue` for the given `key` and its children. If `key` is undefined,\n * default values are added to all elements in `elements`. Default values consist of all fixed and pattern\n * values defined in the relevant elements.\n * @param existingValue - The\n * @param elements - The elements to which default values should be added.\n * @param key - (optional) The key of the element(s) for which default values should be added. Elements with nested\n * keys are also included. If undefined, default values for all elements are added.\n * @returns `existingValue` with default values added\n */\nexport function applyDefaultValuesToElement(\n existingValue: object,\n elements: Record<string, InternalSchemaElement>,\n key?: string\n): object {\n for (const [elementKey, element] of Object.entries(elements)) {\n if (key === undefined || key === elementKey) {\n applyFixedOrPatternValue(existingValue, elementKey, element, elements);\n continue;\n }\n\n const keyDifference = getPathDifference(key, elementKey);\n if (keyDifference !== undefined) {\n applyFixedOrPatternValue(existingValue, keyDifference, element, elements);\n }\n }\n\n return existingValue;\n}\n\nexport function getDefaultValuesForNewSliceEntry(\n key: string,\n slice: SliceDefinition,\n slicing: SlicingRules,\n schema: InternalTypeSchema\n): Resource {\n const visitor = new DefaultValueVisitor([{ [SLICE_NAME_KEY]: slice.name }], slice.path, 'element');\n const crawler = new SchemaCrawler(schema, visitor);\n crawler.crawlSlice(key, slice, slicing);\n return visitor.getDefaultValue()[0];\n}\n\ntype ValueContext = {\n type: 'resource' | 'element' | 'slice';\n path: string;\n values: any[];\n};\n\nclass DefaultValueVisitor implements SchemaVisitor {\n private rootValue: any;\n\n private readonly schemaStack: InternalTypeSchema[];\n private readonly valueStack: ValueContext[];\n\n constructor(rootValue: any, path: string, type: ValueContext['type']) {\n this.schemaStack = [];\n this.valueStack = [];\n\n this.rootValue = deepClone(rootValue);\n this.valueStack.splice(0, this.valueStack.length, {\n type,\n path,\n values: [this.rootValue],\n });\n }\n\n private get schema(): InternalTypeSchema {\n return this.schemaStack[this.schemaStack.length - 1];\n }\n\n private get value(): ValueContext {\n return this.valueStack[this.valueStack.length - 1];\n }\n\n onEnterSchema(schema: InternalTypeSchema): void {\n this.schemaStack.push(schema);\n }\n\n onExitSchema(): void {\n this.schemaStack.pop();\n }\n\n onEnterElement(path: string, element: InternalSchemaElement, elementsContext: ElementsContextType): void {\n // eld-6: Fixed value may only be specified if there is one type\n // eld-7: Pattern may only be specified if there is one type\n // It may be possible to optimize this by checking element.type.length > 1 and short-circuiting\n\n const parentValues = this.value.values;\n const parentPath = this.value.path;\n const key = getPathDifference(parentPath, path);\n if (key === undefined) {\n throw new Error(`Expected ${path} to be prefixed by ${parentPath}`);\n }\n const elementValues: any[] = [];\n\n for (const parentValue of parentValues) {\n if (parentValue === undefined) {\n continue;\n }\n\n const elementsKeyPrefix = getPathDifference(elementsContext.path, parentPath);\n const parentArray: any[] = Array.isArray(parentValue) ? parentValue : [parentValue];\n for (const parent of parentArray) {\n applyMinimums(parent, key, element, elementsContext.elements, elementsKeyPrefix);\n applyFixedOrPatternValue(parent, key, element, elementsContext.elements);\n const elementValue = getValueAtKey(parent, key, elementsContext.elements, elementsKeyPrefix);\n if (elementValue !== undefined) {\n elementValues.push(elementValue);\n }\n }\n }\n\n this.valueStack.push({\n type: 'element',\n path: path,\n values: elementValues,\n });\n }\n\n onExitElement(path: string, element: InternalSchemaElement, elementsContext: ElementsContextType): void {\n const elementValueContext = this.valueStack.pop();\n if (!elementValueContext) {\n throw new Error('Expected value context to exist when exiting element');\n }\n\n const key = getPathDifference(this.value.path, path);\n if (key === undefined) {\n throw new Error(`Expected ${path} to be prefixed by ${this.value.path}`);\n }\n\n const elementsKeyPrefix = getPathDifference(elementsContext.path, this.value.path);\n for (const parentValue of this.value.values) {\n const elementValue = getValueAtKey(parentValue, key, elementsContext.elements, elementsKeyPrefix);\n\n // remove empty items from arrays\n if (Array.isArray(elementValue)) {\n for (let i = elementValue.length - 1; i >= 0; i--) {\n const value: any = elementValue[i];\n if (!isPopulated(value)) {\n elementValue.splice(i, 1);\n }\n }\n }\n\n if (isEmpty(elementValue)) {\n // setting undefined to delete the key\n setValueAtKey(parentValue, undefined, key, element);\n }\n }\n }\n\n onEnterSlice(path: string, slice: SliceDefinitionWithTypes, slicing: VisitorSlicingRules): void {\n const elementValues = this.value.values;\n const sliceValues: any[] = [];\n\n for (const value of elementValues) {\n if (value !== undefined) {\n const elementValues = Array.isArray(value) ? value : [value];\n const matchingItems: any[] = this.getMatchingSliceValues(elementValues, slice, slicing);\n sliceValues.push(matchingItems);\n }\n }\n\n this.valueStack.push({\n type: 'slice',\n path,\n values: sliceValues,\n });\n }\n\n getMatchingSliceValues(elementValue: any[], slice: SliceDefinitionWithTypes, slicing: VisitorSlicingRules): any[] {\n const matchingItems: any[] = [];\n for (const arrayItem of elementValue) {\n const sliceName: string | undefined =\n arrayItem[SLICE_NAME_KEY] ?? getValueSliceName(arrayItem, [slice], slicing.discriminator, this.schema.url);\n\n if (sliceName === slice.name) {\n matchingItems.push(arrayItem);\n }\n }\n\n // Make sure at least slice.min values exist\n for (let i = matchingItems.length; i < slice.min; i++) {\n if (isComplexTypeCode(slice.type[0].code)) {\n const emptySliceValue = Object.create(null);\n matchingItems.push(emptySliceValue);\n\n // push onto input array so that it propagates upwards as well\n elementValue.push(emptySliceValue);\n }\n }\n\n return matchingItems;\n }\n\n onExitSlice(): void {\n const sliceValuesContext = this.valueStack.pop();\n if (!sliceValuesContext) {\n throw new Error('Expected value context to exist in onExitSlice');\n }\n\n for (const sliceValueArray of sliceValuesContext.values) {\n for (let i = sliceValueArray.length - 1; i >= 0; i--) {\n const sliceValue = sliceValueArray[i];\n if (SLICE_NAME_KEY in sliceValue) {\n delete sliceValue[SLICE_NAME_KEY];\n }\n }\n }\n }\n\n getDefaultValue(): any {\n return this.rootValue;\n }\n}\n\nfunction applyMinimums(\n parent: any,\n key: string,\n element: InternalSchemaElement,\n elements: Record<string, InternalSchemaElement>,\n /** The prefix, if any, that should be added to keys when looking up values in `elements` */\n elementsKeyPrefix: string | undefined\n): void {\n const existingValue = getValueAtKey(parent, key, elements, elementsKeyPrefix);\n\n if (element.min > 0 && existingValue === undefined) {\n if (isComplexTypeCode(element.type[0].code)) {\n if (element.isArray) {\n setValueAtKey(parent, [Object.create(null)], key, element);\n } else {\n setValueAtKey(parent, Object.create(null), key, element);\n }\n }\n }\n}\n\nfunction setValueAtKey(parent: any, value: any, key: string, element: InternalSchemaElement): void {\n if (key.includes('.')) {\n throw new Error('key cannot be nested');\n }\n\n let resolvedKey = key;\n\n if (key.includes('[x]')) {\n const code = element.type[0].code;\n resolvedKey = key.replace('[x]', capitalize(code));\n }\n\n if (value === undefined) {\n delete parent[resolvedKey];\n } else {\n parent[resolvedKey] = value;\n }\n}\n\nfunction getValueAtKey(\n value: object,\n key: string,\n elements: Record<string, InternalSchemaElement>,\n /** The prefix, if any, that should be added to keys when looking up values in `elements` */\n elementsKeyPrefix: string | undefined\n): any {\n const keyParts = key.split('.');\n let last: any = value;\n let answer: any;\n for (let i = 0; i < keyParts.length; i++) {\n let keyPart = keyParts[i];\n if (keyPart.includes('[x]')) {\n const key = (elementsKeyPrefix ? elementsKeyPrefix + '.' : '') + keyParts.slice(0, i + 1).join('.');\n const keyPartElem = elements[key];\n\n // this should loop through all possible types instead of using type[0]\n const code = keyPartElem.type[0].code;\n keyPart = keyPart.replace('[x]', capitalize(code));\n }\n\n // final key part\n if (i === keyParts.length - 1) {\n if (Array.isArray(last)) {\n answer = last.map((item) => item[keyPart]);\n } else {\n answer = last[keyPart];\n }\n continue;\n }\n\n // intermediate key part\n if (Array.isArray(last)) {\n last = last.map((lastItem) => lastItem[keyPart]);\n } else if (isObject(last)) {\n if (last[keyPart] === undefined) {\n return undefined;\n }\n last = last[keyPart];\n } else {\n return undefined;\n }\n }\n\n return answer;\n}\n\nexport function applyFixedOrPatternValue(\n inputValue: any,\n key: string,\n element: InternalSchemaElement,\n elements: Record<string, InternalSchemaElement>\n): any {\n if (!(element.fixed || element.pattern)) {\n return inputValue;\n }\n\n if (Array.isArray(inputValue)) {\n return inputValue.map((iv) => applyFixedOrPatternValue(iv, key, element, elements));\n }\n\n if (inputValue === undefined || inputValue === null) {\n inputValue = Object.create(null);\n }\n\n const outputValue = inputValue;\n\n const keyParts = key.split('.');\n let last: any = outputValue;\n for (let i = 0; i < keyParts.length; i++) {\n let keyPart = keyParts[i];\n if (keyPart.includes('[x]')) {\n const keyPartElem = elements[keyParts.slice(0, i + 1).join('.')];\n const code = keyPartElem.type[0].code;\n keyPart = keyPart.replace('[x]', capitalize(code));\n }\n\n if (i === keyParts.length - 1) {\n const lastArray = Array.isArray(last) ? last : [last];\n for (const item of lastArray) {\n if (element.fixed) {\n item[keyPart] ??= element.fixed.value;\n } else if (element.pattern) {\n item[keyPart] = applyPattern(item[keyPart], element.pattern.value);\n }\n }\n } else {\n if (!(keyPart in last)) {\n const elementKey = keyParts.slice(0, i + 1).join('.');\n last[keyPart] = elements[elementKey].isArray ? [Object.create(null)] : Object.create(null);\n }\n last = last[keyPart];\n }\n }\n return outputValue;\n}\n\nfunction applyPattern(existingValue: any, pattern: any): any {\n if (Array.isArray(pattern) && (Array.isArray(existingValue) || existingValue === undefined)) {\n if ((existingValue?.length ?? 0) > 0) {\n // Cannot yet apply a pattern to a non-empty array since that would require considering cardinality and slicing\n return existingValue;\n }\n return deepClone(pattern);\n } else if (isObject(pattern)) {\n if ((isObject(existingValue) && !Array.isArray(existingValue)) || existingValue === undefined) {\n const resultObj = (deepClone(existingValue) ?? Object.create(null)) as { [key: string]: any };\n for (const key of Object.keys(pattern)) {\n resultObj[key] = applyPattern(resultObj[key], pattern[key]);\n }\n return resultObj;\n }\n }\n\n return existingValue;\n}\n", "import { CodeableConcept, Coding, ConceptMap, ConceptMapGroup, OperationOutcome } from '@medplum/fhirtypes';\nimport { OperationOutcomeError, badRequest, isOperationOutcome } from '../outcomes';\n\nexport interface ConceptMapTranslateParameters {\n url?: string;\n source?: string;\n code?: string;\n system?: string;\n coding?: Coding;\n codeableConcept?: CodeableConcept;\n targetsystem?: string;\n}\n\nexport interface ConceptMapTranslateMatch {\n equivalence?: string;\n concept?: Coding;\n}\n\nexport interface ConceptMapTranslateOutput {\n result: boolean;\n message?: string;\n match?: ConceptMapTranslateMatch[];\n}\n\nexport function conceptMapTranslate(map: ConceptMap, params: ConceptMapTranslateParameters): ConceptMapTranslateOutput {\n if (!map.group) {\n throw new OperationOutcomeError(badRequest('ConceptMap does not specify a mapping group', 'ConceptMap.group'));\n }\n\n const sourceCodes = constructSourceSet(params);\n if (isOperationOutcome(sourceCodes)) {\n throw new OperationOutcomeError(sourceCodes);\n }\n\n const matches = translateCodes(\n sourceCodes,\n params.targetsystem ? map.group.filter((g) => g.target === params.targetsystem) : map.group\n );\n\n const result = matches.length > 0;\n\n return {\n result,\n match: result ? matches : undefined,\n };\n}\n\nfunction constructSourceSet(params: ConceptMapTranslateParameters): Record<string, string[]> | OperationOutcome {\n if (params.code && !params.coding && !params.codeableConcept) {\n if (params.system === undefined) {\n return badRequest(`Missing required 'system' input parameter with 'code' parameter`);\n }\n return { [params.system]: [params.code] };\n } else if (params.coding && !params.code && !params.codeableConcept) {\n return { [params.coding.system ?? '']: [params.coding.code ?? ''] };\n } else if (params.codeableConcept && !params.code && !params.coding) {\n return indexCodes(params.codeableConcept);\n } else if (params.code || params.coding || params.codeableConcept) {\n return badRequest('Ambiguous input: multiple source codings provided');\n } else {\n return badRequest(\n `No source provided: 'code'+'system', 'coding', or 'codeableConcept' input parameter is required`\n );\n }\n}\n\nfunction indexCodes(concept: CodeableConcept): Record<string, string[]> {\n const result: Record<string, string[]> = Object.create(null);\n if (!concept.coding?.length) {\n return result;\n }\n\n for (const { system, code } of concept.coding) {\n if (!code) {\n continue;\n }\n const key = system ?? '';\n result[key] = result[key] ? [...result[key], code] : [code];\n }\n return result;\n}\n\nfunction translateCodes(sourceCodes: Record<string, string[]>, groups: ConceptMapGroup[]): ConceptMapTranslateMatch[] {\n const matches: ConceptMapTranslateMatch[] = [];\n for (const [system, codes] of Object.entries(sourceCodes)) {\n for (const group of groups.filter((g) => (g.source ?? '') === system)) {\n let mappings: ConceptMapTranslateMatch[] | undefined = group.element\n ?.filter((m) => codes.includes(m.code as string))\n .flatMap(\n (m) =>\n m.target?.map((target) => ({\n equivalence: target.equivalence,\n concept: {\n system: group.target,\n code: target.code,\n display: target.display,\n },\n })) ?? []\n );\n\n if (!mappings?.length) {\n mappings = handleUnmappedCodes(codes, group);\n }\n if (mappings) {\n matches.push(...mappings);\n }\n }\n }\n return matches;\n}\n\nfunction handleUnmappedCodes(codes: string[], group: ConceptMapGroup): ConceptMapTranslateMatch[] | undefined {\n switch (group.unmapped?.mode) {\n case 'provided':\n return codes.map((code) => ({\n equivalence: 'equal',\n concept: { system: group.target, code },\n }));\n case 'fixed':\n return [\n {\n equivalence: 'equivalent',\n concept: {\n system: group.target,\n code: group.unmapped.code,\n display: group.unmapped.display,\n },\n },\n ];\n default:\n return undefined;\n }\n}\n", "import { Token, Tokenizer } from '../fhirlexer/tokenize';\nimport { FHIRPATH_KEYWORDS, FHIRPATH_OPERATORS } from '../fhirpath/tokenize';\n\nconst MAPPING_LANGUAGE_OPERATORS = [...FHIRPATH_OPERATORS, '->', '<<', '>>', '=='];\n\nexport function tokenize(str: string): Token[] {\n return new Tokenizer(str, FHIRPATH_KEYWORDS, MAPPING_LANGUAGE_OPERATORS).tokenize();\n}\n", "import {\n ConceptMap,\n StructureMap,\n StructureMapGroup,\n StructureMapGroupInput,\n StructureMapGroupRule,\n StructureMapGroupRuleDependent,\n StructureMapGroupRuleSource,\n StructureMapGroupRuleTarget,\n StructureMapGroupRuleTargetParameter,\n StructureMapStructure,\n} from '@medplum/fhirtypes';\nimport { Atom, Parser } from '../fhirlexer/parse';\nimport { FunctionAtom, LiteralAtom, SymbolAtom } from '../fhirpath/atoms';\nimport { OperatorPrecedence, initFhirPathParserBuilder } from '../fhirpath/parse';\nimport { tokenize } from './tokenize';\n\n/**\n * Mapping from FHIR Mapping Language equivalence operators to FHIR ConceptMap equivalence codes.\n *\n * See: https://build.fhir.org/mapping.g4 for FHIR Mapping Language operators.\n *\n * See: https://hl7.org/fhir/r4/valueset-concept-map-equivalence.html for ConceptMap equivalence codes.\n *\n * @internal\n */\nconst CONCEPT_MAP_EQUIVALENCE: Record<string, string> = {\n '-': 'disjoint',\n '==': 'equal',\n};\n\nclass StructureMapParser {\n readonly structureMap: Partial<StructureMap> = {\n resourceType: 'StructureMap',\n status: 'active',\n };\n\n constructor(readonly parser: Parser) {}\n\n parse(): StructureMap {\n while (this.parser.hasMore()) {\n const next = this.parser.peek()?.value;\n switch (next) {\n case 'map':\n this.parseMap();\n break;\n case 'uses':\n this.parseUses();\n break;\n case 'imports':\n this.parseImport();\n break;\n case 'group':\n this.parseGroup();\n break;\n case 'conceptmap':\n this.parseConceptMap();\n break;\n default:\n throw new Error(`Unexpected token: ${next}`);\n }\n }\n return this.structureMap as StructureMap;\n }\n\n private parseMap(): void {\n // 'map' url '=' identifier\n // map \"http://hl7.org/fhir/StructureMap/tutorial\" = tutorial\n this.parser.consume('Symbol', 'map');\n this.structureMap.url = this.parser.consume('String').value;\n this.parser.consume('=');\n this.structureMap.name = this.parser.consume().value;\n }\n\n private parseUses(): void {\n // 'uses' url structureAlias? 'as' modelMode\n // uses \"http://hl7.org/fhir/StructureDefinition/tutorial-left\" as source\n this.parser.consume('Symbol', 'uses');\n const result: Partial<StructureMapStructure> = {};\n result.url = this.parser.consume('String').value;\n if (this.parser.peek()?.value === 'alias') {\n this.parser.consume('Symbol', 'alias');\n result.alias = this.parser.consume('Symbol').value;\n }\n this.parser.consume('Symbol', 'as');\n result.mode = this.parser.consume().value as 'source' | 'queried' | 'target' | 'produced';\n if (!this.structureMap.structure) {\n this.structureMap.structure = [];\n }\n this.structureMap.structure.push(result as StructureMapStructure);\n }\n\n private parseImport(): void {\n this.parser.consume('Symbol', 'imports');\n if (!this.structureMap.import) {\n this.structureMap.import = [];\n }\n this.structureMap.import.push(this.parser.consume('String').value);\n }\n\n private parseGroup(): void {\n // 'group' identifier parameters extends? typeMode? rules\n // group tutorial(source src : TLeft, target tgt : TRight) {\n const result: Partial<StructureMapGroup> = {};\n this.parser.consume('Symbol', 'group');\n result.name = this.parser.consume('Symbol').value;\n result.input = this.parseParameters();\n\n if (this.parser.peek()?.value === 'extends') {\n this.parser.consume('Symbol', 'extends');\n result.extends = this.parser.consume('Symbol').value;\n }\n\n if (this.parser.peek()?.value === '<<') {\n this.parser.consume('<<');\n result.typeMode = this.parser.consume().value as 'none' | 'types' | 'type-and-types';\n if (this.parser.peek()?.value === '+') {\n this.parser.consume('+');\n result.typeMode = 'type-and-types';\n }\n this.parser.consume('>>');\n } else {\n result.typeMode = 'none';\n }\n\n result.rule = this.parseRules();\n\n if (!this.structureMap.group) {\n this.structureMap.group = [];\n }\n this.structureMap.group.push(result as StructureMapGroup);\n }\n\n private parseParameters(): StructureMapGroupInput[] {\n const parameters: StructureMapGroupInput[] = [];\n this.parser.consume('(');\n while (this.parser.hasMore() && this.parser.peek()?.value !== ')') {\n parameters.push(this.parseParameter());\n if (this.parser.peek()?.value === ',') {\n this.parser.consume(',');\n }\n }\n this.parser.consume(')');\n return parameters;\n }\n\n private parseParameter(): StructureMapGroupInput {\n // inputMode identifier type?\n // ':' identifier\n // source src : TLeft\n const result: Partial<StructureMapGroupInput> = {};\n result.mode = this.parser.consume().value as 'source' | 'target';\n result.name = this.parser.consume('Symbol').value;\n if (this.parser.peek()?.value === ':') {\n this.parser.consume(':');\n result.type = this.parser.consume('Symbol').value;\n }\n return result as StructureMapGroupInput;\n }\n\n private parseRules(): StructureMapGroupRule[] {\n const rules = [];\n this.parser.consume('{');\n while (this.parser.hasMore() && this.parser.peek()?.value !== '}') {\n rules.push(this.parseRule());\n }\n this.parser.consume('}');\n return rules;\n }\n\n private parseRule(): StructureMapGroupRule {\n const result: Partial<StructureMapGroupRule> = {\n source: this.parseRuleSources(),\n };\n\n if (this.parser.peek()?.value === '->') {\n this.parser.consume('->');\n result.target = this.parseRuleTargets();\n }\n\n if (this.parser.peek()?.value === 'then') {\n this.parser.consume('Symbol', 'then');\n if (this.parser.peek()?.id === '{') {\n result.rule = this.parseRules();\n } else {\n result.dependent = this.parseRuleDependents();\n }\n }\n\n if (this.parser.peek()?.id === 'String') {\n result.name = this.parser.consume().value;\n } else {\n result.name = result.source?.[0]?.element;\n }\n\n this.parser.consume(';');\n return result as StructureMapGroupRule;\n }\n\n private parseRuleSources(): StructureMapGroupRuleSource[] {\n if (this.parser.hasMore() && this.parser.peek()?.value === 'for') {\n // The \"for\" keyword is optional\n // It is not in the official grammar: https://build.fhir.org/mapping.g4\n // But it is used in the examples: https://build.fhir.org/mapping-tutorial.html\n this.parser.consume('Symbol', 'for');\n }\n const sources = [this.parseRuleSource()];\n while (this.parser.hasMore() && this.parser.peek()?.value === ',') {\n this.parser.consume(',');\n sources.push(this.parseRuleSource());\n }\n return sources;\n }\n\n private parseRuleSource(): StructureMapGroupRuleSource {\n const result: Partial<StructureMapGroupRuleSource> = {};\n\n const context = this.parseRuleContext();\n const parts = context.split('.');\n result.context = parts[0];\n result.element = parts[1];\n\n if (this.parser.hasMore() && this.parser.peek()?.value === ':') {\n this.parser.consume(':');\n result.type = this.parser.consume().value;\n }\n\n if (this.parser.hasMore() && this.parser.peek()?.value === 'default') {\n this.parser.consume('Symbol', 'default');\n result.defaultValueString = this.parser.consume('String').value;\n }\n\n if (\n this.parser.peek()?.value === 'first' ||\n this.parser.peek()?.value === 'not_first' ||\n this.parser.peek()?.value === 'last' ||\n this.parser.peek()?.value === 'not_last' ||\n this.parser.peek()?.value === 'only_one'\n ) {\n result.listMode = this.parser.consume().value as 'first' | 'not_first' | 'last' | 'not_last' | 'only_one';\n }\n\n if (this.parser.peek()?.value === 'as') {\n this.parser.consume('Symbol', 'as');\n result.variable = this.parser.consume().value;\n }\n\n if (this.parser.peek()?.value === 'log') {\n this.parser.consume('Symbol', 'log');\n result.logMessage = this.parser.consume().value;\n }\n\n if (this.parser.peek()?.value === 'where') {\n this.parser.consume('Symbol', 'where');\n const whereFhirPath = this.parser.consumeAndParse(OperatorPrecedence.Arrow);\n result.condition = whereFhirPath.toString();\n }\n\n if (this.parser.peek()?.value === 'check') {\n this.parser.consume('Symbol', 'check');\n const checkFhirPath = this.parser.consumeAndParse(OperatorPrecedence.Arrow);\n result.check = checkFhirPath.toString();\n }\n\n return result as StructureMapGroupRuleSource;\n }\n\n private parseRuleTargets(): StructureMapGroupRuleTarget[] {\n const targets = [this.parseRuleTarget()];\n while (this.parser.hasMore() && this.parser.peek()?.value === ',') {\n this.parser.consume(',');\n targets.push(this.parseRuleTarget());\n }\n return targets;\n }\n\n private parseRuleTarget(): StructureMapGroupRuleTarget {\n const result: StructureMapGroupRuleTarget = {};\n\n const context = this.parseRuleContext();\n const parts = context.split('.');\n result.contextType = 'variable';\n result.context = parts[0];\n result.element = parts[1];\n\n if (this.parser.peek()?.value === '=') {\n this.parser.consume('=');\n this.parseRuleTargetTransform(result);\n }\n\n if (this.parser.peek()?.value === 'as') {\n this.parser.consume('Symbol', 'as');\n result.variable = this.parser.consume().value;\n }\n\n if (this.parser.peek()?.value === 'share') {\n this.parser.consume('Symbol', 'share');\n result.listMode = ['share'];\n this.parser.consume('Symbol'); // NB: Not in the spec, but used by FHIRCH maps\n }\n\n if (\n this.parser.peek()?.value === 'first' ||\n this.parser.peek()?.value === 'last' ||\n this.parser.peek()?.value === 'collate'\n ) {\n result.listMode = [this.parser.consume().value as 'first' | 'last' | 'collate'];\n }\n\n return result;\n }\n\n private parseRuleTargetTransform(result: StructureMapGroupRuleTarget): void {\n const transformAtom = this.parser.consumeAndParse(OperatorPrecedence.As);\n if (transformAtom instanceof FunctionAtom) {\n result.transform = transformAtom.name as 'append' | 'truncate';\n result.parameter = transformAtom.args?.map(atomToParameter);\n } else if (transformAtom instanceof LiteralAtom || transformAtom instanceof SymbolAtom) {\n result.transform = 'copy';\n result.parameter = [atomToParameter(transformAtom)];\n } else {\n result.transform = 'evaluate';\n result.parameter = [{ valueString: transformAtom.toString() }];\n }\n }\n\n private parseRuleContext(): string {\n let identifier = this.parser.consume().value;\n while (this.parser.peek()?.value === '.') {\n this.parser.consume('.');\n identifier += '.' + this.parser.consume().value;\n }\n return identifier;\n }\n\n private parseRuleDependents(): StructureMapGroupRuleDependent[] | undefined {\n const atom = this.parser.consumeAndParse(OperatorPrecedence.Arrow) as FunctionAtom;\n return [\n {\n name: atom.name,\n variable: atom.args.map((arg) => (arg as SymbolAtom).name),\n },\n ];\n }\n\n private parseConceptMap(): void {\n this.parser.consume('Symbol', 'conceptmap');\n\n const conceptMap: ConceptMap = {\n resourceType: 'ConceptMap',\n status: 'active',\n url: '#' + this.parser.consume('String').value,\n };\n\n this.parser.consume('{');\n\n const prefixes: Record<string, string> = {};\n\n let next = this.parser.peek()?.value;\n while (next !== '}') {\n if (next === 'prefix') {\n this.parseConceptMapPrefix(prefixes);\n } else {\n this.parseConceptMapRule(conceptMap, prefixes);\n }\n next = this.parser.peek()?.value;\n }\n this.parser.consume('}');\n\n if (!this.structureMap.contained) {\n this.structureMap.contained = [];\n }\n this.structureMap.contained.push(conceptMap as ConceptMap);\n }\n\n private parseConceptMapPrefix(prefixes: Record<string, string>): void {\n this.parser.consume('Symbol', 'prefix');\n const prefix = this.parser.consume().value;\n this.parser.consume('=');\n const uri = this.parser.consume().value;\n prefixes[prefix] = uri;\n }\n\n private parseConceptMapRule(conceptMap: Partial<ConceptMap>, prefixes: Record<string, string>): void {\n const sourcePrefix = this.parser.consume().value;\n const sourceSystem = prefixes[sourcePrefix];\n this.parser.consume(':');\n const sourceCode = this.parser.consume().value;\n const equivalence = CONCEPT_MAP_EQUIVALENCE[this.parser.consume().value] as 'relatedto';\n const targetPrefix = this.parser.consume().value;\n const targetSystem = prefixes[targetPrefix];\n this.parser.consume(':');\n const targetCode = this.parser.consume().value;\n\n let group = conceptMap?.group?.find((g) => g.source === sourceSystem && g.target === targetSystem);\n\n if (!group) {\n group = { source: sourceSystem, target: targetSystem, element: [] };\n if (!conceptMap.group) {\n conceptMap.group = [];\n }\n conceptMap.group.push(group);\n }\n\n if (!group.element) {\n group.element = [];\n }\n\n group.element.push({\n code: sourceCode,\n target: [{ code: targetCode, equivalence }],\n });\n }\n}\n\nfunction atomToParameter(atom: Atom): StructureMapGroupRuleTargetParameter {\n if (atom instanceof SymbolAtom) {\n return { valueId: atom.name };\n }\n if (atom instanceof LiteralAtom) {\n return literalToParameter(atom);\n }\n throw new Error(`Unknown parameter atom type: ${atom.constructor.name} (${atom.toString()})`);\n}\n\nfunction literalToParameter(literalAtom: LiteralAtom): StructureMapGroupRuleTargetParameter {\n switch (literalAtom.value.type) {\n case 'boolean':\n return { valueBoolean: literalAtom.value.value as boolean };\n case 'decimal':\n return { valueDecimal: literalAtom.value.value as number };\n case 'integer':\n return { valueInteger: literalAtom.value.value as number };\n case 'dateTime':\n case 'string':\n return { valueString: literalAtom.value.value as string };\n default:\n throw new Error('Unknown target literal type: ' + literalAtom.value.type);\n }\n}\n\nconst fhirPathParserBuilder = initFhirPathParserBuilder()\n .registerInfix('->', { precedence: OperatorPrecedence.Arrow })\n .registerInfix(';', { precedence: OperatorPrecedence.Semicolon });\n\n/**\n * Parses a FHIR Mapping Language document into an AST.\n * @param input - The FHIR Mapping Language document to parse.\n * @returns The AST representing the document.\n */\nexport function parseMappingLanguage(input: string): StructureMap {\n const parser = fhirPathParserBuilder.construct(tokenize(input));\n parser.removeComments();\n return new StructureMapParser(parser).parse();\n}\n", "import {\n ConceptMap,\n ExtractResource,\n ResourceType,\n StructureMap,\n StructureMapGroup,\n StructureMapGroupInput,\n StructureMapGroupRule,\n StructureMapGroupRuleDependent,\n StructureMapGroupRuleSource,\n StructureMapGroupRuleTarget,\n StructureMapGroupRuleTargetParameter,\n} from '@medplum/fhirtypes';\nimport { generateId } from '../crypto';\nimport { evalFhirPathTyped } from '../fhirpath/parse';\nimport { getTypedPropertyValue, toJsBoolean, toTypedValue } from '../fhirpath/utils';\nimport { TypedValue } from '../types';\nimport { InternalSchemaElement, tryGetDataType } from '../typeschema/types';\nimport { conceptMapTranslate } from './conceptmaptranslate';\n\n/**\n * The TransformMapCollection class is a collection of StructureMap and ConceptMap resources.\n * It is used to store and retrieve imported StructureMaps and ConceptMaps by URL.\n */\nexport class TransformMapCollection {\n constructor(readonly resources: (StructureMap | ConceptMap)[] = []) {}\n\n get<K extends ResourceType>(resourceType: K, url: string): ExtractResource<K>[] {\n const result = [];\n for (const r of this.resources) {\n if (r.resourceType === resourceType && r.url && this.matchesUrl(r.url as string, url)) {\n result.push(r);\n }\n }\n return result as ExtractResource<K>[];\n }\n\n private matchesUrl(url: string, pattern: string): boolean {\n if (pattern.includes('*')) {\n const parts = pattern.split('*');\n return url.startsWith(parts[0]) && url.endsWith(parts[1]);\n } else {\n return url === pattern;\n }\n }\n}\n\ninterface TransformContext {\n root: StructureMap;\n transformMaps?: TransformMapCollection;\n parent?: TransformContext;\n variables?: Record<string, TypedValue[] | TypedValue>;\n}\n\n/**\n * Transforms input values using a FHIR StructureMap.\n *\n * See: https://www.hl7.org/fhir/mapping-language.html\n *\n * @param structureMap - The StructureMap to transform.\n * @param input - The input values.\n * @param transformMaps - Optional collection of imported StructureMaps and ConceptMaps.\n * @returns The transformed values.\n */\nexport function structureMapTransform(\n structureMap: StructureMap,\n input: TypedValue[],\n transformMaps = new TransformMapCollection()\n): TypedValue[] {\n return evalStructureMap({ root: structureMap, transformMaps }, structureMap, input);\n}\n\n/**\n * Evaluates a FHIR StructureMap.\n *\n * @param ctx - The transform context.\n * @param structureMap - The FHIR StructureMap definition.\n * @param input - The input values.\n * @returns The transformed values.\n * @internal\n */\nfunction evalStructureMap(ctx: TransformContext, structureMap: StructureMap, input: TypedValue[]): TypedValue[] {\n evalImports(ctx, structureMap);\n registerGlobals(ctx, structureMap);\n return evalGroup(ctx, structureMap.group[0], input);\n}\n\n/**\n * Evaluates the imports in a FHIR StructureMap.\n * For each import statement, the loader function is called to load the imported StructureMap.\n * The imported StructureMap is then hoisted into the current context.\n * @param ctx - The transform context.\n * @param structureMap - The FHIR StructureMap definition.\n * @internal\n */\nfunction evalImports(ctx: TransformContext, structureMap: StructureMap): void {\n const transformMaps = getTransformMaps(ctx);\n if (transformMaps && structureMap.import) {\n for (const url of structureMap.import) {\n const importedMaps = transformMaps.get('StructureMap', url);\n for (const importedMap of importedMaps) {\n registerGlobals(ctx, importedMap);\n }\n }\n }\n}\n\n/**\n * Registers all globals in a FHIR StructureMap into the current context.\n * Adds all contained StructureMaps and ConceptMaps to the map collection.\n * Hoists the groups in a FHIR StructureMap into the current context.\n * This is necessary to allow groups to reference each other.\n *\n * @param ctx - The transform context.\n * @param structureMap - The FHIR StructureMap definition.\n * @internal\n */\nfunction registerGlobals(ctx: TransformContext, structureMap: StructureMap): void {\n const transformMaps = getTransformMaps(ctx);\n if (transformMaps && structureMap.contained) {\n for (const c of structureMap.contained) {\n if (c.resourceType === 'StructureMap' || c.resourceType === 'ConceptMap') {\n transformMaps.resources.push(c);\n }\n }\n }\n\n if (structureMap.group) {\n for (const group of structureMap.group) {\n setVariable(ctx, group.name as string, { type: 'StructureMapGroup', value: group });\n }\n }\n}\n\n/**\n * Evaluates a FHIR StructureMapGroup.\n *\n * A \"group\" is similar to a function in a programming language.\n *\n * @param ctx - The transform context.\n * @param group - The FHIR StructureMapGroup definition.\n * @param input - The input values.\n * @returns The transformed values.\n * @internal\n */\nfunction evalGroup(ctx: TransformContext, group: StructureMapGroup, input: TypedValue[]): TypedValue[] {\n const sourceDefinitions: StructureMapGroupInput[] = [];\n const targetDefinitions: StructureMapGroupInput[] = [];\n\n for (const inputDefinition of group.input as StructureMapGroupInput[]) {\n if (inputDefinition.mode === 'source') {\n sourceDefinitions.push(inputDefinition);\n }\n if (inputDefinition.mode === 'target') {\n targetDefinitions.push(inputDefinition);\n }\n }\n\n if (sourceDefinitions.length === 0) {\n throw new Error('Missing source definitions');\n }\n\n if (targetDefinitions.length === 0) {\n throw new Error('Missing target definitions');\n }\n\n if (input.length < sourceDefinitions.length) {\n throw new Error(`Not enough arguments (got ${input.length}, min ${sourceDefinitions.length})`);\n }\n\n if (input.length > sourceDefinitions.length + targetDefinitions.length) {\n throw new Error(\n `Too many arguments (got ${input.length}, max ${sourceDefinitions.length + targetDefinitions.length})`\n );\n }\n\n const variables: Record<string, TypedValue> = {};\n const outputs = [];\n let inputIndex = 0;\n\n for (const sourceDefinition of sourceDefinitions) {\n safeAssign(variables, sourceDefinition.name as string, input[inputIndex++]);\n }\n\n for (const targetDefinition of targetDefinitions) {\n const output = input[inputIndex++] ?? { type: targetDefinition.type ?? 'BackboneElement', value: {} };\n safeAssign(variables, targetDefinition.name as string, output);\n outputs.push(output);\n }\n\n const newContext: TransformContext = { root: ctx.root, parent: ctx, variables };\n\n if (group.rule) {\n for (const rule of group.rule) {\n evalRule(newContext, rule);\n }\n }\n\n return outputs;\n}\n\n/**\n * Entry point for evaluating a rule.\n * Rule sources are evaluated first, followed by the rule target, child rules, and dependent groups.\n * Rule sources are evaluated recursively to handle multiple source statements.\n *\n * @param ctx - The transform context.\n * @param rule - The FHIR Mapping rule definition.\n * @internal\n */\nfunction evalRule(ctx: TransformContext, rule: StructureMapGroupRule): void {\n // https://build.fhir.org/mapping-language.html#7.8.0.8.1\n // If there are multiple source statements, the rule applies for the permutation of the source elements from each source statement.\n // E.g. if there are 2 source statements, each with 2 matching elements, the rule applies 4 times, one for each combination.\n // Typically, if there is more than one source statement, only one of the elements would repeat.\n // If any of the source data elements have no value, then the rule never applies;\n // only existing permutations are executed: for multiple source statements, all of them need to match.\n if (rule.source) {\n evalRuleSourceAt(ctx, rule, 0);\n }\n}\n\n/**\n * Recursively evaluates a rule at a specific source index.\n *\n * @param ctx - The transform context.\n * @param rule - The FHIR Mapping rule definition.\n * @param index - The source index to evaluate.\n * @internal\n */\nfunction evalRuleSourceAt(\n ctx: TransformContext,\n rule: StructureMapGroupRule & { source: StructureMapGroupRuleSource[] },\n index: number\n): void {\n const source = rule.source[index];\n for (const sourceValue of evalSource(ctx, source)) {\n setVariable(ctx, '_', sourceValue);\n\n if (source.variable) {\n setVariable(ctx, source.variable, sourceValue);\n }\n\n if (index < rule.source.length - 1) {\n // If there are more sources, evaluate the next source\n evalRuleSourceAt(ctx, rule, index + 1);\n } else {\n // Otherwise, evaluate the rule after the sources\n evalRuleAfterSources(ctx, rule);\n }\n }\n}\n\n/**\n * Evaluates a rule after the sources have been evaluated.\n *\n * This includes the rule targets, child rules, and dependent groups.\n *\n * @param ctx - The transform context.\n * @param rule - The FHIR Mapping rule definition.\n * @internal\n */\nfunction evalRuleAfterSources(ctx: TransformContext, rule: StructureMapGroupRule): void {\n if (tryEvalShorthandRule(ctx, rule)) {\n return;\n }\n if (rule.target) {\n for (const target of rule.target) {\n evalTarget(ctx, target);\n }\n }\n if (rule.rule) {\n for (const childRule of rule.rule) {\n evalRule(ctx, childRule);\n }\n }\n if (rule.dependent) {\n for (const dependent of rule.dependent) {\n evalDependent(ctx, dependent);\n }\n }\n}\n\n/**\n * Tries to evaluate a shorthand rule.\n * @param ctx - The transform context.\n * @param rule - The FHIR Mapping rule definition.\n * @returns True if the rule is a shorthand rule, false otherwise.\n */\nfunction tryEvalShorthandRule(ctx: TransformContext, rule: StructureMapGroupRule): boolean {\n // First, check if this is actually a shorthand rule\n // Shorthand rule has exactly one target, no transform, no rule, and no dependent\n if (!rule.target || rule.target.length !== 1 || rule.target[0].transform || rule.rule || rule.dependent) {\n return false;\n }\n\n // Determine the source value\n let sourceValue = getVariable(ctx, '_');\n if (Array.isArray(sourceValue)) {\n sourceValue = sourceValue[0];\n }\n if (!sourceValue) {\n return false;\n }\n\n // Ok, this is a shorthand rule.\n // Next, try to find a \"types\" group that matches the input and output types\n const group = tryFindTypesGroup(ctx, sourceValue);\n if (!group) {\n // No group found, fallback to simple copy transform\n // This is commonly used for primitive types such as \"string\" and \"code\"\n evalTarget(ctx, { ...rule.target[0], transform: 'copy', parameter: [{ valueId: '_' }] });\n return true;\n }\n\n const target = rule.target[0];\n const targetContext = getVariable(ctx, target.context as string) as TypedValue;\n const originalValue = targetContext.value[target.element as string];\n const isArray = isArrayProperty(targetContext, target.element as string) || Array.isArray(originalValue);\n const newContext: TransformContext = { root: ctx.root, parent: ctx, variables: {} };\n const targetValue = evalGroup(newContext, group, [sourceValue]);\n setTargetValue(ctx, target, targetContext, targetValue, isArray, originalValue);\n return true;\n}\n\n/**\n * Tries to find a \"types\" group that matches the input and output types.\n * This is used to determine the transform for a shorthand rule.\n * @param ctx - The transform context.\n * @param sourceValue - The source value.\n * @returns The matching group, if found; otherwise, undefined.\n */\nfunction tryFindTypesGroup(ctx: TransformContext, sourceValue: TypedValue): StructureMapGroup | undefined {\n let currentContext: TransformContext | undefined = ctx;\n while (currentContext) {\n if (currentContext.variables) {\n for (const value of Object.values(currentContext.variables)) {\n const array = arrayify(value);\n for (const entry of array) {\n if (entry.type === 'StructureMapGroup') {\n const group = entry.value as StructureMapGroup;\n if (\n (group.typeMode === 'types' || group.typeMode === 'type-and-types') &&\n group.input.length === 2 &&\n group.input[0].mode === 'source' &&\n group.input[0].type === sourceValue.type &&\n group.input[1].mode === 'target'\n ) {\n return group;\n }\n }\n }\n }\n }\n currentContext = currentContext.parent;\n }\n\n return undefined;\n}\n\n/**\n * Evaluates a FHIR Mapping source definition.\n *\n * If the source has a condition, the condition is evaluated.\n * If the source has a check, the check is evaluated.\n *\n * @param ctx - The transform context.\n * @param source - The FHIR Mapping source definition.\n * @returns The evaluated source values.\n * @internal\n */\nfunction evalSource(ctx: TransformContext, source: StructureMapGroupRuleSource): TypedValue[] {\n const sourceContext = getVariable(ctx, source.context as string) as TypedValue | undefined;\n if (!sourceContext) {\n return [];\n }\n\n const sourceElement = source.element;\n if (!sourceElement) {\n return [sourceContext];\n }\n\n let sourceValue = evalFhirPathTyped(sourceElement, [sourceContext]);\n if (!sourceValue || sourceValue.length === 0) {\n return [];\n }\n\n if (source.condition) {\n if (!evalCondition(sourceContext, { [source.variable as string]: sourceValue[0] }, source.condition)) {\n return [];\n }\n }\n\n if (source.check) {\n if (!evalCondition(sourceContext, { [source.variable as string]: sourceValue[0] }, source.check)) {\n throw new Error('Check failed: ' + source.check);\n }\n }\n\n if (source.listMode) {\n sourceValue = evalListMode(source, sourceValue);\n }\n\n return sourceValue;\n}\n\n/**\n * Evaluates a FHIRPath condition for a FHIR Mapping source.\n *\n * This is used for both the \"condition\" and \"check\" properties.\n *\n * @param input - The input value, typically the rule source.\n * @param variables - The variables in scope for the FHIRPath expression.\n * @param condition - The FHIRPath condition to evaluate.\n * @returns True if the condition is true, false otherwise.\n * @internal\n */\nfunction evalCondition(input: TypedValue, variables: Record<string, TypedValue>, condition: string): boolean {\n return toJsBoolean(evalFhirPathTyped(condition, [input], variables));\n}\n\n/**\n * Evaluates the list mode for a FHIR Mapping source.\n *\n * @param source - The FHIR Mapping source definition.\n * @param sourceValue - The source values.\n * @returns The evaluated source values.\n * @internal\n */\nfunction evalListMode(source: StructureMapGroupRuleSource, sourceValue: TypedValue[]): TypedValue[] {\n // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check\n switch (source.listMode) {\n case 'first':\n return [sourceValue[0]];\n case 'not_first':\n return sourceValue.slice(1);\n case 'last':\n return [sourceValue[sourceValue.length - 1]];\n case 'not_last':\n return sourceValue.slice(0, sourceValue.length - 1);\n case 'only_one':\n if (sourceValue.length !== 1) {\n throw new Error('Expected only one value');\n }\n break;\n }\n return sourceValue;\n}\n\n/**\n * Evaluates a FHIR Mapping target definition.\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @internal\n */\nfunction evalTarget(ctx: TransformContext, target: StructureMapGroupRuleTarget): void {\n const targetContext = getVariable(ctx, target.context as string) as TypedValue | undefined;\n if (!targetContext) {\n throw new Error('Target not found: ' + target.context);\n }\n\n const originalValue = targetContext.value[target.element as string];\n let targetValue: TypedValue[];\n\n // Determine if the target property is an array field or not\n // If the target property is an array, then we need to append to the array\n const isArray = isArrayProperty(targetContext, target.element as string) || Array.isArray(originalValue);\n\n if (!target.transform) {\n const elementTypes = tryGetPropertySchema(targetContext, target.element as string)?.type;\n const elementType = elementTypes?.length === 1 ? elementTypes[0].code : undefined;\n if (isArray || originalValue === undefined) {\n targetValue = [elementType ? { type: elementType, value: {} } : toTypedValue({})];\n } else {\n targetValue = [elementType ? { type: elementType, value: originalValue } : toTypedValue(originalValue)];\n }\n } else {\n switch (target.transform) {\n case 'append':\n targetValue = evalAppend(ctx, target);\n break;\n case 'cast':\n targetValue = evalCast(ctx, target);\n break;\n case 'cc':\n targetValue = evalCc(ctx, target);\n break;\n case 'copy':\n targetValue = evalCopy(ctx, target);\n break;\n case 'create':\n targetValue = evalCreate(ctx, target);\n break;\n case 'evaluate':\n targetValue = evalEvaluate(ctx, target);\n break;\n case 'translate':\n targetValue = evalTranslate(ctx, target);\n break;\n case 'truncate':\n targetValue = evalTruncate(ctx, target);\n break;\n case 'uuid':\n targetValue = [{ type: 'string', value: generateId() }];\n break;\n default:\n throw new Error(`Unsupported transform: ${target.transform}`);\n }\n }\n\n setTargetValue(ctx, target, targetContext, targetValue, isArray, originalValue);\n}\n\n/**\n * Sets a target value.\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @param targetContext - The target context.\n * @param targetValue - The target value.\n * @param isArray - True if the target property is an array field.\n * @param originalValue - The original value of the target property.\n * @internal\n */\nfunction setTargetValue(\n ctx: TransformContext,\n target: StructureMapGroupRuleTarget,\n targetContext: TypedValue,\n targetValue: TypedValue[],\n isArray: boolean,\n originalValue: any\n): void {\n if (targetValue.length === 0) {\n return;\n }\n\n if (isArray) {\n if (!originalValue) {\n originalValue = [];\n safeAssign(targetContext.value, target.element as string, originalValue);\n }\n for (const el of targetValue) {\n originalValue.push(el.value);\n }\n } else {\n safeAssign(targetContext.value, target.element as string, targetValue[0].value);\n }\n\n if (target.variable) {\n setVariable(ctx, target.variable, unarrayify(targetValue));\n }\n}\n\n/**\n * Returns true if the target property is an array field.\n *\n * @param targetContext - The target context.\n * @param element - The element to check (i.e., the property name).\n * @returns True if the target property is an array field.\n * @internal\n */\nfunction isArrayProperty(targetContext: TypedValue, element: string): boolean | undefined {\n return tryGetPropertySchema(targetContext, element)?.isArray;\n}\n\n/**\n * Returns the type schema\n * @param targetContext - The target context.\n * @param element - The element to check (i.e., the property name).\n * @returns the type schema for the target element, if it is loeaded\n * @internal\n */\nfunction tryGetPropertySchema(targetContext: TypedValue, element: string): InternalSchemaElement | undefined {\n return tryGetDataType(targetContext.type)?.elements?.[element];\n}\n\n/**\n * Evaluates the \"append\" transform.\n *\n * \"Source is element or string - just append them all together\"\n *\n * See: https://build.fhir.org/mapping-language.html#7.8.0.8.2\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @returns The evaluated target values.\n * @internal\n */\nfunction evalAppend(ctx: TransformContext, target: StructureMapGroupRuleTarget): TypedValue[] {\n const arg1 = resolveParameter(ctx, target.parameter?.[0])?.[0]?.value;\n const arg2 = resolveParameter(ctx, target.parameter?.[1])?.[0]?.value;\n return [{ type: 'string', value: (arg1 ?? '').toString() + (arg2 ?? '').toString() }];\n}\n\n/**\n * Evaluates the \"cast\" transform.\n *\n * \"Cast source from one type to another. target type can be left as implicit if there is one and only one target type known.\"\n *\n * See: https://build.fhir.org/mapping-language.html#7.8.0.8.2\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @returns The evaluated target values.\n * @internal\n */\nfunction evalCast(ctx: TransformContext, target: StructureMapGroupRuleTarget): TypedValue[] {\n const arg1 = resolveParameter(ctx, target.parameter?.[0])?.[0];\n const arg2 = resolveParameter(ctx, target.parameter?.[1])?.[0]?.value;\n if (arg2 === 'string') {\n return [{ type: 'string', value: arg1?.value?.toString() }];\n }\n return [arg1];\n}\n\n/**\n * Evaluates the \"cc\" transform.\n *\n * \"Create a CodeableConcept from the parameters provided.\"\n *\n * If there are two parameters, the first is the system and the second is the code.\n *\n * If there is only one parameter, it is the text.\n *\n * See: https://build.fhir.org/mapping-language.html#7.8.0.8.2\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @returns The evaluated target values.\n * @internal\n */\nfunction evalCc(ctx: TransformContext, target: StructureMapGroupRuleTarget): TypedValue[] {\n const params = target.parameter as StructureMapGroupRuleTargetParameter[];\n if (params.length === 2) {\n // system and code\n const system = resolveParameter(ctx, params[0])?.[0]?.value;\n const code = resolveParameter(ctx, params[1])?.[0]?.value;\n return [{ type: 'CodeableConcept', value: { coding: [{ system, code }] } }];\n } else {\n // text\n const text = resolveParameter(ctx, params[0])?.[0]?.value;\n return [{ type: 'CodeableConcept', value: { text } }];\n }\n}\n\n/**\n * Evaluates the \"copy\" transform.\n *\n * \"Simply copy the source to the target as is (only allowed when the types in source and target match- typically for primitive types).\n * In the concrete syntax, this is simply represented as the source variable, e.g. src.a = tgt.b\"\n *\n * See: https://build.fhir.org/mapping-language.html#7.8.0.8.2\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @returns The evaluated target values.\n * @internal\n */\nfunction evalCopy(ctx: TransformContext, target: StructureMapGroupRuleTarget): TypedValue[] {\n return (target.parameter as StructureMapGroupRuleTargetParameter[]).flatMap((p) => resolveParameter(ctx, p));\n}\n\n/**\n * Evaluates the \"create\" transform.\n *\n * \"Use the standard API to create a new instance of data.\n * Where structure definitions have been provided, the type parameter must be a string which is a known type of a root element.\n * Where they haven't, the application must know the name somehow.\"\"\n *\n * See: https://build.fhir.org/mapping-language.html#7.8.0.8.2\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @returns The evaluated target values.\n * @internal\n */\nfunction evalCreate(ctx: TransformContext, target: StructureMapGroupRuleTarget): TypedValue[] {\n const result: Record<string, unknown> = {};\n if (target.parameter && target.parameter.length > 0) {\n result.resourceType = resolveParameter(ctx, target.parameter?.[0])?.[0]?.value;\n }\n return [toTypedValue(result)];\n}\n\n/**\n * Evaluates the \"evaluate\" transform.\n *\n * \"Execute the supplied FHIRPath expression and use the value returned by that.\"\n *\n * See: https://build.fhir.org/mapping-language.html#7.8.0.8.2\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @returns The evaluated target values.\n * @internal\n */\nfunction evalEvaluate(ctx: TransformContext, target: StructureMapGroupRuleTarget): TypedValue[] {\n const typedExpr = resolveParameter(ctx, target.parameter?.[0]);\n const expr = typedExpr[0].value as string;\n return evalFhirPathTyped(expr, [], buildFhirPathVariables(ctx) as Record<string, TypedValue>);\n}\n\n/**\n * Evaluates the \"translate\" transform.\n *\n * \"Use the translate operation. The source is some type of code or coded datatype,\n * and the source and map_uri are passed to the translate operation.\n * The output determines what value from the translate operation is used for the result of the operation\n * (code, system, display, Coding, or CodeableConcept)\"\n *\n * See: https://build.fhir.org/mapping-language.html#7.8.0.8.2\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @returns The evaluated target values.\n * @internal\n */\nfunction evalTranslate(ctx: TransformContext, target: StructureMapGroupRuleTarget): TypedValue[] {\n const args = (target.parameter as StructureMapGroupRuleTargetParameter[]).flatMap((p) => resolveParameter(ctx, p));\n const sourceValue = args[0].value;\n const mapUri = args[1].value;\n const transformMaps = getTransformMaps(ctx);\n const conceptMap = transformMaps?.get('ConceptMap', mapUri)[0];\n if (!conceptMap) {\n throw new Error('ConceptMap not found: ' + mapUri);\n }\n // TODO: Verify whether system is actually required\n // The FHIR Mapping Language spec does not say whether it is required\n // But our current implementation requires it\n const result = conceptMapTranslate(conceptMap, { system: conceptMap.group?.[0]?.source, code: sourceValue });\n return [toTypedValue(result.match?.[0]?.concept?.code)];\n}\n\n/**\n * Evaluates the \"truncate\" transform.\n *\n * \"Source must be some stringy type that has some meaningful length property\"\n *\n * See: https://build.fhir.org/mapping-language.html#7.8.0.8.2\n *\n * @param ctx - The transform context.\n * @param target - The FHIR Mapping target definition.\n * @returns The evaluated target values.\n * @internal\n */\nfunction evalTruncate(ctx: TransformContext, target: StructureMapGroupRuleTarget): TypedValue[] {\n const targetValue = resolveParameter(ctx, target.parameter?.[0])?.[0];\n const targetLength = resolveParameter(ctx, target.parameter?.[1])?.[0]?.value as number;\n if (targetValue.type === 'string') {\n return [{ type: 'string', value: targetValue.value.substring(0, targetLength) }];\n }\n return [targetValue];\n}\n\n/**\n * Evaluates a rule dependent group.\n *\n * See: https://hl7.org/fhir/r4/structuremap-definitions.html#StructureMap.group.rule.dependent\n *\n * @param ctx - The transform context.\n * @param dependent - The FHIR Mapping dependent definition.\n * @internal\n */\nfunction evalDependent(ctx: TransformContext, dependent: StructureMapGroupRuleDependent): void {\n const dependentGroup = getVariable(ctx, dependent.name as string) as TypedValue | undefined;\n if (!dependentGroup) {\n throw new Error('Dependent group not found: ' + dependent.name);\n }\n\n const variables = dependent.variable as string[];\n const args: TypedValue[] = [];\n for (const variable of variables) {\n const variableValue = getVariable(ctx, variable) as TypedValue | undefined;\n if (!variableValue) {\n throw new Error('Dependent variable not found: ' + variable);\n }\n args.push(variableValue);\n }\n\n const newContext: TransformContext = { root: ctx.root, parent: ctx, variables: {} };\n evalGroup(newContext, dependentGroup.value as StructureMapGroup, args);\n}\n\nfunction getTransformMaps(ctx: TransformContext): TransformMapCollection | undefined {\n if (ctx.transformMaps) {\n return ctx.transformMaps;\n }\n if (ctx.parent) {\n return getTransformMaps(ctx.parent);\n }\n return undefined;\n}\n\n/**\n * Resolves the value of a FHIR Mapping target parameter.\n *\n * For literal values, the value is returned as-is.\n *\n * For variables, the value is looked up in the current context.\n *\n * @param ctx - The transform context.\n * @param parameter - The FHIR Mapping target parameter definition.\n * @returns The resolved parameter values.\n * @internal\n */\nfunction resolveParameter(\n ctx: TransformContext,\n parameter: StructureMapGroupRuleTargetParameter | undefined\n): TypedValue[] {\n const typedParameter = { type: 'StructureMapGroupRuleTargetParameter', value: parameter };\n let paramValue = getTypedPropertyValue(typedParameter, 'value');\n if (!paramValue) {\n throw new Error('Missing target parameter: ' + JSON.stringify(parameter));\n }\n\n paramValue = arrayify(paramValue);\n\n if (paramValue.length === 1 && paramValue[0].type === 'id') {\n const variableValue = getVariable(ctx, paramValue[0].value as string);\n if (!variableValue) {\n throw new Error('Variable not found: ' + paramValue[0].value);\n }\n return arrayify(variableValue);\n }\n\n return paramValue;\n}\n\n/**\n * Returns a variable value by name.\n *\n * Recursively searches the parent context if the variable is not found in the current context.\n *\n * @param ctx - The transform context.\n * @param name - The variable name.\n * @returns The variable value.\n * @internal\n */\nfunction getVariable(ctx: TransformContext, name: string): TypedValue[] | TypedValue | undefined {\n const value = ctx.variables?.[name];\n if (value) {\n return value;\n }\n if (ctx.parent) {\n return getVariable(ctx.parent, name);\n }\n return undefined;\n}\n\n/**\n * Builds a collection of FHIRPath variables from the current context.\n *\n * Recursively searches the parent context to build the complete set of variables.\n *\n * @param ctx - The transform context.\n * @param result - The builder output.\n * @returns The result with the FHIRPath variables.\n * @internal\n */\nfunction buildFhirPathVariables(\n ctx: TransformContext,\n result: Record<string, TypedValue[] | TypedValue> = {}\n): Record<string, TypedValue[] | TypedValue> {\n if (ctx.parent) {\n buildFhirPathVariables(ctx.parent, result);\n }\n if (ctx.variables) {\n for (const [key, value] of Object.entries(ctx.variables)) {\n result[key] = value;\n result['%' + key] = value;\n }\n }\n return result;\n}\n\n/**\n * Sets a variable value in the current context.\n *\n * @param ctx - The transform context.\n * @param name - The variable name.\n * @param value - The variable value.\n * @internal\n */\nfunction setVariable(ctx: TransformContext, name: string, value: TypedValue[] | TypedValue): void {\n if (!ctx.variables) {\n ctx.variables = {};\n }\n safeAssign(ctx.variables, name, value);\n}\n\nfunction safeAssign(target: Record<string, unknown>, key: string, value: unknown): void {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n throw new Error('Invalid key: ' + key);\n }\n target[key] = value;\n}\n\nfunction arrayify<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value];\n}\n\nfunction unarrayify<T>(value: T[]): T | T[] {\n return value.length === 1 ? value[0] : value;\n}\n", "const DEFAULT_INDENT = ' '.repeat(2);\n\nexport class FileBuilder {\n private readonly indent: string;\n private readonly b: string[];\n indentCount: number;\n\n constructor(indent = DEFAULT_INDENT, header = true) {\n this.indent = indent;\n this.b = [];\n this.indentCount = 0;\n\n if (header) {\n this.appendNoWrap('/*');\n this.appendNoWrap(' * This is a generated file');\n this.appendNoWrap(' * Do not edit manually.');\n this.appendNoWrap(' */');\n this.newLine();\n }\n }\n\n newLine(): void {\n this.b.push('\\n');\n }\n\n appendNoWrap(line: string): void {\n this.b.push(this.indent.repeat(this.indentCount));\n this.b.push(line);\n this.b.push('\\n');\n }\n\n append(line: string): void {\n const nowrap = this.indent.repeat(this.indentCount) + line;\n if (nowrap.length < 160) {\n this.b.push(nowrap);\n this.b.push('\\n');\n } else {\n let first = true;\n for (const wrappedLine of wordWrap(nowrap, 120 - this.indent.length * this.indentCount)) {\n if (first) {\n this.b.push(this.indent.repeat(this.indentCount));\n } else {\n this.b.push(this.indent.repeat(this.indentCount + 2));\n }\n this.b.push(wrappedLine.trim());\n this.b.push('\\n');\n first = false;\n }\n }\n }\n\n toString(): string {\n return this.b.join('').replaceAll('\\n\\n\\n', '\\n\\n');\n }\n}\n\n/**\n * Returns a word-wrapped string.\n * Based on: https://stackoverflow.com/a/38709683\n * @param text - Original input string.\n * @param maxLength - Width in number of characters.\n * @returns Array of lines.\n */\nexport function wordWrap(text: string, maxLength: number): string[] {\n const result = [];\n let line: string[] = [];\n let length = 0;\n text.split(' ').forEach(function (word) {\n if (length + word.length > maxLength) {\n result.push(line.join(' ').trim());\n line = [];\n length = 0;\n }\n length += word.length + 1;\n line.push(word);\n });\n if (line.length > 0) {\n result.push(line.join(' ').trim());\n }\n return result;\n}\n", "import { Token, Tokenizer } from '../fhirlexer/tokenize';\nimport { FHIRPATH_KEYWORDS, FHIRPATH_OPERATORS } from '../fhirpath/tokenize';\n\nconst MAPPING_LANGUAGE_OPERATORS = [...FHIRPATH_OPERATORS, 'eq', 'ne', 'co'];\n\nexport function tokenize(str: string): Token[] {\n return new Tokenizer(str, FHIRPATH_KEYWORDS, MAPPING_LANGUAGE_OPERATORS, {\n dateTimeLiterals: true,\n symbolRegex: /[^\\s\\])]/,\n }).tokenize();\n}\n", "// See: https://hl7.org/fhir/search_filter.html\n\nimport { Operator } from '../search/search';\n\n/**\n * The FhirFilterExpression type is the base type of all filter expressions.\n */\nexport type FhirFilterExpression = FhirFilterComparison | FhirFilterNegation | FhirFilterConnective;\n\n/**\n * The FhirFilterComparison class represents a comparison expression.\n */\nexport class FhirFilterComparison {\n constructor(\n readonly path: string,\n readonly operator: Operator,\n readonly value: string\n ) {}\n}\n\n/**\n * The FhirFilterNegation class represents a negation expression.\n * It contains a single child expression.\n */\nexport class FhirFilterNegation {\n constructor(readonly child: FhirFilterExpression) {}\n}\n\n/**\n * The FhirFilterConnective class represents a connective expression.\n * It contains a list of child expressions.\n */\nexport class FhirFilterConnective {\n constructor(\n readonly keyword: 'and' | 'or',\n readonly left: FhirFilterExpression,\n readonly right: FhirFilterExpression\n ) {}\n}\n", "import { Parser } from '../fhirlexer/parse';\nimport { initFhirPathParserBuilder } from '../fhirpath/parse';\nimport { OperationOutcomeError, badRequest } from '../outcomes';\nimport { Operator } from '../search/search';\nimport { tokenize } from './tokenize';\nimport { FhirFilterComparison, FhirFilterConnective, FhirFilterExpression, FhirFilterNegation } from './types';\n\n/**\n * The operatorMap maps FHIR _filter operators to Medplum search operators.\n * See _filter operators: https://www.hl7.org/fhir/search_filter.html#ops\n */\nconst operatorMap: Record<string, Operator | undefined> = {\n // eq - an item in the set has an equal value\n eq: Operator.EQUALS,\n // ne - An item in the set has an unequal value\n ne: Operator.NOT_EQUALS,\n // co - An item in the set contains this value\n co: Operator.CONTAINS,\n // sw - An item in the set starts with this value\n sw: Operator.STARTS_WITH,\n // ew - An item in the set ends with this value\n ew: undefined,\n // gt / lt / ge / le - A value in the set is (greater than, less than, greater or equal, less or equal) the given value\n gt: Operator.GREATER_THAN,\n lt: Operator.LESS_THAN,\n ge: Operator.GREATER_THAN_OR_EQUALS,\n le: Operator.LESS_THAN_OR_EQUALS,\n // ap - A value in the set is approximately the same as this value.\n // Note that the recommended value for the approximation is 10% of the stated value (or for a date, 10% of the gap between now and the date), but systems may choose other values where appropriate\n ap: Operator.APPROXIMATELY,\n // sa - The value starts after the specified value\n sa: Operator.STARTS_AFTER,\n // eb - The value ends before the specified value\n eb: Operator.ENDS_BEFORE,\n // pr - The set is empty or not (value is false or true)\n pr: Operator.PRESENT,\n // po - True if a (implied) date period in the set overlaps with the implied period in the value\n po: undefined,\n // ss - True if the value subsumes a concept in the set\n ss: undefined,\n // sb - True if the value is subsumed by a concept in the set\n sb: undefined,\n // in - True if one of the concepts is in the nominated value set by URI, either a relative, literal or logical vs\n in: Operator.IN,\n // ni - True if none of the concepts are in the nominated value set by URI, either a relative, literal or logical vs\n ni: Operator.NOT_IN,\n // re - True if one of the references in set points to the given URL\n re: Operator.EQUALS,\n // identifier - True if the identifier is in the identifier set (Medplum extension)\n identifier: Operator.IDENTIFIER,\n};\n\nfunction getOperator(value: string): Operator {\n const operator = operatorMap[value];\n if (!operator) {\n throw new OperationOutcomeError(badRequest('Invalid operator: ' + value));\n }\n return operator;\n}\n\nclass FilterParameterParser {\n constructor(readonly parser: Parser) {}\n\n parse(): FhirFilterExpression {\n let result: FhirFilterExpression;\n\n if (this.parser.peek()?.value === '(') {\n this.parser.consume('(');\n result = this.parse();\n this.parser.consume(')');\n } else if (this.parser.peek()?.value === 'not') {\n this.parser.consume('Symbol', 'not');\n this.parser.consume('(');\n result = new FhirFilterNegation(this.parse());\n this.parser.consume(')');\n } else {\n result = new FhirFilterComparison(\n this.parser.consume('Symbol').value,\n getOperator(this.parser.consume('Symbol').value),\n this.parser.consume().value\n );\n }\n\n const next = this.parser.peek()?.value;\n if (next === 'and' || next === 'or') {\n this.parser.consume('Symbol', next);\n return new FhirFilterConnective(next, result, this.parse());\n }\n\n return result;\n }\n}\n\nconst fhirPathParserBuilder = initFhirPathParserBuilder();\n\n/**\n * Parses a FHIR _filter parameter expression into an AST.\n * @param input - The FHIR _filter parameter expression.\n * @returns The AST representing the filters.\n */\nexport function parseFilterParameter(input: string): FhirFilterExpression {\n const parser = fhirPathParserBuilder.construct(tokenize(input));\n parser.removeComments();\n return new FilterParameterParser(parser).parse();\n}\n", "import { isStringArray } from './utils';\n\n/**\n * The Hl7Context class represents the parsing context for an HL7 message.\n *\n * @see MSH-1: https://hl7-definition.caristix.com/v2/HL7v2.6/Fields/MSH.1\n * @see MSH-2: https://hl7-definition.caristix.com/v2/HL7v2.6/Fields/MSH.2\n * @see See this tutorial on MSH, and why it's a bad idea to use anything other than the default values: https://www.hl7soup.com/HL7TutorialMSH.html\n */\nexport class Hl7Context {\n constructor(\n public readonly segmentSeparator = '\\r',\n public readonly fieldSeparator = '|',\n public readonly componentSeparator = '^',\n public readonly repetitionSeparator = '~',\n public readonly escapeCharacter = '\\\\',\n public readonly subcomponentSeparator = '&'\n ) {}\n\n /**\n * Returns the MSH-1 field value based on the configured separators.\n * @returns The HL7 MSH-1 field value.\n */\n getMsh1(): string {\n return this.fieldSeparator;\n }\n\n /**\n * Returns the MSH-2 field value based on the configured separators.\n * @returns The HL7 MSH-2 field value.\n */\n getMsh2(): string {\n return this.componentSeparator + this.repetitionSeparator + this.escapeCharacter + this.subcomponentSeparator;\n }\n}\n\n/**\n * The Hl7Message class represents one HL7 message.\n * A message is a collection of segments.\n */\nexport class Hl7Message {\n readonly context: Hl7Context;\n readonly segments: Hl7Segment[];\n\n /**\n * Creates a new HL7 message.\n * @param segments - The HL7 segments.\n * @param context - Optional HL7 parsing context.\n */\n constructor(segments: Hl7Segment[], context = new Hl7Context()) {\n this.context = context;\n this.segments = segments;\n }\n\n /**\n * Returns the HL7 message header.\n * @returns The HL7 message header.\n */\n get header(): Hl7Segment {\n return this.segments[0];\n }\n\n /**\n * Returns an HL7 segment by index or by name.\n * @param index - The HL7 segment index or name.\n * @returns The HL7 segment if found; otherwise, undefined.\n * @deprecated Use getSegment() instead. This method will be removed in a future release.\n */\n get(index: number | string): Hl7Segment | undefined {\n return this.getSegment(index);\n }\n\n /**\n * Returns all HL7 segments of a given name.\n * @param name - The HL7 segment name.\n * @returns An array of HL7 segments with the specified name.\n * @deprecated Use getAllSegments() instead. This method will be removed in a future release.\n */\n getAll(name: string): Hl7Segment[] {\n return this.getAllSegments(name);\n }\n\n /**\n * Returns an HL7 segment by index or by name.\n *\n * When using a numeric index, the first segment (usually the MSH header segment) is at index 0.\n *\n * When using a string index, this method returns the first segment with the specified name.\n *\n * @param index - The HL7 segment index or name.\n * @returns The HL7 segment if found; otherwise, undefined.\n */\n getSegment(index: number | string): Hl7Segment | undefined {\n if (typeof index === 'number') {\n return this.segments[index];\n }\n return this.segments.find((s) => s.name === index);\n }\n\n /**\n * Returns all HL7 segments of a given name.\n * @param name - The HL7 segment name.\n * @returns An array of HL7 segments with the specified name.\n */\n getAllSegments(name: string): Hl7Segment[] {\n return this.segments.filter((s) => s.name === name);\n }\n\n /**\n * Returns the HL7 message as a string.\n * @returns The HL7 message as a string.\n */\n toString(): string {\n return this.segments.map((s) => s.toString()).join(this.context.segmentSeparator);\n }\n\n /**\n * Returns an HL7 \"ACK\" (acknowledgement) message for this message.\n * @returns The HL7 \"ACK\" message.\n */\n buildAck(): Hl7Message {\n const now = new Date();\n const msh = this.getSegment('MSH');\n const sendingApp = msh?.getField(3)?.toString() ?? '';\n const sendingFacility = msh?.getField(4)?.toString() ?? '';\n const receivingApp = msh?.getField(5)?.toString() ?? '';\n const receivingFacility = msh?.getField(6)?.toString() ?? '';\n const controlId = msh?.getField(10)?.toString() ?? '';\n const versionId = msh?.getField(12)?.toString() ?? '2.5.1';\n\n return new Hl7Message([\n new Hl7Segment(\n [\n 'MSH',\n this.context.getMsh2(),\n receivingApp,\n receivingFacility,\n sendingApp,\n sendingFacility,\n formatHl7DateTime(now),\n '',\n this.buildAckMessageType(msh),\n now.getTime().toString(),\n 'P',\n versionId,\n ],\n this.context\n ),\n new Hl7Segment(['MSA', 'AA', controlId, 'OK'], this.context),\n ]);\n }\n\n private buildAckMessageType(msh: Hl7Segment | undefined): string {\n // MSH 7 is the message type\n // https://hl7-definition.caristix.com/v2/HL7v2.4/DataTypes/MSG\n // In HL7 v2.1, the message type is a single field\n // In HL7 v2.2 through v2.3, message type has two components.\n // In HL7 v2.3.1 and later, message type has three components.\n // Rather than using version to determine behavior, we instead mirror the original message.\n const messageType = msh?.getField(9);\n const triggerEvent = messageType?.getComponent(2);\n const messageStructure = messageType?.getComponent(3);\n let result = 'ACK';\n if (triggerEvent && messageStructure) {\n result = `ACK^${triggerEvent}^ACK`;\n } else if (triggerEvent) {\n result = `ACK^${triggerEvent}`;\n }\n return result;\n }\n\n /**\n * Parses an HL7 message string into an Hl7Message object.\n * @param text - The HL7 message text.\n * @returns The parsed HL7 message.\n */\n static parse(text: string): Hl7Message {\n if (!text.startsWith('MSH')) {\n const err = new Error('Invalid HL7 message');\n (err as any).type = 'entity.parse.failed';\n throw err;\n }\n const context = new Hl7Context(\n '\\r',\n text.charAt(3), // Field separator, recommended \"|\"\n text.charAt(4), // Component separator, recommended \"^\"\n text.charAt(5), // Repetition separator, recommended \"~\"\n text.charAt(6), // Escape character, recommended \"\\\"\n text.charAt(7) // Subcomponent separator, recommended \"&\"\n );\n return new Hl7Message(\n text.split(/[\\r\\n]+/).map((line) => Hl7Segment.parse(line, context)),\n context\n );\n }\n}\n\n/**\n * The Hl7Segment class represents one HL7 segment.\n * A segment is a collection of fields.\n * The name field is the first field.\n */\nexport class Hl7Segment {\n readonly context: Hl7Context;\n readonly name: string;\n readonly fields: Hl7Field[];\n\n /**\n * Creates a new HL7 segment.\n * @param fields - The HL7 fields. The first field is the segment name.\n * @param context - Optional HL7 parsing context.\n */\n constructor(fields: Hl7Field[] | string[], context = new Hl7Context()) {\n this.context = context;\n if (isStringArray(fields)) {\n this.fields = fields.map((f) => Hl7Field.parse(f, context));\n } else {\n this.fields = fields;\n }\n this.name = this.fields[0].components[0][0];\n }\n\n /**\n * Returns an HL7 field by index.\n * @param index - The HL7 field index.\n * @returns The HL7 field.\n * @deprecated Use getSegment() instead. This method includes the segment name in the index, which leads to confusing behavior. This method will be removed in a future release.\n */\n get(index: number): Hl7Field {\n return this.fields[index];\n }\n\n /**\n * Returns an HL7 field by index.\n *\n * Note that the index is 1-based, not 0-based.\n *\n * For example, to get the first field, use `getField(1)`.\n *\n * This aligns with HL7 field names such as PID.1, PID.2, etc.\n *\n * Field zero is the segment name.\n *\n * @param index - The HL7 field index.\n * @returns The HL7 field.\n */\n getField(index: number): Hl7Field {\n if (this.name === 'MSH') {\n // MSH segments require special handling due to field separator\n if (index === 1) {\n // MSH.1 is the field separator\n return new Hl7Field([[this.context.getMsh1()]], this.context);\n }\n if (index === 2) {\n // MSH.2 is the encoding characters\n return new Hl7Field([[this.context.getMsh2()]], this.context);\n }\n if (index > 2) {\n // MSH.3 through MSH.n are offset by 1\n return this.fields[index - 1];\n }\n }\n return this.fields[index];\n }\n\n /**\n * Returns an HL7 component by field index and component index.\n *\n * This is a shortcut for `getField(field).getComponent(component)`.\n *\n * Note that both indexex are 1-based, not 0-based.\n *\n * For example, to get the first component, use `getComponent(1, 1)`.\n *\n * This aligns with HL7 component names such as MSH.9.2.\n *\n * @param fieldIndex - The HL7 field index.\n * @param component - The component index.\n * @param subcomponent - Optional subcomponent index.\n * @param repetition - Optional repetition index.\n * @returns The string value of the specified component.\n */\n getComponent(fieldIndex: number, component: number, subcomponent?: number, repetition = 0): string {\n return this.getField(fieldIndex)?.getComponent(component, subcomponent, repetition) ?? '';\n }\n\n /**\n * Returns the HL7 segment as a string.\n * @returns The HL7 segment as a string.\n */\n toString(): string {\n return this.fields.map((f) => f.toString()).join(this.context.fieldSeparator);\n }\n\n /**\n * Parses an HL7 segment string into an Hl7Segment object.\n * @param text - The HL7 segment text.\n * @param context - Optional HL7 parsing context.\n * @returns The parsed HL7 segment.\n */\n static parse(text: string, context = new Hl7Context()): Hl7Segment {\n return new Hl7Segment(\n text.split(context.fieldSeparator).map((f) => Hl7Field.parse(f, context)),\n context\n );\n }\n}\n\n/**\n * The Hl7Field class represents one HL7 field.\n * A field is a collection of components.\n */\nexport class Hl7Field {\n readonly context: Hl7Context;\n readonly components: string[][];\n\n /**\n * Creates a new HL7 field.\n * @param components - The HL7 components.\n * @param context - Optional HL7 parsing context.\n */\n constructor(components: string[][], context = new Hl7Context()) {\n this.context = context;\n this.components = components;\n }\n\n /**\n * Returns an HL7 component by index.\n * @param component - The component index.\n * @param subcomponent - Optional subcomponent index.\n * @param repetition - Optional repetition index.\n * @returns The string value of the specified component.\n * @deprecated Use getComponent() instead. This method will be removed in a future release.\n */\n get(component: number, subcomponent?: number, repetition = 0): string {\n return this.getComponent(component + 1, subcomponent, repetition);\n }\n\n /**\n * Returns an HL7 component by index.\n *\n * Note that the index is 1-based, not 0-based.\n *\n * For example, to get the first component, use `getComponent(1)`.\n *\n * This aligns with HL7 component names such as MSH.9.2.\n *\n * @param component - The component index.\n * @param subcomponent - Optional subcomponent index.\n * @param repetition - Optional repetition index.\n * @returns The string value of the specified component.\n */\n getComponent(component: number, subcomponent?: number, repetition = 0): string {\n let value = this.components[repetition][component - 1] ?? '';\n\n if (subcomponent !== undefined) {\n value = value.split(this.context.subcomponentSeparator)[subcomponent] ?? '';\n }\n\n return value;\n }\n\n /**\n * Returns the HL7 field as a string.\n * @returns The HL7 field as a string.\n */\n toString(): string {\n return this.components.map((r) => r.join(this.context.componentSeparator)).join(this.context.repetitionSeparator);\n }\n\n /**\n * Parses an HL7 field string into an Hl7Field object.\n * @param text - The HL7 field text.\n * @param context - Optional HL7 parsing context.\n * @returns The parsed HL7 field.\n */\n static parse(text: string, context = new Hl7Context()): Hl7Field {\n return new Hl7Field(\n text.split(context.repetitionSeparator).map((r) => r.split(context.componentSeparator)),\n context\n );\n }\n}\n\nexport interface Hl7DateParseOptions {\n /**\n * Default timezone offset.\n * Example: \"-0500\"\n */\n tzOffset?: string;\n}\n\n/**\n * Returns a formatted string representing the date in ISO-8601 format.\n *\n * HL7-Definition V2\n * Specifies a point in time using a 24-hour clock notation.\n *\n * Format: YYYY[MM[DD[HH[MM[SS[. S[S[S[S]]]]]]]]][+/-ZZZZ].\n *\n * @param hl7DateTime - Date/time string.\n * @param options - Optional parsing options.\n * @returns The date in ISO-8601 format.\n */\nexport function parseHl7DateTime(hl7DateTime: string | undefined, options?: Hl7DateParseOptions): string | undefined {\n if (!hl7DateTime) {\n return undefined;\n }\n\n const year = parseIntOrDefault(hl7DateTime.slice(0, 4), 0);\n const month = parseIntOrDefault(hl7DateTime.slice(4, 6), 1) - 1; // Months are 0-indexed in JavaScript Date\n const day = parseIntOrDefault(hl7DateTime.slice(6, 8), 1); // Default to first day of month\n const hour = parseIntOrDefault(hl7DateTime.slice(8, 10), 0);\n const minute = parseIntOrDefault(hl7DateTime.slice(10, 12), 0);\n const second = parseIntOrDefault(hl7DateTime.slice(12, 14), 0);\n\n let millisecond = 0;\n if (hl7DateTime.includes('.')) {\n millisecond = parseIntOrDefault(hl7DateTime.slice(15, 19), 0);\n }\n\n let date = new Date(Date.UTC(year, month, day, hour, minute, second, millisecond));\n\n const tzOffset = parseTimeZoneOffset(hl7DateTime, options?.tzOffset);\n if (tzOffset !== 0) {\n date = new Date(date.getTime() - tzOffset);\n }\n\n return date.toISOString();\n}\n\n/**\n * Parses an integer value from a string.\n * @param str - The string to parse.\n * @param defaultValue - The default value to return if the string is not a number.\n * @returns The parsed integer value, or the default value if the string is not a number.\n */\nfunction parseIntOrDefault(str: string, defaultValue: number): number {\n const result = parseInt(str, 10);\n return isNaN(result) ? defaultValue : result;\n}\n\n/**\n * Returns the timezone offset in milliseconds.\n * @param hl7DateTime - The HL7 date/time string.\n * @param defaultOffset - Optional default timezone offset.\n * @returns The timezone offset in milliseconds.\n */\nfunction parseTimeZoneOffset(hl7DateTime: string, defaultOffset?: string): number {\n let offsetStr = defaultOffset;\n\n const plusIndex = hl7DateTime.indexOf('+');\n if (plusIndex !== -1) {\n offsetStr = hl7DateTime.slice(plusIndex);\n }\n\n const minusIndex = hl7DateTime.indexOf('-');\n if (minusIndex !== -1) {\n offsetStr = hl7DateTime.slice(minusIndex);\n }\n\n if (!offsetStr) {\n return 0;\n }\n\n const sign = offsetStr.startsWith('-') ? -1 : 1;\n\n // Remove plus, minus, and optional colon\n offsetStr = offsetStr.slice(1).replace(':', '');\n\n const hour = parseInt(offsetStr.slice(0, 2), 10);\n const minute = parseInt(offsetStr.slice(2, 4), 10);\n return sign * (hour * 60 * 60 * 1000 + minute * 60 * 1000);\n}\n\n/**\n * Formats an ISO date/time string into an HL7 date/time string.\n * @param isoDate - The ISO date/time string.\n * @returns The HL7 date/time string.\n */\nexport function formatHl7DateTime(isoDate: Date | string): string {\n const date = isoDate instanceof Date ? isoDate : new Date(isoDate);\n const isoString = date.toISOString();\n\n // Replace \"T\" and all dashes (-) and colons (:) with empty strings\n // Replace Z with \"+0000\"\n // Replace the last 3 digits before 'Z' with the 4-digit milliseconds\n let result = isoString.replace(/[-:T]/g, '').replace(/(\\.\\d+)?Z$/, '');\n\n const milliseconds = date.getUTCMilliseconds();\n if (milliseconds > 0) {\n result += '.' + milliseconds.toString();\n }\n\n return result;\n}\n", "/*\n * Once upon a time, we used Winston, and that was fine.\n * Then the log4j fiasco happened, and everyone started auditing logging libraries.\n * And we decided that we did not use any fancy logging features,\n * and that logging to console.log was actually perfectly adequate.\n */\n\n/**\n * Logging level, with greater values representing more detailed logs emitted.\n *\n * The zero value means no server logs will be emitted.\n */\nexport enum LogLevel {\n NONE = 0,\n ERROR,\n WARN,\n INFO,\n DEBUG,\n}\n\nexport interface LoggerOptions {\n prefix?: string;\n}\n\nexport interface LoggerConfig {\n write: (msg: string) => void;\n metadata: Record<string, any>;\n level: LogLevel;\n options?: LoggerOptions;\n}\n\nexport type LoggerConfigOverride = Partial<LoggerConfig>;\n\nexport class Logger {\n readonly prefix?: string;\n constructor(\n readonly write: (msg: string) => void,\n readonly metadata: Record<string, any> = {},\n public level: LogLevel = LogLevel.INFO,\n readonly options?: LoggerOptions\n ) {\n if (options?.prefix) {\n this.prefix = options.prefix;\n }\n\n this.error = this.error.bind(this);\n this.warn = this.warn.bind(this);\n this.info = this.info.bind(this);\n this.debug = this.debug.bind(this);\n this.log = this.log.bind(this);\n }\n\n clone(override?: LoggerConfigOverride): Logger {\n const config = this.getLoggerConfig();\n const mergedConfig = override\n ? { ...config, override, options: { ...config.options, ...override.options } }\n : config;\n return new Logger(mergedConfig.write, mergedConfig.metadata, mergedConfig.level, mergedConfig.options);\n }\n\n private getLoggerConfig(): LoggerConfig {\n const { write, metadata, level, options } = this;\n return { write, metadata, level, options };\n }\n\n error(msg: string, data?: Record<string, any> | Error): void {\n this.log(LogLevel.ERROR, msg, data);\n }\n\n warn(msg: string, data?: Record<string, any> | Error): void {\n this.log(LogLevel.WARN, msg, data);\n }\n\n info(msg: string, data?: Record<string, any> | Error): void {\n this.log(LogLevel.INFO, msg, data);\n }\n\n debug(msg: string, data?: Record<string, any> | Error): void {\n this.log(LogLevel.DEBUG, msg, data);\n }\n\n log(level: LogLevel, msg: string, data?: Record<string, any> | Error): void {\n if (level > this.level) {\n return;\n }\n if (data instanceof Error) {\n data = {\n error: data.toString(),\n stack: data.stack?.split('\\n'),\n };\n }\n this.write(\n JSON.stringify({\n level: LogLevel[level],\n timestamp: new Date().toISOString(),\n msg: this.prefix ? `${this.prefix}${msg}` : msg,\n ...data,\n ...this.metadata,\n })\n );\n }\n}\n\nexport function parseLogLevel(level: string): LogLevel {\n const value = LogLevel[level.toUpperCase() as keyof typeof LogLevel];\n if (value === undefined) {\n throw new Error(`Invalid log level: ${level}`);\n }\n\n return value;\n}\n", "import { OperationOutcomeIssue } from '@medplum/fhirtypes';\nimport { createStructureIssue, OperationOutcomeError, validationError } from './outcomes';\nimport { isResourceType } from './typeschema/types';\n\n/**\n * Validates that the given string is a valid FHIR resource type.\n *\n * On success, silently returns void.\n * On failure, throws an OperationOutcomeError.\n *\n * @example\n * ```ts\n * validateResourceType('Patient'); // nothing\n * validateResourceType('XYZ'); // throws OperationOutcomeError\n * ```\n *\n * Note that this depends on globalSchema, which is populated by the StructureDefinition loader.\n *\n * @example\n * In a server context, you can load all schema definitions:\n *\n * ```ts\n * import { indexStructureDefinitionBundle } from '@medplum/core';\n * import { readJson } from '@medplum/definitions';\n * import { Bundle } from '@medplum/fhirtypes';\n *\n * indexStructureDefinitionBundle(readJson('fhir/r4/profiles-resources.json') as Bundle);\n * ```\n *\n * @example\n * In a client context, you can load the schema definitions using MedplumClient:\n *\n * ```ts\n * import { MedplumClient } from '@medplum/core';\n *\n * const medplum = new MedplumClient();\n * await medplum.requestSchema('Patient');\n * ```\n *\n * @param resourceType - The candidate resource type string.\n */\nexport function validateResourceType(resourceType: string): void {\n if (!resourceType) {\n throw new OperationOutcomeError(validationError('Resource type is null'));\n }\n if (!isResourceType(resourceType)) {\n throw new OperationOutcomeError(validationError('Unknown resource type'));\n }\n}\n\n/**\n * Recursively checks for null values in an object.\n *\n * Note that \"null\" is a special value in JSON that is not allowed in FHIR.\n * @param value - Input value of any type.\n * @param path - Path string to the value for OperationOutcome.\n * @param issues - Output list of issues.\n */\nexport function checkForNull(value: unknown, path: string, issues: OperationOutcomeIssue[]): void {\n if (value === null) {\n issues.push(createStructureIssue(path, 'Invalid null value'));\n } else if (Array.isArray(value)) {\n checkArrayForNull(value, path, issues);\n } else if (typeof value === 'object') {\n checkObjectForNull(value as Record<string, unknown>, path, issues);\n }\n}\n\nfunction checkArrayForNull(array: unknown[], path: string, issues: OperationOutcomeIssue[]): void {\n for (let i = 0; i < array.length; i++) {\n if (array[i] === undefined) {\n issues.push(createStructureIssue(`${path}[${i}]`, 'Invalid undefined value'));\n } else {\n checkForNull(array[i], `${path}[${i}]`, issues);\n }\n }\n}\n\nfunction checkObjectForNull(obj: Record<string, unknown>, path: string, issues: OperationOutcomeIssue[]): void {\n for (const [key, value] of Object.entries(obj)) {\n checkForNull(value, `${path}${path ? '.' : ''}${key}`, issues);\n }\n}\n", "import { Readable } from 'stream';\n\n/**\n * Reads data from a Readable stream and returns a Promise that resolves with a Buffer containing all the data.\n * @param stream - The Readable stream to read from.\n * @returns A Promise that resolves with a Buffer containing all the data from the Readable stream.\n */\nexport function streamToBuffer(stream: Readable): Promise<Buffer> {\n const chunks: Uint8Array[] = [];\n return new Promise<Buffer>((resolve, reject) => {\n stream.on('data', (chunk: Uint8Array) => chunks.push(Buffer.from(chunk)));\n stream.on('error', (err: Error) => {\n stream.destroy();\n reject(err);\n });\n stream.on('end', () => {\n resolve(Buffer.concat(chunks));\n });\n stream.on('close', () => {\n stream.destroy();\n });\n });\n}\n", "import { Resource, ViewDefinition, ViewDefinitionSelect } from '@medplum/fhirtypes';\nimport { evalFhirPathTyped } from '../fhirpath/parse';\nimport { getTypedPropertyValue, toTypedValue } from '../fhirpath/utils';\nimport { TypedValue } from '../types';\n\n/**\n * Represents a \"selection structure\" in the SQL-on-FHIR specification.\n *\n * In practice, this can be a ViewDefinition or ViewDefinitionSelect.\n *\n * TypeScript does not like checks for properties that are not part of the type, so we use this interface instead.\n */\nexport interface SelectionStructure {\n forEach?: string;\n forEachOrNull?: string;\n column?: ViewDefinitionSelect['column'];\n select?: SelectionStructure[];\n unionAll?: SelectionStructure[];\n}\n\n/**\n * SQL on FHIR output row.\n */\nexport type OutputRow = Record<string, any>;\n\n/**\n * Evaluates a SQL-on-FHIR view on a set of FHIR resources.\n * @param view - The view definition.\n * @param resources - The array of FHIR resources.\n * @returns The output rows.\n */\nexport function evalSqlOnFhir(view: ViewDefinition, resources: Resource[]): OutputRow[] {\n const result = [];\n\n for (const resource of resources) {\n result.push(...processResource(view, resource));\n }\n\n return result;\n}\n\n/**\n * Processes a FHIR resource with a ViewDefinition to emit all rows.\n *\n * This step emits all rows produced by a ViewDefinition on an input Resource, by setting up a recursive call.\n *\n * See: https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/implementer_guidance.html#process-a-resource-entry-point\n *\n * @param v - The view definition.\n * @param r - The FHIR resource.\n * @returns The output rows.\n */\nfunction processResource(v: ViewDefinition, r: Resource): OutputRow[] {\n if (!v.resource) {\n throw new Error('Resource type is required');\n }\n\n if (v.resource !== r.resourceType) {\n return [];\n }\n\n const variables: Record<string, TypedValue> = {};\n if (v.constant) {\n for (const c of v.constant) {\n const typedConstant = { type: 'ViewDefinitionConstant', value: c };\n variables['%' + c.name] = getTypedPropertyValue(typedConstant, 'value') as TypedValue;\n }\n }\n\n const typedResource = toTypedValue(r);\n\n if (v.where) {\n for (const where of v.where) {\n const whereResult = evalFhirPathTyped(where.path, [typedResource], variables);\n if (whereResult.length !== 1) {\n return [];\n }\n if (whereResult[0].type !== 'boolean') {\n throw new Error('WHERE clause must evaluate to a boolean');\n }\n if (!whereResult[0].value) {\n return [];\n }\n }\n }\n\n return process(v, typedResource, variables);\n}\n\n/**\n * Processes a selection structure and node to emit all rows.\n *\n * This step emits all rows for a given Selection Structure and Node. We first generate sets of\n * \"partial rows\" (i.e., sets of incomplete column bindings from the various clauses of V) and combine them to emit complete rows.\n *\n * This function is deliberately structured to match the pseudocode in the SQL-on-FHIR specification.\n * See: https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/implementer_guidance.html#processs-n-recursive-step\n *\n * @param s - The selection structure.\n * @param n - The node (element) from a FHIR resource.\n * @param variables - The variables.\n * @returns An array of output rows.\n */\nfunction process(s: SelectionStructure, n: TypedValue, variables: Record<string, TypedValue>): OutputRow[] {\n const result: OutputRow[] = [];\n\n // 1. Define a list of Nodes foci as\n let foci: TypedValue[];\n if (s.forEach) {\n // If S.forEach is defined: fhirpath(S.forEach, N)\n foci = evalFhirPathTyped(s.forEach, [n], variables);\n } else if (s.forEachOrNull) {\n // Else if S.forEachOrNull is defined: fhirpath(S.forEachOrNull, N)\n foci = evalFhirPathTyped(s.forEachOrNull, [n], variables);\n } else {\n // Otherwise: [N] (a list with just the input node)\n foci = [n];\n }\n\n // 2. For each element f of foci\n for (const f of foci) {\n // Initialize an empty list parts (each element of parts will be a list of partial rows)\n const parts: OutputRow[][] = [];\n\n // Process Columns:\n for (const col of s.column ?? []) {\n // For each Column col of S.column, define val as fhirpath(col.path, f)\n const val = evalFhirPathTyped(col.path, [f], variables);\n\n // Define b as a row whose column named col.name takes the value\n let b: OutputRow;\n\n if (val.length === 0) {\n // If val was the empty set: null\n b = { [col.name]: null };\n } else if (col.collection) {\n // Else if col.collection is true: val\n b = { [col.name]: val.map((v) => v.value) };\n } else if (val.length === 1) {\n // Else if val has a single element e: e\n b = { [col.name]: val[0].value };\n } else {\n // Else: throw \"Multiple values found but not expected for column\"\n throw new Error('Multiple values found but not expected for column');\n }\n\n // Append [b] to parts\n // (Note: append a list so the final element of parts is now a list containing the single row b).)\n parts.push([b]);\n }\n\n // Process Selects:\n // For each selection structure sel of S.select\n for (const sel of s.select ?? []) {\n // Define rows as the collection of all rows emitted by Process(sel, f)\n const rows = process(sel, f, variables);\n\n // Append rows to parts\n // (Note: do not append the elements but the whole list, so the final element of parts is now the list rows)\n parts.push(rows);\n }\n\n // Process UnionAlls:\n // Initialize urows as an empty list of rows\n // For each selection structure u of S.unionAll\n if (s.unionAll) {\n const urows: OutputRow[] = [];\n for (const u of s.unionAll) {\n // For each row r in Process(u, f)\n for (const r of process(u, f, variables)) {\n // Append r to urows\n urows.push(r);\n }\n }\n\n // Append urows to parts\n // (Note: do not append the elements but the whole list, so the final element of parts is now the list urows\n parts.push(urows);\n }\n\n // For every list of partial rows prows in the Cartesian product of parts\n // (Note: the Cartesian product is always between a Selection Structure and its direct children, not deeper descendants.\n // Because the process is recursive, rows generated by, for example, a .select[0].select[0].select[0] will eventually bubble up\n // to the top level, but the bubbling happens one level at a time.)\n result.push(...cartesianProduct(parts));\n }\n\n // If foci is an empty list and S.forEachOrNull is defined\n if (foci.length === 0 && s.forEachOrNull) {\n // (Note: when this condition is met, no rows have been emitted so far)\n // Initialize a blank row r\n const r: OutputRow = {};\n\n // For each Column c in ValidateColumns(V, [])\n for (const c of s.column ?? []) {\n // Bind the column c.name to null in the row r\n r[c.name] = null;\n }\n\n // Emit the row r\n result.push(r);\n }\n\n return result;\n}\n\n/**\n * Returns the Cartesian product of the given arrays.\n *\n * For example, if there are two sets of partial rows:\n *\n * [{\"a\": 1},{\"a\": 2}] with bindings for the variable a\n * [{\"b\": 3},{\"b\": 4}] with bindings for the variable b\n *\n * Then the Cartesian product of these sets consists of four complete rows:\n *\n * [\n * {\"a\": 1, \"b\": 3},\n * {\"a\": 1, \"b\": 4},\n * {\"a\": 2, \"b\": 3},\n * {\"a\": 2, \"b\": 4}\n * ]\n *\n * @param parts - The arrays to combine.\n * @returns The Cartesian product of the arrays.\n */\nfunction cartesianProduct(parts: OutputRow[][]): OutputRow[] {\n if (parts.length === 0) {\n return [];\n }\n\n let temp = parts[0];\n for (let i = 1; i < parts.length; i++) {\n temp = cartesianProductHelper(temp, parts[i]);\n }\n\n return temp;\n}\n\nfunction cartesianProductHelper(aArray: OutputRow[], bArray: OutputRow[]): OutputRow[] {\n const result = [];\n for (const a of aArray) {\n for (const b of bArray) {\n result.push(combinePartialRows(a, b));\n }\n }\n return result;\n}\n\nfunction combinePartialRows(a: OutputRow, b: OutputRow): OutputRow {\n const result: OutputRow = {};\n Object.assign(result, a);\n Object.assign(result, b);\n return result;\n}\n", "import { MEDPLUM_VERSION } from './client';\nimport { normalizeErrorString } from './outcomes';\n\nexport const MEDPLUM_RELEASES_URL = 'https://meta.medplum.com/releases';\n\nexport type ReleaseManifest = { tag_name: string; assets: { name: string; browser_download_url: string }[] };\n\nconst releaseManifests = new Map<string, ReleaseManifest>();\n\n/**\n * Clears the locally-cached `ReleaseManifest`s for all versions.\n */\nexport function clearReleaseCache(): void {\n releaseManifests.clear();\n}\n\n/**\n * Asserts that a given candidate is a `ReleaseManifest`.\n * @param candidate - An object assumed to be a `ReleaseManifest`.\n */\nexport function assertReleaseManifest(candidate: unknown): asserts candidate is ReleaseManifest {\n const manifest = candidate as ReleaseManifest;\n if (!manifest.tag_name) {\n throw new Error('Manifest missing tag_name');\n }\n const assets = manifest.assets;\n if (!assets?.length) {\n throw new Error('Manifest missing assets list');\n }\n for (const asset of assets) {\n if (!asset.browser_download_url) {\n throw new Error('Asset missing browser download URL');\n }\n if (!asset.name) {\n throw new Error('Asset missing name');\n }\n }\n}\n\n/**\n * Fetches the manifest for a given Medplum release version.\n * @param appName - The name of the app to fetch the manifest for.\n * @param version - The version to fetch. If no `version` is provided, defaults to the `latest` version.\n * @param params - An optional list of key-value pairs to be appended to the URL query string.\n * @returns - The manifest for the specified or latest version.\n */\nexport async function fetchVersionManifest(\n appName: string,\n version?: string,\n params?: Record<string, string>\n): Promise<ReleaseManifest> {\n let manifest = releaseManifests.get(version ?? 'latest');\n if (!manifest) {\n const versionTag = version ? `v${version}` : 'latest';\n const url = new URL(`${MEDPLUM_RELEASES_URL}/${versionTag}.json`);\n url.searchParams.set('a', appName);\n url.searchParams.set('c', MEDPLUM_VERSION);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n }\n const res = await fetch(url.toString());\n if (res.status !== 200) {\n let message: string | undefined;\n try {\n message = ((await res.json()) as { message: string }).message;\n } catch (err) {\n console.error(`Failed to parse message from body: ${normalizeErrorString(err)}`);\n }\n throw new Error(\n `Received status code ${res.status} while fetching manifest for version '${version ?? 'latest'}'. Message: ${message}`\n );\n }\n const response = (await res.json()) as ReleaseManifest;\n assertReleaseManifest(response);\n manifest = response;\n releaseManifests.set(version ?? 'latest', manifest);\n if (!version) {\n releaseManifests.set(manifest.tag_name.slice(1), manifest);\n }\n }\n return manifest;\n}\n\n/**\n * Tests that a given version string follows the basic semver pattern of `<int>.<int>.<int>`, which is used for Medplum versions.\n *\n * @param version - A version string that should be tested for valid semver semantics.\n * @returns `true` if `version` is a valid semver version that conforms to the Medplum versioning system, otherwise `false`.\n */\nexport function isValidMedplumSemver(version: string): boolean {\n return /^\\d+\\.\\d+\\.\\d+$/.test(version);\n}\n\n/**\n * Tests that a given version string is a valid existing Medplum release version.\n * @param appName - The name of the app to check the version for.\n * @param version - A version to be checked against the existing Medplum repo releases.\n * @returns `true` if `version` is a valid semver version that corresponds to an existing release, otherwise `false`.\n */\nexport async function checkIfValidMedplumVersion(appName: string, version: string): Promise<boolean> {\n if (!isValidMedplumSemver(version)) {\n return false;\n }\n try {\n await fetchVersionManifest(appName, version);\n } catch (_err) {\n return false;\n }\n return true;\n}\n\n/**\n * Fetches the latest Medplum release version string.\n * @param appName - The name of the app to fetch the latest version for.\n * @returns A version string corresponding to the latest Medplum release version.\n */\nexport async function fetchLatestVersionString(appName: string): Promise<string> {\n const latest = await fetchVersionManifest(appName);\n if (!latest.tag_name.startsWith('v')) {\n throw new Error(`Invalid release name found. Release tag '${latest.tag_name}' did not start with 'v'`);\n }\n return latest.tag_name.slice(1);\n}\n\n/**\n * Checks if a newer version of Medplum is available and logs a warning if so.\n * @param appName - The name of the app to check the version for.\n * @param params - An optional list of key-value pairs to be appended to the URL query string.\n */\nexport async function warnIfNewerVersionAvailable(appName: string, params?: Record<string, string>): Promise<void> {\n try {\n const current = MEDPLUM_VERSION.split('-')[0];\n const manifest = await fetchVersionManifest(appName, undefined, params);\n const latest = manifest.tag_name.slice(1);\n if (current !== latest) {\n console.warn(\n `A new version (v${latest}) of Medplum is available. Your current version (v${current}) may be missing important updates and bug fixes.`\n );\n }\n } catch (err) {\n console.warn(`Failed to check for newer version: ${normalizeErrorString(err)}`);\n }\n}\n", "/*\n * C-CDA date/time formats:\n * 1. YYYY\n * 2. YYYYMMDD\n * 3. YYYYMMDDHHMM\n * 4. YYYYMMDDHHMMSS\n * 5. YYYYMMDDHHMM-TZ\n * 6. YYYYMMDDHHMMSS-TZ\n *\n * FHIR date formats:\n * 1. YYYY\n * 2. YYYY-MM-DD\n *\n * FHIR date/time formats:\n * 1. YYYY-MM-DDTHH:MM:SS-TZ\n */\n\n/**\n * Map the C-CDA date to the FHIR date.\n * @param date - The C-CDA date.\n * @returns The FHIR date.\n */\nexport function mapCcdaToFhirDate(date: string | undefined): string | undefined {\n if (!date) {\n return undefined;\n }\n\n const year = date.substring(0, 4);\n let month = '01';\n let day = '01';\n\n if (date.length >= 8) {\n month = date.substring(4, 6);\n day = date.substring(6, 8);\n }\n\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Map the C-CDA date time to the FHIR date time.\n * @param dateTime - The C-CDA date time.\n * @returns The FHIR date time.\n */\nexport function mapCcdaToFhirDateTime(dateTime: string | undefined): string | undefined {\n if (!dateTime) {\n return undefined;\n }\n\n const year = dateTime.substring(0, 4);\n let month = '01';\n let day = '01';\n let hour = '00';\n let minute = '00';\n let second = '00';\n let tz = 'Z';\n\n if (dateTime.length >= 8) {\n month = dateTime.substring(4, 6);\n day = dateTime.substring(6, 8);\n }\n\n if (dateTime.length >= 12) {\n hour = dateTime.substring(8, 10);\n minute = dateTime.substring(10, 12);\n }\n\n if (dateTime.length >= 14) {\n second = dateTime.substring(12, 14);\n }\n\n if (dateTime.length > 14) {\n tz = dateTime.substring(14);\n }\n\n return `${year}-${month}-${day}T${hour}:${minute}:${second}${tz}`;\n}\n\n/**\n * Map the FHIR date to the C-CDA date.\n * @param date - The FHIR date.\n * @returns The C-CDA date.\n */\nexport function mapFhirToCcdaDate(date: string | undefined): string | undefined {\n if (!date) {\n return undefined;\n }\n return date.substring(0, 10).replace(/-/g, '');\n}\n\n/**\n * Map the FHIR date time to the C-CDA date time.\n * @param dateTime - The FHIR date time.\n * @returns The C-CDA date time.\n */\nexport function mapFhirToCcdaDateTime(dateTime: string | undefined): string | undefined {\n if (!dateTime) {\n return undefined;\n }\n\n const [date, time] = dateTime.split('T');\n\n const outDate = date.replaceAll('-', ''); // Remove dashes\n\n const outTime = (time ?? '')\n .replaceAll(/\\.\\d+/g, '') // Remove decimal point seconds\n .replaceAll(/:/g, '') // Remove colons\n .replaceAll(/Z/g, '+0000'); // Replace Z with +0000\n\n return `${outDate}${outTime}`;\n}\n", "// Source: https://confluence.hl7.org/spaces/SD/pages/51227465/C-CDA+OIDs\n\nexport const OID_REASON_FOR_REFERRAL = '1.3.6.1.4.1.19376.1.5.3.1.3.1';\nexport const OID_REASON_FOR_REFERRAL_V2 = '1.3.6.1.4.1.19376.1.5.3.1.3.1.2';\nexport const OID_DISCHARGE_DIET_SECTION = '1.3.6.1.4.1.19376.1.5.3.1.3.33';\nexport const OID_HL7_REGISTERED_MODELS = '2.16.840.1.113883.1.3';\nexport const OID_REFERRAL_DOCUMENT_TYPE_VALUE_SET = '2.16.840.1.113883.1.11.20.2.3';\nexport const OID_TRANSFERL_DOCUMENT_TYPE_VALUE_SET = '2.16.840.1.113883.1.11.20.2.4';\nexport const OID_WOUND_MEASURMENTS_CHARACTERISTICS_VALUE_SET_VALUE_SET = '2.16.840.1.113883.1.11.20.2.5';\nexport const OID_WOUND_TYPE_VALUE_SET_VALUE_SET = '2.16.840.1.113883.1.11.20.2.6';\nexport const OID_FINDING_OF_NUTRITIONAL_STATUS_VALUE_SET = '2.16.840.1.113883.1.11.20.2.7';\nexport const OID_NUTRITION_ASSESSMENT_VALUE_SET = '2.16.840.1.113883.1.11.20.2.8';\nexport const OID_NUTRITION_RECOMMENDATIONS_VALUE_SET = '2.16.840.1.113883.1.11.20.2.9';\nexport const OID_PAN_CANADIAN_LOINC_OBSERVATION_CODE_SYSTEM = '2.16.840.1.113883.2.20.5.1';\nexport const OID_NCI_THESAURUS_CODE_SYSTEM = '2.16.840.1.113883.3.26.1.1';\nexport const OID_PROBLEM_TYPE_SNOMED_CT_VALUE_SET = '2.16.840.1.113883.3.88.12.3221.7.2';\nexport const OID_US_SSN_CODE_SYSTEM = '2.16.840.1.113883.4.1';\nexport const OID_US_DLN_CODE_SYSTEM = '2.16.840.1.113883.4.3';\nexport const OID_US_NPI_CODE_SYSTEM = '2.16.840.1.113883.4.6';\nexport const OID_UNII_CODE_SYSTEM = '2.16.840.1.113883.4.9';\nexport const OID_ADMINISTRATIVE_GENDER_CODE_SYSTEM = '2.16.840.1.113883.5.1';\nexport const OID_ACT_CODE_CODE_SYSTEM = '2.16.840.1.113883.5.4';\nexport const OID_ACT_CLASS_CODE_SYSTEM = '2.16.840.1.113883.5.6';\nexport const OID_CONFIDENTIALITY_VALUE_SET = '2.16.840.1.113883.5.25';\nexport const OID_LOINC_CODE_SYSTEM = '2.16.840.1.113883.6.1';\nexport const OID_CPT_CODE_SYSTEM = '2.16.840.1.113883.6.12';\nexport const OID_MDC_CODE_SYSTEM = '2.16.840.1.113883.6.24';\nexport const OID_NDC_CODE_SYSTEM = '2.16.840.1.113883.6.69';\nexport const OID_RXNORM_CODE_SYSTEM = '2.16.840.1.113883.6.88';\nexport const OID_SNOMED_CT_CODE_SYSTEM = '2.16.840.1.113883.6.96';\nexport const OID_NUCC_TAXONOMY_CODE_SYSTEM = '2.16.840.1.113883.6.101';\nexport const OID_NDF_RT_CODE_SYSTEM = '2.16.840.1.113883.6.209';\nexport const OID_CDC_RACE_AND_ETHNICITY_CODE_SYSTEM = '2.16.840.1.113883.6.238';\nexport const OID_VA_MED_RT_CODE_SYSTEM = '2.16.840.1.113883.6.345';\nexport const OID_GENERAL_STATUS_SECTION = '2.16.840.1.113883.10.20.2.5';\nexport const OID_PHYSICIAN_OF_RECORD_PARTICIPANT = '2.16.840.1.113883.10.20.6.2.2';\nexport const OID_PHYSICIAN_OF_RECORD_PARTICIPANT_V2 = '2.16.840.1.113883.10.20.6.2.2.2';\nexport const OID_PREGNANCY_OBSERVATION = '2.16.840.1.113883.10.20.15.3.8';\nexport const OID_INTERVENTIONS_SECTION = '2.16.840.1.113883.10.20.21.2.3';\nexport const OID_INTERVENTIONS_SECTION_V2 = '2.16.840.1.113883.10.20.21.2.3.2';\nexport const OID_US_REALM_CDA_HEADER = '2.16.840.1.113883.10.20.22.1.1';\nexport const OID_US_REALM_HEADER_V2 = '2.16.840.1.113883.10.20.22.1.1.2';\nexport const OID_CONTINUITY_OF_CARE_DOCUMENT = '2.16.840.1.113883.10.20.22.1.2';\nexport const OID_CONTINUITY_OF_CARE_DOCUMENT_V2 = '2.16.840.1.113883.10.20.22.1.2.2';\nexport const OID_HISTORY_AND_PHYSICAL = '2.16.840.1.113883.10.20.22.1.3';\nexport const OID_HISTORY_AND_PHYSICAL_V2 = '2.16.840.1.113883.10.20.22.1.3.2';\nexport const OID_CONSULTATION_NOTE = '2.16.840.1.113883.10.20.22.1.4';\nexport const OID_CONSULTATION_NOTE_V2 = '2.16.840.1.113883.10.20.22.1.4.2';\nexport const OID_DIAGNOSTIC_IMAGING_REPORT_DIR = '2.16.840.1.113883.10.20.22.1.5';\nexport const OID_DIAGNOSTIC_IMAGING_REPORT_DIR_V2 = '2.16.840.1.113883.10.20.22.1.5.2';\nexport const OID_PROCEDURE_NOTE = '2.16.840.1.113883.10.20.22.1.6';\nexport const OID_PROCEDURE_NOTE_V2 = '2.16.840.1.113883.10.20.22.1.6.2';\nexport const OID_OPERATIVE_NOTE = '2.16.840.1.113883.10.20.22.1.7';\nexport const OID_OPERATIVE_NOTE_V2 = '2.16.840.1.113883.10.20.22.1.7.2';\nexport const OID_DISCHARGE_SUMMARY = '2.16.840.1.113883.10.20.22.1.8';\nexport const OID_DISCHARGE_SUMMARY_V2 = '2.16.840.1.113883.10.20.22.1.8.2';\nexport const OID_PROGRESS_NOTE = '2.16.840.1.113883.10.20.22.1.9';\nexport const OID_PROGRESS_NOTE_V2 = '2.16.840.1.113883.10.20.22.1.9.2';\nexport const OID_UNSTRUCTURED_DOCUMENT = '2.16.840.1.113883.10.20.22.1.10';\nexport const OID_UNSTRUCTURED_DOCUMENT_V2 = '2.16.840.1.113883.10.20.22.1.10.2';\nexport const OID_PATIENT_QUESTIONNAIRE_SUMMARY_DOCUMENT = '2.16.840.1.113883.10.20.22.1.11';\nexport const OID_MDS_PATIENT_QUESTIONNAIRE_SUMMARY_DOCUMENT = '2.16.840.1.113883.10.20.22.1.11.1';\nexport const OID_OASIS_PATIENT_QUESTIONNAIRE_SUMMARY_DOCUMENT = '2.16.840.1.113883.10.20.22.1.11.2';\nexport const OID_TRANSFER_SUMMARY_DOCUMENT_MASS_HIE = '2.16.840.1.113883.10.20.22.1.12';\nexport const OID_TRANSFER_SUMMARY_C_CDA = '2.16.840.1.113883.10.20.22.1.13';\nexport const OID_REFERAL_SUMMARY_C_CDA = '2.16.840.1.113883.10.20.22.1.14';\nexport const OID_CARE_PLAN_C_CDA = '2.16.840.1.113883.10.20.22.1.15';\nexport const OID_CARE_PLAN_NUTRITION = '2.16.840.1.113883.10.20.22.1.16';\nexport const OID_MINIMALLY_STRUCTURED_DOCUMENT_XDOC = '2.16.840.1.113883.10.20.22.1.17';\nexport const OID_MEDICATIONS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.1';\nexport const OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.1.1';\nexport const OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED_V2 = '2.16.840.1.113883.10.20.22.2.1.1.2';\nexport const OID_MEDICATIONS_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.1.2';\nexport const OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.2';\nexport const OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.2.1';\nexport const OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED_V2 = '2.16.840.1.113883.10.20.22.2.2.1.2';\nexport const OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.2.2';\nexport const OID_RESULTS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.3';\nexport const OID_RESULTS_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.3.1';\nexport const OID_RESULTS_SECTION_ENTRIES_REQUIRED_V2 = '2.16.840.1.113883.10.20.22.2.3.1.2';\nexport const OID_RESULTS_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.3.2';\nexport const OID_VITAL_SIGNS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.4';\nexport const OID_VITAL_SIGNS_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.4.1';\nexport const OID_VITAL_SIGNS_SECTION_ENTRIES_REQUIRED_V2 = '2.16.840.1.113883.10.20.22.2.4.1.2';\nexport const OID_VITAL_SIGNS_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.4.2';\nexport const OID_PROBLEMS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.5';\nexport const OID_PROBLEMS_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.5.1';\nexport const OID_PROBLEMS_SECTION_V2_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.5.1 .2';\nexport const OID_PROBLEMS_SECTION_V2_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.5.2';\nexport const OID_ALLERGIES_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.6';\nexport const OID_ALLERGIES_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.6.1';\nexport const OID_ALLERGIES_SECTION_ENTRIES_REQUIRED_V2 = '2.16.840.1.113883.10.20.22.2.6.1.2';\nexport const OID_ALLERGIES_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.6.2';\nexport const OID_PROCEDURES_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.7';\nexport const OID_PROCEDURES_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.7.1';\nexport const OID_PROCEDURES_SECTION_ENTRIES_REQUIRED_V2 = '2.16.840.1.113883.10.20.22.2.7.1.2';\nexport const OID_PROCEDURES_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.7.2';\nexport const OID_ASSESSMENTS_SECTION = '2.16.840.1.113883.10.20.22.2.8';\nexport const OID_ASSESSMENT_AND_PLAN_SECTION = '2.16.840.1.113883.10.20.22.2.9';\nexport const OID_ASSESSMENT_AND_PLAN_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.9';\nexport const OID_PLAN_OF_CARE_SECTION = '2.16.840.1.113883.10.20.22.2.10';\nexport const OID_PLAN_OF_CARE_TREATMENT_V2 = '2.16.840.1.113883.10.20.22.2.10.2';\nexport const OID_HOSPITAL_DISCHARGE_MEDICATIONS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.11';\nexport const OID_HOSPITAL_DISCHARGE_MEDICATIONS_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.11.1';\nexport const OID_REASON_FOR_VISIT_SECTION = '2.16.840.1.113883.10.20.22.2.12';\nexport const OID_CHIEF_COMPLAINT_REASON_FOR_VISIT_SECTION = '2.16.840.1.113883.10.20.22.2.13';\nexport const OID_FUNCTIONAL_STATUS_SECTION = '2.16.840.1.113883.10.20.22.2.14';\nexport const OID_FUNCTIONAL_STATUS_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.14.2';\nexport const OID_FAMILY_HISTORY_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.15';\nexport const OID_HOSPITAL_DISCHARGE_STUDIES_SUMMARY_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.16';\nexport const OID_SOCIAL_HISTORY_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.17';\nexport const OID_SOCIAL_HISTORY_SECTION_V2_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.17.2';\nexport const OID_PAYERS_SECTION = '2.16.840.1.113883.10.20.22.2.18';\nexport const OID_PAYERS_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.18.2';\nexport const OID_PHYSICAL_EXAM_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.19';\nexport const OID_PHYSICAL_EXAM_SECTION_V2_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.19.2';\nexport const OID_HISTORY_OF_PAST_ILLNESS_SECTION = '2.16.840.1.113883.10.20.22.2.20';\nexport const OID_HISTORY_OF_PAST_ILLNESS_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.20.2';\nexport const OID_ADVANCE_DIRECTIVE_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.21';\nexport const OID_ADVANCE_DIRECTIVE_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.21.1';\nexport const OID_ADVANCE_DIRECTIVE_SECTION_ENTRIES_REQUIRED_V2 = '2.16.840.1.113883.10.20.22.2.21.1.2';\nexport const OID_ADVANCE_DIRECTIVE_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.21.2';\nexport const OID_ENCOUNTERS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.22';\nexport const OID_ENCOUNTERS_SECTION_ENTRIES_REQUIRED = '2.16.840.1.113883.10.20.22.2.22.1';\nexport const OID_ENCOUNTERS_SECTION_ENTRIES_REQUIRED_V2 = '2.16.840.1.113883.10.20.22.2.22.1.2';\nexport const OID_ENCOUNTERS_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.22.2';\nexport const OID_MEDICAL_EQUIPMENT_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.23';\nexport const OID_MEDICAL_EQUIPMENT_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.23.2';\nexport const OID_HOSPITAL_DISCHARGE_DIAGNOSIS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.24';\nexport const OID_HOSPITAL_DISCHARGE_DIAGNOSIS_SECTION_ENTRIES_OPTIONAL_V2 = '2.16.840.1.113883.10.20.22.2.24.2';\nexport const OID_ANESTHESIA_SECTION = '2.16.840.1.113883.10.20.22.2.25';\nexport const OID_ANESTHESIA_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.25.2';\nexport const OID_SURGERY_DESCRIPTION_SECTION = '2.16.840.1.113883.10.20.22.2.26';\nexport const OID_PROCEDURE_DESCRIPTION_SECTION = '2.16.840.1.113883.10.20.22.2.27';\nexport const OID_PROCEDURE_FINDINGS_SECTION = '2.16.840.1.113883.10.20.22.2.28';\nexport const OID_PROCEDURE_FINDINGS_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.28.2';\nexport const OID_PROCEDURE_INDICATIONS_SECTION = '2.16.840.1.113883.10.20.22.2.29';\nexport const OID_PLANNED_PROCEDURE_SECTION = '2.16.840.1.113883.10.20.22.2.30';\nexport const OID_PLANNED_PROCEDURE_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.30.2';\nexport const OID_PROCEDURE_SPECIMENS_REMOVED_SECTION = '2.16.840.1.113883.10.20.22.2.31';\nexport const OID_COMPLICATIONS_OPNOTE_SECTION = '2.16.840.1.113883.10.20.22.2.32';\nexport const OID_IMPLANTS_SECTION = '2.16.840.1.113883.10.20.22.2.33';\nexport const OID_PREOPERATIVE_DIAGNOSIS_SECTION = '2.16.840.1.113883.10.20.22.2.34';\nexport const OID_PREOPERATIVE_DIAGNOSIS_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.34.2';\nexport const OID_POSTOPERATIVE_DIAGNOSIS_SECTION = '2.16.840.1.113883.10.20.22.2.35';\nexport const OID_POSTPROCEDURE_DIAGNOSIS_SECTION = '2.16.840.1.113883.10.20.22.2.36';\nexport const OID_POSTPROCEDURE_DIAGNOSIS_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.36.2';\nexport const OID_COMPLICATIONS_SECTION = '2.16.840.1.113883.10.20.22.2.37';\nexport const OID_COMPLICATIONS_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.37.2';\nexport const OID_MEDICATIONS_ADMINISTERED_SECTION = '2.16.840.1.113883.10.20.22.2.38';\nexport const OID_MEDICATIONS_ADMINISTERED_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.38.2';\nexport const OID_MEDICAL_GENERAL_HISTORY_SECTION = '2.16.840.1.113883.10.20.22.2.39';\nexport const OID_PROCEDURE_IMPLANTS_SECTION = '2.16.840.1.113883.10.20.22.2.40';\nexport const OID_HOSPITAL_DISCHARGE_INSTRUCTIONS_SECTION = '2.16.840.1.113883.10.20.22.2.41';\nexport const OID_HOSPITAL_CONSULTATIONS_SECTION = '2.16.840.1.113883.10.20.22.2.42';\nexport const OID_HOSPITAL_ADMISSION_DIAGNOSIS_SECTION = '2.16.840.1.113883.10.20.22.2.43';\nexport const OID_HOSPITAL_ADMISSION_DIAGNOSIS_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.43.2';\nexport const OID_HOSPITAL_ADMISSION_MEDICATIONS_SECTION_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.44';\nexport const OID_INSTRUCTIONS_SECTION = '2.16.840.1.113883.10.20.22.2.45';\nexport const OID_INSTRUCTION_SECTION_V2 = '2.16.840.1.113883.10.20.22.2.45.2';\nexport const OID_FUNCTIONAL_ASSESSMENT_SECTION = '2.16.840.1.113883.10.20.22.2.46';\nexport const OID_PROCEDURES_ASSESSMENT_SECTION = '2.16.840.1.113883.10.20.22.2.47';\nexport const OID_PLAN_OF_CARE_ASSESSMENT_SECTION = '2.16.840.1.113883.10.20.22.2.48';\nexport const OID_ASSESSMENT_PROBLEM_SECTION = '2.16.840.1.113883.10.20.22.2.49';\nexport const OID_ASSESSMENT_IMMUNIZATIONS_SECTION = '2.16.840.1.113883.10.20.22.2.50';\nexport const OID_ASSESSMENT_ENCOUNTERS_SECTION = '2.16.840.1.113883.10.20.22.2.51';\nexport const OID_ASSESSMENT_SOCIAL_HISTORY_SECTION = '2.16.840.1.113883.10.20.22.2.52';\nexport const OID_ASSESSMENT_VITAL_SIGNS_SECTION = '2.16.840.1.113883.10.20.22.2.53';\nexport const OID_ASSESSMENT_HOSPITAL_ADMISSION_DIAGNOSIS_SECTION = '2.16.840.1.113883.10.20.22.2.54';\nexport const OID_PATIENT_DATA_FRAMEWORK_SECTION_MASS_HIE = '2.16.840.1.113883.10.20.22.2.55';\nexport const OID_MENTAL_STATUS_SECTION = '2.16.840.1.113883.10.20.22.2.56';\nexport const OID_NUTRITION_SECTION = '2.16.840.1.113883.10.20.22.2.57';\nexport const OID_HEALTH_CONCERNS_SECTION = '2.16.840.1.113883.10.20.22.2.58';\nexport const OID_FREE_TO_USE = '2.16.840.1.113883.10.20.22.2.59';\nexport const OID_GOALS_SECTION = '2.16.840.1.113883.10.20.22.2.60';\nexport const OID_HEALTH_STATUS_EVALUATIONS_OUTCOMES_SECTION = '2.16.840.1.113883.10.20.22.2.61';\nexport const OID_PHYSICAL_FINDINGS_OF_SKIN_ENTRIES_OPTIONAL = '2.16.840.1.113883.10.20.22.2.62';\nexport const OID_COURSE_OF_CARE = '2.16.840.1.113883.10.20.22.2.64:2014-06-09';\nexport const OID_NOTES_SECTION = '2.16.840.1.113883.10.20.22.2.65';\nexport const OID_NUTRITION_ASSESSMENT_SECTION = '2.16.840.1.113883.10.20.22.2.66';\nexport const OID_HEALTH_CONCERNS_SECTION_NUTRITION = '2.16.840.1.113883.10.20.22.2.67';\nexport const OID_ASSESSMENTS_EVALUATIONS_AND_OUTCOMES_SECTION_NUTRITION = '2.16.840.1.113883.10.20.22.2.68';\nexport const OID_INTERVENTIONS_SECTION_NUTRITION = '2.16.840.1.113883.10.20.22.2.69';\nexport const OID_PREGNANCY_SECTION = '2.16.840.1.113883.10.20.22.2.80';\nexport const OID_INFECTIOUS_DISEASES_SECTION_ID = '2.16.840.1.113883.10.20.22.2.400';\nexport const OID_CARE_TEAMS_SECTION = '2.16.840.1.113883.10.20.22.2.500';\nexport const OID_RESULT_ORGANIZER = '2.16.840.1.113883.10.20.22.4.1';\nexport const OID_RESULT_ORGANIZER_V2 = '2.16.840.1.113883.10.20.22.4.1.2';\nexport const OID_RESULT_OBSERVATION = '2.16.840.1.113883.10.20.22.4.2';\nexport const OID_RESULT_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.2.2';\nexport const OID_PROBLEM_ACT = '2.16.840.1.113883.10.20.22.4.3';\nexport const OID_PROBLEM_CONCERN_ACT_CONDITION_V2 = '2.16.840.1.113883.10.20.22.4.3.2';\nexport const OID_PROBLEM_OBSERVATION = '2.16.840.1.113883.10.20.22.4.4';\nexport const OID_PROBLEM_OBSSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.4.2';\nexport const OID_HEALTH_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.5';\nexport const OID_HEALTH_STATUS_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.5.2';\nexport const OID_PROBLEM_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.6';\nexport const OID_PROBLEM_STATUS_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.6.2';\nexport const OID_ALLERGY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.7';\nexport const OID_ALLERGY_INTOLERANCE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.7.2';\nexport const OID_SEVERITY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.8';\nexport const OID_SEVERITY_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.8.2';\nexport const OID_REACTION_OBSERVATION = '2.16.840.1.113883.10.20.22.4.9';\nexport const OID_REACTION_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.9.2';\nexport const OID_PROCEDURE_ACTIVITY = '2.16.840.1.113883.10.20.22.4.11';\nexport const OID_PROCEDURE_ACTIVITY_ACT = '2.16.840.1.113883.10.20.22.4.12';\nexport const OID_PROCEDURE_ACTIVITY_ACT_V2 = '2.16.840.1.113883.10.20.22.4.12.2';\nexport const OID_PROCEDURE_ACTIVITY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.13';\nexport const OID_PROCEDURE_ACTIVITY_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.13.2';\nexport const OID_PROCEDURE_ACTIVITY_PROCEDURE = '2.16.840.1.113883.10.20.22.4.14';\nexport const OID_PROCEDURE_ACTIVITY_PROCEDURE_V2 = '2.16.840.1.113883.10.20.22.4.14.2';\nexport const OID_IMMUNIZATION_SUBSTANCE_ADMINISTRATION = '2.16.840.1.113883.10.20.22.4.15';\nexport const OID_MEDICATION_ACTIVITY = '2.16.840.1.113883.10.20.22.4.16';\nexport const OID_MEDICATION_ACTIVITY_V2 = '2.16.840.1.113883.10.20.22.4.16.2';\nexport const OID_MEDICATION_SUPPLY_ORDER = '2.16.840.1.113883.10.20.22.4.17';\nexport const OID_MEDICATION_SUPPLY_ORDER_V2 = '2.16.840.1.113883.10.20.22.4.17.2';\nexport const OID_MEDICATION_DISPENSE = '2.16.840.1.113883.10.20.22.4.18';\nexport const OID_MEDICATION_DISPENSE_V2 = '2.16.840.1.113883.10.20.22.4.18.2';\nexport const OID_INDICATION = '2.16.840.1.113883.10.20.22.4.19';\nexport const OID_INSTRUCTIONS = '2.16.840.1.113883.10.20.22.4.20';\nexport const OID_INSTRUCTION_V2 = '2.16.840.1.113883.10.20.22.4.20.2';\nexport const OID_MEDICATION_SERIES_NUMBER = '2.16.840.1.113883.10.20.22.4.21';\nexport const OID_SEQUENCE_NUMBER = '2.16.840.1.113883.10.20.22.4.22';\nexport const OID_MEDICATION_INFORMATION_MANUFACTURED_MATERIAL = '2.16.840.1.113883.10.20.22.4.23';\nexport const OID_MEDICATION_INFORMATION_MANUFACTURED_MATERIAL_V2 = '2.16.840.1.113883.10.20.22.4.23.2';\nexport const OID_DRUG_VEHICLE_PARTICIPANT = '2.16.840.1.113883.10.20.22.4.24';\nexport const OID_PRECONDITION_CRITERION = '2.16.840.1.113883.10.20.22.4.25';\nexport const OID_VITAL_SIGNS_ORGANIZER = '2.16.840.1.113883.10.20.22.4.26';\nexport const OID_VITAL_SIGNS_ORGANIZER_V2 = '2.16.840.1.113883.10.20.22.4.26.2';\nexport const OID_VITAL_SIGNS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.27';\nexport const OID_VITAL_SIGNS_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.27.2';\nexport const OID_ALLERGY_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.28';\nexport const OID_ALLERGY_STATUS_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.28.2';\nexport const OID_ON_NO_MEDICATIONS = '2.16.840.1.113883.10.20.22.4.29';\nexport const OID_ALLERGY_PROBLEM_ACT = '2.16.840.1.113883.10.20.22.4.30';\nexport const OID_ALLERGY_PROBLEM_ACT_V2 = '2.16.840.1.113883.10.20.22.4.30.2';\nexport const OID_AGE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.31';\nexport const OID_ENCOUNTER_LOCATION = '2.16.840.1.113883.10.20.22.4.32';\nexport const OID_HOSPITAL_DISCHARGE_DIAGNOSIS = '2.16.840.1.113883.10.20.22.4.33';\nexport const OID_HOSPITAL_DISCHARGE_DIAGNOSIS_V2 = '2.16.840.1.113883.10.20.22.4.33.2';\nexport const OID_HOSPITAL_ADMISSION_DIAGNOSIS = '2.16.840.1.113883.10.20.22.4.34';\nexport const OID_HOSPITAL_ADMISSION_DIAGNOSIS_V2 = '2.16.840.1.113883.10.20.22.4.34.2';\nexport const OID_DISCHARGE_MEDICATION = '2.16.840.1.113883.10.20.22.4.35';\nexport const OID_DISCHARGE_MEDICATION_V2 = '2.16.840.1.113883.10.20.22.4.35.2';\nexport const OID_ADMISSION_MEDICATION = '2.16.840.1.113883.10.20.22.4.36';\nexport const OID_ADMISSION_MEDICATION_V2 = '2.16.840.1.113883.10.20.22.4.36.2';\nexport const OID_PRODUCT_INSTANCE = '2.16.840.1.113883.10.20.22.4.37';\nexport const OID_SOCIAL_HISTORY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.38';\nexport const OID_SOCIAL_HISTORY_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.38.1';\nexport const OID_PLAN_OF_CARE_ACTIVITY_ACT = '2.16.840.1.113883.10.20.22.4.39';\nexport const OID_PLANNED_ACT_V2 = '2.16.840.1.113883.10.20.22.4.39.2';\nexport const OID_PLAN_OF_CARE_ACTIVITY_ENCOUNTER = '2.16.840.1.113883.10.20.22.4.40';\nexport const OID_PLANNED_ENCOUNTER_V2 = '2.16.840.1.113883.10.20.22.4.40.2';\nexport const OID_PLAN_OF_CARE_ACTIVITY_PROCEDURE = '2.16.840.1.113883.10.20.22.4.41';\nexport const OID_PLANNED_PROCEDURE_V2 = '2.16.840.1.113883.10.20.22.4.41.2';\nexport const OID_PLAN_OF_CARE_ACTIVITY_SUBSTANCE_ADMINSTRATION = '2.16.840.1.113883.10.20.22.4.42';\nexport const OID_PLANNED_MEDICATION_ACTIVITY_V2 = '2.16.840.1.113883.10.20.22.4.42.2';\nexport const OID_PLAN_OF_CARE_ACTIVITY_SUPPLY = '2.16.840.1.113883.10.20.22.4.43';\nexport const OID_PLANNED_SUPPLY_V2 = '2.16.840.1.113883.10.20.22.4.43.2';\nexport const OID_PLAN_OF_CARE_ACTIVITY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.44';\nexport const OID_PLANNED_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.44.2';\nexport const OID_FAMILY_HISTORY_ORGANIZER = '2.16.840.1.113883.10.20.22.4.45';\nexport const OID_FAMILY_HISTORY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.46';\nexport const OID_FAMILY_HISTORY_DEATH_OBSERVATION = '2.16.840.1.113883.10.20.22.4.47';\nexport const OID_ADVANCE_DIRECTIVE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.48';\nexport const OID_ADVANCE_DIRECTIVE_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.48.2';\nexport const OID_ENCOUNTER_ACTIVITIES = '2.16.840.1.113883.10.20.22.4.49';\nexport const OID_ENCOUNTER_ACTIVITIES_V2 = '2.16.840.1.113883.10.20.22.4.49.2';\nexport const OID_NON_MEDICINAL_SUPPLY_ACTIVITY = '2.16.840.1.113883.10.20.22.4.50';\nexport const OID_NON_MEDICINAL_SUPPLY_ACTIVITY_V2 = '2.16.840.1.113883.10.20.22.4.50.2';\nexport const OID_POSTPROCEDURE_DIAGNOSIS = '2.16.840.1.113883.10.20.22.4.51';\nexport const OID_POSTPROCEDURE_DIAGNOSIS_V2 = '2.16.840.1.113883.10.20.22.4.51.2';\nexport const OID_IMMUNIZATION_ACTIVITY = '2.16.840.1.113883.10.20.22.4.52';\nexport const OID_IMMUNIZATION_ACTIVITY_V2 = '2.16.840.1.113883.10.20.22.4.52.2';\nexport const OID_IMMUNIZATION_REFUSAL_REASON = '2.16.840.1.113883.10.20.22.4.53';\nexport const OID_IMMUNIZATION_MEDICATION_INFORMATION = '2.16.840.1.113883.10.20.22.4.54';\nexport const OID_COVERAGE_ACTIVITY = '2.16.840.1.113883.10.20.22.4.60';\nexport const OID_COVERAGE_ACTIVITY_V2 = '2.16.840.1.113883.10.20.22.4.60.2';\nexport const OID_POLICY_ACTIVITY = '2.16.840.1.113883.10.20.22.4.61';\nexport const OID_POLICY_ACTIVITY_V2 = '2.16.840.1.113883.10.20.22.4.61.2';\nexport const OID_COMMENT_TEMPLATE = '2.16.840.1.113883.10.20.22.4.64';\nexport const OID_PREOPERATIVE_DIAGNOSIS = '2.16.840.1.113883.10.20.22.4.65';\nexport const OID_PREOPERATIVE_DIAGNOSIS_V2 = '2.16.840.1.113883.10.20.22.4.65.2';\nexport const OID_FUNCTIONAL_STATUS_RESULT_ORGANIZER = '2.16.840.1.113883.10.20.22.4.66';\nexport const OID_FUNCTIONAL_STATUS_EVALUATION_ORGANIZER = '2.16.840.1.113883.10.20.22.4.66.2';\nexport const OID_FUNCTIONAL_STATUS_RESULT_OBSERVATION = '2.16.840.1.113883.10.20.22.4.67';\nexport const OID_FUNCTIONAL_STATUS_EVALUATION_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.67.2';\nexport const OID_FUNCTIONAL_STATUS_PROBLEM_OBSERVATION = '2.16.840.1.113883.10.20.22.4.68';\nexport const OID_FUNCTIONAL_STATUS_PROBLEM_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.68.2';\nexport const OID_ASSESSMENT_SCALE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.69';\nexport const OID_PRESSURE_ULCER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.70';\nexport const OID_PRESSURE_ULCER_ON_A_PROBLEM_LIST = '2.16.840.1.113883.10.20.22.4.71';\nexport const OID_CAREGIVER_SUPPORT_AND_ABILITY = '2.16.840.1.113883.10.20.22.4.72';\nexport const OID_COGNITIVE_STATUS_PROBLEM_OBSERVATION = '2.16.840.1.113883.10.20.22.4.73';\nexport const OID_COGNITIVE_STATUS_PROBLEM_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.73.2';\nexport const OID_COGNITIVE_STATUS_RESULT_OBSERVATION = '2.16.840.1.113883.10.20.22.4.74';\nexport const OID_COGNITIVE_STATUS_RESULT_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.74.2';\nexport const OID_COGNITIVE_STATUS_RESULT_ORGANIZER = '2.16.840.1.113883.10.20.22.4.75';\nexport const OID_NUMBER_OF_PRESSURE_ULCERS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.76';\nexport const OID_NUMBER_OF_PRESSURE_ULCERS_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.76.2';\nexport const OID_HIGHEST_PRESSURE_ULCER_STAGE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.77';\nexport const OID_SMOKING_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.78';\nexport const OID_CURRENT_SMOKING_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.78.2';\nexport const OID_DECEASED_OBSERVATION = '2.16.840.1.113883.10.20.22.4.79';\nexport const OID_DECEASED_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.79.2';\nexport const OID_ENCOUNTER_DIAGNOSIS = '2.16.840.1.113883.10.20.22.4.80';\nexport const OID_ENCOUNTER_DIAGNOSIS_V2 = '2.16.840.1.113883.10.20.22.4.80.2';\nexport const OID_COGNITIVE_ASSESSMENT_ANSWER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.81';\nexport const OID_COGNITIVE_ASSESSMENT_OBSERVATION = '2.16.840.1.113883.10.20.22.4.82';\nexport const OID_FUNCTIONAL_ASSESSMENT_ANSWER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.83';\nexport const OID_FUNCTIONAL_ASSESSMENT_OBSERVATION = '2.16.840.1.113883.10.20.22.4.84';\nexport const OID_TOBACCO_USE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.85';\nexport const OID_TOBACCO_USE_OBSERVATION_V2 = '2.16.840.1.113883.10.20.22.4.85.2';\nexport const OID_ASSESSMENT_SCALE_SUPPORTING_OBSERVATION = '2.16.840.1.113883.10.20.22.4.86';\nexport const OID_PAYER_PERFORMER = '2.16.840.1.113883.10.20.22.4.87';\nexport const OID_GUARANTOR_PERFORMER = '2.16.840.1.113883.10.20.22.4.88';\nexport const OID_COVERED_PARTY_PARTICIPANT = '2.16.840.1.113883.10.20.22.4.89';\nexport const OID_POLICY_HOLDER_PARTICIPANT = '2.16.840.1.113883.10.20.22.4.90';\nexport const OID_FUNCTIONAL_ASSESSMENT_PROBLEM_OBSERVATION = '2.16.840.1.113883.10.20.22.4.91';\nexport const OID_PROCEDURE_ACT_PERFORMED = '2.16.840.1.113883.10.20.22.4.92';\nexport const OID_PROCEDURE_ACT_PERFORMED_V2 = '2.16.840.1.113883.10.20.22.4.92.2';\nexport const OID_PLAN_OF_CARE_ASSESSMENT_ACT = '2.16.840.1.113883.10.20.22.4.93';\nexport const OID_PROCEDURE_ACT_PERFORMED_TEMPORAL = '2.16.840.1.113883.10.20.22.4.94';\nexport const OID_ASSESSMENT_PROBLEM_OBSERVATION = '2.16.840.1.113883.10.20.22.4.95';\nexport const OID_PRESSURE_ULCER_SIZE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.96';\nexport const OID_PRESSURE_ULCER_INFORMATION_OBSERVATION = '2.16.840.1.113883.10.20.22.4.97';\nexport const OID_IMMUNIZATION_RECEIVED_ACTIVITY = '2.16.840.1.113883.10.20.22.4.98';\nexport const OID_IMMUNIZATION_NOT_RECEIVED_ACTIVITY = '2.16.840.1.113883.10.20.22.4.99';\nexport const OID_ASSESSMENT_ENCOUNTER_ACTIVITIES = '2.16.840.1.113883.10.20.22.4.100';\nexport const OID_ASSESSMENT_INDICATION = '2.16.840.1.113883.10.20.22.4.101';\nexport const OID_ASSESSMENT_NUMBER_OF_PRESSURE_ULCERS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.102';\nexport const OID_ASSESSMENT_PRESSURE_ULCER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.103';\nexport const OID_ASSESSMENT_TOBACCO_USE = '2.16.840.1.113883.10.20.22.4.104';\nexport const OID_ASSESSMENT_VITAL_SIGN_OBSERVATION = '2.16.840.1.113883.10.20.22.4.105';\nexport const OID_ASSESSMENT_VITAL_SIGNS_ORGANIZER = '2.16.840.1.113883.10.20.22.4.106';\nexport const OID_ASSESSMENT_HOSPITAL_ADMISSION_DIAGNOSIS = '2.16.840.1.113883.10.20.22.4.107';\nexport const OID_ADVANCE_DIRECTIVE_ORGANIZER = '2.16.840.1.113883.10.20.22.4.108';\nexport const OID_CHARACTERISTICS_OF_CARE_ENVIRONMENT = '2.16.840.1.113883.10.20.22.4.109';\nexport const OID_PROGRESS_TOWARD_GOAL_OBSERVATION = '2.16.840.1.113883.10.20.22.4.110';\nexport const OID_RELIGIOUS_AND_CULTURAL_OBSERVATION = '2.16.840.1.113883.10.20.22.4.111';\nexport const OID_CLINICAL_STATEMENT_REFERENCE = '2.16.840.1.113883.10.20.22.4.112';\nexport const OID_PROGNOSIS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.113';\nexport const OID_WOUND_OBSERVATION = '2.16.840.1.113883.10.20.22.4.114';\nexport const OID_EXTERNAL_DOCUMENT_REFERENCE = '2.16.840.1.113883.10.20.22.4.115';\nexport const OID_ALERTS_RISKS_PRECAUTIONS_ORGANIZER = '2.16.840.1.113883.10.20.22.4.116';\nexport const OID_ALERTS_RISKS_PRECAUTIONS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.117';\nexport const OID_SUBSTANCE_ADMINISTRATION_SERIES = '2.16.840.1.113883.10.20.22.4.118';\nexport const OID_AUTHOR_PARTICIPANT = '2.16.840.1.113883.10.20.22.4.119';\nexport const OID_PLANNED_IMMUNIZATION_ACTIVITY = '2.16.840.1.113883.10.20.22.4.120';\nexport const OID_GOAL_OBSERVATION = '2.16.840.1.113883.10.20.22.4.121';\nexport const OID_ACT_REFERENCE = '2.16.840.1.113883.10.20.22.4.122';\nexport const OID_DRUG_MONITORING = '2.16.840.1.113883.10.20.22.4.123';\nexport const OID_NUTRITIONAL_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.124';\nexport const OID_MENTAL_STATUS_PROBLEM_OBSERVATION = '2.16.840.1.113883.10.20.22.4.125';\nexport const OID_COGNITIVE_ABILITIES_OBSERVATION = '2.16.840.1.113883.10.20.22.4.126';\nexport const OID_SENSORY_AND_SPEECH_STATUS = '2.16.840.1.113883.10.20.22.4.127';\nexport const OID_SELF_CARE_ACTIVITIES_ADL_AND_IADL = '2.16.840.1.113883.10.20.22.4.128';\nexport const OID_PLANNED_COVERAGE = '2.16.840.1.113883.10.20.22.4.129';\nexport const OID_NUTRITION_RECOMMENDATIONS = '2.16.840.1.113883.10.20.22.4.130';\nexport const OID_NUTRITION_RECOMMENDATION_V2 = '2.16.840.1.113883.10.20.22.4.130';\nexport const OID_INTERVENTION_ACT = '2.16.840.1.113883.10.20.22.4.131';\nexport const OID_HEALTH_CONCERN_ACT = '2.16.840.1.113883.10.20.22.4.132';\nexport const OID_WOUND_MEASURMENTS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.133';\nexport const OID_WOUND_HEALING_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.134';\nexport const OID_MEDICAL_EQUIPMENT_ORGANIZER = '2.16.840.1.113883.10.20.22.4.135';\nexport const OID_RISK_CONCERN_ACT = '2.16.840.1.113883.10.20.22.4.136';\nexport const OID_DIET = '2.16.840.1.113883.10.20.22.4.138';\nexport const OID_PATIENT_REFERRAL_ACTIVITY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.140';\nexport const OID_HANDOFF_COMMUNICATION = '2.16.840.1.113883.10.20.22.4.141';\nexport const OID_PATIENT_PREFERENCE = '2.16.840.1.113883.10.20.22.4.142';\nexport const OID_PROVIDER_PREFERENCE = '2.16.840.1.113883.10.20.22.4.143';\nexport const OID_OUTCOME_OBSERVATION = '2.16.840.1.113883.10.20.22.4.144';\nexport const OID_CRITICALITY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.145';\nexport const OID_PLANNED_INTERVENTION_ACT = '2.16.840.1.113883.10.20.22.4.146';\nexport const OID_MEDICATION_FREE_TEXT_SIG = '2.16.840.1.113883.10.20.22.4.147';\nexport const OID_BIRTH_SEX = '2.16.840.1.113883.10.20.22.4.200';\nexport const OID_SECTION_TIME_RANGE = '2.16.840.1.113883.10.20.22.4.201';\nexport const OID_NOTE_ACTIVITY = '2.16.840.1.113883.10.20.22.4.202';\nexport const OID_ADVANCE_DIRECTIVES_INTERVENTION = '2.16.840.1.113883.10.20.22.4.204';\nexport const OID_COMPENSATION_AND_SECTOR_EMPLOYMENT_TYPE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.210';\nexport const OID_DAILY_WORK_HOURS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.211';\nexport const OID_EMPLOYMENT_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.212';\nexport const OID_HAZARDOUS_DUTY_WORK_OBSERVATION = '2.16.840.1.113883.10.20.22.4.213';\nexport const OID_JOB_DUTIES_OBSERVATION = '2.16.840.1.113883.10.20.22.4.214';\nexport const OID_OCCUPATIONAL_EXPOSURES_OBSERVATION = '2.16.840.1.113883.10.20.22.4.215';\nexport const OID_PAST_OR_PRESENT_INDUSTRY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.216';\nexport const OID_PAST_OR_PRESENT_OCCUPATION_OBSERVATION = '2.16.840.1.113883.10.20.22.4.217';\nexport const OID_RETIREMENT_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.218';\nexport const OID_USUAL_INDUSTRY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.219';\nexport const OID_USUAL_OCCUPATION_DURATION = '2.16.840.1.113883.10.20.22.4.220';\nexport const OID_USUAL_OCCUPATION_OBSERVATION = '2.16.840.1.113883.10.20.22.4.221';\nexport const OID_WEEKLY_WORK_DAYS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.222';\nexport const OID_WORK_SCHEDULE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.223';\nexport const OID_SUPERVISORY_LEVEL_OBSERVATION = '2.16.840.1.113883.10.20.22.4.224';\nexport const OID_FOOD_NUTRITION_RELATED_HISTORY_ORGANIZER_NUTRITION = '2.16.840.1.113883.10.20.22.4.231';\nexport const OID_ANTHROPOMETRIC_MEASUREMENTS_ORGANIZER_NUTRITION = '2.16.840.1.113883.10.20.22.4.232';\nexport const OID_BIOCHEMICAL_DATA_MEDICAL_TESTS_AND_PROCEDURES_ORGANIZER_NUTRITION = '2.16.840.1.113883.10.20.22.4.233';\nexport const OID_CLIENT_HISTORY_ORGANIZER_NUTRITION = '2.16.840.1.113883.10.20.22.4.234';\nexport const OID_NUTRITION_FOCUSED_PHYSICAL_FINDINGS_ORGANIZER_NUTRITION = '2.16.840.1.113883.10.20.22.4.235';\nexport const OID_NUTRITION_DIAGNOSIS_NUTRITION = '2.16.840.1.113883.10.20.22.4.237';\nexport const OID_ASSESSMENT_AND_EVALUATION_ORGANIZER = '2.16.840.1.113883.10.20.22.4.238';\nexport const OID_HEALTH_CONCERN_ACT_NUTRITION = '2.16.840.1.113883.10.20.22.4.239';\nexport const OID_DIAGNOSIS_ETIOLOGY_NUTRITION = '2.16.840.1.113883.10.20.22.4.240';\nexport const OID_PLANNED_INTERVENTION_ACT_NUTRITION = '2.16.840.1.113883.10.20.22.4.243';\nexport const OID_DIET_STATEMENT_NUTRITION = '2.16.840.1.113883.10.20.22.4.244';\nexport const OID_INTERVENTION_ACT_NUTRITION = '2.16.840.1.113883.10.20.22.4.247';\nexport const OID_RECOMMENDATION_PERFORMED_NUTRITION = '2.16.840.1.113883.10.20.22.4.248';\nexport const OID_ORDER_NUTRITION = '2.16.840.1.113883.10.20.22.4.249';\nexport const OID_MONITORING_EVALUATION_AND_OUTCOME_NUTRITION = '2.16.840.1.113883.10.20.22.4.250';\nexport const OID_ORDER_ITEM_NUTRITION = '2.16.840.1.113883.10.20.22.4.254';\nexport const OID_ENTERAL_ORDER_ADMINISTRATION_NUTRITION = '2.16.840.1.113883.10.20.22.4.255';\nexport const OID_ORDER_SUPPLIED_NUTRITION = '2.16.840.1.113883.10.20.22.4.256';\nexport const OID_ORDER_ITEM_SUPPLIED_NUTRITION = '2.16.840.1.113883.10.20.22.4.257';\nexport const OID_ENTERAL_ORDER_ADMINISTRATION_PERFORMED_NUTRITION = '2.16.840.1.113883.10.20.22.4.258';\nexport const OID_DIET_ITEM_NUTRITION = '2.16.840.1.113883.10.20.22.4.259';\nexport const OID_ENTERAL_ORDER_ITEM_NUTRITION = '2.16.840.1.113883.10.20.22.4.260';\nexport const OID_ENTERAL_ORDER_ITEM_SUPPLIED_NUTRITION = '2.16.840.1.113883.10.20.22.4.261';\nexport const OID_RESTRICTED_FOOD_ITEM_NUTRITION = '2.16.840.1.113883.10.20.22.4.262';\nexport const OID_FEEDING_DEVICE_NUTRITION = '2.16.840.1.113883.10.20.22.4.263';\nexport const OID_GESTATIONAL_AGE_OF_PREGNANCY = '2.16.840.1.113883.10.20.22.4.280';\nexport const OID_PREGNANCY_INTENTION_IN_NEXT_YEAR = '2.16.840.1.113883.10.20.22.4.281';\nexport const OID_GRAVIDA_TOTAL_PREGNANCIES = '2.16.840.1.113883.10.20.22.4.282';\nexport const OID_PARITY_TOTAL_PREGNANCIES_DELIVERED_PAST_20_WEEKS = '2.16.840.1.113883.10.20.22.4.283';\nexport const OID_PREGNANCY_OUTCOME = '2.16.840.1.113883.10.20.22.4.284';\nexport const OID_POSTPARTUM_STATUS = '2.16.840.1.113883.10.20.22.4.285';\nexport const OID_PREGNANCY_PLURALITY = '2.16.840.1.113883.10.20.22.4.286';\nexport const OID_ABORTA_TOTAL_PREGNANCIES_DELIVERED_BEFORE_20_WEEKS = '2.16.840.1.113883.10.20.22.4.287';\nexport const OID_TERM_TOTAL_PREGNANCIES_DELIVERED_PAST_27_WEEKS = '2.16.840.1.113883.10.20.22.4.288';\nexport const OID_PRETERM_TOTAL_PREGNANCIES_DELIVERED_BETWEEN_20_AND_37_WEEKS = '2.16.840.1.113883.10.20.22.4.289';\nexport const OID_LIVING_CHILDREN_NUMBER_OF_LIVING_CHILDREN = '2.16.840.1.113883.10.20.22.4.290';\nexport const OID_NUMBER_COUNT_OF_OTHER_PREGNANCY_OUTCOME = '2.16.840.1.113883.10.20.22.4.291';\nexport const OID_PREGNANCY_SUMMARY_ORGANIZER = '2.16.840.1.113883.10.20.22.4.292';\nexport const OID_DETAILED_PREGNANCY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.293';\nexport const OID_DATE_OF_LAST_LIVE_BIRTH = '2.16.840.1.113883.10.20.22.4.294';\nexport const OID_DATE_OF_FIRST_PRENATAL_CARE_VISIT_FOR_THIS_PREGNANCY = '2.16.840.1.113883.10.20.22.4.295';\nexport const OID_TOTAL_NUMBER_OF_PRENATAL_CARE_VISITS_FOR_THIS_PREGNANCY = '2.16.840.1.113883.10.20.22.4.296';\nexport const OID_DETAILED_ESTIMATED_DATE_OF_DELIVERY = '2.16.840.1.113883.10.20.22.4.297';\nexport const OID_PREGNANCY_RELATED_FINDING = '2.16.840.1.113883.10.20.22.4.298';\nexport const OID_METHOD_OF_DELIVERY = '2.16.840.1.113883.10.20.22.4.299';\nexport const OID_D_RH_TYPE = '2.16.840.1.113883.10.20.22.4.300';\nexport const OID_AIDC_UDI_OBSERVATION = '2.16.840.1.113883.10.20.22.4.300';\nexport const OID_D_RH_SENSITIZED = '2.16.840.1.113883.10.20.22.4.301';\nexport const OID_BRAND_NAME_OBSERVATION = '2.16.840.1.113883.10.20.22.4.301';\nexport const OID_D_IMMUNE_GLOBULIN_RHIG_GIVEN = '2.16.840.1.113883.10.20.22.4.302';\nexport const OID_CATALOG_NUMBER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.302';\nexport const OID_D_IMMUNE_GLOBULIN_RHIG = '2.16.840.1.113883.10.20.22.4.303';\nexport const OID_DEVICE_COMPANY_NAME_OBSERVATION = '2.16.840.1.113883.10.20.22.4.303';\nexport const OID_DEVICE_IDENTIFIER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.304';\nexport const OID_DEVICE_STATUS_IMPLANTED_OBSERVATION = '2.16.840.1.113883.10.20.22.4.305';\nexport const OID_DEVICE_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.306';\nexport const OID_DEVICE_TYPE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.307';\nexport const OID_DISTINCT_IDENTIFICATION_CODE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.308';\nexport const OID_EXPIRATION_DATE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.309';\nexport const OID_IMPLANTABLE_DEVICE_INSTANCE = '2.16.840.1.113883.10.20.22.4.310';\nexport const OID_IMPLANTABLE_DEVICE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.311';\nexport const OID_MPLANTABLE_DEVICE_ORGANIZER = '2.16.840.1.113883.10.20.22.4.312';\nexport const OID_IMPLANTABLE_DEVICE_PROCEDURE = '2.16.840.1.113883.10.20.22.4.313';\nexport const OID_LATEX_SAFETY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.314';\nexport const OID_LOT_OR_BATCH_NUMBER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.315';\nexport const OID_MANUFACTURING_DATE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.316';\nexport const OID_MODEL_NUMBER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.317';\nexport const OID_MRI_SAFETY_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.318';\nexport const OID_SERIAL_NUMBER_OBSERVATION = '2.16.840.1.113883.10.20.22.4.319';\nexport const OID_UDI_ENTRY_TYPE = '2.16.840.1.113883.10.20.22.4.320';\nexport const OID_UDI_OBSERVATION = '2.16.840.1.113883.10.20.22.4.321';\nexport const OID_TRANSMISSION_BASED_PRECAUTIONS_ID = '2.16.840.1.113883.10.20.22.4.400';\nexport const OID_CURRENT_INFECTIOUS_DISEASE_DIAGNOSIS_ID = '2.16.840.1.113883.10.20.22.4.401';\nexport const OID_HEALTHCARE_ASSOCIATED_INFECTION_FLAG_ID = '2.16.840.1.113883.10.20.22.4.402';\nexport const OID_PAST_INFECTIOUS_DISEASE_DIAGNOSIS_ID = '2.16.840.1.113883.10.20.22.4.403';\nexport const OID_RULED_OUT_EXCLUDED_DIAGNOSIS_ID = '2.16.840.1.113883.10.20.22.4.404';\nexport const OID_CURRENT_TRANSMISSION_BASED_PRECAUTIONS_FLAG_ID = '2.16.840.1.113883.10.20.22.4.405';\nexport const OID_LABORATORY_BATTERY_ID = '2.16.840.1.113883.10.20.22.4.406';\nexport const OID_LABORATORY_OBSERVATION_ID = '2.16.840.1.113883.10.20.22.4.407';\nexport const OID_LABORATORY_OBSERVATION_CODED_ID = '2.16.840.1.113883.10.20.22.4.408';\nexport const OID_SPECIMEN_PARTICIPANT_ID = '2.16.840.1.113883.10.20.22.4.410';\nexport const OID_ISOLATE_PARTICIPANT_ID = '2.16.840.1.113883.10.20.22.4.411';\nexport const OID_RELEVANT_CLINICAL_INFORMATION_ORGANIZER_ID = '2.16.840.1.113883.10.20.22.4.412';\nexport const OID_RELEVANT_CLINICAL_INFORMATION_OBSERVATION_ID = '2.16.840.1.113883.10.20.22.4.413';\nexport const OID_RELATED_SUBJECT_ID = '2.16.840.1.113883.10.20.22.4.414';\nexport const OID_SPECIMEN_COLLECTION_PROCEDURE_ID = '2.16.840.1.113883.10.20.22.4.415';\nexport const OID_LABORATORY_RESULT_ORGANIZER_ID = '2.16.840.1.113883.10.20.22.4.416';\nexport const OID_SPECIMEN_OBSERVATION_ID = '2.16.840.1.113883.10.20.22.4.417';\nexport const OID_LABORATORY_RESULT_STATUS_ID = '2.16.840.1.113883.10.20.22.4.418';\nexport const OID_LABORATORY_OBSERVATION_RESULT_STATUS_ID = '2.16.840.1.113883.10.20.22.4.419';\nexport const OID_SPECIMEN_REJECT_REASON_OBSERVATION_ID = '2.16.840.1.113883.10.20.22.4.420';\nexport const OID_SPECIMEN_CONDITION_OBSERVATION_ID = '2.16.840.1.113883.10.20.22.4.421';\nexport const OID_LABORATORY_CULTURE_ORGANIZER_ID = '2.16.840.1.113883.10.20.22.4.422';\nexport const OID_LABORATORY_OBSERVATION_SUB_TYPE_ID = '2.16.840.1.113883.10.20.22.4.423';\nexport const OID_REFERENCE_RANGE_CRITERION_ID = '2.16.840.1.113883.10.20.22.4.424';\nexport const OID_INFECTIOUS_DISEASE_SIGN_SYMPTOM_ID = '2.16.840.1.113883.10.20.22.4.425';\nexport const OID_LABORATORY_OBSERVATION_NUMERIC_ID = '2.16.840.1.113883.10.20.22.4.426';\nexport const OID_LABORATORY_OBSERVATION_NON_NUMERIC_ID = '2.16.840.1.113883.10.20.22.4.427';\nexport const OID_CURRENT_ANTIMICROBIAL_TREATMENT_ID = '2.16.840.1.113883.10.20.22.4.428';\nexport const OID_PAST_ANTIMICROBIAL_TREATMENT_ID = '2.16.840.1.113883.10.20.22.4.429';\nexport const OID_LABORATORY_OBSERVATION_PATHOGEN_CARRIER_STATE_ID = '2.16.840.1.113883.10.20.22.4.430';\nexport const OID_LABORATORY_OBSERVATION_PATHOGEN_IDENTIFIED_ID = '2.16.840.1.113883.10.20.22.4.431';\nexport const OID_ANTIMICROBIAL_SUSCEPTIBILITY_BATTERY_ORGANIZER_ID = '2.16.840.1.113883.10.20.22.4.432';\nexport const OID_DIFFERENTIAL_DIAGNOSIS_ID = '2.16.840.1.113883.10.20.22.4.433';\nexport const OID_INFECTIOUS_DISEASE_CARRIER_ID = '2.16.840.1.113883.10.20.22.4.434';\nexport const OID_EXTERNAL_REPORT_ID = '2.16.840.1.113883.10.20.22.4.435';\nexport const OID_DIAGNOSTIC_IMAGING_DOCUMENT_REFERENCE_ID = '2.16.840.1.113883.10.20.22.4.436';\nexport const OID_CARE_TEAM_ORGANIZER_ENTRY = '2.16.840.1.113883.10.20.22.4.500';\nexport const OID_SEXUAL_ORIENTATION_OBSERVATION = '2.16.840.1.113883.10.20.22.4.501';\nexport const OID_DATE_OF_DIAGNOSIS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.502';\nexport const OID_BASIC_OCCUPATION_OBSERVATION = '2.16.840.1.113883.10.20.22.4.503';\nexport const OID_BASIC_INDUSTRY_OBSERVATION = '2.16.840.1.113883.10.20.22.4.504';\nexport const OID_DISABILITY_STATUS_OBSERVATION = '2.16.840.1.113883.10.20.22.4.505';\nexport const OID_TRIBAL_AFFILIATION_OBSERVATION = '2.16.840.1.113883.10.20.22.4.506';\nexport const OID_SEX_OBSERVATION = '2.16.840.1.113883.10.20.22.4.507';\nexport const OID_MEDICATION_ADHERENCE = '2.16.840.1.113883.10.20.22.4.508';\nexport const OID_CARE_EXPERIENCE_PREFERENCE = '2.16.840.1.113883.10.20.22.4.509';\nexport const OID_TREATMENT_INTERVENTION_PREFERENCE = '2.16.840.1.113883.10.20.22.4.510';\nexport const OID_SMOKING_STATUS = '2.16.840.1.113883.10.20.22.4.511';\nexport const OID_AVERAGE_BLOOD_PRESSURE_ORGANIZER = '2.16.840.1.113883.10.20.22.4.512';\nexport const OID_ADVANCE_DIRECTIVE_EXISTENCE_OBSERVATION = '2.16.840.1.113883.10.20.22.4.513';\nexport const OID_INTERPRETER_NEEDED_OBSERVATION = '2.16.840.1.113883.10.20.22.4.514';\nexport const OID_INSTRUCTION_OBSERVATION = '2.16.840.1.113883.10.20.22.4.515';\nexport const OID_US_REALM_HEADER_PATIENT_NAME_PTN_US_FIELDED = '2.16.840.1.113883.10.20.22.5.1';\nexport const OID_US_REALM_HEADER_PERSON_NAME_PN_US_FIELDED = '2.16.840.1.113883.10.20.22.5.1.1';\nexport const OID_US_REALM_HEADER_ADDRESS = '2.16.840.1.113883.10.20.22.5.2';\nexport const OID_US_REALM_DATE_AND_TIME_DT_US_FIELDED = '2.16.840.1.113883.10.20.22.5.3';\nexport const OID_US_REALM_DATE_AND_TIME_DTM_US_FIELDED = '2.16.840.1.113883.10.20.22.5.4';\nexport const OID_CLINICAL_STATEMENT_AUTHOR = '2.16.840.1.113883.10.20.22.5.5';\nexport const OID_PROVENANCE_AUTHOR_PARTICIPATION = '2.16.840.1.113883.10.20.22.5.6';\nexport const OID_PROVENANCE_ASSEMBLER_PARTICIPATION = '2.16.840.1.113883.10.20.22.5.7';\nexport const OID_RELATED_PERSON_RELATIONSHIP_AND_NAME_PARTICIPANT_PARTICIPATION = '2.16.840.1.113883.10.20.22.5.8';\nexport const OID_TRANSMISSION_BASED_PRECAUTION_TYPES_VALUE_SET = '2.16.840.1.113883.10.20.22.5.300';\nexport const OID_RELEVANT_CLINICAL_INFORMATION_TYPE_VALUE_SET = '2.16.840.1.113883.10.20.22.5.301';\nexport const OID_LABORATORY_OBSERVATION_SUB_TYPE_VALUE_SET = '2.16.840.1.113883.10.20.22.5.302';\nexport const OID_NATURE_OF_ABNORMAL_TESTING_VALUE_SET = '2.16.840.1.113883.10.20.22.5.303';\nexport const OID_ORGANISM_VALUE_SET = '2.16.840.1.113883.10.20.22.5.304';\nexport const OID_MICROBIOLOGY_AND_ANTIMICROBIAL_SUSCEPTIBILITY_TESTS_VALUE_SET = '2.16.840.1.113883.10.20.22.5.305';\nexport const OID_INFECTIOUS_DISEASES_VALUE_SET = '2.16.840.1.113883.10.20.22.5.306';\nexport const OID_US_CORE_CONDITION_CODES_VALUE_SET = '2.16.840.1.113883.10.20.22.5.307';\nexport const OID_US_CORE_PROCEDURE_CODES_VALUE_SET = '2.16.840.1.113883.10.20.22.5.308';\nexport const OID_SUBSTANCE_OR_DEVICE_ALLERGY_INTOLERANCE_OBSERVATION = '2.16.840.1.113883.10.20.24.3.90';\nexport const OID_SUBSTANCE_OR_DEVICE_ALLERGY_INTOLERANCE_OBSERVATION_V2 = '2.16.840.1.113883.10.20.24.3.90.2';\nexport const OID_VALUE_SET_ROOT_VALUE_SET = '2.16.840.1.113883.11.20.9';\nexport const OID_MOODCODEEVNINT_VALUE_SET = '2.16.840.1.113883.11.20.9.18';\nexport const OID_PROBLEMACT_STATUSCODE_VALUE_SET = '2.16.840.1.113883.11.20.9.19';\nexport const OID_TELECOM_USE_CONSOLIDATED_US_REALM_HEADER_VALUE_SET = '2.16.840.1.113883.11.20.9.20';\nexport const OID_AGEPQ_UCUM_VALUE_SET = '2.16.840.1.113883.11.20.9.21';\nexport const OID_PROCEDUREACT_STATUSCODE_VALUE_SET = '2.16.840.1.113883.11.20.9.22';\nexport const OID_PLANNED_MOODCODE_ACT_ENCOUNTER_PROCEDURE_VALUE_SET = '2.16.840.1.113883.11.20.9.23';\nexport const OID_PLANNED_MOODCODE_SUBSTANCEADMINISTRATION_SUPPLY_VALUE_SET = '2.16.840.1.113883.11.20.9.24';\nexport const OID_PLANNED_MOODCODE_OBSERVATION_VALUE_SET = '2.16.840.1.113883.11.20.9.25';\nexport const OID_ENTITYPERSONNAMEPARTQUALIFIER_VALUE_SET = '2.16.840.1.113883.11.20.9.26';\nexport const OID_PARTICIPANTASSOCIATEDENTITYFAMILYCLASSCODE_VALUE_SET = '2.16.840.1.113883.11.20.9.27';\nexport const OID_DICOMPURPOSEOFREFERENCE_VALUE_SET = '2.16.840.1.113883.11.20.9.28';\nexport const OID_DIRQUANTITYMEASUREMENTTYPECODES_VALUE_SET = '2.16.840.1.113883.11.20.9.29';\nexport const OID_DICOMQUANTITYMEASUREMENTTYPECODES_VALUE_SET = '2.16.840.1.113883.11.20.9.30';\nexport const OID_CONSULTDOCUMENTTYPE_VALUE_SET = '2.16.840.1.113883.11.20.9.31';\nexport const OID_DIRDOCUMENTTYPECODES_VALUE_SET = '2.16.840.1.113883.11.20.9.32';\nexport const OID_INDROLECLASSCODES_VALUE_SET = '2.16.840.1.113883.11.20.9.33';\nexport const OID_PATIENT_EDUCATION_VALUE_SET = '2.16.840.1.113883.11.20.9.34';\nexport const OID_PRESSURE_ULCER_STAGES_VALUE_SET = '2.16.840.1.113883.11.20.9.35';\nexport const OID_PRESSURE_POINT_VALUE_SET = '2.16.840.1.113883.11.20.9.36';\nexport const OID_BODY_TARGETSITE_QUALIFIERS_VALUE_SET = '2.16.840.1.113883.11.20.9.37';\nexport const OID_SMOKING_STATUS_VALUE_SET = '2.16.840.1.113883.11.20.9.38';\nexport const OID_CURRENT_SMOKING_STATUS_VALUE_SET = '2.16.840.1.113883.11.20.9.38.2';\nexport const OID_RESULTS_STATUS_VALUE_SET = '2.16.840.1.113883.11.20.9.39';\nexport const OID_DAYWKPQ_UCUM_VALUE_SET = '2.16.840.1.113883.11.20.9.40';\nexport const OID_TOBACCO_USE_VALUE_SET = '2.16.840.1.113883.11.20.9.41';\nexport const OID_MENTAL_STATUS_OBSERVATION_TYPE_VALUE_SET = '2.16.840.1.113883.11.20.9.43';\nexport const OID_MENTAL_AND_FUNCTIONAL_STATUS_OBSERVATION_RESPONSE_VALUE_SET_VALUE_SET = '2.16.840.1.113883.11.20.9.44';\nexport const OID_CONSCIOUSNESS_LEVEL_VALUE_SET_VALUE_SET = '2.16.840.1.113883.11.20.9.45';\nexport const OID_ABILITY_VALUE_SET_VALUE_SET = '2.16.840.1.113883.11.20.9.46';\nexport const OID_ADL_RESULT_TYPE_VALUE_SET = '2.16.840.1.113883.11.20.9.47';\nexport const OID_COGNITIVE_FUNCTIONAL_FINDING_VALUE_SET_VALUE_SET = '2.16.840.1.113883.11.20.9.48';\nexport const OID_CARE_ENVIRONMENT_CHARACTERISTICS_VALUE_SET = '2.16.840.1.113883.11.20.9.49';\nexport const OID_SENSORY_STATUS_PROBLEM_TYPE_VALUE_SET = '2.16.840.1.113883.11.20.9.50';\nexport const OID_ENCOUNTER_SNOMED_CT_CODES_VALUE_SET = '2.16.840.1.113883.11.20.9.52';\nexport const OID_HEALTH_CONCERN_TYPE_VALUE_SET = '2.16.840.1.113883.11.20.9.53';\nexport const OID_PLANNED_INTERVENTION_MOODCODE_VALUE_SET = '2.16.840.1.113883.11.20.9.54';\nexport const OID_GOAL_ACHIEVEMENT_VALUE_SET = '2.16.840.1.113883.11.20.9.55';\nexport const OID_REFERRAL_TYPE_VALUE_SET_VALUE_SET = '2.16.840.1.113883.11.20.9.56';\nexport const OID_PRIORITY_ORDER_VALUE_SET = '2.16.840.1.113883.11.20.9.57';\nexport const OID_WOUND_HEALING_STATUS_VALUE_SET_VALUE_SET = '2.16.840.1.113883.11.20.9.58';\nexport const OID_DIRSECTIONTYPECODES_VALUE_SET = '2.16.840.1.113883.11.20.9.59';\nexport const OID_PRIORITY_LEVEL_VALUE_SET = '2.16.840.1.113883.11.20.9.60';\nexport const OID_CARE_MODEL_VALUE_SET = '2.16.840.1.113883.11.20.9.61';\nexport const OID_RELATED_DOCUMENT_APPEND_REPLACE_VALUE_SET = '2.16.840.1.113883.11.20.9.62';\nexport const OID_ENOUNTERDISCHARGEDISPOSITION_VALUE_SET = '2.16.840.1.113883.11.20.9.63';\nexport const OID_PATIENT_LANGUAGE_VALUE_SET = '2.16.840.1.113883.11.20.9.64';\nexport const OID_PHYSICAL_EXAM_TYPE_VALUE_SET = '2.16.840.1.113883.11.20.9.65';\nexport const OID_PATIENT_REFERRAL_ACT_MOODCODE_VALUE_SET = '2.16.840.1.113883.11.20.9.66';\nexport const OID_PROBLEM_TYPE_LOINC_VALUE_SET = '2.16.840.1.113883.11.20.9.67';\nexport const OID_NOTE_TYPES_VALUE_SET = '2.16.840.1.113883.11.20.9.68;';\nexport const OID_ADVANCE_DIRECTIVES_INTERVENTION_VALUE_SET_VALUE_SET = '2.16.840.1.113883.11.20.9.69?';\nexport const OID_PREGNANCY_STATUS_DETERMINATION_METHOD_VALUE_SET = '2.16.840.1.113883.11.20.9.80';\nexport const OID_ESTIMATED_DATE_OF_DELIVERY_CODE_VALUE_SET = '2.16.840.1.113883.11.20.9.81';\nexport const OID_ESTIMATED_GESTATIONAL_AGE_CODE_VALUE_SET = '2.16.840.1.113883.11.20.9.82';\nexport const OID_OTHER_PREGNANCY_OUTCOME_VALUE_SET = '2.16.840.1.113883.11.20.9.84';\nexport const OID_PREGNANCY_STATUS_VALUE_SET = '2.16.840.1.113883.11.20.9.85';\nexport const OID_PREGNANCY_OUTCOME_VALUE_SET = '2.16.840.1.113883.11.20.9.86';\nexport const OID_POSTPARTUM_STATUS_VALUE_SET = '2.16.840.1.113883.11.20.9.87';\nexport const OID_PREGNANCY_RELATED_FINDINGS_VALUE_SET = '2.16.840.1.113883.11.20.9.88';\nexport const OID_D_RH_TYPE_VALUE_SET = '2.16.840.1.113883.11.20.9.89';\nexport const OID_D_RH_SENSITIZED_VALUE_SET = '2.16.840.1.113883.11.20.9.90';\nexport const OID_RHO_D_IMMUNE_GLOBULIN_VALUE_SET = '2.16.840.1.113883.11.20.9.91';\nexport const OID_CVX_CODE_SYSTEM = '2.16.840.1.113883.12.292';\n", "import { CPT, HTTP_HL7_ORG, HTTP_TERMINOLOGY_HL7_ORG, LOINC, NDC, RXNORM, SNOMED } from '@medplum/core';\nimport { CodeableConcept, MedicationRequest } from '@medplum/fhirtypes';\nimport {\n OID_ASSESSMENT_SCALE_OBSERVATION,\n OID_ASSESSMENT_SCALE_SUPPORTING_OBSERVATION,\n OID_AVERAGE_BLOOD_PRESSURE_ORGANIZER,\n OID_BASIC_INDUSTRY_OBSERVATION,\n OID_BASIC_OCCUPATION_OBSERVATION,\n OID_BIRTH_SEX,\n OID_COGNITIVE_STATUS_RESULT_OBSERVATION,\n OID_COGNITIVE_STATUS_RESULT_ORGANIZER,\n OID_CONFIDENTIALITY_VALUE_SET,\n OID_CPT_CODE_SYSTEM,\n OID_CURRENT_SMOKING_STATUS_OBSERVATION,\n OID_CVX_CODE_SYSTEM,\n OID_DIET_STATEMENT_NUTRITION,\n OID_FUNCTIONAL_STATUS_RESULT_OBSERVATION,\n OID_FUNCTIONAL_STATUS_RESULT_ORGANIZER,\n OID_LABORATORY_BATTERY_ID,\n OID_LABORATORY_OBSERVATION_ID,\n OID_LABORATORY_RESULT_ORGANIZER_ID,\n OID_LOINC_CODE_SYSTEM,\n OID_MDC_CODE_SYSTEM,\n OID_MEDICATION_ADHERENCE,\n OID_MONITORING_EVALUATION_AND_OUTCOME_NUTRITION,\n OID_NCI_THESAURUS_CODE_SYSTEM,\n OID_NDC_CODE_SYSTEM,\n OID_NDF_RT_CODE_SYSTEM,\n OID_NUCC_TAXONOMY_CODE_SYSTEM,\n OID_NUTRITION_RECOMMENDATION_V2,\n OID_PAN_CANADIAN_LOINC_OBSERVATION_CODE_SYSTEM,\n OID_PREGNANCY_OBSERVATION,\n OID_PRESSURE_ULCER_OBSERVATION,\n OID_PROBLEM_OBSERVATION,\n OID_PROBLEM_OBSSERVATION_V2,\n OID_PROCEDURE_ACTIVITY_OBSERVATION,\n OID_PROCEDURES_SECTION_ENTRIES_REQUIRED,\n OID_RESULT_OBSERVATION,\n OID_RESULT_OBSERVATION_V2,\n OID_RESULT_ORGANIZER,\n OID_RESULT_ORGANIZER_V2,\n OID_RXNORM_CODE_SYSTEM,\n OID_SECTION_TIME_RANGE,\n OID_SEX_OBSERVATION,\n OID_SEXUAL_ORIENTATION_OBSERVATION,\n OID_SMOKING_STATUS_OBSERVATION,\n OID_SNOMED_CT_CODE_SYSTEM,\n OID_SOCIAL_HISTORY_OBSERVATION,\n OID_SOCIAL_HISTORY_OBSERVATION_V2,\n OID_TOBACCO_USE_OBSERVATION,\n OID_TRIBAL_AFFILIATION_OBSERVATION,\n OID_UNII_CODE_SYSTEM,\n OID_US_DLN_CODE_SYSTEM,\n OID_US_NPI_CODE_SYSTEM,\n OID_US_SSN_CODE_SYSTEM,\n OID_VA_MED_RT_CODE_SYSTEM,\n OID_VITAL_SIGNS_OBSERVATION,\n OID_VITAL_SIGNS_OBSERVATION_V2,\n OID_VITAL_SIGNS_ORGANIZER,\n OID_VITAL_SIGNS_ORGANIZER_V2,\n OID_WOUND_MEASURMENTS_OBSERVATION,\n OID_WOUND_OBSERVATION,\n} from './oids';\nimport { CcdaCode, CcdaValue } from './types';\n\nexport interface EnumEntry<TFhirValue extends string = string, TCcdaValue extends string = string> {\n fhirValue: TFhirValue;\n ccdaValue: TCcdaValue;\n displayName: string;\n}\n\nexport class EnumMapper<TFhirValue extends string, TCcdaValue extends string> {\n readonly systemName: string;\n readonly ccdaSystemOid: string;\n readonly fhirSystemUrl: string;\n readonly entries: EnumEntry<TFhirValue, TCcdaValue>[];\n readonly ccdaToFhirMap: Record<TCcdaValue, EnumEntry<TFhirValue, TCcdaValue>>;\n readonly fhirToCcdaMap: Record<TFhirValue, EnumEntry<TFhirValue, TCcdaValue>>;\n\n constructor(\n systemName: string,\n ccdaSystemOid: string,\n fhirSystemUrl: string,\n entries: EnumEntry<TFhirValue, TCcdaValue>[]\n ) {\n this.systemName = systemName;\n this.ccdaSystemOid = ccdaSystemOid;\n this.fhirSystemUrl = fhirSystemUrl;\n this.entries = entries;\n this.ccdaToFhirMap = {} as Record<TCcdaValue, EnumEntry<TFhirValue, TCcdaValue>>;\n this.fhirToCcdaMap = {} as Record<TFhirValue, EnumEntry<TFhirValue, TCcdaValue>>;\n\n for (const entry of entries) {\n if (!this.ccdaToFhirMap[entry.ccdaValue]) {\n this.ccdaToFhirMap[entry.ccdaValue] = entry;\n }\n if (!this.fhirToCcdaMap[entry.fhirValue]) {\n this.fhirToCcdaMap[entry.fhirValue] = entry;\n }\n }\n }\n\n getEntryByFhir(fhir: TFhirValue): EnumEntry<TFhirValue, TCcdaValue> | undefined {\n return this.fhirToCcdaMap[fhir];\n }\n\n mapCcdaToFhir(ccda: TCcdaValue): TFhirValue | undefined {\n return this.ccdaToFhirMap[ccda]?.fhirValue;\n }\n\n mapCcdaToFhirWithDefault(ccda: TCcdaValue | undefined, defaultValue: TFhirValue): TFhirValue {\n if (!ccda) {\n return defaultValue;\n }\n return this.mapCcdaToFhir(ccda) ?? defaultValue;\n }\n\n mapFhirToCcdaWithDefault(fhir: TFhirValue | undefined, defaultValue: TCcdaValue): TCcdaValue {\n if (!fhir) {\n return defaultValue;\n }\n return this.mapFhirToCcda(fhir) ?? defaultValue;\n }\n\n mapCcdaToFhirCodeableConcept(ccda: TCcdaValue): CodeableConcept | undefined {\n const entry = this.ccdaToFhirMap[ccda];\n if (!entry) {\n return undefined;\n }\n return {\n coding: [{ system: this.fhirSystemUrl, code: entry.fhirValue, display: entry.displayName }],\n text: entry.displayName,\n };\n }\n\n mapFhirToCcda(fhir: TFhirValue | undefined): TCcdaValue | undefined {\n if (!fhir) {\n return undefined;\n }\n return this.fhirToCcdaMap[fhir]?.ccdaValue;\n }\n\n mapFhirToCcdaCode(fhir: TFhirValue | undefined): CcdaCode | undefined {\n if (!fhir) {\n return undefined;\n }\n const entry = this.fhirToCcdaMap[fhir];\n if (!entry) {\n return undefined;\n }\n return {\n '@_code': entry.ccdaValue,\n '@_displayName': entry.displayName,\n '@_codeSystem': this.ccdaSystemOid,\n '@_codeSystemName': this.systemName,\n };\n }\n}\n\n/**\n * Every non-HTTPS URL will be flagged by our security tools as a potential vulnerability.\n * We therefore use one constant to minimize the number of false positives.\n * All of these URLs are used as identifiers, not as web links.\n */\nexport const HTTP = 'http:';\n\n/*\n * FHIR Code Systems\n */\n\nexport const CLINICAL_CONDITION_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/condition-clinical`;\nexport const CONDITION_VERIFICATION_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/condition-verification`;\nexport const LANGUAGE_MODE_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/v3-LanguageAbilityMode`;\nexport const LANGUAGE_PROFICIENCY_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/v3-LanguageAbilityProficiency`;\nexport const RACE_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/v3-Race`;\nexport const CONDITION_CATEGORY_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/condition-category`;\nexport const CONDITION_VER_STATUS_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/condition-ver-status`;\nexport const ALLERGY_CLINICAL_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/allergyintolerance-clinical`;\nexport const ALLERGY_VERIFICATION_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/allergyintolerance-verification`;\nexport const ACT_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/v3-ActCode`;\nexport const PARTICIPATION_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/v3-ParticipationType`;\nexport const DIAGNOSIS_ROLE_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/diagnosis-role`;\nexport const CONFIDENTIALITY_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/v3-Confidentiality`;\nexport const OBSERVATION_CATEGORY_CODE_SYSTEM = `${HTTP_TERMINOLOGY_HL7_ORG}/CodeSystem/observation-category`;\n\n/*\n * FHIR Value Sets\n */\n\nexport const ADDRESS_USE_VALUE_SET = `${HTTP_HL7_ORG}/fhir/ValueSet/address-use`;\nexport const NAME_USE_VALUE_SET = `${HTTP_HL7_ORG}/fhir/ValueSet/name-use`;\nexport const ADMINISTRATIVE_GENDER_VALUE_SET = `${HTTP_HL7_ORG}/fhir/ValueSet/administrative-gender`;\nexport const CONTACT_ENTITY_USE_VALUE_SET = `${HTTP_HL7_ORG}/fhir/ValueSet/contactentity-use`;\nexport const MEDICATION_REQUEST_STATUS_VALUE_SET = `${HTTP_HL7_ORG}/fhir/ValueSet/medicationrequest-status`;\n\n/*\n * FHIR standard extensions\n */\n\nexport const LANGUAGE_MODE_URL = `${HTTP_HL7_ORG}/fhir/StructureDefinition/language-mode`;\nexport const LANGUAGE_PROFICIENCY_URL = `${HTTP_HL7_ORG}/fhir/StructureDefinition/language-proficiency`;\n\n/*\n * US-Core\n */\n\nexport const US_CORE_PATIENT_URL = `${HTTP_HL7_ORG}/fhir/us/core/StructureDefinition/us-core-patient`;\nexport const US_CORE_RACE_URL = `${HTTP_HL7_ORG}/fhir/us/core/StructureDefinition/us-core-race`;\nexport const US_CORE_ETHNICITY_URL = `${HTTP_HL7_ORG}/fhir/us/core/StructureDefinition/us-core-ethnicity`;\nexport const US_CORE_CONDITION_URL = `${HTTP_HL7_ORG}/fhir/us/core/StructureDefinition/us-core-condition`;\nexport const US_CORE_MEDICATION_REQUEST_URL = `${HTTP_HL7_ORG}/fhir/us/core/StructureDefinition/us-core-medicationrequest`;\n\n/*\n * External Terminologies:\n * https://terminology.hl7.org/external_terminologies.html\n */\n\nexport const CCDA_TEMPLATE_CODE_SYSTEM = `${HTTP_HL7_ORG}/cda/template`;\nexport const NCI_THESAURUS_URL = `${HTTP}//ncithesaurus-stage.nci.nih.gov`;\nexport const US_SSN_URL = `${HTTP_HL7_ORG}/fhir/sid/us-ssn`;\nexport const US_DRIVER_LICENSE_URL = `${HTTP_HL7_ORG}/fhir/sid/us-dln`;\nexport const US_NPI_URL = `${HTTP_HL7_ORG}/fhir/sid/us-npi`;\nexport const UNII_URL = `${HTTP}//fdasis.nlm.nih.gov`;\nexport const NUCC_TAXONOMY_URL = `${HTTP}//nucc.org/provider-taxonomy`;\nexport const VA_MEDRT_URL = `${HTTP}//va.gov/terminology/medrt`;\nexport const NDFRT_URL = `${HTTP}//hl7.org/fhir/ndfrt`;\nexport const CVX_URL = `${HTTP}//nucc.org/cvx`;\nexport const FHIR_CVX_URL = `${HTTP_HL7_ORG}/fhir/sid/cvx`;\nexport const XSI_URL = `${HTTP}//www.w3.org/2001/XMLSchema-instance`;\nexport const CCDA_NARRATIVE_REFERENCE_URL = 'https://medplum.com/fhir/StructureDefinition/ccda-narrative-reference';\n\nexport const SYSTEM_MAPPER = new EnumMapper<string, string>('System', '', '', [\n {\n ccdaValue: OID_PAN_CANADIAN_LOINC_OBSERVATION_CODE_SYSTEM,\n fhirValue: 'https://fhir.infoway-inforoute.ca/CodeSystem/pCLOCD',\n displayName: 'pan-Canadian LOINC Observation Code Database (pCLOCD)',\n },\n {\n ccdaValue: OID_NCI_THESAURUS_CODE_SYSTEM,\n fhirValue: NCI_THESAURUS_URL,\n displayName: 'NCI Thesaurus',\n },\n { ccdaValue: OID_US_SSN_CODE_SYSTEM, fhirValue: US_SSN_URL, displayName: 'SSN' },\n { ccdaValue: OID_US_DLN_CODE_SYSTEM, fhirValue: US_DRIVER_LICENSE_URL, displayName: 'DLN' },\n { ccdaValue: OID_US_NPI_CODE_SYSTEM, fhirValue: US_NPI_URL, displayName: 'NPI' },\n {\n ccdaValue: OID_UNII_CODE_SYSTEM,\n fhirValue: UNII_URL,\n displayName: 'Unique Ingredient Identifier (UNII)',\n },\n { ccdaValue: OID_LOINC_CODE_SYSTEM, fhirValue: LOINC, displayName: 'LOINC' },\n {\n ccdaValue: OID_CPT_CODE_SYSTEM,\n fhirValue: CPT,\n displayName: 'Current Procedural Terminology (CPT)',\n },\n {\n ccdaValue: OID_MDC_CODE_SYSTEM,\n fhirValue: 'urn:iso:std:iso:11073:10101',\n displayName: 'Medical Device Communications (MDC)',\n },\n {\n ccdaValue: OID_NDC_CODE_SYSTEM,\n fhirValue: NDC,\n displayName: 'National Drug Code (NDC)',\n },\n {\n ccdaValue: OID_RXNORM_CODE_SYSTEM,\n fhirValue: RXNORM,\n displayName: 'RxNorm',\n },\n { ccdaValue: OID_SNOMED_CT_CODE_SYSTEM, fhirValue: SNOMED, displayName: 'SNOMED CT' },\n {\n ccdaValue: OID_NUCC_TAXONOMY_CODE_SYSTEM,\n fhirValue: NUCC_TAXONOMY_URL,\n displayName: 'NUCC Health Care Provider Taxonomy',\n },\n {\n ccdaValue: OID_VA_MED_RT_CODE_SYSTEM,\n fhirValue: VA_MEDRT_URL,\n displayName: 'Medication Reference Terminology (MED-RT)',\n },\n {\n ccdaValue: OID_NDF_RT_CODE_SYSTEM,\n fhirValue: NDFRT_URL,\n displayName: 'National Drug File Reference Terminology (NDF-RT)',\n },\n { ccdaValue: OID_CVX_CODE_SYSTEM, fhirValue: CVX_URL, displayName: 'CVX' },\n\n // Alternate FHIR System:\n {\n ccdaValue: OID_CVX_CODE_SYSTEM,\n fhirValue: FHIR_CVX_URL,\n displayName: 'Vaccine Administered Code Set (CVX)',\n },\n]);\n\n/**\n * Map the C-CDA system to the FHIR system.\n * @param ccda - The C-CDA system to map.\n * @returns The FHIR system.\n */\nexport function mapCcdaSystemToFhir(ccda: string | undefined): string | undefined {\n if (!ccda) {\n return undefined;\n }\n return SYSTEM_MAPPER.mapCcdaToFhir(ccda) ?? `urn:oid:${ccda}`;\n}\n\n/**\n * Map the FHIR system to the C-CDA system.\n * @param system - The system to map.\n * @returns The C-CDA system.\n */\nexport function mapFhirSystemToCcda(system: string | undefined): string | undefined {\n if (!system) {\n return undefined;\n }\n if (system.startsWith('urn:oid:')) {\n return system.replace('urn:oid:', '');\n }\n return SYSTEM_MAPPER.mapFhirToCcda(system);\n}\n\n/**\n * Map the codeable concept to the C-CDA code.\n * @param codeableConcept - The codeable concept to map.\n * @returns The C-CDA code.\n */\nexport function mapCodeableConceptToCcdaCode(codeableConcept: CodeableConcept | undefined): CcdaCode | undefined {\n if (!codeableConcept) {\n return undefined;\n }\n\n const systemEntry = codeableConcept.coding?.[0]?.system\n ? SYSTEM_MAPPER.getEntryByFhir(codeableConcept.coding[0].system)\n : undefined;\n const system = systemEntry?.ccdaValue;\n const systemName = systemEntry?.displayName;\n\n return {\n '@_code': codeableConcept?.coding?.[0]?.code,\n '@_displayName': codeableConcept?.coding?.[0]?.display,\n '@_codeSystem': system,\n '@_codeSystemName': systemName,\n };\n}\n\n/**\n * Map the codeable concept to the C-CDA value.\n * @param codeableConcept - The codeable concept to map.\n * @returns The C-CDA value.\n */\nexport function mapCodeableConceptToCcdaValue(codeableConcept: CodeableConcept | undefined): CcdaValue | undefined {\n const code = mapCodeableConceptToCcdaCode(codeableConcept);\n if (!code) {\n return undefined;\n }\n return {\n '@_xsi:type': 'CD',\n ...code,\n };\n}\n\nexport const CONFIDENTIALITY_MAPPER = new EnumMapper(\n 'Confidentiality',\n OID_CONFIDENTIALITY_VALUE_SET,\n CONFIDENTIALITY_CODE_SYSTEM,\n [\n { ccdaValue: 'U', fhirValue: 'U', displayName: 'unrestricted' },\n { ccdaValue: 'L', fhirValue: 'L', displayName: 'low' },\n { ccdaValue: 'M', fhirValue: 'M', displayName: 'moderate' },\n { ccdaValue: 'N', fhirValue: 'N', displayName: 'normal' },\n { ccdaValue: 'R', fhirValue: 'R', displayName: 'restricted' },\n { ccdaValue: 'V', fhirValue: 'V', displayName: 'very restricted' },\n ]\n);\n\nexport const HUMAN_NAME_USE_MAPPER = new EnumMapper('HumanNameUse', '', NAME_USE_VALUE_SET, [\n { ccdaValue: 'C', fhirValue: 'usual', displayName: 'Common/Called by' },\n { ccdaValue: 'L', fhirValue: 'official', displayName: 'Legal' },\n { ccdaValue: 'TEMP', fhirValue: 'temp', displayName: 'Temporary' },\n { ccdaValue: 'N', fhirValue: 'nickname', displayName: 'Nickname' },\n { ccdaValue: 'ANON', fhirValue: 'anonymous', displayName: 'Anonymous' },\n { ccdaValue: 'M', fhirValue: 'maiden', displayName: 'Maiden' },\n { ccdaValue: 'M', fhirValue: 'old', displayName: 'Old' },\n]);\n\nexport const GENDER_MAPPER = new EnumMapper('Gender', '', ADMINISTRATIVE_GENDER_VALUE_SET, [\n { ccdaValue: 'F', fhirValue: 'female', displayName: 'Female' },\n { ccdaValue: 'M', fhirValue: 'male', displayName: 'Male' },\n { ccdaValue: 'UN', fhirValue: 'unknown', displayName: 'Unknown' },\n { ccdaValue: 'UN', fhirValue: 'other', displayName: 'Other' },\n]);\n\nexport const ADDRESS_USE_MAPPER = new EnumMapper('AddressUse', '', ADDRESS_USE_VALUE_SET, [\n { ccdaValue: 'HP', fhirValue: 'home', displayName: 'Home' },\n { ccdaValue: 'WP', fhirValue: 'work', displayName: 'Work' },\n]);\n\nexport const TELECOM_USE_MAPPER = new EnumMapper('TelecomUse', '', CONTACT_ENTITY_USE_VALUE_SET, [\n { ccdaValue: 'WP', fhirValue: 'work', displayName: 'Work' },\n { ccdaValue: 'HP', fhirValue: 'home', displayName: 'Home' },\n]);\n\nexport const ALLERGY_STATUS_MAPPER = new EnumMapper<string, string>(\n 'AllergyStatus',\n '',\n ALLERGY_VERIFICATION_CODE_SYSTEM,\n [\n { ccdaValue: 'unconfirmed', fhirValue: 'unconfirmed', displayName: 'Unconfirmed' },\n { ccdaValue: 'provisional', fhirValue: 'provisional', displayName: 'Provisional' },\n { ccdaValue: 'differential', fhirValue: 'differential', displayName: 'Differential' },\n { ccdaValue: 'confirmed', fhirValue: 'confirmed', displayName: 'Confirmed' },\n { ccdaValue: 'refuted', fhirValue: 'refuted', displayName: 'Refuted' },\n { ccdaValue: 'entered-in-error', fhirValue: 'entered-in-error', displayName: 'Entered in Error' },\n { ccdaValue: 'unknown', fhirValue: 'unknown', displayName: 'Unknown' },\n ]\n);\n\nexport const ALLERGY_SEVERITY_MAPPER = new EnumMapper<'mild' | 'moderate' | 'severe', string>(\n 'AllergySeverity',\n SNOMED,\n ALLERGY_CLINICAL_CODE_SYSTEM,\n [\n { ccdaValue: '255604002', fhirValue: 'mild', displayName: 'Mild' },\n { ccdaValue: '6736007', fhirValue: 'moderate', displayName: 'Moderate' },\n { ccdaValue: '24484000', fhirValue: 'severe', displayName: 'Severe' },\n ]\n);\n\nexport const PROBLEM_STATUS_MAPPER = new EnumMapper<string, string>(\n 'ProblemStatus',\n '',\n CONDITION_VER_STATUS_CODE_SYSTEM,\n [\n { ccdaValue: 'active', fhirValue: 'active', displayName: 'Active' },\n { ccdaValue: 'inactive', fhirValue: 'inactive', displayName: 'Inactive' },\n { ccdaValue: 'resolved', fhirValue: 'inactive', displayName: 'Resolved' },\n { ccdaValue: 'remission', fhirValue: 'inactive', displayName: 'In Remission' },\n { ccdaValue: 'relapse', fhirValue: 'active', displayName: 'Relapse' },\n { ccdaValue: 'resolved relapse', fhirValue: 'inactive', displayName: 'Resolved Relapse' },\n { ccdaValue: 'aborted', fhirValue: 'aborted', displayName: 'Aborted' },\n ]\n);\n\nexport const ENCOUNTER_STATUS_MAPPER = new EnumMapper('EncounterStatus', '', '', [\n { ccdaValue: 'active', fhirValue: 'in-progress', displayName: 'In Progress' },\n { ccdaValue: 'completed', fhirValue: 'finished', displayName: 'Finished' },\n { ccdaValue: 'aborted', fhirValue: 'cancelled', displayName: 'Cancelled' },\n { ccdaValue: 'cancelled', fhirValue: 'cancelled', displayName: 'Cancelled' },\n { ccdaValue: 'unknown', fhirValue: 'unknown', displayName: 'Unknown' },\n]);\n\nexport const PROCEDURE_STATUS_MAPPER = new EnumMapper('ProcedureStatus', '', '', [\n { ccdaValue: 'completed', fhirValue: 'completed', displayName: 'Completed' },\n { ccdaValue: 'aborted', fhirValue: 'stopped', displayName: 'Stopped' },\n { ccdaValue: 'cancelled', fhirValue: 'not-done', displayName: 'Not Done' },\n { ccdaValue: 'new', fhirValue: 'not-done', displayName: 'Draft' },\n { ccdaValue: 'unknown', fhirValue: 'unknown', displayName: 'Unknown' },\n]);\n\nexport const MEDICATION_STATUS_MAPPER = new EnumMapper<\n Required<MedicationRequest['status']>,\n 'active' | 'completed' | 'aborted' | 'cancelled'\n>('MedicationStatus', '', MEDICATION_REQUEST_STATUS_VALUE_SET, [\n { ccdaValue: 'active', fhirValue: 'active', displayName: 'Active' },\n { ccdaValue: 'completed', fhirValue: 'completed', displayName: 'Completed' },\n { ccdaValue: 'aborted', fhirValue: 'stopped', displayName: 'Stopped' },\n { ccdaValue: 'cancelled', fhirValue: 'cancelled', displayName: 'Cancelled' },\n { ccdaValue: 'aborted', fhirValue: 'entered-in-error', displayName: 'Entered in Error' },\n { ccdaValue: 'active', fhirValue: 'draft', displayName: 'Draft' },\n { ccdaValue: 'cancelled', fhirValue: 'unknown', displayName: 'Unknown' },\n]);\n\nexport const OBSERVATION_CATEGORY_MAPPER = new EnumMapper<string, string>(\n 'ObservationCategory',\n '',\n OBSERVATION_CATEGORY_CODE_SYSTEM,\n [\n // ## social-history\n // FHIR Definition: Social History Observations define the patient's occupational, personal (e.g., lifestyle), social, familial, and environmental history and health risk factors that may impact the patient's health.\n // Mapped C-CDA Templates:\n { ccdaValue: OID_SOCIAL_HISTORY_OBSERVATION, fhirValue: 'social-history', displayName: 'Social History' },\n { ccdaValue: OID_SOCIAL_HISTORY_OBSERVATION_V2, fhirValue: 'social-history', displayName: 'Social History V2' },\n { ccdaValue: OID_SMOKING_STATUS_OBSERVATION, fhirValue: 'social-history', displayName: 'Smoking Status' },\n {\n ccdaValue: OID_CURRENT_SMOKING_STATUS_OBSERVATION,\n fhirValue: 'social-history',\n displayName: 'Current Smoking Status',\n },\n { ccdaValue: OID_TOBACCO_USE_OBSERVATION, fhirValue: 'social-history', displayName: 'Tobacco Use' },\n { ccdaValue: OID_BASIC_OCCUPATION_OBSERVATION, fhirValue: 'social-history', displayName: 'Basic Occupation' },\n { ccdaValue: OID_BASIC_INDUSTRY_OBSERVATION, fhirValue: 'social-history', displayName: 'Basic Industry' },\n { ccdaValue: OID_SEXUAL_ORIENTATION_OBSERVATION, fhirValue: 'social-history', displayName: 'Sexual Orientation' },\n { ccdaValue: OID_SEX_OBSERVATION, fhirValue: 'social-history', displayName: 'Sex Observation' },\n { ccdaValue: OID_BIRTH_SEX, fhirValue: 'social-history', displayName: 'Gender Identity' },\n { ccdaValue: OID_SECTION_TIME_RANGE, fhirValue: 'social-history', displayName: 'Gender Identity V2' },\n { ccdaValue: OID_PREGNANCY_OBSERVATION, fhirValue: 'social-history', displayName: 'Pregnancy Status' },\n { ccdaValue: OID_TRIBAL_AFFILIATION_OBSERVATION, fhirValue: 'social-history', displayName: 'Tribal Affiliation' },\n\n // ## vital-signs\n // FHIR Definition: Clinical observations measure the body's basic functions such as blood pressure, heart rate, respiratory rate, height, weight, body mass index, head circumference, pulse oximetry, temperature, and body surface area.\n // Mapped C-CDA Templates:\n { ccdaValue: OID_VITAL_SIGNS_ORGANIZER, fhirValue: 'vital-signs', displayName: 'Vital Signs Organizer' },\n {\n ccdaValue: OID_VITAL_SIGNS_ORGANIZER_V2,\n fhirValue: 'vital-signs',\n displayName: 'Vital Signs Organizer V2',\n },\n { ccdaValue: OID_VITAL_SIGNS_OBSERVATION, fhirValue: 'vital-signs', displayName: 'Vital Signs Observation' },\n {\n ccdaValue: OID_VITAL_SIGNS_OBSERVATION_V2,\n fhirValue: 'vital-signs',\n displayName: 'Vital Signs Observation V2',\n },\n {\n ccdaValue: OID_AVERAGE_BLOOD_PRESSURE_ORGANIZER,\n fhirValue: 'vital-signs',\n displayName: 'Average Blood Pressure Organizer',\n },\n\n // ## laboratory\n // FHIR Definition: The results of observations generated by laboratories. Laboratory results are typically generated by laboratories providing analytic services in areas such as chemistry, hematology, serology, histology, cytology, anatomic pathology (including digital pathology), microbiology, and/or virology.\n // Mapped C-CDA Templates:\n { ccdaValue: OID_RESULT_ORGANIZER, fhirValue: 'laboratory', displayName: 'Result Organizer' },\n { ccdaValue: OID_RESULT_ORGANIZER_V2, fhirValue: 'laboratory', displayName: 'Result Organizer V2' },\n { ccdaValue: OID_RESULT_OBSERVATION, fhirValue: 'laboratory', displayName: 'Result Observation' },\n { ccdaValue: OID_RESULT_OBSERVATION_V2, fhirValue: 'laboratory', displayName: 'Result Observation V2' },\n { ccdaValue: OID_LABORATORY_BATTERY_ID, fhirValue: 'laboratory', displayName: 'Laboratory Battery (ID)' },\n {\n ccdaValue: OID_LABORATORY_OBSERVATION_ID,\n fhirValue: 'laboratory',\n displayName: 'Laboratory Observation (ID)',\n },\n {\n ccdaValue: OID_LABORATORY_RESULT_ORGANIZER_ID,\n fhirValue: 'laboratory',\n displayName: 'Laboratory Result Organizer (ID)',\n },\n\n // ## survey\n // FHIR Definition: Assessment tool/survey instrument observations (e.g., Apgar Scores, Montreal Cognitive Assessment (MoCA)).\n // Mapped C-CDA Templates:\n { ccdaValue: OID_ASSESSMENT_SCALE_OBSERVATION, fhirValue: 'survey', displayName: 'Assessment Scale Observation' },\n {\n ccdaValue: OID_ASSESSMENT_SCALE_SUPPORTING_OBSERVATION,\n fhirValue: 'survey',\n displayName: 'Assessment Scale Supporting Observation',\n },\n {\n ccdaValue: OID_COGNITIVE_STATUS_RESULT_OBSERVATION,\n fhirValue: 'survey',\n displayName: 'Cognitive Status Result Observation',\n },\n {\n ccdaValue: OID_COGNITIVE_STATUS_RESULT_ORGANIZER,\n fhirValue: 'survey',\n displayName: 'Cognitive Status Result Organizer',\n },\n {\n ccdaValue: OID_FUNCTIONAL_STATUS_RESULT_OBSERVATION,\n fhirValue: 'survey',\n displayName: 'Functional Status Result Observation',\n },\n {\n ccdaValue: OID_FUNCTIONAL_STATUS_RESULT_ORGANIZER,\n fhirValue: 'survey',\n displayName: 'Functional Status Result Organizer',\n },\n\n // ## exam\n // FHIR Definition: Observations generated by physical exam findings including direct observations made by a clinician and use of simple instruments and the result of simple maneuvers performed directly on the patient's body.\n // Mapped C-CDA Templates:\n { ccdaValue: OID_PROBLEM_OBSERVATION, fhirValue: 'exam', displayName: 'Problem Observation' },\n { ccdaValue: OID_PROBLEM_OBSSERVATION_V2, fhirValue: 'exam', displayName: 'Problem Observation V2' },\n { ccdaValue: OID_PRESSURE_ULCER_OBSERVATION, fhirValue: 'exam', displayName: 'Pressure Ulcer Observation' },\n { ccdaValue: OID_WOUND_OBSERVATION, fhirValue: 'exam', displayName: 'Wound Observation' },\n { ccdaValue: OID_WOUND_MEASURMENTS_OBSERVATION, fhirValue: 'exam', displayName: 'Wound Measurements Observation' },\n { ccdaValue: OID_PROCEDURES_SECTION_ENTRIES_REQUIRED, fhirValue: 'exam', displayName: 'Procedure Section' },\n { ccdaValue: OID_PROCEDURE_ACTIVITY_OBSERVATION, fhirValue: 'exam', displayName: 'Procedure Activity Observation' },\n\n // ## therapy\n // FHIR Definition: Observations generated by non-interventional treatment protocols (e.g. occupational, physical, radiation, nutritional and medication therapy)\n // Mapped C-CDA Templates:\n { ccdaValue: OID_MEDICATION_ADHERENCE, fhirValue: 'therapy', displayName: 'Medication Adherence' },\n { ccdaValue: OID_NUTRITION_RECOMMENDATION_V2, fhirValue: 'therapy', displayName: 'Nutrition Recommendations' },\n { ccdaValue: OID_DIET_STATEMENT_NUTRITION, fhirValue: 'therapy', displayName: 'Diet Statement (Nutrition)' },\n {\n ccdaValue: OID_MONITORING_EVALUATION_AND_OUTCOME_NUTRITION,\n fhirValue: 'therapy',\n displayName: 'Monitoring, Evaluation and Outcome (Nutrition)',\n },\n ]\n);\n", "import { XMLBuilder, XMLParser } from 'fast-xml-parser';\nimport { XSI_URL } from './systems';\nimport { Ccda } from './types';\n\nconst ARRAY_PATHS = [\n 'ClinicalDocument.recordTarget',\n\n // Always arrays\n '.id',\n '.templateId',\n '.name',\n '.addr',\n '.telecom',\n '.streetAddressLine',\n '.author',\n '.effectiveTime',\n\n // Name\n 'name.given',\n 'name.suffix',\n 'name.prefix',\n\n // Patient\n 'patient.raceCode',\n 'patient.ethnicGroupCode',\n 'patient.languageCommunication',\n\n // Document structure arrays\n 'component.structuredBody.component',\n 'component.structuredBody.component.section',\n 'component.section',\n 'component.observation',\n 'component.act',\n\n 'section.entry',\n\n 'entry.act',\n 'entry.organizer',\n 'entry.substanceAdministration',\n 'entry.observation',\n 'entry.encounter',\n 'entry.procedure',\n\n 'encounter.performer',\n 'encounter.participant',\n 'encounter.entryRelationship',\n\n 'entryRelationship.observation',\n 'entryRelationship.substanceAdministration',\n 'entryRelationship.act',\n\n 'organizer.component',\n\n 'substanceAdministration.consumable.manufacturedProduct',\n 'substanceAdministration.entryRelationship',\n 'substanceAdministration.performer',\n\n // Act paths\n 'act.entryRelationship',\n 'act.performer',\n\n // Observation paths\n 'observation.participant',\n 'observation.entryRelationship',\n 'observation.referenceRange',\n\n 'consumable.manufacturedProduct',\n\n 'manufacturedProduct.manufacturedMaterial',\n 'manufacturedProduct.manufacturerOrganization',\n 'manufacturedProduct.manufacturedLabeledDrug',\n\n 'manufacturedMaterial.code',\n 'manufacturedMaterial.lotNumberText',\n];\n\nexport function convertXmlToCcda(xml: string): Ccda {\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n parseAttributeValue: false,\n parseTagValue: false,\n isArray: (_tagName, jPath, _isLeafNode, _isAttribute) => ARRAY_PATHS.some((p) => jPath.endsWith(p)),\n });\n\n const parsedData = parser.parse(xml);\n return parsedData.ClinicalDocument;\n}\n\nexport function convertCcdaToXml(ccda: Ccda): string {\n const builder = new XMLBuilder({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n format: true,\n suppressBooleanAttributes: false,\n suppressEmptyNode: true,\n });\n return builder.build({\n '?xml': { '@_version': '1.0', '@_encoding': 'UTF-8' },\n '?xml-stylesheet': { '@_type': 'text/xsl', '@_href': 'CDA.xsl' },\n ClinicalDocument: {\n '@_xmlns': 'urn:hl7-org:v3',\n '@_xmlns:xsi': XSI_URL,\n '@_xmlns:voc': 'urn:hl7-org:v3/voc',\n '@_xmlns:sdtc': 'urn:hl7-org:sdtc',\n ...ccda,\n },\n });\n}\n\nexport function parseXml(xml: string): any {\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n parseAttributeValue: false,\n parseTagValue: false,\n });\n return parser.parse(xml);\n}\n\nexport function convertToCompactXml(obj: any): string {\n if (!obj) {\n return '';\n }\n if (typeof obj === 'string') {\n return obj;\n }\n const builder = new XMLBuilder({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n format: false,\n suppressBooleanAttributes: false,\n suppressEmptyNode: true,\n });\n const xml = builder.build(obj) as string;\n return xml\n .split('\\n')\n .map((line: string) => line.trim())\n .join('');\n}\n", "import { createReference, generateId, isUUID, LOINC, UCUM } from '@medplum/core';\nimport {\n Address,\n AllergyIntolerance,\n AllergyIntoleranceReaction,\n Bundle,\n CarePlan,\n CareTeam,\n CareTeamParticipant,\n CodeableConcept,\n Coding,\n Composition,\n CompositionEvent,\n CompositionSection,\n Condition,\n ContactPoint,\n Encounter,\n EncounterDiagnosis,\n Extension,\n Goal,\n HumanName,\n Identifier,\n Immunization,\n ImmunizationPerformer,\n Medication,\n Observation,\n ObservationReferenceRange,\n Organization,\n Patient,\n Period,\n Practitioner,\n PractitionerQualification,\n PractitionerRole,\n Procedure,\n Reference,\n Resource,\n} from '@medplum/fhirtypes';\nimport { mapCcdaToFhirDate, mapCcdaToFhirDateTime } from './datetime';\nimport {\n OID_ALLERGIES_SECTION_ENTRIES_REQUIRED,\n OID_CARE_TEAMS_SECTION,\n OID_GOAL_OBSERVATION,\n OID_GOALS_SECTION,\n OID_HEALTH_CONCERNS_SECTION,\n OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL,\n OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED,\n OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED,\n OID_NOTES_SECTION,\n OID_PAYERS_SECTION,\n OID_PLAN_OF_CARE_SECTION,\n OID_PROBLEMS_SECTION_ENTRIES_REQUIRED,\n OID_PROCEDURES_SECTION_ENTRIES_REQUIRED,\n OID_REASON_FOR_REFERRAL,\n} from './oids';\nimport {\n ACT_CODE_SYSTEM,\n ADDRESS_USE_MAPPER,\n ALLERGY_CLINICAL_CODE_SYSTEM,\n ALLERGY_SEVERITY_MAPPER,\n ALLERGY_VERIFICATION_CODE_SYSTEM,\n CCDA_NARRATIVE_REFERENCE_URL,\n CCDA_TEMPLATE_CODE_SYSTEM,\n CLINICAL_CONDITION_CODE_SYSTEM,\n CONDITION_CATEGORY_CODE_SYSTEM,\n CONDITION_VER_STATUS_CODE_SYSTEM,\n CONDITION_VERIFICATION_CODE_SYSTEM,\n DIAGNOSIS_ROLE_CODE_SYSTEM,\n ENCOUNTER_STATUS_MAPPER,\n HUMAN_NAME_USE_MAPPER,\n mapCcdaSystemToFhir,\n MEDICATION_STATUS_MAPPER,\n OBSERVATION_CATEGORY_MAPPER,\n PARTICIPATION_CODE_SYSTEM,\n PROCEDURE_STATUS_MAPPER,\n TELECOM_USE_MAPPER,\n US_CORE_CONDITION_URL,\n US_CORE_ETHNICITY_URL,\n US_CORE_MEDICATION_REQUEST_URL,\n US_CORE_RACE_URL,\n} from './systems';\nimport {\n Ccda,\n CcdaAct,\n CcdaAddr,\n CcdaAssignedEntity,\n CcdaAuthor,\n CcdaCode,\n CcdaCustodian,\n CcdaDocumentationOf,\n CcdaEffectiveTime,\n CcdaEncounter,\n CcdaEntry,\n CcdaId,\n CcdaName,\n CcdaObservation,\n CcdaOrganizer,\n CcdaOrganizerComponent,\n CcdaPatientRole,\n CcdaPerformer,\n CcdaProcedure,\n CcdaReferenceRange,\n CcdaSection,\n CcdaSubstanceAdministration,\n CcdaTelecom,\n CcdaTemplateId,\n CcdaText,\n} from './types';\nimport { convertToCompactXml } from './xml';\n\n/**\n * Converts C-CDA documents to FHIR resources\n * Following Medplum TypeScript rules:\n * - Generates new FHIR resource IDs\n * - Preserves original C-CDA IDs as identifiers\n * - Adds proper metadata and timestamps\n *\n * @param ccda - The C-CDA document to convert\n * @returns The converted FHIR resources\n */\nexport function convertCcdaToFhir(ccda: Ccda): Bundle {\n return new CcdaToFhirConverter(ccda).convert();\n}\n\nclass CcdaToFhirConverter {\n private readonly ccda: Ccda;\n private readonly resources: Resource[] = [];\n private patient?: Patient;\n\n constructor(ccda: Ccda) {\n this.ccda = ccda;\n }\n\n convert(): Bundle {\n this.processHeader();\n const composition = this.createComposition();\n\n return {\n resourceType: 'Bundle',\n type: 'document',\n entry: [\n { resource: composition },\n ...(this.patient ? [{ resource: this.patient }] : []),\n ...this.resources.map((resource) => ({ resource })),\n ],\n };\n }\n\n private processHeader(): void {\n const patientRole = this.ccda.recordTarget?.[0]?.patientRole;\n if (patientRole) {\n this.patient = this.createPatient(patientRole);\n }\n }\n\n private createPatient(patientRole: CcdaPatientRole): Patient {\n const patient = patientRole.patient;\n const extensions: Extension[] = [];\n\n if (patient.raceCode && patient.raceCode.length > 0) {\n extensions.push({\n url: US_CORE_RACE_URL,\n extension: patient.raceCode.map((raceCode) => ({\n url: 'ombCategory',\n valueCoding: this.mapCodeToCoding(raceCode),\n })),\n });\n }\n\n if (patient.ethnicGroupCode && patient.ethnicGroupCode.length > 0) {\n extensions.push({\n url: US_CORE_ETHNICITY_URL,\n extension: patient.ethnicGroupCode.map((ethnicGroupCode) => ({\n url: 'ombCategory',\n valueCoding: this.mapCodeToCoding(ethnicGroupCode),\n })),\n });\n }\n\n return {\n resourceType: 'Patient',\n id: this.mapId(patientRole.id),\n identifier: this.mapIdentifiers(patientRole.id),\n name: this.mapCcdaNameArrayFhirHumanNameArray(patient.name),\n gender: this.mapGenderCode(patient.administrativeGenderCode?.['@_code']),\n birthDate: mapCcdaToFhirDate(patient.birthTime?.['@_value']),\n address: this.mapAddresses(patientRole.addr),\n telecom: this.mapTelecom(patientRole.telecom),\n extension: extensions.length > 0 ? extensions : undefined,\n };\n }\n\n private mapId(ids: CcdaId[] | undefined): string {\n // If there is an id without a root, then use that as the FHIR resource ID\n const serverId = ids?.find((id) => !id['@_extension'] && id['@_root'] && isUUID(id['@_root']));\n if (serverId) {\n return serverId['@_root'] as string;\n }\n\n // Otherwise generate a UUID\n return generateId();\n }\n\n private mapIdentifiers(ids: CcdaId[] | undefined): Identifier[] | undefined {\n if (!ids) {\n return undefined;\n }\n const result: Identifier[] = [];\n for (const id of ids) {\n if (!id['@_extension'] && id['@_root'] && isUUID(id['@_root'])) {\n // By convention, we use id without a root as the FHIR resource ID\n continue;\n }\n result.push({\n system: mapCcdaSystemToFhir(id['@_root']),\n value: id['@_extension'],\n });\n }\n return result;\n }\n\n private mapCcdaNameArrayFhirHumanNameArray(names: CcdaName[] | undefined): HumanName[] | undefined {\n return names?.map((n) => this.mapCcdaNameToFhirHumanName(n)).filter(Boolean) as HumanName[];\n }\n\n private mapCcdaNameToFhirHumanName(name: CcdaName | undefined): HumanName | undefined {\n if (!name) {\n return undefined;\n }\n\n const result: HumanName = {};\n\n const use = name['@_use'] ? HUMAN_NAME_USE_MAPPER.mapCcdaToFhir(name['@_use']) : undefined;\n if (use) {\n result.use = use;\n }\n\n if (name.prefix) {\n result.prefix = name.prefix.map(nodeToString)?.filter(Boolean) as string[];\n }\n\n if (name.family) {\n result.family = nodeToString(name.family);\n }\n\n if (name.given) {\n result.given = name.given.map(nodeToString)?.filter(Boolean) as string[];\n }\n\n if (name.suffix) {\n result.suffix = name.suffix.map(nodeToString)?.filter(Boolean) as string[];\n }\n\n return result;\n }\n\n private mapAddresses(addresses: CcdaAddr[] | undefined): Address[] | undefined {\n if (!addresses || addresses.length === 0 || addresses.every((addr) => addr['@_nullFlavor'] === 'UNK')) {\n return undefined;\n }\n return addresses?.map((addr) => ({\n '@_use': addr['@_use'] ? ADDRESS_USE_MAPPER.mapCcdaToFhir(addr['@_use']) : undefined,\n line: addr.streetAddressLine,\n city: addr.city,\n state: addr.state,\n postalCode: addr.postalCode,\n country: addr.country,\n }));\n }\n\n private mapTelecom(telecoms: CcdaTelecom[] | undefined): ContactPoint[] | undefined {\n if (!telecoms || telecoms.length === 0 || telecoms.every((tel) => tel['@_nullFlavor'] === 'UNK')) {\n return undefined;\n }\n return telecoms?.map((tel) => ({\n '@_use': tel['@_use'] ? TELECOM_USE_MAPPER.mapCcdaToFhir(tel['@_use']) : undefined,\n system: this.getTelecomSystem(tel['@_value']),\n value: this.getTelecomValue(tel['@_value']),\n }));\n }\n\n private createComposition(): Composition {\n const components = this.ccda.component?.structuredBody?.component || [];\n const sections: CompositionSection[] = [];\n\n for (const component of components) {\n for (const section of component.section) {\n const resources = this.processSection(section);\n sections.push({\n title: section.title,\n code: this.mapCode(section.code),\n text: {\n status: 'generated',\n div: `<div xmlns=\"http://www.w3.org/1999/xhtml\">${convertToCompactXml(section.text)}</div>`,\n },\n entry: resources.map(createReference),\n });\n this.resources.push(...resources);\n }\n }\n\n return {\n resourceType: 'Composition',\n id: this.mapId(this.ccda.id),\n language: this.ccda.languageCode?.['@_code'],\n status: 'final',\n type: this.ccda.code\n ? (this.mapCode(this.ccda.code) as CodeableConcept)\n : { coding: [{ system: LOINC, code: '34133-9' }] },\n confidentiality: this.ccda.confidentialityCode?.['@_code'] as Composition['confidentiality'],\n author: this.ccda.author?.[0]\n ? [\n this.mapAuthorToReference(this.ccda.author?.[0]) as Reference<\n Practitioner | Organization | Patient | PractitionerRole\n >,\n ]\n : [{ display: 'Medplum' }],\n custodian: this.mapCustodianToReference(this.ccda.custodian),\n event: this.mapDocumentationOfToEvent(this.ccda.documentationOf),\n date: mapCcdaToFhirDateTime(this.ccda.effectiveTime?.[0]?.['@_value']) ?? new Date().toISOString(),\n title: this.ccda.title ?? 'Medical Summary',\n section: sections,\n };\n }\n\n private processSection(section: CcdaSection): Resource[] {\n const resources: Resource[] = [];\n\n if (section.entry) {\n for (const entry of section.entry) {\n this.processEntry(section, entry, resources);\n }\n }\n\n return resources;\n }\n\n private processEntry(section: CcdaSection, entry: CcdaEntry, resources: Resource[]): void {\n for (const act of entry.act ?? []) {\n const resource = this.processAct(section, act);\n if (resource) {\n resources.push(resource);\n }\n }\n\n for (const substanceAdmin of entry.substanceAdministration ?? []) {\n const resource = this.processSubstanceAdministration(section, substanceAdmin);\n if (resource) {\n resources.push(resource);\n }\n }\n\n for (const organizer of entry.organizer ?? []) {\n resources.push(this.processOrganizer(section, organizer));\n }\n\n for (const observation of entry.observation ?? []) {\n resources.push(this.processObservation(section, observation));\n }\n\n for (const encounter of entry.encounter ?? []) {\n resources.push(this.processEncounter(section, encounter));\n }\n\n for (const procedure of entry.procedure ?? []) {\n resources.push(this.processProcedure(section, procedure));\n }\n }\n\n private processAct(section: CcdaSection, act: CcdaAct): Resource | undefined {\n const templateId = section.templateId[0]['@_root'];\n switch (templateId) {\n case OID_ALLERGIES_SECTION_ENTRIES_REQUIRED:\n return this.processAllergyIntoleranceAct(act);\n case OID_PROBLEMS_SECTION_ENTRIES_REQUIRED:\n return this.processConditionAct(act);\n case OID_PLAN_OF_CARE_SECTION:\n return this.processCarePlanAct(act);\n case OID_HEALTH_CONCERNS_SECTION:\n return this.processConditionAct(act);\n case OID_PROCEDURES_SECTION_ENTRIES_REQUIRED:\n return this.processProcedureAct(act);\n case OID_REASON_FOR_REFERRAL:\n // This is part of USCDI v3, which is optional, and not yet implemented\n return undefined;\n case OID_NOTES_SECTION:\n // This is part of USCDI v3, which is optional, and not yet implemented\n return undefined;\n case OID_PAYERS_SECTION:\n // This is part of USCDI v3, which is optional, and not yet implemented\n return undefined;\n default:\n throw new Error('Unhandled act templateId: ' + templateId);\n }\n }\n\n private processAllergyIntoleranceAct(act: CcdaAct): Resource | undefined {\n const observation = act.entryRelationship?.find((rel) => rel['@_typeCode'] === 'SUBJ')?.observation?.[0];\n if (!observation) {\n return undefined;\n }\n\n const allergy: AllergyIntolerance = {\n resourceType: 'AllergyIntolerance',\n id: this.mapId(act.id),\n clinicalStatus: this.createClinicalStatus(act),\n verificationStatus: this.createVerificationStatus(),\n type: 'allergy',\n category: ['food'],\n patient: createReference(this.patient as Patient),\n recorder: this.mapAuthorToReference(act.author?.[0]),\n recordedDate: this.mapEffectiveTimeToDateTime(act.effectiveTime?.[0]),\n onsetDateTime: this.mapEffectiveTimeToDateTime(observation.effectiveTime?.[0]),\n };\n\n // Set category based on the observation.value code\n if ((observation.value as CcdaCode)?.['@_code'] === '414285001') {\n allergy.category = ['food'];\n }\n\n allergy.extension = this.mapTextReference(observation.text);\n\n const allergenCode = observation.participant?.[0]?.participantRole?.playingEntity?.code;\n if (allergenCode) {\n allergy.code = this.mapCode(allergenCode);\n\n // Add allergen reference\n if (allergy.code && allergenCode.originalText?.reference?.['@_value']) {\n allergy.code.extension = this.mapTextReference(allergenCode.originalText);\n }\n }\n\n const reactionObservations = observation.entryRelationship?.find(\n (rel) => rel['@_typeCode'] === 'MFST'\n )?.observation;\n if (reactionObservations) {\n allergy.reaction = reactionObservations.map((ro) => this.processReaction(ro));\n }\n\n allergy.asserter = this.mapAuthorToReference(observation.author?.[0]);\n\n return allergy;\n }\n\n private processConditionAct(act: CcdaAct): Resource | undefined {\n const observation = act.entryRelationship?.find((rel) => rel['@_typeCode'] === 'SUBJ')?.observation?.[0];\n if (!observation) {\n return undefined;\n }\n\n const result: Condition = {\n resourceType: 'Condition',\n id: this.mapId(act.id),\n identifier: this.concatArrays(this.mapIdentifiers(act.id), this.mapIdentifiers(observation.id)),\n meta: {\n profile: [US_CORE_CONDITION_URL],\n },\n clinicalStatus: {\n coding: [\n {\n system: CLINICAL_CONDITION_CODE_SYSTEM,\n code: this.mapStatus(act.statusCode['@_code']),\n },\n ],\n },\n verificationStatus: {\n coding: [\n {\n system: CONDITION_VERIFICATION_CODE_SYSTEM,\n code: 'confirmed',\n },\n ],\n },\n category: [\n {\n coding: [\n {\n system: CONDITION_CATEGORY_CODE_SYSTEM,\n code: 'problem-list-item',\n display: 'Problem List Item',\n },\n ],\n },\n ],\n code: this.mapCode(observation.value as CcdaCode),\n subject: createReference(this.patient as Patient),\n onsetDateTime: mapCcdaToFhirDateTime(observation.effectiveTime?.[0]?.low?.['@_value']),\n abatementDateTime: mapCcdaToFhirDateTime(observation.effectiveTime?.[0]?.high?.['@_value']),\n recordedDate: this.mapEffectiveTimeToDateTime(act.effectiveTime?.[0]),\n recorder: this.mapAuthorToReference(observation.author?.[0]),\n asserter: this.mapAuthorToReference(observation.author?.[0]),\n };\n\n result.extension = this.mapTextReference(observation.text);\n\n return result;\n }\n\n private processCarePlanAct(act: CcdaAct): Resource | undefined {\n const result: CarePlan = {\n resourceType: 'CarePlan',\n id: this.mapId(act.id),\n identifier: this.mapIdentifiers(act.id),\n status: 'active',\n intent: 'plan',\n title: 'CARE PLAN',\n category: act.code ? [this.mapCode(act.code) as CodeableConcept] : undefined,\n subject: createReference(this.patient as Patient),\n description: nodeToString(act.text),\n };\n\n return result;\n }\n\n private processProcedureAct(act: CcdaAct): Resource | undefined {\n const result: Procedure = {\n resourceType: 'Procedure',\n id: this.mapId(act.id),\n identifier: this.mapIdentifiers(act.id),\n status: 'completed',\n code: this.mapCode(act.code),\n subject: createReference(this.patient as Patient),\n performedDateTime: mapCcdaToFhirDateTime(act.effectiveTime?.[0]?.['@_value']),\n recorder: this.mapAuthorToReference(act.author?.[0]),\n asserter: this.mapAuthorToReference(act.author?.[0]),\n extension: this.mapTextReference(act.text),\n };\n\n return result;\n }\n\n private processSubstanceAdministration(\n section: CcdaSection,\n substanceAdmin: CcdaSubstanceAdministration\n ): Resource | undefined {\n const templateId = section.templateId[0]['@_root'];\n switch (templateId) {\n case OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED:\n case OID_PLAN_OF_CARE_SECTION:\n return this.processMedicationSubstanceAdministration(substanceAdmin);\n case OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL:\n case OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED:\n return this.processImmunizationSubstanceAdministration(substanceAdmin);\n default:\n throw new Error('Unhandled substance administration templateId: ' + templateId);\n }\n }\n\n private processMedicationSubstanceAdministration(substanceAdmin: CcdaSubstanceAdministration): Resource | undefined {\n const cdaId = this.mapId(substanceAdmin.id);\n const medicationCode = substanceAdmin.consumable?.manufacturedProduct?.[0]?.manufacturedMaterial?.[0]?.code?.[0];\n const routeCode = substanceAdmin.routeCode;\n const doseQuantity = substanceAdmin.doseQuantity;\n const manufacturerOrg = substanceAdmin.consumable?.manufacturedProduct?.[0]?.manufacturerOrganization?.[0];\n\n let medication: Medication | undefined = undefined;\n let medicationCodeableConcept: CodeableConcept | undefined = undefined;\n\n if (manufacturerOrg) {\n // If there is a manufacturer, create a Medication resource\n // We need to do this to fully represent the medication for round trip data preservation\n medication = {\n resourceType: 'Medication',\n id: 'med-' + cdaId,\n code: this.mapCode(medicationCode),\n extension: this.mapTextReference(medicationCode?.originalText),\n manufacturer: manufacturerOrg\n ? {\n identifier: {\n value: manufacturerOrg.id?.[0]?.['@_root'],\n },\n display: manufacturerOrg.name?.[0],\n }\n : undefined,\n };\n } else {\n // Otherwise, create a CodeableConcept for the medication\n // Avoid contained resources as much as possible\n medicationCodeableConcept = {\n ...this.mapCode(medicationCode),\n extension: this.mapTextReference(medicationCode?.originalText),\n };\n }\n\n return {\n resourceType: 'MedicationRequest',\n id: cdaId,\n contained: medication ? [medication] : undefined,\n meta: {\n profile: [US_CORE_MEDICATION_REQUEST_URL],\n },\n status: MEDICATION_STATUS_MAPPER.mapCcdaToFhirWithDefault(substanceAdmin.statusCode?.['@_code'], 'active'),\n intent: 'order',\n medicationReference: medication ? { reference: '#med-' + cdaId } : undefined,\n medicationCodeableConcept,\n subject: createReference(this.patient as Patient),\n authoredOn: mapCcdaToFhirDateTime(substanceAdmin.author?.[0]?.time?.['@_value']),\n dispenseRequest: substanceAdmin.effectiveTime?.[0]\n ? {\n validityPeriod: {\n start: mapCcdaToFhirDateTime(substanceAdmin.effectiveTime?.[0]?.low?.['@_value']),\n end: mapCcdaToFhirDateTime(substanceAdmin.effectiveTime?.[0]?.high?.['@_value']),\n },\n }\n : undefined,\n dosageInstruction: [\n {\n text: substanceAdmin.text?.reference?.['@_value'],\n extension: this.mapTextReference(substanceAdmin.text),\n route: routeCode ? this.mapCode(routeCode) : undefined,\n timing: {\n repeat: {\n when: ['HS'],\n },\n },\n doseAndRate: doseQuantity\n ? [\n {\n doseQuantity: {\n system: UCUM,\n value: Number(doseQuantity['@_value']),\n code: '[IU]',\n unit: '[IU]',\n },\n },\n ]\n : undefined,\n },\n ],\n };\n }\n\n private processImmunizationSubstanceAdministration(\n substanceAdmin: CcdaSubstanceAdministration\n ): Resource | undefined {\n const consumable = substanceAdmin.consumable;\n if (!consumable) {\n return undefined;\n }\n\n const result: Immunization = {\n resourceType: 'Immunization',\n id: this.mapId(substanceAdmin.id),\n identifier: this.mapIdentifiers(substanceAdmin.id),\n status: 'completed',\n vaccineCode: this.mapCode(\n consumable.manufacturedProduct?.[0]?.manufacturedMaterial?.[0]?.code?.[0]\n ) as CodeableConcept,\n patient: createReference(this.patient as Patient),\n occurrenceDateTime: mapCcdaToFhirDateTime(substanceAdmin.effectiveTime?.[0]?.['@_value']),\n lotNumber: consumable.manufacturedProduct?.[0]?.manufacturedMaterial?.[0]?.lotNumberText?.[0],\n };\n\n if (substanceAdmin.performer) {\n result.performer = this.mapCcdaPerformerArrayToImmunizationPerformerArray(substanceAdmin.performer);\n }\n\n result.extension = this.mapTextReference(substanceAdmin.text);\n\n if (substanceAdmin.consumable?.manufacturedProduct?.[0]?.manufacturerOrganization?.[0]) {\n result.manufacturer = {\n display: substanceAdmin.consumable?.manufacturedProduct?.[0]?.manufacturerOrganization?.[0]?.name?.[0],\n };\n }\n\n return result;\n }\n\n private processReaction(reactionObs: CcdaObservation): AllergyIntoleranceReaction {\n const reaction: AllergyIntoleranceReaction = {\n id: this.mapId(reactionObs.id),\n manifestation: [this.mapCode(reactionObs.value as CcdaCode)] as CodeableConcept[],\n onset: mapCcdaToFhirDateTime(reactionObs.effectiveTime?.[0]?.low?.['@_value']),\n };\n\n this.processSeverity(reactionObs, reaction);\n\n // Add reaction reference\n if (reaction.manifestation && reaction.manifestation.length > 0 && reactionObs.text?.reference?.['@_value']) {\n reaction.manifestation[0].extension = this.mapTextReference(reactionObs.text);\n }\n\n return reaction;\n }\n\n private processSeverity(reactionObs: CcdaObservation, reaction: AllergyIntoleranceReaction): void {\n const severityObs = reactionObs.entryRelationship?.find((rel) => rel['@_typeCode'] === 'SUBJ')?.observation?.[0];\n if (!severityObs) {\n return;\n }\n\n const severityCode = (severityObs.value as CcdaCode)?.['@_code'];\n if (severityCode) {\n reaction.severity = ALLERGY_SEVERITY_MAPPER.mapCcdaToFhir(severityCode);\n }\n\n reaction.extension = this.mapTextReference(severityObs.text);\n }\n\n private mapEffectiveTimeToDateTime(effectiveTime: CcdaEffectiveTime | undefined): string | undefined {\n if (effectiveTime?.['@_value']) {\n return mapCcdaToFhirDateTime(effectiveTime['@_value']);\n }\n return undefined;\n }\n\n private mapEffectiveTimeToPeriod(effectiveTime: CcdaEffectiveTime | undefined): Period | undefined {\n if (!effectiveTime?.['@_value'] && (effectiveTime?.low || effectiveTime?.high)) {\n return {\n start: mapCcdaToFhirDateTime(effectiveTime?.low?.['@_value']),\n end: mapCcdaToFhirDateTime(effectiveTime?.high?.['@_value']),\n };\n }\n return undefined;\n }\n\n private mapGenderCode(code: string | undefined): Patient['gender'] {\n if (!code) {\n return undefined;\n }\n const map: { [key: string]: Patient['gender'] } = {\n F: 'female',\n M: 'male',\n UN: 'unknown',\n };\n return map[code];\n }\n\n private getTelecomSystem(value: string | undefined): ContactPoint['system'] {\n if (!value) {\n return undefined;\n }\n if (value.startsWith('tel:')) {\n return 'phone';\n }\n if (value.startsWith('mailto:')) {\n return 'email';\n }\n return 'other';\n }\n\n private getTelecomValue(value: string | undefined): string | undefined {\n if (!value) {\n return undefined;\n }\n return value.replace(/^(tel:|mailto:)/, '');\n }\n\n private mapStatus(status: string): string {\n const map: { [key: string]: string } = {\n active: 'active',\n suspended: 'inactive',\n aborted: 'inactive',\n completed: 'resolved',\n };\n return map[status] ?? 'active';\n }\n\n private mapCode(code: CcdaCode | undefined): CodeableConcept | undefined {\n if (!code) {\n return undefined;\n }\n\n return {\n coding: [\n {\n system: mapCcdaSystemToFhir(code['@_codeSystem']),\n code: code['@_code'],\n display: code['@_displayName'],\n },\n ],\n\n text: code['@_displayName'],\n };\n }\n\n private mapCodeToCoding(code: CcdaCode | undefined): Coding | undefined {\n if (!code) {\n return undefined;\n }\n\n return {\n system: mapCcdaSystemToFhir(code['@_codeSystem']),\n code: code['@_code'],\n display: code['@_displayName'],\n };\n }\n\n private createClinicalStatus(act: CcdaAct): CodeableConcept {\n return {\n coding: [\n {\n system: ALLERGY_CLINICAL_CODE_SYSTEM,\n code: this.mapStatus(act.statusCode['@_code']),\n },\n ],\n };\n }\n\n private createVerificationStatus(): CodeableConcept {\n return {\n coding: [\n {\n system: ALLERGY_VERIFICATION_CODE_SYSTEM,\n code: 'confirmed',\n },\n ],\n };\n }\n\n private mapAuthorToReference(author: CcdaAuthor | undefined): Reference<Practitioner> | undefined {\n if (!author) {\n return undefined;\n }\n\n const practitioner: Practitioner = {\n resourceType: 'Practitioner',\n id: this.mapId(author.assignedAuthor?.id),\n identifier: this.mapIdentifiers(author.assignedAuthor?.id),\n name: this.mapCcdaNameArrayFhirHumanNameArray(author.assignedAuthor?.assignedPerson?.name),\n address: this.mapAddresses(author.assignedAuthor?.addr),\n telecom: this.mapTelecom(author.assignedAuthor?.telecom),\n qualification: author.assignedAuthor?.code\n ? [this.mapCode(author.assignedAuthor?.code) as PractitionerQualification]\n : undefined,\n };\n\n this.resources.push(practitioner);\n\n return createReference(practitioner);\n }\n\n private mapAssignedEntityToReference(\n assignedEntity: CcdaAssignedEntity | undefined\n ): Reference<PractitionerRole> | undefined {\n if (!assignedEntity) {\n return undefined;\n }\n\n const assignedPerson = assignedEntity.assignedPerson;\n const representedOrganization = assignedEntity.representedOrganization;\n\n const practitioner: Practitioner = {\n resourceType: 'Practitioner',\n id: this.mapId(assignedEntity?.id),\n identifier: this.mapIdentifiers(assignedEntity?.id),\n name: this.mapCcdaNameArrayFhirHumanNameArray(assignedPerson?.name),\n address: this.mapAddresses(assignedEntity?.addr),\n telecom: this.mapTelecom(assignedEntity?.telecom),\n };\n\n this.resources.push(practitioner);\n\n const organization: Organization = {\n resourceType: 'Organization',\n id: this.mapId(assignedEntity?.id),\n identifier: this.mapIdentifiers(representedOrganization?.id),\n name: representedOrganization?.name?.[0],\n address: this.mapAddresses(representedOrganization?.addr),\n };\n this.resources.push(organization);\n\n const practitionerRole: PractitionerRole = {\n resourceType: 'PractitionerRole',\n id: this.mapId(assignedEntity?.id),\n practitioner: createReference(practitioner),\n organization: createReference(organization),\n };\n this.resources.push(practitionerRole);\n\n return createReference(practitionerRole);\n }\n\n private mapCustodianToReference(custodian: CcdaCustodian | undefined): Reference<Organization> | undefined {\n if (!custodian) {\n return undefined;\n }\n\n const organization: Organization = {\n resourceType: 'Organization',\n id: this.mapId(custodian.assignedCustodian.representedCustodianOrganization.id),\n identifier: this.mapIdentifiers(custodian.assignedCustodian.representedCustodianOrganization.id),\n name: custodian.assignedCustodian.representedCustodianOrganization.name?.[0],\n address: this.mapAddresses(custodian.assignedCustodian.representedCustodianOrganization.addr),\n telecom: this.mapTelecom(custodian.assignedCustodian.representedCustodianOrganization.telecom),\n };\n\n this.resources.push(organization);\n\n return createReference(organization);\n }\n\n private mapDocumentationOfToEvent(documentationOf: CcdaDocumentationOf | undefined): CompositionEvent[] | undefined {\n if (!documentationOf) {\n return undefined;\n }\n\n const serviceEvent = documentationOf.serviceEvent;\n if (!serviceEvent) {\n return undefined;\n }\n\n return [\n {\n code: serviceEvent.code ? [this.mapCode(serviceEvent.code) as CodeableConcept] : undefined,\n period: this.mapEffectiveTimeToPeriod(serviceEvent.effectiveTime?.[0]),\n },\n ];\n }\n\n private concatArrays<T>(array1: T[] | undefined, array2: T[] | undefined): T[] | undefined {\n if (!array1) {\n return array2;\n }\n if (!array2) {\n return array1;\n }\n return [...array1, ...array2];\n }\n\n private mapCcdaPerformerArrayToImmunizationPerformerArray(performers: CcdaPerformer[]): ImmunizationPerformer[] {\n const result: ImmunizationPerformer[] = [];\n\n for (const performer of performers) {\n const entity = performer.assignedEntity;\n const reference = this.mapAssignedEntityToReference(entity);\n if (reference) {\n result.push({ actor: reference });\n }\n }\n\n return result;\n }\n\n private processOrganizer(section: CcdaSection, organizer: CcdaOrganizer): Resource {\n const templateId = section.templateId[0]['@_root'];\n if (templateId === OID_CARE_TEAMS_SECTION) {\n return this.processCareTeamOrganizer(organizer);\n }\n return this.processVitalsOrganizer(organizer);\n }\n\n private processCareTeamOrganizer(organizer: CcdaOrganizer): CareTeam {\n const participants: CareTeamParticipant[] = [];\n\n if (organizer.component) {\n for (const component of organizer.component) {\n const participant = this.processCareTeamMember(component);\n if (participant) {\n participants.push(participant);\n }\n }\n }\n\n const result: CareTeam = {\n resourceType: 'CareTeam',\n id: this.mapId(organizer.id),\n identifier: this.mapIdentifiers(organizer.id),\n participant: participants.length > 0 ? participants : undefined,\n };\n\n return result;\n }\n\n private processCareTeamMember(component: CcdaOrganizerComponent): CareTeamParticipant | undefined {\n const act = component.act?.[0];\n if (!act) {\n return undefined;\n }\n\n const performer = act.performer?.[0];\n if (!performer) {\n return undefined;\n }\n\n return {\n role: performer.functionCode ? [this.mapCode(performer.functionCode) as CodeableConcept] : undefined,\n member: this.mapAssignedEntityToReference(performer.assignedEntity),\n period: this.mapEffectiveTimeToPeriod(act.effectiveTime?.[0]),\n };\n }\n\n private processVitalsOrganizer(organizer: CcdaOrganizer): Observation {\n const result: Observation = {\n resourceType: 'Observation',\n id: this.mapId(organizer.id),\n identifier: this.mapIdentifiers(organizer.id),\n status: 'final',\n category: this.mapObservationTemplateIdToObservationCategory(organizer.templateId),\n code: this.mapCode(organizer.code) as CodeableConcept,\n subject: createReference(this.patient as Patient),\n };\n\n if (organizer.effectiveTime?.[0]?.['@_value']) {\n result.effectiveDateTime = mapCcdaToFhirDateTime(organizer.effectiveTime?.[0]?.['@_value']);\n }\n\n if (organizer.component) {\n const members: Reference<Observation>[] = [];\n for (const component of organizer.component) {\n members.push(...this.processVitalsComponent(component));\n }\n\n if (members.length > 0) {\n result.hasMember = members;\n }\n }\n return result;\n }\n\n private processVitalsComponent(component: CcdaOrganizerComponent): Reference<Observation>[] {\n const result: Reference<Observation>[] = [];\n if (component.observation) {\n for (const observation of component.observation) {\n const child = this.processVitalsObservation(observation);\n result.push(createReference(child));\n this.resources.push(child);\n }\n }\n return result;\n }\n\n private processObservation(section: CcdaSection, observation: CcdaObservation): Resource {\n const observationTemplateId = observation.templateId[0]['@_root'];\n if (observationTemplateId === OID_GOALS_SECTION || observationTemplateId === OID_GOAL_OBSERVATION) {\n // Goal template\n return this.processGoalObservation(observation);\n }\n\n const sectionTemplateId = section.templateId[0]['@_root'];\n switch (sectionTemplateId) {\n case OID_PLAN_OF_CARE_SECTION:\n case OID_GOALS_SECTION:\n return this.processGoalObservation(observation);\n default:\n // Treat this as a normal observation by default\n return this.processVitalsObservation(observation);\n }\n }\n\n private processGoalObservation(observation: CcdaObservation): Goal {\n const result: Goal = {\n resourceType: 'Goal',\n id: this.mapId(observation.id),\n identifier: this.mapIdentifiers(observation.id),\n lifecycleStatus: this.mapGoalLifecycleStatus(observation),\n description: this.mapCode(observation.code) as CodeableConcept,\n subject: createReference(this.patient as Patient),\n startDate: mapCcdaToFhirDate(observation.effectiveTime?.[0]?.['@_value']),\n // note: this.mapNote(observation.text),\n };\n\n result.target = observation.entryRelationship?.map((entryRelationship) => {\n return {\n measure: this.mapCode(entryRelationship.act?.[0]?.code) as CodeableConcept,\n detailCodeableConcept: this.mapCode(entryRelationship.act?.[0]?.code) as CodeableConcept,\n dueDate: mapCcdaToFhirDateTime(entryRelationship.act?.[0]?.effectiveTime?.[0]?.low?.['@_value']),\n };\n });\n\n result.extension = this.mapTextReference(observation.text);\n\n return result;\n }\n\n private mapGoalLifecycleStatus(observation: CcdaObservation): Goal['lifecycleStatus'] {\n // - Map from observation's `statusCode/@code`\n // - Mapping logic:\n // - If statusCode is \"active\" \u2192 \"active\"\n // - If statusCode is \"completed\" \u2192 \"achieved\"\n // - If statusCode is \"cancelled\" \u2192 \"cancelled\"\n // - If statusCode is \"aborted\" \u2192 \"cancelled\"\n // - If no status or other value \u2192 \"active\"\n const map: { [key: string]: Goal['lifecycleStatus'] } = {\n active: 'active',\n completed: 'completed',\n cancelled: 'cancelled',\n aborted: 'cancelled',\n };\n return map[observation.statusCode['@_code'] ?? 'active'];\n }\n\n private processVitalsObservation(observation: CcdaObservation): Observation {\n const result: Observation = {\n resourceType: 'Observation',\n id: this.mapId(observation.id),\n identifier: this.mapIdentifiers(observation.id),\n status: 'final',\n category: this.mapObservationTemplateIdToObservationCategory(observation.templateId),\n code: this.mapCode(observation.code) as CodeableConcept,\n subject: createReference(this.patient as Patient),\n referenceRange: this.mapReferenceRangeArray(observation.referenceRange),\n performer: observation.author\n ?.map((author) => this.mapAuthorToReference(author))\n .filter(Boolean) as Reference<Practitioner>[],\n };\n\n if (observation.value?.['@_xsi:type']) {\n switch (observation.value['@_xsi:type']) {\n case 'PQ': // Physical Quantity\n case 'CO': // Count of individuals\n result.valueQuantity = {\n value: observation.value['@_value'] ? parseFloat(observation.value['@_value']) : undefined,\n unit: observation.value['@_unit'],\n system: UCUM,\n code: observation.value['@_unit'],\n };\n break;\n\n case 'CD': // Code\n case 'CE': // Code with Extensions\n result.valueCodeableConcept = this.mapCode(observation.value);\n break;\n\n case 'ST': // String\n result.valueString = observation.value['#text'] ?? '';\n break;\n\n default:\n console.warn(`Unhandled observation value type: ${observation.value['@_xsi:type']}`);\n }\n }\n\n if (observation.effectiveTime?.[0]?.['@_value']) {\n result.effectiveDateTime = mapCcdaToFhirDateTime(observation.effectiveTime?.[0]?.['@_value']);\n }\n\n result.extension = this.mapTextReference(observation.text);\n\n return result;\n }\n\n private mapObservationTemplateIdToObservationCategory(\n templateIds: CcdaTemplateId[] | undefined\n ): CodeableConcept[] | undefined {\n if (!templateIds) {\n return undefined;\n }\n\n const codes = new Set<string>();\n const result: CodeableConcept[] = [];\n\n for (const templateId of templateIds) {\n const category = OBSERVATION_CATEGORY_MAPPER.mapCcdaToFhirCodeableConcept(templateId['@_root']);\n if (category?.coding?.[0]?.code && !codes.has(category.coding[0].code)) {\n codes.add(category.coding[0].code);\n result.push(category);\n }\n }\n\n for (const templateId of templateIds) {\n result.push({\n coding: [\n {\n system: CCDA_TEMPLATE_CODE_SYSTEM,\n code: templateId['@_root'],\n version: templateId['@_extension'],\n },\n ],\n });\n }\n\n return Array.from(result.values());\n }\n\n private mapReferenceRangeArray(\n referenceRange: CcdaReferenceRange[] | undefined\n ): ObservationReferenceRange[] | undefined {\n if (!referenceRange || referenceRange.length === 0) {\n return undefined;\n }\n\n return referenceRange.map((r) => this.mapReferenceRange(r)).filter(Boolean) as ObservationReferenceRange[];\n }\n\n private mapReferenceRange(referenceRange: CcdaReferenceRange | undefined): ObservationReferenceRange | undefined {\n if (!referenceRange) {\n return undefined;\n }\n\n const observationRange = referenceRange.observationRange;\n if (!observationRange) {\n return undefined;\n }\n\n const result: ObservationReferenceRange = {};\n\n result.extension = this.mapTextReference(observationRange.text);\n\n return result;\n }\n\n private processEncounter(section: CcdaSection, encounter: CcdaEncounter): Encounter {\n // Create the main encounter resource\n const result: Encounter = {\n resourceType: 'Encounter',\n id: this.mapId(encounter.id),\n identifier: this.mapIdentifiers(encounter.id),\n status: ENCOUNTER_STATUS_MAPPER.mapCcdaToFhirWithDefault(encounter.statusCode?.['@_code'], 'unknown'),\n class: {\n system: ACT_CODE_SYSTEM,\n code: encounter.code?.['@_code'] ?? 'AMB',\n display: encounter.code?.['@_displayName'] ?? 'Ambulatory',\n },\n type: encounter.code ? [this.mapCode(encounter.code) as CodeableConcept] : undefined,\n subject: createReference(this.patient as Patient),\n period: this.mapEffectiveTimeToPeriod(encounter.effectiveTime?.[0]),\n };\n\n // Add participant information\n if (encounter.performer) {\n result.participant = encounter.performer.map((performer) => ({\n type: [\n {\n coding: [\n {\n system: PARTICIPATION_CODE_SYSTEM,\n code: performer['@_typeCode'] ?? 'PPRF',\n display: 'Primary Performer',\n },\n ],\n },\n ],\n individual: this.mapAssignedEntityToReference(performer.assignedEntity),\n }));\n }\n\n // Add diagnoses from entryRelationships\n if (encounter.entryRelationship) {\n const diagnoses = encounter.entryRelationship\n .filter((rel) => rel['@_typeCode'] === 'RSON')\n .map((rel) => {\n const observation = rel.observation?.[0];\n if (!observation) {\n return undefined;\n }\n\n // Create Condition resource\n const condition: Condition = {\n resourceType: 'Condition',\n id: this.mapId(observation.id),\n identifier: this.mapIdentifiers(observation.id),\n clinicalStatus: {\n coding: [\n {\n system: CLINICAL_CONDITION_CODE_SYSTEM,\n code: 'active',\n },\n ],\n },\n verificationStatus: {\n coding: [\n {\n system: CONDITION_VER_STATUS_CODE_SYSTEM,\n code: 'confirmed',\n },\n ],\n },\n code: this.mapCode(observation.value as CcdaCode),\n subject: createReference(this.patient as Patient),\n onsetDateTime: mapCcdaToFhirDateTime(observation.effectiveTime?.[0]?.low?.['@_value']),\n };\n\n // Add condition to resources array\n this.resources.push(condition);\n\n return {\n condition: createReference(condition),\n use: {\n coding: [\n {\n system: DIAGNOSIS_ROLE_CODE_SYSTEM,\n code: 'AD',\n display: 'Admission diagnosis',\n },\n ],\n },\n };\n })\n .filter(Boolean) as EncounterDiagnosis[];\n\n if (diagnoses.length > 0) {\n result.diagnosis = diagnoses;\n }\n }\n\n result.extension = this.mapTextReference(encounter.text);\n\n return result;\n }\n\n private processProcedure(section: CcdaSection, procedure: CcdaProcedure): Procedure {\n const result: Procedure = {\n resourceType: 'Procedure',\n id: this.mapId(procedure.id),\n identifier: this.mapIdentifiers(procedure.id),\n status: PROCEDURE_STATUS_MAPPER.mapCcdaToFhirWithDefault(procedure.statusCode?.['@_code'], 'completed'),\n code: this.mapCode(procedure.code),\n subject: createReference(this.patient as Patient),\n performedDateTime: this.mapEffectiveTimeToDateTime(procedure.effectiveTime?.[0]),\n performedPeriod: this.mapEffectiveTimeToPeriod(procedure.effectiveTime?.[0]),\n bodySite: procedure.targetSiteCode ? [this.mapCode(procedure.targetSiteCode) as CodeableConcept] : undefined,\n extension: this.mapTextReference(procedure.text),\n };\n\n return result;\n }\n\n private mapTextReference(text: CcdaText | undefined): Extension[] | undefined {\n if (!text?.reference?.['@_value']) {\n return undefined;\n }\n\n return [\n {\n url: CCDA_NARRATIVE_REFERENCE_URL,\n valueString: text.reference?.['@_value'],\n },\n ];\n }\n}\n\nfunction nodeToString(node: CcdaText | string | undefined): string | undefined {\n if (!node) {\n return undefined;\n }\n if (typeof node === 'string') {\n return node;\n }\n if (typeof node === 'object' && '#text' in node) {\n return node['#text'];\n }\n return undefined;\n}\n", "import {\n OID_ALLERGIES_SECTION_ENTRIES_REQUIRED,\n OID_ASSESSMENTS_SECTION,\n OID_CARE_TEAMS_SECTION,\n OID_CONTINUITY_OF_CARE_DOCUMENT,\n OID_ENCOUNTERS_SECTION_ENTRIES_REQUIRED,\n OID_GOALS_SECTION,\n OID_HEALTH_CONCERNS_SECTION,\n OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL,\n OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED,\n OID_MEDICAL_EQUIPMENT_ENTRIES_OPTIONAL,\n OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED,\n OID_MENTAL_STATUS_SECTION,\n OID_NOTES_SECTION,\n OID_PAYERS_SECTION,\n OID_PLAN_OF_CARE_SECTION,\n OID_PROBLEMS_SECTION_ENTRIES_REQUIRED,\n OID_PROCEDURES_SECTION_ENTRIES_REQUIRED,\n OID_REASON_FOR_REFERRAL,\n OID_RESULTS_SECTION_ENTRIES_REQUIRED,\n OID_SOCIAL_HISTORY_SECTION_ENTRIES_OPTIONAL,\n OID_US_REALM_CDA_HEADER,\n OID_VITAL_SIGNS_SECTION_ENTRIES_REQUIRED,\n} from './oids';\nimport { CcdaTemplateId } from './types';\n\nexport const CCDA_TEMPLATE_IDS = [\n {\n '@_root': OID_US_REALM_CDA_HEADER,\n '@_extension': '2015-08-01',\n },\n {\n '@_root': OID_US_REALM_CDA_HEADER,\n },\n {\n '@_root': OID_CONTINUITY_OF_CARE_DOCUMENT,\n '@_extension': '2015-08-01',\n },\n {\n '@_root': OID_CONTINUITY_OF_CARE_DOCUMENT,\n },\n];\n\n// Order from Alice Newman guide:\n\n// A) USCDI Data Class/Element: Allergies and Intolerances\n// B) USCDI Data Class/Element: Medications\n// C) USCDI Data Class/Element: Problems\n// D) USCDI Data Class/Element: Immunizations\n// E) USCDI Data Class/Element: Vital Signs\n// F) USCDI Data Class/Element: Smoking Status\n// G) USCDI Data Class/Element: Procedures\n// H) USCDI Data Class/Element: Clinical Notes\n// I) USCDI Data Class/Element: Laboratory Tests\n// J) USCDI Data Class/Element: Laboratory Values/Results\n// K) USCDI Data Class/Element: Clinical Notes\n// L) USCDI Data Class/Element: Unique Device Identifiers for a Patient\u2019s Implantable Device(s)\n// M) USCDI Data Class/Element: Assessment and Plan of Treatment:\n// N) USCDI Data Class/Element: Goals\n// O) USCDI Data Class/Element: HealthConcerns\n// P) USCDI Data Class/Element: Clinical Notes\n\n// Example C-CDA Structure based on your USCDI list:\n\n// 1. Allergies and Intolerances\n// 2. Medications\n// 3. Problems\n// 4. Immunizations\n// 5. Vital Signs\n// 6. Social History (including Smoking Status)\n// 7. Procedures\n// 8. Results (including Laboratory Tests and Values/Results)\n// 9. Clinical Notes\n// 10. Device (Unique Device Identifiers)\n// 11. Plan of Care (including Assessment and Plan of Treatment, Goals, and Health Concerns)\n\n// Allergies and Intolerances 2.16.840.1.113883.10.20.22.2.6.1 Allergies Section (entries required)\nexport const ALLERGIES_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_ALLERGIES_SECTION_ENTRIES_REQUIRED, '@_extension': '2015-08-01' },\n];\n\n// Medications 2.16.840.1.113883.10.20.22.2.1.1 Medications Section (entries required)\nexport const MEDICATIONS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED, '@_extension': '2014-06-09' },\n];\n\n// Problems 2.16.840.1.113883.10.20.22.2.5.1 Problem Section (entries required)\nexport const PROBLEMS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_PROBLEMS_SECTION_ENTRIES_REQUIRED, '@_extension': '2015-08-01' },\n];\n\n// Immunizations 2.16.840.1.113883.10.20.22.2.2.1 Immunizations Section (entries required)\nexport const IMMUNIZATIONS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL },\n { '@_root': OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL, '@_extension': '2015-08-01' },\n { '@_root': OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED },\n { '@_root': OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED, '@_extension': '2015-08-01' },\n];\n\n// Vital Signs 2.16.840.1.113883.10.20.22.2.4.1 Vital Signs Section (entries required)\nexport const VITAL_SIGNS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_VITAL_SIGNS_SECTION_ENTRIES_REQUIRED, '@_extension': '2015-08-01' },\n];\n\n// Smoking Status 2.16.840.1.113883.10.20.22.2.16 Social History Section (entries required) - Use appropriate entries within this section for smoking status.\nexport const SOCIAL_HISTORY_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_SOCIAL_HISTORY_SECTION_ENTRIES_OPTIONAL },\n { '@_root': OID_SOCIAL_HISTORY_SECTION_ENTRIES_OPTIONAL, '@_extension': '2015-08-01' },\n];\n\n// Procedures 2.16.840.1.113883.10.20.22.2.7.1 Procedures Section (entries required)\n// 47519-4\nexport const PROCEDURES_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_PROCEDURES_SECTION_ENTRIES_REQUIRED, '@_extension': '2014-06-09' },\n];\n\n// Clinical Notes 2.16.840.1.113883.10.20.22.2.10 General Header Constraints (entries required) - Clinical notes can be included in various sections using this template ID.\nexport const CLINICAL_NOTES_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_PLAN_OF_CARE_SECTION, '@_extension': '2014-06-09' },\n];\n\n// Laboratory Tests (Orders) 2.16.840.1.113883.10.20.22.2.3.1 Results Section (entries required) - Lab orders would be included as entries in this section.\nexport const LAB_TESTS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_RESULTS_SECTION_ENTRIES_REQUIRED, '@_extension': '2015-08-01' },\n];\n\n// Laboratory Values/Results 2.16.840.1.113883.10.20.22.2.3.1 Results Section (entries required) - Lab results would also be included as entries in this section.\nexport const RESULTS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_RESULTS_SECTION_ENTRIES_REQUIRED, '@_extension': '2015-08-01' },\n];\n\n// Unique Device Identifiers for a Patient's Implantable Device(s) 2.16.840.1.113883.10.20.22.2.23 Medical Equipment Section - Use appropriate entries within this section for implantable devices.\nexport const DEVICES_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_MEDICAL_EQUIPMENT_ENTRIES_OPTIONAL, '@_extension': '2015-08-01' },\n];\n\nexport const ASSESSMENTS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [{ '@_root': OID_ASSESSMENTS_SECTION }];\n\n// Assessment and Plan of Treatment 2.16.840.1.113883.10.20.22.2.17 Assessment and Plan Section\nexport const PLAN_OF_TREATMENT_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_PLAN_OF_CARE_SECTION },\n { '@_root': OID_PLAN_OF_CARE_SECTION, '@_extension': '2014-06-09' },\n];\n\n// Encounters 2.16.840.1.113883.10.20.22.2.22.1 Encounters Section (entries required)\nexport const ENCOUNTERS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_ENCOUNTERS_SECTION_ENTRIES_REQUIRED, '@_extension': '2015-08-01' },\n];\n\n// Goals 2.16.840.1.113883.10.20.22.2.17 Assessment and Plan Section - Goals can be included as entries within this section.\nexport const GOALS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [{ '@_root': OID_GOALS_SECTION }];\n\n// Health Concerns 2.16.840.1.113883.10.20.22.2.58 Health concerns can be represented as problems.\nexport const HEALTH_CONCERNS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_HEALTH_CONCERNS_SECTION, '@_extension': '2015-08-01' },\n];\n\nexport const REASON_FOR_REFERRAL_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_REASON_FOR_REFERRAL, '@_extension': '2014-06-09' },\n];\n\nexport const MENTAL_STATUS_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_MENTAL_STATUS_SECTION, '@_extension': '2015-08-01' },\n];\n\nexport const PATIENT_NOTES_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_NOTES_SECTION, '@_extension': '2016-11-01' },\n];\n\nexport const CARE_TEAM_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_CARE_TEAMS_SECTION, '@_extension': '2022-06-01' },\n];\n\nexport const INSURANCE_SECTION_TEMPLATE_IDS: CcdaTemplateId[] = [\n { '@_root': OID_PAYERS_SECTION, '@_extension': '2015-08-01' },\n];\n\n// 1. Allergies and Intolerances\n// 2. Medications\n// 3. Problems\n// 4. Immunizations\n// 5. Vital Signs\n// 6. Social History (including Smoking Status)\n// 7. Procedures\n// 8. Results (including Laboratory Tests and Values/Results)\n// 9. Clinical Notes\n// 10. Device (Unique Device Identifiers)\n// 11. Plan of Care (including Assessment and Plan of Treatment, Goals, and Health Concerns)\n\nexport const LOINC_TO_TEMPLATE_IDS: Record<string, CcdaTemplateId[]> = {\n '48765-2': ALLERGIES_SECTION_TEMPLATE_IDS,\n '10160-0': MEDICATIONS_SECTION_TEMPLATE_IDS,\n '11450-4': PROBLEMS_SECTION_TEMPLATE_IDS,\n '11369-6': IMMUNIZATIONS_SECTION_TEMPLATE_IDS,\n '11348-0': IMMUNIZATIONS_SECTION_TEMPLATE_IDS,\n '9176-8': IMMUNIZATIONS_SECTION_TEMPLATE_IDS,\n '8716-3': VITAL_SIGNS_SECTION_TEMPLATE_IDS,\n '29762-2': SOCIAL_HISTORY_SECTION_TEMPLATE_IDS,\n '47519-4': PROCEDURES_SECTION_TEMPLATE_IDS,\n '30954-2': RESULTS_SECTION_TEMPLATE_IDS,\n '51848-0': ASSESSMENTS_SECTION_TEMPLATE_IDS,\n '18776-5': PLAN_OF_TREATMENT_SECTION_TEMPLATE_IDS,\n '46240-8': ENCOUNTERS_SECTION_TEMPLATE_IDS,\n '61146-7': GOALS_SECTION_TEMPLATE_IDS,\n '75310-3': HEALTH_CONCERNS_SECTION_TEMPLATE_IDS,\n '42349-1': REASON_FOR_REFERRAL_SECTION_TEMPLATE_IDS,\n '10190-7': MENTAL_STATUS_SECTION_TEMPLATE_IDS,\n '11488-4': PATIENT_NOTES_SECTION_TEMPLATE_IDS,\n '85847-2': CARE_TEAM_SECTION_TEMPLATE_IDS,\n '48768-6': INSURANCE_SECTION_TEMPLATE_IDS,\n};\n", "import { capitalize } from '@medplum/core';\nimport {\n Address,\n AllergyIntolerance,\n Bundle,\n CarePlan,\n CareTeam,\n CodeableConcept,\n Coding,\n Composition,\n CompositionEvent,\n CompositionSection,\n Condition,\n ContactPoint,\n DosageDoseAndRate,\n Encounter,\n Extension,\n ExtractResource,\n Goal,\n HumanName,\n Identifier,\n Immunization,\n ImmunizationPerformer,\n MedicationRequest,\n Narrative,\n Observation,\n ObservationReferenceRange,\n Organization,\n Patient,\n Period,\n Practitioner,\n Procedure,\n Reference,\n Resource,\n ResourceType,\n} from '@medplum/fhirtypes';\nimport { mapFhirToCcdaDate, mapFhirToCcdaDateTime } from './datetime';\nimport {\n OID_ACT_CLASS_CODE_SYSTEM,\n OID_ACT_CODE_CODE_SYSTEM,\n OID_ADMINISTRATIVE_GENDER_CODE_SYSTEM,\n OID_ALLERGY_OBSERVATION,\n OID_ALLERGY_PROBLEM_ACT,\n OID_AUTHOR_PARTICIPANT,\n OID_CARE_TEAM_ORGANIZER_ENTRY,\n OID_CDC_RACE_AND_ETHNICITY_CODE_SYSTEM,\n OID_ENCOUNTER_ACTIVITIES,\n OID_ENCOUNTER_LOCATION,\n OID_HL7_REGISTERED_MODELS,\n OID_IMMUNIZATION_ACTIVITY,\n OID_IMMUNIZATION_MEDICATION_INFORMATION,\n OID_INSTRUCTIONS,\n OID_LOINC_CODE_SYSTEM,\n OID_MEDICATION_ACTIVITY,\n OID_MEDICATION_FREE_TEXT_SIG,\n OID_MEDICATION_INFORMATION_MANUFACTURED_MATERIAL,\n OID_PLAN_OF_CARE_ACTIVITY_OBSERVATION,\n OID_PROBLEM_ACT,\n OID_PROBLEM_OBSERVATION,\n OID_PROCEDURE_ACTIVITY_ACT,\n OID_PROCEDURE_ACTIVITY_PROCEDURE,\n OID_REACTION_OBSERVATION,\n OID_SEVERITY_OBSERVATION,\n OID_SNOMED_CT_CODE_SYSTEM,\n OID_VITAL_SIGNS_OBSERVATION,\n OID_VITAL_SIGNS_ORGANIZER,\n} from './oids';\nimport {\n ADDRESS_USE_MAPPER,\n ALLERGY_SEVERITY_MAPPER,\n ALLERGY_STATUS_MAPPER,\n CCDA_NARRATIVE_REFERENCE_URL,\n CCDA_TEMPLATE_CODE_SYSTEM,\n CONFIDENTIALITY_MAPPER,\n GENDER_MAPPER,\n HUMAN_NAME_USE_MAPPER,\n mapCodeableConceptToCcdaCode,\n mapCodeableConceptToCcdaValue,\n mapFhirSystemToCcda,\n MEDICATION_STATUS_MAPPER,\n OBSERVATION_CATEGORY_MAPPER,\n PROBLEM_STATUS_MAPPER,\n TELECOM_USE_MAPPER,\n US_CORE_ETHNICITY_URL,\n US_CORE_RACE_URL,\n} from './systems';\nimport { CCDA_TEMPLATE_IDS, LOINC_TO_TEMPLATE_IDS } from './templates';\nimport {\n Ccda,\n CcdaAddr,\n CcdaAuthor,\n CcdaCode,\n CcdaCustodian,\n CcdaDocumentationOf,\n CcdaEffectiveTime,\n CcdaEntry,\n CcdaId,\n CcdaLanguageCommunication,\n CcdaName,\n CcdaNarrative,\n CcdaObservation,\n CcdaOrganizer,\n CcdaOrganizerComponent,\n CcdaPatient,\n CcdaPerformer,\n CcdaQuantity,\n CcdaRecordTarget,\n CcdaReference,\n CcdaReferenceRange,\n CcdaSection,\n CcdaSubstanceAdministration,\n CcdaTelecom,\n CcdaTemplateId,\n CcdaText,\n CcdaTimeStamp,\n CcdaValue,\n} from './types';\nimport { parseXml } from './xml';\n\n/**\n * Convert a FHIR bundle to a C-CDA document.\n * @param bundle - The FHIR bundle to convert.\n * @returns The C-CDA document.\n */\nexport function convertFhirToCcda(bundle: Bundle): Ccda {\n return new FhirToCcdaConverter(bundle).convert();\n}\n\n/**\n * The FhirToCcdaConverter class is responsible for converting a FHIR bundle to a C-CDA document.\n */\nclass FhirToCcdaConverter {\n private readonly bundle: Bundle;\n private readonly composition: Composition;\n private readonly patient: Patient;\n\n /**\n * Creates a new FhirToCcdaConverter for the given FHIR bundle.\n * @param bundle - The FHIR bundle to convert.\n */\n constructor(bundle: Bundle) {\n this.bundle = bundle;\n\n const composition = this.findResource('Composition');\n if (!composition) {\n throw new Error('Composition not found');\n }\n\n const patient = this.findResource('Patient');\n if (!patient) {\n throw new Error('Patient not found');\n }\n\n this.composition = composition;\n this.patient = patient;\n }\n\n /**\n * Convert the FHIR bundle to a C-CDA document.\n * @returns The C-CDA document.\n */\n convert(): Ccda {\n const sections = this.createSections();\n\n // Be careful! Order is important!\n // Validate changes with ETT: https://ett.healthit.gov/ett/#/validators/ccdauscidv3#ccdaValdReport\n return {\n realmCode: {\n '@_code': 'US',\n },\n typeId: {\n '@_root': OID_HL7_REGISTERED_MODELS,\n '@_extension': 'POCD_HD000040',\n },\n templateId: CCDA_TEMPLATE_IDS,\n id: this.mapIdentifiers(this.composition.id, undefined),\n // Consol Continuity of Care Document (CCD) (V3) SHALL contain exactly one [1..1] code (CONF:1198-17180)\n // @code=\"34133-9\" Summarization of Episode Note (CodeSystem: 2.16.840.1.113883.6.1 LOINC) (CONF:1198-17181, CONF:1198-32138)\n code: {\n '@_code': '34133-9',\n '@_displayName': 'Summarization of Episode Note',\n '@_codeSystem': OID_LOINC_CODE_SYSTEM,\n '@_codeSystemName': 'LOINC',\n },\n title: this.composition.title,\n effectiveTime: this.mapEffectiveTime(this.composition.date, undefined),\n confidentialityCode: this.composition.confidentiality\n ? CONFIDENTIALITY_MAPPER.mapFhirToCcdaCode(this.composition.confidentiality)\n : undefined,\n // Consol US Realm Header SHALL contain exactly one [1..1] languageCode,\n // which SHALL be selected from ValueSet Language 2.16.840.1.113883.1.11.11526 DYNAMIC (CONF:5372, R2.1=CONF:1198-5372, DSTU:806)\n languageCode: { '@_code': this.composition.language ?? 'en-US' },\n recordTarget: this.createRecordTarget(),\n author: this.mapAuthor(this.composition.author?.[0], this.composition.date),\n custodian: this.mapCustodian(this.composition.custodian),\n documentationOf: this.mapDocumentationOf(this.composition.event),\n component:\n sections.length > 0\n ? {\n structuredBody: {\n component: sections.map((section) => ({\n section: [section],\n })),\n },\n }\n : undefined,\n };\n }\n\n /**\n * Find a resource in the FHIR bundle by resource type.\n * @param resourceType - The type of resource to find.\n * @returns The resource if found, otherwise undefined.\n */\n private findResource<K extends ResourceType>(resourceType: K): ExtractResource<K> | undefined {\n return this.bundle.entry?.find((e) => e.resource?.resourceType === resourceType)?.resource as ExtractResource<K>;\n }\n\n /**\n * Find a resource in the FHIR bundle by reference.\n * @param reference - The reference to the resource.\n * @returns The resource if found, otherwise undefined.\n */\n private findResourceByReference<T extends Resource>(reference: Reference<T> | undefined): T | undefined {\n if (!reference?.reference) {\n return undefined;\n }\n const [resourceType, id] = reference.reference.split('/');\n if (!resourceType || !id) {\n return undefined;\n }\n return this.bundle.entry?.find((e) => e.resource?.resourceType === resourceType && e.resource?.id === id)\n ?.resource as T;\n }\n\n /**\n * Find resources in the FHIR bundle by references.\n * @param references - The references to the resources.\n * @returns The resources if found, otherwise undefined.\n */\n private findResourcesByReferences(references: Reference[] | undefined): Resource[] {\n if (!references) {\n return [];\n }\n return references.map((ref) => this.findResourceByReference(ref)).filter((r): r is Resource => !!r);\n }\n\n /**\n * Create the record target for the C-CDA document.\n * @returns The record target.\n */\n private createRecordTarget(): CcdaRecordTarget[] {\n if (!this.patient) {\n throw new Error('Patient not found');\n }\n\n return [\n {\n patientRole: {\n id: this.mapIdentifiers(this.patient.id, this.patient.identifier),\n addr: this.mapFhirAddressArrayToCcdaAddressArray(this.patient.address),\n telecom: this.mapTelecom(this.patient.telecom),\n patient: this.mapPatient(this.patient),\n },\n },\n ];\n }\n\n /**\n * Map the patient to the C-CDA patient.\n * @param patient - The patient to map.\n * @returns The C-CDA patient.\n */\n private mapPatient(patient: Patient): CcdaPatient {\n return {\n name: this.mapNames(patient.name),\n administrativeGenderCode: this.mapGender(patient.gender),\n birthTime: this.mapBirthDate(patient.birthDate),\n raceCode: this.mapRace(patient.extension),\n ethnicGroupCode: this.mapEthnicity(patient.extension),\n languageCommunication: this.mapLanguageCommunication(patient.communication),\n };\n }\n\n /**\n * Map the names to the C-CDA names.\n * @param names - The names to map.\n * @returns The C-CDA names.\n */\n private mapNames(names: HumanName[] | undefined): CcdaName[] | undefined {\n return names?.map((name) => ({\n '@_use': name.use ? HUMAN_NAME_USE_MAPPER.mapFhirToCcdaWithDefault(name.use, 'L') : undefined,\n prefix: name.prefix,\n family: name.family,\n given: name.given,\n suffix: name.suffix,\n }));\n }\n\n /**\n * Map the gender to the C-CDA gender.\n * @param gender - The gender to map.\n * @returns The C-CDA gender.\n */\n\n private mapGender(gender: Patient['gender']): CcdaCode | undefined {\n if (!gender) {\n return undefined;\n }\n return {\n '@_xsi:type': 'CE',\n '@_code': GENDER_MAPPER.mapFhirToCcda(gender),\n '@_displayName': gender ? capitalize(gender) : 'Unknown',\n '@_codeSystem': OID_ADMINISTRATIVE_GENDER_CODE_SYSTEM,\n '@_codeSystemName': 'AdministrativeGender',\n };\n }\n\n /**\n * Map the birth date to the C-CDA birth date.\n * @param birthDate - The birth date to map.\n * @returns The C-CDA birth date.\n */\n private mapBirthDate(birthDate: string | undefined): CcdaTimeStamp | undefined {\n if (!birthDate) {\n return undefined;\n }\n return {\n '@_value': birthDate.replace(/-/g, ''),\n };\n }\n\n /**\n * Map the addresses to the C-CDA addresses.\n * @param addresses - The addresses to map.\n * @returns The C-CDA addresses.\n */\n private mapFhirAddressArrayToCcdaAddressArray(addresses: Address[] | undefined): CcdaAddr[] {\n if (!addresses || addresses.length === 0) {\n return [{ '@_nullFlavor': 'UNK' }];\n }\n return addresses.map((addr) => this.mapFhirAddressToCcdaAddress(addr)).filter(Boolean) as CcdaAddr[];\n }\n\n private mapFhirAddressToCcdaAddress(address: Address | undefined): CcdaAddr | undefined {\n if (!address) {\n return undefined;\n }\n const result: CcdaAddr = {\n '@_use': address.use ? ADDRESS_USE_MAPPER.mapFhirToCcda(address.use as 'home' | 'work') : undefined,\n streetAddressLine: address.line || [],\n city: address.city,\n state: address.state,\n postalCode: address.postalCode,\n country: address.country,\n };\n\n return result;\n }\n\n /**\n * Map the race to the C-CDA race.\n * @param extensions - The extensions to map.\n * @returns The C-CDA race.\n */\n private mapRace(extensions: Extension[] | undefined): CcdaCode[] | undefined {\n const raceExt = extensions?.find((e) => e.url === US_CORE_RACE_URL);\n const ombCategory = raceExt?.extension?.find((e) => e.url === 'ombCategory')?.valueCoding;\n\n if (!ombCategory) {\n return undefined;\n }\n\n return [\n {\n '@_code': ombCategory.code,\n '@_displayName': ombCategory.display,\n '@_codeSystem': OID_CDC_RACE_AND_ETHNICITY_CODE_SYSTEM,\n '@_codeSystemName': 'CDC Race and Ethnicity',\n },\n ];\n }\n\n /**\n * Map the ethnicity to the C-CDA ethnicity.\n * @param extensions - The extensions to map.\n * @returns The C-CDA ethnicity.\n */\n private mapEthnicity(extensions: Extension[] | undefined): CcdaCode[] | undefined {\n const ethnicityExt = extensions?.find((e) => e.url === US_CORE_ETHNICITY_URL);\n const ombCategory = ethnicityExt?.extension?.find((e) => e.url === 'ombCategory')?.valueCoding;\n\n if (!ombCategory) {\n return undefined;\n }\n\n return [\n {\n '@_code': ombCategory.code,\n '@_displayName': ombCategory.display,\n '@_codeSystem': OID_CDC_RACE_AND_ETHNICITY_CODE_SYSTEM,\n '@_codeSystemName': 'CDC Race and Ethnicity',\n },\n ];\n }\n\n /**\n * Map the language communication to the C-CDA language communication.\n * @param communication - The communication to map.\n * @returns The C-CDA language communication.\n */\n private mapLanguageCommunication(communication: Patient['communication']): CcdaLanguageCommunication[] | undefined {\n if (!communication?.length) {\n return undefined;\n }\n\n return [\n {\n '@_languageCode': communication[0].language?.coding?.[0]?.code,\n },\n ];\n }\n\n /**\n * Create the sections for the C-CDA document.\n * @returns The sections.\n */\n private createSections(): CcdaSection[] {\n const sections: CcdaSection[] = [];\n\n if (this.composition.section) {\n for (const section of this.composition.section) {\n sections.push(this.createSection(section));\n }\n }\n\n return sections;\n }\n\n private createSection(section: CompositionSection): CcdaSection {\n // Get the section code to determine type\n const sectionCode = section.code?.coding?.[0]?.code;\n if (!sectionCode) {\n throw new Error(`Missing section code: ${JSON.stringify(section.code)}`);\n }\n\n const templateId = LOINC_TO_TEMPLATE_IDS[sectionCode];\n if (!templateId) {\n throw new Error(`Unknown section code: ${sectionCode}`);\n }\n\n const resources = this.findResourcesByReferences(section.entry);\n\n return {\n templateId: templateId,\n code: mapCodeableConceptToCcdaCode(section.code),\n title: section.title,\n text: this.mapFhirTextDivToCcdaSectionText(section.text),\n entry: resources.map((resource) => this.createEntry(section, resource)),\n '@_nullFlavor': resources.length === 0 ? 'NI' : undefined,\n };\n }\n\n private createEntry(section: CompositionSection, resource: Resource): CcdaEntry {\n switch (resource.resourceType) {\n case 'AllergyIntolerance':\n return this.createAllergyEntry(resource as AllergyIntolerance);\n case 'CarePlan':\n return this.createPlanOfTreatmentCarePlanEntry(resource);\n case 'CareTeam':\n return this.createCareTeamEntry(resource);\n case 'Condition':\n return this.createProblemEntry(resource);\n case 'Encounter':\n return this.createEncounterEntry(resource);\n case 'Goal':\n return this.createPlanOfTreatmentGoalEntry(resource);\n case 'Immunization':\n return this.createImmunizationEntry(resource as Immunization);\n case 'MedicationRequest':\n return this.createMedicationEntry(resource as MedicationRequest);\n case 'Procedure':\n return this.createHistoryOfProceduresEntry(resource) as CcdaEntry;\n case 'Observation':\n return this.createObservationEntry(resource as Observation);\n default:\n throw new Error(`Unknown resource type: ${resource.resourceType}`);\n }\n }\n\n private mapFhirTextDivToCcdaSectionText(text: Narrative | undefined): CcdaNarrative | undefined {\n if (!text) {\n return undefined;\n }\n\n const result = parseXml(text.div)?.div;\n\n if (result?.['@_xmlns']) {\n delete result['@_xmlns'];\n }\n\n return result;\n }\n\n /**\n * Create the C-CDA allergy entry for the FHIR allergy.\n * @param allergy - The FHIR allergy to create the C-CDA allergy entry for.\n * @returns The C-CDA allergy entry.\n */\n private createAllergyEntry(allergy: AllergyIntolerance): CcdaEntry {\n const reaction = allergy.reaction?.[0];\n return {\n act: [\n {\n '@_classCode': 'ACT',\n '@_moodCode': 'EVN',\n templateId: [\n {\n '@_root': OID_ALLERGY_PROBLEM_ACT,\n },\n {\n '@_root': OID_ALLERGY_PROBLEM_ACT,\n '@_extension': '2015-08-01',\n },\n ],\n id: this.mapIdentifiers(allergy.id, allergy.identifier),\n code: {\n '@_code': 'CONC',\n '@_codeSystem': OID_ACT_CLASS_CODE_SYSTEM,\n },\n statusCode: {\n '@_code': ALLERGY_STATUS_MAPPER.mapFhirToCcdaWithDefault(\n allergy.clinicalStatus?.coding?.[0]?.code,\n 'active'\n ),\n },\n effectiveTime: this.mapEffectiveTime(allergy.recordedDate, undefined),\n author: this.mapAuthor(allergy.recorder, allergy.recordedDate),\n text: this.createTextFromExtensions(allergy.extension),\n entryRelationship: [\n {\n '@_typeCode': 'SUBJ',\n observation: [\n {\n '@_classCode': 'OBS',\n '@_moodCode': 'EVN',\n templateId: [\n {\n '@_root': OID_ALLERGY_OBSERVATION,\n },\n {\n '@_root': OID_ALLERGY_OBSERVATION,\n '@_extension': '2014-06-09',\n },\n ],\n id: this.mapIdentifiers(allergy.id, allergy.identifier),\n code: {\n '@_code': 'ASSERTION',\n '@_codeSystem': OID_ACT_CODE_CODE_SYSTEM,\n },\n statusCode: {\n '@_code': 'completed',\n },\n author: this.mapAuthor(allergy.asserter, allergy.recordedDate),\n effectiveTime: this.mapEffectiveDate(allergy.onsetDateTime, undefined),\n value: this.mapAllergyCategory(allergy.category),\n text: this.createTextFromExtensions(allergy.extension),\n participant: [\n {\n '@_typeCode': 'CSM',\n participantRole: {\n '@_classCode': 'MANU',\n playingEntity: {\n '@_classCode': 'MMAT',\n code: {\n ...mapCodeableConceptToCcdaCode(allergy.code),\n originalText: {\n reference: this.getNarrativeReference(allergy.code?.extension),\n },\n },\n },\n },\n },\n ],\n entryRelationship: reaction\n ? [\n {\n '@_typeCode': 'MFST',\n '@_inversionInd': 'true',\n observation: [\n {\n '@_classCode': 'OBS',\n '@_moodCode': 'EVN',\n templateId: [\n {\n '@_root': OID_REACTION_OBSERVATION,\n },\n {\n '@_root': OID_REACTION_OBSERVATION,\n '@_extension': '2014-06-09',\n },\n ],\n id: this.mapIdentifiers(reaction.id, undefined),\n code: {\n '@_code': 'ASSERTION',\n '@_codeSystem': OID_ACT_CODE_CODE_SYSTEM,\n },\n statusCode: {\n '@_code': 'completed',\n },\n effectiveTime: this.mapEffectiveDate(allergy.onsetDateTime, allergy.onsetPeriod),\n value: mapCodeableConceptToCcdaValue(reaction.manifestation?.[0]),\n text: this.createTextFromExtensions(reaction.manifestation?.[0]?.extension),\n entryRelationship: [\n {\n '@_typeCode': 'SUBJ',\n '@_inversionInd': 'true',\n observation: [\n {\n '@_classCode': 'OBS',\n '@_moodCode': 'EVN',\n templateId: [\n {\n '@_root': OID_SEVERITY_OBSERVATION,\n },\n {\n '@_root': OID_SEVERITY_OBSERVATION,\n '@_extension': '2014-06-09',\n },\n ],\n code: {\n '@_code': 'SEV',\n '@_codeSystem': OID_ACT_CODE_CODE_SYSTEM,\n '@_codeSystemName': 'ActCode',\n },\n statusCode: {\n '@_code': 'completed',\n },\n value: {\n '@_xsi:type': 'CD',\n '@_code': ALLERGY_SEVERITY_MAPPER.mapFhirToCcdaWithDefault(\n reaction.severity,\n 'M'\n ),\n '@_displayName': reaction.severity ? capitalize(reaction.severity) : undefined,\n '@_codeSystem': OID_SNOMED_CT_CODE_SYSTEM,\n '@_codeSystemName': 'SNOMED CT',\n },\n text: this.createTextFromExtensions(reaction.extension),\n },\n ],\n },\n ],\n },\n ],\n },\n ]\n : [],\n },\n ],\n },\n ],\n },\n ],\n };\n }\n\n /**\n * Map the FHIR allergy category to the C-CDA allergy category.\n * @param category - The category to map.\n * @returns The C-CDA allergy category.\n */\n private mapAllergyCategory(category: AllergyIntolerance['category']): CcdaValue | undefined {\n if (!category) {\n return undefined;\n }\n\n if (category.length === 1 && category[0] === 'food') {\n return {\n '@_xsi:type': 'CD',\n '@_code': '414285001',\n '@_displayName': 'Allergy to food (finding)',\n '@_codeSystem': OID_SNOMED_CT_CODE_SYSTEM,\n '@_codeSystemName': 'SNOMED CT',\n };\n }\n\n return undefined;\n }\n\n /**\n * Map the FHIR author to the C-CDA author.\n * @param author - The author to map.\n * @param time - The time to map.\n * @returns The C-CDA author.\n */\n private mapAuthor(author: Reference | undefined, time?: string): CcdaAuthor[] | undefined {\n if (!author) {\n return undefined;\n }\n\n const practitioner = this.findResourceByReference(author);\n if (practitioner?.resourceType !== 'Practitioner') {\n return undefined;\n }\n\n return [\n {\n templateId: [\n {\n '@_root': OID_AUTHOR_PARTICIPANT,\n },\n ],\n time: time ? { '@_value': mapFhirToCcdaDateTime(time) } : undefined,\n assignedAuthor: {\n id: this.mapIdentifiers(practitioner.id, practitioner.identifier),\n addr: this.mapFhirAddressArrayToCcdaAddressArray(practitioner.address),\n telecom: this.mapTelecom(practitioner.telecom),\n code: mapCodeableConceptToCcdaCode(practitioner.qualification?.[0]),\n assignedPerson: {\n name: this.mapNames(practitioner.name),\n },\n },\n },\n ];\n }\n\n private mapCustodian(custodian: Reference<Organization> | undefined): CcdaCustodian | undefined {\n if (!custodian) {\n return undefined;\n }\n\n const organization = this.findResourceByReference(custodian);\n if (!organization) {\n return undefined;\n }\n\n return {\n assignedCustodian: {\n representedCustodianOrganization: {\n id: this.mapIdentifiers(organization.id, organization.identifier),\n name: organization.name ? [organization.name] : undefined,\n telecom: this.mapTelecom(organization.telecom),\n addr: this.mapFhirAddressArrayToCcdaAddressArray(organization.address),\n },\n },\n };\n }\n\n private mapDocumentationOf(events: CompositionEvent[] | undefined): CcdaDocumentationOf | undefined {\n if (!events || events.length === 0) {\n return undefined;\n }\n\n const event = events[0];\n if (!event || (!event.code && !event.period)) {\n return undefined;\n }\n\n return {\n serviceEvent: {\n '@_classCode': 'PCPR',\n code: mapCodeableConceptToCcdaCode(event.code?.[0]),\n effectiveTime: this.mapEffectiveDate(undefined, event.period),\n },\n };\n }\n\n /**\n * Create the C-CDA medication entry for the FHIR medication.\n * @param med - The FHIR medication to create the C-CDA medication entry for.\n * @returns The C-CDA medication entry.\n */\n private createMedicationEntry(med: MedicationRequest): CcdaEntry {\n // Get medication details either from contained resource or inline concept\n const medication = med.contained?.find((r) => r.resourceType === 'Medication');\n const medicationCode = medication?.code || med.medicationCodeableConcept;\n const manufacturer = medication?.manufacturer;\n\n // Get narrative references\n const medicationRef = '#Medication_6'; // Main reference\n const medicationNameRef = '#MedicationName_6'; // For manufacturedMaterial\n const sigRef = '#MedicationSig_6'; // For instructions\n\n return {\n substanceAdministration: [\n {\n '@_classCode': 'SBADM',\n '@_moodCode': 'EVN',\n templateId: [{ '@_root': OID_MEDICATION_ACTIVITY, '@_extension': '2014-06-09' }],\n id: [{ '@_root': med.id || crypto.randomUUID() }],\n text: { reference: { '@_value': medicationRef } },\n statusCode: { '@_code': MEDICATION_STATUS_MAPPER.mapFhirToCcdaWithDefault(med.status, 'active') },\n // effectiveTime: this.mapEffectiveTime(med.dosageInstruction?.[0]?.timing?.event?.[0], undefined),\n effectiveTime: this.mapEffectiveDate(undefined, med.dispenseRequest?.validityPeriod),\n routeCode: this.mapMedicationRoute(med.dosageInstruction?.[0]?.route),\n doseQuantity: this.mapDoseQuantity(med.dosageInstruction?.[0]?.doseAndRate?.[0]),\n consumable: {\n '@_typeCode': 'CSM',\n manufacturedProduct: [\n {\n '@_classCode': 'MANU',\n templateId: [\n { '@_root': OID_MEDICATION_INFORMATION_MANUFACTURED_MATERIAL, '@_extension': '2014-06-09' },\n ],\n manufacturedMaterial: [\n {\n code: [\n {\n ...(mapCodeableConceptToCcdaCode(medicationCode) as CcdaCode),\n originalText: { reference: { '@_value': medicationNameRef } },\n },\n ],\n },\n ],\n manufacturerOrganization: manufacturer\n ? [\n {\n id: this.mapIdentifiers(\n manufacturer.id,\n manufacturer.identifier ? [manufacturer.identifier] : undefined\n ),\n name: [manufacturer.display as string],\n },\n ]\n : undefined,\n },\n ],\n },\n entryRelationship: [\n {\n '@_typeCode': 'COMP',\n substanceAdministration: [\n {\n '@_classCode': 'SBADM',\n '@_moodCode': 'EVN',\n templateId: [{ '@_root': OID_MEDICATION_FREE_TEXT_SIG }],\n code: {\n '@_code': '76662-6',\n '@_codeSystem': OID_LOINC_CODE_SYSTEM,\n '@_codeSystemName': 'LOINC',\n '@_displayName': 'Medication Instructions',\n },\n text: { reference: { '@_value': sigRef } },\n consumable: {\n manufacturedProduct: [\n {\n manufacturedLabeledDrug: [\n {\n '@_nullFlavor': 'NA',\n },\n ],\n },\n ],\n },\n },\n ],\n },\n ],\n },\n ],\n };\n }\n\n /**\n * Map the FHIR medication route to the C-CDA medication route.\n * @param route - The route to map.\n * @returns The C-CDA medication route.\n */\n private mapMedicationRoute(route: CodeableConcept | undefined): CcdaCode | undefined {\n if (!route) {\n return undefined;\n }\n return mapCodeableConceptToCcdaCode(route);\n }\n\n /**\n * Map the FHIR dose quantity to the C-CDA dose quantity.\n * @param doseAndRate - The dose and rate to map.\n * @returns The C-CDA dose quantity.\n */\n private mapDoseQuantity(doseAndRate: DosageDoseAndRate | undefined): CcdaQuantity | undefined {\n if (!doseAndRate?.doseQuantity) {\n return undefined;\n }\n\n return {\n '@_xsi:type': 'PQ',\n '@_value': doseAndRate.doseQuantity.value?.toString(),\n '@_unit': doseAndRate.doseQuantity.unit,\n };\n }\n\n /**\n * Map the FHIR telecom to the C-CDA telecom.\n * @param contactPoints - The contact points to map.\n * @returns The C-CDA telecom.\n */\n private mapTelecom(contactPoints: ContactPoint[] | undefined): CcdaTelecom[] {\n if (!contactPoints || contactPoints.length === 0) {\n return [{ '@_nullFlavor': 'UNK' }];\n }\n return contactPoints?.map((cp) => ({\n '@_use': cp.use ? TELECOM_USE_MAPPER.mapFhirToCcda(cp.use as 'home' | 'work') : undefined,\n '@_value': `${this.mapTelecomSystemToPrefix(cp.system)}${cp.value}`,\n }));\n }\n\n /**\n * Map the FHIR telecom system to the C-CDA telecom system.\n * @param system - The system to map.\n * @returns The C-CDA telecom system.\n */\n private mapTelecomSystemToPrefix(system: string | undefined): string {\n if (system === 'email') {\n return 'mailto:';\n }\n if (system === 'phone') {\n return 'tel:';\n }\n if (system === 'fax') {\n return 'fax:';\n }\n return '';\n }\n\n /**\n * Map the FHIR identifiers to the C-CDA identifiers.\n * @param id - The FHIR resource ID\n * @param identifiers - The FHIR identifiers to map.\n * @returns The C-CDA identifiers.\n */\n private mapIdentifiers(id: string | undefined, identifiers: Identifier[] | undefined): CcdaId[] | undefined {\n const result: CcdaId[] = [];\n\n if (id) {\n result.push({ '@_root': id });\n }\n\n if (identifiers) {\n for (const id of identifiers) {\n const root = mapFhirSystemToCcda(id.system);\n if (!root) {\n continue;\n }\n result.push({ '@_root': root, '@_extension': id.value });\n }\n }\n\n return result;\n }\n\n /**\n * Get the narrative reference from the FHIR extensions.\n * @param extensions - The extensions to get the narrative reference from.\n * @returns The C-CDA narrative reference.\n */\n private getNarrativeReference(extensions: Extension[] | undefined): CcdaReference | undefined {\n const ref = extensions?.find((e) => e.url === CCDA_NARRATIVE_REFERENCE_URL)?.valueString;\n\n return ref ? { '@_value': ref } : undefined;\n }\n\n /**\n * Create the C-CDA observation text for the FHIR observation.\n * @param extensions - The extensions to create the C-CDA observation text for.\n * @returns The C-CDA observation text.\n */\n private createTextFromExtensions(extensions: Extension[] | undefined): CcdaText | undefined {\n const ref = this.getNarrativeReference(extensions);\n return ref ? { reference: ref } : undefined;\n }\n\n private createProblemEntry(problem: Condition): CcdaEntry {\n return {\n act: [\n {\n '@_classCode': 'ACT',\n '@_moodCode': 'EVN',\n templateId: [{ '@_root': OID_PROBLEM_ACT }, { '@_root': OID_PROBLEM_ACT, '@_extension': '2015-08-01' }],\n id: this.mapIdentifiers(problem.id, undefined),\n code: {\n '@_code': 'CONC',\n '@_codeSystem': OID_ACT_CLASS_CODE_SYSTEM,\n },\n // statusCode: { '@_code': this.mapStatus(problem.clinicalStatus?.coding?.[0]?.code) },\n statusCode: {\n '@_code': PROBLEM_STATUS_MAPPER.mapFhirToCcdaWithDefault(\n problem.clinicalStatus?.coding?.[0]?.code,\n 'active'\n ),\n },\n effectiveTime: this.mapEffectiveTime(problem.recordedDate, undefined),\n entryRelationship: [\n {\n '@_typeCode': 'SUBJ',\n observation: [\n {\n '@_classCode': 'OBS',\n '@_moodCode': 'EVN',\n templateId: [\n { '@_root': OID_PROBLEM_OBSERVATION },\n { '@_root': OID_PROBLEM_OBSERVATION, '@_extension': '2015-08-01' },\n ],\n id: this.mapIdentifiers(undefined, problem.identifier),\n text: this.createTextFromExtensions(problem.extension),\n code: {\n '@_code': '55607006',\n '@_codeSystem': OID_SNOMED_CT_CODE_SYSTEM,\n '@_codeSystemName': 'SNOMED CT',\n '@_displayName': 'Problem',\n },\n statusCode: { '@_code': 'completed' },\n effectiveTime: [\n {\n low: problem.onsetDateTime ? { '@_value': mapFhirToCcdaDate(problem.onsetDateTime) } : undefined,\n high: problem.abatementDateTime\n ? { '@_value': mapFhirToCcdaDateTime(problem.abatementDateTime) }\n : undefined,\n },\n ],\n value: mapCodeableConceptToCcdaValue(problem.code),\n author: this.mapAuthor(problem.asserter, problem.recordedDate),\n },\n ],\n },\n ],\n },\n ],\n };\n }\n\n private createImmunizationEntry(immunization: Immunization): CcdaEntry {\n const manufacturer = immunization?.manufacturer;\n const result = {\n substanceAdministration: [\n {\n '@_classCode': 'SBADM',\n '@_moodCode': 'EVN',\n '@_negationInd': 'false',\n templateId: [\n { '@_root': OID_IMMUNIZATION_ACTIVITY },\n { '@_root': OID_IMMUNIZATION_ACTIVITY, '@_extension': '2015-08-01' },\n ],\n id: this.mapIdentifiers(immunization.id, immunization.identifier),\n text: this.createTextFromExtensions(immunization.extension),\n statusCode: { '@_code': 'completed' },\n effectiveTime: [{ '@_value': mapFhirToCcdaDate(immunization.occurrenceDateTime) }],\n consumable: {\n manufacturedProduct: [\n {\n '@_classCode': 'MANU',\n templateId: [\n { '@_root': OID_IMMUNIZATION_MEDICATION_INFORMATION },\n { '@_root': OID_IMMUNIZATION_MEDICATION_INFORMATION, '@_extension': '2014-06-09' },\n ],\n manufacturedMaterial: [\n {\n code: [mapCodeableConceptToCcdaCode(immunization.vaccineCode) as CcdaCode],\n lotNumberText: immunization.lotNumber ? [immunization.lotNumber] : undefined,\n },\n ],\n manufacturerOrganization: manufacturer\n ? [\n {\n id: this.mapIdentifiers(\n manufacturer.id,\n manufacturer.identifier ? [manufacturer.identifier] : undefined\n ),\n name: [manufacturer.display as string],\n },\n ]\n : undefined,\n },\n ],\n },\n },\n ],\n } satisfies CcdaEntry;\n\n if (immunization.performer) {\n (result.substanceAdministration[0] as CcdaSubstanceAdministration).performer = immunization.performer\n .map((p) => this.mapImmunizationPerformerToCcdaPerformer(p))\n .filter(Boolean) as CcdaPerformer[];\n }\n\n return result;\n }\n\n /**\n * Map the FHIR author to the C-CDA performer.\n * @param performer - The performer to map.\n * @returns The C-CDA performer.\n */\n private mapImmunizationPerformerToCcdaPerformer(\n performer: ImmunizationPerformer | undefined\n ): CcdaPerformer | undefined {\n if (!performer) {\n return undefined;\n }\n\n const resource = this.findResourceByReference(performer.actor);\n if (!resource) {\n return undefined;\n }\n\n let practitioner: Practitioner | undefined = undefined;\n let organization: Organization | undefined = undefined;\n\n if (resource.resourceType === 'PractitionerRole') {\n practitioner = this.findResourceByReference(resource.practitioner) as Practitioner;\n organization = this.findResourceByReference(resource.organization) as Organization;\n } else if (resource.resourceType === 'Practitioner') {\n practitioner = resource as Practitioner;\n } else if (resource.resourceType === 'Organization') {\n organization = resource as Organization;\n }\n\n return {\n assignedEntity: {\n id: this.mapIdentifiers(resource.id, resource.identifier) as CcdaId[],\n addr: this.mapFhirAddressArrayToCcdaAddressArray(practitioner?.address),\n telecom: this.mapTelecom(resource.telecom),\n assignedPerson: practitioner\n ? {\n id: this.mapIdentifiers(practitioner.id, practitioner.identifier) as CcdaId[],\n name: this.mapNames(practitioner.name),\n }\n : undefined,\n representedOrganization: organization\n ? {\n id: this.mapIdentifiers(organization.id, organization.identifier) as CcdaId[],\n name: organization.name ? [organization.name] : undefined,\n addr: this.mapFhirAddressArrayToCcdaAddressArray(organization.address),\n telecom: this.mapTelecom(organization.telecom),\n }\n : undefined,\n },\n };\n }\n\n private createObservationEntry(observation: Observation): CcdaEntry {\n if (observation.hasMember) {\n // Organizer\n return {\n organizer: [this.createVitalSignsOrganizer(observation)],\n };\n } else {\n // Direct observation\n return {\n observation: [this.createVitalSignObservation(observation)],\n };\n }\n }\n\n private createPlanOfTreatmentCarePlanEntry(resource: CarePlan): CcdaEntry {\n return {\n act: [\n {\n '@_classCode': 'ACT',\n '@_moodCode': 'INT',\n id: this.mapIdentifiers(resource.id, resource.identifier),\n code: mapCodeableConceptToCcdaValue(resource.category?.[0]) as CcdaCode,\n templateId: [{ '@_root': OID_INSTRUCTIONS }],\n statusCode: { '@_code': 'completed' },\n text: resource.description\n ? { '#text': resource.description }\n : this.createTextFromExtensions(resource.extension),\n },\n ],\n };\n }\n\n private createPlanOfTreatmentGoalEntry(resource: Goal): CcdaEntry {\n const result: CcdaEntry = {\n observation: [\n {\n '@_classCode': 'OBS',\n '@_moodCode': 'GOL',\n templateId: [{ '@_root': OID_PLAN_OF_CARE_ACTIVITY_OBSERVATION }],\n id: this.mapIdentifiers(resource.id, resource.identifier),\n code: mapCodeableConceptToCcdaCode(resource.description),\n statusCode: { '@_code': this.mapGoalStatus(resource.lifecycleStatus) },\n effectiveTime: [{ '@_value': mapFhirToCcdaDateTime(resource.startDate) }],\n text: this.createTextFromExtensions(resource.extension),\n entryRelationship: resource.target?.map((target) => ({\n '@_typeCode': 'RSON',\n '@_inversionInd': 'true',\n act: [\n {\n '@_classCode': 'ACT',\n '@_moodCode': 'EVN',\n templateId: [\n { '@_root': OID_PROCEDURE_ACTIVITY_ACT },\n { '@_root': OID_PROCEDURE_ACTIVITY_ACT, '@_extension': '2014-06-09' },\n ],\n code: mapCodeableConceptToCcdaCode(target.measure) as CcdaCode,\n statusCode: { '@_code': 'completed' },\n effectiveTime: [{ '@_value': mapFhirToCcdaDateTime(resource.startDate) }],\n },\n ],\n })),\n },\n ],\n };\n\n return result;\n }\n\n private mapGoalStatus(status: string | undefined): string {\n switch (status) {\n case 'achieved':\n return 'completed';\n case 'cancelled':\n return 'cancelled';\n default:\n return 'active';\n }\n }\n\n private createVitalSignsOrganizer(observation: Observation): CcdaOrganizer {\n const components: CcdaOrganizerComponent[] = [];\n\n if (observation.hasMember) {\n for (const member of observation.hasMember) {\n const child = this.findResourceByReference(member);\n if (child) {\n components.push({\n observation: [this.createVitalSignObservation(child as Observation)],\n });\n }\n }\n }\n\n const result: CcdaOrganizer = {\n '@_classCode': 'CLUSTER',\n '@_moodCode': 'EVN',\n templateId: [\n { '@_root': OID_VITAL_SIGNS_ORGANIZER },\n { '@_root': OID_VITAL_SIGNS_ORGANIZER, '@_extension': '2015-08-01' },\n ],\n id: this.mapIdentifiers(observation.id, observation.identifier) as CcdaId[],\n code: mapCodeableConceptToCcdaCode(observation.code) as CcdaCode,\n statusCode: { '@_code': 'completed' },\n effectiveTime: [{ '@_value': mapFhirToCcdaDateTime(observation.effectiveDateTime) }],\n component: components,\n };\n\n return result;\n }\n\n private createVitalSignObservation(observation: Observation): CcdaObservation {\n const result: CcdaObservation = {\n '@_classCode': 'OBS',\n '@_moodCode': 'EVN',\n templateId: this.mapObservationTemplateId(observation),\n id: this.mapIdentifiers(observation.id, observation.identifier) as CcdaId[],\n code: mapCodeableConceptToCcdaCode(observation.code),\n statusCode: { '@_code': 'completed' },\n effectiveTime: [{ '@_value': mapFhirToCcdaDateTime(observation.effectiveDateTime) }],\n value: this.mapObservationValue(observation),\n referenceRange: this.mapReferenceRangeArray(observation.referenceRange),\n text: this.createTextFromExtensions(observation.extension),\n author: this.mapAuthor(observation.performer?.[0], observation.effectiveDateTime),\n };\n\n return result;\n }\n\n private mapObservationTemplateId(observation: Observation): CcdaTemplateId[] {\n // If the Observation.category includes at least one entry with system \"http://hl7.org/cda/template\",\n // then use those template IDs directly.\n const templateIds = observation.category\n ?.filter((c) => c.coding?.some((coding) => coding.system === CCDA_TEMPLATE_CODE_SYSTEM))\n .map((c) => c.coding?.find((coding) => coding.system === CCDA_TEMPLATE_CODE_SYSTEM))\n .filter((c) => c?.code) as (Coding & { code: string })[];\n\n if (templateIds && templateIds.length > 0) {\n return templateIds.map((id) =>\n id.version ? { '@_root': id.code, '@_extension': id.version } : { '@_root': id.code }\n );\n }\n\n // If the Observation.category includes at least one entry with a mapping in OBSERVATION_CATEGORY_MAPPER,\n // then use the template ID from the mapping.\n if (observation.category?.[0]?.coding?.[0]?.code) {\n const category = OBSERVATION_CATEGORY_MAPPER.getEntryByFhir(observation.category?.[0]?.coding?.[0]?.code);\n if (category) {\n return [{ '@_root': category.ccdaValue }, { '@_root': category.ccdaValue, '@_extension': '2014-06-09' }];\n }\n }\n\n // Otherwise, fall back to the default template ID.\n return [\n { '@_root': OID_VITAL_SIGNS_OBSERVATION },\n { '@_root': OID_VITAL_SIGNS_OBSERVATION, '@_extension': '2014-06-09' },\n ];\n }\n\n private mapObservationValue(observation: Observation): CcdaValue | undefined {\n if (observation.valueQuantity) {\n return {\n '@_xsi:type': 'PQ',\n '@_unit': observation.valueQuantity.unit,\n '@_value': observation.valueQuantity.value?.toString(),\n };\n }\n\n if (observation.valueCodeableConcept) {\n return mapCodeableConceptToCcdaValue(observation.valueCodeableConcept);\n }\n\n return undefined;\n }\n\n private mapReferenceRangeArray(\n referenceRange: ObservationReferenceRange[] | undefined\n ): CcdaReferenceRange[] | undefined {\n if (!referenceRange || referenceRange.length === 0) {\n return undefined;\n }\n\n return referenceRange.map((range) => this.mapReferenceRange(range)).filter(Boolean) as CcdaReferenceRange[];\n }\n\n private mapReferenceRange(referenceRange: ObservationReferenceRange | undefined): CcdaReferenceRange | undefined {\n if (!referenceRange) {\n return undefined;\n }\n\n const narrativeReference = this.getNarrativeReference(referenceRange.extension);\n if (narrativeReference) {\n // Special case for reference ranges that are a narrative reference\n return {\n observationRange: {\n text: { reference: narrativeReference },\n value: { '@_xsi:type': 'ED', reference: narrativeReference },\n },\n };\n }\n\n return {\n observationRange: {\n text: this.createTextFromExtensions(referenceRange.extension),\n },\n };\n }\n\n private createHistoryOfProceduresEntry(resource: Procedure | Observation): CcdaEntry | undefined {\n if (resource.resourceType === 'Procedure') {\n // A <procedure> in C-CDA typically represents a direct intervention, like a surgery, that changes a patient's physical state.\n // In contrast, an <act> is a broader category encompassing actions that don't necessarily alter the physical state, such as counseling, education, or referrals.\n // The key distinction lies in whether the action primarily focuses on a physical change in the patient or a broader interaction or process.\n const actCodes = [\n // Counseling and Education:\n '183948003', // Patient education (procedure)\n '409063005', // Counseling (procedure)\n '311331002', // Patient counseling (procedure)\n '61310001', // Nutrition education (procedure)\n // Care Management:\n '183945009', // Referral to specialist (procedure)\n '309814009', // Discharge planning (procedure)\n '278373008', // Home visit (procedure)\n // Social Services:\n '410606002', // Social service procedure (procedure)\n '183933003', // Social work assessment (procedure)\n // Other:\n '24642003', // Psychiatry procedure or service (procedure)\n '225338006', // Physiotherapy procedure (procedure)\n '128939004', // First aid (procedure)\n ];\n const procedureCode = resource.code?.coding?.[0]?.code;\n if (procedureCode && actCodes.includes(procedureCode)) {\n // Create an <act> entry\n return {\n act: [\n {\n '@_classCode': 'ACT',\n '@_moodCode': 'EVN',\n templateId: [\n { '@_root': OID_PROCEDURE_ACTIVITY_ACT },\n { '@_root': OID_PROCEDURE_ACTIVITY_ACT, '@_extension': '2014-06-09' },\n ],\n id: this.mapIdentifiers(resource.id, resource.identifier) as CcdaId[],\n code: mapCodeableConceptToCcdaCode(resource.code) as CcdaCode,\n statusCode: { '@_code': 'completed' },\n effectiveTime: this.mapEffectiveTime(resource.performedDateTime, resource.performedPeriod),\n text: this.createTextFromExtensions(resource.extension),\n },\n ],\n };\n }\n return {\n procedure: [\n {\n '@_classCode': 'PROC',\n '@_moodCode': 'EVN',\n templateId: [\n { '@_root': OID_PROCEDURE_ACTIVITY_PROCEDURE },\n { '@_root': OID_PROCEDURE_ACTIVITY_PROCEDURE, '@_extension': '2014-06-09' },\n ],\n id: this.mapIdentifiers(resource.id, resource.identifier) as CcdaId[],\n code: mapCodeableConceptToCcdaCode(resource.code) as CcdaCode,\n statusCode: { '@_code': 'completed' },\n effectiveTime: this.mapEffectiveTime(resource.performedDateTime, resource.performedPeriod),\n text: this.createTextFromExtensions(resource.extension),\n targetSiteCode: mapCodeableConceptToCcdaCode(resource.bodySite?.[0]) as CcdaCode,\n },\n ],\n };\n }\n if (resource.resourceType === 'Observation') {\n // Create an <observation> entry\n return {\n observation: [\n {\n '@_classCode': 'OBS',\n '@_moodCode': 'EVN',\n templateId: this.mapObservationTemplateId(resource),\n id: this.mapIdentifiers(resource.id, resource.identifier) as CcdaId[],\n code: mapCodeableConceptToCcdaCode(resource.code) as CcdaCode,\n value: this.mapObservationValue(resource),\n statusCode: { '@_code': 'completed' },\n effectiveTime: this.mapEffectiveTime(resource.effectiveDateTime, resource.effectivePeriod),\n text: this.createTextFromExtensions(resource.extension),\n },\n ],\n };\n }\n throw new Error(`Unknown history of procedures resource type: ${(resource as any).resourceType}`);\n }\n\n private mapEffectiveTime(dateTime: string | undefined, period: Period | undefined): CcdaEffectiveTime[] | undefined {\n if (period) {\n return [\n {\n '@_xsi:type': 'IVL_TS',\n low: { '@_value': mapFhirToCcdaDateTime(period.start) },\n high: { '@_value': mapFhirToCcdaDateTime(period.end) },\n },\n ];\n }\n if (dateTime) {\n return [\n {\n '@_xsi:type': 'TS',\n '@_value': mapFhirToCcdaDateTime(dateTime),\n },\n ];\n }\n return undefined;\n }\n\n private mapEffectiveDate(dateTime: string | undefined, period: Period | undefined): CcdaEffectiveTime[] | undefined {\n if (period) {\n return [\n {\n '@_xsi:type': 'IVL_TS',\n low: period.start ? { '@_value': mapFhirToCcdaDate(period.start) } : undefined,\n high: period.end ? { '@_value': mapFhirToCcdaDate(period.end) } : undefined,\n },\n ];\n }\n if (dateTime) {\n return [\n {\n '@_xsi:type': 'TS',\n '@_value': mapFhirToCcdaDate(dateTime),\n },\n ];\n }\n return undefined;\n }\n\n private createEncounterEntry(encounter: Encounter): CcdaEntry {\n return {\n encounter: [\n {\n '@_classCode': 'ENC',\n '@_moodCode': 'EVN',\n templateId: [\n {\n '@_root': OID_ENCOUNTER_ACTIVITIES,\n },\n {\n '@_root': OID_ENCOUNTER_ACTIVITIES,\n '@_extension': '2015-08-01',\n },\n ],\n id: this.mapIdentifiers(encounter.id, encounter.identifier),\n code: mapCodeableConceptToCcdaCode(encounter.type?.[0]),\n text: this.createTextFromExtensions(encounter.extension),\n effectiveTime: this.mapEffectiveTime(undefined, encounter.period),\n entryRelationship: encounter.participant?.map((participant) => ({\n '@_typeCode': 'LOC',\n participantRole: {\n '@_classCode': 'SDLOC',\n templateId: [\n {\n '@_root': OID_ENCOUNTER_LOCATION,\n },\n ],\n code: mapCodeableConceptToCcdaCode(participant.type?.[0]),\n },\n })),\n },\n ],\n };\n }\n\n private createCareTeamEntry(careTeam: CareTeam): CcdaEntry {\n return {\n organizer: [\n {\n '@_classCode': 'CLUSTER',\n '@_moodCode': 'EVN',\n templateId: [\n {\n '@_root': OID_CARE_TEAM_ORGANIZER_ENTRY,\n '@_extension': '2022-07-01',\n },\n {\n '@_root': OID_CARE_TEAM_ORGANIZER_ENTRY,\n '@_extension': '2022-06-01',\n },\n ],\n id: this.mapIdentifiers(careTeam.id, careTeam.identifier) as CcdaId[],\n component: careTeam.participant?.map((participant) => ({\n '@_typeCode': 'PRF',\n role: participant.role,\n })) as CcdaOrganizerComponent[],\n },\n ],\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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F,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,CACF,CACF,EACA,QAAW,CACT,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,IAAO,CACL,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,WAAc,CACZ,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,YAAa,CACX,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CACf,uDACA,kDACA,wDACA,sDACF,CACF,EACA,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,CACF,CACF,EACA,WAAc,CACZ,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,cACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,cACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,CACF,CACF,EACA,gBAAmB,CACjB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,OAAU,CACR,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,OAAU,CACR,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,aAAgB,CACd,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,CACF,CACF,EACA,cAAiB,CACf,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,cACV,CACF,CACF,CACF,CACF,EACA,aAAgB,CACd,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,YAAe,CACb,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,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,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,OAAU,CACR,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,sBAAyB,CACvB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,mBAAsB,CACpB,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,cAAe,CACb,KAAQ,CACN,CACE,KAAQ,SACV,EACA,CACE,KAAQ,iBACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,YAAe,CACb,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,mBACV,CACF,CACF,EACA,iBAAoB,CAClB,KAAQ,CACN,CACE,KAAQ,OACV,CACF,CACF,EACA,yBAA4B,CAC1B,KAAQ,CACN,CACE,KAAQ,WACR,QAAW,CAAC,wDAAwD,CACtE,CACF,CACF,EACA,mBAAsB,CACpB,KAAQ,CACN,CACE,KAAQ,WACR,QAAW,CAAC,wDAAwD,CACtE,CACF,CACF,CACF,CACF,EACA,kBAAqB,CACnB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,UAAW,CACT,KAAQ,CACN,CACE,KAAQ,OACV,EACA,CACE,KAAQ,WACR,QAAW,CAAC,wDAAwD,CACtE,CACF,CACF,EACA,UAAW,CACT,KAAQ,CACN,CACE,KAAQ,OACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,WACR,QAAW,CAAC,wDAAwD,CACtE,CACF,CACF,CACF,CACF,EACA,SAAY,CACV,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,kBAAqB,CACnB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,eAAkB,CAChB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,oBAAuB,CACrB,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,0BACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,aAAgB,CACd,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,MAAS,CACP,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,uBACV,CACF,CACF,EACA,iBAAoB,CAClB,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,uBACV,CACF,CACF,EACA,kBAAmB,CACjB,KAAQ,CACN,CACE,KAAQ,cACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,IACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,eACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,qBACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,mBACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,CACF,CACF,EACA,mBAAsB,CACpB,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,aAAgB,CACd,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,WAAY,CACV,KAAQ,CACN,CACE,KAAQ,cACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,IACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,eACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,qBACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,mBACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,CACF,CACF,EACA,aAAc,CACZ,KAAQ,CACN,CACE,KAAQ,cACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,IACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,eACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,qBACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,mBACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,CACF,CACF,EACA,QAAW,CACT,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,0BACV,CACF,CACF,EACA,cAAe,CACb,KAAQ,CACN,CACE,KAAQ,MACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,UACV,CACF,CACF,EACA,cAAe,CACb,KAAQ,CACN,CACE,KAAQ,MACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,UACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,IACV,CACF,CACF,EACA,WAAc,CACZ,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,6BACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,iBAAoB,CAClB,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,0BACV,CACF,CACF,EACA,QAAW,CACT,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,0BACV,CACF,CACF,CACF,CACF,EACA,sCAAyC,CACvC,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,yBAA4B,CAC1B,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,cAAiB,CACf,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,uCACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,MAAS,CACP,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,sBAAyB,CACvB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,IAAO,CACL,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,IAAO,CACL,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,sBAAyB,CACvB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,QAAW,CACT,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CACf,8DACA,6DACF,CACF,CACF,CACF,EACA,cAAiB,CACf,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CACf,8DACA,6DACF,CACF,CACF,CACF,EACA,YAAe,CACb,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,yBAA4B,CAC1B,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,WAAY,CACV,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,cACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,IACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,eACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,qBACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,mBACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,4BAA+B,CAC7B,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,IAAO,CACL,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,IACV,CACF,CACF,EACA,aAAgB,CACd,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,SAAY,CACV,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,MAAS,CACP,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CAAC,6DAA6D,CACjF,CACF,CACF,CACF,CACF,EACA,yBAA4B,CAC1B,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,SAAY,CACV,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CAAC,kDAAkD,CACtE,CACF,CACF,CACF,CACF,EACA,yBAA4B,CAC1B,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,SAAY,CACV,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,IACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,IAAO,CACL,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,WAAc,CACZ,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,IACV,CACF,CACF,EACA,SAAY,CACV,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,CACF,CACF,EACA,UAAa,CACX,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,IAAO,CACL,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,WAAY,CACV,KAAQ,CACN,CACE,KAAQ,cACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,IACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,SACV,EACA,CACE,KAAQ,KACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,WACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,eACV,EACA,CACE,KAAQ,aACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,YACV,EACA,CACE,KAAQ,qBACV,EACA,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,mBACV,EACA,CACE,KAAQ,cACV,EACA,CACE,KAAQ,QACV,EACA,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,UAAa,CACX,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,MAAS,CACP,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,WAAc,CACZ,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CAAC,sDAAsD,CAC1E,CACF,CACF,CACF,CACF,EACA,gBAAmB,CACjB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,QAAW,CACT,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,aAAgB,CACd,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,OAAU,CACR,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,UAAa,CACX,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,CACF,CACF,EACA,KAAQ,CACN,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,IACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,QAAW,CACT,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CAAC,6DAA6D,CACjF,CACF,CACF,EACA,SAAY,CACV,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,IAAO,CACL,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,SAAY,CACV,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,YAAe,CACb,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,CACF,CACF,EACA,MAAS,CACP,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,UAAa,CACX,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,OAAU,CACR,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,IAAO,CACL,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,OACV,CACF,CACF,CACF,CACF,EACA,oBAAuB,CACrB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,IAAO,CACL,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,cAAiB,CACf,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CAAC,6DAA6D,CACjF,CACF,CACF,CACF,CACF,EACA,OAAU,CACR,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,CACF,CACF,EACA,WAAc,CACZ,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,SAAU,CACR,KAAQ,CACN,CACE,KAAQ,OACV,EACA,CACE,KAAQ,iBACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,uBAA0B,CACxB,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,CACF,CACF,EACA,mBAAsB,CACpB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,cAAiB,CACf,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,iBAAoB,CAClB,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,MAAS,CACP,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,MAAS,CACP,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,YACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,CACF,CACF,EACA,iBAAoB,CAClB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,YACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,OAAU,CACR,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,6BAAgC,CAC9B,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,CACF,CACF,EACA,SAAY,CACV,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,MAAS,CACP,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,WACR,QAAW,CAAC,wDAAwD,CACtE,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,WACR,QAAW,CAAC,wDAAwD,CACtE,CACF,CACF,CACF,CACF,EACA,MAAS,CACP,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,CACF,CACF,EACA,UAAa,CACX,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,YACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,gBAAmB,CACjB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,IAAO,CACL,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,YACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CAAC,kDAAkD,CACtE,CACF,CACF,CACF,CACF,EACA,YAAe,CACb,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,OAAU,CACR,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,WACR,QAAW,CAAC,wDAAwD,CACtE,CACF,CACF,EACA,OAAU,CACR,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,CACF,CACF,EACA,UAAa,CACX,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,IAAO,CACL,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CACf,uDACA,2DACA,wDACA,kDACA,iDACA,sDACF,CACF,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,YACR,cAAiB,CACf,uDACA,2DACA,wDACA,kDACA,iDACA,sDACF,CACF,CACF,CACF,EACA,aAAgB,CACd,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,cACV,CACF,CACF,CACF,CACF,EACA,gBAAmB,CACjB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,YAAa,CACX,KAAQ,CACN,CACE,KAAQ,UACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,QACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,eAAkB,CAChB,KAAQ,CACN,CACE,KAAQ,+BACV,CACF,CACF,CACF,CACF,EACA,8BAAiC,CAC/B,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,CACF,CACF,EACA,OAAU,CACR,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,kBAAqB,CACnB,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,UACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,cACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,CACF,CACF,EACA,aAAgB,CACd,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,YAAa,CACX,KAAQ,CACN,CACE,KAAQ,UACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,QACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,SAAY,CACV,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,YAAe,CACb,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,aAAgB,CACd,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,aAAgB,CACd,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,aACV,CACF,CACF,CACF,CACF,EACA,kBAAqB,CACnB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,YAAa,CACX,KAAQ,CACN,CACE,KAAQ,QACV,EACA,CACE,KAAQ,YACR,cAAiB,CAAC,kDAAkD,CACtE,EACA,CACE,KAAQ,MACV,EACA,CACE,KAAQ,UACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,iBACV,CACF,CACF,EACA,UAAa,CACX,KAAQ,CACN,CACE,KAAQ,YACV,CACF,CACF,CACF,CACF,EACA,aAAgB,CACd,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,KAAQ,CACN,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,WAAY,CACV,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,iBACV,EACA,CACE,KAAQ,UACV,EACA,CACE,KAAQ,OACV,EACA,CACE,KAAQ,YACR,cAAiB,CACf,yDACA,wDACA,wDACA,4DACA,gDACA,mDACA,sDACF,CACF,CACF,CACF,CACF,CACF,EACA,cAAiB,CACf,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,eAAkB,CAChB,SAAY,CACV,GAAM,CACJ,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,UAAa,CACX,IAAO,iBACP,KAAQ,CACN,CACE,KAAQ,WACV,CACF,CACF,EACA,MAAS,CACP,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,OAAU,CACR,KAAQ,CACN,CACE,KAAQ,KACV,CACF,CACF,EACA,KAAQ,CACN,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,CACF,CACF,EACA,iBAAoB,CAClB,SAAY,CACV,aAAgB,CACd,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,SAAY,CACV,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,gBAAmB,CACjB,KAAQ,CACN,CACE,KAAQ,MACV,CACF,CACF,EACA,YAAe,CACb,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,SAAY,CACV,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,aAAgB,CACd,IAAO,EACP,KAAQ,CACN,CACE,KAAQ,QACV,CACF,CACF,EACA,QAAW,CACT,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,EACA,WAAc,CACZ,KAAQ,CACN,CACE,KAAQ,SACV,CACF,CACF,CACF,CACF,C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",
|
|
6
|
+
"names": ["index_exports", "__export", "ACT_CODE_SYSTEM", "ADDRESS_USE_MAPPER", "ADDRESS_USE_VALUE_SET", "ADMINISTRATIVE_GENDER_VALUE_SET", "ALLERGIES_SECTION_TEMPLATE_IDS", "ALLERGY_CLINICAL_CODE_SYSTEM", "ALLERGY_SEVERITY_MAPPER", "ALLERGY_STATUS_MAPPER", "ALLERGY_VERIFICATION_CODE_SYSTEM", "ASSESSMENTS_SECTION_TEMPLATE_IDS", "CARE_TEAM_SECTION_TEMPLATE_IDS", "CCDA_NARRATIVE_REFERENCE_URL", "CCDA_TEMPLATE_CODE_SYSTEM", "CCDA_TEMPLATE_IDS", "CLINICAL_CONDITION_CODE_SYSTEM", "CLINICAL_NOTES_SECTION_TEMPLATE_IDS", "CONDITION_CATEGORY_CODE_SYSTEM", "CONDITION_VERIFICATION_CODE_SYSTEM", "CONDITION_VER_STATUS_CODE_SYSTEM", "CONFIDENTIALITY_CODE_SYSTEM", "CONFIDENTIALITY_MAPPER", "CONTACT_ENTITY_USE_VALUE_SET", "CVX_URL", "DEVICES_SECTION_TEMPLATE_IDS", "DIAGNOSIS_ROLE_CODE_SYSTEM", "ENCOUNTERS_SECTION_TEMPLATE_IDS", "ENCOUNTER_STATUS_MAPPER", "EnumMapper", "FHIR_CVX_URL", "GENDER_MAPPER", "GOALS_SECTION_TEMPLATE_IDS", "HEALTH_CONCERNS_SECTION_TEMPLATE_IDS", "HTTP", "HUMAN_NAME_USE_MAPPER", "IMMUNIZATIONS_SECTION_TEMPLATE_IDS", "INSURANCE_SECTION_TEMPLATE_IDS", "LAB_TESTS_SECTION_TEMPLATE_IDS", "LANGUAGE_MODE_CODE_SYSTEM", "LANGUAGE_MODE_URL", "LANGUAGE_PROFICIENCY_CODE_SYSTEM", "LANGUAGE_PROFICIENCY_URL", "LOINC_TO_TEMPLATE_IDS", "MEDICATIONS_SECTION_TEMPLATE_IDS", "MEDICATION_REQUEST_STATUS_VALUE_SET", "MEDICATION_STATUS_MAPPER", "MENTAL_STATUS_SECTION_TEMPLATE_IDS", "NAME_USE_VALUE_SET", "NCI_THESAURUS_URL", "NDFRT_URL", "NUCC_TAXONOMY_URL", "OBSERVATION_CATEGORY_CODE_SYSTEM", "OBSERVATION_CATEGORY_MAPPER", "PARTICIPATION_CODE_SYSTEM", "PATIENT_NOTES_SECTION_TEMPLATE_IDS", "PLAN_OF_TREATMENT_SECTION_TEMPLATE_IDS", "PROBLEMS_SECTION_TEMPLATE_IDS", "PROBLEM_STATUS_MAPPER", "PROCEDURES_SECTION_TEMPLATE_IDS", "PROCEDURE_STATUS_MAPPER", "RACE_CODE_SYSTEM", "REASON_FOR_REFERRAL_SECTION_TEMPLATE_IDS", "RESULTS_SECTION_TEMPLATE_IDS", "SOCIAL_HISTORY_SECTION_TEMPLATE_IDS", "SYSTEM_MAPPER", "TELECOM_USE_MAPPER", "UNII_URL", "US_CORE_CONDITION_URL", "US_CORE_ETHNICITY_URL", "US_CORE_MEDICATION_REQUEST_URL", "US_CORE_PATIENT_URL", "US_CORE_RACE_URL", "US_DRIVER_LICENSE_URL", "US_NPI_URL", "US_SSN_URL", "VA_MEDRT_URL", "VITAL_SIGNS_SECTION_TEMPLATE_IDS", "XSI_URL", "convertCcdaToFhir", "convertCcdaToXml", "convertFhirToCcda", "convertToCompactXml", "convertXmlToCcda", "mapCcdaSystemToFhir", "mapCcdaToFhirDate", "mapCcdaToFhirDateTime", "mapCodeableConceptToCcdaCode", "mapCodeableConceptToCcdaValue", "mapFhirSystemToCcda", "mapFhirToCcdaDate", "mapFhirToCcdaDateTime", "parseXml", "__toCommonJS", "PrefixOperatorAtom", "operator", "child", "InfixOperatorAtom", "left", "right", "ParserBuilder", "tokenType", "parselet", "precedence", "builder", "parser", "token", "input", "Parser", "tokens", "prefixParselets", "infixParselets", "expected", "prefix", "next", "nextToken", "expectedId", "expectedValue", "actual", "t", "UCUM", "LOINC", "SNOMED", "RXNORM", "CPT", "NDC", "HTTP_HL7_ORG", "HTTP_TERMINOLOGY_HL7_ORG", "UNAUTHORIZED_ID", "unauthorized", "UNAUTHORIZED_ID", "unauthorizedTokenExpired", "unauthorizedTokenAudience", "inflateElement", "path", "partial", "max", "inflateBaseSchema", "base", "output", "key", "schema", "property", "base_schema_default", "DATA_TYPES", "inflateBaseSchema", "base_schema_default", "PROFILE_DATA_TYPES", "getDataTypesMap", "profileUrl", "dataTypes", "PROFILE_DATA_TYPES", "tryGetDataType", "type", "profileUrl", "profileType", "getDataTypesMap", "DATA_TYPES", "validationRegexes", "booleanToTypedValue", "value", "PropertyType", "toTypedValue", "isQuantity", "isResource", "isCodeableConcept", "isCoding", "toJsBoolean", "obj", "singleton", "collection", "type", "getTypedPropertyValue", "input", "path", "options", "elementDefinition", "getElementDefinition", "getTypedPropertyValueWithSchema", "getTypedPropertyValueWithoutSchema", "typedValue", "element", "types", "resultValue", "resultType", "primitiveExtension", "lastPathSegmentIndex", "lastPathSegment", "candidatePath", "capitalize", "i", "assignPrimitiveExtension", "isEmpty", "toTypedValueWithType", "result", "propertyValue", "trimmedPath", "propertyType", "propertyName", "v", "removeDuplicates", "arr", "found", "j", "fhirPathEquals", "fhirPathNot", "fhirPathArrayEquals", "x", "y", "val", "index", "fhirPathArrayNotEquals", "xValue", "yValue", "isQuantityEquivalent", "deepEquals", "fhirPathArrayEquivalent", "fhirPathEquivalentCompare", "fhirPathEquivalent", "xType", "xValueRaw", "yType", "yValueRaw", "fhirPathIs", "desiredType", "isDateString", "isDateTimeString", "isPeriod", "validationRegexes", "isQuantity", "input", "isQuantityEquivalent", "x", "y", "deepEquals", "object1", "object2", "keys1", "keys2", "key", "val1", "val2", "isObject", "obj", "assignPrimitiveExtension", "target", "primitiveExtension", "safeAssign", "source", "createReference", "resource", "reference", "getReferenceString", "display", "getDisplayString", "isReference", "resolveId", "isProfileResource", "resource", "getDisplayString", "profileName", "getProfileResourceDisplayString", "deviceName", "getDeviceDisplayString", "formatCodeableConcept", "code", "isCodeableConcept", "isTextObject", "getReferenceString", "names", "formatHumanName", "device", "calculateAge", "birthDateStr", "endDateStr", "startDate", "endDate", "startYear", "startMonth", "startDay", "endYear", "endMonth", "endDay", "years", "months", "days", "getExtension", "resource", "urls", "curr", "i", "e", "isEmpty", "v", "t", "isPopulated", "arg", "isUUID", "input", "isObject", "obj", "isCoding", "value", "isObject", "isCodeableConcept", "isTextObject", "byteToHex", "n", "capitalize", "word", "formatHumanName", "name", "options", "builder", "textStr", "ensureString", "formatCodeableConcept", "codeableConcept", "ensureString", "c", "formatCoding", "coding", "includeCode", "display", "code", "ensureString", "input", "PropertyType", "getElementDefinition", "typeName", "propertyName", "profileUrl", "typeSchema", "tryGetDataType", "getElementDefinitionFromElements", "elements", "simpleMatch", "i", "c", "testProperty", "element", "isResource", "value", "isReference", "parseDateString", "str", "stub", "functions", "_context", "input", "booleanToTypedValue", "e", "isEmpty", "context", "criteria", "toJsBoolean", "value", "other", "otherArray", "getRootInput", "PropertyType", "result", "num", "numValue", "removeDuplicates", "_input", "_other", "toTypedValue", "criterion", "trueResult", "otherwiseResult", "evalResult", "validateInput", "lowerStr", "isQuantity", "match", "substringAtom", "applyStringFunc", "substring", "startAtom", "lengthAtom", "start", "length", "startIndex", "endIndex", "prefixAtom", "prefix", "suffixAtom", "suffix", "patternAtom", "substitionAtom", "pattern", "substition", "regexAtom", "regex", "separatorAtom", "separator", "i", "applyMathFunc", "baseAtom", "base", "expAtom", "x", "_nameAtom", "endAtom", "unitsAtom", "startDate", "endDate", "unit", "age", "calculateAge", "typeAtom", "typeName", "SymbolAtom", "DotAtom", "fhirPathIs", "refStr", "ref", "resourceType", "id", "isResource", "systemAtom", "system", "expectedResourceType", "resource", "reference", "resolveId", "urlAtom", "url", "extension", "getExtension", "func", "argsAtoms", "atom", "quantity", "numberInput", "type", "returnValue", "count", "element", "last", "LiteralAtom", "value", "SymbolAtom", "name", "context", "input", "variableValue", "e", "typedValue", "isResource", "getTypedPropertyValue", "EmptySetAtom", "UnaryOperatorAtom", "PrefixOperatorAtom", "operator", "child", "impl", "AsAtom", "InfixOperatorAtom", "left", "right", "functions", "BooleanInfixOperatorAtom", "ArithemticOperatorAtom", "leftEvalResult", "rightEvalResult", "leftValue", "rightValue", "leftNumber", "isQuantity", "rightNumber", "result", "booleanToTypedValue", "PropertyType", "toTypedValue", "ConcatAtom", "ContainsAtom", "InAtom", "singleton", "DotAtom", "UnionAtom", "leftResult", "rightResult", "removeDuplicates", "EqualsAtom", "fhirPathArrayEquals", "NotEqualsAtom", "fhirPathArrayNotEquals", "EquivalentAtom", "fhirPathArrayEquivalent", "NotEquivalentAtom", "fhirPathNot", "IsAtom", "typeName", "fhirPathIs", "AndAtom", "OrAtom", "XorAtom", "ImpliesAtom", "FunctionAtom", "args", "arg", "IndexerAtom", "expr", "evalResult", "index", "FHIRPATH_OPERATORS", "OperatorPrecedence", "PARENTHESES_PARSELET", "parser", "expr", "INDEXER_PARSELET", "left", "IndexerAtom", "FUNCTION_CALL_PARSELET", "SymbolAtom", "args", "FunctionAtom", "parseQuantity", "str", "parts", "value", "unit", "initFhirPathParserBuilder", "ParserBuilder", "_", "token", "LiteralAtom", "PropertyType", "parseDateString", "EmptySetAtom", "right", "UnaryOperatorAtom", "x", "ArithemticOperatorAtom", "y", "DotAtom", "UnionAtom", "EqualsAtom", "NotEqualsAtom", "EquivalentAtom", "NotEquivalentAtom", "ConcatAtom", "AndAtom", "AsAtom", "ContainsAtom", "InAtom", "IsAtom", "OrAtom", "XorAtom", "ImpliesAtom", "fhirPathParserBuilder", "SearchParameterType", "Operator", "AccessPolicyInteraction", "generateId", "c", "r", "ContentType", "_a", "Events", "DEFAULT", "DEFAULT_ACCEPT", "ContentType", "OAuthGrantType", "OAuthTokenType", "OAuthTokenAuthMethod", "OAuthClientAssertionType", "MAPPING_LANGUAGE_OPERATORS", "FHIRPATH_OPERATORS", "fhirPathParserBuilder", "initFhirPathParserBuilder", "OperatorPrecedence", "DEFAULT_INDENT", "MAPPING_LANGUAGE_OPERATORS", "FHIRPATH_OPERATORS", "fhirPathParserBuilder", "initFhirPathParserBuilder", "LogLevel", "mapCcdaToFhirDate", "date", "year", "month", "day", "mapCcdaToFhirDateTime", "dateTime", "hour", "minute", "second", "tz", "mapFhirToCcdaDate", "mapFhirToCcdaDateTime", "time", "outDate", "outTime", "OID_REASON_FOR_REFERRAL", "OID_HL7_REGISTERED_MODELS", "OID_PAN_CANADIAN_LOINC_OBSERVATION_CODE_SYSTEM", "OID_NCI_THESAURUS_CODE_SYSTEM", "OID_US_SSN_CODE_SYSTEM", "OID_US_DLN_CODE_SYSTEM", "OID_US_NPI_CODE_SYSTEM", "OID_UNII_CODE_SYSTEM", "OID_ADMINISTRATIVE_GENDER_CODE_SYSTEM", "OID_ACT_CODE_CODE_SYSTEM", "OID_ACT_CLASS_CODE_SYSTEM", "OID_CONFIDENTIALITY_VALUE_SET", "OID_LOINC_CODE_SYSTEM", "OID_CPT_CODE_SYSTEM", "OID_MDC_CODE_SYSTEM", "OID_NDC_CODE_SYSTEM", "OID_RXNORM_CODE_SYSTEM", "OID_SNOMED_CT_CODE_SYSTEM", "OID_NUCC_TAXONOMY_CODE_SYSTEM", "OID_NDF_RT_CODE_SYSTEM", "OID_CDC_RACE_AND_ETHNICITY_CODE_SYSTEM", "OID_VA_MED_RT_CODE_SYSTEM", "OID_PREGNANCY_OBSERVATION", "OID_US_REALM_CDA_HEADER", "OID_CONTINUITY_OF_CARE_DOCUMENT", "OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED", "OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL", "OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED", "OID_RESULTS_SECTION_ENTRIES_REQUIRED", "OID_VITAL_SIGNS_SECTION_ENTRIES_REQUIRED", "OID_PROBLEMS_SECTION_ENTRIES_REQUIRED", "OID_ALLERGIES_SECTION_ENTRIES_REQUIRED", "OID_PROCEDURES_SECTION_ENTRIES_REQUIRED", "OID_ASSESSMENTS_SECTION", "OID_PLAN_OF_CARE_SECTION", "OID_SOCIAL_HISTORY_SECTION_ENTRIES_OPTIONAL", "OID_PAYERS_SECTION", "OID_ENCOUNTERS_SECTION_ENTRIES_REQUIRED", "OID_MEDICAL_EQUIPMENT_ENTRIES_OPTIONAL", "OID_MENTAL_STATUS_SECTION", "OID_HEALTH_CONCERNS_SECTION", "OID_GOALS_SECTION", "OID_NOTES_SECTION", "OID_CARE_TEAMS_SECTION", "OID_RESULT_ORGANIZER", "OID_RESULT_ORGANIZER_V2", "OID_RESULT_OBSERVATION", "OID_RESULT_OBSERVATION_V2", "OID_PROBLEM_ACT", "OID_PROBLEM_OBSERVATION", "OID_PROBLEM_OBSSERVATION_V2", "OID_ALLERGY_OBSERVATION", "OID_SEVERITY_OBSERVATION", "OID_REACTION_OBSERVATION", "OID_PROCEDURE_ACTIVITY_ACT", "OID_PROCEDURE_ACTIVITY_OBSERVATION", "OID_PROCEDURE_ACTIVITY_PROCEDURE", "OID_MEDICATION_ACTIVITY", "OID_INSTRUCTIONS", "OID_MEDICATION_INFORMATION_MANUFACTURED_MATERIAL", "OID_VITAL_SIGNS_ORGANIZER", "OID_VITAL_SIGNS_ORGANIZER_V2", "OID_VITAL_SIGNS_OBSERVATION", "OID_VITAL_SIGNS_OBSERVATION_V2", "OID_ALLERGY_PROBLEM_ACT", "OID_ENCOUNTER_LOCATION", "OID_SOCIAL_HISTORY_OBSERVATION", "OID_SOCIAL_HISTORY_OBSERVATION_V2", "OID_PLAN_OF_CARE_ACTIVITY_OBSERVATION", "OID_ENCOUNTER_ACTIVITIES", "OID_IMMUNIZATION_ACTIVITY", "OID_IMMUNIZATION_MEDICATION_INFORMATION", "OID_FUNCTIONAL_STATUS_RESULT_ORGANIZER", "OID_FUNCTIONAL_STATUS_RESULT_OBSERVATION", "OID_ASSESSMENT_SCALE_OBSERVATION", "OID_PRESSURE_ULCER_OBSERVATION", "OID_COGNITIVE_STATUS_RESULT_OBSERVATION", "OID_COGNITIVE_STATUS_RESULT_ORGANIZER", "OID_SMOKING_STATUS_OBSERVATION", "OID_CURRENT_SMOKING_STATUS_OBSERVATION", "OID_TOBACCO_USE_OBSERVATION", "OID_ASSESSMENT_SCALE_SUPPORTING_OBSERVATION", "OID_WOUND_OBSERVATION", "OID_AUTHOR_PARTICIPANT", "OID_GOAL_OBSERVATION", "OID_NUTRITION_RECOMMENDATION_V2", "OID_WOUND_MEASURMENTS_OBSERVATION", "OID_MEDICATION_FREE_TEXT_SIG", "OID_BIRTH_SEX", "OID_SECTION_TIME_RANGE", "OID_DIET_STATEMENT_NUTRITION", "OID_MONITORING_EVALUATION_AND_OUTCOME_NUTRITION", "OID_LABORATORY_BATTERY_ID", "OID_LABORATORY_OBSERVATION_ID", "OID_LABORATORY_RESULT_ORGANIZER_ID", "OID_CARE_TEAM_ORGANIZER_ENTRY", "OID_SEXUAL_ORIENTATION_OBSERVATION", "OID_BASIC_OCCUPATION_OBSERVATION", "OID_BASIC_INDUSTRY_OBSERVATION", "OID_TRIBAL_AFFILIATION_OBSERVATION", "OID_SEX_OBSERVATION", "OID_MEDICATION_ADHERENCE", "OID_AVERAGE_BLOOD_PRESSURE_ORGANIZER", "OID_CVX_CODE_SYSTEM", "EnumMapper", "systemName", "ccdaSystemOid", "fhirSystemUrl", "entries", "entry", "fhir", "ccda", "defaultValue", "HTTP", "CLINICAL_CONDITION_CODE_SYSTEM", "Ec", "CONDITION_VERIFICATION_CODE_SYSTEM", "LANGUAGE_MODE_CODE_SYSTEM", "LANGUAGE_PROFICIENCY_CODE_SYSTEM", "RACE_CODE_SYSTEM", "CONDITION_CATEGORY_CODE_SYSTEM", "CONDITION_VER_STATUS_CODE_SYSTEM", "ALLERGY_CLINICAL_CODE_SYSTEM", "ALLERGY_VERIFICATION_CODE_SYSTEM", "ACT_CODE_SYSTEM", "PARTICIPATION_CODE_SYSTEM", "DIAGNOSIS_ROLE_CODE_SYSTEM", "CONFIDENTIALITY_CODE_SYSTEM", "OBSERVATION_CATEGORY_CODE_SYSTEM", "ADDRESS_USE_VALUE_SET", "rn", "NAME_USE_VALUE_SET", "ADMINISTRATIVE_GENDER_VALUE_SET", "CONTACT_ENTITY_USE_VALUE_SET", "MEDICATION_REQUEST_STATUS_VALUE_SET", "LANGUAGE_MODE_URL", "LANGUAGE_PROFICIENCY_URL", "US_CORE_PATIENT_URL", "US_CORE_RACE_URL", "US_CORE_ETHNICITY_URL", "US_CORE_CONDITION_URL", "US_CORE_MEDICATION_REQUEST_URL", "CCDA_TEMPLATE_CODE_SYSTEM", "NCI_THESAURUS_URL", "US_SSN_URL", "US_DRIVER_LICENSE_URL", "US_NPI_URL", "UNII_URL", "NUCC_TAXONOMY_URL", "VA_MEDRT_URL", "NDFRT_URL", "CVX_URL", "FHIR_CVX_URL", "XSI_URL", "CCDA_NARRATIVE_REFERENCE_URL", "SYSTEM_MAPPER", "OID_PAN_CANADIAN_LOINC_OBSERVATION_CODE_SYSTEM", "OID_NCI_THESAURUS_CODE_SYSTEM", "OID_US_SSN_CODE_SYSTEM", "OID_US_DLN_CODE_SYSTEM", "OID_US_NPI_CODE_SYSTEM", "OID_UNII_CODE_SYSTEM", "OID_LOINC_CODE_SYSTEM", "gc", "OID_CPT_CODE_SYSTEM", "Tc", "OID_MDC_CODE_SYSTEM", "OID_NDC_CODE_SYSTEM", "bc", "OID_RXNORM_CODE_SYSTEM", "vc", "OID_SNOMED_CT_CODE_SYSTEM", "xc", "OID_NUCC_TAXONOMY_CODE_SYSTEM", "OID_VA_MED_RT_CODE_SYSTEM", "OID_NDF_RT_CODE_SYSTEM", "OID_CVX_CODE_SYSTEM", "mapCcdaSystemToFhir", "mapFhirSystemToCcda", "system", "mapCodeableConceptToCcdaCode", "codeableConcept", "systemEntry", "mapCodeableConceptToCcdaValue", "code", "CONFIDENTIALITY_MAPPER", "OID_CONFIDENTIALITY_VALUE_SET", "HUMAN_NAME_USE_MAPPER", "GENDER_MAPPER", "ADDRESS_USE_MAPPER", "TELECOM_USE_MAPPER", "ALLERGY_STATUS_MAPPER", "ALLERGY_SEVERITY_MAPPER", "PROBLEM_STATUS_MAPPER", "ENCOUNTER_STATUS_MAPPER", "PROCEDURE_STATUS_MAPPER", "MEDICATION_STATUS_MAPPER", "OBSERVATION_CATEGORY_MAPPER", "OID_SOCIAL_HISTORY_OBSERVATION", "OID_SOCIAL_HISTORY_OBSERVATION_V2", "OID_SMOKING_STATUS_OBSERVATION", "OID_CURRENT_SMOKING_STATUS_OBSERVATION", "OID_TOBACCO_USE_OBSERVATION", "OID_BASIC_OCCUPATION_OBSERVATION", "OID_BASIC_INDUSTRY_OBSERVATION", "OID_SEXUAL_ORIENTATION_OBSERVATION", "OID_SEX_OBSERVATION", "OID_BIRTH_SEX", "OID_SECTION_TIME_RANGE", "OID_PREGNANCY_OBSERVATION", "OID_TRIBAL_AFFILIATION_OBSERVATION", "OID_VITAL_SIGNS_ORGANIZER", "OID_VITAL_SIGNS_ORGANIZER_V2", "OID_VITAL_SIGNS_OBSERVATION", "OID_VITAL_SIGNS_OBSERVATION_V2", "OID_AVERAGE_BLOOD_PRESSURE_ORGANIZER", "OID_RESULT_ORGANIZER", "OID_RESULT_ORGANIZER_V2", "OID_RESULT_OBSERVATION", "OID_RESULT_OBSERVATION_V2", "OID_LABORATORY_BATTERY_ID", "OID_LABORATORY_OBSERVATION_ID", "OID_LABORATORY_RESULT_ORGANIZER_ID", "OID_ASSESSMENT_SCALE_OBSERVATION", "OID_ASSESSMENT_SCALE_SUPPORTING_OBSERVATION", "OID_COGNITIVE_STATUS_RESULT_OBSERVATION", "OID_COGNITIVE_STATUS_RESULT_ORGANIZER", "OID_FUNCTIONAL_STATUS_RESULT_OBSERVATION", "OID_FUNCTIONAL_STATUS_RESULT_ORGANIZER", "OID_PROBLEM_OBSERVATION", "OID_PROBLEM_OBSSERVATION_V2", "OID_PRESSURE_ULCER_OBSERVATION", "OID_WOUND_OBSERVATION", "OID_WOUND_MEASURMENTS_OBSERVATION", "OID_PROCEDURES_SECTION_ENTRIES_REQUIRED", "OID_PROCEDURE_ACTIVITY_OBSERVATION", "OID_MEDICATION_ADHERENCE", "OID_NUTRITION_RECOMMENDATION_V2", "OID_DIET_STATEMENT_NUTRITION", "OID_MONITORING_EVALUATION_AND_OUTCOME_NUTRITION", "import_fast_xml_parser", "ARRAY_PATHS", "convertXmlToCcda", "xml", "_tagName", "jPath", "_isLeafNode", "_isAttribute", "p", "convertCcdaToXml", "ccda", "XSI_URL", "parseXml", "convertToCompactXml", "obj", "line", "convertCcdaToFhir", "ccda", "CcdaToFhirConverter", "resource", "patientRole", "patient", "extensions", "US_CORE_RACE_URL", "raceCode", "US_CORE_ETHNICITY_URL", "ethnicGroupCode", "mapCcdaToFhirDate", "ids", "serverId", "id", "tl", "Se", "result", "mapCcdaSystemToFhir", "names", "n", "name", "use", "HUMAN_NAME_USE_MAPPER", "nodeToString", "addresses", "addr", "ADDRESS_USE_MAPPER", "telecoms", "tel", "TELECOM_USE_MAPPER", "components", "sections", "component", "section", "resources", "convertToCompactXml", "ne", "gc", "mapCcdaToFhirDateTime", "entry", "act", "substanceAdmin", "organizer", "observation", "encounter", "procedure", "templateId", "OID_ALLERGIES_SECTION_ENTRIES_REQUIRED", "OID_PROBLEMS_SECTION_ENTRIES_REQUIRED", "OID_PLAN_OF_CARE_SECTION", "OID_HEALTH_CONCERNS_SECTION", "OID_PROCEDURES_SECTION_ENTRIES_REQUIRED", "OID_REASON_FOR_REFERRAL", "OID_NOTES_SECTION", "OID_PAYERS_SECTION", "rel", "allergy", "allergenCode", "reactionObservations", "ro", "US_CORE_CONDITION_URL", "CLINICAL_CONDITION_CODE_SYSTEM", "CONDITION_VERIFICATION_CODE_SYSTEM", "CONDITION_CATEGORY_CODE_SYSTEM", "OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED", "OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL", "OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED", "cdaId", "medicationCode", "routeCode", "doseQuantity", "manufacturerOrg", "medication", "medicationCodeableConcept", "US_CORE_MEDICATION_REQUEST_URL", "MEDICATION_STATUS_MAPPER", "tn", "consumable", "reactionObs", "reaction", "severityObs", "severityCode", "ALLERGY_SEVERITY_MAPPER", "effectiveTime", "code", "value", "status", "ALLERGY_CLINICAL_CODE_SYSTEM", "ALLERGY_VERIFICATION_CODE_SYSTEM", "author", "practitioner", "assignedEntity", "assignedPerson", "representedOrganization", "organization", "practitionerRole", "custodian", "documentationOf", "serviceEvent", "array1", "array2", "performers", "performer", "entity", "reference", "OID_CARE_TEAMS_SECTION", "participants", "participant", "members", "child", "observationTemplateId", "OID_GOALS_SECTION", "OID_GOAL_OBSERVATION", "entryRelationship", "templateIds", "codes", "category", "OBSERVATION_CATEGORY_MAPPER", "CCDA_TEMPLATE_CODE_SYSTEM", "referenceRange", "r", "observationRange", "ENCOUNTER_STATUS_MAPPER", "ACT_CODE_SYSTEM", "PARTICIPATION_CODE_SYSTEM", "diagnoses", "condition", "CONDITION_VER_STATUS_CODE_SYSTEM", "DIAGNOSIS_ROLE_CODE_SYSTEM", "PROCEDURE_STATUS_MAPPER", "text", "CCDA_NARRATIVE_REFERENCE_URL", "node", "CCDA_TEMPLATE_IDS", "OID_US_REALM_CDA_HEADER", "OID_CONTINUITY_OF_CARE_DOCUMENT", "ALLERGIES_SECTION_TEMPLATE_IDS", "OID_ALLERGIES_SECTION_ENTRIES_REQUIRED", "MEDICATIONS_SECTION_TEMPLATE_IDS", "OID_MEDICATIONS_SECTION_ENTRIES_REQUIRED", "PROBLEMS_SECTION_TEMPLATE_IDS", "OID_PROBLEMS_SECTION_ENTRIES_REQUIRED", "IMMUNIZATIONS_SECTION_TEMPLATE_IDS", "OID_IMMUNIZATIONS_SECTION_ENTRIES_OPTIONAL", "OID_IMMUNIZATIONS_SECTION_ENTRIES_REQUIRED", "VITAL_SIGNS_SECTION_TEMPLATE_IDS", "OID_VITAL_SIGNS_SECTION_ENTRIES_REQUIRED", "SOCIAL_HISTORY_SECTION_TEMPLATE_IDS", "OID_SOCIAL_HISTORY_SECTION_ENTRIES_OPTIONAL", "PROCEDURES_SECTION_TEMPLATE_IDS", "OID_PROCEDURES_SECTION_ENTRIES_REQUIRED", "CLINICAL_NOTES_SECTION_TEMPLATE_IDS", "OID_PLAN_OF_CARE_SECTION", "LAB_TESTS_SECTION_TEMPLATE_IDS", "OID_RESULTS_SECTION_ENTRIES_REQUIRED", "RESULTS_SECTION_TEMPLATE_IDS", "DEVICES_SECTION_TEMPLATE_IDS", "OID_MEDICAL_EQUIPMENT_ENTRIES_OPTIONAL", "ASSESSMENTS_SECTION_TEMPLATE_IDS", "OID_ASSESSMENTS_SECTION", "PLAN_OF_TREATMENT_SECTION_TEMPLATE_IDS", "ENCOUNTERS_SECTION_TEMPLATE_IDS", "OID_ENCOUNTERS_SECTION_ENTRIES_REQUIRED", "GOALS_SECTION_TEMPLATE_IDS", "OID_GOALS_SECTION", "HEALTH_CONCERNS_SECTION_TEMPLATE_IDS", "OID_HEALTH_CONCERNS_SECTION", "REASON_FOR_REFERRAL_SECTION_TEMPLATE_IDS", "OID_REASON_FOR_REFERRAL", "MENTAL_STATUS_SECTION_TEMPLATE_IDS", "OID_MENTAL_STATUS_SECTION", "PATIENT_NOTES_SECTION_TEMPLATE_IDS", "OID_NOTES_SECTION", "CARE_TEAM_SECTION_TEMPLATE_IDS", "OID_CARE_TEAMS_SECTION", "INSURANCE_SECTION_TEMPLATE_IDS", "OID_PAYERS_SECTION", "LOINC_TO_TEMPLATE_IDS", "convertFhirToCcda", "bundle", "FhirToCcdaConverter", "composition", "patient", "sections", "OID_HL7_REGISTERED_MODELS", "CCDA_TEMPLATE_IDS", "OID_LOINC_CODE_SYSTEM", "CONFIDENTIALITY_MAPPER", "section", "resourceType", "e", "reference", "id", "references", "ref", "r", "names", "name", "HUMAN_NAME_USE_MAPPER", "gender", "GENDER_MAPPER", "I", "OID_ADMINISTRATIVE_GENDER_CODE_SYSTEM", "birthDate", "addresses", "addr", "address", "ADDRESS_USE_MAPPER", "extensions", "ombCategory", "US_CORE_RACE_URL", "OID_CDC_RACE_AND_ETHNICITY_CODE_SYSTEM", "US_CORE_ETHNICITY_URL", "communication", "sectionCode", "templateId", "LOINC_TO_TEMPLATE_IDS", "resources", "mapCodeableConceptToCcdaCode", "resource", "text", "result", "parseXml", "allergy", "reaction", "OID_ALLERGY_PROBLEM_ACT", "OID_ACT_CLASS_CODE_SYSTEM", "ALLERGY_STATUS_MAPPER", "OID_ALLERGY_OBSERVATION", "OID_ACT_CODE_CODE_SYSTEM", "OID_REACTION_OBSERVATION", "mapCodeableConceptToCcdaValue", "OID_SEVERITY_OBSERVATION", "ALLERGY_SEVERITY_MAPPER", "OID_SNOMED_CT_CODE_SYSTEM", "category", "author", "time", "practitioner", "OID_AUTHOR_PARTICIPANT", "mapFhirToCcdaDateTime", "custodian", "organization", "events", "event", "med", "medication", "medicationCode", "manufacturer", "OID_MEDICATION_ACTIVITY", "MEDICATION_STATUS_MAPPER", "OID_MEDICATION_INFORMATION_MANUFACTURED_MATERIAL", "OID_MEDICATION_FREE_TEXT_SIG", "route", "doseAndRate", "contactPoints", "cp", "TELECOM_USE_MAPPER", "system", "identifiers", "root", "mapFhirSystemToCcda", "CCDA_NARRATIVE_REFERENCE_URL", "problem", "OID_PROBLEM_ACT", "PROBLEM_STATUS_MAPPER", "OID_PROBLEM_OBSERVATION", "mapFhirToCcdaDate", "immunization", "OID_IMMUNIZATION_ACTIVITY", "OID_IMMUNIZATION_MEDICATION_INFORMATION", "p", "performer", "observation", "OID_INSTRUCTIONS", "OID_PLAN_OF_CARE_ACTIVITY_OBSERVATION", "target", "OID_PROCEDURE_ACTIVITY_ACT", "status", "components", "member", "child", "OID_VITAL_SIGNS_ORGANIZER", "templateIds", "c", "coding", "CCDA_TEMPLATE_CODE_SYSTEM", "OBSERVATION_CATEGORY_MAPPER", "OID_VITAL_SIGNS_OBSERVATION", "referenceRange", "range", "narrativeReference", "actCodes", "procedureCode", "OID_PROCEDURE_ACTIVITY_PROCEDURE", "dateTime", "period", "encounter", "OID_ENCOUNTER_ACTIVITIES", "participant", "OID_ENCOUNTER_LOCATION", "careTeam", "OID_CARE_TEAM_ORGANIZER_ENTRY"]
|
|
7
|
+
}
|