gdc-sdk-node-ts 2.3.1 → 2.3.2

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.
@@ -0,0 +1,18 @@
1
+ import type { JWK } from 'gdc-common-utils-ts/models/jwk';
2
+ export type ResolveDidWebKeyAgreementJwkOptions = Readonly<{
3
+ fetchImpl?: typeof fetch;
4
+ /**
5
+ * Transport override for a local/private route serving the same DID document.
6
+ * The returned document must still identify itself with the requested DID.
7
+ */
8
+ didDocumentUrl?: string;
9
+ }>;
10
+ /**
11
+ * Resolves the first public key-agreement JWK from a did:web document.
12
+ *
13
+ * The DID remains the recipient identity and the document remains the source
14
+ * of its public keys. `didDocumentUrl` changes only the HTTP route used to
15
+ * retrieve that same document, which is useful when a local GW publishes its
16
+ * external DID while being reached through localhost.
17
+ */
18
+ export declare function resolveDidWebKeyAgreementJwk(recipientDid: string, options?: ResolveDidWebKeyAgreementJwkOptions): Promise<JWK>;
@@ -0,0 +1,46 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { getBaseUrlFromDidWeb, normalizeDidWeb } from 'gdc-common-utils-ts/utils/did';
3
+ /**
4
+ * Resolves the first public key-agreement JWK from a did:web document.
5
+ *
6
+ * The DID remains the recipient identity and the document remains the source
7
+ * of its public keys. `didDocumentUrl` changes only the HTTP route used to
8
+ * retrieve that same document, which is useful when a local GW publishes its
9
+ * external DID while being reached through localhost.
10
+ */
11
+ export async function resolveDidWebKeyAgreementJwk(recipientDid, options = {}) {
12
+ const did = normalizeDidWeb(String(recipientDid || '').trim());
13
+ if (!did.startsWith('did:web:')) {
14
+ throw new Error('resolveDidWebKeyAgreementJwk requires a did:web recipient.');
15
+ }
16
+ const didDocumentUrl = String(options.didDocumentUrl || '').trim()
17
+ || `${getBaseUrlFromDidWeb(did)}.well-known/did.json`;
18
+ const response = await (options.fetchImpl || fetch)(didDocumentUrl, {
19
+ headers: { Accept: 'application/did+json, application/json' },
20
+ });
21
+ if (!response.ok) {
22
+ throw new Error(`Could not resolve DID document '${did}' (HTTP ${response.status}).`);
23
+ }
24
+ const document = await response.json();
25
+ if (normalizeDidWeb(String(document.id || '').trim()) !== did) {
26
+ throw new Error(`Resolved DID document does not belong to '${did}'.`);
27
+ }
28
+ const methods = new Map((document.verificationMethod || []).map((method) => [method.id, method]));
29
+ for (const agreement of document.keyAgreement || []) {
30
+ const method = typeof agreement === 'string' ? methods.get(agreement) : agreement;
31
+ const jwk = publicKeyAgreementJwk(method);
32
+ if (jwk)
33
+ return jwk;
34
+ }
35
+ throw new Error(`DID document '${did}' has no public keyAgreement JWK.`);
36
+ }
37
+ function publicKeyAgreementJwk(method) {
38
+ const publicKeyJwk = method?.publicKeyJwk;
39
+ if (!publicKeyJwk || typeof publicKeyJwk !== 'object')
40
+ return undefined;
41
+ if (publicKeyJwk.use && publicKeyJwk.use !== 'enc')
42
+ return undefined;
43
+ if (publicKeyJwk.kid || !method?.id.includes('#'))
44
+ return publicKeyJwk;
45
+ return { ...publicKeyJwk, kid: method.id.slice(method.id.lastIndexOf('#') + 1) };
46
+ }
package/dist/index.d.ts CHANGED
@@ -26,6 +26,7 @@ export * from './UserProfileIndexStore.js';
26
26
  export * from './wallet-backed-job-manager.js';
27
27
  export * from './node-crypto-helper.js';
28
28
  export * from './node-managed-wallet.js';
29
+ export * from './did-web-recipient.js';
29
30
  export * from './constants/lifecycle.js';
30
31
  export * from './consent-claim-helpers.js';
31
32
  export * from './session.js';
