@fedify/vocab-runtime 2.4.5-dev.2453 → 2.4.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 (120) hide show
  1. package/deno.json +5 -2
  2. package/dist/{docloader-BMHljyYb.d.cts → docloader-DOWjnNCz.d.cts} +4 -8
  3. package/dist/{docloader-BMHljyYb.d.ts → docloader-DOWjnNCz.d.ts} +4 -8
  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/internal/portable-dereference.cjs +1 -1
  9. package/dist/internal/portable-dereference.d.cts +2 -2
  10. package/dist/internal/portable-dereference.d.ts +2 -2
  11. package/dist/internal/portable-dereference.js +1 -1
  12. package/dist/internal.cjs +6 -0
  13. package/dist/internal.d.cts +108 -0
  14. package/dist/internal.d.ts +108 -0
  15. package/dist/internal.js +3 -0
  16. package/dist/mod.cjs +104 -38
  17. package/dist/mod.d.cts +3 -336
  18. package/dist/mod.d.ts +4 -336
  19. package/dist/mod.js +104 -38
  20. package/dist/{portable-6Aj-7dZJ.d.cts → portable-CTnZWX7J.d.cts} +1 -1
  21. package/dist/{portable-nkTiqH0w.d.ts → portable-Cq-rhQgY.d.ts} +1 -1
  22. package/dist/public-fetch-Dvfvawei.cjs +527 -0
  23. package/dist/public-fetch-ziZ39L5I.js +510 -0
  24. package/dist/tests/{body-7TFr1l-z.cjs → body-BO79kuv1.cjs} +1 -1
  25. package/dist/tests/{body-BgPyhKpw.mjs → body-CUzCsDZ4.mjs} +1 -1
  26. package/dist/tests/body.test.cjs +1 -1
  27. package/dist/tests/body.test.mjs +1 -1
  28. package/dist/tests/decimal.test.cjs +3 -3
  29. package/dist/tests/decimal.test.mjs +3 -3
  30. package/dist/tests/{docloader-D89fiHaF.cjs → docloader-B79_YgeY.cjs} +111 -39
  31. package/dist/tests/{docloader-BlukyWHf.mjs → docloader-C5ZHGEfO.mjs} +106 -40
  32. package/dist/tests/docloader.test.cjs +531 -1293
  33. package/dist/tests/docloader.test.mjs +472 -1252
  34. package/dist/tests/esm-Ds-eHTfY.mjs +1229 -0
  35. package/dist/tests/esm-Q2tdh5f3.cjs +1215 -0
  36. package/dist/tests/internal/portable-dereference.test.cjs +3 -3
  37. package/dist/tests/internal/portable-dereference.test.mjs +3 -3
  38. package/dist/tests/{jsonld-cache-BEXiiM-V.cjs → jsonld-cache-DKIWKxEX.cjs} +1 -1
  39. package/dist/tests/{jsonld-cache-DFnWsLuZ.mjs → jsonld-cache-KH40z9_7.mjs} +1 -1
  40. package/dist/tests/jsonld-cache.test.cjs +2 -2
  41. package/dist/tests/jsonld-cache.test.mjs +2 -2
  42. package/dist/tests/node-BvS3AhZ2.cjs +89 -0
  43. package/dist/tests/node-Cg4pRuYy.mjs +84 -0
  44. package/dist/tests/pin-grant-CPrpk1l4.cjs +70 -0
  45. package/dist/tests/pin-grant-H5XhM1Xs.mjs +53 -0
  46. package/dist/tests/pin-grant.test.cjs +87 -0
  47. package/dist/tests/pin-grant.test.d.cts +1 -0
  48. package/dist/tests/pin-grant.test.d.mts +1 -0
  49. package/dist/tests/pin-grant.test.mjs +88 -0
  50. package/dist/tests/pinners/bun.test.cjs +166 -0
  51. package/dist/tests/pinners/bun.test.d.cts +1 -0
  52. package/dist/tests/pinners/bun.test.d.mts +1 -0
  53. package/dist/tests/pinners/bun.test.mjs +167 -0
  54. package/dist/tests/pinners/node.test.cjs +269 -0
  55. package/dist/tests/pinners/node.test.d.cts +1 -0
  56. package/dist/tests/pinners/node.test.d.mts +1 -0
  57. package/dist/tests/pinners/node.test.mjs +268 -0
  58. package/dist/tests/pinners/pinners.test.cjs +196 -0
  59. package/dist/tests/pinners/pinners.test.d.cts +1 -0
  60. package/dist/tests/pinners/pinners.test.d.mts +1 -0
  61. package/dist/tests/pinners/pinners.test.mjs +197 -0
  62. package/dist/tests/pinners/testdata/node-child.cjs +135 -0
  63. package/dist/tests/pinners/testdata/node-child.d.cts +1 -0
  64. package/dist/tests/pinners/testdata/node-child.d.mts +1 -0
  65. package/dist/tests/pinners/testdata/node-child.mjs +134 -0
  66. package/dist/tests/pinners-CJi9ZkYs.cjs +220 -0
  67. package/dist/tests/pinners-zSofbiy2.mjs +203 -0
  68. package/dist/tests/{portable-dereference-DmTYDy2p.mjs → portable-dereference-BHRE5Pkm.mjs} +2 -2
  69. package/dist/tests/{portable-dereference-qTLMqe2_.cjs → portable-dereference-mRHrC7PI.cjs} +2 -2
  70. package/dist/tests/{portable-media-4hfHamVH.mjs → portable-media-BpVpe1gj.mjs} +3 -3
  71. package/dist/tests/{portable-media-DH6pp6-g.cjs → portable-media-C3yY0W5E.cjs} +3 -3
  72. package/dist/tests/portable-media.test.cjs +1 -1
  73. package/dist/tests/portable-media.test.mjs +1 -1
  74. package/dist/tests/portable-workers.test.cjs +2 -2
  75. package/dist/tests/portable-workers.test.mjs +2 -2
  76. package/dist/tests/public-fetch-DNkPoks1.mjs +170 -0
  77. package/dist/tests/public-fetch-U822jbKB.cjs +186 -0
  78. package/dist/tests/public-fetch.test.cjs +1073 -0
  79. package/dist/tests/public-fetch.test.d.cts +1 -0
  80. package/dist/tests/public-fetch.test.d.mts +1 -0
  81. package/dist/tests/public-fetch.test.mjs +1074 -0
  82. package/dist/tests/{request--AMbrM1o.mjs → request-3AP-HBJ6.mjs} +1 -1
  83. package/dist/tests/{request-RH-Q21hI.cjs → request-BhopJTZd.cjs} +1 -1
  84. package/dist/tests/request.test.cjs +1 -1
  85. package/dist/tests/request.test.mjs +1 -1
  86. package/dist/tests/support-BmzI3dRJ.cjs +363 -0
  87. package/dist/tests/support-CiHDVJgh.mjs +272 -0
  88. package/dist/tests/{url-BNakuZ8k.cjs → url-Bdj-rTcC.cjs} +88 -12
  89. package/dist/tests/{url-DMxmp7ZG.mjs → url-COj5Q8mb.mjs} +65 -13
  90. package/dist/tests/url.test.cjs +4 -1
  91. package/dist/tests/url.test.mjs +4 -1
  92. package/dist/url-4EE56sNP.d.cts +352 -0
  93. package/dist/url-4EE56sNP.d.ts +352 -0
  94. package/dist/{url-DrGTR8yv.cjs → url-DIKZnaJv.cjs} +88 -12
  95. package/dist/{url-Dzyp-NsC.js → url-mWveR5GA.js} +65 -13
  96. package/package.json +14 -3
  97. package/src/docloader.test.ts +696 -48
  98. package/src/docloader.ts +186 -75
  99. package/src/internal.ts +13 -0
  100. package/src/pin-grant.test.ts +118 -0
  101. package/src/pin-grant.ts +99 -0
  102. package/src/pinners/bun.test.ts +278 -0
  103. package/src/pinners/bun.ts +223 -0
  104. package/src/pinners/mod.ts +59 -0
  105. package/src/pinners/node.test.ts +415 -0
  106. package/src/pinners/node.ts +144 -0
  107. package/src/pinners/pinners.test.ts +316 -0
  108. package/src/pinners/testdata/node-child.ts +147 -0
  109. package/src/pinners/testdata/proxy-child.ts +82 -0
  110. package/src/pinners/testdata/support.ts +343 -0
  111. package/src/pinners/testdata/tls-cert.pem +12 -0
  112. package/src/pinners/testdata/tls-child.ts +88 -0
  113. package/src/pinners/testdata/tls-key.pem +5 -0
  114. package/src/pinners/types.ts +48 -0
  115. package/src/public-fetch.test.ts +1286 -0
  116. package/src/public-fetch.ts +338 -0
  117. package/src/testdata/public-fetch-deno-worker.ts +74 -0
  118. package/src/url.test.ts +11 -0
  119. package/src/url.ts +113 -22
  120. package/tsdown.config.ts +7 -2
