npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gdc-sdk-node-ts

v2.6.2

Published

Next-generation Node runtime package for the GDC SDK family

Readme

gdc-sdk-node-ts

See ARCHITECTURE.md and CONTRIBUTING.md before adding node runtime facades, execution adapters, or orchestration tests.

Short rule:

  • 101 tests must read like executable tutorials
  • gdc-common-utils-ts owns the canonical step-by-step editors/readers, shared fixtures, and reusable payload examples
  • gdc-sdk-node-ts starts after that shared authoring step and at ProfileRuntime -> loadProfile(...) -> workspace/session -> actor facade in node runtime tests

Node runtime package for consuming the shared GDC SDK contracts against real gateway backends.

Use this package when your backend needs to:

  • call GW APIs
  • submit/poll async operations
  • orchestrate onboarding, consent, communication, and search flows
  • consume the shared relationship invitation/acceptance contracts from gdc-sdk-core-ts

This package is for runtime execution. It is not the place where the canonical business contract is defined.

Important test-harness boundary:

  • gdc-sdk-node-ts is not the product BFF
  • the live E2E suite simulates a controlled virtual API with a BFF-like role only to validate GW CORE lifecycles end to end
  • the current live suites run with the future user job manager queue disabled, so every high-level call goes directly through that controlled virtual API
  • app-side job queues, offline retry, local vault/read models, and the future user job manager are separate follow-up concerns

Important live-run rule:

  • the canonical live E2E result must come from the user's real terminal/TTY
  • do not assume an AI agent sandbox has equivalent localhost, Docker, DNS, or GCP connectivity
  • if sandboxed runs disagree with the user's terminal, trust the user's terminal for live GW validation

Required live validation order:

  1. local process E2E from real TTY
  2. local Docker image/container E2E
  3. staging E2E
  4. production image/deploy only after staging is green

Architectural rule:

  • shared contracts and actor boundaries come from gdc-sdk-core-ts
  • this package executes those flows against GW
  • this package should not widen an actor facade just because the runtime client happens to expose the underlying method

Start Here

If you are integrating this package for the first time, open these in order:

  1. gdc-sdk-core-ts/docs/101-SDK_PACKAGE_BOUNDARIES.md Why core, node, and front are separate packages, what belongs in each one, and why actor-scoped facades must stay aligned across runtimes.
  2. docs/101-SDK_END_TO_END.md Ordered onboarding guide with end-to-end journeys, copy/paste snippets, and the recommended reading path for new backend integrators.
  3. docs/101-PROFESSIONAL-CONSENT-SMART.md Canonical professional DID, consent, VP and SMART flow without literal sections or caller-built audience URLs.
  4. docs/101-AUTHORIZED_SUBJECT_DIRECTORY.md Signed OpenID account discovery of already-authorized subjects without treating the account token as VP, SMART or wallet proof.
  5. docs/101-SDK_INTEGRATION.md Real backend setup plus the public runtime entrypoints: HostOnboardingSdk, OrganizationControllerSdk, IndividualControllerSdk, ProfessionalSdk, route-context usage, and the canonical ProfileRuntime -> loadProfile(...) -> workspace/session -> actor facade -> submit/poll shape.
  6. tests/101-live-full-cycle-bff-runtime.e2e.test.mjs Canonical live backend/BFF walkthrough on a fresh local GW lifecycle: host/tenant activation, employee provisioning, individual bootstrap, consent grant, professional SMART token, clinical read, and final cleanup.
  7. docs/101-LIVE_GW_LOCAL.md Exact TTY/local/Docker commands for running the SDK against a real local GW CORE, including tenant bootstrap and employee-seat setup.
  8. docs/101-DISCOVERY.md Node/BFF dataspace discovery, hosting-operator resolution, provider resolution, and the correct integration boundary for fallback and cache.
  9. gdc-sdk-core-ts/docs/101-SDK_FLOWS.md Actor split and business-flow map across organization, individual, permissions, invitation, import, and SMART flows.
  10. gwtemplate-node-ts/docs/PORTAL_API_TO_GW_CORE.md Canonical portal/BFF functional map over GW CORE, including the domain split between employees, related persons, members, and consents.
  11. gdc-common-utils-ts/src/examples/ Shared payload values used by the docs and tests.
  12. gdc-common-utils-ts/docs/101-LIFECYCLE.md Canonical enable/disable/delete semantics and copy/paste placeholders.
  13. tests/101-backend-profile-runtime.test.mjs Technical runtime slice for profile/device/session orchestration internals. Read this after the public actor SDK guides, not before them.
  14. tests/101-individual-controller-backend-runtime.test.mjs Technical wrapper slice around the generic profile runtime. This is not the main onboarding path for new integrators.
  15. docs/V2_INDIVIDUAL_REGISTRATION_RECONCILIATION.md Historical reconciliation note for the current CORE registration baseline.
  16. docs/NEXT_STEPS.md Follow-up scope after GW CORE live validation, including the future user job manager boundary.

