first commit

This commit is contained in:
2025-10-26 23:10:15 +08:00
commit 8f0345b7be
14961 changed files with 2356381 additions and 0 deletions

88
node_modules/@n8n/node-cli/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,88 @@
# License
Portions of this software are licensed as follows:
- Content of branches other than the main branch (i.e. "master") are not licensed.
- Source code files that contain ".ee." in their filename or ".ee" in their dirname are NOT licensed under
the Sustainable Use License.
To use source code files that contain ".ee." in their filename or ".ee" in their dirname you must hold a
valid n8n Enterprise License specifically allowing you access to such source code files and as defined
in "LICENSE_EE.md".
- All third party components incorporated into the n8n Software are licensed under the original license
provided by the owner of the applicable component.
- Content outside of the above mentioned files or restrictions is available under the "Sustainable Use
License" as defined below.
## Sustainable Use License
Version 1.0
### Acceptance
By using the software, you agree to all of the terms and conditions below.
### Copyright License
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license
to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject
to the limitations below.
### Limitations
You may use or modify the software only for your own internal business purposes or for non-commercial or
personal use. You may distribute the software or provide it to others only if you do so free of charge for
non-commercial purposes. You may not alter, remove, or obscure any licensing, copyright, or other notices of
the licensor in the software. Any use of the licensors trademarks is subject to applicable law.
### Patents
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to
license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case
subject to the limitations and conditions in this license. This license does not cover any patent claims that
you cause to be infringed by modifications or additions to the software. If you or your company make any
written claim that the software infringes or contributes to infringement of any patent, your patent license
for the software granted under these terms ends immediately. If your company makes such a claim, your patent
license ends immediately for work on behalf of your company.
### Notices
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these
terms. If you modify the software, you must include in any modified copies of the software a prominent notice
stating that you have modified the software.
### No Other Rights
These terms do not imply any licenses other than those expressly granted in these terms.
### Termination
If you use the software in violation of these terms, such use is not licensed, and your license will
automatically terminate. If the licensor provides you with a notice of your violation, and you cease all
violation of this license no later than 30 days after you receive that notice, your license will be reinstated
retroactively. However, if you violate these terms after such reinstatement, any additional violation of these
terms will cause your license to terminate automatically and permanently.
### No Liability
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will
not be liable to you for any damages arising out of these terms or the use or nature of the software, under
any kind of legal claim.
### Definitions
The “licensor” is the entity offering these terms.
The “software” is the software the licensor makes available under these terms, including any portion of it.
“You” refers to the individual or entity agreeing to these terms.
“Your company” is any legal entity, sole proprietorship, or other kind of organization that you work for, plus
all organizations that have control over, are under the control of, or are under common control with that
organization. Control means ownership of substantially all the assets of an entity, or the power to direct its
management and policies by vote, contract, or otherwise. Control can be direct or indirect.
“Your license” is the license granted to you for the software under these terms.
“Use” means anything you do with the software requiring your license.
“Trademark” means trademarks, service marks, and similar rights.

27
node_modules/@n8n/node-cli/LICENSE_EE.md generated vendored Normal file
View File

@@ -0,0 +1,27 @@
# The n8n Enterprise License (the “Enterprise License”)
Copyright (c) 2022-present n8n GmbH.
With regard to the n8n Software:
This software and associated documentation files (the "Software") may only be used in production, if
you (and any entity that you represent) hold a valid n8n Enterprise license corresponding to your
usage. Subject to the foregoing sentence, you are free to modify this Software and publish patches
to the Software. You agree that n8n and/or its licensors (as applicable) retain all right, title and
interest in and to all such modifications and/or patches, and all such modifications and/or patches
may only be used, copied, modified, displayed, distributed, or otherwise exploited with a valid n8n
Enterprise license for the corresponding usage. Notwithstanding the foregoing, you may copy and
modify the Software for development and testing purposes, without requiring a subscription. You
agree that n8n and/or its licensors (as applicable) retain all right, title and interest in and to
all such modifications. You are not granted any other rights beyond what is expressly stated herein.
Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, and/or
sell the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For all third party components incorporated into the n8n Software, those components are licensed
under the original license provided by the owner of the applicable component.

289
node_modules/@n8n/node-cli/README.md generated vendored Normal file
View File

