webcrawlerapi-js 1.0.9 → 1.0.10

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/api.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {CrawlRequest, Job, JobId, ScrapeRequest, ScrapeResponse} from "./model";
2
2
  import { JobStatus } from "./constants";
3
+ import { WebcrawlerApiError, createErrorFromResponse } from "./errors";
3
4
 
4
5
  const BASE_PATH = "https://api.webcrawlerapi.com"
5
6
  const initialPullDelayMs = 2000
@@ -29,18 +30,7 @@ export class WebcrawlerClient {
29
30
  'body': JSON.stringify(scrapeRequest),
30
31
  };
31
32
 
32
- const response = await fetch(url, requestOptions);
33
- if (response.ok) {
34
- return response.json();
35
- }
36
- try {
37
- const data = await response.json();
38
- throw new Error(
39
- `failed to scrape, response returned ${response.status} ${response.statusText}: ${data?.error}`
40
- );
41
- } catch (e) {
42
- throw e;
43
- }
33
+ return await this.sendRequest(url, requestOptions);
44
34
  }
45
35
 
46
36
  public async scrapeWithMeta(scrapeRequest: ScrapeRequest, maxPollingRetries: number = MaxPullRetries): Promise<ScrapeResponse> {
@@ -58,7 +48,7 @@ export class WebcrawlerClient {
58
48
  const jobIdResponse: JobId = await this.sendRequest(url, requestOptions);
59
49
 
60
50
  if (jobIdResponse.id === '') {
61
- throw new Error("Failed to fetch job status");
51
+ throw new WebcrawlerApiError('invalid_response', 'Failed to fetch job status', 0);
62
52
  }
63
53
 
64
54
  let delayIntervalMs = initialPullDelayMs;
@@ -75,7 +65,7 @@ export class WebcrawlerClient {
75
65
  delayIntervalMs = scrapeResult.recommended_pull_delay_ms;
76
66
  }
77
67
  }
78
- throw new Error("Scraping took too long, please retry or increase the number of polling retries");
68
+ throw new WebcrawlerApiError('timeout', 'Scraping took too long, please retry or increase the number of polling retries', 0);
79
69
  }
80
70
 
81
71
  public async scrape(scrapeRequest: ScrapeRequest, maxPollingRetries: number = MaxPullRetries): Promise<any> {
@@ -96,21 +86,7 @@ export class WebcrawlerClient {
96
86
  'Expires': '0'
97
87
  },
98
88
  };
99
- const response = await fetch(url, requestOptions);
100
- if (response.ok) {
101
- return response.json();
102
- }
103
-
104
- try {
105
- const data = await response.json();
106
- throw new Error(
107
- `failed to fetch job status ${response.status} ${response.statusText}: ${JSON.stringify(data)}`
108
- );
109
- } catch (e) {
110
- throw new Error(
111
- `failed to fetch job status ${response.status} ${response.statusText}`
112
- );
113
- }
89
+ return await this.sendRequest(url, requestOptions);
114
90
  }
115
91
 
116
92
  public async crawl(crawlRequest: CrawlRequest): Promise<Job> {
@@ -132,7 +108,7 @@ export class WebcrawlerClient {
132
108
  const jobIdResponse: JobId = await this.sendRequest(url, requestOptions);
133
109
 
134
110
  if (jobIdResponse.id === '') {
135
- throw new Error("Failed to fetch job status");
111
+ throw new WebcrawlerApiError('invalid_response', 'Failed to fetch job status', 0);
136
112
  }
137
113
 
138
114
  let delayIntervalMs = initialPullDelayMs;
@@ -186,7 +162,7 @@ export class WebcrawlerClient {
186
162
  delayIntervalMs = job.recommended_pull_delay_ms;
187
163
  }
188
164
  }
189
- throw new Error("Crawling took too long, please retry or increase the number of polling retries");
165
+ throw new WebcrawlerApiError('timeout', 'Crawling took too long, please retry or increase the number of polling retries', 0);
190
166
  }
191
167
 
192
168
  public async crawlAsync(crawlRequest: CrawlRequest): Promise<JobId> {
@@ -216,36 +192,35 @@ export class WebcrawlerClient {
216
192
  'Pragma': 'no-cache',
217
193
  'Expires': '0'
218
194
  }
219
- }
220
- const response = await fetch(url, requestOptions);
221
- if (response.ok) {
222
- return response.json();
223
- }
224
-
225
- try {
226
- const data = await response.json();
227
- throw new Error(
228
- `failed to fetch job status ${response.status} ${response.statusText}: ${JSON.stringify(data)}`
229
- );
230
- } catch (e) {
231
- throw new Error(
232
- `failed to fetch job status ${response.status} ${response.statusText}`
233
- );
234
- }
195
+ };
196
+ return await this.sendRequest(url, requestOptions);
235
197
  }