package/dist/mod.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { _ as logRequest, a as DocumentLoaderOptions, c as getDocumentLoader, d as withDocumentLoaderTimeout, f as CreateRequestOptions, g as getUserAgent, h as createActivityPubRequest, i as DocumentLoaderFactoryOptions, l as getRemoteDocument, m as GetUserAgentOptions, n as DocumentLoader, o as GetDocumentLoaderOptions, p as FetchError, r as DocumentLoaderFactory, s as RemoteDocument, t as AuthenticatedDocumentLoaderFactory, u as resolveDocumentLoaderTimeout } from "./docloader-BMHljyYb.js";
3
- import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-nkTiqH0w.js";
2
+ import { _ as parseJsonLdId, a as expandIPv6Address, b as withGatewayHints, c as getFe34Origin, d as haveSameIriOrigin, f as isGatewayUrl, g as parseIri, h as parseGatewayUrl, i as canonicalizePortableUri, l as getGatewayHints, m as isValidPublicIPv6Address, n as UrlError, o as formatIri, p as isValidPublicIPv4Address, r as arePortableUrisEqual, s as fromCompatibleEf61Id, u as haveSameFe34Origin, v as toCompatibleEf61Id, x as withoutGatewayHints, y as validatePublicUrl } from "./url-4EE56sNP.js";
3
+ import { _ as logRequest, a as DocumentLoaderOptions, c as getDocumentLoader, d as withDocumentLoaderTimeout, f as CreateRequestOptions, g as getUserAgent, h as createActivityPubRequest, i as DocumentLoaderFactoryOptions, l as getRemoteDocument, m as GetUserAgentOptions, n as DocumentLoader, o as GetDocumentLoaderOptions, p as FetchError, r as DocumentLoaderFactory, s as RemoteDocument, t as AuthenticatedDocumentLoaderFactory, u as resolveDocumentLoaderTimeout } from "./docloader-DOWjnNCz.js";
4
+ import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-Cq-rhQgY.js";
4
5
  import { TracerProvider } from "@opentelemetry/api";
