@fedify/fedify 2.4.0-dev.2219 → 2.4.0-dev.2233

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 (73) hide show
  1. package/dist/{builder-C5rYBZ16.mjs → builder-BmoDmjWk.mjs} +2 -2
  2. package/dist/compat/transformers.test.mjs +1 -1
  3. package/dist/{compound-proof-D0xY7MBs.mjs → compound-proof-Pn8766TV.mjs} +2 -2
  4. package/dist/{deno-Dj1TFhf4.mjs → deno-62DcBMwc.mjs} +1 -1
  5. package/dist/{docloader-CzyJe90o.mjs → docloader-Be9TaKa_.mjs} +2 -2
  6. package/dist/federation/builder.test.mjs +1 -1
  7. package/dist/federation/compound-fanout.test.mjs +3 -3
  8. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  9. package/dist/federation/handler.test.mjs +73 -6
  10. package/dist/federation/hashlink-media.test.mjs +2 -2
  11. package/dist/federation/idempotency.test.mjs +2 -2
  12. package/dist/federation/metrics.test.mjs +1 -1
  13. package/dist/federation/middleware.test.mjs +7 -7
  14. package/dist/federation/mod.cjs +1 -1
  15. package/dist/federation/mod.js +1 -1
  16. package/dist/federation/outgoing-proof.test.mjs +248 -6
  17. package/dist/federation/portable-actor.test.mjs +3 -3
  18. package/dist/federation/portable-chain.test.mjs +3 -3
  19. package/dist/federation/portable-collection.test.mjs +4 -4
  20. package/dist/federation/portable-delivery.test.mjs +2 -2
  21. package/dist/federation/portable-gateway-keys.test.mjs +4 -4
  22. package/dist/federation/portable-inbox.test.mjs +4 -4
  23. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  24. package/dist/federation/portable-route-activity.test.mjs +2 -2
  25. package/dist/federation/portable.test.mjs +80 -3
  26. package/dist/federation/send.test.mjs +3 -3
  27. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  28. package/dist/federation/tasks/tasks.test.mjs +2 -2
  29. package/dist/federation/temporal.test.mjs +1 -1
  30. package/dist/federation/webfinger.test.mjs +1 -1
  31. package/dist/{federation-DC1yfb-8.cjs → federation-BVkPF8vF.cjs} +246 -31
  32. package/dist/{http-Cuk0w0rp.cjs → http-BbDZCEnR.cjs} +1 -1
  33. package/dist/{http-Dx7yMmfw.js → http-D4JQwy-3.js} +1 -1
  34. package/dist/{http-D-DpP4sd.mjs → http-SIEv_YGW.mjs} +4 -4
  35. package/dist/{key-Djyt1BFx.mjs → key-CtAhsSFI.mjs} +2 -2
  36. package/dist/{kv-cache-CUZOCSKZ.js → kv-cache-DXFAaMh2.js} +1 -1
  37. package/dist/{kv-cache-BmLTVLXc.cjs → kv-cache-DxLtK3Np.cjs} +1 -1
  38. package/dist/{kv-cache-ecUXfCqW.mjs → kv-cache-Fn7dxiMu.mjs} +1 -1
  39. package/dist/{ld-CNVWdooi.mjs → ld-BuI0RakW.mjs} +3 -3
  40. package/dist/{metrics-BnYleVpg.mjs → metrics-jr7IbGjd.mjs} +1 -1
  41. package/dist/{middleware-Bh592bk8.mjs → middleware-BWhRFmO0.mjs} +1 -1
  42. package/dist/{middleware-C2_LcbL3.mjs → middleware-BhtaUtlt.mjs} +243 -28
  43. package/dist/{middleware-BdzcazaN.js → middleware-D0t1NBIE.js} +246 -31
  44. package/dist/mod.cjs +4 -4
  45. package/dist/mod.js +4 -4
  46. package/dist/nodeinfo/handler.test.mjs +1 -1
  47. package/dist/{owner-D_BDPwm2.mjs → owner-CoNYVTQp.mjs} +3 -3
  48. package/dist/{portable-collection-hcONGyUs.mjs → portable-collection-CxP30PWK.mjs} +2 -2
  49. package/dist/{portable-collection-DwJ8YJP2.cjs → portable-collection-FLEUNFj5.cjs} +1 -1
  50. package/dist/{portable-collection-C0c0rUN9.js → portable-collection-ZczKiIeK.js} +1 -1
  51. package/dist/{portable-key-B880yVlC.mjs → portable-key-90Ux6h7F.mjs} +1 -1
  52. package/dist/{proof-z6Prgi5I.mjs → proof-Bhsb_KYD.mjs} +4 -4
  53. package/dist/{send-Cw545L_2.mjs → send-CdRJj10a.mjs} +3 -3
  54. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  55. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  56. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  57. package/dist/sig/compound-proof.test.mjs +2 -2
  58. package/dist/sig/http.test.mjs +2 -2
  59. package/dist/sig/key.test.mjs +1 -1
  60. package/dist/sig/ld.test.mjs +2 -2
  61. package/dist/sig/mod.cjs +2 -2
  62. package/dist/sig/mod.js +2 -2
  63. package/dist/sig/owner.test.mjs +1 -1
  64. package/dist/sig/portable-collection.test.mjs +1 -1
  65. package/dist/sig/portable-key.test.mjs +6 -6
  66. package/dist/sig/proof.test.mjs +5 -3
  67. package/dist/{temporal-Cy-8WCod.mjs → temporal-vBRC6bwu.mjs} +1 -1
  68. package/dist/testing/kv.test.mjs +1 -1
  69. package/dist/utils/docloader.test.mjs +2 -2
  70. package/dist/utils/kv-cache.test.mjs +1 -1
  71. package/dist/utils/mod.cjs +1 -1
  72. package/dist/utils/mod.js +1 -1
  73. package/package.json +7 -7
