@fedify/vocab-runtime 2.5.0-dev.2446 → 2.5.0-dev.2492

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 (121) hide show
  1. package/deno.json +5 -2
  2. package/dist/{docloader-BBnBpbsG.d.cts → docloader-DD6binpN.d.cts} +4 -8
  3. package/dist/{docloader-BBnBpbsG.d.ts → docloader-DD6binpN.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 +72 -7
  9. package/dist/internal/portable-dereference.d.cts +63 -3
  10. package/dist/internal/portable-dereference.d.ts +63 -3
  11. package/dist/internal/portable-dereference.js +72 -8
  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-BDydjp5H.d.ts → portable-1VSnSJFj.d.ts} +1 -1
  21. package/dist/{portable-Dfvcg_Wy.d.cts → portable-Cr7NwZwb.d.cts} +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-Ddx6WgR5.cjs → body-C8d1PmDa.cjs} +1 -1
  25. package/dist/tests/{body-CQi2bVi5.mjs → body-HSLxhexN.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 +4 -4
  29. package/dist/tests/decimal.test.mjs +4 -4
  30. package/dist/tests/{docloader-C3vDXwn1.cjs → docloader-D1fNCGam.cjs} +111 -39
  31. package/dist/tests/{docloader-CrVannnU.mjs → docloader-DkEUFJui.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-CbBR-th-.mjs +84 -0
  43. package/dist/tests/node-Ch29PDXR.cjs +89 -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-DLH_npLf.cjs +220 -0
  67. package/dist/tests/pinners-tGcl49uF.mjs +203 -0
  68. package/dist/tests/{portable-dereference-DaTBBvCj.cjs → portable-dereference-BUoDzMxk.cjs} +2 -2
  69. package/dist/tests/{portable-dereference-sGRs_qdT.mjs → portable-dereference-Du2k3ngE.mjs} +2 -2
  70. package/dist/tests/{portable-media-BJf_gGQz.cjs → portable-media-BM9zgj9g.cjs} +3 -3
  71. package/dist/tests/{portable-media-Cc58dUeE.mjs → portable-media-CTxGibcj.mjs} +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-BGj5pzUD.cjs +186 -0
  77. package/dist/tests/public-fetch-Cxo3Yzdf.mjs +170 -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-glvxfx2U.cjs → request-7q-Zfe1p.cjs} +2 -2
  83. package/dist/tests/{request-1pPyYz3e.mjs → request-D5uGtmmq.mjs} +2 -2
  84. package/dist/tests/request.test.cjs +2 -2
  85. package/dist/tests/request.test.mjs +2 -2
  86. package/dist/tests/support-Bj44xlQu.mjs +272 -0
  87. package/dist/tests/support-BrdlbLJk.cjs +363 -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/portable-dereference.ts +116 -6
  100. package/src/internal.ts +13 -0
  101. package/src/pin-grant.test.ts +118 -0
  102. package/src/pin-grant.ts +99 -0
  103. package/src/pinners/bun.test.ts +278 -0
  104. package/src/pinners/bun.ts +223 -0
  105. package/src/pinners/mod.ts +59 -0
  106. package/src/pinners/node.test.ts +415 -0
  107. package/src/pinners/node.ts +144 -0
  108. package/src/pinners/pinners.test.ts +316 -0
  109. package/src/pinners/testdata/node-child.ts +147 -0
  110. package/src/pinners/testdata/proxy-child.ts +82 -0
  111. package/src/pinners/testdata/support.ts +343 -0
  112. package/src/pinners/testdata/tls-cert.pem +12 -0
  113. package/src/pinners/testdata/tls-child.ts +88 -0
  114. package/src/pinners/testdata/tls-key.pem +5 -0
  115. package/src/pinners/types.ts +48 -0
  116. package/src/public-fetch.test.ts +1286 -0
  117. package/src/public-fetch.ts +338 -0
  118. package/src/testdata/public-fetch-deno-worker.ts +74 -0
  119. package/src/url.test.ts +11 -0
  120. package/src/url.ts +113 -22
  121. package/tsdown.config.ts +7 -2
package/deno.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.5.0-dev.2446+2014cb03",
3
+ "version": "2.5.0-dev.2492+4648c04e",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts",
7
7
  "./internal/jsonld-cache": "./src/internal/jsonld-cache.ts",
8
8
  "./internal/portable-dereference": "./src/internal/portable-dereference.ts",
9
9
  "./internal/signed-representation": "./src/internal/signed-representation.ts",
10
+ "./internal": "./src/internal.ts",
10
11
  "./jsonld": "./src/jsonld.ts",
11
12
  "./temporal": "./src/temporal.ts"
12
13
  },