6
+
5
7
  //#region src/contexts.d.ts
6
8
  declare const preloadedContexts: Record<string, unknown>;
7
9
  //#endregion
@@ -473,338 +475,4 @@ interface PropertyPreprocessorContext {
473
475
  */
474
476
  type PropertyPreprocessor<T = unknown> = (value: Json, context: PropertyPreprocessorContext) => T | undefined | Error | Promise<T | undefined | Error>;
475
477
  //#endregion
476
- //#region src/url.d.ts
477
- declare class UrlError extends Error {
478
- /**
479
- * The failure category. Defaults to `"disallowed"`.
480
- * `"dns"` includes lookup errors and results with no usable IP addresses.
481
- * @since 2.0.29
482
- */
483
- readonly reason: "dns" | "disallowed";
484
- constructor(message: string, options?: {
485
- cause?: unknown;
486
- reason?: "dns" | "disallowed";
487
- });
488
- }
489
- /**
490
- * Parses a JSON-LD `@id` value as an IRI, including FEP-ef61 portable
491
- * ActivityPub IRIs. See {@link parseIri} for how IRIs are parsed.
492
- * @param id The `@id` value.
493
- * @param base The base IRI to resolve a relative `@id` against.
494
- * @returns The parsed IRI, or `undefined` if `id` is missing or a blank node
495
- * identifier.
496
- * @throws {TypeError} If `id` is not a valid IRI.
497
- * @since 2.4.0
498
- */
499
- declare function parseJsonLdId(id: string | undefined, base?: string | URL): URL | undefined;
500
- /**
501
- * Parses an IRI as a URL, including FEP-ef61 portable ActivityPub IRIs.
502
- * Portable URI and FEP-ef61 compatible identifier strings whose path contains
503
- * a `.` or `..` segment, including percent-encoded spellings, throw a
504
- * `TypeError`: JavaScript `URL` would otherwise identify a different object.
505
- * This also applies to compatible identifier strings used as relative bases.
506
- * A `URL` argument may already have lost such segments before this function
507
- * receives it.
508
- *
509
- * Portable IRIs, e.g., `ap://did:key:z6Mk.../actor`, cannot be represented by
510
- * JavaScript `URL` as they are, so the returned `URL` keeps the DID authority
511
- * percent-encoded, e.g., `ap+ef61://did%3Akey%3Az6Mk.../actor`. Use
512
- * {@link formatIri} to get the canonical string back.
513
- * @param iri The IRI to parse.
514
- * @param base The base IRI to resolve a relative IRI against.
515
- * @returns The parsed IRI.
516
- * @throws {TypeError} If the IRI is malformed.
517
- * @since 2.4.0
518
- */
519
- declare function parseIri(iri: string | URL, base?: string | URL): URL;
520
- /**
521
- * Formats a URL as an IRI, including FEP-ef61 portable ActivityPub IRIs.
522
- * Portable URI and FEP-ef61 compatible identifier strings with dot segments
523
- * throw a `TypeError` because their paths cannot be represented by JavaScript
524
- * `URL` without normalization.
525
- *
526
- * Portable IRIs are formatted with the `ap+ef61:` scheme and a decoded DID
527
- * authority, even if they were parsed from `ap:` IRIs. FEP-ef61 currently
528
- * recommends the `ap:` scheme, so a future major version of Fedify may change
529
- * the scheme this function produces. Do not compare its results as strings to
530
- * tell whether two portable IRIs identify the same object; use
531
- * `arePortableUrisEqual()` instead.
532
- * @param iri The IRI to format.
533
- * @returns The formatted IRI. A string that cannot be parsed as a URL at all
534
- * is returned unchanged.
535
- * @throws {TypeError} If the IRI is a malformed portable IRI or FEP-ef61
536
- * compatible identifier, e.g., one with dot segments.
537
- * @since 2.4.0
538
- */
539
- declare function formatIri(iri: string | URL): string;
540
- /**
541
- * Canonicalizes a FEP-ef61 portable ActivityPub URI for comparison.
542
- *
543
- * This accepts both `ap:` and `ap+ef61:` URI strings with decoded or
544
- * percent-encoded DID authorities. The returned value uses the `ap+ef61:`
545
- * scheme, a decoded DID authority, and no query component. Pass the raw URI
546
- * string, not a `URL` object, because JavaScript `URL` normalizes opaque path
547
- * segments before Fedify can compare them.
548
- *
549
- * The `ap+ef61:` scheme of the result is a choice of Fedify's, whereas
550
- * FEP-ef61 currently canonicalizes portable URIs with the `ap:` scheme.
551
- * A future major version of Fedify may change the scheme of the result, so
552
- * do not persist the result as the only copy of an identifier; keep the
553
- * original URI, and canonicalize it again when comparing.
554
- *
555
- * @param input The raw portable ActivityPub URI string to canonicalize.
556
- * @returns The canonical portable ActivityPub URI string.
557
- * @throws {TypeError} If the input is not a valid portable ActivityPub IRI.
558
- * @since 2.4.0
559
- */
560
- declare function canonicalizePortableUri(input: string): string;
561
- /**
562
- * Checks whether two FEP-ef61 portable ActivityPub URIs identify the same
563
- * portable object.
564
- *
565
- * Non-string inputs return `false`. Non-portable URI strings use strict string
566
- * equality. Portable URI strings are compared through
567
- * {@link canonicalizePortableUri}; malformed portable URI strings return
568
- * `false` unless they are exactly equal.
569
- *
570
- * @since 2.4.0
571
- */
572
- declare function arePortableUrisEqual(left: string, right: string): boolean;
573
- /**
574
- * Computes an IRI's FEP-fe34 origin.
575
- *
576
- * HTTP(S) IRIs use their web origin. FEP-ef61 portable ActivityPub IRIs and
577
- * DID URLs use their DID as a cryptographic origin.
578
- *
579
- * @throws {TypeError} If the IRI does not have a supported FEP-fe34 origin.
580
- * @since 2.4.0
581
- */
582
- declare function getFe34Origin(input: string | URL): string;
583
- /**
584
- * Checks whether two IRIs have the same FEP-fe34 origin.
585
- *
586
- * Malformed or unsupported IRIs are treated as non-matching.
587
- *
588
- * @since 2.4.0
589
- */
590
- declare function haveSameFe34Origin(left: string | URL, right: string | URL): boolean;
591
- /**
592
- * Checks whether two IRIs have the same origin. Unlike comparing
593
- * `URL.origin`, which is `"null"` for URLs with non-special schemes, this
594
- * compares FEP-ef61 portable ActivityPub IRIs by their schemes and DIDs, so
595
- * that `ap:` and `ap+ef61:` IRIs of the same DID with decoded or
596
- * percent-encoded authorities have the same origin. Other IRIs with
597
- * a non-special scheme and a host are compared by their schemes and hosts.
598
- *
599
- * This is not an FEP-fe34 origin check: an `ap+ef61:` IRI and the `did:key`
600
- * verification method of the same DID have different origins here. Use
601
- * {@link haveSameFe34Origin} for that.
602
- * @param left The first IRI.
603
- * @param right The second IRI.
604
- * @returns `true` if the IRIs have the same origin.
605
- * @since 2.4.0
606
- */
607
- declare function haveSameIriOrigin(left: URL, right: URL): boolean;
608
- /**
609
- * Checks whether the URL is an FEP-ef61 gateway base URI, i.e., an HTTP(S)
610
- * URI with no credentials, path, query, or fragment, such as
611
- * `https://example.com/`. FEP-ef61 requires every item of a portable actor's
612
- * `gateways` to be such a URI. A URI with an empty query or fragment
613
- * delimiter, such as `https://example.com/?`, is not a gateway base URI.
614
- *
615
- * Note that the `URL` class normalizes `https://example.com` to
616
- * `https://example.com/`, so both are gateway base URIs.
617
- * @param url The URL to check.
618
- * @returns `true` if the URL is an FEP-ef61 gateway base URI.
619
- * @since 2.4.0
620
- */
621
- declare function isGatewayUrl(url: URL): boolean;
622
- /**
623
- * Parses and validates an FEP-ef61 gateway base URI, i.e., an HTTP(S) URI with
624
- * no credentials, path, query, or fragment, such as `https://example.com`.
625
- * See {@link isGatewayUrl} for the rules.
626
- * @param url The gateway base URI to parse.
627
- * @returns The parsed gateway base URI, whose path is `/`.
628
- * @throws {TypeError} If the URI is malformed, or is not an HTTP(S) base URI
629
- * with no credentials, path, query, or fragment. In the
630
- * latter case, the message starts with
631
- * `Invalid FEP-ef61 gateway:`.
632
- * @since 2.4.0
633
- */
634
- declare function parseGatewayUrl(url: string): URL;
635
- /**
636
- * Converts an [FEP-ef61] compatible identifier into a portable ActivityPub
637
- * URI.
638
- *
639
- * A compatible identifier is an HTTP(S) URL under a gateway's fixed
640
- * `/.well-known/apgateway/` path, such as
641
- * `https://server.example/.well-known/apgateway/did:key:z6Mk.../objects/1`.
642
- * This function removes the gateway part and returns the corresponding
643
- * portable URI, e.g., `ap+ef61://did:key:z6Mk.../objects/1`, in the same
644
- * internal `URL` form that {@link parseIri} produces. The path, query, and
645
- * fragment are preserved, so the result is a portable URI, not a comparison
646
- * form; pass its `href` to {@link canonicalizePortableUri} or
647
- * {@link arePortableUrisEqual} to compare it with other portable URIs.
648
- *
649
- * The conversion only reveals the *claimed* portable identifier. Anyone can
650
- * publish a compatible identifier for any DID on their own server, so the
651
- * gateway that served it is neither the object's origin nor authorized to act
652
- * for the DID. Callers must still verify the retrieved document's Object
653
- * Integrity Proof against the DID, as FEP-ef61 requires, and should keep the
654
- * original URL if they need the gateway as a retrieval hint.
655
- *
656
- * Arbitrary gateway paths are not supported.
657
- *
658
- * [FEP-ef61]: https://w3id.org/fep/ef61
659
- *
660
- * @param input The URL to convert.
661
- * @returns The portable ActivityPub URI, or `null` if the input is not an
662
- * HTTP(S) URL whose path starts with `/.well-known/apgateway/did:`.
663
- * Other gateway routes, such as the gateway discovery endpoint and
664
- * hashlink media URLs, also yield `null`.
665
- * @throws {TypeError} If the input looks like a compatible identifier but is
666
- * malformed, e.g., it has an invalid DID, no object path,
667
- * invalid percent-encoding, credentials, or location
668
- * hints (`@gateway` query parameters, or the legacy
669
- * `gateways` parameter), or its raw string path would
670
- * change during URL parsing. Already-parsed `URL`
671
- * arguments cannot reveal segments lost by their parser.
672
- * @since 2.4.0
673
- */
674
- declare function fromCompatibleEf61Id(input: string | URL): URL | null;
675
- /**
676
- * Converts a portable ActivityPub URI into an [FEP-ef61] compatible
677
- * identifier, which is an HTTP(S) URL under the gateway's fixed
678
- * `/.well-known/apgateway/` path.
679
- *
680
- * For example, `ap+ef61://did:key:z6Mk.../objects/1` and
681
- * `https://server.example` yield
682
- * `https://server.example/.well-known/apgateway/did:key:z6Mk.../objects/1`.
683
- * Both `ap:` and `ap+ef61:` URIs with decoded or percent-encoded DID
684
- * authorities are accepted. Publishers should use the first gateway in the
685
- * actor's `gateways` list, as FEP-ef61 requires.
686
- *
687
- * The path and fragment are preserved, with characters that are not allowed
688
- * in HTTP(S) URLs percent-encoded the same way as
689
- * {@link canonicalizePortableUri} does. FEP-ef61 location hints (`@gateway`
690
- * query parameters, and the legacy `gateways` parameter) are removed, since
691
- * compatible identifiers must not have them; other query parameters are kept
692
- * in order.
693
- *
694
- * Arbitrary gateway paths are not supported.
695
- *
696
- * [FEP-ef61]: https://w3id.org/fep/ef61
697
- *
698
- * @param portableId The `ap:` or `ap+ef61:` URI to convert.
699
- * @param gateway The gateway's HTTP(S) origin, e.g., `https://server.example`.
700
- * @returns The compatible identifier.
701
- * @throws {TypeError} If the portable ID is not a valid `ap:` or `ap+ef61:`
702
- * URI, if its path has `.` or `..` segments (which
703
- * HTTP(S) URLs cannot represent without changing the
704
- * identified object), or if the gateway is
705
- * not an HTTP(S) origin with no credentials, path, query,
706
- * or fragment.
707
- * @since 2.4.0
708
- */
709
- declare function toCompatibleEf61Id(portableId: string | URL, gateway: string | URL): URL;
710
- /**
711
- * Returns a copy of an [FEP-ef61] portable ActivityPub URI with `@gateway`
712
- * location hints for the given gateways, which tell consumers where they can
713
- * retrieve the object. Put hints on *references* to portable actors, e.g.,
714
- * in `actor`, `attributedTo`, `to`, or `cc`, when constructing an object, as
715
- * FEP-ef61 recommends:
716
- *
717
- * ~~~~ typescript
718
- * withGatewayHints("ap://did:key:z6Mk.../actor", [
719
- * "https://server1.example",
720
- * "https://server2.example",
721
- * ]);
722
- * // ap+ef61://did:key:z6Mk.../actor?@gateway=https%3A%2F%2Fserver1.example&@gateway=https%3A%2F%2Fserver2.example
723
- * ~~~~
724
- *
725
- * Do not put hints on an object's own `id`. Hints do not change the
726
- * identity of a portable URI, since FEP-ef61 drops the query when comparing
727
- * portable URIs, but implementations that do not canonicalize portable URIs
728
- * would take a hinted ID for another object. Add hints before signing the
729
- * object, since its Object Integrity Proof covers its references too.
730
- *
731
- * The hints that the URI already has, including the legacy `gateways`
732
- * parameter, are replaced. Each gateway becomes a `@gateway` query
733
- * parameter whose value is its URI-encoded origin, e.g.,
734
- * `@gateway=https%3A%2F%2Fserver1.example`, in the given order after the
735
- * other query parameters. Duplicate gateways are dropped, and an empty list
736
- * removes the hints as {@link withoutGatewayHints} does. The other query
737
- * parameters, their order, and the fragment are kept, but percent-encoding
738
- * is normalized the same way as {@link canonicalizePortableUri} does it.
739
- *
740
- * Fedify follows at most five hints when dereferencing a portable URI
741
- * (three for the key ID of an HTTP Signature), so list the preferred
742
- * gateways first; there is no limit on the number of hints added here.
743
- *
744
- * [FEP-ef61]: https://w3id.org/fep/ef61
745
- *
746
- * @param portableId The `ap:` or `ap+ef61:` URI. Pass the raw string rather
747
- * than a `URL` if its path may have `.` or `..` segments,
748
- * because the `URL` class resolves them.
749
- * @param gateways The gateways, e.g., the `gateways` of the actor that the
750
- * URI refers to. Each has to be an HTTP(S) origin with no
751
- * credentials, path, query, or fragment.
752
- * @returns The portable URI with the hints, in the same internal `URL` form
753
- * as {@link parseIri} returns. Use {@link formatIri} to get its
754
- * canonical string.
755
- * @throws {TypeError} If the portable ID is not a valid `ap:` or `ap+ef61:`
756
- * URI, e.g., it is a compatible identifier, which must
757
- * not have location hints; if its path has `.` or `..`
758
- * segments, which the `URL` class cannot represent; or
759
- * if a gateway is invalid.
760
- * @since 2.4.0
761
- */
762
- declare function withGatewayHints(portableId: string | URL, gateways: Iterable<string | URL>): URL;
763
- /**
764
- * Returns a copy of an [FEP-ef61] portable ActivityPub URI without its
765
- * location hints, i.e., `@gateway` query parameters and the legacy
766
- * `gateways` parameter. The other query parameters, their order, and the
767
- * fragment are kept, but percent-encoding is normalized the same way as
768
- * {@link canonicalizePortableUri} does it.
769
- *
770
- * [FEP-ef61]: https://w3id.org/fep/ef61
771
- *
772
- * @param portableId The `ap:` or `ap+ef61:` URI. Pass the raw string rather
773
- * than a `URL` if its path may have `.` or `..` segments,
774
- * because the `URL` class resolves them.
775
- * @returns The portable URI without the hints, in the same internal `URL`
776
- * form as {@link parseIri} returns.
777
- * @throws {TypeError} If the portable ID is not a valid `ap:` or `ap+ef61:`
778
- * URI, or if its path has `.` or `..` segments, which
779
- * the `URL` class cannot represent.
780
- * @since 2.4.0
781
- */
782
- declare function withoutGatewayHints(portableId: string | URL): URL;
783
- /**
784
- * Gets the gateways in the `@gateway` location hints of an [FEP-ef61]
785
- * portable ActivityPub URI, in order. Hints that are not valid gateways,
786
- * i.e., HTTP(S) origins with no credentials, path, query, or fragment, are
787
- * skipped, and so are duplicates. The legacy `gateways` parameter is not
788
- * read.
789
- *
790
- * Unlike Fedify's dereferencing, which follows at most five hints, this
791
- * returns all of them.
792
- *
793
- * [FEP-ef61]: https://w3id.org/fep/ef61
794
- *
795
- * @param portableId The `ap:` or `ap+ef61:` URI.
796
- * @returns The gateways, e.g., `https://server1.example/`.
797
- * @throws {TypeError} If the portable ID is not a valid `ap:` or `ap+ef61:`
798
- * URI.
799
- * @since 2.4.0
800
- */
801
- declare function getGatewayHints(portableId: string | URL): URL[];
802
- /**
803
- * Validates a URL to prevent SSRF attacks.
804
- */
805
- declare function validatePublicUrl(url: string): Promise<void>;
806
- declare function isValidPublicIPv4Address(address: string): boolean;
807
- declare function isValidPublicIPv6Address(address: string): boolean;
808
- declare function expandIPv6Address(address: string): string;
809
- //#endregion
810
478
  export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type FetchPortableMediaOptions, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type ParsedDigestMultibase, type ParsedHashlink, type PortableMedia, type PortableObjectReferrer, type PortableObjectVerification, type PortableObjectVerifier, type PortableObjectVerifierOptions, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, computeDigestMultibase, createActivityPubRequest, createHashlink, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, fetchPortableMedia, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, getGatewayHints, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isGatewayUrl, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, normalizeLanguageTag, parseDecimal, parseDidKeyVerificationMethod, parseDigestMultibase, parseGatewayUrl, parseHashlink, parseIri, parseJsonLdId, preloadedContexts, resolveDocumentLoaderTimeout, toCompatibleEf61Id, validatePublicUrl, verifyDigestMultibase, verifyHashlink, withDocumentLoaderTimeout, withGatewayHints, withoutGatewayHints };
