@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.
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 +782 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +782 -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 +84 -0
  14. package/src/base/assertion.ts +179 -0
  15. package/src/base/assertions.ts +153 -0
  16. package/src/base/cbor.ts +122 -0
  17. package/src/base/digest.ts +204 -0
  18. package/src/base/elide.ts +390 -0
  19. package/src/base/envelope-decodable.ts +186 -0
  20. package/src/base/envelope-encodable.ts +71 -0
  21. package/src/base/envelope.ts +988 -0
  22. package/src/base/error.ts +421 -0
  23. package/src/base/index.ts +56 -0
  24. package/src/base/leaf.ts +147 -0
  25. package/src/base/queries.ts +244 -0
  26. package/src/base/walk.ts +215 -0
  27. package/src/base/wrap.ts +26 -0
  28. package/src/extension/attachment.ts +280 -0
  29. package/src/extension/compress.ts +176 -0
  30. package/src/extension/encrypt.ts +297 -0
  31. package/src/extension/expression.ts +404 -0
  32. package/src/extension/index.ts +72 -0
  33. package/src/extension/proof.ts +227 -0
  34. package/src/extension/recipient.ts +440 -0
  35. package/src/extension/salt.ts +114 -0
  36. package/src/extension/signature.ts +398 -0
  37. package/src/extension/types.ts +92 -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
