@forge/lint 5.16.3-next.7 → 5.17.0-next.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @forge/lint
2
2
 
3
+ ## 5.17.0-next.8
4
+
5
+ ### Minor Changes
6
+
7
+ - 594a6b9: Added new FunctionTimeoutLinter that detects a conflict where the same function is referenced by both a trigger and a consumer module, but has a timeoutSeconds exceeding the trigger maximum.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [747866d]
12
+ - @forge/manifest@12.5.0-next.6
13
+ - @forge/cli-shared@8.19.0-next.8
14
+
3
15
  ## 5.16.3-next.7
4
16
 
5
17
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/lint/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAkB,MAAM,oBAAoB,CAAC;AAexG,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,iBAAiB,WAAY,UAAU,eAAe,UAAU,EAAE,4BAAuB,IA+CrG,CAAC;AAEF,eAAO,MAAM,YAAY,gBAAiB,UAAU,EAAE,KAAG,YAQxD,CAAC;AAEF,eAAO,MAAM,eAAe,aAChB,MAAM,UACR,eAAe,oBACN,OAAO,GAAG,QAAQ,CAAC,aAAa,KAChD,QAAQ,SAAS,CAOnB,CAAC;AAEF,eAAO,MAAM,IAAI,gBACF,MAAM,EAAE,YACX,QAAQ,eACL,MAAM,UACX,UAAU,kBACF,cAAc,6BAjBpB,MAAM,UACR,eAAe,oBACN,OAAO,GAAG,QAAQ,CAAC,aAAa,KAChD,QAAQ,SAAS,CAAC,YAgBV,eAAe,EAAE,KAczB,QAAQ,UAAU,EAAE,CAyCtB,CAAC;AAwBF,eAAO,MAAM,QAAQ,WACX,UAAU,kBACF,cAAc,WACtB,eAAe,KACtB,QAAQ,UAAU,EAAE,CAGtB,CAAC"}
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/lint/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAkB,MAAM,oBAAoB,CAAC;AAgBxG,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,iBAAiB,WAAY,UAAU,eAAe,UAAU,EAAE,4BAAuB,IA+CrG,CAAC;AAEF,eAAO,MAAM,YAAY,gBAAiB,UAAU,EAAE,KAAG,YAQxD,CAAC;AAEF,eAAO,MAAM,eAAe,aAChB,MAAM,UACR,eAAe,oBACN,OAAO,GAAG,QAAQ,CAAC,aAAa,KAChD,QAAQ,SAAS,CAOnB,CAAC;AAEF,eAAO,MAAM,IAAI,gBACF,MAAM,EAAE,YACX,QAAQ,eACL,MAAM,UACX,UAAU,kBACF,cAAc,6BAjBpB,MAAM,UACR,eAAe,oBACN,OAAO,GAAG,QAAQ,CAAC,aAAa,KAChD,QAAQ,SAAS,CAAC,YAgBV,eAAe,EAAE,KAezB,QAAQ,UAAU,EAAE,CAyCtB,CAAC;AAwBF,eAAO,MAAM,QAAQ,WACX,UAAU,kBACF,cAAc,WACtB,eAAe,KACtB,QAAQ,UAAU,EAAE,CAGtB,CAAC"}
package/out/lint/lint.js CHANGED
@@ -20,6 +20,7 @@ const frame_component_linter_1 = require("./linters/frame-component-linter/frame
20
20
  const deprecated_egress_permissions_manifest_linter_1 = require("./linters/manifest-linter/deprecated-egress-permissions-manifest-linter");
21
21
  const llm_module_linter_1 = require("./linters/llm-module-linter/llm-module-linter");
22
22
  const deprecated_api_module_linter_1 = require("./linters/deprecated-api-module-linter/deprecated-api-module-linter");
23
+ const function_timeout_linter_1 = require("./linters/function-timeout-linter/function-timeout-linter");
23
24
  const reportLintResults = (logger, lintResults, showSummary = true) => {
24
25
  let numErrors = 0, numWarnings = 0;
25
26
  let noProblemsFound = true;
@@ -93,7 +94,8 @@ const lint = async (filesToLint, manifest, environment, logger, statsigService,
93
94
  new frame_component_linter_1.FrameComponentLinter(environment, manifest, logger),
94
95
  new llm_module_linter_1.LlmModuleLinter(environment, manifest, logger),
95
96
  new deprecated_api_module_linter_1.DeprecatedApiModuleLinter(environment, manifest, logger),
96
- new deprecated_egress_permissions_manifest_linter_1.DeprecatedEgressPermissionsManifestLinter(logger, statsigService)
97
+ new deprecated_egress_permissions_manifest_linter_1.DeprecatedEgressPermissionsManifestLinter(logger, statsigService),
98
+ new function_timeout_linter_1.FunctionTimeoutLinter(manifest, logger)
97
99
  ]) => {
98
100
  const { include, exclude } = await (0, cli_shared_1.listTSConfigIncludeExclude)(new cli_shared_1.FileSystemReader());
99
101
  const tsInclude = new Set(include);
@@ -0,0 +1,17 @@
1
+ import { ManifestSchema as Manifest } from '@forge/manifest';
2
+ import { AbstractLinter } from '../../abstract-linter';
3
+ import { LintLogger, LintResult } from '../../linter-interface';
4
+ export declare const LONG_RUNNING_MODULE_TYPES: Set<string>;
5
+ export declare class FunctionTimeoutLinter extends AbstractLinter {
6
+ private readonly manifest;
7
+ constructor(manifest: Manifest, logger: LintLogger);
8
+ bootstrap(): Promise<void>;
9
+ batchExecuteImpl(): Promise<LintResult[]>;
10
+ private getTimeoutLimit;
11
+ private makeError;
12
+ private getSharedViolationMessage;
13
+ private getExceededViolationMessage;
14
+ private findViolations;
15
+ private findTimeoutViolations;
16
+ }
17
+ //# sourceMappingURL=function-timeout-linter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-timeout-linter.d.ts","sourceRoot":"","sources":["../../../../src/lint/linters/function-timeout-linter/function-timeout-linter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,IAAI,QAAQ,EAA0B,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAa,UAAU,EAAE,UAAU,EAAkB,MAAM,wBAAwB,CAAC;AAO3F,eAAO,MAAM,yBAAyB,aAA4C,CAAC;AA8CnF,qBAAa,qBAAsB,SAAQ,cAAc;IAErD,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,QAAQ,EACnC,MAAM,EAAE,UAAU;IAKd,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1B,gBAAgB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAW/C,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,yBAAyB;IAOjC,OAAO,CAAC,2BAA2B;IAWnC,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,qBAAqB;CAa9B"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FunctionTimeoutLinter = exports.LONG_RUNNING_MODULE_TYPES = void 0;
4
+ const manifest_1 = require("@forge/manifest");
5
+ const abstract_linter_1 = require("../../abstract-linter");
6
+ const linter_interface_1 = require("../../linter-interface");
7
+ const text_1 = require("../../text");
8
+ exports.LONG_RUNNING_MODULE_TYPES = new Set(['consumer', 'scheduledTrigger']);
9
+ const MAX_TIMEOUT_SECONDS = 25;
10
+ const WEBTRIGGER_MAX_TIMEOUT_SECONDS = 55;
11
+ const LONG_RUNNING_MAX_TIMEOUT_SECONDS = 900;
12
+ const hasDirectFunction = (m) => typeof m.key === 'string' && typeof m.function === 'string';
13
+ const hasResolverFunction = (m) => typeof m.key === 'string' &&
14
+ typeof m.resolver === 'object' &&
15
+ m.resolver !== null &&
16
+ 'function' in m.resolver &&
17
+ typeof m.resolver.function === 'string';
18
+ function collectAllFunctionRefs(modules) {
19
+ const refs = new Map();
20
+ const addRef = (functionKey, moduleKey, moduleType) => {
21
+ const existing = refs.get(functionKey) ?? [];
22
+ existing.push({ moduleKey, moduleType });
23
+ refs.set(functionKey, existing);
24
+ };
25
+ for (const [moduleType, moduleArray] of Object.entries(modules)) {
26
+ if (!Array.isArray(moduleArray))
27
+ continue;
28
+ for (const m of moduleArray) {
29
+ if (hasDirectFunction(m))
30
+ addRef(m.function, m.key, moduleType);
31
+ if (hasResolverFunction(m))
32
+ addRef(m.resolver.function, m.key, moduleType);
33
+ }
34
+ }
35
+ return refs;
36
+ }
37
+ class FunctionTimeoutLinter extends abstract_linter_1.AbstractLinter {
38
+ manifest;
39
+ constructor(manifest, logger) {
40
+ super(logger);
41
+ this.manifest = manifest;
42
+ }
43
+ async bootstrap() {
44
+ }
45
+ async batchExecuteImpl() {
46
+ const lintResult = new linter_interface_1.LintResult(manifest_1.MANIFEST_FILE);
47
+ const modules = this.manifest.modules;
48
+ if (modules) {
49
+ lintResult.batchAdd(...this.findTimeoutViolations(modules));
50
+ }
51
+ return [lintResult];
52
+ }
53
+ getTimeoutLimit(ref) {
54
+ if (exports.LONG_RUNNING_MODULE_TYPES.has(ref.moduleType))
55
+ return LONG_RUNNING_MAX_TIMEOUT_SECONDS;
56
+ if (ref.moduleType === 'webtrigger')
57
+ return WEBTRIGGER_MAX_TIMEOUT_SECONDS;
58
+ return MAX_TIMEOUT_SECONDS;
59
+ }
60
+ makeError(msg) {
61
+ return { class: linter_interface_1.LintClass.Error, ...msg, line: 0, column: 0 };
62
+ }
63
+ getSharedViolationMessage(functionKey, ref) {
64
+ return {
65
+ message: text_1.messages.verifiers.functionTimeout.shared.message(functionKey, ref.moduleKey, ref.moduleType),
66
+ reference: text_1.messages.verifiers.functionTimeout.shared.reference
67
+ };
68
+ }
69
+ getExceededViolationMessage(functionKey, timeout, ref) {
70
+ return {
71
+ message: text_1.messages.verifiers.functionTimeout.exceeded.message(functionKey, timeout, ref.moduleKey, ref.moduleType),
72
+ reference: text_1.messages.verifiers.functionTimeout.exceeded.reference
73
+ };
74
+ }
75
+ findViolations(functionKey, timeout, refs) {
76
+ const consumerShared = refs.some((r) => exports.LONG_RUNNING_MODULE_TYPES.has(r.moduleType));
77
+ return refs
78
+ .filter((r) => {
79
+ const limit = this.getTimeoutLimit(r);
80
+ if (limit === LONG_RUNNING_MAX_TIMEOUT_SECONDS)
81
+ return false;
82
+ if (!consumerShared)
83
+ return true;
84
+ return timeout > limit;
85
+ })
86
+ .map((r) => this.makeError(consumerShared
87
+ ? this.getSharedViolationMessage(functionKey, r)
88
+ : this.getExceededViolationMessage(functionKey, timeout, r)));
89
+ }
90
+ findTimeoutViolations(modules) {
91
+ const allFunctionRefs = collectAllFunctionRefs(modules);
92
+ const functionTimeouts = new Map((modules.function ?? []).map((f) => [f.key, f.timeoutSeconds]));
93
+ const errors = [];
94
+ for (const [functionKey, refs] of allFunctionRefs) {
95
+ const timeout = functionTimeouts.get(functionKey);
96
+ if (timeout === undefined)
97
+ continue;
98
+ errors.push(...this.findViolations(functionKey, timeout, refs));
99
+ }
100
+ return errors;
101
+ }
102
+ }
103
+ exports.FunctionTimeoutLinter = FunctionTimeoutLinter;
@@ -69,6 +69,16 @@ export declare const messages: {
69
69
  message: () => string;
70
70
  reference: string;
71
71
  };
72
+ functionTimeout: {
73
+ shared: {
74
+ message: (functionKey: string, conflictingModuleKey: string, conflictingModuleType: string) => string;
75
+ reference: string;
76
+ };
77
+ exceeded: {
78
+ message: (functionKey: string, timeoutSeconds: number, moduleKey: string, moduleType: string) => string;
79
+ reference: string;
80
+ };
81
+ };
72
82
  };
73
83
  };