package/dist/mod.js CHANGED
@@ -1,6 +1,7 @@
1
1
 
2
2
  import { t as preloadedContexts } from "./contexts-DWrj4AGR.js";
3
- import { S as withoutGatewayHints, _ as parseIri, a as expandIPv6Address, b as validatePublicUrl, c as getFe34Origin, d as haveSameIriOrigin, f as isGatewayUrl, g as parseGatewayUrl, i as canonicalizePortableUri, l as getGatewayHints, m as isValidPublicIPv6Address, n as UrlError, o as formatIri, p as isValidPublicIPv4Address, r as arePortableUrisEqual, s as fromCompatibleEf61Id, u as haveSameFe34Origin, v as parseJsonLdId, x as withGatewayHints, y as toCompatibleEf61Id } from "./url-Dzyp-NsC.js";
3
+ import { n as openPublicFetch, t as openDirectFetch } from "./public-fetch-ziZ39L5I.js";
4
+ import { C as toCompatibleEf61Id, E as withoutGatewayHints, T as withGatewayHints, a as canonicalizePortableUri, b as parseIri, c as fromCompatibleEf61Id, d as haveSameFe34Origin, f as haveSameIriOrigin, h as isValidPublicIPv6Address, l as getFe34Origin, m as isValidPublicIPv4Address, n as UrlError, o as expandIPv6Address, p as isGatewayUrl, r as arePortableUrisEqual, s as formatIri, u as getGatewayHints, w as validatePublicUrl, x as parseJsonLdId, y as parseGatewayUrl } from "./url-mWveR5GA.js";
4
5
  import { getLogger } from "@logtape/logtape";