@@ -0,0 +1,244 @@
1
+ // Cbor type available if needed later
2
+ import { Envelope } from "./envelope";
3
+ import type { EnvelopeEncodableValue } from "./envelope-encodable";
4
+ import { EnvelopeError } from "./error";
5
+
6
+ /// Provides methods for querying envelope structure and extracting data.
7
+ ///
8
+ /// The `queries` module contains methods for:
9
+ ///
10
+ /// 1. **Structural queries**: Methods for examining the envelope's structure
11
+ /// (`subject()`, `assertions()`)
12
+ /// 2. **Type queries**: Methods for determining the envelope's type
13
+ /// (`isLeaf()`, `isNode()`, etc.)
14
+ /// 3. **Content extraction**: Methods for extracting typed content from
15
+ /// envelopes (`extractSubject()`, `extractObjectForPredicate()`)
16
+ /// 4. **Assertion queries**: Methods for finding assertions with specific
17
+ /// predicates (`assertionWithPredicate()`)
18
+ ///
19
+ /// These methods enable traversal and inspection of envelope hierarchies,
20
+ /// allowing for flexible manipulation and access to envelope data structures.
21
+
22
+ /// Implementation of hasAssertions()
23
+ Envelope.prototype.hasAssertions = function (this: Envelope): boolean {
24
+ const c = this.case();
25
+ return c.type === "node" && c.assertions.length > 0;
26
+ };
27
+
28
+ /// Implementation of asAssertion()
29
+ Envelope.prototype.asAssertion = function (this: Envelope): Envelope | undefined {
30
+ const c = this.case();
31
+ return c.type === "assertion" ? this : undefined;
32
+ };
33
+
34
+ /// Implementation of tryAssertion()
35
+ Envelope.prototype.tryAssertion = function (this: Envelope): Envelope {
36
+ const result = this.asAssertion();
37
+ if (result === undefined) {
38
+ throw EnvelopeError.notAssertion();
39
+ }
40
+ return result;
41
+ };
42
+
43
+ /// Implementation of asPredicate()
44
+ Envelope.prototype.asPredicate = function (this: Envelope): Envelope | undefined {
45
+ // Refer to subject in case the assertion is a node and therefore has
46
+ // its own assertions
47
+ const subj = this.subject();
48
+ const c = subj.case();
49
+ if (c.type === "assertion") {
50
+ return c.assertion.predicate();
51
+ }
52
+ return undefined;
53
+ };
54
+
55
+ /// Implementation of tryPredicate()
56
+ Envelope.prototype.tryPredicate = function (this: Envelope): Envelope {
57
+ const result = this.asPredicate();
58
+ if (result === undefined) {
59
+ throw EnvelopeError.notAssertion();
60
+ }
61
+ return result;
62
+ };
63
+
64
+ /// Implementation of asObject()
65
+ Envelope.prototype.asObject = function (this: Envelope): Envelope | undefined {
66
+ // Refer to subject in case the assertion is a node and therefore has
67
+ // its own assertions
68
+ const subj = this.subject();
69
+ const c = subj.case();
70
+ if (c.type === "assertion") {
71
+ return c.assertion.object();
72
+ }
73
+ return undefined;
74
+ };
75
+
76
+ /// Implementation of tryObject()
77
+ Envelope.prototype.tryObject = function (this: Envelope): Envelope {
78
+ const result = this.asObject();
79
+ if (result === undefined) {
80
+ throw EnvelopeError.notAssertion();
81
+ }
82
+ return result;
83
+ };
84
+
85
+ /// Implementation of isAssertion()
86
+ Envelope.prototype.isAssertion = function (this: Envelope): boolean {
87
+ return this.case().type === "assertion";
88
+ };
89
+
90
+ /// Implementation of isElided()
91
+ Envelope.prototype.isElided = function (this: Envelope): boolean {
92
+ return this.case().type === "elided";
93
+ };
94
+
95
+ /// Implementation of isLeaf()
96
+ Envelope.prototype.isLeaf = function (this: Envelope): boolean {
97
+ return this.case().type === "leaf";
98
+ };
99
+
100
+ /// Implementation of isNode()
101
+ Envelope.prototype.isNode = function (this: Envelope): boolean {
102
+ return this.case().type === "node";
103
+ };
104
+
105
+ /// Implementation of isWrapped()
106
+ Envelope.prototype.isWrapped = function (this: Envelope): boolean {
107
+ return this.case().type === "wrapped";
108
+ };
109
+
110
+ /// Implementation of isInternal()
111
+ Envelope.prototype.isInternal = function (this: Envelope): boolean {
112
+ const type = this.case().type;
113
+ return type === "node" || type === "wrapped" || type === "assertion";
114
+ };
115
+
116
+ /// Implementation of isObscured()
117
+ Envelope.prototype.isObscured = function (this: Envelope): boolean {
118
+ const type = this.case().type;
119
+ return type === "elided" || type === "encrypted" || type === "compressed";
120
+ };
121
+
122
+ /// Implementation of assertionsWithPredicate()
123
+ Envelope.prototype.assertionsWithPredicate = function (
124
+ this: Envelope,
125
+ predicate: EnvelopeEncodableValue,
126
+ ): Envelope[] {
127
+ const predicateEnv = Envelope.new(predicate);
128
+ const predicateDigest = predicateEnv.digest();
129
+
130
+ return this.assertions().filter((assertion) => {
131
+ const pred = assertion.subject().asPredicate();
132
+ return pred?.digest().equals(predicateDigest) === true;
133
+ });
134
+ };
135
+
136
+ /// Implementation of assertionWithPredicate()
137
+ Envelope.prototype.assertionWithPredicate = function (
138
+ this: Envelope,
139
+ predicate: EnvelopeEncodableValue,
140
+ ): Envelope {
141
+ const matches = this.assertionsWithPredicate(predicate);
142
+
143
+ if (matches.length === 0) {
144
+ throw EnvelopeError.nonexistentPredicate();
145
+ }
146
+ if (matches.length > 1) {
147
+ throw EnvelopeError.ambiguousPredicate();
148
+ }
149
+
150
+ return matches[0];
151
+ };
152
+
153
+ /// Implementation of optionalAssertionWithPredicate()
154
+ Envelope.prototype.optionalAssertionWithPredicate = function (
155
+ this: Envelope,
156
+ predicate: EnvelopeEncodableValue,
157
+ ): Envelope | undefined {
158
+ const matches = this.assertionsWithPredicate(predicate);
159
+
160
+ if (matches.length === 0) {
161
+ return undefined;
162
+ }
163
+ if (matches.length > 1) {
164
+ throw EnvelopeError.ambiguousPredicate();
165
+ }
166
+
167
+ return matches[0];
168
+ };
169
+
170
+ /// Implementation of objectForPredicate()
171
+ Envelope.prototype.objectForPredicate = function (
172
+ this: Envelope,
173
+ predicate: EnvelopeEncodableValue,
174
+ ): Envelope {
175
+ const assertion = this.assertionWithPredicate(predicate);
176
+ const obj = assertion.asObject();
177
+ if (obj === undefined) {
178
+ throw EnvelopeError.notAssertion();
179
+ }
180
+ return obj;
181
+ };
182
+
183
+ /// Implementation of optionalObjectForPredicate()
184
+ Envelope.prototype.optionalObjectForPredicate = function (
185
+ this: Envelope,
186
+ predicate: EnvelopeEncodableValue,
187
+ ): Envelope | undefined {
188
+ const matches = this.assertionsWithPredicate(predicate);
189
+
190
+ if (matches.length === 0) {
191
+ return undefined;
192
+ }
193
+ if (matches.length > 1) {
194
+ throw EnvelopeError.ambiguousPredicate();
195
+ }
196
+
197
+ const obj = matches[0].subject().asObject();
198
+ return obj;
199
+ };
200
+
201
+ /// Implementation of objectsForPredicate()
202
+ Envelope.prototype.objectsForPredicate = function (
203
+ this: Envelope,
204
+ predicate: EnvelopeEncodableValue,
205
+ ): Envelope[] {
206
+ return this.assertionsWithPredicate(predicate).map((assertion) => {
207
+ const obj = assertion.asObject();
208
+ if (obj === undefined) {
209
+ throw EnvelopeError.notAssertion();
210
+ }
211
+ return obj;
212
+ });
213
+ };
214
+
215
+ /// Implementation of elementsCount()
216
+ Envelope.prototype.elementsCount = function (this: Envelope): number {
217
+ let count = 1; // Count this envelope
218
+
219
+ const c = this.case();
220
+ switch (c.type) {
221
+ case "node":
222
+ count += c.subject.elementsCount();
223
+ for (const assertion of c.assertions) {
224
+ count += assertion.elementsCount();
225
+ }
226
+ break;
227
+ case "assertion":
228
+ count += c.assertion.predicate().elementsCount();
229
+ count += c.assertion.object().elementsCount();
230
+ break;
231
+ case "wrapped":
232
+ count += c.envelope.elementsCount();
233
+ break;
234
+ case "leaf":
235
+ case "elided":
236
+ case "knownValue":
237
+ case "encrypted":
238
+ case "compressed":
239
+ // These cases don't contribute additional elements
240
+ break;
241
+ }
242
+
243
+ return count;
244
+ };
@@ -0,0 +1,215 @@
1
+ import { Envelope } from "./envelope";
2
+
3
+ /// Functions for traversing and manipulating the envelope hierarchy.
4
+ ///
5
+ /// This module provides functionality for traversing the hierarchical structure
6
+ /// of envelopes, allowing for operations such as inspection, transformation,
7
+ /// and extraction of specific elements. It implements a visitor pattern that
8
+ /// enables executing arbitrary code on each element of an envelope in a
9
+ /// structured way.
10
+ ///
11
+ /// The traversal can be performed in two modes:
12
+ /// - Structure-based traversal: Visits every element in the envelope hierarchy
13
+ /// - Tree-based traversal: Skips node elements and focuses on the semantic
14
+ /// content
15
+
16
+ /// The type of incoming edge provided to the visitor.
17
+ ///
18
+ /// This enum identifies how an envelope element is connected to its parent in
19
+ /// the hierarchy during traversal. It helps the visitor function understand the
20
+ /// semantic relationship between elements.
21
+ export enum EdgeType {
22
+ /// No incoming edge (root)
23
+ None = "none",
24
+ /// Element is the subject of a node
25
+ Subject = "subject",
26
+ /// Element is an assertion on a node
27
+ Assertion = "assertion",
28
+ /// Element is the predicate of an assertion
29
+ Predicate = "predicate",
30
+ /// Element is the object of an assertion
31
+ Object = "object",
32
+ /// Element is the content wrapped by another envelope
33
+ Content = "content",
34
+ }
35
+
36
+ /// Returns a short text label for the edge type, or undefined if no label is
37
+ /// needed.
38
+ ///
39
+ /// This is primarily used for tree formatting to identify relationships
40
+ /// between elements.
41
+ ///
42
+ /// @param edgeType - The edge type
43
+ /// @returns A short label or undefined
44
+ export function edgeLabel(edgeType: EdgeType): string | undefined {
45
+ switch (edgeType) {
46
+ case EdgeType.Subject:
47
+ return "subj";
48
+ case EdgeType.Content:
49
+ return "cont";
50
+ case EdgeType.Predicate:
51
+ return "pred";
52
+ case EdgeType.Object:
53
+ return "obj";
54
+ case EdgeType.None:
55
+ case EdgeType.Assertion:
56
+ return undefined;
57
+ default:
58
+ return undefined;
59
+ }
60
+ }
61
+
62
+ /// A visitor function that is called for each element in the envelope.
63
+ ///
64
+ /// The visitor function takes the following parameters:
65
+ /// - `envelope`: The current envelope element being visited
66
+ /// - `level`: The depth level in the hierarchy (0 for root)
67
+ /// - `incomingEdge`: The type of edge connecting this element to its parent
68
+ /// - `state`: Optional context passed down from the parent's visitor call
69
+ ///
70
+ /// The visitor returns a tuple of:
71
+ /// - The state that will be passed to child elements
72
+ /// - A boolean indicating whether to stop traversal (true = stop)
73
+ ///
74
+ /// This enables accumulating state or passing context during traversal.
75
+ export type Visitor<State> = (
76
+ envelope: Envelope,
77
+ level: number,
78
+ incomingEdge: EdgeType,
79
+ state: State,
80
+ ) => [State, boolean];
81
+
82
+ /// Implementation of walk()
83
+ Envelope.prototype.walk = function <State>(
84
+ this: Envelope,
85
+ hideNodes: boolean,
86
+ state: State,
87
+ visit: Visitor<State>,
88
+ ): void {
89
+ if (hideNodes) {
90
+ walkTree(this, 0, EdgeType.None, state, visit);
91
+ } else {
92
+ walkStructure(this, 0, EdgeType.None, state, visit);
93
+ }
94
+ };
95
+
96
+ /// Recursive implementation of structure-based traversal.
97
+ ///
98
+ /// This internal function performs the actual recursive traversal of the
99
+ /// envelope structure, visiting every element and maintaining the
100
+ /// correct level and edge relationships.
101
+ function walkStructure<State>(
102
+ envelope: Envelope,
103
+ level: number,
104
+ incomingEdge: EdgeType,
105
+ state: State,
106
+ visit: Visitor<State>,
107
+ ): void {
108
+ // Visit this envelope
109
+ const [newState, stop] = visit(envelope, level, incomingEdge, state);
110
+ if (stop) {
111
+ return;
112
+ }
113
+
114
+ const nextLevel = level + 1;
115
+ const c = envelope.case();
116
+
117
+ switch (c.type) {
118
+ case "node":
119
+ // Visit subject
120
+ walkStructure(c.subject, nextLevel, EdgeType.Subject, newState, visit);
121
+ // Visit all assertions
122
+ for (const assertion of c.assertions) {
123
+ walkStructure(assertion, nextLevel, EdgeType.Assertion, newState, visit);
124
+ }
125
+ break;
126
+
127
+ case "wrapped":
128
+ // Visit wrapped envelope
129
+ walkStructure(c.envelope, nextLevel, EdgeType.Content, newState, visit);
130
+ break;
131
+
132
+ case "assertion":
133
+ // Visit predicate and object
134
+ walkStructure(c.assertion.predicate(), nextLevel, EdgeType.Predicate, newState, visit);
135
+ walkStructure(c.assertion.object(), nextLevel, EdgeType.Object, newState, visit);
136
+ break;
137
+
138
+ case "leaf":
139
+ case "elided":
140
+ case "knownValue":
141
+ case "encrypted":
142
+ case "compressed":
143
+ // Leaf nodes and other types have no children
144
+ break;
145
+ }
146
+ }
147
+
148
+ /// Recursive implementation of tree-based traversal.
149
+ ///
150
+ /// This internal function performs the actual recursive traversal of the
151
+ /// envelope's semantic tree, skipping node containers and focusing on
152
+ /// the semantic content elements. It maintains the correct level and
153
+ /// edge relationships while skipping structural elements.
154
+ function walkTree<State>(
155
+ envelope: Envelope,
156
+ level: number,
157
+ incomingEdge: EdgeType,
158
+ state: State,
159
+ visit: Visitor<State>,
160
+ ): State {
161
+ let currentState = state;
162
+ let subjectLevel = level;
163
+
164
+ // Skip visiting if this is a node
165
+ if (!envelope.isNode()) {
166
+ const [newState, stop] = visit(envelope, level, incomingEdge, currentState);
167
+ if (stop) {
168
+ return newState;
169
+ }
170
+ currentState = newState;
171
+ subjectLevel = level + 1;
172
+ }
173
+
174
+ const c = envelope.case();
175
+
176
+ switch (c.type) {
177
+ case "node": {
178
+ // Visit subject
179
+ const assertionState = walkTree(
180
+ c.subject,
181
+ subjectLevel,
182
+ EdgeType.Subject,
183
+ currentState,
184
+ visit,
185
+ );
186
+ // Visit all assertions
187
+ const assertionLevel = subjectLevel + 1;
188
+ for (const assertion of c.assertions) {
189
+ walkTree(assertion, assertionLevel, EdgeType.Assertion, assertionState, visit);
190
+ }
191
+ break;
192
+ }
193
+
194
+ case "wrapped":
195
+ // Visit wrapped envelope
196
+ walkTree(c.envelope, subjectLevel, EdgeType.Content, currentState, visit);
197
+ break;
198
+
199
+ case "assertion":
200
+ // Visit predicate and object
201
+ walkTree(c.assertion.predicate(), subjectLevel, EdgeType.Predicate, currentState, visit);
202
+ walkTree(c.assertion.object(), subjectLevel, EdgeType.Object, currentState, visit);
203
+ break;
204
+
205
+ case "leaf":
206
+ case "elided":
207
+ case "knownValue":
208
+ case "encrypted":
209
+ case "compressed":
210
+ // Leaf nodes and other types have no children
211
+ break;
212
+ }
213
+
214
+ return currentState;
215
+ }
@@ -0,0 +1,26 @@
1
+ import { Envelope } from "./envelope";
2
+ import { EnvelopeError } from "./error";
3
+
4
+ /// Support for wrapping and unwrapping envelopes.
5
+ ///
6
+ /// Wrapping allows treating an envelope (including its assertions) as a single
7
+ /// unit, making it possible to add assertions about the envelope as a whole.
8
+
9
+ /// Implementation of wrap()
10
+ Envelope.prototype.wrap = function (this: Envelope): Envelope {
11
+ return Envelope.newWrapped(this);
12
+ };
13
+
14
+ /// Implementation of tryUnwrap()
15
+ Envelope.prototype.tryUnwrap = function (this: Envelope): Envelope {
16
+ const c = this.subject().case();
17
+ if (c.type === "wrapped") {
18
+ return c.envelope;
19
+ }
20
+ throw EnvelopeError.notWrapped();
21
+ };
22
+
23
+ /// Implementation of unwrap() - alias for tryUnwrap()
24
+ Envelope.prototype.unwrap = function (this: Envelope): Envelope {
25
+ return this.tryUnwrap();
26
+ };