Skip to content

Commit 2334b6d

Browse files
committed
Refactor actor alias logic and harden type safety
Refactored `getActorUri()` to use a more idiomatic `??` pattern and avoid `let`. Hardened the `mapActorAlias()` signature by using a template literal type for the `path` parameter. Consolidated the `ACTOR_ALIAS_PREFIX` constant by exporting it from the builder and reusing it in the middleware. #753 (comment) #753 (comment) #753 (comment) Assisted-by: Gemini CLI:gemini-3-flash-preview Assisted-by: Codex:gpt-5.5
1 parent 80322f0 commit 2334b6d

5 files changed

Lines changed: 25 additions & 11 deletions

File tree

packages/fedify/src/federation/builder.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ test("FederationBuilder", async (t) => {
4444
RouterError,
4545
'Actor alias for "instance" already set.',
4646
);
47+
assertThrows(
48+
() =>
49+
createFederationBuilder<string>()
50+
.setActorDispatcher("/users/{identifier}", actorDispatcher)
51+
.mapActorAlias("/actor", "instance")
52+
.mapActorAlias("/actor", "bot"),
53+
RouterError,
54+
'Actor alias path "/actor" conflicts with existing route "actorAlias:instance".',
55+
);
4756
builder.setActorDispatcher("/users/{identifier}", actorDispatcher)
4857
.mapActorAlias("/actor", "instance");
4958

packages/fedify/src/federation/builder.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ import type {
6363
} from "./handler.ts";
6464
import { Router, RouterError } from "./router.ts";
6565

66-
const ACTOR_ALIAS_PREFIX = "actorAlias:";
66+
export const ACTOR_ALIAS_PREFIX = "actorAlias:";
6767

6868
function validateSingleIdentifierVariablePath(
6969
path: string,
@@ -524,7 +524,7 @@ export class FederationBuilderImpl<TContextData>
524524
callbacks.aliasMapper = mapper;
525525
return setters;
526526
},
527-
mapActorAlias: (path: string, identifier: string) => {
527+
mapActorAlias: (path: `/${string}`, identifier: string) => {
528528
if (this.router.has(`${ACTOR_ALIAS_PREFIX}${identifier}`)) {
529529
throw new RouterError(
530530
`Actor alias for "${identifier}" already set.`,
@@ -536,6 +536,12 @@ export class FederationBuilderImpl<TContextData>
536536
"Path for actor alias must have no variables.",
537537
);
538538
}
539+
const existingRoute = this.router.route(path);
540+
if (existingRoute != null) {
541+
throw new RouterError(
542+
`Actor alias path "${path}" conflicts with existing route "${existingRoute.name}".`,
543+
);
544+
}
539545
this.router.add(path, `${ACTOR_ALIAS_PREFIX}${identifier}`);
540546
return setters;
541547
},

packages/fedify/src/federation/federation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1120,7 +1120,7 @@ export interface ActorCallbackSetters<TContextData> {
11201120
* @since 2.3.0
11211121
*/
11221122
mapActorAlias(
1123-
path: string,
1123+
path: `/${string}`,
11241124
identifier: string,
11251125
): ActorCallbackSetters<TContextData>;
11261126

packages/fedify/src/federation/middleware.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,8 @@ test("Federation.fetch()", async (t) => {
12241224
assertEquals(response.status, 200);
12251225
const body = await response.json() as Record<string, unknown>;
12261226
assertEquals(body.subject, "acct:bot@example.com");
1227+
assertExists(body.links);
1228+
assert(Array.isArray(body.links));
12271229
const selfLink = (body.links as Record<string, unknown>[]).find((l) =>
12281230
l.rel === "self"
12291231
);

packages/fedify/src/federation/middleware.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ import type {
112112
} from "./queue.ts";
113113
import { createExponentialBackoffPolicy, type RetryPolicy } from "./retry.ts";
114114
import { RouterError } from "./router.ts";
115+
import { ACTOR_ALIAS_PREFIX } from "./builder.ts";
115116

116-
const ACTOR_ALIAS_PREFIX = "actorAlias:";
117117
import {
118118
extractInboxes,
119119
sendActivity,
@@ -1794,16 +1794,13 @@ export class ContextImpl<TContextData> implements Context<TContextData> {
17941794
}
17951795

17961796
getActorUri(identifier: string): URL {
1797-
let path = this.federation.router.build(
1797+
const path = this.federation.router.build(
17981798
`${ACTOR_ALIAS_PREFIX}${identifier}`,
17991799
{},
1800+
) ?? this.federation.router.build(
1801+
"actor",
1802+
{ identifier },
18001803
);
1801-
if (path == null) {
1802-
path = this.federation.router.build(
1803-
"actor",
1804-
{ identifier },
1805-
);
1806-
}
18071804
if (path == null) {
18081805
throw new RouterError("No actor dispatcher registered.");
18091806
}

0 commit comments

Comments
 (0)