5
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
6
7
  import process from "node:process";
@@ -12,7 +13,7 @@ import { PublicKeyInfo } from "pkijs";
12
13
  import baseX from "@multiformats/base-x";
13
14
  //#region deno.json
14
15
  var name = "@fedify/vocab-runtime";
15
- var version = "2.4.5-dev.2453+1304e862";
16
+ var version = "2.4.5";
16
17
  //#endregion
17
18
  //#region src/request.ts
18
19
  /**
@@ -437,7 +438,7 @@ async function bufferErrorResponse(response, url, signal) {
437
438
  try {
438
439
  await readBoundedBytes(response, MAX_ERROR_BODY_SIZE, url);
439
440
  } catch (error) {
440
- await clone.body?.cancel().catch(() => {});
441
+ clone.body?.cancel().catch(() => {});
441
442
  if (signal?.aborted) throw error;
442
443
  logger.debug("Failed to read the error response body from {url}: {error}", {
443
444
  url,
@@ -469,14 +470,48 @@ async function bufferErrorResponse(response, url, signal) {
469
470
  * Gets a {@link RemoteDocument} from the given response.
470
471
  * @param url The URL of the document to load.
471
472
  * @param response The response to get the document from.
472
- * @param fetch The function to fetch the document.
473
+ * @param fetch The function to fetch the document. It is called with the URL
474
+ * of an alternate document, if any, after the response body has
475
+ * been either read or its cancellation has been started.
473
476
  * @param options The options for loading the document.
474
477
  * @returns The loaded remote document.
475
478
  * @throws {FetchError} If the response is not OK.
476
479
  * @internal
477
480
  */
