gdc-common-utils-ts 2.0.18 → 2.1.0

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.
Files changed (57) hide show
  1. package/README.md +0 -1
  2. package/dist/examples/bundle-didcomm-payload.d.ts +11 -0
  3. package/dist/examples/bundle-didcomm-payload.js +13 -0
  4. package/dist/examples/communication-attached-bundle-session.js +2 -1
  5. package/dist/examples/communication-didcomm-payload.d.ts +35 -0
  6. package/dist/examples/communication-didcomm-payload.js +38 -0
  7. package/dist/examples/index.d.ts +4 -0
  8. package/dist/examples/index.js +4 -0
  9. package/dist/examples/profile-manager-mem.d.ts +45 -0
  10. package/dist/examples/profile-manager-mem.js +29 -0
  11. package/dist/examples/shared.d.ts +1 -0
  12. package/dist/examples/shared.js +1 -0
  13. package/dist/examples/wallet-mem.d.ts +38 -0
  14. package/dist/examples/wallet-mem.js +40 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/interfaces/IProfileOutboxRepository.d.ts +36 -0
  18. package/dist/interfaces/IProfileOutboxRepository.js +2 -0
  19. package/dist/interfaces/IWallet.d.ts +74 -0
  20. package/dist/interfaces/IWallet.js +2 -0
  21. package/dist/interfaces/IWalletQueue.d.ts +30 -0
  22. package/dist/interfaces/IWalletQueue.js +2 -0
  23. package/dist/interfaces/index.d.ts +8 -0
  24. package/dist/interfaces/index.js +8 -0
  25. package/dist/models/communication-attached-bundle-session.d.ts +193 -0
  26. package/dist/models/communication-attached-bundle-session.js +34 -0
  27. package/dist/models/index.d.ts +3 -0
  28. package/dist/models/index.js +3 -0
  29. package/dist/models/interoperable-claims/diagnostic-report-claims.d.ts +1 -2
  30. package/dist/models/interoperable-claims/diagnostic-report-claims.js +1 -2
  31. package/dist/models/profile-manager.d.ts +85 -0
  32. package/dist/models/profile-manager.js +2 -0
  33. package/dist/models/wallet.d.ts +59 -0
  34. package/dist/models/wallet.js +22 -0
  35. package/dist/utils/backend-message-manager-mem.d.ts +67 -0
  36. package/dist/utils/backend-message-manager-mem.js +227 -0
  37. package/dist/utils/bundle-didcomm-payload.d.ts +29 -0
  38. package/dist/utils/bundle-didcomm-payload.js +48 -0
  39. package/dist/utils/communication-attached-bundle-session-helpers.d.ts +54 -0
  40. package/dist/utils/communication-attached-bundle-session-helpers.js +517 -0
  41. package/dist/utils/communication-attached-bundle-session.d.ts +59 -302
  42. package/dist/utils/communication-attached-bundle-session.js +114 -839
  43. package/dist/utils/communication-consent-access-editor.d.ts +85 -0
  44. package/dist/utils/communication-consent-access-editor.js +244 -0
  45. package/dist/utils/communication-didcomm-payload.d.ts +33 -0
  46. package/dist/utils/communication-didcomm-payload.js +75 -0
  47. package/dist/utils/index.d.ts +8 -0
  48. package/dist/utils/index.js +8 -0
  49. package/dist/utils/profile-manager-mem.d.ts +69 -0
  50. package/dist/utils/profile-manager-mem.js +79 -0
  51. package/dist/utils/profile-outbox-memory-repository.d.ts +34 -0
  52. package/dist/utils/profile-outbox-memory-repository.js +57 -0
  53. package/dist/utils/wallet-mem.d.ts +93 -0
  54. package/dist/utils/wallet-mem.js +277 -0
  55. package/dist/utils/wallet-memory-queue.d.ts +32 -0
  56. package/dist/utils/wallet-memory-queue.js +82 -0
  57. package/package.json +1 -1