package/dist/index.js CHANGED
@@ -27,6 +27,7 @@ export * from './UserProfileIndexStore.js';
27
27
  export * from './wallet-backed-job-manager.js';
28
28
  export * from './node-crypto-helper.js';
29
29
  export * from './node-managed-wallet.js';
30
+ export * from './did-web-recipient.js';
30
31
  export * from './constants/lifecycle.js';
31
32
  export * from './consent-claim-helpers.js';
32
33
  export * from './session.js';
@@ -51,9 +51,7 @@ export declare class NodeManagedWallet implements IWallet {
51
51
  * Computes a digest of a string using the configured runtime helper.
52
52
  */
53
53
  digest(data: string, algorithm: string): Promise<string>;
54
- /**
55
- * Protects one confidential document using one owner-specific symmetric storage key.
56
- */
54
+ /** Protects one document with a random AES-256-GCM CEK wrapped to the owner's storage ML-KEM key. */
57
55
  protectConfidentialData(doc: any, entityId: string): Promise<any>;
58
56
  /**
59
57
  * Protects one confidential document using the richer execution-context model.
@@ -140,8 +138,6 @@ export declare class NodeManagedWallet implements IWallet {
140
138
  private resolveOwnerId;
141
139
  private resolveStorageOwnerId;
142
140
  private createManagedKey;
143
- private createStorageKey;
144
- private requireStorageKey;
145
141
  private requireManagedKey;
146
142
  private inferRelevantScopes;
147
143
  private matchesSelection;
@@ -13,6 +13,7 @@ const DEFAULT_POLICY = {
13
13
  'vc-signing': 'ES384',
14
14
  'comm-signing': 'ML-DSA-44',
15
15
  'comm-encryption': 'ML-KEM-768',
16
+ 'document-at-rest': 'ML-KEM-768',
16
17
  },
17
18
  };
18
19
  /**
@@ -99,9 +100,6 @@ export class NodeManagedWallet {
99
100
  ownerState.keys.push(descriptor);
100
101
  created.push(descriptor.descriptor.publicJwk);
101
102
  }
102
- if (!ownerState.storageKey) {
103
- ownerState.storageKey = await this.createStorageKey(request, ownerId);
104
- }
105
103
  return { keys: created };
106
104
  }
107
105
  /**
@@ -136,9 +134,7 @@ export class NodeManagedWallet {
136
134
  async digest(data, algorithm) {
137
135
  return this.cryptoHelper.digestString(data, algorithm);
138
136
  }
139
- /**
140
- * Protects one confidential document using one owner-specific symmetric storage key.
141
- */
137
+ /** Protects one document with a random AES-256-GCM CEK wrapped to the owner's storage ML-KEM key. */
142
138
  async protectConfidentialData(doc, entityId) {
143
139
  return this.protectManagedConfidentialData(doc, {
144
140
  profile: { profileId: entityId },
@@ -151,10 +147,24 @@ export class NodeManagedWallet {
151
147
  async protectManagedConfidentialData(doc, context, _options) {
152
148
  if (!doc?.content)
153
149
  return doc;
154
- const storageKey = this.requireStorageKey(context);
150
+ const storageKey = this.requireManagedKey(context, {
151
+ ownerScope: context.profile?.profileId ? 'profile' : 'runtime',
152
+ purpose: 'document-at-rest',
153
+ }, 'enc');
155
154
  const ownerId = this.resolveStorageOwnerId(context);
156
- const contentString = JSON.stringify(doc.content);
157
- const encrypted = await this.cryptography.encrypt(contentString, storageKey, ownerId);
155
+ const ownerBinding = createHash('sha256').update(ownerId).digest('base64url');
156
+ const privateJwk = {
157
+ ...storageKey.descriptor.publicJwk,
158
+ dBytes: storageKey.privateMaterial,
159
+ };
160
+ const encrypted = await this.cryptography.encryptJweToCompact(JSON.stringify(doc.content), {
161
+ typ: 'gdc-confidential-document+jwe',
162
+ cty: 'application/json',
163
+ enc: 'A256GCM',
164
+ gdc_pq_profile: 'confidential-pqc-v1',
165
+ gdc_key_purpose: 'document-at-rest',
166
+ gdc_owner_binding: ownerBinding,
167
+ }, privateJwk, storageKey.descriptor.publicJwk);
158
168
  const { content, ...docWithoutContent } = doc;
159
169
  return {
160
170
  ...docWithoutContent,
@@ -176,13 +186,26 @@ export class NodeManagedWallet {
176
186
  async unprotectManagedConfidentialData(doc, context, _options) {
177
187
  if (!doc?.jwe)
178
188
  return doc;
179
- const storageKey = this.requireStorageKey(context);
189
+ const storageKey = this.requireManagedKey(context, {
190
+ ownerScope: context.profile?.profileId ? 'profile' : 'runtime',
191
+ purpose: 'document-at-rest',
192
+ }, 'enc');
180
193
  const ownerId = this.resolveStorageOwnerId(context);
181
- const decrypted = await this.cryptography.decrypt(doc.jwe, storageKey, ownerId);
194
+ const privateJwk = {
195
+ ...storageKey.descriptor.publicJwk,
196
+ dBytes: storageKey.privateMaterial,
197
+ };
198
+ const decrypted = await this.cryptography.decryptJwe(doc.jwe, privateJwk);
199
+ const header = decrypted.protectedHeader;
200
+ if (header.gdc_pq_profile !== 'confidential-pqc-v1'
201
+ || header.gdc_key_purpose !== 'document-at-rest'
202
+ || header.gdc_owner_binding !== createHash('sha256').update(ownerId).digest('base64url')) {
203
+ throw new Error('Confidential document is not bound to this storage owner and policy.');
204
+ }
182
205
  const { jwe, ...docWithoutJwe } = doc;
183
206
  return {
184
207
  ...docWithoutJwe,
185
- content: JSON.parse(decrypted),
208
+ content: JSON.parse(Content.bytesToStringUTF8(decrypted.decryptedBytes)),
186
209
  };
187
210
  }
188
211
  /**
@@ -480,25 +503,6 @@ export class NodeManagedWallet {
480
503
  privateMaterial: signer.getPrivateMaterial(),
481
504
  };
482
505
  }
483
- async createStorageKey(request, ownerId) {
484
- if (request.mode === 'deterministic' && request.seedMaterial !== undefined) {
485
- return this.deriveSeedBytes(request.seedMaterial, ownerId, 'document-at-rest', 32);
486
- }
487
- return this.cryptoHelper.getRandomBytes(32);
488
- }
489
- requireStorageKey(context) {
490
- if (context.profile?.profileId) {
491
- const state = this.owners.get(`profile:${context.profile.profileId}:${context.walletId ?? 'default'}`);
492
- if (state?.storageKey)
493
- return state.storageKey;
494
- }
495
- if (context.runtime?.runtimeId) {
496
- const state = this.owners.get(`runtime:${context.runtime.runtimeId}:${context.walletId ?? 'default'}`);
497
- if (state?.storageKey)
498
- return state.storageKey;
499
- }
500
- throw new Error('NodeManagedWallet has no storage key for the provided context. Provision keys first.');
501
- }
502
506
  requireManagedKey(context, selection, expectedUse) {
503
507
  if (selection.keyId) {
504
508
  for (const ownerState of this.owners.values()) {
@@ -79,7 +79,11 @@ export declare class IndividualControllerSdk {
79
79
  */
80
80
  importIpsOrFhirAndUpdateIndex(ctx: RouteContext, input: IpsOrFhirImportInput): Promise<SubmitAndPollResult>;
81
81
  /**
82
- * Creates or updates a `RelatedPerson` for non-employee caregivers or family members.
82
+ * Compatibility adapter for the older direct `RelatedPerson` route.
83
+ *
84
+ * @deprecated Author one or more typed RelatedPerson entries in a Bundle,
85
+ * attach that completed Bundle to a Communication outbox job, and call
86
+ * `ingestCommunicationAndUpdateIndex(...)`.
83
87
  */
84
88
  upsertRelatedPersonAndPoll(ctx: RouteContext, input: RelatedPersonUpsertInput): Promise<SubmitAndPollResult>;
85
89
  /**
@@ -111,7 +111,11 @@ export class IndividualControllerSdk {
111
111
  return requireClientMethod(this.client, 'importIpsOrFhirAndUpdateIndex')(ctx, input);
112
112
  }
113
113
  /**
114
- * Creates or updates a `RelatedPerson` for non-employee caregivers or family members.
114
+ * Compatibility adapter for the older direct `RelatedPerson` route.
115
+ *
116
+ * @deprecated Author one or more typed RelatedPerson entries in a Bundle,
117
+ * attach that completed Bundle to a Communication outbox job, and call
118
+ * `ingestCommunicationAndUpdateIndex(...)`.
115
119
  */
116
120
  upsertRelatedPersonAndPoll(ctx, input) {
117
121
  assertFacadeCapability(this.capabilities, ActorCapabilities.IndividualUpsertRelatedPerson, ActorKinds.IndividualController, 'upsertRelatedPersonAndPoll');
@@ -7,7 +7,10 @@ export declare class IndividualMemberSdk {
7
7
  private readonly client;
8
8
  constructor(client: NodeRuntimeClient);
9
9
  /**
10
- * Creates or updates the member/caregiver `RelatedPerson` relationship to the subject.
10
+ * Compatibility adapter for the older direct `RelatedPerson` route.
11
+ *
12
+ * @deprecated Author a typed RelatedPerson Bundle, attach it to a
13
+ * Communication outbox job, and call `ingestCommunicationAndUpdateIndex(...)`.
11
14
  */
12
15
  upsertRelatedPersonAndPoll(ctx: RouteContext, input: RelatedPersonUpsertInput): Promise<import("gdc-sdk-core-ts/polling-model.js").SubmitAndPollResult>;
13
16
  /**
@@ -6,7 +6,10 @@ export class IndividualMemberSdk {
6
6
  this.client = client;
7
7
  }
8
8
  /**
9
- * Creates or updates the member/caregiver `RelatedPerson` relationship to the subject.
9
+ * Compatibility adapter for the older direct `RelatedPerson` route.
10
+ *
11
+ * @deprecated Author a typed RelatedPerson Bundle, attach it to a
12
+ * Communication outbox job, and call `ingestCommunicationAndUpdateIndex(...)`.
10
13
  */
11
14
  upsertRelatedPersonAndPoll(ctx, input) {
12
15
  return requireClientMethod(this.client, 'upsertRelatedPersonAndPoll')(ctx, input);
@@ -1,5 +1,6 @@
1
1
  import type { JWK } from 'gdc-common-utils-ts/models/jwk';
2
2
  import type { ActorKind } from 'gdc-common-utils-ts/models/actor-session';
3
+ import type { ConfidentialStorageProfile } from 'gdc-sdk-core-ts';
3
4
  import type { RouteContext } from './individual-onboarding.js';
4
5
  import type { SecureDidcommTransportAdapter } from 'gdc-sdk-core-ts';
5
6
  import { type PinProtectedProfileSecret, type ProfileProtectionOptions, type ServerProfileSealer } from './server-profile-protection.js';
@@ -19,6 +20,10 @@ export type ServerProfileRecord = Readonly<{
19
20
  clientId: string;
20
21
  deviceDid: string;
21
22
  publicJwks: Record<string, unknown>[];
23
+ /** Public recipient key for local confidential storage; not a DCR communication key. */
24
+ storagePublicJwk?: Record<string, unknown>;
25
+ /** Server-owned policy; browser input is never authoritative for this value. */
26
+ confidentialStorageProfile?: ConfidentialStorageProfile;
22
27
  protectedWalletSeed: PinProtectedProfileSecret;
23
28
  protectedVpToken: PinProtectedProfileSecret;
24
29
  failedUnlocks: number;
@@ -77,18 +82,30 @@ export type ResolvedServerProfileSession = Readonly<{
77
82
  scopes: string[];
78
83
  accessToken: string;
79
84
  secureTransportAdapter: SecureDidcommTransportAdapter;
85
+ /** Storage adapter available only while the PIN-unlocked session is alive. */
86
+ confidentialStorageAdapter: Readonly<{
87
+ protect(document: Readonly<{
88
+ id?: string;
89
+ content: unknown;
90
+ }>): Promise<unknown>;
91
+ unprotect(document: Readonly<{
92
+ id?: string;
93
+ jwe: string;
94
+ }>): Promise<unknown>;
95
+ }>;
80
96
  }>;
81
97
  export type ServerProfileSessionManagerOptions = Readonly<{
82
98
  store: ServerProfileStore;
83
99
  sealer: ServerProfileSealer;
84
100
  gatewayBaseUrl: string;
85
- recipientDid: string;
86
101
  resolveRecipientJwk: (recipientDid: string) => Promise<JWK>;
87
102
  fetchImpl?: typeof fetch;
88
103
  sessionTtlSeconds?: number;
89
104
  maxFailedUnlocks?: number;
90
105
  lockSeconds?: number;
91
106
  profileProtection?: ProfileProtectionOptions;
107
+ /** Product/tenant policy applied to enrollment and upgraded on next PIN unlock. */
108
+ requiredConfidentialStorageProfile?: ConfidentialStorageProfile;
92
109
  now?: () => Date;
93
110
  }>;
94
111
  /**
@@ -109,6 +126,13 @@ export declare class ServerProfileSessionManager {
109
126
  lock(ownerId: string, sessionId: string): Promise<void>;
110
127
  private createClient;
111
128
  private createWallet;
129
+ /**
130
+ * Upgrade legacy profiles deterministically after successful PIN unlock.
131
+ * The protected seed already owns the storage pair, so migration neither
132
+ * exports a private key nor calls KMS/GW. Only its public JWK and policy label
133
+ * are added to the durable profile record.
134
+ */
135
+ private ensureRequiredStorageProfile;
112
136
  private requireOwnedProfile;
113
137
  private requireSubject;
114
138
  private now;
@@ -21,6 +21,9 @@ export class ServerProfileSessionManager {
21
21
  const wallet = await this.createWallet(input.profileId, seed);
22
22
  const context = walletContext(input.profileId);
23
23
  const publicKeys = await wallet.getPublicJwks(context, {});
24
+ const storagePublicJwk = publicKeys.find((entry) => entry.purpose === 'document-at-rest')?.publicJwk;
25
+ if (!storagePublicJwk)
26
+ throw new Error('Server profile enrollment requires a document-at-rest ML-KEM key.');
24
27
  const client = this.createClient(input.routeContext, input.idToken);
25
28
  const activation = await client.activateProfileDeviceWithActivationRequest({
26
29
  ...input.routeContext,
@@ -30,7 +33,7 @@ export class ServerProfileSessionManager {
30
33
  application_type: 'web',
31
34
  actor_did: input.actorDid,
32
35
  profile_did: input.profileDid,
33
- jwks: { keys: publicKeys.map((entry) => entry.publicJwk) },
36
+ jwks: { keys: publicKeys.filter((entry) => entry.purpose !== 'document-at-rest').map((entry) => entry.publicJwk) },
34
37
  },
35
38
  });
36
39
  const dcrBody = terminalBody(activation.dcr.poll.body);
@@ -51,7 +54,9 @@ export class ServerProfileSessionManager {
51
54
  allowedSubjectDids: unique(input.allowedSubjectDids),
52
55
  clientId,
53
56
  deviceDid,
54
- publicJwks: publicKeys.map((entry) => entry.publicJwk),
57
+ publicJwks: publicKeys.filter((entry) => entry.purpose !== 'document-at-rest').map((entry) => entry.publicJwk),
58
+ storagePublicJwk: storagePublicJwk,
59
+ confidentialStorageProfile: this.options.requiredConfidentialStorageProfile ?? 'confidential-basic-v1',
55
60
  protectedWalletSeed: await protectServerProfileSecret(seed, input.pin, `${input.profileId}:wallet-seed`, this.options.sealer, this.options.profileProtection),
56
61
  protectedVpToken: await protectServerProfileSecret(input.vpToken, input.pin, `${input.profileId}:vp-token`, this.options.sealer, this.options.profileProtection),
57
62
  failedUnlocks: 0,
@@ -65,7 +70,7 @@ export class ServerProfileSessionManager {
65
70
  return this.options.store.listProfiles(ownerId);
66
71
  }
67
72
  async unlock(input) {
68
- const profile = await this.requireOwnedProfile(input.ownerId, input.profileId);
73
+ let profile = await this.requireOwnedProfile(input.ownerId, input.profileId);
69
74
  this.requireSubject(profile, input.subjectDid);
70
75
  const now = this.now();
71
76
  if (profile.lockedUntil && new Date(profile.lockedUntil) > now) {
@@ -88,6 +93,7 @@ export class ServerProfileSessionManager {
88
93
  await this.options.store.putProfile({ ...profile, failedUnlocks: failures, lockedUntil, updatedAt: now.toISOString() });
89
94
  throw new Error('Profile PIN rejected.');
90
95
  }
96
+ profile = await this.ensureRequiredStorageProfile(profile, seed);
91
97
  const wallet = await this.createWallet(profile.profileId, seed);
92
98
  const assertion = await buildWalletClientAssertion(wallet, profile, this.options.gatewayBaseUrl, now);
93
99
  const token = await this.createClient(profile.routeContext, input.idToken).requestSmartToken({
@@ -141,9 +147,13 @@ export class ServerProfileSessionManager {
141
147
  scopes: session.scopes,
142
148
  accessToken: await this.options.sealer.unseal(session.sealedAccessToken, `${sessionId}:access-token`),
143
149
  secureTransportAdapter: {
144
- pack: (message) => wallet.packForRecipientWithContext(message, this.options.recipientDid, { context }),
150
+ pack: (message) => wallet.packForRecipientWithContext(message, profile.providerDid, { context }),
145
151
  unpack: async (jwe) => (await wallet.unpackWithContext(jwe, { context })).content,
146
152
  },
153
+ confidentialStorageAdapter: {
154
+ protect: (document) => wallet.protectManagedConfidentialData(document, context),
155
+ unprotect: (document) => wallet.unprotectManagedConfidentialData(document, context),
156
+ },
147
157
  };
148
158
  }
149
159
  async lock(ownerId, sessionId) {
@@ -164,7 +174,7 @@ export class ServerProfileSessionManager {
164
174
  const context = walletContext(profileId);
165
175
  await wallet.provisionManagedKeys(context, {
166
176
  ownerScope: 'profile',
167
- purposes: ['actor-signing'],
177
+ purposes: ['actor-signing', 'document-at-rest'],
168
178
  mode: 'deterministic',
169
179
  seedMaterial: seed,
170
180
  });
@@ -176,6 +186,35 @@ export class ServerProfileSessionManager {
176
186
  });
177
187
  return wallet;
178
188
  }
189
+ /**
190
+ * Upgrade legacy profiles deterministically after successful PIN unlock.
191
+ * The protected seed already owns the storage pair, so migration neither
192
+ * exports a private key nor calls KMS/GW. Only its public JWK and policy label
193
+ * are added to the durable profile record.
194
+ */
195
+ async ensureRequiredStorageProfile(profile, seed) {
196
+ const required = this.options.requiredConfidentialStorageProfile ?? profile.confidentialStorageProfile ?? 'confidential-basic-v1';
197
+ if (required !== 'confidential-pqc-v1')
198
+ return profile;
199
+ const wallet = await this.createWallet(profile.profileId, seed);
200
+ const storageKeys = await wallet.getPublicJwks(walletContext(profile.profileId), {
201
+ ownerScope: 'profile', purpose: 'document-at-rest', alg: 'ML-KEM-768',
202
+ });
203
+ const storagePublicJwk = storageKeys[0]?.publicJwk;
204
+ if (!storagePublicJwk)
205
+ throw new Error('Required ML-KEM storage key could not be provisioned.');
206
+ const hasKey = profile.storagePublicJwk?.kid === storagePublicJwk.kid;
207
+ if (profile.confidentialStorageProfile === required && hasKey)
208
+ return profile;
209
+ const upgraded = {
210
+ ...profile,
211
+ confidentialStorageProfile: required,
212
+ storagePublicJwk,
213
+ updatedAt: this.now().toISOString(),
214
+ };
215
+ await this.options.store.putProfile(upgraded);
216
+ return upgraded;
217
+ }
179
218
  async requireOwnedProfile(ownerId, profileId) {
180
219
  const profile = await this.options.store.getProfile(profileId);
181
220
  if (!profile || profile.ownerId !== ownerId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-sdk-node-ts",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
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.",
@@ -35,8 +35,8 @@
35
35
  "test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
36
36
  },
37
37
  "dependencies": {
38
- "gdc-common-utils-ts": "^2.3.1",
39
- "gdc-sdk-core-ts": "~2.3.1"
38
+ "gdc-common-utils-ts": "^2.3.2",
39
+ "gdc-sdk-core-ts": "~2.3.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^20.14.10",