@fedify/vocab-runtime 2.4.0-dev.1618 → 2.4.0-dev.1655

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 (57) hide show
  1. package/deno.json +1 -1
  2. package/dist/{docloader-DnUMWHaJ.d.cts → docloader-C_dir7Xb.d.ts} +2 -0
  3. package/dist/{docloader-xRGn1azD.d.ts → docloader-D2DTRiyA.d.cts} +2 -0
  4. package/dist/internal/jsonld-cache.cjs +1 -1
  5. package/dist/internal/jsonld-cache.d.cts +1 -1
  6. package/dist/internal/jsonld-cache.d.ts +1 -1
  7. package/dist/internal/jsonld-cache.js +1 -1
  8. package/dist/mod.cjs +159 -8
  9. package/dist/mod.d.cts +91 -2
  10. package/dist/mod.d.ts +91 -2
  11. package/dist/mod.js +152 -9
  12. package/dist/tests/decimal.test.cjs +6 -5
  13. package/dist/tests/decimal.test.mjs +6 -5
  14. package/dist/tests/digest-BJlumPNJ.mjs +140 -0
  15. package/dist/tests/digest-DLwUyHCK.cjs +175 -0
  16. package/dist/tests/digest.test.cjs +103 -0
  17. package/dist/tests/digest.test.d.cts +1 -0
  18. package/dist/tests/digest.test.d.mts +1 -0
  19. package/dist/tests/digest.test.mjs +103 -0
  20. package/dist/tests/{docloader-QmR6VOqT.cjs → docloader-DVVWmXLI.cjs} +12 -6
  21. package/dist/tests/{docloader-CBVde8Va.mjs → docloader-HbEvzi-t.mjs} +12 -6
  22. package/dist/tests/docloader.test.cjs +3 -3
  23. package/dist/tests/docloader.test.mjs +3 -3
  24. package/dist/tests/jsonld-cache.test.cjs +1 -1
  25. package/dist/tests/jsonld-cache.test.mjs +1 -1
  26. package/dist/tests/{key-_wXwomh_.cjs → key-BcOWDLMm.cjs} +1 -1
  27. package/dist/tests/{key-CDGDH_vC.mjs → key-Drbw_2tw.mjs} +1 -1
  28. package/dist/tests/key.test.cjs +2 -2
  29. package/dist/tests/key.test.mjs +2 -2
  30. package/dist/tests/langstr.test.cjs +1 -1
  31. package/dist/tests/langstr.test.mjs +1 -1
  32. package/dist/tests/multibase/multibase.test.cjs +1 -1
  33. package/dist/tests/multibase/multibase.test.mjs +1 -1
  34. package/dist/tests/{multibase-B4bvakyA.mjs → multibase-Cluwl8OM.mjs} +12 -1
  35. package/dist/tests/{multibase-Bz_UUDtL.cjs → multibase-DcDQDNnI.cjs} +17 -0
  36. package/dist/tests/{request-BHx0fCb5.mjs → request-B6s_F7bi.mjs} +1 -1
  37. package/dist/tests/{request-DxSceLvB.cjs → request-DvEyU5Zf.cjs} +1 -1
  38. package/dist/tests/request.test.cjs +1 -1
  39. package/dist/tests/request.test.mjs +1 -1
  40. package/dist/tests/{url-BvjYQdxL.cjs → url-CsOV_B_P.cjs} +26 -0
  41. package/dist/tests/{url-a2D8NAgh.mjs → url-Du7RQQgP.mjs} +15 -1
  42. package/dist/tests/url.test.cjs +18 -1
  43. package/dist/tests/url.test.mjs +18 -1
  44. package/dist/{url-Ck3dGEwH.cjs → url-DD4F0ULf.cjs} +26 -0
  45. package/dist/{url-m1YxGNZ0.js → url-DGVbSVVi.js} +15 -1
  46. package/package.json +1 -1
  47. package/src/contexts/fep-ef61.json +10 -0
  48. package/src/contexts/security-data-integrity-v1.json +0 -4
  49. package/src/contexts.ts +2 -0
  50. package/src/digest.test.ts +220 -0
  51. package/src/digest.ts +229 -0
  52. package/src/docloader.ts +2 -0
  53. package/src/mod.ts +12 -0
  54. package/src/url.test.ts +23 -0
  55. package/src/url.ts +23 -0
  56. /package/dist/tests/{langstr-CbAxaeEZ.cjs → langstr-BOQHfJDr.cjs} +0 -0
  57. /package/dist/tests/{langstr-Di5AvKpB.mjs → langstr-DDLc4833.mjs} +0 -0
package/dist/mod.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { a as formatIri, c as haveSameIriOrigin, d as parseIri, f as parseJsonLdId, i as expandIPv6Address, l as isValidPublicIPv4Address, n as arePortableUrisEqual, o as getFe34Origin, p as validatePublicUrl, r as canonicalizePortableUri, s as haveSameFe34Origin, t as UrlError, u as isValidPublicIPv6Address } from "./url-m1YxGNZ0.js";
2
+ import { a as formatIri, c as haveSameIriOrigin, d as isValidPublicIPv6Address, f as parseGatewayUrl, h as validatePublicUrl, i as expandIPv6Address, l as isGatewayUrl, m as parseJsonLdId, n as arePortableUrisEqual, o as getFe34Origin, p as parseIri, r as canonicalizePortableUri, s as haveSameFe34Origin, t as UrlError, u as isValidPublicIPv4Address } from "./url-DGVbSVVi.js";
3
3
  import { getLogger } from "@logtape/logtape";
4
4
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
5
5
  import process from "node:process";
