gdc-sdk-node-ts 2.0.7 → 2.0.9

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 CHANGED
@@ -682,6 +682,19 @@ For legal-organization onboarding from a Node BFF/runtime, keep the host steps s
682
682
  Use `OrganizationControllerSdk.submitLegalOrganizationVerificationTransaction(...)` or
683
683
  `NodeHttpClient.submitLegalOrganizationVerificationTransaction(...)` for step 1.
684
684
 
685
+ Existing-tenant recovery/reverification:
686
+
687
+ - use `OrganizationControllerSdk.submitLegalOrganizationIssue(...)` or
688
+ `NodeHttpClient.submitLegalOrganizationIssue(...)` to refresh the ICA-backed
689
+ verification for an already registered tenant without creating a new Offer
690
+ - GW is expected to return one new controller activation code as
691
+ `org.schema.IndividualProduct.serialNumber`
692
+ - then continue with the existing helper chain:
693
+ `recoverOrganizationControllerWithIssueWithDeps(...)`
694
+ or directly `_exchange -> _dcr`
695
+ - for a reproducible local/staging runtime check, use:
696
+ `npm run test:e2e:live-gw:issue-recovery`
697
+
685
698
  Rules:
686
699
 
687
700
  - `_transaction` and `_activate` are different flows
@@ -714,3 +727,23 @@ Live E2E legal PDF source:
714
727
  - public URL: set `LIVE_GW_HOST_VERIFICATION_PDF_URL=https://.../file.pdf`
715
728
  - if both are present, the live suite prefers `LIVE_GW_HOST_VERIFICATION_PDF_URL`
716
729
  - Dropbox-style links are normalized to `dl=1` direct-download mode automatically
