Why
A local helper can replace an object's delivery function, but the outbox delivery rules still inspect the original function when the listener calls it:
federation
.setOutboxListeners("/users/{identifier}/outbox")
.on(Activity, async (ctx, activity) => {
const target = { deliver: async () => {} };
const setup = () => {
target.deliver = async () => {
await ctx.sendActivity(
{ identifier: ctx.identifier }, "followers", activity,
);
};
};
setup();
await target.deliver();
});
setup() runs synchronously and replaces the empty function before target.deliver() is called. The listener delivers, but outbox-listener-delivery-required reports it as undelivered. Removing await from ctx.sendActivity() also makes outbox-listener-delivery-not-awaited miss the dropped promise.
Both failures were reproduced on Linux with Deno 2.9.5 (Deno lint) and Node.js 22.23.2 (ESLint), using the @fedify/lint 2.4.0 development checkout at 4abb5ee4 on the #1088 branch. The assignment behavior predates that PR.
In packages/lint/src/lib/reachability.ts, walkUsedScopes() creates a function map for each scope. The assignment updates setup's function map, but walkUsedScopes() does not carry that update back to the caller. The later call still resolves to the initial empty function. This differs from #1054: the helper is inside the listener, rather than at module scope.
Scope
Handle a directly called local setup helper that writes a delivery function to an enclosing object before the listener calls that function. Both outbox delivery rules should scan the installed function. Preserve the order of the setup and delivery calls; an uncalled setup helper, or one called only after the empty function is used, should not make the listener count as delivering.
A local documentation follow-up to #1088 (960f8552) records this limitation under outbox-listener-delivery-required and outbox-listener-delivery-not-awaited in docs/manual/lint.md. After implementing the fix, update both sections to remove or narrow the limitation and adjust the workaround to match the supported behavior.
Preserving functions already on the object is tracked separately in #1125. No module-scope helpers, cross-file analysis, or type information. Start with the straight-line synchronous setup shown above; asynchronous setup and writes whose execution cannot be established are outside this issue's initial scope.
Suggested checks
Test the example in both Deno lint and ESLint, with an awaited delivery and a dropped delivery promise. Add cases where setup is never called, is called only after target.deliver(), or writes to a local object that shadows the listener's target. Those cases should still report missing delivery, and should not report a dropped promise in an uninvoked delivery function.
Why
A local helper can replace an object's delivery function, but the outbox delivery rules still inspect the original function when the listener calls it:
setup()runs synchronously and replaces the empty function beforetarget.deliver()is called. The listener delivers, butoutbox-listener-delivery-requiredreports it as undelivered. Removingawaitfromctx.sendActivity()also makesoutbox-listener-delivery-not-awaitedmiss the dropped promise.Both failures were reproduced on Linux with Deno 2.9.5 (Deno lint) and Node.js 22.23.2 (ESLint), using the
@fedify/lint2.4.0 development checkout at4abb5ee4on the #1088 branch. The assignment behavior predates that PR.In packages/lint/src/lib/reachability.ts,
walkUsedScopes()creates a function map for each scope. The assignment updatessetup's function map, butwalkUsedScopes()does not carry that update back to the caller. The later call still resolves to the initial empty function. This differs from #1054: the helper is inside the listener, rather than at module scope.Scope
Handle a directly called local setup helper that writes a delivery function to an enclosing object before the listener calls that function. Both outbox delivery rules should scan the installed function. Preserve the order of the setup and delivery calls; an uncalled setup helper, or one called only after the empty function is used, should not make the listener count as delivering.
A local documentation follow-up to #1088 (
960f8552) records this limitation underoutbox-listener-delivery-requiredandoutbox-listener-delivery-not-awaitedin docs/manual/lint.md. After implementing the fix, update both sections to remove or narrow the limitation and adjust the workaround to match the supported behavior.Preserving functions already on the object is tracked separately in #1125. No module-scope helpers, cross-file analysis, or type information. Start with the straight-line synchronous setup shown above; asynchronous setup and writes whose execution cannot be established are outside this issue's initial scope.
Suggested checks
Test the example in both Deno lint and ESLint, with an awaited delivery and a dropped delivery promise. Add cases where
setupis never called, is called only aftertarget.deliver(), or writes to a local object that shadows the listener'starget. Those cases should still report missing delivery, and should not report a dropped promise in an uninvoked delivery function.