@@ -0,0 +1,289 @@
# @n8n/node-cli
Official CLI for developing community nodes for n8n.
## 🚀 Getting Started
**To create a new node**, run:
```bash
npm create @n8n/node@latest # or pnpm/yarn/...
```
This will generate a project with `npm` scripts that use this CLI under the hood.
## 📦 Generated Project Commands
After creating your node with `npm create @n8n/node`, you'll use these commands in your project:
### Development
```bash
npm run dev
# Runs: n8n-node dev
```
### Building
```bash
npm run build
# Runs: n8n-node build
```
### Linting
```bash
npm run lint
# Runs: n8n-node lint
npm run lint:fix
# Runs: n8n-node lint --fix
```
### Publishing
```bash
npm run release
# Runs: n8n-node release
```
## 🛠️ CLI Reference
> **Note:** These commands are typically wrapped by `npm` scripts in generated projects.
```bash
n8n-node [COMMAND] [OPTIONS]
```
### Commands
#### `n8n-node new`
Create a new node project.
```bash
n8n-node new [NAME] [OPTIONS]
```
**Flags:**
| Flag | Description |
|------|-------------|
| `-f, --force` | Overwrite destination folder if it already exists |
| `--skip-install` | Skip installing dependencies |
| `--template <template>` | Choose template: `declarative/custom`, `declarative/github-issues`, `programmatic/example` |
**Examples:**
```bash
n8n-node new
n8n-node new n8n-nodes-my-app --skip-install
n8n-node new n8n-nodes-my-app --force
n8n-node new n8n-nodes-my-app --template declarative/custom
```
> **Note:** This command is used internally by `npm create @n8n/node` to provide the interactive scaffolding experience.
#### `n8n-node dev`
Run n8n with your node in development mode with hot reload.
```bash
n8n-node dev [--external-n8n] [--custom-user-folder <value>]
```
**Flags:**
| Flag | Description |
|------|-------------|
| `--external-n8n` | Run n8n externally instead of in a subprocess |
| `--custom-user-folder <path>` | Folder to use to store user-specific n8n data (default: `~/.n8n-node-cli`) |
This command:
- Starts n8n on `http://localhost:5678` (unless using `--external-n8n`)
- Links your node to n8n's custom nodes directory (`~/.n8n-node-cli/.n8n/custom`)
- Rebuilds on file changes for live preview
- Watches for changes in your `src/` directory
**Examples:**
```bash
# Standard development with built-in n8n
n8n-node dev
# Use external n8n instance
n8n-node dev --external-n8n
# Custom n8n extensions directory
n8n-node dev --custom-user-folder /home/user
```
#### `n8n-node build`
Compile your node and prepare it for distribution.
```bash
n8n-node build
```
**Flags:** None
Generates:
- Compiled TypeScript code
- Bundled node package
- Optimized assets and icons
- Ready-to-publish package in `dist/`
#### `n8n-node lint`
Lint the node in the current directory.
```bash
n8n-node lint [--fix]
```
**Flags:**
| Flag | Description |
|------|-------------|
| `--fix` | Automatically fix problems |
**Examples:**
```bash
# Check for linting issues
n8n-node lint
# Automatically fix fixable issues
n8n-node lint --fix
```
#### `n8n-node cloud-support`
Manage n8n Cloud eligibility.
```bash
n8n-node cloud-support [enable|disable]
```
**Arguments:**
| Argument | Description |
|----------|-------------|
| _(none)_ | Show current cloud support status |
| `enable` | Enable strict mode + default ESLint config |
| `disable` | Allow custom ESLint config (disables cloud eligibility) |
Strict mode enforces the default ESLint configuration and community node rules required for n8n Cloud verification. When disabled, you can customize your ESLint config but your node won't be eligible for n8n Cloud verification.
#### `n8n-node release`
Publish your community node package to npm.
```bash
n8n-node release
```
**Flags:** None
This command handles the complete release process using [release-it](https://github.com/release-it/release-it):
- Builds the node
- Runs linting checks
- Updates changelog
- Creates git tags
- Creates GitHub releases
- Publishes to npm
## 🔄 Development Workflow
The recommended workflow using the scaffolding tool:
1. **Create your node**:
```bash
npm create @n8n/node my-awesome-node
cd my-awesome-node
```
2. **Start development**:
```bash
npm run dev
```
- Starts n8n on `http://localhost:5678`
- Links your node automatically
- Rebuilds on file changes
3. **Test your node** at `http://localhost:5678`
4. **Lint your code**:
```bash
npm run lint
```
5. **Build for production**:
```bash
npm run build
```
6. **Publish**:
```bash
npm run release
```
## 📁 Project Structure
The CLI expects your project to follow this structure:
```
my-node/
├── src/
│ ├── nodes/
│ │ └── MyNode/
│ │ ├── MyNode.node.ts
│ │ └── MyNode.node.json
│ └── credentials/
├── package.json
└── tsconfig.json
```
## ⚙️ Configuration
The CLI reads configuration from your `package.json`:
```json
{
"name": "n8n-nodes-my-awesome-node",
"n8n": {
"n8nNodesApiVersion": 1,
"nodes": [
"dist/nodes/MyNode/MyNode.node.js"
],
"credentials": [
"dist/credentials/MyNodeAuth.credentials.js"
]
}
}
```
## 🐛 Troubleshooting
### Development server issues
```bash
# Clear n8n custom nodes cache
rm -rf ~/.n8n-node-cli/.n8n/custom
# Restart development server
npm run dev
```
### Build failures
```bash
# Run linting first
npm run lint
# Clean build
npm run build
```
## 📚 Resources
- **[Creating Nodes Guide](https://docs.n8n.io/integrations/creating-nodes/)** - Complete documentation
- **[Node Development Reference](https://docs.n8n.io/integrations/creating-nodes/build/reference/)** - API specifications
- **[Community Forum](https://community.n8n.io)** - Get help and showcase your nodes
- **[@n8n/create-node](https://www.npmjs.com/package/@n8n/create-node)** - Recommended scaffolding tool
## 🤝 Contributing
Found an issue? Contribute to the [n8n repository](https://github.com/n8n-io/n8n) on GitHub.
---
**Happy node development! 🎉**

5
node_modules/@n8n/node-cli/bin/n8n-node.mjs generated vendored Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env node
import { execute } from '@oclif/core';
await execute({ dir: import.meta.url });

1
node_modules/@n8n/node-cli/dist/build.tsbuildinfo generated vendored Normal file

File diff suppressed because one or more lines are too long

8
node_modules/@n8n/node-cli/dist/commands/build.d.ts generated vendored Normal file
View File

@@ -0,0 +1,8 @@
import { Command } from '@oclif/core';
export default class Build extends Command {
static description: string;
static examples: string[];
static flags: {};
run(): Promise<void>;
}
export declare function copyStaticFiles(): Promise<void[]>;

62
node_modules/@n8n/node-cli/dist/commands/build.js generated vendored Normal file
View File

@@ -0,0 +1,62 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.copyStaticFiles = copyStaticFiles;
const prompts_1 = require("@clack/prompts");
const core_1 = require("@oclif/core");
const fast_glob_1 = __importDefault(require("fast-glob"));
const promises_1 = require("node:fs/promises");
const node_path_1 = __importDefault(require("node:path"));
const picocolors_1 = __importDefault(require("picocolors"));
const rimraf_1 = require("rimraf");
const child_process_1 = require("../utils/child-process");
const prompts_2 = require("../utils/prompts");
class Build extends core_1.Command {
async run() {
await this.parse(Build);
const commandName = 'n8n-node build';
(0, prompts_1.intro)(picocolors_1.default.inverse(` ${commandName} `));
await (0, prompts_2.ensureN8nPackage)(commandName);
const buildSpinner = (0, prompts_1.spinner)();
buildSpinner.start('Building TypeScript files');
await (0, rimraf_1.rimraf)('dist');
try {
await runTscBuild();
buildSpinner.stop('TypeScript build successful');
}
catch (error) {
(0, prompts_1.cancel)('TypeScript build failed');
this.exit(1);
}
const copyStaticFilesSpinner = (0, prompts_1.spinner)();
copyStaticFilesSpinner.start('Copying static files');
await copyStaticFiles();
copyStaticFilesSpinner.stop('Copied static files');
(0, prompts_1.outro)('✓ Build successful');
}
}
Build.description = 'Compile the node in the current directory and copy assets';
Build.examples = ['<%= config.bin %> <%= command.id %>'];
Build.flags = {};
exports.default = Build;
async function runTscBuild() {
return await (0, child_process_1.runCommand)('tsc', [], {
context: 'local',
printOutput: ({ stdout, stderr }) => {
prompts_1.log.error(stdout.concat(stderr).toString());
},
});
}
async function copyStaticFiles() {
const staticFiles = fast_glob_1.default.sync(['**/*.{png,svg}', '**/__schema__/**/*.json'], {
ignore: ['dist', 'node_modules'],
});
return await Promise.all(staticFiles.map(async (filePath) => {
const destPath = node_path_1.default.join('dist', filePath);
await (0, promises_1.mkdir)(node_path_1.default.dirname(destPath), { recursive: true });
return await (0, promises_1.cp)(filePath, destPath, { recursive: true });
}));
}
//# sourceMappingURL=build.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":";;;;;AAsDA,0CAYC;AAlED,4CAAoE;AACpE,sCAAsC;AACtC,0DAA6B;AAC7B,+CAA6C;AAC7C,0DAA6B;AAC7B,4DAAoC;AACpC,mCAAgC;AAEhC,0DAAoD;AACpD,8CAAoD;AAEpD,MAAqB,KAAM,SAAQ,cAAO;IAKzC,KAAK,CAAC,GAAG;QACR,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAExB,MAAM,WAAW,GAAG,gBAAgB,CAAC;QACrC,IAAA,eAAK,EAAC,oBAAU,CAAC,OAAO,CAAC,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;QAE9C,MAAM,IAAA,0BAAgB,EAAC,WAAW,CAAC,CAAC;QAEpC,MAAM,YAAY,GAAG,IAAA,iBAAO,GAAE,CAAC;QAC/B,YAAY,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAChD,MAAM,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC;QAErB,IAAI,CAAC;YACJ,MAAM,WAAW,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAA,gBAAM,EAAC,yBAAyB,CAAC,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;QAED,MAAM,sBAAsB,GAAG,IAAA,iBAAO,GAAE,CAAC;QACzC,sBAAsB,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACrD,MAAM,eAAe,EAAE,CAAC;QACxB,sBAAsB,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAEnD,IAAA,eAAK,EAAC,oBAAoB,CAAC,CAAC;IAC7B,CAAC;;AA9Be,iBAAW,GAAG,2DAA2D,CAAC;AAC1E,cAAQ,GAAG,CAAC,qCAAqC,CAAC,CAAC;AACnD,WAAK,GAAG,EAAE,CAAC;kBAHP,KAAK;AAkC1B,KAAK,UAAU,WAAW;IACzB,OAAO,MAAM,IAAA,0BAAU,EAAC,KAAK,EAAE,EAAE,EAAE;QAClC,OAAO,EAAE,OAAO;QAChB,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;YACnC,aAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7C,CAAC;KACD,CAAC,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,eAAe;IACpC,MAAM,WAAW,GAAG,mBAAI,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,yBAAyB,CAAC,EAAE;QAC5E,MAAM,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC;KAChC,CAAC,CAAC;IAEH,OAAO,MAAM,OAAO,CAAC,GAAG,CACvB,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAClC,MAAM,QAAQ,GAAG,mBAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC7C,MAAM,IAAA,gBAAK,EAAC,mBAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,OAAO,MAAM,IAAA,aAAE,EAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC,CAAC,CACF,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,14 @@
import { Command } from '@oclif/core';
export default class CloudSupport extends Command {
static description: string;
static examples: string[];
static args: {
action: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
};
run(): Promise<void>;
private enableCloudSupport;
private disableCloudSupport;
private updateEslintConfig;
private updateStrictMode;
private showCloudSupportStatus;
}

View File

@@ -0,0 +1,136 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const prompts_1 = require("@clack/prompts");
const core_1 = require("@oclif/core");
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const picocolors_1 = __importDefault(require("picocolors"));
const command_suggestions_1 = require("../utils/command-suggestions");
const package_1 = require("../utils/package");
const prompts_2 = require("../utils/prompts");
class CloudSupport extends core_1.Command {
async run() {
const { args } = await this.parse(CloudSupport);
await (0, prompts_2.ensureN8nPackage)('cloud-support');
const workingDir = process.cwd();
if (args.action === 'enable') {
await this.enableCloudSupport(workingDir);
}
else if (args.action === 'disable') {
await this.disableCloudSupport(workingDir);
}
else {
await this.showCloudSupportStatus(workingDir);
}
}
async enableCloudSupport(workingDir) {
(0, prompts_1.intro)(picocolors_1.default.inverse(' n8n-node cloud-support enable '));
await this.updateEslintConfig(workingDir, true);
prompts_1.log.success(`Updated ${picocolors_1.default.cyan('eslint.config.mjs')} to use default config`);
await this.updateStrictMode(workingDir, true);
prompts_1.log.success(`Enabled strict mode in ${picocolors_1.default.cyan('package.json')}`);
const lintCommand = await (0, command_suggestions_1.suggestLintCommand)();
(0, prompts_1.outro)(`Cloud support enabled. Run "${lintCommand}" to check compliance - your node must pass linting to be eligible for n8n Cloud publishing.`);
}
async disableCloudSupport(workingDir) {
(0, prompts_1.intro)(picocolors_1.default.inverse(' n8n-node cloud-support disable '));
prompts_1.log.warning(`This will make your node ineligible for n8n Cloud verification!
The following changes will be made:
• Switch to ${picocolors_1.default.magenta('configWithoutCloudSupport')} in ${picocolors_1.default.cyan('eslint.config.mjs')}
• Disable strict mode in ${picocolors_1.default.cyan('package.json')}`);
const confirmed = await (0, prompts_2.withCancelHandler)((0, prompts_1.confirm)({
message: 'Are you sure you want to disable cloud support?',
initialValue: false,
}));
if (!confirmed) {
(0, prompts_2.onCancel)('Cloud support unchanged');
return;
}
await this.updateEslintConfig(workingDir, false);
prompts_1.log.success(`Updated ${picocolors_1.default.cyan('eslint.config.mjs')} to use ${picocolors_1.default.magenta('configWithoutCloudSupport')}`);
await this.updateStrictMode(workingDir, false);
prompts_1.log.success(`Disabled strict mode in ${picocolors_1.default.cyan('package.json')}`);
(0, prompts_1.outro)("Cloud support disabled. Your node may pass linting but it won't pass verification for n8n Cloud.");
}
async updateEslintConfig(workingDir, enableCloud) {
const eslintConfigPath = node_path_1.default.resolve(workingDir, 'eslint.config.mjs');
const newConfig = enableCloud
? `import { config } from '@n8n/node-cli/eslint';
export default config;
`
: `import { configWithoutCloudSupport } from '@n8n/node-cli/eslint';
export default configWithoutCloudSupport;
`;
await promises_1.default.writeFile(eslintConfigPath, newConfig, 'utf-8');
}
async updateStrictMode(workingDir, enableStrict) {
await (0, package_1.updatePackageJson)(workingDir, (packageJson) => {
packageJson.n8n = packageJson.n8n ?? {};
packageJson.n8n.strict = enableStrict;
return packageJson;
});
}
async showCloudSupportStatus(workingDir) {
(0, prompts_1.intro)(picocolors_1.default.inverse(' n8n-node cloud-support '));
try {
const packageJson = await (0, package_1.getPackageJson)(workingDir);
const eslintConfigPath = node_path_1.default.resolve(workingDir, 'eslint.config.mjs');
const isStrictMode = packageJson?.n8n?.strict === true;
let isUsingDefaultConfig = false;
try {
const eslintConfig = await promises_1.default.readFile(eslintConfigPath, 'utf-8');
const normalizedConfig = eslintConfig.replace(/\s+/g, ' ').trim();
const expectedDefault = "import { config } from '@n8n/node-cli/eslint'; export default config;";
isUsingDefaultConfig = normalizedConfig === expectedDefault;
}
catch {
}
const isCloudSupported = isStrictMode && isUsingDefaultConfig;
if (isCloudSupported) {
prompts_1.log.success(`✅ Cloud support is ${picocolors_1.default.green('ENABLED')}
• Strict mode: ${picocolors_1.default.green('enabled')}
• ESLint config: ${picocolors_1.default.green('using default config')}
• Status: ${picocolors_1.default.green('eligible')} for n8n Cloud verification ${picocolors_1.default.dim('(if lint passes)')}`);
}
else {
prompts_1.log.warning(`⚠️ Cloud support is ${picocolors_1.default.yellow('DISABLED')}
• Strict mode: ${isStrictMode ? picocolors_1.default.green('enabled') : picocolors_1.default.red('disabled')}
• ESLint config: ${isUsingDefaultConfig ? picocolors_1.default.green('using default config') : picocolors_1.default.red('using custom config')}
• Status: ${picocolors_1.default.red('NOT eligible')} for n8n Cloud verification`);
}
const enableCommand = await (0, command_suggestions_1.suggestCloudSupportCommand)('enable');
const disableCommand = await (0, command_suggestions_1.suggestCloudSupportCommand)('disable');
const lintCommand = await (0, command_suggestions_1.suggestLintCommand)();
prompts_1.log.info(`Available commands:
${enableCommand} - Enable cloud support
${disableCommand} - Disable cloud support
${lintCommand} - Check compliance for cloud publishing`);
(0, prompts_1.outro)('Use the commands above to change cloud support settings or check compliance');
}
catch (error) {
prompts_1.log.error('Failed to read package.json or determine cloud support status');
(0, prompts_1.outro)('Make sure you are in the root directory of your node package');
}
}
}
CloudSupport.description = 'Enable or disable cloud support for this node';
CloudSupport.examples = [
'<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> enable',
'<%= config.bin %> <%= command.id %> disable',
];
CloudSupport.args = {
action: core_1.Args.string({
description: 'Action to perform (defaults to showing current status)',
required: false,
options: ['enable', 'disable'],
}),
};
exports.default = CloudSupport;
//# sourceMappingURL=cloud-support.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"cloud-support.js","sourceRoot":"","sources":["../../src/commands/cloud-support.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,10 @@
import { Command } from '@oclif/core';
export default class Dev extends Command {
static description: string;
static examples: string[];
static flags: {
'external-n8n': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
'custom-user-folder': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
};
run(): Promise<void>;
}

109
node_modules/@n8n/node-cli/dist/commands/dev/index.js generated vendored Normal file
View File

@@ -0,0 +1,109 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const prompts_1 = require("@clack/prompts");
const core_1 = require("@oclif/core");
const node_os_1 = __importDefault(require("node:os"));
const node_path_1 = __importDefault(require("node:path"));
const picocolors_1 = __importDefault(require("picocolors"));
const rimraf_1 = require("rimraf");
const filesystem_1 = require("../../utils/filesystem");
const package_manager_1 = require("../../utils/package-manager");
const prompts_2 = require("../../utils/prompts");
const validation_1 = require("../../utils/validation");
const build_1 = require("../build");
const utils_1 = require("./utils");
const child_process_1 = require("../../utils/child-process");
class Dev extends core_1.Command {
async run() {
const { flags } = await this.parse(Dev);
const packageManager = (await (0, package_manager_1.detectPackageManager)()) ?? 'npm';
const { runPersistentCommand } = (0, utils_1.commands)();
(0, prompts_1.intro)(picocolors_1.default.inverse(' n8n-node dev '));
await (0, prompts_2.ensureN8nPackage)('n8n-node dev');
await (0, build_1.copyStaticFiles)();
const linkingSpinner = (0, prompts_1.spinner)();
linkingSpinner.start('Linking custom node to n8n');
await (0, child_process_1.runCommand)(packageManager, ['link']);
const n8nUserFolder = flags['custom-user-folder'];
const customNodesFolder = node_path_1.default.join(n8nUserFolder, '.n8n', 'custom');
await (0, filesystem_1.ensureFolder)(customNodesFolder);
const packageName = await (0, utils_1.readPackageName)();
const invalidNodeNameError = (0, validation_1.validateNodeName)(packageName);
if (invalidNodeNameError)
return (0, prompts_2.onCancel)(invalidNodeNameError);
await (0, rimraf_1.rimraf)(node_path_1.default.join(customNodesFolder, 'package.json'));
await (0, child_process_1.runCommand)(packageManager, ['link', packageName], {
cwd: customNodesFolder,
});
linkingSpinner.stop('Linked custom node to n8n');
if (!flags['external-n8n']) {
let setupComplete = false;
const npxN8nSpinner = (0, prompts_1.spinner)();
npxN8nSpinner.start('Starting n8n dev server');
prompts_1.log.warn(picocolors_1.default.dim('First run may take a few minutes while dependencies are installed'));
try {
await Promise.race([
new Promise((resolve) => {
runPersistentCommand('npx', ['-y', '--quiet', '--prefer-online', 'n8n@latest'], {
cwd: n8nUserFolder,
env: {
...process.env,
N8N_DEV_RELOAD: 'true',
N8N_RUNNERS_ENABLED: 'true',
DB_SQLITE_POOL_SIZE: '10',
N8N_USER_FOLDER: n8nUserFolder,
},
name: 'n8n',
color: picocolors_1.default.green,
allowOutput: (line) => {
if (line.includes('Initializing n8n process')) {
resolve();
}
return setupComplete;
},
});
}),
new Promise((_, reject) => {
setTimeout(() => {
const error = new Error('n8n startup timeout after 120 seconds');
reject(error);
}, 120_000);
}),
]);
setupComplete = true;
npxN8nSpinner.stop('Started n8n dev server');
}
catch (error) {
npxN8nSpinner.stop('Failed to start n8n dev server');
(0, prompts_2.onCancel)(error instanceof Error ? error.message : 'Unknown error occurred', 1);
return;
}
}
(0, prompts_1.outro)('✓ Setup complete');
runPersistentCommand(packageManager, ['exec', '--', 'tsc', '--watch'], {
name: 'build',
color: picocolors_1.default.cyan,
});
}
}
Dev.description = 'Run n8n with the node and rebuild on changes for live preview';
Dev.examples = [
'<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> --external-n8n',
'<%= config.bin %> <%= command.id %> --custom-user-folder /Users/test',
];
Dev.flags = {
'external-n8n': core_1.Flags.boolean({
default: false,
description: 'By default n8n-node dev will run n8n in a sub process. Enable this option if you would like to run n8n elsewhere. Make sure to set N8N_DEV_RELOAD to true in that case.',
}),
'custom-user-folder': core_1.Flags.directory({
default: node_path_1.default.join(node_os_1.default.homedir(), '.n8n-node-cli'),
description: 'Folder to use to store user-specific n8n data. By default it will use ~/.n8n-node-cli. The node CLI will install your node here.',
}),
};
exports.default = Dev;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/dev/index.ts"],"names":[],"mappings":";;;;;AAAA,4CAA4D;AAC5D,sCAA6C;AAC7C,sDAAyB;AACzB,0DAA6B;AAC7B,4DAAoC;AACpC,mCAAgC;AAEhC,uDAAsD;AACtD,iEAAmE;AACnE,iDAAiE;AACjE,uDAA0D;AAC1D,oCAA2C;AAC3C,mCAAoD;AACpD,6DAAuD;AAEvD,MAAqB,GAAI,SAAQ,cAAO;IAoBvC,KAAK,CAAC,GAAG;QACR,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAExC,MAAM,cAAc,GAAG,CAAC,MAAM,IAAA,sCAAoB,GAAE,CAAC,IAAI,KAAK,CAAC;QAC/D,MAAM,EAAE,oBAAoB,EAAE,GAAG,IAAA,gBAAQ,GAAE,CAAC;QAE5C,IAAA,eAAK,EAAC,oBAAU,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAE5C,MAAM,IAAA,0BAAgB,EAAC,cAAc,CAAC,CAAC;QAEvC,MAAM,IAAA,uBAAe,GAAE,CAAC;QAExB,MAAM,cAAc,GAAG,IAAA,iBAAO,GAAE,CAAC;QACjC,cAAc,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACnD,MAAM,IAAA,0BAAU,EAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QAE3C,MAAM,aAAa,GAAG,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAClD,MAAM,iBAAiB,GAAG,mBAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAErE,MAAM,IAAA,yBAAY,EAAC,iBAAiB,CAAC,CAAC;QAEtC,MAAM,WAAW,GAAG,MAAM,IAAA,uBAAe,GAAE,CAAC;QAC5C,MAAM,oBAAoB,GAAG,IAAA,6BAAgB,EAAC,WAAW,CAAC,CAAC;QAE3D,IAAI,oBAAoB;YAAE,OAAO,IAAA,kBAAQ,EAAC,oBAAoB,CAAC,CAAC;QAGhE,MAAM,IAAA,eAAM,EAAC,mBAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC,CAAC;QAC3D,MAAM,IAAA,0BAAU,EAAC,cAAc,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE;YACvD,GAAG,EAAE,iBAAiB;SACtB,CAAC,CAAC;QAEH,cAAc,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAEjD,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;YAC5B,IAAI,aAAa,GAAG,KAAK,CAAC;YAC1B,MAAM,aAAa,GAAG,IAAA,iBAAO,GAAE,CAAC;YAChC,aAAa,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAC/C,aAAG,CAAC,IAAI,CAAC,oBAAU,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC,CAAC;YAG9F,IAAI,CAAC;gBACJ,MAAM,OAAO,CAAC,IAAI,CAAC;oBAClB,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;wBAC7B,oBAAoB,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,iBAAiB,EAAE,YAAY,CAAC,EAAE;4BAC/E,GAAG,EAAE,aAAa;4BAClB,GAAG,EAAE;gCACJ,GAAG,OAAO,CAAC,GAAG;gCACd,cAAc,EAAE,MAAM;gCACtB,mBAAmB,EAAE,MAAM;gCAC3B,mBAAmB,EAAE,IAAI;gCACzB,eAAe,EAAE,aAAa;6BAC9B;4BACD,IAAI,EAAE,KAAK;4BACX,KAAK,EAAE,oBAAU,CAAC,KAAK;4BACvB,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE;gCACrB,IAAI,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,EAAE,CAAC;oCAC/C,OAAO,EAAE,CAAC;gCACX,CAAC;gCAED,OAAO,aAAa,CAAC;4BACtB,CAAC;yBACD,CAAC,CAAC;oBACJ,CAAC,CAAC;oBACF,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;wBAC/B,UAAU,CAAC,GAAG,EAAE;4BACf,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;4BACjE,MAAM,CAAC,KAAK,CAAC,CAAC;wBACf,CAAC,EAAE,OAAO,CAAC,CAAC;oBACb,CAAC,CAAC;iBACF,CAAC,CAAC;gBAEH,aAAa,GAAG,IAAI,CAAC;gBACrB,aAAa,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,aAAa,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;gBACrD,IAAA,kBAAQ,EAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,EAAE,CAAC,CAAC,CAAC;gBAC/E,OAAO;YACR,CAAC;QACF,CAAC;QAED,IAAA,eAAK,EAAC,kBAAkB,CAAC,CAAC;QAG1B,oBAAoB,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE;YACtE,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,oBAAU,CAAC,IAAI;SACtB,CAAC,CAAC;IACJ,CAAC;;AA3Ge,eAAW,GAAG,+DAA+D,CAAC;AAC9E,YAAQ,GAAG;IAC1B,qCAAqC;IACrC,oDAAoD;IACpD,sEAAsE;CACtE,CAAC;AACc,SAAK,GAAG;IACvB,cAAc,EAAE,YAAK,CAAC,OAAO,CAAC;QAC7B,OAAO,EAAE,KAAK;QACd,WAAW,EACV,yKAAyK;KAC1K,CAAC;IACF,oBAAoB,EAAE,YAAK,CAAC,SAAS,CAAC;QACrC,OAAO,EAAE,mBAAI,CAAC,IAAI,CAAC,iBAAE,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC;QACjD,WAAW,EACV,kIAAkI;KACnI,CAAC;CACF,CAAC;kBAlBkB,GAAG"}

View File

@@ -0,0 +1,12 @@
import { type ChildProcess } from 'node:child_process';
import type { Formatter } from 'picocolors/types';
export declare function commands(): {
runPersistentCommand: (cmd: string, args: string[], opts?: {
cwd?: string;
env?: NodeJS.ProcessEnv;
name?: string;
color?: Formatter;
allowOutput?: (line: string) => boolean;
}) => ChildProcess;
};
export declare function readPackageName(): Promise<string>;

115
node_modules/@n8n/node-cli/dist/commands/dev/utils.js generated vendored Normal file
View File

@@ -0,0 +1,115 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.commands = commands;
exports.readPackageName = readPackageName;
const node_child_process_1 = require("node:child_process");
const promises_1 = __importDefault(require("node:fs/promises"));
const json_1 = require("../../utils/json");
function commands() {
const childProcesses = [];
let isShuttingDown = false;
const registerChild = (child) => {
childProcesses.push(child);
};
const killChild = (child, signal) => {
if (!child.killed) {
child.kill(signal);
}
};
const forceKillAllChildren = () => {
childProcesses.forEach((child) => killChild(child, 'SIGKILL'));
process.exit(1);
};
const gracefulShutdown = (signal) => {
if (childProcesses.length === 0) {
process.exit();
return;
}
let exitedCount = 0;
const totalChildren = childProcesses.length;
const forceExitTimer = setTimeout(forceKillAllChildren, 5000);
const onChildExit = () => {
exitedCount++;
if (exitedCount === totalChildren) {
clearTimeout(forceExitTimer);
process.exit();
}
};
childProcesses.forEach((child) => {
if (!child.killed) {
child.once('exit', onChildExit);
killChild(child, signal);
setTimeout(() => killChild(child, 'SIGKILL'), 5000);
}
else {
onChildExit();
}
});
};
const handleSignal = (signal) => {
if (isShuttingDown) {
console.log('\nForce killing processes...');
forceKillAllChildren();
return;
}
isShuttingDown = true;
if (signal === 'SIGINT') {
console.log('\nShutting down gracefully... (press Ctrl+C again to force quit)');
}
gracefulShutdown(signal);
};
process.on('SIGINT', () => handleSignal('SIGINT'));
process.on('SIGTERM', () => handleSignal('SIGTERM'));
const stripAnsiCodes = (input) => input
.replace(/\x1Bc/g, '')
.replace(/\x1B\[2J/g, '')
.replace(/\x1B\[3J/g, '')
.replace(/\x1B\[H/g, '')
.replace(/\x1B\[0?m/g, '');
const createLogger = (name, color, allowOutput) => (text) => {
if (allowOutput && !allowOutput(text))
return;
const prefix = name ? (color ? color(`[${name}]`) : `[${name}]`) : '';
console.log(prefix ? `${prefix} ${text}` : text);
};
const processOutput = (data, logger) => {
data
.toString()
.split('\n')
.map((line) => stripAnsiCodes(line).trim())
.filter(Boolean)
.forEach(logger);
};
const runPersistentCommand = (cmd, args, opts = {}) => {
const child = (0, node_child_process_1.spawn)(cmd, args, {
cwd: opts.cwd,
env: { ...process.env, ...opts.env },
stdio: ['inherit', 'pipe', 'pipe'],
shell: process.platform === 'win32',
});
registerChild(child);
const logger = createLogger(opts.name, opts.color, opts.allowOutput);
const handleOutput = (data) => processOutput(data, logger);
child.stdout?.on('data', handleOutput);
child.stderr?.on('data', handleOutput);
child.on('close', (code) => {
if (!isShuttingDown) {
console.log(`${opts.name ?? cmd} exited with code ${code}`);
process.exit(code ?? 0);
}
});
return child;
};
return {
runPersistentCommand,
};
}
async function readPackageName() {
return await promises_1.default
.readFile('package.json', 'utf-8')
.then((packageJson) => (0, json_1.jsonParse)(packageJson)?.name ?? 'unknown');
}
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/commands/dev/utils.ts"],"names":[],"mappings":"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"}

13
node_modules/@n8n/node-cli/dist/commands/lint.d.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import { Command } from '@oclif/core';
export default class Lint extends Command {
static description: string;
static examples: string[];
static flags: {
fix: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
};
run(): Promise<void>;
private checkStrictMode;
private verifyEslintConfig;
private handleLintErrors;
private containsCloudOnlyErrors;
}

121
node_modules/@n8n/node-cli/dist/commands/lint.js generated vendored Normal file
View File

@@ -0,0 +1,121 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const picocolors_1 = __importDefault(require("picocolors"));
const child_process_1 = require("../utils/child-process");
const command_suggestions_1 = require("../utils/command-suggestions");
const package_1 = require("../utils/package");
const prompts_1 = require("../utils/prompts");
const validation_1 = require("../utils/validation");
class Lint extends core_1.Command {
async run() {
const { flags } = await this.parse(Lint);
await (0, prompts_1.ensureN8nPackage)('lint');
await this.checkStrictMode();
const args = ['.'];
if (flags.fix) {
args.push('--fix');
}
let eslintOutput = '';
try {
await (0, child_process_1.runCommand)('eslint', args, {
context: 'local',
stdio: 'pipe',
env: { ...process.env, FORCE_COLOR: '1' },
printOutput: ({ stdout, stderr }) => {
eslintOutput = Buffer.concat([...stdout, ...stderr]).toString();
process.stdout.write(Buffer.concat(stdout));
process.stderr.write(Buffer.concat(stderr));
},
});
}
catch (error) {
if (error instanceof child_process_1.ChildProcessError) {
await this.handleLintErrors(eslintOutput);
if (error.signal) {
process.kill(process.pid, error.signal);
}
else {
process.exit(error.code ?? 0);
}
}
throw error;
}
}
async checkStrictMode() {
try {
const workingDir = process.cwd();
const packageJson = await (0, package_1.getPackageJson)(workingDir);
if (!packageJson?.n8n?.strict) {
return;
}
await this.verifyEslintConfig(workingDir);
}
catch (error) {
return;
}
}
async verifyEslintConfig(workingDir) {
const eslintConfigPath = node_path_1.default.resolve(workingDir, 'eslint.config.mjs');
const templatePath = node_path_1.default.resolve(__dirname, '../template/templates/shared/default/eslint.config.mjs');
const expectedConfig = await promises_1.default.readFile(templatePath, 'utf-8');
try {
const currentConfig = await promises_1.default.readFile(eslintConfigPath, 'utf-8');
const normalizedCurrent = currentConfig.replace(/\s+/g, ' ').trim();
const normalizedExpected = expectedConfig.replace(/\s+/g, ' ').trim();
if (normalizedCurrent !== normalizedExpected) {
const enableCommand = await (0, command_suggestions_1.suggestCloudSupportCommand)('enable');
this.log(`${picocolors_1.default.red('Strict mode violation:')} ${picocolors_1.default.cyan('eslint.config.mjs')} has been modified from the default configuration.
${picocolors_1.default.dim('Expected:')}
${picocolors_1.default.gray(expectedConfig)}
To restore default config: ${enableCommand}
To disable strict mode: set ${picocolors_1.default.yellow('"strict": false')} in ${picocolors_1.default.cyan('package.json')} under the ${picocolors_1.default.yellow('"n8n"')} section.`);
process.exit(1);
}
}
catch (error) {
if ((0, validation_1.isEnoentError)(error)) {
const enableCommand = await (0, command_suggestions_1.suggestCloudSupportCommand)('enable');
this.log(`${picocolors_1.default.red('Strict mode violation:')} ${picocolors_1.default.cyan('eslint.config.mjs')} not found. Expected default configuration.
To create default config: ${enableCommand}`);
process.exit(1);
}
throw error;
}
}
async handleLintErrors(eslintOutput) {
if (this.containsCloudOnlyErrors(eslintOutput)) {
const disableCommand = await (0, command_suggestions_1.suggestCloudSupportCommand)('disable');
this.log(`${picocolors_1.default.yellow('⚠️ n8n Cloud compatibility issues detected')}
These lint failures prevent verification to n8n Cloud.
To disable cloud compatibility checks:
${disableCommand}
${picocolors_1.default.dim(`Note: This will switch to ${picocolors_1.default.magenta('configWithoutCloudSupport')} and disable strict mode`)}`);
}
}
containsCloudOnlyErrors(errorMessage) {
const cloudOnlyRules = [
'@n8n/eslint-plugin-community-nodes/no-restricted-globals',
'@n8n/eslint-plugin-community-nodes/no-restricted-imports',
];
return cloudOnlyRules.some((rule) => errorMessage.includes(rule));
}
}
Lint.description = 'Lint the node in the current directory. Includes auto-fixing. In strict mode, verifies eslint config is unchanged from default.';
Lint.examples = ['<%= config.bin %> <%= command.id %>'];
Lint.flags = {
fix: core_1.Flags.boolean({ description: 'Automatically fix problems', default: false }),
};
exports.default = Lint;
//# sourceMappingURL=lint.js.map

1
node_modules/@n8n/node-cli/dist/commands/lint.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/commands/lint.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,14 @@
import { Command } from '@oclif/core';
export default class New extends Command {
static description: string;
static examples: string[];
static args: {
name: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
};
static flags: {
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
'skip-install': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
template: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
};
run(): Promise<void>;
}

142
node_modules/@n8n/node-cli/dist/commands/new/index.js generated vendored Normal file
View File

@@ -0,0 +1,142 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const prompts_1 = require("@clack/prompts");
const core_1 = require("@oclif/core");
const change_case_1 = require("change-case");
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const picocolors_1 = __importDefault(require("picocolors"));
const prompts_2 = require("./prompts");
const utils_1 = require("./utils");
const templates_1 = require("../../template/templates");
const child_process_1 = require("../../utils/child-process");
const filesystem_1 = require("../../utils/filesystem");
const git_1 = require("../../utils/git");
const package_manager_1 = require("../../utils/package-manager");
const prompts_3 = require("../../utils/prompts");
const validation_1 = require("../../utils/validation");
class New extends core_1.Command {
async run() {
const { flags, args } = await this.parse(New);
const [typeFlag, templateFlag] = flags.template?.split('/') ?? [];
(0, prompts_1.intro)(picocolors_1.default.inverse((0, utils_1.createIntro)()));
const nodeName = args.name ?? (await (0, prompts_2.nodeNamePrompt)());
const invalidNodeNameError = (0, validation_1.validateNodeName)(nodeName);
if (invalidNodeNameError)
return (0, prompts_3.onCancel)(invalidNodeNameError);
const destination = node_path_1.default.resolve(process.cwd(), nodeName);
let overwrite = false;
if (await (0, filesystem_1.folderExists)(destination)) {
if (!flags.force) {
const shouldOverwrite = await (0, prompts_1.confirm)({
message: `./${nodeName} already exists, do you want to overwrite?`,
});
if ((0, prompts_1.isCancel)(shouldOverwrite) || !shouldOverwrite)
return (0, prompts_3.onCancel)();
}
overwrite = true;
}
const type = typeFlag ?? (await (0, prompts_2.nodeTypePrompt)());
if (!(0, templates_1.isTemplateType)(type)) {
return (0, prompts_3.onCancel)(`Invalid template type: ${type}`);
}
let template = templates_1.templates.programmatic.example;
if (templateFlag) {
const name = (0, change_case_1.camelCase)(templateFlag);
if (!(0, templates_1.isTemplateName)(type, name)) {
return (0, prompts_3.onCancel)(`Invalid template name: ${name} for type: ${type}`);
}
template = (0, templates_1.getTemplate)(type, name);
}
else if (type === 'declarative') {
const chosenTemplate = await (0, prompts_2.declarativeTemplatePrompt)();
template = (0, templates_1.getTemplate)('declarative', chosenTemplate);
}
const config = (await template.prompts?.()) ?? {};
const packageManager = (await (0, package_manager_1.detectPackageManager)()) ?? 'npm';
const templateData = {
destinationPath: destination,
nodePackageName: nodeName,
config,
user: (0, git_1.tryReadGitUser)(),
packageManager: {
name: packageManager,
installCommand: packageManager === 'npm' ? 'ci' : 'install',
},
};
const copyingSpinner = (0, prompts_1.spinner)();
copyingSpinner.start('Copying files');
if (overwrite) {
await promises_1.default.rm(destination, { recursive: true, force: true });
}
await (0, filesystem_1.delayAtLeast)(template.run(templateData), 1000);
copyingSpinner.stop('Files copied');
const gitSpinner = (0, prompts_1.spinner)();
gitSpinner.start('Initializing git repository');
try {
await (0, git_1.initGit)(destination);
gitSpinner.stop('Git repository initialized');
}
catch (error) {
if (error instanceof child_process_1.ChildProcessError) {
gitSpinner.stop(`Could not initialize git repository: ${error.message}`, error.code ?? undefined);
process.exit(error.code ?? 1);
}
else {
throw error;
}
}
if (!flags['skip-install']) {
const installingSpinner = (0, prompts_1.spinner)();
installingSpinner.start('Installing dependencies');
try {
await (0, filesystem_1.delayAtLeast)((0, child_process_1.runCommand)(packageManager, ['install'], {
cwd: destination,
printOutput: ({ stdout, stderr }) => {
prompts_1.log.error(stdout.concat(stderr).toString());
},
}), 1000);
}
catch (error) {
if (error instanceof child_process_1.ChildProcessError) {
installingSpinner.stop(`Could not install dependencies: ${error.message}`, error.code ?? undefined);
process.exit(error.code ?? 1);
}
else {
throw error;
}
}
installingSpinner.stop('Dependencies installed');
}
(0, prompts_1.note)(`cd ./${nodeName} && ${packageManager} run dev
📚 Documentation: https://docs.n8n.io/integrations/creating-nodes/build/${type}-style-node/
💬 Community: https://community.n8n.io`, 'Next Steps');
(0, prompts_1.outro)(`Created ./${nodeName}`);
}
}
New.description = 'Create a starter community node in a new directory';
New.examples = [
'<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> n8n-nodes-my-app --skip-install',
'<%= config.bin %> <%= command.id %> n8n-nodes-my-app --force',
'<%= config.bin %> <%= command.id %> n8n-nodes-my-app --template declarative/custom',
];
New.args = {
name: core_1.Args.string({ name: 'Name' }),
};
New.flags = {
force: core_1.Flags.boolean({
char: 'f',
description: 'Overwrite destination folder if it already exists',
}),
'skip-install': core_1.Flags.boolean({ description: 'Skip installing dependencies' }),
template: core_1.Flags.string({
options: ['declarative/github-issues', 'declarative/custom', 'programmatic/example'],
}),
};
exports.default = New;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/new/index.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,3 @@
export declare const nodeNamePrompt: () => Promise<string>;
export declare const nodeTypePrompt: () => Promise<"declarative" | "programmatic">;
export declare const declarativeTemplatePrompt: () => Promise<"custom" | "githubIssues">;

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.declarativeTemplatePrompt = exports.nodeTypePrompt = exports.nodeNamePrompt = void 0;
const prompts_1 = require("@clack/prompts");
const templates_1 = require("../../template/templates");
const prompts_2 = require("../../utils/prompts");
const validation_1 = require("../../utils/validation");
const nodeNamePrompt = async () => await (0, prompts_2.withCancelHandler)((0, prompts_1.text)({
message: "Package name (must start with 'n8n-nodes-' or '@org/n8n-nodes-')",
placeholder: 'n8n-nodes-my-app',
validate: validation_1.validateNodeName,
defaultValue: 'n8n-nodes-my-app',
}));
exports.nodeNamePrompt = nodeNamePrompt;
const nodeTypePrompt = async () => await (0, prompts_2.withCancelHandler)((0, prompts_1.select)({
message: 'What kind of node are you building?',
options: [
{
label: 'HTTP API',
value: 'declarative',
hint: 'Low-code, faster approval for n8n Cloud',
},
{
label: 'Other',
value: 'programmatic',
hint: 'Programmatic node with full flexibility',
},
],
initialValue: 'declarative',
}));
exports.nodeTypePrompt = nodeTypePrompt;
const declarativeTemplatePrompt = async () => await (0, prompts_2.withCancelHandler)((0, prompts_1.select)({
message: 'What template do you want to use?',
options: Object.entries(templates_1.templates.declarative).map(([value, template]) => ({
value: value,
label: template.name,
hint: template.description,
})),
initialValue: 'githubIssues',
}));
exports.declarativeTemplatePrompt = declarativeTemplatePrompt;
//# sourceMappingURL=prompts.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/commands/new/prompts.ts"],"names":[],"mappings":";;;AAAA,4CAA8C;AAE9C,wDAAqD;AACrD,iDAAwD;AACxD,uDAA0D;AAEnD,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE,CACxC,MAAM,IAAA,2BAAiB,EACtB,IAAA,cAAI,EAAC;IACJ,OAAO,EAAE,kEAAkE;IAC3E,WAAW,EAAE,kBAAkB;IAC/B,QAAQ,EAAE,6BAAgB;IAC1B,YAAY,EAAE,kBAAkB;CAChC,CAAC,CACF,CAAC;AARU,QAAA,cAAc,kBAQxB;AAEI,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE,CACxC,MAAM,IAAA,2BAAiB,EACtB,IAAA,gBAAM,EAAiC;IACtC,OAAO,EAAE,qCAAqC;IAC9C,OAAO,EAAE;QACR;YACC,KAAK,EAAE,UAAU;YACjB,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,yCAAyC;SAC/C;QACD;YACC,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,yCAAyC;SAC/C;KACD;IACD,YAAY,EAAE,aAAa;CAC3B,CAAC,CACF,CAAC;AAlBU,QAAA,cAAc,kBAkBxB;AAEI,MAAM,yBAAyB,GAAG,KAAK,IAAI,EAAE,CACnD,MAAM,IAAA,2BAAiB,EACtB,IAAA,gBAAM,EAAqC;IAC1C,OAAO,EAAE,mCAAmC;IAC5C,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,qBAAS,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1E,KAAK,EAAE,KAA2C;QAClD,KAAK,EAAE,QAAQ,CAAC,IAAI;QACpB,IAAI,EAAE,QAAQ,CAAC,WAAW;KAC1B,CAAC,CAAC;IACH,YAAY,EAAE,cAAc;CAC5B,CAAC,CACF,CAAC;AAXU,QAAA,yBAAyB,6BAWnC"}

View File

@@ -0,0 +1 @@
export declare const createIntro: () => string;

11
node_modules/@n8n/node-cli/dist/commands/new/utils.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createIntro = void 0;
const package_manager_1 = require("../../utils/package-manager");
const createIntro = () => {
const maybePackageManager = (0, package_manager_1.detectPackageManagerFromUserAgent)();
const packageManager = maybePackageManager ?? 'npm';
return maybePackageManager ? ` ${packageManager} create @n8n/node ` : ' n8n-node new ';
};
exports.createIntro = createIntro;
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/commands/new/utils.ts"],"names":[],"mappings":";;;AAAA,iEAAgF;AAEzE,MAAM,WAAW,GAAG,GAAG,EAAE;IAC/B,MAAM,mBAAmB,GAAG,IAAA,mDAAiC,GAAE,CAAC;IAChE,MAAM,cAAc,GAAG,mBAAmB,IAAI,KAAK,CAAC;IACpD,OAAO,mBAAmB,CAAC,CAAC,CAAC,IAAI,cAAc,oBAAoB,CAAC,CAAC,CAAC,gBAAgB,CAAC;AACxF,CAAC,CAAC;AAJW,QAAA,WAAW,eAItB"}

View File

@@ -0,0 +1,8 @@
import { Command } from '@oclif/core';
export default class Prerelease extends Command {
static description: string;
static examples: string[];
static flags: {};
static hidden: boolean;
run(): Promise<void>;
}

20
node_modules/@n8n/node-cli/dist/commands/prerelease.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const package_manager_1 = require("../utils/package-manager");
class Prerelease extends core_1.Command {
async run() {
await this.parse(Prerelease);
const packageManager = (await (0, package_manager_1.detectPackageManager)()) ?? 'npm';
if (!process.env.RELEASE_MODE) {
this.log(`Run \`${packageManager} run release\` to publish the package`);
process.exit(1);
}
}
}
Prerelease.description = 'Only for internal use. Prevent npm publish, instead require npm run release';
Prerelease.examples = ['<%= config.bin %> <%= command.id %>'];
Prerelease.flags = {};
Prerelease.hidden = true;
exports.default = Prerelease;
//# sourceMappingURL=prerelease.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"prerelease.js","sourceRoot":"","sources":["../../src/commands/prerelease.ts"],"names":[],"mappings":";;AAAA,sCAAsC;AAEtC,8DAAgE;AAEhE,MAAqB,UAAW,SAAQ,cAAO;IAO9C,KAAK,CAAC,GAAG;QACR,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAE7B,MAAM,cAAc,GAAG,CAAC,MAAM,IAAA,sCAAoB,GAAE,CAAC,IAAI,KAAK,CAAC;QAE/D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,SAAS,cAAc,uCAAuC,CAAC,CAAC;YACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;;AAfe,sBAAW,GAC1B,6EAA6E,CAAC;AAC/D,mBAAQ,GAAG,CAAC,qCAAqC,CAAC,CAAC;AACnD,gBAAK,GAAG,EAAE,CAAC;AACX,iBAAM,GAAG,IAAI,CAAC;kBALV,UAAU"}

View File

@@ -0,0 +1,7 @@
import { Command } from '@oclif/core';
export default class Release extends Command {
static description: string;
static examples: string[];
static flags: {};
run(): Promise<void>;
}

49
node_modules/@n8n/node-cli/dist/commands/release.js generated vendored Normal file
View File

@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const child_process_1 = require("../utils/child-process");
const package_manager_1 = require("../utils/package-manager");
class Release extends core_1.Command {
async run() {
await this.parse(Release);
const pm = (await (0, package_manager_1.detectPackageManager)()) ?? 'npm';
try {
await (0, child_process_1.runCommand)('release-it', [
'-n',
'--git.requireBranch main',
'--git.requireCleanWorkingDir',
'--git.requireUpstream',
'--git.requireCommits',
'--git.commit',
'--git.tag',
'--git.push',
'--git.changelog="npx auto-changelog --stdout --unreleased --commit-limit false -u --hide-credit"',
'--github.release',
`--hooks.before:init="${pm} run lint && ${pm} run build"`,
'--hooks.after:bump="npx auto-changelog -p"',
], {
stdio: 'inherit',
context: 'local',
env: {
RELEASE_MODE: 'true',
},
});
}
catch (error) {
if (error instanceof child_process_1.ChildProcessError) {
if (error.signal) {
process.kill(process.pid, error.signal);
}
else {
process.exit(error.code ?? 0);
}
}
throw error;
}
}
}
Release.description = 'Publish your community node package to npm';
Release.examples = ['<%= config.bin %> <%= command.id %>'];
Release.flags = {};
exports.default = Release;
//# sourceMappingURL=release.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"release.js","sourceRoot":"","sources":["../../src/commands/release.ts"],"names":[],"mappings":";;AAAA,sCAAsC;AAEtC,0DAAuE;AACvE,8DAAgE;AAEhE,MAAqB,OAAQ,SAAQ,cAAO;IAK3C,KAAK,CAAC,GAAG;QACR,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAE1B,MAAM,EAAE,GAAG,CAAC,MAAM,IAAA,sCAAoB,GAAE,CAAC,IAAI,KAAK,CAAC;QAEnD,IAAI,CAAC;YACJ,MAAM,IAAA,0BAAU,EACf,YAAY,EACZ;gBACC,IAAI;gBACJ,0BAA0B;gBAC1B,8BAA8B;gBAC9B,uBAAuB;gBACvB,sBAAsB;gBACtB,cAAc;gBACd,WAAW;gBACX,YAAY;gBACZ,kGAAkG;gBAClG,kBAAkB;gBAClB,wBAAwB,EAAE,gBAAgB,EAAE,aAAa;gBACzD,4CAA4C;aAC5C,EACD;gBACC,KAAK,EAAE,SAAS;gBAChB,OAAO,EAAE,OAAO;gBAChB,GAAG,EAAE;oBACJ,YAAY,EAAE,MAAM;iBACpB;aACD,CACD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,KAAK,YAAY,iCAAiB,EAAE,CAAC;gBACxC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBAClB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;gBACzC,CAAC;qBAAM,CAAC;oBACP,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;gBAC/B,CAAC;YACF,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;;AA5Ce,mBAAW,GAAG,4CAA4C,CAAC;AAC3D,gBAAQ,GAAG,CAAC,qCAAqC,CAAC,CAAC;AACnD,aAAK,GAAG,EAAE,CAAC;kBAHP,OAAO"}

3
node_modules/@n8n/node-cli/dist/configs/eslint.d.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export declare const config: import("@typescript-eslint/utils/dist/ts-eslint").FlatConfig.ConfigArray;
export declare const configWithoutCloudSupport: import("@typescript-eslint/utils/dist/ts-eslint").FlatConfig.ConfigArray;
export default config;

64
node_modules/@n8n/node-cli/dist/configs/eslint.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.configWithoutCloudSupport = exports.config = void 0;
const js_1 = __importDefault(require("@eslint/js"));
const eslint_plugin_community_nodes_1 = require("@n8n/eslint-plugin-community-nodes");
const config_1 = require("eslint/config");
const eslint_import_resolver_typescript_1 = require("eslint-import-resolver-typescript");
const eslint_plugin_import_x_1 = __importDefault(require("eslint-plugin-import-x"));
const eslint_plugin_n8n_nodes_base_1 = __importDefault(require("eslint-plugin-n8n-nodes-base"));
const typescript_eslint_1 = __importDefault(require("typescript-eslint"));
function createConfig(supportCloud = true) {
return typescript_eslint_1.default.config((0, config_1.globalIgnores)(['dist']), {
files: ['**/*.ts'],
extends: [
js_1.default.configs.recommended,
typescript_eslint_1.default.configs.recommended,
supportCloud
? eslint_plugin_community_nodes_1.n8nCommunityNodesPlugin.configs.recommended
: eslint_plugin_community_nodes_1.n8nCommunityNodesPlugin.configs.recommendedWithoutN8nCloudSupport,
eslint_plugin_import_x_1.default.configs['flat/recommended'],
],
rules: {
'prefer-spread': 'off',
},
}, {
plugins: { 'n8n-nodes-base': eslint_plugin_n8n_nodes_base_1.default },
settings: {
'import-x/resolver-next': [(0, eslint_import_resolver_typescript_1.createTypeScriptImportResolver)()],
},
}, {
files: ['package.json'],
rules: {
...eslint_plugin_n8n_nodes_base_1.default.configs.community.rules,
},
languageOptions: {
parser: typescript_eslint_1.default.parser,
parserOptions: {
extraFileExtensions: ['.json'],
},
},
}, {
files: ['./credentials/**/*.ts'],
rules: {
...eslint_plugin_n8n_nodes_base_1.default.configs.credentials.rules,
'n8n-nodes-base/cred-class-field-documentation-url-miscased': 'off',
'n8n-nodes-base/cred-class-field-type-options-password-missing': 'off',
},
}, {
files: ['./nodes/**/*.ts'],
rules: {
...eslint_plugin_n8n_nodes_base_1.default.configs.nodes.rules,
'n8n-nodes-base/node-class-description-inputs-wrong-regular-node': 'off',
'n8n-nodes-base/node-class-description-outputs-wrong': 'off',
'n8n-nodes-base/node-param-type-options-max-value-present': 'off',
},
});
}
exports.config = createConfig();
exports.configWithoutCloudSupport = createConfig(false);
exports.default = exports.config;
//# sourceMappingURL=eslint.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"eslint.js","sourceRoot":"","sources":["../../src/configs/eslint.ts"],"names":[],"mappings":";;;;;;AAAA,oDAAgC;AAChC,sFAA6E;AAC7E,0CAA8C;AAC9C,yFAAmF;AACnF,oFAAkD;AAClD,gGAA0D;AAC1D,0EAA+D;AAE/D,SAAS,YAAY,CAAC,YAAY,GAAG,IAAI;IACxC,OAAO,2BAAQ,CAAC,MAAM,CACrB,IAAA,sBAAa,EAAC,CAAC,MAAM,CAAC,CAAC,EACvB;QACC,KAAK,EAAE,CAAC,SAAS,CAAC;QAClB,OAAO,EAAE;YACR,YAAM,CAAC,OAAO,CAAC,WAAW;YAC1B,2BAAQ,CAAC,OAAO,CAAC,WAAW;YAC5B,YAAY;gBACX,CAAC,CAAC,uDAAuB,CAAC,OAAO,CAAC,WAAW;gBAC7C,CAAC,CAAC,uDAAuB,CAAC,OAAO,CAAC,iCAAiC;YACpE,gCAAY,CAAC,OAAO,CAAC,kBAAkB,CAAC;SACxC;QACD,KAAK,EAAE;YACN,eAAe,EAAE,KAAK;SACtB;KACD,EACD;QACC,OAAO,EAAE,EAAE,gBAAgB,EAAE,sCAAc,EAAE;QAC7C,QAAQ,EAAE;YACT,wBAAwB,EAAE,CAAC,IAAA,kEAA8B,GAAE,CAAC;SAC5D;KACD,EACD;QACC,KAAK,EAAE,CAAC,cAAc,CAAC;QACvB,KAAK,EAAE;YACN,GAAG,sCAAc,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK;SACzC;QACD,eAAe,EAAE;YAChB,MAAM,EAAE,2BAAQ,CAAC,MAAM;YACvB,aAAa,EAAE;gBACd,mBAAmB,EAAE,CAAC,OAAO,CAAC;aAC9B;SACD;KACD,EACD;QACC,KAAK,EAAE,CAAC,uBAAuB,CAAC;QAChC,KAAK,EAAE;YACN,GAAG,sCAAc,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK;YAE3C,4DAA4D,EAAE,KAAK;YAEnE,+DAA+D,EAAE,KAAK;SACtE;KACD,EACD;QACC,KAAK,EAAE,CAAC,iBAAiB,CAAC;QAC1B,KAAK,EAAE;YACN,GAAG,sCAAc,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;YAErC,iEAAiE,EAAE,KAAK;YACxE,qDAAqD,EAAE,KAAK;YAE5D,0DAA0D,EAAE,KAAK;SACjE;KACD,CACD,CAAC;AACH,CAAC;AACY,QAAA,MAAM,GAAG,YAAY,EAAE,CAAC;AACxB,QAAA,yBAAyB,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;AAE7D,kBAAe,cAAM,CAAC"}

16
node_modules/@n8n/node-cli/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
import Build from './commands/build';
import CloudSupport from './commands/cloud-support';
import Dev from './commands/dev';
import Lint from './commands/lint';
import New from './commands/new';
import Prerelease from './commands/prerelease';
import Release from './commands/release';
export declare const commands: {
new: typeof New;
build: typeof Build;
dev: typeof Dev;
prerelease: typeof Prerelease;
release: typeof Release;
lint: typeof Lint;
'cloud-support': typeof CloudSupport;
};

23
node_modules/@n8n/node-cli/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.commands = void 0;
const build_1 = __importDefault(require("./commands/build"));
const cloud_support_1 = __importDefault(require("./commands/cloud-support"));
const dev_1 = __importDefault(require("./commands/dev"));
const lint_1 = __importDefault(require("./commands/lint"));
const new_1 = __importDefault(require("./commands/new"));
const prerelease_1 = __importDefault(require("./commands/prerelease"));
const release_1 = __importDefault(require("./commands/release"));
exports.commands = {
new: new_1.default,
build: build_1.default,
dev: dev_1.default,
prerelease: prerelease_1.default,
release: release_1.default,
lint: lint_1.default,
'cloud-support': cloud_support_1.default,
};
//# sourceMappingURL=index.js.map

1
node_modules/@n8n/node-cli/dist/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,6DAAqC;AACrC,6EAAoD;AACpD,yDAAiC;AACjC,2DAAmC;AACnC,yDAAiC;AACjC,uEAA+C;AAC/C,iEAAyC;AAE5B,QAAA,QAAQ,GAAG;IACvB,GAAG,EAAE,aAAG;IACR,KAAK,EAAE,eAAK;IACZ,GAAG,EAAE,aAAG;IACR,UAAU,EAAE,oBAAU;IACtB,OAAO,EAAE,iBAAO;IAChB,IAAI,EAAE,cAAI;IAEV,eAAe,EAAE,uBAAY;CAC7B,CAAC"}

29
node_modules/@n8n/node-cli/dist/template/core.d.ts generated vendored Normal file
View File

@@ -0,0 +1,29 @@
export type TemplateData<Config extends object = object> = {
destinationPath: string;
nodePackageName: string;
user?: Partial<{
name: string;
email: string;
}>;
packageManager: {
name: 'npm' | 'yarn' | 'pnpm';
installCommand: string;
};
config: Config;
};
type Require<T, K extends keyof T> = T & {
[P in K]-?: T[P];
};
export type Template<Config extends object = object> = {
name: string;
description: string;
path: string;
prompts?: () => Promise<Config>;
run?: (data: TemplateData<Config>) => Promise<void>;
};
export type TemplateWithRun<Config extends object = object> = Require<Template<Config>, 'run'>;
export declare function copyTemplateFilesToDestination<Config extends object>(template: Template<Config>, data: TemplateData): Promise<void>;
export declare function copyDefaultTemplateFilesToDestination(data: TemplateData): Promise<void>;
export declare function templateStaticFiles(data: TemplateData): Promise<void>;
export declare function createTemplate<Config extends object>(template: Template<Config>): TemplateWithRun<Config>;
export {};

55
node_modules/@n8n/node-cli/dist/template/core.js generated vendored Normal file
View File

@@ -0,0 +1,55 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.copyTemplateFilesToDestination = copyTemplateFilesToDestination;
exports.copyDefaultTemplateFilesToDestination = copyDefaultTemplateFilesToDestination;
exports.templateStaticFiles = templateStaticFiles;
exports.createTemplate = createTemplate;
const fast_glob_1 = __importDefault(require("fast-glob"));
const handlebars_1 = __importDefault(require("handlebars"));
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const filesystem_1 = require("../utils/filesystem");
async function copyTemplateFilesToDestination(template, data) {
await (0, filesystem_1.copyFolder)({
source: template.path,
destination: data.destinationPath,
ignore: ['dist', 'node_modules'],
});
}
async function copyDefaultTemplateFilesToDestination(data) {
await (0, filesystem_1.copyFolder)({
source: node_path_1.default.resolve(__dirname, 'templates/shared/default'),
destination: data.destinationPath,
ignore: ['dist', 'node_modules'],
});
}
async function templateStaticFiles(data) {
const files = await (0, fast_glob_1.default)('**/*.{md,json,yml}', {
ignore: ['tsconfig.json', 'tsconfig.build.json'],
cwd: data.destinationPath,
absolute: true,
dot: true,
});
await Promise.all(files.map(async (file) => {
const content = await promises_1.default.readFile(file, 'utf-8');
const newContent = handlebars_1.default.compile(content, { noEscape: true })(data);
if (newContent !== content) {
await promises_1.default.writeFile(file, newContent);
}
}));
}
function createTemplate(template) {
return {
...template,
run: async (data) => {
await copyDefaultTemplateFilesToDestination(data);
await copyTemplateFilesToDestination(template, data);
await templateStaticFiles(data);
await template.run?.(data);
},
};
}
//# sourceMappingURL=core.js.map

1
node_modules/@n8n/node-cli/dist/template/core.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/template/core.ts"],"names":[],"mappings":";;;;;AAgCA,wEASC;AAED,sFAMC;AAED,kDAkBC;AAED,wCAYC;AAnFD,0DAA6B;AAC7B,4DAAoC;AACpC,gEAAkC;AAClC,0DAA6B;AAE7B,oDAAiD;AA2B1C,KAAK,UAAU,8BAA8B,CACnD,QAA0B,EAC1B,IAAkB;IAElB,MAAM,IAAA,uBAAU,EAAC;QAChB,MAAM,EAAE,QAAQ,CAAC,IAAI;QACrB,WAAW,EAAE,IAAI,CAAC,eAAe;QACjC,MAAM,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC;KAChC,CAAC,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,qCAAqC,CAAC,IAAkB;IAC7E,MAAM,IAAA,uBAAU,EAAC;QAChB,MAAM,EAAE,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,0BAA0B,CAAC;QAC3D,WAAW,EAAE,IAAI,CAAC,eAAe;QACjC,MAAM,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC;KAChC,CAAC,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,mBAAmB,CAAC,IAAkB;IAC3D,MAAM,KAAK,GAAG,MAAM,IAAA,mBAAI,EAAC,oBAAoB,EAAE;QAC9C,MAAM,EAAE,CAAC,eAAe,EAAE,qBAAqB,CAAC;QAChD,GAAG,EAAE,IAAI,CAAC,eAAe;QACzB,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,IAAI;KACT,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACxB,MAAM,OAAO,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,oBAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAEzE,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;YAC5B,MAAM,kBAAE,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACtC,CAAC;IACF,CAAC,CAAC,CACF,CAAC;AACH,CAAC;AAED,SAAgB,cAAc,CAC7B,QAA0B;IAE1B,OAAO;QACN,GAAG,QAAQ;QACX,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACnB,MAAM,qCAAqC,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,8BAA8B,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACrD,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAChC,MAAM,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;KACD,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,17 @@
export declare function updateNodeAst({ nodePath, className, baseUrl, }: {
nodePath: string;
className: string;
baseUrl: string;
}): import("ts-morph").SourceFile;
export declare function updateCredentialAst({ repoName, baseUrl, credentialPath, credentialName, credentialDisplayName, credentialClassName, }: {
repoName: string;
credentialPath: string;
credentialName: string;
credentialDisplayName: string;
credentialClassName: string;
baseUrl: string;
}): import("ts-morph").SourceFile;
export declare function addCredentialToNode({ nodePath, credentialName, }: {
nodePath: string;
credentialName: string;
}): import("ts-morph").SourceFile;

View File

@@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateNodeAst = updateNodeAst;
exports.updateCredentialAst = updateCredentialAst;
exports.addCredentialToNode = addCredentialToNode;
const change_case_1 = require("change-case");
const ts_morph_1 = require("ts-morph");
const ast_1 = require("../../../../utils/ast");
function updateNodeAst({ nodePath, className, baseUrl, }) {
const sourceFile = (0, ast_1.loadSingleSourceFile)(nodePath);
const classDecl = sourceFile.getClasses()[0];
classDecl.rename(className);
const nodeDescriptionObj = classDecl
.getPropertyOrThrow('description')
.getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
(0, ast_1.updateStringProperty)({
obj: nodeDescriptionObj,
key: 'displayName',
value: (0, change_case_1.capitalCase)(className),
});
(0, ast_1.updateStringProperty)({
obj: nodeDescriptionObj,
key: 'name',
value: (0, change_case_1.camelCase)(className),
});
(0, ast_1.updateStringProperty)({
obj: nodeDescriptionObj,
key: 'description',
value: `Interact with the ${(0, change_case_1.capitalCase)(className)} API`,
});
const icon = (0, ast_1.getChildObjectLiteral)({ obj: nodeDescriptionObj, key: 'icon' });
(0, ast_1.updateStringProperty)({
obj: icon,
key: 'light',
value: `file:${(0, change_case_1.camelCase)(className)}.svg`,
});
(0, ast_1.updateStringProperty)({
obj: icon,
key: 'dark',
value: `file:${(0, change_case_1.camelCase)(className)}.dark.svg`,
});
const requestDefaults = (0, ast_1.getChildObjectLiteral)({
obj: nodeDescriptionObj,
key: 'requestDefaults',
});
(0, ast_1.updateStringProperty)({
obj: requestDefaults,
key: 'baseURL',
value: baseUrl,
});
const defaults = (0, ast_1.getChildObjectLiteral)({
obj: nodeDescriptionObj,
key: 'defaults',
});
(0, ast_1.updateStringProperty)({ obj: defaults, key: 'name', value: (0, change_case_1.capitalCase)(className) });
return sourceFile;
}
function updateCredentialAst({ repoName, baseUrl, credentialPath, credentialName, credentialDisplayName, credentialClassName, }) {
const sourceFile = (0, ast_1.loadSingleSourceFile)(credentialPath);
const classDecl = sourceFile.getClasses()[0];
classDecl.rename(credentialClassName);
(0, ast_1.updateStringProperty)({
obj: classDecl,
key: 'displayName',
value: credentialDisplayName,
});
(0, ast_1.updateStringProperty)({
obj: classDecl,
key: 'name',
value: credentialName,
});
const docUrlProp = classDecl.getProperty('documentationUrl');
if (docUrlProp) {
const initializer = docUrlProp.getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.StringLiteral);
const newUrl = initializer.getLiteralText().replace('/repo', `/${repoName}`);
initializer.setLiteralValue(newUrl);
}
const testProperty = classDecl.getProperty('test');
if (testProperty) {
const testRequest = testProperty
.getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression)
.getPropertyOrThrow('request')
.asKindOrThrow(ts_morph_1.SyntaxKind.PropertyAssignment)
.getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
(0, ast_1.updateStringProperty)({
obj: testRequest,
key: 'baseURL',
value: baseUrl,
});
}
return sourceFile;
}
function addCredentialToNode({ nodePath, credentialName, }) {
const sourceFile = (0, ast_1.loadSingleSourceFile)(nodePath);
const classDecl = sourceFile.getClasses()[0];
const descriptionProp = classDecl
.getPropertyOrThrow('description')
.getInitializerIfKindOrThrow(ts_morph_1.SyntaxKind.ObjectLiteralExpression);
const credentialsProp = descriptionProp.getPropertyOrThrow('credentials');
if (credentialsProp.getKind() === ts_morph_1.SyntaxKind.PropertyAssignment) {
const initializer = credentialsProp.getFirstDescendantByKindOrThrow(ts_morph_1.SyntaxKind.ArrayLiteralExpression);
const credentialObject = ts_morph_1.ts.factory.createObjectLiteralExpression([
ts_morph_1.ts.factory.createPropertyAssignment(ts_morph_1.ts.factory.createIdentifier('name'), ts_morph_1.ts.factory.createStringLiteral(credentialName, true)),
ts_morph_1.ts.factory.createPropertyAssignment(ts_morph_1.ts.factory.createIdentifier('required'), ts_morph_1.ts.factory.createTrue()),
]);
initializer.addElement((0, ts_morph_1.printNode)(credentialObject));
}
return sourceFile;
}
//# sourceMappingURL=ast.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../../../../src/template/templates/declarative/custom/ast.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,161 @@
import { camelCase, capitalCase } from 'change-case';
import { ts, SyntaxKind, printNode } from 'ts-morph';
import {
getChildObjectLiteral,
loadSingleSourceFile,
updateStringProperty,
} from '../../../../utils/ast';
export function updateNodeAst({
nodePath,
className,
baseUrl,
}: { nodePath: string; className: string; baseUrl: string }) {
const sourceFile = loadSingleSourceFile(nodePath);
const classDecl = sourceFile.getClasses()[0];
classDecl.rename(className);
const nodeDescriptionObj = classDecl
.getPropertyOrThrow('description')
.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
updateStringProperty({
obj: nodeDescriptionObj,
key: 'displayName',
value: capitalCase(className),
});
updateStringProperty({
obj: nodeDescriptionObj,
key: 'name',
value: camelCase(className),
});
updateStringProperty({
obj: nodeDescriptionObj,
key: 'description',
value: `Interact with the ${capitalCase(className)} API`,
});
const icon = getChildObjectLiteral({ obj: nodeDescriptionObj, key: 'icon' });
updateStringProperty({
obj: icon,
key: 'light',
value: `file:${camelCase(className)}.svg`,
});
updateStringProperty({
obj: icon,
key: 'dark',
value: `file:${camelCase(className)}.dark.svg`,
});
const requestDefaults = getChildObjectLiteral({
obj: nodeDescriptionObj,
key: 'requestDefaults',
});
updateStringProperty({
obj: requestDefaults,
key: 'baseURL',
value: baseUrl,
});
const defaults = getChildObjectLiteral({
obj: nodeDescriptionObj,
key: 'defaults',
});
updateStringProperty({ obj: defaults, key: 'name', value: capitalCase(className) });
return sourceFile;
}
export function updateCredentialAst({
repoName,
baseUrl,
credentialPath,
credentialName,
credentialDisplayName,
credentialClassName,
}: {
repoName: string;
credentialPath: string;
credentialName: string;
credentialDisplayName: string;
credentialClassName: string;
baseUrl: string;
}) {
const sourceFile = loadSingleSourceFile(credentialPath);
const classDecl = sourceFile.getClasses()[0];
classDecl.rename(credentialClassName);
updateStringProperty({
obj: classDecl,
key: 'displayName',
value: credentialDisplayName,
});
updateStringProperty({
obj: classDecl,
key: 'name',
value: credentialName,
});
const docUrlProp = classDecl.getProperty('documentationUrl');
if (docUrlProp) {
const initializer = docUrlProp.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral);
const newUrl = initializer.getLiteralText().replace('/repo', `/${repoName}`);
initializer.setLiteralValue(newUrl);
}
const testProperty = classDecl.getProperty('test');
if (testProperty) {
const testRequest = testProperty
.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression)
.getPropertyOrThrow('request')
.asKindOrThrow(SyntaxKind.PropertyAssignment)
.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
updateStringProperty({
obj: testRequest,
key: 'baseURL',
value: baseUrl,
});
}
return sourceFile;
}
export function addCredentialToNode({
nodePath,
credentialName,
}: { nodePath: string; credentialName: string }) {
const sourceFile = loadSingleSourceFile(nodePath);
const classDecl = sourceFile.getClasses()[0];
const descriptionProp = classDecl
.getPropertyOrThrow('description')
.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
const credentialsProp = descriptionProp.getPropertyOrThrow('credentials');
if (credentialsProp.getKind() === SyntaxKind.PropertyAssignment) {
const initializer = credentialsProp.getFirstDescendantByKindOrThrow(
SyntaxKind.ArrayLiteralExpression,
);
const credentialObject = ts.factory.createObjectLiteralExpression([
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier('name'),
ts.factory.createStringLiteral(credentialName, true),
),
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier('required'),
ts.factory.createTrue(),
),
]);
initializer.addElement(printNode(credentialObject));
}
return sourceFile;
}

View File

@@ -0,0 +1,3 @@
export declare const credentialTypePrompt: () => Promise<"custom" | "apiKey" | "bearer" | "basicAuth" | "none" | "oauth2">;
export declare const baseUrlPrompt: () => Promise<string>;
export declare const oauthFlowPrompt: () => Promise<"clientCredentials" | "authorizationCode">;

View File

@@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.oauthFlowPrompt = exports.baseUrlPrompt = exports.credentialTypePrompt = void 0;
const prompts_1 = require("@clack/prompts");
const prompts_2 = require("../../../../utils/prompts");
const credentialTypePrompt = async () => await (0, prompts_2.withCancelHandler)((0, prompts_1.select)({
message: 'What type of authentication does your API use?',
options: [
{
label: 'API Key',
value: 'apiKey',
hint: 'Send a secret key via headers, query, or body',
},
{
label: 'Bearer Token',
value: 'bearer',
hint: 'Send a token via Authorization header (Authorization: Bearer <token>)',
},
{
label: 'OAuth2',
value: 'oauth2',
hint: 'Use an OAuth 2.0 flow to obtain access tokens on behalf of a user or app',
},
{
label: 'Basic Auth',
value: 'basicAuth',
hint: 'Send username and password encoded in base64 via the Authorization header',
},
{
label: 'Custom',
value: 'custom',
hint: 'Create your own credential logic; an empty credential class will be scaffolded for you',
},
{
label: 'None',
value: 'none',
hint: 'No authentication; no credential class will be generated',
},
],
initialValue: 'apiKey',
}));
exports.credentialTypePrompt = credentialTypePrompt;
const baseUrlPrompt = async () => await (0, prompts_2.withCancelHandler)((0, prompts_1.text)({
message: "What's the base URL of the API?",
placeholder: 'https://api.example.com/v2',
defaultValue: 'https://api.example.com/v2',
validate: (value) => {
if (!value)
return;
if (!value.startsWith('https://') && !value.startsWith('http://')) {
return 'Base URL must start with http(s)://';
}
try {
new URL(value);
}
catch (error) {
return 'Must be a valid URL';
}
return;
},
}));
exports.baseUrlPrompt = baseUrlPrompt;
const oauthFlowPrompt = async () => await (0, prompts_2.withCancelHandler)((0, prompts_1.select)({
message: 'What OAuth2 flow does your API use?',
options: [
{
label: 'Authorization code',
value: 'authorizationCode',
hint: 'Users log in and approve access (use this if unsure)',
},
{
label: 'Client credentials',
value: 'clientCredentials',
hint: 'Server-to-server auth without user interaction',
},
],
initialValue: 'authorizationCode',
}));
exports.oauthFlowPrompt = oauthFlowPrompt;
//# sourceMappingURL=prompts.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../../../src/template/templates/declarative/custom/prompts.ts"],"names":[],"mappings":";;;AAAA,4CAA8C;AAG9C,uDAA8D;AAEvD,MAAM,oBAAoB,GAAG,KAAK,IAAI,EAAE,CAC9C,MAAM,IAAA,2BAAiB,EACtB,IAAA,gBAAM,EAAiB;IACtB,OAAO,EAAE,gDAAgD;IACzD,OAAO,EAAE;QACR;YACC,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,+CAA+C;SACrD;QACD;YACC,KAAK,EAAE,cAAc;YACrB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,uEAAuE;SAC7E;QACD;YACC,KAAK,EAAE,QAAQ;YACf,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,0EAA0E;SAChF;QACD;YACC,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,WAAW;YAClB,IAAI,EAAE,2EAA2E;SACjF;QACD;YACC,KAAK,EAAE,QAAQ;YACf,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,wFAAwF;SAC9F;QACD;YACC,KAAK,EAAE,MAAM;YACb,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,0DAA0D;SAChE;KACD;IACD,YAAY,EAAE,QAAQ;CACtB,CAAC,CACF,CAAC;AAtCU,QAAA,oBAAoB,wBAsC9B;AAEI,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE,CACvC,MAAM,IAAA,2BAAiB,EACtB,IAAA,cAAI,EAAC;IACJ,OAAO,EAAE,iCAAiC;IAC1C,WAAW,EAAE,4BAA4B;IACzC,YAAY,EAAE,4BAA4B;IAC1C,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;QACnB,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACnE,OAAO,qCAAqC,CAAC;QAC9C,CAAC;QAED,IAAI,CAAC;YACJ,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,qBAAqB,CAAC;QAC9B,CAAC;QACD,OAAO;IACR,CAAC;CACD,CAAC,CACF,CAAC;AArBU,QAAA,aAAa,iBAqBvB;AAEI,MAAM,eAAe,GAAG,KAAK,IAAI,EAAE,CACzC,MAAM,IAAA,2BAAiB,EACtB,IAAA,gBAAM,EAA4C;IACjD,OAAO,EAAE,qCAAqC;IAC9C,OAAO,EAAE;QACR;YACC,KAAK,EAAE,oBAAoB;YAC3B,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,sDAAsD;SAC5D;QACD;YACC,KAAK,EAAE,oBAAoB;YAC3B,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,gDAAgD;SACtD;KACD;IACD,YAAY,EAAE,mBAAmB;CACjC,CAAC,CACF,CAAC;AAlBU,QAAA,eAAe,mBAkBzB"}

View File

@@ -0,0 +1,87 @@
import { select, text } from '@clack/prompts';
import type { CredentialType } from './types';
import { withCancelHandler } from '../../../../utils/prompts';
export const credentialTypePrompt = async () =>
await withCancelHandler(
select<CredentialType>({
message: 'What type of authentication does your API use?',
options: [
{
label: 'API Key',
value: 'apiKey',
hint: 'Send a secret key via headers, query, or body',
},
{
label: 'Bearer Token',
value: 'bearer',
hint: 'Send a token via Authorization header (Authorization: Bearer <token>)',
},
{
label: 'OAuth2',
value: 'oauth2',
hint: 'Use an OAuth 2.0 flow to obtain access tokens on behalf of a user or app',
},
{
label: 'Basic Auth',
value: 'basicAuth',
hint: 'Send username and password encoded in base64 via the Authorization header',
},
{
label: 'Custom',
value: 'custom',
hint: 'Create your own credential logic; an empty credential class will be scaffolded for you',
},
{
label: 'None',
value: 'none',
hint: 'No authentication; no credential class will be generated',
},
],
initialValue: 'apiKey',
}),
);
export const baseUrlPrompt = async () =>
await withCancelHandler(
text({
message: "What's the base URL of the API?",
placeholder: 'https://api.example.com/v2',
defaultValue: 'https://api.example.com/v2',
validate: (value) => {
if (!value) return;
if (!value.startsWith('https://') && !value.startsWith('http://')) {
return 'Base URL must start with http(s)://';
}
try {
new URL(value);
} catch (error) {
return 'Must be a valid URL';
}
return;
},
}),
);
export const oauthFlowPrompt = async () =>
await withCancelHandler(
select<'clientCredentials' | 'authorizationCode'>({
message: 'What OAuth2 flow does your API use?',
options: [
{
label: 'Authorization code',
value: 'authorizationCode',
hint: 'Users log in and approve access (use this if unsure)',
},
{
label: 'Client credentials',
value: 'clientCredentials',
hint: 'Server-to-server auth without user interaction',
},
],
initialValue: 'authorizationCode',
}),
);

View File

@@ -0,0 +1,2 @@
import type { CustomTemplateConfig } from './types';
export declare const customTemplate: import("../../../core").TemplateWithRun<CustomTemplateConfig>;

View File

@@ -0,0 +1,83 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.customTemplate = void 0;
const change_case_1 = require("change-case");
const node_path_1 = __importDefault(require("node:path"));
const ast_1 = require("./ast");
const prompts_1 = require("./prompts");
const filesystem_1 = require("../../../../utils/filesystem");
const package_1 = require("../../../../utils/package");
const core_1 = require("../../../core");
exports.customTemplate = (0, core_1.createTemplate)({
name: 'Start from scratch',
description: 'Blank template with guided setup',
path: node_path_1.default.join(__dirname, 'template'),
prompts: async () => {
const baseUrl = await (0, prompts_1.baseUrlPrompt)();
const credentialType = await (0, prompts_1.credentialTypePrompt)();
if (credentialType === 'oauth2') {
const flow = await (0, prompts_1.oauthFlowPrompt)();
return { credentialType, baseUrl, flow };
}
return { credentialType, baseUrl };
},
run: async (data) => {
await renameNode(data, 'Example');
await addCredential(data);
},
});
async function renameNode(data, oldNodeName) {
const { config, nodePackageName: nodeName, destinationPath } = data;
const newClassName = (0, change_case_1.pascalCase)(nodeName.replace('n8n-nodes-', ''));
const oldNodeDir = node_path_1.default.resolve(destinationPath, `nodes/${oldNodeName}`);
await (0, filesystem_1.renameFilesInDirectory)(oldNodeDir, oldNodeName, newClassName);
const newNodeDir = await (0, filesystem_1.renameDirectory)(oldNodeDir, newClassName);
const newNodePath = node_path_1.default.resolve(newNodeDir, `${newClassName}.node.ts`);
const newNodeAst = (0, ast_1.updateNodeAst)({
nodePath: newNodePath,
baseUrl: config.baseUrl,
className: newClassName,
});
await (0, filesystem_1.writeFileSafe)(newNodePath, newNodeAst.getFullText());
const nodes = [`dist/nodes/${newClassName}/${newClassName}.node.js`];
await (0, package_1.setNodesPackageJson)(destinationPath, nodes);
}
async function addCredential(data) {
const { config, destinationPath, nodePackageName } = data;
if (config.credentialType === 'none')
return;
const credentialTemplateName = config.credentialType === 'oauth2'
? config.credentialType + (0, change_case_1.pascalCase)(config.flow)
: config.credentialType;
const credentialTemplatePath = node_path_1.default.resolve(__dirname, `../../shared/credentials/${credentialTemplateName}.credentials.ts`);
const nodeName = nodePackageName.replace('n8n-nodes', '');
const repoName = nodeName;
const { baseUrl, credentialType } = config;
const credentialClassName = config.credentialType === 'oauth2'
? (0, change_case_1.pascalCase)(`${nodeName}-OAuth2-api`)
: (0, change_case_1.pascalCase)(`${nodeName}-api`);
const credentialName = (0, change_case_1.camelCase)(`${nodeName}${credentialType === 'oauth2' ? 'OAuth2Api' : 'Api'}`);
const credentialDisplayName = `${(0, change_case_1.capitalCase)(nodeName)} ${credentialType === 'oauth2' ? 'OAuth2 API' : 'API'}`;
const updatedCredentialAst = (0, ast_1.updateCredentialAst)({
repoName,
baseUrl,
credentialName,
credentialDisplayName,
credentialClassName,
credentialPath: credentialTemplatePath,
});
await (0, filesystem_1.writeFileSafe)(node_path_1.default.resolve(destinationPath, `credentials/${credentialClassName}.credentials.ts`), updatedCredentialAst.getFullText());
await (0, package_1.addCredentialPackageJson)(destinationPath, `dist/credentials/${credentialClassName}.credentials.js`);
for (const nodePath of await (0, package_1.getPackageJsonNodes)(destinationPath)) {
const srcNodePath = node_path_1.default.resolve(destinationPath, nodePath.replace(/.js$/, '.ts').replace(/^dist\//, ''));
const updatedNodeAst = (0, ast_1.addCredentialToNode)({
nodePath: srcNodePath,
credentialName,
});
await (0, filesystem_1.writeFileSafe)(srcNodePath, updatedNodeAst.getFullText());
}
}
//# sourceMappingURL=template.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../../src/template/templates/declarative/custom/template.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,121 @@
import { camelCase, capitalCase, pascalCase } from 'change-case';
import path from 'node:path';
import { addCredentialToNode, updateCredentialAst, updateNodeAst } from './ast';
import { baseUrlPrompt, credentialTypePrompt, oauthFlowPrompt } from './prompts';
import type { CustomTemplateConfig } from './types';
import {
renameDirectory,
renameFilesInDirectory,
writeFileSafe,
} from '../../../../utils/filesystem';
import {
setNodesPackageJson,
addCredentialPackageJson,
getPackageJsonNodes,
} from '../../../../utils/package';
import { createTemplate, type TemplateData } from '../../../core';
export const customTemplate = createTemplate({
name: 'Start from scratch',
description: 'Blank template with guided setup',
path: path.join(__dirname, 'template'),
prompts: async (): Promise<CustomTemplateConfig> => {
const baseUrl = await baseUrlPrompt();
const credentialType = await credentialTypePrompt();
if (credentialType === 'oauth2') {
const flow = await oauthFlowPrompt();
return { credentialType, baseUrl, flow };
}
return { credentialType, baseUrl };
},
run: async (data) => {
await renameNode(data, 'Example');
await addCredential(data);
},
});
async function renameNode(data: TemplateData<CustomTemplateConfig>, oldNodeName: string) {
const { config, nodePackageName: nodeName, destinationPath } = data;
const newClassName = pascalCase(nodeName.replace('n8n-nodes-', ''));
const oldNodeDir = path.resolve(destinationPath, `nodes/${oldNodeName}`);
await renameFilesInDirectory(oldNodeDir, oldNodeName, newClassName);
const newNodeDir = await renameDirectory(oldNodeDir, newClassName);
const newNodePath = path.resolve(newNodeDir, `${newClassName}.node.ts`);
const newNodeAst = updateNodeAst({
nodePath: newNodePath,
baseUrl: config.baseUrl,
className: newClassName,
});
await writeFileSafe(newNodePath, newNodeAst.getFullText());
const nodes = [`dist/nodes/${newClassName}/${newClassName}.node.js`];
await setNodesPackageJson(destinationPath, nodes);
}
async function addCredential(data: TemplateData<CustomTemplateConfig>) {
const { config, destinationPath, nodePackageName } = data;
if (config.credentialType === 'none') return;
const credentialTemplateName =
config.credentialType === 'oauth2'
? config.credentialType + pascalCase(config.flow)
: config.credentialType;
const credentialTemplatePath = path.resolve(
__dirname,
`../../shared/credentials/${credentialTemplateName}.credentials.ts`,
);
const nodeName = nodePackageName.replace('n8n-nodes', '');
const repoName = nodeName;
const { baseUrl, credentialType } = config;
const credentialClassName =
config.credentialType === 'oauth2'
? pascalCase(`${nodeName}-OAuth2-api`)
: pascalCase(`${nodeName}-api`);
const credentialName = camelCase(
`${nodeName}${credentialType === 'oauth2' ? 'OAuth2Api' : 'Api'}`,
);
const credentialDisplayName = `${capitalCase(nodeName)} ${
credentialType === 'oauth2' ? 'OAuth2 API' : 'API'
}`;
const updatedCredentialAst = updateCredentialAst({
repoName,
baseUrl,
credentialName,
credentialDisplayName,
credentialClassName,
credentialPath: credentialTemplatePath,
});
await writeFileSafe(
path.resolve(destinationPath, `credentials/${credentialClassName}.credentials.ts`),
updatedCredentialAst.getFullText(),
);
await addCredentialPackageJson(
destinationPath,
`dist/credentials/${credentialClassName}.credentials.js`,
);
for (const nodePath of await getPackageJsonNodes(destinationPath)) {
const srcNodePath = path.resolve(
destinationPath,
nodePath.replace(/.js$/, '.ts').replace(/^dist\//, ''),
);
const updatedNodeAst = addCredentialToNode({
nodePath: srcNodePath,
credentialName,
});
await writeFileSafe(srcNodePath, updatedNodeAst.getFullText());
}
}

View File

@@ -0,0 +1,46 @@
# {{nodePackageName}}
This is an n8n community node. It lets you use _app/service name_ in your n8n workflows.
_App/service name_ is _one or two sentences describing the service this node integrates with_.
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/sustainable-use-license/) workflow automation platform.
[Installation](#installation)
[Operations](#operations)
[Credentials](#credentials)
[Compatibility](#compatibility)
[Usage](#usage)
[Resources](#resources)
[Version history](#version-history)
## Installation
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
## Operations
_List the operations supported by your node._
## Credentials
_If users need to authenticate with the app/service, provide details here. You should include prerequisites (such as signing up with the service), available authentication methods, and how to set them up._
## Compatibility
_State the minimum n8n version, as well as which versions you test against. You can also include any known version incompatibility issues._
## Usage
_This is an optional section. Use it to help users with any difficult or confusing aspects of the node._
_By the time users are looking for community nodes, they probably already know n8n basics. But if you expect new users, you can link to the [Try it out](https://docs.n8n.io/try-it-out/) documentation to help them get started._
## Resources
* [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
* _Link to app/service documentation._
## Version history
_This is another optional section. If your node has multiple versions, include a short description of available versions and what changed, as well as any compatibility impact._

View File

@@ -0,0 +1,18 @@
{
"node": "{{nodePackageName}}",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development", "Developer Tools"],
"resources": {
"credentialDocumentation": [
{
"url": "https://github.com/org/repo?tab=readme-ov-file#credentials"
}
],
"primaryDocumentation": [
{
"url": "https://github.com/org/repo?tab=readme-ov-file"
}
]
}
}

View File

@@ -0,0 +1,50 @@
import { NodeConnectionType, type INodeType, type INodeTypeDescription } from 'n8n-workflow';
import { userDescription } from './resources/user';
import { companyDescription } from './resources/company';
export class Example implements INodeType {
description: INodeTypeDescription = {
displayName: 'Example',
name: 'example',
icon: { light: 'file:example.svg', dark: 'file:example.dark.svg' },
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Interact with the Example API',
defaults: {
name: 'Example',
},
usableAsTool: true,
inputs: [NodeConnectionType.Main],
outputs: [NodeConnectionType.Main],
credentials: [],
requestDefaults: {
baseURL: 'https://api.example.com',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
},
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'User',
value: 'user',
},
{
name: 'Company',
value: 'company',
},
],
default: 'user',
},
...userDescription,
...companyDescription,
],
};
}

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="aquamarine"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cpu">
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
<rect x="9" y="9" width="6" height="6"></rect>
<line x1="9" y1="1" x2="9" y2="4"></line>
<line x1="15" y1="1" x2="15" y2="4"></line>
<line x1="9" y1="20" x2="9" y2="23"></line>
<line x1="15" y1="20" x2="15" y2="23"></line>
<line x1="20" y1="9" x2="23" y2="9"></line>
<line x1="20" y1="14" x2="23" y2="14"></line>
<line x1="1" y1="9" x2="4" y2="9"></line>
<line x1="1" y1="14" x2="4" y2="14"></line>
</svg>

After

Width:  |  Height:  |  Size: 698 B

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="darkblue"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cpu">
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
<rect x="9" y="9" width="6" height="6"></rect>
<line x1="9" y1="1" x2="9" y2="4"></line>
<line x1="15" y1="1" x2="15" y2="4"></line>
<line x1="9" y1="20" x2="9" y2="23"></line>
<line x1="15" y1="20" x2="15" y2="23"></line>
<line x1="20" y1="9" x2="23" y2="9"></line>
<line x1="20" y1="14" x2="23" y2="14"></line>
<line x1="1" y1="9" x2="4" y2="9"></line>
<line x1="1" y1="14" x2="4" y2="14"></line>
</svg>

After

Width:  |  Height:  |  Size: 696 B

View File

@@ -0,0 +1,61 @@
import type { INodeProperties } from 'n8n-workflow';
const showOnlyForCompanyGetMany = {
operation: ['getAll'],
resource: ['company'],
};
export const companyGetManyDescription: INodeProperties[] = [
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
...showOnlyForCompanyGetMany,
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
routing: {
send: {
type: 'query',
property: 'limit',
},
output: {
maxResults: '={{$value}}',
},
},
description: 'Max number of results to return',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: showOnlyForCompanyGetMany,
},
default: false,
description: 'Whether to return all results or only up to a given limit',
routing: {
send: {
paginate: '={{ $value }}',
},
operations: {
pagination: {
type: 'offset',
properties: {
limitParameter: 'limit',
offsetParameter: 'offset',
pageSize: 100,
type: 'query',
},
},
},
},
},
];

View File

@@ -0,0 +1,34 @@
import type { INodeProperties } from 'n8n-workflow';
import { companyGetManyDescription } from './getAll';
const showOnlyForCompanies = {
resource: ['company'],
};
export const companyDescription: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: showOnlyForCompanies,
},
options: [
{
name: 'Get Many',
value: 'getAll',
action: 'Get companies',
description: 'Get companies',
routing: {
request: {
method: 'GET',
url: '/companies',
},
},
},
],
default: 'getAll',
},
...companyGetManyDescription,
];

View File

@@ -0,0 +1,26 @@
import type { INodeProperties } from 'n8n-workflow';
const showOnlyForUserCreate = {
operation: ['create'],
resource: ['user'],
};
export const userCreateDescription: INodeProperties[] = [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
required: true,
displayOptions: {
show: showOnlyForUserCreate,
},
description: 'The name of the user',
routing: {
send: {
type: 'body',
property: 'name',
},
},
},
];

View File

@@ -0,0 +1,17 @@
import type { INodeProperties } from 'n8n-workflow';
const showOnlyForUserGet = {
operation: ['get'],
resource: ['user'],
};
export const userGetDescription: INodeProperties[] = [
{
displayName: 'User ID',
name: 'userId',
type: 'string',
displayOptions: { show: showOnlyForUserGet },
default: '',
description: "The user's ID to retrieve",
},
];

View File

@@ -0,0 +1,60 @@
import type { INodeProperties } from 'n8n-workflow';
import { userCreateDescription } from './create';
import { userGetDescription } from './get';
const showOnlyForUsers = {
resource: ['user'],
};
export const userDescription: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: showOnlyForUsers,
},
options: [
{
name: 'Get Many',
value: 'getAll',
action: 'Get users',
description: 'Get many users',
routing: {
request: {
method: 'GET',
url: '/users',
},
},
},
{
name: 'Get',
value: 'get',
action: 'Get a user',
description: 'Get the data of a single user',
routing: {
request: {
method: 'GET',
url: '=/users/{{$parameter.userId}}',
},
},
},
{
name: 'Create',
value: 'create',
action: 'Create a new user',
description: 'Create a new user',
routing: {
request: {
method: 'POST',
url: '/users',
},
},
},
],
default: 'getAll',
},
...userGetDescription,
...userCreateDescription,
];

View File

@@ -0,0 +1,48 @@
{
"name": "{{nodePackageName}}",
"version": "0.1.0",
"description": "",
"license": "MIT",
"homepage": "",
"keywords": [
"n8n-community-node-package"
],
"author": {
"name": "{{user.name}}",
"email": "{{user.email}}"
},
"repository": {
"type": "git",
"url": "https://github.com/<...>/n8n-nodes-<...>.git"
},
"scripts": {
"build": "n8n-node build",
"build:watch": "tsc --watch",
"dev": "n8n-node dev",
"lint": "n8n-node lint",
"lint:fix": "n8n-node lint --fix",
"release": "n8n-node release",
"prepublishOnly": "n8n-node prerelease"
},
"files": [
"dist"
],
"n8n": {
"n8nNodesApiVersion": 1,
"strict": true,
"credentials": [],
"nodes": [
"dist/nodes/Example/Example.node.js"
]
},
"devDependencies": {
"@n8n/node-cli": "*",
"eslint": "9.32.0",
"prettier": "3.6.2",
"release-it": "^19.0.4",
"typescript": "5.9.2"
},
"peerDependencies": {
"n8n-workflow": "*"
}
}

View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"strict": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2019",
"lib": ["es2019", "es2020", "es2022.error"],
"removeComments": true,
"useUnknownInCatchVariables": false,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"preserveConstEnums": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"declaration": true,
"sourceMap": true,
"skipLibCheck": true,
"outDir": "./dist/"
},
"include": ["credentials/**/*", "nodes/**/*", "nodes/**/*.json", "package.json"]
}