If you need the shortest path:

Current live teaching target:

Executable Usage Examples

Open these tests when you want to see exact method calls and exact inputs:

Live GW CORE Flow

Use tests/101-live-full-cycle-bff-runtime.e2e.test.mjs as the canonical live 101 for backend/BFF integrators.

Use tests/live-gw-node-runtime.e2e.test.mjs as the broader runtime-regression suite.

Before running that suite, read:

Execution requirement:

  • run the live suite from a real user terminal/TTY
  • if an AI agent is assisting, it should prefer a long-lived TTY process and avoid treating sandbox-local connectivity failures as product failures

Teaching rule:

  • defaults come from gdc-common-utils-ts/examples
  • override with env vars only when your tenant, bearer, or route is different
  • local GW default is http://127.0.0.1:3000
  • Docker-exposed GW can be overridden with BASE_URL=http://127.0.0.1:8000
  • LIVE_GW_E2E_EXECUTION_MODE=direct is the current and only supported mode for live validation; queued app-side job management is a later phase

Current live 101 flow covered by the test suite:

  1. activate one hosted tenant / legal organization
  2. provision one professional employee through the organization controller
  3. load the individual-controller profile and bootstrap one hosted individual
  4. confirm the returned order and verify the invoice bundle projection
  5. ingest one IPS/clinical Communication whose attached payload is the canonical document Bundle with Composition first entry
  6. grant professional consent for one patient-summary section
  7. load the professional profile and request one SMART token
  8. read the allowed IPS bundle as the professional actor
  9. clean up consent, individual, employee, tenant, and host state

Run the main live 101:

npm run test:e2e:live-full-cycle

What is still not fully covered as one single root lifecycle:

  • initial organization license listing
  • extra-seat activation after the portal-side fictitious payment confirmation
  • relisting licenses after seat activation
  • one employee bundle with employee A and employee B
  • selective disable/purge validation across both employees
  • consent escalation from partial IPS access to broader IPS access
  • final cleanup of consent, individual, remaining employees, and tenant

Current invoice/readback behavior:

  • both host and individual Order/_batch-response flows now return the flat compatibility claims and an embedded invoice Bundle
  • the invoice bundle contains one FHIR Invoice, one PDF DocumentReference, and one structured JSON/XML DocumentReference
  • live suites can read that bundle back through the same high-level response body that the virtual API exposes to the simulated front

Current runtime boundary:

  • OrganizationControllerSdk.confirmOrganizationLicenseOrder(...) now uses the public host Order/_batch route used by GW CORE for portal-managed post-payment seat activation
  • that route is commercial routing only: iss remains the exact controller DID registered by tenant DCR, aud remains the tenant id, and GW resolves its JWS/JWE key identifiers from the tenant rather than the host
  • applications use the high-level SDK method and configured transport profile; they do not populate meta.jws, move public keys, or construct DIDComm by hand
  • the long root lifecycle is still not fully closed because the suite does not yet orchestrate the whole license list -> pay -> confirm -> relist -> two employees -> selective purge -> cleanup dialogue as one single test

