gdc-sdk-node-ts 2.9.11 → 2.9.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/family-organization-search.d.ts +22 -0
- package/dist/family-organization-search.js +56 -0
- package/dist/individual-onboarding.d.ts +3 -2
- package/dist/individual-start.d.ts +26 -2
- package/dist/individual-start.js +86 -19
- package/dist/node-runtime-client.d.ts +7 -1
- package/dist/node-runtime-client.js +11 -1
- package/dist/orchestration/individual-controller-sdk.d.ts +12 -2
- package/dist/orchestration/individual-controller-sdk.js +7 -2
- package/dist/server-profile-session.d.ts +59 -1
- package/dist/server-profile-session.js +115 -16
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -533,7 +533,7 @@ const individualControllerDid = buildIndividualMemberDidWebFromPrivateIdentifier
|
|
|
533
533
|
roleValue: 'RESPRSN',
|
|
534
534
|
});
|
|
535
535
|
|
|
536
|
-
// did:web:host.example.org:health-care:organization:taxid:VATES-B00112233:individual:
|
|
536
|
+
// did:web:host.example.org:health-care:organization:taxid:VATES-B00112233:individual:multibase:zG9H82pae9SCXvec3D4YKqhX8bj8F1mRgzxMEdwXXonT7BWsvsUiP2u52sWQTeESpoMee:member:zG9DrMLpQW8eoCc9Ay9AFxuMGiswgJePpbUMz9svJCZ8tKjUd4xoExgCPA5jmHc6hPATJ:RESPRSN
|
|
537
537
|
```
|
|
538
538
|
|
|
539
539
|
### Use shared invitation contract from Node
|
|
@@ -8,6 +8,20 @@ export type FamilyOrganizationSearchInput = Readonly<{
|
|
|
8
8
|
timeoutSeconds?: number;
|
|
9
9
|
intervalSeconds?: number;
|
|
10
10
|
}>;
|
|
11
|
+
export type OwnedFamilyOrganizationDirectoryInput = Readonly<{
|
|
12
|
+
verifiedContact: Readonly<{
|
|
13
|
+
email?: string;
|
|
14
|
+
telephone?: string;
|
|
15
|
+
}>;
|
|
16
|
+
requestThid?: string;
|
|
17
|
+
timeoutSeconds?: number;
|
|
18
|
+
intervalSeconds?: number;
|
|
19
|
+
}>;
|
|
20
|
+
export type OwnedFamilyOrganizationDirectoryEntry = Readonly<{
|
|
21
|
+
resourceId: string;
|
|
22
|
+
alternateName: string;
|
|
23
|
+
claims: Readonly<Record<string, unknown>>;
|
|
24
|
+
}>;
|
|
11
25
|
type SearchFamilyOrganizationWithDeps = {
|
|
12
26
|
routeCtx: RouteContext;
|
|
13
27
|
input: FamilyOrganizationSearchInput;
|
|
@@ -27,4 +41,12 @@ type SearchFamilyOrganizationWithDeps = {
|
|
|
27
41
|
* `null`.
|
|
28
42
|
*/
|
|
29
43
|
export declare function searchFamilyOrganizationWithDeps(deps: SearchFamilyOrganizationWithDeps): Promise<FamilyOrganizationSummary | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Lists the complete owner-scoped individual Organization directory.
|
|
46
|
+
*
|
|
47
|
+
* CORE requires an exact verified owner contact and deliberately performs no
|
|
48
|
+
* global or prefix scan. Callers may filter the returned alternate names only
|
|
49
|
+
* after this authenticated directory boundary.
|
|
50
|
+
*/
|
|
51
|
+
export declare function listOwnedFamilyOrganizationsWithDeps(routeCtx: RouteContext, input: OwnedFamilyOrganizationDirectoryInput, deps: Pick<SearchFamilyOrganizationWithDeps, 'individualFamilyOrganizationSearchPath' | 'individualFamilyOrganizationSearchPollPath' | 'submitAndPoll' | 'defaultTimeoutMs' | 'defaultIntervalMs'>): Promise<OwnedFamilyOrganizationDirectoryEntry[]>;
|
|
30
52
|
export {};
|
|
@@ -49,6 +49,62 @@ export async function searchFamilyOrganizationWithDeps(deps) {
|
|
|
49
49
|
}
|
|
50
50
|
return readFamilyOrganizationSummaryFromResponseBody(result.poll.body);
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Lists the complete owner-scoped individual Organization directory.
|
|
54
|
+
*
|
|
55
|
+
* CORE requires an exact verified owner contact and deliberately performs no
|
|
56
|
+
* global or prefix scan. Callers may filter the returned alternate names only
|
|
57
|
+
* after this authenticated directory boundary.
|
|
58
|
+
*/
|
|
59
|
+
export async function listOwnedFamilyOrganizationsWithDeps(routeCtx, input, deps) {
|
|
60
|
+
const email = String(input.verifiedContact?.email || '').trim().toLowerCase();
|
|
61
|
+
const telephone = String(input.verifiedContact?.telephone || '').trim();
|
|
62
|
+
if (!email && !telephone) {
|
|
63
|
+
throw new Error('A verified owner email or telephone is required.');
|
|
64
|
+
}
|
|
65
|
+
const claims = {
|
|
66
|
+
'@context': 'org.schema',
|
|
67
|
+
...(email ? { [ClaimsOrganizationSchemaorg.ownerEmail]: email } : {}),
|
|
68
|
+
...(telephone ? { [ClaimsOrganizationSchemaorg.ownerTelephone]: telephone } : {}),
|
|
69
|
+
};
|
|
70
|
+
const pollOptions = resolvePollOptionsFromSeconds(input.timeoutSeconds, input.intervalSeconds, {
|
|
71
|
+
timeoutMs: deps.defaultTimeoutMs,
|
|
72
|
+
intervalMs: deps.defaultIntervalMs,
|
|
73
|
+
});
|
|
74
|
+
const result = await deps.submitAndPoll(deps.individualFamilyOrganizationSearchPath(routeCtx), deps.individualFamilyOrganizationSearchPollPath(routeCtx), {
|
|
75
|
+
jti: `jti-${createRuntimeUuid()}`,
|
|
76
|
+
thid: input.requestThid || `owned-family-directory-${createRuntimeUuid()}`,
|
|
77
|
+
iss: routeCtx.tenantId,
|
|
78
|
+
aud: routeCtx.tenantId,
|
|
79
|
+
type: 'application/api+json',
|
|
80
|
+
body: {
|
|
81
|
+
data: [{
|
|
82
|
+
type: 'Family-search-v1.0',
|
|
83
|
+
resource: { meta: { claims } },
|
|
84
|
+
}],
|
|
85
|
+
},
|
|
86
|
+
}, pollOptions);
|
|
87
|
+
if (result.poll.status !== 200)
|
|
88
|
+
return [];
|
|
89
|
+
const root = result.poll.body?.body || result.poll.body;
|
|
90
|
+
const responseEntries = Array.isArray(root?.data) ? root.data : [];
|
|
91
|
+
const resources = responseEntries.flatMap((entry) => Array.isArray(entry?.resource?.entry)
|
|
92
|
+
? entry.resource.entry.map((item) => item?.resource).filter(Boolean)
|
|
93
|
+
: []);
|
|
94
|
+
return resources.flatMap((resource) => {
|
|
95
|
+
const resourceClaims = resource?.meta?.claims;
|
|
96
|
+
if (!resource?.id || !resourceClaims || typeof resourceClaims !== 'object')
|
|
97
|
+
return [];
|
|
98
|
+
const ownerEmail = String(resourceClaims[ClaimsOrganizationSchemaorg.ownerEmail] || '').trim().toLowerCase();
|
|
99
|
+
const ownerTelephone = String(resourceClaims[ClaimsOrganizationSchemaorg.ownerTelephone] || '').trim();
|
|
100
|
+
if (!((email && ownerEmail === email) || (telephone && ownerTelephone === telephone)))
|
|
101
|
+
return [];
|
|
102
|
+
const alternateName = String(resourceClaims[ClaimsOrganizationSchemaorg.alternateName] || '').trim();
|
|
103
|
+
if (!alternateName)
|
|
104
|
+
return [];
|
|
105
|
+
return [{ resourceId: String(resource.id), alternateName, claims: resourceClaims }];
|
|
106
|
+
});
|
|
107
|
+
}
|
|
52
108
|
function createRuntimeUuid() {
|
|
53
109
|
const fromCrypto = globalThis.crypto?.randomUUID?.();
|
|
54
110
|
if (fromCrypto) {
|
|
@@ -29,8 +29,9 @@ export type IndividualOrganizationOrderResult = SubmitAndPollResult & Readonly<{
|
|
|
29
29
|
activationCode: string;
|
|
30
30
|
/**
|
|
31
31
|
* Governed `RelatedPerson.identifier` automatically materialized by GW for
|
|
32
|
-
* the principal Organization owner/controller.
|
|
33
|
-
* `
|
|
32
|
+
* the principal Organization owner/controller. GW reuses the bare UUID from
|
|
33
|
+
* `Organization.owner.identifier.value`; document helpers expose it as an
|
|
34
|
+
* `urn:uuid:<UUID>` FHIR reference only when a reference is required.
|
|
34
35
|
*/
|
|
35
36
|
controllerRelatedPersonIdentifier: string;
|
|
36
37
|
/**
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { SecureIdTypesIndividual } from 'gdc-common-utils-ts';
|
|
2
2
|
export { buildIndividualMemberDidWebFromPrivateIdentifiers } from 'gdc-common-utils-ts';
|
|
3
3
|
import type { FamilyRegistrationStatus } from 'gdc-common-utils-ts/utils/family-organization-summary';
|
|
4
|
+
import type { IndividualOnboardingDraftResult } from 'gdc-common-utils-ts/models/individual-onboarding';
|
|
4
5
|
import type { PollOptions, SubmitAndPollResult } from './orchestration/client-port.js';
|
|
5
6
|
import type { RouteContext } from './individual-onboarding.js';
|
|
6
7
|
import type { OfferPreview } from './order-offer-summary.js';
|
|
7
8
|
export type IndividualOrganizationRegistrationInput = {
|
|
9
|
+
/**
|
|
10
|
+
* Preferred high-level input produced by `createIndividualOnboardingEditor()`.
|
|
11
|
+
*
|
|
12
|
+
* The SDK converts this draft into the GW Bundle and signed-PDF attachment.
|
|
13
|
+
* Registration remains separate from Order confirmation, enrollment and
|
|
14
|
+
* opening a profile.
|
|
15
|
+
*/
|
|
16
|
+
onboardingDraft?: IndividualOnboardingDraftResult;
|
|
8
17
|
/**
|
|
9
18
|
* Preferred route identifier for the selected personal indexing service provider.
|
|
10
19
|
*
|
|
@@ -25,7 +34,7 @@ export type IndividualOrganizationRegistrationInput = {
|
|
|
25
34
|
* This is not the technical subject identifier. It is the nearby name the
|
|
26
35
|
* controller uses to refer to the person in the UI, for example `Charly`.
|
|
27
36
|
*/
|
|
28
|
-
alternateName
|
|
37
|
+
alternateName?: string;
|
|
29
38
|
/**
|
|
30
39
|
* CORE-canonical controller contact channel for individual bootstrap.
|
|
31
40
|
*
|
|
@@ -53,6 +62,12 @@ export type IndividualOrganizationRegistrationInput = {
|
|
|
53
62
|
* a product gateway extension.
|
|
54
63
|
*/
|
|
55
64
|
controllerTelephone?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Stable UUID of the principal controller/RESPRSN assignment. In a self
|
|
67
|
+
* registration this may be the same UUID already assigned to the person.
|
|
68
|
+
* Omit only for a brand-new assignment so the SDK creates it once.
|
|
69
|
+
*/
|
|
70
|
+
controllerIdentifier?: string;
|
|
56
71
|
controllerRole?: string;
|
|
57
72
|
additionalClaims?: Record<string, unknown>;
|
|
58
73
|
timeoutSeconds?: number;
|
|
@@ -91,6 +106,12 @@ export type IndividualOrganizationBootstrapIdentity = {
|
|
|
91
106
|
providerDidWeb: string;
|
|
92
107
|
/** Canonical child DID built beneath the exact provider DID returned by GW. */
|
|
93
108
|
subjectDid: string;
|
|
109
|
+
/**
|
|
110
|
+
* Canonical principal-controller member DID accepted by individual DCR.
|
|
111
|
+
* Optional only for compatibility with older registration receipts that did
|
|
112
|
+
* not expose the controller contact needed to derive it.
|
|
113
|
+
*/
|
|
114
|
+
controllerActorDid?: string;
|
|
94
115
|
};
|
|
95
116
|
type RegisterIndividualOrganizationDeps = {
|
|
96
117
|
input: IndividualOrganizationRegistrationInput;
|
|
@@ -122,4 +143,7 @@ export declare function startIndividualOrganizationWithDeps(deps: RegisterIndivi
|
|
|
122
143
|
* `:organization:taxid:` DID path. `buildIndividualDidWeb(...)` only adds the
|
|
123
144
|
* individual suffix and never rewrites that provider lineage.
|
|
124
145
|
*/
|
|
125
|
-
export declare function readIndividualOrganizationBootstrapIdentity(responseBody: unknown
|
|
146
|
+
export declare function readIndividualOrganizationBootstrapIdentity(responseBody: unknown, controller?: Readonly<{
|
|
147
|
+
controllerEmail?: string;
|
|
148
|
+
controllerTelephone?: string;
|
|
149
|
+
}>): IndividualOrganizationBootstrapIdentity | undefined;
|
package/dist/individual-start.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { ClaimsOfferSchemaorg, ClaimsOrganizationSchemaorg, ClaimsPersonSchemaorg, ClaimsServiceSchemaorg, } from 'gdc-common-utils-ts/constants';
|
|
3
|
-
import { buildIndividualDidWeb, buildSecureIdValueIndividual, extractPrimaryClaims, readFamilyOrganizationSummaryFromResponseBody, SecureIdTypesIndividual, } from 'gdc-common-utils-ts';
|
|
4
|
+
import { HealthcareActorRoleCodes, HL7_CODING_SYSTEM_V3_ROLE_CODE, buildIndividualMemberDidWebFromPrivateIdentifiers, buildIndividualDidWeb, buildSecureIdValueIndividual, extractPrimaryClaims, normalizeUuid, readFamilyOrganizationSummaryFromResponseBody, SecureIdTypesIndividual, } from 'gdc-common-utils-ts';
|
|
4
5
|
export { buildIndividualMemberDidWebFromPrivateIdentifiers } from 'gdc-common-utils-ts';
|
|
6
|
+
import { DocumentReferenceClaim } from 'gdc-common-utils-ts/models/interoperable-claims/document-reference-claims';
|
|
7
|
+
import { buildIndividualOrganizationRegistrationGatewayRequestFromDraft } from 'gdc-sdk-core-ts';
|
|
5
8
|
import { GwCoreLifecycleRequestType } from './constants/lifecycle.js';
|
|
6
9
|
import { resolvePollOptionsFromSeconds } from './poll-options.js';
|
|
7
10
|
/**
|
|
@@ -22,20 +25,40 @@ export async function registerIndividualOrganizationWithDeps(deps) {
|
|
|
22
25
|
* in the payload for compatibility, but the owner claims are the live GW
|
|
23
26
|
* routing/indexing contract for this flow.
|
|
24
27
|
*/
|
|
28
|
+
const onboardingDraft = deps.input.onboardingDraft;
|
|
29
|
+
if (onboardingDraft && !hasSignedPdfEvidence(onboardingDraft)) {
|
|
30
|
+
const subjectAlternateName = String(onboardingDraft.formFields.subjectAlternateName
|
|
31
|
+
|| onboardingDraft.claims?.[ClaimsOrganizationSchemaorg.alternateName]
|
|
32
|
+
|| '').trim();
|
|
33
|
+
if (!subjectAlternateName) {
|
|
34
|
+
throw new Error('registerIndividualOrganization subjectAlternateName is required when signed PDF evidence is absent.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
25
37
|
const alternateName = String(deps.input.alternateName || '').trim();
|
|
26
|
-
if (!alternateName) {
|
|
38
|
+
if (!onboardingDraft && !alternateName) {
|
|
27
39
|
throw new Error('registerIndividualOrganization requires alternateName.');
|
|
28
40
|
}
|
|
29
|
-
const controllerEmail = String(deps.input.controllerEmail
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
const controllerEmail = String(deps.input.controllerEmail
|
|
42
|
+
|| onboardingDraft?.formFields.controllerEmail
|
|
43
|
+
|| onboardingDraft?.claims?.[ClaimsOrganizationSchemaorg.ownerEmail]
|
|
44
|
+
|| '').trim();
|
|
45
|
+
const controllerTelephone = String(deps.input.controllerTelephone
|
|
46
|
+
|| onboardingDraft?.formFields.controllerPhone
|
|
47
|
+
|| onboardingDraft?.claims?.[ClaimsOrganizationSchemaorg.ownerTelephone]
|
|
48
|
+
|| '').trim();
|
|
49
|
+
if (!onboardingDraft && !controllerEmail && !controllerTelephone) {
|
|
32
50
|
throw new Error('registerIndividualOrganization requires controllerEmail, or controllerTelephone only for compatibility/extension flows.');
|
|
33
51
|
}
|
|
34
52
|
const controllerRole = String(deps.input.controllerRole || 'RESPRSN').trim();
|
|
53
|
+
const controllerIdentifier = canonicalControllerUuid(deps.input.controllerIdentifier
|
|
54
|
+
|| deps.input.additionalClaims?.[ClaimsOrganizationSchemaorg.ownerIdentifierValue]
|
|
55
|
+
|| onboardingDraft?.claims?.[ClaimsOrganizationSchemaorg.ownerIdentifierValue]
|
|
56
|
+
|| randomUUID());
|
|
35
57
|
const claims = {
|
|
36
58
|
'@context': 'org.schema',
|
|
37
59
|
...(deps.input.additionalClaims || {}),
|
|
38
|
-
[ClaimsOrganizationSchemaorg.alternateName]: alternateName,
|
|
60
|
+
...(alternateName ? { [ClaimsOrganizationSchemaorg.alternateName]: alternateName } : {}),
|
|
61
|
+
[ClaimsOrganizationSchemaorg.ownerIdentifierValue]: controllerIdentifier,
|
|
39
62
|
[ClaimsServiceSchemaorg.category]: deps.routeCtx.sector,
|
|
40
63
|
[ClaimsPersonSchemaorg.hasOccupationalRoleValue]: controllerRole,
|
|
41
64
|
...(controllerEmail
|
|
@@ -51,18 +74,24 @@ export async function registerIndividualOrganizationWithDeps(deps) {
|
|
|
51
74
|
}
|
|
52
75
|
: {}),
|
|
53
76
|
};
|
|
77
|
+
const registrationBody = onboardingDraft
|
|
78
|
+
? buildIndividualOrganizationRegistrationGatewayRequestFromDraft({
|
|
79
|
+
draft: onboardingDraft,
|
|
80
|
+
routingClaims: claims,
|
|
81
|
+
})
|
|
82
|
+
: {
|
|
83
|
+
data: [{
|
|
84
|
+
type: GwCoreLifecycleRequestType.IndividualOrganizationRegistration,
|
|
85
|
+
resource: { meta: { claims } },
|
|
86
|
+
}],
|
|
87
|
+
};
|
|
54
88
|
const registrationPayload = {
|
|
55
89
|
jti: `jti-${createRuntimeUuid()}`,
|
|
56
90
|
iss: deps.routeCtx.tenantId,
|
|
57
91
|
aud: deps.routeCtx.tenantId,
|
|
58
92
|
type: 'application/didcomm-plain+json',
|
|
59
93
|
thid: `family-org-${createRuntimeUuid()}`,
|
|
60
|
-
body:
|
|
61
|
-
data: [{
|
|
62
|
-
type: GwCoreLifecycleRequestType.IndividualOrganizationRegistration,
|
|
63
|
-
resource: { meta: { claims } },
|
|
64
|
-
}],
|
|
65
|
-
},
|
|
94
|
+
body: registrationBody,
|
|
66
95
|
};
|
|
67
96
|
const pollOptions = resolvePollOptionsFromSeconds(deps.input.timeoutSeconds, deps.input.intervalSeconds, {
|
|
68
97
|
timeoutMs: deps.defaultTimeoutMs,
|
|
@@ -90,9 +119,30 @@ export async function registerIndividualOrganizationWithDeps(deps) {
|
|
|
90
119
|
offerPreview: deps.getOfferPreviewFromResponse(registration),
|
|
91
120
|
registrationStatus: registrationSummary?.status,
|
|
92
121
|
orderConfirmationRequired: registrationSummary?.status !== 'already_exists',
|
|
93
|
-
identity: readIndividualOrganizationBootstrapIdentity(registration.poll.body
|
|
122
|
+
identity: readIndividualOrganizationBootstrapIdentity(registration.poll.body, {
|
|
123
|
+
controllerEmail,
|
|
124
|
+
controllerTelephone,
|
|
125
|
+
}),
|
|
94
126
|
};
|
|
95
127
|
}
|
|
128
|
+
function canonicalControllerUuid(value) {
|
|
129
|
+
const hexadecimal = normalizeUuid(String(value || '').trim());
|
|
130
|
+
if (!hexadecimal) {
|
|
131
|
+
throw new TypeError('controllerIdentifier must be a UUID or urn:uuid identifier.');
|
|
132
|
+
}
|
|
133
|
+
return [
|
|
134
|
+
hexadecimal.slice(0, 8),
|
|
135
|
+
hexadecimal.slice(8, 12),
|
|
136
|
+
hexadecimal.slice(12, 16),
|
|
137
|
+
hexadecimal.slice(16, 20),
|
|
138
|
+
hexadecimal.slice(20),
|
|
139
|
+
].join('-');
|
|
140
|
+
}
|
|
141
|
+
function hasSignedPdfEvidence(draft) {
|
|
142
|
+
const claims = draft.documentReference?.resource?.meta?.claims;
|
|
143
|
+
return Boolean(String(claims?.[DocumentReferenceClaim.ContentType] || '').trim()
|
|
144
|
+
&& String(claims?.[DocumentReferenceClaim.ContentData] || '').trim());
|
|
145
|
+
}
|
|
96
146
|
/** @deprecated Use `registerIndividualOrganizationWithDeps`. */
|
|
97
147
|
export async function startIndividualOrganizationWithDeps(deps) {
|
|
98
148
|
return registerIndividualOrganizationWithDeps(deps);
|
|
@@ -105,7 +155,7 @@ export async function startIndividualOrganizationWithDeps(deps) {
|
|
|
105
155
|
* `:organization:taxid:` DID path. `buildIndividualDidWeb(...)` only adds the
|
|
106
156
|
* individual suffix and never rewrites that provider lineage.
|
|
107
157
|
*/
|
|
108
|
-
export function readIndividualOrganizationBootstrapIdentity(responseBody) {
|
|
158
|
+
export function readIndividualOrganizationBootstrapIdentity(responseBody, controller) {
|
|
109
159
|
const root = asRecord(responseBody);
|
|
110
160
|
const body = asRecord(root?.body) || root;
|
|
111
161
|
const entries = Array.isArray(body?.data) ? body.data : [];
|
|
@@ -126,16 +176,33 @@ export function readIndividualOrganizationBootstrapIdentity(responseBody) {
|
|
|
126
176
|
catch {
|
|
127
177
|
return undefined;
|
|
128
178
|
}
|
|
179
|
+
const subjectDid = buildIndividualDidWeb({
|
|
180
|
+
providerDidWeb,
|
|
181
|
+
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
182
|
+
secureIdValueIndividual,
|
|
183
|
+
});
|
|
184
|
+
const controllerEmail = String(controller?.controllerEmail || claims[ClaimsOrganizationSchemaorg.ownerEmail] || '').trim();
|
|
185
|
+
const controllerTelephone = String(controller?.controllerTelephone || claims[ClaimsOrganizationSchemaorg.ownerTelephone] || '').trim();
|
|
186
|
+
const controllerActorDid = controllerEmail || controllerTelephone
|
|
187
|
+
? buildIndividualMemberDidWebFromPrivateIdentifiers({
|
|
188
|
+
providerDidWeb,
|
|
189
|
+
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
190
|
+
privateIdValueIndividual: resourceId,
|
|
191
|
+
secureIdTypeMember: controllerEmail
|
|
192
|
+
? SecureIdTypesIndividual.Email
|
|
193
|
+
: SecureIdTypesIndividual.Phone,
|
|
194
|
+
privateIdValueMember: controllerEmail || controllerTelephone,
|
|
195
|
+
roleType: HL7_CODING_SYSTEM_V3_ROLE_CODE,
|
|
196
|
+
roleValue: HealthcareActorRoleCodes.Controller,
|
|
197
|
+
})
|
|
198
|
+
: undefined;
|
|
129
199
|
return {
|
|
130
200
|
resourceId,
|
|
131
201
|
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
132
202
|
secureIdValueIndividual,
|
|
133
203
|
providerDidWeb,
|
|
134
|
-
subjectDid
|
|
135
|
-
|
|
136
|
-
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
137
|
-
secureIdValueIndividual,
|
|
138
|
-
}),
|
|
204
|
+
subjectDid,
|
|
205
|
+
...(controllerActorDid ? { controllerActorDid } : {}),
|
|
139
206
|
};
|
|
140
207
|
}
|
|
141
208
|
function asRecord(value) {
|
|
@@ -7,7 +7,7 @@ import type { HostedTenantDescendantKind } from './host-onboarding.js';
|
|
|
7
7
|
import type { NodeLegalOrganizationVerificationTransactionInput, NodeOrganizationDidBindingInput, NodeOrganizationActivationInput } from './orchestration/client-port.js';
|
|
8
8
|
import { type IndividualOrganizationConfirmOrderInput, type IndividualOrganizationOrderResult, type RouteContext } from './individual-onboarding.js';
|
|
9
9
|
import { type EnsureFamilyOrganizationRegistrationInput } from './family-organization-registration.js';
|
|
10
|
-
import { type FamilyOrganizationSearchInput } from './family-organization-search.js';
|
|
10
|
+
import { type FamilyOrganizationSearchInput, type OwnedFamilyOrganizationDirectoryInput } from './family-organization-search.js';
|
|
11
11
|
import { type SmartTokenRequestInput } from './smart-token.js';
|
|
12
12
|
import { type EmployeeDeviceActivationRequestInput, type EmployeeDeviceActivationResult, type EmployeeDeviceOtpRecoveryInput, type EmployeeDeviceOtpRecoveryResult, type EmployeeDeviceRevocationInput } from './device-activation.js';
|
|
13
13
|
import { type OrganizationLicenseOrderConfirmInput } from './organization-license-order.js';
|
|
@@ -386,6 +386,12 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
|
|
|
386
386
|
missingFields?: string[];
|
|
387
387
|
updatedAt?: string;
|
|
388
388
|
}> | null>;
|
|
389
|
+
/** Lists all individual Organizations owned by one verified account contact. */
|
|
390
|
+
listOwnedFamilyOrganizations(ctx: RouteContext, input: OwnedFamilyOrganizationDirectoryInput): Promise<Readonly<{
|
|
391
|
+
resourceId: string;
|
|
392
|
+
alternateName: string;
|
|
393
|
+
claims: Readonly<Record<string, unknown>>;
|
|
394
|
+
}>[]>;
|
|
389
395
|
/**
|
|
390
396
|
* Searches one existing family/individual registration and starts the
|
|
391
397
|
* bootstrap flow only when the registration does not already exist.
|
|
@@ -4,7 +4,7 @@ import { confirmLegalOrganizationOrderWithDeps, HostLifecycleRequestType, Hosted
|
|
|
4
4
|
import { GwCoreLifecycleRequestType } from './constants/lifecycle.js';
|
|
5
5
|
import { confirmIndividualOrganizationOrderWithDeps, } from './individual-onboarding.js';
|
|
6
6
|
import { ensureFamilyOrganizationRegistrationWithDeps, } from './family-organization-registration.js';
|
|
7
|
-
import { searchFamilyOrganizationWithDeps } from './family-organization-search.js';
|
|
7
|
+
import { listOwnedFamilyOrganizationsWithDeps, searchFamilyOrganizationWithDeps, } from './family-organization-search.js';
|
|
8
8
|
import { requestSmartTokenWithDeps } from './smart-token.js';
|
|
9
9
|
import { activateEmployeeDeviceWithActivationCodeWithDeps, activateEmployeeDeviceWithActivationRequestWithDeps, recoverEmployeeDeviceWithOtpWithDeps, revokeEmployeeDeviceWithDeps, } from './device-activation.js';
|
|
10
10
|
import { extractOfferIdFromResponseBody, extractOfferPreviewFromResponseBody, } from './order-offer-summary.js';
|
|
@@ -680,6 +680,16 @@ export class HttpRuntimeClient {
|
|
|
680
680
|
submitAndPoll: this.submitAndPoll.bind(this),
|
|
681
681
|
});
|
|
682
682
|
}
|
|
683
|
+
/** Lists all individual Organizations owned by one verified account contact. */
|
|
684
|
+
async listOwnedFamilyOrganizations(ctx, input) {
|
|
685
|
+
return listOwnedFamilyOrganizationsWithDeps(ctx, input, {
|
|
686
|
+
defaultTimeoutMs: 20000,
|
|
687
|
+
defaultIntervalMs: 1000,
|
|
688
|
+
individualFamilyOrganizationSearchPath: this.paths.individualFamilyOrganizationSearchPath.bind(this.paths),
|
|
689
|
+
individualFamilyOrganizationSearchPollPath: this.paths.individualFamilyOrganizationSearchPollPath.bind(this.paths),
|
|
690
|
+
submitAndPoll: this.submitAndPoll.bind(this),
|
|
691
|
+
});
|
|
692
|
+
}
|
|
683
693
|
/**
|
|
684
694
|
* Searches one existing family/individual registration and starts the
|
|
685
695
|
* bootstrap flow only when the registration does not already exist.
|
|
@@ -9,6 +9,15 @@ import type { NodeCapability } from '../session.js';
|
|
|
9
9
|
import type { IndividualOrganizationLifecycleInput } from 'gdc-sdk-core-ts';
|
|
10
10
|
import type { BlockchainArtifactRegistrationInput, ClinicalBundleSearchInput, ClinicalSectionUpdateInput, SubjectSectionUpdateInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, ClinicalSummaryUpdateInput, CommunicationIngestionInput, CommunicationParticipantRuntimeSearchInput, DigitalTwinGenerationInput, DigitalTwinSecondaryUseConsentInput, DigitalTwinSecondaryUseConsentResult, DigitalTwinSubjectLinkPurgeInput, DigitalTwinSubjectLinkPurgeResult, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, ProfessionalAccessRequestDecisionInput, ProfessionalAccessRequestSearchInput, RelatedPersonUpsertInput } from '../resource-operations.js';
|
|
11
11
|
import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
|
|
12
|
+
/** Profile-owned defaults applied to subject-section mutations after unlock. */
|
|
13
|
+
export type IndividualControllerProfileDefaults = Readonly<{
|
|
14
|
+
attester: SubjectSectionUpdateInput['attester'];
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* High-level section input. An opened controller profile supplies its own
|
|
18
|
+
* protected RESPRSN attester; standalone facades must still provide one.
|
|
19
|
+
*/
|
|
20
|
+
export type IndividualControllerSubjectSectionUpdateInput = Omit<SubjectSectionUpdateInput, 'attester'> & Partial<Pick<SubjectSectionUpdateInput, 'attester'>>;
|
|
12
21
|
/**
|
|
13
22
|
* Individual-controller oriented facade over a `NodeRuntimeClient`.
|
|
14
23
|
*
|
|
@@ -18,10 +27,11 @@ import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-
|
|
|
18
27
|
export declare class IndividualControllerSdk {
|
|
19
28
|
private readonly client;
|
|
20
29
|
private readonly capabilities?;
|
|
30
|
+
private readonly profileDefaults?;
|
|
21
31
|
/**
|
|
22
32
|
* @param client Runtime client implementation used to submit and poll GW flows.
|
|
23
33
|
*/
|
|
24
|
-
constructor(client: NodeRuntimeClient, capabilities?: readonly NodeCapability[] | undefined);
|
|
34
|
+
constructor(client: NodeRuntimeClient, capabilities?: readonly NodeCapability[] | undefined, profileDefaults?: IndividualControllerProfileDefaults | undefined);
|
|
25
35
|
/**
|
|
26
36
|
* Registers the personal organization/subject index and returns its Offer.
|
|
27
37
|
* Wallet creation, activation exchange, DCR, and session opening are later
|
|
@@ -118,7 +128,7 @@ export declare class IndividualControllerSdk {
|
|
|
118
128
|
* appointments or contracts, while preserving the indexed Composition
|
|
119
129
|
* author/attester compatibility contract.
|
|
120
130
|
*/
|
|
121
|
-
updateSubjectSection(ctx: RouteContext, input:
|
|
131
|
+
updateSubjectSection(ctx: RouteContext, input: IndividualControllerSubjectSectionUpdateInput): Promise<SubmitAndPollResult>;
|
|
122
132
|
/**
|
|
123
133
|
* Updates the multi-section summary through a Composition-first document.
|
|
124
134
|
* On a direct call, use the authenticated profile `actorDid` as `sender` and
|
|
@@ -18,9 +18,10 @@ export class IndividualControllerSdk {
|
|
|
18
18
|
/**
|
|
19
19
|
* @param client Runtime client implementation used to submit and poll GW flows.
|
|
20
20
|
*/
|
|
21
|
-
constructor(client, capabilities) {
|
|
21
|
+
constructor(client, capabilities, profileDefaults) {
|
|
22
22
|
this.client = client;
|
|
23
23
|
this.capabilities = capabilities;
|
|
24
|
+
this.profileDefaults = profileDefaults;
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
27
|
* Registers the personal organization/subject index and returns its Offer.
|
|
@@ -173,7 +174,11 @@ export class IndividualControllerSdk {
|
|
|
173
174
|
*/
|
|
174
175
|
updateSubjectSection(ctx, input) {
|
|
175
176
|
assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualIngestCommunication, ActorKinds.IndividualController, 'updateSubjectSection');
|
|
176
|
-
|
|
177
|
+
const attester = input.attester || this.profileDefaults?.attester;
|
|
178
|
+
if (!attester) {
|
|
179
|
+
throw new Error('updateSubjectSection requires an explicit attester unless the facade comes from an opened controller profile.');
|
|
180
|
+
}
|
|
181
|
+
return requireClientMethod(this.client, 'updateSubjectSection')(ctx, { ...input, attester });
|
|
177
182
|
}
|
|
178
183
|
/**
|
|
179
184
|
* Updates the multi-section summary through a Composition-first document.
|
|
@@ -11,7 +11,8 @@ import { ProfessionalSdk } from './orchestration/professional-sdk.js';
|
|
|
11
11
|
import { DigitalTwinSdk } from './orchestration/digital-twin-sdk.js';
|
|
12
12
|
import type { SmartTokenExchangeResult } from './smart-token.js';
|
|
13
13
|
import type { DigitalTwinSearchInput, DigitalTwinSearchResult } from './digital-twin.js';
|
|
14
|
-
import type { RouteContext } from './individual-onboarding.js';
|
|
14
|
+
import type { IndividualOrganizationOrderResult, RouteContext } from './individual-onboarding.js';
|
|
15
|
+
import type { IndividualOrganizationRegistrationResult } from './individual-start.js';
|
|
15
16
|
import type { HostRouteContext } from './host-onboarding.js';
|
|
16
17
|
import type { SecureDidcommTransportAdapter } from 'gdc-sdk-core-ts';
|
|
17
18
|
import { type PinProtectedProfileSecret, type ProfileProtectionOptions, type ServerProfileSealer } from './server-profile-protection.js';
|
|
@@ -145,6 +146,13 @@ export type ServerProfileEnrollmentInput = Readonly<{
|
|
|
145
146
|
idToken: string;
|
|
146
147
|
/** One-time code returned by the completed organization flow or employee `License/_issue`. */
|
|
147
148
|
activationCode: string;
|
|
149
|
+
/**
|
|
150
|
+
* Principal controller identifier projected by the SDK from
|
|
151
|
+
* `Organization.owner.identifier.value` and the matching automatic
|
|
152
|
+
* `RelatedPerson.identifier`. Individual-controller callers pass this
|
|
153
|
+
* identifier; the SDK constructs protected document-attester metadata.
|
|
154
|
+
*/
|
|
155
|
+
controllerRelatedPersonIdentifier?: string;
|
|
148
156
|
/**
|
|
149
157
|
* Optional explicit installation identity. Normally omit it so the SDK
|
|
150
158
|
* derives the identity from the wallet key it creates for this profile.
|
|
@@ -292,6 +300,43 @@ export type OpenedServerIndividualController = Readonly<{
|
|
|
292
300
|
session: ResolvedServerProfileSession;
|
|
293
301
|
profile: ServerProfileRecord;
|
|
294
302
|
sdk: IndividualControllerSdk;
|
|
303
|
+
/** Returns the protected RelatedPerson URI for FHIR document attestation. */
|
|
304
|
+
getAttesterUriForDocs(): string;
|
|
305
|
+
}>;
|
|
306
|
+
/** One-time self-controller enrollment. It never opens a working session. */
|
|
307
|
+
export type ServerSelfIndividualControllerEnrollmentInput = Readonly<{
|
|
308
|
+
ownerId: string;
|
|
309
|
+
profileId: string;
|
|
310
|
+
registration: Pick<IndividualOrganizationRegistrationResult, 'identity'>;
|
|
311
|
+
order: Pick<IndividualOrganizationOrderResult, 'activationCode' | 'controllerRelatedPersonIdentifier'>;
|
|
312
|
+
routeContext: RouteContext;
|
|
313
|
+
pin: string;
|
|
314
|
+
idToken: string;
|
|
315
|
+
redirectUris: string[];
|
|
316
|
+
clientName: string;
|
|
317
|
+
}>;
|
|
318
|
+
/**
|
|
319
|
+
* One-time enrollment when the controller and represented subject are
|
|
320
|
+
* different identities. Demographics and alternateName belong to registration
|
|
321
|
+
* evidence and are deliberately not accepted by this operation.
|
|
322
|
+
*/
|
|
323
|
+
export type ServerIndividualControllerEnrollmentInput = Readonly<{
|
|
324
|
+
ownerId: string;
|
|
325
|
+
profileId: string;
|
|
326
|
+
/** Stable controller/member DID that owns the wallet and DCR client. */
|
|
327
|
+
controllerActorDid: string;
|
|
328
|
+
/**
|
|
329
|
+
* Authorized subject alias used by the product, when different from the
|
|
330
|
+
* hosted subject DID returned by registration (for example, an animal card).
|
|
331
|
+
*/
|
|
332
|
+
subjectDid?: string;
|
|
333
|
+
registration: Pick<IndividualOrganizationRegistrationResult, 'identity'>;
|
|
334
|
+
order: Pick<IndividualOrganizationOrderResult, 'activationCode' | 'controllerRelatedPersonIdentifier'>;
|
|
335
|
+
routeContext: RouteContext;
|
|
336
|
+
pin: string;
|
|
337
|
+
idToken: string;
|
|
338
|
+
redirectUris: string[];
|
|
339
|
+
clientName: string;
|
|
295
340
|
}>;
|
|
296
341
|
/** Server-owned role evidence used to sign a fresh professional VP. */
|
|
297
342
|
export type ServerProfessionalProofInput = Readonly<{
|
|
@@ -368,6 +413,12 @@ export type ServerProfileSessionManagerOptions = Readonly<{
|
|
|
368
413
|
export declare class ServerProfileSessionManager {
|
|
369
414
|
private readonly options;
|
|
370
415
|
constructor(options: ServerProfileSessionManagerOptions);
|
|
416
|
+
/**
|
|
417
|
+
* Enrolls a controller for a different represented subject. The SDK consumes
|
|
418
|
+
* the automatic GW RESPRSN assignment and does not ask the caller to build an
|
|
419
|
+
* attester. This operation does not accept or update subject demographics.
|
|
420
|
+
*/
|
|
421
|
+
enrollIndividualController(input: ServerIndividualControllerEnrollmentInput): Promise<ServerProfileRecord>;
|
|
371
422
|
/**
|
|
372
423
|
* Resolves the canonical IPS author/attester projection from one owned
|
|
373
424
|
* profile without exposing its protected wallet material to the portal.
|
|
@@ -383,6 +434,13 @@ export declare class ServerProfileSessionManager {
|
|
|
383
434
|
*/
|
|
384
435
|
sourceAuthor?: ClinicalSourceAuthorSelection;
|
|
385
436
|
}>): Promise<ClinicalCreatorIpsExport>;
|
|
437
|
+
/**
|
|
438
|
+
* Enrolls the controller of a self-managed individual from SDK-owned
|
|
439
|
+
* registration and Order projections. The caller never parses claims or
|
|
440
|
+
* constructs FHIR attester metadata, and this operation does not open the
|
|
441
|
+
* resulting profile.
|
|
442
|
+
*/
|
|
443
|
+
enrollSelfIndividualController(input: ServerSelfIndividualControllerEnrollmentInput): Promise<ServerProfileRecord>;
|
|
386
444
|
enroll(input: ServerProfileEnrollmentInput): Promise<ServerProfileRecord>;
|
|
387
445
|
/**
|
|
388
446
|
* Rotates an employee wallet after a fresh OTP-authenticated GW recovery.
|
|
@@ -24,20 +24,23 @@ import { ProfilePinRejectedError, openServerProfileSecret, protectServerProfileS
|
|
|
24
24
|
* RelatedPerson/PractitionerRole assignment returned by GW.
|
|
25
25
|
*/
|
|
26
26
|
export function buildProfileAttester(input) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
const assignmentIdentifier = String(input.assignmentIdentifier || '').trim();
|
|
28
|
+
const hexadecimal = normalizeUuid(assignmentIdentifier);
|
|
29
|
+
const reference = hexadecimal
|
|
30
|
+
? `${UrnPrefixes.Uuid}${[
|
|
31
|
+
hexadecimal.slice(0, 8),
|
|
32
|
+
hexadecimal.slice(8, 12),
|
|
33
|
+
hexadecimal.slice(12, 16),
|
|
34
|
+
hexadecimal.slice(16, 20),
|
|
35
|
+
hexadecimal.slice(20),
|
|
36
|
+
].join('-')}`
|
|
37
|
+
: assignmentIdentifier;
|
|
38
|
+
if (!/^(?:urn:|https?:\/\/)/i.test(reference)) {
|
|
39
|
+
throw new TypeError('Profile attester assignmentIdentifier must be a governed RelatedPerson or PractitionerRole URI.');
|
|
40
|
+
}
|
|
38
41
|
return Object.freeze({
|
|
39
42
|
mode: input.mode,
|
|
40
|
-
party: Object.freeze({ reference
|
|
43
|
+
party: Object.freeze({ reference }),
|
|
41
44
|
});
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
@@ -69,6 +72,42 @@ export class ServerProfileSessionManager {
|
|
|
69
72
|
constructor(options) {
|
|
70
73
|
this.options = options;
|
|
71
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Enrolls a controller for a different represented subject. The SDK consumes
|
|
77
|
+
* the automatic GW RESPRSN assignment and does not ask the caller to build an
|
|
78
|
+
* attester. This operation does not accept or update subject demographics.
|
|
79
|
+
*/
|
|
80
|
+
async enrollIndividualController(input) {
|
|
81
|
+
const identity = input.registration.identity;
|
|
82
|
+
if (!identity) {
|
|
83
|
+
throw new Error('Individual-controller enrollment requires the registered individual identity.');
|
|
84
|
+
}
|
|
85
|
+
const controllerActorDid = String(input.controllerActorDid || '').trim();
|
|
86
|
+
if (!controllerActorDid) {
|
|
87
|
+
throw new Error('Individual-controller enrollment requires controllerActorDid.');
|
|
88
|
+
}
|
|
89
|
+
const subjectDid = String(input.subjectDid || identity.subjectDid).trim();
|
|
90
|
+
if (!subjectDid) {
|
|
91
|
+
throw new Error('Individual-controller enrollment requires subjectDid.');
|
|
92
|
+
}
|
|
93
|
+
return this.enroll({
|
|
94
|
+
ownerId: input.ownerId,
|
|
95
|
+
profileId: input.profileId,
|
|
96
|
+
actorKind: ActorKinds.IndividualController,
|
|
97
|
+
actorMode: 'controller',
|
|
98
|
+
actorDid: controllerActorDid,
|
|
99
|
+
profileDid: controllerActorDid,
|
|
100
|
+
providerDid: identity.providerDidWeb,
|
|
101
|
+
routeContext: input.routeContext,
|
|
102
|
+
allowedSubjectDids: [subjectDid],
|
|
103
|
+
pin: input.pin,
|
|
104
|
+
idToken: input.idToken,
|
|
105
|
+
activationCode: input.order.activationCode,
|
|
106
|
+
controllerRelatedPersonIdentifier: input.order.controllerRelatedPersonIdentifier,
|
|
107
|
+
redirectUris: input.redirectUris,
|
|
108
|
+
clientName: input.clientName,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
72
111
|
/**
|
|
73
112
|
* Resolves the canonical IPS author/attester projection from one owned
|
|
74
113
|
* profile without exposing its protected wallet material to the portal.
|
|
@@ -76,11 +115,50 @@ export class ServerProfileSessionManager {
|
|
|
76
115
|
async exportClinicalCreatorIps(input) {
|
|
77
116
|
return exportServerProfileClinicalCreatorIps(await this.requireOwnedProfile(input.ownerId, input.profileId), { sourceAuthor: input.sourceAuthor });
|
|
78
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Enrolls the controller of a self-managed individual from SDK-owned
|
|
120
|
+
* registration and Order projections. The caller never parses claims or
|
|
121
|
+
* constructs FHIR attester metadata, and this operation does not open the
|
|
122
|
+
* resulting profile.
|
|
123
|
+
*/
|
|
124
|
+
async enrollSelfIndividualController(input) {
|
|
125
|
+
const identity = input.registration.identity;
|
|
126
|
+
if (!identity) {
|
|
127
|
+
throw new Error('Self individual-controller enrollment requires the registered individual identity.');
|
|
128
|
+
}
|
|
129
|
+
const controllerActorDid = String(identity.controllerActorDid || '').trim();
|
|
130
|
+
if (!controllerActorDid) {
|
|
131
|
+
throw new Error('Self individual-controller enrollment requires the registered controller actor DID.');
|
|
132
|
+
}
|
|
133
|
+
return this.enroll({
|
|
134
|
+
ownerId: input.ownerId,
|
|
135
|
+
profileId: input.profileId,
|
|
136
|
+
actorKind: ActorKinds.IndividualController,
|
|
137
|
+
actorMode: 'self',
|
|
138
|
+
actorDid: controllerActorDid,
|
|
139
|
+
profileDid: controllerActorDid,
|
|
140
|
+
providerDid: identity.providerDidWeb,
|
|
141
|
+
routeContext: input.routeContext,
|
|
142
|
+
allowedSubjectDids: [identity.subjectDid],
|
|
143
|
+
pin: input.pin,
|
|
144
|
+
idToken: input.idToken,
|
|
145
|
+
activationCode: input.order.activationCode,
|
|
146
|
+
controllerRelatedPersonIdentifier: input.order.controllerRelatedPersonIdentifier,
|
|
147
|
+
redirectUris: input.redirectUris,
|
|
148
|
+
clientName: input.clientName,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
79
151
|
async enroll(input) {
|
|
80
152
|
const normalizedClinicalCreatorBinding = input.clinicalCreatorBinding
|
|
81
153
|
? normalizeClinicalCreatorBinding(input.clinicalCreatorBinding)
|
|
82
154
|
: undefined;
|
|
83
|
-
const
|
|
155
|
+
const controllerAttester = input.controllerRelatedPersonIdentifier
|
|
156
|
+
? buildProfileAttester({
|
|
157
|
+
assignmentIdentifier: input.controllerRelatedPersonIdentifier,
|
|
158
|
+
mode: CompositionAttesterModes.Personal,
|
|
159
|
+
})
|
|
160
|
+
: undefined;
|
|
161
|
+
const attester = controllerAttester || input.attester || (normalizedClinicalCreatorBinding
|
|
84
162
|
? buildProfileAttester({
|
|
85
163
|
assignmentIdentifier: normalizedClinicalCreatorBinding.authorIdentifier,
|
|
86
164
|
mode: normalizedClinicalCreatorBinding.kind === FhirIpsCreatorKinds.Professional
|
|
@@ -145,8 +223,12 @@ export class ServerProfileSessionManager {
|
|
|
145
223
|
const activation = await client.activateProfileDeviceWithActivationRequest(activationRequest);
|
|
146
224
|
const dcrBody = terminalBody(activation.dcr.poll.body);
|
|
147
225
|
const clientId = findText(dcrBody, ['client_id', 'clientId']);
|
|
148
|
-
if (!clientId)
|
|
149
|
-
|
|
226
|
+
if (!clientId) {
|
|
227
|
+
const diagnostics = findText(dcrBody, ['diagnostics', 'details', 'message']);
|
|
228
|
+
throw new Error(diagnostics
|
|
229
|
+
? `GW DCR failed: ${diagnostics}`
|
|
230
|
+
: 'GW DCR did not return client_id.');
|
|
231
|
+
}
|
|
150
232
|
const deviceDid = findText(dcrBody, ['device_did', 'deviceDid', 'did']) || clientId;
|
|
151
233
|
const now = this.now();
|
|
152
234
|
const managedVpToken = input.vpToken || (input.professionalProof
|
|
@@ -562,6 +644,10 @@ export class ServerProfileSessionManager {
|
|
|
562
644
|
|| (profile.actorMode !== 'self' && profile.actorMode !== 'controller')) {
|
|
563
645
|
throw new Error('Profile is not an individual controller.');
|
|
564
646
|
}
|
|
647
|
+
const profileAttester = profile.attester;
|
|
648
|
+
if (!profileAttester) {
|
|
649
|
+
throw new Error('The opened individual-controller profile has no RelatedPerson attester.');
|
|
650
|
+
}
|
|
565
651
|
const client = new NodeHttpClient({
|
|
566
652
|
baseUrl: this.options.gatewayBaseUrl,
|
|
567
653
|
ctx: profile.routeContext,
|
|
@@ -571,7 +657,20 @@ export class ServerProfileSessionManager {
|
|
|
571
657
|
transportProfile: TransportProfiles.DidcommEncryptedForm,
|
|
572
658
|
secureTransportAdapter: session.secureTransportAdapter,
|
|
573
659
|
});
|
|
574
|
-
return {
|
|
660
|
+
return {
|
|
661
|
+
session,
|
|
662
|
+
profile,
|
|
663
|
+
sdk: new IndividualControllerSdk(client, undefined, {
|
|
664
|
+
attester: profileAttester,
|
|
665
|
+
}),
|
|
666
|
+
getAttesterUriForDocs: () => {
|
|
667
|
+
const reference = String(profileAttester.party.reference || '').trim();
|
|
668
|
+
if (!reference) {
|
|
669
|
+
throw new Error('The opened individual-controller profile has no RelatedPerson attester URI.');
|
|
670
|
+
}
|
|
671
|
+
return reference;
|
|
672
|
+
},
|
|
673
|
+
};
|
|
575
674
|
}
|
|
576
675
|
/**
|
|
577
676
|
* Opens the high-level organization-controller API without exposing wallet,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdc-sdk-node-ts",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.14",
|
|
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.",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"prepare": "npm run build",
|
|
16
16
|
"type-check": "tsc -p tsconfig.json --noEmit && tsc -p docs/snippets/tsconfig.json",
|
|
17
17
|
"check:product-neutrality": "node scripts/check-product-neutrality.mjs",
|
|
18
|
-
"prepublishOnly": "npm run check:product-neutrality && npm run type-check && npm test",
|
|
18
|
+
"prepublishOnly": "npm run check:product-neutrality && npm run type-check && npm test && npm run test:e2e:live-full-cycle",
|
|
19
19
|
"local:close": "PORTS=3000 bash ./scripts/local-close.sh",
|
|
20
20
|
"docker:close": "PORTS=8000 bash ./scripts/local-close.sh",
|
|
21
21
|
"test": "npm run build && node --test tests/*.test.mjs",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"gdc-common-utils-ts": "2.9.
|
|
42
|
-
"gdc-sdk-core-ts": "2.9.
|
|
41
|
+
"gdc-common-utils-ts": "2.9.14",
|
|
42
|
+
"gdc-sdk-core-ts": "2.9.9"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^20.14.10",
|