View File

@@ -0,0 +1,9 @@
export type CustomTemplateConfig = {
credentialType: 'apiKey' | 'bearer' | 'basicAuth' | 'custom' | 'none';
baseUrl: string;
} | {
credentialType: 'oauth2';
baseUrl: string;
flow: string;
};
export type CredentialType = CustomTemplateConfig['credentialType'];

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/template/templates/declarative/custom/types.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,8 @@
export type CustomTemplateConfig =
| {
credentialType: 'apiKey' | 'bearer' | 'basicAuth' | 'custom' | 'none';
baseUrl: string;
}
| { credentialType: 'oauth2'; baseUrl: string; flow: string };
export type CredentialType = CustomTemplateConfig['credentialType'];

View File

@@ -0,0 +1 @@
export declare const githubIssuesTemplate: import("../../../core").TemplateWithRun<object>;

View File

@@ -0,0 +1,14 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.githubIssuesTemplate = void 0;
const node_path_1 = __importDefault(require("node:path"));
const core_1 = require("../../../core");
exports.githubIssuesTemplate = (0, core_1.createTemplate)({
name: 'GitHub Issues API',
description: 'Demo node with multiple operations and credentials',
path: node_path_1.default.join(__dirname, 'template'),
});
//# sourceMappingURL=template.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../../src/template/templates/declarative/github-issues/template.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAE7B,wCAA+C;AAElC,QAAA,oBAAoB,GAAG,IAAA,qBAAc,EAAC;IAClD,IAAI,EAAE,mBAAmB;IACzB,WAAW,EAAE,oDAAoD;IACjE,IAAI,EAAE,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;CACtC,CAAC,CAAC"}