The exact pending release-readiness checklist lives in:

Optional live lifecycle extension:

  • set RUN_LIVE_GW_E2E_INDIVIDUAL_LIFECYCLE=1 to extend the same suite with disableIndividual(...) + purgeIndividual(...) against the real gwtemplate-node-ts runtime contract
  • this extra block is intentionally separate from the default happy path because it changes lifecycle state and should only run when that tenant/test subject is disposable

Shared example source of truth:

The two medication defaults used by the live test are intentionally reusable:

  • Ibuprofen 400 mg
  • Paracetamol 600 mg
  • both every 8 hours
  • both PRN / dosage-asneeded = true
  • note text keeps the 4 hour gap in English

Run the full live runtime baseline:

npm run test:e2e:live-gw

Select one live transport profile from the same Node entrypoint:

npm run test:e2e:live-gw:didcomm-plain
npm run test:e2e:live-gw:legacy-fhir
npm run test:e2e:live-gw:all

Profile note:

  • didcomm-plain is the explicit demo-only live profile
  • legacy-fhir exercises raw application/fhir+json async batch submission for org.hl7.fhir.*
  • all runs every implemented profile from the same suite file
  • each NodeHttpClient selects one profile at construction; every facade operation and poll uses it and cannot override or downgrade it

Run the IPS ingestion/search branch as well:

RUN_LIVE_GW_E2E_IPS_INGESTION=1 \
npm run test:e2e:live-gw

Implementation note:

  • the beginner search story stays on FHIR parameters such as Composition.section
  • gdc-sdk-node-ts submits the search request through the runtime facade
  • GW CORE resolves it internally from indexed subject sections and returns the consolidated IPS bundle document

Common overrides:

BASE_URL=http://127.0.0.1:3000 \
AUTH_BEARER=... \
TENANT_ID=VATES-B00112233 \
TENANT_ROUTE_ID=acme-live \
JURISDICTION=ES \
SECTOR=health-care \
SUBJECT_DID=did:web:api.acme.org:individual:123 \
RUN_LIVE_GW_E2E_IPS_INGESTION=1 \
LIVE_GW_NODE_E2E_DEBUG=1 \
npm run test:e2e:live-gw

Docker-exposed GW example:

BASE_URL=http://127.0.0.1:8000 \
RUN_LIVE_GW_E2E_IPS_INGESTION=1 \
npm run test:e2e:live-gw

Documentation consistency rule for this repo family:

  • scripts, README examples, Swagger examples, and internal tests must reuse the same example data and flow order
  • if a new request/response example is added, add it first to gdc-common-utils-ts/examples and consume it from there instead of re-hardcoding values locally

Dataspace Discovery Quick Map

Use the Node discovery layer when your backend or BFF needs to:

  • start from preloaded hosting-operator semantics
  • fetch the canonical host-scoped /<hosting-operator-network-context>/.well-known/dspace-version entrypoint
  • derive the participant-scoped /dsp/catalog/dcat.json artifact
  • return normalized provider/operator matches to portal or app backends

Primary references:

Architecture note:

  • the reusable discovery resolver logic lives in gdc-sdk-core-ts
  • gdc-sdk-node-ts re-exports it and supplies the Node runtime surface

Copy/paste starting point:

import { createDefaultFirstDataspaceDiscovery } from 'gdc-sdk-node-ts';
import { DataspaceSectors } from 'gdc-common-utils-ts';
import { HostNetworkTypes } from 'gdc-common-utils-ts/constants/network';

const discovery = createDefaultFirstDataspaceDiscovery({
  version: 'v1',
  networkType: HostNetworkTypes.Test,
  defaults,
});

const providers = await discovery.getIndexProviders({
  sector: DataspaceSectors.AnimalCare,
  jurisdiction: 'ES',
});

Actor Split And Runtime Scope

This package must be understandable from the same actor split used by the shared contracts:

  • organization controller
  • organization employee / professional member
  • individual controller
  • individual member / self
  • related person
  • professional with consented access

