Skip to content

Commit 845c15c

Browse files
UN-2731: Update Unstract n8n node to meet verification standards (#14)
* feat: enhance n8n nodes with TypeScript, OCR discovery, and security compliance Major improvements to n8n community package: 🔧 TypeScript Migration: - Convert all nodes from JavaScript to TypeScript - Add proper TypeScript configuration (tsconfig.json) - Update build system with TypeScript compilation - Add type safety and better development experience 🔍 Enhanced Node Discovery: - Add comprehensive OCR keywords to package.json - Improve LLMWhisperer node description for better searchability - Add dynamic subtitle showing current operation mode - Create codex files (.node.json) for proper categorization 🛡️ Security Compliance: - Replace setTimeout with Promise-based delay mechanism - Ensure compliance with n8n community package security rules - Remove restricted globals usage - Pass all n8n security validation checks 📁 Improved Project Structure: - Reorganize nodes into separate directories - Add proper build tooling (gulp, prettier, eslint) - Create comprehensive package configuration - Add MIT license and proper documentation ✨ Node Enhancements: - LLMWhisperer: Enhanced OCR functionality with better descriptions - Unstract: Document processing with improved error handling - UnstractHITL: Human-in-the-loop operations (Push/Fetch) - All nodes: TypeScript types and better maintainability 🔨 Development Tools: - Add linting with n8n-specific rules - Configure prettier for code formatting - Set up proper build pipeline - Add comprehensive package metadata The package now passes all n8n community security checks and is ready for publication and verification. * fix: resolve security vulnerabilities and TypeScript compatibility - Fix critical form-data vulnerability with package override (^4.0.4) - Update TypeScript to 5.9.2 and @typescript-eslint/parser to 8.40.0 - Fix TypeScript type compatibility issues: - Add NodeConnectionType for inputs/outputs - Add IHttpRequestMethods type for HTTP methods - Fix credential icon types with 'as const' - All security checks pass - Build successful with updated dependencies * feat: enhance prepublish checks and fix TypeScript compliance (#15) - Add security audit to prepublish script for n8n compatibility - Fix LLMWhisperer class name casing (LLMWhisperer -> LlmWhisperer) - Rename UnstractHITL credentials to UnstractHITLApi for consistency - Include credentials in lint scripts for comprehensive checking * ci: add comprehensive PR checks workflow - Add GitHub Actions workflow for automated PR validation - Includes build, lint, security audit, and package structure checks - Uses enhanced prepublishOnly script for n8n compatibility validation - Verifies all required dist files exist before package submission * fix: improve file validation in PR checks workflow - Replace unreliable test syntax with explicit if statements - Add comprehensive missing file reporting - Ensure CI properly fails when required build files are missing - Validate all files referenced in package.json n8n section * chore: revert version to 0.3.4
1 parent 8a63e7d commit 845c15c

34 files changed

+4747
-1812
lines changed

.editorconfig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
indent_style = tab
6+
indent_size = 2
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[package.json]
12+
indent_style = space
13+
indent_size = 2
14+
15+
[*.md]
16+
trim_trailing_whitespace = false
17+
18+
[*.yml]
19+
indent_style = space
20+
indent_size = 2

.eslintrc.js

Lines changed: 143 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,145 @@
1+
/**
2+
* @type {import('@types/eslint').ESLint.ConfigData}
3+
*/
14
module.exports = {
2-
root: true,
3-
env: {
4-
browser: true,
5-
es6: true,
6-
node: true,
7-
},
8-
parserOptions: {
9-
ecmaVersion: 2020,
10-
sourceType: 'module',
11-
},
12-
extends: [
13-
'eslint:recommended',
14-
],
15-
rules: {
16-
'no-console': 'off',
17-
'semi': ['error', 'always'],
18-
'quotes': ['error', 'single'],
19-
'indent': ['error', 4],
20-
'comma-dangle': ['error', 'always-multiline'],
21-
'no-trailing-spaces': 'error',
22-
'eol-last': 'error',
23-
'no-multiple-empty-lines': ['error', { 'max': 1 }],
24-
},
25-
ignorePatterns: ['dist/**/*'],
5+
root: true,
6+
7+
env: {
8+
browser: true,
9+
es6: true,
10+
node: true,
11+
},
12+
13+
parser: '@typescript-eslint/parser',
14+
15+
parserOptions: {
16+
project: ['./tsconfig.json'],
17+
sourceType: 'module',
18+
extraFileExtensions: ['.json'],
19+
},
20+
21+
ignorePatterns: ['.eslintrc.js', '**/*.js', '**/node_modules/**', '**/dist/**'],
22+
23+
overrides: [
24+
{
25+
files: ['package.json'],
26+
plugins: ['eslint-plugin-n8n-nodes-base'],
27+
extends: ['plugin:n8n-nodes-base/community'],
28+
rules: {
29+
'n8n-nodes-base/community-package-json-name-still-default': 'off',
30+
},
31+
},
32+
{
33+
files: ['./credentials/**/*.ts'],
34+
plugins: ['eslint-plugin-n8n-nodes-base'],
35+
extends: ['plugin:n8n-nodes-base/credentials'],
36+
rules: {
37+
'n8n-nodes-base/cred-class-field-authenticate-type-assertion': 'error',
38+
'n8n-nodes-base/cred-class-field-display-name-missing-oauth2': 'error',
39+
'n8n-nodes-base/cred-class-field-display-name-miscased': 'error',
40+
'n8n-nodes-base/cred-class-field-documentation-url-missing': 'error',
41+
'n8n-nodes-base/cred-class-field-documentation-url-miscased': 'off',
42+
'n8n-nodes-base/cred-class-field-name-missing-oauth2': 'error',
43+
'n8n-nodes-base/cred-class-field-name-unsuffixed': 'error',
44+
'n8n-nodes-base/cred-class-field-name-uppercase-first-char': 'error',
45+
'n8n-nodes-base/cred-class-field-properties-assertion': 'error',
46+
'n8n-nodes-base/cred-class-field-type-options-password-missing': 'error',
47+
'n8n-nodes-base/cred-class-name-missing-oauth2-suffix': 'error',
48+
'n8n-nodes-base/cred-class-name-unsuffixed': 'error',
49+
'n8n-nodes-base/cred-filename-against-convention': 'error',
50+
},
51+
},
52+
{
53+
files: ['./nodes/**/*.ts'],
54+
plugins: ['eslint-plugin-n8n-nodes-base'],
55+
extends: ['plugin:n8n-nodes-base/nodes'],
56+
rules: {
57+
'n8n-nodes-base/node-class-description-credentials-name-unsuffixed': 'error',
58+
'n8n-nodes-base/node-class-description-display-name-unsuffixed-trigger-node': 'error',
59+
'n8n-nodes-base/node-class-description-empty-string': 'error',
60+
'n8n-nodes-base/node-class-description-icon-not-svg': 'error',
61+
'n8n-nodes-base/node-class-description-inputs-wrong-regular-node': 'off',
62+
'n8n-nodes-base/node-class-description-inputs-wrong-trigger-node': 'error',
63+
'n8n-nodes-base/node-class-description-missing-subtitle': 'error',
64+
'n8n-nodes-base/node-class-description-non-core-color-present': 'error',
65+
'n8n-nodes-base/node-class-description-name-miscased': 'error',
66+
'n8n-nodes-base/node-class-description-name-unsuffixed-trigger-node': 'error',
67+
'n8n-nodes-base/node-class-description-outputs-wrong': 'off',
68+
'n8n-nodes-base/node-dirname-against-convention': 'error',
69+
'n8n-nodes-base/node-execute-block-double-assertion-for-items': 'error',
70+
'n8n-nodes-base/node-execute-block-wrong-error-thrown': 'error',
71+
'n8n-nodes-base/node-filename-against-convention': 'error',
72+
'n8n-nodes-base/node-param-array-type-assertion': 'error',
73+
'n8n-nodes-base/node-param-color-type-unused': 'error',
74+
'n8n-nodes-base/node-param-default-missing': 'error',
75+
'n8n-nodes-base/node-param-default-wrong-for-boolean': 'error',
76+
'n8n-nodes-base/node-param-default-wrong-for-collection': 'error',
77+
'n8n-nodes-base/node-param-default-wrong-for-fixed-collection': 'error',
78+
'n8n-nodes-base/node-param-default-wrong-for-multi-options': 'error',
79+
'n8n-nodes-base/node-param-default-wrong-for-number': 'error',
80+
'n8n-nodes-base/node-param-default-wrong-for-simplify': 'error',
81+
'n8n-nodes-base/node-param-default-wrong-for-string': 'error',
82+
'n8n-nodes-base/node-param-description-boolean-without-whether': 'error',
83+
'n8n-nodes-base/node-param-description-comma-separated-hyphen': 'error',
84+
'n8n-nodes-base/node-param-description-empty-string': 'error',
85+
'n8n-nodes-base/node-param-description-excess-final-period': 'error',
86+
'n8n-nodes-base/node-param-description-excess-inner-whitespace': 'error',
87+
'n8n-nodes-base/node-param-description-identical-to-display-name': 'error',
88+
'n8n-nodes-base/node-param-description-line-break-html-tag': 'error',
89+
'n8n-nodes-base/node-param-description-lowercase-first-char': 'error',
90+
'n8n-nodes-base/node-param-description-miscased-id': 'error',
91+
'n8n-nodes-base/node-param-description-miscased-json': 'error',
92+
'n8n-nodes-base/node-param-description-miscased-url': 'error',
93+
'n8n-nodes-base/node-param-description-missing-final-period': 'error',
94+
'n8n-nodes-base/node-param-description-missing-for-ignore-ssl-issues': 'error',
95+
'n8n-nodes-base/node-param-description-missing-for-return-all': 'error',
96+
'n8n-nodes-base/node-param-description-missing-for-simplify': 'error',
97+
'n8n-nodes-base/node-param-description-missing-from-dynamic-multi-options': 'error',
98+
'n8n-nodes-base/node-param-description-missing-from-dynamic-options': 'error',
99+
'n8n-nodes-base/node-param-description-missing-from-limit': 'error',
100+
'n8n-nodes-base/node-param-description-unencoded-angle-brackets': 'error',
101+
'n8n-nodes-base/node-param-description-unneeded-backticks': 'error',
102+
'n8n-nodes-base/node-param-description-untrimmed': 'error',
103+
'n8n-nodes-base/node-param-description-url-missing-protocol': 'error',
104+
'n8n-nodes-base/node-param-description-weak': 'error',
105+
'n8n-nodes-base/node-param-description-wrong-for-dynamic-multi-options': 'error',
106+
'n8n-nodes-base/node-param-description-wrong-for-dynamic-options': 'error',
107+
'n8n-nodes-base/node-param-description-wrong-for-ignore-ssl-issues': 'error',
108+
'n8n-nodes-base/node-param-description-wrong-for-limit': 'error',
109+
'n8n-nodes-base/node-param-description-wrong-for-return-all': 'error',
110+
'n8n-nodes-base/node-param-description-wrong-for-simplify': 'error',
111+
'n8n-nodes-base/node-param-description-wrong-for-upsert': 'error',
112+
'n8n-nodes-base/node-param-display-name-excess-inner-whitespace': 'error',
113+
'n8n-nodes-base/node-param-display-name-miscased-id': 'error',
114+
'n8n-nodes-base/node-param-display-name-miscased': 'error',
115+
'n8n-nodes-base/node-param-display-name-not-first-position': 'error',
116+
'n8n-nodes-base/node-param-display-name-untrimmed': 'error',
117+
'n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options': 'error',
118+
'n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options': 'error',
119+
'n8n-nodes-base/node-param-display-name-wrong-for-simplify': 'error',
120+
'n8n-nodes-base/node-param-display-name-wrong-for-update-fields': 'error',
121+
'n8n-nodes-base/node-param-min-value-wrong-for-limit': 'error',
122+
'n8n-nodes-base/node-param-multi-options-type-unsorted-items': 'error',
123+
'n8n-nodes-base/node-param-name-untrimmed': 'error',
124+
'n8n-nodes-base/node-param-operation-option-action-wrong-for-get-many': 'error',
125+
'n8n-nodes-base/node-param-operation-option-description-wrong-for-get-many': 'error',
126+
'n8n-nodes-base/node-param-operation-option-without-action': 'error',
127+
'n8n-nodes-base/node-param-operation-without-no-data-expression': 'error',
128+
'n8n-nodes-base/node-param-option-description-identical-to-name': 'error',
129+
'n8n-nodes-base/node-param-option-name-containing-star': 'error',
130+
'n8n-nodes-base/node-param-option-name-duplicate': 'error',
131+
'n8n-nodes-base/node-param-option-name-wrong-for-get-many': 'error',
132+
'n8n-nodes-base/node-param-option-name-wrong-for-upsert': 'error',
133+
'n8n-nodes-base/node-param-option-value-duplicate': 'error',
134+
'n8n-nodes-base/node-param-options-type-unsorted-items': 'error',
135+
'n8n-nodes-base/node-param-placeholder-miscased-id': 'error',
136+
'n8n-nodes-base/node-param-placeholder-missing-email': 'error',
137+
'n8n-nodes-base/node-param-required-false': 'error',
138+
'n8n-nodes-base/node-param-resource-with-plural-option': 'error',
139+
'n8n-nodes-base/node-param-resource-without-no-data-expression': 'error',
140+
'n8n-nodes-base/node-param-type-options-missing-from-limit': 'error',
141+
'n8n-nodes-base/node-param-type-options-password-missing': 'error',
142+
},
143+
},
144+
],
26145
};

.eslintrc.prepublish.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
* @type {import('@types/eslint').ESLint.ConfigData}
33
*/
44
module.exports = {
5-
extends: ['@n8n_io/eslint-config/base'],
6-
rules: {
7-
'n8n-local-rules/node-param-description-identical-to-display-name': 'off',
8-
'n8n-local-rules/node-param-description-line-break': 'off',
9-
'n8n-local-rules/node-param-description-missing-final-period': 'off',
10-
'@typescript-eslint/no-explicit-any': 'off',
11-
'@typescript-eslint/no-unused-vars': 'off',
12-
'@typescript-eslint/explicit-module-boundary-types': 'off',
13-
},
5+
extends: "./.eslintrc.js",
6+
7+
overrides: [
8+
{
9+
files: ['package.json'],
10+
plugins: ['eslint-plugin-n8n-nodes-base'],
11+
rules: {
12+
'n8n-nodes-base/community-package-json-name-still-default': 'error',
13+
},
14+
},
15+
],
1416
};

.github/workflows/pr-checks.yml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: PR Checks
2+
3+
on: [pull_request]
4+
5+
jobs:
6+
validate:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v4
10+
11+
- uses: actions/setup-node@v4
12+
with:
13+
node-version: '20'
14+
15+
- name: Install dependencies
16+
run: npm ci
17+
18+
- name: Run comprehensive checks
19+
run: npm run prepublishOnly
20+
21+
- name: Validate package structure
22+
run: npm pack --dry-run
23+
24+
- name: Verify built files match package.json n8n section
25+
run: |
26+
# Check that all files referenced in package.json n8n section actually exist
27+
MISSING_FILES=""
28+
29+
if [ ! -f "dist/credentials/LLMWhispererApi.credentials.js" ]; then
30+
MISSING_FILES="$MISSING_FILES dist/credentials/LLMWhispererApi.credentials.js"
31+
fi
32+
33+
if [ ! -f "dist/credentials/UnstractApi.credentials.js" ]; then
34+
MISSING_FILES="$MISSING_FILES dist/credentials/UnstractApi.credentials.js"
35+
fi
36+
37+
if [ ! -f "dist/credentials/UnstractHITLApi.credentials.js" ]; then
38+
MISSING_FILES="$MISSING_FILES dist/credentials/UnstractHITLApi.credentials.js"
39+
fi
40+
41+
if [ ! -f "dist/nodes/LlmWhisperer/LlmWhisperer.node.js" ]; then
42+
MISSING_FILES="$MISSING_FILES dist/nodes/LlmWhisperer/LlmWhisperer.node.js"
43+
fi
44+
45+
if [ ! -f "dist/nodes/Unstract/Unstract.node.js" ]; then
46+
MISSING_FILES="$MISSING_FILES dist/nodes/Unstract/Unstract.node.js"
47+
fi
48+
49+
if [ ! -f "dist/nodes/UnstractHitlFetch/UnstractHitlFetch.node.js" ]; then
50+
MISSING_FILES="$MISSING_FILES dist/nodes/UnstractHitlFetch/UnstractHitlFetch.node.js"
51+
fi
52+
53+
if [ ! -f "dist/nodes/UnstractHitlPush/UnstractHitlPush.node.js" ]; then
54+
MISSING_FILES="$MISSING_FILES dist/nodes/UnstractHitlPush/UnstractHitlPush.node.js"
55+
fi
56+
57+
if [ -n "$MISSING_FILES" ]; then
58+
echo "❌ Missing required files:"
59+
for file in $MISSING_FILES; do
60+
echo " - $file"
61+
done
62+
exit 1
63+
fi
64+
65+
echo "✅ All required n8n files exist in dist/"

.npmignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
*.tsbuildinfo

.prettierrc.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
module.exports = {
2+
/**
3+
* https://prettier.io/docs/en/options.html#semicolons
4+
*/
5+
semi: true,
6+
7+
/**
8+
* https://prettier.io/docs/en/options.html#trailing-commas
9+
*/
10+
trailingComma: 'all',
11+
12+
/**
13+
* https://prettier.io/docs/en/options.html#bracket-spacing
14+
*/
15+
bracketSpacing: true,
16+
17+
/**
18+
* https://prettier.io/docs/en/options.html#tabs
19+
*/
20+
useTabs: true,
21+
22+
/**
23+
* https://prettier.io/docs/en/options.html#tab-width
24+
*/
25+
tabWidth: 2,
26+
27+
/**
28+
* https://prettier.io/docs/en/options.html#arrow-function-parentheses
29+
*/
30+
arrowParens: 'always',
31+
32+
/**
33+
* https://prettier.io/docs/en/options.html#quotes
34+
*/
35+
singleQuote: true,
36+
37+
/**
38+
* https://prettier.io/docs/en/options.html#quote-props
39+
*/
40+
quoteProps: 'as-needed',
41+
42+
/**
43+
* https://prettier.io/docs/en/options.html#end-of-line
44+
*/
45+
endOfLine: 'lf',
46+
47+
/**
48+
* https://prettier.io/docs/en/options.html#print-width
49+
*/
50+
printWidth: 100,
51+
};

LICENSE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright 2024 Unstract
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of
4+
this software and associated documentation files (the "Software"), to deal in
5+
the Software without restriction, including without limitation the rights to
6+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7+
of the Software, and to permit persons to whom the Software is furnished to do
8+
so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

credentials/LLMWhispererApi.credentials.js

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)