@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,398 @@
1
+ /**
2
+ * Signature Extension for Gordian Envelope
3
+ *
4
+ * Provides functionality for digitally signing Envelopes and verifying signatures,
5
+ * with optional metadata support.
6
+ *
7
+ * The signature extension allows:
8
+ * - Signing envelope subjects to validate their authenticity
9
+ * - Adding metadata to signatures (e.g., signer identity, date, purpose)
10
+ * - Verification of signatures, both with and without metadata
11
+ * - Support for multiple signatures on a single envelope
12
+ */
13
+
14
+ import { Envelope } from "../base/envelope";
15
+ import { EnvelopeError } from "../base/error";
16
+ import {
17
+ ecdsaSign,
18
+ ecdsaVerify,
19
+ ecdsaPublicKeyFromPrivateKey,
20
+ ECDSA_PRIVATE_KEY_SIZE,
21
+ ECDSA_PUBLIC_KEY_SIZE,
22
+ ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE,
23
+ } from "@bcts/crypto";
24
+ import { SecureRandomNumberGenerator, rngRandomData } from "@bcts/rand";
25
+
26
+ /**
27
+ * Known value for the 'signed' predicate.
28
+ * This is the standard predicate used for signature assertions.
29
+ */
30
+ export const SIGNED = "signed";
31
+
32
+ /**
33
+ * Known value for the 'verifiedBy' predicate.
34
+ * Used to indicate verification status.
35
+ */
36
+ export const VERIFIED_BY = "verifiedBy";
37
+
38
+ /**
39
+ * Known value for the 'note' predicate.
40
+ * Used for adding notes/comments to signatures.
41
+ */
42
+ export const NOTE = "note";
43
+
44
+ /**
45
+ * Represents a cryptographic signature.
46
+ */
47
+ export class Signature {
48
+ readonly #data: Uint8Array;
49
+
50
+ constructor(data: Uint8Array) {
51
+ this.#data = data;
52
+ }
53
+
54
+ /**
55
+ * Returns the raw signature bytes.
56
+ */
57
+ data(): Uint8Array {
58
+ return this.#data;
59
+ }
60
+
61
+ /**
62
+ * Returns the hex-encoded signature.
63
+ */
64
+ hex(): string {
65
+ return Array.from(this.#data)
66
+ .map((b) => b.toString(16).padStart(2, "0"))
67
+ .join("");
68
+ }
69
+
70
+ /**
71
+ * Creates a Signature from hex string.
72
+ */
73
+ static fromHex(hex: string): Signature {
74
+ const bytes = new Uint8Array(hex.length / 2);
75
+ for (let i = 0; i < hex.length; i += 2) {
76
+ bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
77
+ }
78
+ return new Signature(bytes);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Interface for types that can sign data.
84
+ */
85
+ export interface Signer {
86
+ /**
87
+ * Signs the provided data and returns a Signature.
88
+ */
89
+ sign(data: Uint8Array): Signature;
90
+ }
91
+
92
+ /**
93
+ * Interface for types that can verify signatures.
94
+ */
95
+ export interface Verifier {
96
+ /**
97
+ * Verifies a signature against the provided data.
98
+ * Returns true if the signature is valid.
99
+ */
100
+ verify(data: Uint8Array, signature: Signature): boolean;
101
+ }
102
+
103
+ /**
104
+ * ECDSA signing key using secp256k1 curve.
105
+ * Uses @bcts/crypto functions.
106
+ */
107
+ export class SigningPrivateKey implements Signer {
108
+ readonly #privateKey: Uint8Array;
109
+
110
+ constructor(privateKey: Uint8Array) {
111
+ if (privateKey.length !== ECDSA_PRIVATE_KEY_SIZE) {
112
+ throw new Error(`Private key must be ${ECDSA_PRIVATE_KEY_SIZE} bytes`);
113
+ }
114
+ this.#privateKey = privateKey;
115
+ }
116
+
117
+ /**
118
+ * Generates a new random private key.
119
+ */
120
+ static generate(): SigningPrivateKey {
121
+ const rng = new SecureRandomNumberGenerator();
122
+ const privateKey = rngRandomData(rng, ECDSA_PRIVATE_KEY_SIZE);
123
+ return new SigningPrivateKey(privateKey);
124
+ }
125
+
126
+ /**
127
+ * Creates a private key from hex string.
128
+ */
129
+ static fromHex(hex: string): SigningPrivateKey {
130
+ const bytes = new Uint8Array(hex.length / 2);
131
+ for (let i = 0; i < hex.length; i += 2) {
132
+ bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
133
+ }
134
+ return new SigningPrivateKey(bytes);
135
+ }
136
+
137
+ /**
138
+ * Returns the corresponding public key.
139
+ */
140
+ publicKey(): SigningPublicKey {
141
+ const publicKey = ecdsaPublicKeyFromPrivateKey(this.#privateKey);
142
+ return new SigningPublicKey(publicKey);
143
+ }
144
+
145
+ /**
146
+ * Signs data and returns a Signature.
147
+ */
148
+ sign(data: Uint8Array): Signature {
149
+ const signatureBytes = ecdsaSign(this.#privateKey, data);
150
+ return new Signature(signatureBytes);
151
+ }
152
+
153
+ /**
154
+ * Returns the raw private key bytes.
155
+ */
156
+ data(): Uint8Array {
157
+ return this.#privateKey;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * ECDSA public key for signature verification using secp256k1 curve.
163
+ * Uses @bcts/crypto functions.
164
+ */
165
+ export class SigningPublicKey implements Verifier {
166
+ readonly #publicKey: Uint8Array;
167
+
168
+ constructor(publicKey: Uint8Array) {
169
+ if (
170
+ publicKey.length !== ECDSA_PUBLIC_KEY_SIZE &&
171
+ publicKey.length !== ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE
172
+ ) {
173
+ throw new Error(
174
+ `Public key must be ${ECDSA_PUBLIC_KEY_SIZE} bytes (compressed) or ${ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE} bytes (uncompressed)`,
175
+ );
176
+ }
177
+ this.#publicKey = publicKey;
178
+ }
179
+
180
+ /**
181
+ * Creates a public key from hex string.
182
+ */
183
+ static fromHex(hex: string): SigningPublicKey {
184
+ const bytes = new Uint8Array(hex.length / 2);
185
+ for (let i = 0; i < hex.length; i += 2) {
186
+ bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
187
+ }
188
+ return new SigningPublicKey(bytes);
189
+ }
190
+
191
+ /**
192
+ * Verifies a signature against the provided data.
193
+ */
194
+ verify(data: Uint8Array, signature: Signature): boolean {
195
+ try {
196
+ return ecdsaVerify(this.#publicKey, signature.data(), data);
197
+ } catch {
198
+ return false;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Returns the raw public key bytes.
204
+ */
205
+ data(): Uint8Array {
206
+ return this.#publicKey;
207
+ }
208
+
209
+ /**
210
+ * Returns the hex-encoded public key.
211
+ */
212
+ hex(): string {
213
+ return Array.from(this.#publicKey)
214
+ .map((b) => b.toString(16).padStart(2, "0"))
215
+ .join("");
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Metadata that can be attached to a signature.
221
+ */
222
+ export class SignatureMetadata {
223
+ readonly #assertions: [string, unknown][] = [];
224
+
225
+ /**
226
+ * Adds an assertion to the metadata.
227
+ */
228
+ withAssertion(predicate: string, object: unknown): SignatureMetadata {
229
+ const metadata = new SignatureMetadata();
230
+ metadata.#assertions.push(...this.#assertions);
231
+ metadata.#assertions.push([predicate, object]);
232
+ return metadata;
233
+ }
234
+
235
+ /**
236
+ * Returns all assertions in the metadata.
237
+ */
238
+ assertions(): [string, unknown][] {
239
+ return this.#assertions;
240
+ }
241
+
242
+ /**
243
+ * Returns true if this metadata has any assertions.
244
+ */
245
+ hasAssertions(): boolean {
246
+ return this.#assertions.length > 0;
247
+ }
248
+ }
249
+
250
+ // Implementation
251
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
252
+ if (Envelope?.prototype) {
253
+ Envelope.prototype.addSignature = function (this: Envelope, signer: Signer): Envelope {
254
+ return this.addSignatureWithMetadata(signer, undefined);
255
+ };
256
+
257
+ Envelope.prototype.addSignatureWithMetadata = function (
258
+ this: Envelope,
259
+ signer: Signer,
260
+ metadata?: SignatureMetadata,
261
+ ): Envelope {
262
+ const digest = this.subject().digest();
263
+ const signature = signer.sign(digest.data());
264
+ let signatureEnvelope = Envelope.new(signature.data());
265
+
266
+ if (metadata?.hasAssertions() === true) {
267
+ // Add metadata assertions to the signature
268
+ for (const [predicate, object] of metadata.assertions()) {
269
+ signatureEnvelope = signatureEnvelope.addAssertion(
270
+ predicate,
271
+ object as string | number | boolean,
272
+ );
273
+ }
274
+
275
+ // Wrap the signature with metadata
276
+ signatureEnvelope = signatureEnvelope.wrap();
277
+
278
+ // Sign the wrapped envelope
279
+ const outerSignature = signer.sign(signatureEnvelope.digest().data());
280
+ signatureEnvelope = signatureEnvelope.addAssertion(SIGNED, outerSignature.data());
281
+ }
282
+
283
+ return this.addAssertion(SIGNED, signatureEnvelope);
284
+ };
285
+
286
+ Envelope.prototype.addSignatures = function (this: Envelope, signers: Signer[]): Envelope {
287
+ return signers.reduce((envelope, signer) => envelope.addSignature(signer), this);
288
+ };
289
+
290
+ Envelope.prototype.hasSignatureFrom = function (this: Envelope, verifier: Verifier): boolean {
291
+ const subjectDigest = this.subject().digest();
292
+ const signatures = this.signatures();
293
+
294
+ for (const sigEnvelope of signatures) {
295
+ const c = sigEnvelope.case();
296
+
297
+ if (c.type === "leaf") {
298
+ // Simple signature - verify directly
299
+ try {
300
+ const sigData = sigEnvelope.asByteString();
301
+ if (sigData !== undefined) {
302
+ const signature = new Signature(sigData);
303
+ if (verifier.verify(subjectDigest.data(), signature)) {
304
+ return true;
305
+ }
306
+ }
307
+ } catch {
308
+ continue;
309
+ }
310
+ } else if (c.type === "node") {
311
+ // Signature with metadata - it's a node with 'signed' assertion
312
+ // The structure is: { wrapped_signature [signed: outer_signature] }
313
+ // Check if this node has a 'signed' assertion
314
+ const outerSigs = sigEnvelope.assertions().filter((a) => {
315
+ const aC = a.case();
316
+ if (aC.type === "assertion") {
317
+ const pred = aC.assertion.predicate();
318
+ try {
319
+ return pred.asText() === SIGNED;
320
+ } catch {
321
+ return false;
322
+ }
323
+ }
324
+ return false;
325
+ });
326
+
327
+ for (const outerSig of outerSigs) {
328
+ const outerSigCase = outerSig.case();
329
+ if (outerSigCase.type === "assertion") {
330
+ const outerSigObj = outerSigCase.assertion.object();
331
+ try {
332
+ const outerSigData = outerSigObj.asByteString();
333
+ if (outerSigData !== undefined) {
334
+ const outerSignature = new Signature(outerSigData);
335
+
336
+ // The subject of this node should be a wrapped envelope
337
+ const nodeSubject = c.subject;
338
+ const nodeSubjectCase = nodeSubject.case();
339
+
340
+ // Verify outer signature against the wrapped envelope
341
+ if (
342
+ nodeSubjectCase.type === "wrapped" &&
343
+ verifier.verify(nodeSubject.digest().data(), outerSignature)
344
+ ) {
345
+ // Now verify inner signature
346
+ const wrapped = nodeSubjectCase.envelope;
347
+ const innerSig = wrapped.subject();
348
+ const innerSigData = innerSig.asByteString();
349
+ if (innerSigData !== undefined) {
350
+ const innerSignature = new Signature(innerSigData);
351
+ if (verifier.verify(subjectDigest.data(), innerSignature)) {
352
+ return true;
353
+ }
354
+ }
355
+ }
356
+ }
357
+ } catch {
358
+ continue;
359
+ }
360
+ }
361
+ }
362
+ }
363
+ }
364
+
365
+ return false;
366
+ };
367
+
368
+ Envelope.prototype.verifySignatureFrom = function (this: Envelope, verifier: Verifier): Envelope {
369
+ if (this.hasSignatureFrom(verifier)) {
370
+ return this;
371
+ }
372
+ throw EnvelopeError.general("No valid signature found from the given verifier");
373
+ };
374
+
375
+ Envelope.prototype.signatures = function (this: Envelope): Envelope[] {
376
+ const assertions = this.assertions();
377
+ return assertions
378
+ .filter((a) => {
379
+ const c = a.case();
380
+ if (c.type === "assertion") {
381
+ const pred = c.assertion.predicate();
382
+ try {
383
+ return pred.asText() === SIGNED;
384
+ } catch {
385
+ return false;
386
+ }
387
+ }
388
+ return false;
389
+ })
390
+ .map((a) => {
391
+ const c = a.case();
392
+ if (c.type === "assertion") {
393
+ return c.assertion.object();
394
+ }
395
+ throw EnvelopeError.general("Invalid signature assertion");
396
+ });
397
+ };
398
+ }
@@ -0,0 +1,92 @@
1
+ import { Envelope } from "../base/envelope";
2
+ import { type EnvelopeEncodableValue } from "../base/envelope-encodable";
3
+ import { EnvelopeError } from "../base/error";
4
+
5
+ /// Type system for Gordian Envelopes.
6
+ ///
7
+ /// This module provides functionality for adding, querying, and verifying types
8
+ /// within envelopes. In Gordian Envelope, types are implemented using the
9
+ /// special `'isA'` predicate (the string "isA"), which is semantically
10
+ /// equivalent to the RDF `rdf:type` concept.
11
+ ///
12
+ /// Type information enables:
13
+ /// - Semantic classification of envelopes
14
+ /// - Type verification before processing content
15
+ /// - Conversion between domain objects and envelopes
16
+ /// - Schema validation
17
+ ///
18
+ /// ## Type Representation
19
+ ///
20
+ /// Types are represented as assertions with the `'isA'` predicate and an object
21
+ /// that specifies the type. The type object is typically a string or an envelope.
22
+ ///
23
+ /// ## Usage Patterns
24
+ ///
25
+ /// The type system is commonly used in two ways:
26
+ ///
27
+ /// 1. **Type Tagging**: Adding type information to envelopes to indicate their
28
+ /// semantic meaning
29
+ ///
30
+ /// ```typescript
31
+ /// // Create an envelope representing a person
32
+ /// const person = Envelope.new("Alice")
33
+ /// .addType("Person")
34
+ /// .addAssertion("age", 30);
35
+ /// ```
36
+ ///
37
+ /// 2. **Type Checking**: Verifying that an envelope has the expected type
38
+ /// before processing
39
+ ///
40
+ /// ```typescript
41
+ /// function processPerson(envelope: Envelope): void {
42
+ /// // Verify this is a person before processing
43
+ /// envelope.checkType("Person");
44
+ ///
45
+ /// // Now we can safely extract person-specific information
46
+ /// const name = envelope.subject().extractString();
47
+ /// const age = envelope.objectForPredicate("age").extractNumber();
48
+ ///
49
+ /// console.log(`${name} is ${age} years old`);
50
+ /// }
51
+ /// ```
52
+
53
+ /// The standard predicate for type assertions
54
+ export const IS_A = "isA";
55
+
56
+ /// Implementation of addType()
57
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
58
+ if (Envelope?.prototype) {
59
+ Envelope.prototype.addType = function (this: Envelope, object: EnvelopeEncodableValue): Envelope {
60
+ return this.addAssertion(IS_A, object);
61
+ };
62
+
63
+ /// Implementation of types()
64
+ Envelope.prototype.types = function (this: Envelope): Envelope[] {
65
+ return this.objectsForPredicate(IS_A);
66
+ };
67
+
68
+ /// Implementation of getType()
69
+ Envelope.prototype.getType = function (this: Envelope): Envelope {
70
+ const t = this.types();
71
+ if (t.length === 0) {
72
+ throw EnvelopeError.invalidType();
73
+ }
74
+ if (t.length === 1) {
75
+ return t[0];
76
+ }
77
+ throw EnvelopeError.ambiguousType();
78
+ };
79
+
80
+ /// Implementation of hasType()
81
+ Envelope.prototype.hasType = function (this: Envelope, t: EnvelopeEncodableValue): boolean {
82
+ const e = Envelope.new(t);
83
+ return this.types().some((x) => x.digest().equals(e.digest()));
84
+ };
85
+
86
+ /// Implementation of checkType()
87
+ Envelope.prototype.checkType = function (this: Envelope, t: EnvelopeEncodableValue): void {
88
+ if (!this.hasType(t)) {
89
+ throw EnvelopeError.invalidType();
90
+ }
91
+ };
92
+ }
@@ -0,0 +1,116 @@
1
+ import { Envelope } from "../base/envelope";
2
+
3
+ // Type for CBOR values that can appear in diagnostic notation
4
+ type CborValue =
5
+ | string
6
+ | number
7
+ | boolean
8
+ | null
9
+ | Uint8Array
10
+ | CborValue[]
11
+ | Map<CborValue, CborValue>
12
+ | { tag: number; value: CborValue }
13
+ | { type: number; value: unknown };
14
+
15
+ /// Diagnostic notation formatting for Gordian Envelopes.
16
+ ///
17
+ /// This module provides methods for converting envelopes to CBOR diagnostic
18
+ /// notation, a human-readable text format defined in RFC 8949 §8.
19
+ ///
20
+ /// See [RFC-8949 §8](https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation)
21
+ /// for information on CBOR diagnostic notation.
22
+
23
+ // Note: Method declarations are in the base Envelope class.
24
+ // This module provides the prototype implementations.
25
+
26
+ /// Converts a CBOR value to diagnostic notation
27
+ function cborToDiagnostic(cbor: CborValue, indent = 0): string {
28
+ // Handle tagged values (CBOR tags)
29
+ if (typeof cbor === "object" && cbor !== null && "tag" in cbor && "value" in cbor) {
30
+ const tagged = cbor as { tag: number; value: CborValue };
31
+ return `${tagged.tag}(${cborToDiagnostic(tagged.value, indent)})`;
32
+ }
33
+
34
+ // Handle arrays
35
+ if (Array.isArray(cbor)) {
36
+ if (cbor.length === 0) {
37
+ return "[]";
38
+ }
39
+ const items = cbor.map((item) => cborToDiagnostic(item, indent + 2));
40
+ return `[${items.join(", ")}]`;
41
+ }
42
+
43
+ // Handle Maps
44
+ if (cbor instanceof Map) {
45
+ if (cbor.size === 0) {
46
+ return "{}";
47
+ }
48
+ const entries: string[] = [];
49
+ for (const [key, value] of cbor) {
50
+ const keyStr = cborToDiagnostic(key, indent + 2);
51
+ const valueStr = cborToDiagnostic(value, indent + 2);
52
+ entries.push(`${keyStr}: ${valueStr}`);
53
+ }
54
+ return `{${entries.join(", ")}}`;
55
+ }
56
+
57
+ // Handle Uint8Array (byte strings)
58
+ if (cbor instanceof Uint8Array) {
59
+ const hex = Array.from(cbor)
60
+ .map((b) => b.toString(16).padStart(2, "0"))
61
+ .join("");
62
+ return `h'${hex}'`;
63
+ }
64
+
65
+ // Handle strings
66
+ if (typeof cbor === "string") {
67
+ return JSON.stringify(cbor);
68
+ }
69
+
70
+ // Handle CBOR objects with type information
71
+ if (typeof cbor === "object" && cbor !== null && "type" in cbor) {
72
+ const typed = cbor as { type: number; value: unknown };
73
+ switch (typed.type) {
74
+ case 0: // Unsigned
75
+ return String(typed.value);
76
+ case 1: // Negative
77
+ return String(-1 - Number(typed.value));
78
+ case 7: {
79
+ // Simple
80
+ const simpleValue = typed.value;
81
+ if (simpleValue !== null && typeof simpleValue === "object" && "type" in simpleValue) {
82
+ const floatValue = simpleValue as { type: string; value: unknown };
83
+ if (floatValue.type === "Float") {
84
+ return String(floatValue.value);
85
+ }
86
+ }
87
+ if (simpleValue === 20) return "false";
88
+ if (simpleValue === 21) return "true";
89
+ if (simpleValue === 22) return "null";
90
+ if (simpleValue === 23) return "undefined";
91
+ return `simple(${String(simpleValue)})`;
92
+ }
93
+ }
94
+ }
95
+
96
+ // Fallback for primitives
97
+ if (typeof cbor === "boolean") return String(cbor);
98
+ if (typeof cbor === "number") return String(cbor);
99
+ if (typeof cbor === "bigint") return String(cbor);
100
+ if (cbor === null) return "null";
101
+ if (cbor === undefined) return "undefined";
102
+
103
+ // Unknown type - try JSON stringify
104
+ try {
105
+ return JSON.stringify(cbor);
106
+ } catch {
107
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
108
+ return String(cbor);
109
+ }
110
+ }
111
+
112
+ /// Implementation of diagnostic()
113
+ Envelope.prototype.diagnostic = function (this: Envelope): string {
114
+ const cbor = this.taggedCbor();
115
+ return cborToDiagnostic(cbor);
116
+ };
@@ -0,0 +1,25 @@
1
+ import { Envelope } from "../base/envelope";
2
+ import { cborData } from "@bcts/dcbor";
3
+
4
+ /// Hex formatting for Gordian Envelopes.
5
+ ///
6
+ /// This module provides methods for converting envelopes to hexadecimal
7
+ /// representations of their CBOR encoding, useful for debugging and
8
+ /// low-level inspection.
9
+
10
+ // Note: Method declarations are in the base Envelope class.
11
+ // This module provides the prototype implementations.
12
+
13
+ /// Implementation of hex()
14
+ Envelope.prototype.hex = function (this: Envelope): string {
15
+ const bytes = this.cborBytes();
16
+ return Array.from(bytes)
17
+ .map((b) => b.toString(16).padStart(2, "0"))
18
+ .join("");
19
+ };
20
+
21
+ /// Implementation of cborBytes()
22
+ Envelope.prototype.cborBytes = function (this: Envelope): Uint8Array {
23
+ const cbor = this.taggedCbor();
24
+ return cborData(cbor);
25
+ };
@@ -0,0 +1,13 @@
1
+ /// Format module exports for Gordian Envelope.
2
+ ///
3
+ /// This module provides various formatting options for displaying and
4
+ /// serializing envelopes, including hex, diagnostic, notation, tree,
5
+ /// and mermaid diagram formats.
6
+
7
+ // Export types
8
+ export type { TreeFormatOptions } from "./tree";
9
+
10
+ // Import side-effect modules to register prototype extensions
11
+ import "./hex";
12
+ import "./diagnostic";
13
+ import "./tree";