478
481
  async function getRemoteDocument(url, response, fetch, options) {
479
- const documentUrl = response.url === "" ? url : response.url;
482
+ const result = await parseRemoteDocument(url, response, options);
483
+ if ("alternate" in result) return await fetch(result.alternate, options);
484
+ return result.document;
485
+ }
486
+ /**
487
+ * Starts cancelling the body of a response without waiting for it to settle.
488
+ * The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
489
+ * settles only after the other branch is cancelled too, per the Streams
490
+ * standard, so awaiting it could block forever. Failures are only logged.
491
+ * @param response The response whose body is cancelled.
492
+ * @param url The URL of the response, for logging.
493
+ */
494
+ function cancelResponseBody(response, url) {
495
+ response.body?.cancel().catch((error) => {
496
+ logger.debug("Failed to cancel the response body from {url}: {error}", {
497
+ url,
498
+ error
499
+ });
500
+ });
501
+ }
502
+ /**
503
+ * Gets a {@link RemoteDocument} from the given response, or the URL of an
504
+ * alternate document to load instead. When an alternate document is found,
505
+ * the response body has been either read or its cancellation has been started
506
+ * (without waiting for it), so that the alternate document can be loaded after
507
+ * the response's session is closed.
508
+ * @param url The URL of the document to load.
509
+ * @param response The response to get the document from.
510
+ * @returns The loaded remote document, or the alternate document URL.
511
+ * @throws {FetchError} If the response is not OK.
512
+ */
513
+ async function parseRemoteDocument(url, response, options) {
514
+ const documentUrl = url;
480
515
  const docUrl = new URL(documentUrl);
481
516
  if (!response.ok) {
482
517
  if (options?.suppressError) logger.warn("Failed to fetch document: {status} {url} {headers}", {
@@ -518,7 +553,8 @@ async function getRemoteDocument(url, response, fetch, options) {
518
553
  alternateUrl: altUri.href,
519
554
  url: documentUrl
520
555
  });
521
- return await fetch(altUri.href, options);
556
+ cancelResponseBody(response, documentUrl);
557
+ return { alternate: altUri.href };
522
558
  }
523
559
  }
524
560
  }