730
+
731
+ Recovery-specific live rule:
732
+
733
+ - `Organization/_issue` can succeed and still be followed by `_exchange` failure
734
+ if the controller `id_token` is not production-shaped enough
735
+ - the current GW `_exchange` path expects at least:
736
+ - syntactically valid JWT format
737
+ - `tenant_id` claim matching the target tenant
738
+ - the bundled recovery runner generates a syntactically valid demo JWT if
739
+ `CONTROLLER_ID_TOKEN` is not provided, but production/staging should use a
740
+ real IdP-issued token
741
+
742
+ Minimal live recovery command:
743
+
744
+ ```bash
745
+ PDF_PATH=/Users/fernando/GITS/gdc-workspace/examples/TEST-A4-Antifraud.pdf \
746
+ BASE_URL=http://127.0.0.1:3000 \
747
+ TENANT_ID=acme-id \
748
+ npm run test:e2e:live-gw:issue-recovery
749
+ ```
@@ -9,6 +9,8 @@
9
9
  export declare const GwCoreLifecycleAction: Readonly<{
10
10
  readonly Batch: "_batch";
11
11
  readonly BatchResponse: "_batch-response";
12
+ readonly Issue: "_issue";
13
+ readonly IssueResponse: "_issue-response";
12
14
  readonly Transaction: "_transaction";
13
15
  readonly TransactionResponse: "_transaction-response";
14
16
  readonly Disable: "_disable";
@@ -11,6 +11,8 @@ import { LifecycleRequestType } from 'gdc-common-utils-ts';
11
11
  export const GwCoreLifecycleAction = Object.freeze({
12
12
  Batch: '_batch',
13
13
  BatchResponse: '_batch-response',
14
+ Issue: '_issue',
15
+ IssueResponse: '_issue-response',
14
16
  Transaction: '_transaction',
15
17
  TransactionResponse: '_transaction-response',
16
18
  Disable: '_disable',
@@ -14,6 +14,7 @@ export async function activateEmployeeDeviceWithActivationCodeWithDeps(deps) {
14
14
  }
15
15
  const dcrPayload = {
16
16
  thid: `dcr-${createRuntimeUuid()}`,
17
+ code: deps.input.activationCode,
17
18
  ...deps.input.dcrPayload,
18
19
  };
19
20
  const dcr = await deps.submitAndPollWithBearerToken(initialAccessToken, deps.identityDeviceDcrPath(deps.routeCtx), deps.identityDeviceDcrPollPath(deps.routeCtx), dcrPayload, deps.input.pollOptions);
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export * from './family-organization-search.js';
13
13
  export * from './family-organization-registration.js';
14
14
  export * from './individual-onboarding.js';
15
15
  export * from './device-activation.js';
16
+ export * from './organization-controller-recovery.js';
16
17
  export * from './smart-token.js';
17
18
  export * from './order-offer-summary.js';
18
19
  export * from './organization-license-order.js';
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ export * from './family-organization-search.js';
14
14
  export * from './family-organization-registration.js';
15
15
  export * from './individual-onboarding.js';
16
16
  export * from './device-activation.js';
17
+ export * from './organization-controller-recovery.js';
17
18
  export * from './smart-token.js';
18
19
  export * from './order-offer-summary.js';
19
20
  export * from './organization-license-order.js';
@@ -127,6 +127,16 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
127
127
  * - `_activate` remains available only for the older ICA `_verify` based flow
128
128
  */
129
129
  submitLegalOrganizationVerificationTransaction(hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
130
+ /**
131
+ * Starts the host-side existing-tenant legal-organization reissue flow that
132
+ * GW CORE forwards to ICA `_verify`.
133
+ *
134
+ * Semantics:
135
+ * - reuse the same signed evidence/controller binding contract as `_transaction`
136
+ * - do not create a new Offer
137
+ * - expect GW CORE to reissue one controller activation code in the response
138
+ */
139
+ submitLegalOrganizationIssue(hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
130
140
  /**
131
141
  * Submits one tenant-scoped DID document binding request.
132
142
  *
@@ -411,6 +421,8 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
411
421
  private requireHostRouteContext;
412
422
  hostRegistryOrganizationTransactionPath(ctx?: HostRouteContext): string;
413
423
  hostRegistryOrganizationTransactionPollPath(ctx?: HostRouteContext): string;
424
+ hostRegistryOrganizationIssuePath(ctx?: HostRouteContext): string;
425
+ hostRegistryOrganizationIssuePollPath(ctx?: HostRouteContext): string;
414
426
  hostRegistryOrganizationActivatePath(ctx?: HostRouteContext): string;
415
427
  hostRegistryOrganizationActivatePollPath(ctx?: HostRouteContext): string;
416
428
  hostRegistryOrganizationDisablePath(ctx?: HostRouteContext): string;
@@ -465,6 +477,8 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
465
477
  individualBundleSearchPollPath(ctx: RouteContext): string;
466
478
  identityTokenExchangePath(ctx: RouteContext): string;
467
479
  identityTokenExchangePollPath(ctx: RouteContext): string;
480
+ identityDeviceDcrPath(ctx: RouteContext): string;
481
+ identityDeviceDcrPollPath(ctx: RouteContext): string;
468
482
  identityOpenIdSmartTokenPath(ctx: RouteContext): string;
469
483
  identityOpenIdSmartTokenPollPath(ctx: RouteContext): string;
470
484
  private appendHttpTrace;
@@ -126,6 +126,27 @@ export class HttpRuntimeClient {
126
126
  });
127
127
  return this.submitAndPoll(this.hostRegistryOrganizationTransactionPath(hostCtx), this.hostRegistryOrganizationTransactionPollPath(hostCtx), payload, pollOptions);
128
128
  }
129
+ /**
130
+ * Starts the host-side existing-tenant legal-organization reissue flow that
131
+ * GW CORE forwards to ICA `_verify`.
132
+ *
133
+ * Semantics:
134
+ * - reuse the same signed evidence/controller binding contract as `_transaction`
135
+ * - do not create a new Offer
136
+ * - expect GW CORE to reissue one controller activation code in the response
137
+ */
138
+ async submitLegalOrganizationIssue(hostCtx, input, pollOptions) {
139
+ const thid = `organization-issue-${runtimeUuid()}`;
140
+ const jti = `organization-issue-jti-${runtimeUuid()}`;
141
+ const verificationBundle = buildLegalOrganizationVerificationGatewayRequestBundle(input);
142
+ const payload = this.wrapBundleAsGatewayTransactionMessage({
143
+ thid,
144
+ jti,
145
+ hostCtx,
146
+ bundle: verificationBundle,
147
+ });
148
+ return this.submitAndPoll(this.hostRegistryOrganizationIssuePath(hostCtx), this.hostRegistryOrganizationIssuePollPath(hostCtx), payload, pollOptions);
149
+ }
129
150
  /**
130
151
  * Submits one tenant-scoped DID document binding request.
131
152
  *
@@ -895,6 +916,8 @@ export class HttpRuntimeClient {
895
916
  }
896
917
  hostRegistryOrganizationTransactionPath(ctx) { return this.hostRegistryPath(ctx, 'Organization', GwCoreLifecycleAction.Transaction); }
897
918
  hostRegistryOrganizationTransactionPollPath(ctx) { return this.hostRegistryPath(ctx, 'Organization', GwCoreLifecycleAction.TransactionResponse); }
919
+ hostRegistryOrganizationIssuePath(ctx) { return this.hostRegistryPath(ctx, 'Organization', GwCoreLifecycleAction.Issue); }
920
+ hostRegistryOrganizationIssuePollPath(ctx) { return this.hostRegistryPath(ctx, 'Organization', GwCoreLifecycleAction.IssueResponse); }
898
921
  hostRegistryOrganizationActivatePath(ctx) { return this.hostRegistryPath(ctx, 'Organization', '_activate'); }
899
922
  hostRegistryOrganizationActivatePollPath(ctx) { return this.hostRegistryPath(ctx, 'Organization', '_activate-response'); }
900
923
  hostRegistryOrganizationDisablePath(ctx) { return this.hostRegistryPath(ctx, 'Organization', GwCoreLifecycleAction.Disable); }
@@ -959,6 +982,12 @@ export class HttpRuntimeClient {
959
982
  identityTokenExchangePollPath(ctx) {
960
983
  return `/${encodeURIComponent('host')}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/${encodeURIComponent(ctx.tenantId)}/identity/auth/_exchange-response`;
961
984
  }
985
+ identityDeviceDcrPath(ctx) {
986
+ return `/${encodeURIComponent('host')}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/${encodeURIComponent(ctx.tenantId)}/identity/auth/_dcr`;
987
+ }
988
+ identityDeviceDcrPollPath(ctx) {
989
+ return `/${encodeURIComponent('host')}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/${encodeURIComponent(ctx.tenantId)}/identity/auth/_dcr-response`;
990
+ }
962
991
  identityOpenIdSmartTokenPath(ctx) {
963
992
  return `/${encodeURIComponent(ctx.tenantId)}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/identity/openid/smart/token`;
964
993
  }
@@ -54,6 +54,7 @@ export type NodeOrganizationDidBindingInput = OrganizationDidBindingInput;
54
54
  */
55
55
  export type RuntimeClient = {
56
56
  submitLegalOrganizationVerificationTransaction?: (hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
57
+ submitLegalOrganizationIssue?: (hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
57
58
  submitOrganizationDidBinding?: (ctx: RouteContext, input: NodeOrganizationDidBindingInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
58
59
  activateOrganizationInGatewayFromIcaProof?: (hostCtx: HostRouteContext, input: NodeOrganizationActivationInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
59
60
  confirmLegalOrganizationOrder?: (hostCtx: HostRouteContext, input: LegalOrganizationOrderInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
@@ -29,6 +29,11 @@ export declare class OrganizationControllerSdk {
29
29
  * - `_activate` remains only for the legacy ICA `_verify` compatibility path
30
30
  */
31
31
  submitLegalOrganizationVerificationTransaction(hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
32
+ /**
33
+ * Starts the host-side existing-tenant legal-organization reissue flow that
34
+ * refreshes verification and reissues one controller activation code.
35
+ */
36
+ submitLegalOrganizationIssue(hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
32
37
  /**
33
38
  * Binds the current tenant organization DID document to one public alias
34
39
  * view.
@@ -28,6 +28,13 @@ export class OrganizationControllerSdk {
28
28
  submitLegalOrganizationVerificationTransaction(hostCtx, input, pollOptions) {
29
29
  return requireClientMethod(this.client, 'submitLegalOrganizationVerificationTransaction')(hostCtx, input, pollOptions);
30
30
  }
31
+ /**
32
+ * Starts the host-side existing-tenant legal-organization reissue flow that
33
+ * refreshes verification and reissues one controller activation code.
34
+ */
35
+ submitLegalOrganizationIssue(hostCtx, input, pollOptions) {
36
+ return requireClientMethod(this.client, 'submitLegalOrganizationIssue')(hostCtx, input, pollOptions);
37
+ }
31
38
  /**
32
39
  * Binds the current tenant organization DID document to one public alias
33
40
  * view.
@@ -0,0 +1,31 @@
1
+ import type { NodeLegalOrganizationVerificationTransactionInput, PollOptions, SubmitAndPollResult } from './orchestration/client-port.js';
2
+ import type { HostRouteContext } from './host-onboarding.js';
3
+ import type { RouteContext } from './individual-onboarding.js';
4
+ import type { EmployeeDeviceActivationResult } from './device-activation.js';
5
+ export type OrganizationControllerRecoveryInput = {
6
+ issueInput: NodeLegalOrganizationVerificationTransactionInput;
7
+ controllerIdToken: string;
8
+ dcrPayload: Record<string, unknown>;
9
+ issuePollOptions?: PollOptions;
10
+ activationPollOptions?: PollOptions;
11
+ };
12
+ export type OrganizationControllerRecoveryResult = {
13
+ issue: SubmitAndPollResult;
14
+ activationCode: string;
15
+ activation: EmployeeDeviceActivationResult;
16
+ };
17
+ type RecoverOrganizationControllerWithIssueDeps = {
18
+ hostCtx: HostRouteContext;
19
+ tenantCtx: RouteContext;
20
+ input: OrganizationControllerRecoveryInput;
21
+ submitLegalOrganizationIssue: (hostCtx: HostRouteContext, input: NodeLegalOrganizationVerificationTransactionInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
22
+ identityTokenExchangePath: (ctx: RouteContext) => string;
23
+ identityTokenExchangePollPath: (ctx: RouteContext) => string;
24
+ identityDeviceDcrPath: (ctx: RouteContext) => string;
25
+ identityDeviceDcrPollPath: (ctx: RouteContext) => string;
26
+ submitAndPollWithBearerToken: (bearerToken: string | undefined, submitPath: string, pollPath: string, payload: {
27
+ thid?: string;
28
+ } & Record<string, unknown>, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
29
+ };
30
+ export declare function recoverOrganizationControllerWithIssueWithDeps(deps: RecoverOrganizationControllerWithIssueDeps): Promise<OrganizationControllerRecoveryResult>;
31
+ export {};
@@ -0,0 +1,48 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { activateEmployeeDeviceWithActivationCodeWithDeps } from './device-activation.js';
3
+ export async function recoverOrganizationControllerWithIssueWithDeps(deps) {
4
+ const issue = await deps.submitLegalOrganizationIssue(deps.hostCtx, deps.input.issueInput, deps.input.issuePollOptions);
5
+ const activationCode = readActivationCodeFromIssueResult(issue);
6
+ if (!activationCode) {
7
+ throw new Error('recoverOrganizationControllerWithIssue: missing org.schema.IndividualProduct.serialNumber in Organization/_issue response.');
8
+ }
9
+ const activation = await activateEmployeeDeviceWithActivationCodeWithDeps({
10
+ routeCtx: deps.tenantCtx,
11
+ input: {
12
+ activationCode,
13
+ idToken: deps.input.controllerIdToken,
14
+ dcrPayload: deps.input.dcrPayload,
15
+ pollOptions: deps.input.activationPollOptions,
16
+ },
17
+ identityTokenExchangePath: deps.identityTokenExchangePath,
18
+ identityTokenExchangePollPath: deps.identityTokenExchangePollPath,
19
+ identityDeviceDcrPath: deps.identityDeviceDcrPath,
20
+ identityDeviceDcrPollPath: deps.identityDeviceDcrPollPath,
21
+ submitAndPollWithBearerToken: deps.submitAndPollWithBearerToken,
22
+ });
23
+ return {
24
+ issue,
25
+ activationCode,
26
+ activation,
27
+ };
28
+ }
29
+ function readActivationCodeFromIssueResult(result) {
30
+ const pollBody = (result?.poll?.body || {});
31
+ const body = (pollBody.body || pollBody);
32
+ const data = Array.isArray(body.data) ? body.data : Array.isArray(pollBody.data) ? pollBody.data : [];
33
+ const firstEntry = data[0] || {};
34
+ const claims = firstEntry.meta?.claims;
35
+ const activationCode = String(claims?.['org.schema.IndividualProduct.serialNumber'] || '').trim();
36
+ if (activationCode) {
37
+ return activationCode;
38
+ }
39
+ const diagnostics = firstEntry.response?.outcome
40
+ ?.issue
41
+ ?.map((issue) => String(issue?.diagnostics || '').trim())
42
+ .filter(Boolean)
43
+ .join(' | ');
44
+ if (diagnostics) {
45
+ throw new Error(`recoverOrganizationControllerWithIssue: Organization/_issue failed: ${diagnostics}`);
46
+ }
47
+ return '';
48
+ }
@@ -1,8 +1,5 @@
1
1
  import { CommunicationAttachedBundleSession, IndividualBundleVault, buildVitalSignObservationClaims, createSummaryOperationRequestParameters, createSummaryOperationRequestParametersResource, summarizeClinicalBundle, toClinicalResourceExpandedViews, type BuildVitalSignObservationClaimsInput } from 'gdc-common-utils-ts';
2
- import { type FhirDocumentFacade, type FhirBundleEntryLike, type FhirDocumentEntryQuery, type FhirDocumentSection, type FhirDocumentFamilyQuery, type FhirDocumentResourceQuery, type FhirDocumentSectionCounts, type FhirDocumentSectionSummary, type FhirResourceLike } from 'gdc-sdk-core-ts';
3
2
  import type { BackendOrganizationControllerProfile, BackendProfessionalProfile } from './backend-profile-runtime.js';
4
- type LocalTextAndIntDisplay = ReturnType<FhirDocumentFacade['getLocalTextAndIntDisplay']>;
5
- type NarrativeResult = ReturnType<FhirDocumentFacade['getNarrative']>;
6
3
  export type IpsRequestDraft = Readonly<{
7
4
  subjectId: string;
8
5
  purpose?: string;
@@ -16,74 +13,11 @@ export type ProcessedClinicalBundleResponse = Readonly<{
16
13
  totalSections: number;
17
14
  totalResources: number;
18
15
  totalResourcesInSection: Readonly<Record<string, number>>;
16
+ totalNarratives: number;
17
+ totalNotes: number;
19
18
  summary: ReturnType<typeof summarizeClinicalBundle>;
20
19
  views: ReturnType<typeof toClinicalResourceExpandedViews>;
21
- sectionCounts: FhirDocumentSectionCounts;
22
- sectionSummary: FhirDocumentSectionSummary;
23
- getSections: () => FhirDocumentSection[];
24
- getEntries: (input?: FhirDocumentEntryQuery) => FhirBundleEntryLike[];
25
- getResources: (query?: string | FhirDocumentResourceQuery) => FhirResourceLike[];
26
- getByDates: (query: string | FhirDocumentResourceQuery, start?: string, end?: string) => FhirResourceLike[];
27
- getSectionCounts: (input?: {
28
- sections?: readonly string[];
29
- }) => FhirDocumentSectionCounts;
30
- getSectionSummary: (input?: {
31
- sections?: readonly string[];
32
- }) => FhirDocumentSectionSummary;
33
- getLocalTextAndIntDisplay: (resourceOrEntry: FhirResourceLike | FhirBundleEntryLike) => LocalTextAndIntDisplay;
34
- getXhtmlOrDerived: (resourceOrEntry: FhirResourceLike | FhirBundleEntryLike) => string | undefined;
35
- getNarrative: (resourceOrEntry: FhirResourceLike | FhirBundleEntryLike) => NarrativeResult;
36
- getAllergies: (query?: FhirDocumentFamilyQuery & {
37
- clinicalStatus?: readonly string[];
38
- verificationStatus?: readonly string[];
39
- criticality?: readonly string[];
40
- }) => FhirBundleEntryLike[];
41
- getConditions: (query?: FhirDocumentFamilyQuery & {
42
- clinicalStatus?: readonly string[];
43
- verificationStatus?: readonly string[];
44
- severity?: readonly string[];
45
- }) => FhirBundleEntryLike[];
46
- getMedications: (query?: FhirDocumentFamilyQuery & {
47
- status?: readonly string[];
48
- }) => FhirBundleEntryLike[];
49
- getVitalSigns: (query?: FhirDocumentFamilyQuery & {
50
- code?: readonly string[];
51
- }) => FhirBundleEntryLike[];
52
- reader: ClinicalBundleQueryBuilder;
53
20
  }>;
54
- export type ClinicalBundleQueryBuilder = Readonly<{
55
- allSections: () => ClinicalBundleQueryBuilder;
56
- inSections: (sections?: readonly string[]) => ClinicalBundleQueryBuilder;
57
- between: (start?: string, end?: string) => ClinicalBundleQueryBuilder;
58
- matchingText: (searchText?: string) => ClinicalBundleQueryBuilder;
59
- paginate: (input?: {
60
- count?: number;
61
- page?: number;
62
- offset?: number;
63
- }) => ClinicalBundleQueryBuilder;
64
- getEntries: (resourceTypes?: readonly string[]) => FhirBundleEntryLike[];
65
- getResources: (resourceType?: string) => FhirResourceLike[];
66
- getAllergies: (query?: {
67
- clinicalStatus?: readonly string[];
68
- verificationStatus?: readonly string[];
69
- criticality?: readonly string[];
70
- }) => FhirBundleEntryLike[];
71
- getConditions: (query?: {
72
- clinicalStatus?: readonly string[];
73
- verificationStatus?: readonly string[];
74
- severity?: readonly string[];
75
- }) => FhirBundleEntryLike[];
76
- getMedications: (query?: {
77
- status?: readonly string[];
78
- }) => FhirBundleEntryLike[];
79
- getVitalSigns: (query?: {
80
- code?: readonly string[];
81
- }) => FhirBundleEntryLike[];
82
- getSectionCounts: () => FhirDocumentSectionCounts;
83
- }>;
84
- /**
85
- * Builds one reusable high-level clinical response view over the bundle body.
86
- */
87
21
  declare function buildProcessedClinicalBundleResponse(body: unknown): ProcessedClinicalBundleResponse;
88
22
  declare class ProfessionalSubjectIpsWorkspace {
89
23
  private readonly subjectId;
@@ -1,6 +1,5 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
- import { CommunicationAttachedBundleSession, IndividualBundleVault, VaultMemRepository, buildVitalSignObservationClaims, buildBundleDocumentFromClaims, createConsentAccessEditor, createInvoiceBundleEditor, createSummaryOperationRequestParameters, createSummaryOperationRequestParametersResource, createSummaryOperationRequestReferencePath, extractResourceMetaClaimsFromBundle, readEmployeeSearchResults, summarizeClinicalBundle, summarizeLicenseListRecords, toClinicalResourceExpandedViews, } from 'gdc-common-utils-ts';
3
- import { createFhirDocumentFacade, } from 'gdc-sdk-core-ts';
2
+ import { CommunicationAttachedBundleSession, IndividualBundleVault, VaultMemRepository, buildVitalSignObservationClaims, createConsentAccessEditor, createInvoiceBundleEditor, createSummaryOperationRequestParameters, createSummaryOperationRequestParametersResource, createSummaryOperationRequestReferencePath, readEmployeeSearchResults, summarizeClinicalBundle, summarizeLicenseListRecords, toClinicalResourceExpandedViews, } from 'gdc-common-utils-ts';
4
3
  function normalizeSectionList(sectionList) {
5
4
  return Array.from(new Set((sectionList || []).map((item) => String(item || '').trim()).filter(Boolean)));
6
5
  }
@@ -22,121 +21,19 @@ function buildClinicalSectionCounts(views) {
22
21
  }
23
22
  return out;
24
23
  }
25
- /**
26
- * Normalizes the shared claims-first bundle examples into one FHIR document
27
- * bundle so the section-aware facade can read `Composition.section` without
28
- * teaching raw claim plumbing to SDK consumers.
29
- */
30
- function normalizeClinicalDocumentBundle(bundle) {
31
- if (bundle?.resourceType === 'Bundle' && Array.isArray(bundle?.entry)) {
32
- return bundle;
33
- }
34
- const claimsList = extractResourceMetaClaimsFromBundle(bundle).map((item) => item.claims);
35
- if (claimsList.length === 0) {
36
- return bundle;
37
- }
38
- const subjectDid = claimsList
39
- .map((claims) => {
40
- const subjectKeys = Object.keys(claims).filter((key) => String(key || '').toLowerCase().endsWith('.subject'));
41
- for (const key of subjectKeys) {
42
- const value = claims[key];
43
- if (typeof value === 'string' && value.trim())
44
- return value.trim();
45
- }
46
- return '';
47
- })
48
- .find(Boolean);
49
- return buildBundleDocumentFromClaims({
50
- claimsList,
51
- ...(subjectDid ? { subjectDid } : {}),
52
- });
53
- }
54
- /**
55
- * Creates one immutable high-level reader that keeps section/date/text/paging
56
- * filters chainable across generic and family-specific clinical queries.
57
- */
58
- function createClinicalBundleQueryBuilder(facade, state = {}) {
59
- const next = (patch) => createClinicalBundleQueryBuilder(facade, {
60
- ...state,
61
- ...patch,
62
- });
63
- return {
64
- allSections: () => next({ sections: [] }),
65
- inSections: (sections) => next({ sections: sections ? [...sections] : [] }),
66
- between: (start, end) => next({ start, end }),
67
- matchingText: (searchText) => next({ searchText }),
68
- paginate: (input) => next({
69
- count: input?.count,
70
- page: input?.page,
71
- offset: input?.offset,
72
- }),
73
- getEntries: (resourceTypes) => facade.getEntries({
74
- sections: state.sections,
75
- resourceTypes,
76
- start: state.start,
77
- end: state.end,
78
- searchText: state.searchText,
79
- count: state.count,
80
- page: state.page,
81
- offset: state.offset,
82
- }),
83
- getResources: (resourceType) => facade.getResources({
84
- ...state,
85
- ...(resourceType ? { resourceType } : {}),
86
- }),
87
- getAllergies: (query = {}) => facade.getAllergies({
88
- ...state,
89
- ...query,
90
- }),
91
- getConditions: (query = {}) => facade.getConditions({
92
- ...state,
93
- ...query,
94
- }),
95
- getMedications: (query = {}) => facade.getMedications({
96
- ...state,
97
- ...query,
98
- }),
99
- getVitalSigns: (query = {}) => facade.getVitalSigns({
100
- ...state,
101
- ...query,
102
- }),
103
- getSectionCounts: () => facade.getSectionCounts({ sections: state.sections }),
104
- };
105
- }
106
- /**
107
- * Builds one reusable high-level clinical response view over the bundle body.
108
- */
109
24
  function buildProcessedClinicalBundleResponse(body) {
110
25
  const bundle = unwrapBody(body);
111
26
  const views = toClinicalResourceExpandedViews(bundle);
112
27
  const summary = summarizeClinicalBundle(bundle);
113
- const documentFacade = createFhirDocumentFacade(normalizeClinicalDocumentBundle(bundle));
114
- const reader = createClinicalBundleQueryBuilder(documentFacade);
115
- const sectionCounts = documentFacade.getSectionCounts();
116
- const sectionSummary = documentFacade.getSectionSummary();
117
28
  return {
118
29
  totalErrors: 0,
119
30
  totalSections: Object.keys(buildClinicalSectionCounts(views)).length,
120
31
  totalResources: summary.totalEntries,
121
32
  totalResourcesInSection: buildClinicalSectionCounts(views),
33
+ totalNarratives: summary.xhtmlEntries,
34
+ totalNotes: summary.notedEntries,
122
35
  summary,
123
36
  views,
124
- sectionCounts,
125
- sectionSummary,
126
- getSections: () => documentFacade.getSections(),
127
- getEntries: (input) => documentFacade.getEntries(input),
128
- getResources: (query) => documentFacade.getResources(query),
129
- getByDates: (query, start, end) => documentFacade.getByDates(query, start, end),
130
- getSectionCounts: (input) => documentFacade.getSectionCounts(input),
131
- getSectionSummary: (input) => documentFacade.getSectionSummary(input),
132
- getLocalTextAndIntDisplay: (resourceOrEntry) => documentFacade.getLocalTextAndIntDisplay(resourceOrEntry),
133
- getXhtmlOrDerived: (resourceOrEntry) => documentFacade.getXhtmlOrDerived(resourceOrEntry),
134
- getNarrative: (resourceOrEntry) => documentFacade.getNarrative(resourceOrEntry),
135
- getAllergies: (query = {}) => documentFacade.getAllergies(query),
136
- getConditions: (query = {}) => documentFacade.getConditions(query),
137
- getMedications: (query = {}) => documentFacade.getMedications(query),
138
- getVitalSigns: (query = {}) => documentFacade.getVitalSigns(query),
139
- reader,
140
37
  };
141
38
  }
142
39
  class ProfessionalSubjectIpsWorkspace {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-sdk-node-ts",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
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.",
@@ -25,14 +25,15 @@
25
25
  "test:e2e:live-gw:legacy-fhir": "npm run build && RUN_LIVE_GW_E2E=1 LIVE_GW_E2E_TRANSPORT=legacy-fhir node --test tests/live-gw-node-runtime.e2e.test.mjs",
26
26
  "test:e2e:live-gw:all": "npm run build && RUN_LIVE_GW_E2E=1 LIVE_GW_E2E_TRANSPORT=all node --test tests/live-gw-node-runtime.e2e.test.mjs",
27
27
  "test:e2e:live-gw:host-transaction:clean": "bash ./scripts/run-live-gw-host-transaction-clean.sh",
28
+ "test:e2e:live-gw:issue-recovery": "npm run build && node ./scripts/run-live-organization-issue-recovery.mjs",
28
29
  "test:e2e:live-profile-runtime:individual": "npm run build && RUN_LIVE_PROFILE_RUNTIME_E2E=1 node --test tests/live-profile-runtime-individual.e2e.test.mjs",
29
30
  "test:e2e:live-profile-runtime:professional": "npm run build && RUN_LIVE_PROFILE_RUNTIME_PROFESSIONAL_E2E=1 node --test tests/live-profile-runtime-professional.e2e.test.mjs",
30
31
  "test:e2e:live-dialogue:consent-professional": "npm run build && RUN_LIVE_DIALOGUE_CONSENT_PROFESSIONAL_E2E=1 node --test tests/live-dialogue-consent-professional-access.e2e.test.mjs",
31
32
  "test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
32
33
  },
33
34
  "dependencies": {
34
- "gdc-common-utils-ts": "^2.0.7",
35
- "gdc-sdk-core-ts": "^2.0.6"
35
+ "gdc-common-utils-ts": "^2.0.11",
36
+ "gdc-sdk-core-ts": "^2.0.8"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@types/node": "^20.14.10",