@n8n/eslint-plugin-community-nodes 0.27.0 → 0.28.0

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.
@@ -33,12 +33,26 @@ export class MyNode implements INodeType {
33
33
  ### ✅ Correct
34
34
 
35
35
  ```typescript
36
+ import { sleep } from 'n8n-workflow';
37
+
36
38
  export class MyNode implements INodeType {
37
39
  async execute(this: IExecuteFunctions) {
38
40
  // Use n8n context methods instead
39
41
  const timezone = this.getTimezone();
40
42
 
43
+ // Use the sleep helper instead of setTimeout
44
+ await sleep(1000);
45
+
41
46
  return this.prepareOutputData([]);
42
47
  }
43
48
  }
44
49
  ```
50
+
51
+ ## Alternatives to restricted timer globals
52
+
53
+ `n8n-workflow` exports helpers that work the same everywhere, including on
54
+ n8n Cloud, instead of reaching for the restricted timer globals directly:
55
+
56
+ - Instead of `setTimeout(resolve, ms)`, use `await sleep(ms)`.
57
+ - Instead of `setTimeout` + `clearTimeout` for a cancellable delay, use
58
+ `await sleepWithAbort(ms, abortSignal)`.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@n8n/eslint-plugin-community-nodes",
3
3
  "type": "module",
4
- "version": "0.27.0",
4
+ "version": "0.28.0",
5
5
  "main": "./dist/plugin.js",
6
6
  "types": "./dist/plugin.d.ts",
7
7
  "exports": {
@@ -27,7 +27,7 @@
27
27
  "vitest": "^4.1.9",
28
28
  "@n8n/typescript-config": "1.9.0",
29
29
  "@n8n/vitest-config": "1.19.0",
30
- "n8n-workflow": "2.33.0"
30
+ "n8n-workflow": "2.34.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "eslint": "9.29.0",
@@ -70,6 +70,17 @@ var __dirname = '/path';
70
70
  const __filename = 'file.js';
71
71
  `,
72
72
  },
73
+ {
74
+ name: 'sleep and sleepWithAbort from n8n-workflow should be allowed',
75
+ code: `
76
+ import { sleep, sleepWithAbort } from 'n8n-workflow';
77
+
78
+ async function wait(signal) {
79
+ await sleep(1000);
80
+ await sleepWithAbort(1000, signal);
81
+ }
82
+ `,
83
+ },
73
84
  ],
74
85
  invalid: [
75
86
  {
@@ -82,7 +93,12 @@ const __filename = 'file.js';
82
93
  },
83
94
  {
84
95
  code: 'setTimeout(() => {}, 1000);',
85
- errors: [{ messageId: 'restrictedGlobal', data: { name: 'setTimeout' } }],
96
+ errors: [
97
+ {
98
+ messageId: 'restrictedGlobalWithHint',
99
+ data: { name: 'setTimeout', hint: "Use the 'sleep' helper from 'n8n-workflow' instead." },
100
+ },
101
+ ],
86
102
  },
87
103
  {
88
104
  code: 'clearInterval(timer);',
@@ -90,7 +106,15 @@ const __filename = 'file.js';
90
106
  },
91
107
  {
92
108
  code: 'clearTimeout(timer);',
93
- errors: [{ messageId: 'restrictedGlobal', data: { name: 'clearTimeout' } }],
109
+ errors: [
110
+ {
111
+ messageId: 'restrictedGlobalWithHint',
112
+ data: {
113
+ name: 'clearTimeout',
114
+ hint: "Use 'sleepWithAbort' from 'n8n-workflow' with an AbortSignal instead.",
115
+ },
116
+ },
117
+ ],
94
118
  },
95
119
  {
96
120
  code: 'setInterval(() => {}, 1000);',
@@ -17,6 +17,12 @@ const restrictedGlobals = [
17
17
  '__filename',
18
18
  ];
19
19
 
20
+ // Nudge toward the n8n-workflow alternatives that work under n8n Cloud's restrictions
21
+ const restrictedGlobalHints: Record<string, string> = {
22
+ setTimeout: "Use the 'sleep' helper from 'n8n-workflow' instead.",
23
+ clearTimeout: "Use 'sleepWithAbort' from 'n8n-workflow' with an AbortSignal instead.",
24
+ };
25
+
20
26
  export const NoRestrictedGlobalsRule = createRule({
21
27
  name: 'no-restricted-globals',
22
28
  meta: {
@@ -26,6 +32,7 @@ export const NoRestrictedGlobalsRule = createRule({
26
32
  },
27
33
  messages: {
28
34
  restrictedGlobal: "Use of restricted global '{{ name }}' is not allowed",
35
+ restrictedGlobalWithHint: "Use of restricted global '{{ name }}' is not allowed. {{ hint }}",
29
36
  },
30
37
  schema: [],
31
38
  },
@@ -43,11 +50,21 @@ export const NoRestrictedGlobalsRule = createRule({
43
50
  return;
44
51
  }
45
52
 
46
- context.report({
47
- node: ref.identifier,
48
- messageId: 'restrictedGlobal',
49
- data: { name },
50
- });
53
+ const hint = restrictedGlobalHints[name];
54
+
55
+ context.report(
56
+ hint
57
+ ? {
58
+ node: ref.identifier,
59
+ messageId: 'restrictedGlobalWithHint',
60
+ data: { name, hint },
61
+ }
62
+ : {
63
+ node: ref.identifier,
64
+ messageId: 'restrictedGlobal',
65
+ data: { name },
66
+ },
67
+ );
51
68
  }
52
69
 
53
70
  return {