@fedify/lint 2.4.0-dev.2155 → 2.4.0-dev.2159

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.
package/deno.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@fedify/lint",
3
- "version": "2.4.0-dev.2155+2ccc5085",
3
+ "version": "2.4.0-dev.2159+bb072eda",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts",
7
7
  "./oxlint": "./src/oxlint.ts"
8
8
  },
9
9
  "imports": {
10
+ "@fedify/fixture": "jsr:@fedify/fixture@^2.2.14",
10
11
  "eslint": "npm:eslint@^9.0.0",
11
12
  "estree": "npm:@types/estree@^1.0.8",
12
13
  "oxlint": "npm:oxlint@^1.66.0"
package/dist/index.cjs CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
  //#endregion
27
- const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-rhAvEA9G.cjs");
27
+ const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-Hs9elJlF.cjs");
28
28
  let _fxts_core = require("@fxts/core");
29
29
  let _typescript_eslint_parser = require("@typescript-eslint/parser");
30
30
  _typescript_eslint_parser = __toESM(_typescript_eslint_parser, 1);
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as name, C as eslint$5, D as eslint$11, E as eslint$10, O as eslint$21, S as eslint$4, T as eslint$13, _ as eslint$15, a as eslint$23, b as eslint$2, c as eslint$16, d as eslint$22, f as eslint$6, g as eslint$14, h as eslint$9, i as eslint$27, j as version, k as RULE_IDS, l as eslint$17, m as eslint$8, n as eslint$25, o as eslint$18, p as eslint$7, r as eslint$26, s as eslint$19, t as eslint$24, u as eslint$20, v as eslint$1, w as eslint$12, x as eslint$3, y as eslint } from "./outbox-listener-delivery-required-vNt3zM_a.js";
1
+ import { A as name, C as eslint$5, D as eslint$11, E as eslint$10, O as eslint$21, S as eslint$4, T as eslint$13, _ as eslint$15, a as eslint$23, b as eslint$2, c as eslint$16, d as eslint$22, f as eslint$6, g as eslint$14, h as eslint$9, i as eslint$27, j as version, k as RULE_IDS, l as eslint$17, m as eslint$8, n as eslint$25, o as eslint$18, p as eslint$7, r as eslint$26, s as eslint$19, t as eslint$24, u as eslint$20, v as eslint$1, w as eslint$12, x as eslint$3, y as eslint } from "./outbox-listener-delivery-required-Cz_XWiEU.js";
2
2
  import { fromEntries, keys, map, pipe } from "@fxts/core";
3
3
  import parser from "@typescript-eslint/parser";
4
4
  //#region src/index.ts
@@ -1,7 +1,7 @@
1
1
  import { always, every, head, isEmpty, isObject, negate, pipe, pipeLazy, prop, some, toArray, unless, when } from "@fxts/core";
2
2
  //#region deno.json
3
3
  var name = "@fedify/lint";
4
- var version = "2.4.0-dev.2155+2ccc5085";
4
+ var version = "2.4.0-dev.2159+bb072eda";
5
5
  //#endregion
6
6
  //#region src/lib/const.ts
7
7
  /**
@@ -621,7 +621,7 @@ const eslint$4 = {
621
621
  //#endregion
622
622
  //#region src/rules/media-uploader-authorization-required.ts
623
623
  const MESSAGE$3 = "setMediaUploader() should be protected with .authorize(); without it the endpoint accepts uploads from anyone who can reach the URL. Add .authorize() unless a public upload endpoint is intended.";
624
- function getParent(node) {
624
+ function getParent$1(node) {
625
625
  if (node == null || typeof node !== "object") return null;
626
626
  const parent = node.parent;
627
627
  return isNode(parent) ? parent : null;
@@ -631,11 +631,11 @@ function getParent(node) {
631
631
  * `.authorize(...)`, i.e. `federation.setMediaUploader(...).authorize(...)`.
632
632
  */
633
633
  function isDirectlyAuthorized(callNode) {
634
- const member = getParent(callNode);
634
+ const member = getParent$1(callNode);
635
635
  if (member == null || member.type !== "MemberExpression" || member.computed === true) return false;
636
636
  const property = member.property;
637
637
  if (!isNode(property) || property.type !== "Identifier" || property.name !== "authorize") return false;
638
- const call = getParent(member);
638
+ const call = getParent$1(member);
639
639
  return call != null && call.type === "CallExpression" && call.callee === member;
640
640
  }
641
641
  function createRule$3(buildReport) {
@@ -653,7 +653,7 @@ function createRule$3(buildReport) {
653
653
  }
654
654
  if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || !federationTracker.isFederationObject(node.callee.object)) return;
655
655
  if (isDirectlyAuthorized(node)) return;
656
- const parent = getParent(node);
656
+ const parent = getParent$1(node);
657
657
  let varName = null;
