@n8n/eslint-plugin-community-nodes 0.10.0 → 0.11.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @n8n/eslint-plugin-community-nodes@0.10.0 build /home/runner/work/n8n/n8n/packages/@n8n/eslint-plugin-community-nodes
2
+ > @n8n/eslint-plugin-community-nodes@0.11.0 build /home/runner/work/n8n/n8n/packages/@n8n/eslint-plugin-community-nodes
3
3
  > tsc --project tsconfig.build.json
4
4
 
package/README.md CHANGED
@@ -43,22 +43,24 @@ export default [
43
43
  🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
44
44
  💡 Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
45
45
 
46
- | Name                             | Description | 💼 | ⚠️ | 🔧 | 💡 |
47
- | :--------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | :--- | :--- | :- | :- |
48
- | [ai-node-package-json](docs/rules/ai-node-package-json.md) | Enforce consistency between n8n.aiNodeSdkVersion and ai-node-sdk peer dependency in community node packages | ✅ ☑️ | | | |
49
- | [cred-class-field-icon-missing](docs/rules/cred-class-field-icon-missing.md) | Credential class must have an `icon` property defined | ✅ ☑️ | | | 💡 |
50
- | [credential-documentation-url](docs/rules/credential-documentation-url.md) | Enforce valid credential documentationUrl format (URL or lowercase alphanumeric slug) | ✅ ☑️ | | 🔧 | |
51
- | [credential-password-field](docs/rules/credential-password-field.md) | Ensure credential fields with sensitive names have typeOptions.password = true | ✅ ☑️ | | 🔧 | |
52
- | [credential-test-required](docs/rules/credential-test-required.md) | Ensure credentials have a credential test | ✅ ☑️ | | | 💡 |
53
- | [icon-validation](docs/rules/icon-validation.md) | Validate node and credential icon files exist, are SVG format, and light/dark icons are different | ✅ ☑️ | | | 💡 |
54
- | [no-credential-reuse](docs/rules/no-credential-reuse.md) | Prevent credential re-use security issues by ensuring nodes only reference credentials from the same package | ✅ ☑️ | | | 💡 |
55
- | [no-deprecated-workflow-functions](docs/rules/no-deprecated-workflow-functions.md) | Disallow usage of deprecated functions and types from n8n-workflow package | ✅ ☑️ | | | 💡 |
56
- | [no-http-request-with-manual-auth](docs/rules/no-http-request-with-manual-auth.md) | Disallow this.helpers.httpRequest() in functions that call this.getCredentials(). Use this.helpers.httpRequestWithAuthentication() instead. | ✅ ☑️ | | | |
57
- | [no-restricted-globals](docs/rules/no-restricted-globals.md) | Disallow usage of restricted global variables in community nodes. | ✅ | | | |
58
- | [no-restricted-imports](docs/rules/no-restricted-imports.md) | Disallow usage of restricted imports in community nodes. | ✅ | | | |
59
- | [node-class-description-icon-missing](docs/rules/node-class-description-icon-missing.md) | Node class description must have an `icon` property defined | ✅ ☑️ | | | 💡 |
60
- | [node-usable-as-tool](docs/rules/node-usable-as-tool.md) | Ensure node classes have usableAsTool property | ✅ ☑️ | | 🔧 | |
61
- | [package-name-convention](docs/rules/package-name-convention.md) | Enforce correct package naming convention for n8n community nodes | ✅ ☑️ | | | 💡 |
62
- | [resource-operation-pattern](docs/rules/resource-operation-pattern.md) | Enforce proper resource/operation pattern for better UX in n8n nodes | | ✅ ☑️ | | |
46
+ | Name                                | Description | 💼 | ⚠️ | 🔧 | 💡 |
47
+ | :--------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | :--- | :--- | :- | :- |
48
+ | [ai-node-package-json](docs/rules/ai-node-package-json.md) | Enforce consistency between n8n.aiNodeSdkVersion and ai-node-sdk peer dependency in community node packages | ✅ ☑️ | | | |
49
+ | [cred-class-field-icon-missing](docs/rules/cred-class-field-icon-missing.md) | Credential class must have an `icon` property defined | ✅ ☑️ | | | 💡 |
50
+ | [credential-documentation-url](docs/rules/credential-documentation-url.md) | Enforce valid credential documentationUrl format (URL or lowercase alphanumeric slug) | ✅ ☑️ | | 🔧 | |
51
+ | [credential-password-field](docs/rules/credential-password-field.md) | Ensure credential fields with sensitive names have typeOptions.password = true | ✅ ☑️ | | 🔧 | |
52
+ | [credential-test-required](docs/rules/credential-test-required.md) | Ensure credentials have a credential test | ✅ ☑️ | | | 💡 |
53
+ | [icon-validation](docs/rules/icon-validation.md) | Validate node and credential icon files exist, are SVG format, and light/dark icons are different | ✅ ☑️ | | | 💡 |
54
+ | [no-credential-reuse](docs/rules/no-credential-reuse.md) | Prevent credential re-use security issues by ensuring nodes only reference credentials from the same package | ✅ ☑️ | | | 💡 |
55
+ | [no-deprecated-workflow-functions](docs/rules/no-deprecated-workflow-functions.md) | Disallow usage of deprecated functions and types from n8n-workflow package | ✅ ☑️ | | | 💡 |
56
+ | [no-http-request-with-manual-auth](docs/rules/no-http-request-with-manual-auth.md) | Disallow this.helpers.httpRequest() in functions that call this.getCredentials(). Use this.helpers.httpRequestWithAuthentication() instead. | ✅ ☑️ | | | |
57
+ | [no-restricted-globals](docs/rules/no-restricted-globals.md) | Disallow usage of restricted global variables in community nodes. | ✅ | | | |
58
+ | [no-restricted-imports](docs/rules/no-restricted-imports.md) | Disallow usage of restricted imports in community nodes. | ✅ | | | |
59
+ | [node-class-description-icon-missing](docs/rules/node-class-description-icon-missing.md) | Node class description must have an `icon` property defined | ✅ ☑️ | | | 💡 |
60
+ | [node-connection-type-literal](docs/rules/node-connection-type-literal.md) | Disallow string literals in node description `inputs`/`outputs` use `NodeConnectionTypes` enum instead | ✅ ☑️ | | 🔧 | |
61
+ | [node-usable-as-tool](docs/rules/node-usable-as-tool.md) | Ensure node classes have usableAsTool property | ✅ ☑️ | | 🔧 | |
62
+ | [options-sorted-alphabetically](docs/rules/options-sorted-alphabetically.md) | Enforce alphabetical ordering of options arrays in n8n node properties | | ✅ ☑️ | | |
63
+ | [package-name-convention](docs/rules/package-name-convention.md) | Enforce correct package naming convention for n8n community nodes | ✅ ☑️ | | | 💡 |
64
+ | [resource-operation-pattern](docs/rules/resource-operation-pattern.md) | Enforce proper resource/operation pattern for better UX in n8n nodes | | ✅ ☑️ | | |
63
65
 
64
66
  <!-- end auto-generated rules list -->
package/dist/plugin.d.ts CHANGED
@@ -25,10 +25,12 @@ declare const configs: {
25
25
  '@n8n/community-nodes/no-credential-reuse': "error";
26
26
  '@n8n/community-nodes/no-http-request-with-manual-auth': "error";
27
27
  '@n8n/community-nodes/icon-validation': "error";
28
+ '@n8n/community-nodes/options-sorted-alphabetically': "warn";
28
29
  '@n8n/community-nodes/resource-operation-pattern': "warn";
29
30
  '@n8n/community-nodes/credential-documentation-url': "error";
30
31
  '@n8n/community-nodes/node-class-description-icon-missing': "error";
31
32
  '@n8n/community-nodes/cred-class-field-icon-missing': "error";
33
+ '@n8n/community-nodes/node-connection-type-literal': "error";
32
34
  };
33
35
  };
