@fulmenhq/tsfulmen 0.1.13 → 0.1.14

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/CHANGELOG.md CHANGED
@@ -7,23 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [0.1.13] - 2025-11-19
10
+ ## [0.1.14] - 2025-11-28
11
11
 
12
12
  ### Added
13
13
 
14
- - **Enterprise Configuration Loading** (`@fulmenhq/tsfulmen/config`) - Compliant implementation of Fulmen Forge Workhorse "Three-Layer Configuration" pattern:
15
- - **Three Layers**: Defaults (required) User Config (XDG paths) → Environment Variables (prefix-based)
16
- - **Schema Validation**: Optional validation against Crucible-compliant JSON/YAML schemas via AJV
17
- - **Metadata Tracking**: Detailed traceability of active layers, paths, and validation status
18
- - **Type Safety**: Generic `loadConfig<T>` with typed error handling (`ConfigValidationError`)
14
+ - **Unified Hashing Stack** (`@fulmenhq/tsfulmen/fulhash`) - Consolidated hashing implementations on `hash-wasm`
15
+ - Added support for **CRC32** and **CRC32C** algorithms (block and streaming)
16
+ - Implemented `multiHash()` for efficient single-pass multi-algorithm hashing
17
+ - Implemented `verify()` for checksum validation
18
+ - Benchmarked performance: XXH3-128 (~5GB/s), SHA-256 (~2GB/s), CRC (~1.2GB/s)
19
+ - Added comprehensive tests for streaming, concurrency, and encoding handling
19
20
 
20
21
  ### Changed
21
22
 
22
- - **Crucible SSOT** - Updated to v0.2.19 (syncs latest config, docs, and schemas)
23
+ - **Crucible SSOT** - Updated to v0.2.20 (syncs latest fulhash types)
24
+ - **Dependency Cleanup** - Removed `crc-32` and `fast-crc32c` in favor of `hash-wasm` consolidation
25
+ - **Bug Fixes**:
26
+ - Fixed `Digest.parse` to support 8-char CRC checksums
27
+ - Fixed XXH3 encoding handling for non-UTF8 inputs
28
+ - Improved `MultiHashResult` typing and deduplication
23
29
 
24
30
  ---
25
31
 
26
- ## [0.1.12] - 2025-11-18
32
+ ## [0.1.13] - 2025-11-20
27
33
 
28
34
  ### Fixed
29
35
 
@@ -1,28 +1,80 @@
1
1
  /**
2
- * Type definitions for FulHash hashing module
2
+ * FulHash Types
3
+ * Code generated by scripts/codegen/generate-fulhash-types.ts; DO NOT EDIT.
4
+ */
5
+ /**
6
+ * Supported hashing algorithms for FulHash
3
7
  */
4
8
  declare enum Algorithm {
9
+ /**
10
+ * XXH3 (128-bit)
11
+ * Fast non-cryptographic hash (default). Excellent collision resistance, extremely high throughput (50GB/s+).
12
+ */
5
13
  XXH3_128 = "xxh3-128",
6
- SHA256 = "sha256"
7
- }
8
- interface HashOptions {
9
- algorithm?: Algorithm;
10
- encoding?: BufferEncoding;
14
+ /**
15
+ * SHA-256
16
+ * Cryptographic security standard. Resistant to intentional collisions. Use for security verification.
17
+ */
18
+ SHA256 = "sha256",
19
+ /**
20
+ * CRC-32 (IEEE)
21
+ * 32-bit Cyclic Redundancy Check. Standard for GZIP/ZIP/PNG legacy format interoperability.
22
+ */
23
+ CRC32 = "crc32",
24
+ /**
25
+ * CRC-32C (Castagnoli)
26
+ * 32-bit CRC (Castagnoli). HW accelerated (SSE4.2/ARMv8). Use for cloud storage (GCS, AWS) and networking.
27
+ */
28
+ CRC32C = "crc32c"
11
29
  }