View File

@@ -0,0 +1,9 @@
import path from 'node:path';
import { createTemplate } from '../../../core';
export const githubIssuesTemplate = createTemplate({
name: 'GitHub Issues API',
description: 'Demo node with multiple operations and credentials',
path: path.join(__dirname, 'template'),
});

View File

@@ -0,0 +1,73 @@
# {{nodePackageName}}
This is an n8n community node. It lets you use GitHub Issues in your n8n workflows.
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/sustainable-use-license/) workflow automation platform.
[Installation](#installation)
[Operations](#operations)
[Credentials](#credentials)
[Compatibility](#compatibility)
[Usage](#usage)
[Resources](#resources)
## Installation
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
## Operations
- Issues
- Get an issue
- Get many issues in a repository
- Create a new issue
- Issue Comments
- Get many issue comments
## Credentials
You can use either access token or OAuth2 to use this node.
### Access token
1. Open your GitHub profile [Settings](https://github.com/settings/profile).
2. In the left navigation, select [Developer settings](https://github.com/settings/apps).
3. In the left navigation, under Personal access tokens, select Tokens (classic).
4. Select Generate new token > Generate new token (classic).
5. Enter a descriptive name for your token in the Note field, like n8n integration.
6. Select the Expiration you'd like for the token, or select No expiration.
7. Select Scopes for your token. For most of the n8n GitHub nodes, add the `repo` scope.
- A token without assigned scopes can only access public information.
8. Select Generate token.
9. Copy the token.
Refer to [Creating a personal access token (classic)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) for more information. Refer to Scopes for OAuth apps for more information on GitHub scopes.
![Generated Access token in GitHub](https://docs.github.com/assets/cb-17251/mw-1440/images/help/settings/personal-access-tokens.webp)
### OAuth2
If you're self-hosting n8n, create a new GitHub [OAuth app](https://docs.github.com/en/apps/oauth-apps):
1. Open your GitHub profile [Settings](https://github.com/settings/profile).
2. In the left navigation, select [Developer settings](https://github.com/settings/apps).
3. In the left navigation, select OAuth apps.
4. Select New OAuth App.
- If you haven't created an app before, you may see Register a new application instead. Select it.
5. Enter an Application name, like n8n integration.
6. Enter the Homepage URL for your app's website.
7. If you'd like, add the optional Application description, which GitHub displays to end-users.
8. From n8n, copy the OAuth Redirect URL and paste it into the GitHub Authorization callback URL.
9. Select Register application.
10. Copy the Client ID and Client Secret this generates and add them to your n8n credential.
Refer to the [GitHub Authorizing OAuth apps documentation](https://docs.github.com/en/apps/oauth-apps/using-oauth-apps/authorizing-oauth-apps) for more information on the authorization process.
## Compatibility
Compatible with n8n@1.60.0 or later
## Resources
* [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
* [GitHub API docs](https://docs.github.com/en/rest/issues)

View File

@@ -0,0 +1,45 @@
import type {
IAuthenticateGeneric,
Icon,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class GithubIssuesApi implements ICredentialType {
name = 'githubIssuesApi';
displayName = 'GitHub Issues API';
icon: Icon = { light: 'file:../icons/github.svg', dark: 'file:../icons/github.dark.svg' };
documentationUrl =
'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#deleting-a-personal-access-token';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
typeOptions: { password: true },
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=token {{$credentials?.accessToken}}',
},
},
};
test: ICredentialTestRequest = {
request: {
baseURL: 'https://api.github.com',
url: '/user',
method: 'GET',
},
};
}

View File

@@ -0,0 +1,54 @@
import type { Icon, ICredentialType, INodeProperties } from 'n8n-workflow';
export class GithubIssuesOAuth2Api implements ICredentialType {
name = 'githubIssuesOAuth2Api';
extends = ['oAuth2Api'];
displayName = 'GitHub Issues OAuth2 API';
icon: Icon = { light: 'file:../icons/github.svg', dark: 'file:../icons/github.dark.svg' };
documentationUrl = 'https://docs.github.com/en/apps/oauth-apps';
properties: INodeProperties[] = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://github.com/login/oauth/authorize',
required: true,
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://github.com/login/oauth/access_token',
required: true,
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: 'repo',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'header',
},
];
}

View File

@@ -0,0 +1,3 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0165 0C8.94791 0 0 9.01388 0 20.1653C0 29.0792 5.73324 36.6246 13.6868 39.2952C14.6812 39.496 15.0454 38.8613 15.0454 38.3274C15.0454 37.8599 15.0126 36.2575 15.0126 34.5879C9.4445 35.79 8.28498 32.1841 8.28498 32.1841C7.39015 29.847 6.06429 29.2463 6.06429 29.2463C4.24185 28.011 6.19704 28.011 6.19704 28.011C8.21861 28.1446 9.27938 30.081 9.27938 30.081C11.0686 33.1522 13.9518 32.2844 15.1118 31.7502C15.2773 30.4481 15.8079 29.5467 16.3713 29.046C11.9303 28.5785 7.25781 26.8425 7.25781 19.0967C7.25781 16.8932 8.05267 15.0905 9.31216 13.6884C9.11344 13.1877 8.41732 11.1174 9.51128 8.34644C9.51128 8.34644 11.2014 7.81217 15.0122 10.4164C16.6438 9.97495 18.3263 9.7504 20.0165 9.74851C21.7067 9.74851 23.4295 9.98246 25.0205 10.4164C28.8317 7.81217 30.5218 8.34644 30.5218 8.34644C31.6158 11.1174 30.9192 13.1877 30.7205 13.6884C32.0132 15.0905 32.7753 16.8932 32.7753 19.0967C32.7753 26.8425 28.1028 28.5449 23.6287 29.046C24.358 29.6802 24.9873 30.882 24.9873 32.7851C24.9873 35.4893 24.9545 37.6596 24.9545 38.327C24.9545 38.8613 25.3192 39.496 26.3132 39.2956C34.2667 36.6242 39.9999 29.0792 39.9999 20.1653C40.0327 9.01388 31.052 0 20.0165 0Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,3 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0165 0C8.94791 0 0 9.01388 0 20.1653C0 29.0792 5.73324 36.6246 13.6868 39.2952C14.6812 39.496 15.0454 38.8613 15.0454 38.3274C15.0454 37.8599 15.0126 36.2575 15.0126 34.5879C9.4445 35.79 8.28498 32.1841 8.28498 32.1841C7.39015 29.847 6.06429 29.2463 6.06429 29.2463C4.24185 28.011 6.19704 28.011 6.19704 28.011C8.21861 28.1446 9.27938 30.081 9.27938 30.081C11.0686 33.1522 13.9518 32.2844 15.1118 31.7502C15.2773 30.4481 15.8079 29.5467 16.3713 29.046C11.9303 28.5785 7.25781 26.8425 7.25781 19.0967C7.25781 16.8932 8.05267 15.0905 9.31216 13.6884C9.11344 13.1877 8.41732 11.1174 9.51128 8.34644C9.51128 8.34644 11.2014 7.81217 15.0122 10.4164C16.6438 9.97495 18.3263 9.7504 20.0165 9.74851C21.7067 9.74851 23.4295 9.98246 25.0205 10.4164C28.8317 7.81217 30.5218 8.34644 30.5218 8.34644C31.6158 11.1174 30.9192 13.1877 30.7205 13.6884C32.0132 15.0905 32.7753 16.8932 32.7753 19.0967C32.7753 26.8425 28.1028 28.5449 23.6287 29.046C24.358 29.6802 24.9873 30.882 24.9873 32.7851C24.9873 35.4893 24.9545 37.6596 24.9545 38.327C24.9545 38.8613 25.3192 39.496 26.3132 39.2956C34.2667 36.6242 39.9999 29.0792 39.9999 20.1653C40.0327 9.01388 31.052 0 20.0165 0Z" fill="#24292F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,18 @@
{
"node": "{{nodePackageName}}",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development", "Developer Tools"],
"resources": {
"credentialDocumentation": [
{
"url": "https://github.com/org/repo?tab=readme-ov-file#credentials"
}
],
"primaryDocumentation": [
{
"url": "https://github.com/org/repo?tab=readme-ov-file"
}
]
}
}

View File

@@ -0,0 +1,96 @@
import { NodeConnectionType, type INodeType, type INodeTypeDescription } from 'n8n-workflow';
import { issueDescription } from './resources/issue';
import { issueCommentDescription } from './resources/issueComment';
import { getRepositories } from './listSearch/getRepositories';
import { getUsers } from './listSearch/getUsers';
import { getIssues } from './listSearch/getIssues';
export class GithubIssues implements INodeType {
description: INodeTypeDescription = {
displayName: 'GitHub Issues',
name: 'githubIssues',
icon: { light: 'file:../../icons/github.svg', dark: 'file:../../icons/github.dark.svg' },
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume issues from the GitHub API',
defaults: {
name: 'GitHub Issues',
},
usableAsTool: true,
inputs: [NodeConnectionType.Main],
outputs: [NodeConnectionType.Main],
credentials: [
{
name: 'githubIssuesApi',
required: true,
displayOptions: {
show: {
authentication: ['accessToken'],
},
},
},
{
name: 'githubIssuesOAuth2Api',
required: true,
displayOptions: {
show: {
authentication: ['oAuth2'],
},
},
},
],
requestDefaults: {
baseURL: 'https://api.github.com',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
},
properties: [
{
displayName: 'Authentication',
name: 'authentication',
type: 'options',
options: [
{
name: 'Access Token',
value: 'accessToken',
},
{
name: 'OAuth2',
value: 'oAuth2',
},
],
default: 'accessToken',
},
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Issue',
value: 'issue',
},
{
name: 'Issue Comment',
value: 'issueComment',
},
],
default: 'issue',
},
...issueDescription,
...issueCommentDescription,
],
};
methods = {
listSearch: {
getRepositories,
getUsers,
getIssues,
},
};
}

View File

@@ -0,0 +1,49 @@
import type {
ILoadOptionsFunctions,
INodeListSearchResult,
INodeListSearchItems,
} from 'n8n-workflow';
import { githubApiRequest } from '../shared/transport';
type IssueSearchItem = {
number: number;
title: string;
html_url: string;
};
type IssueSearchResponse = {
items: IssueSearchItem[];
total_count: number;
};
export async function getIssues(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const page = paginationToken ? +paginationToken : 1;
const per_page = 100;
let responseData: IssueSearchResponse = {
items: [],
total_count: 0,
};
const owner = this.getNodeParameter('owner', '', { extractValue: true });
const repository = this.getNodeParameter('repository', '', { extractValue: true });
const filters = [filter, `repo:${owner}/${repository}`];
responseData = await githubApiRequest.call(this, 'GET', '/search/issues', {
q: filters.filter(Boolean).join(' '),
page,
per_page,
});
const results: INodeListSearchItems[] = responseData.items.map((item: IssueSearchItem) => ({
name: item.title,
value: item.number,
url: item.html_url,
}));
const nextPaginationToken = page * per_page < responseData.total_count ? page + 1 : undefined;
return { results, paginationToken: nextPaginationToken };
}

View File

@@ -0,0 +1,50 @@
import type {
ILoadOptionsFunctions,
INodeListSearchItems,
INodeListSearchResult,
} from 'n8n-workflow';
import { githubApiRequest } from '../shared/transport';
type RepositorySearchItem = {
name: string;
html_url: string;
};
type RepositorySearchResponse = {
items: RepositorySearchItem[];
total_count: number;
};
export async function getRepositories(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const owner = this.getCurrentNodeParameter('owner', { extractValue: true });
const page = paginationToken ? +paginationToken : 1;
const per_page = 100;
const q = `${filter ?? ''} user:${owner} fork:true`;
let responseData: RepositorySearchResponse = {
items: [],
total_count: 0,
};
try {
responseData = await githubApiRequest.call(this, 'GET', '/search/repositories', {
q,
page,
per_page,
});
} catch {
// will fail if the owner does not have any repositories
}
const results: INodeListSearchItems[] = responseData.items.map((item: RepositorySearchItem) => ({
name: item.name,
value: item.name,
url: item.html_url,
}));
const nextPaginationToken = page * per_page < responseData.total_count ? page + 1 : undefined;
return { results, paginationToken: nextPaginationToken };
}

View File

@@ -0,0 +1,49 @@
import type {
ILoadOptionsFunctions,
INodeListSearchResult,
INodeListSearchItems,
} from 'n8n-workflow';
import { githubApiRequest } from '../shared/transport';
type UserSearchItem = {
login: string;
html_url: string;
};
type UserSearchResponse = {
items: UserSearchItem[];
total_count: number;
};
export async function getUsers(
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
): Promise<INodeListSearchResult> {
const page = paginationToken ? +paginationToken : 1;
const per_page = 100;
let responseData: UserSearchResponse = {
items: [],
total_count: 0,
};
try {
responseData = await githubApiRequest.call(this, 'GET', '/search/users', {
q: filter,
page,
per_page,
});
} catch {
// will fail if the owner does not have any users
}
const results: INodeListSearchItems[] = responseData.items.map((item: UserSearchItem) => ({
name: item.login,
value: item.login,
url: item.html_url,
}));
const nextPaginationToken = page * per_page < responseData.total_count ? page + 1 : undefined;
return { results, paginationToken: nextPaginationToken };
}

View File

@@ -0,0 +1,74 @@
import type { INodeProperties } from 'n8n-workflow';
const showOnlyForIssueCreate = {
operation: ['create'],
resource: ['issue'],
};
export const issueCreateDescription: INodeProperties[] = [
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
required: true,
displayOptions: {
show: showOnlyForIssueCreate,
},
description: 'The title of the issue',
routing: {
send: {
type: 'body',
property: 'title',
},
},
},
{
displayName: 'Body',
name: 'body',
type: 'string',
typeOptions: {
rows: 5,
},
default: '',
displayOptions: {
show: showOnlyForIssueCreate,
},
description: 'The body of the issue',
routing: {
send: {
type: 'body',
property: 'body',
},
},
},
{
displayName: 'Labels',
name: 'labels',
type: 'collection',
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add Label',
},
displayOptions: {
show: showOnlyForIssueCreate,
},
default: { label: '' },
options: [
{
displayName: 'Label',
name: 'label',
type: 'string',
default: '',
description: 'Label to add to issue',
},
],
routing: {
send: {
type: 'body',
property: 'labels',
value: '={{$value.map((data) => data.label)}}',
},
},
},
];

View File

@@ -0,0 +1,14 @@
import type { INodeProperties } from 'n8n-workflow';
import { issueSelect } from '../../shared/descriptions';
const showOnlyForIssueGet = {
operation: ['get'],
resource: ['issue'],
};
export const issueGetDescription: INodeProperties[] = [
{
...issueSelect,
displayOptions: { show: showOnlyForIssueGet },
},
];

View File

@@ -0,0 +1,124 @@
import type { INodeProperties } from 'n8n-workflow';
import { parseLinkHeader } from '../../shared/utils';
const showOnlyForIssueGetMany = {
operation: ['getAll'],
resource: ['issue'],
};
export const issueGetManyDescription: INodeProperties[] = [
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
...showOnlyForIssueGetMany,
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
routing: {
send: {
type: 'query',
property: 'per_page',
},
output: {
maxResults: '={{$value}}',
},
},
description: 'Max number of results to return',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: showOnlyForIssueGetMany,
},
default: false,
description: 'Whether to return all results or only up to a given limit',
routing: {
send: {
paginate: '={{ $value }}',
type: 'query',
property: 'per_page',
value: '100',
},
operations: {
pagination: {
type: 'generic',
properties: {
continue: `={{ !!(${parseLinkHeader.toString()})($response.headers?.link).next }}`,
request: {
url: `={{ (${parseLinkHeader.toString()})($response.headers?.link)?.next ?? $request.url }}`,
},
},
},
},
},
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
typeOptions: {
multipleValueButtonText: 'Add Filter',
},
displayOptions: {
show: showOnlyForIssueGetMany,
},
default: {},
options: [
{
displayName: 'Updated Since',
name: 'since',
type: 'dateTime',
default: '',
description: 'Return only issues updated at or after this time',
routing: {
request: {
qs: {
since: '={{$value}}',
},
},
},
},
{
displayName: 'State',
name: 'state',
type: 'options',
options: [
{
name: 'All',
value: 'all',
description: 'Returns issues with any state',
},
{
name: 'Closed',
value: 'closed',
description: 'Return issues with "closed" state',
},
{
name: 'Open',
value: 'open',
description: 'Return issues with "open" state',
},
],
default: 'open',
description: 'The issue state to filter on',
routing: {
request: {
qs: {
state: '={{$value}}',
},
},
},
},
],
},
];

View File

@@ -0,0 +1,75 @@
import type { INodeProperties } from 'n8n-workflow';
import { repoNameSelect, repoOwnerSelect } from '../../shared/descriptions';
import { issueGetManyDescription } from './getAll';
import { issueGetDescription } from './get';
import { issueCreateDescription } from './create';
const showOnlyForIssues = {
resource: ['issue'],
};
export const issueDescription: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: showOnlyForIssues,
},
options: [
{
name: 'Get Many',
value: 'getAll',
action: 'Get issues in a repository',
description: 'Get many issues in a repository',
routing: {
request: {
method: 'GET',
url: '=/repos/{{$parameter.owner}}/{{$parameter.repository}}/issues',
},
},
},
{
name: 'Get',
value: 'get',
action: 'Get an issue',
description: 'Get the data of a single issue',
routing: {
request: {
method: 'GET',
url: '=/repos/{{$parameter.owner}}/{{$parameter.repository}}/issues/{{$parameter.issue}}',
},
},
},
{
name: 'Create',
value: 'create',
action: 'Create a new issue',
description: 'Create a new issue',
routing: {
request: {
method: 'POST',
url: '=/repos/{{$parameter.owner}}/{{$parameter.repository}}/issues',
},
},
},
],
default: 'getAll',
},
{
...repoOwnerSelect,
displayOptions: {
show: showOnlyForIssues,
},
},
{
...repoNameSelect,
displayOptions: {
show: showOnlyForIssues,
},
},
...issueGetManyDescription,
...issueGetDescription,
...issueCreateDescription,
];