34
36
  recommendedWithoutN8nCloudSupport: {
@@ -53,10 +55,12 @@ declare const configs: {
53
55
  '@n8n/community-nodes/no-credential-reuse': "error";
54
56
  '@n8n/community-nodes/no-http-request-with-manual-auth': "error";
55
57
  '@n8n/community-nodes/icon-validation': "error";
58
+ '@n8n/community-nodes/options-sorted-alphabetically': "warn";
56
59
  '@n8n/community-nodes/credential-documentation-url': "error";
57
60
  '@n8n/community-nodes/resource-operation-pattern': "warn";
58
61
  '@n8n/community-nodes/node-class-description-icon-missing': "error";
59
62
  '@n8n/community-nodes/cred-class-field-icon-missing': "error";
63
+ '@n8n/community-nodes/node-connection-type-literal': "error";
60
64
  };
61
65
  };
62
66
  };
@@ -86,10 +90,12 @@ declare const pluginWithConfigs: {
86
90
  '@n8n/community-nodes/no-credential-reuse': "error";
87
91
  '@n8n/community-nodes/no-http-request-with-manual-auth': "error";
88
92
  '@n8n/community-nodes/icon-validation': "error";
93
+ '@n8n/community-nodes/options-sorted-alphabetically': "warn";
89
94
  '@n8n/community-nodes/resource-operation-pattern': "warn";
90
95
  '@n8n/community-nodes/credential-documentation-url': "error";
91
96
  '@n8n/community-nodes/node-class-description-icon-missing': "error";
92
97
  '@n8n/community-nodes/cred-class-field-icon-missing': "error";
98
+ '@n8n/community-nodes/node-connection-type-literal': "error";
93
99
  };
94
100
  };
95
101
  recommendedWithoutN8nCloudSupport: {
@@ -114,10 +120,12 @@ declare const pluginWithConfigs: {
114
120
  '@n8n/community-nodes/no-credential-reuse': "error";
115
121
  '@n8n/community-nodes/no-http-request-with-manual-auth': "error";
116
122
  '@n8n/community-nodes/icon-validation': "error";
123
+ '@n8n/community-nodes/options-sorted-alphabetically': "warn";
117
124
  '@n8n/community-nodes/credential-documentation-url': "error";
118
125
  '@n8n/community-nodes/resource-operation-pattern': "warn";
119
126
  '@n8n/community-nodes/node-class-description-icon-missing': "error";
120
127
  '@n8n/community-nodes/cred-class-field-icon-missing': "error";
128
+ '@n8n/community-nodes/node-connection-type-literal': "error";
121
129
  };
122
130
  };
123
131
  };
