diff --git a/README.md b/README.md index 06ea132..8e9cf6d 100644 --- a/README.md +++ b/README.md @@ -1,247 +1,31 @@ -![Banner image](https://user-images.githubusercontent.com/10284570/173569848-c624317f-42b1-45a6-ab09-f0ea3c247648.png) +# n8n-nodes-scraperapi -# n8n-nodes-starter +This is an n8n community node. It lets you use _ScraperAPI_ in your n8n workflows. -This starter repository helps you build custom integrations for [n8n](https://n8n.io). It includes example nodes, credentials, the node linter, and all the tooling you need to get started. +_ScraperAPI_ is a solution to help you unlock and scrape any website, no matter the scale or difficulty. -## Quick Start +[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform. -> [!TIP] -> **New to building n8n nodes?** The fastest way to get started is with `npm create @n8n/node`. This command scaffolds a complete node package for you using the [@n8n/node-cli](https://www.npmjs.com/package/@n8n/node-cli). +[Installation](#installation) +[Credentials](#credentials) +[Compatibility](#compatibility) +[Resources](#resources) +[Version history](#version-history) -**To create a new node package from scratch:** +## Installation -```bash -npm create @n8n/node -``` +Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation. -**Already using this starter? Start developing with:** +## Credentials -```bash -npm run dev -``` - -This starts n8n with your nodes loaded and hot reload enabled. - -## What's Included - -This starter repository includes two example nodes to learn from: - -- **[Example Node](nodes/Example/)** - A simple starter node that shows the basic structure with a custom `execute` method -- **[GitHub Issues Node](nodes/GithubIssues/)** - A complete, production-ready example built using the **declarative style**: - - **Low-code approach** - Define operations declaratively without writing request logic - - Multiple resources (Issues, Comments) - - Multiple operations (Get, Get All, Create) - - Two authentication methods (OAuth2 and Personal Access Token) - - List search functionality for dynamic dropdowns - - Proper error handling and typing - - Ideal for HTTP API-based integrations - -> [!TIP] -> The declarative/low-code style (used in GitHub Issues) is the recommended approach for building nodes that interact with HTTP APIs. It significantly reduces boilerplate code and handles requests automatically. - -Browse these examples to understand both approaches, then modify them or create your own. - -## Finding Inspiration - -Looking for more examples? Check out these resources: - -- **[npm Community Nodes](https://www.npmjs.com/search?q=keywords:n8n-community-node-package)** - Browse thousands of community-built nodes on npm using the `n8n-community-node-package` tag -- **[n8n Built-in Nodes](https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/nodes)** - Study the source code of n8n's official nodes for production-ready patterns and best practices -- **[n8n Credentials](https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/credentials)** - See how authentication is implemented for various services - -These are excellent resources to understand how to structure your nodes, handle different API patterns, and implement advanced features. - -## Prerequisites - -Before you begin, install the following on your development machine: - -### Required - -- **[Node.js](https://nodejs.org/)** (v22 or higher) and npm - - Linux/Mac/WSL: Install via [nvm](https://github.com/nvm-sh/nvm) - - Windows: Follow [Microsoft's NodeJS guide](https://learn.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-windows) -- **[git](https://git-scm.com/downloads)** - -### Recommended - -- Follow n8n's [development environment setup guide](https://docs.n8n.io/integrations/creating-nodes/build/node-development-environment/) - -> [!NOTE] -> The `@n8n/node-cli` is included as a dev dependency and will be installed automatically when you run `npm install`. The CLI includes n8n for local development, so you don't need to install n8n globally. - -## Getting Started with this Starter - -Follow these steps to create your own n8n community node package: - -### 1. Create Your Repository - -[Generate a new repository](https://github.com/n8n-io/n8n-nodes-starter/generate) from this template, then clone it: - -```bash -git clone https://github.com//.git -cd -``` - -### 2. Install Dependencies - -```bash -npm install -``` - -This installs all required dependencies including the `@n8n/node-cli`. - -### 3. Explore the Examples - -Browse the example nodes in [nodes/](nodes/) and [credentials/](credentials/) to understand the structure: - -- Start with [nodes/Example/](nodes/Example/) for a basic node -- Study [nodes/GithubIssues/](nodes/GithubIssues/) for a real-world implementation - -### 4. Build Your Node - -Edit the example nodes to fit your use case, or create new node files by copying the structure from [nodes/Example/](nodes/Example/). - -> [!TIP] -> If you want to scaffold a completely new node package, use `npm create @n8n/node` to start fresh with the CLI's interactive generator. - -### 5. Configure Your Package - -Update `package.json` with your details: - -- `name` - Your package name (must start with `n8n-nodes-`) -- `author` - Your name and email -- `repository` - Your repository URL -- `description` - What your node does - -Make sure your node is registered in the `n8n.nodes` array. - -### 6. Develop and Test Locally - -Start n8n with your node loaded: - -```bash -npm run dev -``` - -This command runs `n8n-node dev` which: - -- Builds your node with watch mode -- Starts n8n with your node available -- Automatically rebuilds when you make changes -- Opens n8n in your browser (usually http://localhost:5678) - -You can now test your node in n8n workflows! - -> [!NOTE] -> Learn more about CLI commands in the [@n8n/node-cli documentation](https://www.npmjs.com/package/@n8n/node-cli). - -### 7. Lint Your Code - -Check for errors: - -```bash -npm run lint -``` - -Auto-fix issues when possible: - -```bash -npm run lint:fix -``` - -### 8. Build for Production - -When ready to publish: - -```bash -npm run build -``` - -This compiles your TypeScript code to the `dist/` folder. - -### 9. Prepare for Publishing - -Before publishing: - -1. **Update documentation**: Replace this README with your node's documentation. Use [README_TEMPLATE.md](README_TEMPLATE.md) as a starting point. -2. **Update the LICENSE**: Add your details to the [LICENSE](LICENSE.md) file. -3. **Test thoroughly**: Ensure your node works in different scenarios. - -### 10. Publish to npm - -Publish your package to make it available to the n8n community: - -```bash -npm publish -``` - -Learn more about [publishing to npm](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry). - -### 11. Submit for Verification (Optional) - -Get your node verified for n8n Cloud: - -1. Ensure your node meets the [requirements](https://docs.n8n.io/integrations/creating-nodes/deploy/submit-community-nodes/): - - Uses MIT license ✅ (included in this starter) - - No external package dependencies - - Follows n8n's design guidelines - - Passes quality and security review - -2. Submit through the [n8n Creator Portal](https://creators.n8n.io/nodes) - -**Benefits of verification:** - -- Available directly in n8n Cloud -- Discoverable in the n8n nodes panel -- Verified badge for quality assurance -- Increased visibility in the n8n community - -## Available Scripts - -This starter includes several npm scripts to streamline development: - -| Script | Description | -| --------------------- | ---------------------------------------------------------------- | -| `npm run dev` | Start n8n with your node and watch for changes (runs `n8n-node dev`) | -| `npm run build` | Compile TypeScript to JavaScript for production (runs `n8n-node build`) | -| `npm run build:watch` | Build in watch mode (auto-rebuild on changes) | -| `npm run lint` | Check your code for errors and style issues (runs `n8n-node lint`) | -| `npm run lint:fix` | Automatically fix linting issues when possible (runs `n8n-node lint --fix`) | -| `npm run release` | Create a new release (runs `n8n-node release`) | - -> [!TIP] -> These scripts use the [@n8n/node-cli](https://www.npmjs.com/package/@n8n/node-cli) under the hood. You can also run CLI commands directly, e.g., `npx n8n-node dev`. - -## Troubleshooting - -### My node doesn't appear in n8n - -1. Make sure you ran `npm install` to install dependencies -2. Check that your node is listed in `package.json` under `n8n.nodes` -3. Restart the dev server with `npm run dev` -4. Check the console for any error messages - -### Linting errors - -Run `npm run lint:fix` to automatically fix most common issues. For remaining errors, check the [n8n node development guidelines](https://docs.n8n.io/integrations/creating-nodes/). - -### TypeScript errors - -Make sure you're using Node.js v22 or higher and have run `npm install` to get all type definitions. +Sign up and get your API key [here](https://dashboard.scraperapi.com/signup) ## Resources -- **[n8n Node Documentation](https://docs.n8n.io/integrations/creating-nodes/)** - Complete guide to building nodes -- **[n8n Community Forum](https://community.n8n.io/)** - Get help and share your nodes -- **[@n8n/node-cli Documentation](https://www.npmjs.com/package/@n8n/node-cli)** - CLI tool reference -- **[n8n Creator Portal](https://creators.n8n.io/nodes)** - Submit your node for verification -- **[Submit Community Nodes Guide](https://docs.n8n.io/integrations/creating-nodes/deploy/submit-community-nodes/)** - Verification requirements and process +- API Endpoint: use ScraperAPI's endpoint for web scraping. Configure parameters and send GET requests enabling JS rendering, setting geotargeting, etc. -## Contributing +## Version history -Have suggestions for improving this starter? [Open an issue](https://github.com/n8n-io/n8n-nodes-starter/issues) or submit a pull request! +- "0.1.0": API resource. -## License -[MIT](https://github.com/n8n-io/n8n-nodes-starter/blob/master/LICENSE.md) diff --git a/README_TEMPLATE.md b/README_TEMPLATE.md deleted file mode 100644 index a8d7860..0000000 --- a/README_TEMPLATE.md +++ /dev/null @@ -1,48 +0,0 @@ -# n8n-nodes-_node-name_ - -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/reference/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._ - - diff --git a/credentials/GithubIssuesApi.credentials.ts b/credentials/GithubIssuesApi.credentials.ts deleted file mode 100644 index f8b267e..0000000 --- a/credentials/GithubIssuesApi.credentials.ts +++ /dev/null @@ -1,45 +0,0 @@ -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', - }, - }; -} diff --git a/credentials/GithubIssuesOAuth2Api.credentials.ts b/credentials/GithubIssuesOAuth2Api.credentials.ts deleted file mode 100644 index 0eb98fc..0000000 --- a/credentials/GithubIssuesOAuth2Api.credentials.ts +++ /dev/null @@ -1,54 +0,0 @@ -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', - }, - ]; -} diff --git a/credentials/ScraperApiApi.credentials.ts b/credentials/ScraperApiApi.credentials.ts new file mode 100644 index 0000000..1880108 --- /dev/null +++ b/credentials/ScraperApiApi.credentials.ts @@ -0,0 +1,43 @@ +import type { + IAuthenticateGeneric, + Icon, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; + +export class ScraperApiApi implements ICredentialType { + name = 'scraperApi-Api'; + displayName = 'ScraperAPI API'; + icon: Icon = 'file:../icons/ScraperApi.svg'; + documentationUrl = 'https://docs.scraperapi.com/dashboard-and-billing/api-key'; + properties: INodeProperties[] = [ + { + displayName: 'API Key', + name: 'apiKey', + type: 'string', + typeOptions: { + password: true, + }, + default: '', + }, + ]; + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + qs: { + api_key: '={{$credentials.apiKey}}', + }, + }, + }; + test: ICredentialTestRequest = { + request: { + baseURL: 'https://api.scraperapi.com', + url: '/', + method: 'GET', + qs: { + url: 'https://httpbin.org/ip', + }, + }, + }; +} diff --git a/icons/ScraperApi.svg b/icons/ScraperApi.svg new file mode 100644 index 0000000..4ab7347 --- /dev/null +++ b/icons/ScraperApi.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/icons/github.dark.svg b/icons/github.dark.svg deleted file mode 100644 index 0366b08..0000000 --- a/icons/github.dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/icons/github.svg b/icons/github.svg deleted file mode 100644 index fe1ac05..0000000 --- a/icons/github.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/nodes/Example/Example.node.json b/nodes/Example/Example.node.json deleted file mode 100644 index 266250f..0000000 --- a/nodes/Example/Example.node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "node": "n8n-nodes-example", - "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" - } - ] - } -} diff --git a/nodes/Example/Example.node.ts b/nodes/Example/Example.node.ts deleted file mode 100644 index fb5e019..0000000 --- a/nodes/Example/Example.node.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { - IExecuteFunctions, - INodeExecutionData, - INodeType, - INodeTypeDescription, -} from 'n8n-workflow'; -import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow'; - -export class Example implements INodeType { - description: INodeTypeDescription = { - displayName: 'Example', - name: 'example', - icon: { light: 'file:example.svg', dark: 'file:example.dark.svg' }, - group: ['input'], - version: 1, - description: 'Basic Example Node', - defaults: { - name: 'Example', - }, - inputs: [NodeConnectionTypes.Main], - outputs: [NodeConnectionTypes.Main], - usableAsTool: true, - properties: [ - // Node properties which the user gets displayed and - // can change on the node. - { - displayName: 'My String', - name: 'myString', - type: 'string', - default: '', - placeholder: 'Placeholder value', - description: 'The description text', - }, - ], - }; - - // The function below is responsible for actually doing whatever this node - // is supposed to do. In this case, we're just appending the `myString` property - // with whatever the user has entered. - // You can make async calls and use `await`. - async execute(this: IExecuteFunctions): Promise { - const items = this.getInputData(); - - let item: INodeExecutionData; - let myString: string; - - // Iterates over all input items and add the key "myString" with the - // value the parameter "myString" resolves to. - // (This could be a different value for each item in case it contains an expression) - for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { - try { - myString = this.getNodeParameter('myString', itemIndex, '') as string; - item = items[itemIndex]; - - item.json.myString = myString; - } catch (error) { - // This node should never fail but we want to showcase how - // to handle errors. - if (this.continueOnFail()) { - items.push({ json: this.getInputData(itemIndex)[0].json, error, pairedItem: itemIndex }); - } else { - // Adding `itemIndex` allows other workflows to handle this error - if (error.context) { - // If the error thrown already contains the context property, - // only append the itemIndex - error.context.itemIndex = itemIndex; - throw error; - } - throw new NodeOperationError(this.getNode(), error, { - itemIndex, - }); - } - } - } - - return [items]; - } -} diff --git a/nodes/Example/example.dark.svg b/nodes/Example/example.dark.svg deleted file mode 100644 index c07cb10..0000000 --- a/nodes/Example/example.dark.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/nodes/Example/example.svg b/nodes/Example/example.svg deleted file mode 100644 index 703e1fe..0000000 --- a/nodes/Example/example.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/nodes/GithubIssues/GithubIssues.node.json b/nodes/GithubIssues/GithubIssues.node.json deleted file mode 100644 index 5eca62e..0000000 --- a/nodes/GithubIssues/GithubIssues.node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "node": "n8n-nodes-github-issues", - "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" - } - ] - } -} diff --git a/nodes/GithubIssues/GithubIssues.node.ts b/nodes/GithubIssues/GithubIssues.node.ts deleted file mode 100644 index 9965864..0000000 --- a/nodes/GithubIssues/GithubIssues.node.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { NodeConnectionTypes, 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: [NodeConnectionTypes.Main], - outputs: [NodeConnectionTypes.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, - }, - }; -} diff --git a/nodes/GithubIssues/listSearch/getIssues.ts b/nodes/GithubIssues/listSearch/getIssues.ts deleted file mode 100644 index f340b03..0000000 --- a/nodes/GithubIssues/listSearch/getIssues.ts +++ /dev/null @@ -1,49 +0,0 @@ -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 { - 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 }; -} diff --git a/nodes/GithubIssues/listSearch/getRepositories.ts b/nodes/GithubIssues/listSearch/getRepositories.ts deleted file mode 100644 index 9f5a6b1..0000000 --- a/nodes/GithubIssues/listSearch/getRepositories.ts +++ /dev/null @@ -1,50 +0,0 @@ -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 { - 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 }; -} diff --git a/nodes/GithubIssues/listSearch/getUsers.ts b/nodes/GithubIssues/listSearch/getUsers.ts deleted file mode 100644 index d8e0853..0000000 --- a/nodes/GithubIssues/listSearch/getUsers.ts +++ /dev/null @@ -1,49 +0,0 @@ -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 { - 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 }; -} diff --git a/nodes/GithubIssues/resources/issue/create.ts b/nodes/GithubIssues/resources/issue/create.ts deleted file mode 100644 index 4c7fd7e..0000000 --- a/nodes/GithubIssues/resources/issue/create.ts +++ /dev/null @@ -1,74 +0,0 @@ -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)}}', - }, - }, - }, -]; diff --git a/nodes/GithubIssues/resources/issue/get.ts b/nodes/GithubIssues/resources/issue/get.ts deleted file mode 100644 index 7dc7181..0000000 --- a/nodes/GithubIssues/resources/issue/get.ts +++ /dev/null @@ -1,14 +0,0 @@ -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 }, - }, -]; diff --git a/nodes/GithubIssues/resources/issue/getAll.ts b/nodes/GithubIssues/resources/issue/getAll.ts deleted file mode 100644 index b5b5fed..0000000 --- a/nodes/GithubIssues/resources/issue/getAll.ts +++ /dev/null @@ -1,124 +0,0 @@ -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}}', - }, - }, - }, - }, - ], - }, -]; diff --git a/nodes/GithubIssues/resources/issue/index.ts b/nodes/GithubIssues/resources/issue/index.ts deleted file mode 100644 index 6c915d2..0000000 --- a/nodes/GithubIssues/resources/issue/index.ts +++ /dev/null @@ -1,75 +0,0 @@ -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, -]; diff --git a/nodes/GithubIssues/resources/issueComment/getAll.ts b/nodes/GithubIssues/resources/issueComment/getAll.ts deleted file mode 100644 index 53b2057..0000000 --- a/nodes/GithubIssues/resources/issueComment/getAll.ts +++ /dev/null @@ -1,65 +0,0 @@ -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 }}`, - }, - }, - }, - }, - }, - }, -]; diff --git a/nodes/GithubIssues/resources/issueComment/index.ts b/nodes/GithubIssues/resources/issueComment/index.ts deleted file mode 100644 index 886f7c2..0000000 --- a/nodes/GithubIssues/resources/issueComment/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -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, -]; diff --git a/nodes/GithubIssues/shared/descriptions.ts b/nodes/GithubIssues/shared/descriptions.ts deleted file mode 100644 index aaeaaa8..0000000 --- a/nodes/GithubIssues/shared/descriptions.ts +++ /dev/null @@ -1,151 +0,0 @@ -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}}', - }, - ], -}; diff --git a/nodes/GithubIssues/shared/transport.ts b/nodes/GithubIssues/shared/transport.ts deleted file mode 100644 index 3555ee0..0000000 --- a/nodes/GithubIssues/shared/transport.ts +++ /dev/null @@ -1,32 +0,0 @@ -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); -} diff --git a/nodes/GithubIssues/shared/utils.ts b/nodes/GithubIssues/shared/utils.ts deleted file mode 100644 index 2b91882..0000000 --- a/nodes/GithubIssues/shared/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -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; -} diff --git a/nodes/ScraperApi/ScraperApi.node.json b/nodes/ScraperApi/ScraperApi.node.json new file mode 100644 index 0000000..eb66211 --- /dev/null +++ b/nodes/ScraperApi/ScraperApi.node.json @@ -0,0 +1,24 @@ +{ + "node": "n8n-nodes-base.ScraperApi", + "nodeVersion": "1.0", + "codexVersion": "1.0", + "categories": [ + "Data & Storage", + "Miscellaneous", + "Development", + "Analytics", + "Utility" + ], + "resources": { + "credentialDocumentation": [ + { + "url": "https://docs.scraperapi.com/dashboard-and-billing/api-key" + } + ], + "primaryDocumentation": [ + { + "url": "https://docs.scraperapi.com" + } + ] + } +} \ No newline at end of file diff --git a/nodes/ScraperApi/ScraperApi.node.ts b/nodes/ScraperApi/ScraperApi.node.ts new file mode 100644 index 0000000..e824770 --- /dev/null +++ b/nodes/ScraperApi/ScraperApi.node.ts @@ -0,0 +1,185 @@ +import type { + IExecuteFunctions, + INodeExecutionData, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; +import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow'; +import { ApiResource } from './resources/api.js'; +import type { ApiParameters } from './resources/types.js'; + +export class ScraperApi implements INodeType { + description: INodeTypeDescription = { + displayName: 'ScraperAPI', + name: 'scraperApi', + icon: 'file:../../icons/ScraperApi.svg', + group: ['transform'], + version: 1, + description: 'Official ScraperAPI nodes for n8n', + defaults: { + name: 'ScraperAPI', + }, + inputs: [NodeConnectionTypes.Main], + outputs: [NodeConnectionTypes.Main], + usableAsTool: true, + credentials: [ + { + name: 'scraperApi-Api', + required: true, + }, + ], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { name: 'API', value: 'api' }, + ], + default: 'api', + required: true, + description: 'Choose your ScraperAPI resource', + noDataExpression: true, + }, + { + displayName: 'URL', + name: 'url', + type: 'string', + displayOptions: { show: { resource: ['api'] } }, + default: '', + required: true, + description: 'Target URL to scrape', + }, + { + displayName: 'Optional Parameters', + name: 'optionalParameters', + type: 'collection', + placeholder: 'Add Parameter', + default: {}, + displayOptions: { show: { resource: ['api'] } }, + options: [ + { + displayName: 'Country Code', + name: 'country_code', + type: 'string', + default: '', + description: 'Two-letter country code for geo-specific scraping', + }, + { + displayName: 'Device Type', + name: 'device_type', + type: 'options', + default: 'desktop', + options: [ + { name: 'Desktop', value: 'desktop' }, + { name: 'Mobile', value: 'mobile' }, + ], + description: 'Choose the device type to scrape the page on', + }, + { + displayName: 'Premium', + name: 'premium', + type: 'boolean', + default: false, + description: 'Whether to use premium residential/mobile proxies for higher success rate (Can not be combined with UltraPremium)', + }, + { + displayName: 'Render', + name: 'render', + type: 'boolean', + default: false, + description: 'Whether to enable JavaScript rendering only when needed for dynamic content', + }, + { + displayName: 'Ultra Premium', + name: 'ultraPremium', + type: 'boolean', + default: false, + description: 'Whether to activate advanced bypass mechanisms (Can not be combined with Premium)', + }, + ], + } + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: INodeExecutionData[] = []; + + const resource = this.getNodeParameter('resource', 0) as string; + + for (let i = 0; i < items.length; i++) { + try { + if (resource === 'api') { + const url = this.getNodeParameter('url', i) as string; + + if (!url) { + throw new NodeOperationError(this.getNode(), 'URL is required'); + } + + const optionalParameters = this.getNodeParameter('optionalParameters', i, {}) as { + render?: boolean; + country_code?: string; + premium?: boolean; + ultraPremium?: boolean; + device_type?: string; + }; + + const apiResource = new ApiResource(this); + const apiParams: ApiParameters = { + url, + }; + + if (optionalParameters.render) { + apiParams.render = optionalParameters.render; + } + + if (optionalParameters.country_code) { + apiParams.country_code = optionalParameters.country_code; + } + + if (optionalParameters.premium) { + apiParams.premium = optionalParameters.premium; + } + + if (optionalParameters.ultraPremium) { + apiParams.ultra_premium = optionalParameters.ultraPremium; + } + + if (optionalParameters.device_type) { + apiParams.device_type = optionalParameters.device_type; + } + + const response = await apiResource.submitApiRequest(apiParams); + + returnData.push({ + json: { + body: response.body, + headers: response.headers, + statusCode: response.statusCode, + statusMessage: response.statusMessage, + }, + pairedItem: { + item: i, + }, + }); + } + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ + json: { + error: error.message, + }, + pairedItem: { + item: i, + }, + }); + continue; + } + throw error; + } + } + + return [returnData]; + } +} \ No newline at end of file diff --git a/nodes/ScraperApi/resources/api.ts b/nodes/ScraperApi/resources/api.ts new file mode 100644 index 0000000..e05de76 --- /dev/null +++ b/nodes/ScraperApi/resources/api.ts @@ -0,0 +1,55 @@ +import { ApiParameters, ApiResponse } from './types.js'; +import { IExecuteFunctions, IHttpRequestOptions } from 'n8n-workflow'; + +export class ApiResource { + protected n8n: IExecuteFunctions; + + constructor(n8n: IExecuteFunctions) { + this.n8n = n8n; + } + + async submitApiRequest(params: ApiParameters): Promise { + const qs: Record = { + url: params.url, + output_format: 'json', + autoparse: true, + scraper_sdk: 'n8n', + }; + + if (params.render) { + qs.render = true; + } + + if (params.country_code) { + qs.country_code = params.country_code; + } + + if (params.premium) { + qs.premium = true; + } + + if (params.ultra_premium) { + qs.ultra_premium = true; + } + + if (params.device_type) { + qs.device_type = params.device_type; + } + + const requestOptions: IHttpRequestOptions = { + method: 'GET', + baseURL: 'https://api.scraperapi.com', + url: '/', + qs, + returnFullResponse: true, + }; + + const response = await this.n8n.helpers.httpRequestWithAuthentication.call( + this.n8n, + 'scraperApi-Api', + requestOptions, + ); + + return response; + } +} diff --git a/nodes/ScraperApi/resources/types.ts b/nodes/ScraperApi/resources/types.ts new file mode 100644 index 0000000..0f48af6 --- /dev/null +++ b/nodes/ScraperApi/resources/types.ts @@ -0,0 +1,22 @@ +export enum OutputFormat { + text = 'text', + markdown = 'markdown', + json = 'json', + csv = 'csv', +} + +export interface ApiParameters { + url: string; + country_code?: string; + device_type?: string; + premium?: boolean; + ultra_premium?: boolean; + render?: boolean; +} + +export interface ApiResponse { + body: string | object; + headers: Record; + statusCode: number; + statusMessage: string; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 566f960..b02acb6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "n8n-nodes-<...>", + "name": "n8n-nodes-scraperapi", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "n8n-nodes-<...>", + "name": "n8n-nodes-scraperapi", "version": "0.1.0", "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index 66b8c45..ce2eccc 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,21 @@ { - "name": "n8n-nodes-<...>", + "name": "n8n-nodes-scraperapi", "version": "0.1.0", - "description": "", + "description": "Official ScraperAPI nodes for n8n", "license": "MIT", - "homepage": "", + "homepage": "https://www.scraperapi.com", "keywords": [ - "n8n-community-node-package" + "n8n-community-node-package", + "scraperapi", + "n8n" ], "author": { - "name": "", - "email": "" + "name": "ScraperAPI", + "email": "support@scraperapi.com" }, "repository": { "type": "git", - "url": "https://github.com/<...>/n8n-nodes-<...>.git" + "url": "https://github.com/scraperapi/n8n-nodes-scraperapi.git" }, "scripts": { "build": "n8n-node build", @@ -31,12 +33,10 @@ "n8nNodesApiVersion": 1, "strict": true, "credentials": [ - "dist/credentials/GithubIssuesApi.credentials.js", - "dist/credentials/GithubIssuesOAuth2Api.credentials.js" + "dist/credentials/ScraperApiApi.credentials.js" ], "nodes": [ - "dist/nodes/GithubIssues/GithubIssues.node.js", - "dist/nodes/Example/Example.node.js" + "dist/nodes/ScraperApi/ScraperApi.node.js" ] }, "devDependencies": {