74
84
  //# sourceMappingURL=messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/lint/text/messages.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ;;;2BAGA,MAAM;2BAEN,MAAM,KAAG,MAAM;;;;2BAKf,MAAM;2BAER,MAAM;;;;2BAKJ,MAAM;2BAEN,MAAM,KAAG,MAAM;;;;iCAKT,MAAM;;;;iCAKN,MAAM;;;;8BAKT,MAAM,OAAO,MAAM;;;;+BAKlB,MAAM,UAAU,MAAM,QAAQ,MAAM,GAAG,SAAS,SAAS,MAAM;;;;6BAKjE,MAAM;;;;+BAIJ,MAAM,QAAQ,MAAM,SAAS,MAAM;;;;8BAKpC,MAAM;;;;8BAIN,MAAM;;;;8BAIN,MAAM;;;;;;;;;oCASF,MAAM;;;;;6BAOX,MAAM,WAAW,MAAM;;;;;;;;CAU7C,CAAC"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/lint/text/messages.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;2BAGA,MAAM;2BAEN,MAAM,KAAG,MAAM;;;;2BAKf,MAAM;2BAER,MAAM;;;;2BAKJ,MAAM;2BAEN,MAAM,KAAG,MAAM;;;;iCAKT,MAAM;;;;iCAKN,MAAM;;;;8BAKT,MAAM,OAAO,MAAM;;;;+BAKlB,MAAM,UAAU,MAAM,QAAQ,MAAM,GAAG,SAAS,SAAS,MAAM;;;;6BAKjE,MAAM;;;;+BAIJ,MAAM,QAAQ,MAAM,SAAS,MAAM;;;;8BAKpC,MAAM;;;;8BAIN,MAAM;;;;8BAIN,MAAM;;;;;;;;;oCASF,MAAM;;;;;6BAOX,MAAM,WAAW,MAAM;;;;;;;;;uCAWf,MAAM,wBAAwB,MAAM,yBAAyB,MAAM;;;;uCAKnE,MAAM,kBAAkB,MAAM,aAAa,MAAM,cAAc,MAAM;;;;;CAMnG,CAAC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.messages = void 0;
