@fedify/lint 2.2.0-dev.851 → 2.2.0-dev.869
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 +1 -1
- package/dist/index.cjs +300 -44
- package/dist/index.js +300 -44
- package/package.json +2 -2
package/deno.json
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -29,7 +29,7 @@ let _typescript_eslint_parser = require("@typescript-eslint/parser");
|
|
|
29
29
|
_typescript_eslint_parser = __toESM(_typescript_eslint_parser);
|
|
30
30
|
//#region deno.json
|
|
31
31
|
var name = "@fedify/lint";
|
|
32
|
-
var version = "2.2.0-dev.
|
|
32
|
+
var version = "2.2.0-dev.869+97ccad7a";
|
|
33
33
|
//#endregion
|
|
34
34
|
//#region src/lib/const.ts
|
|
35
35
|
/**
|
|
@@ -146,7 +146,8 @@ const RULE_IDS = {
|
|
|
146
146
|
actorFeaturedTagsPropertyMismatch: "actor-featured-tags-property-mismatch",
|
|
147
147
|
actorInboxPropertyMismatch: "actor-inbox-property-mismatch",
|
|
148
148
|
actorSharedInboxPropertyMismatch: "actor-shared-inbox-property-mismatch",
|
|
149
|
-
collectionFilteringNotImplemented: "collection-filtering-not-implemented"
|
|
149
|
+
collectionFilteringNotImplemented: "collection-filtering-not-implemented",
|
|
150
|
+
outboxListenerDeliveryRequired: "outbox-listener-delivery-required"
|
|
150
151
|
};
|
|
151
152
|
//#endregion
|
|
152
153
|
//#region src/lib/messages.ts
|
|
@@ -191,6 +192,7 @@ function allOf(...predicates) {
|
|
|
191
192
|
return (value) => predicates.every((predicate) => predicate(value));
|
|
192
193
|
}
|
|
193
194
|
const anyOf = (...predicates) => (value) => predicates.some((predicate) => predicate(value));
|
|
195
|
+
const isNode = (obj) => (0, _fxts_core.isObject)(obj) && "type" in obj;
|
|
194
196
|
/**
|
|
195
197
|
* Checks if a node is of a specific type.
|
|
196
198
|
*/
|
|
@@ -443,7 +445,7 @@ function createRequiredRuleEslint(config) {
|
|
|
443
445
|
};
|
|
444
446
|
}
|
|
445
447
|
createRequiredRuleDeno(properties.assertionMethod);
|
|
446
|
-
const eslint$
|
|
448
|
+
const eslint$21 = createRequiredRuleEslint(properties.assertionMethod);
|
|
447
449
|
//#endregion
|
|
448
450
|
//#region src/lib/mismatch.ts
|
|
449
451
|
const isIdentifierWithName = (name) => (node) => allOf(isNodeType("Identifier"), isNodeName(name))(node);
|
|
@@ -513,43 +515,43 @@ const createMismatchRuleEslint = (config) => ({
|
|
|
513
515
|
}))
|
|
514
516
|
});
|
|
515
517
|
createMismatchRuleDeno(properties.featured);
|
|
516
|
-
const eslint$
|
|
518
|
+
const eslint$20 = createMismatchRuleEslint(properties.featured);
|
|
517
519
|
createRequiredRuleDeno(properties.featured);
|
|
518
|
-
const eslint$
|
|
520
|
+
const eslint$19 = createRequiredRuleEslint(properties.featured);
|
|
519
521
|
createMismatchRuleDeno(properties.featuredTags);
|
|
520
|
-
const eslint$
|
|
522
|
+
const eslint$18 = createMismatchRuleEslint(properties.featuredTags);
|
|
521
523
|
createRequiredRuleDeno(properties.featuredTags);
|
|
522
|
-
const eslint$
|
|
524
|
+
const eslint$17 = createRequiredRuleEslint(properties.featuredTags);
|
|
523
525
|
createMismatchRuleDeno(properties.followers);
|
|
524
|
-
const eslint$
|
|
526
|
+
const eslint$16 = createMismatchRuleEslint(properties.followers);
|
|
525
527
|
createRequiredRuleDeno(properties.followers);
|
|
526
|
-
const eslint$
|
|
528
|
+
const eslint$15 = createRequiredRuleEslint(properties.followers);
|
|
527
529
|
createMismatchRuleDeno(properties.following);
|
|
528
|
-
const eslint$
|
|
530
|
+
const eslint$14 = createMismatchRuleEslint(properties.following);
|
|
529
531
|
createRequiredRuleDeno(properties.following);
|
|
530
|
-
const eslint$
|
|
532
|
+
const eslint$13 = createRequiredRuleEslint(properties.following);
|
|
531
533
|
createMismatchRuleDeno(properties.id);
|
|
532
|
-
const eslint$
|
|
534
|
+
const eslint$12 = createMismatchRuleEslint(properties.id);
|
|
533
535
|
createRequiredRuleDeno(properties.id);
|
|
534
|
-
const eslint$
|
|
536
|
+
const eslint$11 = createRequiredRuleEslint(properties.id);
|
|
535
537
|
createMismatchRuleDeno(properties.inbox);
|
|
536
|
-
const eslint$
|
|
538
|
+
const eslint$10 = createMismatchRuleEslint(properties.inbox);
|
|
537
539
|
createRequiredRuleDeno(properties.inbox);
|
|
538
|
-
const eslint$
|
|
540
|
+
const eslint$9 = createRequiredRuleEslint(properties.inbox);
|
|
539
541
|
createMismatchRuleDeno(properties.liked);
|
|
540
|
-
const eslint$
|
|
542
|
+
const eslint$8 = createMismatchRuleEslint(properties.liked);
|
|
541
543
|
createRequiredRuleDeno(properties.liked);
|
|
542
|
-
const eslint$
|
|
544
|
+
const eslint$7 = createRequiredRuleEslint(properties.liked);
|
|
543
545
|
createMismatchRuleDeno(properties.outbox);
|
|
544
|
-
const eslint$
|
|
546
|
+
const eslint$6 = createMismatchRuleEslint(properties.outbox);
|
|
545
547
|
createRequiredRuleDeno(properties.outbox);
|
|
546
|
-
const eslint$
|
|
548
|
+
const eslint$5 = createRequiredRuleEslint(properties.outbox);
|
|
547
549
|
createRequiredRuleDeno(properties.publicKey);
|
|
548
|
-
const eslint$
|
|
550
|
+
const eslint$4 = createRequiredRuleEslint(properties.publicKey);
|
|
549
551
|
createMismatchRuleDeno(properties.sharedInbox);
|
|
550
|
-
const eslint$
|
|
552
|
+
const eslint$3 = createMismatchRuleEslint(properties.sharedInbox);
|
|
551
553
|
createRequiredRuleDeno(properties.sharedInbox);
|
|
552
|
-
const eslint$
|
|
554
|
+
const eslint$2 = createRequiredRuleEslint(properties.sharedInbox);
|
|
553
555
|
//#endregion
|
|
554
556
|
//#region src/rules/collection-filtering-not-implemented.ts
|
|
555
557
|
/**
|
|
@@ -568,7 +570,7 @@ const isFollowersDispatcherCall = (node) => "callee" in node && node.callee && n
|
|
|
568
570
|
* CollectionDispatcher signature: (context, identifier, cursor, filter?) => ...
|
|
569
571
|
*/
|
|
570
572
|
const hasFilterParameter = hasMinParams(4);
|
|
571
|
-
const eslint = {
|
|
573
|
+
const eslint$1 = {
|
|
572
574
|
meta: {
|
|
573
575
|
type: "suggestion",
|
|
574
576
|
docs: { description: "Ensure followers dispatcher implements filtering" },
|
|
@@ -593,33 +595,287 @@ const eslint = {
|
|
|
593
595
|
}
|
|
594
596
|
};
|
|
595
597
|
//#endregion
|
|
598
|
+
//#region src/rules/outbox-listener-delivery-required.ts
|
|
599
|
+
const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
|
|
600
|
+
const isChainedFromOutboxListeners = (expr, federationTracker) => {
|
|
601
|
+
if (expr.type !== "CallExpression") return false;
|
|
602
|
+
if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
|
|
603
|
+
const methodName = expr.callee.property.name;
|
|
604
|
+
if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
|
|
605
|
+
if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
|
|
606
|
+
return false;
|
|
607
|
+
};
|
|
608
|
+
const DELIVERY_METHOD_NAMES = new Set(["sendActivity", "forwardActivity"]);
|
|
609
|
+
const getMemberPropertyName = (expr) => {
|
|
610
|
+
if (expr.type !== "MemberExpression") return null;
|
|
611
|
+
const property = expr.property;
|
|
612
|
+
if (property.type === "Identifier") return property.name;
|
|
613
|
+
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
614
|
+
return null;
|
|
615
|
+
};
|
|
616
|
+
function unwrapContextParam(node) {
|
|
617
|
+
let current = node ?? null;
|
|
618
|
+
while (current?.type === "AssignmentPattern") current = current.left;
|
|
619
|
+
return current;
|
|
620
|
+
}
|
|
621
|
+
function escapeRegExp(value) {
|
|
622
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
623
|
+
}
|
|
624
|
+
function stripCommentsAndStrings(code) {
|
|
625
|
+
let result = "";
|
|
626
|
+
let index = 0;
|
|
627
|
+
const skipQuotedString = (quote) => {
|
|
628
|
+
const start = index;
|
|
629
|
+
index += 1;
|
|
630
|
+
while (index < code.length) {
|
|
631
|
+
const char = code[index];
|
|
632
|
+
if (char === "\\") {
|
|
633
|
+
index += 2;
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
index += 1;
|
|
637
|
+
if (char === quote) break;
|
|
638
|
+
}
|
|
639
|
+
const literal = code.slice(start, index);
|
|
640
|
+
const value = literal.slice(1, -1);
|
|
641
|
+
result += DELIVERY_METHOD_NAMES.has(value) ? literal : `${quote}${quote}`;
|
|
642
|
+
};
|
|
643
|
+
const stripTemplateLiteral = () => {
|
|
644
|
+
const start = index;
|
|
645
|
+
index += 1;
|
|
646
|
+
let raw = "";
|
|
647
|
+
let hasExpression = false;
|
|
648
|
+
while (index < code.length) {
|
|
649
|
+
const char = code[index];
|
|
650
|
+
if (char === "\\") {
|
|
651
|
+
raw += char;
|
|
652
|
+
raw += code[index + 1] ?? "";
|
|
653
|
+
index += 2;
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (char === "`") {
|
|
657
|
+
index += 1;
|
|
658
|
+
if (!hasExpression && DELIVERY_METHOD_NAMES.has(raw)) result += code.slice(start, index);
|
|
659
|
+
else result += "``";
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (char === "$" && code[index + 1] === "{") {
|
|
663
|
+
hasExpression = true;
|
|
664
|
+
result += "`${";
|
|
665
|
+
index += 2;
|
|
666
|
+
let depth = 1;
|
|
667
|
+
while (index < code.length && depth > 0) {
|
|
668
|
+
const exprChar = code[index];
|
|
669
|
+
const next = code[index + 1];
|
|
670
|
+
if (exprChar === "'" || exprChar === "\"") {
|
|
671
|
+
skipQuotedString(exprChar);
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
if (exprChar === "`") {
|
|
675
|
+
stripTemplateLiteral();
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
if (exprChar === "/" && next === "*") {
|
|
679
|
+
index += 2;
|
|
680
|
+
while (index < code.length) {
|
|
681
|
+
if (code[index] === "*" && code[index + 1] === "/") {
|
|
682
|
+
index += 2;
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
index += 1;
|
|
686
|
+
}
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
if (exprChar === "/" && next === "/") {
|
|
690
|
+
index += 2;
|
|
691
|
+
while (index < code.length && code[index] !== "\n") index += 1;
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
result += exprChar;
|
|
695
|
+
index += 1;
|
|
696
|
+
if (exprChar === "{") depth += 1;
|
|
697
|
+
else if (exprChar === "}") depth -= 1;
|
|
698
|
+
}
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
raw += char;
|
|
702
|
+
index += 1;
|
|
703
|
+
}
|
|
704
|
+
result += "``";
|
|
705
|
+
};
|
|
706
|
+
while (index < code.length) {
|
|
707
|
+
const char = code[index];
|
|
708
|
+
const next = code[index + 1];
|
|
709
|
+
if (char === "/" && next === "*") {
|
|
710
|
+
index += 2;
|
|
711
|
+
while (index < code.length) {
|
|
712
|
+
if (code[index] === "*" && code[index + 1] === "/") {
|
|
713
|
+
index += 2;
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
index += 1;
|
|
717
|
+
}
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
if (char === "/" && next === "/") {
|
|
721
|
+
index += 2;
|
|
722
|
+
while (index < code.length && code[index] !== "\n") index += 1;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
if (char === "'" || char === "\"") {
|
|
726
|
+
skipQuotedString(char);
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (char === "`") {
|
|
730
|
+
stripTemplateLiteral();
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
result += char;
|
|
734
|
+
index += 1;
|
|
735
|
+
}
|
|
736
|
+
return result;
|
|
737
|
+
}
|
|
738
|
+
function getDeliveryAliasName(node) {
|
|
739
|
+
if (node.type === "Identifier") return node.name;
|
|
740
|
+
if (node.type === "AssignmentPattern" && node.left.type === "Identifier") return node.left.name;
|
|
741
|
+
return null;
|
|
742
|
+
}
|
|
743
|
+
function buildContextExpressionPattern(contextName) {
|
|
744
|
+
const name = escapeRegExp(contextName);
|
|
745
|
+
const boundedName = String.raw`(?<![\w$])${name}(?![\w$])`;
|
|
746
|
+
return String.raw`(?:${boundedName}|\(\s*${boundedName}(?:\s+as\s+[^)]+)?\s*\))`;
|
|
747
|
+
}
|
|
748
|
+
const resolveListenerReference = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
|
|
749
|
+
if (isFunction(expr)) return expr;
|
|
750
|
+
if (expr.type === "Identifier") {
|
|
751
|
+
if (seen.has(expr.name)) return null;
|
|
752
|
+
seen.add(expr.name);
|
|
753
|
+
const binding = bindings.get(expr.name);
|
|
754
|
+
if (binding == null || !isNode(binding)) return null;
|
|
755
|
+
if (isFunction(binding) || binding.type === "FunctionDeclaration") return binding;
|
|
756
|
+
if (binding.type === "Identifier") return resolveListenerReference(binding, bindings, seen);
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
|
|
760
|
+
const binding = bindings.get(expr.object.name);
|
|
761
|
+
if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
|
|
762
|
+
const propertyName = getMemberPropertyName(expr);
|
|
763
|
+
if (propertyName == null) return null;
|
|
764
|
+
for (const prop of binding.properties) {
|
|
765
|
+
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
766
|
+
if ((prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName || !isNode(prop.value)) continue;
|
|
767
|
+
const value = prop.value;
|
|
768
|
+
if (isFunction(value) || value.type === "FunctionDeclaration") return value;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return null;
|
|
772
|
+
};
|
|
773
|
+
const listenerCallsDeliveryMethod = (sourceCode, listener) => {
|
|
774
|
+
const code = stripCommentsAndStrings(sourceCode.getText(listener));
|
|
775
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
776
|
+
const contextParam = unwrapContextParam(listener.params[0]);
|
|
777
|
+
const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
778
|
+
if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
|
|
779
|
+
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
780
|
+
const keyName = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null;
|
|
781
|
+
if (keyName == null || !DELIVERY_METHOD_NAMES.has(keyName)) continue;
|
|
782
|
+
const alias = getDeliveryAliasName(prop.value);
|
|
783
|
+
if (alias != null) aliases.add(alias);
|
|
784
|
+
}
|
|
785
|
+
if (contextName != null) {
|
|
786
|
+
const contextExpr = buildContextExpressionPattern(contextName);
|
|
787
|
+
if (new RegExp(String.raw`${contextExpr}\s*(?:\?\s*\.\s*(?:sendActivity|forwardActivity)|\.\s*(?:sendActivity|forwardActivity)|\?\s*\.\s*\[\s*["'\`](?:sendActivity|forwardActivity)["'\`]\s*\]|\[\s*["'\`](?:sendActivity|forwardActivity)["'\`]\s*\])\s*\(`).test(code)) return true;
|
|
788
|
+
const destructuringPattern = new RegExp(String.raw`(?:const|let|var)\s*{([^}]*)}\s*=\s*${contextExpr}`, "g");
|
|
789
|
+
for (const match of code.matchAll(destructuringPattern)) {
|
|
790
|
+
const fields = match[1].split(",").map((field) => field.trim()).filter(Boolean);
|
|
791
|
+
for (const field of fields) {
|
|
792
|
+
const [sourceName, aliasName] = field.split(":").map((part) => part.trim());
|
|
793
|
+
if (!DELIVERY_METHOD_NAMES.has(sourceName)) continue;
|
|
794
|
+
aliases.add(aliasName ?? sourceName);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
const aliasPattern = new RegExp(String.raw`(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*${contextExpr}\s*(?:\?\s*\.\s*(sendActivity|forwardActivity)|\.\s*(sendActivity|forwardActivity)|\?\s*\.\s*\[\s*["'\`](sendActivity|forwardActivity)["'\`]\s*\]|\[\s*["'\`](sendActivity|forwardActivity)["'\`]\s*\])`, "g");
|
|
798
|
+
for (const match of code.matchAll(aliasPattern)) aliases.add(match[1]);
|
|
799
|
+
}
|
|
800
|
+
return globalThis.Array.from(aliases).some((alias) => new RegExp(String.raw`\b${escapeRegExp(alias)}\s*\(`).test(code));
|
|
801
|
+
};
|
|
802
|
+
function createRule(buildReport) {
|
|
803
|
+
return (context) => {
|
|
804
|
+
const federationTracker = trackFederationVariables();
|
|
805
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
806
|
+
const pendingCalls = [];
|
|
807
|
+
const sourceCode = context.sourceCode;
|
|
808
|
+
const inspectCall = (node) => {
|
|
809
|
+
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
|
|
810
|
+
if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
|
|
811
|
+
const listener = node.arguments[1];
|
|
812
|
+
const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
|
|
813
|
+
if (resolvedListener == null) return;
|
|
814
|
+
if (listenerCallsDeliveryMethod(sourceCode, resolvedListener)) return;
|
|
815
|
+
context.report({
|
|
816
|
+
node: resolvedListener,
|
|
817
|
+
...buildReport
|
|
818
|
+
});
|
|
819
|
+
};
|
|
820
|
+
return {
|
|
821
|
+
VariableDeclarator(node) {
|
|
822
|
+
federationTracker.VariableDeclarator(node);
|
|
823
|
+
if (node.id.type === "Identifier" && node.init != null) bindings.set(node.id.name, node.init);
|
|
824
|
+
},
|
|
825
|
+
FunctionDeclaration(node) {
|
|
826
|
+
if (node.id != null) bindings.set(node.id.name, node);
|
|
827
|
+
},
|
|
828
|
+
CallExpression(node) {
|
|
829
|
+
pendingCalls.push(node);
|
|
830
|
+
},
|
|
831
|
+
"Program:exit"() {
|
|
832
|
+
for (const node of pendingCalls) inspectCall(node);
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
createRule({ message: MESSAGE });
|
|
838
|
+
const eslint = {
|
|
839
|
+
meta: {
|
|
840
|
+
type: "suggestion",
|
|
841
|
+
docs: { description: "Warn when an outbox listener omits explicit delivery methods" },
|
|
842
|
+
schema: [],
|
|
843
|
+
messages: { required: "{{ message }}" }
|
|
844
|
+
},
|
|
845
|
+
create: createRule({
|
|
846
|
+
messageId: "required",
|
|
847
|
+
data: { message: MESSAGE }
|
|
848
|
+
})
|
|
849
|
+
};
|
|
850
|
+
//#endregion
|
|
596
851
|
//#region src/index.ts
|
|
597
852
|
/**
|
|
598
853
|
* ESLint plugin for Fedify.
|
|
599
854
|
* Provides lint rules for validating Fedify federation code.
|
|
600
855
|
*/
|
|
601
856
|
const rules = {
|
|
602
|
-
[RULE_IDS.actorIdMismatch]: eslint$
|
|
603
|
-
[RULE_IDS.actorIdRequired]: eslint$
|
|
604
|
-
[RULE_IDS.actorFollowingPropertyRequired]: eslint$
|
|
605
|
-
[RULE_IDS.actorFollowingPropertyMismatch]: eslint$
|
|
606
|
-
[RULE_IDS.actorFollowersPropertyRequired]: eslint$
|
|
607
|
-
[RULE_IDS.actorFollowersPropertyMismatch]: eslint$
|
|
608
|
-
[RULE_IDS.actorOutboxPropertyRequired]: eslint$
|
|
609
|
-
[RULE_IDS.actorOutboxPropertyMismatch]: eslint$
|
|
610
|
-
[RULE_IDS.actorLikedPropertyRequired]: eslint$
|
|
611
|
-
[RULE_IDS.actorLikedPropertyMismatch]: eslint$
|
|
612
|
-
[RULE_IDS.actorFeaturedPropertyRequired]: eslint$
|
|
613
|
-
[RULE_IDS.actorFeaturedPropertyMismatch]: eslint$
|
|
614
|
-
[RULE_IDS.actorFeaturedTagsPropertyRequired]: eslint$
|
|
615
|
-
[RULE_IDS.actorFeaturedTagsPropertyMismatch]: eslint$
|
|
616
|
-
[RULE_IDS.actorInboxPropertyRequired]: eslint$
|
|
617
|
-
[RULE_IDS.actorInboxPropertyMismatch]: eslint$
|
|
618
|
-
[RULE_IDS.actorSharedInboxPropertyRequired]: eslint$
|
|
619
|
-
[RULE_IDS.actorSharedInboxPropertyMismatch]: eslint$
|
|
620
|
-
[RULE_IDS.actorPublicKeyRequired]: eslint$
|
|
621
|
-
[RULE_IDS.actorAssertionMethodRequired]: eslint$
|
|
622
|
-
[RULE_IDS.collectionFilteringNotImplemented]: eslint
|
|
857
|
+
[RULE_IDS.actorIdMismatch]: eslint$12,
|
|
858
|
+
[RULE_IDS.actorIdRequired]: eslint$11,
|
|
859
|
+
[RULE_IDS.actorFollowingPropertyRequired]: eslint$13,
|
|
860
|
+
[RULE_IDS.actorFollowingPropertyMismatch]: eslint$14,
|
|
861
|
+
[RULE_IDS.actorFollowersPropertyRequired]: eslint$15,
|
|
862
|
+
[RULE_IDS.actorFollowersPropertyMismatch]: eslint$16,
|
|
863
|
+
[RULE_IDS.actorOutboxPropertyRequired]: eslint$5,
|
|
864
|
+
[RULE_IDS.actorOutboxPropertyMismatch]: eslint$6,
|
|
865
|
+
[RULE_IDS.actorLikedPropertyRequired]: eslint$7,
|
|
866
|
+
[RULE_IDS.actorLikedPropertyMismatch]: eslint$8,
|
|
867
|
+
[RULE_IDS.actorFeaturedPropertyRequired]: eslint$19,
|
|
868
|
+
[RULE_IDS.actorFeaturedPropertyMismatch]: eslint$20,
|
|
869
|
+
[RULE_IDS.actorFeaturedTagsPropertyRequired]: eslint$17,
|
|
870
|
+
[RULE_IDS.actorFeaturedTagsPropertyMismatch]: eslint$18,
|
|
871
|
+
[RULE_IDS.actorInboxPropertyRequired]: eslint$9,
|
|
872
|
+
[RULE_IDS.actorInboxPropertyMismatch]: eslint$10,
|
|
873
|
+
[RULE_IDS.actorSharedInboxPropertyRequired]: eslint$2,
|
|
874
|
+
[RULE_IDS.actorSharedInboxPropertyMismatch]: eslint$3,
|
|
875
|
+
[RULE_IDS.actorPublicKeyRequired]: eslint$4,
|
|
876
|
+
[RULE_IDS.actorAssertionMethodRequired]: eslint$21,
|
|
877
|
+
[RULE_IDS.collectionFilteringNotImplemented]: eslint$1,
|
|
878
|
+
[RULE_IDS.outboxListenerDeliveryRequired]: eslint
|
|
623
879
|
};
|
|
624
880
|
const recommendedRuleIds = [RULE_IDS.actorIdMismatch, RULE_IDS.actorIdRequired];
|
|
625
881
|
/**
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { always, every, fromEntries, head, isEmpty, isObject, keys, map, negate,
|
|
|
2
2
|
import parser from "@typescript-eslint/parser";
|
|
3
3
|
//#region deno.json
|
|
4
4
|
var name = "@fedify/lint";
|
|
5
|
-
var version = "2.2.0-dev.
|
|
5
|
+
var version = "2.2.0-dev.869+97ccad7a";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/lib/const.ts
|
|
8
8
|
/**
|
|
@@ -119,7 +119,8 @@ const RULE_IDS = {
|
|
|
119
119
|
actorFeaturedTagsPropertyMismatch: "actor-featured-tags-property-mismatch",
|
|
120
120
|
actorInboxPropertyMismatch: "actor-inbox-property-mismatch",
|
|
121
121
|
actorSharedInboxPropertyMismatch: "actor-shared-inbox-property-mismatch",
|
|
122
|
-
collectionFilteringNotImplemented: "collection-filtering-not-implemented"
|
|
122
|
+
collectionFilteringNotImplemented: "collection-filtering-not-implemented",
|
|
123
|
+
outboxListenerDeliveryRequired: "outbox-listener-delivery-required"
|
|
123
124
|
};
|
|
124
125
|
//#endregion
|
|
125
126
|
//#region src/lib/messages.ts
|
|
@@ -164,6 +165,7 @@ function allOf(...predicates) {
|
|
|
164
165
|
return (value) => predicates.every((predicate) => predicate(value));
|
|
165
166
|
}
|
|
166
167
|
const anyOf = (...predicates) => (value) => predicates.some((predicate) => predicate(value));
|
|
168
|
+
const isNode = (obj) => isObject(obj) && "type" in obj;
|
|
167
169
|
/**
|
|
168
170
|
* Checks if a node is of a specific type.
|
|
169
171
|
*/
|
|
@@ -416,7 +418,7 @@ function createRequiredRuleEslint(config) {
|
|
|
416
418
|
};
|
|
417
419
|
}
|
|
418
420
|
createRequiredRuleDeno(properties.assertionMethod);
|
|
419
|
-
const eslint$
|
|
421
|
+
const eslint$21 = createRequiredRuleEslint(properties.assertionMethod);
|
|
420
422
|
//#endregion
|
|
421
423
|
//#region src/lib/mismatch.ts
|
|
422
424
|
const isIdentifierWithName = (name) => (node) => allOf(isNodeType("Identifier"), isNodeName(name))(node);
|
|
@@ -486,43 +488,43 @@ const createMismatchRuleEslint = (config) => ({
|
|
|
486
488
|
}))
|
|
487
489
|
});
|
|
488
490
|
createMismatchRuleDeno(properties.featured);
|
|
489
|
-
const eslint$
|
|
491
|
+
const eslint$20 = createMismatchRuleEslint(properties.featured);
|
|
490
492
|
createRequiredRuleDeno(properties.featured);
|
|
491
|
-
const eslint$
|
|
493
|
+
const eslint$19 = createRequiredRuleEslint(properties.featured);
|
|
492
494
|
createMismatchRuleDeno(properties.featuredTags);
|
|
493
|
-
const eslint$
|
|
495
|
+
const eslint$18 = createMismatchRuleEslint(properties.featuredTags);
|
|
494
496
|
createRequiredRuleDeno(properties.featuredTags);
|
|
495
|
-
const eslint$
|
|
497
|
+
const eslint$17 = createRequiredRuleEslint(properties.featuredTags);
|
|
496
498
|
createMismatchRuleDeno(properties.followers);
|
|
497
|
-
const eslint$
|
|
499
|
+
const eslint$16 = createMismatchRuleEslint(properties.followers);
|
|
498
500
|
createRequiredRuleDeno(properties.followers);
|
|
499
|
-
const eslint$
|
|
501
|
+
const eslint$15 = createRequiredRuleEslint(properties.followers);
|
|
500
502
|
createMismatchRuleDeno(properties.following);
|
|
501
|
-
const eslint$
|
|
503
|
+
const eslint$14 = createMismatchRuleEslint(properties.following);
|
|
502
504
|
createRequiredRuleDeno(properties.following);
|
|
503
|
-
const eslint$
|
|
505
|
+
const eslint$13 = createRequiredRuleEslint(properties.following);
|
|
504
506
|
createMismatchRuleDeno(properties.id);
|
|
505
|
-
const eslint$
|
|
507
|
+
const eslint$12 = createMismatchRuleEslint(properties.id);
|
|
506
508
|
createRequiredRuleDeno(properties.id);
|
|
507
|
-
const eslint$
|
|
509
|
+
const eslint$11 = createRequiredRuleEslint(properties.id);
|
|
508
510
|
createMismatchRuleDeno(properties.inbox);
|
|
509
|
-
const eslint$
|
|
511
|
+
const eslint$10 = createMismatchRuleEslint(properties.inbox);
|
|
510
512
|
createRequiredRuleDeno(properties.inbox);
|
|
511
|
-
const eslint$
|
|
513
|
+
const eslint$9 = createRequiredRuleEslint(properties.inbox);
|
|
512
514
|
createMismatchRuleDeno(properties.liked);
|
|
513
|
-
const eslint$
|
|
515
|
+
const eslint$8 = createMismatchRuleEslint(properties.liked);
|
|
514
516
|
createRequiredRuleDeno(properties.liked);
|
|
515
|
-
const eslint$
|
|
517
|
+
const eslint$7 = createRequiredRuleEslint(properties.liked);
|
|
516
518
|
createMismatchRuleDeno(properties.outbox);
|
|
517
|
-
const eslint$
|
|
519
|
+
const eslint$6 = createMismatchRuleEslint(properties.outbox);
|
|
518
520
|
createRequiredRuleDeno(properties.outbox);
|
|
519
|
-
const eslint$
|
|
521
|
+
const eslint$5 = createRequiredRuleEslint(properties.outbox);
|
|
520
522
|
createRequiredRuleDeno(properties.publicKey);
|
|
521
|
-
const eslint$
|
|
523
|
+
const eslint$4 = createRequiredRuleEslint(properties.publicKey);
|
|
522
524
|
createMismatchRuleDeno(properties.sharedInbox);
|
|
523
|
-
const eslint$
|
|
525
|
+
const eslint$3 = createMismatchRuleEslint(properties.sharedInbox);
|
|
524
526
|
createRequiredRuleDeno(properties.sharedInbox);
|
|
525
|
-
const eslint$
|
|
527
|
+
const eslint$2 = createRequiredRuleEslint(properties.sharedInbox);
|
|
526
528
|
//#endregion
|
|
527
529
|
//#region src/rules/collection-filtering-not-implemented.ts
|
|
528
530
|
/**
|
|
@@ -541,7 +543,7 @@ const isFollowersDispatcherCall = (node) => "callee" in node && node.callee && n
|
|
|
541
543
|
* CollectionDispatcher signature: (context, identifier, cursor, filter?) => ...
|
|
542
544
|
*/
|
|
543
545
|
const hasFilterParameter = hasMinParams(4);
|
|
544
|
-
const eslint = {
|
|
546
|
+
const eslint$1 = {
|
|
545
547
|
meta: {
|
|
546
548
|
type: "suggestion",
|
|
547
549
|
docs: { description: "Ensure followers dispatcher implements filtering" },
|
|
@@ -566,33 +568,287 @@ const eslint = {
|
|
|
566
568
|
}
|
|
567
569
|
};
|
|
568
570
|
//#endregion
|
|
571
|
+
//#region src/rules/outbox-listener-delivery-required.ts
|
|
572
|
+
const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
|
|
573
|
+
const isChainedFromOutboxListeners = (expr, federationTracker) => {
|
|
574
|
+
if (expr.type !== "CallExpression") return false;
|
|
575
|
+
if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
|
|
576
|
+
const methodName = expr.callee.property.name;
|
|
577
|
+
if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
|
|
578
|
+
if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
|
|
579
|
+
return false;
|
|
580
|
+
};
|
|
581
|
+
const DELIVERY_METHOD_NAMES = new Set(["sendActivity", "forwardActivity"]);
|
|
582
|
+
const getMemberPropertyName = (expr) => {
|
|
583
|
+
if (expr.type !== "MemberExpression") return null;
|
|
584
|
+
const property = expr.property;
|
|
585
|
+
if (property.type === "Identifier") return property.name;
|
|
586
|
+
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
587
|
+
return null;
|
|
588
|
+
};
|
|
589
|
+
function unwrapContextParam(node) {
|
|
590
|
+
let current = node ?? null;
|
|
591
|
+
while (current?.type === "AssignmentPattern") current = current.left;
|
|
592
|
+
return current;
|
|
593
|
+
}
|
|
594
|
+
function escapeRegExp(value) {
|
|
595
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
596
|
+
}
|
|
597
|
+
function stripCommentsAndStrings(code) {
|
|
598
|
+
let result = "";
|
|
599
|
+
let index = 0;
|
|
600
|
+
const skipQuotedString = (quote) => {
|
|
601
|
+
const start = index;
|
|
602
|
+
index += 1;
|
|
603
|
+
while (index < code.length) {
|
|
604
|
+
const char = code[index];
|
|
605
|
+
if (char === "\\") {
|
|
606
|
+
index += 2;
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
index += 1;
|
|
610
|
+
if (char === quote) break;
|
|
611
|
+
}
|
|
612
|
+
const literal = code.slice(start, index);
|
|
613
|
+
const value = literal.slice(1, -1);
|
|
614
|
+
result += DELIVERY_METHOD_NAMES.has(value) ? literal : `${quote}${quote}`;
|
|
615
|
+
};
|
|
616
|
+
const stripTemplateLiteral = () => {
|
|
617
|
+
const start = index;
|
|
618
|
+
index += 1;
|
|
619
|
+
let raw = "";
|
|
620
|
+
let hasExpression = false;
|
|
621
|
+
while (index < code.length) {
|
|
622
|
+
const char = code[index];
|
|
623
|
+
if (char === "\\") {
|
|
624
|
+
raw += char;
|
|
625
|
+
raw += code[index + 1] ?? "";
|
|
626
|
+
index += 2;
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
if (char === "`") {
|
|
630
|
+
index += 1;
|
|
631
|
+
if (!hasExpression && DELIVERY_METHOD_NAMES.has(raw)) result += code.slice(start, index);
|
|
632
|
+
else result += "``";
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (char === "$" && code[index + 1] === "{") {
|
|
636
|
+
hasExpression = true;
|
|
637
|
+
result += "`${";
|
|
638
|
+
index += 2;
|
|
639
|
+
let depth = 1;
|
|
640
|
+
while (index < code.length && depth > 0) {
|
|
641
|
+
const exprChar = code[index];
|
|
642
|
+
const next = code[index + 1];
|
|
643
|
+
if (exprChar === "'" || exprChar === "\"") {
|
|
644
|
+
skipQuotedString(exprChar);
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
if (exprChar === "`") {
|
|
648
|
+
stripTemplateLiteral();
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (exprChar === "/" && next === "*") {
|
|
652
|
+
index += 2;
|
|
653
|
+
while (index < code.length) {
|
|
654
|
+
if (code[index] === "*" && code[index + 1] === "/") {
|
|
655
|
+
index += 2;
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
index += 1;
|
|
659
|
+
}
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
if (exprChar === "/" && next === "/") {
|
|
663
|
+
index += 2;
|
|
664
|
+
while (index < code.length && code[index] !== "\n") index += 1;
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
result += exprChar;
|
|
668
|
+
index += 1;
|
|
669
|
+
if (exprChar === "{") depth += 1;
|
|
670
|
+
else if (exprChar === "}") depth -= 1;
|
|
671
|
+
}
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
raw += char;
|
|
675
|
+
index += 1;
|
|
676
|
+
}
|
|
677
|
+
result += "``";
|
|
678
|
+
};
|
|
679
|
+
while (index < code.length) {
|
|
680
|
+
const char = code[index];
|
|
681
|
+
const next = code[index + 1];
|
|
682
|
+
if (char === "/" && next === "*") {
|
|
683
|
+
index += 2;
|
|
684
|
+
while (index < code.length) {
|
|
685
|
+
if (code[index] === "*" && code[index + 1] === "/") {
|
|
686
|
+
index += 2;
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
index += 1;
|
|
690
|
+
}
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
if (char === "/" && next === "/") {
|
|
694
|
+
index += 2;
|
|
695
|
+
while (index < code.length && code[index] !== "\n") index += 1;
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
if (char === "'" || char === "\"") {
|
|
699
|
+
skipQuotedString(char);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (char === "`") {
|
|
703
|
+
stripTemplateLiteral();
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
result += char;
|
|
707
|
+
index += 1;
|
|
708
|
+
}
|
|
709
|
+
return result;
|
|
710
|
+
}
|
|
711
|
+
function getDeliveryAliasName(node) {
|
|
712
|
+
if (node.type === "Identifier") return node.name;
|
|
713
|
+
if (node.type === "AssignmentPattern" && node.left.type === "Identifier") return node.left.name;
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
function buildContextExpressionPattern(contextName) {
|
|
717
|
+
const name = escapeRegExp(contextName);
|
|
718
|
+
const boundedName = String.raw`(?<![\w$])${name}(?![\w$])`;
|
|
719
|
+
return String.raw`(?:${boundedName}|\(\s*${boundedName}(?:\s+as\s+[^)]+)?\s*\))`;
|
|
720
|
+
}
|
|
721
|
+
const resolveListenerReference = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
|
|
722
|
+
if (isFunction(expr)) return expr;
|
|
723
|
+
if (expr.type === "Identifier") {
|
|
724
|
+
if (seen.has(expr.name)) return null;
|
|
725
|
+
seen.add(expr.name);
|
|
726
|
+
const binding = bindings.get(expr.name);
|
|
727
|
+
if (binding == null || !isNode(binding)) return null;
|
|
728
|
+
if (isFunction(binding) || binding.type === "FunctionDeclaration") return binding;
|
|
729
|
+
if (binding.type === "Identifier") return resolveListenerReference(binding, bindings, seen);
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
|
|
733
|
+
const binding = bindings.get(expr.object.name);
|
|
734
|
+
if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
|
|
735
|
+
const propertyName = getMemberPropertyName(expr);
|
|
736
|
+
if (propertyName == null) return null;
|
|
737
|
+
for (const prop of binding.properties) {
|
|
738
|
+
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
739
|
+
if ((prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName || !isNode(prop.value)) continue;
|
|
740
|
+
const value = prop.value;
|
|
741
|
+
if (isFunction(value) || value.type === "FunctionDeclaration") return value;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return null;
|
|
745
|
+
};
|
|
746
|
+
const listenerCallsDeliveryMethod = (sourceCode, listener) => {
|
|
747
|
+
const code = stripCommentsAndStrings(sourceCode.getText(listener));
|
|
748
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
749
|
+
const contextParam = unwrapContextParam(listener.params[0]);
|
|
750
|
+
const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
751
|
+
if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
|
|
752
|
+
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
753
|
+
const keyName = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null;
|
|
754
|
+
if (keyName == null || !DELIVERY_METHOD_NAMES.has(keyName)) continue;
|
|
755
|
+
const alias = getDeliveryAliasName(prop.value);
|
|
756
|
+
if (alias != null) aliases.add(alias);
|
|
757
|
+
}
|
|
758
|
+
if (contextName != null) {
|
|
759
|
+
const contextExpr = buildContextExpressionPattern(contextName);
|
|
760
|
+
if (new RegExp(String.raw`${contextExpr}\s*(?:\?\s*\.\s*(?:sendActivity|forwardActivity)|\.\s*(?:sendActivity|forwardActivity)|\?\s*\.\s*\[\s*["'\`](?:sendActivity|forwardActivity)["'\`]\s*\]|\[\s*["'\`](?:sendActivity|forwardActivity)["'\`]\s*\])\s*\(`).test(code)) return true;
|
|
761
|
+
const destructuringPattern = new RegExp(String.raw`(?:const|let|var)\s*{([^}]*)}\s*=\s*${contextExpr}`, "g");
|
|
762
|
+
for (const match of code.matchAll(destructuringPattern)) {
|
|
763
|
+
const fields = match[1].split(",").map((field) => field.trim()).filter(Boolean);
|
|
764
|
+
for (const field of fields) {
|
|
765
|
+
const [sourceName, aliasName] = field.split(":").map((part) => part.trim());
|
|
766
|
+
if (!DELIVERY_METHOD_NAMES.has(sourceName)) continue;
|
|
767
|
+
aliases.add(aliasName ?? sourceName);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const aliasPattern = new RegExp(String.raw`(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*${contextExpr}\s*(?:\?\s*\.\s*(sendActivity|forwardActivity)|\.\s*(sendActivity|forwardActivity)|\?\s*\.\s*\[\s*["'\`](sendActivity|forwardActivity)["'\`]\s*\]|\[\s*["'\`](sendActivity|forwardActivity)["'\`]\s*\])`, "g");
|
|
771
|
+
for (const match of code.matchAll(aliasPattern)) aliases.add(match[1]);
|
|
772
|
+
}
|
|
773
|
+
return globalThis.Array.from(aliases).some((alias) => new RegExp(String.raw`\b${escapeRegExp(alias)}\s*\(`).test(code));
|
|
774
|
+
};
|
|
775
|
+
function createRule(buildReport) {
|
|
776
|
+
return (context) => {
|
|
777
|
+
const federationTracker = trackFederationVariables();
|
|
778
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
779
|
+
const pendingCalls = [];
|
|
780
|
+
const sourceCode = context.sourceCode;
|
|
781
|
+
const inspectCall = (node) => {
|
|
782
|
+
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
|
|
783
|
+
if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
|
|
784
|
+
const listener = node.arguments[1];
|
|
785
|
+
const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
|
|
786
|
+
if (resolvedListener == null) return;
|
|
787
|
+
if (listenerCallsDeliveryMethod(sourceCode, resolvedListener)) return;
|
|
788
|
+
context.report({
|
|
789
|
+
node: resolvedListener,
|
|
790
|
+
...buildReport
|
|
791
|
+
});
|
|
792
|
+
};
|
|
793
|
+
return {
|
|
794
|
+
VariableDeclarator(node) {
|
|
795
|
+
federationTracker.VariableDeclarator(node);
|
|
796
|
+
if (node.id.type === "Identifier" && node.init != null) bindings.set(node.id.name, node.init);
|
|
797
|
+
},
|
|
798
|
+
FunctionDeclaration(node) {
|
|
799
|
+
if (node.id != null) bindings.set(node.id.name, node);
|
|
800
|
+
},
|
|
801
|
+
CallExpression(node) {
|
|
802
|
+
pendingCalls.push(node);
|
|
803
|
+
},
|
|
804
|
+
"Program:exit"() {
|
|
805
|
+
for (const node of pendingCalls) inspectCall(node);
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
createRule({ message: MESSAGE });
|
|
811
|
+
const eslint = {
|
|
812
|
+
meta: {
|
|
813
|
+
type: "suggestion",
|
|
814
|
+
docs: { description: "Warn when an outbox listener omits explicit delivery methods" },
|
|
815
|
+
schema: [],
|
|
816
|
+
messages: { required: "{{ message }}" }
|
|
817
|
+
},
|
|
818
|
+
create: createRule({
|
|
819
|
+
messageId: "required",
|
|
820
|
+
data: { message: MESSAGE }
|
|
821
|
+
})
|
|
822
|
+
};
|
|
823
|
+
//#endregion
|
|
569
824
|
//#region src/index.ts
|
|
570
825
|
/**
|
|
571
826
|
* ESLint plugin for Fedify.
|
|
572
827
|
* Provides lint rules for validating Fedify federation code.
|
|
573
828
|
*/
|
|
574
829
|
const rules = {
|
|
575
|
-
[RULE_IDS.actorIdMismatch]: eslint$
|
|
576
|
-
[RULE_IDS.actorIdRequired]: eslint$
|
|
577
|
-
[RULE_IDS.actorFollowingPropertyRequired]: eslint$
|
|
578
|
-
[RULE_IDS.actorFollowingPropertyMismatch]: eslint$
|
|
579
|
-
[RULE_IDS.actorFollowersPropertyRequired]: eslint$
|
|
580
|
-
[RULE_IDS.actorFollowersPropertyMismatch]: eslint$
|
|
581
|
-
[RULE_IDS.actorOutboxPropertyRequired]: eslint$
|
|
582
|
-
[RULE_IDS.actorOutboxPropertyMismatch]: eslint$
|
|
583
|
-
[RULE_IDS.actorLikedPropertyRequired]: eslint$
|
|
584
|
-
[RULE_IDS.actorLikedPropertyMismatch]: eslint$
|
|
585
|
-
[RULE_IDS.actorFeaturedPropertyRequired]: eslint$
|
|
586
|
-
[RULE_IDS.actorFeaturedPropertyMismatch]: eslint$
|
|
587
|
-
[RULE_IDS.actorFeaturedTagsPropertyRequired]: eslint$
|
|
588
|
-
[RULE_IDS.actorFeaturedTagsPropertyMismatch]: eslint$
|
|
589
|
-
[RULE_IDS.actorInboxPropertyRequired]: eslint$
|
|
590
|
-
[RULE_IDS.actorInboxPropertyMismatch]: eslint$
|
|
591
|
-
[RULE_IDS.actorSharedInboxPropertyRequired]: eslint$
|
|
592
|
-
[RULE_IDS.actorSharedInboxPropertyMismatch]: eslint$
|
|
593
|
-
[RULE_IDS.actorPublicKeyRequired]: eslint$
|
|
594
|
-
[RULE_IDS.actorAssertionMethodRequired]: eslint$
|
|
595
|
-
[RULE_IDS.collectionFilteringNotImplemented]: eslint
|
|
830
|
+
[RULE_IDS.actorIdMismatch]: eslint$12,
|
|
831
|
+
[RULE_IDS.actorIdRequired]: eslint$11,
|
|
832
|
+
[RULE_IDS.actorFollowingPropertyRequired]: eslint$13,
|
|
833
|
+
[RULE_IDS.actorFollowingPropertyMismatch]: eslint$14,
|
|
834
|
+
[RULE_IDS.actorFollowersPropertyRequired]: eslint$15,
|
|
835
|
+
[RULE_IDS.actorFollowersPropertyMismatch]: eslint$16,
|
|
836
|
+
[RULE_IDS.actorOutboxPropertyRequired]: eslint$5,
|
|
837
|
+
[RULE_IDS.actorOutboxPropertyMismatch]: eslint$6,
|
|
838
|
+
[RULE_IDS.actorLikedPropertyRequired]: eslint$7,
|
|
839
|
+
[RULE_IDS.actorLikedPropertyMismatch]: eslint$8,
|
|
840
|
+
[RULE_IDS.actorFeaturedPropertyRequired]: eslint$19,
|
|
841
|
+
[RULE_IDS.actorFeaturedPropertyMismatch]: eslint$20,
|
|
842
|
+
[RULE_IDS.actorFeaturedTagsPropertyRequired]: eslint$17,
|
|
843
|
+
[RULE_IDS.actorFeaturedTagsPropertyMismatch]: eslint$18,
|
|
844
|
+
[RULE_IDS.actorInboxPropertyRequired]: eslint$9,
|
|
845
|
+
[RULE_IDS.actorInboxPropertyMismatch]: eslint$10,
|
|
846
|
+
[RULE_IDS.actorSharedInboxPropertyRequired]: eslint$2,
|
|
847
|
+
[RULE_IDS.actorSharedInboxPropertyMismatch]: eslint$3,
|
|
848
|
+
[RULE_IDS.actorPublicKeyRequired]: eslint$4,
|
|
849
|
+
[RULE_IDS.actorAssertionMethodRequired]: eslint$21,
|
|
850
|
+
[RULE_IDS.collectionFilteringNotImplemented]: eslint$1,
|
|
851
|
+
[RULE_IDS.outboxListenerDeliveryRequired]: eslint
|
|
596
852
|
};
|
|
597
853
|
const recommendedRuleIds = [RULE_IDS.actorIdMismatch, RULE_IDS.actorIdRequired];
|
|
598
854
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/lint",
|
|
3
|
-
"version": "2.2.0-dev.
|
|
3
|
+
"version": "2.2.0-dev.869+97ccad7a",
|
|
4
4
|
"description": "Fedify linting rules and plugins",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Fedify",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
],
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"eslint": ">=9.0.0",
|
|
50
|
-
"@fedify/fedify": "^2.2.0-dev.
|
|
50
|
+
"@fedify/fedify": "^2.2.0-dev.869+97ccad7a"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"eslint": {
|