The Node runtime layer is where those shared flows are executed against GW. That includes organization onboarding, employee creation, individual bootstrap, permission grants, RelatedPerson upserts, SMART token requests, and clinical data ingestion/search.

Flow Families

  • organization activation and order/offer confirmation
  • employee creation and employee activation
  • individual organization start and order confirmation
  • related person upsert
  • professional access grant
  • invitation / OTP / relationship PIN runtime wiring
  • permission-request Communication
  • composed professional request submission and subject decision correlation
  • communication ingestion and search
  • SMART token retrieval

ProfessionalSdk.requestProfessionalAccess(...) is the bootstrap operation for the inverse flow: it persists a subject-scoped permission-request Communication using the configured HTTP bearer and transport profile, without requiring SMART. The subject later answers through respondToProfessionalAccessRequest(...); that facade uses the normal Consent grant/deny operation and retains the original Communication identifier/thread. listProfessionalAccessRequests(...) provides the subject/requester inbox view, and GatewayActiveConsentProvider supplies evaluateRequestedAccess(...) from the GW Consent source of truth rather than a parallel application table.

Main Flows

1. Controller invites a related person or professional

Typical backend sequence:

  1. build shared invitation payload with gdc-sdk-core-ts
  2. send it through the node runtime client or your backend adapter
  3. persist or return the invitation state to portal/app

What matters here:

  • this package executes the runtime call
  • gdc-sdk-core-ts defines the payload shape
  • callers should not hardcode route families in app code

2. Invitee accepts the relationship

Typical backend sequence:

  1. start OTP challenge
  2. confirm OTP
  3. set relationship PIN if required
  4. activate the relationship channel

Shared contract builders come from gdc-sdk-core-ts; this package is where a Node backend wires them to real runtime operations.

3. Consent-aware communication and search

Use this package when your backend needs to:

  • ingest Communication
  • search clinical bundles
  • request SMART tokens
  • grant access
  • create or update RelatedPerson

What This Package Owns

  • Node runtime client
  • submit/poll orchestration
  • actor-scoped node sessions
  • backend-facing orchestration helpers

What This Package Does Not Own

  • the canonical invitation/OTP/PIN contract
  • application-specific reminder runtime semantics
  • frontend session UX

Those belong to:

  • gdc-sdk-core-ts for shared contracts
  • runtime extensions for product-specific behavior
  • gdc-sdk-front-ts for frontend-facing consumption

Minimal Examples

Use shared invitation contract from Node

import {
  createRelationshipChannelInvitationInput,
  RelationshipAccessActorKinds,
  RelationshipEnrollmentChannels,
  type RelationshipChannelInvitationInput,
} from 'gdc-sdk-core-ts';
import {
  buildIndividualDidWeb,
  HealthcareActorRoles,
  HealthcareConsentPurposes,
} from 'gdc-common-utils-ts';

const tenantId = 'acme-id';
const jurisdiction = 'ES';
const sector = 'health-care';
const providerOrganizationDid = subjectProfile.organizationDid;
const subjectLocalId = subjectProfile.subjectId;
const subjectId = buildIndividualDidWeb({
  organizationDidWeb: providerOrganizationDid,
  subjectId: subjectLocalId,
});
const professionalEmail = invitedProfessional.email;

const invitationInput: RelationshipChannelInvitationInput = {
  tenantId,
  jurisdiction,
  sector,
  subjectId,
  subjectKind: 'person',
  actorKind: RelationshipAccessActorKinds.Professional,
  actorIdentifier: professionalEmail,
  actorRole: HealthcareActorRoles.Physician,
  deliveryChannel: RelationshipEnrollmentChannels.Email,
  deliveryTarget: professionalEmail,
  purpose: HealthcareConsentPurposes.Treatment,
  relationshipLabel: 'primary-physician',
  phonePinOptional: false,
};

const invitation = createRelationshipChannelInvitationInput(invitationInput);

