gdc-sdk-node-ts 2.9.12 → 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-start.d.ts +20 -2
- package/dist/individual-start.js +68 -19
- package/dist/node-runtime-client.d.ts +7 -1
- package/dist/node-runtime-client.js +11 -1
- package/dist/server-profile-session.d.ts +29 -0
- package/dist/server-profile-session.js +48 -4
- 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) {
|
|
@@ -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
|
*
|
|
@@ -97,6 +106,12 @@ export type IndividualOrganizationBootstrapIdentity = {
|
|
|
97
106
|
providerDidWeb: string;
|
|
98
107
|
/** Canonical child DID built beneath the exact provider DID returned by GW. */
|
|
99
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;
|
|
100
115
|
};
|
|
101
116
|
type RegisterIndividualOrganizationDeps = {
|
|
102
117
|
input: IndividualOrganizationRegistrationInput;
|
|
@@ -128,4 +143,7 @@ export declare function startIndividualOrganizationWithDeps(deps: RegisterIndivi
|
|
|
128
143
|
* `:organization:taxid:` DID path. `buildIndividualDidWeb(...)` only adds the
|
|
129
144
|
* individual suffix and never rewrites that provider lineage.
|
|
130
145
|
*/
|
|
131
|
-
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,8 +1,10 @@
|
|
|
1
1
|
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { ClaimsOfferSchemaorg, ClaimsOrganizationSchemaorg, ClaimsPersonSchemaorg, ClaimsServiceSchemaorg, } from 'gdc-common-utils-ts/constants';
|
|
4
|
-
import { buildIndividualDidWeb, buildSecureIdValueIndividual, extractPrimaryClaims, normalizeUuid, 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';
|
|
5
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';
|
|
6
8
|
import { GwCoreLifecycleRequestType } from './constants/lifecycle.js';
|
|
7
9
|
import { resolvePollOptionsFromSeconds } from './poll-options.js';
|
|
8
10
|
/**
|
|
@@ -23,23 +25,39 @@ export async function registerIndividualOrganizationWithDeps(deps) {
|
|
|
23
25
|
* in the payload for compatibility, but the owner claims are the live GW
|
|
24
26
|
* routing/indexing contract for this flow.
|
|
25
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
|
+
}
|
|
26
37
|
const alternateName = String(deps.input.alternateName || '').trim();
|
|
27
|
-
if (!alternateName) {
|
|
38
|
+
if (!onboardingDraft && !alternateName) {
|
|
28
39
|
throw new Error('registerIndividualOrganization requires alternateName.');
|
|
29
40
|
}
|
|
30
|
-
const controllerEmail = String(deps.input.controllerEmail
|
|
31
|
-
|
|
32
|
-
|
|
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) {
|
|
33
50
|
throw new Error('registerIndividualOrganization requires controllerEmail, or controllerTelephone only for compatibility/extension flows.');
|
|
34
51
|
}
|
|
35
52
|
const controllerRole = String(deps.input.controllerRole || 'RESPRSN').trim();
|
|
36
53
|
const controllerIdentifier = canonicalControllerUuid(deps.input.controllerIdentifier
|
|
37
54
|
|| deps.input.additionalClaims?.[ClaimsOrganizationSchemaorg.ownerIdentifierValue]
|
|
55
|
+
|| onboardingDraft?.claims?.[ClaimsOrganizationSchemaorg.ownerIdentifierValue]
|
|
38
56
|
|| randomUUID());
|
|
39
57
|
const claims = {
|
|
40
58
|
'@context': 'org.schema',
|
|
41
59
|
...(deps.input.additionalClaims || {}),
|
|
42
|
-
[ClaimsOrganizationSchemaorg.alternateName]: alternateName,
|
|
60
|
+
...(alternateName ? { [ClaimsOrganizationSchemaorg.alternateName]: alternateName } : {}),
|
|
43
61
|
[ClaimsOrganizationSchemaorg.ownerIdentifierValue]: controllerIdentifier,
|
|
44
62
|
[ClaimsServiceSchemaorg.category]: deps.routeCtx.sector,
|
|
45
63
|
[ClaimsPersonSchemaorg.hasOccupationalRoleValue]: controllerRole,
|
|
@@ -56,18 +74,24 @@ export async function registerIndividualOrganizationWithDeps(deps) {
|
|
|
56
74
|
}
|
|
57
75
|
: {}),
|
|
58
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
|
+
};
|
|
59
88
|
const registrationPayload = {
|
|
60
89
|
jti: `jti-${createRuntimeUuid()}`,
|
|
61
90
|
iss: deps.routeCtx.tenantId,
|
|
62
91
|
aud: deps.routeCtx.tenantId,
|
|
63
92
|
type: 'application/didcomm-plain+json',
|
|
64
93
|
thid: `family-org-${createRuntimeUuid()}`,
|
|
65
|
-
body:
|
|
66
|
-
data: [{
|
|
67
|
-
type: GwCoreLifecycleRequestType.IndividualOrganizationRegistration,
|
|
68
|
-
resource: { meta: { claims } },
|
|
69
|
-
}],
|
|
70
|
-
},
|
|
94
|
+
body: registrationBody,
|
|
71
95
|
};
|
|
72
96
|
const pollOptions = resolvePollOptionsFromSeconds(deps.input.timeoutSeconds, deps.input.intervalSeconds, {
|
|
73
97
|
timeoutMs: deps.defaultTimeoutMs,
|
|
@@ -95,7 +119,10 @@ export async function registerIndividualOrganizationWithDeps(deps) {
|
|
|
95
119
|
offerPreview: deps.getOfferPreviewFromResponse(registration),
|
|
96
120
|
registrationStatus: registrationSummary?.status,
|
|
97
121
|
orderConfirmationRequired: registrationSummary?.status !== 'already_exists',
|
|
98
|
-
identity: readIndividualOrganizationBootstrapIdentity(registration.poll.body
|
|
122
|
+
identity: readIndividualOrganizationBootstrapIdentity(registration.poll.body, {
|
|
123
|
+
controllerEmail,
|
|
124
|
+
controllerTelephone,
|
|
125
|
+
}),
|
|
99
126
|
};
|
|
100
127
|
}
|
|
101
128
|
function canonicalControllerUuid(value) {
|
|
@@ -111,6 +138,11 @@ function canonicalControllerUuid(value) {
|
|
|
111
138
|
hexadecimal.slice(20),
|
|
112
139
|
].join('-');
|
|
113
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
|
+
}
|
|
114
146
|
/** @deprecated Use `registerIndividualOrganizationWithDeps`. */
|
|
115
147
|
export async function startIndividualOrganizationWithDeps(deps) {
|
|
116
148
|
return registerIndividualOrganizationWithDeps(deps);
|
|
@@ -123,7 +155,7 @@ export async function startIndividualOrganizationWithDeps(deps) {
|
|
|
123
155
|
* `:organization:taxid:` DID path. `buildIndividualDidWeb(...)` only adds the
|
|
124
156
|
* individual suffix and never rewrites that provider lineage.
|
|
125
157
|
*/
|
|
126
|
-
export function readIndividualOrganizationBootstrapIdentity(responseBody) {
|
|
158
|
+
export function readIndividualOrganizationBootstrapIdentity(responseBody, controller) {
|
|
127
159
|
const root = asRecord(responseBody);
|
|
128
160
|
const body = asRecord(root?.body) || root;
|
|
129
161
|
const entries = Array.isArray(body?.data) ? body.data : [];
|
|
@@ -144,16 +176,33 @@ export function readIndividualOrganizationBootstrapIdentity(responseBody) {
|
|
|
144
176
|
catch {
|
|
145
177
|
return undefined;
|
|
146
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;
|
|
147
199
|
return {
|
|
148
200
|
resourceId,
|
|
149
201
|
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
150
202
|
secureIdValueIndividual,
|
|
151
203
|
providerDidWeb,
|
|
152
|
-
subjectDid
|
|
153
|
-
|
|
154
|
-
secureIdTypeIndividual: SecureIdTypesIndividual.Uuid,
|
|
155
|
-
secureIdValueIndividual,
|
|
156
|
-
}),
|
|
204
|
+
subjectDid,
|
|
205
|
+
...(controllerActorDid ? { controllerActorDid } : {}),
|
|
157
206
|
};
|
|
158
207
|
}
|
|
159
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.
|
|
@@ -315,6 +315,29 @@ export type ServerSelfIndividualControllerEnrollmentInput = Readonly<{
|
|
|
315
315
|
redirectUris: string[];
|
|
316
316
|
clientName: string;
|
|
317
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;
|
|
340
|
+
}>;
|
|
318
341
|
/** Server-owned role evidence used to sign a fresh professional VP. */
|
|
319
342
|
export type ServerProfessionalProofInput = Readonly<{
|
|
320
343
|
role: string;
|
|
@@ -390,6 +413,12 @@ export type ServerProfileSessionManagerOptions = Readonly<{
|
|
|
390
413
|
export declare class ServerProfileSessionManager {
|
|
391
414
|
private readonly options;
|
|
392
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>;
|
|
393
422
|
/**
|
|
394
423
|
* Resolves the canonical IPS author/attester projection from one owned
|
|
395
424
|
* profile without exposing its protected wallet material to the portal.
|
|
@@ -72,6 +72,42 @@ export class ServerProfileSessionManager {
|
|
|
72
72
|
constructor(options) {
|
|
73
73
|
this.options = options;
|
|
74
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
|
+
}
|
|
75
111
|
/**
|
|
76
112
|
* Resolves the canonical IPS author/attester projection from one owned
|
|
77
113
|
* profile without exposing its protected wallet material to the portal.
|
|
@@ -90,13 +126,17 @@ export class ServerProfileSessionManager {
|
|
|
90
126
|
if (!identity) {
|
|
91
127
|
throw new Error('Self individual-controller enrollment requires the registered individual identity.');
|
|
92
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
|
+
}
|
|
93
133
|
return this.enroll({
|
|
94
134
|
ownerId: input.ownerId,
|
|
95
135
|
profileId: input.profileId,
|
|
96
136
|
actorKind: ActorKinds.IndividualController,
|
|
97
137
|
actorMode: 'self',
|
|
98
|
-
actorDid:
|
|
99
|
-
profileDid:
|
|
138
|
+
actorDid: controllerActorDid,
|
|
139
|
+
profileDid: controllerActorDid,
|
|
100
140
|
providerDid: identity.providerDidWeb,
|
|
101
141
|
routeContext: input.routeContext,
|
|
102
142
|
allowedSubjectDids: [identity.subjectDid],
|
|
@@ -183,8 +223,12 @@ export class ServerProfileSessionManager {
|
|
|
183
223
|
const activation = await client.activateProfileDeviceWithActivationRequest(activationRequest);
|
|
184
224
|
const dcrBody = terminalBody(activation.dcr.poll.body);
|
|
185
225
|
const clientId = findText(dcrBody, ['client_id', 'clientId']);
|
|
186
|
-
if (!clientId)
|
|
187
|
-
|
|
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
|
+
}
|
|
188
232
|
const deviceDid = findText(dcrBody, ['device_did', 'deviceDid', 'did']) || clientId;
|
|
189
233
|
const now = this.now();
|
|
190
234
|
const managedVpToken = input.vpToken || (input.professionalProof
|
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",
|