first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,94 @@
import type { INodeProperties } from 'n8n-workflow';
export const distTagOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
default: 'getMany',
displayOptions: {
show: {
resource: ['distTag'],
},
},
options: [
{
name: 'Get All',
value: 'getMany',
action: 'Returns all the dist-tags for a package',
description: 'Returns all the dist-tags for a package',
routing: {
request: {
method: 'GET',
url: '=/-/package/{{ encodeURIComponent($parameter.packageName) }}/dist-tags',
},
},
},
{
name: 'Update',
value: 'update',
action: 'Update a the dist-tags for a package',
description: 'Update a the dist-tags for a package',
routing: {
request: {
method: 'PUT',
url: '=/-/package/{{ encodeURIComponent($parameter.packageName) }}/dist-tags/{{ encodeURIComponent($parameter.distTagName) }}',
},
send: {
preSend: [
async function (this, requestOptions) {
requestOptions.headers!['content-type'] = 'application/x-www-form-urlencoded';
requestOptions.body = this.getNodeParameter('packageVersion');
return requestOptions;
},
],
},
},
},
],
},
];
export const distTagFields: INodeProperties[] = [
{
displayName: 'Package Name',
name: 'packageName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['distTag'],
operation: ['getMany', 'update'],
},
},
},
{
displayName: 'Package Version',
name: 'packageVersion',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['distTag'],
operation: ['update'],
},
},
},
{
displayName: 'Distribution Tag Name',
name: 'distTagName',
type: 'string',
required: true,
default: 'latest',
displayOptions: {
show: {
resource: ['distTag'],
operation: ['update'],
},
},
},
];
@@ -0,0 +1,19 @@
{
"node": "n8n-nodes-base.npm",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/npm/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.npm/"
}
],
"generic": []
}
}
+57
View File
@@ -0,0 +1,57 @@
import type { INodeType, INodeTypeDescription } from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';
import { distTagFields, distTagOperations } from './DistTagDescription';
import { packageFields, packageOperations } from './PackageDescription';
export class Npm implements INodeType {
description: INodeTypeDescription = {
displayName: 'Npm',
name: 'npm',
icon: 'file:npm.svg',
group: ['input'],
version: 1,
subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}',
description: 'Consume NPM registry API',
defaults: {
name: 'npm',
},
usableAsTool: true,
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'npmApi',
required: false,
},
],
requestDefaults: {
baseURL: '={{ $credentials.registryUrl }}',
},
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Package',
value: 'package',
},
{
name: 'Distribution Tag',
value: 'distTag',
},
],
default: 'package',
},
...packageOperations,
...packageFields,
...distTagOperations,
...distTagFields,
],
};
}
@@ -0,0 +1,181 @@
import type { INodeExecutionData, INodeProperties } from 'n8n-workflow';
import { valid as isValidSemver } from 'semver';
interface PackageJson {
name: string;
version: string;
description: string;
}
export const packageOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
default: 'getMetadata',
displayOptions: {
show: {
resource: ['package'],
},
},
options: [
{
name: 'Get Metadata',
value: 'getMetadata',
action: 'Returns all the metadata for a package at a specific version',
description: 'Returns all the metadata for a package at a specific version',
routing: {
request: {
method: 'GET',
url: '=/{{ encodeURIComponent($parameter.packageName) }}/{{ $parameter.packageVersion }}',
},
},
},
{
name: 'Get Versions',
value: 'getVersions',
action: 'Returns all the versions for a package',
description: 'Returns all the versions for a package',
routing: {
request: {
method: 'GET',
url: '=/{{ encodeURIComponent($parameter.packageName) }}',
},
output: {
postReceive: [
async function (items) {
const allVersions: INodeExecutionData[] = [];
for (const { json } of items) {
const itemVersions = json.time as Record<string, string>;
Object.keys(itemVersions).forEach((version) => {
if (isValidSemver(version)) {
allVersions.push({
json: {
version,
published_at: itemVersions[version],
},
});
}
});
}
allVersions.sort(
(a, b) =>
new Date(b.json.published_at as string).getTime() -
new Date(a.json.published_at as string).getTime(),
);
return allVersions;
},
],
},
},
},
{
name: 'Search',
value: 'search',
action: 'Search for packages',
description: 'Search for packages',
routing: {
request: {
method: 'GET',
url: '/-/v1/search',
qs: {
text: '={{$parameter.query}}',
size: '={{$parameter.limit}}',
from: '={{$parameter.offset}}',
popularity: 0.99,
},
},
output: {
postReceive: [
async function (items) {
return items.flatMap(({ json }) =>
(json.objects as Array<{ package: PackageJson }>).map(
({ package: { name, version, description } }) =>
({ json: { name, version, description } }) as INodeExecutionData,
),
);
},
],
},
},
},
],
},
];
export const packageFields: INodeProperties[] = [
{
displayName: 'Package Name',
name: 'packageName',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: ['package'],
operation: ['getMetadata', 'getVersions'],
},
},
},
{
displayName: 'Package Version',
name: 'packageVersion',
type: 'string',
required: true,
default: 'latest',
displayOptions: {
show: {
resource: ['package'],
operation: ['getMetadata'],
},
},
},
{
displayName: 'Query',
name: 'query',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['package'],
operation: ['search'],
},
},
default: '',
description: 'The query text used to search for packages',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
default: 10,
typeOptions: {
minValue: 1,
maxValue: 100,
},
displayOptions: {
show: {
resource: ['package'],
operation: ['search'],
},
},
description: 'Max number of results to return',
},
{
displayName: 'Offset',
name: 'offset',
type: 'number',
default: 0,
typeOptions: {
minValue: 0,
},
displayOptions: {
show: {
resource: ['package'],
operation: ['search'],
},
},
description: 'Offset to return results from',
},
];
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2500 2500"><path fill="#c00" d="M0 0h2500v2500H0z"/><path fill="#fff" d="M1241.5 268.5h-973v1962.9h972.9V763.5h495v1467.9h495V268.5z"/></svg>

