-
Notifications
You must be signed in to change notification settings - Fork 2
fix: resolve n8n review findings for 0.2.3 (scanner lint + codex node identifier) #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| { | ||
| "node": "n8n-nodes-base.cloudinary", | ||
| "node": "n8n-nodes-cloudinary.cloudinary", | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The MEDIUM review finding. Correcting an earlier version of this comment — I claimed this fix also repairs the duplicate-entry behaviour in the Add-action panel. That was wrong, and the history of this file deserves a straight answer, since it has now flipped What this field actually does: nothing at runtime. n8n's codex loader reads only four keys out of private getCodex(filePath: string): CodexData {
const codexFilePath = this.resolvePath(`${filePath}on`); // .js to .json
const { categories, subcategories, resources: { primaryDocumentation, credentialDocumentation }, alias } = module.require(codexFilePath) as Codex;
...
}
And the dedupe claim was wrong. The vetted-catalog entry that drives the "install this" suggestion comes from n8n's own Strapi API ( So why change it? One reason only: n8n's 2026-08 review flagged the legacy value as a MEDIUM finding, and that review gates the 0.2.3 publish. Secondarily it's what the codex reference describes — Why the earlier revert looked right. In PR #7 the justification was "the codex spec and published community-node practice". Practice genuinely is inconsistent — I checked published packages: Happy to revert if you'd rather not churn the file again — but then we'd need n8n to waive the finding. |
||
| "nodeVersion": "1.0", | ||
| "codexVersion": "1.0", | ||
| "categories": ["Data & Storage", "Marketing & Content"], | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,9 @@ import { | |
| INodeTypeDescription, | ||
| IExecuteFunctions, | ||
| INodeExecutionData, | ||
| JsonObject, | ||
| NodeApiError, | ||
| NodeConnectionTypes, | ||
| NodeOperationError, | ||
| } from 'n8n-workflow'; | ||
| import { cloudinaryProperties } from './descriptions'; | ||
|
|
@@ -13,7 +16,7 @@ export class Cloudinary implements INodeType { | |
| description: INodeTypeDescription = { | ||
| displayName: 'Cloudinary', | ||
| name: 'cloudinary', | ||
| icon: 'file:cloudinary.svg', | ||
| icon: { light: 'file:cloudinary.svg', dark: 'file:cloudinary.dark.svg' }, | ||
| group: ['transform'], | ||
| // v1 exposed the Video Player as flat `player*` params; v2 regrouped them into | ||
| // collections (see widget.fields.ts). Both schemas ship side by side, gated by | ||
|
|
@@ -32,8 +35,8 @@ export class Cloudinary implements INodeType { | |
| defaults: { | ||
| name: 'Cloudinary', | ||
| }, | ||
| inputs: ['main'], | ||
| outputs: ['main'], | ||
| inputs: [NodeConnectionTypes.Main], | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Required by the scanner's Worth naming the trade-off: |
||
| outputs: [NodeConnectionTypes.Main], | ||
| credentials: [ | ||
| { | ||
| name: CREDENTIAL_TYPE, | ||
|
|
@@ -84,7 +87,12 @@ export class Cloudinary implements INodeType { | |
| }); | ||
| continue; | ||
| } | ||
| throw error; | ||
| // Both constructors pass an already-wrapped error of their own type | ||
| // through untouched, so handler-thrown errors keep their context. | ||
| if (error instanceof NodeApiError) { | ||
| throw new NodeApiError(this.getNode(), error as unknown as JsonObject, { itemIndex: i }); | ||
| } | ||
| throw new NodeOperationError(this.getNode(), error as Error, { itemIndex: i }); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why construct a new error instead of rethrowing the caught one. Re-wrapping is safe because it's a pass-through: both constructors return an already-wrapped error of their own class untouched ( One consequence: in that pass-through branch the |
||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { IDataObject, ApplicationError } from 'n8n-workflow'; | ||
| import { IDataObject, INode, NodeOperationError } from 'n8n-workflow'; | ||
| import { sha256 } from './sha256.utils'; | ||
| import { CloudinaryCredentials } from './operations/types'; | ||
| import { version } from '../../package.json'; | ||
|
|
@@ -190,14 +190,15 @@ const escapeMetadataValue = (value: string): string => value.replace(/([=|"])/g, | |
| * matching Cloudinary's multi-value field format. Delimiter characters (`=`, `"`, | ||
| * `|`) are backslash-escaped inside every value — scalar and list element alike — | ||
| * so a value containing them can't be misparsed as another field, pair, or list | ||
| * boundary. Throws ApplicationError on invalid JSON input. | ||
| * boundary. Throws NodeOperationError on invalid JSON input, attributed to the | ||
| * calling node. | ||
| */ | ||
| export const metadataToPipeString = (input: IDataObject | string): string => { | ||
| export const metadataToPipeString = (input: IDataObject | string, node: INode): string => { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Known gap, flagged in review: the error carries no |
||
| let metadata: IDataObject; | ||
| try { | ||
| metadata = typeof input === 'object' ? input : (JSON.parse(input) as IDataObject); | ||
| } catch (error) { | ||
| throw new ApplicationError('Invalid JSON for structured metadata'); | ||
| throw new NodeOperationError(node, 'Invalid JSON for structured metadata'); | ||
| } | ||
| return Object.keys(metadata) | ||
| .map((key) => { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scanner requires credential classes to declare an
icon. Note the duplicated SVG files this needs: n8n resolvesfile:icon paths relative to the declaring class file, so the node resolves intodist/nodes/Cloudinary/and the credential intodist/credentials/. Per-directory copies are the community-node convention; there's no shared-asset path. All four files are in the packed tarball.