@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
@@ -0,0 +1,374 @@
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
+ declare module "./envelope" {
23
+ interface Envelope {
24
+ /// Returns true if the envelope has at least one assertion.
25
+ ///
26
+ /// @returns `true` if there are assertions, `false` otherwise
27
+ hasAssertions(): boolean;
28
+
29
+ /// Returns the envelope as an assertion if it is one.
30
+ ///
31
+ /// @returns The assertion envelope or undefined
32
+ asAssertion(): Envelope | undefined;
33
+
34
+ /// Returns the envelope as an assertion or throws an error.
35
+ ///
36
+ /// @returns The assertion envelope
37
+ /// @throws {EnvelopeError} If the envelope is not an assertion
38
+ tryAssertion(): Envelope;
39
+
40
+ /// Returns the predicate of this assertion envelope.
41
+ ///
42
+ /// @returns The predicate envelope or undefined
43
+ asPredicate(): Envelope | undefined;
44
+
45
+ /// Returns the predicate of this assertion envelope or throws an error.
46
+ ///
47
+ /// @returns The predicate envelope
48
+ /// @throws {EnvelopeError} If the envelope is not an assertion
49
+ tryPredicate(): Envelope;
50
+
51
+ /// Returns the object of this assertion envelope.
52
+ ///
53
+ /// @returns The object envelope or undefined
54
+ asObject(): Envelope | undefined;
55
+
56
+ /// Returns the object of this assertion envelope or throws an error.
57
+ ///
58
+ /// @returns The object envelope
59
+ /// @throws {EnvelopeError} If the envelope is not an assertion
60
+ tryObject(): Envelope;
61
+
62
+ /// Checks if this envelope is case Assertion.
63
+ ///
64
+ /// @returns `true` if this is an assertion envelope
65
+ isAssertion(): boolean;
66
+
67
+ /// Checks if this envelope is case Elided.
68
+ ///
69
+ /// @returns `true` if this is an elided envelope
70
+ isElided(): boolean;
71
+
72
+ /// Checks if this envelope is case Leaf.
73
+ ///
74
+ /// @returns `true` if this is a leaf envelope
75
+ isLeaf(): boolean;
76
+
77
+ /// Checks if this envelope is case Node.
78
+ ///
79
+ /// @returns `true` if this is a node envelope
80
+ isNode(): boolean;
81
+
82
+ /// Checks if this envelope is case Wrapped.
83
+ ///
84
+ /// @returns `true` if this is a wrapped envelope
85
+ isWrapped(): boolean;
86
+
87
+ /// Checks if this envelope is internal (has child elements).
88
+ ///
89
+ /// Internal elements include node, wrapped, and assertion.
90
+ ///
91
+ /// @returns `true` if this envelope has children
92
+ isInternal(): boolean;
93
+
94
+ /// Checks if this envelope is obscured (elided, encrypted, or compressed).
95
+ ///
96
+ /// @returns `true` if this envelope is obscured
97
+ isObscured(): boolean;
98
+
99
+ /// Returns all assertions with the given predicate.
100
+ ///
101
+ /// Match is performed by comparing digests.
102
+ ///
103
+ /// @param predicate - The predicate to search for
104
+ /// @returns An array of matching assertion envelopes
105
+ assertionsWithPredicate(predicate: EnvelopeEncodableValue): Envelope[];
106
+
107
+ /// Returns the assertion with the given predicate.
108
+ ///
109
+ /// @param predicate - The predicate to search for
110
+ /// @returns The matching assertion envelope
111
+ /// @throws {EnvelopeError} If no assertion or multiple assertions match
112
+ assertionWithPredicate(predicate: EnvelopeEncodableValue): Envelope;
113
+
114
+ /// Returns the assertion with the given predicate, or undefined if not
115
+ /// found.
116
+ ///
117
+ /// @param predicate - The predicate to search for
118
+ /// @returns The matching assertion envelope or undefined
119
+ /// @throws {EnvelopeError} If multiple assertions match
120
+ optionalAssertionWithPredicate(predicate: EnvelopeEncodableValue): Envelope | undefined;
121
+
122
+ /// Returns the object of the assertion with the given predicate.
123
+ ///
124
+ /// @param predicate - The predicate to search for
125
+ /// @returns The object envelope
126
+ /// @throws {EnvelopeError} If no assertion or multiple assertions match
127
+ objectForPredicate(predicate: EnvelopeEncodableValue): Envelope;
128
+
129
+ /// Returns the object of the assertion with the given predicate, or
130
+ /// undefined if not found.
131
+ ///
132
+ /// @param predicate - The predicate to search for
133
+ /// @returns The object envelope or undefined
134
+ /// @throws {EnvelopeError} If multiple assertions match
135
+ optionalObjectForPredicate(predicate: EnvelopeEncodableValue): Envelope | undefined;
136
+
137
+ /// Returns the objects of all assertions with the matching predicate.
138
+ ///
139
+ /// @param predicate - The predicate to search for
140
+ /// @returns An array of object envelopes
141
+ objectsForPredicate(predicate: EnvelopeEncodableValue): Envelope[];
142
+
143
+ /// Returns the number of structural elements in the envelope.
144
+ ///
145
+ /// This includes the envelope itself and all nested elements.
146
+ ///
147
+ /// @returns The total element count
148
+ elementsCount(): number;
149
+ }
150
+ }
151
+
152
+ /// Implementation of hasAssertions()
153
+ Envelope.prototype.hasAssertions = function (this: Envelope): boolean {
154
+ const c = this.case();
155
+ return c.type === "node" && c.assertions.length > 0;
156
+ };
157
+
158
+ /// Implementation of asAssertion()
159
+ Envelope.prototype.asAssertion = function (this: Envelope): Envelope | undefined {
160
+ const c = this.case();
161
+ return c.type === "assertion" ? this : undefined;
162
+ };
163
+
164
+ /// Implementation of tryAssertion()
165
+ Envelope.prototype.tryAssertion = function (this: Envelope): Envelope {
166
+ const result = this.asAssertion();
167
+ if (result === undefined) {
168
+ throw EnvelopeError.notAssertion();
169
+ }
170
+ return result;
171
+ };
172
+
173
+ /// Implementation of asPredicate()
174
+ Envelope.prototype.asPredicate = function (this: Envelope): Envelope | undefined {
175
+ // Refer to subject in case the assertion is a node and therefore has
176
+ // its own assertions
177
+ const subj = this.subject();
178
+ const c = subj.case();
179
+ if (c.type === "assertion") {
180
+ return c.assertion.predicate();
181
+ }
182
+ return undefined;
183
+ };
184
+
185
+ /// Implementation of tryPredicate()
186
+ Envelope.prototype.tryPredicate = function (this: Envelope): Envelope {
187
+ const result = this.asPredicate();
188
+ if (result === undefined) {
189
+ throw EnvelopeError.notAssertion();
190
+ }
191
+ return result;
192
+ };
193
+
194
+ /// Implementation of asObject()
195
+ Envelope.prototype.asObject = function (this: Envelope): Envelope | undefined {
196
+ // Refer to subject in case the assertion is a node and therefore has
197
+ // its own assertions
198
+ const subj = this.subject();
199
+ const c = subj.case();
200
+ if (c.type === "assertion") {
201
+ return c.assertion.object();
202
+ }
203
+ return undefined;
204
+ };
205
+
206
+ /// Implementation of tryObject()
207
+ Envelope.prototype.tryObject = function (this: Envelope): Envelope {
208
+ const result = this.asObject();
209
+ if (result === undefined) {
210
+ throw EnvelopeError.notAssertion();
211
+ }
212
+ return result;
213
+ };
214
+
215
+ /// Implementation of isAssertion()
216
+ Envelope.prototype.isAssertion = function (this: Envelope): boolean {
217
+ return this.case().type === "assertion";
218
+ };
219
+
220
+ /// Implementation of isElided()
221
+ Envelope.prototype.isElided = function (this: Envelope): boolean {
222
+ return this.case().type === "elided";
223
+ };
224
+
225
+ /// Implementation of isLeaf()
226
+ Envelope.prototype.isLeaf = function (this: Envelope): boolean {
227
+ return this.case().type === "leaf";
228
+ };
229
+
230
+ /// Implementation of isNode()
231
+ Envelope.prototype.isNode = function (this: Envelope): boolean {
232
+ return this.case().type === "node";
233
+ };
234
+
235
+ /// Implementation of isWrapped()
236
+ Envelope.prototype.isWrapped = function (this: Envelope): boolean {
237
+ return this.case().type === "wrapped";
238
+ };
239
+
240
+ /// Implementation of isInternal()
241
+ Envelope.prototype.isInternal = function (this: Envelope): boolean {
242
+ const type = this.case().type;
243
+ return type === "node" || type === "wrapped" || type === "assertion";
244
+ };
245
+
246
+ /// Implementation of isObscured()
247
+ Envelope.prototype.isObscured = function (this: Envelope): boolean {
248
+ const type = this.case().type;
249
+ return type === "elided" || type === "encrypted" || type === "compressed";
250
+ };
251
+
252
+ /// Implementation of assertionsWithPredicate()
253
+ Envelope.prototype.assertionsWithPredicate = function (
254
+ this: Envelope,
255
+ predicate: EnvelopeEncodableValue,
256
+ ): Envelope[] {
257
+ const predicateEnv = Envelope.new(predicate);
258
+ const predicateDigest = predicateEnv.digest();
259
+
260
+ return this.assertions().filter((assertion) => {
261
+ const pred = assertion.subject().asPredicate();
262
+ return pred?.digest().equals(predicateDigest) === true;
263
+ });
264
+ };
265
+
266
+ /// Implementation of assertionWithPredicate()
267
+ Envelope.prototype.assertionWithPredicate = function (
268
+ this: Envelope,
269
+ predicate: EnvelopeEncodableValue,
270
+ ): Envelope {
271
+ const matches = this.assertionsWithPredicate(predicate);
272
+
273
+ if (matches.length === 0) {
274
+ throw EnvelopeError.nonexistentPredicate();
275
+ }
276
+ if (matches.length > 1) {
277
+ throw EnvelopeError.ambiguousPredicate();
278
+ }
279
+
280
+ return matches[0];
281
+ };
282
+
283
+ /// Implementation of optionalAssertionWithPredicate()
284
+ Envelope.prototype.optionalAssertionWithPredicate = function (
285
+ this: Envelope,
286
+ predicate: EnvelopeEncodableValue,
287
+ ): Envelope | undefined {
288
+ const matches = this.assertionsWithPredicate(predicate);
289
+
290
+ if (matches.length === 0) {
291
+ return undefined;
292
+ }
293
+ if (matches.length > 1) {
294
+ throw EnvelopeError.ambiguousPredicate();
295
+ }
296
+
297
+ return matches[0];
298
+ };
299
+
300
+ /// Implementation of objectForPredicate()
301
+ Envelope.prototype.objectForPredicate = function (
302
+ this: Envelope,
303
+ predicate: EnvelopeEncodableValue,
304
+ ): Envelope {
305
+ const assertion = this.assertionWithPredicate(predicate);
306
+ const obj = assertion.asObject();
307
+ if (obj === undefined) {
308
+ throw EnvelopeError.notAssertion();
309
+ }
310
+ return obj;
311
+ };
312
+
313
+ /// Implementation of optionalObjectForPredicate()
314
+ Envelope.prototype.optionalObjectForPredicate = function (
315
+ this: Envelope,
316
+ predicate: EnvelopeEncodableValue,
317
+ ): Envelope | undefined {
318
+ const matches = this.assertionsWithPredicate(predicate);
319
+
320
+ if (matches.length === 0) {
321
+ return undefined;
322
+ }
323
+ if (matches.length > 1) {
324
+ throw EnvelopeError.ambiguousPredicate();
325
+ }
326
+
327
+ const obj = matches[0].subject().asObject();
328
+ return obj;
329
+ };
330
+
331
+ /// Implementation of objectsForPredicate()
332
+ Envelope.prototype.objectsForPredicate = function (
333
+ this: Envelope,
334
+ predicate: EnvelopeEncodableValue,
335
+ ): Envelope[] {
336
+ return this.assertionsWithPredicate(predicate).map((assertion) => {
337
+ const obj = assertion.asObject();
338
+ if (obj === undefined) {
339
+ throw EnvelopeError.notAssertion();
340
+ }
341
+ return obj;
342
+ });
343
+ };
344
+
345
+ /// Implementation of elementsCount()
346
+ Envelope.prototype.elementsCount = function (this: Envelope): number {
347
+ let count = 1; // Count this envelope
348
+
349
+ const c = this.case();
350
+ switch (c.type) {
351
+ case "node":
352
+ count += c.subject.elementsCount();
353
+ for (const assertion of c.assertions) {
354
+ count += assertion.elementsCount();
355
+ }
356
+ break;
357
+ case "assertion":
358
+ count += c.assertion.predicate().elementsCount();
359
+ count += c.assertion.object().elementsCount();
360
+ break;
361
+ case "wrapped":
362
+ count += c.envelope.elementsCount();
363
+ break;
364
+ case "leaf":
365
+ case "elided":
366
+ case "knownValue":
367
+ case "encrypted":
368
+ case "compressed":
369
+ // These cases don't contribute additional elements
370
+ break;
371
+ }
372
+
373
+ return count;
374
+ };
@@ -0,0 +1,241 @@
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
+ declare module "./envelope" {
83
+ interface Envelope {
84
+ /// Walks the envelope structure, calling the visitor function for each
85
+ /// element.
86
+ ///
87
+ /// This function traverses the entire envelope hierarchy and calls the
88
+ /// visitor function on each element. The traversal can be performed in
89
+ /// two modes:
90
+ ///
91
+ /// - Structure-based traversal (`hideNodes = false`): Visits every element
92
+ /// including node containers
93
+ /// - Tree-based traversal (`hideNodes = true`): Skips node elements and
94
+ /// focuses on semantic content
95
+ ///
96
+ /// The visitor function can optionally return a context value that is
97
+ /// passed to child elements, enabling state to be accumulated or passed
98
+ /// down during traversal.
99
+ ///
100
+ /// @param hideNodes - If true, the visitor will not be called for node
101
+ /// containers
102
+ /// @param state - Initial state passed to the visitor
103
+ /// @param visit - The visitor function called for each element
104
+ walk<State>(hideNodes: boolean, state: State, visit: Visitor<State>): void;
105
+ }
106
+ }
107
+
108
+ /// Implementation of walk()
109
+ Envelope.prototype.walk = function <State>(
110
+ this: Envelope,
111
+ hideNodes: boolean,
112
+ state: State,
113
+ visit: Visitor<State>,
114
+ ): void {
115
+ if (hideNodes) {
116
+ walkTree(this, 0, EdgeType.None, state, visit);
117
+ } else {
118
+ walkStructure(this, 0, EdgeType.None, state, visit);
119
+ }
120
+ };
121
+
122
+ /// Recursive implementation of structure-based traversal.
123
+ ///
124
+ /// This internal function performs the actual recursive traversal of the
125
+ /// envelope structure, visiting every element and maintaining the
126
+ /// correct level and edge relationships.
127
+ function walkStructure<State>(
128
+ envelope: Envelope,
129
+ level: number,
130
+ incomingEdge: EdgeType,
131
+ state: State,
132
+ visit: Visitor<State>,
133
+ ): void {
134
+ // Visit this envelope
135
+ const [newState, stop] = visit(envelope, level, incomingEdge, state);
136
+ if (stop) {
137
+ return;
138
+ }
139
+
140
+ const nextLevel = level + 1;
141
+ const c = envelope.case();
142
+
143
+ switch (c.type) {
144
+ case "node":
145
+ // Visit subject
146
+ walkStructure(c.subject, nextLevel, EdgeType.Subject, newState, visit);
147
+ // Visit all assertions
148
+ for (const assertion of c.assertions) {
149
+ walkStructure(assertion, nextLevel, EdgeType.Assertion, newState, visit);
150
+ }
151
+ break;
152
+
153
+ case "wrapped":
154
+ // Visit wrapped envelope
155
+ walkStructure(c.envelope, nextLevel, EdgeType.Content, newState, visit);
156
+ break;
157
+
158
+ case "assertion":
159
+ // Visit predicate and object
160
+ walkStructure(c.assertion.predicate(), nextLevel, EdgeType.Predicate, newState, visit);
161
+ walkStructure(c.assertion.object(), nextLevel, EdgeType.Object, newState, visit);
162
+ break;
163
+
164
+ case "leaf":
165
+ case "elided":
166
+ case "knownValue":
167
+ case "encrypted":
168
+ case "compressed":
169
+ // Leaf nodes and other types have no children
170
+ break;
171
+ }
172
+ }
173
+
174
+ /// Recursive implementation of tree-based traversal.
175
+ ///
176
+ /// This internal function performs the actual recursive traversal of the
177
+ /// envelope's semantic tree, skipping node containers and focusing on
178
+ /// the semantic content elements. It maintains the correct level and
179
+ /// edge relationships while skipping structural elements.
180
+ function walkTree<State>(
181
+ envelope: Envelope,
182
+ level: number,
183
+ incomingEdge: EdgeType,
184
+ state: State,
185
+ visit: Visitor<State>,
186
+ ): State {
187
+ let currentState = state;
188
+ let subjectLevel = level;
189
+
190
+ // Skip visiting if this is a node
191
+ if (!envelope.isNode()) {
192
+ const [newState, stop] = visit(envelope, level, incomingEdge, currentState);
193
+ if (stop) {
194
+ return newState;
195
+ }
196
+ currentState = newState;
197
+ subjectLevel = level + 1;
198
+ }
199
+
200
+ const c = envelope.case();
201
+
202
+ switch (c.type) {
203
+ case "node": {
204
+ // Visit subject
205
+ const assertionState = walkTree(
206
+ c.subject,
207
+ subjectLevel,
208
+ EdgeType.Subject,
209
+ currentState,
210
+ visit,
211
+ );
212
+ // Visit all assertions
213
+ const assertionLevel = subjectLevel + 1;
214
+ for (const assertion of c.assertions) {
215
+ walkTree(assertion, assertionLevel, EdgeType.Assertion, assertionState, visit);
216
+ }
217
+ break;
218
+ }
219
+
220
+ case "wrapped":
221
+ // Visit wrapped envelope
222
+ walkTree(c.envelope, subjectLevel, EdgeType.Content, currentState, visit);
223
+ break;
224
+
225
+ case "assertion":
226
+ // Visit predicate and object
227
+ walkTree(c.assertion.predicate(), subjectLevel, EdgeType.Predicate, currentState, visit);
228
+ walkTree(c.assertion.object(), subjectLevel, EdgeType.Object, currentState, visit);
229
+ break;
230
+
231
+ case "leaf":
232
+ case "elided":
233
+ case "knownValue":
234
+ case "encrypted":
235
+ case "compressed":
236
+ // Leaf nodes and other types have no children
237
+ break;
238
+ }
239
+
240
+ return currentState;
241
+ }
@@ -0,0 +1,72 @@
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
+ declare module "./envelope" {
10
+ interface Envelope {
11
+ /// Returns a new envelope which wraps the current envelope.
12
+ ///
13
+ /// Wrapping an envelope allows you to treat an envelope (including its
14
+ /// assertions) as a single unit, making it possible to add assertions
15
+ /// about the envelope as a whole.
16
+ ///
17
+ /// @returns A new wrapped envelope
18
+ ///
19
+ /// @example
20
+ /// ```typescript
21
+ /// // Create an envelope with an assertion
22
+ /// const envelope = Envelope.new("Hello.").addAssertion("language", "English");
23
+ ///
24
+ /// // Wrap it to add an assertion about the envelope as a whole
25
+ /// const wrapped = envelope.wrap().addAssertion("authenticated", true);
26
+ /// ```
27
+ wrap(): Envelope;
28
+
29
+ /// Unwraps and returns the inner envelope.
30
+ ///
31
+ /// This extracts the envelope contained within a wrapped envelope.
32
+ ///
33
+ /// @returns The unwrapped envelope
34
+ /// @throws {EnvelopeError} If this is not a wrapped envelope
35
+ ///
36
+ /// @example
37
+ /// ```typescript
38
+ /// // Create an envelope and wrap it
39
+ /// const envelope = Envelope.new("Hello.");
40
+ /// const wrapped = envelope.wrap();
41
+ ///
42
+ /// // Unwrap to get the original envelope
43
+ /// const unwrapped = wrapped.tryUnwrap();
44
+ /// ```
45
+ tryUnwrap(): Envelope;
46
+
47
+ /// Alias for tryUnwrap() - unwraps and returns the inner envelope.
48
+ ///
49
+ /// @returns The unwrapped envelope
50
+ /// @throws {EnvelopeError} If this is not a wrapped envelope
51
+ unwrap(): Envelope;
52
+ }
53
+ }
54
+
55
+ /// Implementation of wrap()
56
+ Envelope.prototype.wrap = function (this: Envelope): Envelope {
57
+ return Envelope.newWrapped(this);
58
+ };
59
+
60
+ /// Implementation of tryUnwrap()
61
+ Envelope.prototype.tryUnwrap = function (this: Envelope): Envelope {
62
+ const c = this.subject().case();
63
+ if (c.type === "wrapped") {
64
+ return c.envelope;
65
+ }
66
+ throw EnvelopeError.notWrapped();
67
+ };
68
+
69
+ /// Implementation of unwrap() - alias for tryUnwrap()
70
+ Envelope.prototype.unwrap = function (this: Envelope): Envelope {
71
+ return this.tryUnwrap();
72
+ };