gdc-sdk-node-ts 2.6.2 → 2.6.3

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
@@ -427,13 +427,16 @@ data ingestion/search.
427
427
 
428
428
  `ProfessionalSdk.requestProfessionalAccess(...)` is the bootstrap operation
429
429
  for the inverse flow: it persists a subject-scoped permission-request
430
- `Communication` using the configured HTTP bearer and transport profile, without
431
- requiring SMART. The subject later answers through
430
+ `Communication` with an attached batch Bundle of normal
431
+ `Consent.status = draft` resources, using the configured HTTP bearer and
432
+ transport profile without requiring SMART. A draft is inbox evidence and
433
+ cannot authorize access. The subject later answers through
432
434
  `respondToProfessionalAccessRequest(...)`; that facade uses the normal Consent
433
435
  grant/deny operation and retains the original Communication identifier/thread.
434
436
  `listProfessionalAccessRequests(...)` provides the subject/requester inbox view,
435
437
  and `GatewayActiveConsentProvider` supplies `evaluateRequestedAccess(...)` from
436
438
  the GW Consent source of truth rather than a parallel application table.
439
+ There is no `AccessRequest` FHIR resource or `AccessRequest.*` claim family.
437
440
 
438
441
  ## Main Flows
439
442
 
@@ -813,6 +816,9 @@ Recovery-specific live rule:
813
816
  and cannot select another tenant
814
817
  - a failed poll `OperationOutcome` is surfaced before the helper checks for
815
818
  `initial_access_token`, preserving the real GW diagnostic
819
+ - every asynchronous success test uses `BundleReader.getResponseAnalysis()`;
820
+ HTTP 200/201 alone is transport completion, not proof that the inner Bundle
821
+ entries succeeded
816
822
  - the bundled recovery runner generates a syntactically valid demo JWT if
817
823
  `CONTROLLER_ID_TOKEN` is not provided, but production/staging should use a
818
824
  real IdP-issued token
@@ -143,6 +143,13 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
143
143
  }, pollOptions?: PollOptions): Promise<PollResult>;
144
144
  /**
145
145
  * Convenience wrapper that performs submit and poll in sequence.
146
+ *
147
+ * A terminal HTTP 200/201 means polling completed; it does not guarantee the
148
+ * business operation succeeded. Consumers and E2E tests must inspect
149
+ * `result.poll.body` with `BundleReader.getResponseAnalysis()` and reject
150
+ * error OperationOutcomes. High-level product code must not manually traverse
151
+ * DIDComm `body`, JSON-API `data[]`, FHIR `entry[]` or `issue[]` to decide
152
+ * success.
146
153
  */