@@ -554,7 +590,7 @@ async function getRemoteDocument(url, response, fetch, options) {
554
590
  alternateUrl: attribs.href,
555
591
  url: documentUrl
556
592
  });
557
- return await fetch(new URL(attribs.href, docUrl).href, options);
593
+ return { alternate: new URL(attribs.href, docUrl).href };
558
594
  }
559
595
  }
560
596
  try {
@@ -569,11 +605,11 @@ async function getRemoteDocument(url, response, fetch, options) {
569
605
  url: documentUrl,
570
606
  headers: Object.fromEntries(response.headers.entries())
571
607
  });
572
- return {
608
+ return { document: {
573
609
  contextUrl,
574
610
  document,
575
611
  documentUrl
576
- };
612
+ } };
577
613
  }
578
614
  /**
579
615
  * Resolves {@link DocumentLoaderFactoryOptions.timeout} into milliseconds.
@@ -678,10 +714,59 @@ function withDocumentLoaderTimeout(loader, timeout) {
678
714
  * @returns The document loader.
679
715
  * @since 1.3.0
680
716
  */
681
- function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, timeout, userAgent } = {}) {
717
+ function getDocumentLoader(options = {}) {
718
+ return createDocumentLoader(options);
719
+ }
720
+ /**
721
+ * The implementation of {@link getDocumentLoader}, with injectable
722
+ * dependencies. This is not exported from any entry point.
723
+ * @param options Options for the document loader.
724
+ * @param deps The dependencies.
725
+ * @returns The document loader.
726
+ */
727
+ function createDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, timeout, userAgent } = {}, deps = {}) {
682
728
  const resolvedTimeout = resolveDocumentLoaderTimeout(timeout);
683
729
  const tracer = trace.getTracerProvider().getTracer(name, version);
684
730
  const maximumRedirection = maxRedirection ?? DEFAULT_MAX_REDIRECTION;
731
+ async function openHop(url, options) {
732
+ try {
733
+ const session = deps.openHop != null ? await deps.openHop(url) : allowPrivateAddress ? await openDirectFetch(url) : await openPublicFetch(url, { signal: options?.signal });
734
+ if (options?.signal?.aborted) {
735
+ await session.close();
736
+ options.signal.throwIfAborted();
737
+ }
738
+ return session;
739
+ } catch (error) {
740
+ if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
741
+ url,
742
+ error
743
+ });
744
+ else logger[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
745
+ url,
746
+ error
747
+ });
748
+ throw error;
749
+ }
750
+ }
751
+ async function fetchHop(session, url, span, options) {
752
+ try {
753
+ const request = createActivityPubRequest(url, { userAgent });
754
+ logRequest(logger, request);
755
+ const response = await session.fetch(request, {
756
+ redirect: "manual",
757
+ signal: options?.signal
758
+ });
759
+ span.setAttribute("http.response.status_code", response.status);
760
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
761
+ const location = response.headers.get("Location");
762
+ cancelResponseBody(response, url);
763
+ return { location };
764
+ }
765
+ return await parseRemoteDocument(url, response, options);
766
+ } finally {
767
+ await session.close();
768
+ }
769
+ }
685
770
  async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
