@fedify/relay 2.0.0-dev.109 → 2.0.0-dev.150

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/README.md CHANGED
@@ -102,7 +102,7 @@ import { MemoryKvStore } from "@fedify/fedify";
102
102
  // Create a Mastodon-style relay
103
103
  const relay = createRelay("mastodon", {
104
104
  kv: new MemoryKvStore(),
105
- domain: "relay.example.com",
105
+ origin: "https://relay.example.com",
106
106
  // Required: Set a subscription handler to approve/reject subscriptions
107
107
  subscriptionHandler: async (ctx, actor) => {
108
108
  // For an open relay, simply return true
@@ -124,7 +124,7 @@ You can also create a LitePub-style relay by changing the type:
124
124
  ~~~~ typescript
125
125
  const relay = createRelay("litepub", {
126
126
  kv: new MemoryKvStore(),
127
- domain: "relay.example.com",
127
+ origin: "https://relay.example.com",
128
128
  subscriptionHandler: async (ctx, actor) => true,
129
129
  });
130
130
  ~~~~
@@ -137,7 +137,7 @@ subscription requests. For an open relay that accepts all subscriptions:
137
137
  ~~~~ typescript
138
138
  const relay = createRelay("mastodon", {
139
139
  kv: new MemoryKvStore(),
140
- domain: "relay.example.com",
140
+ origin: "https://relay.example.com",
141
141
  subscriptionHandler: async (ctx, actor) => true, // Accept all
142
142
  });
143
143
  ~~~~
@@ -147,7 +147,7 @@ You can also implement custom approval logic:
147
147
  ~~~~ typescript
148
148
  const relay = createRelay("mastodon", {
149
149
  kv: new MemoryKvStore(),
150
- domain: "relay.example.com",
150
+ origin: "https://relay.example.com",
151
151
  subscriptionHandler: async (ctx, actor) => {
152
152
  // Example: Only allow subscriptions from specific domains
153
153
  const domain = new URL(actor.id!).hostname;
@@ -200,7 +200,7 @@ import { MemoryKvStore } from "@fedify/fedify";
200
200
  const app = new Hono();
201
201
  const relay = createRelay("mastodon", {
202
202
  kv: new MemoryKvStore(),
203
- domain: "relay.example.com",
203
+ origin: "https://relay.example.com",
204
204
  subscriptionHandler: async (ctx, actor) => true,
205
205
  });
206
206
 
@@ -284,6 +284,8 @@ Public interface for ActivityPub relay implementations.
284
284
  followers of the relay
285
285
  - `getFollower(actorId: string): Promise<RelayFollower | null>`: Gets
286
286
  a specific follower by actor ID
287
+ - `getActorUri(): Promise<URL>`: Gets the URI of the relay actor
288
+ - `getSharedInboxUri(): Promise<URL>`: Gets the shared inbox URI of the relay
287
289
 
288
290
  #### Relay types
289
291
 
@@ -299,7 +301,8 @@ The relay type is specified when calling `createRelay()`:
299
301
  Configuration options for the relay:
300
302
 
301
303
  - `kv: KvStore` (required): Key–value store for persisting relay data
302
- - `domain?: string`: Relay's domain name (defaults to `"localhost"`)
304
+ - `origin: string` (required): Relay's origin URL (e.g.,
305
+ `"https://relay.example.com"`)
303
306
  - `name?: string`: Relay's display name (defaults to `"ActivityPub Relay"`)
304
307
  - `subscriptionHandler: SubscriptionRequestHandler` (required): Handler for
305
308
  subscription approval/rejection
package/dist/mod.cjs CHANGED
@@ -140,11 +140,7 @@ var BaseRelay = class {
140
140
  this.federationBuilder = relayBuilder$1;
141
141
  }
142
142
  async fetch(request) {
143
- if (this.federation == null) {
144
- this.federation = await this.federationBuilder.build(this.options);
145
- this.setupInboxListeners();
146
- }
147
- return await this.federation.fetch(request, { contextData: this.options });
143
+ return await (await this.#getFederation()).fetch(request, { contextData: this.options });
148
144
  }
149
145
  /**
150
146
  * Helper method to parse and validate follower data from storage.
@@ -177,7 +173,7 @@ var BaseRelay = class {
177
173
  *
178
174
  * const relay = createRelay("mastodon", {
179
175
  * kv: new MemoryKvStore(),
180
- * domain: "relay.example.com",
176
+ * origin: "https://relay.example.com",
181
177
  * subscriptionHandler: async (ctx, actor) => true,
182
178
  * });
183
179
  *
@@ -211,7 +207,7 @@ var BaseRelay = class {
211
207
  *
212
208
  * const relay = createRelay("mastodon", {
213
209
  * kv: new MemoryKvStore(),
214
- * domain: "relay.example.com",
210
+ * origin: "https://relay.example.com",
215
211
  * subscriptionHandler: async (ctx, actor) => true,
216
212
  * });
217
213
  *
@@ -230,6 +226,25 @@ var BaseRelay = class {
230
226
  const followerData = await this.options.kv.get(["follower", actorId]);
231
227
  return await this.parseFollowerData(actorId, followerData);
232
228
  }
229
+ async #getFederation() {
230
+ if (this.federation == null) {
231
+ this.federation = await this.federationBuilder.build(this.options);
232
+ this.setupInboxListeners();
233
+ }
234
+ return this.federation;
235
+ }
236
+ async #createContext() {
237
+ const context = (await this.#getFederation()).createContext(new URL(this.options.origin), this.options);
238
+ return context;
239
+ }
240
+ async getActorUri() {
241
+ const context = await this.#createContext();
242
+ return context.getActorUri(RELAY_SERVER_ACTOR);
243
+ }
244
+ async getSharedInboxUri() {
245
+ const context = await this.#createContext();
246
+ return context.getInboxUri();
247
+ }
233
248
  };
234
249
 
235
250
  //#endregion
@@ -419,7 +434,7 @@ var MastodonRelay = class extends BaseRelay {
419
434
  *
420
435
  * const relay = createRelay("mastodon", {
421
436
  * kv: new MemoryKvStore(),
422
- * domain: "relay.example.com",
437
+ * origin: "https://relay.example.com",
423
438
  * subscriptionHandler: async (ctx, actor) => true,
424
439
  * });
425
440
  * ```
package/dist/mod.d.cts CHANGED
@@ -16,12 +16,41 @@ type SubscriptionRequestHandler = (ctx: Context<RelayOptions>, clientActor: Acto
16
16
  * Configuration options for the ActivityPub relay.
17
17
  */
18
18
  interface RelayOptions {
19
+ /**
20
+ * Key-value store for persisting relay data such as follower information
21
+ * and cryptographic keys.
22
+ */
19
23
  kv: KvStore;
20
- domain?: string;
24
+ /**
25
+ * The origin URL where the relay is hosted.
26
+ * Must be a full URL including the protocol (e.g., `"https://relay.example.com"`).
27
+ */
28
+ origin: string;
29
+ /**
30
+ * Display name for the relay actor.
31
+ * Defaults to `"ActivityPub Relay"`.
32
+ */
21
33
  name?: string;
34
+ /**
35
+ * Factory function for creating a document loader to fetch remote
36
+ * ActivityPub objects.
37
+ */
22
38
  documentLoaderFactory?: DocumentLoaderFactory;
39
+ /**
40
+ * Factory function for creating an authenticated document loader
41
+ * that signs HTTP requests when fetching remote objects.
42
+ */
23
43
  authenticatedDocumentLoaderFactory?: AuthenticatedDocumentLoaderFactory;
44
+ /**
45
+ * Message queue for background activity processing.
46
+ * Recommended for production to handle activity forwarding asynchronously.
47
+ */
24
48
  queue?: MessageQueue;
49
+ /**
50
+ * Handler function that determines whether to approve or reject
51
+ * subscription requests from remote actors.
52
+ * Return `true` to approve or `false` to reject.
53
+ */
25
54
  subscriptionHandler: SubscriptionRequestHandler;
26
55
  }
27
56
  /**
@@ -73,6 +102,18 @@ interface Relay {
73
102
  * @returns The follower entry if found, null otherwise
74
103
  */
75
104
  getFollower(actorId: string): Promise<RelayFollower | null>;
105
+ /**
106
+ * Gets the URI of the relay actor.
107
+ *
108
+ * @returns The URI of the relay actor
109
+ */
110
+ getActorUri(): Promise<URL>;
111
+ /**
112
+ * Gets the shared inbox URI of the relay.
113
+ *
114
+ * @returns The shared inbox URI
115
+ */
116
+ getSharedInboxUri(): Promise<URL>;
76
117
  }
77
118
  /**
78
119
  * Type predicate to check if a value is valid RelayFollowerData from KV store.
@@ -98,7 +139,7 @@ interface Relay {
98
139
  *
99
140
  * const relay = createRelay("mastodon", {
100
141
  * kv: new MemoryKvStore(),
101
- * domain: "relay.example.com",
142
+ * origin: "https://relay.example.com",
102
143
  * subscriptionHandler: async (ctx, actor) => true,
103
144
  * });
104
145
  * ```
package/dist/mod.d.ts CHANGED
@@ -17,12 +17,41 @@ type SubscriptionRequestHandler = (ctx: Context<RelayOptions>, clientActor: Acto
17
17
  * Configuration options for the ActivityPub relay.
18
18
  */
19
19
  interface RelayOptions {
20
+ /**
21
+ * Key-value store for persisting relay data such as follower information
22
+ * and cryptographic keys.
23
+ */
20
24
  kv: KvStore;
21
- domain?: string;
25
+ /**
26
+ * The origin URL where the relay is hosted.
27
+ * Must be a full URL including the protocol (e.g., `"https://relay.example.com"`).
28
+ */
29
+ origin: string;
30
+ /**
31
+ * Display name for the relay actor.
32
+ * Defaults to `"ActivityPub Relay"`.
33
+ */
22
34
  name?: string;
35
+ /**
36
+ * Factory function for creating a document loader to fetch remote
37
+ * ActivityPub objects.
38
+ */
23
39
  documentLoaderFactory?: DocumentLoaderFactory;
40
+ /**
41
+ * Factory function for creating an authenticated document loader
42
+ * that signs HTTP requests when fetching remote objects.
43
+ */
24
44
  authenticatedDocumentLoaderFactory?: AuthenticatedDocumentLoaderFactory;
45
+ /**
46
+ * Message queue for background activity processing.
47
+ * Recommended for production to handle activity forwarding asynchronously.
48
+ */
25
49
  queue?: MessageQueue;
50
+ /**
51
+ * Handler function that determines whether to approve or reject
52
+ * subscription requests from remote actors.
53
+ * Return `true` to approve or `false` to reject.
54
+ */
26
55
  subscriptionHandler: SubscriptionRequestHandler;
27
56
  }
28
57
  /**
@@ -74,6 +103,18 @@ interface Relay {
74
103
  * @returns The follower entry if found, null otherwise
75
104
  */
76
105
  getFollower(actorId: string): Promise<RelayFollower | null>;
106
+ /**
107
+ * Gets the URI of the relay actor.
108
+ *
109
+ * @returns The URI of the relay actor
110
+ */
111
+ getActorUri(): Promise<URL>;
112
+ /**
113
+ * Gets the shared inbox URI of the relay.
114
+ *
115
+ * @returns The shared inbox URI
116
+ */
117
+ getSharedInboxUri(): Promise<URL>;
77
118
  }
78
119
  /**
79
120
  * Type predicate to check if a value is valid RelayFollowerData from KV store.
@@ -99,7 +140,7 @@ interface Relay {
99
140
  *
100
141
  * const relay = createRelay("mastodon", {
101
142
  * kv: new MemoryKvStore(),
102
- * domain: "relay.example.com",
143
+ * origin: "https://relay.example.com",
103
144
  * subscriptionHandler: async (ctx, actor) => true,
104
145
  * });
105
146
  * ```
package/dist/mod.js CHANGED
@@ -117,11 +117,7 @@ var BaseRelay = class {
117
117
  this.federationBuilder = relayBuilder$1;
118
118
  }
119
119
  async fetch(request) {
120
- if (this.federation == null) {
121
- this.federation = await this.federationBuilder.build(this.options);
122
- this.setupInboxListeners();
123
- }
124
- return await this.federation.fetch(request, { contextData: this.options });
120
+ return await (await this.#getFederation()).fetch(request, { contextData: this.options });
125
121
  }
126
122
  /**
127
123
  * Helper method to parse and validate follower data from storage.
@@ -154,7 +150,7 @@ var BaseRelay = class {
154
150
  *
155
151
  * const relay = createRelay("mastodon", {
156
152
  * kv: new MemoryKvStore(),
157
- * domain: "relay.example.com",
153
+ * origin: "https://relay.example.com",
158
154
  * subscriptionHandler: async (ctx, actor) => true,
159
155
  * });
160
156
  *
@@ -188,7 +184,7 @@ var BaseRelay = class {
188
184
  *
189
185
  * const relay = createRelay("mastodon", {
190
186
  * kv: new MemoryKvStore(),
191
- * domain: "relay.example.com",
187
+ * origin: "https://relay.example.com",
192
188
  * subscriptionHandler: async (ctx, actor) => true,
193
189
  * });
194
190
  *
@@ -207,6 +203,25 @@ var BaseRelay = class {
207
203
  const followerData = await this.options.kv.get(["follower", actorId]);
208
204
  return await this.parseFollowerData(actorId, followerData);
209
205
  }
206
+ async #getFederation() {
207
+ if (this.federation == null) {
208
+ this.federation = await this.federationBuilder.build(this.options);
209
+ this.setupInboxListeners();
210
+ }
211
+ return this.federation;
212
+ }
213
+ async #createContext() {
214
+ const context = (await this.#getFederation()).createContext(new URL(this.options.origin), this.options);
215
+ return context;
216
+ }
217
+ async getActorUri() {
218
+ const context = await this.#createContext();
219
+ return context.getActorUri(RELAY_SERVER_ACTOR);
220
+ }
221
+ async getSharedInboxUri() {
222
+ const context = await this.#createContext();
223
+ return context.getInboxUri();
224
+ }
210
225
  };
211
226
 
212
227
  //#endregion
@@ -396,7 +411,7 @@ var MastodonRelay = class extends BaseRelay {
396
411
  *
397
412
  * const relay = createRelay("mastodon", {
398
413
  * kv: new MemoryKvStore(),
399
- * domain: "relay.example.com",
414
+ * origin: "https://relay.example.com",
400
415
  * subscriptionHandler: async (ctx, actor) => true,
401
416
  * });
402
417
  * ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/relay",
3
- "version": "2.0.0-dev.109+ecc823d2",
3
+ "version": "2.0.0-dev.150+7daa59dd",
4
4
  "description": "ActivityPub relay support for Fedify",
5
5
  "keywords": [
6
6
  "Fedify",
@@ -52,13 +52,13 @@
52
52
  "@logtape/logtape": "^1.3.5"
53
53
  },
54
54
  "peerDependencies": {
55
- "@fedify/fedify": "^2.0.0-dev.109+ecc823d2"
55
+ "@fedify/fedify": "^2.0.0-dev.150+7daa59dd"
56
56
  },
57
57
  "devDependencies": {
58
58
  "tsdown": "^0.12.9",
59
59
  "typescript": "^5.9.3",
60
- "@fedify/testing": "^2.0.0-dev.109+ecc823d2",
61
- "@fedify/vocab-runtime": "^2.0.0-dev.109+ecc823d2"
60
+ "@fedify/vocab-runtime": "^2.0.0-dev.150+7daa59dd",
61
+ "@fedify/testing": "^2.0.0-dev.150+7daa59dd"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "deno task codegen && tsdown",