@bcts/envelope 1.0.0-alpha.10
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/LICENSE +48 -0
- package/README.md +23 -0
- package/dist/index.cjs +2646 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +782 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +782 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +2644 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +2552 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +84 -0
- package/src/base/assertion.ts +179 -0
- package/src/base/assertions.ts +153 -0
- package/src/base/cbor.ts +122 -0
- package/src/base/digest.ts +204 -0
- package/src/base/elide.ts +390 -0
- package/src/base/envelope-decodable.ts +186 -0
- package/src/base/envelope-encodable.ts +71 -0
- package/src/base/envelope.ts +988 -0
- package/src/base/error.ts +421 -0
- package/src/base/index.ts +56 -0
- package/src/base/leaf.ts +147 -0
- package/src/base/queries.ts +244 -0
- package/src/base/walk.ts +215 -0
- package/src/base/wrap.ts +26 -0
- package/src/extension/attachment.ts +280 -0
- package/src/extension/compress.ts +176 -0
- package/src/extension/encrypt.ts +297 -0
- package/src/extension/expression.ts +404 -0
- package/src/extension/index.ts +72 -0
- package/src/extension/proof.ts +227 -0
- package/src/extension/recipient.ts +440 -0
- package/src/extension/salt.ts +114 -0
- package/src/extension/signature.ts +398 -0
- package/src/extension/types.ts +92 -0
- package/src/format/diagnostic.ts +116 -0
- package/src/format/hex.ts +25 -0
- package/src/format/index.ts +13 -0
- package/src/format/tree.ts +168 -0
- package/src/index.ts +32 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/string.ts +48 -0
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bcts/envelope",
|
|
3
|
+
"version": "1.0.0-alpha.10",
|
|
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://bcts.dev",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/leonardocustodio/bcts",
|
|
12
|
+
"directory": "packages/envelope"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/leonardocustodio/bcts/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
|
+
"default": "./dist/index.mjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"src",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsdown",
|
|
36
|
+
"dev": "tsdown --watch",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"lint": "eslint 'src/**/*.ts' 'tests/**/*.ts'",
|
|
40
|
+
"lint:fix": "eslint 'src/**/*.ts' 'tests/**/*.ts' --fix",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"docs": "typedoc",
|
|
44
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"envelope",
|
|
48
|
+
"cbor",
|
|
49
|
+
"dcbor",
|
|
50
|
+
"gordian",
|
|
51
|
+
"blockchain-commons",
|
|
52
|
+
"privacy",
|
|
53
|
+
"encryption",
|
|
54
|
+
"deterministic",
|
|
55
|
+
"encoding",
|
|
56
|
+
"serialization"
|
|
57
|
+
],
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=18.0.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@bcts/eslint": "^0.1.0",
|
|
63
|
+
"@bcts/tsconfig": "^0.1.0",
|
|
64
|
+
"@eslint/js": "^9.39.2",
|
|
65
|
+
"@types/node": "^25.0.3",
|
|
66
|
+
"@types/pako": "^2.0.3",
|
|
67
|
+
"eslint": "^9.39.2",
|
|
68
|
+
"prettier": "^3.2.5",
|
|
69
|
+
"ts-node": "^10.9.2",
|
|
70
|
+
"tsdown": "^0.18.3",
|
|
71
|
+
"typedoc": "^0.28.15",
|
|
72
|
+
"typescript": "^5.9.3",
|
|
73
|
+
"vitest": "^4.0.16"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@bcts/components": "^1.0.0-alpha.10",
|
|
77
|
+
"@bcts/crypto": "^1.0.0-alpha.10",
|
|
78
|
+
"@bcts/dcbor": "^1.0.0-alpha.10",
|
|
79
|
+
"@bcts/known-values": "^1.0.0-alpha.10",
|
|
80
|
+
"@bcts/rand": "^1.0.0-alpha.10",
|
|
81
|
+
"@bcts/uniform-resources": "^1.0.0-alpha.10",
|
|
82
|
+
"pako": "^2.1.0"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -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,153 @@
|
|
|
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
|
+
/// Implementation of addAssertionEnvelopes
|
|
15
|
+
Envelope.prototype.addAssertionEnvelopes = function (
|
|
16
|
+
this: Envelope,
|
|
17
|
+
assertions: Envelope[],
|
|
18
|
+
): Envelope {
|
|
19
|
+
return assertions.reduce((result, assertion) => result.addAssertionEnvelope(assertion), this);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/// Implementation of addOptionalAssertionEnvelope
|
|
23
|
+
Envelope.prototype.addOptionalAssertionEnvelope = function (
|
|
24
|
+
this: Envelope,
|
|
25
|
+
assertion: Envelope | undefined,
|
|
26
|
+
): Envelope {
|
|
27
|
+
if (assertion === undefined) {
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Validate that the assertion is a valid assertion or obscured envelope
|
|
32
|
+
if (!assertion.isSubjectAssertion() && !assertion.isSubjectObscured()) {
|
|
33
|
+
throw EnvelopeError.invalidFormat();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const c = this.case();
|
|
37
|
+
|
|
38
|
+
// Check if this is already a node
|
|
39
|
+
if (c.type === "node") {
|
|
40
|
+
// Check for duplicate assertions
|
|
41
|
+
const isDuplicate = c.assertions.some((a) => a.digest().equals(assertion.digest()));
|
|
42
|
+
if (isDuplicate) {
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Add the new assertion
|
|
47
|
+
return Envelope.newWithUncheckedAssertions(c.subject, [...c.assertions, assertion]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Otherwise, create a new node with this envelope as subject
|
|
51
|
+
return Envelope.newWithUncheckedAssertions(this.subject(), [assertion]);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/// Implementation of addOptionalAssertion
|
|
55
|
+
Envelope.prototype.addOptionalAssertion = function (
|
|
56
|
+
this: Envelope,
|
|
57
|
+
predicate: EnvelopeEncodableValue,
|
|
58
|
+
object: EnvelopeEncodableValue | undefined,
|
|
59
|
+
): Envelope {
|
|
60
|
+
if (object === undefined || object === null) {
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
return this.addAssertion(predicate, object);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/// Implementation of addNonemptyStringAssertion
|
|
67
|
+
Envelope.prototype.addNonemptyStringAssertion = function (
|
|
68
|
+
this: Envelope,
|
|
69
|
+
predicate: EnvelopeEncodableValue,
|
|
70
|
+
str: string,
|
|
71
|
+
): Envelope {
|
|
72
|
+
if (str.length === 0) {
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
return this.addAssertion(predicate, str);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/// Implementation of addAssertions
|
|
79
|
+
Envelope.prototype.addAssertions = function (this: Envelope, envelopes: Envelope[]): Envelope {
|
|
80
|
+
return envelopes.reduce((result, envelope) => result.addAssertionEnvelope(envelope), this);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/// Implementation of addAssertionIf
|
|
84
|
+
Envelope.prototype.addAssertionIf = function (
|
|
85
|
+
this: Envelope,
|
|
86
|
+
condition: boolean,
|
|
87
|
+
predicate: EnvelopeEncodableValue,
|
|
88
|
+
object: EnvelopeEncodableValue,
|
|
89
|
+
): Envelope {
|
|
90
|
+
if (condition) {
|
|
91
|
+
return this.addAssertion(predicate, object);
|
|
92
|
+
}
|
|
93
|
+
return this;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/// Implementation of addAssertionEnvelopeIf
|
|
97
|
+
Envelope.prototype.addAssertionEnvelopeIf = function (
|
|
98
|
+
this: Envelope,
|
|
99
|
+
condition: boolean,
|
|
100
|
+
assertionEnvelope: Envelope,
|
|
101
|
+
): Envelope {
|
|
102
|
+
if (condition) {
|
|
103
|
+
return this.addAssertionEnvelope(assertionEnvelope);
|
|
104
|
+
}
|
|
105
|
+
return this;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/// Implementation of removeAssertion
|
|
109
|
+
Envelope.prototype.removeAssertion = function (this: Envelope, target: Envelope): Envelope {
|
|
110
|
+
const assertions = this.assertions();
|
|
111
|
+
const targetDigest = target.digest();
|
|
112
|
+
|
|
113
|
+
const index = assertions.findIndex((a) => a.digest().equals(targetDigest));
|
|
114
|
+
|
|
115
|
+
if (index === -1) {
|
|
116
|
+
// Assertion not found, return unchanged
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Remove the assertion
|
|
121
|
+
const newAssertions = [...assertions.slice(0, index), ...assertions.slice(index + 1)];
|
|
122
|
+
|
|
123
|
+
if (newAssertions.length === 0) {
|
|
124
|
+
// No assertions left, return just the subject
|
|
125
|
+
return this.subject();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Return envelope with remaining assertions
|
|
129
|
+
return Envelope.newWithUncheckedAssertions(this.subject(), newAssertions);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/// Implementation of replaceAssertion
|
|
133
|
+
Envelope.prototype.replaceAssertion = function (
|
|
134
|
+
this: Envelope,
|
|
135
|
+
assertion: Envelope,
|
|
136
|
+
newAssertion: Envelope,
|
|
137
|
+
): Envelope {
|
|
138
|
+
return this.removeAssertion(assertion).addAssertionEnvelope(newAssertion);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/// Implementation of replaceSubject
|
|
142
|
+
Envelope.prototype.replaceSubject = function (this: Envelope, subject: Envelope): Envelope {
|
|
143
|
+
return this.assertions().reduce((e, a) => e.addAssertionEnvelope(a), subject);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/// Implementation of assertions
|
|
147
|
+
Envelope.prototype.assertions = function (this: Envelope): Envelope[] {
|
|
148
|
+
const c = this.case();
|
|
149
|
+
if (c.type === "node") {
|
|
150
|
+
return c.assertions;
|
|
151
|
+
}
|
|
152
|
+
return [];
|
|
153
|
+
};
|
package/src/base/cbor.ts
ADDED
|
@@ -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
|
+
}
|