@@ -465,10 +465,6 @@ const preloadedContexts = {
465
465
  "id": "@id",
466
466
  "type": "@type",
467
467
  "@protected": true,
468
- "digestMultibase": {
469
- "@id": "https://w3id.org/security#digestMultibase",
470
- "@type": "https://w3id.org/security#multibase"
471
- },
472
468
  "proof": {
473
469
  "@id": "https://w3id.org/security#proof",
474
470
  "@type": "@id",
@@ -4305,6 +4301,14 @@ const preloadedContexts = {
4305
4301
  "@type": "@id"
4306
4302
  }
4307
4303
  } },
4304
+ "https://w3id.org/fep/ef61": { "@context": {
4305
+ "gateways": {
4306
+ "@id": "https://w3id.org/fep/ef61/gateways",
4307
+ "@type": "@id",
4308
+ "@container": "@list"
4309
+ },
4310
+ "digestMultibase": "https://www.w3.org/ns/credentials/v2#digestMultibase"
4311
+ } },
4308
4312
  "https://join-lemmy.org/context.json": { "@context": ["https://w3id.org/security/v1", {
4309
4313
  "as": "https://www.w3.org/ns/activitystreams#",
4310
4314
  "lemmy": "https://join-lemmy.org/ns#",
@@ -4363,7 +4367,7 @@ const preloadedContexts = {
4363
4367
  //#endregion
4364
4368
  //#region deno.json
4365
4369
  var name = "@fedify/vocab-runtime";
4366
- var version = "2.4.0-dev.1618+58858243";
4370
+ var version = "2.4.0-dev.1655+b8f3c3d5";
4367
4371
  //#endregion
4368
4372
  //#region src/link.ts
4369
4373
  const parametersNeedLowerCase = ["rel", "type"];
@@ -4794,8 +4798,10 @@ async function getRemoteDocument(url, response, fetch) {
4794
4798
  * - <https://www.w3.org/ns/activitystreams>
4795
4799
  * - <https://w3id.org/security/v1>
4796
4800
  * - <https://w3id.org/security/data-integrity/v1>
4801
+ * - <https://w3id.org/security/data-integrity/v2>
4797
4802
  * - <https://www.w3.org/ns/did/v1>
4798
4803
  * - <https://w3id.org/security/multikey/v1>
4804
+ * - <https://w3id.org/fep/ef61>
4799
4805
  * - <https://purl.archive.org/socialweb/webfinger>
4800
4806
  * - <http://schema.org/>
4801
4807
  * @param options Options for the document loader.
@@ -4926,8 +4932,8 @@ function validateCryptoKey(key, type) {
4926
4932
  }
4927
4933
  //#endregion
4928
4934
  //#region src/multibase/util.ts
4929
- const textDecoder = new TextDecoder();
4930
- const decodeText = (bytes) => textDecoder.decode(bytes);
4935
+ const textDecoder$1 = new TextDecoder();
4936
+ const decodeText = (bytes) => textDecoder$1.decode(bytes);
4931
4937
  const textEncoder = new TextEncoder();
4932
4938
  const encodeText = (text) => textEncoder.encode(text);
4933
4939
  function concat(arrs, length) {
@@ -5516,6 +5522,143 @@ function parseDecimal(value) {
5516
5522
  return normalized;
5517
5523
  }
5518
5524
  //#endregion
5525
+ //#region src/digest.ts
5526
+ const SHA2_256_MULTIHASH_CODE = 18;
5527
+ const SHA2_256_DIGEST_LENGTH = 32;
5528
+ const MAX_DIGEST_MULTIBASE_LENGTH = 1 + (SHA2_256_DIGEST_LENGTH + 2) * 8;
5529
+ const textDecoder = new TextDecoder();
5530
+ const getArrayBufferByteLength = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength")?.get;
5531
+ function isCanonicalVarintPrefix(data, prefix) {
5532
+ const canonical = addMulticodecPrefix(prefix.code, new Uint8Array());
5533
+ if (canonical.length !== prefix.prefixLength) return false;
5534
+ for (let i = 0; i < canonical.length; i++) if (data[i] !== canonical[i]) return false;
5535
+ return true;
5536
+ }
5537
+ function toWebCryptoBytes(bytes) {
5538
+ if (getArrayBufferByteLength != null) try {
5539
+ getArrayBufferByteLength.call(bytes.buffer);
5540
+ return bytes;
5541
+ } catch {}
5542
+ return new Uint8Array(bytes);
5543
+ }
5544
+ /**
5545
+ * Computes the SHA-256 digest of a byte sequence and encodes it as a
5546
+ * base58-btc multibase multihash suitable for FEP-ef61's
5547
+ * `digestMultibase` property.
5548
+ *
5549
+ * @param bytes The bytes to digest.
5550
+ * @returns The base58-btc multibase-encoded SHA-256 multihash.
5551
+ * @since 2.4.0
5552
+ */
5553
+ async function computeDigestMultibase(bytes) {
5554
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", toWebCryptoBytes(bytes)));
5555
+ const multihash = addMulticodecPrefix(SHA2_256_MULTIHASH_CODE, addMulticodecPrefix(digest.length, digest));
5556
+ return textDecoder.decode(encodeMultibase("base58btc", multihash));
5557
+ }
5558
+ /**
5559
+ * Parses and validates a SHA-256 `digestMultibase` value.
5560
+ *
5561
+ * @param value The multibase-encoded multihash to parse.
5562
+ * @returns The digest algorithm and raw digest bytes.
5563
+ * @throws {TypeError} If the value is malformed or uses an unsupported hash
5564
+ * algorithm.
5565
+ * @since 2.4.0
5566
+ */
5567
+ function parseDigestMultibase(value) {
5568
+ if (value.length > MAX_DIGEST_MULTIBASE_LENGTH) throw new TypeError("Invalid digestMultibase encoding.");
5569
+ let multihash;
5570
+ try {
5571
+ multihash = decodeMultibase(value);
5572
+ } catch (error) {
5573
+ throw new TypeError("Invalid digestMultibase encoding.", { cause: error });
5574
+ }
5575
+ const encoding = encodingFromBaseData(value);
5576
+ if (value !== textDecoder.decode(encodeMultibase(encoding.name, multihash))) throw new TypeError("Invalid digestMultibase encoding.");
5577
+ let algorithmPrefix;
5578
+ try {
5579
+ algorithmPrefix = getMulticodecPrefix(multihash);
5580
+ } catch (error) {
5581
+ throw new TypeError("Invalid digestMultibase multihash.", { cause: error });
5582
+ }
5583
+ if (!isCanonicalVarintPrefix(multihash, algorithmPrefix)) throw new TypeError("Invalid digestMultibase multihash.");
5584
+ if (algorithmPrefix.code !== SHA2_256_MULTIHASH_CODE) throw new TypeError(`Unsupported digest algorithm: 0x${algorithmPrefix.code.toString(16)}`);
5585
+ const lengthAndDigest = multihash.subarray(algorithmPrefix.prefixLength);
5586
+ let lengthPrefix;
5587
+ try {
5588
+ lengthPrefix = getMulticodecPrefix(lengthAndDigest);
5589
+ } catch (error) {
5590
+ throw new TypeError("Invalid digestMultibase multihash.", { cause: error });
5591
+ }
5592
+ if (!isCanonicalVarintPrefix(lengthAndDigest, lengthPrefix)) throw new TypeError("Invalid digestMultibase multihash.");
5593
+ const digest = lengthAndDigest.slice(lengthPrefix.prefixLength);
5594
+ if (lengthPrefix.code !== SHA2_256_DIGEST_LENGTH || digest.length !== SHA2_256_DIGEST_LENGTH) throw new TypeError("Invalid SHA-256 digest length.");
5595
+ return {
5596
+ algorithm: "sha2-256",
5597
+ digest
5598
+ };
5599
+ }
5600
+ function extractDigestMultibase(value) {
5601
+ const hashlink = typeof value === "string" ? value : value.href;
5602
+ const match = /^hl:([^:\r\n\u2028\u2029]+)$/i.exec(hashlink);
5603
+ if (match == null || match[0].length !== hashlink.length) throw new TypeError("Invalid simple hashlink.");
5604
+ return match[1];
5605
+ }
5606
+ /**
5607
+ * Parses a metadata-free `hl:` hashlink and validates its resource digest.
5608
+ *
5609
+ * @param value The simple hashlink to parse.
5610
+ * @returns The `digestMultibase` value carried by the hashlink.
5611
+ * @throws {TypeError} If the hashlink is malformed, contains metadata, or
5612
+ * carries an invalid or unsupported digest.
5613
+ * @since 2.4.0
5614
+ */
5615
+ function parseHashlink(value) {
5616
+ const digestMultibase = extractDigestMultibase(value);
5617
+ parseDigestMultibase(digestMultibase);
5618
+ return { digestMultibase };
5619
+ }
5620
+ /**
5621
+ * Creates a metadata-free `hl:` hashlink from a `digestMultibase` value.
5622
+ *
5623
+ * @param digestMultibase The SHA-256 multibase multihash to embed.
5624
+ * @returns The simple hashlink.
5625
+ * @throws {TypeError} If the digest is malformed or unsupported.
5626
+ * @since 2.4.0
5627
+ */
5628
+ function createHashlink(digestMultibase) {
5629
+ parseDigestMultibase(digestMultibase);
5630
+ return `hl:${digestMultibase}`;
5631
+ }
5632
+ /**
5633
+ * Verifies that bytes match a SHA-256 `digestMultibase` value.
5634
+ *
5635
+ * @param bytes The bytes to verify.
5636
+ * @param digestMultibase The expected digest.
5637
+ * @returns Whether the bytes match the digest.
5638
+ * @throws {TypeError} If the digest is malformed or unsupported.
5639
+ * @since 2.4.0
5640
+ */
5641
+ async function verifyDigestMultibase(bytes, digestMultibase) {
5642
+ const expected = parseDigestMultibase(digestMultibase).digest;
5643
+ const actual = new Uint8Array(await crypto.subtle.digest("SHA-256", toWebCryptoBytes(bytes)));
5644
+ if (expected.length !== actual.length) return false;
5645
+ let difference = 0;
5646
+ for (let i = 0; i < expected.length; i++) difference |= expected[i] ^ actual[i];
5647
+ return difference === 0;
5648
+ }
5649
+ /**
5650
+ * Verifies that bytes match the resource digest in a simple hashlink.
5651
+ *
5652
+ * @param bytes The bytes to verify.
5653
+ * @param hashlink The simple hashlink containing the expected digest.
5654
+ * @returns Whether the bytes match the hashlink digest.
5655
+ * @throws {TypeError} If the hashlink or digest is malformed or unsupported.
5656
+ * @since 2.4.0
5657
+ */
5658
+ async function verifyHashlink(bytes, hashlink) {
5659
+ return await verifyDigestMultibase(bytes, extractDigestMultibase(hashlink));
5660
+ }
5661
+ //#endregion
5519
5662
  //#region src/langstr.ts
5520
5663
  /**
5521
5664
  * A language-tagged string which corresponds to the `rdf:langString` type.
@@ -5544,4 +5687,4 @@ LanguageString.prototype[Symbol.for("nodejs.util.inspect.custom")] = function(_d
5544
5687
  return `<${this.locale.baseName}> ${inspect(this.toString(), options)}`;
5545
5688
  };
5546
5689
  //#endregion
5547
- export { FetchError, LanguageString, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, createActivityPubRequest, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseIri, parseJsonLdId, preloadedContexts, validatePublicUrl };
5690
+ export { FetchError, LanguageString, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, computeDigestMultibase, createActivityPubRequest, createHashlink, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isGatewayUrl, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseDigestMultibase, parseGatewayUrl, parseHashlink, parseIri, parseJsonLdId, preloadedContexts, validatePublicUrl, verifyDigestMultibase, verifyHashlink };
@@ -1,9 +1,10 @@
1
1
  require("./chunk-C2EiDwsr.cjs");
2
- require("./docloader-QmR6VOqT.cjs");
3
- require("./url-BvjYQdxL.cjs");
4
- require("./key-_wXwomh_.cjs");
5
- require("./multibase-Bz_UUDtL.cjs");
6
- require("./langstr-CbAxaeEZ.cjs");
2
+ require("./docloader-DVVWmXLI.cjs");
3
+ require("./url-CsOV_B_P.cjs");
4
+ require("./key-BcOWDLMm.cjs");
5
+ require("./multibase-DcDQDNnI.cjs");
6
+ require("./digest-DLwUyHCK.cjs");
7
+ require("./langstr-BOQHfJDr.cjs");
7
8
  let node_assert = require("node:assert");
8
9
  let node_test = require("node:test");
9
10
  //#region src/decimal.ts
@@ -1,8 +1,9 @@
1
- import "./docloader-CBVde8Va.mjs";
2
- import "./url-a2D8NAgh.mjs";
3
- import "./key-CDGDH_vC.mjs";
4
- import "./multibase-B4bvakyA.mjs";
5
- import "./langstr-Di5AvKpB.mjs";
1
+ import "./docloader-HbEvzi-t.mjs";
2
+ import "./url-Du7RQQgP.mjs";
3
+ import "./key-Drbw_2tw.mjs";
4
+ import "./multibase-Cluwl8OM.mjs";
5
+ import "./digest-BJlumPNJ.mjs";
6
+ import "./langstr-DDLc4833.mjs";
6
7
  import { deepStrictEqual, throws } from "node:assert";
7
8
  import { test } from "node:test";
8
9
  //#region src/decimal.ts
@@ -0,0 +1,140 @@
1
+ import { n as getMulticodecPrefix, t as addMulticodecPrefix } from "./multicodec-CyFp54fI.mjs";
2
+ import { n as encodeMultibase, r as encodingFromBaseData, t as decodeMultibase } from "./multibase-Cluwl8OM.mjs";
3
+ //#region src/digest.ts
4
+ const SHA2_256_MULTIHASH_CODE = 18;
5
+ const SHA2_256_DIGEST_LENGTH = 32;
6
+ const MAX_DIGEST_MULTIBASE_LENGTH = 1 + (SHA2_256_DIGEST_LENGTH + 2) * 8;
7
+ const textDecoder = new TextDecoder();
8
+ const getArrayBufferByteLength = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength")?.get;
9
+ function isCanonicalVarintPrefix(data, prefix) {
10
+ const canonical = addMulticodecPrefix(prefix.code, new Uint8Array());
11
+ if (canonical.length !== prefix.prefixLength) return false;
12
+ for (let i = 0; i < canonical.length; i++) if (data[i] !== canonical[i]) return false;
13
+ return true;
14
+ }
15
+ function toWebCryptoBytes(bytes) {
16
+ if (getArrayBufferByteLength != null) try {
17
+ getArrayBufferByteLength.call(bytes.buffer);
18
+ return bytes;
19
+ } catch {}
20
+ return new Uint8Array(bytes);
21
+ }
22
+ /**
23
+ * Computes the SHA-256 digest of a byte sequence and encodes it as a
24
+ * base58-btc multibase multihash suitable for FEP-ef61's
25
+ * `digestMultibase` property.
26
+ *
27
+ * @param bytes The bytes to digest.
28
+ * @returns The base58-btc multibase-encoded SHA-256 multihash.
29
+ * @since 2.4.0
30
+ */
31
+ async function computeDigestMultibase(bytes) {
32
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", toWebCryptoBytes(bytes)));
33
+ const multihash = addMulticodecPrefix(SHA2_256_MULTIHASH_CODE, addMulticodecPrefix(digest.length, digest));
34
+ return textDecoder.decode(encodeMultibase("base58btc", multihash));
35
+ }
36
+ /**
37
+ * Parses and validates a SHA-256 `digestMultibase` value.
38
+ *
39
+ * @param value The multibase-encoded multihash to parse.
40
+ * @returns The digest algorithm and raw digest bytes.
41
+ * @throws {TypeError} If the value is malformed or uses an unsupported hash
42
+ * algorithm.
43
+ * @since 2.4.0
44
+ */
45
+ function parseDigestMultibase(value) {
46
+ if (value.length > MAX_DIGEST_MULTIBASE_LENGTH) throw new TypeError("Invalid digestMultibase encoding.");
47
+ let multihash;
48
+ try {
49
+ multihash = decodeMultibase(value);
50
+ } catch (error) {
51
+ throw new TypeError("Invalid digestMultibase encoding.", { cause: error });
52
+ }
53
+ const encoding = encodingFromBaseData(value);
54
+ if (value !== textDecoder.decode(encodeMultibase(encoding.name, multihash))) throw new TypeError("Invalid digestMultibase encoding.");
55
+ let algorithmPrefix;
56
+ try {
57
+ algorithmPrefix = getMulticodecPrefix(multihash);
58
+ } catch (error) {
59
+ throw new TypeError("Invalid digestMultibase multihash.", { cause: error });
60
+ }
61
+ if (!isCanonicalVarintPrefix(multihash, algorithmPrefix)) throw new TypeError("Invalid digestMultibase multihash.");
62
+ if (algorithmPrefix.code !== SHA2_256_MULTIHASH_CODE) throw new TypeError(`Unsupported digest algorithm: 0x${algorithmPrefix.code.toString(16)}`);
63
+ const lengthAndDigest = multihash.subarray(algorithmPrefix.prefixLength);
64
+ let lengthPrefix;
65
+ try {
66
+ lengthPrefix = getMulticodecPrefix(lengthAndDigest);
67
+ } catch (error) {
68
+ throw new TypeError("Invalid digestMultibase multihash.", { cause: error });
69
+ }
70
+ if (!isCanonicalVarintPrefix(lengthAndDigest, lengthPrefix)) throw new TypeError("Invalid digestMultibase multihash.");
71
+ const digest = lengthAndDigest.slice(lengthPrefix.prefixLength);
72
+ if (lengthPrefix.code !== SHA2_256_DIGEST_LENGTH || digest.length !== SHA2_256_DIGEST_LENGTH) throw new TypeError("Invalid SHA-256 digest length.");
73
+ return {
74
+ algorithm: "sha2-256",
75
+ digest
76
+ };
77
+ }
78
+ function extractDigestMultibase(value) {
79
+ const hashlink = typeof value === "string" ? value : value.href;
80
+ const match = /^hl:([^:\r\n\u2028\u2029]+)$/i.exec(hashlink);
81
+ if (match == null || match[0].length !== hashlink.length) throw new TypeError("Invalid simple hashlink.");
82
+ return match[1];
83
+ }
84
+ /**
85
+ * Parses a metadata-free `hl:` hashlink and validates its resource digest.
86
+ *
87
+ * @param value The simple hashlink to parse.
88
+ * @returns The `digestMultibase` value carried by the hashlink.
89
+ * @throws {TypeError} If the hashlink is malformed, contains metadata, or
90
+ * carries an invalid or unsupported digest.
91
+ * @since 2.4.0
92
+ */
93
+ function parseHashlink(value) {
94
+ const digestMultibase = extractDigestMultibase(value);
95
+ parseDigestMultibase(digestMultibase);
96
+ return { digestMultibase };
97
+ }
98
+ /**
99
+ * Creates a metadata-free `hl:` hashlink from a `digestMultibase` value.
100
+ *
101
+ * @param digestMultibase The SHA-256 multibase multihash to embed.
102
+ * @returns The simple hashlink.
103
+ * @throws {TypeError} If the digest is malformed or unsupported.
104
+ * @since 2.4.0
105
+ */
106
+ function createHashlink(digestMultibase) {
107
+ parseDigestMultibase(digestMultibase);
108
+ return `hl:${digestMultibase}`;
109
+ }
110
+ /**
111
+ * Verifies that bytes match a SHA-256 `digestMultibase` value.
112
+ *
113
+ * @param bytes The bytes to verify.
114
+ * @param digestMultibase The expected digest.
115
+ * @returns Whether the bytes match the digest.
116
+ * @throws {TypeError} If the digest is malformed or unsupported.
117
+ * @since 2.4.0
118
+ */
119
+ async function verifyDigestMultibase(bytes, digestMultibase) {
120
+ const expected = parseDigestMultibase(digestMultibase).digest;
121
+ const actual = new Uint8Array(await crypto.subtle.digest("SHA-256", toWebCryptoBytes(bytes)));
122
+ if (expected.length !== actual.length) return false;
123
+ let difference = 0;
124
+ for (let i = 0; i < expected.length; i++) difference |= expected[i] ^ actual[i];
125
+ return difference === 0;
126
+ }
127
+ /**
128
+ * Verifies that bytes match the resource digest in a simple hashlink.
129
+ *
130
+ * @param bytes The bytes to verify.
131
+ * @param hashlink The simple hashlink containing the expected digest.
132
+ * @returns Whether the bytes match the hashlink digest.
133
+ * @throws {TypeError} If the hashlink or digest is malformed or unsupported.
134
+ * @since 2.4.0
135
+ */
136
+ async function verifyHashlink(bytes, hashlink) {
137
+ return await verifyDigestMultibase(bytes, extractDigestMultibase(hashlink));
138
+ }
139
+ //#endregion
140
+ export { verifyDigestMultibase as a, parseHashlink as i, createHashlink as n, verifyHashlink as o, parseDigestMultibase as r, computeDigestMultibase as t };
@@ -0,0 +1,175 @@
1
+ const require_multicodec = require("./multicodec-CxGVGa91.cjs");
2
+ const require_multibase = require("./multibase-DcDQDNnI.cjs");
3
+ //#region src/digest.ts
4
+ const SHA2_256_MULTIHASH_CODE = 18;
5
+ const SHA2_256_DIGEST_LENGTH = 32;
6
+ const MAX_DIGEST_MULTIBASE_LENGTH = 1 + (SHA2_256_DIGEST_LENGTH + 2) * 8;
7
+ const textDecoder = new TextDecoder();
8
+ const getArrayBufferByteLength = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength")?.get;
9
+ function isCanonicalVarintPrefix(data, prefix) {
10
+ const canonical = require_multicodec.addMulticodecPrefix(prefix.code, new Uint8Array());
11
+ if (canonical.length !== prefix.prefixLength) return false;
12
+ for (let i = 0; i < canonical.length; i++) if (data[i] !== canonical[i]) return false;
13
+ return true;
14
+ }
15
+ function toWebCryptoBytes(bytes) {
16
+ if (getArrayBufferByteLength != null) try {
17
+ getArrayBufferByteLength.call(bytes.buffer);
18
+ return bytes;
19
+ } catch {}
20
+ return new Uint8Array(bytes);
21
+ }
22
+ /**
23
+ * Computes the SHA-256 digest of a byte sequence and encodes it as a
24
+ * base58-btc multibase multihash suitable for FEP-ef61's
25
+ * `digestMultibase` property.
26
+ *
27
+ * @param bytes The bytes to digest.
28
+ * @returns The base58-btc multibase-encoded SHA-256 multihash.
29
+ * @since 2.4.0
30
+ */
31
+ async function computeDigestMultibase(bytes) {
32
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", toWebCryptoBytes(bytes)));
33
+ const multihash = require_multicodec.addMulticodecPrefix(SHA2_256_MULTIHASH_CODE, require_multicodec.addMulticodecPrefix(digest.length, digest));
34
+ return textDecoder.decode(require_multibase.encodeMultibase("base58btc", multihash));
35
+ }
36
+ /**
37
+ * Parses and validates a SHA-256 `digestMultibase` value.
38
+ *
39
+ * @param value The multibase-encoded multihash to parse.
40
+ * @returns The digest algorithm and raw digest bytes.
41
+ * @throws {TypeError} If the value is malformed or uses an unsupported hash
42
+ * algorithm.
43
+ * @since 2.4.0
44
+ */
45
+ function parseDigestMultibase(value) {
46
+ if (value.length > MAX_DIGEST_MULTIBASE_LENGTH) throw new TypeError("Invalid digestMultibase encoding.");
47
+ let multihash;
48
+ try {
49
+ multihash = require_multibase.decodeMultibase(value);
50
+ } catch (error) {
51
+ throw new TypeError("Invalid digestMultibase encoding.", { cause: error });
52
+ }
53
+ const encoding = require_multibase.encodingFromBaseData(value);
54
+ if (value !== textDecoder.decode(require_multibase.encodeMultibase(encoding.name, multihash))) throw new TypeError("Invalid digestMultibase encoding.");
55
+ let algorithmPrefix;
56
+ try {
57
+ algorithmPrefix = require_multicodec.getMulticodecPrefix(multihash);
58
+ } catch (error) {
59
+ throw new TypeError("Invalid digestMultibase multihash.", { cause: error });
60
+ }
61
+ if (!isCanonicalVarintPrefix(multihash, algorithmPrefix)) throw new TypeError("Invalid digestMultibase multihash.");
62
+ if (algorithmPrefix.code !== SHA2_256_MULTIHASH_CODE) throw new TypeError(`Unsupported digest algorithm: 0x${algorithmPrefix.code.toString(16)}`);
63
+ const lengthAndDigest = multihash.subarray(algorithmPrefix.prefixLength);
64
+ let lengthPrefix;
65
+ try {
66
+ lengthPrefix = require_multicodec.getMulticodecPrefix(lengthAndDigest);
67
+ } catch (error) {
68
+ throw new TypeError("Invalid digestMultibase multihash.", { cause: error });
69
+ }
70
+ if (!isCanonicalVarintPrefix(lengthAndDigest, lengthPrefix)) throw new TypeError("Invalid digestMultibase multihash.");
71
+ const digest = lengthAndDigest.slice(lengthPrefix.prefixLength);
72
+ if (lengthPrefix.code !== SHA2_256_DIGEST_LENGTH || digest.length !== SHA2_256_DIGEST_LENGTH) throw new TypeError("Invalid SHA-256 digest length.");
73
+ return {
74
+ algorithm: "sha2-256",
75
+ digest
76
+ };
77
+ }
78
+ function extractDigestMultibase(value) {
79
+ const hashlink = typeof value === "string" ? value : value.href;
80
+ const match = /^hl:([^:\r\n\u2028\u2029]+)$/i.exec(hashlink);
81
+ if (match == null || match[0].length !== hashlink.length) throw new TypeError("Invalid simple hashlink.");
82
+ return match[1];
83
+ }
84
+ /**
85
+ * Parses a metadata-free `hl:` hashlink and validates its resource digest.
86
+ *
87
+ * @param value The simple hashlink to parse.
88
+ * @returns The `digestMultibase` value carried by the hashlink.
89
+ * @throws {TypeError} If the hashlink is malformed, contains metadata, or
90
+ * carries an invalid or unsupported digest.
91
+ * @since 2.4.0
92
+ */
93
+ function parseHashlink(value) {
94
+ const digestMultibase = extractDigestMultibase(value);
95
+ parseDigestMultibase(digestMultibase);
96
+ return { digestMultibase };
97
+ }
98
+ /**
99
+ * Creates a metadata-free `hl:` hashlink from a `digestMultibase` value.
100
+ *
101
+ * @param digestMultibase The SHA-256 multibase multihash to embed.
102
+ * @returns The simple hashlink.
103
+ * @throws {TypeError} If the digest is malformed or unsupported.
104
+ * @since 2.4.0
105
+ */
106
+ function createHashlink(digestMultibase) {
107
+ parseDigestMultibase(digestMultibase);
108
+ return `hl:${digestMultibase}`;
109
+ }
110
+ /**
111
+ * Verifies that bytes match a SHA-256 `digestMultibase` value.
112
+ *
113
+ * @param bytes The bytes to verify.
114
+ * @param digestMultibase The expected digest.
115
+ * @returns Whether the bytes match the digest.
116
+ * @throws {TypeError} If the digest is malformed or unsupported.
117
+ * @since 2.4.0
118
+ */
119
+ async function verifyDigestMultibase(bytes, digestMultibase) {
120
+ const expected = parseDigestMultibase(digestMultibase).digest;
121
+ const actual = new Uint8Array(await crypto.subtle.digest("SHA-256", toWebCryptoBytes(bytes)));
122
+ if (expected.length !== actual.length) return false;
123
+ let difference = 0;
124
+ for (let i = 0; i < expected.length; i++) difference |= expected[i] ^ actual[i];
125
+ return difference === 0;
126
+ }
127
+ /**
128
+ * Verifies that bytes match the resource digest in a simple hashlink.
129
+ *
130
+ * @param bytes The bytes to verify.
131
+ * @param hashlink The simple hashlink containing the expected digest.
132
+ * @returns Whether the bytes match the hashlink digest.
133
+ * @throws {TypeError} If the hashlink or digest is malformed or unsupported.
134
+ * @since 2.4.0
135
+ */
136
+ async function verifyHashlink(bytes, hashlink) {
137
+ return await verifyDigestMultibase(bytes, extractDigestMultibase(hashlink));
138
+ }
139
+ //#endregion
140
+ Object.defineProperty(exports, "computeDigestMultibase", {
141
+ enumerable: true,
142
+ get: function() {
143
+ return computeDigestMultibase;
144
+ }
145
+ });
146
+ Object.defineProperty(exports, "createHashlink", {
147
+ enumerable: true,
148
+ get: function() {
149
+ return createHashlink;
150
+ }
151
+ });
152
+ Object.defineProperty(exports, "parseDigestMultibase", {
153
+ enumerable: true,
154
+ get: function() {
155
+ return parseDigestMultibase;
156
+ }
157
+ });
158
+ Object.defineProperty(exports, "parseHashlink", {
159
+ enumerable: true,
160
+ get: function() {
161
+ return parseHashlink;
162
+ }
163
+ });
164
+ Object.defineProperty(exports, "verifyDigestMultibase", {
165
+ enumerable: true,
166
+ get: function() {
167
+ return verifyDigestMultibase;
168
+ }
169
+ });
170
+ Object.defineProperty(exports, "verifyHashlink", {
171
+ enumerable: true,
172
+ get: function() {
173
+ return verifyHashlink;
174
+ }
175
+ });
@@ -0,0 +1,103 @@
1
+ require("./chunk-C2EiDwsr.cjs");
2
+ const require_multicodec = require("./multicodec-CxGVGa91.cjs");
3
+ const require_multibase = require("./multibase-DcDQDNnI.cjs");
4
+ const require_digest = require("./digest-DLwUyHCK.cjs");
5
+ let node_test = require("node:test");
6
+ let node_assert_strict = require("node:assert/strict");
7
+ //#region src/digest.test.ts
8
+ const encoder = new TextEncoder();
9
+ const decoder = new TextDecoder();
10
+ const bytes = encoder.encode("Hello World!");
11
+ const digestMultibase = "zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e";
12
+ const hashlink = `hl:${digestMultibase}`;
13
+ (0, node_test.test)("computeDigestMultibase() computes a SHA-256 multihash", async () => {
14
+ (0, node_assert_strict.equal)(await require_digest.computeDigestMultibase(bytes), digestMultibase);
15
+ const parsed = require_digest.parseDigestMultibase(digestMultibase);
16
+ (0, node_assert_strict.equal)(parsed.algorithm, "sha2-256");
17
+ (0, node_assert_strict.deepStrictEqual)(parsed.digest, new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)));
18
+ });
19
+ (0, node_test.test)("digest helpers accept SharedArrayBuffer-backed bytes", async () => {
20
+ const sharedBytes = new Uint8Array(new SharedArrayBuffer(bytes.length));
21
+ sharedBytes.set(bytes);
22
+ Object.defineProperty(sharedBytes, "slice", { value: () => {
23
+ throw new Error("Shared input must not be copied with slice().");
24
+ } });
25
+ (0, node_assert_strict.equal)(await require_digest.computeDigestMultibase(sharedBytes), digestMultibase);
26
+ (0, node_assert_strict.equal)(await require_digest.verifyDigestMultibase(sharedBytes, digestMultibase), true);
27
+ });
28
+ (0, node_test.test)("digest helpers ignore spoofed shared buffer tags", async () => {
29
+ const taggedBytes = bytes.slice();
30
+ Object.defineProperty(taggedBytes.buffer, Symbol.toStringTag, { value: "SharedArrayBuffer" });
31
+ Object.defineProperty(taggedBytes, "slice", { value: () => {
32
+ throw new Error("ArrayBuffer input must not be copied.");
33
+ } });
34
+ (0, node_assert_strict.equal)(await require_digest.computeDigestMultibase(taggedBytes), digestMultibase);
35
+ (0, node_assert_strict.equal)(await require_digest.verifyDigestMultibase(taggedBytes, digestMultibase), true);
36
+ });
37
+ (0, node_test.test)("createHashlink() and parseHashlink() round-trip simple hashlinks", () => {
38
+ (0, node_assert_strict.equal)(require_digest.createHashlink(digestMultibase), hashlink);
39
+ (0, node_assert_strict.deepStrictEqual)(require_digest.parseHashlink(hashlink), { digestMultibase });
40
+ (0, node_assert_strict.deepStrictEqual)(require_digest.parseHashlink(new URL(hashlink)), { digestMultibase });
41
+ const base64DigestMultibase = decoder.decode(require_multibase.encodeMultibase("base64", require_multicodec.addMulticodecPrefix(18, require_multicodec.addMulticodecPrefix(32, new Uint8Array(32).fill(255)))));
42
+ const base64Hashlink = require_digest.createHashlink(base64DigestMultibase);
43
+ (0, node_assert_strict.equal)(base64Hashlink, "hl:mEiD//////////////////////////////////////////w");
44
+ (0, node_assert_strict.deepStrictEqual)(require_digest.parseHashlink(base64Hashlink), { digestMultibase: base64DigestMultibase });
45
+ (0, node_assert_strict.deepStrictEqual)(require_digest.parseHashlink(new URL(base64Hashlink)), { digestMultibase: base64DigestMultibase });
46
+ });
47
+ (0, node_test.test)("digest and hashlink verification accepts matching bytes", async () => {
48
+ (0, node_assert_strict.equal)(await require_digest.verifyDigestMultibase(bytes, digestMultibase), true);
49
+ (0, node_assert_strict.equal)(await require_digest.verifyHashlink(bytes, hashlink), true);
50
+ (0, node_assert_strict.equal)(await require_digest.verifyHashlink(bytes, new URL(hashlink)), true);
51
+ });
52
+ (0, node_test.test)("digest and hashlink verification rejects non-matching bytes", async () => {
53
+ const different = encoder.encode("Hello World?");
54
+ (0, node_assert_strict.equal)(await require_digest.verifyDigestMultibase(different, digestMultibase), false);
55
+ (0, node_assert_strict.equal)(await require_digest.verifyHashlink(different, hashlink), false);
56
+ });
57
+ (0, node_test.test)("parseDigestMultibase() rejects unsupported algorithms", () => {
58
+ const sha1Multihash = require_multicodec.addMulticodecPrefix(17, require_multicodec.addMulticodecPrefix(20, new Uint8Array(20)));
59
+ const value = decoder.decode(require_multibase.encodeMultibase("base58btc", sha1Multihash));
60
+ (0, node_assert_strict.throws)(() => require_digest.parseDigestMultibase(value), /* @__PURE__ */ new TypeError("Unsupported digest algorithm: 0x11"));
61
+ });
62
+ (0, node_test.test)("parseDigestMultibase() rejects malformed values", async () => {
63
+ (0, node_assert_strict.throws)(() => require_digest.parseDigestMultibase("not-multibase"), /* @__PURE__ */ new TypeError("Invalid digestMultibase encoding."));
64
+ const missingLength = decoder.decode(require_multibase.encodeMultibase("base58btc", Uint8Array.of(18)));
65
+ (0, node_assert_strict.throws)(() => require_digest.parseDigestMultibase(missingLength), /* @__PURE__ */ new TypeError("Invalid digestMultibase multihash."));
66
+ const multihash = require_multicodec.addMulticodecPrefix(18, require_multicodec.addMulticodecPrefix(32, new Uint8Array(32)));
67
+ const base2 = decoder.decode(require_multibase.encodeMultibase("base2", multihash));
68
+ (0, node_assert_strict.equal)(base2.length, 273);
69
+ (0, node_assert_strict.deepStrictEqual)(require_digest.parseDigestMultibase(base2).digest, new Uint8Array(32));
70
+ (0, node_assert_strict.throws)(() => require_digest.parseDigestMultibase(`${base2}0`), /* @__PURE__ */ new TypeError("Invalid digestMultibase encoding."));
71
+ const padded = decoder.decode(require_multibase.encodeMultibase("base64pad", multihash));
72
+ (0, node_assert_strict.equal)(padded.endsWith("=="), true);
73
+ (0, node_assert_strict.deepStrictEqual)(require_digest.parseDigestMultibase(padded).digest, new Uint8Array(32));
74
+ (0, node_assert_strict.throws)(() => require_digest.parseDigestMultibase(padded.slice(0, -1)), /* @__PURE__ */ new TypeError("Invalid digestMultibase encoding."));
75
+ (0, node_assert_strict.throws)(() => require_digest.createHashlink(`${padded}=`), /* @__PURE__ */ new TypeError("Invalid digestMultibase encoding."));
76
+ const overlongAlgorithm = decoder.decode(require_multibase.encodeMultibase("base58btc", Uint8Array.of(146, 0, 32, ...new Uint8Array(32))));
77
+ (0, node_assert_strict.throws)(() => require_digest.parseDigestMultibase(overlongAlgorithm), /* @__PURE__ */ new TypeError("Invalid digestMultibase multihash."));
78
+ const overlongLength = decoder.decode(require_multibase.encodeMultibase("base58btc", Uint8Array.of(18, 160, 0, ...new Uint8Array(32))));
79
+ (0, node_assert_strict.throws)(() => require_digest.parseDigestMultibase(overlongLength), /* @__PURE__ */ new TypeError("Invalid digestMultibase multihash."));
80
+ const shortDigest = decoder.decode(require_multibase.encodeMultibase("base58btc", require_multicodec.addMulticodecPrefix(18, require_multicodec.addMulticodecPrefix(31, new Uint8Array(31)))));
81
+ (0, node_assert_strict.throws)(() => require_digest.parseDigestMultibase(shortDigest), /* @__PURE__ */ new TypeError("Invalid SHA-256 digest length."));
82
+ await (0, node_assert_strict.rejects)(() => require_digest.verifyDigestMultibase(bytes, shortDigest), /* @__PURE__ */ new TypeError("Invalid SHA-256 digest length."));
83
+ });
84
+ (0, node_test.test)("simple hashlink helpers reject metadata and malformed forms", async () => {
85
+ for (const terminator of [
86
+ "\n",
87
+ "\r",
88
+ "\r\n",
89
+ "\u2028",
90
+ "\u2029"
91
+ ]) {
92
+ const malformedHashlink = `${hashlink}${terminator}`;
93
+ (0, node_assert_strict.throws)(() => require_digest.parseHashlink(malformedHashlink), /* @__PURE__ */ new TypeError("Invalid simple hashlink."));
94
+ await (0, node_assert_strict.rejects)(() => require_digest.verifyHashlink(bytes, malformedHashlink), /* @__PURE__ */ new TypeError("Invalid simple hashlink."));
95
+ }
96
+ (0, node_assert_strict.throws)(() => require_digest.parseHashlink(`${hashlink}:zmetadata`), /* @__PURE__ */ new TypeError("Invalid simple hashlink."));
97
+ await (0, node_assert_strict.rejects)(() => require_digest.verifyHashlink(bytes, `${hashlink}:zmetadata`), /* @__PURE__ */ new TypeError("Invalid simple hashlink."));
98
+ (0, node_assert_strict.throws)(() => require_digest.parseHashlink(`https://example.com/file?hl=${digestMultibase}`), /* @__PURE__ */ new TypeError("Invalid simple hashlink."));
99
+ (0, node_assert_strict.throws)(() => require_digest.parseHashlink("hl:not-multibase"), /* @__PURE__ */ new TypeError("Invalid digestMultibase encoding."));
100
+ await (0, node_assert_strict.rejects)(() => require_digest.verifyHashlink(bytes, "hl:not-multibase"), /* @__PURE__ */ new TypeError("Invalid digestMultibase encoding."));
101
+ (0, node_assert_strict.throws)(() => require_digest.createHashlink("not-multibase"), /* @__PURE__ */ new TypeError("Invalid digestMultibase encoding."));
102
+ });
103
+ //#endregion
@@ -0,0 +1 @@
1
+ export { };