147
154
  submitAndPoll(submitPath: string, pollPath: string, payload: SubmitPayload, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
148
155
  /** Registers one neutral FHIR R5 topic in the tenant-owned CORE catalog. */
@@ -496,8 +503,10 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
496
503
  ingestCommunicationAndUpdateIndex(ctx: RouteContext, input: CommunicationIngestionInput): Promise<SubmitAndPollResult>;
497
504
  /**
498
505
  * Records a subject-scoped permission-request Communication before SMART
499
- * authorization exists. The configured bearer authenticates HTTP and the
500
- * configured secure transport adapter signs/encrypts DIDComm when enabled.
506
+ * authorization exists. The Communication carries a batch Bundle of
507
+ * `Consent.status = draft`; that draft grants nothing. The configured bearer
508
+ * authenticates HTTP and the configured secure transport adapter
509
+ * signs/encrypts DIDComm when enabled.
501
510
  */
502
511
  requestProfessionalAccess(ctx: RouteContext, input: ProfessionalAccessRequestInput): Promise<ProfessionalAccessRequestResult>;
503
512
  /**
@@ -129,6 +129,13 @@ export class HttpRuntimeClient {
129
129
  }
130
130
  /**
131
131
  * Convenience wrapper that performs submit and poll in sequence.
132
+ *
133
+ * A terminal HTTP 200/201 means polling completed; it does not guarantee the
134
+ * business operation succeeded. Consumers and E2E tests must inspect
135
+ * `result.poll.body` with `BundleReader.getResponseAnalysis()` and reject
136
+ * error OperationOutcomes. High-level product code must not manually traverse
137
+ * DIDComm `body`, JSON-API `data[]`, FHIR `entry[]` or `issue[]` to decide
138
+ * success.
132
139
  */
133
140
  async submitAndPoll(submitPath, pollPath, payload, pollOptions) {
134
141
  return this.submitClinicalMessageAndPoll(submitPath, pollPath, payload, this.transportProfile, pollOptions);
@@ -984,8 +991,10 @@ export class HttpRuntimeClient {
984
991
  }
985
992
  /**
986
993
  * Records a subject-scoped permission-request Communication before SMART
987
- * authorization exists. The configured bearer authenticates HTTP and the
988
- * configured secure transport adapter signs/encrypts DIDComm when enabled.
994
+ * authorization exists. The Communication carries a batch Bundle of
995
+ * `Consent.status = draft`; that draft grants nothing. The configured bearer
996
+ * authenticates HTTP and the configured secure transport adapter
997
+ * signs/encrypts DIDComm when enabled.
989
998
  */
990
999
  async requestProfessionalAccess(ctx, input) {
991
1000
  this.requireConfiguredTransportProfile(input.transportProfile);
@@ -77,7 +77,8 @@ export declare class ProfessionalSdk {
77
77
  ingestCommunicationAndUpdateIndex(ctx: RouteContext, input: CommunicationIngestionInput): Promise<SubmitAndPollResult>;
78
78
  /**
79
79
  * Requests subject-controlled access without requiring a SMART token. The
80
- * runtime persists one canonical permission-request Communication.
80
+ * runtime persists one canonical permission-request Communication carrying
81
+ * a draft Consent Bundle. The draft is auditable but never authorizes access.
81
82
  */
82
83
  requestProfessionalAccess(ctx: RouteContext, input: ProfessionalAccessRequestInput): Promise<ProfessionalAccessRequestResult>;
83
84
  /** Lists permission requests sent by or involving this professional. */
@@ -92,7 +92,8 @@ export class ProfessionalSdk {
92
92
  }
93
93
  /**
94
94
  * Requests subject-controlled access without requiring a SMART token. The
95
- * runtime persists one canonical permission-request Communication.
95
+ * runtime persists one canonical permission-request Communication carrying
96
+ * a draft Consent Bundle. The draft is auditable but never authorizes access.
96
97
  */
97
98
  requestProfessionalAccess(ctx, input) {
98
99
  return requireClientMethod(this.client, 'requestProfessionalAccess')(ctx, input);
@@ -268,8 +268,9 @@ export type CommunicationIngestionInput = {
268
268
  /**
269
269
  * Canonical professional-to-subject permission request.
270
270
  *
271
- * This operation records a `Communication`; it never creates Consent or
272
- * requires a SMART token. HTTP authentication and optional secure DIDComm
271
+ * This operation records one `Communication` carrying a normal
272
+ * `Consent.status = draft`; it never creates an active grant and does not
273
+ * require a SMART token. HTTP authentication and optional secure DIDComm
273
274
  * transport remain concerns of the configured runtime client.
274
275
  */
275
276
  export type ProfessionalAccessRequestInput = Omit<PermissionRequestCommunicationInput, 'missing'> & Readonly<{
@@ -288,6 +289,7 @@ export type ProfessionalAccessRequestInput = Omit<PermissionRequestCommunication
288
289
  export type ProfessionalAccessRequestResult = Readonly<{
289
290
  thid: string;
290
291
  communicationIdentifier: string;
292
+ consentIdentifier: string;
291
293
  communication: CommunicationInput;
292
294
  delivery: SubmitAndPollResult;
293
295
  }>;
@@ -846,7 +848,8 @@ export declare function ingestCommunicationAndUpdateIndexWithDeps(routeCtx: Rout
846
848
  /**
847
849
  * Builds and persists one professional access request against the subject's
848
850
  * provider route. The request is deliberately independent from SMART because
849
- * no subject consent exists yet.
851
+ * no active subject consent exists yet. The auditable Communication carries a
852
+ * draft Consent Bundle; the draft itself never authorizes data access.
850
853
  */
851
854
  export declare function requestProfessionalAccessWithDeps(routeCtx: RouteContext, input: ProfessionalAccessRequestInput, deps: {
852
855
  individualCommunicationBatchPath: (ctx: RouteContext, pathFormatSegment: string) => string;
@@ -476,7 +476,8 @@ export async function ingestCommunicationAndUpdateIndexWithDeps(routeCtx, input,
476
476
  /**
477
477
  * Builds and persists one professional access request against the subject's
478
478
  * provider route. The request is deliberately independent from SMART because
479
- * no subject consent exists yet.
479
+ * no active subject consent exists yet. The auditable Communication carries a
480
+ * draft Consent Bundle; the draft itself never authorizes data access.
480
481
  */
481
482
  export async function requestProfessionalAccessWithDeps(routeCtx, input, deps) {
482
483
  const subject = String(input.subject || '').trim();
@@ -498,11 +499,14 @@ export async function requestProfessionalAccessWithDeps(routeCtx, input, deps) {
498
499
  const thid = String(input.thid || '').trim() || `permission-request-${createRuntimeUuid()}`;
499
500
  const communicationIdentifier = String(input.communicationIdentifier || '').trim()
500
501
  || `urn:uuid:${createRuntimeUuid()}`;
502
+ const consentIdentifier = String(input.consentIdentifier || '').trim()
503
+ || `urn:uuid:${createRuntimeUuid()}`;
501
504
  const communication = buildPermissionRequestCommunication({
502
505
  ...input,
503
506
  subject,
504
507
  thid,
505
508
  communicationIdentifier,
509
+ consentIdentifier,
506
510
  missing: {
507
511
  sections: missingSections,
508
512
  resourceTypes: missingResourceTypes,
@@ -518,6 +522,23 @@ export async function requestProfessionalAccessWithDeps(routeCtx, input, deps) {
518
522
  [CommunicationClaim.NoteText]: communication.text,
519
523
  };
520
524
  const persistedCommunication = { ...communication, claims: communicationClaims };
525
+ if (!communication.payload) {
526
+ throw new Error('Permission request requires one draft Consent Bundle.');
527
+ }
528
+ const communicationResource = addFhirResourceToCommunication(createCommunicationResource({
529
+ subject,
530
+ sender: communication.sender,
531
+ recipient: communication.recipient,
532
+ category: Array.isArray(communication.category)
533
+ ? communication.category
534
+ : communication.category ? [communication.category] : undefined,
535
+ noteText: communication.text,
536
+ claims: communicationClaims,
537
+ }), communication.payload, {
538
+ attachmentTitle: 'draft-consent-request.json',
539
+ attachmentContentType: 'application/fhir+json',
540
+ });
541
+ communicationResource.id = communicationIdentifier;
521
542
  const communicationPayload = {
522
543
  thid,
523
544
  body: {
@@ -526,12 +547,7 @@ export async function requestProfessionalAccessWithDeps(routeCtx, input, deps) {
526
547
  data: [{
527
548
  type: 'CommMsgExtended',
528
549
  meta: { claims: communicationClaims },
529
- resource: {
530
- resourceType: ResourceTypesFhirR4.Communication,
531
- id: communicationIdentifier,
532
- status: 'completed',
533
- meta: { claims: communicationClaims },
534
- },
550
+ resource: communicationResource,
535
551
  }],
536
552
  },
537
553
  };
@@ -543,7 +559,13 @@ export async function requestProfessionalAccessWithDeps(routeCtx, input, deps) {
543
559
  pathFormatSegment: 'r4',
544
560
  pollOptions: input.pollOptions,
545
561
  }, deps);
546
- return { thid, communicationIdentifier, communication: persistedCommunication, delivery };
562
+ return {
563
+ thid,
564
+ communicationIdentifier,
565
+ consentIdentifier,
566
+ communication: persistedCommunication,
567
+ delivery,
568
+ };
547
569
  }
548
570
  export async function searchCommunicationParticipantsWithDeps(routeCtx, input, deps) {
549
571
  const payload = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-sdk-node-ts",
3
- "version": "2.6.2",
3
+ "version": "2.6.3",
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.",
@@ -38,8 +38,8 @@
38
38
  "test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
39
39
  },
40
40
  "dependencies": {
41
- "gdc-common-utils-ts": "2.7.1",
42
- "gdc-sdk-core-ts": "2.4.2"
41
+ "gdc-common-utils-ts": "2.7.2",
42
+ "gdc-sdk-core-ts": "2.4.3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^20.14.10",