gdc-sdk-node-ts
gdc-sdk-node-ts
Development and releases follow the mandatory
local-first TDD and release contract.
See ARCHITECTURE.md and CONTRIBUTING.md before adding node runtime facades, execution adapters, or orchestration tests.
Short rule:
101tests must read like executable tutorialsgdc-common-utils-tsowns the canonical step-by-step editors/readers, shared fixtures, and reusable payload examplesgdc-sdk-node-tsstarts after that shared authoring step and atProfileRuntime -> loadProfile(...) -> workspace/session -> actor facadein 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-tsis not the product BFF- the live E2E suite simulates a controlled
virtual APIwith a BFF-like role only to validate GW CORE lifecycles end to end - the current live suites run with the future
user job managerqueue disabled, so every high-level call goes directly through that controlledvirtual 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:
- local process E2E from real TTY
- local Docker image/container E2E
- staging E2E
- 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:
- gdc-sdk-core-ts/docs/101-SDK_PACKAGE_BOUNDARIES.md
Why
core,node, andfrontare separate packages, what belongs in each one, and why actor-scoped facades must stay aligned across runtimes. - 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.
- docs/101-PDQM_PATIENT_MATCH.md
Resolve only
indexProviderDidin Fabric, then use the high-level Node SDK to call IHE PDQmPOST Patient/$matchwith one FHIRParametersrequest. - docs/101-HIGH_LEVEL_CLINICAL_PROFILE_WRITES.md Focused subset of the end-to-end guide: load a professional or individual member/controller profile, create or import clinical data in the selected index provider, and verify it through an authoritative summary readback.
- docs/101-PROFESSIONAL-CONSENT-SMART.md Canonical professional DID, consent, VP and SMART flow without literal sections or caller-built audience URLs.
- 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.
- docs/101-SDK_INTEGRATION.md
Real backend setup plus the public runtime entrypoints:
HostOnboardingSdk,OrganizationControllerSdk,IndividualControllerSdk,ProfessionalSdk, route-context usage, and the canonicalProfileRuntime -> loadProfile(...) -> workspace/session -> actor facade -> submit/pollshape. - 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.
- 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.
- docs/101-DISCOVERY.md Node/BFF dataspace discovery, hosting-operator resolution, provider resolution, and the correct integration boundary for fallback and cache.
- gdc-sdk-core-ts/docs/101-SDK_FLOWS.md Actor split and business-flow map across organization, individual, permissions, invitation, import, and SMART flows.
- 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, andconsents. - gdc-common-utils-ts/src/examples/ Shared payload values used by the docs and tests.
- gdc-common-utils-ts/docs/101-LIFECYCLE.md
Canonical
enable/disable/deletesemantics and copy/paste placeholders. - 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.
- 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.
- docs/V2_INDIVIDUAL_REGISTRATION_RECONCILIATION.md Historical reconciliation note for the current CORE registration baseline.
- 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:
- main onboarding guide: docs/101-SDK_END_TO_END.md
- wallet
context, stableruntimeIdand KMS-encrypted seed custody: docs/101-WALLET_CONTEXT_AND_KEY_CUSTODY.md - first public actor surfaces:
HostOnboardingSdk,OrganizationControllerSdk,IndividualControllerSdk,ProfessionalSdk - GW CORE app identity:
appIdmandatory,appVersionoptional with defaultv1.0 - backend technical identity:
initializeCommunicationIdentity(...)for the technical channel/runtime identity, not the legal organization id - runtime client:
NodeHttpClient - step-by-step runtime usage: docs/101-SDK_INTEGRATION.md
- dataspace discovery and fallback/cache boundary: docs/101-DISCOVERY.md
- strict local-first release order, zero-skip live evidence and the
from/iss/kid/SMARTsubplus native-FHIR boundary: docs/101-LIVE_GW_LOCAL.md
Current live teaching target:
- the main executable tutorial for integrators is now: tests/101-live-full-cycle-bff-runtime.e2e.test.mjs
- the standalone actor-profile suites still exist only as focused technical slices:
- the larger runtime suite remains the regression-oriented environment proof: tests/live-gw-node-runtime.e2e.test.mjs
Executable Usage Examples
Open these tests when you want to see exact method calls and exact inputs:
- tests/host-onboarding.test.mjs Organization activation and order confirmation.
- tests/individual-start.test.mjs Individual organization start flow.
- tests/individual-onboarding.test.mjs Individual order/offer confirmation flow.
- tests/device-activation.test.mjs Employee activation code and activation request flows.
- tests/resource-operations.test.mjs Related person upsert, communication ingestion, search, and access grants.
- tests/smart-token.test.mjs SMART token request flow.
- tests/live-gw-node-runtime.e2e.test.mjs End-to-end runtime wiring against a real GW environment.
- tests/101-live-full-cycle-bff-runtime.e2e.test.mjs
Single live BFF-oriented
101that chains tenant, professional, individual, consent, SMART, read, and cleanup in one executable conversation. - tests/live-profile-runtime-individual.e2e.test.mjs Standalone actor-profile E2E for the individual controller on an already operational tenant, including scenario-owned cleanup.
- tests/101-dataspace-resolver.test.mjs
Minimal
HttpDataspaceResolver101 with one host and one published provider. - tests/101-default-first-dataspace-discovery.test.mjs
Minimal
default-firstdiscovery with one host and one publishedIndexProvider.
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=directis 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:
- activate one hosted tenant / legal organization
- provision one professional employee through the organization controller
- load the individual-controller profile and bootstrap one hosted individual
- confirm the returned order and verify the invoice bundle projection
- ingest one IPS/clinical
Communicationwhose attached payload is the canonical documentBundlewithCompositionfirst entry - grant professional consent for one patient-summary section
- load the professional profile and request one SMART token
- read the allowed IPS bundle as the professional actor
- 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
Aand employeeB - 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-responseflows now return the flat compatibility claims and an embedded invoiceBundle - the invoice bundle contains one FHIR
Invoice, one PDFDocumentReference, and one structured JSON/XMLDocumentReference - 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 hostOrder/_batchroute used by GW CORE for portal-managed post-payment seat activation- that route is commercial routing only:
issremains the exact controller DID registered by tenant DCR,audremains 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 -> cleanupdialogue as one single test
The exact pending release-readiness checklist lives in:
Optional live lifecycle extension:
- set
RUN_LIVE_GW_E2E_INDIVIDUAL_LIFECYCLE=1to extend the same suite withdisableIndividual(...)+purgeIndividual(...)against the realgwtemplate-node-tsruntime 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:
- tenant/route/controller/professional defaults: gdc-common-utils-ts/src/examples/shared.ts
- live employee defaults: gdc-common-utils-ts/src/examples/organization-controller.ts
- live consent defaults: gdc-common-utils-ts/src/examples/individual-controller.ts
The two medication defaults used by the live test are intentionally reusable:
Ibuprofen 400 mgParacetamol 600 mg- both every
8hours - both
PRN/dosage-asneeded = true - note text keeps the
4hour 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-plainis the explicit demo-only live profilelegacy-fhirexercises rawapplication/fhir+jsonasync batch submission fororg.hl7.fhir.*allruns every implemented profile from the same suite file- each
NodeHttpClientselects 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-tssubmits 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/examplesand 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-versionentrypoint - derive the participant-scoped
/dsp/catalog/dcat.jsonartifact - return normalized provider/operator matches to portal or app backends
Primary references:
- docs/101-DISCOVERY.md
- tests/101-default-first-dataspace-discovery.test.mjs
- tests/101-dataspace-resolver.test.mjs
- tests/dataspace-resolver-advanced.test.mjs
- tests/dataspace-resolver.test.mjs
Architecture note:
- the reusable discovery resolver logic lives in
gdc-sdk-core-ts gdc-sdk-node-tsre-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 with an attached batch Bundle of normal
Consent.status = draft resources, using the configured HTTP bearer and
transport profile without requiring SMART. A draft is inbox evidence and
cannot authorize access. 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.
There is no AccessRequest FHIR resource or AccessRequest.* claim family.
The Communication itself uses the standard FHIR notification category;
permission-request is workflow meaning carried by the attached draft Consent,
not a custom category. See the
HL7 category value set,
HL7 v3-ActReason BTG code system,
and the IHE compound normal/break-glass Consent example.
Main Flows
1. Controller invites a related person or professional
Typical backend sequence:
- build shared invitation payload with
gdc-sdk-core-ts - send it through the node runtime client or your backend adapter
- persist or return the invitation state to portal/app
What matters here:
- this package executes the runtime call
gdc-sdk-core-tsdefines the payload shape- callers should not hardcode route families in app code
2. Invitee accepts the relationship
Typical backend sequence:
- start OTP challenge
- confirm OTP
- set relationship PIN if required
- 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-tsfor shared contracts- runtime extensions for product-specific behavior
gdc-sdk-front-tsfor frontend-facing consumption
Minimal Examples
Build the individual-controller DCR actor DID
The controller consumes the licensed member seat with role RESPRSN. A phone
or email stored on the Individual Organization remains contact data and does
not consume another seat. The role keeps its full type and value in protected
data; only the value is appended to the DID.
import { buildIndividualMemberDidWebFromPrivateIdentifiers } from 'gdc-sdk-node-ts';
const individualControllerDid = buildIndividualMemberDidWebFromPrivateIdentifiers({
providerDidWeb: 'did:web:host.example.org:health-care:organization:taxid:VATES-B00112233',
secureIdTypeIndividual: 'UUID',
privateIdValueIndividual: 'a87e5b15-aea4-4475-9c7c-40aa88354b6f',
secureIdTypeMember: 'EMAIL',
privateIdValueMember: 'controller@example.org',
roleType: 'http://terminology.hl7.org/CodeSystem/v3-RoleCode',
roleValue: 'RESPRSN',
});
// did:web:host.example.org:health-care:organization:taxid:VATES-B00112233:individual:multibase:zG9H82pae9SCXvec3D4YKqhX8bj8F1mRgzxMEdwXXonT7BWsvsUiP2u52sWQTeESpoMee:member:zG9DrMLpQW8eoCc9Ay9AFxuMGiswgJePpbUMz9svJCZ8tKjUd4xoExgCPA5jmHc6hPATJ:RESPRSN
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.ConsentCrudsonly if the backend also needs consent management operations
Shared Contract Sources
Reusable payload examples:
- gdc-common-utils-ts/src/examples/organization-controller.ts
- gdc-common-utils-ts/src/examples/individual-controller.ts
- gdc-common-utils-ts/src/examples/professional.ts
- gdc-common-utils-ts/src/examples/shared.ts
- gdc-common-utils-ts/src/examples/lifecycle.ts
- gdc-common-utils-ts/src/examples/api-flow-examples.ts
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.
Typed clinical detail editors
Applications can import the clinical editors from gdc-sdk-node-ts; they do
not need setClaim(...), private claim names or deep package imports for these
FHIR fields:
AllergyIntoleranceEntryEditor:setReactionManifestation(system, code),setReactionSeverity(...)and their matching getters. Reaction severity is distinct from future-riskcriticality.MedicationStatementEntryEditor:setEffectivePeriodStart(...),setEffectivePeriodEnd(...)and their matching getters.ImmunizationEntryEditor:setRoute(system, code),setSite(system, code)and their component/token getters.ObservationEntryEditor:setReferenceRangeText(...)andgetReferenceRangeText().
BundleEditor returns those typed editors through asAllergy(),
asMedicationStatement(), asImmunization() and asObservation().
All registered clinical editor families are exported from the SDK root. A
received Bundle stays read-only in BundleReader; call
reader.toBundleEditor() to create a detached clone before using typed
resource getters or setters. See
Clinical Bundle Getters And Setters 101
for the complete resource table and examples.
Governed ValueSet options through the terminology service
For creation controls, the authenticated BFF can resolve the ValueSet bound to a canonical flat claim without exposing its service token or terminology server to the browser:
import {
AllergyIntoleranceClaim,
MemoryTerminologyValueSetCache,
TerminologyServiceClient,
} from 'gdc-sdk-node-ts';
const terminology = new TerminologyServiceClient({
baseUrl: process.env.TERMINOLOGY_SERVICE_URL!,
serviceToken: process.env.TERMINOLOGY_SERVICE_TOKEN!,
cache: new MemoryTerminologyValueSetCache(),
});
const { document, cacheStatus } = await terminology.expandValueSetForClaim({
sector: 'health-care',
resourceType: 'AllergyIntolerance',
claim: AllergyIntoleranceClaim.Manifestation,
// Optional: language, jurisdiction, version, fhirVersion, offset and count.
});
Omission means English, international/default terminology, the latest installed terminology version and FHIR R4. Jurisdiction is a SNOMED edition selector; it does not become part of LOINC or WHO ATC identity. The cache key still separates language, SNOMED edition/version, claim and page.
MemoryTerminologyValueSetCache.prime(...) accepts immutable startup
snapshots. This permits an application to ship or load the English IPS page
from private storage and render it without a live terminology call. A local
language page is preferred when cached; if refresh fails, an English cached
page is returned with cacheStatus: 'english-fallback-cache'. Complete
catalogs and the service bearer credential remain server-side. A BFF may
replace the memory implementation with a GCS, Firestore or PostgreSQL adapter
implementing TerminologyValueSetCache; large complete catalogs should not be
stored in one Firestore document.
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
- types/constants:
src/identity-bootstrap.ts- function:
initializeCommunicationIdentity(...)
- function:
src/async-polling.ts- types:
AcceptedPollResponse - function:
pollUntilCompleteWithMethod(...)
- types:
src/poll-options.ts- re-export:
resolvePollOptionsFromSeconds(...)
- re-export:
src/host-onboarding.ts- types:
HostRouteContext,LegalOrganizationOrderInput - function:
confirmLegalOrganizationOrderWithDeps(...)
- types:
src/individual-start.ts- types:
IndividualOrganizationBootstrapInput,OfferPreview,IndividualOrganizationStartResult - function:
registerIndividualOrganizationWithDeps(...)
- types:
src/individual-onboarding.ts- types:
RouteContext,IndividualOrganizationConfirmOrderInput,IndividualOrganizationOrderResult - functions:
confirmIndividualOrganizationOrderWithDeps(...),readIndividualOrganizationControllerAssignmentIdentifier(...)
- types:
src/device-activation.ts- types:
EmployeeDeviceActivationInput,EmployeeDeviceActivationRequestInput,EmployeeDeviceActivationResult - functions:
activateEmployeeDeviceWithActivationCodeWithDeps(...),activateEmployeeDeviceWithActivationRequestWithDeps(...)
- types:
src/smart-token.ts- types:
SmartTokenRequestInput,SmartTokenExchangeResult - function:
requestSmartTokenWithDeps(...)
- types:
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(...)
- types:
src/digital-twin.ts- types:
DigitalTwinSearchInput,DigitalTwinSelectionInput,DigitalTwinResearchTag,DigitalTwinMaterializationInput - functions:
searchDigitalTwinsWithDeps(...),saveDigitalTwinSelectionWithDeps(...),materializeDigitalTwinWithDeps(...)
- types:
src/orchestration/digital-twin-sdk.ts- class:
DigitalTwinSdk(requestSmartToken,search,saveSelection,searchSelections,materialize); both searches exposeResearchSubjectthrough FHIR Parameters, with its canonical internal Composition available ascomposition
- class:
src/session.ts- types:
NodeCapability,NodeActorSessionContext,ActorSessionContext - classes:
ActorSession,NodeActorSession
- types:
src/node-runtime-client.ts- types:
HttpRuntimeClientOptions,NodeHttpClientOptions - classes:
HttpRuntimeClient,NodeHttpClient
- types:
src/gdc-session-bridge.ts- functions:
createNodeActorSessionsFromFacades(...),createNodeActorSessionFromFacade(...),createNodeActorSessionsFromDescriptor(...),createNodeActorSessionFromDescriptor(...),createActorSessionsFromFacades(...),createActorSessionFromFacade(...),createActorSessionsFromDescriptor(...),createActorSessionFromDescriptor(...)
- functions:
src/orchestration/client-port.ts- types:
RuntimeClient,NodeRuntimeClient - functions:
requireClientMethod(...),submitAndPollWithMethods(...),canClientSubmitAndPoll(...),submitAndPollWithClient(...)
- types:
src/orchestration/host-onboarding-sdk.ts- class:
HostOnboardingSdk
- class:
src/orchestration/organization-controller-sdk.ts- class:
OrganizationControllerSdk
- class:
src/orchestration/organization-employee-sdk.ts- class:
OrganizationEmployeeSdk
- class:
src/orchestration/individual-controller-sdk.ts- class:
IndividualControllerSdk
- class:
src/orchestration/individual-member-sdk.ts- class:
IndividualMemberSdk
- class:
src/orchestration/personal-sdk.ts- class:
PersonalSdk
- class:
src/orchestration/professional-sdk.ts- class:
ProfessionalSdk
- class:
src/legacy-compat.ts- compatibility aliases for simplified helpers, runtime classes, and legacy names such as
GdcNodeActorSessionandGdcNodeHttpClient
- compatibility aliases for simplified helpers, runtime classes, and legacy names such as
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
cloneImportedClinicalDocumentForDemo(...)for an explicit editable demo copy whose stable FHIR author/attester come from the protectedclinicalCreatorexport; direct summary updates use profileactorDidonly assenderand the real hosted provider-tenant DID asrecipient, never a portal alias- vital-sign helpers
Author and attester provenance in subject sections and documents
The canonical integration boundary is Subject-section author and profile-attester contract. It keeps technical enrollment free of per-write authorship, preserves the write-specific data author and resolves the attester from the unlocked profile.
A document Bundle keeps Composition.author (the organization, EHR/patient
portal or individual that supplies the document) separate from
Composition.attester (the registered personal or professional assignment
that attested its content). A RelatedPerson or PractitionerRole is an
attester, never a shortcut for replacing the source author. For personal
content, the BFF selects Owner when the individual originated/dictated it or
Creator when the registered member originated it. Use
assignmentIdentifier for that RelatedPerson/PractitionerRole UUID; the old
wire name authorIdentifier is deprecated and does not choose the author. The
protected BFF profile resolves both from its registered binding. DIDComm sender
and signing keys remain audit/transport identities.
The BFF never configures ledger routing or supplies channel/smart-contract
names. GW owns that decision and returns one transaction receipt with
per-resource CID/version evidence for a successful clinical Bundle.
See Clinical writes from a Node BFF,
multi-actor IPS export, and the
gateway authorship contract.
Node runtime client
NodeHttpClientNodeHttpClient.submitLegalOrganizationVerificationTransaction(...)NodeHttpClient.ingestCommunicationAndUpdateIndex(...)NodeHttpClient.submitCommunicationAndPoll(...)NodeHttpClient.searchClinicalBundle(...)NodeHttpClient.updateSubjectSection(...)for one explicit section carried by a batch Communication; its typed entries may mix.create(),.update()and exact authored.delete()operationsNodeHttpClient.updateClinicalSummary(...)for a Composition-first multi-section documentNodeHttpClient.searchLatestIps(...)NodeHttpClient.grantProfessionalAccess(...)NodeHttpClient.requestSmartToken(...)
Runtime configuration
Low-level orchestration helpers
createOrganizationEmployeeWithDeps(...)importIpsOrFhirAndUpdateIndexWithDeps(...)upsertRelatedPersonAndPollWithDeps(...)ingestCommunicationAndUpdateIndexWithDeps(...)searchClinicalBundleWithDeps(...)searchLatestIpsWithDeps(...)grantProfessionalAccessWithDeps(...)
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:
- new flow:
Organization/_transaction - legacy compatibility flow:
ICA _verify -> Organization/_activate - downstream business continuation:
Order/_batch
Use OrganizationControllerSdk.submitLegalOrganizationVerificationTransaction(...) or
NodeHttpClient.submitLegalOrganizationVerificationTransaction(...) for step 1.
Existing-tenant recovery/reverification:
- use
OrganizationControllerSdk.submitLegalOrganizationCredentialReissuance(...)orNodeHttpClient.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 inbody.data[0].resource.icaResponse; SDK callers receive that terminal HTTP payload asSubmitAndPollResult.poll.body - separately, GW returns the controller License activation code in
body.data[0].meta.claims['org.schema.IndividualProduct.serialNumber'] Organization/_issueis notLicense/_issue: the activation code is not a VC andLicense:Issuedis 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 underlyingOrganization/_issueroute- 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:
_transactionand_activateare 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
requestIcaEnrollmentor 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=1to exercise:Organization/_transaction -> Order/_batch
- set
RUN_LIVE_GW_E2E_HOST_VERIFICATION_TRANSACTION=0to 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=1direct-download mode automatically
Recovery-specific live rule:
Organization/_issuecan succeed and still be followed by_exchangefailure if the controllerid_tokenis invalid, expired or does not prove the expected actor/contact- the current GW
_exchangepath expects a valid IdP token but takes tenant authority from the already validated request route - a custom
tenant_idclaim is optional; when present it must match the route and cannot select another tenant - a failed poll
OperationOutcomeis surfaced before the helper checks forinitial_access_token, preserving the real GW diagnostic - every asynchronous success test uses
BundleReader.getResponseAnalysis(); HTTP 200/201 alone is transport completion, not proof that the inner Bundle entries succeeded - the bundled recovery runner generates a syntactically valid demo JWT if
CONTROLLER_ID_TOKENis 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