gdc-sdk-node-ts 2.3.1 → 2.3.3
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/did-web-recipient.d.ts +18 -0
- package/dist/did-web-recipient.js +46 -0
- package/dist/gdc-session-bridge.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/individual-controller-backend-runtime.d.ts +10 -5
- package/dist/individual-controller-backend-runtime.js +11 -4
- package/dist/node-managed-wallet.d.ts +1 -5
- package/dist/node-managed-wallet.js +35 -31
- package/dist/node-runtime-client.d.ts +10 -1
- package/dist/node-runtime-client.js +14 -1
- package/dist/orchestration/client-port.d.ts +2 -1
- package/dist/orchestration/individual-controller-sdk.d.ts +14 -2
- package/dist/orchestration/individual-controller-sdk.js +16 -1
- package/dist/orchestration/individual-member-sdk.d.ts +7 -2
- package/dist/orchestration/individual-member-sdk.js +8 -1
- package/dist/orchestration/personal-sdk.d.ts +3 -1
- package/dist/orchestration/personal-sdk.js +4 -0
- package/dist/orchestration/professional-sdk.d.ts +3 -1
- package/dist/orchestration/professional-sdk.js +4 -0
- package/dist/resource-operations.d.ts +15 -1
- package/dist/resource-operations.js +21 -1
- package/dist/server-profile-session.d.ts +25 -1
- package/dist/server-profile-session.js +44 -5
- package/package.json +4 -3
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { JWK } from 'gdc-common-utils-ts/models/jwk';
|
|
2
|
+
export type ResolveDidWebKeyAgreementJwkOptions = Readonly<{
|
|
3
|
+
fetchImpl?: typeof fetch;
|
|
4
|
+
/**
|
|
5
|
+
* Transport override for a local/private route serving the same DID document.
|
|
6
|
+
* The returned document must still identify itself with the requested DID.
|
|
7
|
+
*/
|
|
8
|
+
didDocumentUrl?: string;
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the first public key-agreement JWK from a did:web document.
|
|
12
|
+
*
|
|
13
|
+
* The DID remains the recipient identity and the document remains the source
|
|
14
|
+
* of its public keys. `didDocumentUrl` changes only the HTTP route used to
|
|
15
|
+
* retrieve that same document, which is useful when a local GW publishes its
|
|
16
|
+
* external DID while being reached through localhost.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveDidWebKeyAgreementJwk(recipientDid: string, options?: ResolveDidWebKeyAgreementJwkOptions): Promise<JWK>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { getBaseUrlFromDidWeb, normalizeDidWeb } from 'gdc-common-utils-ts/utils/did';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the first public key-agreement JWK from a did:web document.
|
|
5
|
+
*
|
|
6
|
+
* The DID remains the recipient identity and the document remains the source
|
|
7
|
+
* of its public keys. `didDocumentUrl` changes only the HTTP route used to
|
|
8
|
+
* retrieve that same document, which is useful when a local GW publishes its
|
|
9
|
+
* external DID while being reached through localhost.
|
|
10
|
+
*/
|
|
11
|
+
export async function resolveDidWebKeyAgreementJwk(recipientDid, options = {}) {
|
|
12
|
+
const did = normalizeDidWeb(String(recipientDid || '').trim());
|
|
13
|
+
if (!did.startsWith('did:web:')) {
|
|
14
|
+
throw new Error('resolveDidWebKeyAgreementJwk requires a did:web recipient.');
|
|
15
|
+
}
|
|
16
|
+
const didDocumentUrl = String(options.didDocumentUrl || '').trim()
|
|
17
|
+
|| `${getBaseUrlFromDidWeb(did)}.well-known/did.json`;
|
|
18
|
+
const response = await (options.fetchImpl || fetch)(didDocumentUrl, {
|
|
19
|
+
headers: { Accept: 'application/did+json, application/json' },
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new Error(`Could not resolve DID document '${did}' (HTTP ${response.status}).`);
|
|
23
|
+
}
|
|
24
|
+
const document = await response.json();
|
|
25
|
+
if (normalizeDidWeb(String(document.id || '').trim()) !== did) {
|
|
26
|
+
throw new Error(`Resolved DID document does not belong to '${did}'.`);
|
|
27
|
+
}
|
|
28
|
+
const methods = new Map((document.verificationMethod || []).map((method) => [method.id, method]));
|
|
29
|
+
for (const agreement of document.keyAgreement || []) {
|
|
30
|
+
const method = typeof agreement === 'string' ? methods.get(agreement) : agreement;
|
|
31
|
+
const jwk = publicKeyAgreementJwk(method);
|
|
32
|
+
if (jwk)
|
|
33
|
+
return jwk;
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`DID document '${did}' has no public keyAgreement JWK.`);
|
|
36
|
+
}
|
|
37
|
+
function publicKeyAgreementJwk(method) {
|
|
38
|
+
const publicKeyJwk = method?.publicKeyJwk;
|
|
39
|
+
if (!publicKeyJwk || typeof publicKeyJwk !== 'object')
|
|
40
|
+
return undefined;
|
|
41
|
+
if (publicKeyJwk.use && publicKeyJwk.use !== 'enc')
|
|
42
|
+
return undefined;
|
|
43
|
+
if (publicKeyJwk.kid || !method?.id.includes('#'))
|
|
44
|
+
return publicKeyJwk;
|
|
45
|
+
return { ...publicKeyJwk, kid: method.id.slice(method.id.lastIndexOf('#') + 1) };
|
|
46
|
+
}
|
|
@@ -21,6 +21,7 @@ const capabilityMap = {
|
|
|
21
21
|
[ActorCapabilities.IndividualImportIps]: ActorCapabilities.IndividualImportIps,
|
|
22
22
|
[ActorCapabilities.IndividualGenerateDigitalTwin]: ActorCapabilities.IndividualGenerateDigitalTwin,
|
|
23
23
|
[ActorCapabilities.IndividualIngestCommunication]: ActorCapabilities.IndividualIngestCommunication,
|
|
24
|
+
[ActorCapabilities.IndividualReadClinicalSummary]: ActorCapabilities.IndividualReadClinicalSummary,
|
|
24
25
|
[ActorCapabilities.IndividualUpsertRelatedPerson]: ActorCapabilities.IndividualUpsertRelatedPerson,
|
|
25
26
|
[ActorCapabilities.IndividualMemberDisable]: ActorCapabilities.IndividualMemberDisable,
|
|
26
27
|
[ActorCapabilities.IndividualMemberPurge]: ActorCapabilities.IndividualMemberPurge,
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export * from './UserProfileIndexStore.js';
|
|
|
26
26
|
export * from './wallet-backed-job-manager.js';
|
|
27
27
|
export * from './node-crypto-helper.js';
|
|
28
28
|
export * from './node-managed-wallet.js';
|
|
29
|
+
export * from './did-web-recipient.js';
|
|
29
30
|
export * from './constants/lifecycle.js';
|
|
30
31
|
export * from './consent-claim-helpers.js';
|
|
31
32
|
export * from './session.js';
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export * from './UserProfileIndexStore.js';
|
|
|
27
27
|
export * from './wallet-backed-job-manager.js';
|
|
28
28
|
export * from './node-crypto-helper.js';
|
|
29
29
|
export * from './node-managed-wallet.js';
|
|
30
|
+
export * from './did-web-recipient.js';
|
|
30
31
|
export * from './constants/lifecycle.js';
|
|
31
32
|
export * from './consent-claim-helpers.js';
|
|
32
33
|
export * from './session.js';
|
|
@@ -4,7 +4,7 @@ import type { IndividualOrganizationConfirmOrderInput, RouteContext } from './in
|
|
|
4
4
|
import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from './individual-start.js';
|
|
5
5
|
import type { EnsureFamilyOrganizationRegistrationInput, EnsureFamilyOrganizationRegistrationResult } from './family-organization-registration.js';
|
|
6
6
|
import type { FamilyOrganizationSearchInput } from './family-organization-search.js';
|
|
7
|
-
import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput } from './resource-operations.js';
|
|
7
|
+
import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput } from './resource-operations.js';
|
|
8
8
|
import { type BackendIndividualControllerProfile, type BackendProfileRuntimeClient } from './backend-profile-runtime.js';
|
|
9
9
|
import type { ProfileLoadRequest } from 'gdc-sdk-core-ts';
|
|
10
10
|
/**
|
|
@@ -17,8 +17,8 @@ import type { ProfileLoadRequest } from 'gdc-sdk-core-ts';
|
|
|
17
17
|
* - load an individual-controller profile
|
|
18
18
|
* - start individual registration/bootstrap
|
|
19
19
|
* - confirm the returned order/offer
|
|
20
|
-
* -
|
|
21
|
-
* -
|
|
20
|
+
* - request the available subject clinical summary through Communication
|
|
21
|
+
* - use direct index searches only for compatibility/specialized queries
|
|
22
22
|
*/
|
|
23
23
|
export declare class IndividualControllerBackendRuntime {
|
|
24
24
|
private readonly profileRuntime;
|
|
@@ -47,8 +47,13 @@ export declare class IndividualControllerBackendRuntime {
|
|
|
47
47
|
*/
|
|
48
48
|
confirmIndividualOrganizationOrder(profile: BackendIndividualControllerProfile, input: IndividualOrganizationConfirmOrderInput): Promise<SubmitAndPollResult>;
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
50
|
+
* Reads the authoritative subject document through the canonical
|
|
51
|
+
* `Communication -> Subject/$summary -> FHIR Parameters` lifecycle.
|
|
52
|
+
*/
|
|
53
|
+
requestClinicalSummary(profile: BackendIndividualControllerProfile, ctx: RouteContext, input: ClinicalSummaryRequestInput): Promise<ClinicalSummaryReadResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Compatibility/specialized direct search over the subject bundle index.
|
|
56
|
+
* Prefer `requestClinicalSummary(...)` for the available clinical document.
|
|
52
57
|
*/
|
|
53
58
|
searchClinicalBundle(profile: BackendIndividualControllerProfile, ctx: RouteContext, input: ClinicalBundleSearchInput): Promise<SubmitAndPollResult>;
|
|
54
59
|
/**
|
|
@@ -10,8 +10,8 @@ import { loadBackendIndividualControllerProfile, } from './backend-profile-runti
|
|
|
10
10
|
* - load an individual-controller profile
|
|
11
11
|
* - start individual registration/bootstrap
|
|
12
12
|
* - confirm the returned order/offer
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
13
|
+
* - request the available subject clinical summary through Communication
|
|
14
|
+
* - use direct index searches only for compatibility/specialized queries
|
|
15
15
|
*/
|
|
16
16
|
export class IndividualControllerBackendRuntime {
|
|
17
17
|
constructor(profileRuntime) {
|
|
@@ -51,8 +51,15 @@ export class IndividualControllerBackendRuntime {
|
|
|
51
51
|
return profile.sdk.confirmIndividualOrganizationOrder(input);
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* Reads the authoritative subject document through the canonical
|
|
55
|
+
* `Communication -> Subject/$summary -> FHIR Parameters` lifecycle.
|
|
56
|
+
*/
|
|
57
|
+
requestClinicalSummary(profile, ctx, input) {
|
|
58
|
+
return profile.sdk.requestClinicalSummary(ctx, input);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Compatibility/specialized direct search over the subject bundle index.
|
|
62
|
+
* Prefer `requestClinicalSummary(...)` for the available clinical document.
|
|
56
63
|
*/
|
|
57
64
|
searchClinicalBundle(profile, ctx, input) {
|
|
58
65
|
return profile.sdk.searchClinicalBundle(ctx, input);
|
|
@@ -51,9 +51,7 @@ export declare class NodeManagedWallet implements IWallet {
|
|
|
51
51
|
* Computes a digest of a string using the configured runtime helper.
|
|
52
52
|
*/
|
|
53
53
|
digest(data: string, algorithm: string): Promise<string>;
|
|
54
|
-
/**
|
|
55
|
-
* Protects one confidential document using one owner-specific symmetric storage key.
|
|
56
|
-
*/
|
|
54
|
+
/** Protects one document with a random AES-256-GCM CEK wrapped to the owner's storage ML-KEM key. */
|
|
57
55
|
protectConfidentialData(doc: any, entityId: string): Promise<any>;
|
|
58
56
|
/**
|
|
59
57
|
* Protects one confidential document using the richer execution-context model.
|
|
@@ -140,8 +138,6 @@ export declare class NodeManagedWallet implements IWallet {
|
|
|
140
138
|
private resolveOwnerId;
|
|
141
139
|
private resolveStorageOwnerId;
|
|
142
140
|
private createManagedKey;
|
|
143
|
-
private createStorageKey;
|
|
144
|
-
private requireStorageKey;
|
|
145
141
|
private requireManagedKey;
|
|
146
142
|
private inferRelevantScopes;
|
|
147
143
|
private matchesSelection;
|
|
@@ -13,6 +13,7 @@ const DEFAULT_POLICY = {
|
|
|
13
13
|
'vc-signing': 'ES384',
|
|
14
14
|
'comm-signing': 'ML-DSA-44',
|
|
15
15
|
'comm-encryption': 'ML-KEM-768',
|
|
16
|
+
'document-at-rest': 'ML-KEM-768',
|
|
16
17
|
},
|
|
17
18
|
};
|
|
18
19
|
/**
|
|
@@ -99,9 +100,6 @@ export class NodeManagedWallet {
|
|
|
99
100
|
ownerState.keys.push(descriptor);
|
|
100
101
|
created.push(descriptor.descriptor.publicJwk);
|
|
101
102
|
}
|
|
102
|
-
if (!ownerState.storageKey) {
|
|
103
|
-
ownerState.storageKey = await this.createStorageKey(request, ownerId);
|
|
104
|
-
}
|
|
105
103
|
return { keys: created };
|
|
106
104
|
}
|
|
107
105
|
/**
|
|
@@ -136,9 +134,7 @@ export class NodeManagedWallet {
|
|
|
136
134
|
async digest(data, algorithm) {
|
|
137
135
|
return this.cryptoHelper.digestString(data, algorithm);
|
|
138
136
|
}
|
|
139
|
-
/**
|
|
140
|
-
* Protects one confidential document using one owner-specific symmetric storage key.
|
|
141
|
-
*/
|
|
137
|
+
/** Protects one document with a random AES-256-GCM CEK wrapped to the owner's storage ML-KEM key. */
|
|
142
138
|
async protectConfidentialData(doc, entityId) {
|
|
143
139
|
return this.protectManagedConfidentialData(doc, {
|
|
144
140
|
profile: { profileId: entityId },
|
|
@@ -151,10 +147,24 @@ export class NodeManagedWallet {
|
|
|
151
147
|
async protectManagedConfidentialData(doc, context, _options) {
|
|
152
148
|
if (!doc?.content)
|
|
153
149
|
return doc;
|
|
154
|
-
const storageKey = this.
|
|
150
|
+
const storageKey = this.requireManagedKey(context, {
|
|
151
|
+
ownerScope: context.profile?.profileId ? 'profile' : 'runtime',
|
|
152
|
+
purpose: 'document-at-rest',
|
|
153
|
+
}, 'enc');
|
|
155
154
|
const ownerId = this.resolveStorageOwnerId(context);
|
|
156
|
-
const
|
|
157
|
-
const
|
|
155
|
+
const ownerBinding = createHash('sha256').update(ownerId).digest('base64url');
|
|
156
|
+
const privateJwk = {
|
|
157
|
+
...storageKey.descriptor.publicJwk,
|
|
158
|
+
dBytes: storageKey.privateMaterial,
|
|
159
|
+
};
|
|
160
|
+
const encrypted = await this.cryptography.encryptJweToCompact(JSON.stringify(doc.content), {
|
|
161
|
+
typ: 'gdc-confidential-document+jwe',
|
|
162
|
+
cty: 'application/json',
|
|
163
|
+
enc: 'A256GCM',
|
|
164
|
+
gdc_pq_profile: 'confidential-pqc-v1',
|
|
165
|
+
gdc_key_purpose: 'document-at-rest',
|
|
166
|
+
gdc_owner_binding: ownerBinding,
|
|
167
|
+
}, privateJwk, storageKey.descriptor.publicJwk);
|
|
158
168
|
const { content, ...docWithoutContent } = doc;
|
|
159
169
|
return {
|
|
160
170
|
...docWithoutContent,
|
|
@@ -176,13 +186,26 @@ export class NodeManagedWallet {
|
|
|
176
186
|
async unprotectManagedConfidentialData(doc, context, _options) {
|
|
177
187
|
if (!doc?.jwe)
|
|
178
188
|
return doc;
|
|
179
|
-
const storageKey = this.
|
|
189
|
+
const storageKey = this.requireManagedKey(context, {
|
|
190
|
+
ownerScope: context.profile?.profileId ? 'profile' : 'runtime',
|
|
191
|
+
purpose: 'document-at-rest',
|
|
192
|
+
}, 'enc');
|
|
180
193
|
const ownerId = this.resolveStorageOwnerId(context);
|
|
181
|
-
const
|
|
194
|
+
const privateJwk = {
|
|
195
|
+
...storageKey.descriptor.publicJwk,
|
|
196
|
+
dBytes: storageKey.privateMaterial,
|
|
197
|
+
};
|
|
198
|
+
const decrypted = await this.cryptography.decryptJwe(doc.jwe, privateJwk);
|
|
199
|
+
const header = decrypted.protectedHeader;
|
|
200
|
+
if (header.gdc_pq_profile !== 'confidential-pqc-v1'
|
|
201
|
+
|| header.gdc_key_purpose !== 'document-at-rest'
|
|
202
|
+
|| header.gdc_owner_binding !== createHash('sha256').update(ownerId).digest('base64url')) {
|
|
203
|
+
throw new Error('Confidential document is not bound to this storage owner and policy.');
|
|
204
|
+
}
|
|
182
205
|
const { jwe, ...docWithoutJwe } = doc;
|
|
183
206
|
return {
|
|
184
207
|
...docWithoutJwe,
|
|
185
|
-
content: JSON.parse(decrypted),
|
|
208
|
+
content: JSON.parse(Content.bytesToStringUTF8(decrypted.decryptedBytes)),
|
|
186
209
|
};
|
|
187
210
|
}
|
|
188
211
|
/**
|
|
@@ -480,25 +503,6 @@ export class NodeManagedWallet {
|
|
|
480
503
|
privateMaterial: signer.getPrivateMaterial(),
|
|
481
504
|
};
|
|
482
505
|
}
|
|
483
|
-
async createStorageKey(request, ownerId) {
|
|
484
|
-
if (request.mode === 'deterministic' && request.seedMaterial !== undefined) {
|
|
485
|
-
return this.deriveSeedBytes(request.seedMaterial, ownerId, 'document-at-rest', 32);
|
|
486
|
-
}
|
|
487
|
-
return this.cryptoHelper.getRandomBytes(32);
|
|
488
|
-
}
|
|
489
|
-
requireStorageKey(context) {
|
|
490
|
-
if (context.profile?.profileId) {
|
|
491
|
-
const state = this.owners.get(`profile:${context.profile.profileId}:${context.walletId ?? 'default'}`);
|
|
492
|
-
if (state?.storageKey)
|
|
493
|
-
return state.storageKey;
|
|
494
|
-
}
|
|
495
|
-
if (context.runtime?.runtimeId) {
|
|
496
|
-
const state = this.owners.get(`runtime:${context.runtime.runtimeId}:${context.walletId ?? 'default'}`);
|
|
497
|
-
if (state?.storageKey)
|
|
498
|
-
return state.storageKey;
|
|
499
|
-
}
|
|
500
|
-
throw new Error('NodeManagedWallet has no storage key for the provided context. Provision keys first.');
|
|
501
|
-
}
|
|
502
506
|
requireManagedKey(context, selection, expectedUse) {
|
|
503
507
|
if (selection.keyId) {
|
|
504
508
|
for (const ownerState of this.owners.values()) {
|
|
@@ -11,7 +11,7 @@ import { type SmartTokenRequestInput } from './smart-token.js';
|
|
|
11
11
|
import { type EmployeeDeviceActivationRequestInput, type EmployeeDeviceActivationResult } from './device-activation.js';
|
|
12
12
|
import { type OrganizationLicenseOrderConfirmInput } from './organization-license-order.js';
|
|
13
13
|
import { type IndividualOrganizationBootstrapInput, type IndividualOrganizationStartResult } from './individual-start.js';
|
|
14
|
-
import { type CommunicationIngestionInput, type BlockchainArtifactRegistrationInput, type VitalSignBatchCommunicationFromSearchResponseInput, type CommunicationParticipantRuntimeSearchInput, type ClinicalBundleSearchInput, type GrantProfessionalAccessInput, type GrantProfessionalAccessResult, type IndividualMemberLifecycleInput, type IndividualMemberLicenseAddInput, type IndividualMemberLicenseInvitationInput, type IndividualMemberLicenseTransitionInput, type LicenseListRuntimeSearchInput, type LicenseOfferRuntimeSearchInput, type LicenseOrderRuntimeSearchInput, type OrganizationEmployeeCreationInput, type OrganizationEmployeeLifecycleInput, type OrganizationEmployeeSearchInput, type RevokeProfessionalAccessInput, type RevokeProfessionalAccessResult, type RelatedPersonUpsertInput } from './resource-operations.js';
|
|
14
|
+
import { type CommunicationIngestionInput, type BlockchainArtifactRegistrationInput, type VitalSignBatchCommunicationFromSearchResponseInput, type CommunicationParticipantRuntimeSearchInput, type ClinicalBundleSearchInput, type ClinicalSummaryReadResult, type ClinicalSummaryRequestInput, type GrantProfessionalAccessInput, type GrantProfessionalAccessResult, type IndividualMemberLifecycleInput, type IndividualMemberLicenseAddInput, type IndividualMemberLicenseInvitationInput, type IndividualMemberLicenseTransitionInput, type LicenseListRuntimeSearchInput, type LicenseOfferRuntimeSearchInput, type LicenseOrderRuntimeSearchInput, type OrganizationEmployeeCreationInput, type OrganizationEmployeeLifecycleInput, type OrganizationEmployeeSearchInput, type RevokeProfessionalAccessInput, type RevokeProfessionalAccessResult, type RelatedPersonUpsertInput } from './resource-operations.js';
|
|
15
15
|
import type { LegalOrganizationOrderInput } from './host-onboarding.js';
|
|
16
16
|
import type { SmartTokenExchangeResult } from './smart-token.js';
|
|
17
17
|
import type { NodeRuntimeClient, PollOptions, PollResult, SubmitAndPollResult, SubmitPayload, SubmitResponse } from './orchestration/client-port.js';
|
|
@@ -402,6 +402,15 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
|
|
|
402
402
|
* @param input Communication payload plus route/format options.
|
|
403
403
|
*/
|
|
404
404
|
ingestCommunicationAndUpdateIndex(ctx: RouteContext, input: CommunicationIngestionInput): Promise<SubmitAndPollResult>;
|
|
405
|
+
/**
|
|
406
|
+
* Reads the authoritative clinical document currently available for one
|
|
407
|
+
* subject through an auditable `Communication` request to `Subject/$summary`.
|
|
408
|
+
*
|
|
409
|
+
* Unlike `ingestCommunicationAndUpdateIndex(...)`, this method does not
|
|
410
|
+
* represent a write or projection lifecycle. It returns the GW document and
|
|
411
|
+
* a `BundleReader` for section-by-section navigation.
|
|
412
|
+
*/
|
|
413
|
+
requestClinicalSummary(ctx: RouteContext, input: ClinicalSummaryRequestInput): Promise<ClinicalSummaryReadResult>;
|
|
405
414
|
/**
|
|
406
415
|
* Renders and submits one canonical clinical outbox job without requiring
|
|
407
416
|
* BFF, portal or assistant consumers to know GW wire envelopes.
|
|
@@ -12,7 +12,7 @@ import { activateEmployeeDeviceWithActivationCodeWithDeps, activateEmployeeDevic
|
|
|
12
12
|
import { extractOfferIdFromResponseBody, extractOfferPreviewFromResponseBody, } from './order-offer-summary.js';
|
|
13
13
|
import { confirmOrganizationLicenseOrderWithDeps } from './organization-license-order.js';
|
|
14
14
|
import { startIndividualOrganizationWithDeps } from './individual-start.js';
|
|
15
|
-
import { createOrganizationEmployeeWithDeps, disableIndividualMemberWithDeps, disableIndividualOrganizationWithDeps, disableOrganizationEmployeeWithDeps, listIndividualLicenseOffersWithDeps, listIndividualLicenseOrdersWithDeps, listIndividualLicensesWithDeps, addFreeIndividualMemberLicensesWithDeps, issueIndividualMemberLicenseWithDeps, transitionIndividualMemberLicenseWithDeps, listOrganizationLicenseOffersWithDeps, listOrganizationLicenseOrdersWithDeps, listOrganizationLicensesWithDeps, grantProfessionalAccessWithDeps, ingestCommunicationAndUpdateIndexWithDeps, buildVitalSignBatchCommunicationFromSearchResponse, registerBlockchainArtifactAndUpdateIndexWithDeps, revokeProfessionalAccessWithDeps, searchCommunicationParticipantsWithDeps, purgeIndividualMemberWithDeps, purgeIndividualOrganizationWithDeps, purgeOrganizationEmployeeWithDeps, searchIndividualLicensesWithDeps, searchIndividualLicenseOffersWithDeps, searchIndividualLicenseOrdersWithDeps, searchOrganizationLicensesWithDeps, searchOrganizationLicenseOffersWithDeps, searchOrganizationLicenseOrdersWithDeps, searchOrganizationEmployeesWithDeps, searchClinicalBundleWithDeps, searchLatestIpsWithDeps, upsertRelatedPersonAndPollWithDeps, normalizeCommunicationPathFormatSegment, } from './resource-operations.js';
|
|
15
|
+
import { createOrganizationEmployeeWithDeps, disableIndividualMemberWithDeps, disableIndividualOrganizationWithDeps, disableOrganizationEmployeeWithDeps, listIndividualLicenseOffersWithDeps, listIndividualLicenseOrdersWithDeps, listIndividualLicensesWithDeps, addFreeIndividualMemberLicensesWithDeps, issueIndividualMemberLicenseWithDeps, transitionIndividualMemberLicenseWithDeps, listOrganizationLicenseOffersWithDeps, listOrganizationLicenseOrdersWithDeps, listOrganizationLicensesWithDeps, grantProfessionalAccessWithDeps, ingestCommunicationAndUpdateIndexWithDeps, buildVitalSignBatchCommunicationFromSearchResponse, registerBlockchainArtifactAndUpdateIndexWithDeps, requestClinicalSummaryWithDeps, revokeProfessionalAccessWithDeps, searchCommunicationParticipantsWithDeps, purgeIndividualMemberWithDeps, purgeIndividualOrganizationWithDeps, purgeOrganizationEmployeeWithDeps, searchIndividualLicensesWithDeps, searchIndividualLicenseOffersWithDeps, searchIndividualLicenseOrdersWithDeps, searchOrganizationLicensesWithDeps, searchOrganizationLicenseOffersWithDeps, searchOrganizationLicenseOrdersWithDeps, searchOrganizationEmployeesWithDeps, searchClinicalBundleWithDeps, searchLatestIpsWithDeps, upsertRelatedPersonAndPollWithDeps, normalizeCommunicationPathFormatSegment, } from './resource-operations.js';
|
|
16
16
|
import { submitAndPollWithMethods } from './orchestration/client-port.js';
|
|
17
17
|
import { buildRuntimeHeaders, fetchWithTimeout, parseResponseBody, pollBatchResponseWithRuntimeConfig, postJsonWithRuntimeConfig, postRenderedWithRuntimeConfig, } from './runtime-transport.js';
|
|
18
18
|
import { activateOrganizationInGatewayFromIcaProofWithDeps, submitLegalOrganizationIssueWithDeps, submitLegalOrganizationVerificationTransactionWithDeps, submitOrganizationDidBindingWithDeps, } from './runtime-host-submission.js';
|
|
@@ -743,6 +743,19 @@ export class HttpRuntimeClient {
|
|
|
743
743
|
submitAndPoll: this.submitAndPoll.bind(this),
|
|
744
744
|
});
|
|
745
745
|
}
|
|
746
|
+
/**
|
|
747
|
+
* Reads the authoritative clinical document currently available for one
|
|
748
|
+
* subject through an auditable `Communication` request to `Subject/$summary`.
|
|
749
|
+
*
|
|
750
|
+
* Unlike `ingestCommunicationAndUpdateIndex(...)`, this method does not
|
|
751
|
+
* represent a write or projection lifecycle. It returns the GW document and
|
|
752
|
+
* a `BundleReader` for section-by-section navigation.
|
|
753
|
+
*/
|
|
754
|
+
async requestClinicalSummary(ctx, input) {
|
|
755
|
+
return requestClinicalSummaryWithDeps(ctx, input, {
|
|
756
|
+
submitSummaryCommunication: this.submitCommunicationOutboxJobAndPoll.bind(this),
|
|
757
|
+
});
|
|
758
|
+
}
|
|
746
759
|
/**
|
|
747
760
|
* Renders and submits one canonical clinical outbox job without requiring
|
|
748
761
|
* BFF, portal or assistant consumers to know GW wire envelopes.
|
|
@@ -13,7 +13,7 @@ import type { FamilyOrganizationSearchInput } from '../family-organization-searc
|
|
|
13
13
|
import type { EnsureFamilyOrganizationRegistrationInput, EnsureFamilyOrganizationRegistrationResult } from '../family-organization-registration.js';
|
|
14
14
|
import type { OrganizationLicenseOrderConfirmInput } from '../organization-license-order.js';
|
|
15
15
|
import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
|
|
16
|
-
import type { CommunicationIngestionInput, BlockchainArtifactRegistrationInput, CommunicationParticipantRuntimeSearchInput, ClinicalBundleSearchInput, VitalSignBatchCommunicationFromSearchResponseInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLifecycleInput, OrganizationEmployeeSearchInput, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, RelatedPersonUpsertInput } from '../resource-operations.js';
|
|
16
|
+
import type { CommunicationIngestionInput, BlockchainArtifactRegistrationInput, CommunicationParticipantRuntimeSearchInput, ClinicalBundleSearchInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, VitalSignBatchCommunicationFromSearchResponseInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLifecycleInput, OrganizationEmployeeSearchInput, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, RelatedPersonUpsertInput } from '../resource-operations.js';
|
|
17
17
|
/**
|
|
18
18
|
* Shared node-runtime activation input.
|
|
19
19
|
*
|
|
@@ -89,6 +89,7 @@ export type RuntimeClient = {
|
|
|
89
89
|
disableIndividualOrganization?: (ctx: RouteContext, input: IndividualOrganizationLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
90
90
|
purgeIndividualOrganization?: (ctx: RouteContext, input: IndividualOrganizationLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
91
91
|
ingestCommunicationAndUpdateIndex?: (ctx: RouteContext, input: CommunicationIngestionInput) => Promise<SubmitAndPollResult>;
|
|
92
|
+
requestClinicalSummary?: (ctx: RouteContext, input: ClinicalSummaryRequestInput) => Promise<ClinicalSummaryReadResult>;
|
|
92
93
|
registerBlockchainArtifactAndUpdateIndex?: (ctx: RouteContext, input: BlockchainArtifactRegistrationInput) => Promise<SubmitAndPollResult>;
|
|
93
94
|
searchCommunicationParticipants?: (ctx: RouteContext, input: CommunicationParticipantRuntimeSearchInput) => Promise<SubmitAndPollResult>;
|
|
94
95
|
grantProfessionalAccess?: (ctx: RouteContext, input: GrantProfessionalAccessInput) => Promise<GrantProfessionalAccessResult>;
|
|
@@ -7,7 +7,7 @@ import type { IndividualOrganizationConfirmOrderInput, RouteContext } from '../i
|
|
|
7
7
|
import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from '../individual-start.js';
|
|
8
8
|
import type { NodeCapability } from '../session.js';
|
|
9
9
|
import type { IndividualOrganizationLifecycleInput } from 'gdc-sdk-core-ts';
|
|
10
|
-
import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, RelatedPersonUpsertInput } from '../resource-operations.js';
|
|
10
|
+
import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, RelatedPersonUpsertInput } from '../resource-operations.js';
|
|
11
11
|
import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
|
|
12
12
|
/**
|
|
13
13
|
* Individual-controller oriented facade over a `NodeRuntimeClient`.
|
|
@@ -79,13 +79,25 @@ export declare class IndividualControllerSdk {
|
|
|
79
79
|
*/
|
|
80
80
|
importIpsOrFhirAndUpdateIndex(ctx: RouteContext, input: IpsOrFhirImportInput): Promise<SubmitAndPollResult>;
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* Compatibility adapter for the older direct `RelatedPerson` route.
|
|
83
|
+
*
|
|
84
|
+
* @deprecated Author one or more typed RelatedPerson entries in a Bundle,
|
|
85
|
+
* attach that completed Bundle to a Communication outbox job, and call
|
|
86
|
+
* `ingestCommunicationAndUpdateIndex(...)`.
|
|
83
87
|
*/
|
|
84
88
|
upsertRelatedPersonAndPoll(ctx: RouteContext, input: RelatedPersonUpsertInput): Promise<SubmitAndPollResult>;
|
|
85
89
|
/**
|
|
86
90
|
* Ingests a FHIR `Communication` and waits for indexing.
|
|
87
91
|
*/
|
|
88
92
|
ingestCommunicationAndUpdateIndex(ctx: RouteContext, input: CommunicationIngestionInput): Promise<SubmitAndPollResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Reads the subject's available clinical document through
|
|
95
|
+
* `Communication -> Subject/$summary -> FHIR Parameters`.
|
|
96
|
+
*
|
|
97
|
+
* The returned `BundleReader` navigates the authoritative GW document by
|
|
98
|
+
* section. This method never ingests resources or updates the index.
|
|
99
|
+
*/
|
|
100
|
+
requestClinicalSummary(ctx: RouteContext, input: ClinicalSummaryRequestInput): Promise<ClinicalSummaryReadResult>;
|
|
89
101
|
/**
|
|
90
102
|
* Registers one FHIR resource or raw artifact on blockchain before it is
|
|
91
103
|
* attached to a subject communication.
|
|
@@ -111,7 +111,11 @@ export class IndividualControllerSdk {
|
|
|
111
111
|
return requireClientMethod(this.client, 'importIpsOrFhirAndUpdateIndex')(ctx, input);
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* Compatibility adapter for the older direct `RelatedPerson` route.
|
|
115
|
+
*
|
|
116
|
+
* @deprecated Author one or more typed RelatedPerson entries in a Bundle,
|
|
117
|
+
* attach that completed Bundle to a Communication outbox job, and call
|
|
118
|
+
* `ingestCommunicationAndUpdateIndex(...)`.
|
|
115
119
|
*/
|
|
116
120
|
upsertRelatedPersonAndPoll(ctx, input) {
|
|
117
121
|
assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualUpsertRelatedPerson, ActorKinds.IndividualController, 'upsertRelatedPersonAndPoll');
|
|
@@ -124,6 +128,17 @@ export class IndividualControllerSdk {
|
|
|
124
128
|
assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualIngestCommunication, ActorKinds.IndividualController, 'ingestCommunicationAndUpdateIndex');
|
|
125
129
|
return requireClientMethod(this.client, 'ingestCommunicationAndUpdateIndex')(ctx, input);
|
|
126
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Reads the subject's available clinical document through
|
|
133
|
+
* `Communication -> Subject/$summary -> FHIR Parameters`.
|
|
134
|
+
*
|
|
135
|
+
* The returned `BundleReader` navigates the authoritative GW document by
|
|
136
|
+
* section. This method never ingests resources or updates the index.
|
|
137
|
+
*/
|
|
138
|
+
requestClinicalSummary(ctx, input) {
|
|
139
|
+
assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualReadClinicalSummary, ActorKinds.IndividualController, 'requestClinicalSummary');
|
|
140
|
+
return requireClientMethod(this.client, 'requestClinicalSummary')(ctx, input);
|
|
141
|
+
}
|
|
127
142
|
/**
|
|
128
143
|
* Registers one FHIR resource or raw artifact on blockchain before it is
|
|
129
144
|
* attached to a subject communication.
|
|
@@ -2,12 +2,15 @@ import { type IndividualMemberCredentialInput, type IndividualMemberVpPayloadInp
|
|
|
2
2
|
import { type NodeRuntimeClient } from './client-port.js';
|
|
3
3
|
import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
|
|
4
4
|
import type { RouteContext } from '../individual-onboarding.js';
|
|
5
|
-
import type { ClinicalBundleSearchInput, CommunicationIngestionInput, RelatedPersonUpsertInput } from '../resource-operations.js';
|
|
5
|
+
import type { ClinicalBundleSearchInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, CommunicationIngestionInput, RelatedPersonUpsertInput } from '../resource-operations.js';
|
|
6
6
|
export declare class IndividualMemberSdk {
|
|
7
7
|
private readonly client;
|
|
8
8
|
constructor(client: NodeRuntimeClient);
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Compatibility adapter for the older direct `RelatedPerson` route.
|
|
11
|
+
*
|
|
12
|
+
* @deprecated Author a typed RelatedPerson Bundle, attach it to a
|
|
13
|
+
* Communication outbox job, and call `ingestCommunicationAndUpdateIndex(...)`.
|
|
11
14
|
*/
|
|
12
15
|
upsertRelatedPersonAndPoll(ctx: RouteContext, input: RelatedPersonUpsertInput): Promise<import("gdc-sdk-core-ts/polling-model.js").SubmitAndPollResult>;
|
|
13
16
|
/**
|
|
@@ -20,6 +23,8 @@ export declare class IndividualMemberSdk {
|
|
|
20
23
|
* SMART token and accepted RelatedPerson/Consent relationship.
|
|
21
24
|
*/
|
|
22
25
|
ingestCommunicationAndUpdateIndex(ctx: RouteContext, input: CommunicationIngestionInput): Promise<import("gdc-sdk-core-ts/polling-model.js").SubmitAndPollResult>;
|
|
26
|
+
/** Reads the member-authorized `$summary` document without mutating the subject index. */
|
|
27
|
+
requestClinicalSummary(ctx: RouteContext, input: ClinicalSummaryRequestInput): Promise<ClinicalSummaryReadResult>;
|
|
23
28
|
/** Reads subject-scoped clinical documents permitted to this member. */
|
|
24
29
|
searchClinicalBundle(ctx: RouteContext, input: ClinicalBundleSearchInput): Promise<import("gdc-sdk-core-ts/polling-model.js").SubmitAndPollResult>;
|
|
25
30
|
/** Reads the latest permitted IPS document for the selected subject. */
|
|
@@ -6,7 +6,10 @@ export class IndividualMemberSdk {
|
|
|
6
6
|
this.client = client;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Compatibility adapter for the older direct `RelatedPerson` route.
|
|
10
|
+
*
|
|
11
|
+
* @deprecated Author a typed RelatedPerson Bundle, attach it to a
|
|
12
|
+
* Communication outbox job, and call `ingestCommunicationAndUpdateIndex(...)`.
|
|
10
13
|
*/
|
|
11
14
|
upsertRelatedPersonAndPoll(ctx, input) {
|
|
12
15
|
return requireClientMethod(this.client, 'upsertRelatedPersonAndPoll')(ctx, input);
|
|
@@ -25,6 +28,10 @@ export class IndividualMemberSdk {
|
|
|
25
28
|
ingestCommunicationAndUpdateIndex(ctx, input) {
|
|
26
29
|
return requireClientMethod(this.client, 'ingestCommunicationAndUpdateIndex')(ctx, input);
|
|
27
30
|
}
|
|
31
|
+
/** Reads the member-authorized `$summary` document without mutating the subject index. */
|
|
32
|
+
requestClinicalSummary(ctx, input) {
|
|
33
|
+
return requireClientMethod(this.client, 'requestClinicalSummary')(ctx, input);
|
|
34
|
+
}
|
|
28
35
|
/** Reads subject-scoped clinical documents permitted to this member. */
|
|
29
36
|
searchClinicalBundle(ctx, input) {
|
|
30
37
|
return requireClientMethod(this.client, 'searchClinicalBundle')(ctx, input);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type NodeRuntimeClient, type PollOptions, type SubmitAndPollResult, type SubmitPayload } from './client-port.js';
|
|
2
2
|
import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from '../individual-start.js';
|
|
3
3
|
import type { RouteContext } from '../individual-onboarding.js';
|
|
4
|
-
import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IpsOrFhirImportInput, LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput } from '../resource-operations.js';
|
|
4
|
+
import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IpsOrFhirImportInput, LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput } from '../resource-operations.js';
|
|
5
5
|
import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
|
|
6
6
|
export declare class PersonalSdk {
|
|
7
7
|
private readonly client;
|
|
@@ -14,6 +14,8 @@ export declare class PersonalSdk {
|
|
|
14
14
|
importIpsOrFhirAndUpdateIndex(ctx: RouteContext, input: IpsOrFhirImportInput): Promise<SubmitAndPollResult>;
|
|
15
15
|
/** Ingests canonical Communication and waits for projection completion. */
|
|
16
16
|
ingestCommunicationAndUpdateIndex(ctx: RouteContext, input: CommunicationIngestionInput): Promise<SubmitAndPollResult>;
|
|
17
|
+
/** Reads the current subject through Communication `$summary`; it performs no ingestion. */
|
|
18
|
+
requestClinicalSummary(ctx: RouteContext, input: ClinicalSummaryRequestInput): Promise<ClinicalSummaryReadResult>;
|
|
17
19
|
/** Registers one FHIR resource or raw artifact on blockchain before communication. */
|
|
18
20
|
registerBlockchainArtifactAndUpdateIndex(ctx: RouteContext, input: BlockchainArtifactRegistrationInput): Promise<SubmitAndPollResult>;
|
|
19
21
|
/** Searches indexed communication channel records by subject and participant identifiers. */
|
|
@@ -20,6 +20,10 @@ export class PersonalSdk {
|
|
|
20
20
|
ingestCommunicationAndUpdateIndex(ctx, input) {
|
|
21
21
|
return requireClientMethod(this.client, 'ingestCommunicationAndUpdateIndex')(ctx, input);
|
|
22
22
|
}
|
|
23
|
+
/** Reads the current subject through Communication `$summary`; it performs no ingestion. */
|
|
24
|
+
requestClinicalSummary(ctx, input) {
|
|
25
|
+
return requireClientMethod(this.client, 'requestClinicalSummary')(ctx, input);
|
|
26
|
+
}
|
|
23
27
|
/** Registers one FHIR resource or raw artifact on blockchain before communication. */
|
|
24
28
|
registerBlockchainArtifactAndUpdateIndex(ctx, input) {
|
|
25
29
|
return requireClientMethod(this.client, 'registerBlockchainArtifactAndUpdateIndex')(ctx, input);
|
|
@@ -2,7 +2,7 @@ import { type ProfessionalEmployeeCredentialInput, type ProfessionalSmartVpPaylo
|
|
|
2
2
|
import { type NodeRuntimeClient, type PollOptions, type SubmitAndPollResult, type SubmitPayload } from './client-port.js';
|
|
3
3
|
import type { RouteContext } from '../individual-onboarding.js';
|
|
4
4
|
import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
|
|
5
|
-
import type { BlockchainArtifactRegistrationInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, ClinicalBundleSearchInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, VitalSignBatchCommunicationFromSearchResponseInput } from '../resource-operations.js';
|
|
5
|
+
import type { BlockchainArtifactRegistrationInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, ClinicalBundleSearchInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, VitalSignBatchCommunicationFromSearchResponseInput } from '../resource-operations.js';
|
|
6
6
|
/**
|
|
7
7
|
* Professional-oriented facade for runtime actions that belong to the
|
|
8
8
|
* professional actor itself after tenant and employee provisioning have already
|
|
@@ -59,6 +59,8 @@ export declare class ProfessionalSdk {
|
|
|
59
59
|
* session and tenant route context.
|
|
60
60
|
*/
|
|
61
61
|
searchClinicalBundle(ctx: RouteContext, input: ClinicalBundleSearchInput): Promise<SubmitAndPollResult>;
|
|
62
|
+
/** Reads the consent-authorized clinical `$summary` document without ingestion. */
|
|
63
|
+
requestClinicalSummary(ctx: RouteContext, input: ClinicalSummaryRequestInput): Promise<ClinicalSummaryReadResult>;
|
|
62
64
|
/**
|
|
63
65
|
* Reads the latest IPS-oriented document projection available to the
|
|
64
66
|
* professional actor for one subject.
|
|
@@ -67,6 +67,10 @@ export class ProfessionalSdk {
|
|
|
67
67
|
searchClinicalBundle(ctx, input) {
|
|
68
68
|
return requireClientMethod(this.client, 'searchClinicalBundle')(ctx, input);
|
|
69
69
|
}
|
|
70
|
+
/** Reads the consent-authorized clinical `$summary` document without ingestion. */
|
|
71
|
+
requestClinicalSummary(ctx, input) {
|
|
72
|
+
return requireClientMethod(this.client, 'requestClinicalSummary')(ctx, input);
|
|
73
|
+
}
|
|
70
74
|
/**
|
|
71
75
|
* Reads the latest IPS-oriented document projection available to the
|
|
72
76
|
* professional actor for one subject.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { DeviceAppType } from 'gdc-common-utils-ts/constants/device';
|
|
2
|
+
import { type ClinicalSummaryReadResult, type ClinicalSummaryRequestInput } from 'gdc-sdk-core-ts';
|
|
2
3
|
import type { LicenseOfferSearchState, LicenseOrderSearchState } from 'gdc-common-utils-ts/utils/license-commercial-search';
|
|
3
4
|
import type { LicenseListSearchState } from 'gdc-common-utils-ts/utils/license-list-search';
|
|
4
5
|
import type { BundleSearchQuery, CommMsgExtendedCommunicationOutboxJob, CommunicationOutboxJob, CommunicationInput, DateRange, EmployeeSearchValue, IndividualOrganizationLifecycleInput, TransportProfile } from 'gdc-sdk-core-ts';
|
|
5
6
|
import type { BundleEntry, BundleJsonApi } from 'gdc-common-utils-ts/models/bundle';
|
|
6
7
|
import type { SubmitAndPollResult } from './orchestration/client-port.js';
|
|
7
8
|
import type { RouteContext } from './individual-onboarding.js';
|
|
9
|
+
export type { ClinicalSummaryReadResult, ClinicalSummaryRequestInput, } from 'gdc-sdk-core-ts';
|
|
8
10
|
export type OrganizationEmployeeCreationInput = {
|
|
9
11
|
/**
|
|
10
12
|
* Canonical employee/person claims sent to CORE GW.
|
|
@@ -675,6 +677,19 @@ export declare function searchCommunicationParticipantsWithDeps(routeCtx: RouteC
|
|
|
675
677
|
intervalMs?: number;
|
|
676
678
|
}) => Promise<SubmitAndPollResult>;
|
|
677
679
|
}): Promise<SubmitAndPollResult>;
|
|
680
|
+
/**
|
|
681
|
+
* Requests the subject's available clinical document through the canonical
|
|
682
|
+
* auditable read lifecycle:
|
|
683
|
+
*
|
|
684
|
+
* `Communication -> Subject/$summary -> FHIR Parameters -> Bundle document`.
|
|
685
|
+
*
|
|
686
|
+
* The dependency name intentionally says `submit`, not `ingest`: GW receives a
|
|
687
|
+
* Communication transport record, but the business operation is a read and
|
|
688
|
+
* must not be exposed to applications as index ingestion.
|
|
689
|
+
*/
|
|
690
|
+
export declare function requestClinicalSummaryWithDeps(routeCtx: RouteContext, input: ClinicalSummaryRequestInput, deps: {
|
|
691
|
+
submitSummaryCommunication: (ctx: RouteContext, input: CommunicationIngestionInput) => Promise<SubmitAndPollResult>;
|
|
692
|
+
}): Promise<ClinicalSummaryReadResult>;
|
|
678
693
|
export declare function searchClinicalBundleWithDeps(routeCtx: RouteContext, input: ClinicalBundleSearchInput, deps: {
|
|
679
694
|
bundleSearchPath: (ctx: RouteContext) => string;
|
|
680
695
|
bundleSearchPollPath: (ctx: RouteContext) => string;
|
|
@@ -757,4 +772,3 @@ export declare function generateDigitalTwinFromSubjectDataWithDeps(routeCtx: Rou
|
|
|
757
772
|
}) => Promise<SubmitAndPollResult>;
|
|
758
773
|
}): Promise<SubmitAndPollResult>;
|
|
759
774
|
export declare function normalizeCommunicationPathFormatSegment(raw?: string): string;
|
|
760
|
-
export {};
|
|
@@ -3,7 +3,7 @@ import { HealthcareBasicSections, ResourceTypesFhirR4 } from 'gdc-common-utils-t
|
|
|
3
3
|
import { Format } from 'gdc-common-utils-ts/constants/Schemas';
|
|
4
4
|
import { RelatedPersonClaim } from 'gdc-common-utils-ts/models/interoperable-claims/related-person-claims';
|
|
5
5
|
import { buildCommunicationParticipantSearchBundle, buildBlockchainArtifactDocumentReference, BundleQuery, createInteroperableResourceOperationEditor, IndividualOrganizationLifecycleEditor, LicenseOfferSearchEditor, LicenseOrderSearchEditor, InteroperableLifecycleStatuses, LicenseListSearchEditor, buildLicenseIssueEntry, buildLicensePurchaseEntry, } from 'gdc-common-utils-ts';
|
|
6
|
-
import { addFhirResourceToCommunication, createCommunicationResource, } from 'gdc-sdk-core-ts';
|
|
6
|
+
import { addFhirResourceToCommunication, createCommunicationResource, buildClinicalSummaryCommunicationJob, readClinicalSummaryOperationResult, } from 'gdc-sdk-core-ts';
|
|
7
7
|
import { GwCoreLifecycleRequestMethod, GwCoreLifecycleRequestType, GwCoreLifecycleTodo, } from './constants/lifecycle.js';
|
|
8
8
|
import { buildEmployeeBatchEntry, buildEmployeeSearchBundle, ConsentClaims, } from 'gdc-sdk-core-ts';
|
|
9
9
|
export async function createOrganizationEmployeeWithDeps(routeCtx, input, options, deps) {
|
|
@@ -390,6 +390,26 @@ export async function searchCommunicationParticipantsWithDeps(routeCtx, input, d
|
|
|
390
390
|
};
|
|
391
391
|
return deps.submitAndPoll(deps.communicationSearchPath(routeCtx), deps.communicationSearchPollPath(routeCtx), payload, input.pollOptions);
|
|
392
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* Requests the subject's available clinical document through the canonical
|
|
395
|
+
* auditable read lifecycle:
|
|
396
|
+
*
|
|
397
|
+
* `Communication -> Subject/$summary -> FHIR Parameters -> Bundle document`.
|
|
398
|
+
*
|
|
399
|
+
* The dependency name intentionally says `submit`, not `ingest`: GW receives a
|
|
400
|
+
* Communication transport record, but the business operation is a read and
|
|
401
|
+
* must not be exposed to applications as index ingestion.
|
|
402
|
+
*/
|
|
403
|
+
export async function requestClinicalSummaryWithDeps(routeCtx, input, deps) {
|
|
404
|
+
const communicationJob = buildClinicalSummaryCommunicationJob(input);
|
|
405
|
+
const operation = await deps.submitSummaryCommunication(routeCtx, {
|
|
406
|
+
communicationJob,
|
|
407
|
+
clinicalFormat: input.clinicalFormat || 'api',
|
|
408
|
+
transportProfile: input.transportProfile,
|
|
409
|
+
pollOptions: input.pollOptions,
|
|
410
|
+
});
|
|
411
|
+
return readClinicalSummaryOperationResult(operation);
|
|
412
|
+
}
|
|
393
413
|
export async function searchClinicalBundleWithDeps(routeCtx, input, deps) {
|
|
394
414
|
const query = buildBundleSearchQuery(input);
|
|
395
415
|
const payload = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JWK } from 'gdc-common-utils-ts/models/jwk';
|
|
2
2
|
import type { ActorKind } from 'gdc-common-utils-ts/models/actor-session';
|
|
3
|
+
import type { ConfidentialStorageProfile } from 'gdc-sdk-core-ts';
|
|
3
4
|
import type { RouteContext } from './individual-onboarding.js';
|
|
4
5
|
import type { SecureDidcommTransportAdapter } from 'gdc-sdk-core-ts';
|
|
5
6
|
import { type PinProtectedProfileSecret, type ProfileProtectionOptions, type ServerProfileSealer } from './server-profile-protection.js';
|
|
@@ -19,6 +20,10 @@ export type ServerProfileRecord = Readonly<{
|
|
|
19
20
|
clientId: string;
|
|
20
21
|
deviceDid: string;
|
|
21
22
|
publicJwks: Record<string, unknown>[];
|
|
23
|
+
/** Public recipient key for local confidential storage; not a DCR communication key. */
|
|
24
|
+
storagePublicJwk?: Record<string, unknown>;
|
|
25
|
+
/** Server-owned policy; browser input is never authoritative for this value. */
|
|
26
|
+
confidentialStorageProfile?: ConfidentialStorageProfile;
|
|
22
27
|
protectedWalletSeed: PinProtectedProfileSecret;
|
|
23
28
|
protectedVpToken: PinProtectedProfileSecret;
|
|
24
29
|
failedUnlocks: number;
|
|
@@ -77,18 +82,30 @@ export type ResolvedServerProfileSession = Readonly<{
|
|
|
77
82
|
scopes: string[];
|
|
78
83
|
accessToken: string;
|
|
79
84
|
secureTransportAdapter: SecureDidcommTransportAdapter;
|
|
85
|
+
/** Storage adapter available only while the PIN-unlocked session is alive. */
|
|
86
|
+
confidentialStorageAdapter: Readonly<{
|
|
87
|
+
protect(document: Readonly<{
|
|
88
|
+
id?: string;
|
|
89
|
+
content: unknown;
|
|
90
|
+
}>): Promise<unknown>;
|
|
91
|
+
unprotect(document: Readonly<{
|
|
92
|
+
id?: string;
|
|
93
|
+
jwe: string;
|
|
94
|
+
}>): Promise<unknown>;
|
|
95
|
+
}>;
|
|
80
96
|
}>;
|
|
81
97
|
export type ServerProfileSessionManagerOptions = Readonly<{
|
|
82
98
|
store: ServerProfileStore;
|
|
83
99
|
sealer: ServerProfileSealer;
|
|
84
100
|
gatewayBaseUrl: string;
|
|
85
|
-
recipientDid: string;
|
|
86
101
|
resolveRecipientJwk: (recipientDid: string) => Promise<JWK>;
|
|
87
102
|
fetchImpl?: typeof fetch;
|
|
88
103
|
sessionTtlSeconds?: number;
|
|
89
104
|
maxFailedUnlocks?: number;
|
|
90
105
|
lockSeconds?: number;
|
|
91
106
|
profileProtection?: ProfileProtectionOptions;
|
|
107
|
+
/** Product/tenant policy applied to enrollment and upgraded on next PIN unlock. */
|
|
108
|
+
requiredConfidentialStorageProfile?: ConfidentialStorageProfile;
|
|
92
109
|
now?: () => Date;
|
|
93
110
|
}>;
|
|
94
111
|
/**
|
|
@@ -109,6 +126,13 @@ export declare class ServerProfileSessionManager {
|
|
|
109
126
|
lock(ownerId: string, sessionId: string): Promise<void>;
|
|
110
127
|
private createClient;
|
|
111
128
|
private createWallet;
|
|
129
|
+
/**
|
|
130
|
+
* Upgrade legacy profiles deterministically after successful PIN unlock.
|
|
131
|
+
* The protected seed already owns the storage pair, so migration neither
|
|
132
|
+
* exports a private key nor calls KMS/GW. Only its public JWK and policy label
|
|
133
|
+
* are added to the durable profile record.
|
|
134
|
+
*/
|
|
135
|
+
private ensureRequiredStorageProfile;
|
|
112
136
|
private requireOwnedProfile;
|
|
113
137
|
private requireSubject;
|
|
114
138
|
private now;
|
|
@@ -21,6 +21,9 @@ export class ServerProfileSessionManager {
|
|
|
21
21
|
const wallet = await this.createWallet(input.profileId, seed);
|
|
22
22
|
const context = walletContext(input.profileId);
|
|
23
23
|
const publicKeys = await wallet.getPublicJwks(context, {});
|
|
24
|
+
const storagePublicJwk = publicKeys.find((entry) => entry.purpose === 'document-at-rest')?.publicJwk;
|
|
25
|
+
if (!storagePublicJwk)
|
|
26
|
+
throw new Error('Server profile enrollment requires a document-at-rest ML-KEM key.');
|
|
24
27
|
const client = this.createClient(input.routeContext, input.idToken);
|
|
25
28
|
const activation = await client.activateProfileDeviceWithActivationRequest({
|
|
26
29
|
...input.routeContext,
|
|
@@ -30,7 +33,7 @@ export class ServerProfileSessionManager {
|
|
|
30
33
|
application_type: 'web',
|
|
31
34
|
actor_did: input.actorDid,
|
|
32
35
|
profile_did: input.profileDid,
|
|
33
|
-
jwks: { keys: publicKeys.map((entry) => entry.publicJwk) },
|
|
36
|
+
jwks: { keys: publicKeys.filter((entry) => entry.purpose !== 'document-at-rest').map((entry) => entry.publicJwk) },
|
|
34
37
|
},
|
|
35
38
|
});
|
|
36
39
|
const dcrBody = terminalBody(activation.dcr.poll.body);
|
|
@@ -51,7 +54,9 @@ export class ServerProfileSessionManager {
|
|
|
51
54
|
allowedSubjectDids: unique(input.allowedSubjectDids),
|
|
52
55
|
clientId,
|
|
53
56
|
deviceDid,
|
|
54
|
-
publicJwks: publicKeys.map((entry) => entry.publicJwk),
|
|
57
|
+
publicJwks: publicKeys.filter((entry) => entry.purpose !== 'document-at-rest').map((entry) => entry.publicJwk),
|
|
58
|
+
storagePublicJwk: storagePublicJwk,
|
|
59
|
+
confidentialStorageProfile: this.options.requiredConfidentialStorageProfile ?? 'confidential-basic-v1',
|
|
55
60
|
protectedWalletSeed: await protectServerProfileSecret(seed, input.pin, `${input.profileId}:wallet-seed`, this.options.sealer, this.options.profileProtection),
|
|
56
61
|
protectedVpToken: await protectServerProfileSecret(input.vpToken, input.pin, `${input.profileId}:vp-token`, this.options.sealer, this.options.profileProtection),
|
|
57
62
|
failedUnlocks: 0,
|
|
@@ -65,7 +70,7 @@ export class ServerProfileSessionManager {
|
|
|
65
70
|
return this.options.store.listProfiles(ownerId);
|
|
66
71
|
}
|
|
67
72
|
async unlock(input) {
|
|
68
|
-
|
|
73
|
+
let profile = await this.requireOwnedProfile(input.ownerId, input.profileId);
|
|
69
74
|
this.requireSubject(profile, input.subjectDid);
|
|
70
75
|
const now = this.now();
|
|
71
76
|
if (profile.lockedUntil && new Date(profile.lockedUntil) > now) {
|
|
@@ -88,6 +93,7 @@ export class ServerProfileSessionManager {
|
|
|
88
93
|
await this.options.store.putProfile({ ...profile, failedUnlocks: failures, lockedUntil, updatedAt: now.toISOString() });
|
|
89
94
|
throw new Error('Profile PIN rejected.');
|
|
90
95
|
}
|
|
96
|
+
profile = await this.ensureRequiredStorageProfile(profile, seed);
|
|
91
97
|
const wallet = await this.createWallet(profile.profileId, seed);
|
|
92
98
|
const assertion = await buildWalletClientAssertion(wallet, profile, this.options.gatewayBaseUrl, now);
|
|
93
99
|
const token = await this.createClient(profile.routeContext, input.idToken).requestSmartToken({
|
|
@@ -141,9 +147,13 @@ export class ServerProfileSessionManager {
|
|
|
141
147
|
scopes: session.scopes,
|
|
142
148
|
accessToken: await this.options.sealer.unseal(session.sealedAccessToken, `${sessionId}:access-token`),
|
|
143
149
|
secureTransportAdapter: {
|
|
144
|
-
pack: (message) => wallet.packForRecipientWithContext(message,
|
|
150
|
+
pack: (message) => wallet.packForRecipientWithContext(message, profile.providerDid, { context }),
|
|
145
151
|
unpack: async (jwe) => (await wallet.unpackWithContext(jwe, { context })).content,
|
|
146
152
|
},
|
|
153
|
+
confidentialStorageAdapter: {
|
|
154
|
+
protect: (document) => wallet.protectManagedConfidentialData(document, context),
|
|
155
|
+
unprotect: (document) => wallet.unprotectManagedConfidentialData(document, context),
|
|
156
|
+
},
|
|
147
157
|
};
|
|
148
158
|
}
|
|
149
159
|
async lock(ownerId, sessionId) {
|
|
@@ -164,7 +174,7 @@ export class ServerProfileSessionManager {
|
|
|
164
174
|
const context = walletContext(profileId);
|
|
165
175
|
await wallet.provisionManagedKeys(context, {
|
|
166
176
|
ownerScope: 'profile',
|
|
167
|
-
purposes: ['actor-signing'],
|
|
177
|
+
purposes: ['actor-signing', 'document-at-rest'],
|
|
168
178
|
mode: 'deterministic',
|
|
169
179
|
seedMaterial: seed,
|
|
170
180
|
});
|
|
@@ -176,6 +186,35 @@ export class ServerProfileSessionManager {
|
|
|
176
186
|
});
|
|
177
187
|
return wallet;
|
|
178
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Upgrade legacy profiles deterministically after successful PIN unlock.
|
|
191
|
+
* The protected seed already owns the storage pair, so migration neither
|
|
192
|
+
* exports a private key nor calls KMS/GW. Only its public JWK and policy label
|
|
193
|
+
* are added to the durable profile record.
|
|
194
|
+
*/
|
|
195
|
+
async ensureRequiredStorageProfile(profile, seed) {
|
|
196
|
+
const required = this.options.requiredConfidentialStorageProfile ?? profile.confidentialStorageProfile ?? 'confidential-basic-v1';
|
|
197
|
+
if (required !== 'confidential-pqc-v1')
|
|
198
|
+
return profile;
|
|
199
|
+
const wallet = await this.createWallet(profile.profileId, seed);
|
|
200
|
+
const storageKeys = await wallet.getPublicJwks(walletContext(profile.profileId), {
|
|
201
|
+
ownerScope: 'profile', purpose: 'document-at-rest', alg: 'ML-KEM-768',
|
|
202
|
+
});
|
|
203
|
+
const storagePublicJwk = storageKeys[0]?.publicJwk;
|
|
204
|
+
if (!storagePublicJwk)
|
|
205
|
+
throw new Error('Required ML-KEM storage key could not be provisioned.');
|
|
206
|
+
const hasKey = profile.storagePublicJwk?.kid === storagePublicJwk.kid;
|
|
207
|
+
if (profile.confidentialStorageProfile === required && hasKey)
|
|
208
|
+
return profile;
|
|
209
|
+
const upgraded = {
|
|
210
|
+
...profile,
|
|
211
|
+
confidentialStorageProfile: required,
|
|
212
|
+
storagePublicJwk,
|
|
213
|
+
updatedAt: this.now().toISOString(),
|
|
214
|
+
};
|
|
215
|
+
await this.options.store.putProfile(upgraded);
|
|
216
|
+
return upgraded;
|
|
217
|
+
}
|
|
179
218
|
async requireOwnedProfile(ownerId, profileId) {
|
|
180
219
|
const profile = await this.options.store.getProfile(profileId);
|
|
181
220
|
if (!profile || profile.ownerId !== ownerId)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdc-sdk-node-ts",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "Next-generation Node runtime package for the GDC SDK family",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Antifraud Services Inc.",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc -p tsconfig.json",
|
|
15
15
|
"type-check": "tsc -p tsconfig.json --noEmit",
|
|
16
|
+
"prepublishOnly": "npm run type-check && npm test",
|
|
16
17
|
"local:close": "PORTS=3000 bash ./scripts/local-close.sh",
|
|
17
18
|
"docker:close": "PORTS=8000 bash ./scripts/local-close.sh",
|
|
18
19
|
"test": "npm run build && node --test tests/*.test.mjs",
|
|
@@ -35,8 +36,8 @@
|
|
|
35
36
|
"test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
|
-
"gdc-common-utils-ts": "^2.3.
|
|
39
|
-
"gdc-sdk-core-ts": "~2.3.
|
|
39
|
+
"gdc-common-utils-ts": "^2.3.2",
|
|
40
|
+
"gdc-sdk-core-ts": "~2.3.2"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/node": "^20.14.10",
|