658
658
  if (parent != null && parent.type === "VariableDeclarator") {
659
659
  const id = parent.id;
@@ -692,7 +692,7 @@ const eslint$3 = {
692
692
  //#region src/rules/media-uploader-object-uri-required.ts
693
693
  const MESSAGE$2 = "setMediaUploader() callbacks should return a value derived from ctx.getObjectUri(): either return ctx.getObjectUri(...) directly, or an object whose id is ctx.getObjectUri(...).";
694
694
  const GETTER_NAME = "getObjectUri";
695
- const getMemberPropertyName$1 = (node) => {
695
+ const getMemberPropertyName$2 = (node) => {
696
696
  if (node.type !== "MemberExpression") return null;
697
697
  const property = node.property;
698
698
  if (property.type === "Identifier") return property.name;
@@ -722,7 +722,7 @@ const resolveCallbackReference = (expr, bindings, seen = /* @__PURE__ */ new Set
722
722
  if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
723
723
  const binding = bindings.get(expr.object.name);
724
724
  if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
725
- const propertyName = getMemberPropertyName$1(expr);
725
+ const propertyName = getMemberPropertyName$2(expr);
726
726
  if (propertyName == null) return null;
727
727
  for (const prop of binding.properties) {
728
728
  if (!isNode(prop) || prop.type !== "Property") continue;
@@ -815,7 +815,7 @@ function toNode(value) {
815
815
  function isPromiseResolveCall(expr) {
816
816
  if (expr.type !== "CallExpression") return false;
817
817
  const callee = toNode(expr.callee);
818
- if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$1(callee) !== "resolve") return false;
818
+ if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== "resolve") return false;
819
819
  const object = toNode(callee.object);
820
820
  return object != null && object.type === "Identifier" && object.name === "Promise";
821
821
  }
@@ -846,7 +846,7 @@ function isGetObjectUriCall(expr, ctx) {
846
846
  if (expr.type !== "CallExpression") return false;
847
847
  if (ctx.ctxName == null) return false;
848
848
  const callee = toNode(expr.callee);
849
- if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$1(callee) !== GETTER_NAME) return false;
849
+ if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== GETTER_NAME) return false;
850
850
  const object = unwrapExpression(callee.object);
851
851
  return object != null && object.type === "Identifier" && object.name === ctx.ctxName;
852
852
  }
@@ -1012,23 +1012,23 @@ const eslint$2 = {
1012
1012
  };
1013
1013
  //#endregion
1014
1014
  //#region src/lib/outbox-listener.ts
1015
- const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
1016
- const isChainedFromOutboxListeners = (expr, federationTracker) => {
1015
+ const DELIVERY_METHOD_NAMES$1 = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
1016
+ const isChainedFromOutboxListeners$1 = (expr, federationTracker) => {
1017
1017
  if (expr.type !== "CallExpression") return false;
1018
1018
  if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
1019
1019
  const methodName = expr.callee.property.name;
1020
1020
  if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
1021
- if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
1021
+ if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners$1(expr.callee.object, federationTracker);
1022
1022
  return false;
1023
1023
  };
1024
- const getMemberPropertyName = (expr) => {
1024
+ const getMemberPropertyName$1 = (expr) => {
1025
1025
  if (expr.type !== "MemberExpression") return null;
1026
1026
  const property = expr.property;
1027
1027
  if (property.type === "Identifier") return property.name;
1028
1028
  if (property.type === "Literal" && typeof property.value === "string") return property.value;
1029
1029
  return null;
1030
1030
  };
1031
- function unwrapContextParam(node) {
1031
+ function unwrapContextParam$1(node) {
1032
1032
  let current = node ?? null;
1033
1033
  while (current?.type === "AssignmentPattern") current = current.left;
1034
1034
  return current;
@@ -1054,7 +1054,7 @@ const resolveFunctionBinding = (expr, bindings, seen = /* @__PURE__ */ new Set()
1054
1054
  if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
1055
1055
  const binding = bindings.get(expr.object.name);
1056
1056
  if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
1057
- const propertyName = getMemberPropertyName(expr);
1057
+ const propertyName = getMemberPropertyName$1(expr);
1058
1058
  if (propertyName == null) return null;
1059
1059
  for (const prop of binding.properties) {
1060
1060
  if (!isNode(prop) || prop.type !== "Property") continue;
@@ -1078,7 +1078,7 @@ function createOutboxListenerVisitor(onListener) {
1078
1078
  const pendingCalls = [];
1079
1079
  const inspectCall = (node) => {
1080
1080
  if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
1081
- if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
1081
+ if (!isChainedFromOutboxListeners$1(node.callee.object, federationTracker)) return;
1082
1082
  const listener = node.arguments[1];
1083
1083
  const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveFunctionBinding(listener, bindings) : null;
1084
1084
  if (resolvedListener == null) return;
@@ -1737,7 +1737,7 @@ function deliveryAliasesOf(pattern, out) {
1737
1737
  if (prop.type !== "Property") continue;
1738
1738
  const key = asNode(get(prop, "key"));
1739
1739
  const keyName = key?.type === "Identifier" ? key.name : key?.type === "Literal" && typeof key.value === "string" ? key.value : null;
1740
- if (keyName == null || !DELIVERY_METHOD_NAMES.has(keyName)) continue;
1740
+ if (keyName == null || !DELIVERY_METHOD_NAMES$1.has(keyName)) continue;
1741
1741
  const value = asNode(get(prop, "value"));
1742
1742
  if (value?.type === "Identifier") out.add(value.name);
1743
1743
  else if (value?.type === "AssignmentPattern") {
@@ -1755,7 +1755,7 @@ function deliveryAliasesOf(pattern, out) {
1755
1755
  */
1756
1756
  function findDeliveryTargets(listener, scopes) {
1757
1757
  const aliases = /* @__PURE__ */ new Set();
1758
- const contextParam = unwrapContextParam(listener.params[0]);
1758
+ const contextParam = unwrapContextParam$1(listener.params[0]);
1759
1759
  const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
1760
1760
  if (contextParam != null) deliveryAliasesOf(contextParam, aliases);
1761
1761
  if (contextName != null) for (const scope of scopes) for (const statement of scope.statements) visitAll(statement, (node) => {
@@ -1771,7 +1771,7 @@ function findDeliveryTargets(listener, scopes) {
1771
1771
  if (id.type !== "Identifier" || source.type !== "MemberExpression") return;
1772
1772
  const object = unwrap(asNode(get(source, "object")) ?? source);
1773
1773
  const name = memberName(source);
1774
- if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES.has(name)) aliases.add(id.name);
1774
+ if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES$1.has(name)) aliases.add(id.name);
1775
1775
  }, false);
1776
1776
  return {
1777
1777
  contextName,
@@ -1929,7 +1929,7 @@ function checkListener(listener, report) {
1929
1929
  const callee = unwrap(call.callee);
1930
1930
  if (callee.type === "MemberExpression") {
1931
1931
  const name = memberName(callee);
1932
- if (name == null || !DELIVERY_METHOD_NAMES.has(name)) return false;
1932
+ if (name == null || !DELIVERY_METHOD_NAMES$1.has(name)) return false;
1933
1933
  const object = unwrap(asNode(get(callee, "object")) ?? callee);
1934
1934
  return object.type === "Identifier" && object.name === targets.contextName;
1935
1935
  }
@@ -2012,6 +2012,122 @@ const eslint$1 = {
2012
2012
  //#endregion
2013
2013
  //#region src/rules/outbox-listener-delivery-required.ts
2014
2014
  const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
2015
+ const isChainedFromOutboxListeners = (expr, federationTracker) => {
2016
+ if (expr.type !== "CallExpression") return false;
2017
+ if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
2018
+ const methodName = expr.callee.property.name;
2019
+ if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
2020
+ if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
2021
+ return false;
2022
+ };
2023
+ const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
2024
+ const getMemberPropertyName = (expr) => {
2025
+ if (expr.type !== "MemberExpression") return null;
2026
+ const property = expr.property;
2027
+ if (property.type === "Identifier" && !expr.computed) return property.name;
2028
+ if (property.type === "Literal" && typeof property.value === "string") return property.value;
2029
+ return null;
2030
+ };
2031
+ function unwrapContextParam(node) {
2032
+ let current = node ?? null;
2033
+ while (current?.type === "AssignmentPattern") current = current.left;
2034
+ return current;
2035
+ }
2036
+ const getParent = (node) => node.parent ?? null;
2037
+ const isFunctionLike = (node) => node.type === "FunctionDeclaration" || isFunction(node);
2038
+ function bindPattern(node, bindings) {
2039
+ switch (node.type) {
2040
+ case "Identifier":
2041
+ bindings.set(node.name, { value: null });
2042
+ break;
2043
+ case "AssignmentPattern":
2044
+ bindPattern(node.left, bindings);
2045
+ break;
2046
+ case "RestElement":
2047
+ bindPattern(node.argument, bindings);
2048
+ break;
2049
+ case "ObjectPattern":
2050
+ for (const property of node.properties) bindPattern(property.type === "Property" ? property.value : property, bindings);
2051
+ break;
2052
+ case "ArrayPattern":
2053
+ for (const element of node.elements) if (element != null) bindPattern(element, bindings);
2054
+ break;
2055
+ }
2056
+ }
2057
+ function createBindingIndex() {
2058
+ const scopes = /* @__PURE__ */ new Map();
2059
+ const scopeBindings = (scope) => {
2060
+ let bindings = scopes.get(scope);
2061
+ if (bindings == null) {
2062
+ bindings = /* @__PURE__ */ new Map();
2063
+ scopes.set(scope, bindings);
2064
+ if (isFunctionLike(scope)) {
2065
+ if (scope.type === "FunctionExpression" && scope.id != null) bindings.set(scope.id.name, { value: scope });
2066
+ for (const param of scope.params) bindPattern(param, bindings);
2067
+ }
2068
+ if (scope.type === "ClassExpression" && scope.id != null) bindings.set(scope.id.name, { value: null });
2069
+ if (scope.type === "CatchClause" && scope.param != null) bindPattern(scope.param, bindings);
2070
+ }
2071
+ return bindings;
2072
+ };
2073
+ const enclosingScope = (node, functionScope = false) => {
2074
+ let scope = getParent(node);
2075
+ while (scope.type !== "Program" && scope.type !== "StaticBlock" && scope.type !== "TSModuleBlock" && !isFunctionLike(scope) && (functionScope || ![
2076
+ "BlockStatement",
2077
+ "ForStatement",
2078
+ "ForInStatement",
2079
+ "ForOfStatement",
2080
+ "SwitchStatement",
2081
+ "CatchClause"
2082
+ ].includes(scope.type))) scope = getParent(scope);
2083
+ return scope;
2084
+ };
2085
+ const lookup = (node, name) => {
2086
+ for (let scope = node; scope != null; scope = getParent(scope)) {
2087
+ const binding = scopeBindings(scope).get(name);
2088
+ if (binding != null) return binding;
2089
+ }
2090
+ return null;
2091
+ };
2092
+ return {
2093
+ lookup,
2094
+ declare(node, pattern, value, hoisted = false) {
2095
+ const bindings = scopeBindings(enclosingScope(node, hoisted));
2096
+ if (pattern.type === "Identifier") {
2097
+ if (hoisted && value == null && bindings.has(pattern.name)) return;
2098
+ if (node.type === "FunctionDeclaration" && bindings.get(pattern.name)?.initializedVariable) return;
2099
+ bindings.set(pattern.name, {
2100
+ value,
2101
+ initializedVariable: node.type === "VariableDeclarator" && value != null
2102
+ });
2103
+ } else bindPattern(pattern, bindings);
2104
+ }
2105
+ };
2106
+ }
2107
+ const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
2108
+ if (expr.type !== "Identifier") return expr;
2109
+ const binding = bindings.lookup(expr, expr.name);
2110
+ if (binding == null || binding.value == null || seen.has(binding)) return null;
2111
+ seen.add(binding);
2112
+ return resolveBindingValue(binding.value, bindings, seen);
2113
+ };
2114
+ const resolveListenerReference = (expr, bindings) => {
2115
+ const target = resolveBindingValue(expr, bindings);
2116
+ if (target == null) return null;
2117
+ if (isFunctionLike(target)) return target;
2118
+ if (target.type !== "MemberExpression") return null;
2119
+ const object = resolveBindingValue(target.object, bindings);
2120
+ if (object?.type !== "ObjectExpression") return null;
2121
+ const propertyName = getMemberPropertyName(target);
2122
+ if (propertyName == null) return null;
2123
+ for (const prop of object.properties) {
2124
+ if (prop.type !== "Property") continue;
2125
+ if ((prop.key.type === "Identifier" && !prop.computed ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName) continue;
2126
+ const value = resolveBindingValue(prop.value, bindings);
2127
+ if (value != null && isFunctionLike(value)) return value;
2128
+ }
2129
+ return null;
2130
+ };
2015
2131
  function escapeRegExp(value) {
2016
2132
  return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
2017
2133
  }
@@ -2177,11 +2293,52 @@ function collectDeliveryScanCode(sourceCode, root, used, visited) {
2177
2293
  return result;
2178
2294
  }).join("\n");
2179
2295
  }
2180
- const listenerCallsDeliveryMethod = (sourceCode, listener) => {
2296
+ function unwrapArgument(node) {
2297
+ while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
2298
+ return node;
2299
+ }
2300
+ function functionCallsDelivery(sourceCode, listener, bindings, calls, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
2301
+ let indices = visited.get(listener);
2302
+ if (indices?.has(contextIndex)) return false;
2303
+ if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
2304
+ indices.add(contextIndex);
2305
+ if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex)) return true;
2306
+ const param = unwrapContextParam(listener.params[contextIndex]);
2307
+ if (param?.type !== "Identifier") return false;
2308
+ const contextBinding = bindings.lookup(listener, param.name);
2309
+ const reachable = [];
2310
+ collectReachableStatements(listener.body, reachable);
2311
+ for (const call of calls) {
2312
+ let child = call;
2313
+ let owner = getParent(child);
2314
+ while (owner != null && !isFunctionLike(owner)) {
2315
+ const field = owner;
2316
+ if ((field.type === "PropertyDefinition" || field.type === "AccessorProperty") && !field.static && field.value === child) break;
2317
+ child = owner;
2318
+ owner = getParent(owner);
2319
+ }
2320
+ if (owner !== listener) continue;
2321
+ const [callStart, callEnd] = getRange(call);
2322
+ if (!reachable.some((statement) => {
2323
+ const [start, end] = getRange(statement);
2324
+ return start <= callStart && callEnd <= end;
2325
+ })) continue;
2326
+ const helper = resolveListenerReference(call.callee, bindings);
2327
+ if (helper == null || "generator" in helper && helper.generator) continue;
2328
+ for (const [index, argument] of call.arguments.entries()) {
2329
+ if (argument.type === "SpreadElement") break;
2330
+ const arg = unwrapArgument(argument);
2331
+ if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
2332
+ if (functionCallsDelivery(sourceCode, helper, bindings, calls, index, visited)) return true;
2333
+ }
2334
+ }
2335
+ return false;
2336
+ }
2337
+ const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0) => {
2181
2338
  const used = computeUsedFunctions(listener.body);
2182
2339
  const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, /* @__PURE__ */ new Set()));
2183
2340
  const aliases = /* @__PURE__ */ new Set();
2184
- const contextParam = unwrapContextParam(listener.params[0]);
2341
+ const contextParam = unwrapContextParam(listener.params[contextIndex]);
2185
2342
  const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
2186
2343
  if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
2187
2344
  if (!isNode(prop) || prop.type !== "Property") continue;
@@ -2209,14 +2366,48 @@ const listenerCallsDeliveryMethod = (sourceCode, listener) => {
2209
2366
  };
2210
2367
  function createRule(buildReport) {
2211
2368
  return (context) => {
2369
+ const federationTracker = trackFederationVariables();
2370
+ const bindings = createBindingIndex();
2371
+ const pendingCalls = [];
2212
2372
  const sourceCode = context.sourceCode;
2213
- return createOutboxListenerVisitor((listener) => {
2214
- if (listenerCallsDeliveryMethod(sourceCode, listener)) return;
2373
+ const inspectCall = (node) => {
2374
+ if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
2375
+ if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
2376
+ const listener = node.arguments[1];
2377
+ const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
2378
+ if (resolvedListener == null) return;
2379
+ if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls)) return;
2215
2380
  context.report({
2216
- node: listener,
2381
+ node: resolvedListener,
2217
2382
  ...buildReport
2218
2383
  });
2219
- });
2384
+ };
2385
+ return {
2386
+ VariableDeclarator(node) {
2387
+ federationTracker.VariableDeclarator(node);
2388
+ const declaration = getParent(node);
2389
+ bindings.declare(node, node.id, node.init, declaration?.kind === "var");
2390
+ },
2391
+ FunctionDeclaration(node) {
2392
+ if (node.id == null) return;
2393
+ const parent = getParent(node);
2394
+ const owner = parent == null ? null : getParent(parent);
2395
+ const inVarBody = parent?.type === "BlockStatement" && owner != null && (owner.type === "StaticBlock" || isFunctionLike(owner) && owner.body === parent);
2396
+ bindings.declare(node, node.id, node, inVarBody);
2397
+ },
2398
+ ImportDeclaration(node) {
2399
+ for (const specifier of node.specifiers) bindings.declare(node, specifier.local, null);
2400
+ },
2401
+ ClassDeclaration(node) {
2402
+ if (node.id != null) bindings.declare(node, node.id, null);
2403
+ },
2404
+ CallExpression(node) {
2405
+ pendingCalls.push(node);
2406
+ },
2407
+ "Program:exit"() {
2408
+ for (const node of pendingCalls) inspectCall(node);
2409
+ }
2410
+ };
2220
2411
  };
2221
2412
  }
2222
2413
  const eslint = {
@@ -1,7 +1,7 @@
1
1
  let _fxts_core = require("@fxts/core");
2
2
  //#region deno.json
3
3
  var name = "@fedify/lint";
4
- var version = "2.4.0-dev.2155+2ccc5085";
4
+ var version = "2.4.0-dev.2159+bb072eda";
5
5
  //#endregion
6
6
  //#region src/lib/const.ts
7
7
  /**
@@ -621,7 +621,7 @@ const eslint$4 = {
621
621
  //#endregion
622
622
  //#region src/rules/media-uploader-authorization-required.ts
623
623
  const MESSAGE$3 = "setMediaUploader() should be protected with .authorize(); without it the endpoint accepts uploads from anyone who can reach the URL. Add .authorize() unless a public upload endpoint is intended.";
624
- function getParent(node) {
624
+ function getParent$1(node) {
625
625
  if (node == null || typeof node !== "object") return null;
626
626
  const parent = node.parent;
627
627
  return isNode(parent) ? parent : null;
@@ -631,11 +631,11 @@ function getParent(node) {
631
631
  * `.authorize(...)`, i.e. `federation.setMediaUploader(...).authorize(...)`.
632
632
  */
633
633
  function isDirectlyAuthorized(callNode) {
634
- const member = getParent(callNode);
634
+ const member = getParent$1(callNode);
635
635
  if (member == null || member.type !== "MemberExpression" || member.computed === true) return false;
636
636
  const property = member.property;
637
637
  if (!isNode(property) || property.type !== "Identifier" || property.name !== "authorize") return false;
638
- const call = getParent(member);
638
+ const call = getParent$1(member);
639
639
  return call != null && call.type === "CallExpression" && call.callee === member;
640
640
  }
641
641
  function createRule$3(buildReport) {
@@ -653,7 +653,7 @@ function createRule$3(buildReport) {
653
653
  }
654
654
  if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || !federationTracker.isFederationObject(node.callee.object)) return;
655
655
  if (isDirectlyAuthorized(node)) return;
656
- const parent = getParent(node);
656
+ const parent = getParent$1(node);
657
657
  let varName = null;
658
658
  if (parent != null && parent.type === "VariableDeclarator") {
659
659
  const id = parent.id;
@@ -692,7 +692,7 @@ const eslint$3 = {
692
692
  //#region src/rules/media-uploader-object-uri-required.ts
693
693
  const MESSAGE$2 = "setMediaUploader() callbacks should return a value derived from ctx.getObjectUri(): either return ctx.getObjectUri(...) directly, or an object whose id is ctx.getObjectUri(...).";
694
694
  const GETTER_NAME = "getObjectUri";
695
- const getMemberPropertyName$1 = (node) => {
695
+ const getMemberPropertyName$2 = (node) => {
696
696
  if (node.type !== "MemberExpression") return null;
697
697
  const property = node.property;
698
698
  if (property.type === "Identifier") return property.name;
@@ -722,7 +722,7 @@ const resolveCallbackReference = (expr, bindings, seen = /* @__PURE__ */ new Set
722
722
  if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
723
723
  const binding = bindings.get(expr.object.name);
724
724
  if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
725
- const propertyName = getMemberPropertyName$1(expr);
725
+ const propertyName = getMemberPropertyName$2(expr);
726
726
  if (propertyName == null) return null;
727
727
  for (const prop of binding.properties) {
728
728
  if (!isNode(prop) || prop.type !== "Property") continue;
@@ -815,7 +815,7 @@ function toNode(value) {
815
815
  function isPromiseResolveCall(expr) {
816
816
  if (expr.type !== "CallExpression") return false;
817
817
  const callee = toNode(expr.callee);
818
- if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$1(callee) !== "resolve") return false;
818
+ if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== "resolve") return false;
819
819
  const object = toNode(callee.object);
820
820
  return object != null && object.type === "Identifier" && object.name === "Promise";
821
821
  }
@@ -846,7 +846,7 @@ function isGetObjectUriCall(expr, ctx) {
846
846
  if (expr.type !== "CallExpression") return false;
847
847
  if (ctx.ctxName == null) return false;
848
848
  const callee = toNode(expr.callee);
849
- if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$1(callee) !== GETTER_NAME) return false;
849
+ if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== GETTER_NAME) return false;
850
850
  const object = unwrapExpression(callee.object);
851
851
  return object != null && object.type === "Identifier" && object.name === ctx.ctxName;
852
852
  }
@@ -1012,23 +1012,23 @@ const eslint$2 = {
1012
1012
  };
1013
1013
  //#endregion
1014
1014
  //#region src/lib/outbox-listener.ts
1015
- const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
1016
- const isChainedFromOutboxListeners = (expr, federationTracker) => {
1015
+ const DELIVERY_METHOD_NAMES$1 = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
1016
+ const isChainedFromOutboxListeners$1 = (expr, federationTracker) => {
1017
1017
  if (expr.type !== "CallExpression") return false;
1018
1018
  if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
1019
1019
  const methodName = expr.callee.property.name;
1020
1020
  if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
1021
- if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
1021
+ if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners$1(expr.callee.object, federationTracker);
1022
1022
  return false;
1023
1023
  };
1024
- const getMemberPropertyName = (expr) => {
1024
+ const getMemberPropertyName$1 = (expr) => {
1025
1025
  if (expr.type !== "MemberExpression") return null;
1026
1026
  const property = expr.property;
1027
1027
  if (property.type === "Identifier") return property.name;
1028
1028
  if (property.type === "Literal" && typeof property.value === "string") return property.value;
1029
1029
  return null;
1030
1030
  };
1031
- function unwrapContextParam(node) {
1031
+ function unwrapContextParam$1(node) {
1032
1032
  let current = node ?? null;
1033
1033
  while (current?.type === "AssignmentPattern") current = current.left;
1034
1034
  return current;
@@ -1054,7 +1054,7 @@ const resolveFunctionBinding = (expr, bindings, seen = /* @__PURE__ */ new Set()
1054
1054
  if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
1055
1055
  const binding = bindings.get(expr.object.name);
1056
1056
  if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
1057
- const propertyName = getMemberPropertyName(expr);
1057
+ const propertyName = getMemberPropertyName$1(expr);
1058
1058
  if (propertyName == null) return null;
1059
1059
  for (const prop of binding.properties) {
1060
1060
  if (!isNode(prop) || prop.type !== "Property") continue;
@@ -1078,7 +1078,7 @@ function createOutboxListenerVisitor(onListener) {
1078
1078
  const pendingCalls = [];
1079
1079
  const inspectCall = (node) => {
1080
1080
  if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
1081
- if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
1081
+ if (!isChainedFromOutboxListeners$1(node.callee.object, federationTracker)) return;
1082
1082
  const listener = node.arguments[1];
1083
1083
  const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveFunctionBinding(listener, bindings) : null;
1084
1084
  if (resolvedListener == null) return;
@@ -1737,7 +1737,7 @@ function deliveryAliasesOf(pattern, out) {
1737
1737
  if (prop.type !== "Property") continue;
1738
1738
  const key = asNode(get(prop, "key"));
1739
1739
  const keyName = key?.type === "Identifier" ? key.name : key?.type === "Literal" && typeof key.value === "string" ? key.value : null;
1740
- if (keyName == null || !DELIVERY_METHOD_NAMES.has(keyName)) continue;
1740
+ if (keyName == null || !DELIVERY_METHOD_NAMES$1.has(keyName)) continue;
1741
1741
  const value = asNode(get(prop, "value"));
1742
1742
  if (value?.type === "Identifier") out.add(value.name);
1743
1743
  else if (value?.type === "AssignmentPattern") {
@@ -1755,7 +1755,7 @@ function deliveryAliasesOf(pattern, out) {
1755
1755
  */
1756
1756
  function findDeliveryTargets(listener, scopes) {
1757
1757
  const aliases = /* @__PURE__ */ new Set();
1758
- const contextParam = unwrapContextParam(listener.params[0]);
1758
+ const contextParam = unwrapContextParam$1(listener.params[0]);
1759
1759
  const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
1760
1760
  if (contextParam != null) deliveryAliasesOf(contextParam, aliases);
1761
1761
  if (contextName != null) for (const scope of scopes) for (const statement of scope.statements) visitAll(statement, (node) => {
@@ -1771,7 +1771,7 @@ function findDeliveryTargets(listener, scopes) {
1771
1771
  if (id.type !== "Identifier" || source.type !== "MemberExpression") return;
1772
1772
  const object = unwrap(asNode(get(source, "object")) ?? source);
1773
1773
  const name = memberName(source);
1774
- if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES.has(name)) aliases.add(id.name);
1774
+ if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES$1.has(name)) aliases.add(id.name);
1775
1775
  }, false);
1776
1776
  return {
1777
1777
  contextName,
@@ -1929,7 +1929,7 @@ function checkListener(listener, report) {
1929
1929
  const callee = unwrap(call.callee);
1930
1930
  if (callee.type === "MemberExpression") {
1931
1931
  const name = memberName(callee);
1932
- if (name == null || !DELIVERY_METHOD_NAMES.has(name)) return false;
1932
+ if (name == null || !DELIVERY_METHOD_NAMES$1.has(name)) return false;
1933
1933
  const object = unwrap(asNode(get(callee, "object")) ?? callee);
1934
1934
  return object.type === "Identifier" && object.name === targets.contextName;
1935
1935
  }
@@ -2012,6 +2012,122 @@ const eslint$1 = {
2012
2012
  //#endregion
2013
2013
  //#region src/rules/outbox-listener-delivery-required.ts
2014
2014
  const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
2015
+ const isChainedFromOutboxListeners = (expr, federationTracker) => {
2016
+ if (expr.type !== "CallExpression") return false;
2017
+ if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
2018
+ const methodName = expr.callee.property.name;
2019
+ if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
2020
+ if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
2021
+ return false;
2022
+ };
2023
+ const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
2024
+ const getMemberPropertyName = (expr) => {
2025
+ if (expr.type !== "MemberExpression") return null;
2026
+ const property = expr.property;
2027
+ if (property.type === "Identifier" && !expr.computed) return property.name;
2028
+ if (property.type === "Literal" && typeof property.value === "string") return property.value;
2029
+ return null;
2030
+ };
2031
+ function unwrapContextParam(node) {
2032
+ let current = node ?? null;
2033
+ while (current?.type === "AssignmentPattern") current = current.left;
2034
+ return current;
2035
+ }
2036
+ const getParent = (node) => node.parent ?? null;
2037
+ const isFunctionLike = (node) => node.type === "FunctionDeclaration" || isFunction(node);
2038
+ function bindPattern(node, bindings) {
2039
+ switch (node.type) {
2040
+ case "Identifier":
2041
+ bindings.set(node.name, { value: null });
2042
+ break;
2043
+ case "AssignmentPattern":
2044
+ bindPattern(node.left, bindings);
2045
+ break;
2046
+ case "RestElement":
2047
+ bindPattern(node.argument, bindings);
2048
+ break;
2049
+ case "ObjectPattern":
2050
+ for (const property of node.properties) bindPattern(property.type === "Property" ? property.value : property, bindings);
2051
+ break;
2052
+ case "ArrayPattern":
2053
+ for (const element of node.elements) if (element != null) bindPattern(element, bindings);
2054
+ break;
2055
+ }
2056
+ }
2057
+ function createBindingIndex() {
2058
+ const scopes = /* @__PURE__ */ new Map();
2059
+ const scopeBindings = (scope) => {
2060
+ let bindings = scopes.get(scope);
2061
+ if (bindings == null) {
2062
+ bindings = /* @__PURE__ */ new Map();
2063
+ scopes.set(scope, bindings);
2064
+ if (isFunctionLike(scope)) {
2065
+ if (scope.type === "FunctionExpression" && scope.id != null) bindings.set(scope.id.name, { value: scope });
2066
+ for (const param of scope.params) bindPattern(param, bindings);
2067
+ }
2068
+ if (scope.type === "ClassExpression" && scope.id != null) bindings.set(scope.id.name, { value: null });
2069
+ if (scope.type === "CatchClause" && scope.param != null) bindPattern(scope.param, bindings);
2070
+ }
2071
+ return bindings;
2072
+ };
2073
+ const enclosingScope = (node, functionScope = false) => {
2074
+ let scope = getParent(node);
2075
+ while (scope.type !== "Program" && scope.type !== "StaticBlock" && scope.type !== "TSModuleBlock" && !isFunctionLike(scope) && (functionScope || ![
2076
+ "BlockStatement",
2077
+ "ForStatement",
2078
+ "ForInStatement",
2079
+ "ForOfStatement",
2080
+ "SwitchStatement",
2081
+ "CatchClause"
2082
+ ].includes(scope.type))) scope = getParent(scope);
2083
+ return scope;
2084
+ };
2085
+ const lookup = (node, name) => {
2086
+ for (let scope = node; scope != null; scope = getParent(scope)) {
2087
+ const binding = scopeBindings(scope).get(name);
2088
+ if (binding != null) return binding;
2089
+ }
2090
+ return null;
2091
+ };
2092
+ return {
2093
+ lookup,
2094
+ declare(node, pattern, value, hoisted = false) {
2095
+ const bindings = scopeBindings(enclosingScope(node, hoisted));
2096
+ if (pattern.type === "Identifier") {
2097
+ if (hoisted && value == null && bindings.has(pattern.name)) return;
2098
+ if (node.type === "FunctionDeclaration" && bindings.get(pattern.name)?.initializedVariable) return;
2099
+ bindings.set(pattern.name, {
2100
+ value,
2101
+ initializedVariable: node.type === "VariableDeclarator" && value != null
2102
+ });
2103
+ } else bindPattern(pattern, bindings);
2104
+ }
2105
+ };
2106
+ }
2107
+ const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
2108
+ if (expr.type !== "Identifier") return expr;
2109
+ const binding = bindings.lookup(expr, expr.name);
2110
+ if (binding == null || binding.value == null || seen.has(binding)) return null;
2111
+ seen.add(binding);
2112
+ return resolveBindingValue(binding.value, bindings, seen);
2113
+ };
2114
+ const resolveListenerReference = (expr, bindings) => {
2115
+ const target = resolveBindingValue(expr, bindings);
2116
+ if (target == null) return null;
2117
+ if (isFunctionLike(target)) return target;
2118
+ if (target.type !== "MemberExpression") return null;
2119
+ const object = resolveBindingValue(target.object, bindings);
2120
+ if (object?.type !== "ObjectExpression") return null;
2121
+ const propertyName = getMemberPropertyName(target);
2122
+ if (propertyName == null) return null;
2123
+ for (const prop of object.properties) {
2124
+ if (prop.type !== "Property") continue;
2125
+ if ((prop.key.type === "Identifier" && !prop.computed ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName) continue;
2126
+ const value = resolveBindingValue(prop.value, bindings);
2127
+ if (value != null && isFunctionLike(value)) return value;
2128
+ }
2129
+ return null;
2130
+ };
2015
2131
  function escapeRegExp(value) {
2016
2132
  return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
2017
2133
  }
@@ -2177,11 +2293,52 @@ function collectDeliveryScanCode(sourceCode, root, used, visited) {
2177
2293
  return result;
2178
2294
  }).join("\n");
2179
2295
  }
2180
- const listenerCallsDeliveryMethod = (sourceCode, listener) => {
2296
+ function unwrapArgument(node) {
2297
+ while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
2298
+ return node;
2299
+ }
2300
+ function functionCallsDelivery(sourceCode, listener, bindings, calls, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
2301
+ let indices = visited.get(listener);
2302
+ if (indices?.has(contextIndex)) return false;
2303
+ if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
2304
+ indices.add(contextIndex);
2305
+ if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex)) return true;
2306
+ const param = unwrapContextParam(listener.params[contextIndex]);
2307
+ if (param?.type !== "Identifier") return false;
2308
+ const contextBinding = bindings.lookup(listener, param.name);
2309
+ const reachable = [];
2310
+ collectReachableStatements(listener.body, reachable);
2311
+ for (const call of calls) {
2312
+ let child = call;
2313
+ let owner = getParent(child);
2314
+ while (owner != null && !isFunctionLike(owner)) {
2315
+ const field = owner;
2316
+ if ((field.type === "PropertyDefinition" || field.type === "AccessorProperty") && !field.static && field.value === child) break;
2317
+ child = owner;
2318
+ owner = getParent(owner);
2319
+ }
2320
+ if (owner !== listener) continue;
2321
+ const [callStart, callEnd] = getRange(call);
2322
+ if (!reachable.some((statement) => {
2323
+ const [start, end] = getRange(statement);
2324
+ return start <= callStart && callEnd <= end;
2325
+ })) continue;
2326
+ const helper = resolveListenerReference(call.callee, bindings);
2327
+ if (helper == null || "generator" in helper && helper.generator) continue;
2328
+ for (const [index, argument] of call.arguments.entries()) {
2329
+ if (argument.type === "SpreadElement") break;
2330
+ const arg = unwrapArgument(argument);
2331
+ if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
2332
+ if (functionCallsDelivery(sourceCode, helper, bindings, calls, index, visited)) return true;
2333
+ }
2334
+ }
2335
+ return false;
2336
+ }
2337
+ const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0) => {
2181
2338
  const used = computeUsedFunctions(listener.body);
2182
2339
  const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, /* @__PURE__ */ new Set()));
2183
2340
  const aliases = /* @__PURE__ */ new Set();
2184
- const contextParam = unwrapContextParam(listener.params[0]);
2341
+ const contextParam = unwrapContextParam(listener.params[contextIndex]);
2185
2342
  const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
2186
2343
  if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
2187
2344
  if (!isNode(prop) || prop.type !== "Property") continue;
@@ -2209,14 +2366,48 @@ const listenerCallsDeliveryMethod = (sourceCode, listener) => {
2209
2366
  };
2210
2367
  function createRule(buildReport) {
2211
2368
  return (context) => {
2369
+ const federationTracker = trackFederationVariables();
2370
+ const bindings = createBindingIndex();
2371
+ const pendingCalls = [];
2212
2372
  const sourceCode = context.sourceCode;
2213
- return createOutboxListenerVisitor((listener) => {
2214
- if (listenerCallsDeliveryMethod(sourceCode, listener)) return;
2373
+ const inspectCall = (node) => {
2374
+ if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
2375
+ if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
2376
+ const listener = node.arguments[1];
2377
+ const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
2378
+ if (resolvedListener == null) return;
2379
+ if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls)) return;
2215
2380
  context.report({
2216
- node: listener,
2381
+ node: resolvedListener,
2217
2382
  ...buildReport
2218
2383
  });
2219
- });
2384
+ };
2385
+ return {
2386
+ VariableDeclarator(node) {
2387
+ federationTracker.VariableDeclarator(node);
2388
+ const declaration = getParent(node);
2389
+ bindings.declare(node, node.id, node.init, declaration?.kind === "var");
2390
+ },
2391
+ FunctionDeclaration(node) {
2392
+ if (node.id == null) return;
2393
+ const parent = getParent(node);
2394
+ const owner = parent == null ? null : getParent(parent);
2395
+ const inVarBody = parent?.type === "BlockStatement" && owner != null && (owner.type === "StaticBlock" || isFunctionLike(owner) && owner.body === parent);
2396
+ bindings.declare(node, node.id, node, inVarBody);
2397
+ },
2398
+ ImportDeclaration(node) {
2399
+ for (const specifier of node.specifiers) bindings.declare(node, specifier.local, null);
2400
+ },
2401
+ ClassDeclaration(node) {
2402
+ if (node.id != null) bindings.declare(node, node.id, null);
2403
+ },
2404
+ CallExpression(node) {
2405
+ pendingCalls.push(node);
2406
+ },
2407
+ "Program:exit"() {
2408
+ for (const node of pendingCalls) inspectCall(node);
2409
+ }
2410
+ };
2220
2411
  };
2221
2412
  }
2222
2413
  const eslint = {
package/dist/oxlint.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-rhAvEA9G.cjs");
1
+ const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-Hs9elJlF.cjs");
2
2
  //#region src/oxlint.ts
3
3
  const rules = {
4
4
  [require_outbox_listener_delivery_required.RULE_IDS.actorIdMismatch]: require_outbox_listener_delivery_required.eslint$18,
package/dist/oxlint.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as name, C as eslint$5, D as eslint$11, E as eslint$10, O as eslint$21, S as eslint$4, T as eslint$13, _ as eslint$15, a as eslint$23, b as eslint$2, c as eslint$16, d as eslint$22, f as eslint$6, g as eslint$14, h as eslint$9, i as eslint$27, j as version, k as RULE_IDS, l as eslint$17, m as eslint$8, n as eslint$25, o as eslint$18, p as eslint$7, r as eslint$26, s as eslint$19, t as eslint$24, u as eslint$20, v as eslint$1, w as eslint$12, x as eslint$3, y as eslint } from "./outbox-listener-delivery-required-vNt3zM_a.js";
1
+ import { A as name, C as eslint$5, D as eslint$11, E as eslint$10, O as eslint$21, S as eslint$4, T as eslint$13, _ as eslint$15, a as eslint$23, b as eslint$2, c as eslint$16, d as eslint$22, f as eslint$6, g as eslint$14, h as eslint$9, i as eslint$27, j as version, k as RULE_IDS, l as eslint$17, m as eslint$8, n as eslint$25, o as eslint$18, p as eslint$7, r as eslint$26, s as eslint$19, t as eslint$24, u as eslint$20, v as eslint$1, w as eslint$12, x as eslint$3, y as eslint } from "./outbox-listener-delivery-required-Cz_XWiEU.js";
2
2
  //#region src/oxlint.ts
3
3
  const rules = {
4
4
  [RULE_IDS.actorIdMismatch]: eslint,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/lint",
3
- "version": "2.4.0-dev.2155+2ccc5085",
3
+ "version": "2.4.0-dev.2159+bb072eda",
4
4
  "description": "Fedify linting rules and plugins",
5
5
  "keywords": [
6
6
  "Fedify",
@@ -51,7 +51,7 @@
51
51
  ],
52
52
  "peerDependencies": {
53
53
  "eslint": ">=9.0.0",
54
- "@fedify/fedify": "^2.4.0-dev.2155+2ccc5085"
54
+ "@fedify/fedify": "^2.4.0-dev.2159+bb072eda"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
57
  "eslint": {
@@ -71,7 +71,7 @@
71
71
  "tsdown": "^0.22.0",
72
72
  "typescript": "^6.0.0",
73
73
  "@fedify/fixture": "2.0.0",
74
- "@fedify/vocab": "2.4.0-dev.2155+2ccc5085"
74
+ "@fedify/vocab": "2.4.0-dev.2159+bb072eda"
75
75
  },
76
76
  "scripts": {
77
77
  "build:self": "tsdown",