gdc-sdk-node-ts 2.0.2 → 2.0.5
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 +72 -30
- package/dist/backend-profile-runtime.d.ts +48 -0
- package/dist/backend-profile-runtime.js +62 -0
- package/dist/family-organization-registration.d.ts +40 -0
- package/dist/family-organization-registration.js +54 -0
- package/dist/family-organization-search.d.ts +30 -0
- package/dist/family-organization-search.js +59 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/individual-controller-backend-runtime.d.ts +13 -0
- package/dist/individual-controller-backend-runtime.js +14 -0
- package/dist/legal-organization-onboarding-facade.d.ts +70 -0
- package/dist/legal-organization-onboarding-facade.js +169 -0
- package/dist/node-runtime-client.d.ts +47 -4
- package/dist/node-runtime-client.js +75 -7
- package/dist/orchestration/client-port.d.ts +7 -1
- package/dist/orchestration/individual-controller-sdk.d.ts +18 -1
- package/dist/orchestration/individual-controller-sdk.js +21 -0
- package/dist/orchestration/organization-controller-sdk.d.ts +3 -2
- package/dist/orchestration/organization-controller-sdk.js +3 -2
- package/dist/orchestration/professional-sdk.d.ts +11 -1
- package/dist/orchestration/professional-sdk.js +14 -0
- package/dist/organization-controller-backend-runtime.d.ts +65 -0
- package/dist/organization-controller-backend-runtime.js +83 -0
- package/dist/professional-backend-runtime.d.ts +36 -0
- package/dist/professional-backend-runtime.js +41 -0
- package/dist/profile-workspace.d.ts +82 -0
- package/dist/profile-workspace.js +127 -0
- package/dist/resource-operations.d.ts +24 -0
- package/dist/resource-operations.js +24 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -42,6 +42,13 @@ Important live-run rule:
|
|
|
42
42
|
- if sandboxed runs disagree with the user's terminal, trust the user's
|
|
43
43
|
terminal for live GW validation
|
|
44
44
|
|
|
45
|
+
Required live validation order:
|
|
46
|
+
|
|
47
|
+
1. local process E2E from real TTY
|
|
48
|
+
2. local Docker image/container E2E
|
|
49
|
+
3. staging E2E
|
|
50
|
+
4. production image/deploy only after staging is green
|
|
51
|
+
|
|
45
52
|
Architectural rule:
|
|
46
53
|
|
|
47
54
|
- shared contracts and actor boundaries come from `gdc-sdk-core-ts`
|
|
@@ -56,6 +63,10 @@ If you are integrating this package for the first time, open these in order:
|
|
|
56
63
|
1. [gdc-sdk-core-ts/docs/101-SDK_PACKAGE_BOUNDARIES.md](https://github.com/Global-DataCare/gdc-sdk-core-ts/blob/main/docs/101-SDK_PACKAGE_BOUNDARIES.md)
|
|
57
64
|
Why `core`, `node`, and `front` are separate packages, what belongs in each
|
|
58
65
|
one, and why actor-scoped facades must stay aligned across runtimes.
|
|
66
|
+
1. [tests/101-live-full-cycle-bff-runtime.e2e.test.mjs](./tests/101-live-full-cycle-bff-runtime.e2e.test.mjs)
|
|
67
|
+
Canonical live backend/BFF walkthrough on a fresh local GW lifecycle:
|
|
68
|
+
host/tenant activation, employee provisioning, individual bootstrap,
|
|
69
|
+
consent grant, professional SMART token, clinical read, and final cleanup.
|
|
59
70
|
1. [tests/101-backend-profile-runtime.test.mjs](./tests/101-backend-profile-runtime.test.mjs)
|
|
60
71
|
Minimal backend-generic walkthrough for loading one actor profile,
|
|
61
72
|
registering one trusted device/runtime context, connecting to one subject
|
|
@@ -115,21 +126,15 @@ If you need the shortest path:
|
|
|
115
126
|
- dataspace discovery and fallback/cache boundary:
|
|
116
127
|
[docs/101-DISCOVERY.md](./docs/101-DISCOVERY.md)
|
|
117
128
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
- the
|
|
121
|
-
|
|
122
|
-
- the standalone
|
|
123
|
-
[tests/live-profile-runtime-individual.e2e.test.mjs](tests/live-profile-runtime-individual.e2e.test.mjs)
|
|
124
|
-
-
|
|
125
|
-
|
|
126
|
-
-
|
|
127
|
-
- the canonical current index/`Composition` read helper
|
|
128
|
-
- if that actor flow creates lifecycle-owned state, `disable` / `purge` belong
|
|
129
|
-
at the end of that scenario as cleanup
|
|
130
|
-
- until that live proof exists, treat `getLatestIps(...)` and
|
|
131
|
-
`searchClinicalBundle(...)` as candidate read contracts, not yet final
|
|
132
|
-
wording
|
|
129
|
+
Current live teaching target:
|
|
130
|
+
|
|
131
|
+
- the main executable tutorial for integrators is now:
|
|
132
|
+
[tests/101-live-full-cycle-bff-runtime.e2e.test.mjs](tests/101-live-full-cycle-bff-runtime.e2e.test.mjs)
|
|
133
|
+
- the standalone actor-profile suites still exist as focused technical slices:
|
|
134
|
+
- [tests/live-profile-runtime-individual.e2e.test.mjs](tests/live-profile-runtime-individual.e2e.test.mjs)
|
|
135
|
+
- [tests/live-profile-runtime-professional.e2e.test.mjs](tests/live-profile-runtime-professional.e2e.test.mjs)
|
|
136
|
+
- the larger runtime suite remains the regression-oriented environment proof:
|
|
137
|
+
[tests/live-gw-node-runtime.e2e.test.mjs](tests/live-gw-node-runtime.e2e.test.mjs)
|
|
133
138
|
|
|
134
139
|
## Executable Usage Examples
|
|
135
140
|
|
|
@@ -149,6 +154,9 @@ Open these tests when you want to see exact method calls and exact inputs:
|
|
|
149
154
|
SMART token request flow.
|
|
150
155
|
- [tests/live-gw-node-runtime.e2e.test.mjs](tests/live-gw-node-runtime.e2e.test.mjs)
|
|
151
156
|
End-to-end runtime wiring against a real GW environment.
|
|
157
|
+
- [tests/101-live-full-cycle-bff-runtime.e2e.test.mjs](tests/101-live-full-cycle-bff-runtime.e2e.test.mjs)
|
|
158
|
+
Single live BFF-oriented `101` that chains tenant, professional, individual,
|
|
159
|
+
consent, SMART, read, and cleanup in one executable conversation.
|
|
152
160
|
- [tests/live-profile-runtime-individual.e2e.test.mjs](tests/live-profile-runtime-individual.e2e.test.mjs)
|
|
153
161
|
Standalone actor-profile E2E for the individual controller on an already
|
|
154
162
|
operational tenant, including scenario-owned cleanup.
|
|
@@ -160,8 +168,11 @@ Open these tests when you want to see exact method calls and exact inputs:
|
|
|
160
168
|
|
|
161
169
|
## Live GW CORE Flow
|
|
162
170
|
|
|
171
|
+
Use [tests/101-live-full-cycle-bff-runtime.e2e.test.mjs](tests/101-live-full-cycle-bff-runtime.e2e.test.mjs)
|
|
172
|
+
as the canonical live `101` for backend/BFF integrators.
|
|
173
|
+
|
|
163
174
|
Use [tests/live-gw-node-runtime.e2e.test.mjs](tests/live-gw-node-runtime.e2e.test.mjs)
|
|
164
|
-
as the
|
|
175
|
+
as the broader runtime-regression suite.
|
|
165
176
|
|
|
166
177
|
Before running that suite, read:
|
|
167
178
|
|
|
@@ -182,18 +193,23 @@ Teaching rule:
|
|
|
182
193
|
- `LIVE_GW_E2E_EXECUTION_MODE=direct` is the current and only supported mode
|
|
183
194
|
for live validation; queued app-side job management is a later phase
|
|
184
195
|
|
|
185
|
-
Current live flow covered by the test suite:
|
|
196
|
+
Current live `101` flow covered by the test suite:
|
|
186
197
|
|
|
187
|
-
1.
|
|
188
|
-
2.
|
|
189
|
-
3.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
1. activate one hosted tenant / legal organization
|
|
199
|
+
2. provision one professional employee through the organization controller
|
|
200
|
+
3. load the individual-controller profile and bootstrap one hosted individual
|
|
201
|
+
4. confirm the returned order and verify the invoice bundle projection
|
|
202
|
+
5. ingest one IPS/clinical `Communication` through the individual controller
|
|
203
|
+
6. grant professional consent for one patient-summary section
|
|
204
|
+
7. load the professional profile and request one SMART token
|
|
205
|
+
8. read the allowed IPS bundle as the professional actor
|
|
206
|
+
9. clean up consent, individual, employee, tenant, and host state
|
|
207
|
+
|
|
208
|
+
Run the main live `101`:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
npm run test:e2e:101:live-full-cycle
|
|
212
|
+
```
|
|
197
213
|
|
|
198
214
|
What is still not fully covered as one single root lifecycle:
|
|
199
215
|
|
|
@@ -659,9 +675,9 @@ modules below.
|
|
|
659
675
|
|
|
660
676
|
For legal-organization onboarding from a Node BFF/runtime, keep the host steps separate:
|
|
661
677
|
|
|
662
|
-
1. `Organization/_transaction`
|
|
663
|
-
2. legacy
|
|
664
|
-
3. `Order/_batch`
|
|
678
|
+
1. new flow: `Organization/_transaction`
|
|
679
|
+
2. legacy compatibility flow: `ICA _verify -> Organization/_activate`
|
|
680
|
+
3. downstream business continuation: `Order/_batch`
|
|
665
681
|
|
|
666
682
|
Use `OrganizationControllerSdk.submitLegalOrganizationVerificationTransaction(...)` or
|
|
667
683
|
`NodeHttpClient.submitLegalOrganizationVerificationTransaction(...)` for step 1.
|
|
@@ -672,3 +688,29 @@ Rules:
|
|
|
672
688
|
- transport/runtime communication keys stay outside the business payload
|
|
673
689
|
- controller binding key stays in `body.data[].resource.controller.*`
|
|
674
690
|
- do not mix this path with `requestIcaEnrollment` or Fabric
|
|
691
|
+
|
|
692
|
+
Legacy compatibility coverage in the live suite:
|
|
693
|
+
|
|
694
|
+
- the canonical test is `LIVE professional lifecycle on GW`
|
|
695
|
+
- set `RUN_LIVE_GW_E2E_HOST_VERIFICATION_TRANSACTION=1` to exercise:
|
|
696
|
+
- `Organization/_transaction -> Order/_batch`
|
|
697
|
+
- set `RUN_LIVE_GW_E2E_HOST_VERIFICATION_TRANSACTION=0` to exercise the older:
|
|
698
|
+
- `ICA _verify -> Organization/_activate -> Order/_batch`
|
|
699
|
+
|
|
700
|
+
Dedicated legacy live command:
|
|
701
|
+
|
|
702
|
+
```bash
|
|
703
|
+
cd /Users/fernando/GITS/gdc-workspace/gdc-sdk-node-ts
|
|
704
|
+
RUN_LIVE_GW_E2E=1 \
|
|
705
|
+
RUN_LIVE_GW_E2E_ACTOR_CHAIN=1 \
|
|
706
|
+
RUN_LIVE_GW_E2E_HOST_VERIFICATION_TRANSACTION=0 \
|
|
707
|
+
LIVE_GW_E2E_SUITE=professional \
|
|
708
|
+
node --test tests/live-gw-node-runtime.e2e.test.mjs
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Live E2E legal PDF source:
|
|
712
|
+
|
|
713
|
+
- local file: set `LIVE_GW_HOST_VERIFICATION_PDF_PATH=/abs/path/file.pdf`
|
|
714
|
+
- public URL: set `LIVE_GW_HOST_VERIFICATION_PDF_URL=https://.../file.pdf`
|
|
715
|
+
- if both are present, the live suite prefers `LIVE_GW_HOST_VERIFICATION_PDF_URL`
|
|
716
|
+
- Dropbox-style links are normalized to `dl=1` direct-download mode automatically
|
|
@@ -2,6 +2,8 @@ import type { ActorProfileDescriptor, IJobManager, ActorKind, LoadedActorProfile
|
|
|
2
2
|
import type { ActorSession } from './session.js';
|
|
3
3
|
import type { RuntimeClient } from './orchestration/client-port.js';
|
|
4
4
|
import { IndividualControllerSdk } from './orchestration/individual-controller-sdk.js';
|
|
5
|
+
import { OrganizationControllerSdk } from './orchestration/organization-controller-sdk.js';
|
|
6
|
+
import { ProfessionalSdk } from './orchestration/professional-sdk.js';
|
|
5
7
|
import type { RouteContext } from './individual-onboarding.js';
|
|
6
8
|
/**
|
|
7
9
|
* Result of registering one trusted backend runtime device/profile context.
|
|
@@ -49,6 +51,16 @@ export type BackendIndividualControllerProfile = {
|
|
|
49
51
|
session: ActorSession;
|
|
50
52
|
sdk: IndividualControllerSdk;
|
|
51
53
|
};
|
|
54
|
+
export type BackendOrganizationControllerProfile = {
|
|
55
|
+
profile: BackendLoadedActorProfile;
|
|
56
|
+
session: ActorSession;
|
|
57
|
+
sdk: OrganizationControllerSdk;
|
|
58
|
+
};
|
|
59
|
+
export type BackendProfessionalProfile = {
|
|
60
|
+
profile: BackendLoadedActorProfile;
|
|
61
|
+
session: ActorSession;
|
|
62
|
+
sdk: ProfessionalSdk;
|
|
63
|
+
};
|
|
52
64
|
/**
|
|
53
65
|
* Canonical backend runtime contract for loading one actor profile after
|
|
54
66
|
* authentication and then working with trusted device registration and subject
|
|
@@ -153,6 +165,15 @@ export declare class DirectBackendProfileRuntime implements BackendProfileRuntim
|
|
|
153
165
|
private forgetLoadedProfile;
|
|
154
166
|
private resolveLoadedProfile;
|
|
155
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Preferred developer-facing factory for the current backend profile runtime.
|
|
170
|
+
*
|
|
171
|
+
* Use this helper in tutorials and app/BFF code when you already have one
|
|
172
|
+
* configured runtime client and want the canonical
|
|
173
|
+
* `loadProfile(...) -> session -> actor facade` entrypoint without exposing the
|
|
174
|
+
* concrete class name in every example.
|
|
175
|
+
*/
|
|
176
|
+
export declare function createBackendProfileRuntime(options: DirectBackendProfileRuntimeOptions): BackendProfileRuntimeClient;
|
|
156
177
|
/**
|
|
157
178
|
* Minimal in-memory `JobManager` for backend runtimes that do not need durable
|
|
158
179
|
* persistence during one live session.
|
|
@@ -203,6 +224,24 @@ export declare function requireBackendIndividualControllerSession(profile: Backe
|
|
|
203
224
|
* backend profile.
|
|
204
225
|
*/
|
|
205
226
|
export declare function requireBackendIndividualControllerSdk(profile: BackendLoadedActorProfile): IndividualControllerSdk;
|
|
227
|
+
/**
|
|
228
|
+
* Returns the organization-controller session from one loaded backend profile.
|
|
229
|
+
*/
|
|
230
|
+
export declare function requireBackendOrganizationControllerSession(profile: BackendLoadedActorProfile): ActorSession;
|
|
231
|
+
/**
|
|
232
|
+
* Materializes the organization-controller facade directly from one loaded
|
|
233
|
+
* backend profile.
|
|
234
|
+
*/
|
|
235
|
+
export declare function requireBackendOrganizationControllerSdk(profile: BackendLoadedActorProfile): OrganizationControllerSdk;
|
|
236
|
+
/**
|
|
237
|
+
* Returns the professional session from one loaded backend profile.
|
|
238
|
+
*/
|
|
239
|
+
export declare function requireBackendProfessionalSession(profile: BackendLoadedActorProfile): ActorSession;
|
|
240
|
+
/**
|
|
241
|
+
* Materializes the professional facade directly from one loaded backend
|
|
242
|
+
* profile.
|
|
243
|
+
*/
|
|
244
|
+
export declare function requireBackendProfessionalSdk(profile: BackendLoadedActorProfile): ProfessionalSdk;
|
|
206
245
|
/**
|
|
207
246
|
* Loads one backend profile and resolves the individual-controller session in
|
|
208
247
|
* one step.
|
|
@@ -212,3 +251,12 @@ export declare function requireBackendIndividualControllerSdk(profile: BackendLo
|
|
|
212
251
|
* from the individual-controller flow.
|
|
213
252
|
*/
|
|
214
253
|
export declare function loadBackendIndividualControllerProfile(client: BackendProfileRuntimeClient, input: ProfileLoadRequest): Promise<BackendIndividualControllerProfile>;
|
|
254
|
+
/**
|
|
255
|
+
* Loads one backend profile and resolves the organization-controller session in
|
|
256
|
+
* one step.
|
|
257
|
+
*/
|
|
258
|
+
export declare function loadBackendOrganizationControllerProfile(client: BackendProfileRuntimeClient, input: ProfileLoadRequest): Promise<BackendOrganizationControllerProfile>;
|
|
259
|
+
/**
|
|
260
|
+
* Loads one backend profile and resolves the professional session in one step.
|
|
261
|
+
*/
|
|
262
|
+
export declare function loadBackendProfessionalProfile(client: BackendProfileRuntimeClient, input: ProfileLoadRequest): Promise<BackendProfessionalProfile>;
|
|
@@ -185,6 +185,17 @@ export class DirectBackendProfileRuntime {
|
|
|
185
185
|
throw new Error(`DirectBackendProfileRuntime has not loaded one backend profile for '${normalizedUserId}'.`);
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Preferred developer-facing factory for the current backend profile runtime.
|
|
190
|
+
*
|
|
191
|
+
* Use this helper in tutorials and app/BFF code when you already have one
|
|
192
|
+
* configured runtime client and want the canonical
|
|
193
|
+
* `loadProfile(...) -> session -> actor facade` entrypoint without exposing the
|
|
194
|
+
* concrete class name in every example.
|
|
195
|
+
*/
|
|
196
|
+
export function createBackendProfileRuntime(options) {
|
|
197
|
+
return new DirectBackendProfileRuntime(options);
|
|
198
|
+
}
|
|
188
199
|
/**
|
|
189
200
|
* Minimal in-memory `JobManager` for backend runtimes that do not need durable
|
|
190
201
|
* persistence during one live session.
|
|
@@ -410,6 +421,32 @@ export function requireBackendIndividualControllerSession(profile) {
|
|
|
410
421
|
export function requireBackendIndividualControllerSdk(profile) {
|
|
411
422
|
return requireBackendIndividualControllerSession(profile).asIndividualController();
|
|
412
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Returns the organization-controller session from one loaded backend profile.
|
|
426
|
+
*/
|
|
427
|
+
export function requireBackendOrganizationControllerSession(profile) {
|
|
428
|
+
return requireBackendActorSession(profile, ActorKinds.OrganizationController);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Materializes the organization-controller facade directly from one loaded
|
|
432
|
+
* backend profile.
|
|
433
|
+
*/
|
|
434
|
+
export function requireBackendOrganizationControllerSdk(profile) {
|
|
435
|
+
return requireBackendOrganizationControllerSession(profile).asOrganizationController();
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Returns the professional session from one loaded backend profile.
|
|
439
|
+
*/
|
|
440
|
+
export function requireBackendProfessionalSession(profile) {
|
|
441
|
+
return requireBackendActorSession(profile, ActorKinds.Professional);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Materializes the professional facade directly from one loaded backend
|
|
445
|
+
* profile.
|
|
446
|
+
*/
|
|
447
|
+
export function requireBackendProfessionalSdk(profile) {
|
|
448
|
+
return requireBackendProfessionalSession(profile).asProfessional();
|
|
449
|
+
}
|
|
413
450
|
/**
|
|
414
451
|
* Loads one backend profile and resolves the individual-controller session in
|
|
415
452
|
* one step.
|
|
@@ -427,6 +464,31 @@ export async function loadBackendIndividualControllerProfile(client, input) {
|
|
|
427
464
|
sdk: session.asIndividualController(),
|
|
428
465
|
};
|
|
429
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Loads one backend profile and resolves the organization-controller session in
|
|
469
|
+
* one step.
|
|
470
|
+
*/
|
|
471
|
+
export async function loadBackendOrganizationControllerProfile(client, input) {
|
|
472
|
+
const profile = await loadBackendProfile(client, input);
|
|
473
|
+
const session = requireBackendOrganizationControllerSession(profile);
|
|
474
|
+
return {
|
|
475
|
+
profile,
|
|
476
|
+
session,
|
|
477
|
+
sdk: session.asOrganizationController(),
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Loads one backend profile and resolves the professional session in one step.
|
|
482
|
+
*/
|
|
483
|
+
export async function loadBackendProfessionalProfile(client, input) {
|
|
484
|
+
const profile = await loadBackendProfile(client, input);
|
|
485
|
+
const session = requireBackendProfessionalSession(profile);
|
|
486
|
+
return {
|
|
487
|
+
profile,
|
|
488
|
+
session,
|
|
489
|
+
sdk: session.asProfessional(),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
430
492
|
function createRuntimeUuid() {
|
|
431
493
|
const fromCrypto = globalThis.crypto?.randomUUID?.();
|
|
432
494
|
if (fromCrypto) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FamilyOrganizationSummary } from 'gdc-common-utils-ts/utils/family-organization-summary';
|
|
2
|
+
import type { RouteContext } from './individual-onboarding.js';
|
|
3
|
+
import { type IndividualOrganizationStartResult } from './individual-start.js';
|
|
4
|
+
import { type FamilyOrganizationSearchInput } from './family-organization-search.js';
|
|
5
|
+
import type { PollOptions, SubmitAndPollResult } from './orchestration/client-port.js';
|
|
6
|
+
export type EnsureFamilyOrganizationRegistrationInput = FamilyOrganizationSearchInput & Readonly<{
|
|
7
|
+
controllerEmail?: string;
|
|
8
|
+
controllerRole?: string;
|
|
9
|
+
serviceProviderDid?: string;
|
|
10
|
+
tenantId?: string;
|
|
11
|
+
jurisdiction?: string;
|
|
12
|
+
sector?: string;
|
|
13
|
+
additionalClaims?: Record<string, unknown>;
|
|
14
|
+
}>;
|
|
15
|
+
export type EnsureFamilyOrganizationRegistrationResult = Readonly<{
|
|
16
|
+
status: 'already_exists' | 'resume_required' | 'new_created';
|
|
17
|
+
summary?: FamilyOrganizationSummary;
|
|
18
|
+
started?: IndividualOrganizationStartResult;
|
|
19
|
+
}>;
|
|
20
|
+
type EnsureFamilyOrganizationRegistrationDeps = {
|
|
21
|
+
routeCtx: RouteContext;
|
|
22
|
+
input: EnsureFamilyOrganizationRegistrationInput;
|
|
23
|
+
defaultTimeoutMs?: number;
|
|
24
|
+
defaultIntervalMs?: number;
|
|
25
|
+
individualFamilyOrganizationSearchPath: (ctx: RouteContext) => string;
|
|
26
|
+
individualFamilyOrganizationSearchPollPath: (ctx: RouteContext) => string;
|
|
27
|
+
individualFamilyOrganizationBatchPath: (ctx: RouteContext) => string;
|
|
28
|
+
individualFamilyOrganizationPollPath: (ctx: RouteContext) => string;
|
|
29
|
+
submitAndPoll: (submitPath: string, pollPath: string, payload: {
|
|
30
|
+
thid?: string;
|
|
31
|
+
} & Record<string, unknown>, options?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* High-level controller orchestration for phone-first onboarding channels:
|
|
35
|
+
* - first search whether the family/individual registration already exists
|
|
36
|
+
* - if it exists, return the normalized summary
|
|
37
|
+
* - otherwise start the bootstrap flow using the same business input
|
|
38
|
+
*/
|
|
39
|
+
export declare function ensureFamilyOrganizationRegistrationWithDeps(deps: EnsureFamilyOrganizationRegistrationDeps): Promise<EnsureFamilyOrganizationRegistrationResult>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { startIndividualOrganizationWithDeps, } from './individual-start.js';
|
|
3
|
+
import { searchFamilyOrganizationWithDeps, } from './family-organization-search.js';
|
|
4
|
+
import { extractOfferIdFromResponseBody, extractOfferPreviewFromResponseBody } from './order-offer-summary.js';
|
|
5
|
+
/**
|
|
6
|
+
* High-level controller orchestration for phone-first onboarding channels:
|
|
7
|
+
* - first search whether the family/individual registration already exists
|
|
8
|
+
* - if it exists, return the normalized summary
|
|
9
|
+
* - otherwise start the bootstrap flow using the same business input
|
|
10
|
+
*/
|
|
11
|
+
export async function ensureFamilyOrganizationRegistrationWithDeps(deps) {
|
|
12
|
+
const summary = await searchFamilyOrganizationWithDeps({
|
|
13
|
+
routeCtx: deps.routeCtx,
|
|
14
|
+
input: deps.input,
|
|
15
|
+
defaultTimeoutMs: deps.defaultTimeoutMs,
|
|
16
|
+
defaultIntervalMs: deps.defaultIntervalMs,
|
|
17
|
+
individualFamilyOrganizationSearchPath: deps.individualFamilyOrganizationSearchPath,
|
|
18
|
+
individualFamilyOrganizationSearchPollPath: deps.individualFamilyOrganizationSearchPollPath,
|
|
19
|
+
submitAndPoll: deps.submitAndPoll,
|
|
20
|
+
});
|
|
21
|
+
if (summary?.status === 'already_exists' || summary?.status === 'resume_required') {
|
|
22
|
+
return {
|
|
23
|
+
status: summary.status,
|
|
24
|
+
summary,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const started = await startIndividualOrganizationWithDeps({
|
|
28
|
+
input: {
|
|
29
|
+
serviceProviderDid: deps.input.serviceProviderDid,
|
|
30
|
+
tenantId: deps.input.tenantId,
|
|
31
|
+
jurisdiction: deps.input.jurisdiction,
|
|
32
|
+
sector: deps.input.sector,
|
|
33
|
+
alternateName: deps.input.usualname,
|
|
34
|
+
controllerEmail: deps.input.controllerEmail,
|
|
35
|
+
controllerTelephone: deps.input.controllerPhone,
|
|
36
|
+
controllerRole: deps.input.controllerRole,
|
|
37
|
+
additionalClaims: deps.input.additionalClaims,
|
|
38
|
+
timeoutSeconds: deps.input.timeoutSeconds,
|
|
39
|
+
intervalSeconds: deps.input.intervalSeconds,
|
|
40
|
+
},
|
|
41
|
+
routeCtx: deps.routeCtx,
|
|
42
|
+
defaultTimeoutMs: deps.defaultTimeoutMs,
|
|
43
|
+
defaultIntervalMs: deps.defaultIntervalMs,
|
|
44
|
+
individualFamilyOrganizationBatchPath: deps.individualFamilyOrganizationBatchPath,
|
|
45
|
+
individualFamilyOrganizationPollPath: deps.individualFamilyOrganizationPollPath,
|
|
46
|
+
submitAndPoll: deps.submitAndPoll,
|
|
47
|
+
getOfferIdFromResponse: (result) => extractOfferIdFromResponseBody(result.poll.body),
|
|
48
|
+
getOfferPreviewFromResponse: (result) => extractOfferPreviewFromResponseBody(result.poll.body),
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
status: 'new_created',
|
|
52
|
+
started,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type FamilyOrganizationSummary } from 'gdc-common-utils-ts/utils/family-organization-summary';
|
|
2
|
+
import type { PollOptions, SubmitAndPollResult } from './orchestration/client-port.js';
|
|
3
|
+
import type { RouteContext } from './individual-onboarding.js';
|
|
4
|
+
export type FamilyOrganizationSearchInput = Readonly<{
|
|
5
|
+
controllerPhone: string;
|
|
6
|
+
usualname: string;
|
|
7
|
+
birthDate?: string;
|
|
8
|
+
timeoutSeconds?: number;
|
|
9
|
+
intervalSeconds?: number;
|
|
10
|
+
}>;
|
|
11
|
+
type SearchFamilyOrganizationWithDeps = {
|
|
12
|
+
routeCtx: RouteContext;
|
|
13
|
+
input: FamilyOrganizationSearchInput;
|
|
14
|
+
defaultTimeoutMs?: number;
|
|
15
|
+
defaultIntervalMs?: number;
|
|
16
|
+
individualFamilyOrganizationSearchPath: (ctx: RouteContext) => string;
|
|
17
|
+
individualFamilyOrganizationSearchPollPath: (ctx: RouteContext) => string;
|
|
18
|
+
submitAndPoll: (submitPath: string, pollPath: string, payload: {
|
|
19
|
+
thid?: string;
|
|
20
|
+
} & Record<string, unknown>, options?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Searches one existing family/individual organization registration by the
|
|
24
|
+
* current phone-first business key used by UHC/UNID channel flows.
|
|
25
|
+
*
|
|
26
|
+
* Returns one normalized summary when the registration exists, otherwise
|
|
27
|
+
* `null`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function searchFamilyOrganizationWithDeps(deps: SearchFamilyOrganizationWithDeps): Promise<FamilyOrganizationSummary | null>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { readFamilyOrganizationSummaryFromResponseBody, } from 'gdc-common-utils-ts/utils/family-organization-summary';
|
|
3
|
+
import { ClaimsOrganizationSchemaorg, ClaimsServiceSchemaorg } from 'gdc-common-utils-ts/constants';
|
|
4
|
+
import { resolvePollOptionsFromSeconds } from './poll-options.js';
|
|
5
|
+
/**
|
|
6
|
+
* Searches one existing family/individual organization registration by the
|
|
7
|
+
* current phone-first business key used by UHC/UNID channel flows.
|
|
8
|
+
*
|
|
9
|
+
* Returns one normalized summary when the registration exists, otherwise
|
|
10
|
+
* `null`.
|
|
11
|
+
*/
|
|
12
|
+
export async function searchFamilyOrganizationWithDeps(deps) {
|
|
13
|
+
const controllerPhone = String(deps.input.controllerPhone || '').trim();
|
|
14
|
+
const usualname = String(deps.input.usualname || '').trim();
|
|
15
|
+
const birthDate = String(deps.input.birthDate || '').trim();
|
|
16
|
+
if (!controllerPhone) {
|
|
17
|
+
throw new Error('searchFamilyOrganization requires controllerPhone.');
|
|
18
|
+
}
|
|
19
|
+
if (!usualname) {
|
|
20
|
+
throw new Error('searchFamilyOrganization requires usualname.');
|
|
21
|
+
}
|
|
22
|
+
const claims = {
|
|
23
|
+
'@context': 'org.schema',
|
|
24
|
+
[ClaimsOrganizationSchemaorg.ownerTelephone]: controllerPhone,
|
|
25
|
+
[ClaimsOrganizationSchemaorg.alternateName]: usualname,
|
|
26
|
+
[ClaimsServiceSchemaorg.category]: deps.routeCtx.sector,
|
|
27
|
+
...(birthDate ? { 'org.schema.Organization.foundingDate': birthDate } : {}),
|
|
28
|
+
};
|
|
29
|
+
const payload = {
|
|
30
|
+
jti: `jti-${createRuntimeUuid()}`,
|
|
31
|
+
thid: `family-search-${createRuntimeUuid()}`,
|
|
32
|
+
iss: deps.routeCtx.tenantId,
|
|
33
|
+
aud: deps.routeCtx.tenantId,
|
|
34
|
+
type: 'application/api+json',
|
|
35
|
+
body: {
|
|
36
|
+
data: [{
|
|
37
|
+
type: 'Family-search-v1.0',
|
|
38
|
+
meta: { claims },
|
|
39
|
+
resource: { meta: { claims } },
|
|
40
|
+
}],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const pollOptions = resolvePollOptionsFromSeconds(deps.input.timeoutSeconds, deps.input.intervalSeconds, {
|
|
44
|
+
timeoutMs: deps.defaultTimeoutMs,
|
|
45
|
+
intervalMs: deps.defaultIntervalMs,
|
|
46
|
+
});
|
|
47
|
+
const result = await deps.submitAndPoll(deps.individualFamilyOrganizationSearchPath(deps.routeCtx), deps.individualFamilyOrganizationSearchPollPath(deps.routeCtx), payload, pollOptions);
|
|
48
|
+
if (result.poll.status !== 200) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return readFamilyOrganizationSummaryFromResponseBody(result.poll.body);
|
|
52
|
+
}
|
|
53
|
+
function createRuntimeUuid() {
|
|
54
|
+
const fromCrypto = globalThis.crypto?.randomUUID?.();
|
|
55
|
+
if (fromCrypto) {
|
|
56
|
+
return fromCrypto;
|
|
57
|
+
}
|
|
58
|
+
return `fallback-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
59
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,15 +4,20 @@ export * from './identity-bootstrap.js';
|
|
|
4
4
|
export * from './async-polling.js';
|
|
5
5
|
export * from './backend-profile-runtime.js';
|
|
6
6
|
export * from './individual-controller-backend-runtime.js';
|
|
7
|
+
export * from './organization-controller-backend-runtime.js';
|
|
8
|
+
export * from './professional-backend-runtime.js';
|
|
7
9
|
export * from './poll-options.js';
|
|
8
10
|
export * from './host-onboarding.js';
|
|
9
11
|
export * from './individual-start.js';
|
|
12
|
+
export * from './family-organization-search.js';
|
|
13
|
+
export * from './family-organization-registration.js';
|
|
10
14
|
export * from './individual-onboarding.js';
|
|
11
15
|
export * from './device-activation.js';
|
|
12
16
|
export * from './smart-token.js';
|
|
13
17
|
export * from './order-offer-summary.js';
|
|
14
18
|
export * from './organization-license-order.js';
|
|
15
19
|
export * from './resource-operations.js';
|
|
20
|
+
export * from './profile-workspace.js';
|
|
16
21
|
export * from './constants/lifecycle.js';
|
|
17
22
|
export * from './consent-claim-helpers.js';
|
|
18
23
|
export * from './session.js';
|
package/dist/index.js
CHANGED
|
@@ -5,15 +5,20 @@ export * from './identity-bootstrap.js';
|
|
|
5
5
|
export * from './async-polling.js';
|
|
6
6
|
export * from './backend-profile-runtime.js';
|
|
7
7
|
export * from './individual-controller-backend-runtime.js';
|
|
8
|
+
export * from './organization-controller-backend-runtime.js';
|
|
9
|
+
export * from './professional-backend-runtime.js';
|
|
8
10
|
export * from './poll-options.js';
|
|
9
11
|
export * from './host-onboarding.js';
|
|
10
12
|
export * from './individual-start.js';
|
|
13
|
+
export * from './family-organization-search.js';
|
|
14
|
+
export * from './family-organization-registration.js';
|
|
11
15
|
export * from './individual-onboarding.js';
|
|
12
16
|
export * from './device-activation.js';
|
|
13
17
|
export * from './smart-token.js';
|
|
14
18
|
export * from './order-offer-summary.js';
|
|
15
19
|
export * from './organization-license-order.js';
|
|
16
20
|
export * from './resource-operations.js';
|
|
21
|
+
export * from './profile-workspace.js';
|
|
17
22
|
export * from './constants/lifecycle.js';
|
|
18
23
|
export * from './consent-claim-helpers.js';
|
|
19
24
|
export * from './session.js';
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { SubmitAndPollResult } from 'gdc-sdk-core-ts';
|
|
2
|
+
import type { FamilyOrganizationSummary } from 'gdc-common-utils-ts/utils/family-organization-summary';
|
|
2
3
|
import type { IndividualOrganizationConfirmOrderInput, RouteContext } from './individual-onboarding.js';
|
|
3
4
|
import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from './individual-start.js';
|
|
5
|
+
import type { EnsureFamilyOrganizationRegistrationInput, EnsureFamilyOrganizationRegistrationResult } from './family-organization-registration.js';
|
|
6
|
+
import type { FamilyOrganizationSearchInput } from './family-organization-search.js';
|
|
4
7
|
import type { ClinicalBundleSearchInput } from './resource-operations.js';
|
|
5
8
|
import { type BackendIndividualControllerProfile, type BackendProfileRuntimeClient } from './backend-profile-runtime.js';
|
|
6
9
|
import type { ProfileLoadRequest } from 'gdc-sdk-core-ts';
|
|
@@ -29,6 +32,16 @@ export declare class IndividualControllerBackendRuntime {
|
|
|
29
32
|
* Starts the current CORE individual/family bootstrap flow.
|
|
30
33
|
*/
|
|
31
34
|
startIndividualOrganization(profile: BackendIndividualControllerProfile, input: IndividualOrganizationBootstrapInput): Promise<IndividualOrganizationStartResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Searches one existing family/individual registration before deciding
|
|
37
|
+
* whether the backend should create or resume it.
|
|
38
|
+
*/
|
|
39
|
+
searchFamilyOrganization(profile: BackendIndividualControllerProfile, ctx: RouteContext, input: FamilyOrganizationSearchInput): Promise<FamilyOrganizationSummary | null>;
|
|
40
|
+
/**
|
|
41
|
+
* High-level onboarding gate for channel apps:
|
|
42
|
+
* search the registration first and only bootstrap when still missing.
|
|
43
|
+
*/
|
|
44
|
+
ensureFamilyOrganizationRegistration(profile: BackendIndividualControllerProfile, ctx: RouteContext, input: EnsureFamilyOrganizationRegistrationInput): Promise<EnsureFamilyOrganizationRegistrationResult>;
|
|
32
45
|
/**
|
|
33
46
|
* Confirms the order returned by the individual bootstrap flow.
|
|
34
47
|
*/
|
|
@@ -30,6 +30,20 @@ export class IndividualControllerBackendRuntime {
|
|
|
30
30
|
startIndividualOrganization(profile, input) {
|
|
31
31
|
return profile.sdk.startIndividualOrganization(input);
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Searches one existing family/individual registration before deciding
|
|
35
|
+
* whether the backend should create or resume it.
|
|
36
|
+
*/
|
|
37
|
+
searchFamilyOrganization(profile, ctx, input) {
|
|
38
|
+
return profile.sdk.searchFamilyOrganization(ctx, input);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* High-level onboarding gate for channel apps:
|
|
42
|
+
* search the registration first and only bootstrap when still missing.
|
|
43
|
+
*/
|
|
44
|
+
ensureFamilyOrganizationRegistration(profile, ctx, input) {
|
|
45
|
+
return profile.sdk.ensureFamilyOrganizationRegistration(ctx, input);
|
|
46
|
+
}
|
|
33
47
|
/**
|
|
34
48
|
* Confirms the order returned by the individual bootstrap flow.
|
|
35
49
|
*/
|