@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,204 @@
1
+ import { sha256 } from "@bcts/crypto";
2
+
3
+ /// A cryptographic digest used to uniquely identify digital objects.
4
+ ///
5
+ /// Digests in Gordian Envelope are always SHA-256 hashes (32 bytes).
6
+ /// This is a fundamental building block for the Merkle-like digest tree
7
+ /// that enables privacy features while maintaining integrity.
8
+ ///
9
+ /// Based on BCR-2021-002: Digests for Digital Objects
10
+ /// @see https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2021-002-digest.md
11
+ export class Digest {
12
+ readonly #data: Uint8Array;
13
+
14
+ /// Creates a new Digest from raw bytes.
15
+ ///
16
+ /// @param data - The 32-byte digest data
17
+ /// @throws {Error} If data is not exactly 32 bytes
18
+ constructor(data: Uint8Array) {
19
+ if (data.length !== 32) {
20
+ throw new Error(`Digest must be exactly 32 bytes, got ${data.length} bytes`);
21
+ }
22
+ this.#data = data;
23
+ }
24
+
25
+ /// Returns the raw digest bytes.
26
+ ///
27
+ /// @returns A Uint8Array containing the 32-byte digest
28
+ data(): Uint8Array {
29
+ return this.#data;
30
+ }
31
+
32
+ /// Creates a digest from an image (arbitrary byte array).
33
+ ///
34
+ /// This is the primary way to create a digest from data. The data is
35
+ /// hashed using SHA-256 to produce a 32-byte digest.
36
+ ///
37
+ /// @param image - The data to hash
38
+ /// @returns A new Digest instance
39
+ ///
40
+ /// @example
41
+ /// ```typescript
42
+ /// const digest = Digest.fromImage(new TextEncoder().encode("Hello, world!"));
43
+ /// ```
44
+ static fromImage(image: Uint8Array): Digest {
45
+ const hash = sha256(image);
46
+ return new Digest(hash);
47
+ }
48
+
49
+ /// Creates a digest from multiple digests.
50
+ ///
51
+ /// This is used to combine digests in the Merkle-like tree structure.
52
+ /// The digests are concatenated and then hashed.
53
+ ///
54
+ /// @param digests - An array of digests to combine
55
+ /// @returns A new Digest instance representing the combined digests
56
+ ///
57
+ /// @example
58
+ /// ```typescript
59
+ /// const digest1 = Digest.fromImage(data1);
60
+ /// const digest2 = Digest.fromImage(data2);
61
+ /// const combined = Digest.fromDigests([digest1, digest2]);
62
+ /// ```
63
+ static fromDigests(digests: Digest[]): Digest {
64
+ const totalLength = digests.length * 32;
65
+ const combined = new Uint8Array(totalLength);
66
+ let offset = 0;
67
+ for (const digest of digests) {
68
+ combined.set(digest.data(), offset);
69
+ offset += 32;
70
+ }
71
+ return Digest.fromImage(combined);
72
+ }
73
+
74
+ /// Returns the hexadecimal string representation of the digest.
75
+ ///
76
+ /// @returns A 64-character hexadecimal string
77
+ ///
78
+ /// @example
79
+ /// ```typescript
80
+ /// const digest = Digest.fromImage(data);
81
+ /// console.log(digest.hex()); // "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9"
82
+ /// ```
83
+ hex(): string {
84
+ return Array.from(this.#data)
85
+ .map((b) => b.toString(16).padStart(2, "0"))
86
+ .join("");
87
+ }
88
+
89
+ /// Returns an abbreviated hexadecimal representation for visual comparison.
90
+ ///
91
+ /// Following Blockchain Commons conventions, this returns the first 7
92
+ /// hexadecimal digits of the digest, which provides sufficient entropy
93
+ /// for human visual comparison while being easy to read.
94
+ ///
95
+ /// @returns A 7-character hexadecimal string
96
+ ///
97
+ /// @example
98
+ /// ```typescript
99
+ /// const digest = Digest.fromImage(data);
100
+ /// console.log(digest.short()); // "5feceb6"
101
+ /// ```
102
+ short(): string {
103
+ return this.hex().substring(0, 7);
104
+ }
105
+
106
+ /// Creates a digest from a hexadecimal string.
107
+ ///
108
+ /// @param hex - A 64-character hexadecimal string
109
+ /// @returns A new Digest instance
110
+ /// @throws {Error} If the hex string is not exactly 64 characters
111
+ ///
112
+ /// @example
113
+ /// ```typescript
114
+ /// const digest = Digest.fromHex("5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9");
115
+ /// ```
116
+ static fromHex(hex: string): Digest {
117
+ if (hex.length !== 64) {
118
+ throw new Error(`Hex string must be exactly 64 characters, got ${hex.length}`);
119
+ }
120
+ const data = new Uint8Array(32);
121
+ for (let i = 0; i < 32; i++) {
122
+ data[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
123
+ }
124
+ return new Digest(data);
125
+ }
126
+
127
+ /// Checks if two digests are equal.
128
+ ///
129
+ /// @param other - The other digest to compare with
130
+ /// @returns `true` if the digests are equal, `false` otherwise
131
+ equals(other: Digest): boolean {
132
+ if (this.#data.length !== other.#data.length) {
133
+ return false;
134
+ }
135
+ for (let i = 0; i < this.#data.length; i++) {
136
+ if (this.#data[i] !== other.#data[i]) {
137
+ return false;
138
+ }
139
+ }
140
+ return true;
141
+ }
142
+
143
+ /// Returns a string representation of the digest (short form).
144
+ ///
145
+ /// @returns The short hexadecimal representation
146
+ toString(): string {
147
+ return this.short();
148
+ }
149
+
150
+ /// Creates a deep copy of the digest.
151
+ ///
152
+ /// @returns A new Digest instance with the same data
153
+ clone(): Digest {
154
+ return new Digest(new Uint8Array(this.#data));
155
+ }
156
+ }
157
+
158
+ /// Trait for types that can provide a digest.
159
+ ///
160
+ /// This is equivalent to Rust's `DigestProvider` trait. Types that
161
+ /// implement this interface can be used in contexts where a digest
162
+ /// is needed for identity or integrity verification.
163
+ export interface DigestProvider {
164
+ /// Returns the digest of this object.
165
+ ///
166
+ /// The digest uniquely identifies the semantic content of the object,
167
+ /// regardless of whether parts of it are elided, encrypted, or compressed.
168
+ digest(): Digest;
169
+ }
170
+
171
+ /// Helper function to create a digest from a string.
172
+ ///
173
+ /// This is a convenience function for creating digests from text strings,
174
+ /// which are encoded as UTF-8 before hashing.
175
+ ///
176
+ /// @param text - The text to hash
177
+ /// @returns A new Digest instance
178
+ ///
179
+ /// @example
180
+ /// ```typescript
181
+ /// const digest = digestFromString("Hello, world!");
182
+ /// ```
183
+ export function digestFromString(text: string): Digest {
184
+ const encoder = new TextEncoder();
185
+ return Digest.fromImage(encoder.encode(text));
186
+ }
187
+
188
+ /// Helper function to create a digest from a number.
189
+ ///
190
+ /// The number is converted to a big-endian byte representation before hashing.
191
+ ///
192
+ /// @param num - The number to hash
193
+ /// @returns A new Digest instance
194
+ ///
195
+ /// @example
196
+ /// ```typescript
197
+ /// const digest = digestFromNumber(42);
198
+ /// ```
199
+ export function digestFromNumber(num: number): Digest {
200
+ const buffer = new ArrayBuffer(8);
201
+ const view = new DataView(buffer);
202
+ view.setFloat64(0, num, false); // big-endian
203
+ return Digest.fromImage(new Uint8Array(buffer));
204
+ }