236
198
 
237
-
238
199
  private async sendRequest(url: string, requestOptions: any): Promise<any> {
239
200
  let response: Response;
240
201
  try {
241
202
  response = await fetch(url, requestOptions);
242
203
  } catch (e) {
243
- throw new Error(`Failed to send request: ${e}`);
204
+ throw new WebcrawlerApiError('network_error', `Failed to send request: ${e}`, 0);
244
205
  }
206
+
245
207
  if (!response.ok) {
246
- const errorResponse = await response.json();
247
- throw new Error(`${JSON.stringify(errorResponse)}`);
208
+ try {
209
+ const errorData = await response.json();
210
+ throw createErrorFromResponse(response, errorData);
211
+ } catch (e) {
212
+ if (e instanceof WebcrawlerApiError) {
213
+ throw e;
214
+ }
215
+ // If we can't parse the error response, create a generic error
216
+ throw new WebcrawlerApiError(
217
+ 'unknown_error',
218
+ `Request failed with status ${response.status} ${response.statusText}`,
219
+ response.status
220
+ );
221
+ }
248
222
  }
223
+
249
224
  return response.json();
250
225
  }
251
226
  }
@@ -0,0 +1,12 @@
1
+ export declare class WebcrawlerApiError extends Error {
2
+ errorCode: string;
3
+ errorMessage: string;
4
+ statusCode?: number;
5
+ constructor(errorCode: string, errorMessage: string, statusCode?: number);
6
+ }
7
+ export interface ErrorResponse {
8
+ error_code?: string;
9
+ error_message?: string;
10
+ error?: string;
11
+ }
12
+ export declare function createErrorFromResponse(response: Response, data: ErrorResponse): WebcrawlerApiError;
package/dist/errors.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebcrawlerApiError = void 0;
4
+ exports.createErrorFromResponse = createErrorFromResponse;
5
+ class WebcrawlerApiError extends Error {
6
+ constructor(errorCode, errorMessage, statusCode) {
7
+ super(errorMessage);
8
+ this.name = 'WebcrawlerApiError';
9
+ this.errorCode = errorCode;
10
+ this.errorMessage = errorMessage;
11
+ this.statusCode = statusCode;
12
+ }
13
+ }
14
+ exports.WebcrawlerApiError = WebcrawlerApiError;
15
+ function createErrorFromResponse(response, data) {
16
+ const errorCode = data.error_code || 'unknown_error';
17
+ const errorMessage = data.error_message || data.error || 'Unknown error';
18
+ return new WebcrawlerApiError(errorCode, errorMessage, response.status);
19
+ }
package/errors.ts ADDED
@@ -0,0 +1,25 @@
1
+ export class WebcrawlerApiError extends Error {
2
+ public errorCode: string;
3
+ public errorMessage: string;
4
+ public statusCode?: number;
5
+
6
+ constructor(errorCode: string, errorMessage: string, statusCode?: number) {
7
+ super(errorMessage);
8
+ this.name = 'WebcrawlerApiError';
9
+ this.errorCode = errorCode;
10
+ this.errorMessage = errorMessage;
11
+ this.statusCode = statusCode;
12
+ }
13
+ }
14
+
15
+ export interface ErrorResponse {
16
+ error_code?: string;
17
+ error_message?: string;
18
+ error?: string;
19
+ }
20
+
21
+ export function createErrorFromResponse(response: Response, data: ErrorResponse): WebcrawlerApiError {
22
+ const errorCode = data.error_code || 'unknown_error';
23
+ const errorMessage = data.error_message || data.error || 'Unknown error';
24
+ return new WebcrawlerApiError(errorCode, errorMessage, response.status);
25
+ }
package/index.ts CHANGED
@@ -1 +1,4 @@
1
- export * from "./api";
1
+ export * from "./api";
2
+ export * from "./model";
3
+ export * from "./constants";
4
+ export * from "./errors";
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "webcrawlerapi-js",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
 
