sos-sdk-core-ts 0.5.1 → 0.5.2
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/CHANGELOG.md +11 -0
- package/dist/communication-bundle.js +10 -1
- package/dist/encounter-transfer-communication.d.ts +28 -0
- package/dist/encounter-transfer-communication.js +51 -2
- package/docs/CARE_EMERGENCY_AND_ALERT_ORCHESTRATION.md +7 -3
- package/docs/ENCOUNTER_TRANSFER_PROJECTION.md +37 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.5.2 - 2026-10-04
|
|
6
|
+
|
|
7
|
+
- Add runtime-neutral accepted-handover orchestration that prepares the
|
|
8
|
+
Encounter/EncounterHistory projection together with the retained, added and
|
|
9
|
+
removed professional Group audience and a family-safe journey event.
|
|
10
|
+
- Preserve every inner resource identifier and project both provider-owned
|
|
11
|
+
Encounter snapshots with idempotent creates, so a subject index does not
|
|
12
|
+
require a prior copy of the source Encounter.
|
|
13
|
+
- Consume the neutral emergency Group builders and remove the obsolete
|
|
14
|
+
product-prefixed helper names from SDK tests and examples.
|
|
15
|
+
|
|
5
16
|
## 0.5.1 - 2026-10-04
|
|
6
17
|
|
|
7
18
|
- Add the shared Bundle synchronization draft contract for clinical documents,
|
|
@@ -153,7 +153,16 @@ export class CommunicationMessageEditor extends CommunicationAttachedBundleSessi
|
|
|
153
153
|
claims,
|
|
154
154
|
fullUrl: `urn:uuid:${identifier}`,
|
|
155
155
|
...(request ? { request } : {}),
|
|
156
|
-
})
|
|
156
|
+
});
|
|
157
|
+
const activePosition = this.getActiveEntryPosition();
|
|
158
|
+
if (activePosition === null)
|
|
159
|
+
throw new TypeError('communication_bundle_active_entry_required');
|
|
160
|
+
const bundle = this.getAttachedBundle();
|
|
161
|
+
const activeResource = bundle.data[activePosition]?.resource;
|
|
162
|
+
if (!activeResource)
|
|
163
|
+
throw new TypeError('communication_bundle_active_resource_required');
|
|
164
|
+
activeResource.id = identifier;
|
|
165
|
+
this.setAttachedBundle(bundle).saveAndReleaseActiveEntry();
|
|
157
166
|
return this;
|
|
158
167
|
}
|
|
159
168
|
/** Adds a PDF, image, audio or calendar file as an inner DocumentReference. */
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { type ClinicalCoding, type FhirResource } from 'fhir-data-utils-ts';
|
|
2
|
+
import { type CareJourneyMovementKindValue, type EncounterCommunicationAudienceReconciliation } from 'sos-data-utils-ts/encounter-communication-audience';
|
|
3
|
+
import type { ProfessionalServiceGroupAssignment } from 'sos-data-utils-ts/organization-service-catalog';
|
|
4
|
+
export { CareJourneyMovementKind } from 'sos-data-utils-ts/encounter-communication-audience';
|
|
5
|
+
export type { CareJourneyMovementKindValue, EncounterCommunicationAudienceReconciliation, } from 'sos-data-utils-ts/encounter-communication-audience';
|
|
2
6
|
export type EncounterTransferSourceDisposition = 'returning' | 'not-returning';
|
|
3
7
|
export type EncounterTransferProjectionInput = Readonly<{
|
|
4
8
|
communicationIdentifier: string;
|
|
@@ -21,6 +25,18 @@ export type EncounterTransferProjectionInput = Readonly<{
|
|
|
21
25
|
destinationHistoryIdentifier: string;
|
|
22
26
|
encounterClass: ClinicalCoding;
|
|
23
27
|
}>;
|
|
28
|
+
export type EncounterTransferOrchestrationInput = EncounterTransferProjectionInput & Readonly<{
|
|
29
|
+
communicationThreadId: string;
|
|
30
|
+
movementKind: CareJourneyMovementKindValue;
|
|
31
|
+
sourceAssignments: readonly ProfessionalServiceGroupAssignment[];
|
|
32
|
+
destinationAssignments: readonly ProfessionalServiceGroupAssignment[];
|
|
33
|
+
currentProfessionalGroupReferences: readonly string[];
|
|
34
|
+
handoverAccepted: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
export type EncounterTransferOrchestration = Readonly<{
|
|
37
|
+
projectionCommunication: Record<string, unknown>;
|
|
38
|
+
audienceReconciliation: EncounterCommunicationAudienceReconciliation;
|
|
39
|
+
}>;
|
|
24
40
|
/**
|
|
25
41
|
* Builds the completed Communication used to project an already-committed
|
|
26
42
|
* provider transfer to the subject index.
|
|
@@ -38,3 +54,15 @@ export type EncounterTransferProjectionInput = Readonly<{
|
|
|
38
54
|
* into the FHIR sender, author or attester fields.
|
|
39
55
|
*/
|
|
40
56
|
export declare function prepareEncounterTransferProjectionCommunication(input: EncounterTransferProjectionInput): Record<string, unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* Prepare one runtime-neutral transfer plan after the destination accepts
|
|
59
|
+
* handover.
|
|
60
|
+
*
|
|
61
|
+
* The product BFF authorizes the actor and resolves current service Group
|
|
62
|
+
* assignments before this call. Its persistence adapter then commits the
|
|
63
|
+
* Encounter/EncounterHistory transaction, the new future channel audience and
|
|
64
|
+
* the family-safe journey event under the same idempotency key. Historical
|
|
65
|
+
* Communication recipients remain immutable. This SDK neither stores state
|
|
66
|
+
* nor interprets Group membership as access authority.
|
|
67
|
+
*/
|
|
68
|
+
export declare function prepareEncounterTransferOrchestration(input: EncounterTransferOrchestrationInput): EncounterTransferOrchestration;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { EncounterClaim, EncounterHistoryClaim, EncounterStatusR5, buildEncounterHistoryR5FlatEntry, } from 'fhir-data-utils-ts';
|
|
2
|
+
import { CareJourneyMovementKind, reconcileEncounterCommunicationAudience, } from 'sos-data-utils-ts/encounter-communication-audience';
|
|
2
3
|
import { CommunicationBundleEditor } from './communication-bundle.js';
|
|
4
|
+
export { CareJourneyMovementKind } from 'sos-data-utils-ts/encounter-communication-audience';
|
|
3
5
|
/**
|
|
4
6
|
* Builds the completed Communication used to project an already-committed
|
|
5
7
|
* provider transfer to the subject index.
|
|
@@ -53,13 +55,60 @@ export function prepareEncounterTransferProjectionCommunication(input) {
|
|
|
53
55
|
.setSent(input.occurredAt)
|
|
54
56
|
.setAttachmentTitle('encounter-transfer-bundle.json')
|
|
55
57
|
.setInnerBundleType('batch')
|
|
56
|
-
.addResource(sourceUpdated, { method: '
|
|
58
|
+
.addResource(sourceUpdated, { method: 'POST', url: 'Encounter' })
|
|
57
59
|
.addResource(sourceHistory, { method: 'POST', url: 'EncounterHistory' })
|
|
58
|
-
.addResource(destinationUpdated, { method: '
|
|
60
|
+
.addResource(destinationUpdated, { method: 'POST', url: 'Encounter' })
|
|
59
61
|
.addResource(destinationHistory, { method: 'POST', url: 'EncounterHistory' })
|
|
60
62
|
.doneCommunication();
|
|
61
63
|
return editor.build();
|
|
62
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Prepare one runtime-neutral transfer plan after the destination accepts
|
|
67
|
+
* handover.
|
|
68
|
+
*
|
|
69
|
+
* The product BFF authorizes the actor and resolves current service Group
|
|
70
|
+
* assignments before this call. Its persistence adapter then commits the
|
|
71
|
+
* Encounter/EncounterHistory transaction, the new future channel audience and
|
|
72
|
+
* the family-safe journey event under the same idempotency key. Historical
|
|
73
|
+
* Communication recipients remain immutable. This SDK neither stores state
|
|
74
|
+
* nor interprets Group membership as access authority.
|
|
75
|
+
*/
|
|
76
|
+
export function prepareEncounterTransferOrchestration(input) {
|
|
77
|
+
assertMovementMatchesDisposition(input.movementKind, input.sourceDisposition);
|
|
78
|
+
const source = normalizeEncounter(input.sourceEncounter, input.subjectReference);
|
|
79
|
+
const destination = normalizeEncounter(input.destinationEncounter, input.subjectReference);
|
|
80
|
+
const sourceStatus = input.sourceDisposition === 'returning'
|
|
81
|
+
? EncounterStatusR5.OnHold
|
|
82
|
+
: EncounterStatusR5.Completed;
|
|
83
|
+
const projectionCommunication = prepareEncounterTransferProjectionCommunication(input);
|
|
84
|
+
const audienceReconciliation = reconcileEncounterCommunicationAudience({
|
|
85
|
+
idempotencyKey: input.communicationIdentifier,
|
|
86
|
+
communicationThreadId: input.communicationThreadId,
|
|
87
|
+
subjectReference: input.subjectReference,
|
|
88
|
+
movementKind: input.movementKind,
|
|
89
|
+
sourceEncounterReference: `Encounter/${source.id}`,
|
|
90
|
+
sourceEncounterStatus: sourceStatus,
|
|
91
|
+
sourceHealthcareServiceReference: text(claimsOf(source)[EncounterClaim.ServiceType]),
|
|
92
|
+
sourceAssignments: input.sourceAssignments,
|
|
93
|
+
destinationEncounterReference: `Encounter/${destination.id}`,
|
|
94
|
+
destinationEncounterStatus: EncounterStatusR5.InProgress,
|
|
95
|
+
destinationHealthcareServiceReference: text(claimsOf(destination)[EncounterClaim.ServiceType]),
|
|
96
|
+
destinationAssignments: input.destinationAssignments,
|
|
97
|
+
currentProfessionalGroupReferences: input.currentProfessionalGroupReferences,
|
|
98
|
+
occurredAt: input.occurredAt,
|
|
99
|
+
handoverAccepted: input.handoverAccepted,
|
|
100
|
+
});
|
|
101
|
+
return Object.freeze({ projectionCommunication, audienceReconciliation });
|
|
102
|
+
}
|
|
103
|
+
function assertMovementMatchesDisposition(movementKind, sourceDisposition) {
|
|
104
|
+
const matches = movementKind === CareJourneyMovementKind.TemporaryTransfer
|
|
105
|
+
? sourceDisposition === 'returning'
|
|
106
|
+
: (movementKind === CareJourneyMovementKind.PermanentTransfer
|
|
107
|
+
|| movementKind === CareJourneyMovementKind.ReturnTransfer)
|
|
108
|
+
&& sourceDisposition === 'not-returning';
|
|
109
|
+
if (!matches)
|
|
110
|
+
throw new TypeError('encounter_transfer_movement_disposition_mismatch');
|
|
111
|
+
}
|
|
63
112
|
function normalizeEncounter(resource, subjectReference) {
|
|
64
113
|
if (resource.resourceType !== 'Encounter')
|
|
65
114
|
throw new TypeError('fhir_encounter_required');
|
|
@@ -14,14 +14,18 @@ human policy stays in Vet and UHC.
|
|
|
14
14
|
3. Admission, diagnostics, intensive care, ward and discharge are represented
|
|
15
15
|
by Encounter/location continuity. Controllers see only what current Consent
|
|
16
16
|
and actor/subject authorization permit.
|
|
17
|
-
4.
|
|
17
|
+
4. Accepted service handover reconciles the future professional audience from
|
|
18
|
+
exact HealthcareService Group assignments. Temporary movement retains the
|
|
19
|
+
source Groups; definitive movement removes them. Historical Communication
|
|
20
|
+
recipients are immutable.
|
|
21
|
+
5. An authorized practitioner may start voice/video contact or send an
|
|
18
22
|
encrypted message to the controller or permitted related recipients before
|
|
19
23
|
publishing stressful results.
|
|
20
|
-
|
|
24
|
+
6. Voice/video signaling carries no clinical Bundle. A preliminary clinical
|
|
21
25
|
Bundle may be delivered by encrypted message only with `updateIndex=false`.
|
|
22
26
|
A call or preliminary message must not publish, attest or update the subject
|
|
23
27
|
index. Final clinical publication is a separate workflow.
|
|
24
|
-
|
|
28
|
+
7. A missed call may create an encrypted text/audio message in the same thread;
|
|
25
29
|
its recipients and thread authorization are revalidated by the service.
|
|
26
30
|
|
|
27
31
|
The orchestrator returns a plan. The persistence owner must enforce the
|
|
@@ -18,6 +18,43 @@ After the provider has committed those four records, it can call
|
|
|
18
18
|
completed `Communication` whose inner batch Bundle projects the same four
|
|
19
19
|
changes to the subject-index provider. The Communication is continuity and
|
|
20
20
|
discovery evidence; it is not a substitute for the provider transaction.
|
|
21
|
+
Both Encounter snapshots use idempotent `POST Encounter` commands because the
|
|
22
|
+
provider may already hold an Encounter that the subject index has never seen.
|
|
23
|
+
Their stable resource identifiers let the receiver return create-or-existing
|
|
24
|
+
readback without requiring a prior index record. Each milestone uses
|
|
25
|
+
`POST EncounterHistory` and retains its own stable identifier.
|
|
26
|
+
|
|
27
|
+
For a channel that follows the patient's journey, call
|
|
28
|
+
`prepareEncounterTransferOrchestration(...)` after the destination accepts the
|
|
29
|
+
handover. It returns the same projection Communication together with an
|
|
30
|
+
idempotent professional-Group audience reconciliation and a family-safe journey
|
|
31
|
+
event:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const transferPlan = prepareEncounterTransferOrchestration({
|
|
35
|
+
...encounterProjectionInput,
|
|
36
|
+
communicationThreadId: emergencyThreadId,
|
|
37
|
+
movementKind: patientWillReturn
|
|
38
|
+
? CareJourneyMovementKind.TemporaryTransfer
|
|
39
|
+
: CareJourneyMovementKind.PermanentTransfer,
|
|
40
|
+
sourceAssignments: authorizedSourceServiceGroups,
|
|
41
|
+
destinationAssignments: authorizedDestinationServiceGroups,
|
|
42
|
+
currentProfessionalGroupReferences,
|
|
43
|
+
handoverAccepted: true,
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The runtime persists the provider Encounter transaction, future channel
|
|
48
|
+
audience and journey event under the same idempotency key. A permanent transfer
|
|
49
|
+
removes the departed service Groups. A temporary transfer retains them while
|
|
50
|
+
adding the temporary destination Groups. Returning is another accepted
|
|
51
|
+
movement with the temporary Encounter as source and the resumed Encounter as
|
|
52
|
+
destination.
|
|
53
|
+
|
|
54
|
+
Only future messages use the reconciled audience. Previously sent
|
|
55
|
+
Communications keep their original recipient snapshots. Family recipients
|
|
56
|
+
receive the separately authorized journey projection, never professional Group
|
|
57
|
+
membership or internal clinical messages.
|
|
21
58
|
|
|
22
59
|
The department Organization is `Communication.sender`. Each Encounter keeps an
|
|
23
60
|
explicit `HealthcareService`. The authenticated professional/device remains
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sos-sdk-core-ts",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Runtime-neutral shared orchestration for SOS, Vet and UHC products",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Connecting Solution & Applications Ltd",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@noble/post-quantum": "0.7.1",
|
|
74
74
|
"fhir-data-utils-ts": "0.6.1",
|
|
75
75
|
"gdc-common-utils-ts": "2.9.21",
|
|
76
|
-
"sos-data-utils-ts": "0.6.
|
|
76
|
+
"sos-data-utils-ts": "0.6.5"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@types/node": "^22.5.0",
|