@@ -154,10 +162,12 @@ declare const n8nCommunityNodesPlugin: {
154
162
  '@n8n/community-nodes/no-credential-reuse': "error";
155
163
  '@n8n/community-nodes/no-http-request-with-manual-auth': "error";
156
164
  '@n8n/community-nodes/icon-validation': "error";
165
+ '@n8n/community-nodes/options-sorted-alphabetically': "warn";
157
166
  '@n8n/community-nodes/resource-operation-pattern': "warn";
158
167
  '@n8n/community-nodes/credential-documentation-url': "error";
159
168
  '@n8n/community-nodes/node-class-description-icon-missing': "error";
160
169
  '@n8n/community-nodes/cred-class-field-icon-missing': "error";
170
+ '@n8n/community-nodes/node-connection-type-literal': "error";
161
171
  };
162
172
  };
163
173
  recommendedWithoutN8nCloudSupport: {
@@ -182,10 +192,12 @@ declare const n8nCommunityNodesPlugin: {
182
192
  '@n8n/community-nodes/no-credential-reuse': "error";
183
193
  '@n8n/community-nodes/no-http-request-with-manual-auth': "error";
184
194
  '@n8n/community-nodes/icon-validation': "error";
195
+ '@n8n/community-nodes/options-sorted-alphabetically': "warn";
185
196
  '@n8n/community-nodes/credential-documentation-url': "error";
186
197
  '@n8n/community-nodes/resource-operation-pattern': "warn";
187
198
  '@n8n/community-nodes/node-class-description-icon-missing': "error";
188
199
  '@n8n/community-nodes/cred-class-field-icon-missing': "error";
200
+ '@n8n/community-nodes/node-connection-type-literal': "error";
189
201
  };
190
202
  };
191
203
  };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAG7C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAYzC,QAAA,MAAM,OAAO;;;;;;;;;;uBAHI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;CAgDE,CAAC;AAE1C,QAAA,MAAM,iBAAiB;;;;;;;;;;;2BAlDN,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;WAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;CAkDiC,CAAC;AAEzE,QAAA,MAAM,uBAAuB;;;;;;;;;;;2BApDZ,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;WAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;CAoDU,CAAC;AAClD,eAAe,iBAAiB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAG7C,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAYzC,QAAA,MAAM,OAAO;;;;;;;;;;uBAHI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;CAoDE,CAAC;AAE1C,QAAA,MAAM,iBAAiB;;;;;;;;;;;2BAtDN,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;WAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;CAsDiC,CAAC;AAEzE,QAAA,MAAM,uBAAuB;;;;;;;;;;;2BAxDZ,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;WAAtB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;CAwDU,CAAC;AAClD,eAAe,iBAAiB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
package/dist/plugin.js CHANGED
@@ -27,10 +27,12 @@ const configs = {
27
27
  '@n8n/community-nodes/no-credential-reuse': 'error',
28
28
  '@n8n/community-nodes/no-http-request-with-manual-auth': 'error',
29
29
  '@n8n/community-nodes/icon-validation': 'error',
30
+ '@n8n/community-nodes/options-sorted-alphabetically': 'warn',
30
31
  '@n8n/community-nodes/resource-operation-pattern': 'warn',
31
32
  '@n8n/community-nodes/credential-documentation-url': 'error',
32
33
  '@n8n/community-nodes/node-class-description-icon-missing': 'error',
33
34
  '@n8n/community-nodes/cred-class-field-icon-missing': 'error',
35
+ '@n8n/community-nodes/node-connection-type-literal': 'error',
34
36
  },
35
37
  },
36
38
  recommendedWithoutN8nCloudSupport: {
@@ -48,10 +50,12 @@ const configs = {
48
50
  '@n8n/community-nodes/no-credential-reuse': 'error',
49
51
  '@n8n/community-nodes/no-http-request-with-manual-auth': 'error',
50
52
  '@n8n/community-nodes/icon-validation': 'error',
53
+ '@n8n/community-nodes/options-sorted-alphabetically': 'warn',
51
54
  '@n8n/community-nodes/credential-documentation-url': 'error',
52
55
  '@n8n/community-nodes/resource-operation-pattern': 'warn',
53
56
  '@n8n/community-nodes/node-class-description-icon-missing': 'error',
54
57
  '@n8n/community-nodes/cred-class-field-icon-missing': 'error',
58
+ '@n8n/community-nodes/node-connection-type-literal': 'error',
55
59
  },
56
60
  },
