@fedify/fedify 1.6.0-dev.773 → 1.6.0-dev.776

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/CHANGES.md CHANGED
@@ -11,6 +11,17 @@ To be released.
11
11
  - Added `Context.lookupWebFinger()` method to make WebFinger lookups
12
12
  accessible from the context. [[#227]]
13
13
 
14
+ - Introduced `FederationBuilder` for creating a federation instance with
15
+ a builder pattern.
16
+
17
+ - Added `createFederationBuilder()` function.
18
+ - Added `Federatable` interface.
19
+ - Added `FederationBuilder` interface.
20
+ - Deprecated `CreateFederationOptions` interface. Use `FederationOptions`
21
+ interface.
22
+
23
+ - Added `Router.trailingSlashInsensitive` property.
24
+
14
25
  [#227]: https://github.com/fedify-dev/fedify/issues/227
15
26
 
16
27
 
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "@fedify/fedify",
3
- "version": "1.6.0-dev.773+fe5e3601",
3
+ "version": "1.6.0-dev.776+e6d9e209",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./mod.ts",
@@ -0,0 +1,651 @@
1
+ import { getLogger } from "@logtape/logtape";
2
+ import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
3
+ import metadata from "../deno.js";
4
+ import { getTypeId } from "../vocab/type.js";
5
+ import { InboxListenerSet } from "./inbox.js";
6
+ import { Router, RouterError } from "./router.js";
7
+ export class FederationBuilderImpl {
8
+ router;
9
+ actorCallbacks;
10
+ nodeInfoDispatcher;
11
+ objectCallbacks;
12
+ objectTypeIds;
13
+ inboxPath;
14
+ inboxCallbacks;
15
+ outboxCallbacks;
16
+ followingCallbacks;
17
+ followersCallbacks;
18
+ likedCallbacks;
19
+ featuredCallbacks;
20
+ featuredTagsCallbacks;
21
+ inboxListeners;
22
+ inboxErrorHandler;
23
+ sharedInboxKeyDispatcher;
24
+ constructor() {
25
+ this.router = new Router();
26
+ this.objectCallbacks = {};
27
+ this.objectTypeIds = {};
28
+ }
29
+ async build(options) {
30
+ const { FederationImpl } = await import("./middleware.js");
31
+ const f = new FederationImpl(options);
32
+ // Assign the existing router instance but preserve the settings
33
+ // Keep the original trailingSlashInsensitive configuration
34
+ const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
35
+ f.router = this.router;
36
+ f.router.trailingSlashInsensitive = trailingSlashInsensitiveValue;
37
+ f.actorCallbacks = this.actorCallbacks;
38
+ f.nodeInfoDispatcher = this.nodeInfoDispatcher;
39
+ f.objectCallbacks = this.objectCallbacks;
40
+ f.objectTypeIds = this.objectTypeIds;
41
+ f.inboxPath = this.inboxPath;
42
+ f.inboxCallbacks = this.inboxCallbacks;
43
+ f.outboxCallbacks = this.outboxCallbacks;
44
+ f.followingCallbacks = this.followingCallbacks;
45
+ f.followersCallbacks = this.followersCallbacks;
46
+ f.likedCallbacks = this.likedCallbacks;
47
+ f.featuredCallbacks = this.featuredCallbacks;
48
+ f.featuredTagsCallbacks = this.featuredTagsCallbacks;
49
+ f.inboxListeners = this.inboxListeners;
50
+ f.inboxErrorHandler = this.inboxErrorHandler;
51
+ f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
52
+ return f;
53
+ }
54
+ _getTracer() {
55
+ return trace.getTracer(metadata.name, metadata.version);
56
+ }
57
+ setActorDispatcher(path, dispatcher) {
58
+ if (this.router.has("actor")) {
59
+ throw new RouterError("Actor dispatcher already set.");
60
+ }
61
+ const variables = this.router.add(path, "actor");
62
+ if (variables.size !== 1 ||
63
+ !(variables.has("identifier") || variables.has("handle"))) {
64
+ throw new RouterError("Path for actor dispatcher must have one variable: {identifier}");
65
+ }
66
+ if (variables.has("handle")) {
67
+ getLogger(["fedify", "federation", "actor"]).warn("The {{handle}} variable in the actor dispatcher path is deprecated. " +
68
+ "Use {{identifier}} instead.");
69
+ }
70
+ const callbacks = {
71
+ dispatcher: async (context, identifier) => {
72
+ const actor = await this._getTracer().startActiveSpan("activitypub.dispatch_actor", {
73
+ kind: SpanKind.SERVER,
74
+ attributes: { "fedify.actor.identifier": identifier },
75
+ }, async (span) => {
76
+ try {
77
+ const actor = await dispatcher(context, identifier);
78
+ span.setAttribute("activitypub.actor.id", (actor?.id ?? context.getActorUri(identifier)).href);
79
+ if (actor == null) {
80
+ span.setStatus({ code: SpanStatusCode.ERROR });
81
+ }
82
+ else {
83
+ span.setAttribute("activitypub.actor.type", getTypeId(actor).href);
84
+ }
85
+ return actor;
86
+ }
87
+ catch (error) {
88
+ span.setStatus({
89
+ code: SpanStatusCode.ERROR,
90
+ message: String(error),
91
+ });
92
+ throw error;
93
+ }
94
+ finally {
95
+ span.end();
96
+ }
97
+ });
98
+ if (actor == null)
99
+ return null;
100
+ const logger = getLogger(["fedify", "federation", "actor"]);
101
+ if (actor.id == null) {
102
+ logger.warn("Actor dispatcher returned an actor without an id property. " +
103
+ "Set the property with Context.getActorUri(identifier).");
104
+ }
105
+ else if (actor.id.href != context.getActorUri(identifier).href) {
106
+ logger.warn("Actor dispatcher returned an actor with an id property that " +
107
+ "does not match the actor URI. Set the property with " +
108
+ "Context.getActorUri(identifier).");
109
+ }
110
+ if (this.followingCallbacks != null &&
111
+ this.followingCallbacks.dispatcher != null) {
112
+ if (actor.followingId == null) {
113
+ logger.warn("You configured a following collection dispatcher, but the " +
114
+ "actor does not have a following property. Set the property " +
115
+ "with Context.getFollowingUri(identifier).");
116
+ }
117
+ else if (actor.followingId.href != context.getFollowingUri(identifier).href) {
118
+ logger.warn("You configured a following collection dispatcher, but the " +
119
+ "actor's following property does not match the following " +
120
+ "collection URI. Set the property with " +
121
+ "Context.getFollowingUri(identifier).");
122
+ }
123
+ }
124
+ if (this.followersCallbacks != null &&
125
+ this.followersCallbacks.dispatcher != null) {
126
+ if (actor.followersId == null) {
127
+ logger.warn("You configured a followers collection dispatcher, but the " +
128
+ "actor does not have a followers property. Set the property " +
129
+ "with Context.getFollowersUri(identifier).");
130
+ }
131
+ else if (actor.followersId.href != context.getFollowersUri(identifier).href) {
132
+ logger.warn("You configured a followers collection dispatcher, but the " +
133
+ "actor's followers property does not match the followers " +
134
+ "collection URI. Set the property with " +
135
+ "Context.getFollowersUri(identifier).");
136
+ }
137
+ }
138
+ if (this.outboxCallbacks != null &&
139
+ this.outboxCallbacks.dispatcher != null) {
140
+ if (actor?.outboxId == null) {
141
+ logger.warn("You configured an outbox collection dispatcher, but the " +
142
+ "actor does not have an outbox property. Set the property " +
143
+ "with Context.getOutboxUri(identifier).");
144
+ }
145
+ else if (actor.outboxId.href != context.getOutboxUri(identifier).href) {
146
+ logger.warn("You configured an outbox collection dispatcher, but the " +
147
+ "actor's outbox property does not match the outbox collection " +
148
+ "URI. Set the property with Context.getOutboxUri(identifier).");
149
+ }
150
+ }
151
+ if (this.likedCallbacks != null &&
152
+ this.likedCallbacks.dispatcher != null) {
153
+ if (actor?.likedId == null) {
154
+ logger.warn("You configured a liked collection dispatcher, but the " +
155
+ "actor does not have a liked property. Set the property " +
156
+ "with Context.getLikedUri(identifier).");
157
+ }
158
+ else if (actor.likedId.href != context.getLikedUri(identifier).href) {
159
+ logger.warn("You configured a liked collection dispatcher, but the " +
160
+ "actor's liked property does not match the liked collection " +
161
+ "URI. Set the property with Context.getLikedUri(identifier).");
162
+ }
163
+ }
164
+ if (this.featuredCallbacks != null &&
165
+ this.featuredCallbacks.dispatcher != null) {
166
+ if (actor?.featuredId == null) {
167
+ logger.warn("You configured a featured collection dispatcher, but the " +
168
+ "actor does not have a featured property. Set the property " +
169
+ "with Context.getFeaturedUri(identifier).");
170
+ }
171
+ else if (actor.featuredId.href != context.getFeaturedUri(identifier).href) {
172
+ logger.warn("You configured a featured collection dispatcher, but the " +
173
+ "actor's featured property does not match the featured collection " +
174
+ "URI. Set the property with Context.getFeaturedUri(identifier).");
175
+ }
176
+ }
177
+ if (this.featuredTagsCallbacks != null &&
178
+ this.featuredTagsCallbacks.dispatcher != null) {
179
+ if (actor?.featuredTagsId == null) {
180
+ logger.warn("You configured a featured tags collection dispatcher, but the " +
181
+ "actor does not have a featuredTags property. Set the property " +
182
+ "with Context.getFeaturedTagsUri(identifier).");
183
+ }
184
+ else if (actor.featuredTagsId.href !=
185
+ context.getFeaturedTagsUri(identifier).href) {
186
+ logger.warn("You configured a featured tags collection dispatcher, but the " +
187
+ "actor's featuredTags property does not match the featured tags " +
188
+ "collection URI. Set the property with " +
189
+ "Context.getFeaturedTagsUri(identifier).");
190
+ }
191
+ }
192
+ if (this.router.has("inbox")) {
193
+ if (actor.inboxId == null) {
194
+ logger.warn("You configured inbox listeners, but the actor does not " +
195
+ "have an inbox property. Set the property with " +
196
+ "Context.getInboxUri(identifier).");
197
+ }
198
+ else if (actor.inboxId.href != context.getInboxUri(identifier).href) {
199
+ logger.warn("You configured inbox listeners, but the actor's inbox " +
200
+ "property does not match the inbox URI. Set the property " +
201
+ "with Context.getInboxUri(identifier).");
202
+ }
203
+ if (actor.endpoints == null || actor.endpoints.sharedInbox == null) {
204
+ logger.warn("You configured inbox listeners, but the actor does not have " +
205
+ "a endpoints.sharedInbox property. Set the property with " +
206
+ "Context.getInboxUri().");
207
+ }
208
+ else if (actor.endpoints.sharedInbox.href != context.getInboxUri().href) {
209
+ logger.warn("You configured inbox listeners, but the actor's " +
210
+ "endpoints.sharedInbox property does not match the shared inbox " +
211
+ "URI. Set the property with Context.getInboxUri().");
212
+ }
213
+ }
214
+ if (callbacks.keyPairsDispatcher != null) {
215
+ if (actor.publicKeyId == null) {
216
+ logger.warn("You configured a key pairs dispatcher, but the actor does " +
217
+ "not have a publicKey property. Set the property with " +
218
+ "Context.getActorKeyPairs(identifier).");
219
+ }
220
+ if (actor.assertionMethodId == null) {
221
+ logger.warn("You configured a key pairs dispatcher, but the actor does " +
222
+ "not have an assertionMethod property. Set the property " +
223
+ "with Context.getActorKeyPairs(identifier).");
224
+ }
225
+ }
226
+ return actor;
227
+ },
228
+ };
229
+ this.actorCallbacks = callbacks;
230
+ const setters = {
231
+ setKeyPairsDispatcher: (dispatcher) => {
232
+ callbacks.keyPairsDispatcher = (ctx, identifier) => this._getTracer().startActiveSpan("activitypub.dispatch_actor_key_pairs", {
233
+ kind: SpanKind.SERVER,
234
+ attributes: {
235
+ "activitypub.actor.id": ctx.getActorUri(identifier).href,
236
+ "fedify.actor.identifier": identifier,
237
+ },
238
+ }, async (span) => {
239
+ try {
240
+ return await dispatcher(ctx, identifier);
241
+ }
242
+ catch (e) {
243
+ span.setStatus({
244
+ code: SpanStatusCode.ERROR,
245
+ message: String(e),
246
+ });
247
+ throw e;
248
+ }
249
+ finally {
250
+ span.end();
251
+ }
252
+ });
253
+ return setters;
254
+ },
255
+ mapHandle(mapper) {
256
+ callbacks.handleMapper = mapper;
257
+ return setters;
258
+ },
259
+ mapAlias(mapper) {
260
+ callbacks.aliasMapper = mapper;
261
+ return setters;
262
+ },
263
+ authorize(predicate) {
264
+ callbacks.authorizePredicate = predicate;
265
+ return setters;
266
+ },
267
+ };
268
+ return setters;
269
+ }
270
+ setNodeInfoDispatcher(path, dispatcher) {
271
+ if (this.router.has("nodeInfo")) {
272
+ throw new RouterError("NodeInfo dispatcher already set.");
273
+ }
274
+ const variables = this.router.add(path, "nodeInfo");
275
+ if (variables.size !== 0) {
276
+ throw new RouterError("Path for NodeInfo dispatcher must have no variables.");
277
+ }
278
+ this.nodeInfoDispatcher = dispatcher;
279
+ }
280
+ setObjectDispatcher(
281
+ // deno-lint-ignore no-explicit-any
282
+ cls, path, dispatcher) {
283
+ const routeName = `object:${cls.typeId.href}`;
284
+ if (this.router.has(routeName)) {
285
+ throw new RouterError(`Object dispatcher for ${cls.name} already set.`);
286
+ }
287
+ const variables = this.router.add(path, routeName);
288
+ if (variables.size < 1) {
289
+ throw new RouterError("Path for object dispatcher must have at least one variable.");
290
+ }
291
+ const callbacks = {
292
+ dispatcher: (ctx, values) => {
293
+ const tracer = this._getTracer();
294
+ return tracer.startActiveSpan("activitypub.dispatch_object", {
295
+ kind: SpanKind.SERVER,
296
+ attributes: {
297
+ "fedify.object.type": cls.typeId.href,
298
+ ...globalThis.Object.fromEntries(globalThis.Object.entries(values).map(([k, v]) => [
299
+ `fedify.object.values.${k}`,
300
+ v,
301
+ ])),
302
+ },
303
+ }, async (span) => {
304
+ try {
305
+ const object = await dispatcher(ctx, values);
306
+ span.setAttribute("activitypub.object.id", (object?.id ?? ctx.getObjectUri(cls, values)).href);
307
+ if (object == null) {
308
+ span.setStatus({ code: SpanStatusCode.ERROR });
309
+ }
310
+ else {
311
+ span.setAttribute("activitypub.object.type", getTypeId(object).href);
312
+ }
313
+ return object;
314
+ }
315
+ catch (e) {
316
+ span.setStatus({
317
+ code: SpanStatusCode.ERROR,
318
+ message: String(e),
319
+ });
320
+ throw e;
321
+ }
322
+ finally {
323
+ span.end();
324
+ }
325
+ });
326
+ },
327
+ parameters: variables,
328
+ };
329
+ this.objectCallbacks[cls.typeId.href] = callbacks;
330
+ this.objectTypeIds[cls.typeId.href] = cls;
331
+ const setters = {
332
+ authorize(predicate) {
333
+ callbacks.authorizePredicate = predicate;
334
+ return setters;
335
+ },
336
+ };
337
+ return setters;
338
+ }
339
+ setInboxDispatcher(path, dispatcher) {
340
+ if (this.inboxCallbacks != null) {
341
+ throw new RouterError("Inbox dispatcher already set.");
342
+ }
343
+ if (this.router.has("inbox")) {
344
+ if (this.inboxPath !== path) {
345
+ throw new RouterError("Inbox dispatcher path must match inbox listener path.");
346
+ }
347
+ }
348
+ else {
349
+ const variables = this.router.add(path, "inbox");
350
+ if (variables.size !== 1 ||
351
+ !(variables.has("identifier") || variables.has("handle"))) {
352
+ throw new RouterError("Path for inbox dispatcher must have one variable: {identifier}");
353
+ }
354
+ if (variables.has("handle")) {
355
+ getLogger(["fedify", "federation", "inbox"]).warn("The {{handle}} variable in the inbox dispatcher path is deprecated. " +
356
+ "Use {{identifier}} instead.");
357
+ }
358
+ this.inboxPath = path;
359
+ }
360
+ const callbacks = { dispatcher };
361
+ this.inboxCallbacks = callbacks;
362
+ const setters = {
363
+ setCounter(counter) {
364
+ callbacks.counter = counter;
365
+ return setters;
366
+ },
367
+ setFirstCursor(cursor) {
368
+ callbacks.firstCursor = cursor;
369
+ return setters;
370
+ },
371
+ setLastCursor(cursor) {
372
+ callbacks.lastCursor = cursor;
373
+ return setters;
374
+ },
375
+ authorize(predicate) {
376
+ callbacks.authorizePredicate = predicate;
377
+ return setters;
378
+ },
379
+ };
380
+ return setters;
381
+ }
382
+ setOutboxDispatcher(path, dispatcher) {
383
+ if (this.router.has("outbox")) {
384
+ throw new RouterError("Outbox dispatcher already set.");
385
+ }
386
+ const variables = this.router.add(path, "outbox");
387
+ if (variables.size !== 1 ||
388
+ !(variables.has("identifier") || variables.has("handle"))) {
389
+ throw new RouterError("Path for outbox dispatcher must have one variable: {identifier}");
390
+ }
391
+ if (variables.has("handle")) {
392
+ getLogger(["fedify", "federation", "outbox"]).warn("The {{handle}} variable in the outbox dispatcher path is deprecated. " +
393
+ "Use {{identifier}} instead.");
394
+ }
395
+ const callbacks = { dispatcher };
396
+ this.outboxCallbacks = callbacks;
397
+ const setters = {
398
+ setCounter(counter) {
399
+ callbacks.counter = counter;
400
+ return setters;
401
+ },
402
+ setFirstCursor(cursor) {
403
+ callbacks.firstCursor = cursor;
404
+ return setters;
405
+ },
406
+ setLastCursor(cursor) {
407
+ callbacks.lastCursor = cursor;
408
+ return setters;
409
+ },
410
+ authorize(predicate) {
411
+ callbacks.authorizePredicate = predicate;
412
+ return setters;
413
+ },
414
+ };
415
+ return setters;
416
+ }
417
+ setFollowingDispatcher(path, dispatcher) {
418
+ if (this.router.has("following")) {
419
+ throw new RouterError("Following collection dispatcher already set.");
420
+ }
421
+ const variables = this.router.add(path, "following");
422
+ if (variables.size !== 1 ||
423
+ !(variables.has("identifier") || variables.has("handle"))) {
424
+ throw new RouterError("Path for following collection dispatcher must have one variable: " +
425
+ "{identifier}");
426
+ }
427
+ if (variables.has("handle")) {
428
+ getLogger(["fedify", "federation", "collection"]).warn("The {{handle}} variable in the following collection dispatcher path " +
429
+ "is deprecated. Use {{identifier}} instead.");
430
+ }
431
+ const callbacks = { dispatcher };
432
+ this.followingCallbacks = callbacks;
433
+ const setters = {
434
+ setCounter(counter) {
435
+ callbacks.counter = counter;
436
+ return setters;
437
+ },
438
+ setFirstCursor(cursor) {
439
+ callbacks.firstCursor = cursor;
440
+ return setters;
441
+ },
442
+ setLastCursor(cursor) {
443
+ callbacks.lastCursor = cursor;
444
+ return setters;
445
+ },
446
+ authorize(predicate) {
447
+ callbacks.authorizePredicate = predicate;
448
+ return setters;
449
+ },
450
+ };
451
+ return setters;
452
+ }
453
+ setFollowersDispatcher(path, dispatcher) {
454
+ if (this.router.has("followers")) {
455
+ throw new RouterError("Followers collection dispatcher already set.");
456
+ }
457
+ const variables = this.router.add(path, "followers");
458
+ if (variables.size !== 1 ||
459
+ !(variables.has("identifier") || variables.has("handle"))) {
460
+ throw new RouterError("Path for followers collection dispatcher must have one variable: " +
461
+ "{identifier}");
462
+ }
463
+ if (variables.has("handle")) {
464
+ getLogger(["fedify", "federation", "collection"]).warn("The {{handle}} variable in the followers collection dispatcher path " +
465
+ "is deprecated. Use {{identifier}} instead.");
466
+ }
467
+ const callbacks = { dispatcher };
468
+ this.followersCallbacks = callbacks;
469
+ const setters = {
470
+ setCounter(counter) {
471
+ callbacks.counter = counter;
472
+ return setters;
473
+ },
474
+ setFirstCursor(cursor) {
475
+ callbacks.firstCursor = cursor;
476
+ return setters;
477
+ },
478
+ setLastCursor(cursor) {
479
+ callbacks.lastCursor = cursor;
480
+ return setters;
481
+ },
482
+ authorize(predicate) {
483
+ callbacks.authorizePredicate = predicate;
484
+ return setters;
485
+ },
486
+ };
487
+ return setters;
488
+ }
489
+ setLikedDispatcher(path, dispatcher) {
490
+ if (this.router.has("liked")) {
491
+ throw new RouterError("Liked collection dispatcher already set.");
492
+ }
493
+ const variables = this.router.add(path, "liked");
494
+ if (variables.size !== 1 ||
495
+ !(variables.has("identifier") || variables.has("handle"))) {
496
+ throw new RouterError("Path for liked collection dispatcher must have one variable: " +
497
+ "{identifier}");
498
+ }
499
+ if (variables.has("handle")) {
500
+ getLogger(["fedify", "federation", "collection"]).warn("The {{handle}} variable in the liked collection dispatcher path " +
501
+ "is deprecated. Use {{identifier}} instead.");
502
+ }
503
+ const callbacks = { dispatcher };
504
+ this.likedCallbacks = callbacks;
505
+ const setters = {
506
+ setCounter(counter) {
507
+ callbacks.counter = counter;
508
+ return setters;
509
+ },
510
+ setFirstCursor(cursor) {
511
+ callbacks.firstCursor = cursor;
512
+ return setters;
513
+ },
514
+ setLastCursor(cursor) {
515
+ callbacks.lastCursor = cursor;
516
+ return setters;
517
+ },
518
+ authorize(predicate) {
519
+ callbacks.authorizePredicate = predicate;
520
+ return setters;
521
+ },
522
+ };
523
+ return setters;
524
+ }
525
+ setFeaturedDispatcher(path, dispatcher) {
526
+ if (this.router.has("featured")) {
527
+ throw new RouterError("Featured collection dispatcher already set.");
528
+ }
529
+ const variables = this.router.add(path, "featured");
530
+ if (variables.size !== 1 ||
531
+ !(variables.has("identifier") || variables.has("handle"))) {
532
+ throw new RouterError("Path for featured collection dispatcher must have one variable: " +
533
+ "{identifier}");
534
+ }
535
+ if (variables.has("handle")) {
536
+ getLogger(["fedify", "federation", "collection"]).warn("The {{handle}} variable in the featured collection dispatcher path " +
537
+ "is deprecated. Use {{identifier}} instead.");
538
+ }
539
+ const callbacks = { dispatcher };
540
+ this.featuredCallbacks = callbacks;
541
+ const setters = {
542
+ setCounter(counter) {
543
+ callbacks.counter = counter;
544
+ return setters;
545
+ },
546
+ setFirstCursor(cursor) {
547
+ callbacks.firstCursor = cursor;
548
+ return setters;
549
+ },
550
+ setLastCursor(cursor) {
551
+ callbacks.lastCursor = cursor;
552
+ return setters;
553
+ },
554
+ authorize(predicate) {
555
+ callbacks.authorizePredicate = predicate;
556
+ return setters;
557
+ },
558
+ };
559
+ return setters;
560
+ }
561
+ setFeaturedTagsDispatcher(path, dispatcher) {
562
+ if (this.router.has("featuredTags")) {
563
+ throw new RouterError("Featured tags collection dispatcher already set.");
564
+ }
565
+ const variables = this.router.add(path, "featuredTags");
566
+ if (variables.size !== 1 ||
567
+ !(variables.has("identifier") || variables.has("handle"))) {
568
+ throw new RouterError("Path for featured tags collection dispatcher must have one " +
569
+ "variable: {identifier}");
570
+ }
571
+ if (variables.has("handle")) {
572
+ getLogger(["fedify", "federation", "collection"]).warn("The {{handle}} variable in the featured tags collection dispatcher " +
573
+ "path is deprecated. Use {{identifier}} instead.");
574
+ }
575
+ const callbacks = { dispatcher };
576
+ this.featuredTagsCallbacks = callbacks;
577
+ const setters = {
578
+ setCounter(counter) {
579
+ callbacks.counter = counter;
580
+ return setters;
581
+ },
582
+ setFirstCursor(cursor) {
583
+ callbacks.firstCursor = cursor;
584
+ return setters;
585
+ },
586
+ setLastCursor(cursor) {
587
+ callbacks.lastCursor = cursor;
588
+ return setters;
589
+ },
590
+ authorize(predicate) {
591
+ callbacks.authorizePredicate = predicate;
592
+ return setters;
593
+ },
594
+ };
595
+ return setters;
596
+ }
597
+ setInboxListeners(inboxPath, sharedInboxPath) {
598
+ if (this.inboxListeners != null) {
599
+ throw new RouterError("Inbox listeners already set.");
600
+ }
601
+ if (this.router.has("inbox")) {
602
+ if (this.inboxPath !== inboxPath) {
603
+ throw new RouterError("Inbox listener path must match inbox dispatcher path.");
604
+ }
605
+ }
606
+ else {
607
+ const variables = this.router.add(inboxPath, "inbox");
608
+ if (variables.size !== 1 ||
609
+ !(variables.has("identifier") || variables.has("handle"))) {
610
+ throw new RouterError("Path for inbox must have one variable: {identifier}");
611
+ }
612
+ this.inboxPath = inboxPath;
613
+ if (variables.has("handle")) {
614
+ getLogger(["fedify", "federation", "inbox"]).warn("The {{handle}} variable in the inbox path is deprecated. " +
615
+ "Use {{identifier}} instead.");
616
+ }
617
+ }
618
+ if (sharedInboxPath != null) {
619
+ const siVars = this.router.add(sharedInboxPath, "sharedInbox");
620
+ if (siVars.size !== 0) {
621
+ throw new RouterError("Path for shared inbox must have no variables.");
622
+ }
623
+ }
624
+ const listeners = this.inboxListeners = new InboxListenerSet();
625
+ const setters = {
626
+ on(
627
+ // deno-lint-ignore no-explicit-any
628
+ type, listener) {
629
+ listeners.add(type, listener);
630
+ return setters;
631
+ },
632
+ onError: (handler) => {
633
+ this.inboxErrorHandler = handler;
634
+ return setters;
635
+ },
636
+ setSharedKeyDispatcher: (dispatcher) => {
637
+ this.sharedInboxKeyDispatcher = dispatcher;
638
+ return setters;
639
+ },
640
+ };
641
+ return setters;
642
+ }
643
+ }
644
+ /**
645
+ * Creates a new {@link FederationBuilder} instance.
646
+ * @returns A new {@link FederationBuilder} instance.
647
+ * @since 1.6.0
648
+ */
649
+ export function createFederationBuilder() {
650
+ return new FederationBuilderImpl();
651
+ }