@fedify/vocab 2.4.0-dev.2233 → 2.4.0-dev.2240

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab",
3
- "version": "2.4.0-dev.2233+abb4a6b2",
3
+ "version": "2.4.0-dev.2240+79613fa6",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts"
package/dist/mod.cjs CHANGED
@@ -36,7 +36,7 @@ let _fedify_vocab_runtime_temporal = require("@fedify/vocab-runtime/temporal");
36
36
  let es_toolkit = require("es-toolkit");
37
37
  //#region deno.json
38
38
  var name = "@fedify/vocab";
39
- var version = "2.4.0-dev.2233+abb4a6b2";
39
+ var version = "2.4.0-dev.2240+79613fa6";
40
40
  //#endregion
41
41
  //#region src/type.ts
42
42
  function getTypeId(object) {
@@ -79546,7 +79546,9 @@ const MAX_PORTABLE_ATTEMPTS = 5;
79546
79546
  *
79547
79547
  * [FEP-ef61] portable objects, including portable actors found through
79548
79548
  * WebFinger, are looked up only if the `verifyPortableObject` option is
79549
- * given; see {@link LookupObjectOptions.verifyPortableObject}.
79549
+ * given; see {@link LookupObjectOptions.verifyPortableObject}. Pass
79550
+ * {@link LookupObjectOptions.gateways} to look up a portable ID without
79551
+ * `@gateway` location hints.
79550
79552
  *
79551
79553
  * [FEP-ef61]: https://w3id.org/fep/ef61
79552
79554
  *
@@ -79604,7 +79606,8 @@ async function lookupObjectInternal(identifier, options = {}) {
79604
79606
  if (candidate == null) return null;
79605
79607
  return await lookupPortableObject(portable, candidate, void 0);
79606
79608
  }
79607
- identifier = toAcctUrl(identifier) ?? new URL(identifier);
79609
+ if (options.verifyPortableObject != null && (0, _fedify_vocab_runtime_internal_portable_dereference.parseCompatibleEf61Reference)(identifier) === null) return null;
79610
+ identifier = toAcctUrl(identifier) ?? (0, _fedify_vocab_runtime.parseIri)(identifier);
79608
79611
  }