57
61
  };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,MAAM,GAAG;IACd,IAAI,EAAE;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,SAAS,EAAE,sBAAsB;KACjC;IACD,8EAA8E;IAC9E,KAAK,EAAE,KAA+B;CACd,CAAC;AAE1B,MAAM,OAAO,GAAG;IACf,WAAW,EAAE;QACZ,OAAO,EAAE,CAAC,+BAA+B,CAAC;QAC1C,OAAO,EAAE;YACR,sBAAsB,EAAE,MAAM;SAC9B;QACD,KAAK,EAAE;YACN,2CAA2C,EAAE,OAAO;YACpD,4CAA4C,EAAE,OAAO;YACrD,4CAA4C,EAAE,OAAO;YACrD,gDAAgD,EAAE,OAAO;YACzD,uDAAuD,EAAE,OAAO;YAChE,0CAA0C,EAAE,OAAO;YACnD,8CAA8C,EAAE,OAAO;YACvD,+CAA+C,EAAE,OAAO;YACxD,0CAA0C,EAAE,OAAO;YACnD,uDAAuD,EAAE,OAAO;YAChE,sCAAsC,EAAE,OAAO;YAC/C,iDAAiD,EAAE,MAAM;YACzD,mDAAmD,EAAE,OAAO;YAC5D,0DAA0D,EAAE,OAAO;YACnE,oDAAoD,EAAE,OAAO;SAC7D;KACD;IACD,iCAAiC,EAAE;QAClC,OAAO,EAAE,CAAC,+BAA+B,CAAC;QAC1C,OAAO,EAAE;YACR,sBAAsB,EAAE,MAAM;SAC9B;QACD,KAAK,EAAE;YACN,2CAA2C,EAAE,OAAO;YACpD,gDAAgD,EAAE,OAAO;YACzD,uDAAuD,EAAE,OAAO;YAChE,0CAA0C,EAAE,OAAO;YACnD,8CAA8C,EAAE,OAAO;YACvD,+CAA+C,EAAE,OAAO;YACxD,0CAA0C,EAAE,OAAO;YACnD,uDAAuD,EAAE,OAAO;YAChE,sCAAsC,EAAE,OAAO;YAC/C,mDAAmD,EAAE,OAAO;YAC5D,iDAAiD,EAAE,MAAM;YACzD,0DAA0D,EAAE,OAAO;YACnE,oDAAoD,EAAE,OAAO;SAC7D;KACD;CACuC,CAAC;AAE1C,MAAM,iBAAiB,GAAG,EAAE,GAAG,MAAM,EAAE,OAAO,EAA0B,CAAC;AAEzE,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAClD,eAAe,iBAAiB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,MAAM,GAAG;IACd,IAAI,EAAE;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,SAAS,EAAE,sBAAsB;KACjC;IACD,8EAA8E;IAC9E,KAAK,EAAE,KAA+B;CACd,CAAC;AAE1B,MAAM,OAAO,GAAG;IACf,WAAW,EAAE;QACZ,OAAO,EAAE,CAAC,+BAA+B,CAAC;QAC1C,OAAO,EAAE;YACR,sBAAsB,EAAE,MAAM;SAC9B;QACD,KAAK,EAAE;YACN,2CAA2C,EAAE,OAAO;YACpD,4CAA4C,EAAE,OAAO;YACrD,4CAA4C,EAAE,OAAO;YACrD,gDAAgD,EAAE,OAAO;YACzD,uDAAuD,EAAE,OAAO;YAChE,0CAA0C,EAAE,OAAO;YACnD,8CAA8C,EAAE,OAAO;YACvD,+CAA+C,EAAE,OAAO;YACxD,0CAA0C,EAAE,OAAO;YACnD,uDAAuD,EAAE,OAAO;YAChE,sCAAsC,EAAE,OAAO;YAC/C,oDAAoD,EAAE,MAAM;YAC5D,iDAAiD,EAAE,MAAM;YACzD,mDAAmD,EAAE,OAAO;YAC5D,0DAA0D,EAAE,OAAO;YACnE,oDAAoD,EAAE,OAAO;YAC7D,mDAAmD,EAAE,OAAO;SAC5D;KACD;IACD,iCAAiC,EAAE;QAClC,OAAO,EAAE,CAAC,+BAA+B,CAAC;QAC1C,OAAO,EAAE;YACR,sBAAsB,EAAE,MAAM;SAC9B;QACD,KAAK,EAAE;YACN,2CAA2C,EAAE,OAAO;YACpD,gDAAgD,EAAE,OAAO;YACzD,uDAAuD,EAAE,OAAO;YAChE,0CAA0C,EAAE,OAAO;YACnD,8CAA8C,EAAE,OAAO;YACvD,+CAA+C,EAAE,OAAO;YACxD,0CAA0C,EAAE,OAAO;YACnD,uDAAuD,EAAE,OAAO;YAChE,sCAAsC,EAAE,OAAO;YAC/C,oDAAoD,EAAE,MAAM;YAC5D,mDAAmD,EAAE,OAAO;YAC5D,iDAAiD,EAAE,MAAM;YACzD,0DAA0D,EAAE,OAAO;YACnE,oDAAoD,EAAE,OAAO;YAC7D,mDAAmD,EAAE,OAAO;SAC5D;KACD;CACuC,CAAC;AAE1C,MAAM,iBAAiB,GAAG,EAAE,GAAG,MAAM,EAAE,OAAO,EAA0B,CAAC;AAEzE,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAClD,eAAe,iBAAiB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
@@ -5,6 +5,7 @@ export declare const rules: {
5
5
  'credential-password-field': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingPasswordOption", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
6
6
  'no-deprecated-workflow-functions': import("@typescript-eslint/utils/ts-eslint").RuleModule<"deprecatedRequestFunction" | "deprecatedFunction" | "deprecatedType" | "deprecatedWithoutReplacement" | "suggestReplaceFunction" | "suggestReplaceType", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
7
7
  'node-usable-as-tool': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingUsableAsTool", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
8
+ 'options-sorted-alphabetically': import("@typescript-eslint/utils/ts-eslint").RuleModule<"optionsNotSorted", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
8
9
  'package-name-convention': import("@typescript-eslint/utils/ts-eslint").RuleModule<"renameTo" | "invalidPackageName", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
9
10
  'credential-test-required': import("@typescript-eslint/utils/ts-eslint").RuleModule<"addTemplate" | "missingCredentialTest", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
10
11
  'no-credential-reuse': import("@typescript-eslint/utils/ts-eslint").RuleModule<"didYouMean" | "useAvailable" | "credentialNotInPackage", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
@@ -17,5 +18,6 @@ export declare const rules: {
17
18
  }], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
