@fedify/fedify 1.0.0-dev.402 → 1.0.0-dev.404

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.
@@ -197,10 +197,10 @@ export class FederationImpl {
197
197
  const logger = getLogger(["fedify", "federation", "inbox"]);
198
198
  const baseUrl = new URL(message.baseUrl);
199
199
  let context = this.#createContext(baseUrl, ctxData);
200
- if (message.handle) {
200
+ if (message.identifier != null) {
201
201
  context = this.#createContext(baseUrl, ctxData, {
202
202
  documentLoader: await context.getDocumentLoader({
203
- handle: message.handle,
203
+ identifier: message.identifier,
204
204
  }),
205
205
  });
206
206
  }
@@ -208,7 +208,8 @@ export class FederationImpl {
208
208
  const identity = await this.sharedInboxKeyDispatcher(context);
209
209
  if (identity != null) {
210
210
  context = this.#createContext(baseUrl, ctxData, {
211
- documentLoader: "handle" in identity
211
+ documentLoader: "identifier" in identity || "username" in identity ||
212
+ "handle" in identity
212
213
  ? await context.getDocumentLoader(identity)
213
214
  : context.getDocumentLoader(identity),
214
215
  });
@@ -332,36 +333,41 @@ export class FederationImpl {
332
333
  throw new RouterError("Actor dispatcher already set.");
333
334
  }
334
335
  const variables = this.router.add(path, "actor");