The backend should obtain those variables from:

  • tenant route selection
  • target subject identifier
  • invited actor identity
  • selected enrollment channel
  • intended purpose/relationship label

Request SMART token and search bundle

import { NodeHttpClient } from 'gdc-sdk-node-ts';
import {
  EXAMPLE_LATEST_IPS_SEARCH_INPUT,
} from 'gdc-common-utils-ts/examples/individual-controller';
import { HealthcareBasicSections } from 'gdc-common-utils-ts/constants/healthcare';
import { buildSmartCompositionReadScope } from 'gdc-common-utils-ts/utils/smart-scope';

const client = new NodeHttpClient({ baseUrl: process.env.BASE_URL! });

const subjectDid = EXAMPLE_LATEST_IPS_SEARCH_INPUT.subject;

const token = await client.requestSmartToken({
  ctx,
  actorDid: 'did:web:doctor.example.org:employee:001',
  subjectDid,
  scopes: [
    buildSmartCompositionReadScope({
      subjectDid,
      sections: HealthcareBasicSections.PatientSummaryDocument.claim,
    }),
  ],
  idToken: '...',
});

const result = await client.searchClinicalBundle(ctx, {
  subject: subjectDid,
});

Teaching rule:

  • start with the composition read scope when the actor only needs subject-scoped read access
  • add SmartGatewayScopesFhirR4.ConsentCruds only if the backend also needs consent management operations

Shared Contract Sources

Reusable payload examples:

Local terminology route for Next.js

LocalTerminologyBffService keeps complete JSON catalogs in the application BFF and returns only matching options:

const terminology = new LocalTerminologyBffService(catalogs);

export async function GET(request: Request) {
  const url = new URL(request.url);
  return Response.json(terminology.search({
    text: url.searchParams.get('text') || '',
    language: url.searchParams.get('language') || 'en',
    jurisdiction: url.searchParams.get('jurisdiction') || undefined,
    systems: url.searchParams.getAll('system'),
    limit: 20,
  }));
}

The application validates which systems are allowed for the specific coded form field. This MVP service performs no external terminology request.

API Index

Full Public Surface