@@ -3,28 +3,28 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { d as isPortableUri, f as isSameObjectId, i as getPortableDid, l as isPortableId, n as getCanonicalPortableId, o as hasPortableActor, r as getGatewayKeyBase, s as isCompatibleKeyId, t as getAuthenticationOrigin, u as isPortableKeyId } from "./portable-key-id-Ca30-hOa.mjs";
5
5
  import { n as isPubliclyAddressedNode, t as isInAudience } from "./audience-CHQ-FlLD.mjs";
6
- import { n as version, t as name } from "./deno-Dj1TFhf4.mjs";
7
- import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-BnYleVpg.mjs";
6
+ import { n as version, t as name } from "./deno-62DcBMwc.mjs";
7
+ import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-jr7IbGjd.mjs";
8
8
  import { t as formatAcceptSignature } from "./accept-CPVqki78.mjs";
9
- import { c as validateCryptoKey, n as exportJwk, s as importJwk } from "./key-Djyt1BFx.mjs";
9
+ import { c as validateCryptoKey, n as exportJwk, s as importJwk } from "./key-CtAhsSFI.mjs";
10
10
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-4IJqPYos.mjs";
11
- import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-CNVWdooi.mjs";
12
- import { a as signObject, c as verifyPortableObjectProof, i as hasProofLike, s as verifyObject, t as InvalidPortableObjectIdError, u as verifyPortableObjectProofWithRoot } from "./proof-z6Prgi5I.mjs";
13
- import { a as listRequestSignatures, c as selectRequestSignature, d as validateMaxSignatures, f as verifyRequest, p as verifyRequestDetailed, s as parseRfc9421SignatureInput } from "./http-D-DpP4sd.mjs";
14
- import { t as getAuthenticatedDocumentLoader } from "./docloader-CzyJe90o.mjs";
15
- import { n as kvCache } from "./kv-cache-ecUXfCqW.mjs";
11
+ import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-BuI0RakW.mjs";
12
+ import { a as signObject, c as verifyPortableObjectProof, i as hasProofLike, s as verifyObject, t as InvalidPortableObjectIdError, u as verifyPortableObjectProofWithRoot } from "./proof-Bhsb_KYD.mjs";
13
+ import { a as listRequestSignatures, c as selectRequestSignature, d as validateMaxSignatures, f as verifyRequest, p as verifyRequestDetailed, s as parseRfc9421SignatureInput } from "./http-SIEv_YGW.mjs";
14
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-Be9TaKa_.mjs";
15
+ import { n as kvCache } from "./kv-cache-Fn7dxiMu.mjs";
16
16
  import { d as readBoundedText, l as BodyTooLargeError, t as getNodeInfo, u as MAX_BODY_SIZE } from "./client-CEnNz8O7.mjs";
