@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.
- 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 +978 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +978 -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 +85 -0
- package/src/base/assertion.ts +179 -0
- package/src/base/assertions.ts +304 -0
- package/src/base/cbor.ts +122 -0
- package/src/base/digest.ts +204 -0
- package/src/base/elide.ts +526 -0
- package/src/base/envelope-decodable.ts +229 -0
- package/src/base/envelope-encodable.ts +71 -0
- package/src/base/envelope.ts +790 -0
- package/src/base/error.ts +421 -0
- package/src/base/index.ts +56 -0
- package/src/base/leaf.ts +226 -0
- package/src/base/queries.ts +374 -0
- package/src/base/walk.ts +241 -0
- package/src/base/wrap.ts +72 -0
- package/src/extension/attachment.ts +369 -0
- package/src/extension/compress.ts +293 -0
- package/src/extension/encrypt.ts +379 -0
- package/src/extension/expression.ts +404 -0
- package/src/extension/index.ts +72 -0
- package/src/extension/proof.ts +276 -0
- package/src/extension/recipient.ts +557 -0
- package/src/extension/salt.ts +223 -0
- package/src/extension/signature.ts +463 -0
- package/src/extension/types.ts +222 -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
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import { type Digest, type DigestProvider } from "./digest";
|
|
2
|
+
import { Envelope } from "./envelope";
|
|
3
|
+
import { Assertion } from "./assertion";
|
|
4
|
+
import { EnvelopeError } from "./error";
|
|
5
|
+
|
|
6
|
+
/// Types of obscuration that can be applied to envelope elements.
|
|
7
|
+
///
|
|
8
|
+
/// This enum identifies the different ways an envelope element can be obscured.
|
|
9
|
+
export enum ObscureType {
|
|
10
|
+
/// The element has been elided, showing only its digest.
|
|
11
|
+
Elided = "elided",
|
|
12
|
+
|
|
13
|
+
/// The element has been encrypted using symmetric encryption.
|
|
14
|
+
/// TODO: Implement when encrypt feature is added
|
|
15
|
+
Encrypted = "encrypted",
|
|
16
|
+
|
|
17
|
+
/// The element has been compressed to reduce its size.
|
|
18
|
+
/// TODO: Implement when compress feature is added
|
|
19
|
+
Compressed = "compressed",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Actions that can be performed on parts of an envelope to obscure them.
|
|
23
|
+
///
|
|
24
|
+
/// Gordian Envelope supports several ways to obscure parts of an envelope while
|
|
25
|
+
/// maintaining its semantic integrity and digest tree.
|
|
26
|
+
export type ObscureAction =
|
|
27
|
+
| { type: "elide" }
|
|
28
|
+
| { type: "encrypt"; key: unknown } // TODO: SymmetricKey type
|
|
29
|
+
| { type: "compress" };
|
|
30
|
+
|
|
31
|
+
/// Helper to create elide action
|
|
32
|
+
export function elideAction(): ObscureAction {
|
|
33
|
+
return { type: "elide" };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Support for eliding elements from envelopes.
|
|
37
|
+
declare module "./envelope" {
|
|
38
|
+
interface Envelope {
|
|
39
|
+
/// Returns the elided variant of this envelope.
|
|
40
|
+
///
|
|
41
|
+
/// Elision replaces an envelope with just its digest, hiding its content
|
|
42
|
+
/// while maintaining the integrity of the envelope's digest tree.
|
|
43
|
+
///
|
|
44
|
+
/// @returns The elided envelope
|
|
45
|
+
elide(): Envelope;
|
|
46
|
+
|
|
47
|
+
/// Returns a version of this envelope with elements in the target set
|
|
48
|
+
/// obscured using the specified action.
|
|
49
|
+
///
|
|
50
|
+
/// @param target - The set of digests that identify elements to be obscured
|
|
51
|
+
/// @param action - The action to perform on the targeted elements
|
|
52
|
+
/// @returns The modified envelope
|
|
53
|
+
elideRemovingSetWithAction(target: Set<Digest>, action: ObscureAction): Envelope;
|
|
54
|
+
|
|
55
|
+
/// Returns a version of this envelope with elements in the target set
|
|
56
|
+
/// elided.
|
|
57
|
+
///
|
|
58
|
+
/// @param target - The set of digests that identify elements to be elided
|
|
59
|
+
/// @returns The modified envelope
|
|
60
|
+
elideRemovingSet(target: Set<Digest>): Envelope;
|
|
61
|
+
|
|
62
|
+
/// Returns a version of this envelope with elements in the target array
|
|
63
|
+
/// obscured using the specified action.
|
|
64
|
+
///
|
|
65
|
+
/// @param target - An array of DigestProviders
|
|
66
|
+
/// @param action - The action to perform
|
|
67
|
+
/// @returns The modified envelope
|
|
68
|
+
elideRemovingArrayWithAction(target: DigestProvider[], action: ObscureAction): Envelope;
|
|
69
|
+
|
|
70
|
+
/// Returns a version of this envelope with elements in the target array
|
|
71
|
+
/// elided.
|
|
72
|
+
///
|
|
73
|
+
/// @param target - An array of DigestProviders
|
|
74
|
+
/// @returns The modified envelope
|
|
75
|
+
elideRemovingArray(target: DigestProvider[]): Envelope;
|
|
76
|
+
|
|
77
|
+
/// Returns a version of this envelope with the target element obscured.
|
|
78
|
+
///
|
|
79
|
+
/// @param target - A DigestProvider
|
|
80
|
+
/// @param action - The action to perform
|
|
81
|
+
/// @returns The modified envelope
|
|
82
|
+
elideRemovingTargetWithAction(target: DigestProvider, action: ObscureAction): Envelope;
|
|
83
|
+
|
|
84
|
+
/// Returns a version of this envelope with the target element elided.
|
|
85
|
+
///
|
|
86
|
+
/// @param target - A DigestProvider
|
|
87
|
+
/// @returns The modified envelope
|
|
88
|
+
elideRemovingTarget(target: DigestProvider): Envelope;
|
|
89
|
+
|
|
90
|
+
/// Returns a version of this envelope with only elements in the target set
|
|
91
|
+
/// revealed, and all other elements obscured.
|
|
92
|
+
///
|
|
93
|
+
/// @param target - The set of digests that identify elements to be revealed
|
|
94
|
+
/// @param action - The action to perform on other elements
|
|
95
|
+
/// @returns The modified envelope
|
|
96
|
+
elideRevealingSetWithAction(target: Set<Digest>, action: ObscureAction): Envelope;
|
|
97
|
+
|
|
98
|
+
/// Returns a version of this envelope with only elements in the target set
|
|
99
|
+
/// revealed.
|
|
100
|
+
///
|
|
101
|
+
/// @param target - The set of digests that identify elements to be revealed
|
|
102
|
+
/// @returns The modified envelope
|
|
103
|
+
elideRevealingSet(target: Set<Digest>): Envelope;
|
|
104
|
+
|
|
105
|
+
/// Returns a version of this envelope with elements not in the target array
|
|
106
|
+
/// obscured.
|
|
107
|
+
///
|
|
108
|
+
/// @param target - An array of DigestProviders
|
|
109
|
+
/// @param action - The action to perform
|
|
110
|
+
/// @returns The modified envelope
|
|
111
|
+
elideRevealingArrayWithAction(target: DigestProvider[], action: ObscureAction): Envelope;
|
|
112
|
+
|
|
113
|
+
/// Returns a version of this envelope with elements not in the target array
|
|
114
|
+
/// elided.
|
|
115
|
+
///
|
|
116
|
+
/// @param target - An array of DigestProviders
|
|
117
|
+
/// @returns The modified envelope
|
|
118
|
+
elideRevealingArray(target: DigestProvider[]): Envelope;
|
|
119
|
+
|
|
120
|
+
/// Returns a version of this envelope with all elements except the target
|
|
121
|
+
/// element obscured.
|
|
122
|
+
///
|
|
123
|
+
/// @param target - A DigestProvider
|
|
124
|
+
/// @param action - The action to perform
|
|
125
|
+
/// @returns The modified envelope
|
|
126
|
+
elideRevealingTargetWithAction(target: DigestProvider, action: ObscureAction): Envelope;
|
|
127
|
+
|
|
128
|
+
/// Returns a version of this envelope with all elements except the target
|
|
129
|
+
/// element elided.
|
|
130
|
+
///
|
|
131
|
+
/// @param target - A DigestProvider
|
|
132
|
+
/// @returns The modified envelope
|
|
133
|
+
elideRevealingTarget(target: DigestProvider): Envelope;
|
|
134
|
+
|
|
135
|
+
/// Returns the unelided variant of this envelope by revealing the original
|
|
136
|
+
/// content.
|
|
137
|
+
///
|
|
138
|
+
/// @param envelope - The original unelided envelope
|
|
139
|
+
/// @returns The revealed envelope
|
|
140
|
+
/// @throws {EnvelopeError} If digests don't match
|
|
141
|
+
unelide(envelope: Envelope): Envelope;
|
|
142
|
+
|
|
143
|
+
/// Returns the set of digests of nodes matching the specified criteria.
|
|
144
|
+
///
|
|
145
|
+
/// @param targetDigests - Optional set of digests to filter by
|
|
146
|
+
/// @param obscureTypes - Array of ObscureType values to match against
|
|
147
|
+
/// @returns A Set of matching digests
|
|
148
|
+
nodesMatching(targetDigests: Set<Digest> | undefined, obscureTypes: ObscureType[]): Set<Digest>;
|
|
149
|
+
|
|
150
|
+
/// Returns a new envelope with elided nodes restored from the provided set.
|
|
151
|
+
///
|
|
152
|
+
/// @param envelopes - An array of envelopes that may match elided nodes
|
|
153
|
+
/// @returns The envelope with restored nodes
|
|
154
|
+
walkUnelide(envelopes: Envelope[]): Envelope;
|
|
155
|
+
|
|
156
|
+
/// Returns a new envelope with nodes matching target digests replaced.
|
|
157
|
+
///
|
|
158
|
+
/// @param target - Set of digests identifying nodes to replace
|
|
159
|
+
/// @param replacement - The envelope to use for replacement
|
|
160
|
+
/// @returns The modified envelope
|
|
161
|
+
/// @throws {EnvelopeError} If replacement is invalid
|
|
162
|
+
walkReplace(target: Set<Digest>, replacement: Envelope): Envelope;
|
|
163
|
+
|
|
164
|
+
/// Checks if two envelopes are identical (same structure and content).
|
|
165
|
+
///
|
|
166
|
+
/// @param other - The other envelope to compare with
|
|
167
|
+
/// @returns `true` if identical
|
|
168
|
+
isIdenticalTo(other: Envelope): boolean;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// Implementation of elide()
|
|
173
|
+
Envelope.prototype.elide = function (this: Envelope): Envelope {
|
|
174
|
+
const c = this.case();
|
|
175
|
+
if (c.type === "elided") {
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
return Envelope.newElided(this.digest());
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/// Core elision logic
|
|
182
|
+
function elideSetWithAction(
|
|
183
|
+
envelope: Envelope,
|
|
184
|
+
target: Set<Digest>,
|
|
185
|
+
isRevealing: boolean,
|
|
186
|
+
action: ObscureAction,
|
|
187
|
+
): Envelope {
|
|
188
|
+
const selfDigest = envelope.digest();
|
|
189
|
+
const targetContainsSelf = Array.from(target).some((d) => d.equals(selfDigest));
|
|
190
|
+
|
|
191
|
+
// Target Matches isRevealing elide
|
|
192
|
+
// false false false
|
|
193
|
+
// false true true
|
|
194
|
+
// true false true
|
|
195
|
+
// true true false
|
|
196
|
+
|
|
197
|
+
if (targetContainsSelf !== isRevealing) {
|
|
198
|
+
// Should obscure this envelope
|
|
199
|
+
if (action.type === "elide") {
|
|
200
|
+
return envelope.elide();
|
|
201
|
+
} else if (action.type === "encrypt") {
|
|
202
|
+
// TODO: Implement encryption
|
|
203
|
+
throw new Error("Encryption not yet implemented");
|
|
204
|
+
} else if (action.type === "compress") {
|
|
205
|
+
// TODO: Implement compression
|
|
206
|
+
throw new Error("Compression not yet implemented");
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const c = envelope.case();
|
|
211
|
+
|
|
212
|
+
// Recursively process structure
|
|
213
|
+
if (c.type === "assertion") {
|
|
214
|
+
const predicate = elideSetWithAction(c.assertion.predicate(), target, isRevealing, action);
|
|
215
|
+
const object = elideSetWithAction(c.assertion.object(), target, isRevealing, action);
|
|
216
|
+
const elidedAssertion = new Assertion(predicate, object);
|
|
217
|
+
return Envelope.newWithAssertion(elidedAssertion);
|
|
218
|
+
} else if (c.type === "node") {
|
|
219
|
+
const elidedSubject = elideSetWithAction(c.subject, target, isRevealing, action);
|
|
220
|
+
const elidedAssertions = c.assertions.map((a) =>
|
|
221
|
+
elideSetWithAction(a, target, isRevealing, action),
|
|
222
|
+
);
|
|
223
|
+
return Envelope.newWithUncheckedAssertions(elidedSubject, elidedAssertions);
|
|
224
|
+
} else if (c.type === "wrapped") {
|
|
225
|
+
const elidedEnvelope = elideSetWithAction(c.envelope, target, isRevealing, action);
|
|
226
|
+
return Envelope.newWrapped(elidedEnvelope);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return envelope;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/// Implementation of elideRemovingSetWithAction
|
|
233
|
+
Envelope.prototype.elideRemovingSetWithAction = function (
|
|
234
|
+
this: Envelope,
|
|
235
|
+
target: Set<Digest>,
|
|
236
|
+
action: ObscureAction,
|
|
237
|
+
): Envelope {
|
|
238
|
+
return elideSetWithAction(this, target, false, action);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/// Implementation of elideRemovingSet
|
|
242
|
+
Envelope.prototype.elideRemovingSet = function (this: Envelope, target: Set<Digest>): Envelope {
|
|
243
|
+
return elideSetWithAction(this, target, false, elideAction());
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/// Implementation of elideRemovingArrayWithAction
|
|
247
|
+
Envelope.prototype.elideRemovingArrayWithAction = function (
|
|
248
|
+
this: Envelope,
|
|
249
|
+
target: DigestProvider[],
|
|
250
|
+
action: ObscureAction,
|
|
251
|
+
): Envelope {
|
|
252
|
+
const targetSet = new Set(target.map((p) => p.digest()));
|
|
253
|
+
return elideSetWithAction(this, targetSet, false, action);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/// Implementation of elideRemovingArray
|
|
257
|
+
Envelope.prototype.elideRemovingArray = function (
|
|
258
|
+
this: Envelope,
|
|
259
|
+
target: DigestProvider[],
|
|
260
|
+
): Envelope {
|
|
261
|
+
const targetSet = new Set(target.map((p) => p.digest()));
|
|
262
|
+
return elideSetWithAction(this, targetSet, false, elideAction());
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/// Implementation of elideRemovingTargetWithAction
|
|
266
|
+
Envelope.prototype.elideRemovingTargetWithAction = function (
|
|
267
|
+
this: Envelope,
|
|
268
|
+
target: DigestProvider,
|
|
269
|
+
action: ObscureAction,
|
|
270
|
+
): Envelope {
|
|
271
|
+
return this.elideRemovingArrayWithAction([target], action);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/// Implementation of elideRemovingTarget
|
|
275
|
+
Envelope.prototype.elideRemovingTarget = function (
|
|
276
|
+
this: Envelope,
|
|
277
|
+
target: DigestProvider,
|
|
278
|
+
): Envelope {
|
|
279
|
+
return this.elideRemovingArray([target]);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/// Implementation of elideRevealingSetWithAction
|
|
283
|
+
Envelope.prototype.elideRevealingSetWithAction = function (
|
|
284
|
+
this: Envelope,
|
|
285
|
+
target: Set<Digest>,
|
|
286
|
+
action: ObscureAction,
|
|
287
|
+
): Envelope {
|
|
288
|
+
return elideSetWithAction(this, target, true, action);
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
/// Implementation of elideRevealingSet
|
|
292
|
+
Envelope.prototype.elideRevealingSet = function (this: Envelope, target: Set<Digest>): Envelope {
|
|
293
|
+
return elideSetWithAction(this, target, true, elideAction());
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/// Implementation of elideRevealingArrayWithAction
|
|
297
|
+
Envelope.prototype.elideRevealingArrayWithAction = function (
|
|
298
|
+
this: Envelope,
|
|
299
|
+
target: DigestProvider[],
|
|
300
|
+
action: ObscureAction,
|
|
301
|
+
): Envelope {
|
|
302
|
+
const targetSet = new Set(target.map((p) => p.digest()));
|
|
303
|
+
return elideSetWithAction(this, targetSet, true, action);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/// Implementation of elideRevealingArray
|
|
307
|
+
Envelope.prototype.elideRevealingArray = function (
|
|
308
|
+
this: Envelope,
|
|
309
|
+
target: DigestProvider[],
|
|
310
|
+
): Envelope {
|
|
311
|
+
const targetSet = new Set(target.map((p) => p.digest()));
|
|
312
|
+
return elideSetWithAction(this, targetSet, true, elideAction());
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
/// Implementation of elideRevealingTargetWithAction
|
|
316
|
+
Envelope.prototype.elideRevealingTargetWithAction = function (
|
|
317
|
+
this: Envelope,
|
|
318
|
+
target: DigestProvider,
|
|
319
|
+
action: ObscureAction,
|
|
320
|
+
): Envelope {
|
|
321
|
+
return this.elideRevealingArrayWithAction([target], action);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/// Implementation of elideRevealingTarget
|
|
325
|
+
Envelope.prototype.elideRevealingTarget = function (
|
|
326
|
+
this: Envelope,
|
|
327
|
+
target: DigestProvider,
|
|
328
|
+
): Envelope {
|
|
329
|
+
return this.elideRevealingArray([target]);
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/// Implementation of unelide
|
|
333
|
+
Envelope.prototype.unelide = function (this: Envelope, envelope: Envelope): Envelope {
|
|
334
|
+
if (this.digest().equals(envelope.digest())) {
|
|
335
|
+
return envelope;
|
|
336
|
+
}
|
|
337
|
+
throw EnvelopeError.invalidDigest();
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
/// Implementation of nodesMatching
|
|
341
|
+
Envelope.prototype.nodesMatching = function (
|
|
342
|
+
this: Envelope,
|
|
343
|
+
targetDigests: Set<Digest> | undefined,
|
|
344
|
+
obscureTypes: ObscureType[],
|
|
345
|
+
): Set<Digest> {
|
|
346
|
+
const result = new Set<Digest>();
|
|
347
|
+
|
|
348
|
+
const visitor = (envelope: Envelope): void => {
|
|
349
|
+
// Check if this node matches the target digests
|
|
350
|
+
const digestMatches =
|
|
351
|
+
targetDigests === undefined ||
|
|
352
|
+
Array.from(targetDigests).some((d) => d.equals(envelope.digest()));
|
|
353
|
+
|
|
354
|
+
if (!digestMatches) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// If no obscure types specified, include all nodes
|
|
359
|
+
if (obscureTypes.length === 0) {
|
|
360
|
+
result.add(envelope.digest());
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Check if this node matches any of the specified obscure types
|
|
365
|
+
const c = envelope.case();
|
|
366
|
+
const typeMatches = obscureTypes.some((obscureType) => {
|
|
367
|
+
if (obscureType === ObscureType.Elided && c.type === "elided") {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
if (obscureType === ObscureType.Encrypted && c.type === "encrypted") {
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
if (obscureType === ObscureType.Compressed && c.type === "compressed") {
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
return false;
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
if (typeMatches) {
|
|
380
|
+
result.add(envelope.digest());
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
// Walk the envelope tree
|
|
385
|
+
walkEnvelope(this, visitor);
|
|
386
|
+
|
|
387
|
+
return result;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
/// Helper to walk envelope tree
|
|
391
|
+
function walkEnvelope(envelope: Envelope, visitor: (e: Envelope) => void): void {
|
|
392
|
+
visitor(envelope);
|
|
393
|
+
|
|
394
|
+
const c = envelope.case();
|
|
395
|
+
if (c.type === "node") {
|
|
396
|
+
walkEnvelope(c.subject, visitor);
|
|
397
|
+
for (const assertion of c.assertions) {
|
|
398
|
+
walkEnvelope(assertion, visitor);
|
|
399
|
+
}
|
|
400
|
+
} else if (c.type === "assertion") {
|
|
401
|
+
walkEnvelope(c.assertion.predicate(), visitor);
|
|
402
|
+
walkEnvelope(c.assertion.object(), visitor);
|
|
403
|
+
} else if (c.type === "wrapped") {
|
|
404
|
+
walkEnvelope(c.envelope, visitor);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/// Implementation of walkUnelide
|
|
409
|
+
Envelope.prototype.walkUnelide = function (this: Envelope, envelopes: Envelope[]): Envelope {
|
|
410
|
+
// Build a lookup map of digest -> envelope
|
|
411
|
+
const envelopeMap = new Map<string, Envelope>();
|
|
412
|
+
for (const env of envelopes) {
|
|
413
|
+
envelopeMap.set(env.digest().hex(), env);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return walkUnelideWithMap(this, envelopeMap);
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
/// Helper for walkUnelide with map
|
|
420
|
+
function walkUnelideWithMap(envelope: Envelope, envelopeMap: Map<string, Envelope>): Envelope {
|
|
421
|
+
const c = envelope.case();
|
|
422
|
+
|
|
423
|
+
if (c.type === "elided") {
|
|
424
|
+
// Try to find a matching envelope to restore
|
|
425
|
+
const replacement = envelopeMap.get(envelope.digest().hex());
|
|
426
|
+
return replacement ?? envelope;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (c.type === "node") {
|
|
430
|
+
const newSubject = walkUnelideWithMap(c.subject, envelopeMap);
|
|
431
|
+
const newAssertions = c.assertions.map((a) => walkUnelideWithMap(a, envelopeMap));
|
|
432
|
+
|
|
433
|
+
if (
|
|
434
|
+
newSubject.isIdenticalTo(c.subject) &&
|
|
435
|
+
newAssertions.every((a, i) => a.isIdenticalTo(c.assertions[i]))
|
|
436
|
+
) {
|
|
437
|
+
return envelope;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return Envelope.newWithUncheckedAssertions(newSubject, newAssertions);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (c.type === "wrapped") {
|
|
444
|
+
const newEnvelope = walkUnelideWithMap(c.envelope, envelopeMap);
|
|
445
|
+
if (newEnvelope.isIdenticalTo(c.envelope)) {
|
|
446
|
+
return envelope;
|
|
447
|
+
}
|
|
448
|
+
return Envelope.newWrapped(newEnvelope);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (c.type === "assertion") {
|
|
452
|
+
const newPredicate = walkUnelideWithMap(c.assertion.predicate(), envelopeMap);
|
|
453
|
+
const newObject = walkUnelideWithMap(c.assertion.object(), envelopeMap);
|
|
454
|
+
|
|
455
|
+
if (
|
|
456
|
+
newPredicate.isIdenticalTo(c.assertion.predicate()) &&
|
|
457
|
+
newObject.isIdenticalTo(c.assertion.object())
|
|
458
|
+
) {
|
|
459
|
+
return envelope;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return Envelope.newAssertion(newPredicate, newObject);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return envelope;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/// Implementation of walkReplace
|
|
469
|
+
Envelope.prototype.walkReplace = function (
|
|
470
|
+
this: Envelope,
|
|
471
|
+
target: Set<Digest>,
|
|
472
|
+
replacement: Envelope,
|
|
473
|
+
): Envelope {
|
|
474
|
+
// Check if this node matches the target
|
|
475
|
+
if (Array.from(target).some((d) => d.equals(this.digest()))) {
|
|
476
|
+
return replacement;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const c = this.case();
|
|
480
|
+
|
|
481
|
+
if (c.type === "node") {
|
|
482
|
+
const newSubject = c.subject.walkReplace(target, replacement);
|
|
483
|
+
const newAssertions = c.assertions.map((a) => a.walkReplace(target, replacement));
|
|
484
|
+
|
|
485
|
+
if (
|
|
486
|
+
newSubject.isIdenticalTo(c.subject) &&
|
|
487
|
+
newAssertions.every((a, i) => a.isIdenticalTo(c.assertions[i]))
|
|
488
|
+
) {
|
|
489
|
+
return this;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Validate that all assertions are either assertions or obscured
|
|
493
|
+
return Envelope.newWithAssertions(newSubject, newAssertions);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (c.type === "wrapped") {
|
|
497
|
+
const newEnvelope = c.envelope.walkReplace(target, replacement);
|
|
498
|
+
if (newEnvelope.isIdenticalTo(c.envelope)) {
|
|
499
|
+
return this;
|
|
500
|
+
}
|
|
501
|
+
return Envelope.newWrapped(newEnvelope);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (c.type === "assertion") {
|
|
505
|
+
const newPredicate = c.assertion.predicate().walkReplace(target, replacement);
|
|
506
|
+
const newObject = c.assertion.object().walkReplace(target, replacement);
|
|
507
|
+
|
|
508
|
+
if (
|
|
509
|
+
newPredicate.isIdenticalTo(c.assertion.predicate()) &&
|
|
510
|
+
newObject.isIdenticalTo(c.assertion.object())
|
|
511
|
+
) {
|
|
512
|
+
return this;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return Envelope.newAssertion(newPredicate, newObject);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return this;
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
/// Implementation of isIdenticalTo
|
|
522
|
+
Envelope.prototype.isIdenticalTo = function (this: Envelope, other: Envelope): boolean {
|
|
523
|
+
// Two envelopes are identical if they have the same digest
|
|
524
|
+
// and the same case type (to handle wrapped vs unwrapped with same content)
|
|
525
|
+
return this.digest().equals(other.digest()) && this.case().type === other.case().type;
|
|
526
|
+
};
|