gdc-common-utils-ts 2.3.4 → 2.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/actor-session.d.ts +1 -0
- package/dist/constants/actor-session.js +7 -0
- package/dist/utils/bundle-entry-editor.js +2 -2
- package/dist/utils/bundle-query.d.ts +17 -0
- package/dist/utils/bundle-query.js +9 -2
- package/dist/utils/bundle-reader.d.ts +16 -0
- package/dist/utils/bundle-reader.js +56 -2
- package/dist/utils/client-assertion.d.ts +2 -2
- package/dist/utils/communication-bundle-document-request.d.ts +9 -8
- package/dist/utils/communication-bundle-document-request.js +14 -9
- package/dist/utils/communication-fhir-r4.js +24 -17
- package/package.json +5 -2
|
@@ -38,6 +38,7 @@ export declare const ActorCapabilities: Readonly<{
|
|
|
38
38
|
readonly IndividualImportIps: "individual.import_ips";
|
|
39
39
|
readonly IndividualGenerateDigitalTwin: "individual.generate_digital_twin";
|
|
40
40
|
readonly IndividualIngestCommunication: "individual.ingest_communication";
|
|
41
|
+
readonly IndividualReadClinicalSummary: "individual.read_clinical_summary";
|
|
41
42
|
readonly IndividualUpsertRelatedPerson: "individual.upsert_related_person";
|
|
42
43
|
readonly IndividualMemberDisable: "individual_member.disable";
|
|
43
44
|
readonly IndividualMemberPurge: "individual_member.purge";
|
|
@@ -40,6 +40,7 @@ export const ActorCapabilities = Object.freeze({
|
|
|
40
40
|
IndividualImportIps: 'individual.import_ips',
|
|
41
41
|
IndividualGenerateDigitalTwin: 'individual.generate_digital_twin',
|
|
42
42
|
IndividualIngestCommunication: 'individual.ingest_communication',
|
|
43
|
+
IndividualReadClinicalSummary: 'individual.read_clinical_summary',
|
|
43
44
|
IndividualUpsertRelatedPerson: 'individual.upsert_related_person',
|
|
44
45
|
IndividualMemberDisable: 'individual_member.disable',
|
|
45
46
|
IndividualMemberPurge: 'individual_member.purge',
|
|
@@ -161,6 +162,12 @@ export const ActorCapabilityDocs = Object.freeze({
|
|
|
161
162
|
programmingHint: 'Choose the route family carefully (`api`, `didcomm-plain`, `legacy-fhir`) to match the runtime transport profile.',
|
|
162
163
|
relatedMethods: ['ingestCommunicationAndUpdateIndex'],
|
|
163
164
|
},
|
|
165
|
+
[ActorCapabilities.IndividualReadClinicalSummary]: {
|
|
166
|
+
actorKind: ActorKinds.IndividualController,
|
|
167
|
+
summary: 'Reads the clinical summary currently available for one subject through an auditable Communication.',
|
|
168
|
+
programmingHint: 'Use requestClinicalSummary with Subject/$summary and attached FHIR Parameters. Do not route reads through ingestion methods.',
|
|
169
|
+
relatedMethods: ['requestClinicalSummary'],
|
|
170
|
+
},
|
|
164
171
|
[ActorCapabilities.IndividualUpsertRelatedPerson]: {
|
|
165
172
|
actorKind: ActorKinds.IndividualController,
|
|
166
173
|
summary: 'Creates or updates one related-person/member relationship for the individual scope.',
|
|
@@ -234,7 +234,7 @@ export class BundleEntryEditor {
|
|
|
234
234
|
resourceType: this.bundleEditor.getAllowedResourceType() || EmployeeResourceTypes.employee,
|
|
235
235
|
meta: { claims: {} },
|
|
236
236
|
};
|
|
237
|
-
entry.resource.meta = entry.resource.meta || {};
|
|
237
|
+
entry.resource.meta = entry.resource.meta || { claims: {} };
|
|
238
238
|
entry.resource.meta.claims = {
|
|
239
239
|
...(entry.resource.meta.claims || {}),
|
|
240
240
|
[String(key).trim()]: cloneClaimValue(value),
|
|
@@ -264,7 +264,7 @@ export class BundleEntryEditor {
|
|
|
264
264
|
resourceType: this.bundleEditor.getAllowedResourceType() || EmployeeResourceTypes.employee,
|
|
265
265
|
meta: { claims: {} },
|
|
266
266
|
};
|
|
267
|
-
entry.resource.meta = entry.resource.meta || {};
|
|
267
|
+
entry.resource.meta = entry.resource.meta || { claims: {} };
|
|
268
268
|
entry.resource.meta.claims = claims;
|
|
269
269
|
return this;
|
|
270
270
|
}
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import type { BundleEntry, BundleJsonApi } from '../models/bundle.js';
|
|
2
|
+
/** Canonical date interval shared by clinical Bundle read filters. */
|
|
3
|
+
export type BundleResourceDateFilter = Readonly<{
|
|
4
|
+
start?: string;
|
|
5
|
+
end?: string;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Filters Bundle resources by section, resource type and clinical date.
|
|
9
|
+
*
|
|
10
|
+
* Use `types` and `date` in new code. The flat names remain temporary
|
|
11
|
+
* compatibility aliases for callers created before the document readers were
|
|
12
|
+
* aligned with `FhirDocumentFacade`.
|
|
13
|
+
*/
|
|
2
14
|
export type BundleResourceIdFilters = Readonly<{
|
|
3
15
|
sections?: string | readonly string[];
|
|
16
|
+
types?: string | readonly string[];
|
|
17
|
+
date?: BundleResourceDateFilter;
|
|
18
|
+
/** @deprecated Use `types`. */
|
|
4
19
|
resourceTypes?: string | readonly string[];
|
|
20
|
+
/** @deprecated Use `date.start`. */
|
|
5
21
|
dateFrom?: string;
|
|
22
|
+
/** @deprecated Use `date.end`. */
|
|
6
23
|
dateTo?: string;
|
|
7
24
|
}>;
|
|
8
25
|
/**
|
|
@@ -82,7 +82,7 @@ export class BundleQuery {
|
|
|
82
82
|
}
|
|
83
83
|
matchesResourceFilters(entry, filters) {
|
|
84
84
|
const resourceType = asTrimmedString(entry?.resource?.resourceType);
|
|
85
|
-
const resourceTypeFilters = normalizeTokenInput(filters.resourceTypes);
|
|
85
|
+
const resourceTypeFilters = normalizeTokenInput(filters.types !== undefined ? filters.types : filters.resourceTypes);
|
|
86
86
|
if (resourceTypeFilters.length > 0 && !resourceTypeFilters.includes(resourceType)) {
|
|
87
87
|
return false;
|
|
88
88
|
}
|
|
@@ -92,7 +92,9 @@ export class BundleQuery {
|
|
|
92
92
|
return false;
|
|
93
93
|
}
|
|
94
94
|
const entryDate = this.resolveEntryDate(claims);
|
|
95
|
-
|
|
95
|
+
const dateFrom = filters.date !== undefined ? filters.date.start : filters.dateFrom;
|
|
96
|
+
const dateTo = filters.date !== undefined ? filters.date.end : filters.dateTo;
|
|
97
|
+
if (!this.matchesDateRange(entryDate, dateFrom, dateTo)) {
|
|
96
98
|
return false;
|
|
97
99
|
}
|
|
98
100
|
return true;
|
|
@@ -116,6 +118,11 @@ export class BundleQuery {
|
|
|
116
118
|
const normalized = String(key || '').toLowerCase();
|
|
117
119
|
if (normalized.endsWith('.date')
|
|
118
120
|
|| normalized.endsWith('.effective')
|
|
121
|
+
|| normalized.endsWith('.effective-datetime')
|
|
122
|
+
|| normalized.endsWith('.effective-period-start')
|
|
123
|
+
|| normalized.endsWith('.onset-datetime')
|
|
124
|
+
|| normalized.endsWith('.occurrence-datetime')
|
|
125
|
+
|| normalized.endsWith('.recorded-date')
|
|
119
126
|
|| normalized.endsWith('.sent')
|
|
120
127
|
|| normalized.endsWith('.authored-on')) {
|
|
121
128
|
const dateValue = asTrimmedString(value);
|
|
@@ -114,6 +114,21 @@ export declare class BundleReader {
|
|
|
114
114
|
getDocumentSectionResourceCount(sectionCodeOrClaim: string): number;
|
|
115
115
|
/** Returns bundle resource references listed under one document section. */
|
|
116
116
|
getDocumentSectionResourceReferences(sectionCodeOrClaim: string): string[];
|
|
117
|
+
/**
|
|
118
|
+
* Returns stable resource IDs that both belong to one Composition section
|
|
119
|
+
* and match the optional resource-type/date filters.
|
|
120
|
+
*
|
|
121
|
+
* Use this after `$summary` when a screen or channel needs the concrete
|
|
122
|
+
* resources for one section. `getDocumentSectionResourceCount(...)` counts
|
|
123
|
+
* declared Composition references; this method resolves those references
|
|
124
|
+
* against the returned Bundle and can narrow the result.
|
|
125
|
+
*/
|
|
126
|
+
getDocumentSectionResourceIds(sectionCodeOrClaim: string, filters?: BundleResourceIdFilters): string[];
|
|
127
|
+
/**
|
|
128
|
+
* Returns cloned Bundle entries for the resources selected from one
|
|
129
|
+
* Composition section, optionally filtered by type and inclusive date range.
|
|
130
|
+
*/
|
|
131
|
+
getDocumentSectionResourceEntries(sectionCodeOrClaim: string, filters?: BundleResourceIdFilters): BundleReaderEntry[];
|
|
117
132
|
/** Returns the active entry response status when present. */
|
|
118
133
|
getEntryResponseStatus(): string | undefined;
|
|
119
134
|
/** Returns all active entry issue severities. */
|
|
@@ -150,6 +165,7 @@ export declare class BundleReader {
|
|
|
150
165
|
private buildEntrySummary;
|
|
151
166
|
private buildSeverityBucket;
|
|
152
167
|
private resolveEntryIdentifier;
|
|
168
|
+
private resolveEntryReferenceCandidates;
|
|
153
169
|
}
|
|
154
170
|
export declare function unwrapBundleLikeResponseBody(input: unknown): Record<string, unknown>;
|
|
155
171
|
export declare function readFirstBundleResourceFromResponseBody(input: unknown): Record<string, unknown> | undefined;
|
|
@@ -136,7 +136,13 @@ export class BundleReader {
|
|
|
136
136
|
}
|
|
137
137
|
const entries = this.getEntries();
|
|
138
138
|
for (let index = 0; index < entries.length; index += 1) {
|
|
139
|
-
|
|
139
|
+
const resource = asRecord(entries[index].resource);
|
|
140
|
+
const resourceType = asNonEmptyString(resource.resourceType) || 'resource';
|
|
141
|
+
const candidates = new Set([
|
|
142
|
+
...this.resolveEntryReferenceCandidates(entries[index]),
|
|
143
|
+
`${resourceType}#${index}`,
|
|
144
|
+
]);
|
|
145
|
+
if (candidates.has(normalizedIdentifier)) {
|
|
140
146
|
return index;
|
|
141
147
|
}
|
|
142
148
|
}
|
|
@@ -235,7 +241,9 @@ export class BundleReader {
|
|
|
235
241
|
if (!normalized) {
|
|
236
242
|
return undefined;
|
|
237
243
|
}
|
|
238
|
-
|
|
244
|
+
const normalizedClaim = normalizeSectionClaim(normalized);
|
|
245
|
+
return this.getDocumentSections().find((section) => section.code === normalized
|
|
246
|
+
|| (section.claim !== undefined && normalizeSectionClaim(section.claim) === normalizedClaim));
|
|
239
247
|
}
|
|
240
248
|
/** Returns the number of resource references inside one document section. */
|
|
241
249
|
getDocumentSectionResourceCount(sectionCodeOrClaim) {
|
|
@@ -245,6 +253,30 @@ export class BundleReader {
|
|
|
245
253
|
getDocumentSectionResourceReferences(sectionCodeOrClaim) {
|
|
246
254
|
return [...(this.getDocumentSectionByCode(sectionCodeOrClaim)?.entryReferences || [])];
|
|
247
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Returns stable resource IDs that both belong to one Composition section
|
|
258
|
+
* and match the optional resource-type/date filters.
|
|
259
|
+
*
|
|
260
|
+
* Use this after `$summary` when a screen or channel needs the concrete
|
|
261
|
+
* resources for one section. `getDocumentSectionResourceCount(...)` counts
|
|
262
|
+
* declared Composition references; this method resolves those references
|
|
263
|
+
* against the returned Bundle and can narrow the result.
|
|
264
|
+
*/
|
|
265
|
+
getDocumentSectionResourceIds(sectionCodeOrClaim, filters = {}) {
|
|
266
|
+
const references = new Set(this.getDocumentSectionResourceReferences(sectionCodeOrClaim));
|
|
267
|
+
if (references.size === 0) {
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
return this.getResourceIds(filters).filter((resourceId) => this.getEntriesByIds([resourceId]).some((entry) => this.resolveEntryReferenceCandidates(entry)
|
|
271
|
+
.some((reference) => references.has(reference))));
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Returns cloned Bundle entries for the resources selected from one
|
|
275
|
+
* Composition section, optionally filtered by type and inclusive date range.
|
|
276
|
+
*/
|
|
277
|
+
getDocumentSectionResourceEntries(sectionCodeOrClaim, filters = {}) {
|
|
278
|
+
return this.getEntriesByIds(this.getDocumentSectionResourceIds(sectionCodeOrClaim, filters));
|
|
279
|
+
}
|
|
248
280
|
/** Returns the active entry response status when present. */
|
|
249
281
|
getEntryResponseStatus() {
|
|
250
282
|
const entry = this.getRequiredActiveEntry();
|
|
@@ -434,10 +466,32 @@ export class BundleReader {
|
|
|
434
466
|
.find((key) => String(key || '').toLowerCase().endsWith('.identifier'));
|
|
435
467
|
return identifierKey ? normalizeOptionalString(claims[identifierKey]) : undefined;
|
|
436
468
|
}
|
|
469
|
+
resolveEntryReferenceCandidates(entry) {
|
|
470
|
+
const resource = asRecord(entry.resource);
|
|
471
|
+
const resourceType = asNonEmptyString(resource.resourceType);
|
|
472
|
+
const resourceId = asNonEmptyString(resource.id);
|
|
473
|
+
return Array.from(new Set([
|
|
474
|
+
asNonEmptyString(entry.id),
|
|
475
|
+
asNonEmptyString(entry.fullUrl),
|
|
476
|
+
resourceId,
|
|
477
|
+
resourceType && resourceId ? `${resourceType}/${resourceId}` : undefined,
|
|
478
|
+
this.resolveEntryIdentifier(entry),
|
|
479
|
+
].filter((value) => Boolean(value))));
|
|
480
|
+
}
|
|
437
481
|
}
|
|
438
482
|
function normalizeOptionalString(value) {
|
|
439
483
|
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
440
484
|
}
|
|
485
|
+
function normalizeSectionClaim(value) {
|
|
486
|
+
const [system, ...codeParts] = value.split('|');
|
|
487
|
+
if (codeParts.length === 0) {
|
|
488
|
+
return value.trim().toLowerCase();
|
|
489
|
+
}
|
|
490
|
+
const normalizedSystem = system.trim().toLowerCase() === 'http://loinc.org'
|
|
491
|
+
? 'loinc'
|
|
492
|
+
: system.trim().toLowerCase();
|
|
493
|
+
return `${normalizedSystem}|${codeParts.join('|').trim().toLowerCase()}`;
|
|
494
|
+
}
|
|
441
495
|
export function unwrapBundleLikeResponseBody(input) {
|
|
442
496
|
const body = input && typeof input === 'object' ? input : {};
|
|
443
497
|
const nested = body.body && typeof body.body === 'object' ? body.body : undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type JWK } from 'jose';
|
|
2
2
|
export type ClientAssertionJwtAlgorithm = 'ES256' | 'ES384' | 'ES512' | 'EdDSA';
|
|
3
3
|
export type BuildClientAssertionJwtInput = {
|
|
4
4
|
clientId: string;
|
|
@@ -34,5 +34,5 @@ export declare function buildClientAssertionJwt(input: BuildClientAssertionJwtIn
|
|
|
34
34
|
*/
|
|
35
35
|
export declare function buildClientAssertionFixture(input: BuildClientAssertionJwtInput): Promise<{
|
|
36
36
|
jwt: string;
|
|
37
|
-
publicJwk:
|
|
37
|
+
publicJwk: JWK;
|
|
38
38
|
}>;
|
|
@@ -150,9 +150,10 @@ export declare function buildBundleSearchReferenceUrl(input: Readonly<{
|
|
|
150
150
|
/**
|
|
151
151
|
* Creates the canonical semantic parameters for an IPS summary-style request.
|
|
152
152
|
*
|
|
153
|
-
* These parameters are the source of truth.
|
|
154
|
-
*
|
|
155
|
-
* them
|
|
153
|
+
* These parameters are the source of truth. The canonical `$summary` read
|
|
154
|
+
* attaches them as one FHIR `Parameters` resource to an auditable
|
|
155
|
+
* `Communication`. Flattening them into a `Bundle/_search` reference is a
|
|
156
|
+
* compatibility path and must not be taught as the primary 101 read flow.
|
|
156
157
|
*/
|
|
157
158
|
export declare function createSummaryOperationRequestParameters(subjectIdOrInput: string | CreateSummaryOperationParametersInput, filterSections?: string[]): ReadonlyArray<ParameterData>;
|
|
158
159
|
/**
|
|
@@ -161,8 +162,8 @@ export declare function createSummaryOperationRequestParameters(subjectIdOrInput
|
|
|
161
162
|
*/
|
|
162
163
|
export declare function createSummaryOperationRequestReferencePath(parameters: ReadonlyArray<ParameterData>): string;
|
|
163
164
|
/**
|
|
164
|
-
* Builds the
|
|
165
|
-
*
|
|
165
|
+
* Builds the canonical FHIR `Parameters` body attached to a `$summary`
|
|
166
|
+
* request `Communication`.
|
|
166
167
|
*/
|
|
167
168
|
export declare function createSummaryOperationRequestParametersResource(parameters: ReadonlyArray<ParameterData>): FhirParametersResource;
|
|
168
169
|
export declare function buildCommunicationRequestOperationWithAttachedParametersClaims(input: CreateSummaryOperationCommunicationInput): Record<string, unknown>;
|
|
@@ -188,11 +189,11 @@ export declare const flattenParametersToSearchReference: typeof createSummaryOpe
|
|
|
188
189
|
* carried inside `Communication`.
|
|
189
190
|
*
|
|
190
191
|
* Current split:
|
|
191
|
-
* - `
|
|
192
|
-
* search-url contract
|
|
193
|
-
* - `setRequestSummaryOperation(...)` builds the operation contract where
|
|
192
|
+
* - `setRequestSummaryOperation(...)` is the canonical 101 read contract where
|
|
194
193
|
* `content-reference` points to the operation path and
|
|
195
194
|
* `content-attachment-data` carries the serialized FHIR `Parameters`
|
|
195
|
+
* - `newSearchWithReferencePath(...)` keeps the older flattened `_search`
|
|
196
|
+
* compatibility contract
|
|
196
197
|
*/
|
|
197
198
|
export declare const communication: Readonly<{
|
|
198
199
|
/**
|
|
@@ -217,9 +217,10 @@ export function buildBundleSearchReferenceUrl(input) {
|
|
|
217
217
|
/**
|
|
218
218
|
* Creates the canonical semantic parameters for an IPS summary-style request.
|
|
219
219
|
*
|
|
220
|
-
* These parameters are the source of truth.
|
|
221
|
-
*
|
|
222
|
-
* them
|
|
220
|
+
* These parameters are the source of truth. The canonical `$summary` read
|
|
221
|
+
* attaches them as one FHIR `Parameters` resource to an auditable
|
|
222
|
+
* `Communication`. Flattening them into a `Bundle/_search` reference is a
|
|
223
|
+
* compatibility path and must not be taught as the primary 101 read flow.
|
|
223
224
|
*/
|
|
224
225
|
export function createSummaryOperationRequestParameters(subjectIdOrInput, filterSections) {
|
|
225
226
|
const input = typeof subjectIdOrInput === 'string'
|
|
@@ -234,10 +235,14 @@ export function createSummaryOperationRequestParameters(subjectIdOrInput, filter
|
|
|
234
235
|
if (!documentTypeDescriptor) {
|
|
235
236
|
throw new Error(`Unsupported documentType: ${String(documentType)}`);
|
|
236
237
|
}
|
|
238
|
+
const sections = normalizeStringArray(input.filterSections);
|
|
239
|
+
if (sections.includes('*')) {
|
|
240
|
+
throw new Error('Omit filterSections to request all available sections; "*" is reserved for SMART permission scopes.');
|
|
241
|
+
}
|
|
237
242
|
return [
|
|
238
243
|
buildSubjectParameter(subjectDid),
|
|
239
244
|
buildDocumentTypeParameter(documentTypeDescriptor.id, documentTypeDescriptor.attributeValue),
|
|
240
|
-
...buildSectionParameters(
|
|
245
|
+
...buildSectionParameters(sections),
|
|
241
246
|
];
|
|
242
247
|
}
|
|
243
248
|
/**
|
|
@@ -267,8 +272,8 @@ export function createSummaryOperationRequestReferencePath(parameters) {
|
|
|
267
272
|
return `individual/org.hl7.fhir.r4/Bundle/_search?${params.filter(Boolean).join('&')}`;
|
|
268
273
|
}
|
|
269
274
|
/**
|
|
270
|
-
* Builds the
|
|
271
|
-
*
|
|
275
|
+
* Builds the canonical FHIR `Parameters` body attached to a `$summary`
|
|
276
|
+
* request `Communication`.
|
|
272
277
|
*/
|
|
273
278
|
export function createSummaryOperationRequestParametersResource(parameters) {
|
|
274
279
|
return buildFhirParametersResourceFromParameterData(parameters);
|
|
@@ -348,11 +353,11 @@ export const flattenParametersToSearchReference = createSummaryOperationRequestR
|
|
|
348
353
|
* carried inside `Communication`.
|
|
349
354
|
*
|
|
350
355
|
* Current split:
|
|
351
|
-
* - `
|
|
352
|
-
* search-url contract
|
|
353
|
-
* - `setRequestSummaryOperation(...)` builds the operation contract where
|
|
356
|
+
* - `setRequestSummaryOperation(...)` is the canonical 101 read contract where
|
|
354
357
|
* `content-reference` points to the operation path and
|
|
355
358
|
* `content-attachment-data` carries the serialized FHIR `Parameters`
|
|
359
|
+
* - `newSearchWithReferencePath(...)` keeps the older flattened `_search`
|
|
360
|
+
* compatibility contract
|
|
356
361
|
*/
|
|
357
362
|
export const communication = Object.freeze({
|
|
358
363
|
/**
|
|
@@ -35,7 +35,8 @@ export function transformCommunicationClaimsToResourceFhirR4(communicationClaims
|
|
|
35
35
|
const hasReference = Boolean(payloadReference);
|
|
36
36
|
const hasCode = Boolean(payloadCodeRaw);
|
|
37
37
|
const payloadKinds = [hasAttachment, hasReference, hasCode].filter(Boolean).length;
|
|
38
|
-
|
|
38
|
+
const isOperationReferenceWithParameters = hasAttachment && hasReference && !hasCode;
|
|
39
|
+
if (payloadKinds > 1 && !isOperationReferenceWithParameters) {
|
|
39
40
|
const msg = `Communication[${index}] has more than one payload kind (attachment/reference/code).`;
|
|
40
41
|
if (mode === 'strict')
|
|
41
42
|
throw new Error(msg);
|
|
@@ -50,10 +51,10 @@ export function transformCommunicationClaimsToResourceFhirR4(communicationClaims
|
|
|
50
51
|
throw new Error(msg);
|
|
51
52
|
warnings.push(`${msg} Keeping first note only.`);
|
|
52
53
|
}
|
|
53
|
-
const payload =
|
|
54
|
+
const payload = buildPayloads({
|
|
54
55
|
hasAttachment,
|
|
55
|
-
hasReference,
|
|
56
|
-
hasCode,
|
|
56
|
+
hasReference: isOperationReferenceWithParameters || (!hasAttachment && hasReference),
|
|
57
|
+
hasCode: !hasAttachment && !hasReference && hasCode,
|
|
57
58
|
payloadAttachmentData,
|
|
58
59
|
payloadAttachmentType,
|
|
59
60
|
payloadAttachmentTitle,
|
|
@@ -95,8 +96,8 @@ export function transformCommunicationClaimsToResourceFhirR4(communicationClaims
|
|
|
95
96
|
resource['sender'] = { reference: sender };
|
|
96
97
|
if (partOf)
|
|
97
98
|
resource['partOf'] = [{ reference: partOf }];
|
|
98
|
-
if (payload)
|
|
99
|
-
resource['payload'] =
|
|
99
|
+
if (payload.length)
|
|
100
|
+
resource['payload'] = payload;
|
|
100
101
|
if (noteValues.length)
|
|
101
102
|
resource['note'] = [{ text: noteValues[0] }];
|
|
102
103
|
return resource;
|
|
@@ -126,10 +127,13 @@ export function extractCommunicationClaimsFromResourceFhirR4(resource, options =
|
|
|
126
127
|
const partOfRef = resource?.partOf?.[0]?.reference;
|
|
127
128
|
const noteText = resource?.note?.[0]?.text;
|
|
128
129
|
const categoryCoding = resource?.category?.[0]?.coding?.[0];
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const
|
|
130
|
+
const payloads = resource?.payload || [];
|
|
131
|
+
const referencePayload = payloads.find((payload) => payload.contentReference !== undefined);
|
|
132
|
+
const attachmentPayload = payloads.find((payload) => payload.contentAttachment !== undefined);
|
|
133
|
+
const codePayload = payloads.find((payload) => payload.contentCodeableConcept !== undefined);
|
|
134
|
+
const contentReference = referencePayload?.contentReference?.reference;
|
|
135
|
+
const contentAttachment = attachmentPayload?.contentAttachment;
|
|
136
|
+
const contentCodeableConcept = codePayload?.contentCodeableConcept?.coding?.[0];
|
|
133
137
|
setIf(claims, CommunicationClaim.Identifier, identifierValue);
|
|
134
138
|
setIf(claims, CommunicationClaim.Status, status);
|
|
135
139
|
setIf(claims, CommunicationClaim.Sent, sent);
|
|
@@ -177,8 +181,12 @@ function normalizeNoteValues(raw) {
|
|
|
177
181
|
}
|
|
178
182
|
return [];
|
|
179
183
|
}
|
|
180
|
-
function
|
|
184
|
+
function buildPayloads(input) {
|
|
181
185
|
const { hasAttachment, hasReference, hasCode, payloadAttachmentData, payloadAttachmentType, payloadAttachmentTitle, payloadAttachmentUrl, payloadReference, payloadCodeRaw, } = input;
|
|
186
|
+
const payloads = [];
|
|
187
|
+
if (hasReference) {
|
|
188
|
+
payloads.push({ contentReference: { reference: payloadReference } });
|
|
189
|
+
}
|
|
182
190
|
if (hasAttachment) {
|
|
183
191
|
const value = {};
|
|
184
192
|
if (payloadAttachmentData)
|
|
@@ -189,13 +197,12 @@ function buildPayload(input) {
|
|
|
189
197
|
value['title'] = payloadAttachmentTitle;
|
|
190
198
|
if (payloadAttachmentUrl)
|
|
191
199
|
value['url'] = payloadAttachmentUrl;
|
|
192
|
-
|
|
200
|
+
payloads.push({ contentAttachment: value });
|
|
201
|
+
}
|
|
202
|
+
if (hasCode && payloadCodeRaw) {
|
|
203
|
+
payloads.push({ contentCodeableConcept: { coding: [parseSystemCode(payloadCodeRaw)] } });
|
|
193
204
|
}
|
|
194
|
-
|
|
195
|
-
return { contentReference: { reference: payloadReference } };
|
|
196
|
-
if (hasCode && payloadCodeRaw)
|
|
197
|
-
return { contentCodeableConcept: { coding: [parseSystemCode(payloadCodeRaw)] } };
|
|
198
|
-
return undefined;
|
|
205
|
+
return payloads;
|
|
199
206
|
}
|
|
200
207
|
function parseSystemCode(value) {
|
|
201
208
|
const trimmed = String(value || '').trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdc-common-utils-ts",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.6",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -19,7 +19,10 @@
|
|
|
19
19
|
"test": "jest",
|
|
20
20
|
"test:coverage": "jest --coverage",
|
|
21
21
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
22
|
-
"
|
|
22
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
23
|
+
"build": "npm run clean && tsc -p tsconfig.build.json && node patch-esm-imports.mjs",
|
|
24
|
+
"verify:dist": "node scripts/verify-dist-syntax.mjs",
|
|
25
|
+
"prepack": "npm run verify:dist",
|
|
23
26
|
"prepublishOnly": "npm run typecheck && npm test -- --watchman=false && npm run build"
|
|
24
27
|
},
|
|
25
28
|
"main": "./dist/index.js",
|