335
- if (variables.size !== 1 || !variables.has("handle")) {
336
- throw new RouterError("Path for actor dispatcher must have one variable: {handle}");
336
+ if (variables.size !== 1 ||
337
+ !(variables.has("identifier") || variables.has("handle"))) {
338
+ throw new RouterError("Path for actor dispatcher must have one variable: {identifier}");
339
+ }
340
+ if (variables.has("handle")) {
341
+ getLogger(["fedify", "federation", "actor"]).warn("The {handle} variable in the actor dispatcher path is deprecated. " +
342
+ "Use {identifier} instead.");
337
343
  }
338
344
  const callbacks = {
339
- dispatcher: async (context, handle) => {
340
- const actor = await dispatcher(context, handle);
345
+ dispatcher: async (context, identifier) => {
346
+ const actor = await dispatcher(context, identifier);
341
347
  if (actor == null)
342
348
  return null;
343
349
  const logger = getLogger(["fedify", "federation", "actor"]);
344
350
  if (actor.id == null) {
345
351
  logger.warn("Actor dispatcher returned an actor without an id property. " +
346
- "Set the property with Context.getActorUri(handle).");
352
+ "Set the property with Context.getActorUri(identifier).");
347
353
  }
348
- else if (actor.id.href != context.getActorUri(handle).href) {
354
+ else if (actor.id.href != context.getActorUri(identifier).href) {
349
355
  logger.warn("Actor dispatcher returned an actor with an id property that " +
350
356
  "does not match the actor URI. Set the property with " +
351
- "Context.getActorUri(handle).");
357
+ "Context.getActorUri(identifier).");
352
358
  }
353
359
  if (this.followingCallbacks != null &&
354
360
  this.followingCallbacks.dispatcher != null) {
355
361
  if (actor.followingId == null) {
356
362
  logger.warn("You configured a following collection dispatcher, but the " +
357
363
  "actor does not have a following property. Set the property " +
358
- "with Context.getFollowingUri(handle).");
364
+ "with Context.getFollowingUri(identifier).");
359
365
  }
360
- else if (actor.followingId.href != context.getFollowingUri(handle).href) {
366
+ else if (actor.followingId.href != context.getFollowingUri(identifier).href) {
361
367
  logger.warn("You configured a following collection dispatcher, but the " +
362
368
  "actor's following property does not match the following " +
363
369
  "collection URI. Set the property with " +
364
- "Context.getFollowingUri(handle).");
370
+ "Context.getFollowingUri(identifier).");
365
371
  }
366
372
  }
367
373
  if (this.followersCallbacks != null &&
@@ -369,13 +375,13 @@ export class FederationImpl {
369
375
  if (actor.followersId == null) {
370
376
  logger.warn("You configured a followers collection dispatcher, but the " +
371
377
  "actor does not have a followers property. Set the property " +
372
- "with Context.getFollowersUri(handle).");
378
+ "with Context.getFollowersUri(identifier).");
373
379
  }
374
- else if (actor.followersId.href != context.getFollowersUri(handle).href) {
380
+ else if (actor.followersId.href != context.getFollowersUri(identifier).href) {
375
381
  logger.warn("You configured a followers collection dispatcher, but the " +
376
382
  "actor's followers property does not match the followers " +
377
383
  "collection URI. Set the property with " +
378
- "Context.getFollowersUri(handle).");
384
+ "Context.getFollowersUri(identifier).");
379
385
  }
380
386
  }
381
387
  if (this.outboxCallbacks != null &&
@@ -383,12 +389,12 @@ export class FederationImpl {
383
389
  if (actor?.outboxId == null) {
384
390
  logger.warn("You configured an outbox collection dispatcher, but the " +
385
391
  "actor does not have an outbox property. Set the property " +
386
- "with Context.getOutboxUri(handle).");
392
+ "with Context.getOutboxUri(identifier).");
387
393
  }
388
- else if (actor.outboxId.href != context.getOutboxUri(handle).href) {
394
+ else if (actor.outboxId.href != context.getOutboxUri(identifier).href) {
389
395
  logger.warn("You configured an outbox collection dispatcher, but the " +
390
396
  "actor's outbox property does not match the outbox collection " +
391
- "URI. Set the property with Context.getOutboxUri(handle).");
397
+ "URI. Set the property with Context.getOutboxUri(identifier).");
392
398
  }
393
399
  }
394
400
  if (this.likedCallbacks != null &&
@@ -396,12 +402,12 @@ export class FederationImpl {
396
402
  if (actor?.likedId == null) {
397
403
  logger.warn("You configured a liked collection dispatcher, but the " +
398
404
  "actor does not have a liked property. Set the property " +
399
- "with Context.getLikedUri(handle).");
405
+ "with Context.getLikedUri(identifier).");
400
406
  }
401
- else if (actor.likedId.href != context.getLikedUri(handle).href) {
407
+ else if (actor.likedId.href != context.getLikedUri(identifier).href) {
402
408
  logger.warn("You configured a liked collection dispatcher, but the " +
403
409
  "actor's liked property does not match the liked collection " +
404
- "URI. Set the property with Context.getLikedUri(handle).");
410
+ "URI. Set the property with Context.getLikedUri(identifier).");
405
411
  }
406
412
  }
407
413
  if (this.featuredCallbacks != null &&
@@ -409,12 +415,12 @@ export class FederationImpl {
409
415
  if (actor?.featuredId == null) {
410
416
  logger.warn("You configured a featured collection dispatcher, but the " +
411
417
  "actor does not have a featured property. Set the property " +
412
- "with Context.getFeaturedUri(handle).");
418
+ "with Context.getFeaturedUri(identifier).");
413
419
  }
414
- else if (actor.featuredId.href != context.getFeaturedUri(handle).href) {
420
+ else if (actor.featuredId.href != context.getFeaturedUri(identifier).href) {
415
421
  logger.warn("You configured a featured collection dispatcher, but the " +
416
422
  "actor's featured property does not match the featured collection " +
417
- "URI. Set the property with Context.getFeaturedUri(handle).");
423
+ "URI. Set the property with Context.getFeaturedUri(identifier).");
418
424
  }
419
425
  }
420
426
  if (this.featuredTagsCallbacks != null &&
@@ -422,25 +428,26 @@ export class FederationImpl {
422
428
  if (actor?.featuredTagsId == null) {
423
429
  logger.warn("You configured a featured tags collection dispatcher, but the " +
424
430
  "actor does not have a featuredTags property. Set the property " +
425
- "with Context.getFeaturedTagsUri(handle).");
431
+ "with Context.getFeaturedTagsUri(identifier).");
426
432
  }
427
- else if (actor.featuredTagsId.href != context.getFeaturedTagsUri(handle).href) {
433
+ else if (actor.featuredTagsId.href !=
434
+ context.getFeaturedTagsUri(identifier).href) {
428
435
  logger.warn("You configured a featured tags collection dispatcher, but the " +
429
436
  "actor's featuredTags property does not match the featured tags " +
430
437
  "collection URI. Set the property with " +
431
- "Context.getFeaturedTagsUri(handle).");
438
+ "Context.getFeaturedTagsUri(identifier).");
432
439
  }
433
440
  }
434
441
  if (this.router.has("inbox")) {
435
442
  if (actor.inboxId == null) {
436
443
  logger.warn("You configured inbox listeners, but the actor does not " +
437
444
  "have an inbox property. Set the property with " +
438
- "Context.getInboxUri(handle).");
445
+ "Context.getInboxUri(identifier).");
439
446
  }
440
- else if (actor.inboxId.href != context.getInboxUri(handle).href) {
447
+ else if (actor.inboxId.href != context.getInboxUri(identifier).href) {
441
448
  logger.warn("You configured inbox listeners, but the actor's inbox " +
442
449
  "property does not match the inbox URI. Set the property " +
443
- "with Context.getInboxUri(handle).");
450
+ "with Context.getInboxUri(identifier).");
444
451
  }
445
452
  if (actor.endpoints == null || actor.endpoints.sharedInbox == null) {
446
453
  logger.warn("You configured inbox listeners, but the actor does not have " +
@@ -457,12 +464,12 @@ export class FederationImpl {
457
464
  if (actor.publicKeyId == null) {
458
465
  logger.warn("You configured a key pairs dispatcher, but the actor does " +
459
466
  "not have a publicKey property. Set the property with " +
460
- "Context.getActorKeyPairs(handle).");
467
+ "Context.getActorKeyPairs(identifier).");
461
468
  }
462
469
  if (actor.assertionMethodId == null) {
463
470
  logger.warn("You configured a key pairs dispatcher, but the actor does " +
464
471
  "not have an assertionMethod property. Set the property " +
465
- "with Context.getActorKeyPairs(handle).");
472
+ "with Context.getActorKeyPairs(identifier).");
466
473
  }
467
474
  }
468
475
  return actor;
@@ -521,8 +528,13 @@ export class FederationImpl {
521
528
  }
522
529
  else {
523
530
  const variables = this.router.add(path, "inbox");
524
- if (variables.size !== 1 || !variables.has("handle")) {
525
- throw new RouterError("Path for inbox dispatcher must have one variable: {handle}");
531
+ if (variables.size !== 1 ||
532
+ !(variables.has("identifier") || variables.has("handle"))) {
533
+ throw new RouterError("Path for inbox dispatcher must have one variable: {identifier}");
534
+ }
535
+ if (variables.has("handle")) {
536
+ getLogger(["fedify", "federation", "inbox"]).warn("The {handle} variable in the inbox dispatcher path is deprecated. " +
537
+ "Use {identifier} instead.");
526
538
  }
527
539
  this.inboxPath = path;
528
540
  }
@@ -553,8 +565,13 @@ export class FederationImpl {
553
565
  throw new RouterError("Outbox dispatcher already set.");
554
566
  }
555
567
  const variables = this.router.add(path, "outbox");
556
- if (variables.size !== 1 || !variables.has("handle")) {
557
- throw new RouterError("Path for outbox dispatcher must have one variable: {handle}");
568
+ if (variables.size !== 1 ||
569
+ !(variables.has("identifier") || variables.has("handle"))) {
570
+ throw new RouterError("Path for outbox dispatcher must have one variable: {identifier}");
571
+ }
572
+ if (variables.has("handle")) {
573
+ getLogger(["fedify", "federation", "outbox"]).warn("The {handle} variable in the outbox dispatcher path is deprecated. " +
574
+ "Use {identifier} instead.");
558
575
  }
559
576
  const callbacks = { dispatcher };
560
577
  this.outboxCallbacks = callbacks;
@@ -583,8 +600,14 @@ export class FederationImpl {
583
600
  throw new RouterError("Following collection dispatcher already set.");
584
601
  }
585
602
  const variables = this.router.add(path, "following");
586
- if (variables.size !== 1 || !variables.has("handle")) {
587
- throw new RouterError("Path for following collection dispatcher must have one variable: {handle}");
603
+ if (variables.size !== 1 ||
604
+ !(variables.has("identifier") || variables.has("handle"))) {
605
+ throw new RouterError("Path for following collection dispatcher must have one variable: " +
606
+ "{identifier}");
607
+ }
608
+ if (variables.has("handle")) {
609
+ getLogger(["fedify", "federation", "collection"]).warn("The {handle} variable in the following collection dispatcher path " +
610
+ "is deprecated. Use {identifier} instead.");
588
611
  }
589
612
  const callbacks = { dispatcher };
590
613
  this.followingCallbacks = callbacks;
@@ -613,8 +636,14 @@ export class FederationImpl {
613
636
  throw new RouterError("Followers collection dispatcher already set.");
614
637
  }
615
638
  const variables = this.router.add(path, "followers");
616
- if (variables.size !== 1 || !variables.has("handle")) {
617
- throw new RouterError("Path for followers collection dispatcher must have one variable: {handle}");
639
+ if (variables.size !== 1 ||
640
+ !(variables.has("identifier") || variables.has("handle"))) {
641
+ throw new RouterError("Path for followers collection dispatcher must have one variable: " +
642
+ "{identifier}");
643
+ }
644
+ if (variables.has("handle")) {
645
+ getLogger(["fedify", "federation", "collection"]).warn("The {handle} variable in the followers collection dispatcher path " +
646
+ "is deprecated. Use {identifier} instead.");
618
647
  }
619
648
  const callbacks = { dispatcher };
620
649
  this.followersCallbacks = callbacks;
@@ -643,8 +672,14 @@ export class FederationImpl {
643
672
  throw new RouterError("Liked collection dispatcher already set.");
644
673
  }
645
674
  const variables = this.router.add(path, "liked");
646
- if (variables.size !== 1 || !variables.has("handle")) {
647
- throw new RouterError("Path for liked collection dispatcher must have one variable: {handle}");
675
+ if (variables.size !== 1 ||
676
+ !(variables.has("identifier") || variables.has("handle"))) {
677
+ throw new RouterError("Path for liked collection dispatcher must have one variable: " +
678
+ "{identifier}");
679
+ }
680
+ if (variables.has("handle")) {
681
+ getLogger(["fedify", "federation", "collection"]).warn("The {handle} variable in the liked collection dispatcher path " +
682
+ "is deprecated. Use {identifier} instead.");
648
683
  }
649
684
  const callbacks = { dispatcher };
650
685
  this.likedCallbacks = callbacks;
@@ -673,8 +708,14 @@ export class FederationImpl {
673
708
  throw new RouterError("Featured collection dispatcher already set.");
674
709
  }
675
710
  const variables = this.router.add(path, "featured");
676
- if (variables.size !== 1 || !variables.has("handle")) {
677
- throw new RouterError("Path for featured collection dispatcher must have one variable: {handle}");
711
+ if (variables.size !== 1 ||
712
+ !(variables.has("identifier") || variables.has("handle"))) {
713
+ throw new RouterError("Path for featured collection dispatcher must have one variable: " +
714
+ "{identifier}");
715
+ }
716
+ if (variables.has("handle")) {
717
+ getLogger(["fedify", "federation", "collection"]).warn("The {handle} variable in the featured collection dispatcher path " +
718
+ "is deprecated. Use {identifier} instead.");
678
719
  }
679
720
  const callbacks = { dispatcher };
680
721
  this.featuredCallbacks = callbacks;
@@ -703,9 +744,14 @@ export class FederationImpl {
703
744
  throw new RouterError("Featured tags collection dispatcher already set.");
704
745
  }
705
746
  const variables = this.router.add(path, "featuredTags");
706
- if (variables.size !== 1 || !variables.has("handle")) {
747
+ if (variables.size !== 1 ||
748
+ !(variables.has("identifier") || variables.has("handle"))) {
707
749
  throw new RouterError("Path for featured tags collection dispatcher must have one " +
708
- "variable: {handle}");
750
+ "variable: {identifier}");
751
+ }
752
+ if (variables.has("handle")) {
753
+ getLogger(["fedify", "federation", "collection"]).warn("The {handle} variable in the featured tags collection dispatcher " +
754
+ "path is deprecated. Use {identifier} instead.");
709
755
  }
710
756
  const callbacks = { dispatcher };
711
757
  this.featuredTagsCallbacks = callbacks;
@@ -740,10 +786,15 @@ export class FederationImpl {
740
786
  }
741
787
  else {
742
788
  const variables = this.router.add(inboxPath, "inbox");
743
- if (variables.size !== 1 || !variables.has("handle")) {
744
- throw new RouterError("Path for inbox must have one variable: {handle}");
789
+ if (variables.size !== 1 ||
790
+ !(variables.has("identifier") || variables.has("handle"))) {
791
+ throw new RouterError("Path for inbox must have one variable: {identifier}");
745
792
  }
746
793
  this.inboxPath = inboxPath;
794
+ if (variables.has("handle")) {
795
+ getLogger(["fedify", "federation", "inbox"]).warn("The {handle} variable in the inbox path is deprecated. " +
796
+ "Use {identifier} instead.");
797
+ }
747
798
  }
748
799
  if (sharedInboxPath != null) {
749
800
  const siVars = this.router.add(sharedInboxPath, "sharedInbox");
@@ -950,10 +1001,12 @@ export class FederationImpl {
950
1001
  });
951
1002
  case "actor":
952
1003
  context = this.#createContext(request, contextData, {
953
- invokedFromActorDispatcher: { handle: route.values.handle },
1004
+ invokedFromActorDispatcher: {
1005
+ identifier: route.values.identifier ?? route.values.handle,
1006
+ },
954
1007
  });
955
1008
  return await handleActor(request, {
956
- handle: route.values.handle,
1009
+ identifier: route.values.identifier ?? route.values.handle,
957
1010
  context,
958
1011
  actorDispatcher: this.actorCallbacks?.dispatcher,
959
1012
  authorizePredicate: this.actorCallbacks?.authorizePredicate,
@@ -981,7 +1034,7 @@ export class FederationImpl {
981
1034
  case "outbox":
982
1035
  return await handleCollection(request, {
983
1036
  name: "outbox",
984
- handle: route.values.handle,
1037
+ identifier: route.values.identifier ?? route.values.handle,
985
1038
  uriGetter: context.getOutboxUri.bind(context),
986
1039
  context,
987
1040
  collectionCallbacks: this.outboxCallbacks,
@@ -993,7 +1046,7 @@ export class FederationImpl {
993
1046
  if (request.method !== "POST") {
994
1047
  return await handleCollection(request, {
995
1048
  name: "inbox",
996
- handle: route.values.handle,
1049
+ identifier: route.values.identifier ?? route.values.handle,
997
1050
  uriGetter: context.getInboxUri.bind(context),
998
1051
  context,
999
1052
  collectionCallbacks: this.inboxCallbacks,
@@ -1004,7 +1057,7 @@ export class FederationImpl {
1004
1057
  }
1005
1058
  context = this.#createContext(request, contextData, {
1006
1059
  documentLoader: await context.getDocumentLoader({
1007
- handle: route.values.handle,
1060
+ identifier: route.values.identifier ?? route.values.handle,
1008
1061
  }),
1009
1062
  });
1010
1063
  // falls through
@@ -1013,7 +1066,8 @@ export class FederationImpl {
1013
1066
  const identity = await this.sharedInboxKeyDispatcher(context);
1014
1067
  if (identity != null) {
1015
1068
  context = this.#createContext(request, contextData, {
1016
- documentLoader: "handle" in identity
1069
+ documentLoader: "identifier" in identity || "username" in identity ||
1070
+ "handle" in identity
1017
1071
  ? await context.getDocumentLoader(identity)
1018
1072
  : context.getDocumentLoader(identity),
1019
1073
  });
@@ -1022,7 +1076,7 @@ export class FederationImpl {
1022
1076
  if (!this.manuallyStartQueue)
1023
1077
  this.#startQueue(contextData);
1024
1078
  return await handleInbox(request, {
1025
- handle: route.values.handle ?? null,
1079
+ identifier: route.values.identifier ?? route.values.handle ?? null,
1026
1080
  context,
1027
1081
  inboxContextFactory: context.toInboxContext.bind(context),
1028
1082
  kv: this.kv,
@@ -1038,7 +1092,7 @@ export class FederationImpl {
1038
1092
  case "following":
1039
1093
  return await handleCollection(request, {
1040
1094
  name: "following",
1041
- handle: route.values.handle,
1095
+ identifier: route.values.identifier ?? route.values.handle,
1042
1096
  uriGetter: context.getFollowingUri.bind(context),
1043
1097
  context,
1044
1098
  collectionCallbacks: this.followingCallbacks,
@@ -1054,7 +1108,7 @@ export class FederationImpl {
1054
1108
  }
1055
1109
  return await handleCollection(request, {
1056
1110
  name: "followers",
1057
- handle: route.values.handle,
1111
+ identifier: route.values.identifier ?? route.values.handle,
1058
1112
  uriGetter: context.getFollowersUri.bind(context),
1059
1113
  context,
1060
1114
  filter: baseUrl != null ? new URL(baseUrl) : undefined,
@@ -1070,7 +1124,7 @@ export class FederationImpl {
1070
1124
  case "liked":
1071
1125
  return await handleCollection(request, {
1072
1126
  name: "liked",
1073
- handle: route.values.handle,
1127
+ identifier: route.values.identifier ?? route.values.handle,
1074
1128
  uriGetter: context.getLikedUri.bind(context),
1075
1129
  context,
1076
1130
  collectionCallbacks: this.likedCallbacks,
@@ -1081,7 +1135,7 @@ export class FederationImpl {
1081
1135
  case "featured":
1082
1136
  return await handleCollection(request, {
1083
1137
  name: "featured",
1084
- handle: route.values.handle,
1138
+ identifier: route.values.identifier ?? route.values.handle,
1085
1139
  uriGetter: context.getFeaturedUri.bind(context),
1086
1140
  context,
1087
1141
  collectionCallbacks: this.featuredCallbacks,
@@ -1092,7 +1146,7 @@ export class FederationImpl {
1092
1146
  case "featuredTags":
1093
1147
  return await handleCollection(request, {
1094
1148
  name: "featured tags",
1095
- handle: route.values.handle,
1149
+ identifier: route.values.identifier ?? route.values.handle,
1096
1150
  uriGetter: context.getFeaturedTagsUri.bind(context),
1097
1151
  context,
1098
1152
  collectionCallbacks: this.featuredTagsCallbacks,
@@ -1149,8 +1203,8 @@ class ContextImpl {
1149
1203
  }
1150
1204
  return new URL(path, this.url);
1151
1205
  }
1152
- getActorUri(handle) {
1153
- const path = this.federation.router.build("actor", { handle });
1206
+ getActorUri(identifier) {
1207
+ const path = this.federation.router.build("actor", { identifier, handle: identifier });
1154
1208
  if (path == null) {
1155
1209
  throw new RouterError("No actor dispatcher registered.");
1156
1210
  }
@@ -1174,57 +1228,57 @@ class ContextImpl {
1174
1228
  }
1175
1229
  return new URL(path, this.url);
1176
1230
  }
1177
- getOutboxUri(handle) {
1178
- const path = this.federation.router.build("outbox", { handle });
1231
+ getOutboxUri(identifier) {
1232
+ const path = this.federation.router.build("outbox", { identifier, handle: identifier });
1179
1233
  if (path == null) {
1180
1234
  throw new RouterError("No outbox dispatcher registered.");
1181
1235
  }
1182
1236
  return new URL(path, this.url);
1183
1237
  }
1184
- getInboxUri(handle) {
1185
- if (handle == null) {
1238
+ getInboxUri(identifier) {
1239
+ if (identifier == null) {
1186
1240
  const path = this.federation.router.build("sharedInbox", {});
1187
1241
  if (path == null) {
1188
1242
  throw new RouterError("No shared inbox path registered.");
1189
1243
  }
1190
1244
  return new URL(path, this.url);
1191
1245
  }
1192
- const path = this.federation.router.build("inbox", { handle });
1246
+ const path = this.federation.router.build("inbox", { identifier, handle: identifier });
1193
1247
  if (path == null) {
1194
1248
  throw new RouterError("No inbox path registered.");
1195
1249
  }
1196
1250
  return new URL(path, this.url);
1197
1251
  }
1198
- getFollowingUri(handle) {
1199
- const path = this.federation.router.build("following", { handle });
1252
+ getFollowingUri(identifier) {
1253
+ const path = this.federation.router.build("following", { identifier, handle: identifier });
1200
1254
  if (path == null) {
1201
1255
  throw new RouterError("No following collection path registered.");
1202
1256
  }
1203
1257
  return new URL(path, this.url);
1204
1258
  }
1205
- getFollowersUri(handle) {
1206
- const path = this.federation.router.build("followers", { handle });
1259
+ getFollowersUri(identifier) {
1260
+ const path = this.federation.router.build("followers", { identifier, handle: identifier });
1207
1261
  if (path == null) {
1208
1262
  throw new RouterError("No followers collection path registered.");
1209
1263
  }
1210
1264
  return new URL(path, this.url);
1211
1265
  }
1212
- getLikedUri(handle) {
1213
- const path = this.federation.router.build("liked", { handle });
1266
+ getLikedUri(identifier) {
1267
+ const path = this.federation.router.build("liked", { identifier, handle: identifier });
1214
1268
  if (path == null) {
1215
1269
  throw new RouterError("No liked collection path registered.");
1216
1270
  }
1217
1271
  return new URL(path, this.url);
1218
1272
  }
1219
- getFeaturedUri(handle) {
1220
- const path = this.federation.router.build("featured", { handle });
1273
+ getFeaturedUri(identifier) {
1274
+ const path = this.federation.router.build("featured", { identifier, handle: identifier });
1221
1275
  if (path == null) {
1222
1276
  throw new RouterError("No featured collection path registered.");
1223
1277
  }
1224
1278
  return new URL(path, this.url);
1225
1279
  }
1226
- getFeaturedTagsUri(handle) {
1227
- const path = this.federation.router.build("featuredTags", { handle });
1280
+ getFeaturedTagsUri(identifier) {
1281
+ const path = this.federation.router.build("featuredTags", { identifier, handle: identifier });
1228
1282
  if (path == null) {
1229
1283
  throw new RouterError("No featured tags collection path registered.");
1230
1284
  }
@@ -1236,10 +1290,33 @@ class ContextImpl {
1236
1290
  if (uri.origin !== this.url.origin)
1237
1291
  return null;
1238
1292
  const route = this.federation.router.route(uri.pathname);
1293
+ const logger = getLogger(["fedify", "federation"]);
1239
1294
  if (route == null)
1240
1295
  return null;
1241
- else if (route.name === "actor") {
1242
- return { type: "actor", handle: route.values.handle };
1296
+ else if (route.name === "sharedInbox") {
1297
+ return {
1298
+ type: "inbox",
1299
+ identifier: undefined,
1300
+ get handle() {
1301
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1302
+ "use ParseUriResult.identifier instead.");
1303
+ return undefined;
1304
+ },
1305
+ };
1306
+ }
1307
+ const identifier = "identifier" in route.values
1308
+ ? route.values.identifier
1309
+ : route.values.handle;
1310
+ if (route.name === "actor") {
1311
+ return {
1312
+ type: "actor",
1313
+ identifier,
1314
+ get handle() {
1315
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1316
+ "use ParseUriResult.identifier instead.");
1317
+ return identifier;
1318
+ },
1319
+ };
1243
1320
  }
1244
1321
  else if (route.name.startsWith("object:")) {
1245
1322
  const typeId = route.name.replace(/^object:/, "");
@@ -1251,50 +1328,104 @@ class ContextImpl {
1251
1328
  };
1252
1329
  }
1253
1330
  else if (route.name === "inbox") {
1254
- return { type: "inbox", handle: route.values.handle };
1255
- }
1256
- else if (route.name === "sharedInbox") {
1257
- return { type: "inbox" };
1331
+ return {
1332
+ type: "inbox",
1333
+ identifier,
1334
+ get handle() {
1335
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1336
+ "use ParseUriResult.identifier instead.");
1337
+ return identifier;
1338
+ },
1339
+ };
1258
1340
  }
1259
1341
  else if (route.name === "outbox") {
1260
- return { type: "outbox", handle: route.values.handle };
1342
+ return {
1343
+ type: "outbox",
1344
+ identifier,
1345
+ get handle() {
1346
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1347
+ "use ParseUriResult.identifier instead.");
1348
+ return identifier;
1349
+ },
1350
+ };
1261
1351
  }
1262
1352
  else if (route.name === "following") {
1263
- return { type: "following", handle: route.values.handle };
1353
+ return {
1354
+ type: "following",
1355
+ identifier,
1356
+ get handle() {
1357
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1358
+ "use ParseUriResult.identifier instead.");
1359
+ return identifier;
1360
+ },
1361
+ };
1264
1362
  }
1265
1363
  else if (route.name === "followers") {
1266
- return { type: "followers", handle: route.values.handle };
1364
+ return {
1365
+ type: "followers",
1366
+ identifier,
1367
+ get handle() {
1368
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1369
+ "use ParseUriResult.identifier instead.");
1370
+ return identifier;
1371
+ },
1372
+ };
1267
1373
  }
1268
1374
  else if (route.name === "liked") {
1269
- return { type: "liked", handle: route.values.handle };
1375
+ return {
1376
+ type: "liked",
1377
+ identifier,
1378
+ get handle() {
1379
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1380
+ "use ParseUriResult.identifier instead.");
1381
+ return identifier;
1382
+ },
1383
+ };
1270
1384
  }
1271
1385
  else if (route.name === "featured") {
1272
- return { type: "featured", handle: route.values.handle };
1386
+ return {
1387
+ type: "featured",
1388
+ identifier,
1389
+ get handle() {
1390
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1391
+ "use ParseUriResult.identifier instead.");
1392
+ return identifier;
1393
+ },
1394
+ };
1273
1395
  }
1274
1396
  else if (route.name === "featuredTags") {
1275
- return { type: "featuredTags", handle: route.values.handle };
1397
+ return {
1398
+ type: "featuredTags",
1399
+ identifier,
1400
+ get handle() {
1401
+ logger.warn("The ParseUriResult.handle property is deprecated; " +
1402
+ "use ParseUriResult.identifier instead.");
1403
+ return identifier;
1404
+ },
1405
+ };
1276
1406
  }
1277
1407
  return null;
1278
1408
  }
1279
- async getActorKeyPairs(handle) {
1409
+ async getActorKeyPairs(identifier) {
1280
1410
  const logger = getLogger(["fedify", "federation", "actor"]);
1281
1411
  if (this.invokedFromActorKeyPairsDispatcher != null) {
1282
- logger.warn("Context.getActorKeyPairs({getActorKeyPairsHandle}) method is " +
1412
+ logger.warn("Context.getActorKeyPairs({getActorKeyPairsIdentifier}) method is " +
1283
1413
  "invoked from the actor key pairs dispatcher " +
1284
- "({actorKeyPairsDispatcherHandle}); this may cause an infinite loop.", {
1285
- getActorKeyPairsHandle: handle,
1286
- actorKeyPairsDispatcherHandle: this.invokedFromActorKeyPairsDispatcher.handle,
1414
+ "({actorKeyPairsDispatcherIdentifier}); this may cause " +
1415
+ "an infinite loop.", {
1416
+ getActorKeyPairsIdentifier: identifier,
1417
+ actorKeyPairsDispatcherIdentifier: this.invokedFromActorKeyPairsDispatcher.identifier,
1287
1418
  });
1288
1419
  }
1289
1420
  let keyPairs;
1290
1421
  try {
1291
- keyPairs = await this.getKeyPairsFromHandle(handle);
1422
+ keyPairs = await this.getKeyPairsFromIdentifier(identifier);
1292
1423
  }
1293
1424
  catch (_) {
1294
1425
  logger.warn("No actor key pairs dispatcher registered.");
1295
1426
  return [];
1296
1427
  }
1297
- const owner = this.getActorUri(handle);
1428
+ const owner = this.getActorUri(identifier);
1298
1429
  const result = [];
1299
1430
  for (const keyPair of keyPairs) {
1300
1431
  const newPair = {
@@ -1314,12 +1445,12 @@ class ContextImpl {
1314
1445
  }
1315
1446
  return result;
1316
1447
  }
1317
- async getKeyPairsFromHandle(handle) {
1448
+ async getKeyPairsFromIdentifier(identifier) {
1318
1449
  const logger = getLogger(["fedify", "federation", "actor"]);
1319
1450
  if (this.federation.actorCallbacks?.keyPairsDispatcher == null) {
1320
1451
  throw new Error("No actor key pairs dispatcher registered.");
1321
1452
  }
1322
- const path = this.federation.router.build("actor", { handle });
1453
+ const path = this.federation.router.build("actor", { identifier, handle: identifier });
1323
1454
  if (path == null) {
1324
1455
  logger.warn("No actor dispatcher registered.");
1325
1456
  return [];
@@ -1327,10 +1458,10 @@ class ContextImpl {
1327
1458
  const actorUri = new URL(path, this.url);
1328
1459
  const keyPairs = await this.federation.actorCallbacks?.keyPairsDispatcher(new ContextImpl({
1329
1460
  ...this,
1330
- invokedFromActorKeyPairsDispatcher: { handle },
1331
- }), handle);
1461
+ invokedFromActorKeyPairsDispatcher: { identifier },
1462
+ }), identifier);
1332
1463
  if (keyPairs.length < 1) {
1333
- logger.warn("No key pairs found for actor {handle}.", { handle });
1464
+ logger.warn("No key pairs found for actor {identifier}.", { identifier });
1334
1465
  }
1335
1466
  let i = 0;
1336
1467
  const result = [];
@@ -1345,8 +1476,8 @@ class ContextImpl {
1345
1476
  }
1346
1477
  return result;
1347
1478
  }
1348
- async getRsaKeyPairFromHandle(handle) {
1349
- const keyPairs = await this.getKeyPairsFromHandle(handle);
1479
+ async getRsaKeyPairFromIdentifier(identifier) {
1480
+ const keyPairs = await this.getKeyPairsFromIdentifier(identifier);
1350
1481
  for (const keyPair of keyPairs) {
1351
1482
  const { privateKey } = keyPair;
1352
1483
  if (privateKey.algorithm.name === "RSASSA-PKCS1-v1_5" &&
@@ -1356,15 +1487,44 @@ class ContextImpl {
1356
1487
  return keyPair;
1357
1488
  }
1358
1489
  }
1359
- getLogger(["fedify", "federation", "actor"]).warn("No RSA-PKCS#1-v1.5 SHA-256 key found for actor {handle}.", { handle });
1490
+ getLogger(["fedify", "federation", "actor"]).warn("No RSA-PKCS#1-v1.5 SHA-256 key found for actor {identifier}.", { identifier });
1360
1491
  return null;
1361
1492
  }
1362
1493
  getDocumentLoader(identity) {
1363
- if ("handle" in identity) {
1364
- const keyPair = this.getRsaKeyPairFromHandle(identity.handle);
1365
- return keyPair.then((pair) => pair == null
1366
- ? this.documentLoader
1367
- : this.federation.authenticatedDocumentLoaderFactory(pair));
1494
+ if ("identifier" in identity || "username" in identity || "handle" in identity) {
1495
+ let identifierPromise;
1496
+ if ("username" in identity || "handle" in identity) {
1497
+ let username;
1498
+ if ("username" in identity) {
1499
+ username = identity.username;
1500
+ }
1501
+ else {
1502
+ username = identity.handle;
1503
+ getLogger(["fedify", "runtime", "docloader"]).warn('The "handle" property is deprecated; use "identifier" or ' +
1504
+ '"username" instead.', { identity });
1505
+ }
1506
+ const mapper = this.federation.actorCallbacks?.handleMapper;
1507
+ if (mapper == null) {
1508
+ identifierPromise = Promise.resolve(username);
1509
+ }
1510
+ else {
1511
+ const identifier = mapper(this, username);
1512
+ identifierPromise = identifier instanceof Promise
1513
+ ? identifier
1514
+ : Promise.resolve(identifier);
1515
+ }
1516
+ }
1517
+ else {
1518
+ identifierPromise = Promise.resolve(identity.identifier);
1519
+ }
1520
+ return identifierPromise.then((identifier) => {
1521
+ if (identifier == null)
1522
+ return this.documentLoader;
1523
+ const keyPair = this.getRsaKeyPairFromIdentifier(identifier);
1524
+ return keyPair.then((pair) => pair == null
1525
+ ? this.documentLoader
1526
+ : this.federation.authenticatedDocumentLoaderFactory(pair));
1527
+ });
1368
1528
  }
1369
1529
  return this.federation.authenticatedDocumentLoaderFactory(identity);
1370
1530
  }
@@ -1376,10 +1536,35 @@ class ContextImpl {
1376
1536
  }
1377
1537
  async sendActivity(sender, recipients, activity, options = {}) {
1378
1538
  let keys;
1379
- if ("handle" in sender) {
1380
- keys = await this.getKeyPairsFromHandle(sender.handle);
1539
+ let identifier = null;
1540
+ if ("identifier" in sender || "username" in sender || "handle" in sender) {
1541
+ if ("identifier" in sender) {
1542
+ identifier = sender.identifier;
1543
+ }
1544
+ else {
1545
+ let username;
1546
+ if ("username" in sender) {
1547
+ username = sender.username;
1548
+ }
1549
+ else {
1550
+ username = sender.handle;
1551
+ getLogger(["fedify", "federation", "outbox"]).warn('The "handle" property for the sender parameter is deprecated; ' +
1552
+ 'use "identifier" or "username" instead.', { sender });
1553
+ }
1554
+ if (this.federation.actorCallbacks?.handleMapper == null) {
1555
+ identifier = username;
1556
+ }
1557
+ else {
1558
+ const mapped = await this.federation.actorCallbacks.handleMapper(this, username);
1559
+ if (mapped == null) {
1560
+ throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
1561
+ }
1562
+ identifier = mapped;
1563
+ }
1564
+ }
1565
+ keys = await this.getKeyPairsFromIdentifier(identifier);
1381
1566
  if (keys.length < 1) {
1382
- throw new Error(`No key pair found for actor ${JSON.stringify(sender.handle)}.`);
1567
+ throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
1383
1568
  }
1384
1569
  }
1385
1570
  else if (Array.isArray(sender)) {
@@ -1400,14 +1585,15 @@ class ContextImpl {
1400
1585
  expandedRecipients = recipients;
1401
1586
  }
1402
1587
  else if (recipients === "followers") {
1403
- if (!("handle" in sender)) {
1404
- throw new Error("If recipients is 'followers', sender must be an actor handle.");
1588
+ if (identifier == null) {
1589
+ throw new Error('If recipients is "followers", ' +
1590
+ "sender must be an actor identifier or username.");
1405
1591
  }
1406
1592
  expandedRecipients = [];
1407
- for await (const recipient of this.getFollowers(sender.handle)) {
1593
+ for await (const recipient of this.getFollowers(identifier)) {
1408
1594
  expandedRecipients.push(recipient);
1409
1595
  }
1410
- const collectionId = this.federation.router.build("followers", sender);
1596
+ const collectionId = this.federation.router.build("followers", { identifier, handle: identifier });
1411
1597
  opts.collectionSync = collectionId == null
1412
1598
  ? undefined
1413
1599
  : new URL(collectionId, this.url).href;
@@ -1417,11 +1603,11 @@ class ContextImpl {
1417
1603
  }
1418
1604
  return await this.federation.sendActivity(keys, expandedRecipients, activity, opts);
1419
1605
  }
1420
- async *getFollowers(handle) {
1606
+ async *getFollowers(identifier) {
1421
1607
  if (this.federation.followersCallbacks == null) {
1422
1608
  throw new Error("No followers collection dispatcher registered.");
1423
1609
  }
1424
- const result = await this.federation.followersCallbacks.dispatcher(this, handle, null);
1610
+ const result = await this.federation.followersCallbacks.dispatcher(this, identifier, null);
1425
1611
  if (result != null) {
1426
1612
  for (const recipient of result.items)
1427
1613
  yield recipient;
@@ -1430,9 +1616,9 @@ class ContextImpl {
1430
1616
  if (this.federation.followersCallbacks.firstCursor == null) {
1431
1617
  throw new Error("No first cursor dispatcher registered for followers collection.");
1432
1618
  }
1433
- let cursor = await this.federation.followersCallbacks.firstCursor(this, handle);
1619
+ let cursor = await this.federation.followersCallbacks.firstCursor(this, identifier);
1434
1620
  while (cursor != null) {
1435
- const result = await this.federation.followersCallbacks.dispatcher(this, handle, cursor);
1621
+ const result = await this.federation.followersCallbacks.dispatcher(this, identifier, cursor);
1436
1622
  if (result == null)
1437
1623
  break;
1438
1624
  for (const recipient of result.items)
@@ -1453,23 +1639,23 @@ class RequestContextImpl extends ContextImpl {
1453
1639
  this.request = options.request;
1454
1640
  this.url = options.url;
1455
1641
  }
1456
- async getActor(handle) {
1642
+ async getActor(identifier) {
1457
1643
  if (this.federation.actorCallbacks == null ||
1458
1644
  this.federation.actorCallbacks.dispatcher == null) {
1459
1645
  throw new Error("No actor dispatcher registered.");
1460
1646
  }
1461
1647
  if (this.#invokedFromActorDispatcher != null) {
1462
- getLogger(["fedify", "federation", "actor"]).warn("RequestContext.getActor({getActorHandle}) is invoked from " +
1463
- "the actor dispatcher ({actorDispatcherHandle}); " +
1648
+ getLogger(["fedify", "federation", "actor"]).warn("RequestContext.getActor({getActorIdentifier}) is invoked from " +
1649
+ "the actor dispatcher ({actorDispatcherIdentifier}); " +
1464
1650
  "this may cause an infinite loop.", {
1465
- getActorHandle: handle,
1466
- actorDispatcherHandle: this.#invokedFromActorDispatcher.handle,
1651
+ getActorIdentifier: identifier,
1652
+ actorDispatcherIdentifier: this.#invokedFromActorDispatcher.identifier,
1467
1653
  });
1468
1654
  }
1469
1655
  return await this.federation.actorCallbacks.dispatcher(new RequestContextImpl({
1470
1656
  ...this,
1471
- invokedFromActorDispatcher: { handle },
1472
- }), handle);
1657
+ invokedFromActorDispatcher: { identifier },
1658
+ }), identifier);
1473
1659
  }
1474
1660
  async getObject(
1475
1661
  // deno-lint-ignore no-explicit-any
@@ -1527,10 +1713,36 @@ export class InboxContextImpl extends ContextImpl {
1527
1713
  async forwardActivity(forwarder, recipients, options) {
1528
1714
  const logger = getLogger(["fedify", "federation", "inbox"]);
1529
1715
  let keys;
1530
- if ("handle" in forwarder) {
1531
- keys = await this.getKeyPairsFromHandle(forwarder.handle);
1716
+ let identifier = null;
1717
+ if ("identifier" in forwarder || "username" in forwarder ||
1718
+ "handle" in forwarder) {
1719
+ if ("identifier" in forwarder) {
1720
+ identifier = forwarder.identifier;
1721
+ }
1722
+ else {
1723
+ let username;
1724
+ if ("username" in forwarder) {
1725
+ username = forwarder.username;
1726
+ }
1727
+ else {
1728
+ username = forwarder.handle;
1729
+ logger.warn('The "handle" property for the forwarder parameter is deprecated; ' +
1730
+ 'use "identifier" or "username" instead.', { forwarder });
1731
+ }
1732
+ if (this.federation.actorCallbacks?.handleMapper == null) {
1733
+ identifier = username;
1734
+ }
1735
+ else {
1736
+ const mapped = await this.federation.actorCallbacks.handleMapper(this, username);
1737
+ if (mapped == null) {
1738
+ throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
1739
+ }
1740
+ identifier = mapped;
1741
+ }
1742
+ }
1743
+ keys = await this.getKeyPairsFromIdentifier(identifier);
1532
1744
  if (keys.length < 1) {
1533
- throw new Error(`No key pair found for actor ${JSON.stringify(forwarder.handle)}.`);
1745
+ throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
1534
1746
  }
1535
1747
  }
1536
1748
  else if (Array.isArray(forwarder)) {
@@ -1571,11 +1783,12 @@ export class InboxContextImpl extends ContextImpl {
1571
1783
  : undefined;
1572
1784
  }
1573
1785
  if (recipients === "followers") {
1574
- if (!("handle" in forwarder)) {
1575
- throw new Error("If recipients is 'followers', forwarder must be an actor handle.");
1786
+ if (identifier == null) {
1787
+ throw new Error('If recipients is "followers", ' +
1788
+ "forwarder must be an actor identifier or username.");
1576
1789
  }
1577
1790
  const followers = [];
1578
- for await (const recipient of this.getFollowers(forwarder.handle)) {
1791
+ for await (const recipient of this.getFollowers(identifier)) {
1579
1792
  followers.push(recipient);
1580
1793
  }
1581
1794
  recipients = followers;