4
+ const function_timeout_linter_1 = require("../linters/function-timeout-linter/function-timeout-linter");
4
5
  exports.messages = {
5
6
  verifiers: {
6
7
  externalFetch: {
@@ -73,6 +74,16 @@ exports.messages = {
73
74
  deprecatedApiStorage: {
74
75
  message: () => `The \"storage\" export from \"@forge/api\" is deprecated. Use the \"@forge/kvs\" package instead.`,
75
76
  reference: 'deprecated-api-storage'
77
+ },
78
+ functionTimeout: {
79
+ shared: {
80
+ message: (functionKey, conflictingModuleKey, conflictingModuleType) => `Function "${functionKey}" is used by a consumer and ${conflictingModuleType} "${conflictingModuleKey}". Consumer functions must not be shared with other modules. Use a separate function for each module type.`,
81
+ reference: 'https://developer.atlassian.com/platform/forge/use-a-long-running-function/'
82
+ },
83
+ exceeded: {
84
+ message: (functionKey, timeoutSeconds, moduleKey, moduleType) => `Function "${functionKey}" defines timeoutSeconds (${timeoutSeconds}s) but is used by ${moduleType} "${moduleKey}". Only ${[...function_timeout_linter_1.LONG_RUNNING_MODULE_TYPES].join(' and ')} functions support custom timeouts.`,
85
+ reference: 'https://developer.atlassian.com/platform/forge/limits-invocation/'
86
+ }
76
87
  }
77
88
  }
78
89
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/lint",
3
- "version": "5.16.3-next.7",
3
+ "version": "5.17.0-next.8",
4
4
  "description": "Linting for forge apps",
5
5
  "main": "out/index.js",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",
@@ -19,10 +19,10 @@
19
19
  "eslint-plugin-import": "^2.29.1"
20
20
  },
21
21
  "dependencies": {
22
- "@forge/cli-shared": "8.19.0-next.7",
22
+ "@forge/cli-shared": "8.19.0-next.8",
23
23
  "@forge/csp": "5.6.1",
24
24
  "@forge/egress": "2.3.2",
25
- "@forge/manifest": "12.5.0-next.5",
25
+ "@forge/manifest": "12.5.0-next.6",
26
26
  "@typescript-eslint/typescript-estree": "^5.62.0",
27
27
  "array.prototype.flatmap": "^1.3.3",
28
28
  "@atlassian/atlassian-openapi": "^1.0.6",