79609
79612
  if ((0, _fedify_vocab_runtime_internal_portable_dereference.isPortableIri)(identifier)) {
79610
79613
  let candidate;
@@ -79649,7 +79652,7 @@ async function lookupObjectInternal(identifier, options = {}) {
79649
79652
  const candidate = parsePortableCandidate(l.href);
79650
79653
  if (candidate == null) continue;
79651
79654
  const gateways = webFingerGateway == null ? [] : [webFingerGateway];
79652
- gateways.push(...(0, _fedify_vocab_runtime_internal_portable_dereference.getPortableGatewayCandidates)(candidate));
79655
+ if (options.gateways == null) gateways.push(...(0, _fedify_vocab_runtime_internal_portable_dereference.getPortableGatewayCandidates)(candidate));
79653
79656
  const object = await lookupPortableObject(portable, candidate, gateways);
79654
79657
  if (object != null) return object;
79655
79658
  if (options.signal?.aborted) return null;
@@ -79701,10 +79704,13 @@ async function lookupObjectInternal(identifier, options = {}) {
79701
79704
  if (claim === null) {
79702
79705
  logger.debug("Refusing the document {documentUrl}, as it claims to be a portable object with a malformed compatible identifier.", { documentUrl: remoteDoc.documentUrl });
79703
79706
  return null;
79704
- } else if (claim != null) return await dereference(portable, claim.id, claim.inferredGateways, {
79705
- response: remoteDoc,
79706
- contextLoader: snapshot.loader
79707
- });
79707
+ } else if (claim != null) {
79708
+ const explicit = getExplicitGateways(portable, claim.id);
79709
+ return await dereference(portable, claim.id, claim.inferredGateways, explicit, {
79710
+ response: remoteDoc,
79711
+ contextLoader: snapshot.loader
79712
+ });
79713
+ }
79708
79714
  }
79709
79715
  if (object.id != null && (options.crossOrigin !== "trust" || (0, _fedify_vocab_runtime_internal_portable_dereference.isPortableIri)(object.id)) && !(0, _fedify_vocab_runtime.haveSameIriOrigin)(object.id, documentUrl) && !(0, _fedify_vocab_runtime.haveSameFe34Origin)(object.id, documentUrl)) {
79710
79716
  if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
@@ -79720,6 +79726,10 @@ async function lookupObjectInternal(identifier, options = {}) {
79720
79726
  }
79721
79727
  }
79722
79728
  const PORTABLE_IRI_PATTERN = /^ap(?:\+ef61)?:/i;
79729
+ function getExplicitGateways(lookup, id) {
79730
+ if (lookup.options.gateways == null) return void 0;
79731
+ return lookup.explicitGateways ??= (0, _fedify_vocab_runtime_internal_portable_dereference.getPortableGatewayCandidates)(id, lookup.options.gateways);
79732
+ }
79723
79733
  /**
79724
79734
  * Parses a raw portable IRI. URL parsing normalizes dot segments in the
79725
79735
  * opaque path, which would make the parsed IRI identify another portable
@@ -79773,29 +79783,29 @@ function getWebFingerGateway(identifier) {
79773
79783
  }
79774
79784
  /**
79775
79785
  * Looks up a portable object through FEP-ef61 gateways.
79776
- * @param gateways The gateways to ask in order. If omitted, the location
79777
- * hints in the IRI are used, and if there are none, the
79778
- * portable IRI itself is passed to the document loader.
79786
+ * @param inferredGateways Gateways inferred from a compatible identifier or
79787
+ * WebFinger. If omitted, location hints in the IRI
79788
+ * are used unless explicit gateways were given.
79779
79789
  */
79780
- async function lookupPortableObject(lookup, id, gateways) {
79790
+ async function lookupPortableObject(lookup, id, inferredGateways) {
79781
79791
  const { options } = lookup;
79782
79792
  const iri = (0, _fedify_vocab_runtime.formatIri)(id);
79793
+ const explicitGateways = getExplicitGateways(lookup, id);
79783
79794
  if (options.verifyPortableObject == null) {
79784
79795
  logger.debug("Cannot look up the portable object {iri}, as the verifyPortableObject option is not given.", { iri });
79785
79796
  return null;
79786
79797
  }
79787
79798
  const canonicalId = (0, _fedify_vocab_runtime.canonicalizePortableUri)(iri);
79788
- let candidates;
79789
- if (gateways == null) {
79790
- candidates = (0, _fedify_vocab_runtime_internal_portable_dereference.getPortableGatewayCandidates)(id);
79791
- if (candidates.length < 1) {
79792
- const key = `${canonicalId} `;
79793
- if (lookup.remaining < 1 || lookup.attempted.has(key)) return null;
79794
- lookup.attempted.add(key);
79795
- lookup.remaining--;
79796
- return await dereference(lookup, id, []);
79797
- }
79798
- } else candidates = [...gateways];
79799
+ const inferred = inferredGateways ?? (explicitGateways == null ? (0, _fedify_vocab_runtime_internal_portable_dereference.getPortableGatewayCandidates)(id) : []);
79800
+ const candidates = [...inferred];
79801
+ for (const gateway of explicitGateways ?? []) if (!candidates.some((candidate) => candidate.href === gateway.href)) candidates.push(gateway);
79802
+ if (candidates.length < 1 && inferredGateways == null) {
79803
+ const key = `${canonicalId} `;
79804
+ if (lookup.remaining < 1 || lookup.attempted.has(key)) return null;
79805
+ lookup.attempted.add(key);
79806
+ lookup.remaining--;
79807
+ return await dereference(lookup, id, [], explicitGateways == null ? void 0 : []);
79808
+ }
79799
79809
  const selected = [];
79800
79810
  for (const gateway of candidates) {
79801
79811
  if (selected.length >= lookup.remaining) break;
@@ -79806,16 +79816,19 @@ async function lookupPortableObject(lookup, id, gateways) {
79806
79816
  }
79807
79817
  if (selected.length < 1) return null;
79808
79818
  lookup.remaining -= selected.length;
79809
- return await dereference(lookup, id, selected);
79819
+ const selectedInferred = selected.filter((candidate) => inferred.some((gateway) => gateway.href === candidate.href));
79820
+ const selectedExplicit = explicitGateways?.filter((gateway) => selected.some((candidate) => candidate.href === gateway.href));
79821
+ return await dereference(lookup, id, selectedInferred, selectedExplicit);
79810
79822
  }
79811
- async function dereference({ options, documentLoader }, id, gateways, extra = {}) {
79823
+ async function dereference({ options, documentLoader }, id, inferredGateways, gateways, extra = {}) {
79812
79824
  const tracerProvider = options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
79813
79825
  try {
79814
79826
  return await (0, _fedify_vocab_runtime_internal_portable_dereference.dereferencePortableIri)(id, {
79815
79827
  documentLoader,
79816
79828
  contextLoader: extra.contextLoader ?? options.contextLoader ?? (0, _fedify_vocab_runtime.getDocumentLoader)({ userAgent: options.userAgent }),
79817
79829
  tracerProvider,
79818
- inferredGateways: gateways,
79830
+ inferredGateways,
79831
+ ...gateways == null ? {} : { gateways },
79819
79832
  response: extra.response,
79820
79833
  verifyPortableObject: options.verifyPortableObject,
79821
79834
  crossOrigin: options.crossOrigin === "throw" ? "throw" : "ignore",
package/dist/mod.d.cts CHANGED
@@ -24298,6 +24298,18 @@ interface LookupObjectOptions {
24298
24298
  */
24299
24299
  signal?: AbortSignal;
24300
24300
  /**
24301
+ * The [FEP-ef61] gateways to try when looking up a portable
24302
+ * (`ap:`/`ap+ef61:`) object, in order. Each must be an HTTP(S) origin.
24303
+ * An explicit list replaces `@gateway` location hints, including when
24304
+ * it is empty. The gateway named by a compatible identifier or the
24305
+ * WebFinger server is still tried first. At most five gateways are
24306
+ * requested per lookup, including those given here.
24307
+ *
24308
+ * [FEP-ef61]: https://w3id.org/fep/ef61
24309
+ * @since 2.4.0
24310
+ */
24311
+ gateways?: readonly (string | URL)[];
24312
+ /**
24301
24313
  * The [FEP-ef61] policy to apply to portable objects, typically
24302
24314
  * `verifyPortableObject()` from `@fedify/fedify`, which
24303
24315
  * `Context.lookupObject()` uses by default.
@@ -24355,7 +24367,9 @@ interface LookupObjectOptions {
24355
24367
  *
24356
24368
  * [FEP-ef61] portable objects, including portable actors found through
24357
24369
  * WebFinger, are looked up only if the `verifyPortableObject` option is
24358
- * given; see {@link LookupObjectOptions.verifyPortableObject}.
24370
+ * given; see {@link LookupObjectOptions.verifyPortableObject}. Pass
24371
+ * {@link LookupObjectOptions.gateways} to look up a portable ID without
24372
+ * `@gateway` location hints.
24359
24373
  *
24360
24374
  * [FEP-ef61]: https://w3id.org/fep/ef61
24361
24375
  *
package/dist/mod.d.ts CHANGED
@@ -24298,6 +24298,18 @@ interface LookupObjectOptions {
24298
24298
  */
24299
24299
  signal?: AbortSignal;
24300
24300
  /**
24301
+ * The [FEP-ef61] gateways to try when looking up a portable
24302
+ * (`ap:`/`ap+ef61:`) object, in order. Each must be an HTTP(S) origin.
24303
+ * An explicit list replaces `@gateway` location hints, including when
24304
+ * it is empty. The gateway named by a compatible identifier or the
24305
+ * WebFinger server is still tried first. At most five gateways are
24306
+ * requested per lookup, including those given here.
24307
+ *
24308
+ * [FEP-ef61]: https://w3id.org/fep/ef61
24309
+ * @since 2.4.0
24310
+ */
24311
+ gateways?: readonly (string | URL)[];
24312
+ /**
24301
24313
  * The [FEP-ef61] policy to apply to portable objects, typically
24302
24314
  * `verifyPortableObject()` from `@fedify/fedify`, which
24303
24315
  * `Context.lookupObject()` uses by default.
@@ -24355,7 +24367,9 @@ interface LookupObjectOptions {
24355
24367
  *
24356
24368
  * [FEP-ef61] portable objects, including portable actors found through
24357
24369
  * WebFinger, are looked up only if the `verifyPortableObject` option is
24358
- * given; see {@link LookupObjectOptions.verifyPortableObject}.
24370
+ * given; see {@link LookupObjectOptions.verifyPortableObject}. Pass
24371
+ * {@link LookupObjectOptions.gateways} to look up a portable ID without
24372
+ * `@gateway` location hints.
24359
24373
  *
24360
24374
  * [FEP-ef61]: https://w3id.org/fep/ef61
24361
24375
  *
package/dist/mod.js CHANGED
@@ -12,7 +12,7 @@ import { isTemporalDuration, isTemporalInstant } from "@fedify/vocab-runtime/tem
12
12
  import { delay } from "es-toolkit";
13
13
  //#region deno.json
14
14
  var name = "@fedify/vocab";
15
- var version = "2.4.0-dev.2233+abb4a6b2";
15
+ var version = "2.4.0-dev.2240+79613fa6";
16
16
  //#endregion
17
17
  //#region src/type.ts
18
18
  function getTypeId(object) {
@@ -75956,7 +75956,9 @@ const MAX_PORTABLE_ATTEMPTS = 5;
75956
75956
  *
75957
75957
  * [FEP-ef61] portable objects, including portable actors found through
75958
75958
  * WebFinger, are looked up only if the `verifyPortableObject` option is
75959
- * given; see {@link LookupObjectOptions.verifyPortableObject}.
75959
+ * given; see {@link LookupObjectOptions.verifyPortableObject}. Pass
75960
+ * {@link LookupObjectOptions.gateways} to look up a portable ID without
75961
+ * `@gateway` location hints.
75960
75962
  *
75961
75963
  * [FEP-ef61]: https://w3id.org/fep/ef61
75962
75964
  *
@@ -76014,7 +76016,8 @@ async function lookupObjectInternal(identifier, options = {}) {
76014
76016
  if (candidate == null) return null;
76015
76017
  return await lookupPortableObject(portable, candidate, void 0);
76016
76018
  }
76017
- identifier = toAcctUrl(identifier) ?? new URL(identifier);
76019
+ if (options.verifyPortableObject != null && parseCompatibleEf61Reference(identifier) === null) return null;
76020
+ identifier = toAcctUrl(identifier) ?? parseIri(identifier);
76018
76021
  }
76019
76022
  if (isPortableIri(identifier)) {
76020
76023
  let candidate;
@@ -76059,7 +76062,7 @@ async function lookupObjectInternal(identifier, options = {}) {
76059
76062
  const candidate = parsePortableCandidate(l.href);
76060
76063
  if (candidate == null) continue;
76061
76064
  const gateways = webFingerGateway == null ? [] : [webFingerGateway];
76062
- gateways.push(...getPortableGatewayCandidates(candidate));
76065
+ if (options.gateways == null) gateways.push(...getPortableGatewayCandidates(candidate));
76063
76066
  const object = await lookupPortableObject(portable, candidate, gateways);
76064
76067
  if (object != null) return object;
76065
76068
  if (options.signal?.aborted) return null;
@@ -76111,10 +76114,13 @@ async function lookupObjectInternal(identifier, options = {}) {
76111
76114
  if (claim === null) {
76112
76115
  logger.debug("Refusing the document {documentUrl}, as it claims to be a portable object with a malformed compatible identifier.", { documentUrl: remoteDoc.documentUrl });
76113
76116
  return null;
76114
- } else if (claim != null) return await dereference(portable, claim.id, claim.inferredGateways, {
76115
- response: remoteDoc,
76116
- contextLoader: snapshot.loader
76117
- });
76117
+ } else if (claim != null) {
76118
+ const explicit = getExplicitGateways(portable, claim.id);
76119
+ return await dereference(portable, claim.id, claim.inferredGateways, explicit, {
76120
+ response: remoteDoc,
76121
+ contextLoader: snapshot.loader
76122
+ });
76123
+ }
76118
76124
  }
76119
76125
  if (object.id != null && (options.crossOrigin !== "trust" || isPortableIri(object.id)) && !haveSameIriOrigin(object.id, documentUrl) && !haveSameFe34Origin(object.id, documentUrl)) {
76120
76126
  if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
@@ -76130,6 +76136,10 @@ async function lookupObjectInternal(identifier, options = {}) {
76130
76136
  }
76131
76137
  }
76132
76138
  const PORTABLE_IRI_PATTERN = /^ap(?:\+ef61)?:/i;
76139
+ function getExplicitGateways(lookup, id) {
76140
+ if (lookup.options.gateways == null) return void 0;
76141
+ return lookup.explicitGateways ??= getPortableGatewayCandidates(id, lookup.options.gateways);
76142
+ }
76133
76143
  /**
76134
76144
  * Parses a raw portable IRI. URL parsing normalizes dot segments in the
76135
76145
  * opaque path, which would make the parsed IRI identify another portable
@@ -76183,29 +76193,29 @@ function getWebFingerGateway(identifier) {
76183
76193
  }
76184
76194
  /**
76185
76195
  * Looks up a portable object through FEP-ef61 gateways.
76186
- * @param gateways The gateways to ask in order. If omitted, the location
76187
- * hints in the IRI are used, and if there are none, the
76188
- * portable IRI itself is passed to the document loader.
76196
+ * @param inferredGateways Gateways inferred from a compatible identifier or
76197
+ * WebFinger. If omitted, location hints in the IRI
76198
+ * are used unless explicit gateways were given.
76189
76199
  */
76190
- async function lookupPortableObject(lookup, id, gateways) {
76200
+ async function lookupPortableObject(lookup, id, inferredGateways) {
76191
76201
  const { options } = lookup;
76192
76202
  const iri = formatIri(id);
76203
+ const explicitGateways = getExplicitGateways(lookup, id);
76193
76204
  if (options.verifyPortableObject == null) {
76194
76205
  logger.debug("Cannot look up the portable object {iri}, as the verifyPortableObject option is not given.", { iri });
76195
76206
  return null;
76196
76207
  }
76197
76208
  const canonicalId = canonicalizePortableUri(iri);
76198
- let candidates;
76199
- if (gateways == null) {
76200
- candidates = getPortableGatewayCandidates(id);
76201
- if (candidates.length < 1) {
76202
- const key = `${canonicalId} `;
76203
- if (lookup.remaining < 1 || lookup.attempted.has(key)) return null;
76204
- lookup.attempted.add(key);
76205
- lookup.remaining--;
76206
- return await dereference(lookup, id, []);
76207
- }
76208
- } else candidates = [...gateways];
76209
+ const inferred = inferredGateways ?? (explicitGateways == null ? getPortableGatewayCandidates(id) : []);
76210
+ const candidates = [...inferred];
76211
+ for (const gateway of explicitGateways ?? []) if (!candidates.some((candidate) => candidate.href === gateway.href)) candidates.push(gateway);
76212
+ if (candidates.length < 1 && inferredGateways == null) {
76213
+ const key = `${canonicalId} `;
76214
+ if (lookup.remaining < 1 || lookup.attempted.has(key)) return null;
76215
+ lookup.attempted.add(key);
76216
+ lookup.remaining--;
76217
+ return await dereference(lookup, id, [], explicitGateways == null ? void 0 : []);
76218
+ }
76209
76219
  const selected = [];
76210
76220
  for (const gateway of candidates) {
76211
76221
  if (selected.length >= lookup.remaining) break;
@@ -76216,16 +76226,19 @@ async function lookupPortableObject(lookup, id, gateways) {
76216
76226
  }
76217
76227
  if (selected.length < 1) return null;
76218
76228
  lookup.remaining -= selected.length;
76219
- return await dereference(lookup, id, selected);
76229
+ const selectedInferred = selected.filter((candidate) => inferred.some((gateway) => gateway.href === candidate.href));
76230
+ const selectedExplicit = explicitGateways?.filter((gateway) => selected.some((candidate) => candidate.href === gateway.href));
76231
+ return await dereference(lookup, id, selectedInferred, selectedExplicit);
76220
76232
  }
76221
- async function dereference({ options, documentLoader }, id, gateways, extra = {}) {
76233
+ async function dereference({ options, documentLoader }, id, inferredGateways, gateways, extra = {}) {
76222
76234
  const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
76223
76235
  try {
76224
76236
  return await dereferencePortableIri(id, {
76225
76237
  documentLoader,
76226
76238
  contextLoader: extra.contextLoader ?? options.contextLoader ?? getDocumentLoader({ userAgent: options.userAgent }),
76227
76239
  tracerProvider,
76228
- inferredGateways: gateways,
76240
+ inferredGateways,
76241
+ ...gateways == null ? {} : { gateways },
76229
76242
  response: extra.response,
76230
76243
  verifyPortableObject: options.verifyPortableObject,
76231
76244
  crossOrigin: options.crossOrigin === "throw" ? "throw" : "ignore",
@@ -8,7 +8,7 @@ import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { domainToASCII, domainToUnicode } from "node:url";
9
9
  //#region deno.json
10
10
  var name = "@fedify/vocab";
11
- var version = "2.4.0-dev.2233+abb4a6b2";
11
+ var version = "2.4.0-dev.2240+79613fa6";
12
12
  //#endregion
13
13
  //#region src/actor.ts
14
14
  const ACTOR_DISCOVERY_HISTOGRAM_BUCKETS = [
@@ -2,7 +2,7 @@ import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { F as Person, P as Organization, Q as __exportAll, U as Service, a as Application, b as Group } from "./vocab-D3C791xF.mjs";
4
4
  import { t as esm_default } from "./esm-2_Uqq_my.mjs";
5
- import { a as normalizeActorHandle, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "./actor-C8V9Eu9k.mjs";
5
+ import { a as normalizeActorHandle, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "./actor-C-Bvqy6v.mjs";
6
6
  import { createTestMeterProvider, test } from "@fedify/fixture";
7
7
  import { deepStrictEqual, ok, rejects, strictEqual, throws } from "node:assert/strict";
8
8
  //#region ../../node_modules/.pnpm/fast-check@3.23.2/node_modules/fast-check/lib/esm/check/precondition/PreconditionFailure.js
@@ -2,7 +2,7 @@ import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { F as Person, M as OrderedCollection, S as Image, c as CollectionPage, k as Note, s as Collection, u as Create } from "./vocab-D3C791xF.mjs";
4
4
  import { t as esm_default } from "./esm-2_Uqq_my.mjs";
5
- import { n as traverseCollection, t as lookupObject } from "./lookup-2QrSuQR0.mjs";
5
+ import { n as traverseCollection, t as lookupObject } from "./lookup-DiU8zIYx.mjs";
6
6
  import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
7
7
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
8
8
  import { deepStrictEqual, ok, rejects } from "node:assert/strict";
@@ -1126,14 +1126,14 @@ test("accessors verify fetched documents that stand for portable objects", async
1126
1126
  contextLoader: mockDocumentLoader,
1127
1127
  verifyPortableObject: createVerifier()
1128
1128
  }) instanceof Note, expected);
1129
- deepStrictEqual(await (await createHttpActivity(plainUrl)).getObject({
1129
+ await rejects(() => createHttpActivity(plainUrl).then((activity) => activity.getObject({
1130
1130
  documentLoader: createRedirectingLoader({ [plainUrl]: {
1131
1131
  documentUrl: `ap://${did}/objects/x/../1`,
1132
1132
  document: note()
1133
1133
  } }),
1134
1134
  contextLoader: mockDocumentLoader,
1135
1135
  verifyPortableObject: createVerifier()
1136
- }), null);
1136
+ })), TypeError);
1137
1137
  });
1138
1138
  await t.step("a malformed compatible final URL", async () => {
1139
1139
  const malformed = `${compatibleNoteId}?@gateway=https%3A%2F%2Fgw2.example`;
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { A as Object$1 } from "./vocab-D3C791xF.mjs";
4
- import { i as isActor, o as name, s as version } from "./actor-C8V9Eu9k.mjs";
4
+ import { i as isActor, o as name, s as version } from "./actor-C-Bvqy6v.mjs";
5
5
  import { t as getTypeId } from "./type-BMdjm4Eg.mjs";
6
6
  import { canonicalizePortableUri, formatIri, getDocumentLoader, haveSameFe34Origin, haveSameIriOrigin, parseIri } from "@fedify/vocab-runtime";
7
7
  import { lookupWebFinger } from "@fedify/webfinger";
@@ -142,7 +142,9 @@ const MAX_PORTABLE_ATTEMPTS = 5;
142
142
  *
143
143
  * [FEP-ef61] portable objects, including portable actors found through
144
144
  * WebFinger, are looked up only if the `verifyPortableObject` option is
145
- * given; see {@link LookupObjectOptions.verifyPortableObject}.
145
+ * given; see {@link LookupObjectOptions.verifyPortableObject}. Pass
146
+ * {@link LookupObjectOptions.gateways} to look up a portable ID without
147
+ * `@gateway` location hints.
146
148
  *
147
149
  * [FEP-ef61]: https://w3id.org/fep/ef61
148
150
  *
@@ -200,7 +202,8 @@ async function lookupObjectInternal(identifier, options = {}) {
200
202
  if (candidate == null) return null;
201
203
  return await lookupPortableObject(portable, candidate, void 0);
202
204
  }
203
- identifier = toAcctUrl(identifier) ?? new URL(identifier);
205
+ if (options.verifyPortableObject != null && parseCompatibleEf61Reference(identifier) === null) return null;
206
+ identifier = toAcctUrl(identifier) ?? parseIri(identifier);
204
207
  }
205
208
  if (isPortableIri(identifier)) {
206
209
  let candidate;
@@ -245,7 +248,7 @@ async function lookupObjectInternal(identifier, options = {}) {
245
248
  const candidate = parsePortableCandidate(l.href);
246
249
  if (candidate == null) continue;
247
250
  const gateways = webFingerGateway == null ? [] : [webFingerGateway];
248
- gateways.push(...getPortableGatewayCandidates(candidate));
251
+ if (options.gateways == null) gateways.push(...getPortableGatewayCandidates(candidate));
249
252
  const object = await lookupPortableObject(portable, candidate, gateways);
250
253
  if (object != null) return object;
251
254
  if (options.signal?.aborted) return null;
@@ -297,10 +300,13 @@ async function lookupObjectInternal(identifier, options = {}) {
297
300
  if (claim === null) {
298
301
  logger.debug("Refusing the document {documentUrl}, as it claims to be a portable object with a malformed compatible identifier.", { documentUrl: remoteDoc.documentUrl });
299
302
  return null;
300
- } else if (claim != null) return await dereference(portable, claim.id, claim.inferredGateways, {
301
- response: remoteDoc,
302
- contextLoader: snapshot.loader
303
- });
303
+ } else if (claim != null) {
304
+ const explicit = getExplicitGateways(portable, claim.id);
305
+ return await dereference(portable, claim.id, claim.inferredGateways, explicit, {
306
+ response: remoteDoc,
307
+ contextLoader: snapshot.loader
308
+ });
309
+ }
304
310
  }
305
311
  if (object.id != null && (options.crossOrigin !== "trust" || isPortableIri(object.id)) && !haveSameIriOrigin(object.id, documentUrl) && !haveSameFe34Origin(object.id, documentUrl)) {
306
312
  if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
@@ -316,6 +322,10 @@ async function lookupObjectInternal(identifier, options = {}) {
316
322
  }
317
323
  }
318
324
  const PORTABLE_IRI_PATTERN = /^ap(?:\+ef61)?:/i;
325
+ function getExplicitGateways(lookup, id) {
326
+ if (lookup.options.gateways == null) return void 0;
327
+ return lookup.explicitGateways ??= getPortableGatewayCandidates(id, lookup.options.gateways);
328
+ }
319
329
  /**
320
330
  * Parses a raw portable IRI. URL parsing normalizes dot segments in the
321
331
  * opaque path, which would make the parsed IRI identify another portable
@@ -369,29 +379,29 @@ function getWebFingerGateway(identifier) {
369
379
  }
370
380
  /**
371
381
  * Looks up a portable object through FEP-ef61 gateways.
372
- * @param gateways The gateways to ask in order. If omitted, the location
373
- * hints in the IRI are used, and if there are none, the
374
- * portable IRI itself is passed to the document loader.
382
+ * @param inferredGateways Gateways inferred from a compatible identifier or
383
+ * WebFinger. If omitted, location hints in the IRI
384
+ * are used unless explicit gateways were given.
375
385
  */
376
- async function lookupPortableObject(lookup, id, gateways) {
386
+ async function lookupPortableObject(lookup, id, inferredGateways) {
377
387
  const { options } = lookup;
378
388
  const iri = formatIri(id);
389
+ const explicitGateways = getExplicitGateways(lookup, id);
379
390
  if (options.verifyPortableObject == null) {
380
391
  logger.debug("Cannot look up the portable object {iri}, as the verifyPortableObject option is not given.", { iri });
381
392
  return null;
382
393
  }
383
394
  const canonicalId = canonicalizePortableUri(iri);
384
- let candidates;
385
- if (gateways == null) {
386
- candidates = getPortableGatewayCandidates(id);
387
- if (candidates.length < 1) {
388
- const key = `${canonicalId} `;
389
- if (lookup.remaining < 1 || lookup.attempted.has(key)) return null;
390
- lookup.attempted.add(key);
391
- lookup.remaining--;
392
- return await dereference(lookup, id, []);
393
- }
394
- } else candidates = [...gateways];
395
+ const inferred = inferredGateways ?? (explicitGateways == null ? getPortableGatewayCandidates(id) : []);
396
+ const candidates = [...inferred];
397
+ for (const gateway of explicitGateways ?? []) if (!candidates.some((candidate) => candidate.href === gateway.href)) candidates.push(gateway);
398
+ if (candidates.length < 1 && inferredGateways == null) {
399
+ const key = `${canonicalId} `;
400
+ if (lookup.remaining < 1 || lookup.attempted.has(key)) return null;
401
+ lookup.attempted.add(key);
402
+ lookup.remaining--;
403
+ return await dereference(lookup, id, [], explicitGateways == null ? void 0 : []);
404
+ }
395
405
  const selected = [];
396
406
  for (const gateway of candidates) {
397
407
  if (selected.length >= lookup.remaining) break;
@@ -402,16 +412,19 @@ async function lookupPortableObject(lookup, id, gateways) {
402
412
  }
403
413
  if (selected.length < 1) return null;
404
414
  lookup.remaining -= selected.length;
405
- return await dereference(lookup, id, selected);
415
+ const selectedInferred = selected.filter((candidate) => inferred.some((gateway) => gateway.href === candidate.href));
416
+ const selectedExplicit = explicitGateways?.filter((gateway) => selected.some((candidate) => candidate.href === gateway.href));
417
+ return await dereference(lookup, id, selectedInferred, selectedExplicit);
406
418
  }
407
- async function dereference({ options, documentLoader }, id, gateways, extra = {}) {
419
+ async function dereference({ options, documentLoader }, id, inferredGateways, gateways, extra = {}) {
408
420
  const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
409
421
  try {
410
422
  return await dereferencePortableIri(id, {
411
423
  documentLoader,
412
424
  contextLoader: extra.contextLoader ?? options.contextLoader ?? getDocumentLoader({ userAgent: options.userAgent }),
413
425
  tracerProvider,
414
- inferredGateways: gateways,
426
+ inferredGateways,
427
+ ...gateways == null ? {} : { gateways },
415
428
  response: extra.response,
416
429
  verifyPortableObject: options.verifyPortableObject,
417
430
  crossOrigin: options.crossOrigin === "throw" ? "throw" : "ignore",
@@ -2,7 +2,7 @@ import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
3
  import { A as Object$1, F as Person, k as Note, s as Collection } from "./vocab-D3C791xF.mjs";
4
4
  import { t as esm_default } from "./esm-2_Uqq_my.mjs";
5
- import { n as traverseCollection, t as lookupObject } from "./lookup-2QrSuQR0.mjs";
5
+ import { n as traverseCollection, t as lookupObject } from "./lookup-DiU8zIYx.mjs";
6
6
  import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
7
7
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
8
8
  import { deepStrictEqual, equal, ok, rejects } from "node:assert/strict";