View File

@@ -0,0 +1,65 @@
import type { INodeProperties } from 'n8n-workflow';
import { parseLinkHeader } from '../../shared/utils';
const showOnlyForIssueCommentGetMany = {
operation: ['getAll'],
resource: ['issueComment'],
};
export const issueCommentGetManyDescription: INodeProperties[] = [
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
...showOnlyForIssueCommentGetMany,
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
routing: {
send: {
type: 'query',
property: 'per_page',
},
output: {
maxResults: '={{$value}}',
},
},
description: 'Max number of results to return',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: showOnlyForIssueCommentGetMany,
},
default: false,
description: 'Whether to return all results or only up to a given limit',
routing: {
send: {
paginate: '={{ $value }}',
type: 'query',
property: 'per_page',
value: '100',
},
operations: {
pagination: {
type: 'generic',
properties: {
continue: `={{ !!(${parseLinkHeader.toString()})($response.headers?.link).next }}`,
request: {
url: `={{ (${parseLinkHeader.toString()})($response.headers?.link)?.next ?? $request.url }}`,
},
},
},
},
},
},
];

View File

@@ -0,0 +1,47 @@
import type { INodeProperties } from 'n8n-workflow';
import { repoNameSelect, repoOwnerSelect } from '../../shared/descriptions';
import { issueCommentGetManyDescription } from './getAll';
const showOnlyForIssueComments = {
resource: ['issueComment'],
};
export const issueCommentDescription: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: showOnlyForIssueComments,
},
options: [
{
name: 'Get Many',
value: 'getAll',
action: 'Get issue comments',
description: 'Get issue comments',
routing: {
request: {
method: 'GET',
url: '=/repos/{{$parameter.owner}}/{{$parameter.repository}}/issues/comments',
},
},
},
],
default: 'getAll',
},
{
...repoOwnerSelect,
displayOptions: {
show: showOnlyForIssueComments,
},
},
{
...repoNameSelect,
displayOptions: {
show: showOnlyForIssueComments,
},
},
...issueCommentGetManyDescription,
];