This package exports the full gdc-sdk-core-ts surface plus the Node runtime modules below.

  • src/runtime-contracts.ts
    • types/constants: LegacyNodeSourcePackage, NodeRuntimeMode, NodeInteropMode, TenantContext, NodeOperatorContext, NodeFetchLike, NodeRuntimeConfig, NodePackageStatus, GDC_SDK_NODE_STATUS
  • src/identity-bootstrap.ts
    • function: initializeCommunicationIdentity(...)
  • src/async-polling.ts
    • types: AcceptedPollResponse
    • function: pollUntilCompleteWithMethod(...)
  • src/poll-options.ts
    • re-export: resolvePollOptionsFromSeconds(...)
  • src/host-onboarding.ts
    • types: HostRouteContext, LegalOrganizationOrderInput
    • function: confirmLegalOrganizationOrderWithDeps(...)
  • src/individual-start.ts
    • types: IndividualOrganizationBootstrapInput, OfferPreview, IndividualOrganizationStartResult
    • function: startIndividualOrganizationWithDeps(...)
  • src/individual-onboarding.ts
    • types: RouteContext, IndividualOrganizationConfirmOrderInput
    • function: confirmIndividualOrganizationOrderWithDeps(...)
  • src/device-activation.ts
    • types: EmployeeDeviceActivationInput, EmployeeDeviceActivationRequestInput, EmployeeDeviceActivationResult
    • functions: activateEmployeeDeviceWithActivationCodeWithDeps(...), activateEmployeeDeviceWithActivationRequestWithDeps(...)
  • src/smart-token.ts
    • types: SmartTokenRequestInput, SmartTokenExchangeResult
    • function: requestSmartTokenWithDeps(...)
  • src/resource-operations.ts
    • types: OrganizationEmployeeCreationInput, IpsOrFhirImportInput, RelatedPersonUpsertInput, CommunicationIngestionInput, ClinicalDateRange, ClinicalBundleSearchInput, ConsentActorTargetInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, DigitalTwinSecondaryUseConsentInput, DigitalTwinSubjectLinkPurgeInput, SubjectConsentSearchInput
    • functions: createOrganizationEmployeeWithDeps(...), importIpsOrFhirAndUpdateIndexWithDeps(...), upsertRelatedPersonAndPollWithDeps(...), ingestCommunicationAndUpdateIndexWithDeps(...), searchClinicalBundleWithDeps(...), searchSubjectConsentsWithDeps(...), searchLatestIpsWithDeps(...), grantProfessionalAccessWithDeps(...), setDigitalTwinSecondaryUseConsentWithDeps(...), purgeDigitalTwinSubjectLinkWithDeps(...)
  • src/digital-twin.ts
    • types: DigitalTwinSearchInput, DigitalTwinSelectionInput, DigitalTwinResearchTag, DigitalTwinMaterializationInput
    • functions: searchDigitalTwinsWithDeps(...), saveDigitalTwinSelectionWithDeps(...), materializeDigitalTwinWithDeps(...)
  • src/orchestration/digital-twin-sdk.ts
    • class: DigitalTwinSdk (requestSmartToken, search, saveSelection, searchSelections, materialize); both searches expose ResearchSubject through FHIR Parameters, with its canonical internal Composition available as composition
  • src/session.ts
    • types: NodeCapability, NodeActorSessionContext, ActorSessionContext
    • classes: ActorSession, NodeActorSession
  • src/node-runtime-client.ts
    • types: HttpRuntimeClientOptions, NodeHttpClientOptions
    • classes: HttpRuntimeClient, NodeHttpClient
  • src/gdc-session-bridge.ts
    • functions: createNodeActorSessionsFromFacades(...), createNodeActorSessionFromFacade(...), createNodeActorSessionsFromDescriptor(...), createNodeActorSessionFromDescriptor(...), createActorSessionsFromFacades(...), createActorSessionFromFacade(...), createActorSessionsFromDescriptor(...), createActorSessionFromDescriptor(...)
  • src/orchestration/client-port.ts
    • types: RuntimeClient, NodeRuntimeClient
    • functions: requireClientMethod(...), submitAndPollWithMethods(...), canClientSubmitAndPoll(...), submitAndPollWithClient(...)
  • src/orchestration/host-onboarding-sdk.ts
    • class: HostOnboardingSdk
  • src/orchestration/organization-controller-sdk.ts
    • class: OrganizationControllerSdk
  • src/orchestration/organization-employee-sdk.ts
    • class: OrganizationEmployeeSdk
  • src/orchestration/individual-controller-sdk.ts
    • class: IndividualControllerSdk
  • src/orchestration/individual-member-sdk.ts
    • class: IndividualMemberSdk
  • src/orchestration/personal-sdk.ts
    • class: PersonalSdk
  • src/orchestration/professional-sdk.ts
    • class: ProfessionalSdk
  • src/legacy-compat.ts
    • compatibility aliases for simplified helpers, runtime classes, and legacy names such as GdcNodeActorSession and GdcNodeHttpClient

Re-exported shared helpers from gdc-sdk-core-ts

  • consent access helpers
  • relationship invitation/acceptance builders
  • communication/document builders
  • draft/outbox helpers
  • document facade helpers
  • vital-sign helpers

Node runtime client

Runtime configuration

Low-level orchestration helpers

Documentation Rule

  • README explains backend-facing flows first.
  • Shared contract shapes must be documented in gdc-sdk-core-ts, not duplicated here.
  • Route details and GW-specific behavior belong in runtime docs and JSDoc, not in app-facing examples.

Host Onboarding Runtime Flow

For legal-organization onboarding from a Node BFF/runtime, keep the host steps separate:

  1. new flow: Organization/_transaction
  2. legacy compatibility flow: ICA _verify -> Organization/_activate
  3. downstream business continuation: Order/_batch