18
19
  'node-class-description-icon-missing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingIcon" | "addPlaceholder", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
19
20
  'cred-class-field-icon-missing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingIcon" | "addPlaceholder", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
21
+ 'node-connection-type-literal': import("@typescript-eslint/utils/ts-eslint").RuleModule<"stringLiteralInInputs" | "stringLiteralInOutputs" | "unknownStringLiteralInInputs" | "unknownStringLiteralInOutputs", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
20
22
  };
21
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;CAgBuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;CAkBuB,CAAC"}
@@ -10,7 +10,9 @@ import { NoHttpRequestWithManualAuthRule } from './no-http-request-with-manual-a
10
10
  import { NoRestrictedGlobalsRule } from './no-restricted-globals.js';
11
11
  import { NoRestrictedImportsRule } from './no-restricted-imports.js';
12
12
  import { NodeClassDescriptionIconMissingRule } from './node-class-description-icon-missing.js';
13
+ import { NodeConnectionTypeLiteralRule } from './node-connection-type-literal.js';
13
14
  import { NodeUsableAsToolRule } from './node-usable-as-tool.js';
15
+ import { OptionsSortedAlphabeticallyRule } from './options-sorted-alphabetically.js';
14
16
  import { PackageNameConventionRule } from './package-name-convention.js';
15
17
  import { ResourceOperationPatternRule } from './resource-operation-pattern.js';