View File

@@ -0,0 +1,151 @@
import type { INodeProperties } from 'n8n-workflow';
export const repoOwnerSelect: INodeProperties = {
displayName: 'Repository Owner',
name: 'owner',
type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
modes: [
{
displayName: 'Repository Owner',
name: 'list',
type: 'list',
placeholder: 'Select an owner...',
typeOptions: {
searchListMethod: 'getUsers',
searchable: true,
searchFilterRequired: false,
},
},
{
displayName: 'Link',
name: 'url',
type: 'string',
placeholder: 'e.g. https://github.com/n8n-io',
extractValue: {
type: 'regex',
regex: 'https:\\/\\/github.com\\/([-_0-9a-zA-Z]+)',
},
validation: [
{
type: 'regex',
properties: {
regex: 'https:\\/\\/github.com\\/([-_0-9a-zA-Z]+)(?:.*)',
errorMessage: 'Not a valid GitHub URL',
},
},
],
},
{
displayName: 'By Name',
name: 'name',
type: 'string',
placeholder: 'e.g. n8n-io',
validation: [
{
type: 'regex',
properties: {
regex: '[-_a-zA-Z0-9]+',
errorMessage: 'Not a valid GitHub Owner Name',
},
},
],
url: '=https://github.com/{{$value}}',
},
],
};
export const repoNameSelect: INodeProperties = {
displayName: 'Repository Name',
name: 'repository',
type: 'resourceLocator',
default: {
mode: 'list',
value: '',
},
required: true,
modes: [
{
displayName: 'Repository Name',
name: 'list',
type: 'list',
placeholder: 'Select an Repository...',
typeOptions: {
searchListMethod: 'getRepositories',
searchable: true,
},
},
{
displayName: 'Link',
name: 'url',
type: 'string',
placeholder: 'e.g. https://github.com/n8n-io/n8n',
extractValue: {
type: 'regex',
regex: 'https:\\/\\/github.com\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)',
},
validation: [
{
type: 'regex',
properties: {
regex: 'https:\\/\\/github.com\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)(?:.*)',
errorMessage: 'Not a valid GitHub Repository URL',
},
},
],
},
{
displayName: 'By Name',
name: 'name',
type: 'string',
placeholder: 'e.g. n8n',
validation: [
{
type: 'regex',
properties: {
regex: '[-_.0-9a-zA-Z]+',
errorMessage: 'Not a valid GitHub Repository Name',
},
},
],
url: '=https://github.com/{{$parameter["owner"]}}/{{$value}}',
},
],
displayOptions: {
hide: {
resource: ['user', 'organization'],
operation: ['getRepositories'],
},
},
};
export const issueSelect: INodeProperties = {
displayName: 'Issue',
name: 'issue',
type: 'resourceLocator',
default: {
mode: 'list',
value: '',
},
required: true,
modes: [
{
displayName: 'Issue',
name: 'list',
type: 'list',
placeholder: 'Select an Issue...',
typeOptions: {
searchListMethod: 'getIssues',
searchable: true,
},
},
{
displayName: 'By ID',
name: 'name',
type: 'string',
placeholder: 'e.g. 123',
url: '=https://github.com/{{$parameter.owner}}/{{$parameter.repository}}/issues/{{$value}}',
},
],
};

