Skip to content

outbox-listener-delivery-required does not resolve module-scope delivery helpers #1054

Description

@dahlia

Why

A listener that hands delivery to a helper declared outside it is reported as undelivered:

async function deliverToFollowers(ctx, activity) {
  await ctx.sendActivity({ identifier: ctx.identifier }, "followers", activity);
}

federation
  .setOutboxListeners("/users/{identifier}/outbox")
  .on(Activity, async (ctx, activity) => {
    await deliverToFollowers(ctx, activity);
  });

Pulling a delivery helper out to module scope is ordinary, so this is a false positive with no useful fix for the listener.

The behavior predates #1050. The original rule scanned only the listener's own source text, so a helper anywhere else was always invisible. The #1050 rewrite added machinery that can cover this case: collectNamedHelpers() and collectResolvedCallTargets() in packages/lint/src/rules/outbox-listener-delivery-required.ts already resolve calls through variable bindings, object literals, and sibling helpers. They are never given declarations above the listener body.

Scope

Seed the helper map from the enclosing module scope before walking the listener, so that a call to a module-level function resolves the way a local one does. A helper in the same file is the natural boundary.

Add a mutual-recursion test. The visited set already carried by computeUsedFunctions() should cover a cycle between the listener and a module-level helper.

Non-goals

No cross-file analysis and no type information, following #900. A helper imported from another module should keep reporting, and that limitation belongs in docs/manual/lint.md rather than in the analysis.

Suggested checks

Add cases to packages/lint/src/tests/outbox-listener-delivery-required.test.ts for a module-level function declaration, a module-level const arrow binding, a module-level helper that is declared but never called, and a helper imported from another module. The last two should still report.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Fields

Priority

None yet

Effort

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions