gdc-sdk-core-ts
v2.9.10
Published
Runtime-neutral actor and capability contracts for GDC SDK convergence
Readme
gdc-sdk-core-ts
Development and releases follow the mandatory
local-first TDD and release contract.
Local-first release contract
All consumers follow test -> local-network -> test-network -> network.
Every affected live E2E runs against real local services; a live E2E reported
as SKIP blocks the release. Complete those live E2E gates before
npm publish or any container image build. Mocks are diagnostic only.
Release dependencies bottom-up through their consumers. Push the branch, run
npm publish from the branch, verify the registry artifact and a clean npm
installation, and only then merge to main, push it, and delete the branch.
Keep each interactive npm authorization window alive for up to five minutes
and retry no more than three times. After three failed attempts, downstream
work may continue against an immutable npm pack tarball for local validation
only. A local tarball or file: dependency never permits consumer publication,
merge to main, image creation, or deployment; first install and verify the
published dependency from npm.
See ARCHITECTURE.md and CONTRIBUTING.md before adding or reshaping facades, profile contracts, or high-level tests.
Short rule:
101tests must be didactic and step by stepgdc-common-utils-tsowns the canonical step-by-step editors/readers, shared types, and fixturesgdc-sdk-core-tsstarts after that shared authoring step and exposes the neutral business contracts and facades
Runtime-neutral shared contracts and helpers for GDC SDK consumers.
This package is for code that needs to understand and build GDC business flows
without caring about gateway routes, host/... vs :tenantId/..., transport
adapters, or application-specific runtime details.
Actor Split
The shared SDK family starts by separating actor families, because the flows are not the same:
- organization side
- organization controller
- organization employee / professional member
- individual side
- individual controller
- individual member / self
- related person
- professional with consented access
This package is the shared business-contract layer for all of those actors. It does not execute runtime calls, but it defines or normalizes the payloads and evaluations they all depend on.
Start Here
If you are new and need to see real SDK initialization and real method usage, open these documents in this order:
- docs/101-SDK_PACKAGE_BOUNDARIES.md
Why
core,node, andfrontare separate packages, what each one owns, and why actor-scoped facades are enforced. - docs/V2_PROFILE_RUNTIME_MIGRATION.md
How the old
ProfileManagerandJobManagersurface should be split acrosscommon-utils,sdk-core,sdk-node, andsdk-frontin v2. - docs/101-SDK_FLOWS.md Business-flow map from actor split to consent, invitation, import, and SMART.
- docs/101-SUBJECT_INDEX_PROVIDER_RESOLUTION.md Provider-only Fabric discovery followed by PDQm Patient matching over legacy FHIR, DIDComm plain, or encrypted strict transport.
- docs/101-CONTROLLER_DEVICE_LIFECYCLE_SNIPPETS.md Copy/paste-oriented high-level snippets for legal controller recovery, professional device bootstrap, and individual-controller recovery policy.
- docs/ARCHITECTURE_CONTROLLER_DEVICE_LIFECYCLES.md Canonical shared lifecycle note for legal-organization controller recovery, professional device replacement, and individual-controller recovery.
- docs/101-CONSENT_COMMUNICATION.md
How an already-edited consent bundle
Communicationmoves into draft and outbox. - docs/101-IPS_COMMUNICATION_OUTBOX.md
How an IPS request
Communicationfromcommon-utilsmoves into draft and outbox. - gdc-sdk-node-ts/docs/101-SDK_INTEGRATION.md
Real backend initialization with
initializeCommunicationIdentity(...),new NodeHttpClient(...), runtime facades, and step-by-step GW usage. - gdc-sdk-node-ts/docs/101-LIVE_GW_LOCAL.md Exact local TTY and Docker commands for running the SDK against a real local GW CORE.
- gdc-sdk-front-ts/docs/101-SDK_INTEGRATION.md
Real frontend/native initialization with
new ClientSDK(...),initializeSession(...), provider discovery, and profile/session bootstrap.
Important:
gdc-sdk-core-tsdoes not initialize a runtime client by itself.- Runtime initialization lives in
gdc-sdk-node-tsandgdc-sdk-front-ts. - Shared payload shapes and examples live in
gdc-common-utils-ts. - Communication/profile/device/BFF keys belong to runtime transport identity.
- Controller
publicKeyJwkbelongs to business/operation identity and must stay separate from runtime communication keys. - FHIR-like resources in these shared flows may carry
resource.meta.claims, which is a project-specific claims container and not part of base FHIR. - Package-boundary guidance lives in docs/101-SDK_PACKAGE_BOUNDARIES.md.
- Onboarding rule across the repo family: common-utils authoring first, sdk-core contracts second, runtime-specific wire payloads last.
Shared example files to open while reading those guides:
- gdc-common-utils-ts/src/examples/shared.ts
- 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/frontend-session.ts
- gdc-common-utils-ts/src/examples/relationship-access.ts
- gdc-common-utils-ts/src/examples/lifecycle.ts
- gdc-common-utils-ts/docs/101-LIFECYCLE.md
Flow Families
The SDK documentation must cover these families from the start:
Organization bootstrap and activation
- start legal-organization verification transaction
- activate organization from ICA proof
- confirm organization order / offer
- prepare legal organization controller bootstrap payloads
- always declare service capabilities during legal-organization activation
- use
orgControllerDidas the teaching name for the controller DID - prefer
organizationActivationas the local activation builder/result name in examples
Canonical shared onboarding order:
- prepare/submit host
Organization/_transaction - consume ICA result and submit host
Organization/_activate - confirm host
Order/_batch
The _transaction payload is runtime-neutral in this package; real fetch,
polling, and JOSE/DIDComm transport execution remain runtime concerns of
gdc-sdk-node-ts or gdc-sdk-front-ts.
Primary reusable examples:
Employee creation and employee invitation
- create employee payload
- issue employee activation / invitation path
- employee device activation
- employee SMART access
Primary reusable examples:
Individual organization bootstrap
- start individual organization
- confirm individual order / offer
- prepare controller-owned individual bootstrap payloads
Primary reusable examples:
Related person and professional access to individual
- create
RelatedPerson - grant access / create consent
- evaluate requested access
- detect missing permissions
- build permission-request
Communication - invite actor and accept invitation
A permission request is not a new resource type. The reusable constructor
authors one auditable FHIR Communication whose attachment is a batch Bundle
of Consent.status = draft resources. Draft Consent records requested scope
but never grants access; the controller must later author an active Consent.
Primary reusable examples:
- gdc-common-utils-ts/src/examples/related-person.ts
- gdc-common-utils-ts/src/examples/professional.ts
- gdc-common-utils-ts/docs/101-CONSENT_ACCESS.md
Permission lifecycle
- create/grant permissions
- edit or replace permissions
- enable / disable / delete permissions
- grouped permission views for controller UX
- permission-request notifications and lookup
Canonical lifecycle naming for all SDK-facing docs and examples:
enabledisabledelete
Do not teach new integrations with mixed public naming such as revoke,
suspend, or purge when the intended API concept is the common lifecycle.
Clinical data contribution and retrieval
- ingest
Communication - import documents/resources into the subject index
- search clinical bundles
- search latest IPS
- request SMART token for subject-scoped access
These flows may be executed by:
- professional
- individual controller
- related person with write permission
- caregiver or other actor with explicit allowed sections/actions
Main Flows
1. Consent access evaluation
Use this when an app or backend needs to answer:
- can this professional or related person access this subject now?
- what permissions are missing?
- how do I build the canonical permission-request
Communication?
Main helpers:
groupConsentsForControllerView(...)evaluateRequestedAccess(...)getMissingPermissions(...)buildPermissionRequestCommunication(...)buildPermissionRequestCommunicationLookupQuery(...)
The constructor uses only registered Communication.* and Consent.* claims.
Do not add AccessRequest, AccessRequest.*, product names, or notification
transport details to this shared contract.
Reference:
2. Relationship invitation and acceptance
Use this when a controller invites a related person or professional to
connect with an individual or other subject.
The shared flow is:
- controller creates invitation payload
- invitee starts OTP challenge
- invitee confirms OTP
- invitee sets relationship PIN if required
- relationship channel becomes active
Main helpers:
createRelationshipChannelInvitationInput(...)createRelationshipChannelInvitationSummary(...)createRelationshipChannelOtpStartInput(...)createRelationshipChannelOtpConfirmInput(...)createRelationshipChannelOtpChallengeSummary(...)createRelationshipPinPolicy(...)createRelationshipPinSetInput(...)createRelationshipPinVerifyInput(...)createRelationshipLocalKeyEnvelope(...)
What this package does:
- validates and normalizes the shared payloads
- keeps the contract stable across portal, app, and phone-channel consumers
What this package does not do:
- call GW endpoints
- manage OTP providers
- store PIN hashes
- know anything about an application's reminder
Taskruntime
3. Communication and document handling
Use this when an app needs to build or read canonical Communication payloads
carrying an attached document bundle.
The read path is:
document Bundle with Composition first entry -> Communication -> DIDComm/plain
Backend search remains a separate FHIR search-param story, for example with
Composition.section.
For an attached batch or collection, the exact clinical section belongs in
the outer Communication.topic claim and its native FHIR topic
CodeableConcept. Composition.section remains inside a document Bundle; do not
place it on the outer Communication and do not infer the section from
payload.contentCodeableConcept.
Main helpers:
createCommunicationResource(...)buildCommunicationBatchMessage(...)addFhirResourceToCommunication(...)addClaimsResourceToCommunication(...)createCommunicationDraft(...)createOutboxJobFromDraft(...)createCommunicationFacade()
Who Should Use This Package
gdc-sdk-node-tsgdc-sdk-front-ts- portal/backend orchestration layers
- frontend/mobile code that needs stable contracts before runtime wiring
If you need HTTP/GW runtime behavior, use gdc-sdk-node-ts or a future runtime
adapter, not this package directly.
Minimal Examples
Consent check
import {
evaluateRequestedAccess,
getMissingPermissions,
type ConsentCoverageRequest,
} from 'gdc-sdk-core-ts';
import {
EXAMPLE_EMAIL_PROFESSIONAL,
EXAMPLE_INDIVIDUAL_DID_WEB,
} from 'gdc-common-utils-ts/examples/consent-access';
import {
HealthcareActorRoles,
HealthcareBasicSections,
HealthcareConsentPurposes,
} from 'gdc-common-utils-ts/constants/healthcare';
const request: ConsentCoverageRequest = {
subject: EXAMPLE_INDIVIDUAL_DID_WEB,
actor: { actorKind: 'professional', email: EXAMPLE_EMAIL_PROFESSIONAL },
actorRole: HealthcareActorRoles.Physician,
purpose: HealthcareConsentPurposes.Treatment,
sections: [HealthcareBasicSections.HistoryOfMedicationUse.claim],
};
const evaluation = await evaluateRequestedAccess(provider, request);
const missing = getMissingPermissions(evaluation);Relationship invitation
import {
createRelationshipChannelInvitationInput,
createRelationshipChannelOtpStartInput,
createRelationshipPinSetInput,
RelationshipAccessActorKinds,
RelationshipEnrollmentChannels,
RelationshipOtpDeliveryChannels,
RelationshipSubjectKinds,
type RelationshipChannelInvitationInput,
type RelationshipChannelOtpStartInput,
type RelationshipPinSetInput,
} 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 subjectDid = buildIndividualDidWeb({
organizationDidWeb: providerOrganizationDid,
subjectId: subjectLocalId,
});
const emailProfessional = professionalDirectoryEntry.email;
const actorKind = RelationshipAccessActorKinds.Professional;
const actorIdentifier = emailProfessional;
const actorRole = HealthcareActorRoles.Physician;
const deliveryChannel = RelationshipEnrollmentChannels.Phone;
const deliveryTarget = professionalDirectoryEntry.phone;
const purpose = HealthcareConsentPurposes.Treatment;
const phonePinOptional = false;
const invitationInput: RelationshipChannelInvitationInput = {
tenantId,
jurisdiction,
sector,
subjectId: subjectDid,
subjectKind: RelationshipSubjectKinds.Person,
actorKind,
actorIdentifier,
actorRole,
deliveryChannel,
deliveryTarget,
purpose,
relationshipLabel: 'primary-physician',
phonePinOptional,
};
const invitation = createRelationshipChannelInvitationInput(invitationInput);
const invitationId = 'rel-invite-001';
const otpStartInput: RelationshipChannelOtpStartInput = {
invitationId,
deliveryChannel: RelationshipOtpDeliveryChannels.Sms,
locale: 'es-ES',
};
const otpStart = createRelationshipChannelOtpStartInput(otpStartInput);
const challengeId = 'otp-challenge-001';
const pin = '482915';
const pinSetInput: RelationshipPinSetInput = {
invitationId,
challengeId,
channel: deliveryChannel,
pin,
pinConfirmation: pin,
policy: {
minLength: 6,
numericOnly: true,
},
};
const pinSet = createRelationshipPinSetInput(pinSetInput);Where those variables come from:
tenantId,jurisdiction,sectorcome from the selected GW tenant route contextsubjectDidshould be built withbuildIndividualDidWeb(...)from the provider/subject identifiers you already haveactorKind,actorIdentifier,actorRolecome from the invited professional or related persondeliveryChannel,deliveryTargetcome from the chosen channel for enrollmentinvitationId,challengeIdcome from the backend after creating the invitation and starting OTPpincomes from the invitee during channel enrollment
Local terminology fallback
Core re-exports LocalTerminologyProvider,
createClinicalCodeTranslator(...) and their catalog/search contracts. The
provider accepts the established legacy catalog shape
data[].attributes[code] = display; it does not translate free text or make
network requests.
Use the provider directly for cached/offline display and search. Node and Front add runtime-specific facades without changing this canonical contract.
API Index
The canonical contract should live in JSDoc on exported code. This README is the entrypoint, not the exhaustive reference.
Full Public Surface
The following modules define the complete public SDK surface exported by this package:
src/actor-model.ts- types:
ActorKind,Capability,ActorSessionDescriptor,ActorFacadeDescriptor,ActorFlags,ActorSessionDescriptorInput - functions:
filterCapabilitiesForActor(...),expandActorSessionDescriptorToFacades(...),buildActorSessionDescriptorFromActorFlags(...)
- types:
src/bootstrap-facade.ts- types:
BootstrapValidationIssue,OrganizationActivationPayload,BootstrapFacade - function:
createBootstrapFacade()
- types:
src/communication-bundle-contracts.ts- types:
AttachmentObj,CommunicationInput,CommMsgExtendedInput,DateRange,SectionFilter,IncludedResourceType,BundleSearchQuery,CompositionSearchQuery,ProjectedResourceSummary,BundleSearchResult,CommunicationAuditRecord - functions:
assertCommunicationInput(...),assertCommMsgExtendedInput(...),assertBundleSearchQuery(...)
- types:
src/consent-access.ts- types:
PermissionRequestCommunicationInput,PermissionRequestCommunicationLookup,ActiveConsentProvider - functions:
groupConsentsForControllerView(...),evaluateRequestedAccess(...),getMissingPermissions(...),buildPermissionRequestCommunication(...),buildPermissionRequestCommunicationLookupQuery(...)
- types:
src/communication-draft.ts- constant:
CommunicationOutboxStatuses - types:
CommunicationOutboxStatus,CommunicationDraft,CommunicationDraftCreationOptions,OutboxJob,CommunicationOutboxJob,CommunicationOutboxJobOptions - functions:
createCommunicationDraft(...),getCommunicationFromDraft(...),isCommunicationDraftReady(...),addFhirResourceToDraft(...),addClaimsResourceToDraft(...),createOutboxJobFromDraft(...),updateOutboxJobStatus(...)
- constant:
src/communication-document-facade.ts- types:
ResolvedCommunicationDocument,FhirDocumentSection,FhirDocumentFacade - functions:
getDocumentFromCommunication(...),createFhirDocumentFacade(...),createCommunicationFacade()
- types:
src/communication-outbox.ts- types/interfaces:
OutboxQuery,IOutboxRepository - class:
OutboxRepositoryMemory
- types/interfaces:
src/communication-resource-helpers.ts- types:
FhirResourceLike,CommunicationResourceLike,AttachmentEncodingInput,CommunicationCreationOptions,ResourceAttachmentOptions,CommunicationPayloadResolution,ObservationCodeFilter,CommunicationBatchMessageOptions - functions:
createCommunicationResource(...),buildCommunicationBatchMessage(...),addFhirResourceToCommunication(...),addClaimsResourceToCommunication(...),resolveCommunicationPayloads(...),getFirstBundleDocumentFromCommunication(...),getBundleDocumentEntries(...),getBundleDocumentResourcesByType(...),getMedicationClaimsFromCommunicationDocument(...),sortFhirResourcesByDateDescending(...),getObservationsByCodeFromCommunicationDocument(...)
- types:
src/did-resolution-session.ts- functions:
getProviderDidFromSubjectDid(...),resolveDidDocumentServices(...),resolveProviderIdentityForSubject(...)
- functions:
src/discovery-facade.ts- types/interfaces:
DiscoveryResolutionResult,DiscoveryFacade - function:
createStaticDiscoveryFacade(...)
- types/interfaces:
src/identity-model.ts- types:
DidServiceLike,DidDocumentLike,ResolvedServiceEndpointLike,TransportIdentityState,ActorIdentityState,ProviderIdentityState
- types:
src/identity-store.ts- interfaces:
DidDocumentStore,IdentityStore - class:
MemoryIdentityStore
- interfaces:
src/polling-model.ts- types:
SubmitPayload,AsyncPollRequest,SubmitResponse,PollOptions,PollResult,SubmitAndPollResult - function:
resolvePollOptionsFromSeconds(...)
- types:
src/relationship-access.ts- types:
RelationshipEnrollmentChannel,RelationshipSubjectKind,RelationshipAccessActorKind,RelationshipInvitationStatus,RelationshipOtpDeliveryChannel,RelationshipPinKdf,RelationshipChannelInvitationInput,RelationshipChannelInvitationSummary,RelationshipChannelOtpStartInput,RelationshipChannelOtpConfirmInput,RelationshipChannelOtpChallengeSummary,RelationshipPinPolicy,RelationshipPinSetInput,RelationshipPinVerifyInput,RelationshipLocalKeyEnvelope - functions:
createRelationshipChannelInvitationInput(...),createRelationshipChannelInvitationSummary(...),createRelationshipChannelOtpStartInput(...),createRelationshipChannelOtpConfirmInput(...),createRelationshipChannelOtpChallengeSummary(...),createRelationshipPinPolicy(...),createRelationshipPinSetInput(...),createRelationshipPinVerifyInput(...),createRelationshipLocalKeyEnvelope(...)
- types:
src/session-model.ts- types/interfaces:
AppType,AppInfo,DeviceTrustLevel,PersistenceMode,DataPersistencePolicy,InitializeSessionParams,Profile,ProfileRegistryEntry,VaultQueryCondition,VaultQuery,IVaultRepository,IApiConfig,INetwork,IVerifier
- types/interfaces:
src/smart-endpoint-resolver.ts- function:
resolveSmartTokenEndpointForSubject(...)
- function:
src/vital-signs.ts- types:
VitalSignQuantityInput,BloodPressureInput,VitalSignsDocumentBase - functions:
createHeartRateObservation(...),createBodyTemperatureObservation(...),createBloodPressureObservation(...),createVitalSignsFacade(...)
- types:
Actor/session contracts
filterCapabilitiesForActor(...)expandActorSessionDescriptorToFacades(...)buildActorSessionDescriptorFromActorFlags(...)DeviceTrustLevel,PersistenceMode,DataPersistencePolicyMemoryIdentityStore
Identity/discovery/bootstrap
createBootstrapFacade(...)createStaticDiscoveryFacade(...)resolveProviderIdentityForSubject(...)resolveSmartTokenEndpointForSubject(...)gdc-common-utils-ts/docs/101-DATASPACE_DISCOVERY_DEFAULTS.md- shared portal/backend bootstrap guide for
defaults-only,default-first, andinternet-firstdiscovery seeding
- shared portal/backend bootstrap guide for
Discovery boundary rule:
gdc-sdk-core-tsdefines runtime-neutral contracts plus reusable transport-agnostic discovery resolvers that accept injectedfetch- host DSP resolution uses the contextualized hosting-operator entrypoint
/<hosting-operator-network-context>/.well-known/dspace-version - frontend DTO/card mapping belongs to
gdc-sdk-front-ts
Polling helpers
Communication/document contracts
Consent access
groupConsentsForControllerView(...)evaluateRequestedAccess(...)getMissingPermissions(...)buildPermissionRequestCommunication(...)buildPermissionRequestCommunicationLookupQuery(...)
Consent precedence in the shared model:
- explicit deny for a concrete email
- explicit permit for a concrete email
- organization decision
- jurisdiction decision
- default deny
Relationship access
createRelationshipChannelInvitationInput(...)createRelationshipChannelInvitationSummary(...)createRelationshipChannelOtpStartInput(...)createRelationshipChannelOtpConfirmInput(...)createRelationshipChannelOtpChallengeSummary(...)createRelationshipPinPolicy(...)createRelationshipPinSetInput(...)createRelationshipPinVerifyInput(...)createRelationshipLocalKeyEnvelope(...)
Communication/document builders and readers
createCommunicationResource(...)buildCommunicationBatchMessage(...)addFhirResourceToCommunication(...)addClaimsResourceToCommunication(...)resolveCommunicationPayloads(...)getFirstBundleDocumentFromCommunication(...)getBundleDocumentEntries(...)getBundleDocumentResourcesByType(...)getMedicationClaimsFromCommunicationDocument(...)sortFhirResourcesByDateDescending(...)getObservationsByCodeFromCommunicationDocument(...)
Drafts and outbox
createCommunicationDraft(...)getCommunicationFromDraft(...)isCommunicationDraftReady(...)addFhirResourceToDraft(...)addClaimsResourceToDraft(...)createOutboxJobFromDraft(...)updateOutboxJobStatus(...)OutboxJobCommunicationOutboxJobIOutboxRepositoryOutboxRepositoryMemory
IPS search through Communication
For IPS document requests, keep the search semantics separate from the
transport envelope. The frontend/web app should still create a FHIR
Communication, but the actual IPS search story is documented separately as
FHIR search params such as Composition.section.
Employee Contract
Employee lifecycle, search semantics, and the canonical EmployeeDraft
contract live in docs/101-EMPLOYEES.md.
The shared bundle/editor mechanics for employee live in:
Communication.content-referencestores the relative request reference used by the transport envelopeCommunication.recipientis the logical/clinical conversation recipient, not the HTTP endpointDIDComm
to/audbelong to the transport envelope or runtime submitter, not toCommunication.content-reference
Typical flow:
- build
summaryOperationRequestParameters - flatten them to
summaryOperationRequestReferencePath - create
Communicationclaims withcommunication.newIpsSummarySearchCommunication(...) - place the
Communicationin a draft - freeze the draft into an outbox job
- let the runtime/backend resolve the provider and send the envelope
If you need the actual search semantics, read the common-utils 101 that documents the FHIR search-param story separately.
See the executable example:
High-level document facade
Vital signs
createHeartRateObservation(...)createBodyTemperatureObservation(...)createBloodPressureObservation(...)createVitalSignsFacade(...)
Documentation Rule
- JSDoc on exported code is canonical.
- README should explain the business flows first.
- Runtime routing details belong in node/front runtime docs, not in this package.