View File

@@ -0,0 +1,32 @@
import type {
IHookFunctions,
IExecuteFunctions,
IExecuteSingleFunctions,
ILoadOptionsFunctions,
IHttpRequestMethods,
IDataObject,
IHttpRequestOptions,
} from 'n8n-workflow';
export async function githubApiRequest(
this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
method: IHttpRequestMethods,
resource: string,
qs: IDataObject = {},
body: IDataObject | undefined = undefined,
) {
const authenticationMethod = this.getNodeParameter('authentication', 0);
const options: IHttpRequestOptions = {
method: method,
qs,
body,
url: `https://api.github.com${resource}`,
json: true,
};
const credentialType =
authenticationMethod === 'accessToken' ? 'githubIssuesApi' : 'githubIssuesOAuth2Api';
return this.helpers.httpRequestWithAuthentication.call(this, credentialType, options);
}

View File

@@ -0,0 +1,14 @@
export function parseLinkHeader(header?: string): { [rel: string]: string } {
const links: { [rel: string]: string } = {};
for (const part of header?.split(',') ?? []) {
const section = part.trim();
const match = section.match(/^<([^>]+)>\s*;\s*rel="?([^"]+)"?/);
if (match) {
const [, url, rel] = match;
links[rel] = url;
}
}
return links;
}