@@ -0,0 +1,57 @@
1
+ // Copyright 2026 Conectate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
2
+ function clone(value) {
3
+ return JSON.parse(JSON.stringify(value));
4
+ }
5
+ /**
6
+ * In-memory profile outbox repository used by low-level tests and as the
7
+ * default history/response store for `ProfileManagerMem`.
8
+ */
9
+ export class MemoryProfileOutboxRepository {
10
+ messages = new Map();
11
+ responses = new Map();
12
+ /**
13
+ * Stores one immutable message history record.
14
+ */
15
+ async putMessage(record) {
16
+ this.messages.set(record.id, clone(record));
17
+ return clone(record);
18
+ }
19
+ /**
20
+ * Reads one message history record.
21
+ */
22
+ async getMessage(messageId) {
23
+ const stored = this.messages.get(String(messageId || '').trim());
24
+ return stored ? clone(stored) : undefined;
25
+ }
26
+ /**
27
+ * Lists all message history records in insertion order.
28
+ */
29
+ async listMessages() {
30
+ return [...this.messages.values()].map((record) => clone(record));
31
+ }
32
+ /**
33
+ * Stores one decoded response record grouped by thread id.
34
+ */
35
+ async putResponse(record) {
36
+ const normalizedThreadId = String(record.thid || '').trim();
37
+ const current = this.responses.get(normalizedThreadId) || [];
38
+ current.push(clone(record));
39
+ this.responses.set(normalizedThreadId, current);
40
+ return clone(record);
41
+ }
42
+ /**
43
+ * Returns the latest decoded response for the provided thread id.
44
+ */
45
+ async getLatestResponseByThreadId(thid) {
46
+ const stored = this.responses.get(String(thid || '').trim());
47
+ const latest = stored?.[stored.length - 1];
48
+ return latest ? clone(latest) : undefined;
49
+ }
50
+ /**
51
+ * Lists all decoded responses across all threads.
52
+ */
53
+ async listResponses() {
54
+ return [...this.responses.values()]
55
+ .flatMap((records) => records.map((record) => clone(record)));
56
+ }
57
+ }
@@ -0,0 +1,93 @@
1
+ import { CryptographyService } from '../CryptographyService';
2
+ import type { ICryptoHelper } from '../interfaces/ICryptoHelper';
3
+ import type { IWallet } from '../interfaces/IWallet';
4
+ import type { IWalletQueue } from '../interfaces/IWalletQueue';
5
+ import type { JWK, JwkSet } from '../models/jwk';
6
+ import { type WalletEnqueueMessageInput, type WalletManagedEntityDescriptor, type WalletQueuedMessage } from '../models/wallet';
7
+ export type WalletMemOptions = Readonly<{
8
+ cryptoHelper: ICryptoHelper;
9
+ cryptography?: CryptographyService;
10
+ queue?: IWalletQueue;
11
+ }>;
12
+ /**
13
+ * In-memory wallet implementation intended for:
14
+ * - common low-level tests
15
+ * - GW template integration tests
16
+ * - frontend-like and BFF-like local transport simulations
17
+ *
18
+ * Design rule:
19
+ * - keep this class below actor-specific SDK facades
20
+ * - compose cryptography, key cache, local queue, and DIDComm-style
21
+ * pack/unpack in one portable runtime-neutral helper
22
+ */
23
+ export declare class WalletMem implements IWallet {
24
+ private readonly cryptoHelper;
25
+ private readonly cryptography;
26
+ private readonly entities;
27
+ private readonly queue;
28
+ constructor(options: WalletMemOptions);
29
+ /**
30
+ * Computes one digest through the injected platform helper.
31
+ */
32
+ digest(data: string, algorithm: string): Promise<string>;
33
+ /**
34
+ * Provisions one deterministic signing key, one deterministic encryption key,
35
+ * and one deterministic storage key for the provided entity id.
36
+ */
37
+ provisionKeys(entityId: string): Promise<JwkSet>;
38
+ /**
39
+ * Returns the public signing/encryption keys currently provisioned for one entity.
40
+ */
41
+ getPublicKeys(entityId: string): Promise<WalletManagedEntityDescriptor>;
42
+ /**
43
+ * Protects one document in memory using the entity-specific at-rest key.
44
+ */
45
+ protectConfidentialData(doc: unknown, entityId?: string): Promise<unknown>;
46
+ /**
47
+ * Unprotects one document previously encrypted through `protectConfidentialData(...)`.
48
+ */
49
+ unprotectConfidentialData(doc: unknown, entityId?: string): Promise<unknown>;
50
+ /**
51
+ * Signs one JSON payload into a compact JWS carrying the sender public JWK.
52
+ */
53
+ signCompactJws(entityId: string, claims: Record<string, unknown>): Promise<string>;
54
+ /**
55
+ * Encrypts one plaintext for the provided recipient public JWK.
56
+ */
57
+ buildCompactJwe(entityId: string, plaintext: string | Uint8Array, recipientJwk: JWK, contentType?: string): Promise<string>;
58
+ /**
59
+ * Packs one payload as nested compact JWS inside compact JWE.
60
+ */
61
+ pack(content: Record<string, unknown>, recipientJwk: JWK, entityId: string): Promise<string>;
62
+ /**
63
+ * Unpacks one compact JWE and, when present, verifies the nested compact JWS.
64
+ */
65
+ unpack(packedMessage: string, entityId?: string): Promise<{
66
+ content: unknown;
67
+ meta: Record<string, unknown>;
68
+ }>;
69
+ /**
70
+ * Enqueues one message in the local in-memory outbox/queue.
71
+ */
72
+ enqueueMessage(input: WalletEnqueueMessageInput): Promise<WalletQueuedMessage>;
73
+ /**
74
+ * Returns the highest-priority pending message without mutating queue state.
75
+ */
76
+ peekNextMessage(): Promise<WalletQueuedMessage | undefined>;
77
+ /**
78
+ * Marks one queued message as delivered.
79
+ */
80
+ markMessageDelivered(messageId: string): Promise<WalletQueuedMessage>;
81
+ /**
82
+ * Marks one queued message as failed while preserving it in the local queue.
83
+ */
84
+ markMessageFailed(messageId: string, errorMessage: string): Promise<WalletQueuedMessage>;
85
+ /**
86
+ * Lists all queued messages in priority/FIFO processing order.
87
+ */
88
+ listMessages(): Promise<WalletQueuedMessage[]>;
89
+ private deriveSeedBytes;
90
+ private requireEntityId;
91
+ private resolveEntityId;
92
+ private requireEntityState;
93
+ }
@@ -0,0 +1,277 @@
1
+ // Copyright 2026 Conectate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
2
+ import { CryptographyService } from '../CryptographyService.js';
3
+ import { WalletQueueStatuses } from '../models/wallet.js';
4
+ import { Content } from './content.js';
5
+ import { MemoryWalletQueue } from './wallet-memory-queue.js';
6
+ const WALLET_DIGEST_ALGORITHM = 'SHA-512';
7
+ const WALLET_SIGNING_ALGORITHM = 'ML-DSA-44';
8
+ const WALLET_ENCRYPTION_ALGORITHM = 'ML-KEM-768';
9
+ const WALLET_JWE_CONTENT_ENCRYPTION = 'A256GCM';
10
+ const WALLET_JWS_TYPE = 'JWS';
11
+ const WALLET_JWE_TYPE = 'JWE';
12
+ function clone(value) {
13
+ return JSON.parse(JSON.stringify(value));
14
+ }
15
+ /**
16
+ * In-memory wallet implementation intended for:
17
+ * - common low-level tests
18
+ * - GW template integration tests
19
+ * - frontend-like and BFF-like local transport simulations
20
+ *
21
+ * Design rule:
22
+ * - keep this class below actor-specific SDK facades
23
+ * - compose cryptography, key cache, local queue, and DIDComm-style
24
+ * pack/unpack in one portable runtime-neutral helper
25
+ */
26
+ export class WalletMem {
27
+ cryptoHelper;
28
+ cryptography;
29
+ entities = new Map();
30
+ queue;
31
+ constructor(options) {
32
+ this.cryptoHelper = options.cryptoHelper;
33
+ this.cryptography = options.cryptography ?? new CryptographyService(this.cryptoHelper);
34
+ this.queue = options.queue ?? new MemoryWalletQueue(this.cryptoHelper);
35
+ }
36
+ /**
37
+ * Computes one digest through the injected platform helper.
38
+ */
39
+ async digest(data, algorithm) {
40
+ return this.cryptoHelper.digestString(data, algorithm);
41
+ }
42
+ /**
43
+ * Provisions one deterministic signing key, one deterministic encryption key,
44
+ * and one deterministic storage key for the provided entity id.
45
+ */
46
+ async provisionKeys(entityId) {
47
+ const normalizedEntityId = this.requireEntityId(entityId);
48
+ const existing = this.entities.get(normalizedEntityId);
49
+ if (existing) {
50
+ return {
51
+ keys: [
52
+ existing.descriptor.signingJwk,
53
+ existing.descriptor.encryptionJwk,
54
+ ],
55
+ };
56
+ }
57
+ const signingSeed = await this.deriveSeedBytes(normalizedEntityId, 'signing', 32);
58
+ const encryptionSeed = await this.deriveSeedBytes(normalizedEntityId, 'encryption', 64);
59
+ const storageKeyBytes = await this.deriveSeedBytes(normalizedEntityId, 'storage', 32);
60
+ const signingKeyPair = await this.cryptography.generateKeyPairMlDsa(signingSeed, WALLET_SIGNING_ALGORITHM);
61
+ const encryptionKeyPair = await this.cryptography.generateKeyPairMlKem(encryptionSeed, WALLET_ENCRYPTION_ALGORITHM);
62
+ const descriptor = {
63
+ entityId: normalizedEntityId,
64
+ signingJwk: {
65
+ ...signingKeyPair.publicJWKey,
66
+ use: 'sig',
67
+ },
68
+ encryptionJwk: {
69
+ ...encryptionKeyPair.publicJWKey,
70
+ use: 'enc',
71
+ },
72
+ };
73
+ this.entities.set(normalizedEntityId, {
74
+ descriptor,
75
+ signingSecretKeyBytes: signingKeyPair.secretKeyBytes,
76
+ encryptionSecretKeyBytes: encryptionKeyPair.secretKeyBytes,
77
+ storageKeyBytes,
78
+ });
79
+ return {
80
+ keys: [descriptor.signingJwk, descriptor.encryptionJwk],
81
+ };
82
+ }
83
+ /**
84
+ * Returns the public signing/encryption keys currently provisioned for one entity.
85
+ */
86
+ async getPublicKeys(entityId) {
87
+ const state = this.requireEntityState(entityId);
88
+ return clone(state.descriptor);
89
+ }
90
+ /**
91
+ * Protects one document in memory using the entity-specific at-rest key.
92
+ */
93
+ async protectConfidentialData(doc, entityId) {
94
+ const document = doc;
95
+ if (document?.content === undefined) {
96
+ return doc;
97
+ }
98
+ const state = this.requireEntityState(this.resolveEntityId(entityId));
99
+ const ownerId = state.descriptor.entityId;
100
+ const encrypted = await this.cryptography.encrypt(JSON.stringify(document.content), state.storageKeyBytes, ownerId);
101
+ const { content, ...docWithoutContent } = document;
102
+ return {
103
+ ...docWithoutContent,
104
+ jwe: encrypted,
105
+ };
106
+ }
107
+ /**
108
+ * Unprotects one document previously encrypted through `protectConfidentialData(...)`.
109
+ */
110
+ async unprotectConfidentialData(doc, entityId) {
111
+ const document = doc;
112
+ if (document?.jwe === undefined) {
113
+ return doc;
114
+ }
115
+ const state = this.requireEntityState(this.resolveEntityId(entityId));
116
+ const ownerId = state.descriptor.entityId;
117
+ const decrypted = await this.cryptography.decrypt(document.jwe, state.storageKeyBytes, ownerId);
118
+ const { jwe, ...docWithoutJwe } = document;
119
+ return {
120
+ ...docWithoutJwe,
121
+ content: JSON.parse(decrypted),
122
+ };
123
+ }
124
+ /**
125
+ * Signs one JSON payload into a compact JWS carrying the sender public JWK.
126
+ */
127
+ async signCompactJws(entityId, claims) {
128
+ const state = this.requireEntityState(entityId);
129
+ const jws = await this.cryptography.signDataJws(claims, {
130
+ alg: WALLET_SIGNING_ALGORITHM,
131
+ typ: WALLET_JWS_TYPE,
132
+ kid: state.descriptor.signingJwk.kid,
133
+ jwk: state.descriptor.signingJwk,
134
+ }, state.signingSecretKeyBytes);
135
+ return `${jws.protected}.${jws.payload}.${jws.signature}`;
136
+ }
137
+ /**
138
+ * Encrypts one plaintext for the provided recipient public JWK.
139
+ */
140
+ async buildCompactJwe(entityId, plaintext, recipientJwk, contentType) {
141
+ const state = this.requireEntityState(entityId);
142
+ const senderPrivateKey = {
143
+ ...state.descriptor.encryptionJwk,
144
+ dBytes: state.encryptionSecretKeyBytes,
145
+ };
146
+ return this.cryptography.encryptJweToCompact(typeof plaintext === 'string' ? plaintext : Content.bytesToStringUTF8(plaintext), {
147
+ enc: WALLET_JWE_CONTENT_ENCRYPTION,
148
+ typ: WALLET_JWE_TYPE,
149
+ ...(contentType ? { cty: contentType } : {}),
150
+ }, senderPrivateKey, recipientJwk);
151
+ }
152
+ /**
153
+ * Packs one payload as nested compact JWS inside compact JWE.
154
+ */
155
+ async pack(content, recipientJwk, entityId) {
156
+ const compactJws = await this.signCompactJws(entityId, content);
157
+ return this.buildCompactJwe(entityId, compactJws, recipientJwk, WALLET_JWS_TYPE);
158
+ }
159
+ /**
160
+ * Unpacks one compact JWE and, when present, verifies the nested compact JWS.
161
+ */
162
+ async unpack(packedMessage, entityId) {
163
+ const state = this.requireEntityState(this.resolveEntityId(entityId));
164
+ const recipientPrivateKey = {
165
+ ...state.descriptor.encryptionJwk,
166
+ dBytes: state.encryptionSecretKeyBytes,
167
+ };
168
+ const decrypted = await this.cryptography.decryptJwe(packedMessage, recipientPrivateKey);
169
+ const decryptedText = Content.bytesToStringUTF8(decrypted.decryptedBytes);
170
+ const metadata = {
171
+ jwe: {
172
+ header: decrypted.protectedHeader,
173
+ },
174
+ };
175
+ if (decrypted.protectedHeader['cty'] === WALLET_JWS_TYPE) {
176
+ const parsedJws = this.cryptography.parseCompactJws(decryptedText);
177
+ const protectedHeader = parsedJws.protected;
178
+ const publicJwk = protectedHeader['jwk'];
179
+ const verified = publicJwk
180
+ ? await this.cryptography.verifyJws(decryptedText, publicJwk)
181
+ : false;
182
+ metadata.jws = {
183
+ protected: protectedHeader,
184
+ signature: typeof parsedJws.signature === 'string'
185
+ ? parsedJws.signature
186
+ : parsedJws.signature
187
+ ? Content.bytesToRawBase64UrlSafe(parsedJws.signature)
188
+ : '',
189
+ };
190
+ return {
191
+ content: parsedJws.payload,
192
+ meta: {
193
+ ...metadata,
194
+ jws: {
195
+ ...metadata.jws,
196
+ verified,
197
+ },
198
+ },
199
+ };
200
+ }
201
+ return {
202
+ content: JSON.parse(decryptedText),
203
+ meta: metadata,
204
+ };
205
+ }
206
+ /**
207
+ * Enqueues one message in the local in-memory outbox/queue.
208
+ */
209
+ async enqueueMessage(input) {
210
+ return this.queue.enqueue(input);
211
+ }
212
+ /**
213
+ * Returns the highest-priority pending message without mutating queue state.
214
+ */
215
+ async peekNextMessage() {
216
+ return this.queue.peekNextPending();
217
+ }
218
+ /**
219
+ * Marks one queued message as delivered.
220
+ */
221
+ async markMessageDelivered(messageId) {
222
+ return this.queue.update(messageId, {
223
+ status: WalletQueueStatuses.Delivered,
224
+ deliveredAt: new Date().toISOString(),
225
+ errorMessage: undefined,
226
+ });
227
+ }
228
+ /**
229
+ * Marks one queued message as failed while preserving it in the local queue.
230
+ */
231
+ async markMessageFailed(messageId, errorMessage) {
232
+ return this.queue.update(messageId, {
233
+ status: WalletQueueStatuses.Failed,
234
+ errorMessage: String(errorMessage || '').trim(),
235
+ });
236
+ }
237
+ /**
238
+ * Lists all queued messages in priority/FIFO processing order.
239
+ */
240
+ async listMessages() {
241
+ return this.queue.list();
242
+ }
243
+ async deriveSeedBytes(entityId, purpose, size) {
244
+ const blocks = [];
245
+ let counter = 0;
246
+ while (Buffer.concat(blocks).length < size) {
247
+ const digest = await this.digest(`${entityId}:${purpose}:${counter}`, WALLET_DIGEST_ALGORITHM);
248
+ blocks.push(Buffer.from(digest, 'hex'));
249
+ counter += 1;
250
+ }
251
+ return Buffer.concat(blocks).subarray(0, size);
252
+ }
253
+ requireEntityId(entityId) {
254
+ const normalized = String(entityId || '').trim();
255
+ if (!normalized) {
256
+ throw new Error('WalletMem requires a non-empty entityId.');
257
+ }
258
+ return normalized;
259
+ }
260
+ resolveEntityId(entityId) {
261
+ if (entityId) {
262
+ return this.requireEntityId(entityId);
263
+ }
264
+ if (this.entities.size === 1) {
265
+ return [...this.entities.keys()][0];
266
+ }
267
+ throw new Error('WalletMem requires entityId when more than one entity is provisioned.');
268
+ }
269
+ requireEntityState(entityId) {
270
+ const normalized = this.requireEntityId(entityId);
271
+ const state = this.entities.get(normalized);
272
+ if (!state) {
273
+ throw new Error(`WalletMem requires provisionKeys('${normalized}') before this operation.`);
274
+ }
275
+ return state;
276
+ }
277
+ }
@@ -0,0 +1,32 @@
1
+ import type { ICryptoHelper } from '../interfaces/ICryptoHelper';
2
+ import type { IWalletQueue } from '../interfaces/IWalletQueue';
3
+ import { type WalletEnqueueMessageInput, type WalletQueuedMessage } from '../models/wallet';
4
+ /**
5
+ * Default in-memory queue/outbox implementation for `WalletMem`.
6
+ *
7
+ * Contract:
8
+ * - `emergency` outranks `high`, `normal`, and `low`
9
+ * - within the same priority level, FIFO order is preserved through `sequence`
10
+ */
11
+ export declare class MemoryWalletQueue implements IWalletQueue {
12
+ private readonly cryptoHelper;
13
+ private readonly messages;
14
+ private sequence;
15
+ constructor(cryptoHelper: ICryptoHelper);
16
+ /**
17
+ * Enqueues one outbound message.
18
+ */
19
+ enqueue(input: WalletEnqueueMessageInput): Promise<WalletQueuedMessage>;
20
+ /**
21
+ * Returns the next pending message without mutating queue state.
22
+ */
23
+ peekNextPending(): Promise<WalletQueuedMessage | undefined>;
24
+ /**
25
+ * Updates one queued message.
26
+ */
27
+ update(messageId: string, patch: Partial<WalletQueuedMessage>): Promise<WalletQueuedMessage>;
28
+ /**
29
+ * Lists all queued messages in processing order.
30
+ */
31
+ list(): Promise<WalletQueuedMessage[]>;
32
+ }
@@ -0,0 +1,82 @@
1
+ // Copyright 2026 Conectate Soluciones y Aplicaciones SL under the Apache License, Version 2.0.
2
+ import { WalletMessagePriorities, WalletQueueStatuses, } from '../models/wallet.js';
3
+ const WalletMessagePriorityOrder = {
4
+ [WalletMessagePriorities.Emergency]: 0,
5
+ [WalletMessagePriorities.High]: 1,
6
+ [WalletMessagePriorities.Normal]: 2,
7
+ [WalletMessagePriorities.Low]: 3,
8
+ };
9
+ function clone(value) {
10
+ return JSON.parse(JSON.stringify(value));
11
+ }
12
+ function sortQueue(messages) {
13
+ return [...messages].sort((left, right) => {
14
+ const priorityDelta = WalletMessagePriorityOrder[left.priority] - WalletMessagePriorityOrder[right.priority];
15
+ if (priorityDelta !== 0)
16
+ return priorityDelta;
17
+ return left.sequence - right.sequence;
18
+ });
19
+ }
20
+ /**
21
+ * Default in-memory queue/outbox implementation for `WalletMem`.
22
+ *
23
+ * Contract:
24
+ * - `emergency` outranks `high`, `normal`, and `low`
25
+ * - within the same priority level, FIFO order is preserved through `sequence`
26
+ */
27
+ export class MemoryWalletQueue {
28
+ cryptoHelper;
29
+ messages = new Map();
30
+ sequence = 0;
31
+ constructor(cryptoHelper) {
32
+ this.cryptoHelper = cryptoHelper;
33
+ }
34
+ /**
35
+ * Enqueues one outbound message.
36
+ */
37
+ async enqueue(input) {
38
+ this.sequence += 1;
39
+ const queued = {
40
+ id: String(input.id || this.cryptoHelper.randomUUID()).trim(),
41
+ createdAt: String(input.createdAt || new Date().toISOString()).trim(),
42
+ priority: input.priority || WalletMessagePriorities.Normal,
43
+ status: WalletQueueStatuses.Pending,
44
+ sequence: this.sequence,
45
+ payload: clone(input.payload),
46
+ ...(input.thid ? { thid: input.thid } : {}),
47
+ ...(input.messageType ? { messageType: input.messageType } : {}),
48
+ };
49
+ this.messages.set(queued.id, queued);
50
+ return clone(queued);
51
+ }
52
+ /**
53
+ * Returns the next pending message without mutating queue state.
54
+ */
55
+ async peekNextPending() {
56
+ const pending = sortQueue([...this.messages.values()].filter((message) => message.status === WalletQueueStatuses.Pending));
57
+ const next = pending[0];
58
+ return next ? clone(next) : undefined;
59
+ }
60
+ /**
61
+ * Updates one queued message.
62
+ */
63
+ async update(messageId, patch) {
64
+ const normalizedMessageId = String(messageId || '').trim();
65
+ const current = this.messages.get(normalizedMessageId);
66
+ if (!current) {
67
+ throw new Error(`MemoryWalletQueue message not found: ${normalizedMessageId}`);
68
+ }
69
+ const updated = {
70
+ ...current,
71
+ ...patch,
72
+ };
73
+ this.messages.set(normalizedMessageId, updated);
74
+ return clone(updated);
75
+ }
76
+ /**
77
+ * Lists all queued messages in processing order.
78
+ */
79
+ async list() {
80
+ return sortQueue([...this.messages.values()]).map((message) => clone(message));
81
+ }
82
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "2.0.18",
3
+ "version": "2.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },