@fedify/fedify 2.4.0-dev.2193 → 2.4.0-dev.2212

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 (95) hide show
  1. package/dist/audience-CHQ-FlLD.mjs +95 -0
  2. package/dist/{builder-0onlMY4u.mjs → builder-CKAVFvcH.mjs} +2 -2
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/public-audience.test.mjs +1 -1
  6. package/dist/compat/transformers.test.mjs +1 -1
  7. package/dist/{compound-proof-B-Fk6Slo.mjs → compound-proof-BP5SOdXv.mjs} +2 -2
  8. package/dist/{context-B2rlkkyJ.mjs → context-8Tu9kea0.mjs} +2 -0
  9. package/dist/{context-CDcAgm4l.d.cts → context-Bl_Y7T_3.d.cts} +80 -1
  10. package/dist/{context-9T--NSo4.d.ts → context-DMd11IAu.d.ts} +80 -1
  11. package/dist/{deno-DB_Sh0LK.mjs → deno-BKp9fYB3.mjs} +1 -1
  12. package/dist/{docloader-DpWYYY6a.mjs → docloader-BjJreYQU.mjs} +3 -3
  13. package/dist/federation/audience.test.d.mts +2 -0
  14. package/dist/federation/audience.test.mjs +130 -0
  15. package/dist/federation/builder.test.mjs +1 -1
  16. package/dist/federation/compound-fanout.test.mjs +4 -4
  17. package/dist/federation/handler-compound-proof.test.mjs +5 -5
  18. package/dist/federation/handler.test.mjs +7 -7
  19. package/dist/federation/hashlink-media.test.mjs +3 -3
  20. package/dist/federation/idempotency.test.mjs +2 -2
  21. package/dist/federation/metrics.test.mjs +2 -2
  22. package/dist/federation/middleware.test.mjs +9 -9
  23. package/dist/federation/mod.cjs +1 -1
  24. package/dist/federation/mod.d.cts +3 -3
  25. package/dist/federation/mod.d.ts +3 -3
  26. package/dist/federation/mod.js +1 -1
  27. package/dist/federation/outgoing-proof.test.mjs +6 -6
  28. package/dist/federation/portable-actor.test.mjs +4 -4
  29. package/dist/federation/portable-chain.test.mjs +4 -4
  30. package/dist/federation/portable-collection.test.mjs +8 -6
  31. package/dist/federation/portable-delivery.test.d.mts +2 -0
  32. package/dist/federation/portable-delivery.test.mjs +746 -0
  33. package/dist/federation/portable-gateway-keys.test.mjs +6 -6
  34. package/dist/federation/portable-inbox.test.mjs +5 -5
  35. package/dist/federation/portable-parse-uri.test.mjs +2 -2
  36. package/dist/federation/portable-route-activity.test.mjs +3 -3
  37. package/dist/federation/portable.test.mjs +234 -19
  38. package/dist/federation/send.test.mjs +4 -4
  39. package/dist/federation/tasks/enqueue.test.mjs +2 -2
  40. package/dist/federation/tasks/tasks.test.mjs +3 -3
  41. package/dist/federation/temporal.test.mjs +1 -1
  42. package/dist/federation/webfinger.test.mjs +3 -3
  43. package/dist/{federation-BsGo0EH2.cjs → federation-B-0EXZPH.cjs} +679 -35
  44. package/dist/{http-DQf9Mw4V.mjs → http--IGUT1mg.mjs} +5 -5
  45. package/dist/{http-BEivI0TS.cjs → http-ByBlnY3D.cjs} +1 -1
  46. package/dist/{http-BGmxtpo2.js → http-Di8yiDMY.js} +1 -1
  47. package/dist/{key-DA3Yx8p2.mjs → key-ChfV9C3v.mjs} +2 -2
  48. package/dist/{kv-cache-1hafSoqH.cjs → kv-cache-BDwUzxBJ.cjs} +1 -1
  49. package/dist/{kv-cache-OFBvrKh3.mjs → kv-cache-DFIUR-E5.mjs} +1 -1
  50. package/dist/{kv-cache-DduYhpee.js → kv-cache-DpwDPt9V.js} +1 -1
  51. package/dist/{ld-C15yzuY6.mjs → ld-DBBVabBC.mjs} +3 -3
  52. package/dist/{metrics-B1H23mlq.mjs → metrics-DPQKFkiJ.mjs} +1 -1
  53. package/dist/{middleware-BqqycC0P.mjs → middleware-BW7-tHAG.mjs} +422 -44
  54. package/dist/{middleware-BZQnf0nI.mjs → middleware-G9zpxUsD.mjs} +1 -1
  55. package/dist/{middleware-ClaUFaLb.js → middleware-VXGLiHtY.js} +681 -37
  56. package/dist/{mod-B3OBLj5g.d.ts → mod-BVNGHgVr.d.ts} +1 -1
  57. package/dist/{mod-D58KCqUg.d.cts → mod-Cyy1Wahy.d.cts} +1 -1
  58. package/dist/mod.cjs +4 -4
  59. package/dist/mod.d.cts +4 -4
  60. package/dist/mod.d.ts +4 -4
  61. package/dist/mod.js +4 -4
  62. package/dist/nodeinfo/client.test.mjs +1 -1
  63. package/dist/nodeinfo/handler.test.mjs +2 -2
  64. package/dist/{owner-BVpZBM-V.mjs → owner-BW5VsXG3.mjs} +3 -3
  65. package/dist/{portable-collection-BFSShzwh.cjs → portable-collection-CXcAlwei.cjs} +1 -1
  66. package/dist/{portable-collection-Dn2GdZdI.mjs → portable-collection-Ct99JsBe.mjs} +2 -2
  67. package/dist/{portable-collection-D535cSz_.js → portable-collection-FTPa5D91.js} +1 -1
  68. package/dist/{portable-key-Bmwmq7Y6.mjs → portable-key-U3lW06QJ.mjs} +1 -1
  69. package/dist/{proof-DrJWKeOl.mjs → proof-MFz2960e.mjs} +4 -4
  70. package/dist/{send-CT3a-0Vd.mjs → send-Duj8xys1.mjs} +191 -10
  71. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  72. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  73. package/dist/sig/compound-proof-verification.test.mjs +3 -3
  74. package/dist/sig/compound-proof.test.mjs +3 -3
  75. package/dist/sig/http.test.mjs +3 -3
  76. package/dist/sig/key-owner-evidence.test.mjs +2 -2
  77. package/dist/sig/key.test.mjs +2 -2
  78. package/dist/sig/ld.test.mjs +2 -2
  79. package/dist/sig/mod.cjs +2 -2
  80. package/dist/sig/mod.js +2 -2
  81. package/dist/sig/owner.test.mjs +1 -1
  82. package/dist/sig/portable-collection.test.mjs +2 -2
  83. package/dist/sig/portable-key-id.test.mjs +2 -2
  84. package/dist/sig/portable-key.test.mjs +7 -7
  85. package/dist/sig/proof.test.mjs +4 -4
  86. package/dist/{temporal-DNqluyEc.mjs → temporal-CVcPKB1N.mjs} +1 -1
  87. package/dist/testing/kv.test.mjs +1 -1
  88. package/dist/testing/mod.d.mts +79 -0
  89. package/dist/testing/mod.mjs +2 -2
  90. package/dist/{testing-C1itIjqW.mjs → testing-t8xDeJZ5.mjs} +1 -1
  91. package/dist/utils/docloader.test.mjs +6 -6
  92. package/dist/utils/kv-cache.test.mjs +2 -2
  93. package/dist/utils/mod.cjs +1 -1
  94. package/dist/utils/mod.js +1 -1
  95. package/package.json +6 -6
@@ -0,0 +1,95 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { n as getCanonicalPortableId } from "./portable-key-id-Ca30-hOa.mjs";
5
+ import { PUBLIC_COLLECTION } from "@fedify/vocab";
6
+ //#region src/federation/audience.ts
7
+ const AS_NAMESPACE = "https://www.w3.org/ns/activitystreams#";
8
+ /**
9
+ * The addressing properties that determine the audience of an object.
10
+ */
11
+ const ADDRESSING_PROPERTIES = [
12
+ "to",
13
+ "cc",
14
+ "bto",
15
+ "bcc",
16
+ "audience"
17
+ ];
18
+ /**
19
+ * The IDs that Fedify treats as the public collection. Besides the full IRI,
20
+ * the compact forms `as:Public` and `Public` are reserved as public for
21
+ * compatibility with documents whose compact forms survive JSON-LD expansion,
22
+ * e.g., bare `Public`, which expands to a relative IRI.
23
+ */
24
+ const PUBLIC_IDS = /* @__PURE__ */ new Set([
25
+ PUBLIC_COLLECTION.href,
26
+ "as:Public",
27
+ "Public"
28
+ ]);
29
+ /**
30
+ * Checks whether an expanded JSON-LD node is addressed to the public
31
+ * collection, i.e., any of its `to`, `cc`, `bto`, `bcc`, and `audience` has
32
+ * the public collection. Only the node itself is checked, not the nodes
33
+ * embedded in it.
34
+ * @param node The expanded JSON-LD node.
35
+ * @returns `true` if the node is publicly addressed.
36
+ * @internal
37
+ */
38
+ function isPubliclyAddressedNode(node) {
39
+ for (const property of ADDRESSING_PROPERTIES) {
40
+ const values = node[AS_NAMESPACE + property];
41
+ if (!Array.isArray(values)) continue;
42
+ for (const value of values) if (typeof value === "object" && value != null && "@id" in value && typeof value["@id"] === "string" && PUBLIC_IDS.has(value["@id"])) return true;
43
+ }
44
+ return false;
45
+ }
46
+ function getAudienceIds(object) {
47
+ return [
48
+ ...object.toIds,
49
+ ...object.ccIds,
50
+ ...object.btoIds,
51
+ ...object.bccIds,
52
+ ...object.audienceIds
53
+ ];
54
+ }
55
+ /**
56
+ * Checks whether two actor IDs refer to the same actor. [FEP-ef61] portable
57
+ * IDs are compared by their canonical forms, so an `ap:` URI, an `ap+ef61:`
58
+ * URI, and a compatible identifier on any gateway are the same actor if they
59
+ * have the same canonical portable ID. Other IDs are compared as they are.
60
+ *
61
+ * [FEP-ef61]: https://w3id.org/fep/ef61
62
+ */
63
+ function isSameActorId(a, b) {
64
+ const canonicalA = getCanonicalPortableId(a);
65
+ const canonicalB = getCanonicalPortableId(b);
66
+ if (canonicalA != null || canonicalB != null) return canonicalA === canonicalB;
67
+ return a.href === b.href;
68
+ }
69
+ /**
70
+ * Checks whether an actor belongs to the audience of an object: the object is
71
+ * publicly addressed, the actor is one of its addressees, or the actor is
72
+ * a member of one of them according to `isMember()`.
73
+ * @param object The object whose audience is checked.
74
+ * @param actor The actor to check, or `null` if there is no actor. No actor
75
+ * belongs to the audience of an object that is not public.
76
+ * @param options Options for checking the audience.
77
+ * @returns `true` if the actor belongs to the audience of the object.
78
+ * @internal
79
+ */
80
+ async function isInAudience(object, actor, options = {}) {
81
+ const addressees = getAudienceIds(object);
82
+ if (addressees.some((id) => PUBLIC_IDS.has(id.href))) return true;
83
+ const actorId = actor?.id;
84
+ if (actor == null || actorId == null) return false;
85
+ const others = [];
86
+ for (const addressee of addressees) {
87
+ if (isSameActorId(addressee, actorId)) return true;
88
+ others.push(addressee);
89
+ }
90
+ if (options.isMember == null) return false;
91
+ for (const addressee of others) if (await options.isMember(addressee, actor)) return true;
92
+ return false;
93
+ }
94
+ //#endregion
95
+ export { isPubliclyAddressedNode as n, isInAudience as t };
@@ -1,8 +1,8 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-DB_Sh0LK.mjs";
5
4
  import { i as getPortableDid, l as isPortableId, n as getCanonicalPortableId } from "./portable-key-id-Ca30-hOa.mjs";
5
+ import { n as version, t as name } from "./deno-BKp9fYB3.mjs";
6
6
  import { t as ActivityListenerSet } from "./activity-listener-C-BSbKI7.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { Router, RouterError, assertPath } from "@fedify/uri-template";
@@ -89,7 +89,7 @@ var FederationBuilderImpl = class {
89
89
  * specified.
90
90
  */
91
91
  async build(options) {
92
- const { FederationImpl } = await import("./middleware-BZQnf0nI.mjs");
92
+ const { FederationImpl } = await import("./middleware-G9zpxUsD.mjs");
93
93
  const f = new FederationImpl(options);
94
94
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
95
95
  f.router = this.router.clone();
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { $t as ActivityTransformer, n as Context } from "../context-CDcAgm4l.cjs";
2
+ import { en as ActivityTransformer, n as Context } from "../context-Bl_Y7T_3.cjs";
3
3
  import { Activity } from "@fedify/vocab";
4
4
 
5
5
  //#region src/compat/transformers.d.ts
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { $t as ActivityTransformer, n as Context } from "../context-9T--NSo4.js";
2
+ import { en as ActivityTransformer, n as Context } from "../context-DMd11IAu.js";
3
3
  import { Activity } from "@fedify/vocab";
4
4
 
5
5
  //#region src/compat/transformers.d.ts
@@ -5,8 +5,8 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
5
  import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
6
6
  import { t as normalizePublicAudience } from "../public-audience--klBIOk1.mjs";
7
7
  import { Create, Follow, Note, PUBLIC_COLLECTION } from "@fedify/vocab";
8
- import { test } from "@fedify/fixture";
9
8
  import { getDocumentLoader, preloadedContexts } from "@fedify/vocab-runtime";
9
+ import { test } from "@fedify/fixture";
10
10
  //#region src/compat/public-audience.test.ts
11
11
  const PUBLIC_URI = PUBLIC_COLLECTION.href;
12
12
  const AS_CONTEXT = "https://www.w3.org/ns/activitystreams";
@@ -5,7 +5,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
7
7
  import { t as assert } from "../assert-D1Eafmd_.mjs";
8
- import { E as autoIdAssigner, T as actorDehydrator, n as FederationImpl } from "../middleware-BqqycC0P.mjs";
8
+ import { E as autoIdAssigner, T as actorDehydrator, n as FederationImpl } from "../middleware-BW7-tHAG.mjs";
9
9
  import { Follow, Person } from "@fedify/vocab";
10
10
  import { test } from "@fedify/fixture";
11
11
  //#region src/compat/transformers.test.ts
@@ -3,8 +3,8 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { a as getRawCanonicalPortableId, l as isPortableId, n as getCanonicalPortableId } from "./portable-key-id-Ca30-hOa.mjs";
5
5
  import { n as preloadedOnlyDocumentLoader } from "./public-audience--klBIOk1.mjs";
6
- import { c as getNormalizationContextLoader } from "./ld-C15yzuY6.mjs";
7
- import { l as verifyPortableObjectProofPolicy, n as classifyFep2277CoreType, o as verifyMapLocalProof } from "./proof-DrJWKeOl.mjs";
6
+ import { c as getNormalizationContextLoader } from "./ld-DBBVabBC.mjs";
7
+ import { l as verifyPortableObjectProofPolicy, n as classifyFep2277CoreType, o as verifyMapLocalProof } from "./proof-MFz2960e.mjs";
8
8
  import { parseIri, preloadedContexts } from "@fedify/vocab-runtime";
9
9
  import jsonld from "@fedify/vocab-runtime/jsonld";
10
10
  //#region src/sig/compound-proof.ts
@@ -2,6 +2,7 @@ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as mockDocumentLoader } from "./dist-DJQM4u7i.mjs";
5
+ import { t as isInAudience } from "./audience-CHQ-FlLD.mjs";
5
6
  import { RouterError } from "@fedify/uri-template";
6
7
  import { lookupObject, traverseCollection } from "@fedify/vocab";
7
8
  import { lookupWebFinger } from "@fedify/webfinger";
@@ -105,6 +106,7 @@ function createRequestContext(args) {
105
106
  getObject: args.getObject ?? (() => Promise.resolve(null)),
106
107
  getSignedKey: args.getSignedKey ?? (() => Promise.resolve(null)),
107
108
  getSignedKeyOwner: args.getSignedKeyOwner ?? (() => Promise.resolve(null)),
109
+ isSignedByAudience: args.isSignedByAudience ?? (async (object, options) => await isInAudience(object, null) || await isInAudience(object, args.getSignedKeyOwner == null ? null : await args.getSignedKeyOwner(options ?? {}), options)),
108
110
  sendActivity: args.sendActivity ?? ((_params) => {
109
111
  throw new Error("Not implemented");
110
112
  })
@@ -1257,6 +1257,18 @@ interface FanoutMessage {
1257
1257
  readonly inboxes: Readonly<Record<string, {
1258
1258
  readonly actorIds: readonly string[];
1259
1259
  readonly sharedInbox: boolean;
1260
+ /**
1261
+ * The canonical form of the FEP-ef61 portable inbox that the inbox
1262
+ * stands for.
1263
+ * @internal
1264
+ */
1265
+ readonly portableInbox?: string;
1266
+ /**
1267
+ * The compatible identifiers of the portable inbox on each gateway to
1268
+ * deliver through, in order.
1269
+ * @internal
1270
+ */
1271
+ readonly gatewayInboxes?: readonly string[];
1260
1272
  }>>;
1261
1273
  readonly activity: unknown;
1262
1274
  readonly activityId?: string;
@@ -1300,6 +1312,27 @@ interface OutboxMessage {
1300
1312
  * @internal
1301
1313
  */
1302
1314
  readonly circuitHeldSince?: string;
1315
+ /**
1316
+ * The canonical form of the FEP-ef61 portable inbox that {@link inbox}, its
1317
+ * compatible identifier on a gateway, stands for.
1318
+ * @internal
1319
+ */
1320
+ readonly portableInbox?: string;
1321
+ /**
1322
+ * The compatible identifiers of the FEP-ef61 portable inbox on each gateway
1323
+ * that is still to be tried, in order. {@link inbox} is the first one.
1324
+ * If there is more than one, the activity is delivered through the first
1325
+ * gateway that accepts it.
1326
+ * @internal
1327
+ */
1328
+ readonly gatewayInboxes?: readonly string[];
1329
+ /**
1330
+ * The times before which each of {@link gatewayInboxes} must not be tried
1331
+ * again, as their responses' `Retry-After` headers requested, keyed by
1332
+ * the inbox URL, in ISO 8601.
1333
+ * @internal
1334
+ */
1335
+ readonly gatewayNotBefore?: Readonly<Record<string, string>>;
1303
1336
  readonly traceContext: Readonly<Record<string, string>>;
1304
1337
  }
1305
1338
  /**
@@ -4203,6 +4236,52 @@ interface RequestContext<TContextData> extends Context<TContextData> {
4203
4236
  * [instance actor]: https://swicg.github.io/activitypub-http-signature/#instance-actor
4204
4237
  */
4205
4238
  getSignedKeyOwner(options: GetKeyOwnerOptions): Promise<Actor | null>;
4239
+ /**
4240
+ * Checks whether the request is signed by an actor who belongs to
4241
+ * the intended audience of an object, i.e., the owner of the signed key
4242
+ * (see {@link RequestContext.getSignedKeyOwner}) is one of the object's
4243
+ * `to`, `cc`, `bto`, `bcc`, and `audience`, or a member of one of them
4244
+ * according to the `isMember` option.
4245
+ *
4246
+ * Everyone belongs to the audience of a publicly addressed object, so it
4247
+ * returns `true` for such an object even if the request is not signed.
4248
+ *
4249
+ * This can be used in authorization predicates for serving non-public
4250
+ * objects, which [FEP-ef61] requires of gateways serving portable objects.
4251
+ * [FEP-ef61] portable IDs are compared by their canonical forms, so
4252
+ * an `ap:` URI, an `ap+ef61:` URI, and a compatible identifier on any
4253
+ * gateway refer to the same actor.
4254
+ *
4255
+ * [FEP-ef61]: https://w3id.org/fep/ef61
4256
+ *
4257
+ * @param object The object whose audience is checked.
4258
+ * @param options Options for checking the audience.
4259
+ * @returns `true` if the object is publicly addressed, or the request is
4260
+ * signed by an actor who belongs to its audience.
4261
+ * @since 2.4.0
4262
+ */
4263
+ isSignedByAudience(object: Object$1, options?: IsSignedByAudienceOptions): Promise<boolean>;
4264
+ }
4265
+ /**
4266
+ * Options for {@link RequestContext.isSignedByAudience} method.
4267
+ * @since 2.4.0
4268
+ */
4269
+ interface IsSignedByAudienceOptions extends GetKeyOwnerOptions {
4270
+ /**
4271
+ * Checks whether the actor who signed the request is a member of
4272
+ * an addressee of the object that is not the actor itself, e.g.,
4273
+ * a followers collection. Fedify does not resolve collections by itself,
4274
+ * as only the application can tell their members reliably.
4275
+ *
4276
+ * It is called for each such addressee in order until it returns `true`.
4277
+ * If it is omitted, only the actors that the object addresses directly
4278
+ * belong to its audience.
4279
+ * @param addressee The ID of the addressee, e.g., the ID of a followers
4280
+ * collection.
4281
+ * @param actor The actor who signed the request.
4282
+ * @returns `true` if the actor is a member of the addressee.
4283
+ */
4284
+ isMember?: (addressee: URL, actor: Actor) => boolean | Promise<boolean>;
4206
4285
  }
4207
4286
  /**
4208
4287
  * A context for inbox listeners.
@@ -4635,4 +4714,4 @@ interface ActorKeyPair extends CryptoKeyPair {
4635
4714
  readonly multikey: Multikey;
4636
4715
  }
4637
4716
  //#endregion
4638
- export { respondWithObjectIfAcceptable as $, ActivityTransformer as $t, InboxListenerSetters as A, HashlinkMediaDispatcher as At, Message as B, OutboxListenerErrorHandler as Bt, FederationBuilder as C, AuthorizePredicate as Ct, IdempotencyKeyCallback as D, CustomCollectionCounter as Dt, FederationStartQueueOptions as E, CollectionDispatcher as Et, Rfc6570Expression as F, NodeInfoDispatcher as Ft, TaskHandler as G, UnverifiedActivityHandler as Gt, TaskDefinition as H, PortableActorIdMapper as Ht, FederationKvPrefixes as I, ObjectAuthorizePredicate as It, RetryContext as J, SendActivityError as Jt, TaskRegistry as K, UnverifiedActivityReason as Kt, FederationOrigin as L, ObjectDispatcher as Lt, ObjectCallbackSetters as M, InboxErrorHandler as Mt, OutboxListenerSetters as N, InboxListener as Nt, IdempotencyStrategy as O, CustomCollectionCursor as Ot, PortableInboxForwardingOptions as P, MediaUploaderCallback as Pt, respondWithObject as Q, digest as Qt, FederationQueueOptions as R, OutboxErrorHandler as Rt, FederationBenchmarkOptions as S, ActorKeyPairsDispatcher as St, FederationOptions as T, CollectionCursor as Tt, TaskDefinitionOptions as U, PortableCollectionOwnerMapper as Ut, createFederationBuilder as V, OutboxPermanentFailureHandler as Vt, TaskEnqueueOptions as W, SharedInboxKeyDispatcher as Wt, createExponentialBackoffPolicy as X, PageItems as Xt, RetryPolicy as Y, SenderKeyPair as Yt, RespondWithObjectOptions as Z, buildCollectionSynchronizationHeader as Zt, CollectionCallbackSetters as _, normalizeCircuitBreakerOptions as _t, GetObjectOptions as a, MessageQueueListenOptions as at, Federatable as b, ActorDispatcher as bt, OutboxContext as c, CircuitBreakerActivityDrop as ct, PortableRequest as d, CircuitBreakerFailurePolicy as dt, InProcessMessageQueue as et, RequestContext as f, CircuitBreakerKvState as ft, ActorCallbackSetters as g, NormalizedCircuitBreakerOptions as gt, SendActivityOptionsForCollection as h, CircuitBreakerStateChange as ht, GetActorOptions as i, MessageQueueEnqueueOptions as it, MediaUploaderSetters as j, HashlinkMediaRequest as jt, InboxChallengePolicy as k, CustomCollectionDispatcher as kt, ParseUriOptions as l, CircuitBreakerBeforeSendDecision as lt, SendActivityOptions as m, CircuitBreakerState as mt, Context as n, MessageQueue as nt, GetSignedKeyOptions as o, ParallelMessageQueue as ot, RouteActivityOptions as p, CircuitBreakerOptions as pt, CreateExponentialBackoffPolicyOptions as q, WebFingerLinksDispatcher as qt, ForwardActivityOptions as r, MessageQueueDepth as rt, InboxContext as s, CircuitBreaker as st, ActorKeyPair as t, InProcessMessageQueueOptions as tt, ParseUriResult as u, CircuitBreakerCreateOptions as ut, ConstructorWithTypeId as v, parseCircuitBreakerKvState as vt, FederationFetchOptions as w, CollectionCounter as wt, Federation as x, ActorHandleMapper as xt, CustomCollectionCallbackSetters as y, ActorAliasMapper as yt, createFederation as z, OutboxListener as zt };
4717
+ export { respondWithObject as $, digest as $t, InboxChallengePolicy as A, CustomCollectionDispatcher as At, createFederation as B, OutboxListener as Bt, FederationBenchmarkOptions as C, ActorKeyPairsDispatcher as Ct, FederationStartQueueOptions as D, CollectionDispatcher as Dt, FederationOptions as E, CollectionCursor as Et, PortableInboxForwardingOptions as F, MediaUploaderCallback as Ft, TaskEnqueueOptions as G, SharedInboxKeyDispatcher as Gt, createFederationBuilder as H, OutboxPermanentFailureHandler as Ht, Rfc6570Expression as I, NodeInfoDispatcher as It, CreateExponentialBackoffPolicyOptions as J, WebFingerLinksDispatcher as Jt, TaskHandler as K, UnverifiedActivityHandler as Kt, FederationKvPrefixes as L, ObjectAuthorizePredicate as Lt, MediaUploaderSetters as M, HashlinkMediaRequest as Mt, ObjectCallbackSetters as N, InboxErrorHandler as Nt, IdempotencyKeyCallback as O, CustomCollectionCounter as Ot, OutboxListenerSetters as P, InboxListener as Pt, RespondWithObjectOptions as Q, buildCollectionSynchronizationHeader as Qt, FederationOrigin as R, ObjectDispatcher as Rt, Federation as S, ActorHandleMapper as St, FederationFetchOptions as T, CollectionCounter as Tt, TaskDefinition as U, PortableActorIdMapper as Ut, Message as V, OutboxListenerErrorHandler as Vt, TaskDefinitionOptions as W, PortableCollectionOwnerMapper as Wt, RetryPolicy as X, SenderKeyPair as Xt, RetryContext as Y, SendActivityError as Yt, createExponentialBackoffPolicy as Z, PageItems as Zt, ActorCallbackSetters as _, NormalizedCircuitBreakerOptions as _t, GetObjectOptions as a, MessageQueueEnqueueOptions as at, CustomCollectionCallbackSetters as b, ActorAliasMapper as bt, IsSignedByAudienceOptions as c, CircuitBreaker as ct, ParseUriResult as d, CircuitBreakerCreateOptions as dt, ActivityTransformer as en, respondWithObjectIfAcceptable as et, PortableRequest as f, CircuitBreakerFailurePolicy as ft, SendActivityOptionsForCollection as g, CircuitBreakerStateChange as gt, SendActivityOptions as h, CircuitBreakerState as ht, GetActorOptions as i, MessageQueueDepth as it, InboxListenerSetters as j, HashlinkMediaDispatcher as jt, IdempotencyStrategy as k, CustomCollectionCursor as kt, OutboxContext as l, CircuitBreakerActivityDrop as lt, RouteActivityOptions as m, CircuitBreakerOptions as mt, Context as n, InProcessMessageQueueOptions as nt, GetSignedKeyOptions as o, MessageQueueListenOptions as ot, RequestContext as p, CircuitBreakerKvState as pt, TaskRegistry as q, UnverifiedActivityReason as qt, ForwardActivityOptions as r, MessageQueue as rt, InboxContext as s, ParallelMessageQueue as st, ActorKeyPair as t, InProcessMessageQueue as tt, ParseUriOptions as u, CircuitBreakerBeforeSendDecision as ut, CollectionCallbackSetters as v, normalizeCircuitBreakerOptions as vt, FederationBuilder as w, AuthorizePredicate as wt, Federatable as x, ActorDispatcher as xt, ConstructorWithTypeId as y, parseCircuitBreakerKvState as yt, FederationQueueOptions as z, OutboxErrorHandler as zt };
@@ -1259,6 +1259,18 @@ interface FanoutMessage {
1259
1259
  readonly inboxes: Readonly<Record<string, {
1260
1260
  readonly actorIds: readonly string[];
1261
1261
  readonly sharedInbox: boolean;
1262
+ /**
1263
+ * The canonical form of the FEP-ef61 portable inbox that the inbox
1264
+ * stands for.
1265
+ * @internal
1266
+ */
1267
+ readonly portableInbox?: string;
1268
+ /**
1269
+ * The compatible identifiers of the portable inbox on each gateway to
1270
+ * deliver through, in order.
1271
+ * @internal
1272
+ */
1273
+ readonly gatewayInboxes?: readonly string[];
1262
1274
  }>>;
1263
1275
  readonly activity: unknown;
1264
1276
  readonly activityId?: string;
@@ -1302,6 +1314,27 @@ interface OutboxMessage {
1302
1314
  * @internal
1303
1315
  */
1304
1316
  readonly circuitHeldSince?: string;
1317
+ /**
1318
+ * The canonical form of the FEP-ef61 portable inbox that {@link inbox}, its
1319
+ * compatible identifier on a gateway, stands for.
1320
+ * @internal
1321
+ */
1322
+ readonly portableInbox?: string;
1323
+ /**
1324
+ * The compatible identifiers of the FEP-ef61 portable inbox on each gateway
1325
+ * that is still to be tried, in order. {@link inbox} is the first one.
1326
+ * If there is more than one, the activity is delivered through the first
1327
+ * gateway that accepts it.
1328
+ * @internal
1329
+ */
1330
+ readonly gatewayInboxes?: readonly string[];
1331
+ /**
1332
+ * The times before which each of {@link gatewayInboxes} must not be tried
1333
+ * again, as their responses' `Retry-After` headers requested, keyed by
1334
+ * the inbox URL, in ISO 8601.
1335
+ * @internal
1336
+ */
1337
+ readonly gatewayNotBefore?: Readonly<Record<string, string>>;
1305
1338
  readonly traceContext: Readonly<Record<string, string>>;
1306
1339
  }
1307
1340
  /**
@@ -4205,6 +4238,52 @@ interface RequestContext<TContextData> extends Context<TContextData> {
4205
4238
  * [instance actor]: https://swicg.github.io/activitypub-http-signature/#instance-actor
4206
4239
  */
4207
4240
  getSignedKeyOwner(options: GetKeyOwnerOptions): Promise<Actor | null>;
4241
+ /**
4242
+ * Checks whether the request is signed by an actor who belongs to
4243
+ * the intended audience of an object, i.e., the owner of the signed key
4244
+ * (see {@link RequestContext.getSignedKeyOwner}) is one of the object's
4245
+ * `to`, `cc`, `bto`, `bcc`, and `audience`, or a member of one of them
4246
+ * according to the `isMember` option.
4247
+ *
4248
+ * Everyone belongs to the audience of a publicly addressed object, so it
4249
+ * returns `true` for such an object even if the request is not signed.
4250
+ *
4251
+ * This can be used in authorization predicates for serving non-public
4252
+ * objects, which [FEP-ef61] requires of gateways serving portable objects.
4253
+ * [FEP-ef61] portable IDs are compared by their canonical forms, so
4254
+ * an `ap:` URI, an `ap+ef61:` URI, and a compatible identifier on any
4255
+ * gateway refer to the same actor.
4256
+ *
4257
+ * [FEP-ef61]: https://w3id.org/fep/ef61
4258
+ *
4259
+ * @param object The object whose audience is checked.
4260
+ * @param options Options for checking the audience.
4261
+ * @returns `true` if the object is publicly addressed, or the request is
4262
+ * signed by an actor who belongs to its audience.
4263
+ * @since 2.4.0
4264
+ */
4265
+ isSignedByAudience(object: Object$1, options?: IsSignedByAudienceOptions): Promise<boolean>;
4266
+ }
4267
+ /**
4268
+ * Options for {@link RequestContext.isSignedByAudience} method.
4269
+ * @since 2.4.0
4270
+ */
4271
+ interface IsSignedByAudienceOptions extends GetKeyOwnerOptions {
4272
+ /**
4273
+ * Checks whether the actor who signed the request is a member of
4274
+ * an addressee of the object that is not the actor itself, e.g.,
4275
+ * a followers collection. Fedify does not resolve collections by itself,
4276
+ * as only the application can tell their members reliably.
4277
+ *
4278
+ * It is called for each such addressee in order until it returns `true`.
4279
+ * If it is omitted, only the actors that the object addresses directly
4280
+ * belong to its audience.
4281
+ * @param addressee The ID of the addressee, e.g., the ID of a followers
4282
+ * collection.
4283
+ * @param actor The actor who signed the request.
4284
+ * @returns `true` if the actor is a member of the addressee.
4285
+ */
4286
+ isMember?: (addressee: URL, actor: Actor) => boolean | Promise<boolean>;
4208
4287
  }