17
17
  import { t as nodeInfoToJson } from "./types-DE_LV1JB.mjs";
18
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-D_BDPwm2.mjs";
19
- import { t as verifyPortableObject } from "./portable-collection-hcONGyUs.mjs";
20
- import { a as getOrderingDestination, i as sendActivityThroughGateways, n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-Cw545L_2.mjs";
21
- import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-C5rYBZ16.mjs";
18
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-CoNYVTQp.mjs";
19
+ import { t as verifyPortableObject } from "./portable-collection-CxP30PWK.mjs";
20
+ import { a as getOrderingDestination, i as sendActivityThroughGateways, n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-CdRJj10a.mjs";
21
+ import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-BmoDmjWk.mjs";
22
22
  import { t as CircuitBreaker } from "./circuit-breaker-XJ53md4S.mjs";
23
23
  import { t as buildCollectionSynchronizationHeader } from "./collection-CnnP1UAv.mjs";
24
- import { a as verifyCompoundPortableObjectProofs, i as inspectCompoundPortableObjectApplicability, r as findUnsupportedCompoundProofShape, s as verifyServedPortableObjects, t as containsCompoundPortableObject } from "./compound-proof-D0xY7MBs.mjs";
24
+ import { a as verifyCompoundPortableObjectProofs, i as inspectCompoundPortableObjectApplicability, r as findUnsupportedCompoundProofShape, s as verifyServedPortableObjects, t as containsCompoundPortableObject } from "./compound-proof-Pn8766TV.mjs";
25
25
  import { t as KvKeyCache } from "./keycache-Da8A8mZa.mjs";
26
26
  import { t as acceptsJsonLd } from "./negotiation-CCocWT_N.mjs";
27
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-Cy-8WCod.mjs";
27
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-vBRC6bwu.mjs";
28
28
  import { t as createExponentialBackoffPolicy } from "./retry-zX5xw5e0.mjs";
29
29
  import { t as TaskCodec } from "./codec-C6rTrpIp.mjs";
30
30
  import { getLogger, withContext } from "@logtape/logtape";
@@ -359,6 +359,180 @@ function serializeDataPointType(dataPointType) {
359
359
  }
360
360
  }
361
361
  //#endregion
362
+ //#region src/federation/compatible-id-warning.ts
363
+ /** Returns a verified first gateway, without dereferencing the actor. */
364
+ async function getLocalFirstGateway(context, ownerId, did) {
365
+ try {
366
+ const parsed = context.parseUri(ownerId, { portable: true });
367
+ if (parsed?.type !== "actor" || parsed.authority !== did) return null;
368
+ return firstGateway(await context.getActor(parsed.identifier), did);
369
+ } catch (error) {
370
+ getLogger([
371
+ "fedify",
372
+ "federation",
373
+ "object"
374
+ ]).debug("Could not check the first gateway of {ownerId}: {error}", {
375
+ ownerId: ownerId.href,
376
+ error
377
+ });
378
+ return null;
379
+ }
380
+ }
381
+ function firstGateway(actor, did) {
382
+ if (actor?.id == null || getPortableDid(actor.id) !== did) return null;
383
+ const gateway = actor.gateway;
384
+ return gateway != null && isGatewayUrl(gateway) ? gateway : null;
385
+ }
386
+ /** Looks for an actor already embedded in the document being published. */
387
+ function getEmbeddedFirstGateway(json, ownerId, did) {
388
+ const owner = getCanonicalPortableId(ownerId);
389
+ if (owner == null) return null;
390
+ return visitJsonMaps(json, (map) => {
391
+ const id = parseMapId(map, isJsonMap(json) ? json : null);
392
+ if (id == null || getCanonicalPortableId(id) !== owner) return;
393
+ const gateways = map.gateways ?? map["https://w3id.org/fep/ef61/gateways"];
394
+ const first = Array.isArray(gateways) ? gateways[0] : isJsonMap(gateways) && Array.isArray(gateways["@list"]) ? gateways["@list"][0] : null;
395
+ const address = typeof first === "string" ? first : isJsonMap(first) ? first.id ?? first["@id"] : null;
396
+ if (typeof address !== "string") return;
397
+ try {
398
+ const gateway = new URL(address);
399
+ if (getPortableDid(id) === did && isGatewayUrl(gateway)) return gateway;
400
+ } catch (error) {
401
+ if (!(error instanceof TypeError)) throw error;
402
+ }
403
+ }) ?? null;
404
+ }
405
+ function isJsonMap(value) {
406
+ return value != null && typeof value === "object" && !Array.isArray(value);
407
+ }
408
+ /** Warns only when the publisher's actual first gateway is known. */
409
+ function warnCompatibleId(id, did, gateway, kind) {
410
+ if (!isCompatibleEf61Iri(id) || getPortableDid(id) !== did || id.origin === gateway.origin) return;
411
+ getLogger([
412
+ "fedify",
413
+ "federation",
414
+ kind === "activity" ? "outbox" : "object"
415
+ ]).warn("The {kind} {id} has an FEP-ef61 compatible identifier on another gateway than its owner's first gateway, {gateway}. FEP-ef61 requires publishers to use the first gateway in the actor's gateways.", {
416
+ kind,
417
+ id: id.href,
418
+ gateway: gateway.origin
419
+ });
420
+ }
421
+ /** Reads the root ID without dereferencing or expanding the document. */
422
+ function getCompactRootId(json) {
423
+ return isJsonMap(json) ? parseMapId(json, json) : null;
424
+ }
425
+ /** Checks an activity and its embedded objects against each one's owner. */
426
+ async function warnCompatibleIdsInJson(json, context) {
427
+ const root = isJsonMap(json) ? json : null;
428
+ const rootOwners = root == null ? [] : ownerIds(root, "actor", root);
429
+ const gateways = /* @__PURE__ */ new Map();
430
+ const maps = [];
431
+ visitJsonMaps(json, (map, path) => {
432
+ maps.push({
433
+ map,
434
+ path
435
+ });
436
+ });
437
+ for (const { map, path } of maps) {
438
+ const id = parseMapId(map, root);
439
+ if (id == null || !isCompatibleEf61Iri(id)) continue;
440
+ const type = map.type ?? map["@type"];
441
+ if ((Array.isArray(type) ? type : [type]).some((t) => typeof t === "string" && /(?:^|[#/:])(CryptographicKey|Multikey|JsonWebKey|Key)$/.test(t))) continue;
442
+ const did = getPortableDid(id);
443
+ if (did == null) continue;
444
+ const owners = path === "" ? rootOwners : [
445
+ ...ownerIds(map, "actor", root),
446
+ ...ownerIds(map, "attributedTo", root),
447
+ ...rootOwners
448
+ ];
449
+ for (const ownerId of owners) {
450
+ if (getPortableDid(ownerId) !== did) continue;
451
+ const key = getCanonicalPortableId(ownerId);
452
+ if (key == null) continue;
453
+ let gateway = gateways.get(key);
454
+ if (gateway === void 0) {
455
+ gateway = getEmbeddedFirstGateway(json, ownerId, did) ?? (context == null ? null : await getLocalFirstGateway(context, ownerId, did));
456
+ gateways.set(key, gateway);
457
+ }
458
+ if (gateway != null) {
459
+ warnCompatibleId(id, did, gateway, path === "" ? "activity" : "object");
460
+ break;
461
+ }
462
+ }
463
+ }
464
+ }
465
+ function ownerIds(map, term, root) {
466
+ const iri = `https://www.w3.org/ns/activitystreams#${term}`;
467
+ const values = [map[term], map[iri]];
468
+ for (const context of [map["@context"], root?.["@context"]]) for (const [alias, definition] of localContextEntries(context)) {
469
+ const mapped = typeof definition === "string" ? definition : isJsonMap(definition) ? definition["@id"] : null;
470
+ if (mapped === iri || mapped === `as:${term}`) values.push(map[alias]);
471
+ }
472
+ return values.flatMap((value) => parseOwnerIds(value, root));
473
+ }
474
+ function localContextEntries(context) {
475
+ if (Array.isArray(context)) return context.flatMap(localContextEntries);
476
+ return isJsonMap(context) ? Object.entries(context) : [];
477
+ }
478
+ function parseOwnerIds(value, root) {
479
+ if (Array.isArray(value)) return value.flatMap((item) => parseOwnerIds(item, root));
480
+ if (isJsonMap(value)) {
481
+ const id = parseMapId(value, root);
482
+ return id == null ? [] : [id];
483
+ }
484
+ if (typeof value !== "string") return [];
485
+ try {
486
+ return [parseIri(value)];
487
+ } catch (error) {
488
+ if (error instanceof TypeError) return [];
489
+ throw error;
490
+ }
491
+ }
492
+ function parseMapId(map, root) {
493
+ let raw = map["@id"] ?? map.id;
494
+ for (const context of [map["@context"], root?.["@context"]]) for (const [alias, definition] of localContextEntries(context)) if ((typeof definition === "string" ? definition : isJsonMap(definition) ? definition["@id"] : null) === "@id" && typeof map[alias] === "string") {
495
+ raw = map[alias];
496
+ break;
497
+ }
498
+ if (typeof raw !== "string") return null;
499
+ try {
500
+ return parseIri(raw);
501
+ } catch (error) {
502
+ if (error instanceof TypeError) return null;
503
+ throw error;
504
+ }
505
+ }
506
+ function visitJsonMaps(json, visit) {
507
+ const seen = /* @__PURE__ */ new Set();
508
+ const pending = [{
509
+ value: json,
510
+ path: ""
511
+ }];
512
+ while (pending.length > 0) {
513
+ const { value, path } = pending.pop();
514
+ if (value == null || typeof value !== "object" || seen.has(value)) continue;
515
+ seen.add(value);
516
+ if (Array.isArray(value)) {
517
+ for (let i = value.length - 1; i >= 0; i--) pending.push({
518
+ value: value[i],
519
+ path: `${path}/${i}`
520
+ });
521
+ continue;
522
+ }
523
+ const map = value;
524
+ const result = visit(map, path);
525
+ if (result !== void 0) return result;
526
+ for (const key of Object.keys(map).reverse()) {
527
+ if (key === "proof" || key === "@context") continue;
528
+ pending.push({
529
+ value: map[key],
530
+ path: `${path}/${key}`
531
+ });
532
+ }
533
+ }
534
+ }
535
+ //#endregion
362
536
  //#region src/federation/inbox.ts
363
537
  async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
364
538
  const logger = getLogger([
@@ -1202,6 +1376,7 @@ async function handleObject(request, { values, context, objectDispatcher, author
1202
1376
  Vary: "Accept"
1203
1377
  }
1204
1378
  });
1379
+ await warnServedCompatibleObject(context, object, getCompactRootId(jsonLd));
1205
1380
  return new Response(JSON.stringify(jsonLd), { headers: {
1206
1381
  "Content-Type": "application/activity+json",
1207
1382
  Vary: "Accept"
@@ -1305,6 +1480,7 @@ async function handlePortableObject(request, { values, context, objectDispatcher
1305
1480
  return portableObjectInternalServerError(request);
1306
1481
  }
1307
1482
  }
1483
+ if (!tombstone) await warnServedCompatibleObject(context, object, servedId);
1308
1484
  const headers = new Headers({
1309
1485
  "Content-Type": PORTABLE_OBJECT_CONTENT_TYPE,
1310
1486
  Vary: "Accept"
@@ -1315,6 +1491,32 @@ async function handlePortableObject(request, { values, context, objectDispatcher
1315
1491
  headers
1316
1492
  });
1317
1493
  }
1494
+ async function warnServedCompatibleObject(context, object, serializedId) {
1495
+ try {
1496
+ if (isActor(object)) return;
1497
+ const id = typeof serializedId === "string" ? parseIri(serializedId) : serializedId ?? object.id;
1498
+ if (id == null || !isCompatibleEf61Iri(id)) return;
1499
+ const did = getPortableDid(id);
1500
+ if (did == null) return;
1501
+ const ownerIds = object instanceof Activity ? object.actorIds : object.attributionIds;
1502
+ for (const ownerId of ownerIds) {
1503
+ if (getPortableDid(ownerId) !== did) continue;
1504
+ const gateway = await getLocalFirstGateway(context, ownerId, did);
1505
+ if (gateway == null) continue;
1506
+ warnCompatibleId(id, did, gateway, object instanceof Activity ? "activity" : "object");
1507
+ break;
1508
+ }
1509
+ } catch (error) {
1510
+ getLogger([
1511
+ "fedify",
1512
+ "federation",
1513
+ "object"
1514
+ ]).debug("Could not check the first gateway of {objectId}: {error}", {
1515
+ objectId: object.id?.href,
1516
+ error
1517
+ });
1518
+ }
1519
+ }
1318
1520
  function isRequestedPortableObject(object, canonicalId) {
1319
1521
  return object.id != null && getCanonicalPortableId(object.id) === canonicalId;
1320
1522
  }
@@ -1418,13 +1620,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
1418
1620
  * @param parameters The parameters for handling the collection.
1419
1621
  * @returns A promise that resolves to an HTTP response.
1420
1622
  */
1421
- async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, portable, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
1422
- const spanName = name$2.trim().replace(/\s+/g, "_");
1623
+ async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, portable, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
1624
+ const spanName = name$1.trim().replace(/\s+/g, "_");
1423
1625
  tracerProvider = tracerProvider ?? trace.getTracerProvider();
1424
1626
  const tracer = tracerProvider.getTracer(name, version);
1425
1627
  const cursor = new URL(request.url).searchParams.get("cursor");
1426
1628
  const metricBase = {
1427
- kind: getCollectionMetricKind(name$2),
1629
+ kind: getCollectionMetricKind(name$1),
1428
1630
  page: cursor != null,
1429
1631
  dispatcher: "built_in"
1430
1632
  };
@@ -1470,7 +1672,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
1470
1672
  span.setStatus({ code: SpanStatusCode.ERROR });
1471
1673
  return await onNotFound(request);
1472
1674
  }
1473
- const items = filterCollectionItems(page.items, name$2, filterPredicate);
1675
+ const items = filterCollectionItems(page.items, name$1, filterPredicate);
1474
1676
  itemCount = items.length;
1475
1677
  span.setAttribute("fedify.collection.items", itemCount);
1476
1678
  return items;
@@ -1512,7 +1714,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
1512
1714
  } else {
1513
1715
  const uri = new URL(portable == null ? baseUri : pageBase);
1514
1716
  uri.searchParams.set("cursor", cursor);
1515
- const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
1717
+ const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
1516
1718
  kind: SpanKind.SERVER,
1517
1719
  attributes: {
1518
1720
  "activitypub.collection.id": uri.href,
@@ -1528,7 +1730,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
1528
1730
  span.setStatus({ code: SpanStatusCode.ERROR });
1529
1731
  return await onNotFound(request);
1530
1732
  }
1531
- const items = filterCollectionItems(page.items, name$2, filterPredicate);
1733
+ const items = filterCollectionItems(page.items, name$1, filterPredicate);
1532
1734
  itemCount = items.length;
1533
1735
  span.setAttribute("fedify.collection.items", itemCount);
1534
1736
  return {
@@ -1740,7 +1942,7 @@ async function handleOutbox(request, { identifier, context: ctx, outboxContextFa
1740
1942
  headers: { "Content-Type": "text/plain; charset=utf-8" }
1741
1943
  });
1742
1944
  }
1743
- if (!activity.actorIds.every((actorId) => actorId.href === expectedActorId.href)) {
1945
+ if (!activity.actorIds.every((actorId) => isSameObjectId(actorId, expectedActorId))) {
1744
1946
  const error = /* @__PURE__ */ new Error("The activity actor does not match the outbox owner.");
1745
1947
  logger.error("The posted activity actor does not match outbox owner {identifier}.", {
1746
1948
  identifier,
@@ -2719,8 +2921,8 @@ var CustomCollectionHandler = class {
2719
2921
  * @param CollectionPage The CollectionPage constructor.
2720
2922
  * @param filterPredicate Optional filter predicate for items.
2721
2923
  */
2722
- constructor(name$1, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate, portable) {
2723
- this.name = name$1;
2924
+ constructor(name$2, values, context, callbacks, tracerProvider = trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate, portable) {
2925
+ this.name = name$2;
2724
2926
  this.values = values;
2725
2927
  this.context = context;
2726
2928
  this.callbacks = callbacks;
@@ -7144,17 +7346,26 @@ var ContextImpl = class ContextImpl {
7144
7346
  activityId: activity.id?.href,
7145
7347
  activity
7146
7348
  });
7349
+ const activityJsonLd = await activity.toJsonLd({
7350
+ format: "compact",
7351
+ contextLoader: this.contextLoader
7352
+ });
7353
+ try {
7354
+ await warnCompatibleIdsInJson(activityJsonLd, this instanceof RequestContextImpl && !this.isInActorDispatcher() ? this : void 0);
7355
+ } catch (error) {
7356
+ logger.debug("Could not check the first gateway of activity {activityId}: {error}", {
7357
+ activityId: activity.id?.href,
7358
+ error
7359
+ });
7360
+ }
7147
7361
  if (this.federation.fanoutQueue == null || options.immediate || options.fanout === "skip" || (options.fanout ?? "auto") === "auto" && globalThis.Object.keys(inboxes).length < FANOUT_THRESHOLD) {
7148
7362
  await this.federation.sendActivity(keys, inboxes, activity, {
7149
7363
  ...opts,
7364
+ activityJsonLd,
7150
7365
  normalizeExistingProofs: proofCreated || options.normalizeExistingProofs
7151
7366
  });
7152
7367
  return true;
7153
7368
  }
7154
- const activityJsonLd = await activity.toJsonLd({
7155
- format: "compact",
7156
- contextLoader: this.contextLoader
7157
- });
7158
7369
  assertSupportedCompoundProofShape(activityJsonLd, activity.id?.href);
7159
7370
  const keyJwkPairs = await Promise.all(keys.map(async ({ keyId, privateKey }) => ({
7160
7371
  keyId: keyId.href,
@@ -7337,6 +7548,10 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
7337
7548
  #invokedFromActorDispatcher;
7338
7549
  #invokedFromObjectDispatcher;
7339
7550
  request;
7551
+ /** Whether a diagnostic actor lookup would re-enter the actor dispatcher. */
7552
+ isInActorDispatcher() {
7553
+ return this.#invokedFromActorDispatcher != null;
7554
+ }
7340
7555
  url = void 0;
7341
7556
  portableRequest;
7342
7557
  constructor(options) {
@@ -7787,4 +8002,4 @@ function getRequestId(request) {
7787
8002
  return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
7788
8003
  }
7789
8004
  //#endregion
7790
- export { handleNodeInfo as C, autoIdAssigner as E, handleBenchmarkTrigger as S, actorDehydrator as T, coversDelivery as _, OutboxContextImpl as a, PORTABLE_OBJECT_CONTENT_TYPE as b, assertPortableActorActivity as c, handleCustomCollection as d, handleInbox as f, respondWithObjectIfAcceptable as g, respondWithObject as h, KvSpecDeterminer as i, handleActor as l, handleOutbox as m, FederationImpl as n, createFederation as o, handleObject as p, InboxContextImpl as r, handleWebFinger as s, ContextImpl as t, handleCollection as u, forwardPortableInboxActivity as v, handleNodeInfoJrd as w, parseHashlinkGatewayRequest as x, resolvePortableInboxForwardingOptions as y };
8005
+ export { getEmbeddedFirstGateway as C, handleNodeInfoJrd as D, handleNodeInfo as E, actorDehydrator as O, getCompactRootId as S, handleBenchmarkTrigger as T, coversDelivery as _, OutboxContextImpl as a, PORTABLE_OBJECT_CONTENT_TYPE as b, assertPortableActorActivity as c, handleCustomCollection as d, handleInbox as f, respondWithObjectIfAcceptable as g, respondWithObject as h, KvSpecDeterminer as i, autoIdAssigner as k, handleActor as l, handleOutbox as m, FederationImpl as n, createFederation as o, handleObject as p, InboxContextImpl as r, handleWebFinger as s, ContextImpl as t, handleCollection as u, forwardPortableInboxActivity as v, warnCompatibleIdsInJson as w, parseHashlinkGatewayRequest as x, resolvePortableInboxForwardingOptions as y };