fetchfox-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "fetchfox-sdk",
3
+ "version": "1.0.0",
4
+ "description": "AI scraper",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "format": "prettier --write .",
9
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
10
+ "prepare": "husky"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/fetchfox/fetchfox.git"
15
+ },
16
+ "keywords": [
17
+ "ai",
18
+ "scraping"
19
+ ],
20
+ "author": "marcell@fetchfoxai.com",
21
+ "license": "ISC",
22
+ "bugs": {
23
+ "url": "https://github.com/fetchfox/fetchfox/issues"
24
+ },
25
+ "homepage": "https://github.com/fetchfox/fetchfox#readme",
26
+ "devDependencies": {
27
+ "@eslint/js": "^9.31.0",
28
+ "eslint-plugin-promise": "^7.2.1",
29
+ "husky": "^9.1.7",
30
+ "jest": "^30.0.4",
31
+ "prettier": "^3.6.2"
32
+ }
33
+ }
package/package.json~ ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "fetchfox",
3
+ "version": "1.0.0",
4
+ "description": "AI scraper",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/fetchfox/fetchfox.git"
12
+ },
13
+ "keywords": [
14
+ "ai",
15
+ "scraping"
16
+ ],
17
+ "author": "marcell@fetchfoxai.com",
18
+ "license": "ISC",
19
+ "bugs": {
20
+ "url": "https://github.com/fetchfox/fetchfox/issues"
21
+ },
22
+ "homepage": "https://github.com/fetchfox/fetchfox#readme",
23
+ "devDependencies": {
24
+ "jest": "^30.0.4"
25
+ }
26
+ }
package/src/api.js ADDED
@@ -0,0 +1,47 @@
1
+ import { apiKey, host } from './configure.js';
2
+
3
+ const endpoint = (path) => `${host()}${path}`;
4
+
5
+ const clean = (dict) => {
6
+ const result = {};
7
+
8
+ for (const key in dict) {
9
+ if (dict.hasOwnProperty(key)) {
10
+ const snakeKey = key
11
+ .replace(/([A-Z])/g, '_$1') // insert _ before uppercase letters
12
+ .toLowerCase() // convert all to lowercase
13
+ .replace(/^_/, ''); // remove leading _ if any
14
+
15
+ result[snakeKey] = dict[key];
16
+ }
17
+ }
18
+
19
+ if (result.apiKey) {
20
+ delete result.apiKey;
21
+ }
22
+
23
+ return result;
24
+ };
25
+
26
+ export const call = async (method, path, params) => {
27
+ const key = apiKey(params);
28
+ params = clean(params);
29
+
30
+ const args = {
31
+ method,
32
+ headers: {
33
+ Authorization: `Bearer ${key}`,
34
+ 'Content-Type': 'application/json',
35
+ },
36
+ };
37
+
38
+ let url = endpoint(path);
39
+ if (method == 'GET') {
40
+ url += '?' + new URLSearchParams(params).toString();
41
+ } else {
42
+ args.body = JSON.stringify(params);
43
+ }
44
+
45
+ const resp = await fetch(url, args);
46
+ return resp.json();
47
+ };
package/src/api.js~ ADDED
File without changes
@@ -0,0 +1,24 @@
1
+ const config = {
2
+ host: 'https://api.fetchfox.ai',
3
+ };
4
+
5
+ const isNode = typeof process !== 'undefined' &&
6
+ process.versions != null &&
7
+ process.versions.node != null;
8
+
9
+ const safeEnv = (key) => isNode ? process.env[key] : null;
10
+
11
+ export const configure = ({ apiKey, host }) => {
12
+ if (apiKey) {
13
+ config.apiKey = apiKey;
14
+ }
15
+ if (host) {
16
+ config.host = host;
17
+ }
18
+ };
19
+
20
+ export const apiKey = (options) =>
21
+ options?.apiKey || config.apiKey || safeEnv('FETCHFOX_API_KEY');
22
+
23
+ export const host = (options) =>
24
+ options?.host || config.host || safeEnv('FETCHFOX_HOST');
@@ -0,0 +1,18 @@
1
+ const config = {
2
+ host: 'https://api.fetchfox.ai',
3
+ };
4
+
5
+ export const configure = ({ apiKey, host }) => {
6
+ if (apiKey) {
7
+ config.apiKey = apiKey;
8
+ }
9
+ if (host) {
10
+ config.host = host;
11
+ }
12
+ };
13
+
14
+ export const apiKey = (options) =>
15
+ options?.apiKey || config.apiKey || process.env.FETCHFOX_API_KEY;
16
+
17
+ export const host = (options) =>
18
+ options?.host || config.host || process.env.FETCHFOX_HOST;
package/src/crawl.js ADDED
@@ -0,0 +1,5 @@
1
+ import { call } from './api.js';
2
+
3
+ export const crawl = async (pattern, options) => {
4
+ return call('POST', '/api/crawl', { pattern, ...options });
5
+ };
package/src/crawl.js~ ADDED
File without changes
package/src/extract.js ADDED
@@ -0,0 +1,5 @@
1
+ import { call } from './api.js';
2
+
3
+ export const extract = async (urls, template, options) => {
4
+ return call('POST', '/api/extract', { urls, template, ...options });
5
+ };
File without changes
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from './crawl.js';
2
+ export * from './extract.js';
3
+ export * from './jobs.js';
4
+ export * from './user.js';
5
+ export { call } from './api.js';
6
+ export { configure } from './configure.js';
package/src/index.js~ ADDED
File without changes
package/src/jobs.js ADDED
@@ -0,0 +1,10 @@
1
+ import { call } from './api.js';
2
+
3
+ export const jobs = {
4
+ get: async (id) => {
5
+ return call('GET', `/api/jobs/${id}`);
6
+ },
7
+ list: async (limit = 100, offset = 0) => {
8
+ return call('GET', '/api/jobs', { limit, offset });
9
+ },
10
+ };
package/src/jobs.js~ ADDED
@@ -0,0 +1,7 @@
1
+ import { call } from './api.js';
2
+
3
+ export const jobs = {
4
+ list: async (limit = 100, offset = 0) => {
5
+ return call('GET', '/api/jobs', { limit, offset });
6
+ },
7
+ };
package/src/user.js ADDED
@@ -0,0 +1,5 @@
1
+ import { call } from './api.js';
2
+
3
+ export const user = {
4
+ get: async () => call('GET', '/api/user'),
5
+ };
package/src/user.js~ ADDED
File without changes
@@ -0,0 +1,6 @@
1
+ import { crawl } from '../src';
2
+
3
+ test('crawl a pattern @sanity', async () => {
4
+ const out = await crawl('https://pokemondb.net/pokedex/*', { maxVisits: 5 });
5
+ console.log(out);
6
+ }, 30_000);
File without changes
@@ -0,0 +1,7 @@
1
+ import { jobs, configure } from '../src';
2
+
3
+ test('list jobs @sanity', async () => {
4
+ configure({ host: 'http://localhost:3030' });
5
+ const out = await jobs.list();
6
+ console.log(out.results.map((it) => [it.id, it.details.state]));
7
+ });
File without changes