30
+ /**
31
+ * Standard digest payload returned by FulHash helpers
32
+ */
12
33
  interface Digest$1 {
13
- algorithm: Algorithm;
34
+ /**
35
+ * Hash algorithm identifier
36
+ */
37
+ algorithm: "xxh3-128" | "crc32" | "crc32c" | "sha256";
38
+ /**
39
+ * Lowercase hexadecimal representation of the digest
40
+ */
14
41
  hex: string;
15
- bytes: Uint8Array;
42
+ /**
43
+ * Canonical string representation '<algorithm>:<hex>'
44
+ */
16
45
  formatted: string;
46
+ /**
47
+ * Raw digest bytes (optional)
48
+ */
49
+ bytes?: number[];
17
50
  }
18
- interface StreamHasherOptions {
51
+
52
+ interface HashOptions {
19
53
  algorithm?: Algorithm;
54
+ encoding?: BufferEncoding;
20
55
  }
21
56
  interface StreamHasher {
22
57
  update(data: string | Uint8Array): StreamHasher;
23
58
  digest(): Digest$1;
24
59
  reset(): StreamHasher;
25
60
  }
61
+ interface StreamHasherOptions {
62
+ algorithm?: Algorithm;
63
+ }
64
+ type MultiHashResult = Partial<Record<Algorithm, Digest$1>>;
65
+
66
+ type HashInput = string | Uint8Array | AsyncIterable<string | Uint8Array>;
67
+ /**
68
+ * Compute multiple hashes in a single pass over the data.
69
+ * Useful for generating checksums for multiple algorithms (e.g. SHA256 + XXH3)
70
+ * without reading the stream multiple times.
71
+ */
72
+ declare function multiHash(input: HashInput, algorithms: Algorithm[], encoding?: BufferEncoding): Promise<MultiHashResult>;
73
+ /**
74
+ * Verify data against a formatted checksum (e.g. "sha256:abc...").
75
+ * Automatically selects the algorithm from the checksum prefix.
76
+ */
77
+ declare function verify(input: HashInput, checksum: string, encoding?: BufferEncoding): Promise<boolean>;
26
78
 
27
79
  /**
28
80
  * Digest implementation - immutable hash result container
@@ -34,7 +86,7 @@ declare class Digest implements Digest$1 {
34
86
  readonly hex: string;
35
87
  readonly formatted: string;
36
88
  constructor(algorithm: Algorithm, bytes: Uint8Array);
37
- get bytes(): Uint8Array;
89
+ get bytes(): number[];
38
90
  toJSON(): object;
39
91
  toString(): string;
40
92
  static parse(formatted: string): Digest;
@@ -74,9 +126,9 @@ declare function createStreamHasher(options?: StreamHasherOptions): Promise<Stre
74
126
  /**
75
127
  * FulHash - Consistent hashing API for Fulmen ecosystem
76
128
  *
77
- * Provides block and streaming hashing with xxh3-128 and sha256 algorithms.
129
+ * Provides block and streaming hashing with XXH3-128, SHA-256, CRC32, and CRC32C algorithms.
78
130
  * Cross-language compatible with gofulmen and pyfulmen.
79
131
  */
80
132
  declare const VERSION = "1.0.0";
81
133
 
82
- export { Algorithm, Digest, DigestStateError, FulHashError, type HashOptions, InvalidChecksumError, InvalidChecksumFormatError, type StreamHasher, type StreamHasherOptions, UnsupportedAlgorithmError, VERSION, createStreamHasher, hash, hashBytes, hashString };
134
+ export { Algorithm, Digest, DigestStateError, FulHashError, type HashInput, type HashOptions, InvalidChecksumError, InvalidChecksumFormatError, type StreamHasher, type StreamHasherOptions, UnsupportedAlgorithmError, VERSION, createStreamHasher, hash, hashBytes, hashString, multiHash, verify };
@@ -1,5 +1,5 @@
1
+ import { crc32, xxhash128, createXXHash128, createCRC32 } from 'hash-wasm';
1
2
  import { createHash } from 'crypto';
2
- import { xxhash128, createXXHash128 } from 'hash-wasm';
3
3
 
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -11,6 +11,20 @@ var __export = (target, all) => {
11
11
  __defProp(target, name, { get: all[name], enumerable: true });
12
12
  };
13
13
 
14
+ // src/crucible/fulhash/types.ts
15
+ var Algorithm;
16
+ var init_types = __esm({
17
+ "src/crucible/fulhash/types.ts"() {
18
+ Algorithm = /* @__PURE__ */ ((Algorithm2) => {
19
+ Algorithm2["XXH3_128"] = "xxh3-128";
20
+ Algorithm2["SHA256"] = "sha256";
21
+ Algorithm2["CRC32"] = "crc32";
22
+ Algorithm2["CRC32C"] = "crc32c";
23
+ return Algorithm2;
24
+ })(Algorithm || {});
25
+ }
26
+ });
27
+
14
28
  // src/fulhash/errors.ts
15
29
  var FulHashError, UnsupportedAlgorithmError, InvalidChecksumError, InvalidChecksumFormatError, DigestStateError;
16
30
  var init_errors = __esm({
@@ -54,24 +68,70 @@ var init_errors = __esm({
54
68
  };
55
69
  }
56
70
  });
57
-
58
- // src/fulhash/types.ts
59
- var Algorithm;
60
- var init_types = __esm({
61
- "src/fulhash/types.ts"() {
62
- Algorithm = /* @__PURE__ */ ((Algorithm2) => {
63
- Algorithm2["XXH3_128"] = "xxh3-128";
64
- Algorithm2["SHA256"] = "sha256";
65
- return Algorithm2;
66
- })(Algorithm || {});
71
+ function hexToBytes(hex) {
72
+ const bytes = new Uint8Array(4);
73
+ for (let i = 0; i < 4; i++) {
74
+ bytes[i] = Number.parseInt(hex.substr(i * 2, 2), 16);
75
+ }
76
+ return bytes;
77
+ }
78
+ async function hashBytes(data) {
79
+ const hex = await crc32(data);
80
+ return hexToBytes(hex);
81
+ }
82
+ async function hashString(str, encoding = "utf8") {
83
+ if (encoding !== "utf8") {
84
+ const buf = Buffer.from(str, encoding);
85
+ const hex2 = await crc32(buf);
86
+ return hexToBytes(hex2);
87
+ }
88
+ const hex = await crc32(str);
89
+ return hexToBytes(hex);
90
+ }
91
+ async function createHasher() {
92
+ const hasher = await createCRC32();
93
+ return hasher;
94
+ }
95
+ var init_crc32 = __esm({
96
+ "src/fulhash/algorithms/crc32.ts"() {
67
97
  }
68
98
  });
69
- function hashBytes(data) {
99
+ function hexToBytes2(hex) {
100
+ const bytes = new Uint8Array(4);
101
+ for (let i = 0; i < 4; i++) {
102
+ bytes[i] = Number.parseInt(hex.substr(i * 2, 2), 16);
103
+ }
104
+ return bytes;
105
+ }
106
+ async function hashBytes2(data) {
107
+ const hex = await crc32(data, CRC32C_POLYNOMIAL);
108
+ return hexToBytes2(hex);
109
+ }
110
+ async function hashString2(str, encoding = "utf8") {
111
+ if (encoding !== "utf8") {
112
+ const buf = Buffer.from(str, encoding);
113
+ const hex2 = await crc32(buf, CRC32C_POLYNOMIAL);
114
+ return hexToBytes2(hex2);
115
+ }
116
+ const hex = await crc32(str, CRC32C_POLYNOMIAL);
117
+ return hexToBytes2(hex);
118
+ }
119
+ async function createHasher2() {
120
+ const hasher = await createCRC32(CRC32C_POLYNOMIAL);
121
+ return hasher;
122
+ }
123
+ var CRC32C_POLYNOMIAL;
124
+ var init_crc32c = __esm({
125
+ "src/fulhash/algorithms/crc32c.ts"() {
126
+ CRC32C_POLYNOMIAL = 2197175160;
127
+ }
128
+ });
129
+ function hashBytes3(data) {
70
130
  const hash2 = createHash("sha256");
71
131
  hash2.update(data);
72
132
  return new Uint8Array(hash2.digest());
73
133
  }
74
- function hashString(str, encoding = "utf8") {
134
+ function hashString3(str, encoding = "utf8") {
75
135
  const hash2 = createHash("sha256");
76
136
  hash2.update(str, encoding);
77
137
  return new Uint8Array(hash2.digest());
@@ -80,25 +140,25 @@ var init_sha256 = __esm({
80
140
  "src/fulhash/algorithms/sha256.ts"() {
81
141
  }
82
142
  });
83
- function hexToBytes(hex) {
143
+ function hexToBytes3(hex) {
84
144
  const bytes = new Uint8Array(16);
85
145
  for (let i = 0; i < 16; i++) {
86
146
  bytes[i] = Number.parseInt(hex.substr(i * 2, 2), 16);
87
147
  }
88
148
  return bytes;
89
149
  }
90
- async function hashBytes2(data) {
150
+ async function hashBytes4(data) {
91
151
  const hex = await xxhash128(data);
92
- return hexToBytes(hex);
152
+ return hexToBytes3(hex);
93
153
  }
94
- async function hashString2(str, encoding = "utf8") {
154
+ async function hashString4(str, encoding = "utf8") {
95
155
  if (encoding !== "utf8") {
96
156
  throw new FulHashError(
97
157
  "XXH3-128 only supports UTF-8 encoding. Use utf8 encoding or convert data to Uint8Array."
98
158
  );
99
159
  }
100
160
  const hex = await xxhash128(str);
101
- return hexToBytes(hex);
161
+ return hexToBytes3(hex);
102
162
  }
103
163
  var init_xxh3 = __esm({
104
164
  "src/fulhash/algorithms/xxh3.ts"() {
@@ -110,43 +170,66 @@ var init_xxh3 = __esm({
110
170
  var hash_exports = {};
111
171
  __export(hash_exports, {
112
172
  hash: () => hash,
113
- hashBytes: () => hashBytes3,
114
- hashString: () => hashString3
173
+ hashBytes: () => hashBytes5,
174
+ hashString: () => hashString5
115
175
  });
116
176
  async function hash(input, options) {
117
177
  const algorithm = options?.algorithm ?? "xxh3-128" /* XXH3_128 */;
118
178
  const encoding = options?.encoding ?? "utf8";
119
179
  let bytes;
120
- if (algorithm === "sha256" /* SHA256 */) {
121
- if (typeof input === "string") {
122
- bytes = hashString(input, encoding);
123
- } else {
124
- bytes = hashBytes(input);
125
- }
126
- } else if (algorithm === "xxh3-128" /* XXH3_128 */) {
127
- if (typeof input === "string") {
128
- bytes = await hashString2(input, "utf8");
129
- } else {
130
- bytes = await hashBytes2(input);
131
- }
132
- } else {
133
- throw new UnsupportedAlgorithmError(algorithm, ["sha256" /* SHA256 */, "xxh3-128" /* XXH3_128 */]);
180
+ switch (algorithm) {
181
+ case "sha256" /* SHA256 */:
182
+ if (typeof input === "string") {
183
+ bytes = hashString3(input, encoding);
184
+ } else {
185
+ bytes = hashBytes3(input);
186
+ }
187
+ break;
188
+ case "xxh3-128" /* XXH3_128 */:
189
+ if (typeof input === "string") {
190
+ if (encoding !== "utf8") {
191
+ bytes = await hashBytes4(Buffer.from(input, encoding));
192
+ } else {
193
+ bytes = await hashString4(input, "utf8");
194
+ }
195
+ } else {
196
+ bytes = await hashBytes4(input);
197
+ }
198
+ break;
199
+ case "crc32" /* CRC32 */:
200
+ if (typeof input === "string") {
201
+ bytes = await hashString(input, encoding);
202
+ } else {
203
+ bytes = await hashBytes(input);
204
+ }
205
+ break;
206
+ case "crc32c" /* CRC32C */:
207
+ if (typeof input === "string") {
208
+ bytes = await hashString2(input, encoding);
209
+ } else {
210
+ bytes = await hashBytes2(input);
211
+ }
212
+ break;
213
+ default:
214
+ throw new UnsupportedAlgorithmError(algorithm, Object.values(Algorithm));
134
215
  }
135
216
  return new Digest(algorithm, bytes);
136
217
  }
137
- async function hashString3(str, options) {
218
+ async function hashString5(str, options) {
138
219
  return hash(str, options);
139
220
  }
140
- async function hashBytes3(data, options) {
221
+ async function hashBytes5(data, options) {
141
222
  return hash(data, options);
142
223
  }
143
224
  var init_hash = __esm({
144
225
  "src/fulhash/hash.ts"() {
226
+ init_types();
227
+ init_crc32();
228
+ init_crc32c();
145
229
  init_sha256();
146
230
  init_xxh3();
147
231
  init_digest();
148
232
  init_errors();
149
- init_types();
150
233
  }
151
234
  });
152
235
 
@@ -154,8 +237,8 @@ var init_hash = __esm({
154
237
  var Digest;
155
238
  var init_digest = __esm({
156
239
  "src/fulhash/digest.ts"() {
157
- init_errors();
158
240
  init_types();
241
+ init_errors();
159
242
  Digest = class _Digest {
160
243
  algorithm;
161
244
  _bytes;
@@ -169,7 +252,7 @@ var init_digest = __esm({
169
252
  Object.freeze(this);
170
253
  }
171
254
  get bytes() {
172
- return new Uint8Array(this._bytes);
255
+ return Array.from(this._bytes);
173
256
  }
174
257
  toJSON() {
175
258
  return {
@@ -199,7 +282,21 @@ var init_digest = __esm({
199
282
  "hex must contain only lowercase hexadecimal characters"
200
283
  );
201
284
  }
202
- const expectedLength = algorithm === "xxh3-128" /* XXH3_128 */ ? 32 : 64;
285
+ let expectedLength;
286
+ switch (algorithm) {
287
+ case "xxh3-128" /* XXH3_128 */:
288
+ expectedLength = 32;
289
+ break;
290
+ case "sha256" /* SHA256 */:
291
+ expectedLength = 64;
292
+ break;
293
+ case "crc32" /* CRC32 */:
294
+ case "crc32c" /* CRC32C */:
295
+ expectedLength = 8;
296
+ break;
297
+ default:
298
+ throw new UnsupportedAlgorithmError(algorithm, Object.values(Algorithm));
299
+ }
203
300
  if (hex.length !== expectedLength) {
204
301
  throw new InvalidChecksumError(
205
302
  formatted,
@@ -232,14 +329,17 @@ var init_digest = __esm({
232
329
  });
233
330
 
234
331
  // src/fulhash/index.ts
332
+ init_types();
333
+
334
+ // src/fulhash/convenience.ts
235
335
  init_digest();
236
- init_errors();
237
- init_hash();
238
336
 
239
337
  // src/fulhash/stream.ts
338
+ init_types();
339
+ init_crc32();
340
+ init_crc32c();
240
341
  init_digest();
241
342
  init_errors();
242
- init_types();
243
343
 
244
344
  // src/fulhash/wasm-loader.ts
245
345
  init_errors();
@@ -260,7 +360,7 @@ async function initializeWasm() {
260
360
  });
261
361
  return initPromise;
262
362
  }
263
- async function createHasher() {
363
+ async function createHasher3() {
264
364
  if (!isWasmReady) {
265
365
  throw new FulHashError("WASM not initialized. Call initializeWasm() first.");
266
366
  }
@@ -268,6 +368,15 @@ async function createHasher() {
268
368
  }
269
369
 
270
370
  // src/fulhash/stream.ts
371
+ function intToBytes(crc) {
372
+ const bytes = new Uint8Array(4);
373
+ const unsigned = crc >>> 0;
374
+ bytes[0] = unsigned >>> 24 & 255;
375
+ bytes[1] = unsigned >>> 16 & 255;
376
+ bytes[2] = unsigned >>> 8 & 255;
377
+ bytes[3] = unsigned & 255;
378
+ return bytes;
379
+ }
271
380
  var BaseStreamHasher = class {
272
381
  state = "initial" /* INITIAL */;
273
382
  algorithm;
@@ -352,23 +461,153 @@ var XXH3StreamHasher = class extends BaseStreamHasher {
352
461
  return this;
353
462
  }
354
463
  };
464
+ var CRC32StreamHasher = class extends BaseStreamHasher {
465
+ hasher = null;
466
+ constructor() {
467
+ super("crc32" /* CRC32 */);
468
+ }
469
+ async init() {
470
+ this.hasher = await createHasher();
471
+ this.hasher.init();
472
+ }
473
+ update(data) {
474
+ this.ensureNotFinalized();
475
+ this.markUpdating();
476
+ if (!this.hasher) {
477
+ throw new Error("Hasher not initialized");
478
+ }
479
+ if (typeof data === "string") {
480
+ const encoder = new TextEncoder();
481
+ this.hasher.update(encoder.encode(data));
482
+ } else {
483
+ this.hasher.update(data);
484
+ }
485
+ return this;
486
+ }
487
+ digest() {
488
+ this.ensureUpdating();
489
+ this.markFinalized();
490
+ if (!this.hasher) throw new Error("Hasher not initialized");
491
+ const hex = this.hasher.digest();
492
+ const bytes = intToBytes(parseInt(hex, 16));
493
+ return new Digest(this.algorithm, bytes);
494
+ }
495
+ reset() {
496
+ if (!this.hasher) throw new Error("Hasher not initialized");
497
+ this.hasher.init();
498
+ this.markInitial();
499
+ return this;
500
+ }
501
+ };
502
+ var CRC32CStreamHasher = class extends BaseStreamHasher {
503
+ hasher = null;
504
+ constructor() {
505
+ super("crc32c" /* CRC32C */);
506
+ }
507
+ async init() {
508
+ this.hasher = await createHasher2();
509
+ this.hasher.init();
510
+ }
511
+ update(data) {
512
+ this.ensureNotFinalized();
513
+ this.markUpdating();
514
+ if (!this.hasher) throw new Error("Hasher not initialized");
515
+ if (typeof data === "string") {
516
+ const encoder = new TextEncoder();
517
+ this.hasher.update(encoder.encode(data));
518
+ } else {
519
+ this.hasher.update(data);
520
+ }
521
+ return this;
522
+ }
523
+ digest() {
524
+ this.ensureUpdating();
525
+ this.markFinalized();
526
+ if (!this.hasher) throw new Error("Hasher not initialized");
527
+ const hex = this.hasher.digest();
528
+ const bytes = intToBytes(parseInt(hex, 16));
529
+ return new Digest(this.algorithm, bytes);
530
+ }
531
+ reset() {
532
+ if (!this.hasher) throw new Error("Hasher not initialized");
533
+ this.hasher.init();
534
+ this.markInitial();
535
+ return this;
536
+ }
537
+ };
355
538
  async function createStreamHasher(options = {}) {
356
539
  const algorithm = options.algorithm ?? "xxh3-128" /* XXH3_128 */;
357
- if (algorithm === "sha256" /* SHA256 */) {
358
- return new SHA256StreamHasher();
540
+ switch (algorithm) {
541
+ case "sha256" /* SHA256 */:
542
+ return new SHA256StreamHasher();
543
+ case "xxh3-128" /* XXH3_128 */: {
544
+ await initializeWasm();
545
+ const wasmHasher = await createHasher3();
546
+ return new XXH3StreamHasher(wasmHasher);
547
+ }
548
+ case "crc32" /* CRC32 */: {
549
+ const hasher = new CRC32StreamHasher();
550
+ await hasher.init();
551
+ return hasher;
552
+ }
553
+ case "crc32c" /* CRC32C */: {
554
+ const hasher = new CRC32CStreamHasher();
555
+ await hasher.init();
556
+ return hasher;
557
+ }
558
+ default:
559
+ throw new UnsupportedAlgorithmError(algorithm, Object.values(Algorithm));
560
+ }
561
+ }
562
+
563
+ // src/fulhash/convenience.ts
564
+ async function multiHash(input, algorithms, encoding = "utf8") {
565
+ const uniqueAlgorithms = [...new Set(algorithms)];
566
+ const hashers = await Promise.all(
567
+ uniqueAlgorithms.map((algo) => createStreamHasher({ algorithm: algo }))
568
+ );
569
+ if (typeof input === "string") {
570
+ if (encoding !== "utf8") {
571
+ const buf = Buffer.from(input, encoding);
572
+ for (const hasher of hashers) {
573
+ hasher.update(buf);
574
+ }
575
+ } else {
576
+ for (const hasher of hashers) {
577
+ hasher.update(input);
578
+ }
579
+ }
580
+ } else if (input instanceof Uint8Array) {
581
+ for (const hasher of hashers) {
582
+ hasher.update(input);
583
+ }
584
+ } else {
585
+ for await (const chunk of input) {
586
+ for (const hasher of hashers) {
587
+ hasher.update(chunk);
588
+ }
589
+ }
359
590
  }
360
- if (algorithm === "xxh3-128" /* XXH3_128 */) {
361
- await initializeWasm();
362
- const wasmHasher = await createHasher();
363
- return new XXH3StreamHasher(wasmHasher);
591
+ const result = {};
592
+ for (let i = 0; i < uniqueAlgorithms.length; i++) {
593
+ result[uniqueAlgorithms[i]] = hashers[i].digest();
364
594
  }
365
- throw new UnsupportedAlgorithmError(algorithm, ["sha256" /* SHA256 */, "xxh3-128" /* XXH3_128 */]);
595
+ return result;
596
+ }
597
+ async function verify(input, checksum, encoding = "utf8") {
598
+ const expected = Digest.parse(checksum);
599
+ const result = await multiHash(input, [expected.algorithm], encoding);
600
+ const actual = result[expected.algorithm];
601
+ if (!actual) return false;
602
+ return actual.algorithm === expected.algorithm && actual.hex === expected.hex;
366
603
  }
367
604
 
368
605
  // src/fulhash/index.ts
369
- init_types();
606
+ init_digest();
607
+ init_errors();
608
+ init_hash();
370
609
  var VERSION = "1.0.0";
371
610
 
372
- export { Algorithm, Digest, DigestStateError, FulHashError, InvalidChecksumError, InvalidChecksumFormatError, UnsupportedAlgorithmError, VERSION, createStreamHasher, hash, hashBytes3 as hashBytes, hashString3 as hashString };
611
+ export { Algorithm, Digest, DigestStateError, FulHashError, InvalidChecksumError, InvalidChecksumFormatError, UnsupportedAlgorithmError, VERSION, createStreamHasher, hash, hashBytes5 as hashBytes, hashString5 as hashString, multiHash, verify };
373
612
  //# sourceMappingURL=index.js.map
374
613
  //# sourceMappingURL=index.js.map