After

Width:  |  Height:  |  Size: 194 B

@@ -0,0 +1,37 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
describe('Test npm Node', () => {
const credentials = {
npmApi: {
accessToken: 'fake-npm-access-token',
registryUrl: 'https://fake.npm.registry',
},
};
beforeAll(() => {
const { registryUrl } = credentials.npmApi;
const mock = nock(registryUrl); //.matchHeader('Authorization', `Bearer ${accessToken}`);
mock.get('/-/package/n8n/dist-tags').reply(200, {
latest: '0.225.2',
next: '0.226.2',
});
mock.get('/n8n').reply(200, {
time: {
'0.225.2': '2023-04-25T09:45:36.407Z',
'0.226.2': '2023-05-03T09:41:30.844Z',
'0.227.0': '2023-05-03T13:44:32.079Z',
},
});
mock.get('/n8n/latest').reply(200, {
name: 'n8n',
version: '0.225.2',
rest: 'of the properties',
});
});
new NodeTestHarness().setupTests({ credentials });
});
@@ -0,0 +1,117 @@
{
"name": "Test NPM",
"nodes": [
{
"parameters": {},
"name": "Execute Workflow",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [620, 440]
},
{
"parameters": {
"resource": "distTag",
"operation": "getMany",
"packageName": "n8n"
},
"name": "Get All dist-tags",
"type": "n8n-nodes-base.npm",
"credentials": {
"npmApi": {
"id": "1"
}
}
},
{
"parameters": {
"resource": "package",
"operation": "getVersions",
"packageName": "n8n"
},
"name": "Get Versions",
"type": "n8n-nodes-base.npm",
"credentials": {
"npmApi": {
"id": "1"
}
}
},
{
"parameters": {
"resource": "package",
"operation": "getMetadata",
"packageName": "n8n",
"packageVersion": "latest"
},
"name": "Get Metadata",
"type": "n8n-nodes-base.npm",
"credentials": {
"npmApi": {
"id": "1"
}
}
}
],
"pinData": {
"Get All dist-tags": [
{
"json": {
"latest": "0.225.2",
"next": "0.226.2"
}
}
],
"Get Versions": [
{
"json": {
"version": "0.227.0",
"published_at": "2023-05-03T13:44:32.079Z"
}
},
{
"json": {
"version": "0.226.2",
"published_at": "2023-05-03T09:41:30.844Z"
}
},
{
"json": {
"version": "0.225.2",
"published_at": "2023-04-25T09:45:36.407Z"
}
}
],
"Get Metadata": [
{
"json": {
"name": "n8n",
"version": "0.225.2",
"rest": "of the properties"
}
}
]
},
"connections": {
"Execute Workflow": {
"main": [
[
{
"node": "Get All dist-tags",
"type": "main",
"index": 0
},
{
"node": "Get Versions",
"type": "main",
"index": 0
},
{
"node": "Get Metadata",
"type": "main",
"index": 0
}
]
]
}
}
}