@@ -22,7 +23,8 @@
22
23
  "byte-encodings": "npm:byte-encodings@^1.0.11",
23
24
  "fetch-mock": "npm:fetch-mock@^12.5.4",
24
25
  "jsonld": "npm:jsonld@^9.0.0",
25
- "pkijs": "npm:pkijs@^3.2.5"
26
+ "pkijs": "npm:pkijs@^3.2.5",
27
+ "undici": "npm:undici@^7.30.1"
26
28
  },
27
29
  "exclude": [
28
30
  "dist",
@@ -31,6 +33,7 @@
31
33
  "publish": {
32
34
  "exclude": [
33
35
  "**/*.test.ts",
36
+ "src/**/testdata/",
34
37
  "tsdown.config.ts"
35
38
  ]
36
39
  },
@@ -221,7 +221,9 @@ type AuthenticatedDocumentLoaderFactory = (identity: {
221
221
  * Gets a {@link RemoteDocument} from the given response.
222
222
  * @param url The URL of the document to load.
223
223
  * @param response The response to get the document from.
224
- * @param fetch The function to fetch the document.
224
+ * @param fetch The function to fetch the document. It is called with the URL
225
+ * of an alternate document, if any, after the response body has
226
+ * been either read or its cancellation has been started.
225
227
  * @param options The options for loading the document.
226
228
  * @returns The loaded remote document.
227
229
  * @throws {FetchError} If the response is not OK.
@@ -292,12 +294,6 @@ interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions {
292
294
  * @returns The document loader.
293
295
  * @since 1.3.0
294
296
  */
295
- declare function getDocumentLoader({
296
- allowPrivateAddress,
297
- maxRedirection,
298
- skipPreloadedContexts,
299
- timeout,
300
- userAgent
301
- }?: GetDocumentLoaderOptions): DocumentLoader;
297
+ declare function getDocumentLoader(options?: GetDocumentLoaderOptions): DocumentLoader;
302
298
  //#endregion
303
299
  export { isTransientFetchError as _, DocumentLoaderOptions as a, getDocumentLoader as c, withDocumentLoaderTimeout as d, CreateRequestOptions as f, getUserAgent as g, createActivityPubRequest as h, DocumentLoaderFactoryOptions as i, getRemoteDocument as l, GetUserAgentOptions as m, DocumentLoader as n, GetDocumentLoaderOptions as o, FetchError as p, DocumentLoaderFactory as r, RemoteDocument as s, AuthenticatedDocumentLoaderFactory as t, resolveDocumentLoaderTimeout as u, logRequest as v };
@@ -221,7 +221,9 @@ type AuthenticatedDocumentLoaderFactory = (identity: {
221
221
  * Gets a {@link RemoteDocument} from the given response.
222
222
  * @param url The URL of the document to load.
223
223
  * @param response The response to get the document from.
224
- * @param fetch The function to fetch the document.
224
+ * @param fetch The function to fetch the document. It is called with the URL
225
+ * of an alternate document, if any, after the response body has
226
+ * been either read or its cancellation has been started.
225
227
  * @param options The options for loading the document.
226
228
  * @returns The loaded remote document.
227
229
  * @throws {FetchError} If the response is not OK.
@@ -292,12 +294,6 @@ interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions {
292
294
  * @returns The document loader.
293
295
  * @since 1.3.0
294
296
  */
295
- declare function getDocumentLoader({
296
- allowPrivateAddress,
297
- maxRedirection,
298
- skipPreloadedContexts,
299
- timeout,
300
- userAgent
301
- }?: GetDocumentLoaderOptions): DocumentLoader;
297
+ declare function getDocumentLoader(options?: GetDocumentLoaderOptions): DocumentLoader;
302
298
  //#endregion
303
299
  export { isTransientFetchError as _, DocumentLoaderOptions as a, getDocumentLoader as c, withDocumentLoaderTimeout as d, CreateRequestOptions as f, getUserAgent as g, createActivityPubRequest as h, DocumentLoaderFactoryOptions as i, getRemoteDocument as l, GetUserAgentOptions as m, DocumentLoader as n, GetDocumentLoaderOptions as o, FetchError as p, DocumentLoaderFactory as r, RemoteDocument as s, AuthenticatedDocumentLoaderFactory as t, resolveDocumentLoaderTimeout as u, logRequest as v };
@@ -1,6 +1,6 @@
1
1
 
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_url = require("../url-DrGTR8yv.cjs");
3
+ const require_url = require("../url-DIKZnaJv.cjs");
4
4
  const require_jsonld = require("../jsonld.cjs");
5
5
  //#region src/internal/jsonld-cache.ts
6
6
  /**
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as DocumentLoader } from "../docloader-BBnBpbsG.cjs";
2
+ import { n as DocumentLoader } from "../docloader-DD6binpN.cjs";
3
3
 
4
4
  //#region src/internal/jsonld-cache.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as DocumentLoader } from "../docloader-BBnBpbsG.js";
2
+ import { n as DocumentLoader } from "../docloader-DD6binpN.js";
3
3
 
4
4
  //#region src/internal/jsonld-cache.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { _ as parseIri, c as getFe34Origin, d as haveSameIriOrigin, o as formatIri, s as fromCompatibleEf61Id, u as haveSameFe34Origin } from "../url-Dzyp-NsC.js";
2
+ import { b as parseIri, c as fromCompatibleEf61Id, d as haveSameFe34Origin, f as haveSameIriOrigin, l as getFe34Origin, s as formatIri } from "../url-mWveR5GA.js";
3
3
  import jsonld_default from "../jsonld.js";
4
4
  //#region src/internal/jsonld-cache.ts
5
5
  /**
@@ -1,7 +1,7 @@
1
1
 
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_contexts = require("../contexts-B09jb9Pi.cjs");
4
- const require_url = require("../url-DrGTR8yv.cjs");
4
+ const require_url = require("../url-DIKZnaJv.cjs");
5
5
  const require_jsonld = require("../jsonld.cjs");
6
6
  const require_internal_jsonld_cache = require("./jsonld-cache.cjs");
7
7
  let _logtape_logtape = require("@logtape/logtape");
@@ -497,6 +497,35 @@ var PortableObjectRejectedError = class extends Error {
497
497
  this.name = "PortableObjectRejectedError";
498
498
  }
499
499
  };
500
+ /**
501
+ * Asks an observer whether to accept a candidate. A refusal, including an
502
+ * error thrown by the observer, is reported to the observer as an `accept`
503
+ * failure.
504
+ * @returns `true` if there is no observer, or it accepted the candidate.
505
+ * @internal Not part of the public API contract.
506
+ */
507
+ async function acceptCandidate(observer, candidate) {
508
+ if (observer?.accept == null) return true;
509
+ let accepted;
510
+ try {
511
+ accepted = await observer.accept(candidate);
512
+ } catch (error) {
513
+ observer.failed?.({
514
+ operation: "accept",
515
+ error
516
+ });
517
+ return false;
518
+ }
519
+ if (!accepted) observer.failed?.({ operation: "accept" });
520
+ return accepted;
521
+ }
522
+ /** Wraps an error that has already been reported to the observer. */
523
+ var ObservedFailure = class {
524
+ error;
525
+ constructor(error) {
526
+ this.error = error;
527
+ }
528
+ };
500
529
  var PortableObjectRejection = class {
501
530
  message;
502
531
  values;
@@ -592,6 +621,10 @@ async function dereferencePortableIri(url, options) {
592
621
  requestUrl,
593
622
  error
594
623
  });
624
+ options.observer?.failed?.({
625
+ operation: "fetch",
626
+ error
627
+ });
595
628
  attempts.push({
596
629
  type: "error",
597
630
  error
@@ -622,11 +655,33 @@ async function dereferencePortableIri(url, options) {
622
655
  throw new PortableObjectRejection("the proof verifier failed: {error}", { error });
623
656
  }
624
657
  if (!result.verified) throw new PortableObjectRejection("it does not satisfy the FEP-ef61 proof policy: {result}", { result });
625
- const object = await options.parse(document, {
626
- contextLoader,
627
- baseUrl: url
628
- });
658
+ let object;
659
+ try {
660
+ object = await options.parse(document, {
661
+ contextLoader,
662
+ baseUrl: url
663
+ });
664
+ } catch (error) {
665
+ options.observer?.failed?.({
666
+ operation: "parse",
667
+ error
668
+ });
669
+ throw new ObservedFailure(error);
670
+ }
629
671
  signal?.throwIfAborted();
672
+ if (!await acceptCandidate(options.observer, {
673
+ document,
674
+ documentUrl: remoteDocument.documentUrl,
675
+ baseUrl: url,
676
+ contextLoader,
677
+ object
678
+ })) {
679
+ attempts.push({
680
+ type: "rejected",
681
+ source: requestUrl
682
+ });
683
+ continue;
684
+ }
630
685
  if (object != null && typeof object === "object") provenances.set(object, {
631
686
  acceptance: result.unsecured === true ? "unsecured" : "verified",
632
687
  ...result.collectionContext === void 0 ? {} : { collectionContext: result.collectionContext },
@@ -642,20 +697,29 @@ async function dereferencePortableIri(url, options) {
642
697
  url: lookupUrl,
643
698
  requestUrl
644
699
  });
700
+ options.observer?.failed?.({
701
+ operation: "verify",
702
+ error: error.values.error
703
+ });
645
704
  attempts.push({
646
705
  type: "rejected",
647
706
  source: requestUrl
648
707
  });
649
708
  continue;
650
709
  }
710
+ const cause = error instanceof ObservedFailure ? error.error : error;
711
+ if (!(error instanceof ObservedFailure)) options.observer?.failed?.({
712
+ operation: "verify",
713
+ error
714
+ });
651
715
  logger.debug("Failed to parse {url} from {requestUrl}: {error}", {
652
716
  url: lookupUrl,
653
717
  requestUrl,
654
- error
718
+ error: cause
655
719
  });
656
720
  attempts.push({
657
721
  type: "error",
658
- error
722
+ error: cause
659
723
  });
660
724
  }
661
725
  }
@@ -701,6 +765,7 @@ async function checkPortableObjectId(document, expectedId, contextLoader) {
701
765
  }
702
766
  //#endregion
703
767
  exports.PortableObjectRejectedError = PortableObjectRejectedError;
768
+ exports.acceptCandidate = acceptCandidate;
704
769
  exports.copyPortableProvenance = copyPortableProvenance;
705
770
  exports.createSnapshotContextLoader = createSnapshotContextLoader;
706
771
  exports.dereferencePortableIri = dereferencePortableIri;
@@ -1,6 +1,6 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as DocumentLoader, s as RemoteDocument } from "../docloader-BBnBpbsG.cjs";
3
- import { r as PortableObjectVerifier } from "../portable-Dfvcg_Wy.cjs";
2
+ import { n as DocumentLoader, s as RemoteDocument } from "../docloader-DD6binpN.cjs";
3
+ import { r as PortableObjectVerifier } from "../portable-Cr7NwZwb.cjs";
4
4
  import { Span, TracerProvider } from "@opentelemetry/api";
5
5
 
6
6
  //#region src/internal/portable-dereference.d.ts
@@ -311,6 +311,58 @@ interface DereferencePortableIriOptions<T> {
311
311
  baseUrl: URL;
312
312
  }) => Promise<T>;
313
313
  span?: Span;
314
+ /**
315
+ * Observes each candidate document, and may refuse a candidate that was
316
+ * otherwise acceptable, in which case the next gateway is tried.
317
+ */
318
+ observer?: DereferenceObserver<T>;
319
+ }
320
+ /**
321
+ * A candidate document that a lookup or a dereference was about to return.
322
+ *
323
+ * @internal Not part of the public API contract.
324
+ */
325
+ interface DereferenceCandidate<T> {
326
+ /** The document as it was retrieved. */
327
+ readonly document: unknown;
328
+ /** The URL the document was finally retrieved from. */
329
+ readonly documentUrl: string;
330
+ /** The `baseUrl` option the document was parsed with. */
331
+ readonly baseUrl: URL;
332
+ /**
333
+ * The context loader the document was parsed with. It keeps serving the
334
+ * same context documents until the {@link DereferenceObserver.accept}
335
+ * call settles.
336
+ */
337
+ readonly contextLoader: DocumentLoader;
338
+ /** The parsed object. */
339
+ readonly object: T;
340
+ }
341
+ /**
342
+ * Observes the candidates and the swallowed failures of a lookup or
343
+ * a dereference.
344
+ *
345
+ * @internal Not part of the public API contract.
346
+ */
347
+ interface DereferenceObserver<T> {
348
+ /**
349
+ * Called for each candidate whose parse succeeded, right before it would be
350
+ * returned. If it resolves to `false`, the candidate is refused, and the
351
+ * next one is tried. An error it throws is treated as a refusal, and is
352
+ * reported to {@link failed}.
353
+ */
354
+ accept?(candidate: DereferenceCandidate<T>): Promise<boolean>;
355
+ /** Called for each failure that does not propagate by itself. */
356
+ failed?(failure: DereferenceFailure): void;
357
+ }
358
+ /**
359
+ * A failure observed by {@link DereferenceObserver.failed}.
360
+ *
361
+ * @internal Not part of the public API contract.
362
+ */
363
+ interface DereferenceFailure {
364
+ readonly operation: "fetch" | "verify" | "parse" | "origin" | "accept";
365
+ readonly error?: unknown;
314
366
  }
315
367
  /**
316
368
  * The error thrown by {@link dereferencePortableIri} under
@@ -323,6 +375,14 @@ declare class PortableObjectRejectedError extends Error {
323
375
  constructor(message: string);
324
376
  }
325
377
  /**
378
+ * Asks an observer whether to accept a candidate. A refusal, including an
379
+ * error thrown by the observer, is reported to the observer as an `accept`
380
+ * failure.
381
+ * @returns `true` if there is no observer, or it accepted the candidate.
382
+ * @internal Not part of the public API contract.
383
+ */
384
+ declare function acceptCandidate<T>(observer: DereferenceObserver<T> | undefined, candidate: DereferenceCandidate<T>): Promise<boolean>;
385
+ /**
326
386
  * Dereferences an FEP-ef61 portable IRI through its gateways.
327
387
  *
328
388
  * Gateways are tried one by one until one of them returns a single-node
@@ -346,4 +406,4 @@ declare function dereferencePortableIri<T extends {
346
406
  id: URL | null;
347
407
  }>(url: URL, options: DereferencePortableIriOptions<T>): Promise<T | null>;
348
408
  //#endregion
349
- export { DereferencePortableIriOptions, PortableObjectRejectedError, PortableReferrerLink, PortableResponseClaim, copyPortableProvenance, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, getReferrerGateways, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isPortableMode, isUnsecuredPortableObject, isUnverifiedPortableClaim, markUnverifiedPortableClaim, mustDereferencePortableObject, parseCompatibleEf61Reference, recordPortableReferrer, rejectMalformedCompatibleReference, warnUnverifiableEmbeddedObject };
409
+ export { DereferenceCandidate, DereferenceFailure, DereferenceObserver, DereferencePortableIriOptions, PortableObjectRejectedError, PortableReferrerLink, PortableResponseClaim, acceptCandidate, copyPortableProvenance, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, getReferrerGateways, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isPortableMode, isUnsecuredPortableObject, isUnverifiedPortableClaim, markUnverifiedPortableClaim, mustDereferencePortableObject, parseCompatibleEf61Reference, recordPortableReferrer, rejectMalformedCompatibleReference, warnUnverifiableEmbeddedObject };
@@ -1,6 +1,6 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as DocumentLoader, s as RemoteDocument } from "../docloader-BBnBpbsG.js";
3
- import { r as PortableObjectVerifier } from "../portable-BDydjp5H.js";
2
+ import { n as DocumentLoader, s as RemoteDocument } from "../docloader-DD6binpN.js";
3
+ import { r as PortableObjectVerifier } from "../portable-1VSnSJFj.js";
4
4
  import { Span, TracerProvider } from "@opentelemetry/api";
5
5
 
6
6
  //#region src/internal/portable-dereference.d.ts
@@ -311,6 +311,58 @@ interface DereferencePortableIriOptions<T> {
311
311
  baseUrl: URL;
312
312
  }) => Promise<T>;
313
313
  span?: Span;
314
+ /**
315
+ * Observes each candidate document, and may refuse a candidate that was
316
+ * otherwise acceptable, in which case the next gateway is tried.
317
+ */
318
+ observer?: DereferenceObserver<T>;
319
+ }
320
+ /**
321
+ * A candidate document that a lookup or a dereference was about to return.
322
+ *
323
+ * @internal Not part of the public API contract.
324
+ */
325
+ interface DereferenceCandidate<T> {
326
+ /** The document as it was retrieved. */
327
+ readonly document: unknown;
328
+ /** The URL the document was finally retrieved from. */
329
+ readonly documentUrl: string;
330
+ /** The `baseUrl` option the document was parsed with. */
331
+ readonly baseUrl: URL;
332
+ /**
333
+ * The context loader the document was parsed with. It keeps serving the
334
+ * same context documents until the {@link DereferenceObserver.accept}
335
+ * call settles.
336
+ */
337
+ readonly contextLoader: DocumentLoader;
338
+ /** The parsed object. */
339
+ readonly object: T;
340
+ }
341
+ /**
342
+ * Observes the candidates and the swallowed failures of a lookup or
343
+ * a dereference.
344
+ *
345
+ * @internal Not part of the public API contract.
346
+ */
347
+ interface DereferenceObserver<T> {
348
+ /**
349
+ * Called for each candidate whose parse succeeded, right before it would be
350
+ * returned. If it resolves to `false`, the candidate is refused, and the
351
+ * next one is tried. An error it throws is treated as a refusal, and is
352
+ * reported to {@link failed}.
353
+ */
354
+ accept?(candidate: DereferenceCandidate<T>): Promise<boolean>;
355
+ /** Called for each failure that does not propagate by itself. */
356
+ failed?(failure: DereferenceFailure): void;
357
+ }
358
+ /**
359
+ * A failure observed by {@link DereferenceObserver.failed}.
360
+ *
361
+ * @internal Not part of the public API contract.
362
+ */
363
+ interface DereferenceFailure {
364
+ readonly operation: "fetch" | "verify" | "parse" | "origin" | "accept";
365
+ readonly error?: unknown;
314
366
  }
315
367
  /**
316
368
  * The error thrown by {@link dereferencePortableIri} under
@@ -323,6 +375,14 @@ declare class PortableObjectRejectedError extends Error {
323
375
  constructor(message: string);
324
376
  }
325
377
  /**
378
+ * Asks an observer whether to accept a candidate. A refusal, including an
379
+ * error thrown by the observer, is reported to the observer as an `accept`
380
+ * failure.
381
+ * @returns `true` if there is no observer, or it accepted the candidate.
382
+ * @internal Not part of the public API contract.
383
+ */
384
+ declare function acceptCandidate<T>(observer: DereferenceObserver<T> | undefined, candidate: DereferenceCandidate<T>): Promise<boolean>;
385
+ /**
326
386
  * Dereferences an FEP-ef61 portable IRI through its gateways.
327
387
  *
328
388
  * Gateways are tried one by one until one of them returns a single-node
@@ -346,4 +406,4 @@ declare function dereferencePortableIri<T extends {
346
406
  id: URL | null;
347
407
  }>(url: URL, options: DereferencePortableIriOptions<T>): Promise<T | null>;
348
408
  //#endregion
349
- export { DereferencePortableIriOptions, PortableObjectRejectedError, PortableReferrerLink, PortableResponseClaim, copyPortableProvenance, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, getReferrerGateways, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isPortableMode, isUnsecuredPortableObject, isUnverifiedPortableClaim, markUnverifiedPortableClaim, mustDereferencePortableObject, parseCompatibleEf61Reference, recordPortableReferrer, rejectMalformedCompatibleReference, warnUnverifiableEmbeddedObject };
409
+ export { DereferenceCandidate, DereferenceFailure, DereferenceObserver, DereferencePortableIriOptions, PortableObjectRejectedError, PortableReferrerLink, PortableResponseClaim, acceptCandidate, copyPortableProvenance, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, getReferrerGateways, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isPortableMode, isUnsecuredPortableObject, isUnverifiedPortableClaim, markUnverifiedPortableClaim, mustDereferencePortableObject, parseCompatibleEf61Reference, recordPortableReferrer, rejectMalformedCompatibleReference, warnUnverifiableEmbeddedObject };
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { t as preloadedContexts } from "../contexts-DWrj4AGR.js";
3
- import { _ as parseIri, h as parseGatewayOrigin, i as canonicalizePortableUri, o as formatIri, s as fromCompatibleEf61Id, t as GATEWAY_HINT_PARAMETER, u as haveSameFe34Origin, y as toCompatibleEf61Id } from "../url-Dzyp-NsC.js";
3
+ import { C as toCompatibleEf61Id, a as canonicalizePortableUri, b as parseIri, c as fromCompatibleEf61Id, d as haveSameFe34Origin, s as formatIri, t as GATEWAY_HINT_PARAMETER, v as parseGatewayOrigin } from "../url-mWveR5GA.js";
4
4
  import jsonld_default from "../jsonld.js";
5
5
  import { createScopedContextLoader, registerDocumentLoaderWrapper, unwrapReleasedDocumentLoader } from "./jsonld-cache.js";
6
6
  import { getLogger } from "@logtape/logtape";
@@ -496,6 +496,35 @@ var PortableObjectRejectedError = class extends Error {
496
496
  this.name = "PortableObjectRejectedError";
497
497
  }
498
498
  };
499
+ /**
500
+ * Asks an observer whether to accept a candidate. A refusal, including an
501
+ * error thrown by the observer, is reported to the observer as an `accept`
502
+ * failure.
503
+ * @returns `true` if there is no observer, or it accepted the candidate.
504
+ * @internal Not part of the public API contract.
505
+ */
506
+ async function acceptCandidate(observer, candidate) {
507
+ if (observer?.accept == null) return true;
508
+ let accepted;
509
+ try {
510
+ accepted = await observer.accept(candidate);
511
+ } catch (error) {
512
+ observer.failed?.({
513
+ operation: "accept",
514
+ error
515
+ });
516
+ return false;
517
+ }
518
+ if (!accepted) observer.failed?.({ operation: "accept" });
519
+ return accepted;
520
+ }
521
+ /** Wraps an error that has already been reported to the observer. */
522
+ var ObservedFailure = class {
523
+ error;
524
+ constructor(error) {
525
+ this.error = error;
526
+ }
527
+ };
499
528
  var PortableObjectRejection = class {
500
529
  message;
501
530
  values;
@@ -591,6 +620,10 @@ async function dereferencePortableIri(url, options) {
591
620
  requestUrl,
592
621
  error
593
622
  });
623
+ options.observer?.failed?.({
624
+ operation: "fetch",
625
+ error
626
+ });
594
627
  attempts.push({
595
628
  type: "error",
596
629
  error
@@ -621,11 +654,33 @@ async function dereferencePortableIri(url, options) {
621
654
  throw new PortableObjectRejection("the proof verifier failed: {error}", { error });
622
655
  }
623
656
  if (!result.verified) throw new PortableObjectRejection("it does not satisfy the FEP-ef61 proof policy: {result}", { result });
624
- const object = await options.parse(document, {
625
- contextLoader,
626
- baseUrl: url
627
- });
657
+ let object;
658
+ try {
659
+ object = await options.parse(document, {
660
+ contextLoader,
661
+ baseUrl: url
662
+ });
663
+ } catch (error) {
664
+ options.observer?.failed?.({
665
+ operation: "parse",
666
+ error
667
+ });
668
+ throw new ObservedFailure(error);
669
+ }
628
670
  signal?.throwIfAborted();
671
+ if (!await acceptCandidate(options.observer, {
672
+ document,
673
+ documentUrl: remoteDocument.documentUrl,
674
+ baseUrl: url,
675
+ contextLoader,
676
+ object
677
+ })) {
678
+ attempts.push({
679
+ type: "rejected",
680
+ source: requestUrl
681
+ });
682
+ continue;
683
+ }
629
684
  if (object != null && typeof object === "object") provenances.set(object, {
630
685
  acceptance: result.unsecured === true ? "unsecured" : "verified",
631
686
  ...result.collectionContext === void 0 ? {} : { collectionContext: result.collectionContext },
@@ -641,20 +696,29 @@ async function dereferencePortableIri(url, options) {
641
696
  url: lookupUrl,
642
697
  requestUrl
643
698
  });
699
+ options.observer?.failed?.({
700
+ operation: "verify",
701
+ error: error.values.error
702
+ });
644
703
  attempts.push({
645
704
  type: "rejected",
646
705
  source: requestUrl
647
706
  });
648
707
  continue;
649
708
  }
709
+ const cause = error instanceof ObservedFailure ? error.error : error;
710
+ if (!(error instanceof ObservedFailure)) options.observer?.failed?.({
711
+ operation: "verify",
712
+ error
713
+ });
650
714
  logger.debug("Failed to parse {url} from {requestUrl}: {error}", {
651
715
  url: lookupUrl,
652
716
  requestUrl,
653
- error
717
+ error: cause
654
718
  });
655
719
  attempts.push({
656
720
  type: "error",
657
- error
721
+ error: cause
658
722
  });
659
723
  }
660
724
  }
@@ -699,4 +763,4 @@ async function checkPortableObjectId(document, expectedId, contextLoader) {
699
763
  if (actualId !== expectedId) throw new PortableObjectRejection("its @id ({objectId}) does not match the requested portable ID", { objectId: id });
700
764
  }
701
765
  //#endregion
702
- export { PortableObjectRejectedError, copyPortableProvenance, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, getReferrerGateways, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isPortableMode, isUnsecuredPortableObject, isUnverifiedPortableClaim, markUnverifiedPortableClaim, mustDereferencePortableObject, parseCompatibleEf61Reference, recordPortableReferrer, rejectMalformedCompatibleReference, warnUnverifiableEmbeddedObject };
766
+ export { PortableObjectRejectedError, acceptCandidate, copyPortableProvenance, createSnapshotContextLoader, dereferencePortableIri, getPortableGatewayCandidates, getPortableResponseClaim, getReferrerGateways, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isPortableMode, isUnsecuredPortableObject, isUnverifiedPortableClaim, markUnverifiedPortableClaim, mustDereferencePortableObject, parseCompatibleEf61Reference, recordPortableReferrer, rejectMalformedCompatibleReference, warnUnverifiableEmbeddedObject };
@@ -0,0 +1,6 @@
1
+
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const require_public_fetch = require("./public-fetch-Dvfvawei.cjs");
4
+ exports.openDirectFetch = require_public_fetch.openDirectFetch;
5
+ exports.openPublicFetch = require_public_fetch.openPublicFetch;
6
+ exports.withPublicFetch = require_public_fetch.withPublicFetch;
@@ -0,0 +1,108 @@
1
+ /// <reference lib="esnext.temporal" />
2
+ import { t as ResolvedAddress } from "./url-4EE56sNP.cjs";
3
+
4
+ //#region src/public-fetch.d.ts
5
+ /**
6
+ * Options for {@link openPublicFetch} and {@link withPublicFetch}.
7
+ */
8
+ interface PublicFetchOptions {
9
+ /**
10
+ * An `AbortSignal` for cancelling the DNS lookup.
11
+ */
12
+ readonly signal?: AbortSignal;
13
+ /**
14
+ * A function that resolves all addresses of a hostname, used instead of
15
+ * `node:dns.lookup()`. The addresses it returns are validated in the same
16
+ * way as the default lookup's.
17
+ */
18
+ readonly lookup?: (hostname: string) => Promise<readonly ResolvedAddress[]>;
19
+ }
20
+ /**
21
+ * A session for fetching resources from a single origin. Its `fetch()`
22
+ * connects only to the addresses validated when the session was opened, on
23
+ * runtimes that support it.
24
+ */
25
+ interface PublicFetchSession {
26
+ /**
27
+ * A copy of the URL the session was opened for.
28
+ */
29
+ readonly url: URL;
30
+ /**
31
+ * Fetches a resource from the session's origin. Redirects are never
32
+ * followed; the redirect mode is always `"manual"`.
33
+ * @throws {TypeError} If the input's origin differs from the session's, or
34
+ * if the session is closed.
35
+ */
36
+ fetch(input: Request | URL | string, init?: RequestInit): Promise<Response>;
37
+ /**
38
+ * Closes the session. The cancellation of response bodies that have been
39
+ * neither consumed nor cancelled is started (without waiting for it), and
40
+ * the session can no longer fetch. Calling this more than once has no
41
+ * effect.
42
+ */
43
+ close(): Promise<void>;
44
+ }
45
+ /**
46
+ * The functions created by {@link createPublicFetch}.
47
+ */
48
+ interface PublicFetch {
49
+ /**
50
+ * Validates the URL, resolves its hostname, checks that every resolved
51
+ * address is public, and opens a session that connects only to those
52
+ * addresses.
53
+ * @param url The URL to open a session for.
54
+ * @param options The options.
55
+ * @returns The session. It must be closed.
56
+ * @throws {UrlError} If the URL is not allowed, or its hostname cannot be
57
+ * resolved (with `reason: "dns"`).
58
+ */
59
+ openPublicFetch(url: URL | string, options?: PublicFetchOptions): Promise<PublicFetchSession>;
60
+ /**
61
+ * Opens a session with {@link PublicFetch.openPublicFetch}, runs the given
62
+ * function with its `fetch()`, and closes the session afterwards.
63
+ * @param url The URL to open a session for.
64
+ * @param fn The function to run. It must not return a `Response`, since
65
+ * the response bodies are cancelled when the session is closed.
66
+ * @param options The options.
67
+ * @returns The value returned by `fn`.
68
+ * @throws {UrlError} If the URL is not allowed.
69
+ * @throws {TypeError} If `fn` returns a `Response`.
70
+ */
71
+ withPublicFetch<T>(url: URL | string, fn: (fetch: PublicFetchSession["fetch"]) => Promise<T>, options?: PublicFetchOptions): Promise<T>;
72
+ }
73
+ /**
74
+ * Validates the URL, resolves its hostname, checks that every resolved
75
+ * address is public, and opens a session that connects only to those
76
+ * addresses. Connections are pinned on Node.js and Bun. On other runtimes,
77
+ * the session uses the global `fetch()` after the check.
78
+ * @param url The URL to open a session for.
79
+ * @param options The options.
80
+ * @returns The session. It must be closed.
81
+ * @throws {UrlError} If the URL is not allowed, or its hostname cannot be
82
+ * resolved (with `reason: "dns"`).
83
+ */
84
+ declare const openPublicFetch: PublicFetch["openPublicFetch"];
85
+ /**
86
+ * Opens a session with {@link openPublicFetch}, runs the given function with
87
+ * its `fetch()`, and closes the session afterwards.
88
+ * @param url The URL to open a session for.
89
+ * @param fn The function to run. It must not return a `Response`, since
90
+ * the response bodies are cancelled when the session is closed.
91
+ * @param options The options.
92
+ * @returns The value returned by `fn`.
93
+ * @throws {UrlError} If the URL is not allowed.
94
+ * @throws {TypeError} If `fn` returns a `Response`.
95
+ */
96
+ declare const withPublicFetch: PublicFetch["withPublicFetch"];
97
+ /**
98
+ * Opens a session that neither validates nor pins addresses, for callers that
99
+ * allow private addresses. It has the same rules as {@link openPublicFetch}
100
+ * sessions otherwise: requests are limited to the URL's origin, redirects are
101
+ * never followed, and unconsumed response bodies are cancelled on close.
102
+ * It grants nothing beyond calling the global `fetch()` directly.
103
+ * @param url The URL to open a session for.
104
+ * @returns The session. It must be closed.
105
+ */
106
+ declare function openDirectFetch(url: URL | string): Promise<PublicFetchSession>;
107
+ //#endregion
108
+ export { type PublicFetchOptions, type PublicFetchSession, openDirectFetch, openPublicFetch, withPublicFetch };