5
- "description": "JS client for WecrawlerAPI",
5
+ "description": "JS client for WebcrawlerAPI",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "scripts": {
package/dist/api.js DELETED
@@ -1,239 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.WebcrawlerClient = void 0;
13
- const constants_1 = require("./constants");
14
- const BASE_PATH = "https://api.webcrawlerapi.com";
15
- const initialPullDelayMs = 2000;
16
- const MaxPullRetries = 100;
17
- class WebcrawlerClient {
18
- constructor(apiKey, basePath = BASE_PATH, apiVersion = "v1") {
19
- this.apiVersion = "v1";
20
- this.apiKey = apiKey;
21
- this.basePath = basePath;
22
- this.apiVersion = apiVersion;
23
- }
24
- scrapeAsync(scrapeRequest) {
25
- return __awaiter(this, void 0, void 0, function* () {
26
- const url = `${this.basePath}/${this.apiVersion}/scrape`;
27
- const requestOptions = {
28
- 'method': 'POST',
29
- 'headers': {
30
- 'Content-Type': 'application/json',
31
- 'Authorization': `Bearer ${this.apiKey}`,
32
- "User-Agent": "WebcrawlerAPI-NodeJS-Client"
33
- },
34
- 'body': JSON.stringify(scrapeRequest),
35
- };
36
- const response = yield fetch(url, requestOptions);
37
- if (response.ok) {
38
- return response.json();
39
- }
40
- try {
41
- const data = yield response.json();
42
- throw new Error(`failed to scrape, response returned ${response.status} ${response.statusText}: ${data === null || data === void 0 ? void 0 : data.error}`);
43
- }
44
- catch (e) {
45
- throw e;
46
- }
47
- });
48
- }
49
- scrapeWithMeta(scrapeRequest_1) {
50
- return __awaiter(this, arguments, void 0, function* (scrapeRequest, maxPollingRetries = MaxPullRetries) {
51
- const url = `${this.basePath}/${this.apiVersion}/scrape`;
52
- const requestOptions = {
53
- 'method': 'POST',
54
- 'headers': {
55
- 'Content-Type': 'application/json',
56
- 'Authorization': `Bearer ${this.apiKey}`
57
- },
58
- 'body': JSON.stringify(scrapeRequest),
59
- };
60
- const jobIdResponse = yield this.sendRequest(url, requestOptions);
61
- if (jobIdResponse.id === '') {
62
- throw new Error("Failed to fetch job status");
63
- }
64
- let delayIntervalMs = initialPullDelayMs;
65
- for (let i = 0; i < maxPollingRetries; i++) {
66
- yield new Promise(resolve => setTimeout(resolve, delayIntervalMs));
67
- const scrapeResult = yield this.getScrapeResult(jobIdResponse.id);
68
- if (scrapeRequest.debug) {
69
- console.log(`Scrape result: ${JSON.stringify(scrapeResult)}`);
70
- }
71
- if (scrapeResult.status !== constants_1.JobStatus.IN_PROGRESS && scrapeResult.status !== constants_1.JobStatus.NEW) {
72
- return scrapeResult;
73
- }
74
- if (scrapeResult.recommended_pull_delay_ms > 0) {
75
- delayIntervalMs = scrapeResult.recommended_pull_delay_ms;
76
- }
77
- }
78
- throw new Error("Scraping took too long, please retry or increase the number of polling retries");
79
- });
80
- }
81
- scrape(scrapeRequest_1) {
82
- return __awaiter(this, arguments, void 0, function* (scrapeRequest, maxPollingRetries = MaxPullRetries) {
83
- const scrapeResult = yield this.scrapeWithMeta(scrapeRequest, maxPollingRetries);
84
- return scrapeResult.structured_data;
85
- });
86
- }
87
- getScrapeResult(scrapeID) {
88
- return __awaiter(this, void 0, void 0, function* () {
89
- const url = `${this.basePath}/${this.apiVersion}/scrape/${scrapeID}`;
90
- const requestOptions = {
91
- 'method': 'GET',
92
- 'headers': {
93
- 'Content-Type': 'application/json',
94
- 'Authorization': `Bearer ${this.apiKey}`,
95
- "User-Agent": "WebcrawlerAPI-NodeJS-Client",
96
- 'Cache-Control': 'no-cache, no-store, must-revalidate',
97
- 'Pragma': 'no-cache',
98
- 'Expires': '0'
99
- },
100
- };
101
- const response = yield fetch(url, requestOptions);
102
- if (response.ok) {
103
- return response.json();
104
- }
105
- try {
106
- const data = yield response.json();
107
- throw new Error(`failed to fetch job status ${response.status} ${response.statusText}: ${JSON.stringify(data)}`);
108
- }
109
- catch (e) {
110
- throw new Error(`failed to fetch job status ${response.status} ${response.statusText}`);
111
- }
112
- });
113
- }
114
- crawl(crawlRequest) {
115
- return __awaiter(this, void 0, void 0, function* () {
116
- const url = `${this.basePath}/${this.apiVersion}/crawl`;
117
- const requestOptions = {
118
- 'method': 'POST',
119
- 'headers': {
120
- 'Content-Type': 'application/json',
121
- 'Authorization': `Bearer ${this.apiKey}`,
122
- "User-Agent": "WebcrawlerAPI-NodeJS-Client",
123
- 'Cache-Control': 'no-cache, no-store, must-revalidate',
124
- 'Pragma': 'no-cache',
125
- 'Expires': '0'
126
- },
127
- 'body': JSON.stringify(crawlRequest),
128
- };
129
- const jobIdResponse = yield this.sendRequest(url, requestOptions);
130
- if (jobIdResponse.id === '') {
131
- throw new Error("Failed to fetch job status");
132
- }
133
- let delayIntervalMs = initialPullDelayMs;
134
- for (let i = 0; i < MaxPullRetries; i++) {
135
- yield new Promise(resolve => setTimeout(resolve, delayIntervalMs));
136
- const timestamp = new Date().getTime();
137
- const job = yield this.getJob(`${jobIdResponse.id}?t=${timestamp}`);
138
- if (job.status !== constants_1.JobStatus.IN_PROGRESS && job.status !== constants_1.JobStatus.NEW) {
139
- // Transform each job item to include getContent method
140
- job.job_items = job.job_items.map(item => (Object.assign(Object.assign({}, item), { getContent: function () {
141
- return __awaiter(this, void 0, void 0, function* () {
142
- if (job.status !== constants_1.JobStatus.DONE || this.status !== constants_1.JobStatus.DONE) {
143
- return null;
144
- }
145
- let contentUrl;
146
- switch (job.scrape_type) {
147
- case 'html':
148
- contentUrl = this.raw_content_url;
149
- break;
150
- case 'cleaned':
151
- contentUrl = this.cleaned_content_url;
152
- break;
153
- case 'markdown':
154
- contentUrl = this.markdown_content_url;
155
- break;
156
- }
157
- if (!contentUrl) {
158
- return null;
159
- }
160
- const response = yield fetch(contentUrl, {
161
- headers: {
162
- 'Accept-Encoding': 'gzip, deflate, br',
163
- 'Accept': '*/*'
164
- }
165
- });
166
- if (!response.ok) {
167
- throw new Error(`Failed to fetch content: ${response.statusText}`);
168
- }
169
- return yield response.text();
170
- });
171
- } })));
172
- return job;
173
- }
174
- if (job.recommended_pull_delay_ms > 0) {
175
- delayIntervalMs = job.recommended_pull_delay_ms;
176
- }
177
- }
178
- throw new Error("Crawling took too long, please retry or increase the number of polling retries");
179
- });
180
- }
181
- crawlAsync(crawlRequest) {
182
- return __awaiter(this, void 0, void 0, function* () {
183
- const url = `${this.basePath}/${this.apiVersion}/crawl`;
184
- const requestOptions = {
185
- 'method': 'POST',
186
- 'headers': {
187
- 'Content-Type': 'application/json',
188
- 'Authorization': `Bearer ${this.apiKey}`
189
- },
190
- 'body': JSON.stringify(crawlRequest),
191
- };
192
- return yield this.sendRequest(url, requestOptions);
193
- });
194
- }
195
- getJob(jobID) {
196
- return __awaiter(this, void 0, void 0, function* () {
197
- const url = `${this.basePath}/${this.apiVersion}/job/${jobID}`;
198
- const requestOptions = {
199
- 'method': 'GET',
200
- 'headers': {
201
- 'Content-Type': 'application/json',
202
- 'Authorization': `Bearer ${this.apiKey}`,
203
- "User-Agent": "WebcrawlerAPI-NodeJS-Client",
204
- 'Cache-Control': 'no-cache, no-store, must-revalidate',
205
- 'Pragma': 'no-cache',
206
- 'Expires': '0'
207
- }
208
- };
209
- const response = yield fetch(url, requestOptions);
210
- if (response.ok) {
211
- return response.json();
212
- }
213
- try {
214
- const data = yield response.json();
215
- throw new Error(`failed to fetch job status ${response.status} ${response.statusText}: ${JSON.stringify(data)}`);
216
- }
217
- catch (e) {
218
- throw new Error(`failed to fetch job status ${response.status} ${response.statusText}`);
219
- }
220
- });
221
- }
222
- sendRequest(url, requestOptions) {
223
- return __awaiter(this, void 0, void 0, function* () {
224
- let response;
225
- try {
226
- response = yield fetch(url, requestOptions);
227
- }
228
- catch (e) {
229
- throw new Error(`Failed to send request: ${e}`);
230
- }
231
- if (!response.ok) {
232
- const errorResponse = yield response.json();
233
- throw new Error(`${JSON.stringify(errorResponse)}`);
234
- }
235
- return response.json();
236
- });
237
- }
238
- }
239
- exports.WebcrawlerClient = WebcrawlerClient;
package/dist/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./api";
package/dist/index.js DELETED
@@ -1,17 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./api"), exports);