Use OrganizationControllerSdk.submitLegalOrganizationVerificationTransaction(...) or NodeHttpClient.submitLegalOrganizationVerificationTransaction(...) for step 1.

Existing-tenant recovery/reverification:

  • use OrganizationControllerSdk.submitLegalOrganizationCredentialReissuance(...) or NodeHttpClient.submitLegalOrganizationCredentialReissuance(...) to refresh the ICA-backed verification for an already registered tenant without creating a new Offer
  • inside the terminal decoded GW Bundle, the canonical polled response keeps all deduplicated ICA-issued credentials in body.data[0].vc[] and the complete ICA payload in body.data[0].resource.icaResponse; SDK callers receive that terminal HTTP payload as SubmitAndPollResult.poll.body
  • separately, GW returns the controller License activation code in body.data[0].meta.claims['org.schema.IndividualProduct.serialNumber']
  • Organization/_issue is not License/_issue: the activation code is not a VC and License:Issued is not the canonical response entry type for this route
  • then continue with the existing helper chain: recoverOrganizationControllerWithCredentialReissuanceWithDeps(...) or directly _exchange -> _dcr
  • submitLegalOrganizationIssue(...) remains only as a deprecated alias named after the underlying Organization/_issue route
  • for a reproducible local/staging runtime check, use: npm run test:e2e:live-gw:issue-recovery
  • for the deterministic controller-lifecycle contract that also proves post-registration seat preservation and final tenant teardown, use: docs/101-ORGANIZATION_CONTROLLER_LIFECYCLE.md

Rules:

  • _transaction and _activate are different flows
  • transport/runtime communication keys stay outside the business payload
  • controller binding key stays in body.data[].resource.controller.*
  • do not mix this path with requestIcaEnrollment or Fabric

Legacy compatibility coverage in the live suite:

  • the canonical test is LIVE professional lifecycle on GW
  • set RUN_LIVE_GW_E2E_HOST_VERIFICATION_TRANSACTION=1 to exercise:
    • Organization/_transaction -> Order/_batch
  • set RUN_LIVE_GW_E2E_HOST_VERIFICATION_TRANSACTION=0 to exercise the older:
    • ICA _verify -> Organization/_activate -> Order/_batch

Dedicated legacy live command:

cd /Users/fernando/GITS/gdc-workspace/gdc-sdk-node-ts
RUN_LIVE_GW_E2E=1 \
RUN_LIVE_GW_E2E_ACTOR_CHAIN=1 \
RUN_LIVE_GW_E2E_HOST_VERIFICATION_TRANSACTION=0 \
LIVE_GW_E2E_SUITE=professional \
node --test tests/live-gw-node-runtime.e2e.test.mjs

Live E2E legal PDF source:

  • local file: set LIVE_GW_HOST_VERIFICATION_PDF_PATH=/abs/path/file.pdf
  • public URL: set LIVE_GW_HOST_VERIFICATION_PDF_URL=https://.../file.pdf
  • if both are present, the live suite prefers LIVE_GW_HOST_VERIFICATION_PDF_URL
  • Dropbox-style links are normalized to dl=1 direct-download mode automatically

Recovery-specific live rule:

  • Organization/_issue can succeed and still be followed by _exchange failure if the controller id_token is invalid, expired or does not prove the expected actor/contact
  • the current GW _exchange path expects a valid IdP token but takes tenant authority from the already validated request route
  • a custom tenant_id claim is optional; when present it must match the route and cannot select another tenant
  • a failed poll OperationOutcome is surfaced before the helper checks for initial_access_token, preserving the real GW diagnostic
  • the bundled recovery runner generates a syntactically valid demo JWT if CONTROLLER_ID_TOKEN is not provided, but production/staging should use a real IdP-issued token

Minimal live recovery command:

PDF_PATH=/Users/fernando/GITS/gdc-workspace/examples/TEST-A4-Antifraud.pdf \
BASE_URL=http://127.0.0.1:3000 \
TENANT_ID=acme-id \
npm run test:e2e:live-gw:issue-recovery