@qrlwallet/connect 3.0.0 → 3.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.
- package/dist/index.d.mts +38 -19
- package/dist/index.d.ts +38 -19
- package/dist/index.js +340 -164
- package/dist/index.mjs +340 -164
- package/package.json +4 -5
package/dist/index.d.mts
CHANGED
|
@@ -61,6 +61,7 @@ declare class KeyExchange extends EventEmitter<KeyExchangeEvents> {
|
|
|
61
61
|
private awaitingSynAck;
|
|
62
62
|
private awaitingAck;
|
|
63
63
|
private keysExchanged;
|
|
64
|
+
private lastAck;
|
|
64
65
|
constructor(isOriginator: boolean, restored?: Session);
|
|
65
66
|
/**
|
|
66
67
|
* Originator (dApp) side: begin a fresh handshake by generating an
|
|
@@ -91,6 +92,8 @@ declare class KeyExchange extends EventEmitter<KeyExchangeEvents> {
|
|
|
91
92
|
reset(): void;
|
|
92
93
|
private resetInternal;
|
|
93
94
|
areKeysExchanged(): boolean;
|
|
95
|
+
/** The ACK produced for the current handshake, for duplicate-SYNACK replies. */
|
|
96
|
+
getLastAck(): AckMessage | null;
|
|
94
97
|
getSession(): Session | null;
|
|
95
98
|
getCurrentStep(): KeyExchangeMessageType;
|
|
96
99
|
/** Export the current session as a JSON-safe persisted record. */
|
|
@@ -115,12 +118,15 @@ interface DAppMetadata {
|
|
|
115
118
|
/**
|
|
116
119
|
* Stored session for reconnection.
|
|
117
120
|
*
|
|
118
|
-
*
|
|
121
|
+
* Persists the derived AES-256 session key (not the ML-KEM secret key);
|
|
119
122
|
* the ML-KEM keypair is ephemeral and zeroized after the handshake.
|
|
120
123
|
* Re-pair (generate a new QR) to rotate the session key.
|
|
124
|
+
*
|
|
125
|
+
* v3 checkpoints the AEAD counters on every seal/open. v2 records (sparse
|
|
126
|
+
* checkpoints, stale-counter nonce-reuse risk on restore) are dropped.
|
|
121
127
|
*/
|
|
122
128
|
interface DAppSession {
|
|
123
|
-
version:
|
|
129
|
+
version: 3;
|
|
124
130
|
channelId: string;
|
|
125
131
|
keyExchange: PersistedSession;
|
|
126
132
|
dappMetadata: DAppMetadata;
|
|
@@ -133,7 +139,7 @@ interface DAppSession {
|
|
|
133
139
|
interface PendingRequest {
|
|
134
140
|
id: string | number;
|
|
135
141
|
method: string;
|
|
136
|
-
params?: unknown[];
|
|
142
|
+
params?: unknown[] | undefined;
|
|
137
143
|
resolve: (result: unknown) => void;
|
|
138
144
|
reject: (error: Error) => void;
|
|
139
145
|
timestamp: number;
|
|
@@ -142,7 +148,7 @@ interface PendingRequest {
|
|
|
142
148
|
* Key-exchange wire-message types.
|
|
143
149
|
*
|
|
144
150
|
* v2 note: only SYNACK (wallet→dApp) and ACK (dApp→wallet) are transmitted
|
|
145
|
-
* over the relay. The SYN step is carried by the QR code itself
|
|
151
|
+
* over the relay. The SYN step is carried by the QR code itself - the dApp's
|
|
146
152
|
* ML-KEM-768 encapsulation key is embedded in the URI, not sent on the wire.
|
|
147
153
|
*/
|
|
148
154
|
declare enum KeyExchangeMessageType {
|
|
@@ -198,7 +204,7 @@ interface JsonRpcRequest {
|
|
|
198
204
|
jsonrpc?: string;
|
|
199
205
|
id?: string | number;
|
|
200
206
|
method: string;
|
|
201
|
-
params?: unknown[];
|
|
207
|
+
params?: unknown[] | undefined;
|
|
202
208
|
}
|
|
203
209
|
/** JSON-RPC response */
|
|
204
210
|
interface JsonRpcResponse {
|
|
@@ -215,7 +221,7 @@ interface JsonRpcResponse {
|
|
|
215
221
|
* EIP-6963 wallet identity advertised to dApps.
|
|
216
222
|
*
|
|
217
223
|
* `rdns` MUST be unique to this wallet and SHOULD be a reverse-DNS string
|
|
218
|
-
* (per EIP-6963). Defaults work for most consumers
|
|
224
|
+
* (per EIP-6963). Defaults work for most consumers - override only when
|
|
219
225
|
* embedding the SDK inside a different branded wallet.
|
|
220
226
|
*/
|
|
221
227
|
interface EIP6963ProviderInfoOverride {
|
|
@@ -262,10 +268,10 @@ type QrlSignMessageParams = [string, string];
|
|
|
262
268
|
* for the full encoder.
|
|
263
269
|
*/
|
|
264
270
|
interface QrlTypedDataPayload {
|
|
265
|
-
types: Record<string,
|
|
271
|
+
types: Record<string, readonly {
|
|
266
272
|
name: string;
|
|
267
273
|
type: string;
|
|
268
|
-
}
|
|
274
|
+
}[]>;
|
|
269
275
|
primaryType: string;
|
|
270
276
|
domain: Record<string, unknown>;
|
|
271
277
|
message: Record<string, unknown>;
|
|
@@ -316,6 +322,8 @@ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
|
|
|
316
322
|
private eip6963RequestListener;
|
|
317
323
|
private resumeListener;
|
|
318
324
|
private resumeDebounce;
|
|
325
|
+
private readonly requestIdPrefix;
|
|
326
|
+
private requestCounter;
|
|
319
327
|
readonly isQRLConnect = true;
|
|
320
328
|
constructor(options: QRLConnectOptions);
|
|
321
329
|
private startEip6963Announce;
|
|
@@ -385,14 +393,14 @@ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
|
|
|
385
393
|
/**
|
|
386
394
|
* Disconnect from wallet and clean up. Returns once the TERMINATE has
|
|
387
395
|
* either been flushed to the relay or the 800ms best-effort window has
|
|
388
|
-
* elapsed
|
|
396
|
+
* elapsed - the wallet gets an instant disconnect instead of landing in
|
|
389
397
|
* its stale-session grace period.
|
|
390
398
|
*/
|
|
391
399
|
disconnect(): Promise<void>;
|
|
392
400
|
}
|
|
393
401
|
|
|
394
402
|
/**
|
|
395
|
-
* Connection Manager
|
|
403
|
+
* Connection Manager - orchestrates socket lifecycle, the post-quantum
|
|
396
404
|
* handshake, encrypted message routing, and session persistence for the
|
|
397
405
|
* dApp side of QRL Connect v2.
|
|
398
406
|
*/
|
|
@@ -425,12 +433,13 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
425
433
|
private walletPresent;
|
|
426
434
|
private pendingRestore;
|
|
427
435
|
private messageQueue;
|
|
436
|
+
private outboundQueue;
|
|
428
437
|
private static MAX_RECONNECT_FAILURES;
|
|
429
438
|
constructor(options: {
|
|
430
439
|
dappMetadata: DAppMetadata;
|
|
431
|
-
relayUrl?: string;
|
|
432
|
-
chainId?: string;
|
|
433
|
-
storageKey?: string;
|
|
440
|
+
relayUrl?: string | undefined;
|
|
441
|
+
chainId?: string | undefined;
|
|
442
|
+
storageKey?: string | undefined;
|
|
434
443
|
});
|
|
435
444
|
private setupSocketListeners;
|
|
436
445
|
private setupKeyExchangeListeners;
|
|
@@ -498,7 +507,16 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
498
507
|
private handleSynAck;
|
|
499
508
|
private handleDecryptedMessage;
|
|
500
509
|
private sendPlaintext;
|
|
510
|
+
/**
|
|
511
|
+
* Serialize every encrypt+persist+send on a single outbound queue.
|
|
512
|
+
* Callers like sendJsonRpc() are fire-and-forget, so without the queue two
|
|
513
|
+
* rapid provider.request() calls would interleave inside encryptMessage()
|
|
514
|
+
* across its await and could complete out of order; the receiver's
|
|
515
|
+
* contiguous-seq check would then drop the late one. (Nonce reuse itself is
|
|
516
|
+
* already prevented by the synchronous seq reservation in KeyExchange.)
|
|
517
|
+
*/
|
|
501
518
|
private sendEncrypted;
|
|
519
|
+
private sendEncryptedNow;
|
|
502
520
|
private startUnresponsiveTimer;
|
|
503
521
|
private clearUnresponsiveTimer;
|
|
504
522
|
private setStatus;
|
|
@@ -529,7 +547,7 @@ interface JoinResult {
|
|
|
529
547
|
/**
|
|
530
548
|
* Base64-encoded KEM public key the relay has bound to this channel.
|
|
531
549
|
* Populated for wallet joins (v2 protocol). `null` if the dApp hasn't
|
|
532
|
-
* registered a PK yet
|
|
550
|
+
* registered a PK yet - wallet callers must treat this as a retry signal.
|
|
533
551
|
*/
|
|
534
552
|
channelPublicKey: string | null;
|
|
535
553
|
/**
|
|
@@ -632,7 +650,8 @@ declare function computeMessageDigest(messageBytes: Uint8Array): Uint8Array;
|
|
|
632
650
|
* EIP-712-shaped typed-data encoding for `qrl_signTypedData` v1.
|
|
633
651
|
*
|
|
634
652
|
* Byte-identical algorithm with the wallet's signing/typedData.ts; this
|
|
635
|
-
* file is the SDK port (only the
|
|
653
|
+
* file is the SDK port (only the import block differs: the SDK routes
|
|
654
|
+
* SHAKE256 through src/crypto/primitives.ts, its crypto boundary). See
|
|
636
655
|
* the wallet copy for the spec-level comments; behavior must stay in
|
|
637
656
|
* lock-step or the cross-repo parity test fails on next CI.
|
|
638
657
|
*/
|
|
@@ -709,7 +728,7 @@ declare function concatBytes(...parts: Uint8Array[]): Uint8Array;
|
|
|
709
728
|
* substitution of a maliciously-crafted PK with the same fingerprint.
|
|
710
729
|
*
|
|
711
730
|
* Legacy v2.0 URIs (magic=PQP1, 1208-byte blob with embedded PK) are
|
|
712
|
-
* rejected with a clear error by parseConnectionURI
|
|
731
|
+
* rejected with a clear error by parseConnectionURI - this is a hard
|
|
713
732
|
* break before 2.0.0 ships, no backcompat.
|
|
714
733
|
*/
|
|
715
734
|
declare const CID_LEN = 16;
|
|
@@ -728,18 +747,18 @@ declare function computeFingerprint(cid: Uint8Array, pk: Uint8Array): Promise<Ui
|
|
|
728
747
|
declare function fingerprintEquals(a: Uint8Array, b: Uint8Array): boolean;
|
|
729
748
|
/**
|
|
730
749
|
* Encode (cid, pk) as a qrlconnect:// URI. Note the PK is not stored in the
|
|
731
|
-
* URI
|
|
750
|
+
* URI - we compute its fingerprint and embed only that. The caller must
|
|
732
751
|
* upload the PK to the relay separately before publishing this URI.
|
|
733
752
|
*
|
|
734
753
|
* The optional `relayUrl` rides as a sibling query param (not inside the
|
|
735
754
|
* fp-bound blob) so a tampered relay can cause the pairing to fail but
|
|
736
|
-
* cannot substitute the PK
|
|
755
|
+
* cannot substitute the PK - the fp still pins that.
|
|
737
756
|
*/
|
|
738
757
|
declare function generateConnectionURI(cid: Uint8Array, pk: Uint8Array, relayUrl?: string): Promise<string>;
|
|
739
758
|
interface ParsedURI {
|
|
740
759
|
cid: Uint8Array;
|
|
741
760
|
fp: Uint8Array;
|
|
742
|
-
relayUrl?: string;
|
|
761
|
+
relayUrl?: string | undefined;
|
|
743
762
|
}
|
|
744
763
|
declare function parseConnectionURI(uri: string): Promise<ParsedURI>;
|
|
745
764
|
/** Convert 16 raw cid bytes to RFC 4122 UUID hex string. */
|
package/dist/index.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ declare class KeyExchange extends EventEmitter<KeyExchangeEvents> {
|
|
|
61
61
|
private awaitingSynAck;
|
|
62
62
|
private awaitingAck;
|
|
63
63
|
private keysExchanged;
|
|
64
|
+
private lastAck;
|
|
64
65
|
constructor(isOriginator: boolean, restored?: Session);
|
|
65
66
|
/**
|
|
66
67
|
* Originator (dApp) side: begin a fresh handshake by generating an
|
|
@@ -91,6 +92,8 @@ declare class KeyExchange extends EventEmitter<KeyExchangeEvents> {
|
|
|
91
92
|
reset(): void;
|
|
92
93
|
private resetInternal;
|
|
93
94
|
areKeysExchanged(): boolean;
|
|
95
|
+
/** The ACK produced for the current handshake, for duplicate-SYNACK replies. */
|
|
96
|
+
getLastAck(): AckMessage | null;
|
|
94
97
|
getSession(): Session | null;
|
|
95
98
|
getCurrentStep(): KeyExchangeMessageType;
|
|
96
99
|
/** Export the current session as a JSON-safe persisted record. */
|
|
@@ -115,12 +118,15 @@ interface DAppMetadata {
|
|
|
115
118
|
/**
|
|
116
119
|
* Stored session for reconnection.
|
|
117
120
|
*
|
|
118
|
-
*
|
|
121
|
+
* Persists the derived AES-256 session key (not the ML-KEM secret key);
|
|
119
122
|
* the ML-KEM keypair is ephemeral and zeroized after the handshake.
|
|
120
123
|
* Re-pair (generate a new QR) to rotate the session key.
|
|
124
|
+
*
|
|
125
|
+
* v3 checkpoints the AEAD counters on every seal/open. v2 records (sparse
|
|
126
|
+
* checkpoints, stale-counter nonce-reuse risk on restore) are dropped.
|
|
121
127
|
*/
|
|
122
128
|
interface DAppSession {
|
|
123
|
-
version:
|
|
129
|
+
version: 3;
|
|
124
130
|
channelId: string;
|
|
125
131
|
keyExchange: PersistedSession;
|
|
126
132
|
dappMetadata: DAppMetadata;
|
|
@@ -133,7 +139,7 @@ interface DAppSession {
|
|
|
133
139
|
interface PendingRequest {
|
|
134
140
|
id: string | number;
|
|
135
141
|
method: string;
|
|
136
|
-
params?: unknown[];
|
|
142
|
+
params?: unknown[] | undefined;
|
|
137
143
|
resolve: (result: unknown) => void;
|
|
138
144
|
reject: (error: Error) => void;
|
|
139
145
|
timestamp: number;
|
|
@@ -142,7 +148,7 @@ interface PendingRequest {
|
|
|
142
148
|
* Key-exchange wire-message types.
|
|
143
149
|
*
|
|
144
150
|
* v2 note: only SYNACK (wallet→dApp) and ACK (dApp→wallet) are transmitted
|
|
145
|
-
* over the relay. The SYN step is carried by the QR code itself
|
|
151
|
+
* over the relay. The SYN step is carried by the QR code itself - the dApp's
|
|
146
152
|
* ML-KEM-768 encapsulation key is embedded in the URI, not sent on the wire.
|
|
147
153
|
*/
|
|
148
154
|
declare enum KeyExchangeMessageType {
|
|
@@ -198,7 +204,7 @@ interface JsonRpcRequest {
|
|
|
198
204
|
jsonrpc?: string;
|
|
199
205
|
id?: string | number;
|
|
200
206
|
method: string;
|
|
201
|
-
params?: unknown[];
|
|
207
|
+
params?: unknown[] | undefined;
|
|
202
208
|
}
|
|
203
209
|
/** JSON-RPC response */
|
|
204
210
|
interface JsonRpcResponse {
|
|
@@ -215,7 +221,7 @@ interface JsonRpcResponse {
|
|
|
215
221
|
* EIP-6963 wallet identity advertised to dApps.
|
|
216
222
|
*
|
|
217
223
|
* `rdns` MUST be unique to this wallet and SHOULD be a reverse-DNS string
|
|
218
|
-
* (per EIP-6963). Defaults work for most consumers
|
|
224
|
+
* (per EIP-6963). Defaults work for most consumers - override only when
|
|
219
225
|
* embedding the SDK inside a different branded wallet.
|
|
220
226
|
*/
|
|
221
227
|
interface EIP6963ProviderInfoOverride {
|
|
@@ -262,10 +268,10 @@ type QrlSignMessageParams = [string, string];
|
|
|
262
268
|
* for the full encoder.
|
|
263
269
|
*/
|
|
264
270
|
interface QrlTypedDataPayload {
|
|
265
|
-
types: Record<string,
|
|
271
|
+
types: Record<string, readonly {
|
|
266
272
|
name: string;
|
|
267
273
|
type: string;
|
|
268
|
-
}
|
|
274
|
+
}[]>;
|
|
269
275
|
primaryType: string;
|
|
270
276
|
domain: Record<string, unknown>;
|
|
271
277
|
message: Record<string, unknown>;
|
|
@@ -316,6 +322,8 @@ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
|
|
|
316
322
|
private eip6963RequestListener;
|
|
317
323
|
private resumeListener;
|
|
318
324
|
private resumeDebounce;
|
|
325
|
+
private readonly requestIdPrefix;
|
|
326
|
+
private requestCounter;
|
|
319
327
|
readonly isQRLConnect = true;
|
|
320
328
|
constructor(options: QRLConnectOptions);
|
|
321
329
|
private startEip6963Announce;
|
|
@@ -385,14 +393,14 @@ declare class QRLConnectProvider extends EventEmitter<ProviderEvents> {
|
|
|
385
393
|
/**
|
|
386
394
|
* Disconnect from wallet and clean up. Returns once the TERMINATE has
|
|
387
395
|
* either been flushed to the relay or the 800ms best-effort window has
|
|
388
|
-
* elapsed
|
|
396
|
+
* elapsed - the wallet gets an instant disconnect instead of landing in
|
|
389
397
|
* its stale-session grace period.
|
|
390
398
|
*/
|
|
391
399
|
disconnect(): Promise<void>;
|
|
392
400
|
}
|
|
393
401
|
|
|
394
402
|
/**
|
|
395
|
-
* Connection Manager
|
|
403
|
+
* Connection Manager - orchestrates socket lifecycle, the post-quantum
|
|
396
404
|
* handshake, encrypted message routing, and session persistence for the
|
|
397
405
|
* dApp side of QRL Connect v2.
|
|
398
406
|
*/
|
|
@@ -425,12 +433,13 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
425
433
|
private walletPresent;
|
|
426
434
|
private pendingRestore;
|
|
427
435
|
private messageQueue;
|
|
436
|
+
private outboundQueue;
|
|
428
437
|
private static MAX_RECONNECT_FAILURES;
|
|
429
438
|
constructor(options: {
|
|
430
439
|
dappMetadata: DAppMetadata;
|
|
431
|
-
relayUrl?: string;
|
|
432
|
-
chainId?: string;
|
|
433
|
-
storageKey?: string;
|
|
440
|
+
relayUrl?: string | undefined;
|
|
441
|
+
chainId?: string | undefined;
|
|
442
|
+
storageKey?: string | undefined;
|
|
434
443
|
});
|
|
435
444
|
private setupSocketListeners;
|
|
436
445
|
private setupKeyExchangeListeners;
|
|
@@ -498,7 +507,16 @@ declare class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
|
|
|
498
507
|
private handleSynAck;
|
|
499
508
|
private handleDecryptedMessage;
|
|
500
509
|
private sendPlaintext;
|
|
510
|
+
/**
|
|
511
|
+
* Serialize every encrypt+persist+send on a single outbound queue.
|
|
512
|
+
* Callers like sendJsonRpc() are fire-and-forget, so without the queue two
|
|
513
|
+
* rapid provider.request() calls would interleave inside encryptMessage()
|
|
514
|
+
* across its await and could complete out of order; the receiver's
|
|
515
|
+
* contiguous-seq check would then drop the late one. (Nonce reuse itself is
|
|
516
|
+
* already prevented by the synchronous seq reservation in KeyExchange.)
|
|
517
|
+
*/
|
|
501
518
|
private sendEncrypted;
|
|
519
|
+
private sendEncryptedNow;
|
|
502
520
|
private startUnresponsiveTimer;
|
|
503
521
|
private clearUnresponsiveTimer;
|
|
504
522
|
private setStatus;
|
|
@@ -529,7 +547,7 @@ interface JoinResult {
|
|
|
529
547
|
/**
|
|
530
548
|
* Base64-encoded KEM public key the relay has bound to this channel.
|
|
531
549
|
* Populated for wallet joins (v2 protocol). `null` if the dApp hasn't
|
|
532
|
-
* registered a PK yet
|
|
550
|
+
* registered a PK yet - wallet callers must treat this as a retry signal.
|
|
533
551
|
*/
|
|
534
552
|
channelPublicKey: string | null;
|
|
535
553
|
/**
|
|
@@ -632,7 +650,8 @@ declare function computeMessageDigest(messageBytes: Uint8Array): Uint8Array;
|
|
|
632
650
|
* EIP-712-shaped typed-data encoding for `qrl_signTypedData` v1.
|
|
633
651
|
*
|
|
634
652
|
* Byte-identical algorithm with the wallet's signing/typedData.ts; this
|
|
635
|
-
* file is the SDK port (only the
|
|
653
|
+
* file is the SDK port (only the import block differs: the SDK routes
|
|
654
|
+
* SHAKE256 through src/crypto/primitives.ts, its crypto boundary). See
|
|
636
655
|
* the wallet copy for the spec-level comments; behavior must stay in
|
|
637
656
|
* lock-step or the cross-repo parity test fails on next CI.
|
|
638
657
|
*/
|
|
@@ -709,7 +728,7 @@ declare function concatBytes(...parts: Uint8Array[]): Uint8Array;
|
|
|
709
728
|
* substitution of a maliciously-crafted PK with the same fingerprint.
|
|
710
729
|
*
|
|
711
730
|
* Legacy v2.0 URIs (magic=PQP1, 1208-byte blob with embedded PK) are
|
|
712
|
-
* rejected with a clear error by parseConnectionURI
|
|
731
|
+
* rejected with a clear error by parseConnectionURI - this is a hard
|
|
713
732
|
* break before 2.0.0 ships, no backcompat.
|
|
714
733
|
*/
|
|
715
734
|
declare const CID_LEN = 16;
|
|
@@ -728,18 +747,18 @@ declare function computeFingerprint(cid: Uint8Array, pk: Uint8Array): Promise<Ui
|
|
|
728
747
|
declare function fingerprintEquals(a: Uint8Array, b: Uint8Array): boolean;
|
|
729
748
|
/**
|
|
730
749
|
* Encode (cid, pk) as a qrlconnect:// URI. Note the PK is not stored in the
|
|
731
|
-
* URI
|
|
750
|
+
* URI - we compute its fingerprint and embed only that. The caller must
|
|
732
751
|
* upload the PK to the relay separately before publishing this URI.
|
|
733
752
|
*
|
|
734
753
|
* The optional `relayUrl` rides as a sibling query param (not inside the
|
|
735
754
|
* fp-bound blob) so a tampered relay can cause the pairing to fail but
|
|
736
|
-
* cannot substitute the PK
|
|
755
|
+
* cannot substitute the PK - the fp still pins that.
|
|
737
756
|
*/
|
|
738
757
|
declare function generateConnectionURI(cid: Uint8Array, pk: Uint8Array, relayUrl?: string): Promise<string>;
|
|
739
758
|
interface ParsedURI {
|
|
740
759
|
cid: Uint8Array;
|
|
741
760
|
fp: Uint8Array;
|
|
742
|
-
relayUrl?: string;
|
|
761
|
+
relayUrl?: string | undefined;
|
|
743
762
|
}
|
|
744
763
|
declare function parseConnectionURI(uri: string): Promise<ParsedURI>;
|
|
745
764
|
/** Convert 16 raw cid bytes to RFC 4122 UUID hex string. */
|