Skip to content

Commit 2c2a834

Browse files
committed
Log deprecated Router warning once per process
The deprecated `Router`/`RouterError` compatibility shim from `@fedify/fedify/federation` logged a deprecation warning on every wrapped call: `convertRouterError()` wraps every compatibility method, so a deprecated `Router` used on the request path logged on every `route()` call, and the error path logged a second, duplicate warning when constructing the compatibility `RouterError`. For applications that keep the compatibility class during migration, this could flood logs. Consolidate both warnings into a single module-level one-shot `warnDeprecated()` guard so the migration signal is emitted at most once per process, and add a regression test asserting the warning is logged at most once across many wrapped calls and the error path. #758 (comment) Assisted-by: Claude Code:claude-opus-4-7
1 parent 3623012 commit 2c2a834

2 files changed

Lines changed: 67 additions & 8 deletions

File tree

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { test } from "@fedify/fixture";
22
import { RouterError as UriTemplateRouterError } from "@fedify/uri-template";
3+
import { configure, type LogRecord, reset } from "@logtape/logtape";
34
import { assert, assertEquals, assertFalse, assertThrows } from "@std/assert";
45
import { Router, RouterError, type RouterOptions } from "./router.ts";
56

@@ -119,6 +120,59 @@ test("Router.route() returns null for non-path inputs", () => {
119120
assertEquals(router.route("/unknown"), null);
120121
});
121122

123+
let logtapeLock: Promise<void> = Promise.resolve();
124+
125+
function withLogtapeLock<T>(fn: () => Promise<T>): Promise<T> {
126+
const run = logtapeLock.then(fn, fn);
127+
logtapeLock = run.then(() => undefined, () => undefined);
128+
return run;
129+
}
130+
131+
test("Router deprecation warning is logged at most once", () =>
132+
withLogtapeLock(async () => {
133+
const records: LogRecord[] = [];
134+
await reset();
135+
try {
136+
await configure({
137+
sinks: {
138+
buffer(record: LogRecord): void {
139+
records.push(record);
140+
},
141+
},
142+
filters: {},
143+
loggers: [
144+
{
145+
category: ["fedify", "federation", "router", "deprecated"],
146+
sinks: ["buffer"],
147+
},
148+
{ category: ["logtape", "meta"], sinks: [] },
149+
],
150+
});
151+
152+
const router = new Router();
153+
router.add("/users/{name}", "user");
154+
for (let i = 0; i < 50; i++) {
155+
router.route("/users/alice");
156+
router.route("https://example.com/not-a-path");
157+
router.has("user");
158+
router.build("user", { name: "alice" });
159+
}
160+
assertThrows(() => router.add("foo", "bar"), RouterError);
161+
162+
const deprecationWarnings = records.filter((record) =>
163+
typeof record.rawMessage === "string" &&
164+
record.rawMessage.includes("deprecated")
165+
);
166+
assert(
167+
deprecationWarnings.length <= 1,
168+
"expected at most one deprecation warning, got " +
169+
deprecationWarnings.length,
170+
);
171+
} finally {
172+
await reset();
173+
}
174+
}));
175+
122176
test("Compatibility between RouterErrors", () => {
123177
const newError = new UriTemplateRouterError("boom");
124178
assert(newError instanceof UriTemplateRouterError);

packages/fedify/src/federation/router.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ import { getLogger } from "@logtape/logtape";
1212

1313
const logger = getLogger(["fedify", "federation", "router", "deprecated"]);
1414

15+
let deprecationWarned = false;
16+
17+
function warnDeprecated(): void {
18+
if (deprecationWarned) return;
19+
deprecationWarned = true;
20+
logger.warn(
21+
"The `Router` and `RouterError` classes from `@fedify/fedify` are " +
22+
"deprecated. Please use `Router` from `@fedify/uri-template` instead.",
23+
);
24+
}
25+
1526
/**
1627
* Options for the {@link Router}.
1728
* @since 0.12.0
@@ -159,19 +170,13 @@ export class RouterError extends _RouterError {
159170
*/
160171
constructor(message: string) {
161172
super(message);
162-
logger.warn(
163-
"The `RouterError` class from `@fedify/fedify` is deprecated." +
164-
" Please use `Router` from `@fedify/uri-template` instead.",
165-
);
173+
warnDeprecated();
166174
}
167175
}
168176

169177
function convertRouterError<T>(func: () => T): T {
170178
try {
171-
logger.warn(
172-
"The `Router` class from `@fedify/fedify` is deprecated." +
173-
" Please use `Router` from `@fedify/uri-template` instead.",
174-
);
179+
warnDeprecated();
175180
return func();
176181
} catch (error) {
177182
if (error instanceof _RouterError) {

0 commit comments

Comments
 (0)