4209
4288
  /**
4210
4289
  * A context for inbox listeners.
@@ -4637,4 +4716,4 @@ interface ActorKeyPair extends CryptoKeyPair {
4637
4716
  readonly multikey: Multikey;
4638
4717
  }
4639
4718
  //#endregion
4640
- export { respondWithObjectIfAcceptable as $, ActivityTransformer as $t, InboxListenerSetters as A, HashlinkMediaDispatcher as At, Message as B, OutboxListenerErrorHandler as Bt, FederationBuilder as C, AuthorizePredicate as Ct, IdempotencyKeyCallback as D, CustomCollectionCounter as Dt, FederationStartQueueOptions as E, CollectionDispatcher as Et, Rfc6570Expression as F, NodeInfoDispatcher as Ft, TaskHandler as G, UnverifiedActivityHandler as Gt, TaskDefinition as H, PortableActorIdMapper as Ht, FederationKvPrefixes as I, ObjectAuthorizePredicate as It, RetryContext as J, SendActivityError as Jt, TaskRegistry as K, UnverifiedActivityReason as Kt, FederationOrigin as L, ObjectDispatcher as Lt, ObjectCallbackSetters as M, InboxErrorHandler as Mt, OutboxListenerSetters as N, InboxListener as Nt, IdempotencyStrategy as O, CustomCollectionCursor as Ot, PortableInboxForwardingOptions as P, MediaUploaderCallback as Pt, respondWithObject as Q, digest as Qt, FederationQueueOptions as R, OutboxErrorHandler as Rt, FederationBenchmarkOptions as S, ActorKeyPairsDispatcher as St, FederationOptions as T, CollectionCursor as Tt, TaskDefinitionOptions as U, PortableCollectionOwnerMapper as Ut, createFederationBuilder as V, OutboxPermanentFailureHandler as Vt, TaskEnqueueOptions as W, SharedInboxKeyDispatcher as Wt, createExponentialBackoffPolicy as X, PageItems as Xt, RetryPolicy as Y, SenderKeyPair as Yt, RespondWithObjectOptions as Z, buildCollectionSynchronizationHeader as Zt, CollectionCallbackSetters as _, normalizeCircuitBreakerOptions as _t, GetObjectOptions as a, MessageQueueListenOptions as at, Federatable as b, ActorDispatcher as bt, OutboxContext as c, CircuitBreakerActivityDrop as ct, PortableRequest as d, CircuitBreakerFailurePolicy as dt, InProcessMessageQueue as et, RequestContext as f, CircuitBreakerKvState as ft, ActorCallbackSetters as g, NormalizedCircuitBreakerOptions as gt, SendActivityOptionsForCollection as h, CircuitBreakerStateChange as ht, GetActorOptions as i, MessageQueueEnqueueOptions as it, MediaUploaderSetters as j, HashlinkMediaRequest as jt, InboxChallengePolicy as k, CustomCollectionDispatcher as kt, ParseUriOptions as l, CircuitBreakerBeforeSendDecision as lt, SendActivityOptions as m, CircuitBreakerState as mt, Context as n, MessageQueue as nt, GetSignedKeyOptions as o, ParallelMessageQueue as ot, RouteActivityOptions as p, CircuitBreakerOptions as pt, CreateExponentialBackoffPolicyOptions as q, WebFingerLinksDispatcher as qt, ForwardActivityOptions as r, MessageQueueDepth as rt, InboxContext as s, CircuitBreaker as st, ActorKeyPair as t, InProcessMessageQueueOptions as tt, ParseUriResult as u, CircuitBreakerCreateOptions as ut, ConstructorWithTypeId as v, parseCircuitBreakerKvState as vt, FederationFetchOptions as w, CollectionCounter as wt, Federation as x, ActorHandleMapper as xt, CustomCollectionCallbackSetters as y, ActorAliasMapper as yt, createFederation as z, OutboxListener as zt };
4719
+ export { respondWithObject as $, digest as $t, InboxChallengePolicy as A, CustomCollectionDispatcher as At, createFederation as B, OutboxListener as Bt, FederationBenchmarkOptions as C, ActorKeyPairsDispatcher as Ct, FederationStartQueueOptions as D, CollectionDispatcher as Dt, FederationOptions as E, CollectionCursor as Et, PortableInboxForwardingOptions as F, MediaUploaderCallback as Ft, TaskEnqueueOptions as G, SharedInboxKeyDispatcher as Gt, createFederationBuilder as H, OutboxPermanentFailureHandler as Ht, Rfc6570Expression as I, NodeInfoDispatcher as It, CreateExponentialBackoffPolicyOptions as J, WebFingerLinksDispatcher as Jt, TaskHandler as K, UnverifiedActivityHandler as Kt, FederationKvPrefixes as L, ObjectAuthorizePredicate as Lt, MediaUploaderSetters as M, HashlinkMediaRequest as Mt, ObjectCallbackSetters as N, InboxErrorHandler as Nt, IdempotencyKeyCallback as O, CustomCollectionCounter as Ot, OutboxListenerSetters as P, InboxListener as Pt, RespondWithObjectOptions as Q, buildCollectionSynchronizationHeader as Qt, FederationOrigin as R, ObjectDispatcher as Rt, Federation as S, ActorHandleMapper as St, FederationFetchOptions as T, CollectionCounter as Tt, TaskDefinition as U, PortableActorIdMapper as Ut, Message as V, OutboxListenerErrorHandler as Vt, TaskDefinitionOptions as W, PortableCollectionOwnerMapper as Wt, RetryPolicy as X, SenderKeyPair as Xt, RetryContext as Y, SendActivityError as Yt, createExponentialBackoffPolicy as Z, PageItems as Zt, ActorCallbackSetters as _, NormalizedCircuitBreakerOptions as _t, GetObjectOptions as a, MessageQueueEnqueueOptions as at, CustomCollectionCallbackSetters as b, ActorAliasMapper as bt, IsSignedByAudienceOptions as c, CircuitBreaker as ct, ParseUriResult as d, CircuitBreakerCreateOptions as dt, ActivityTransformer as en, respondWithObjectIfAcceptable as et, PortableRequest as f, CircuitBreakerFailurePolicy as ft, SendActivityOptionsForCollection as g, CircuitBreakerStateChange as gt, SendActivityOptions as h, CircuitBreakerState as ht, GetActorOptions as i, MessageQueueDepth as it, InboxListenerSetters as j, HashlinkMediaDispatcher as jt, IdempotencyStrategy as k, CustomCollectionCursor as kt, OutboxContext as l, CircuitBreakerActivityDrop as lt, RouteActivityOptions as m, CircuitBreakerOptions as mt, Context as n, InProcessMessageQueueOptions as nt, GetSignedKeyOptions as o, MessageQueueListenOptions as ot, RequestContext as p, CircuitBreakerKvState as pt, TaskRegistry as q, UnverifiedActivityReason as qt, ForwardActivityOptions as r, MessageQueue as rt, InboxContext as s, ParallelMessageQueue as st, ActorKeyPair as t, InProcessMessageQueue as tt, ParseUriOptions as u, CircuitBreakerBeforeSendDecision as ut, CollectionCallbackSetters as v, normalizeCircuitBreakerOptions as vt, FederationBuilder as w, AuthorizePredicate as wt, Federatable as x, ActorDispatcher as xt, ConstructorWithTypeId as y, parseCircuitBreakerKvState as yt, FederationQueueOptions as z, OutboxErrorHandler as zt };
@@ -3,6 +3,6 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  //#region deno.json
5
5
  var name = "@fedify/fedify";
6
- var version = "2.4.0-dev.2193+402149b5";
6
+ var version = "2.4.0-dev.2212+26ce9858";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,11 +1,11 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { c as validateCryptoKey } from "./key-DA3Yx8p2.mjs";
5
- import { n as doubleKnock } from "./http-DQf9Mw4V.mjs";
4
+ import { c as validateCryptoKey } from "./key-ChfV9C3v.mjs";
5
+ import { n as doubleKnock } from "./http--IGUT1mg.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
- import { curry } from "es-toolkit";
8
7
  import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, resolveDocumentLoaderTimeout, validatePublicUrl, withDocumentLoaderTimeout } from "@fedify/vocab-runtime";
8
+ import { curry } from "es-toolkit";
9
9
  //#region src/utils/docloader.ts
10
10
  const logger = getLogger([
11
11
  "fedify",
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,130 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { n as isPubliclyAddressedNode, t as isInAudience } from "../audience-CHQ-FlLD.mjs";
5
+ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
+ import "../std__assert-DvMDRPes.mjs";
7
+ import { Note, PUBLIC_COLLECTION, Person } from "@fedify/vocab";
8
+ import { parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
9
+ import { mockDocumentLoader, test } from "@fedify/fixture";
10
+ import jsonld from "@fedify/vocab-runtime/jsonld";
11
+ //#region src/federation/audience.test.ts
12
+ const did = "did:key:z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2";
13
+ async function expand(document) {
14
+ const [node] = await jsonld.expand({
15
+ "@context": "https://www.w3.org/ns/activitystreams",
16
+ id: "https://example.com/notes/1",
17
+ type: "Note",
18
+ ...document
19
+ }, { documentLoader: mockDocumentLoader });
20
+ return node;
21
+ }
22
+ test("isPubliclyAddressedNode()", async (t) => {
23
+ for (const property of [
24
+ "to",
25
+ "cc",
26
+ "bto",
27
+ "bcc",
28
+ "audience"
29
+ ]) for (const value of [
30
+ PUBLIC_COLLECTION.href,
31
+ "as:Public",
32
+ "Public",
33
+ ["https://example.com/person", "as:Public"]
34
+ ]) await t.step(`${JSON.stringify(value)} in ${property}`, async () => {
35
+ assertEquals(isPubliclyAddressedNode(await expand({ [property]: value })), true);
36
+ });
37
+ await t.step("not addressed", async () => {
38
+ assertEquals(isPubliclyAddressedNode(await expand({})), false);
39
+ });
40
+ await t.step("addressed to other recipients", async () => {
41
+ assertEquals(isPubliclyAddressedNode(await expand({
42
+ to: "https://example.com/person",
43
+ cc: "https://example.com/person/followers"
44
+ })), false);
45
+ });
46
+ await t.step("Public in other properties", async () => {
47
+ assertEquals(isPubliclyAddressedNode(await expand({ tag: PUBLIC_COLLECTION.href })), false);
48
+ });
49
+ await t.step("a Public literal rather than an ID", async () => {
50
+ assertEquals(isPubliclyAddressedNode(await expand({ to: { "@value": PUBLIC_COLLECTION.href } })), false);
51
+ });
52
+ await t.step("Public that the context maps elsewhere", async () => {
53
+ for (const [context, to] of [[{ as: "https://example.com/ns#" }, "as:Public"], [{ "@base": "https://example.com/" }, "Public"]]) {
54
+ const [node] = await jsonld.expand({
55
+ "@context": ["https://www.w3.org/ns/activitystreams", context],
56
+ id: "https://example.com/notes/1",
57
+ type: "Note",
58
+ to
59
+ }, { documentLoader: mockDocumentLoader });
60
+ assertEquals(isPubliclyAddressedNode(node), false, JSON.stringify(context));
61
+ }
62
+ });
63
+ await t.step("only the node itself is checked", async () => {
64
+ assertEquals(isPubliclyAddressedNode(await expand({
65
+ type: "Create",
66
+ actor: "https://example.com/person",
67
+ object: {
68
+ type: "Note",
69
+ id: "https://example.com/notes/2",
70
+ to: PUBLIC_COLLECTION.href
71
+ }
72
+ })), false);
73
+ });
74
+ });
75
+ test("isInAudience()", async (t) => {
76
+ const person = new Person({ id: new URL("https://example.com/person") });
77
+ const portableId = parseIri(`ap+ef61://${did}/actor`);
78
+ const portable = new Person({ id: toCompatibleEf61Id(portableId, "https://other.example") });
79
+ const followers = new URL("https://example.com/person2/followers");
80
+ await t.step("publicly addressed", async () => {
81
+ for (const actor of [null, person]) assertEquals(await isInAudience(new Note({ cc: PUBLIC_COLLECTION }), actor), true);
82
+ });
83
+ await t.step("no actor", async () => {
84
+ assertEquals(await isInAudience(new Note({ to: person.id }), null), false);
85
+ assertEquals(await isInAudience(new Note({}), null), false);
86
+ });
87
+ await t.step("directly addressed", async () => {
88
+ for (const property of [
89
+ "to",
90
+ "cc",
91
+ "bto",
92
+ "bcc",
93
+ "audience"
94
+ ]) assertEquals(await isInAudience(new Note({ [property]: person.id }), person), true, property);
95
+ });
96
+ await t.step("not addressed", async () => {
97
+ assertEquals(await isInAudience(new Note({ to: new URL("https://example.com/other") }), person), false);
98
+ assertEquals(await isInAudience(new Note({ to: person.id }), new Person({})), false);
99
+ });
100
+ await t.step("portable IDs are compared canonically", async () => {
101
+ for (const addressee of [
102
+ portableId,
103
+ parseIri(`ap://${did}/actor`),
104
+ parseIri(`ap+ef61://${did.replaceAll(":", "%3A")}/actor`),
105
+ toCompatibleEf61Id(portableId, "https://example.com")
106
+ ]) assertEquals(await isInAudience(new Note({ to: addressee }), portable), true, addressee.href);
107
+ assertEquals(await isInAudience(new Note({ to: parseIri(`ap+ef61://${did}/other`) }), portable), false);
108
+ assertEquals(await isInAudience(new Note({ to: new URL(`https://other.example/${did}/actor`) }), portable), false);
109
+ });
110
+ await t.step("isMember()", async () => {
111
+ const calls = [];
112
+ const isMember = (addressee, actor) => {
113
+ calls.push(`${addressee.href} ${actor.id?.href}`);
114
+ return addressee.href === followers.href;
115
+ };
116
+ const note = new Note({
117
+ to: new URL("https://example.com/other"),
118
+ cc: followers
119
+ });
120
+ assertEquals(await isInAudience(note, person, { isMember }), true);
121
+ assertEquals(calls, [`https://example.com/other ${person.id?.href}`, `${followers.href} ${person.id?.href}`]);
122
+ calls.length = 0;
123
+ assertEquals(await isInAudience(new Note({ to: person.id }), person, { isMember }), true);
124
+ assertEquals(await isInAudience(note, null, { isMember }), false);
125
+ assertEquals(calls, []);
126
+ assertEquals(await isInAudience(note, person, { isMember: () => false }), false);
127
+ });
128
+ });
129
+ //#endregion
130
+ export {};
@@ -6,7 +6,7 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import { t as assertExists } from "../assert_exists-cffSpJ7X.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
- import { r as createFederationBuilder } from "../builder-0onlMY4u.mjs";
9
+ import { r as createFederationBuilder } from "../builder-CKAVFvcH.mjs";
10
10
  import { DisallowedOperatorError, DisallowedVarSpecModifierError, DuplicateRouteVariableError, RouteTemplateOptionsNotMatchedError, RouterError } from "@fedify/uri-template";
11
11
  import { Activity, Note, Person } from "@fedify/vocab";
12
12
  import { test } from "@fedify/fixture";
@@ -6,14 +6,14 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
8
  import { t as assert } from "../assert-D1Eafmd_.mjs";
9
- import { n as exportJwk } from "../key-DA3Yx8p2.mjs";
9
+ import { n as exportJwk } from "../key-ChfV9C3v.mjs";
10
10
  import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
11
- import { a as signObject, d as verifyProof } from "../proof-DrJWKeOl.mjs";
11
+ import { a as signObject, d as verifyProof } from "../proof-MFz2960e.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
13
- import { n as FederationImpl } from "../middleware-BqqycC0P.mjs";
13
+ import { n as FederationImpl } from "../middleware-BW7-tHAG.mjs";
14
14
  import { Create, DataIntegrityProof, Note } from "@fedify/vocab";
15
- import { mockDocumentLoader, test } from "@fedify/fixture";
16
15
  import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
16
+ import { mockDocumentLoader, test } from "@fedify/fixture";
17
17
  import serialize from "json-canon";
18
18
  //#region src/federation/compound-fanout.test.ts
19
19
  const options = {
@@ -1,19 +1,19 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { r as createRequestContext, t as createInboxContext } from "../context-B2rlkkyJ.mjs";
4
+ import { r as createRequestContext, t as createInboxContext } from "../context-8Tu9kea0.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
- import { a as compactJsonLd, m as verifyCompactJsonLd, p as signJsonLd } from "../ld-C15yzuY6.mjs";
9
- import { l as signRequest } from "../http-DQf9Mw4V.mjs";
8
+ import { a as compactJsonLd, m as verifyCompactJsonLd, p as signJsonLd } from "../ld-DBBVabBC.mjs";
9
+ import { l as signRequest } from "../http--IGUT1mg.mjs";
10
10
  import { a as rsaPrivateKey3, c as rsaPublicKey3, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
11
- import { f as handleInbox, o as createFederation } from "../middleware-BqqycC0P.mjs";
11
+ import { f as handleInbox, o as createFederation } from "../middleware-BW7-tHAG.mjs";
12
12
  import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
13
13
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
14
14
  import { Create, CryptographicKey, Multikey, Person, Update } from "@fedify/vocab";
15
- import { mockDocumentLoader, test } from "@fedify/fixture";
16
15
  import { encodeMultibase, exportDidKey, parseIri } from "@fedify/vocab-runtime";
16
+ import { mockDocumentLoader, test } from "@fedify/fixture";
17
17
  import serialize from "json-canon";
18
18
  //#region src/federation/handler-compound-proof.test.ts
19
19
  async function sha256(value) {