@forge/lint

5.16.3-next.75.17.0-next.8
out/lint/linters/function-timeout-linter/function-timeout-linter.js
+out/lint/linters/function-timeout-linter/function-timeout-linter.jsNew file
+103
Index: package/out/lint/linters/function-timeout-linter/function-timeout-linter.js
===================================================================
--- package/out/lint/linters/function-timeout-linter/function-timeout-linter.js
+++ package/out/lint/linters/function-timeout-linter/function-timeout-linter.js
@@ -0,0 +1,103 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.FunctionTimeoutLinter = exports.LONG_RUNNING_MODULE_TYPES = void 0;
+const manifest_1 = require("@forge/manifest");
+const abstract_linter_1 = require("../../abstract-linter");
+const linter_interface_1 = require("../../linter-interface");
+const text_1 = require("../../text");
+exports.LONG_RUNNING_MODULE_TYPES = new Set(['consumer', 'scheduledTrigger']);
+const MAX_TIMEOUT_SECONDS = 25;
+const WEBTRIGGER_MAX_TIMEOUT_SECONDS = 55;
+const LONG_RUNNING_MAX_TIMEOUT_SECONDS = 900;
+const hasDirectFunction = (m) => typeof m.key === 'string' && typeof m.function === 'string';
+const hasResolverFunction = (m) => typeof m.key === 'string' &&
+    typeof m.resolver === 'object' &&
+    m.resolver !== null &&
+    'function' in m.resolver &&
+    typeof m.resolver.function === 'string';
+function collectAllFunctionRefs(modules) {
+    const refs = new Map();
+    const addRef = (functionKey, moduleKey, moduleType) => {
+        const existing = refs.get(functionKey) ?? [];
+        existing.push({ moduleKey, moduleType });
+        refs.set(functionKey, existing);
+    };
+    for (const [moduleType, moduleArray] of Object.entries(modules)) {
+        if (!Array.isArray(moduleArray))
+            continue;
+        for (const m of moduleArray) {
+            if (hasDirectFunction(m))
+                addRef(m.function, m.key, moduleType);
+            if (hasResolverFunction(m))
+                addRef(m.resolver.function, m.key, moduleType);
+        }
+    }
+    return refs;
+}
+class FunctionTimeoutLinter extends abstract_linter_1.AbstractLinter {
+    manifest;
+    constructor(manifest, logger) {
+        super(logger);
+        this.manifest = manifest;
+    }
+    async bootstrap() {
+    }
+    async batchExecuteImpl() {
+        const lintResult = new linter_interface_1.LintResult(manifest_1.MANIFEST_FILE);
+        const modules = this.manifest.modules;
+        if (modules) {
+            lintResult.batchAdd(...this.findTimeoutViolations(modules));
+        }
+        return [lintResult];
+    }
+    getTimeoutLimit(ref) {
+        if (exports.LONG_RUNNING_MODULE_TYPES.has(ref.moduleType))
+            return LONG_RUNNING_MAX_TIMEOUT_SECONDS;
+        if (ref.moduleType === 'webtrigger')
+            return WEBTRIGGER_MAX_TIMEOUT_SECONDS;
+        return MAX_TIMEOUT_SECONDS;
+    }
+    makeError(msg) {
+        return { class: linter_interface_1.LintClass.Error, ...msg, line: 0, column: 0 };
+    }
+    getSharedViolationMessage(functionKey, ref) {
+        return {
+            message: text_1.messages.verifiers.functionTimeout.shared.message(functionKey, ref.moduleKey, ref.moduleType),
+            reference: text_1.messages.verifiers.functionTimeout.shared.reference
+        };
+    }
+    getExceededViolationMessage(functionKey, timeout, ref) {
+        return {
+            message: text_1.messages.verifiers.functionTimeout.exceeded.message(functionKey, timeout, ref.moduleKey, ref.moduleType),
+            reference: text_1.messages.verifiers.functionTimeout.exceeded.reference
+        };
+    }
+    findViolations(functionKey, timeout, refs) {
+        const consumerShared = refs.some((r) => exports.LONG_RUNNING_MODULE_TYPES.has(r.moduleType));
+        return refs
+            .filter((r) => {
+            const limit = this.getTimeoutLimit(r);
+            if (limit === LONG_RUNNING_MAX_TIMEOUT_SECONDS)
+                return false;
+            if (!consumerShared)
+                return true;
+            return timeout > limit;
+        })
+            .map((r) => this.makeError(consumerShared
+            ? this.getSharedViolationMessage(functionKey, r)
+            : this.getExceededViolationMessage(functionKey, timeout, r)));
+    }
+    findTimeoutViolations(modules) {
+        const allFunctionRefs = collectAllFunctionRefs(modules);
+        const functionTimeouts = new Map((modules.function ?? []).map((f) => [f.key, f.timeoutSeconds]));
+        const errors = [];
+        for (const [functionKey, refs] of allFunctionRefs) {
+            const timeout = functionTimeouts.get(functionKey);
+            if (timeout === undefined)
+                continue;
+            errors.push(...this.findViolations(functionKey, timeout, refs));
+        }
+        return errors;
+    }
+}
+exports.FunctionTimeoutLinter = FunctionTimeoutLinter;