@bcts/envelope 1.0.0-alpha.5

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 (44) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +23 -0
  3. package/dist/index.cjs +2646 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +978 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +978 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +2644 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +2552 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +85 -0
  14. package/src/base/assertion.ts +179 -0
  15. package/src/base/assertions.ts +304 -0
  16. package/src/base/cbor.ts +122 -0
  17. package/src/base/digest.ts +204 -0
  18. package/src/base/elide.ts +526 -0
  19. package/src/base/envelope-decodable.ts +229 -0
  20. package/src/base/envelope-encodable.ts +71 -0
  21. package/src/base/envelope.ts +790 -0
  22. package/src/base/error.ts +421 -0
  23. package/src/base/index.ts +56 -0
  24. package/src/base/leaf.ts +226 -0
  25. package/src/base/queries.ts +374 -0
  26. package/src/base/walk.ts +241 -0
  27. package/src/base/wrap.ts +72 -0
  28. package/src/extension/attachment.ts +369 -0
  29. package/src/extension/compress.ts +293 -0
  30. package/src/extension/encrypt.ts +379 -0
  31. package/src/extension/expression.ts +404 -0
  32. package/src/extension/index.ts +72 -0
  33. package/src/extension/proof.ts +276 -0
  34. package/src/extension/recipient.ts +557 -0
  35. package/src/extension/salt.ts +223 -0
  36. package/src/extension/signature.ts +463 -0
  37. package/src/extension/types.ts +222 -0
  38. package/src/format/diagnostic.ts +116 -0
  39. package/src/format/hex.ts +25 -0
  40. package/src/format/index.ts +13 -0
  41. package/src/format/tree.ts +168 -0
  42. package/src/index.ts +32 -0
  43. package/src/utils/index.ts +8 -0
  44. package/src/utils/string.ts +48 -0
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@bcts/envelope",
3
+ "version": "1.0.0-alpha.5",
4
+ "type": "module",
5
+ "description": "Gordian Envelope for TypeScript",
6
+ "license": "BSD-2-Clause-Patent",
7
+ "author": "Leonardo Custodio <leonardo@custodio.me>",
8
+ "homepage": "https://github.com/leonardocustodio/blockchain-commons",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/leonardocustodio/blockchain-commons",
12
+ "directory": "packages/envelope"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/leonardocustodio/blockchain-commons/issues"
16
+ },
17
+ "main": "dist/index.cjs",
18
+ "module": "dist/index.mjs",
19
+ "types": "dist/index.d.mts",
20
+ "browser": "dist/index.iife.js",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.mts",
24
+ "import": "./dist/index.mjs",
25
+ "require": "./dist/index.cjs",
26
+ "browser": "./dist/index.iife.js",
27
+ "default": "./dist/index.mjs"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src",
33
+ "README.md"
34
+ ],
35
+ "scripts": {
36
+ "build": "tsdown",
37
+ "dev": "tsdown --watch",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest",
40
+ "lint": "eslint 'src/**/*.ts' 'tests/**/*.ts'",
41
+ "lint:fix": "eslint 'src/**/*.ts' 'tests/**/*.ts' --fix",
42
+ "typecheck": "tsc --noEmit",
43
+ "clean": "rm -rf dist",
44
+ "docs": "typedoc",
45
+ "prepublishOnly": "npm run clean && npm run build"
46
+ },
47
+ "keywords": [
48
+ "envelope",
49
+ "cbor",
50
+ "dcbor",
51
+ "gordian",
52
+ "blockchain-commons",
53
+ "privacy",
54
+ "encryption",
55
+ "deterministic",
56
+ "encoding",
57
+ "serialization"
58
+ ],
59
+ "engines": {
60
+ "node": ">=18.0.0"
61
+ },
62
+ "devDependencies": {
63
+ "@bcts/eslint": "workspace:*",
64
+ "@bcts/tsconfig": "workspace:*",
65
+ "@eslint/js": "^9.39.1",
66
+ "@types/node": "^24.10.1",
67
+ "@types/pako": "^2.0.3",
68
+ "eslint": "^9.39.1",
69
+ "prettier": "^3.2.5",
70
+ "ts-node": "^10.9.2",
71
+ "tsdown": "^0.17.1",
72
+ "typedoc": "^0.28.15",
73
+ "typescript": "^5.9.3",
74
+ "vitest": "^3.2.4"
75
+ },
76
+ "dependencies": {
77
+ "@bcts/components": "workspace:*",
78
+ "@bcts/crypto": "workspace:*",
79
+ "@bcts/dcbor": "workspace:*",
80
+ "@bcts/known-values": "workspace:*",
81
+ "@bcts/rand": "workspace:*",
82
+ "@bcts/uniform-resources": "workspace:*",
83
+ "pako": "^2.1.0"
84
+ }
85
+ }
@@ -0,0 +1,179 @@
1
+ import { Digest, type DigestProvider } from "./digest";
2
+ import { Envelope } from "./envelope";
3
+ import { type EnvelopeEncodable } from "./envelope-encodable";
4
+ import { EnvelopeError } from "./error";
5
+ import type { Cbor } from "@bcts/dcbor";
6
+ import { CborMap } from "@bcts/dcbor";
7
+
8
+ /// A predicate-object relationship representing an assertion about a subject.
9
+ ///
10
+ /// In Gordian Envelope, assertions are the basic building blocks for attaching
11
+ /// information to a subject. An assertion consists of a predicate (which states
12
+ /// what is being asserted) and an object (which provides the assertion's
13
+ /// value).
14
+ ///
15
+ /// Assertions can be attached to envelope subjects to form semantic statements
16
+ /// like: "subject hasAttribute value" or "document signedBy signature".
17
+ ///
18
+ /// Assertions are equivalent to RDF (Resource Description Framework) triples,
19
+ /// where:
20
+ /// - The envelope's subject is the subject of the triple
21
+ /// - The assertion's predicate is the predicate of the triple
22
+ /// - The assertion's object is the object of the triple
23
+ ///
24
+ /// Generally you do not create an instance of this type directly, but
25
+ /// instead use `Envelope.newAssertion()`, or the various functions
26
+ /// on `Envelope` that create assertions.
27
+ export class Assertion implements DigestProvider {
28
+ readonly #predicate: Envelope;
29
+ readonly #object: Envelope;
30
+ readonly #digest: Digest;
31
+
32
+ /// Creates a new assertion and calculates its digest.
33
+ ///
34
+ /// This constructor takes a predicate and object, both of which are
35
+ /// converted to envelopes using the `EnvelopeEncodable` trait. It then
36
+ /// calculates the assertion's digest by combining the digests of the
37
+ /// predicate and object.
38
+ ///
39
+ /// The digest is calculated according to the Gordian Envelope
40
+ /// specification, which ensures that semantically equivalent assertions
41
+ /// always produce the same digest.
42
+ ///
43
+ /// @param predicate - The predicate of the assertion, which states what is
44
+ /// being asserted
45
+ /// @param object - The object of the assertion, which provides the assertion's
46
+ /// value
47
+ ///
48
+ /// @returns A new assertion with the specified predicate, object, and calculated
49
+ /// digest.
50
+ ///
51
+ /// @example
52
+ /// ```typescript
53
+ /// // Direct method - create an assertion envelope
54
+ /// const assertionEnvelope = Envelope.newAssertion("name", "Alice");
55
+ ///
56
+ /// // Or create and add an assertion to a subject
57
+ /// const person = Envelope.new("person").addAssertion("name", "Alice");
58
+ /// ```
59
+ constructor(predicate: EnvelopeEncodable | Envelope, object: EnvelopeEncodable | Envelope) {
60
+ this.#predicate = predicate instanceof Envelope ? predicate : Envelope.new(predicate);
61
+ this.#object = object instanceof Envelope ? object : Envelope.new(object);
62
+ this.#digest = Digest.fromDigests([this.#predicate.digest(), this.#object.digest()]);
63
+ }
64
+
65
+ /// Returns the predicate of the assertion.
66
+ ///
67
+ /// The predicate states what is being asserted about the subject. It is
68
+ /// typically a string or known value, but can be any envelope.
69
+ ///
70
+ /// @returns A clone of the assertion's predicate envelope.
71
+ predicate(): Envelope {
72
+ return this.#predicate;
73
+ }
74
+
75
+ /// Returns the object of the assertion.
76
+ ///
77
+ /// The object provides the value or content of the assertion. It can be any
78
+ /// type that can be represented as an envelope.
79
+ ///
80
+ /// @returns A clone of the assertion's object envelope.
81
+ object(): Envelope {
82
+ return this.#object;
83
+ }
84
+
85
+ /// Returns the digest of this assertion.
86
+ ///
87
+ /// Implementation of the DigestProvider interface.
88
+ ///
89
+ /// @returns The assertion's digest
90
+ digest(): Digest {
91
+ return this.#digest;
92
+ }
93
+
94
+ /// Checks if two assertions are equal based on digest equality.
95
+ ///
96
+ /// Two assertions are considered equal if they have the same digest,
97
+ /// regardless of how they were constructed.
98
+ ///
99
+ /// @param other - The other assertion to compare with
100
+ /// @returns `true` if the assertions are equal, `false` otherwise
101
+ equals(other: Assertion): boolean {
102
+ return this.#digest.equals(other.#digest);
103
+ }
104
+
105
+ /// Converts this assertion to CBOR.
106
+ ///
107
+ /// The CBOR representation of an assertion is a map with a single key-value
108
+ /// pair, where the key is the predicate's CBOR and the value is the object's
109
+ /// CBOR.
110
+ ///
111
+ /// @returns A CBOR representation of this assertion
112
+ toCbor(): Cbor {
113
+ const map = new CborMap();
114
+ map.set(this.#predicate.untaggedCbor(), this.#object.untaggedCbor());
115
+ return map as unknown as Cbor;
116
+ }
117
+
118
+ /// Attempts to create an assertion from a CBOR value.
119
+ ///
120
+ /// The CBOR must be a map with exactly one entry, where the key represents
121
+ /// the predicate and the value represents the object.
122
+ ///
123
+ /// @param cbor - The CBOR value to convert
124
+ /// @returns A new Assertion instance
125
+ /// @throws {EnvelopeError} If the CBOR is not a valid assertion
126
+ static fromCbor(cbor: Cbor): Assertion {
127
+ // Check if cbor is a Map
128
+ if (!(cbor instanceof CborMap)) {
129
+ throw EnvelopeError.invalidAssertion();
130
+ }
131
+
132
+ return Assertion.fromCborMap(cbor);
133
+ }
134
+
135
+ /// Attempts to create an assertion from a CBOR map.
136
+ ///
137
+ /// The map must have exactly one entry, where the key represents the
138
+ /// predicate and the value represents the object. This is used in
139
+ /// the deserialization process.
140
+ ///
141
+ /// @param map - The CBOR map to convert
142
+ /// @returns A new Assertion instance
143
+ /// @throws {EnvelopeError} If the map doesn't have exactly one entry
144
+ static fromCborMap(map: CborMap): Assertion {
145
+ if (map.size !== 1) {
146
+ throw EnvelopeError.invalidAssertion();
147
+ }
148
+
149
+ const entries = Array.from(map.entries());
150
+ const firstEntry = entries[0];
151
+ if (firstEntry === undefined) {
152
+ throw EnvelopeError.invalidAssertion();
153
+ }
154
+ const [predicateCbor, objectCbor] = firstEntry;
155
+
156
+ const predicate = Envelope.fromUntaggedCbor(predicateCbor);
157
+
158
+ const object = Envelope.fromUntaggedCbor(objectCbor);
159
+
160
+ return new Assertion(predicate, object);
161
+ }
162
+
163
+ /// Creates a string representation of this assertion for debugging.
164
+ ///
165
+ /// @returns A string representation
166
+ toString(): string {
167
+ return `Assertion(${String(this.#predicate)}: ${String(this.#object)})`;
168
+ }
169
+
170
+ /// Creates a copy of this assertion.
171
+ ///
172
+ /// Since assertions are immutable and envelopes are cheap to clone,
173
+ /// this returns the same instance.
174
+ ///
175
+ /// @returns This assertion instance
176
+ clone(): Assertion {
177
+ return this;
178
+ }
179
+ }
@@ -0,0 +1,304 @@
1
+ import { Envelope } from "./envelope";
2
+ import type { EnvelopeEncodableValue } from "./envelope-encodable";
3
+ import { EnvelopeError } from "./error";
4
+
5
+ /// Support for adding assertions.
6
+ ///
7
+ /// Assertions are predicate-object pairs that make statements about an
8
+ /// envelope's subject. This implementation provides methods for adding various
9
+ /// types of assertions to envelopes.
10
+ ///
11
+ /// These methods extend the Envelope class to provide a rich API for
12
+ /// working with assertions, matching the Rust bc-envelope implementation.
13
+
14
+ declare module "./envelope" {
15
+ interface Envelope {
16
+ /// Returns a new envelope with multiple assertion envelopes added.
17
+ ///
18
+ /// This is a convenience method for adding multiple assertions at once.
19
+ /// Each assertion in the array must be a valid assertion envelope or an
20
+ /// obscured variant of one.
21
+ ///
22
+ /// @param assertions - An array of valid assertion envelopes to add
23
+ /// @returns A new envelope with all the assertions added
24
+ /// @throws {EnvelopeError} If any of the provided envelopes are not valid
25
+ /// assertion envelopes
26
+ addAssertionEnvelopes(assertions: Envelope[]): Envelope;
27
+
28
+ /// Adds an optional assertion envelope to this envelope.
29
+ ///
30
+ /// If the optional assertion is present, adds it to the envelope.
31
+ /// Otherwise, returns the envelope unchanged. This method is particularly
32
+ /// useful when working with functions that may or may not return an
33
+ /// assertion.
34
+ ///
35
+ /// The method also ensures that duplicate assertions (with the same digest)
36
+ /// are not added, making it idempotent.
37
+ ///
38
+ /// @param assertion - An optional assertion envelope to add
39
+ /// @returns A new envelope with the assertion added if provided, or the
40
+ /// original envelope if no assertion was provided or it was a duplicate
41
+ /// @throws {EnvelopeError} If the provided envelope is not a valid assertion
42
+ /// envelope or an obscured variant
43
+ addOptionalAssertionEnvelope(assertion: Envelope | undefined): Envelope;
44
+
45
+ /// Adds an assertion with the given predicate and optional object.
46
+ ///
47
+ /// This method is useful when you have a predicate but may or may not have
48
+ /// an object value to associate with it. If the object is present, an
49
+ /// assertion is created and added to the envelope. Otherwise, the
50
+ /// envelope is returned unchanged.
51
+ ///
52
+ /// @param predicate - The predicate for the assertion
53
+ /// @param object - An optional object value for the assertion
54
+ /// @returns A new envelope with the assertion added if the object was
55
+ /// provided, or the original envelope if no object was provided
56
+ addOptionalAssertion(
57
+ predicate: EnvelopeEncodableValue,
58
+ object: EnvelopeEncodableValue | undefined,
59
+ ): Envelope;
60
+
61
+ /// Adds an assertion with the given predicate and string value, but only if
62
+ /// the string is non-empty.
63
+ ///
64
+ /// This is a convenience method that only adds an assertion if the string
65
+ /// value is non-empty. It's particularly useful when working with user
66
+ /// input or optional text fields that should only be included if they
67
+ /// contain actual content.
68
+ ///
69
+ /// @param predicate - The predicate for the assertion
70
+ /// @param str - The string value for the assertion
71
+ /// @returns A new envelope with the assertion added if the string is
72
+ /// non-empty, or the original envelope if the string is empty
73
+ addNonemptyStringAssertion(predicate: EnvelopeEncodableValue, str: string): Envelope;
74
+
75
+ /// Returns a new envelope with the given array of assertions added.
76
+ ///
77
+ /// Similar to `addAssertionEnvelopes` but doesn't throw errors. This is
78
+ /// useful when you're certain all envelopes in the array are valid
79
+ /// assertion envelopes and don't need to handle errors.
80
+ ///
81
+ /// @param envelopes - An array of assertion envelopes to add
82
+ /// @returns A new envelope with all the valid assertions added
83
+ addAssertions(envelopes: Envelope[]): Envelope;
84
+
85
+ /// Adds an assertion only if the provided condition is true.
86
+ ///
87
+ /// This method allows for conditional inclusion of assertions based on a
88
+ /// boolean condition. It's a convenient way to add assertions only in
89
+ /// certain circumstances without requiring separate conditional logic.
90
+ ///
91
+ /// @param condition - Boolean that determines whether to add the assertion
92
+ /// @param predicate - The predicate for the assertion
93
+ /// @param object - The object value for the assertion
94
+ /// @returns A new envelope with the assertion added if the condition is
95
+ /// true, or the original envelope if the condition is false
96
+ addAssertionIf(
97
+ condition: boolean,
98
+ predicate: EnvelopeEncodableValue,
99
+ object: EnvelopeEncodableValue,
100
+ ): Envelope;
101
+
102
+ /// Adds an assertion envelope only if the provided condition is true.
103
+ ///
104
+ /// Similar to `addAssertionIf` but works with pre-constructed assertion
105
+ /// envelopes. This is useful when you have already created an assertion
106
+ /// envelope separately and want to conditionally add it.
107
+ ///
108
+ /// @param condition - Boolean that determines whether to add the assertion
109
+ /// envelope
110
+ /// @param assertionEnvelope - The assertion envelope to add
111
+ /// @returns A new envelope with the assertion added if the condition is
112
+ /// true, or the original envelope if the condition is false
113
+ /// @throws {EnvelopeError} If the provided envelope is not a valid assertion
114
+ /// envelope or an obscured variant and the condition is true
115
+ addAssertionEnvelopeIf(condition: boolean, assertionEnvelope: Envelope): Envelope;
116
+
117
+ /// Returns a new envelope with the given assertion removed.
118
+ ///
119
+ /// Finds and removes an assertion matching the target assertion's digest.
120
+ /// If the assertion doesn't exist, returns the same envelope unchanged.
121
+ /// If removing the assertion would leave the envelope with no assertions,
122
+ /// returns just the subject as a new envelope.
123
+ ///
124
+ /// @param target - The assertion envelope to remove
125
+ /// @returns A new envelope with the specified assertion removed if found,
126
+ /// or the original envelope if not found
127
+ removeAssertion(target: Envelope): Envelope;
128
+
129
+ /// Returns a new envelope with the given assertion replaced by a new one.
130
+ ///
131
+ /// This method removes the specified assertion and adds a new one in its
132
+ /// place. If the targeted assertion does not exist, returns the same
133
+ /// envelope with the new assertion added.
134
+ ///
135
+ /// @param assertion - The assertion envelope to replace
136
+ /// @param newAssertion - The new assertion envelope to add
137
+ /// @returns A new envelope with the assertion replaced if found, or the
138
+ /// original envelope with the new assertion added if not found
139
+ /// @throws {EnvelopeError} If the new assertion is not a valid assertion
140
+ /// envelope or an obscured variant
141
+ replaceAssertion(assertion: Envelope, newAssertion: Envelope): Envelope;
142
+
143
+ /// Returns a new envelope with its subject replaced by the provided one.
144
+ ///
145
+ /// This method preserves all assertions from the original envelope but
146
+ /// applies them to a new subject. It effectively creates a new envelope
147
+ /// with the provided subject and copies over all assertions from the
148
+ /// current envelope.
149
+ ///
150
+ /// @param subject - The new subject for the envelope
151
+ /// @returns A new envelope with the new subject and all assertions from the
152
+ /// original envelope
153
+ replaceSubject(subject: Envelope): Envelope;
154
+
155
+ /// Returns the assertions of this envelope.
156
+ ///
157
+ /// For a node envelope, returns the array of assertion envelopes.
158
+ /// For all other envelope types, returns an empty array.
159
+ ///
160
+ /// @returns An array of assertion envelopes
161
+ assertions(): Envelope[];
162
+ }
163
+ }
164
+
165
+ /// Implementation of addAssertionEnvelopes
166
+ Envelope.prototype.addAssertionEnvelopes = function (
167
+ this: Envelope,
168
+ assertions: Envelope[],
169
+ ): Envelope {
170
+ return assertions.reduce((result, assertion) => result.addAssertionEnvelope(assertion), this);
171
+ };
172
+
173
+ /// Implementation of addOptionalAssertionEnvelope
174
+ Envelope.prototype.addOptionalAssertionEnvelope = function (
175
+ this: Envelope,
176
+ assertion: Envelope | undefined,
177
+ ): Envelope {
178
+ if (assertion === undefined) {
179
+ return this;
180
+ }
181
+
182
+ // Validate that the assertion is a valid assertion or obscured envelope
183
+ if (!assertion.isSubjectAssertion() && !assertion.isSubjectObscured()) {
184
+ throw EnvelopeError.invalidFormat();
185
+ }
186
+
187
+ const c = this.case();
188
+
189
+ // Check if this is already a node
190
+ if (c.type === "node") {
191
+ // Check for duplicate assertions
192
+ const isDuplicate = c.assertions.some((a) => a.digest().equals(assertion.digest()));
193
+ if (isDuplicate) {
194
+ return this;
195
+ }
196
+
197
+ // Add the new assertion
198
+ return Envelope.newWithUncheckedAssertions(c.subject, [...c.assertions, assertion]);
199
+ }
200
+
201
+ // Otherwise, create a new node with this envelope as subject
202
+ return Envelope.newWithUncheckedAssertions(this.subject(), [assertion]);
203
+ };
204
+
205
+ /// Implementation of addOptionalAssertion
206
+ Envelope.prototype.addOptionalAssertion = function (
207
+ this: Envelope,
208
+ predicate: EnvelopeEncodableValue,
209
+ object: EnvelopeEncodableValue | undefined,
210
+ ): Envelope {
211
+ if (object === undefined || object === null) {
212
+ return this;
213
+ }
214
+ return this.addAssertion(predicate, object);
215
+ };
216
+
217
+ /// Implementation of addNonemptyStringAssertion
218
+ Envelope.prototype.addNonemptyStringAssertion = function (
219
+ this: Envelope,
220
+ predicate: EnvelopeEncodableValue,
221
+ str: string,
222
+ ): Envelope {
223
+ if (str.length === 0) {
224
+ return this;
225
+ }
226
+ return this.addAssertion(predicate, str);
227
+ };
228
+
229
+ /// Implementation of addAssertions
230
+ Envelope.prototype.addAssertions = function (this: Envelope, envelopes: Envelope[]): Envelope {
231
+ return envelopes.reduce((result, envelope) => result.addAssertionEnvelope(envelope), this);
232
+ };
233
+
234
+ /// Implementation of addAssertionIf
235
+ Envelope.prototype.addAssertionIf = function (
236
+ this: Envelope,
237
+ condition: boolean,
238
+ predicate: EnvelopeEncodableValue,
239
+ object: EnvelopeEncodableValue,
240
+ ): Envelope {
241
+ if (condition) {
242
+ return this.addAssertion(predicate, object);
243
+ }
244
+ return this;
245
+ };
246
+
247
+ /// Implementation of addAssertionEnvelopeIf
248
+ Envelope.prototype.addAssertionEnvelopeIf = function (
249
+ this: Envelope,
250
+ condition: boolean,
251
+ assertionEnvelope: Envelope,
252
+ ): Envelope {
253
+ if (condition) {
254
+ return this.addAssertionEnvelope(assertionEnvelope);
255
+ }
256
+ return this;
257
+ };
258
+
259
+ /// Implementation of removeAssertion
260
+ Envelope.prototype.removeAssertion = function (this: Envelope, target: Envelope): Envelope {
261
+ const assertions = this.assertions();
262
+ const targetDigest = target.digest();
263
+
264
+ const index = assertions.findIndex((a) => a.digest().equals(targetDigest));
265
+
266
+ if (index === -1) {
267
+ // Assertion not found, return unchanged
268
+ return this;
269
+ }
270
+
271
+ // Remove the assertion
272
+ const newAssertions = [...assertions.slice(0, index), ...assertions.slice(index + 1)];
273
+
274
+ if (newAssertions.length === 0) {
275
+ // No assertions left, return just the subject
276
+ return this.subject();
277
+ }
278
+
279
+ // Return envelope with remaining assertions
280
+ return Envelope.newWithUncheckedAssertions(this.subject(), newAssertions);
281
+ };
282
+
283
+ /// Implementation of replaceAssertion
284
+ Envelope.prototype.replaceAssertion = function (
285
+ this: Envelope,
286
+ assertion: Envelope,
287
+ newAssertion: Envelope,
288
+ ): Envelope {
289
+ return this.removeAssertion(assertion).addAssertionEnvelope(newAssertion);
290
+ };
291
+
292
+ /// Implementation of replaceSubject
293
+ Envelope.prototype.replaceSubject = function (this: Envelope, subject: Envelope): Envelope {
294
+ return this.assertions().reduce((e, a) => e.addAssertionEnvelope(a), subject);
295
+ };
296
+
297
+ /// Implementation of assertions
298
+ Envelope.prototype.assertions = function (this: Envelope): Envelope[] {
299
+ const c = this.case();
300
+ if (c.type === "node") {
301
+ return c.assertions;
302
+ }
303
+ return [];
304
+ };
@@ -0,0 +1,122 @@
1
+ import type { Cbor } from "@bcts/dcbor";
2
+ import {
3
+ type CborTagged,
4
+ type CborTaggedEncodable,
5
+ type CborTaggedDecodable,
6
+ tagsForValues,
7
+ cborData,
8
+ decodeCbor,
9
+ } from "@bcts/dcbor";
10
+ import { ENVELOPE } from "@bcts/components";
11
+ import { Envelope } from "./envelope";
12
+
13
+ const TAG_ENVELOPE = ENVELOPE.value;
14
+
15
+ /// Support for CBOR encoding and decoding of `Envelope`.
16
+ ///
17
+ /// All envelopes are tagged with the `envelope` tag (200). Within that tag,
18
+ /// each of the envelope cases has a unique CBOR signature:
19
+ ///
20
+ /// * `.node` contains a CBOR array, the first element of which is the subject,
21
+ /// followed by one or more assertions.
22
+ /// * `.leaf` is tagged #6.24 (TAG_ENCODED_CBOR) or #6.204 (TAG_LEAF), which
23
+ /// are the IANA tag for embedded CBOR.
24
+ /// * `.wrapped` is tagged with the `envelope` tag.
25
+ /// * `.assertion` is a single-element map `{predicate: object}`.
26
+ /// * `.knownValue` is an unsigned 64-bit integer.
27
+ /// * `.encrypted` is tagged with the `crypto-msg` tag.
28
+ /// * `.elided` is a byte string of length 32 (the digest).
29
+ /// * `.compressed` is tagged with the `compressed` tag.
30
+ ///
31
+ /// This module provides implementations of the CBOR encoding/decoding traits
32
+ /// for the Envelope type, matching the Rust bc-envelope implementation.
33
+
34
+ /// Implements CborTagged interface for Envelope.
35
+ ///
36
+ /// Returns the tags that should be used for CBOR encoding.
37
+ export class EnvelopeCBORTagged implements CborTagged {
38
+ cborTags(): ReturnType<typeof tagsForValues> {
39
+ return tagsForValues([TAG_ENVELOPE]);
40
+ }
41
+
42
+ static cborTags(): number[] {
43
+ return tagsForValues([TAG_ENVELOPE]).map((tag) => Number(tag.value));
44
+ }
45
+ }
46
+
47
+ /// Implements CborTaggedEncodable for Envelope.
48
+ ///
49
+ /// Provides the untagged CBOR representation of an envelope.
50
+ export class EnvelopeCBORTaggedEncodable implements CborTaggedEncodable {
51
+ constructor(private readonly envelope: Envelope) {}
52
+
53
+ cborTags(): ReturnType<typeof tagsForValues> {
54
+ return tagsForValues([TAG_ENVELOPE]);
55
+ }
56
+
57
+ untaggedCbor(): Cbor {
58
+ return this.envelope.untaggedCbor();
59
+ }
60
+
61
+ taggedCbor(): Cbor {
62
+ return this.envelope.taggedCbor();
63
+ }
64
+ }
65
+
66
+ /// Implements CborTaggedDecodable for Envelope.
67
+ ///
68
+ /// Provides the ability to decode an envelope from untagged CBOR.
69
+ export class EnvelopeCBORTaggedDecodable<T = Envelope> implements CborTaggedDecodable<T> {
70
+ cborTags(): ReturnType<typeof tagsForValues> {
71
+ return tagsForValues([TAG_ENVELOPE]);
72
+ }
73
+
74
+ static fromUntaggedCbor(cbor: Cbor): Envelope {
75
+ return Envelope.fromUntaggedCbor(cbor);
76
+ }
77
+
78
+ static fromTaggedCbor(cbor: Cbor): Envelope {
79
+ return Envelope.fromTaggedCbor(cbor);
80
+ }
81
+
82
+ fromUntaggedCbor(cbor: Cbor): T {
83
+ return Envelope.fromUntaggedCbor(cbor) as T;
84
+ }
85
+
86
+ fromTaggedCbor(cbor: Cbor): T {
87
+ return Envelope.fromTaggedCbor(cbor) as T;
88
+ }
89
+ }
90
+
91
+ /// Convenience function to convert an Envelope to CBOR.
92
+ ///
93
+ /// @param envelope - The envelope to convert
94
+ /// @returns The CBOR representation (tagged)
95
+ export function envelopeToCbor(envelope: Envelope): Cbor {
96
+ return envelope.taggedCbor();
97
+ }
98
+
99
+ /// Convenience function to create an Envelope from CBOR.
100
+ ///
101
+ /// @param cbor - The CBOR value (expected to be tagged with TAG_ENVELOPE)
102
+ /// @returns A new Envelope
103
+ export function envelopeFromCbor(cbor: Cbor): Envelope {
104
+ return Envelope.fromTaggedCbor(cbor);
105
+ }
106
+
107
+ /// Convenience function to encode an Envelope to CBOR bytes.
108
+ ///
109
+ /// @param envelope - The envelope to encode
110
+ /// @returns The CBOR bytes
111
+ export function envelopeToBytes(envelope: Envelope): Uint8Array {
112
+ return cborData(envelope.taggedCbor());
113
+ }
114
+
115
+ /// Convenience function to decode an Envelope from CBOR bytes.
116
+ ///
117
+ /// @param bytes - The CBOR bytes
118
+ /// @returns A new Envelope
119
+ export function envelopeFromBytes(bytes: Uint8Array): Envelope {
120
+ const cbor = decodeCbor(bytes);
121
+ return Envelope.fromTaggedCbor(cbor);
122
+ }