686
771
  options?.signal?.throwIfAborted();
687
772
  const parsedUrl = new URL(url);
@@ -696,36 +781,15 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
696
781
  documentUrl: currentUrl
697
782
  };
698
783
  }
699
- if (!allowPrivateAddress) {
700
- try {
701
- await validatePublicUrl(currentUrl);
702
- } catch (error) {
703
- if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
704
- url: currentUrl,
705
- error
706
- });
707
- else logger[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
708
- url: currentUrl,
709
- error
710
- });
711
- throw error;
712
- }
713
- options?.signal?.throwIfAborted();
714
- }
784
+ const session = await openHop(currentUrl, options);
715
785
  visited.add(currentUrl);
716
786
  return await tracer.startActiveSpan("activitypub.fetch_document", {
717
787
  kind: SpanKind.CLIENT,
718
788
  attributes: { "url.full": currentUrl }
719
789
  }, async (span) => {
720
790
  try {
721
- const request = createActivityPubRequest(currentUrl, { userAgent });
722
- logRequest(logger, request);
723
- const response = await fetch(request, {
724
- redirect: "manual",
725
- signal: options?.signal
726
- });
727
- span.setAttribute("http.response.status_code", response.status);
728
- if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
791
+ const outcome = await fetchHop(session, currentUrl, span, options);
792
+ if ("location" in outcome) {
729
793
  if (redirected >= maximumRedirection) {
730
794
  logger[options?.suppressError ? "warn" : "error"]("Too many redirections ({redirections}) while fetching document.", {
731
795
  redirections: redirected + 1,
@@ -733,7 +797,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
733
797
  });
734
798
  throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
735
799
  }
736
- const redirectUrl = new URL(response.headers.get("Location"), response.url === "" ? currentUrl : response.url).href;
800
+ const redirectUrl = new URL(outcome.location, currentUrl).href;
737
801
  span.setAttribute("http.redirect.url", redirectUrl);
738
802
  if (visited.has(redirectUrl)) {
739
803
  logger[options?.suppressError ? "warn" : "error"]("Detected a redirect loop while fetching document: {url} -> {redirectUrl}", {
@@ -744,12 +808,14 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
744
808
  }
745
809
  return await load(redirectUrl, options, redirected + 1, visited);
746
810
  }
747
- const result = await getRemoteDocument(currentUrl, response, async (alternateUrl) => {
811
+ let result;
812
+ if ("alternate" in outcome) {
813
+ const alternateUrl = outcome.alternate;
748
814
  options?.signal?.throwIfAborted();
749
815
  if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
750
816
  if (visited.has(alternateUrl)) throw new FetchError(currentUrl, `Redirect loop detected: ${alternateUrl}`);
751
- return await load(alternateUrl, options, redirected + 1, visited);
752
- }, options);
817
+ result = await load(alternateUrl, options, redirected + 1, visited);
818
+ } else result = outcome.document;
753
819
  span.setAttribute("docloader.document_url", result.documentUrl);
754
820
  if (result.contextUrl != null) span.setAttribute("docloader.context_url", result.contextUrl);
755
821
  return result;
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as DocumentLoader } from "./docloader-BMHljyYb.cjs";
2
+ import { n as DocumentLoader } from "./docloader-DOWjnNCz.cjs";
3
3
  import { TracerProvider } from "@opentelemetry/api";
4
4
 
5
5
  //#region src/portable.d.ts
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as DocumentLoader } from "./docloader-BMHljyYb.js";
2
+ import { n as DocumentLoader } from "./docloader-DOWjnNCz.js";
3
3
  import { TracerProvider } from "@opentelemetry/api";
4
4
 
5
5
  //#region src/portable.d.ts