@bcts/xid 1.0.0-alpha.16 → 1.0.0-alpha.18
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 +1 -1
- package/dist/index.cjs +387 -103
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +150 -14
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +150 -14
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +389 -104
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +379 -107
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -12
- package/src/index.ts +3 -0
- package/src/key.ts +81 -15
- package/src/provenance.ts +75 -19
- package/src/service.ts +17 -1
- package/src/xid-document.ts +334 -29
package/src/xid-document.ts
CHANGED
|
@@ -6,13 +6,23 @@
|
|
|
6
6
|
* Ported from bc-xid-rust/src/xid_document.rs
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Envelope,
|
|
11
|
+
Attachments,
|
|
12
|
+
Edges,
|
|
13
|
+
type Edgeable,
|
|
14
|
+
type EnvelopeEncodable,
|
|
15
|
+
type EnvelopeEncodableValue,
|
|
16
|
+
} from "@bcts/envelope";
|
|
17
|
+
import type { Digest } from "@bcts/envelope";
|
|
10
18
|
import {
|
|
11
19
|
KEY,
|
|
12
20
|
DELEGATE,
|
|
13
21
|
SERVICE,
|
|
14
22
|
PROVENANCE,
|
|
15
23
|
DEREFERENCE_VIA,
|
|
24
|
+
ATTACHMENT_RAW as ATTACHMENT_RAW_VAL,
|
|
25
|
+
EDGE_RAW as EDGE_RAW_VAL,
|
|
16
26
|
type KnownValue,
|
|
17
27
|
} from "@bcts/known-values";
|
|
18
28
|
|
|
@@ -26,6 +36,8 @@ import {
|
|
|
26
36
|
type PrivateKeys,
|
|
27
37
|
type Signer,
|
|
28
38
|
type EncapsulationPublicKey,
|
|
39
|
+
type SigningPublicKey,
|
|
40
|
+
type SigningPrivateKey,
|
|
29
41
|
} from "@bcts/components";
|
|
30
42
|
import {
|
|
31
43
|
type ProvenanceMark,
|
|
@@ -45,6 +57,8 @@ const DELEGATE_RAW = DELEGATE.value();
|
|
|
45
57
|
const SERVICE_RAW = SERVICE.value();
|
|
46
58
|
const PROVENANCE_RAW = PROVENANCE.value();
|
|
47
59
|
const DEREFERENCE_VIA_RAW = DEREFERENCE_VIA.value();
|
|
60
|
+
const ATTACHMENT_RAW_VALUE = Number(ATTACHMENT_RAW_VAL);
|
|
61
|
+
const EDGE_RAW_VALUE = Number(EDGE_RAW_VAL);
|
|
48
62
|
|
|
49
63
|
/**
|
|
50
64
|
* Options for creating the inception key.
|
|
@@ -81,8 +95,8 @@ export type XIDGenesisMarkOptions =
|
|
|
81
95
|
export type XIDSigningOptions =
|
|
82
96
|
| { type: "none" }
|
|
83
97
|
| { type: "inception" }
|
|
84
|
-
| { type: "
|
|
85
|
-
| { type: "
|
|
98
|
+
| { type: "privateKeys"; privateKeys: PrivateKeys }
|
|
99
|
+
| { type: "signingPrivateKey"; signingPrivateKey: SigningPrivateKey };
|
|
86
100
|
|
|
87
101
|
/**
|
|
88
102
|
* Options for verifying the signature on an envelope when loading.
|
|
@@ -102,13 +116,15 @@ type ServiceMap = Map<string, Service>;
|
|
|
102
116
|
/**
|
|
103
117
|
* Represents an XID document.
|
|
104
118
|
*/
|
|
105
|
-
export class XIDDocument implements EnvelopeEncodable {
|
|
119
|
+
export class XIDDocument implements EnvelopeEncodable, Edgeable {
|
|
106
120
|
private readonly _xid: XID;
|
|
107
121
|
private readonly _resolutionMethods: Set<string>;
|
|
108
122
|
private readonly _keys: KeyMap;
|
|
109
123
|
private readonly _delegates: DelegateMap;
|
|
110
124
|
private readonly _services: ServiceMap;
|
|
111
125
|
private _provenance: Provenance | undefined;
|
|
126
|
+
private _attachments: Attachments;
|
|
127
|
+
private _edges: Edges;
|
|
112
128
|
|
|
113
129
|
private constructor(
|
|
114
130
|
xid: XID,
|
|
@@ -117,6 +133,8 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
117
133
|
delegates: DelegateMap = new Map(),
|
|
118
134
|
services: ServiceMap = new Map(),
|
|
119
135
|
provenance?: Provenance,
|
|
136
|
+
attachments?: Attachments,
|
|
137
|
+
edges?: Edges,
|
|
120
138
|
) {
|
|
121
139
|
this._xid = xid;
|
|
122
140
|
this._resolutionMethods = resolutionMethods;
|
|
@@ -124,6 +142,8 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
124
142
|
this._delegates = delegates;
|
|
125
143
|
this._services = services;
|
|
126
144
|
this._provenance = provenance;
|
|
145
|
+
this._attachments = attachments ?? new Attachments();
|
|
146
|
+
this._edges = edges ?? new Edges();
|
|
127
147
|
}
|
|
128
148
|
|
|
129
149
|
/**
|
|
@@ -136,9 +156,9 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
136
156
|
const inceptionKey = XIDDocument.inceptionKeyForOptions(keyOptions);
|
|
137
157
|
const provenance = XIDDocument.genesisMarkWithOptions(markOptions);
|
|
138
158
|
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
const xid = XID.
|
|
159
|
+
// XID is the SHA-256 digest of the CBOR encoding of the inception signing public key
|
|
160
|
+
// This matches Rust: XID::new(inception_key.public_keys().signing_public_key())
|
|
161
|
+
const xid = XID.newFromSigningKey(inceptionKey.publicKeys().signingPublicKey());
|
|
142
162
|
const doc = new XIDDocument(xid, new Set(), new Map(), new Map(), new Map(), provenance);
|
|
143
163
|
|
|
144
164
|
doc.addKey(inceptionKey);
|
|
@@ -280,12 +300,11 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
280
300
|
}
|
|
281
301
|
|
|
282
302
|
/**
|
|
283
|
-
* Check if the given public
|
|
303
|
+
* Check if the given signing public key is the inception signing key.
|
|
304
|
+
* Matches Rust: `is_inception_signing_key(&self, signing_public_key: &SigningPublicKey) -> bool`
|
|
284
305
|
*/
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const xidReference = publicKeys.reference();
|
|
288
|
-
return bytesEqual(xidReference.getDigest().toData(), this._xid.toData());
|
|
306
|
+
isInceptionSigningKey(signingPublicKey: SigningPublicKey): boolean {
|
|
307
|
+
return this._xid.validate(signingPublicKey);
|
|
289
308
|
}
|
|
290
309
|
|
|
291
310
|
/**
|
|
@@ -293,7 +312,7 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
293
312
|
*/
|
|
294
313
|
inceptionKey(): Key | undefined {
|
|
295
314
|
for (const key of this._keys.values()) {
|
|
296
|
-
if (this.
|
|
315
|
+
if (this.isInceptionSigningKey(key.publicKeys().signingPublicKey())) {
|
|
297
316
|
return key;
|
|
298
317
|
}
|
|
299
318
|
}
|
|
@@ -338,6 +357,180 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
338
357
|
return inceptionKey;
|
|
339
358
|
}
|
|
340
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Set the name (nickname) for a key identified by its public keys.
|
|
362
|
+
*/
|
|
363
|
+
setNameForKey(publicKeys: PublicKeys, name: string): void {
|
|
364
|
+
const key = this.takeKey(publicKeys);
|
|
365
|
+
if (key === undefined) {
|
|
366
|
+
throw XIDError.notFound("key");
|
|
367
|
+
}
|
|
368
|
+
key.setNickname(name);
|
|
369
|
+
this.addKey(key);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get the inception signing public key, if it exists.
|
|
374
|
+
*/
|
|
375
|
+
inceptionSigningKey(): SigningPublicKey | undefined {
|
|
376
|
+
const key = this.inceptionKey();
|
|
377
|
+
return key?.publicKeys().signingPublicKey();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Get the verification (signing) key for this document.
|
|
382
|
+
* Prefers the inception key. Falls back to the first key.
|
|
383
|
+
*/
|
|
384
|
+
verificationKey(): SigningPublicKey | undefined {
|
|
385
|
+
const inceptionKey = this.inceptionKey();
|
|
386
|
+
if (inceptionKey !== undefined) {
|
|
387
|
+
return inceptionKey.publicKeys().signingPublicKey();
|
|
388
|
+
}
|
|
389
|
+
const firstKey = this._keys.values().next().value;
|
|
390
|
+
return firstKey?.publicKeys().signingPublicKey();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Extract inception private keys from an envelope (convenience static method).
|
|
395
|
+
*/
|
|
396
|
+
static extractInceptionPrivateKeysFromEnvelope(
|
|
397
|
+
envelope: Envelope,
|
|
398
|
+
password: Uint8Array,
|
|
399
|
+
): PrivateKeys | undefined {
|
|
400
|
+
const doc = XIDDocument.fromEnvelope(envelope, password, XIDVerifySignature.None);
|
|
401
|
+
return doc.inceptionPrivateKeys();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Get the private key envelope for a specific key, optionally decrypting it.
|
|
406
|
+
*/
|
|
407
|
+
privateKeyEnvelopeForKey(publicKeys: PublicKeys, password?: string): Envelope | undefined {
|
|
408
|
+
const key = this.findKeyByPublicKeys(publicKeys);
|
|
409
|
+
if (key === undefined) {
|
|
410
|
+
return undefined;
|
|
411
|
+
}
|
|
412
|
+
return key.privateKeyEnvelope(password);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Check that the document contains a key with the given public keys.
|
|
417
|
+
* Throws if not found.
|
|
418
|
+
*/
|
|
419
|
+
checkContainsKey(publicKeys: PublicKeys): void {
|
|
420
|
+
if (this.findKeyByPublicKeys(publicKeys) === undefined) {
|
|
421
|
+
throw XIDError.keyNotFoundInDocument(publicKeys.toString());
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Check that the document contains a delegate with the given XID.
|
|
427
|
+
* Throws if not found.
|
|
428
|
+
*/
|
|
429
|
+
checkContainsDelegate(xid: XID): void {
|
|
430
|
+
if (this.findDelegateByXid(xid) === undefined) {
|
|
431
|
+
throw XIDError.delegateNotFoundInDocument(xid.toString());
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ============================================================================
|
|
436
|
+
// Attachable interface implementation
|
|
437
|
+
// ============================================================================
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Get the attachments container.
|
|
441
|
+
*/
|
|
442
|
+
getAttachments(): Attachments {
|
|
443
|
+
return this._attachments;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Add an attachment with the specified payload and metadata.
|
|
448
|
+
*/
|
|
449
|
+
addAttachment(payload: EnvelopeEncodableValue, vendor: string, conformsTo?: string): void {
|
|
450
|
+
this._attachments.add(payload, vendor, conformsTo);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Check if the document has any attachments.
|
|
455
|
+
*/
|
|
456
|
+
hasAttachments(): boolean {
|
|
457
|
+
return !this._attachments.isEmpty();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Remove all attachments.
|
|
462
|
+
*/
|
|
463
|
+
clearAttachments(): void {
|
|
464
|
+
this._attachments.clear();
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Get an attachment by its digest.
|
|
469
|
+
*/
|
|
470
|
+
getAttachment(digest: Digest): Envelope | undefined {
|
|
471
|
+
return this._attachments.get(digest);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Remove an attachment by its digest.
|
|
476
|
+
*/
|
|
477
|
+
removeAttachment(digest: Digest): Envelope | undefined {
|
|
478
|
+
return this._attachments.remove(digest);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// ============================================================================
|
|
482
|
+
// Edgeable interface implementation
|
|
483
|
+
// ============================================================================
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Get the edges container (read-only).
|
|
487
|
+
*/
|
|
488
|
+
edges(): Edges {
|
|
489
|
+
return this._edges;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Get the edges container (mutable).
|
|
494
|
+
*/
|
|
495
|
+
edgesMut(): Edges {
|
|
496
|
+
return this._edges;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Add an edge envelope.
|
|
501
|
+
*/
|
|
502
|
+
addEdge(edgeEnvelope: Envelope): void {
|
|
503
|
+
this._edges.add(edgeEnvelope);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Get an edge by its digest.
|
|
508
|
+
*/
|
|
509
|
+
getEdge(digest: Digest): Envelope | undefined {
|
|
510
|
+
return this._edges.get(digest);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Remove an edge by its digest.
|
|
515
|
+
*/
|
|
516
|
+
removeEdge(digest: Digest): Envelope | undefined {
|
|
517
|
+
return this._edges.remove(digest);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Remove all edges.
|
|
522
|
+
*/
|
|
523
|
+
clearEdges(): void {
|
|
524
|
+
this._edges.clear();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Check if the document has any edges.
|
|
529
|
+
*/
|
|
530
|
+
hasEdges(): boolean {
|
|
531
|
+
return !this._edges.isEmpty();
|
|
532
|
+
}
|
|
533
|
+
|
|
341
534
|
/**
|
|
342
535
|
* Check if the document is empty (no keys, delegates, services, or provenance).
|
|
343
536
|
*/
|
|
@@ -624,7 +817,8 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
624
817
|
generatorOptions: XIDGeneratorOptionsValue = XIDGeneratorOptions.Omit,
|
|
625
818
|
signingOptions: XIDSigningOptions = { type: "none" },
|
|
626
819
|
): Envelope {
|
|
627
|
-
|
|
820
|
+
// Use tagged CBOR representation, matching Rust's Envelope::new(self.xid)
|
|
821
|
+
let envelope = Envelope.newLeaf(this._xid.taggedCbor());
|
|
628
822
|
|
|
629
823
|
// Add resolution methods
|
|
630
824
|
for (const method of this._resolutionMethods) {
|
|
@@ -654,6 +848,12 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
654
848
|
);
|
|
655
849
|
}
|
|
656
850
|
|
|
851
|
+
// Add attachments before signing so they are included in the signature
|
|
852
|
+
envelope = this._attachments.addToEnvelope(envelope);
|
|
853
|
+
|
|
854
|
+
// Add edges before signing so they are included in the signature
|
|
855
|
+
envelope = this._edges.addToEnvelope(envelope);
|
|
856
|
+
|
|
657
857
|
// Apply signing (uses sign() which wraps the envelope first)
|
|
658
858
|
// PrivateKeys implements Signer from @bcts/components, which is compatible with envelope's sign()
|
|
659
859
|
switch (signingOptions.type) {
|
|
@@ -669,18 +869,18 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
669
869
|
envelope = (envelope as unknown as { sign(s: Signer): Envelope }).sign(privateKeys);
|
|
670
870
|
break;
|
|
671
871
|
}
|
|
672
|
-
case "privateKeyBase": {
|
|
673
|
-
// Derive PrivateKeys from PrivateKeyBase and use for signing
|
|
674
|
-
const privateKeys = signingOptions.privateKeyBase.ed25519PrivateKeys();
|
|
675
|
-
envelope = (envelope as unknown as { sign(s: Signer): Envelope }).sign(privateKeys);
|
|
676
|
-
break;
|
|
677
|
-
}
|
|
678
872
|
case "privateKeys": {
|
|
679
873
|
envelope = (envelope as unknown as { sign(s: Signer): Envelope }).sign(
|
|
680
874
|
signingOptions.privateKeys,
|
|
681
875
|
);
|
|
682
876
|
break;
|
|
683
877
|
}
|
|
878
|
+
case "signingPrivateKey": {
|
|
879
|
+
envelope = (envelope as unknown as { sign(s: Signer): Envelope }).sign(
|
|
880
|
+
signingOptions.signingPrivateKey,
|
|
881
|
+
);
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
684
884
|
case "none":
|
|
685
885
|
default:
|
|
686
886
|
break;
|
|
@@ -691,6 +891,9 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
691
891
|
|
|
692
892
|
// EnvelopeEncodable implementation
|
|
693
893
|
intoEnvelope(): Envelope {
|
|
894
|
+
if (this.isEmpty()) {
|
|
895
|
+
return Envelope.new(this._xid.toData());
|
|
896
|
+
}
|
|
694
897
|
return this.toEnvelope();
|
|
695
898
|
}
|
|
696
899
|
|
|
@@ -711,7 +914,16 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
711
914
|
case XIDVerifySignature.None: {
|
|
712
915
|
const subject = envelopeExt.subject();
|
|
713
916
|
const envelopeToParse = subject.isWrapped() ? subject.tryUnwrap() : envelope;
|
|
714
|
-
|
|
917
|
+
|
|
918
|
+
// Extract attachments from the envelope
|
|
919
|
+
const attachments = Attachments.fromEnvelope(envelopeToParse);
|
|
920
|
+
// Extract edges from the envelope
|
|
921
|
+
const edges = Edges.fromEnvelope(envelopeToParse);
|
|
922
|
+
|
|
923
|
+
const doc = XIDDocument.fromEnvelopeInner(envelopeToParse, password);
|
|
924
|
+
doc._attachments = attachments;
|
|
925
|
+
doc._edges = edges;
|
|
926
|
+
return doc;
|
|
715
927
|
}
|
|
716
928
|
case XIDVerifySignature.Inception: {
|
|
717
929
|
if (!envelopeExt.subject().isWrapped()) {
|
|
@@ -719,6 +931,12 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
719
931
|
}
|
|
720
932
|
|
|
721
933
|
const unwrapped = envelopeExt.tryUnwrap();
|
|
934
|
+
|
|
935
|
+
// Extract attachments from the unwrapped envelope
|
|
936
|
+
const attachments = Attachments.fromEnvelope(unwrapped);
|
|
937
|
+
// Extract edges from the unwrapped envelope
|
|
938
|
+
const edges = Edges.fromEnvelope(unwrapped);
|
|
939
|
+
|
|
722
940
|
const doc = XIDDocument.fromEnvelopeInner(unwrapped, password);
|
|
723
941
|
|
|
724
942
|
const inceptionKey = doc.inceptionKey();
|
|
@@ -732,10 +950,12 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
732
950
|
}
|
|
733
951
|
|
|
734
952
|
// Verify XID matches inception key
|
|
735
|
-
if (!doc.
|
|
953
|
+
if (!doc.isInceptionSigningKey(inceptionKey.publicKeys().signingPublicKey())) {
|
|
736
954
|
throw XIDError.invalidXid();
|
|
737
955
|
}
|
|
738
956
|
|
|
957
|
+
doc._attachments = attachments;
|
|
958
|
+
doc._edges = edges;
|
|
739
959
|
return doc;
|
|
740
960
|
}
|
|
741
961
|
}
|
|
@@ -752,13 +972,28 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
752
972
|
// The envelope may be a node (with assertions) or a leaf
|
|
753
973
|
const envCase = envelope.case();
|
|
754
974
|
const subject = envCase.type === "node" ? envelopeExt.subject() : envelope;
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
).
|
|
758
|
-
|
|
975
|
+
|
|
976
|
+
// Try to extract XID from the subject leaf.
|
|
977
|
+
// Rust-generated documents store the XID as tagged CBOR (Tag 40015 + byte string).
|
|
978
|
+
// TS-generated documents may store it as a raw byte string.
|
|
979
|
+
const leaf = (subject as unknown as { asLeaf(): Cbor | undefined }).asLeaf?.();
|
|
980
|
+
if (leaf === undefined) {
|
|
759
981
|
throw XIDError.invalidXid();
|
|
760
982
|
}
|
|
761
|
-
|
|
983
|
+
let xid: XID;
|
|
984
|
+
try {
|
|
985
|
+
// Try tagged CBOR first (matches Rust's Envelope::new(xid))
|
|
986
|
+
xid = XID.fromTaggedCbor(leaf);
|
|
987
|
+
} catch {
|
|
988
|
+
// Fall back to raw byte string
|
|
989
|
+
const xidData = (
|
|
990
|
+
subject as unknown as { asByteString(): Uint8Array | undefined }
|
|
991
|
+
).asByteString();
|
|
992
|
+
if (xidData === undefined) {
|
|
993
|
+
throw XIDError.invalidXid();
|
|
994
|
+
}
|
|
995
|
+
xid = XID.from(xidData);
|
|
996
|
+
}
|
|
762
997
|
const doc = XIDDocument.fromXid(xid);
|
|
763
998
|
|
|
764
999
|
// Process assertions
|
|
@@ -808,6 +1043,12 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
808
1043
|
doc._provenance = Provenance.tryFromEnvelope(object, password);
|
|
809
1044
|
break;
|
|
810
1045
|
}
|
|
1046
|
+
case ATTACHMENT_RAW_VALUE:
|
|
1047
|
+
// Handled separately by Attachments.fromEnvelope()
|
|
1048
|
+
break;
|
|
1049
|
+
case EDGE_RAW_VALUE:
|
|
1050
|
+
// Handled separately by Edges.fromEnvelope()
|
|
1051
|
+
break;
|
|
811
1052
|
default:
|
|
812
1053
|
throw XIDError.unexpectedPredicate(String(predicate));
|
|
813
1054
|
}
|
|
@@ -821,7 +1062,17 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
821
1062
|
* Create a signed envelope.
|
|
822
1063
|
*/
|
|
823
1064
|
toSignedEnvelope(signingKey: Signer): Envelope {
|
|
824
|
-
|
|
1065
|
+
return this.toSignedEnvelopeOpt(signingKey, XIDPrivateKeyOptions.Omit);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Create a signed envelope with private key options.
|
|
1070
|
+
*/
|
|
1071
|
+
toSignedEnvelopeOpt(
|
|
1072
|
+
signingKey: Signer,
|
|
1073
|
+
privateKeyOptions: XIDPrivateKeyOptionsValue = XIDPrivateKeyOptions.Omit,
|
|
1074
|
+
): Envelope {
|
|
1075
|
+
const envelope = this.toEnvelope(privateKeyOptions, XIDGeneratorOptions.Omit, {
|
|
825
1076
|
type: "none",
|
|
826
1077
|
});
|
|
827
1078
|
return (envelope as unknown as { sign(s: Signer): Envelope }).sign(signingKey);
|
|
@@ -838,7 +1089,50 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
838
1089
|
* Check equality with another XIDDocument.
|
|
839
1090
|
*/
|
|
840
1091
|
equals(other: XIDDocument): boolean {
|
|
841
|
-
|
|
1092
|
+
// Match Rust's PartialEq which compares all fields
|
|
1093
|
+
if (!this._xid.equals(other._xid)) return false;
|
|
1094
|
+
|
|
1095
|
+
// Compare resolution methods
|
|
1096
|
+
if (this._resolutionMethods.size !== other._resolutionMethods.size) return false;
|
|
1097
|
+
for (const m of this._resolutionMethods) {
|
|
1098
|
+
if (!other._resolutionMethods.has(m)) return false;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// Compare keys
|
|
1102
|
+
if (this._keys.size !== other._keys.size) return false;
|
|
1103
|
+
for (const [hash, key] of this._keys) {
|
|
1104
|
+
const otherKey = other._keys.get(hash);
|
|
1105
|
+
if (otherKey === undefined || !key.equals(otherKey)) return false;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// Compare delegates
|
|
1109
|
+
if (this._delegates.size !== other._delegates.size) return false;
|
|
1110
|
+
for (const [hash, delegate] of this._delegates) {
|
|
1111
|
+
const otherDelegate = other._delegates.get(hash);
|
|
1112
|
+
if (otherDelegate === undefined || !delegate.equals(otherDelegate)) return false;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// Compare services
|
|
1116
|
+
if (this._services.size !== other._services.size) return false;
|
|
1117
|
+
for (const [uri, service] of this._services) {
|
|
1118
|
+
const otherService = other._services.get(uri);
|
|
1119
|
+
if (otherService === undefined || !service.equals(otherService)) return false;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Compare provenance
|
|
1123
|
+
if (this._provenance === undefined && other._provenance !== undefined) return false;
|
|
1124
|
+
if (this._provenance !== undefined && other._provenance === undefined) return false;
|
|
1125
|
+
if (this._provenance !== undefined && other._provenance !== undefined) {
|
|
1126
|
+
if (!this._provenance.equals(other._provenance)) return false;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// Compare attachments
|
|
1130
|
+
if (!this._attachments.equals(other._attachments)) return false;
|
|
1131
|
+
|
|
1132
|
+
// Compare edges
|
|
1133
|
+
if (!this._edges.equals(other._edges)) return false;
|
|
1134
|
+
|
|
1135
|
+
return true;
|
|
842
1136
|
}
|
|
843
1137
|
|
|
844
1138
|
/**
|
|
@@ -853,6 +1147,17 @@ export class XIDDocument implements EnvelopeEncodable {
|
|
|
853
1147
|
new Map(Array.from(this._services.entries()).map(([k, v]) => [k, v.clone()])),
|
|
854
1148
|
this._provenance?.clone(),
|
|
855
1149
|
);
|
|
1150
|
+
|
|
1151
|
+
// Clone attachments by iterating and re-adding
|
|
1152
|
+
for (const [, env] of this._attachments.iter()) {
|
|
1153
|
+
doc._attachments.addEnvelope(env);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// Clone edges by iterating and re-adding
|
|
1157
|
+
for (const [, env] of this._edges.iter()) {
|
|
1158
|
+
doc._edges.add(env);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
856
1161
|
return doc;
|
|
857
1162
|
}
|
|
858
1163
|
|