View File

@@ -0,0 +1,51 @@
{
"name": "{{nodePackageName}}",
"version": "0.1.0",
"description": "",
"license": "MIT",
"homepage": "",
"keywords": [
"n8n-community-node-package"
],
"author": {
"name": "{{user.name}}",
"email": "{{user.email}}"
},
"repository": {
"type": "git",
"url": "https://github.com/<...>/n8n-nodes-<...>.git"
},
"scripts": {
"build": "n8n-node build",
"build:watch": "tsc --watch",
"dev": "n8n-node dev",
"lint": "n8n-node lint",
"lint:fix": "n8n-node lint --fix",
"release": "n8n-node release",
"prepublishOnly": "n8n-node prerelease"
},
"files": [
"dist"
],
"n8n": {
"n8nNodesApiVersion": 1,
"strict": true,
"credentials": [
"dist/credentials/GithubIssuesApi.credentials.js",
"dist/credentials/GithubIssuesOAuth2Api.credentials.js"
],
"nodes": [
"dist/nodes/GithubIssues/GithubIssues.node.js"
]
},
"devDependencies": {
"@n8n/node-cli": "*",
"eslint": "9.32.0",
"prettier": "3.6.2",
"release-it": "^19.0.4",
"typescript": "5.9.2"
},
"peerDependencies": {
"n8n-workflow": "*"
}
}

View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"strict": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2019",
"lib": ["es2019", "es2020", "es2022.error"],
"removeComments": true,
"useUnknownInCatchVariables": false,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"preserveConstEnums": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"declaration": true,
"sourceMap": true,
"skipLibCheck": true,
"outDir": "./dist/"
},
"include": ["credentials/**/*", "nodes/**/*", "nodes/**/*.json", "package.json"]
}

View File

@@ -0,0 +1,15 @@
export declare const templates: {
readonly declarative: {
readonly githubIssues: import("../core").TemplateWithRun<object>;
readonly custom: import("../core").TemplateWithRun<import("./declarative/custom/types").CustomTemplateConfig>;
};
readonly programmatic: {
readonly example: import("../core").TemplateWithRun<object>;
};
};
export type TemplateMap = typeof templates;
export type TemplateType = keyof TemplateMap;
export type TemplateName<T extends TemplateType> = keyof TemplateMap[T];
export declare function getTemplate<T extends TemplateType, N extends TemplateName<T>>(type: T, name: N): TemplateMap[T][N];
export declare function isTemplateType(val: unknown): val is TemplateType;
export declare function isTemplateName<T extends TemplateType>(type: T, name: unknown): name is TemplateName<T>;

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.templates = void 0;
exports.getTemplate = getTemplate;
exports.isTemplateType = isTemplateType;
exports.isTemplateName = isTemplateName;
const template_1 = require("./declarative/custom/template");
const template_2 = require("./declarative/github-issues/template");
const template_3 = require("./programmatic/example/template");
exports.templates = {
declarative: {
githubIssues: template_2.githubIssuesTemplate,
custom: template_1.customTemplate,
},
programmatic: {
example: template_3.exampleTemplate,
},
};
function getTemplate(type, name) {
return exports.templates[type][name];
}
function isTemplateType(val) {
return typeof val === 'string' && val in exports.templates;
}
function isTemplateName(type, name) {
return typeof name === 'string' && name in exports.templates[type];
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/template/templates/index.ts"],"names":[],"mappings":";;;AAkBA,kCAKC;AAED,wCAEC;AAED,wCAKC;AAlCD,4DAA+D;AAC/D,mEAA4E;AAC5E,8DAAkE;AAErD,QAAA,SAAS,GAAG;IACxB,WAAW,EAAE;QACZ,YAAY,EAAE,+BAAoB;QAClC,MAAM,EAAE,yBAAc;KACtB;IACD,YAAY,EAAE;QACb,OAAO,EAAE,0BAAe;KACxB;CACQ,CAAC;AAMX,SAAgB,WAAW,CAC1B,IAAO,EACP,IAAO;IAEP,OAAO,iBAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,SAAgB,cAAc,CAAC,GAAY;IAC1C,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,IAAI,iBAAS,CAAC;AACpD,CAAC;AAED,SAAgB,cAAc,CAC7B,IAAO,EACP,IAAa;IAEb,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,iBAAS,CAAC,IAAI,CAAC,CAAC;AAC5D,CAAC"}

Some files were not shown because too many files have changed in this diff Show More