gdc-sdk-node-ts 0.12.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/dist/backend-profile-runtime.d.ts +214 -0
- package/dist/backend-profile-runtime.js +436 -0
- package/dist/consent-claim-helpers.d.ts +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/individual-controller-backend-runtime.d.ts +46 -0
- package/dist/individual-controller-backend-runtime.js +53 -0
- package/dist/node-runtime-client.d.ts +55 -2
- package/dist/node-runtime-client.js +106 -2
- package/dist/orchestration/client-port.d.ts +25 -1
- package/dist/orchestration/individual-controller-sdk.d.ts +6 -1
- package/dist/orchestration/individual-controller-sdk.js +7 -0
- package/dist/orchestration/organization-controller-sdk.d.ts +21 -1
- package/dist/orchestration/organization-controller-sdk.js +24 -0
- package/dist/orchestration/personal-sdk.d.ts +3 -1
- package/dist/orchestration/personal-sdk.js +4 -0
- package/dist/orchestration/professional-sdk.d.ts +6 -1
- package/dist/orchestration/professional-sdk.js +7 -0
- package/dist/resource-operations.d.ts +55 -5
- package/dist/resource-operations.js +26 -4
- package/package.json +7 -3
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { ActorKinds } from 'gdc-common-utils-ts/constants/actor-session';
|
|
3
|
+
import { ProfileAppTypes } from 'gdc-common-utils-ts/constants';
|
|
4
|
+
import { JobStatus } from 'gdc-common-utils-ts/models/confidential-job';
|
|
5
|
+
import { buildActorSessionDescriptorForActorKind, expandActorSessionDescriptorToFacades, prepareLoadedActorProfile, prepareLoadProfile, } from 'gdc-sdk-core-ts';
|
|
6
|
+
import { createActorSessionsFromFacades } from './gdc-session-bridge.js';
|
|
7
|
+
export const BackendSubjectIndexReadModes = Object.freeze({
|
|
8
|
+
LatestIps: 'latest-ips',
|
|
9
|
+
ClinicalBundle: 'clinical-bundle',
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Default backend-generic runtime implementation backed by injected adapters.
|
|
13
|
+
*
|
|
14
|
+
* This class is the first concrete v2 slice intended for backend consumers that
|
|
15
|
+
* need one reusable actor-aware profile runtime after authentication.
|
|
16
|
+
*/
|
|
17
|
+
export class BackendProfileRuntime {
|
|
18
|
+
constructor(adapters, options = {}) {
|
|
19
|
+
this.adapters = adapters;
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
async loadProfile(input) {
|
|
23
|
+
const loadedProfile = await this.adapters.loadProfile(input);
|
|
24
|
+
return {
|
|
25
|
+
...loadedProfile,
|
|
26
|
+
actorSessions: createActorSessionsFromFacades(loadedProfile.facades, this.options.facadeClient),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async closeProfile(_profileKey) { }
|
|
30
|
+
async registerTrustedDevice(input) {
|
|
31
|
+
return this.adapters.registerTrustedDevice(input);
|
|
32
|
+
}
|
|
33
|
+
async connectToSubjectIndex(input) {
|
|
34
|
+
return this.adapters.connectToSubjectIndex(input);
|
|
35
|
+
}
|
|
36
|
+
async getSubjectIndexComposition(input) {
|
|
37
|
+
return this.adapters.getSubjectIndexComposition(input);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Current concrete backend profile runtime over one injected runtime client.
|
|
42
|
+
*
|
|
43
|
+
* This is the pragmatic v2 bridge for backend consumers that already possess
|
|
44
|
+
* an authenticated `RuntimeClient` and need `loadProfile(...)` to materialize
|
|
45
|
+
* actor facades immediately against the current GW CORE contract.
|
|
46
|
+
*/
|
|
47
|
+
export class DirectBackendProfileRuntime {
|
|
48
|
+
constructor(options) {
|
|
49
|
+
this.loadedProfiles = new Map();
|
|
50
|
+
this.options = options;
|
|
51
|
+
}
|
|
52
|
+
async loadProfile(input) {
|
|
53
|
+
const normalized = prepareLoadProfile(input);
|
|
54
|
+
const profileId = String(normalized.profileId
|
|
55
|
+
|| normalized.profileDid
|
|
56
|
+
|| normalized.subjectDid
|
|
57
|
+
|| normalized.providerDid).trim();
|
|
58
|
+
const resolvedAppType = normalized.appType || ProfileAppTypes.Family;
|
|
59
|
+
const descriptor = {
|
|
60
|
+
profileId,
|
|
61
|
+
actorKind: normalized.actorKind,
|
|
62
|
+
actorRole: normalized.actorRole,
|
|
63
|
+
providerDid: normalized.providerDid,
|
|
64
|
+
runtimeClass: normalized.runtimeClass,
|
|
65
|
+
profileDid: normalized.profileDid,
|
|
66
|
+
subjectDid: normalized.subjectDid,
|
|
67
|
+
email: normalized.email,
|
|
68
|
+
phone: normalized.phone,
|
|
69
|
+
deviceDid: normalized.deviceDid,
|
|
70
|
+
appType: resolvedAppType,
|
|
71
|
+
};
|
|
72
|
+
const session = buildActorSessionDescriptorForActorKind({
|
|
73
|
+
actorKind: normalized.actorKind,
|
|
74
|
+
appType: resolvedAppType,
|
|
75
|
+
profileId: descriptor.profileId,
|
|
76
|
+
profileDid: descriptor.profileDid,
|
|
77
|
+
role: descriptor.actorRole,
|
|
78
|
+
});
|
|
79
|
+
const facades = expandActorSessionDescriptorToFacades(session);
|
|
80
|
+
const jobManager = this.options.createJobManager
|
|
81
|
+
? this.options.createJobManager(descriptor, normalized)
|
|
82
|
+
: createJobManagerInMemory(descriptor);
|
|
83
|
+
const loadedProfile = prepareLoadedActorProfile({
|
|
84
|
+
descriptor,
|
|
85
|
+
session,
|
|
86
|
+
facades,
|
|
87
|
+
jobManager,
|
|
88
|
+
});
|
|
89
|
+
const backendProfile = {
|
|
90
|
+
...loadedProfile,
|
|
91
|
+
actorSessions: createActorSessionsFromFacades(loadedProfile.facades, this.options.facadeClient),
|
|
92
|
+
};
|
|
93
|
+
this.rememberLoadedProfile(backendProfile);
|
|
94
|
+
return backendProfile;
|
|
95
|
+
}
|
|
96
|
+
async closeProfile(profileKey) {
|
|
97
|
+
const profile = this.resolveLoadedProfile(profileKey);
|
|
98
|
+
profile.jobManager.shutdown();
|
|
99
|
+
this.forgetLoadedProfile(profile);
|
|
100
|
+
}
|
|
101
|
+
async registerTrustedDevice(input) {
|
|
102
|
+
if (this.options.registerTrustedDevice) {
|
|
103
|
+
return this.options.registerTrustedDevice(input);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
trustedDeviceId: input.deviceDid,
|
|
107
|
+
status: 'already-trusted',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async connectToSubjectIndex(input) {
|
|
111
|
+
if (this.options.connectToSubjectIndex) {
|
|
112
|
+
return this.options.connectToSubjectIndex(input);
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
subjectId: input.subjectId,
|
|
116
|
+
userId: input.userId,
|
|
117
|
+
userRoleCode: input.userRoleCode,
|
|
118
|
+
status: 'already-connected',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
async getSubjectIndexComposition(input) {
|
|
122
|
+
if (this.options.getSubjectIndexComposition) {
|
|
123
|
+
return this.options.getSubjectIndexComposition(input);
|
|
124
|
+
}
|
|
125
|
+
const routeContext = this.options.defaultRouteContext;
|
|
126
|
+
if (!routeContext) {
|
|
127
|
+
throw new Error('DirectBackendProfileRuntime requires defaultRouteContext to read subject index data.');
|
|
128
|
+
}
|
|
129
|
+
const profile = this.resolveLoadedProfile(input.userId);
|
|
130
|
+
if (profile.descriptor.actorKind !== ActorKinds.IndividualController) {
|
|
131
|
+
throw new Error(`DirectBackendProfileRuntime currently resolves subject index reads through IndividualController only, not '${profile.descriptor.actorKind}'.`);
|
|
132
|
+
}
|
|
133
|
+
const sdk = requireBackendIndividualControllerSdk(profile);
|
|
134
|
+
if (this.options.subjectIndexReadMode === BackendSubjectIndexReadModes.ClinicalBundle) {
|
|
135
|
+
const searchResult = await sdk.searchClinicalBundle(routeContext, {
|
|
136
|
+
subject: input.subjectId,
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
subjectId: input.subjectId,
|
|
140
|
+
userId: input.userId,
|
|
141
|
+
userRoleCode: input.userRoleCode,
|
|
142
|
+
composition: searchResult.poll.body,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const latestIps = await sdk.getLatestIps(routeContext, {
|
|
146
|
+
subject: input.subjectId,
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
subjectId: input.subjectId,
|
|
150
|
+
userId: input.userId,
|
|
151
|
+
userRoleCode: input.userRoleCode,
|
|
152
|
+
composition: latestIps.poll.body,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
rememberLoadedProfile(profile) {
|
|
156
|
+
const keys = new Set([
|
|
157
|
+
profile.descriptor.profileId,
|
|
158
|
+
String(profile.descriptor.profileDid || '').trim(),
|
|
159
|
+
String(profile.descriptor.subjectDid || '').trim(),
|
|
160
|
+
String(profile.descriptor.email || '').trim(),
|
|
161
|
+
String(profile.descriptor.phone || '').trim(),
|
|
162
|
+
].filter(Boolean));
|
|
163
|
+
for (const key of keys) {
|
|
164
|
+
this.loadedProfiles.set(key, profile);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
forgetLoadedProfile(profile) {
|
|
168
|
+
const keys = new Set([
|
|
169
|
+
profile.descriptor.profileId,
|
|
170
|
+
String(profile.descriptor.profileDid || '').trim(),
|
|
171
|
+
String(profile.descriptor.subjectDid || '').trim(),
|
|
172
|
+
String(profile.descriptor.email || '').trim(),
|
|
173
|
+
String(profile.descriptor.phone || '').trim(),
|
|
174
|
+
].filter(Boolean));
|
|
175
|
+
for (const key of keys) {
|
|
176
|
+
this.loadedProfiles.delete(key);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
resolveLoadedProfile(userId) {
|
|
180
|
+
const normalizedUserId = String(userId || '').trim();
|
|
181
|
+
const direct = this.loadedProfiles.get(normalizedUserId);
|
|
182
|
+
if (direct) {
|
|
183
|
+
return direct;
|
|
184
|
+
}
|
|
185
|
+
throw new Error(`DirectBackendProfileRuntime has not loaded one backend profile for '${normalizedUserId}'.`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Minimal in-memory `JobManager` for backend runtimes that do not need durable
|
|
190
|
+
* persistence during one live session.
|
|
191
|
+
*/
|
|
192
|
+
export function createJobManagerInMemory(descriptor) {
|
|
193
|
+
let isInitialized = false;
|
|
194
|
+
let sequence = 0;
|
|
195
|
+
const jobs = new Map();
|
|
196
|
+
let listener;
|
|
197
|
+
function notify() {
|
|
198
|
+
listener?.();
|
|
199
|
+
}
|
|
200
|
+
function nextSequence() {
|
|
201
|
+
sequence += 1;
|
|
202
|
+
return sequence;
|
|
203
|
+
}
|
|
204
|
+
function inferFormType(content) {
|
|
205
|
+
const first = Array.isArray(content?.body?.data) ? content.body.data[0] : undefined;
|
|
206
|
+
return String(first?.type || content?.type || '').trim();
|
|
207
|
+
}
|
|
208
|
+
function cloneJob(job) {
|
|
209
|
+
return {
|
|
210
|
+
...job,
|
|
211
|
+
indexed: job.indexed ? structuredClone(job.indexed) : job.indexed,
|
|
212
|
+
content: job.content ? structuredClone(job.content) : job.content,
|
|
213
|
+
jwe: job.jwe ? structuredClone(job.jwe) : job.jwe,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function matchesQuery(job, query) {
|
|
217
|
+
const conditions = Array.isArray(query.where) ? query.where : [];
|
|
218
|
+
return conditions.every((condition) => {
|
|
219
|
+
const currentValue = job[condition.attribute];
|
|
220
|
+
if ('equals' in condition) {
|
|
221
|
+
return currentValue === condition.equals;
|
|
222
|
+
}
|
|
223
|
+
if ('in' in condition) {
|
|
224
|
+
return Array.isArray(condition.in) && condition.in.includes(currentValue);
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
descriptor: { ...descriptor },
|
|
231
|
+
get isInitialized() {
|
|
232
|
+
return isInitialized;
|
|
233
|
+
},
|
|
234
|
+
async initialize() {
|
|
235
|
+
isInitialized = true;
|
|
236
|
+
},
|
|
237
|
+
shutdown() {
|
|
238
|
+
isInitialized = false;
|
|
239
|
+
jobs.clear();
|
|
240
|
+
},
|
|
241
|
+
setListener(nextListener) {
|
|
242
|
+
listener = nextListener;
|
|
243
|
+
},
|
|
244
|
+
async createJob(content, selector) {
|
|
245
|
+
const now = Date.now();
|
|
246
|
+
const job = {
|
|
247
|
+
id: createRuntimeUuid(),
|
|
248
|
+
thid: String(content?.thid || '').trim() || undefined,
|
|
249
|
+
status: JobStatus.DRAFT,
|
|
250
|
+
sequence: nextSequence(),
|
|
251
|
+
createdAtTimestamp: now,
|
|
252
|
+
content: structuredClone(content),
|
|
253
|
+
...selector,
|
|
254
|
+
};
|
|
255
|
+
jobs.set(job.id, job);
|
|
256
|
+
notify();
|
|
257
|
+
return cloneJob(job);
|
|
258
|
+
},
|
|
259
|
+
async findDraftJobByFormType(formType) {
|
|
260
|
+
const normalizedFormType = String(formType || '').trim();
|
|
261
|
+
for (const job of jobs.values()) {
|
|
262
|
+
if (job.status !== JobStatus.DRAFT)
|
|
263
|
+
continue;
|
|
264
|
+
if (inferFormType(job.content) === normalizedFormType) {
|
|
265
|
+
return cloneJob(job);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
},
|
|
270
|
+
async createOrUpdateDraftJob(content, selector) {
|
|
271
|
+
const formType = inferFormType(content);
|
|
272
|
+
const existing = formType ? await this.findDraftJobByFormType(formType) : null;
|
|
273
|
+
if (!existing) {
|
|
274
|
+
return this.createJob(content, selector);
|
|
275
|
+
}
|
|
276
|
+
const current = jobs.get(existing.id);
|
|
277
|
+
if (!current) {
|
|
278
|
+
return this.createJob(content, selector);
|
|
279
|
+
}
|
|
280
|
+
const updated = {
|
|
281
|
+
...current,
|
|
282
|
+
...selector,
|
|
283
|
+
thid: String(content?.thid || '').trim() || current.thid,
|
|
284
|
+
content: structuredClone(content),
|
|
285
|
+
previousSequence: current.sequence,
|
|
286
|
+
sequence: nextSequence(),
|
|
287
|
+
};
|
|
288
|
+
jobs.set(updated.id, updated);
|
|
289
|
+
notify();
|
|
290
|
+
return cloneJob(updated);
|
|
291
|
+
},
|
|
292
|
+
async sync() { },
|
|
293
|
+
async queryJobs(query) {
|
|
294
|
+
const filtered = [...jobs.values()].filter(job => matchesQuery(job, query));
|
|
295
|
+
if (query.orderBy) {
|
|
296
|
+
const { attribute, direction } = query.orderBy;
|
|
297
|
+
filtered.sort((left, right) => {
|
|
298
|
+
const a = left[attribute];
|
|
299
|
+
const b = right[attribute];
|
|
300
|
+
if (a === b)
|
|
301
|
+
return 0;
|
|
302
|
+
const cmp = a > b ? 1 : -1;
|
|
303
|
+
return direction === 'desc' ? -cmp : cmp;
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const offset = Math.max(0, Number(query.offset || 0));
|
|
307
|
+
const limit = query.limit == null ? filtered.length : Math.max(0, Number(query.limit));
|
|
308
|
+
return filtered.slice(offset, offset + limit).map(cloneJob);
|
|
309
|
+
},
|
|
310
|
+
async submitJob(job) {
|
|
311
|
+
const current = jobs.get(job.id);
|
|
312
|
+
if (!current) {
|
|
313
|
+
throw new Error(`JobManager in-memory store cannot submit unknown job '${job.id}'.`);
|
|
314
|
+
}
|
|
315
|
+
jobs.set(job.id, {
|
|
316
|
+
...current,
|
|
317
|
+
status: JobStatus.SENT,
|
|
318
|
+
previousSequence: current.sequence,
|
|
319
|
+
sequence: nextSequence(),
|
|
320
|
+
});
|
|
321
|
+
notify();
|
|
322
|
+
},
|
|
323
|
+
async sealJobWithToken(job) {
|
|
324
|
+
return job;
|
|
325
|
+
},
|
|
326
|
+
async getJobResponseByThid(thid) {
|
|
327
|
+
for (const job of jobs.values()) {
|
|
328
|
+
if (job.thid === thid && job.responseMessageId) {
|
|
329
|
+
return {
|
|
330
|
+
id: job.responseMessageId,
|
|
331
|
+
thid,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return null;
|
|
336
|
+
},
|
|
337
|
+
generateId() {
|
|
338
|
+
return createRuntimeUuid();
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Requires one backend profile-runtime method from one runtime client.
|
|
344
|
+
*/
|
|
345
|
+
export function requireBackendProfileRuntimeMethod(client, method) {
|
|
346
|
+
const candidate = client[method];
|
|
347
|
+
if (typeof candidate !== 'function') {
|
|
348
|
+
throw new Error(`BackendProfileRuntimeClient does not implement '${String(method)}'.`);
|
|
349
|
+
}
|
|
350
|
+
return candidate.bind(client);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Canonical backend helper for loading one actor profile after authentication.
|
|
354
|
+
*/
|
|
355
|
+
export async function loadBackendProfile(client, input) {
|
|
356
|
+
return requireBackendProfileRuntimeMethod(client, 'loadProfile')(input);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Canonical backend helper for closing one loaded actor profile and clearing
|
|
360
|
+
* runtime-owned in-memory state.
|
|
361
|
+
*/
|
|
362
|
+
export async function closeBackendProfile(client, profileKey) {
|
|
363
|
+
return requireBackendProfileRuntimeMethod(client, 'closeProfile')(profileKey);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Canonical backend helper for registering one trusted device/runtime context.
|
|
367
|
+
*/
|
|
368
|
+
export async function registerBackendTrustedDevice(client, input) {
|
|
369
|
+
return requireBackendProfileRuntimeMethod(client, 'registerTrustedDevice')(input);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Canonical backend helper for connecting one actor profile to one subject
|
|
373
|
+
* index after the profile is already loaded.
|
|
374
|
+
*/
|
|
375
|
+
export async function connectBackendToSubjectIndex(client, input) {
|
|
376
|
+
return requireBackendProfileRuntimeMethod(client, 'connectToSubjectIndex')(input);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Canonical backend helper for reading one subject index composition after the
|
|
380
|
+
* relationship is already established.
|
|
381
|
+
*/
|
|
382
|
+
export async function getBackendSubjectIndexComposition(client, input) {
|
|
383
|
+
return requireBackendProfileRuntimeMethod(client, 'getSubjectIndexComposition')(input);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Returns one materialized backend actor session by actor kind.
|
|
387
|
+
*
|
|
388
|
+
* Backend callers should use this instead of scanning `actorSessions` manually.
|
|
389
|
+
*/
|
|
390
|
+
export function requireBackendActorSession(profile, actorKind) {
|
|
391
|
+
const session = profile.actorSessions.find(candidate => candidate.actorKind === actorKind);
|
|
392
|
+
if (!session) {
|
|
393
|
+
throw new Error(`Loaded backend profile does not expose actor kind '${actorKind}'.`);
|
|
394
|
+
}
|
|
395
|
+
return session;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Returns the individual-controller session from one loaded backend profile.
|
|
399
|
+
*
|
|
400
|
+
* This helper is the first concrete bridge from the generic backend runtime to
|
|
401
|
+
* the current individual bootstrap/index use case.
|
|
402
|
+
*/
|
|
403
|
+
export function requireBackendIndividualControllerSession(profile) {
|
|
404
|
+
return requireBackendActorSession(profile, ActorKinds.IndividualController);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Materializes the individual-controller facade directly from one loaded
|
|
408
|
+
* backend profile.
|
|
409
|
+
*/
|
|
410
|
+
export function requireBackendIndividualControllerSdk(profile) {
|
|
411
|
+
return requireBackendIndividualControllerSession(profile).asIndividualController();
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Loads one backend profile and resolves the individual-controller session in
|
|
415
|
+
* one step.
|
|
416
|
+
*
|
|
417
|
+
* This is the first pragmatic use-case helper on top of the generic backend
|
|
418
|
+
* profile runtime, because the current stable CORE bootstrap baseline starts
|
|
419
|
+
* from the individual-controller flow.
|
|
420
|
+
*/
|
|
421
|
+
export async function loadBackendIndividualControllerProfile(client, input) {
|
|
422
|
+
const profile = await loadBackendProfile(client, input);
|
|
423
|
+
const session = requireBackendIndividualControllerSession(profile);
|
|
424
|
+
return {
|
|
425
|
+
profile,
|
|
426
|
+
session,
|
|
427
|
+
sdk: session.asIndividualController(),
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function createRuntimeUuid() {
|
|
431
|
+
const fromCrypto = globalThis.crypto?.randomUUID?.();
|
|
432
|
+
if (fromCrypto) {
|
|
433
|
+
return fromCrypto;
|
|
434
|
+
}
|
|
435
|
+
return `fallback-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
436
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node runtime re-export of canonical consent claim helpers.
|
|
3
3
|
*/
|
|
4
|
-
export { addActorIdentifierList, addActorRoleList, addActors, addCategories, addCategoryList, addClaimValues, addPurposeList, addPurposes, addRoles, addSections, getActorIdentifierList, getActorRoleList, getActors, getCategories, getCategoryList, getClaimValues, getConsentDate, getConsentIdentifier, getConsentPeriodEnd, getConsentPeriodStart, getPurposeList, getPurposes, getRoles, getSections, setActorIdentifierList, setActorRoleList, setActors, setCategories, setCategoryList, setClaimValues, setConsentDate, setConsentIdentifier, setConsentPeriodEnd, setConsentPeriodStart, setPurposeList, setPurposes, setRoles, setSections,
|
|
4
|
+
export { addActorIdentifierList, addActorRoleList, addActors, addCategories, addCategoryList, addClaimValues, addPurposeList, addPurposes, addRoles, addSections, getActorIdentifierList, getActorRoleList, getActors, getCategories, getCategoryList, getClaimValues, getConsentDate, getConsentIdentifier, getConsentPeriodEnd, getConsentPeriodStart, getPurposeList, getPurposes, getRoles, getSections, setActorIdentifierList, setActorRoleList, setActors, setCategories, setCategoryList, setClaimValues, setConsentDate, setConsentIdentifier, setConsentPeriodEnd, setConsentPeriodStart, setPurposeList, setPurposes, setRoles, setSections, } from 'gdc-sdk-core-ts';
|
|
5
|
+
export type { ConsentInteroperableClaims, } from 'gdc-sdk-core-ts';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export * from 'gdc-sdk-core-ts';
|
|
|
2
2
|
export * from './runtime-contracts.js';
|
|
3
3
|
export * from './identity-bootstrap.js';
|
|
4
4
|
export * from './async-polling.js';
|
|
5
|
+
export * from './backend-profile-runtime.js';
|
|
6
|
+
export * from './individual-controller-backend-runtime.js';
|
|
5
7
|
export * from './poll-options.js';
|
|
6
8
|
export * from './host-onboarding.js';
|
|
7
9
|
export * from './individual-start.js';
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ export * from 'gdc-sdk-core-ts';
|
|
|
3
3
|
export * from './runtime-contracts.js';
|
|
4
4
|
export * from './identity-bootstrap.js';
|
|
5
5
|
export * from './async-polling.js';
|
|
6
|
+
export * from './backend-profile-runtime.js';
|
|
7
|
+
export * from './individual-controller-backend-runtime.js';
|
|
6
8
|
export * from './poll-options.js';
|
|
7
9
|
export * from './host-onboarding.js';
|
|
8
10
|
export * from './individual-start.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { SubmitAndPollResult } from 'gdc-sdk-core-ts';
|
|
2
|
+
import type { IndividualOrganizationConfirmOrderInput, RouteContext } from './individual-onboarding.js';
|
|
3
|
+
import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from './individual-start.js';
|
|
4
|
+
import type { ClinicalBundleSearchInput } from './resource-operations.js';
|
|
5
|
+
import { type BackendIndividualControllerProfile, type BackendProfileRuntimeClient } from './backend-profile-runtime.js';
|
|
6
|
+
import type { ProfileLoadRequest } from 'gdc-sdk-core-ts';
|
|
7
|
+
/**
|
|
8
|
+
* Backend use-case facade for the current individual-controller baseline.
|
|
9
|
+
*
|
|
10
|
+
* This class does not redefine the generic backend profile runtime. It wraps
|
|
11
|
+
* that runtime with the current stable CORE-facing actions that backend
|
|
12
|
+
* consumers need first:
|
|
13
|
+
*
|
|
14
|
+
* - load an individual-controller profile
|
|
15
|
+
* - start individual registration/bootstrap
|
|
16
|
+
* - confirm the returned order/offer
|
|
17
|
+
* - search the subject clinical index
|
|
18
|
+
* - request the latest IPS-oriented bundle
|
|
19
|
+
*/
|
|
20
|
+
export declare class IndividualControllerBackendRuntime {
|
|
21
|
+
private readonly profileRuntime;
|
|
22
|
+
constructor(profileRuntime: BackendProfileRuntimeClient);
|
|
23
|
+
/**
|
|
24
|
+
* Loads one individual-controller backend profile and materializes its actor
|
|
25
|
+
* facade in one step.
|
|
26
|
+
*/
|
|
27
|
+
loadProfile(input: ProfileLoadRequest): Promise<BackendIndividualControllerProfile>;
|
|
28
|
+
/**
|
|
29
|
+
* Starts the current CORE individual/family bootstrap flow.
|
|
30
|
+
*/
|
|
31
|
+
startIndividualOrganization(profile: BackendIndividualControllerProfile, input: IndividualOrganizationBootstrapInput): Promise<IndividualOrganizationStartResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Confirms the order returned by the individual bootstrap flow.
|
|
34
|
+
*/
|
|
35
|
+
confirmIndividualOrganizationOrder(profile: BackendIndividualControllerProfile, input: IndividualOrganizationConfirmOrderInput): Promise<SubmitAndPollResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Searches the current subject clinical bundle index through the loaded
|
|
38
|
+
* individual-controller facade.
|
|
39
|
+
*/
|
|
40
|
+
searchClinicalBundle(profile: BackendIndividualControllerProfile, ctx: RouteContext, input: ClinicalBundleSearchInput): Promise<SubmitAndPollResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Reads the latest IPS-oriented bundle through the loaded
|
|
43
|
+
* individual-controller facade.
|
|
44
|
+
*/
|
|
45
|
+
getLatestIps(profile: BackendIndividualControllerProfile, ctx: RouteContext, input: Omit<ClinicalBundleSearchInput, 'includedTypes'>): Promise<SubmitAndPollResult>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { loadBackendIndividualControllerProfile, } from './backend-profile-runtime.js';
|
|
3
|
+
/**
|
|
4
|
+
* Backend use-case facade for the current individual-controller baseline.
|
|
5
|
+
*
|
|
6
|
+
* This class does not redefine the generic backend profile runtime. It wraps
|
|
7
|
+
* that runtime with the current stable CORE-facing actions that backend
|
|
8
|
+
* consumers need first:
|
|
9
|
+
*
|
|
10
|
+
* - load an individual-controller profile
|
|
11
|
+
* - start individual registration/bootstrap
|
|
12
|
+
* - confirm the returned order/offer
|
|
13
|
+
* - search the subject clinical index
|
|
14
|
+
* - request the latest IPS-oriented bundle
|
|
15
|
+
*/
|
|
16
|
+
export class IndividualControllerBackendRuntime {
|
|
17
|
+
constructor(profileRuntime) {
|
|
18
|
+
this.profileRuntime = profileRuntime;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Loads one individual-controller backend profile and materializes its actor
|
|
22
|
+
* facade in one step.
|
|
23
|
+
*/
|
|
24
|
+
loadProfile(input) {
|
|
25
|
+
return loadBackendIndividualControllerProfile(this.profileRuntime, input);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Starts the current CORE individual/family bootstrap flow.
|
|
29
|
+
*/
|
|
30
|
+
startIndividualOrganization(profile, input) {
|
|
31
|
+
return profile.sdk.startIndividualOrganization(input);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Confirms the order returned by the individual bootstrap flow.
|
|
35
|
+
*/
|
|
36
|
+
confirmIndividualOrganizationOrder(profile, input) {
|
|
37
|
+
return profile.sdk.confirmIndividualOrganizationOrder(input);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Searches the current subject clinical bundle index through the loaded
|
|
41
|
+
* individual-controller facade.
|
|
42
|
+
*/
|
|
43
|
+
searchClinicalBundle(profile, ctx, input) {
|
|
44
|
+
return profile.sdk.searchClinicalBundle(ctx, input);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Reads the latest IPS-oriented bundle through the loaded
|
|
48
|
+
* individual-controller facade.
|
|
49
|
+
*/
|
|
50
|
+
getLatestIps(profile, ctx, input) {
|
|
51
|
+
return profile.sdk.getLatestIps(ctx, input);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import type { OrganizationDidBindingInput } from 'gdc-sdk-core-ts';
|
|
1
2
|
import type { AppInfo } from 'gdc-sdk-core-ts';
|
|
2
3
|
import { type ResolvedAppInfo } from 'gdc-sdk-core-ts';
|
|
3
4
|
import { type HostRouteContext, type HostedTenantLifecycleInput } from './host-onboarding.js';
|
|
4
|
-
import type { NodeOrganizationActivationInput } from './orchestration/client-port.js';
|
|
5
|
+
import type { NodeLegalOrganizationVerificationTransactionInput, NodeOrganizationDidBindingInput, NodeOrganizationActivationInput } from './orchestration/client-port.js';
|
|
5
6
|
import { type IndividualOrganizationConfirmOrderInput, type RouteContext } from './individual-onboarding.js';
|
|
6
7
|
import { type SmartTokenRequestInput } from './smart-token.js';
|
|
7
8
|
import { type OrganizationLicenseOrderConfirmInput } from './organization-license-order.js';
|
|
8
9
|
import { type IndividualOrganizationBootstrapInput, type IndividualOrganizationStartResult } from './individual-start.js';
|
|
9
|
-
import { type CommunicationIngestionInput, type ClinicalBundleSearchInput, type GrantProfessionalAccessInput, type GrantProfessionalAccessResult, type IndividualMemberLifecycleInput, type IndividualOrganizationLifecycleInput, type LicenseListRuntimeSearchInput, type LicenseOfferRuntimeSearchInput, type LicenseOrderRuntimeSearchInput, type OrganizationEmployeeCreationInput, type OrganizationEmployeeLifecycleInput, type OrganizationEmployeeSearchInput, type RelatedPersonUpsertInput } from './resource-operations.js';
|
|
10
|
+
import { type CommunicationIngestionInput, type CommunicationParticipantRuntimeSearchInput, type ClinicalBundleSearchInput, type GrantProfessionalAccessInput, type GrantProfessionalAccessResult, type IndividualMemberLifecycleInput, type IndividualOrganizationLifecycleInput, type LicenseListRuntimeSearchInput, type LicenseOfferRuntimeSearchInput, type LicenseOrderRuntimeSearchInput, type OrganizationEmployeeCreationInput, type OrganizationEmployeeLifecycleInput, type OrganizationEmployeeSearchInput, type RelatedPersonUpsertInput } from './resource-operations.js';
|
|
10
11
|
import type { LegalOrganizationOrderInput } from './host-onboarding.js';
|
|
11
12
|
import type { SmartTokenExchangeResult } from './smart-token.js';
|
|
12
13
|
import type { NodeRuntimeClient, PollOptions, PollResult, SubmitAndPollResult, SubmitPayload, SubmitResponse } from './orchestration/client-port.js';
|
|
@@ -109,9 +110,45 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
|
|
|
109
110
|
* Convenience wrapper that performs submit and poll in sequence.
|
|
110
111
|
*/
|
|
111
112
|
submitAndPoll(submitPath: string, pollPath: string, payload: SubmitPayload, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
|
|
113
|
+
/**
|
|
114
|
+
* Starts the host-side legal-organization verification transaction that GW
|
|
115
|
+
* CORE forwards to ICA `_verify`.
|
|
116
|
+
*
|
|
117
|
+
* Runtime ownership:
|
|
118
|
+
* - builds the canonical shared business bundle from `sdk-core/common-utils`
|
|
119
|
+
* - keeps communication/runtime transport concerns at the outer message layer
|
|
120
|
+
* - keeps the controller business key inside the submitted bundle payload
|
|
121
|
+
*
|
|
122
|
+
* Flow separation:
|
|
123
|
+
* - `_transaction` is the first host onboarding step
|
|
124
|
+
* - `_activate` remains a later step that consumes the ICA proof
|
|
125
|
+
*/
|
|
126
|
+
submitLegalOrganizationVerificationTransaction(hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
|
|
127
|
+
/**
|
|
128
|
+
* Submits one tenant-scoped DID document binding request.
|
|
129
|
+
*
|
|
130
|
+
* Binding semantics:
|
|
131
|
+
* - the tenant path identifies the existing organization
|
|
132
|
+
* - `organization.url` carries the public alias/domain list
|
|
133
|
+
* - `controller.sameAs` is optional corroborating identity material
|
|
134
|
+
* - the flow does not rotate or replace organization public keys
|
|
135
|
+
*/
|
|
136
|
+
submitOrganizationDidBinding(ctx: RouteContext, input: NodeOrganizationDidBindingInput | OrganizationDidBindingInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
|
|
112
137
|
/**
|
|
113
138
|
* Activates a legal organization in the gateway host registry using an ICA
|
|
114
139
|
* proof token already obtained by the caller.
|
|
140
|
+
*
|
|
141
|
+
* Plaintext transport note:
|
|
142
|
+
* - this Node runtime currently submits `_activate` as
|
|
143
|
+
* `application/didcomm-plaintext+json`
|
|
144
|
+
* - because there is no real outer JWS/JWE envelope in that mode, the
|
|
145
|
+
* runtime mirrors the technical communication metadata derived from
|
|
146
|
+
* `controller.publicKeyJwk` / `controller.jwks` into `meta.jws.protected`
|
|
147
|
+
* and `meta.jwe.header`
|
|
148
|
+
* - secure JOSE transports should carry those values in the real protected
|
|
149
|
+
* headers instead of plaintext `meta`
|
|
150
|
+
* - this mirrored metadata is transport fallback only; the canonical
|
|
151
|
+
* activation contract remains `body.vp_token` plus `body.controller.*`
|
|
115
152
|
*/
|
|
116
153
|
activateOrganizationInGatewayFromIcaProof(hostCtx: HostRouteContext, input: NodeOrganizationActivationInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
|
|
117
154
|
/**
|
|
@@ -274,6 +311,11 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
|
|
|
274
311
|
* @param input Communication payload plus route/format options.
|
|
275
312
|
*/
|
|
276
313
|
ingestCommunicationAndUpdateIndex(ctx: RouteContext, input: CommunicationIngestionInput): Promise<SubmitAndPollResult>;
|
|
314
|
+
/**
|
|
315
|
+
* Searches communication channel records by subject and participant
|
|
316
|
+
* identifiers through `Communication/_search`.
|
|
317
|
+
*/
|
|
318
|
+
searchCommunicationParticipants(ctx: RouteContext, input: CommunicationParticipantRuntimeSearchInput): Promise<SubmitAndPollResult>;
|
|
277
319
|
/**
|
|
278
320
|
* Alias for `ingestCommunicationAndUpdateIndex(...)`.
|
|
279
321
|
*
|
|
@@ -319,8 +361,15 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
|
|
|
319
361
|
private parseResponseBody;
|
|
320
362
|
private requireRouteContext;
|
|
321
363
|
private routeCtxFromInput;
|
|
364
|
+
/**
|
|
365
|
+
* Reuses the shared bundle business contract while keeping attachment
|
|
366
|
+
* transport fields at the DIDComm/plaintext message layer expected by GW.
|
|
367
|
+
*/
|
|
368
|
+
private wrapBundleAsGatewayTransactionMessage;
|
|
322
369
|
private hostRegistryPath;
|
|
323
370
|
private requireHostRouteContext;
|
|
371
|
+
hostRegistryOrganizationTransactionPath(ctx?: HostRouteContext): string;
|
|
372
|
+
hostRegistryOrganizationTransactionPollPath(ctx?: HostRouteContext): string;
|
|
324
373
|
hostRegistryOrganizationActivatePath(ctx?: HostRouteContext): string;
|
|
325
374
|
hostRegistryOrganizationActivatePollPath(ctx?: HostRouteContext): string;
|
|
326
375
|
hostRegistryOrganizationDisablePath(ctx?: HostRouteContext): string;
|
|
@@ -335,6 +384,8 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
|
|
|
335
384
|
employeeSearchPollPath(ctx?: RouteContext): string;
|
|
336
385
|
organizationLicenseSearchPath(ctx?: RouteContext): string;
|
|
337
386
|
organizationLicenseSearchPollPath(ctx?: RouteContext): string;
|
|
387
|
+
organizationDidBindingPath(ctx?: RouteContext): string;
|
|
388
|
+
organizationDidBindingPollPath(ctx?: RouteContext): string;
|
|
338
389
|
organizationLicenseOfferSearchPath(ctx?: RouteContext): string;
|
|
339
390
|
organizationLicenseOfferSearchPollPath(ctx?: RouteContext): string;
|
|
340
391
|
organizationLicenseOrderSearchPath(ctx?: RouteContext): string;
|
|
@@ -365,6 +416,8 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
|
|
|
365
416
|
individualConsentR4PollPath(ctx: RouteContext): string;
|
|
366
417
|
individualCommunicationBatchPath(ctx: RouteContext, format: 'org.hl7.fhir.api' | 'org.hl7.fhir.r4'): string;
|
|
367
418
|
individualCommunicationPollPath(ctx: RouteContext, format: 'org.hl7.fhir.api' | 'org.hl7.fhir.r4'): string;
|
|
419
|
+
individualCommunicationSearchPath(ctx: RouteContext): string;
|
|
420
|
+
individualCommunicationSearchPollPath(ctx: RouteContext): string;
|
|
368
421
|
individualBundleSearchPath(ctx: RouteContext): string;
|
|
369
422
|
individualBundleSearchPollPath(ctx: RouteContext): string;
|
|
370
423
|
identityTokenExchangePath(ctx: RouteContext): string;
|