16
18
  export const rules = {
@@ -20,6 +22,7 @@ export const rules = {
20
22
  'credential-password-field': CredentialPasswordFieldRule,
21
23
  'no-deprecated-workflow-functions': NoDeprecatedWorkflowFunctionsRule,
22
24
  'node-usable-as-tool': NodeUsableAsToolRule,
25
+ 'options-sorted-alphabetically': OptionsSortedAlphabeticallyRule,
23
26
  'package-name-convention': PackageNameConventionRule,
24
27
  'credential-test-required': CredentialTestRequiredRule,
25
28
  'no-credential-reuse': NoCredentialReuseRule,
@@ -29,5 +32,6 @@ export const rules = {
29
32
  'credential-documentation-url': CredentialDocumentationUrlRule,
30
33
  'node-class-description-icon-missing': NodeClassDescriptionIconMissingRule,
31
34
  'cred-class-field-icon-missing': CredClassFieldIconMissingRule,
35
+ 'node-connection-type-literal': NodeConnectionTypeLiteralRule,
32
36
  };
33
37
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,mCAAmC,EAAE,MAAM,0CAA0C,CAAC;AAC/F,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAE/E,MAAM,CAAC,MAAM,KAAK,GAAG;IACpB,sBAAsB,EAAE,qBAAqB;IAC7C,uBAAuB,EAAE,uBAAuB;IAChD,uBAAuB,EAAE,uBAAuB;IAChD,2BAA2B,EAAE,2BAA2B;IACxD,kCAAkC,EAAE,iCAAiC;IACrE,qBAAqB,EAAE,oBAAoB;IAC3C,yBAAyB,EAAE,yBAAyB;IACpD,0BAA0B,EAAE,0BAA0B;IACtD,qBAAqB,EAAE,qBAAqB;IAC5C,kCAAkC,EAAE,+BAA+B;IACnE,iBAAiB,EAAE,kBAAkB;IACrC,4BAA4B,EAAE,4BAA4B;IAC1D,8BAA8B,EAAE,8BAA8B;IAC9D,qCAAqC,EAAE,mCAAmC;IAC1E,+BAA+B,EAAE,6BAA6B;CACtB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,iCAAiC,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,mCAAmC,EAAE,MAAM,0CAA0C,CAAC;AAC/F,OAAO,EAAE,6BAA6B,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,+BAA+B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAE/E,MAAM,CAAC,MAAM,KAAK,GAAG;IACpB,sBAAsB,EAAE,qBAAqB;IAC7C,uBAAuB,EAAE,uBAAuB;IAChD,uBAAuB,EAAE,uBAAuB;IAChD,2BAA2B,EAAE,2BAA2B;IACxD,kCAAkC,EAAE,iCAAiC;IACrE,qBAAqB,EAAE,oBAAoB;IAC3C,+BAA+B,EAAE,+BAA+B;IAChE,yBAAyB,EAAE,yBAAyB;IACpD,0BAA0B,EAAE,0BAA0B;IACtD,qBAAqB,EAAE,qBAAqB;IAC5C,kCAAkC,EAAE,+BAA+B;IACnE,iBAAiB,EAAE,kBAAkB;IACrC,4BAA4B,EAAE,4BAA4B;IAC1D,8BAA8B,EAAE,8BAA8B;IAC9D,qCAAqC,EAAE,mCAAmC;IAC1E,+BAA+B,EAAE,6BAA6B;IAC9D,8BAA8B,EAAE,6BAA6B;CACrB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const NodeConnectionTypeLiteralRule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"stringLiteralInInputs" | "stringLiteralInOutputs" | "unknownStringLiteralInInputs" | "unknownStringLiteralInOutputs", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
2
+ //# sourceMappingURL=node-connection-type-literal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-connection-type-literal.d.ts","sourceRoot":"","sources":["../../src/rules/node-connection-type-literal.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,6BAA6B,wPA0ExC,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
2
+ import { createRequire } from 'node:module';
3
+ import { isNodeTypeClass, findClassProperty, findObjectProperty, createRule, } from '../utils/index.js';
4
+ // n8n-workflow's ESM dist uses bare module specifiers that Node's native ESM
5
+ // loader cannot resolve. Loading via CJS (createRequire) sidesteps this.
6
+ const { NodeConnectionTypes } = createRequire(import.meta.url)('n8n-workflow');
7
+ // Reverse map: string value (e.g. 'main') → enum key name (e.g. 'Main').
8
+ // Derived directly from NodeConnectionTypes so it stays in sync automatically.
9
+ const VALUE_TO_ENUM_KEY = Object.fromEntries(Object.entries(NodeConnectionTypes).map(([key, value]) => [value, key]));
10
+ export const NodeConnectionTypeLiteralRule = createRule({
11
+ name: 'node-connection-type-literal',
12
+ meta: {
13
+ type: 'problem',
14
+ docs: {
15
+ description: 'Disallow string literals in node description `inputs`/`outputs` — use `NodeConnectionTypes` enum instead',
16
+ },
17
+ messages: {
18
+ stringLiteralInInputs: 'Use NodeConnectionTypes.{{enumKey}} from "n8n-workflow" instead of the string literal "{{value}}" in "inputs".',
19
+ stringLiteralInOutputs: 'Use NodeConnectionTypes.{{enumKey}} from "n8n-workflow" instead of the string literal "{{value}}" in "outputs".',
20
+ unknownStringLiteralInInputs: 'Use the NodeConnectionTypes enum from "n8n-workflow" instead of the string literal "{{value}}" in "inputs".',
21
+ unknownStringLiteralInOutputs: 'Use the NodeConnectionTypes enum from "n8n-workflow" instead of the string literal "{{value}}" in "outputs".',
22
+ },
23
+ fixable: 'code',
24
+ schema: [],
25
+ },
26
+ defaultOptions: [],
27
+ create(context) {
28
+ function checkArrayElements(elements, property) {
29
+ for (const element of elements) {
30
+ if (element?.type !== AST_NODE_TYPES.Literal)
31
+ continue;
32
+ if (typeof element.value !== 'string')
33
+ continue;
34
+ const value = element.value;
35
+ const enumKey = VALUE_TO_ENUM_KEY[value];
36
+ if (enumKey) {
37
+ context.report({
38
+ node: element,
39
+ messageId: property === 'inputs' ? 'stringLiteralInInputs' : 'stringLiteralInOutputs',
40
+ data: { value, enumKey },
41
+ fix(fixer) {
42
+ return fixer.replaceText(element, `NodeConnectionTypes.${enumKey}`);
43
+ },
44
+ });
45
+ }
46
+ else {
47
+ context.report({
48
+ node: element,
49
+ messageId: property === 'inputs'
50
+ ? 'unknownStringLiteralInInputs'
51
+ : 'unknownStringLiteralInOutputs',
52
+ data: { value },
53
+ });
54
+ }
55
+ }
56
+ }
57
+ return {
58
+ ClassDeclaration(node) {
59
+ if (!isNodeTypeClass(node))
60
+ return;
61
+ const descriptionProperty = findClassProperty(node, 'description');
62
+ if (!descriptionProperty)
63
+ return;
64
+ const descriptionValue = descriptionProperty.value;
65
+ if (descriptionValue?.type !== AST_NODE_TYPES.ObjectExpression)
66
+ return;
67
+ for (const prop of ['inputs', 'outputs']) {
68
+ const property = findObjectProperty(descriptionValue, prop);
69
+ if (property?.value.type !== AST_NODE_TYPES.ArrayExpression)
70
+ continue;
71
+ checkArrayElements(property.value.elements, prop);
72
+ }
73
+ },
74
+ };
75
+ },
76
+ });
77
+ //# sourceMappingURL=node-connection-type-literal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-connection-type-literal.js","sourceRoot":"","sources":["../../src/rules/node-connection-type-literal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const OptionsSortedAlphabeticallyRule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"optionsNotSorted", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
2
+ //# sourceMappingURL=options-sorted-alphabetically.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options-sorted-alphabetically.d.ts","sourceRoot":"","sources":["../../src/rules/options-sorted-alphabetically.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,+BAA+B,qJAoH1C,CAAC"}
@@ -0,0 +1,95 @@
1
+ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
2
+ import { isNodeTypeClass, findClassProperty, findObjectProperty, getStringLiteralValue, isFileType, createRule, } from '../utils/index.js';
3
+ export const OptionsSortedAlphabeticallyRule = createRule({
4
+ name: 'options-sorted-alphabetically',
5
+ meta: {
6
+ type: 'suggestion',
7
+ docs: {
8
+ description: 'Enforce alphabetical ordering of options arrays in n8n node properties',
9
+ },
10
+ messages: {
11
+ optionsNotSorted: 'Options in "{{ displayName }}" are not sorted alphabetically. Expected order: {{ expectedOrder }}.',
12
+ },
13
+ schema: [],
14
+ },
15
+ defaultOptions: [],
16
+ create(context) {
17
+ if (!isFileType(context.filename, '.node.ts')) {
18
+ return {};
19
+ }
20
+ const analyzeNodeDescription = (descriptionValue) => {
21
+ if (!descriptionValue || descriptionValue.type !== AST_NODE_TYPES.ObjectExpression) {
22
+ return;
23
+ }
24
+ const propertiesProperty = findObjectProperty(descriptionValue, 'properties');
25
+ if (!propertiesProperty?.value ||
26
+ propertiesProperty.value.type !== AST_NODE_TYPES.ArrayExpression) {
27
+ return;
28
+ }
29
+ for (const property of propertiesProperty.value.elements) {
30
+ if (!property || property.type !== AST_NODE_TYPES.ObjectExpression) {
31
+ continue;
32
+ }
33
+ const typeProperty = findObjectProperty(property, 'type');
34
+ const type = typeProperty ? getStringLiteralValue(typeProperty.value) : null;
35
+ if (type !== 'options') {
36
+ continue;
37
+ }
38
+ const optionsProperty = findObjectProperty(property, 'options');
39
+ if (!optionsProperty?.value ||
40
+ optionsProperty.value.type !== AST_NODE_TYPES.ArrayExpression) {
41
+ continue;
42
+ }
43
+ const optionsArray = optionsProperty.value;
44
+ // Extract all option names; skip if any name is non-literal (dynamic options)
45
+ const names = [];
46
+ let hasDynamicName = false;
47
+ for (const option of optionsArray.elements) {
48
+ if (!option || option.type !== AST_NODE_TYPES.ObjectExpression) {
49
+ hasDynamicName = true;
50
+ break;
51
+ }
52
+ const nameProperty = findObjectProperty(option, 'name');
53
+ const name = nameProperty ? getStringLiteralValue(nameProperty.value) : null;
54
+ if (name === null) {
55
+ hasDynamicName = true;
56
+ break;
57
+ }
58
+ names.push(name);
59
+ }
60
+ if (hasDynamicName || names.length <= 1) {
61
+ continue;
62
+ }
63
+ const sorted = [...names].sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
64
+ const isSorted = names.every((name, i) => name === sorted[i]);
65
+ if (isSorted) {
66
+ continue;
67
+ }
68
+ const displayNameProperty = findObjectProperty(property, 'displayName');
69
+ const displayName = (displayNameProperty ? getStringLiteralValue(displayNameProperty.value) : null) ??
70
+ 'unknown';
71
+ context.report({
72
+ node: optionsArray,
73
+ messageId: 'optionsNotSorted',
74
+ data: {
75
+ displayName,
76
+ expectedOrder: sorted.join(', '),
77
+ },
78
+ });
79
+ }
80
+ };
81
+ return {
82
+ ClassDeclaration(node) {
83
+ if (!isNodeTypeClass(node)) {
84
+ return;
85
+ }
86
+ const descriptionProperty = findClassProperty(node, 'description');
87
+ if (!descriptionProperty) {
88
+ return;
89
+ }
90
+ analyzeNodeDescription(descriptionProperty.value);
91
+ },
92
+ };
93
+ },
94
+ });
95
+ //# sourceMappingURL=options-sorted-alphabetically.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options-sorted-alphabetically.js","sourceRoot":"","sources":["../../src/rules/options-sorted-alphabetically.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ # Disallow string literals in node description `inputs`/`outputs` — use `NodeConnectionTypes` enum instead (`@n8n/community-nodes/node-connection-type-literal`)
2
+
3
+ 💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
4
+
5
+ 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6
+
7
+ <!-- end auto-generated rule header -->
8
+
9
+ ## Rule Details
10
+
11
+ Using raw string literals like `'main'` in `inputs` and `outputs` is fragile: the values are not type-checked, and typos or renamed connection types will go undetected. The `NodeConnectionTypes` object from `n8n-workflow` is the single source of truth and should always be used instead.
12
+
13
+ ## Examples
14
+
15
+ ### ❌ Incorrect
16
+
17
+ ```typescript
18
+ import type { INodeType, INodeTypeDescription } from 'n8n-workflow';
19
+
20
+ export class MyNode implements INodeType {
21
+ description: INodeTypeDescription = {
22
+ displayName: 'My Node',
23
+ name: 'myNode',
24
+ inputs: ['main'],
25
+ outputs: ['main'],
26
+ properties: [],
27
+ };
28
+ }
29
+ ```
30
+
31
+ ### ✅ Correct
32
+
33
+ ```typescript
34
+ import type { INodeType, INodeTypeDescription } from 'n8n-workflow';
35
+ import { NodeConnectionTypes } from 'n8n-workflow';
36
+
37
+ export class MyNode implements INodeType {
38
+ description: INodeTypeDescription = {
39
+ displayName: 'My Node',
40
+ name: 'myNode',
41
+ inputs: [NodeConnectionTypes.Main],
42
+ outputs: [NodeConnectionTypes.Main],
43
+ properties: [],
44
+ };
45
+ }
46
+ ```
@@ -0,0 +1,63 @@
1
+ # Enforce alphabetical ordering of options arrays in n8n node properties (`@n8n/community-nodes/options-sorted-alphabetically`)
2
+
3
+ ⚠️ This rule _warns_ in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
4
+
5
+ <!-- end auto-generated rule header -->
6
+
7
+ ## Rule Details
8
+
9
+ Warns when an `options`-type parameter has its options array not sorted alphabetically by name. Applies to all `type: 'options'` parameters — including `resource`, `operation`, and any other dropdowns.
10
+
11
+ Alphabetical ordering is an [official n8n UI design requirement](https://docs.n8n.io/integrations/creating-nodes/plan/node-ui-design/#lists) and the most frequently flagged issue in community node reviews.
12
+
13
+ Comparison is case-insensitive and locale-aware (handles non-ASCII names such as Spanish or Portuguese labels).
14
+
15
+ ## Examples
16
+
17
+ ### ❌ Incorrect
18
+
19
+ ```typescript
20
+ export class MyNode implements INodeType {
21
+ description: INodeTypeDescription = {
22
+ displayName: 'My Service',
23
+ name: 'myService',
24
+ properties: [
25
+ {
26
+ displayName: 'Resource',
27
+ name: 'resource',
28
+ type: 'options',
29
+ options: [
30
+ { name: 'User', value: 'user' },
31
+ { name: 'Contact', value: 'contact' }, // out of order
32
+ { name: 'Project', value: 'project' },
33
+ ],
34
+ default: 'user',
35
+ },
36
+ ],
37
+ };
38
+ }
39
+ ```
40
+
41
+ ### ✅ Correct
42
+
43
+ ```typescript
44
+ export class MyNode implements INodeType {
45
+ description: INodeTypeDescription = {
46
+ displayName: 'My Service',
47
+ name: 'myService',
48
+ properties: [
49
+ {
50
+ displayName: 'Resource',
51
+ name: 'resource',
52
+ type: 'options',
53
+ options: [
54
+ { name: 'Contact', value: 'contact' },
55
+ { name: 'Project', value: 'project' },
56
+ { name: 'User', value: 'user' },
57
+ ],
58
+ default: 'contact',
59
+ },
60
+ ],
61
+ };
62
+ }
63
+ ```
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.10.0",
4
+ "version": "0.11.0",
5
5
  "main": "./dist/plugin.js",
6
6
  "types": "./dist/plugin.d.ts",
7
7
  "exports": {
@@ -15,17 +15,20 @@
15
15
  "fastest-levenshtein": "1.0.16"
16
16
  },
17
17
  "devDependencies": {
18
+ "@types/node": "24.10.1",
18
19
  "@typescript-eslint/rule-tester": "^8.35.0",
19
20
  "eslint-doc-generator": "^2.2.2",
20
21
  "eslint-plugin-eslint-plugin": "^7.0.0",
21
22
  "rimraf": "6.0.1",
22
- "typescript": "5.9.2",
23
- "vitest": "^3.1.3",
24
- "@n8n/typescript-config": "1.3.0",
25
- "@n8n/vitest-config": "1.7.0"
23
+ "typescript": "6.0.2",
24
+ "vitest": "^4.1.1",
25
+ "@n8n/typescript-config": "1.4.0",
26
+ "@n8n/vitest-config": "1.9.0",
27
+ "n8n-workflow": "2.16.0"
26
28
  },
27
29
  "peerDependencies": {
28
- "eslint": ">= 9"
30
+ "eslint": ">= 9",
31
+ "n8n-workflow": ">=2"
29
32
  },
30
33
  "eslint-doc-generator": {
31
34
  "configEmoji": [
package/src/plugin.ts CHANGED
@@ -31,10 +31,12 @@ const configs = {
31
31
  '@n8n/community-nodes/no-credential-reuse': 'error',
32
32
  '@n8n/community-nodes/no-http-request-with-manual-auth': 'error',
33
33
  '@n8n/community-nodes/icon-validation': 'error',
34
+ '@n8n/community-nodes/options-sorted-alphabetically': 'warn',
34
35
  '@n8n/community-nodes/resource-operation-pattern': 'warn',
35
36
  '@n8n/community-nodes/credential-documentation-url': 'error',
36
37
  '@n8n/community-nodes/node-class-description-icon-missing': 'error',
37
38
  '@n8n/community-nodes/cred-class-field-icon-missing': 'error',
39
+ '@n8n/community-nodes/node-connection-type-literal': 'error',
38
40
  },
39
41
  },
40
42
  recommendedWithoutN8nCloudSupport: {
@@ -52,10 +54,12 @@ const configs = {
52
54
  '@n8n/community-nodes/no-credential-reuse': 'error',
53
55
  '@n8n/community-nodes/no-http-request-with-manual-auth': 'error',
54
56
  '@n8n/community-nodes/icon-validation': 'error',
57
+ '@n8n/community-nodes/options-sorted-alphabetically': 'warn',
55
58
  '@n8n/community-nodes/credential-documentation-url': 'error',
56
59
  '@n8n/community-nodes/resource-operation-pattern': 'warn',
57
60
  '@n8n/community-nodes/node-class-description-icon-missing': 'error',
58
61
  '@n8n/community-nodes/cred-class-field-icon-missing': 'error',
62
+ '@n8n/community-nodes/node-connection-type-literal': 'error',
59
63
  },
60
64
  },
61
65
  } satisfies Record<string, Linter.Config>;