recker 1.0.79 → 1.0.80-next.70ea84d

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.
Files changed (196) hide show
  1. package/dist/browser/browser/mini.d.ts +2 -2
  2. package/dist/browser/browser/recker-mini.d.ts +8 -8
  3. package/dist/browser/browser/recker.d.ts +11 -8
  4. package/dist/browser/browser/recker.js +54 -6
  5. package/dist/browser/core/client.d.ts +15 -10
  6. package/dist/browser/core/client.js +54 -38
  7. package/dist/browser/index.iife.min.js +129 -130
  8. package/dist/browser/index.min.js +129 -130
  9. package/dist/browser/index.mini.iife.js +5697 -636
  10. package/dist/browser/index.mini.iife.min.js +47 -48
  11. package/dist/browser/index.mini.min.js +47 -48
  12. package/dist/browser/index.mini.umd.js +5697 -636
  13. package/dist/browser/index.mini.umd.min.js +47 -48
  14. package/dist/browser/index.umd.min.js +129 -130
  15. package/dist/browser/mini.d.ts +2 -2
  16. package/dist/browser/plugins/proxy-rotator.d.ts +2 -2
  17. package/dist/browser/plugins/proxy-rotator.js +6 -28
  18. package/dist/browser/recker-mini.d.ts +8 -8
  19. package/dist/browser/recker.d.ts +11 -8
  20. package/dist/browser/recker.js +54 -6
  21. package/dist/browser/scrape/document.js +2 -2
  22. package/dist/browser/scrape/element.js +7 -1
  23. package/dist/browser/scrape/parser/nodes/html.js +1 -1
  24. package/dist/browser/scrape/spider.d.ts +52 -0
  25. package/dist/browser/scrape/spider.js +620 -38
  26. package/dist/browser/scrape/types.d.ts +2 -0
  27. package/dist/browser/search/google.d.ts +26 -1
  28. package/dist/browser/search/google.js +427 -45
  29. package/dist/browser/seo/analyzer.d.ts +1 -0
  30. package/dist/browser/seo/analyzer.js +144 -1
  31. package/dist/browser/seo/index.d.ts +1 -1
  32. package/dist/browser/seo/keyword-campaign-analyzer.d.ts +2 -0
  33. package/dist/browser/seo/keyword-campaign-analyzer.js +538 -0
  34. package/dist/browser/seo/keyword-campaign-seed-advanced.d.ts +17 -0
  35. package/dist/browser/seo/keyword-campaign-seed-advanced.js +269 -0
  36. package/dist/browser/seo/keyword-campaign-seed-core.d.ts +29 -0
  37. package/dist/browser/seo/keyword-campaign-seed-core.js +525 -0
  38. package/dist/browser/seo/keyword-campaign-shared.d.ts +165 -0
  39. package/dist/browser/seo/keyword-campaign-shared.js +59 -0
  40. package/dist/browser/seo/keyword-campaign.d.ts +4 -107
  41. package/dist/browser/seo/keyword-campaign.js +2 -380
  42. package/dist/browser/seo/keywords.js +5 -22
  43. package/dist/browser/seo/types.d.ts +19 -0
  44. package/dist/browser/transport/curl.d.ts +5 -1
  45. package/dist/browser/transport/curl.js +207 -50
  46. package/dist/browser/transport/undici.d.ts +4 -3
  47. package/dist/browser/transport/undici.js +123 -49
  48. package/dist/browser/types/index.d.ts +9 -3
  49. package/dist/browser/utils/binary-manager.js +26 -3
  50. package/dist/browser/utils/block-detector.d.ts +8 -0
  51. package/dist/browser/utils/block-detector.js +542 -7
  52. package/dist/cli/commands/hls-runner.js +5 -4
  53. package/dist/cli/commands/live-runner.js +5 -4
  54. package/dist/cli/commands/loadtest-runner.js +3 -2
  55. package/dist/cli/commands/search.d.ts +2 -0
  56. package/dist/cli/commands/search.js +105 -0
  57. package/dist/cli/commands/seo-runner.js +9 -7
  58. package/dist/cli/commands/seo.js +140 -1
  59. package/dist/cli/commands/serve.js +75 -131
  60. package/dist/cli/commands/server-runner.js +59 -82
  61. package/dist/cli/commands/spider-runner.d.ts +37 -1
  62. package/dist/cli/commands/spider-runner.js +134 -10
  63. package/dist/cli/commands/spider.d.ts +18 -1
  64. package/dist/cli/commands/spider.js +457 -27
  65. package/dist/cli/events/handlers/cli.js +30 -1
  66. package/dist/cli/events/handlers/tui.js +26 -0
  67. package/dist/cli/events/types.d.ts +27 -0
  68. package/dist/cli/handler.d.ts +2 -12
  69. package/dist/cli/handler.js +20 -15
  70. package/dist/cli/handlers/protocols.js +39 -12
  71. package/dist/cli/handlers/search.d.ts +2 -0
  72. package/dist/cli/handlers/search.js +171 -0
  73. package/dist/cli/handlers/seo-analyze.d.ts +1 -0
  74. package/dist/cli/handlers/seo-analyze.js +666 -0
  75. package/dist/cli/handlers/seo-robots.d.ts +1 -0
  76. package/dist/cli/handlers/seo-robots.js +76 -0
  77. package/dist/cli/handlers/seo-serp.d.ts +54 -0
  78. package/dist/cli/handlers/seo-serp.js +243 -0
  79. package/dist/cli/handlers/seo-sitemap.d.ts +1 -0
  80. package/dist/cli/handlers/seo-sitemap.js +55 -0
  81. package/dist/cli/handlers/seo-spider.d.ts +1 -0
  82. package/dist/cli/handlers/seo-spider.js +334 -0
  83. package/dist/cli/handlers/seo.d.ts +8 -4
  84. package/dist/cli/handlers/seo.js +294 -442
  85. package/dist/cli/handlers/spider.js +94 -17
  86. package/dist/cli/handlers/streaming.js +5 -1
  87. package/dist/cli/index.js +11 -2
  88. package/dist/cli/presets.d.ts +1 -1
  89. package/dist/cli/presets.js +15 -4
  90. package/dist/cli/tui/app.js +6 -1
  91. package/dist/cli/tui/components/rich-response.d.ts +72 -0
  92. package/dist/cli/tui/components/rich-response.js +117 -0
  93. package/dist/cli/tui/executor-commands/background.js +30 -24
  94. package/dist/cli/tui/executor-commands/testing.js +3 -2
  95. package/dist/cli/tui/hooks/useDomains.d.ts +17 -0
  96. package/dist/cli/tui/hooks/useHelp.js +15 -2
  97. package/dist/cli/tui/job-manager.d.ts +4 -4
  98. package/dist/cli/tui/job-manager.js +5 -1
  99. package/dist/cli/tui/spider-tui.d.ts +63 -0
  100. package/dist/cli/tui/spider-tui.js +120 -2
  101. package/dist/cli/types.d.ts +12 -0
  102. package/dist/cli/types.js +1 -0
  103. package/dist/cli/utils/option-helpers.d.ts +10 -0
  104. package/dist/cli/utils/option-helpers.js +63 -0
  105. package/dist/cli/utils/score-color.d.ts +11 -0
  106. package/dist/cli/utils/score-color.js +11 -0
  107. package/dist/cli/utils/serp-campaign.d.ts +53 -0
  108. package/dist/cli/utils/serp-campaign.js +53 -0
  109. package/dist/cli/utils/serp-config.d.ts +26 -0
  110. package/dist/cli/utils/serp-config.js +125 -0
  111. package/dist/core/client.d.ts +15 -10
  112. package/dist/core/client.js +54 -38
  113. package/dist/index.d.ts +1 -0
  114. package/dist/index.js +1 -0
  115. package/dist/mcp/cli.js +35 -34
  116. package/dist/mcp/client.d.ts +2 -2
  117. package/dist/mcp/client.js +20 -2
  118. package/dist/mcp/contract.d.ts +1 -1
  119. package/dist/mcp/profiles.js +5 -1
  120. package/dist/mcp/prompts/index.js +8 -4
  121. package/dist/mcp/resources/index.js +46 -23
  122. package/dist/mcp/server.js +9 -6
  123. package/dist/mcp/tools/protocols.js +9 -2
  124. package/dist/mcp/tools/registry.js +13 -3
  125. package/dist/mcp/tools/seo.js +427 -2
  126. package/dist/mcp/types.d.ts +5 -1
  127. package/dist/plugins/proxy-rotator.d.ts +2 -2
  128. package/dist/plugins/proxy-rotator.js +6 -28
  129. package/dist/raffel/client.d.ts +38 -0
  130. package/dist/raffel/client.js +282 -0
  131. package/dist/raffel/index.d.ts +2 -0
  132. package/dist/raffel/index.js +2 -0
  133. package/dist/raffel/types.d.ts +40 -0
  134. package/dist/raffel/types.js +14 -0
  135. package/dist/recker.d.ts +13 -7
  136. package/dist/recker.js +58 -6
  137. package/dist/scrape/document.js +2 -2
  138. package/dist/scrape/element.js +7 -1
  139. package/dist/scrape/parser/nodes/html.js +1 -1
  140. package/dist/scrape/spider.d.ts +52 -0
  141. package/dist/scrape/spider.js +620 -38
  142. package/dist/scrape/types.d.ts +2 -0
  143. package/dist/search/google.d.ts +26 -1
  144. package/dist/search/google.js +427 -45
  145. package/dist/search/index.d.ts +1 -1
  146. package/dist/seo/analyzer.d.ts +1 -0
  147. package/dist/seo/analyzer.js +144 -1
  148. package/dist/seo/index.d.ts +1 -1
  149. package/dist/seo/keyword-campaign-analyzer.d.ts +2 -0
  150. package/dist/seo/keyword-campaign-analyzer.js +538 -0
  151. package/dist/seo/keyword-campaign-seed-advanced.d.ts +17 -0
  152. package/dist/seo/keyword-campaign-seed-advanced.js +269 -0
  153. package/dist/seo/keyword-campaign-seed-core.d.ts +29 -0
  154. package/dist/seo/keyword-campaign-seed-core.js +525 -0
  155. package/dist/seo/keyword-campaign-shared.d.ts +165 -0
  156. package/dist/seo/keyword-campaign-shared.js +59 -0
  157. package/dist/seo/keyword-campaign.d.ts +4 -107
  158. package/dist/seo/keyword-campaign.js +2 -380
  159. package/dist/seo/keywords.js +5 -22
  160. package/dist/seo/types.d.ts +19 -0
  161. package/dist/template/index.d.ts +1 -1
  162. package/dist/template/types.d.ts +0 -2
  163. package/dist/testing/index.d.ts +1 -22
  164. package/dist/testing/index.js +1 -11
  165. package/dist/transport/curl.d.ts +5 -1
  166. package/dist/transport/curl.js +207 -50
  167. package/dist/transport/undici.d.ts +4 -3
  168. package/dist/transport/undici.js +123 -49
  169. package/dist/types/index.d.ts +9 -3
  170. package/dist/utils/binary-manager.js +26 -3
  171. package/dist/utils/block-detector.d.ts +8 -0
  172. package/dist/utils/block-detector.js +542 -7
  173. package/dist/version.js +1 -1
  174. package/package.json +12 -1
  175. package/dist/testing/mock-dns-server.d.ts +0 -69
  176. package/dist/testing/mock-dns-server.js +0 -269
  177. package/dist/testing/mock-ftp-server.d.ts +0 -89
  178. package/dist/testing/mock-ftp-server.js +0 -562
  179. package/dist/testing/mock-hls-server.d.ts +0 -80
  180. package/dist/testing/mock-hls-server.js +0 -381
  181. package/dist/testing/mock-http-server.d.ts +0 -124
  182. package/dist/testing/mock-http-server.js +0 -343
  183. package/dist/testing/mock-proxy-server.d.ts +0 -108
  184. package/dist/testing/mock-proxy-server.js +0 -615
  185. package/dist/testing/mock-sse-server.d.ts +0 -76
  186. package/dist/testing/mock-sse-server.js +0 -291
  187. package/dist/testing/mock-telnet-server.d.ts +0 -59
  188. package/dist/testing/mock-telnet-server.js +0 -274
  189. package/dist/testing/mock-udp-server.d.ts +0 -43
  190. package/dist/testing/mock-udp-server.js +0 -188
  191. package/dist/testing/mock-websocket-server.d.ts +0 -76
  192. package/dist/testing/mock-websocket-server.js +0 -334
  193. package/dist/testing/mock-whois-server.d.ts +0 -56
  194. package/dist/testing/mock-whois-server.js +0 -234
  195. package/dist/testing/proxy-certs.d.ts +0 -19
  196. package/dist/testing/proxy-certs.js +0 -208
@@ -0,0 +1,282 @@
1
+ import { ReckerWebSocket } from '../websocket/client.js';
2
+ import { RaffelError } from './types.js';
3
+ class SimpleEmitter {
4
+ listeners = new Map();
5
+ on(event, listener) {
6
+ const set = this.listeners.get(event) ?? new Set();
7
+ set.add(listener);
8
+ this.listeners.set(event, set);
9
+ return this;
10
+ }
11
+ once(event, listener) {
12
+ const wrapped = (...args) => {
13
+ this.off(event, wrapped);
14
+ listener(...args);
15
+ };
16
+ return this.on(event, wrapped);
17
+ }
18
+ off(event, listener) {
19
+ const set = this.listeners.get(event);
20
+ if (set) {
21
+ set.delete(listener);
22
+ if (set.size === 0)
23
+ this.listeners.delete(event);
24
+ }
25
+ return this;
26
+ }
27
+ emit(event, ...args) {
28
+ const set = this.listeners.get(event);
29
+ if (!set)
30
+ return false;
31
+ for (const listener of [...set]) {
32
+ listener(...args);
33
+ }
34
+ return true;
35
+ }
36
+ }
37
+ export class RaffelClient extends SimpleEmitter {
38
+ ws;
39
+ pendingCalls = new Map();
40
+ subscribedChannels = new Map();
41
+ idCounter = 0;
42
+ defaultTimeout;
43
+ onEvent;
44
+ constructor(url, options = {}) {
45
+ super();
46
+ const { channels, channelHandlers, onEvent, defaultTimeout, ...wsOptions } = options;
47
+ this.defaultTimeout = defaultTimeout ?? 30_000;
48
+ this.onEvent = onEvent;
49
+ this.ws = new ReckerWebSocket(url, wsOptions);
50
+ if (channels) {
51
+ for (const ch of channels) {
52
+ this.subscribedChannels.set(ch, channelHandlers?.[ch] ?? null);
53
+ }
54
+ }
55
+ if (channelHandlers) {
56
+ for (const [ch, handler] of Object.entries(channelHandlers)) {
57
+ if (!this.subscribedChannels.has(ch)) {
58
+ this.subscribedChannels.set(ch, handler);
59
+ }
60
+ }
61
+ }
62
+ this.ws.on('message', (msg) => this.handleMessage(msg));
63
+ this.ws.on('open', () => {
64
+ this.emit('raffel:connected');
65
+ for (const [channel] of this.subscribedChannels) {
66
+ this.sendChannelSubscribe(channel);
67
+ }
68
+ });
69
+ this.ws.on('close', (_code, _reason) => {
70
+ this.emit('raffel:disconnected');
71
+ for (const [id, pending] of this.pendingCalls) {
72
+ if (pending.timer)
73
+ clearTimeout(pending.timer);
74
+ pending.reject(new Error(`Connection closed while waiting for response to ${id}`));
75
+ }
76
+ this.pendingCalls.clear();
77
+ });
78
+ this.ws.on('reconnecting', (attempt, delay) => {
79
+ this.emit('ws:reconnecting', attempt, delay);
80
+ });
81
+ this.ws.on('error', (err) => {
82
+ this.emit('ws:error', err);
83
+ });
84
+ }
85
+ async connect() {
86
+ return this.ws.connect();
87
+ }
88
+ close(code, reason) {
89
+ for (const [id, pending] of this.pendingCalls) {
90
+ if (pending.timer)
91
+ clearTimeout(pending.timer);
92
+ pending.reject(new Error(`Connection closed while waiting for response to ${id}`));
93
+ }
94
+ this.pendingCalls.clear();
95
+ this.ws.close(code, reason);
96
+ }
97
+ get isConnected() {
98
+ return this.ws.isConnected;
99
+ }
100
+ get raw() {
101
+ return this.ws;
102
+ }
103
+ async call(procedure, payload, options) {
104
+ const id = this.nextId();
105
+ const timeout = options?.timeout ?? this.defaultTimeout;
106
+ const envelope = {
107
+ id,
108
+ procedure,
109
+ type: 'request',
110
+ payload: payload ?? {},
111
+ metadata: {},
112
+ };
113
+ return new Promise((resolve, reject) => {
114
+ let timer = null;
115
+ const cleanup = () => {
116
+ this.pendingCalls.delete(id);
117
+ if (timer)
118
+ clearTimeout(timer);
119
+ if (options?.signal) {
120
+ options.signal.removeEventListener('abort', onAbort);
121
+ }
122
+ };
123
+ const onAbort = () => {
124
+ cleanup();
125
+ this.sendRaw({ id, type: 'cancel' });
126
+ reject(new Error(`Call to ${procedure} was aborted`));
127
+ };
128
+ if (timeout > 0) {
129
+ timer = setTimeout(() => {
130
+ cleanup();
131
+ reject(new Error(`Call to ${procedure} timed out after ${timeout}ms`));
132
+ }, timeout);
133
+ }
134
+ if (options?.signal) {
135
+ if (options.signal.aborted) {
136
+ reject(new Error(`Call to ${procedure} was aborted`));
137
+ return;
138
+ }
139
+ options.signal.addEventListener('abort', onAbort, { once: true });
140
+ }
141
+ this.pendingCalls.set(id, { resolve, reject, timer });
142
+ this.ws.sendJSON(envelope);
143
+ });
144
+ }
145
+ notify(procedure, payload) {
146
+ const envelope = {
147
+ id: this.nextId(),
148
+ procedure,
149
+ type: 'event',
150
+ payload: payload ?? {},
151
+ metadata: {},
152
+ };
153
+ this.ws.sendJSON(envelope);
154
+ }
155
+ subscribe(channel, handler) {
156
+ this.subscribedChannels.set(channel, handler ?? null);
157
+ if (this.ws.isConnected) {
158
+ this.sendChannelSubscribe(channel);
159
+ }
160
+ }
161
+ unsubscribe(channel) {
162
+ this.subscribedChannels.delete(channel);
163
+ if (this.ws.isConnected) {
164
+ const msg = {
165
+ id: this.nextId(),
166
+ type: 'unsubscribe',
167
+ channel,
168
+ };
169
+ this.ws.sendJSON(msg);
170
+ }
171
+ }
172
+ publish(channel, event, data) {
173
+ const msg = {
174
+ id: this.nextId(),
175
+ type: 'publish',
176
+ channel,
177
+ event,
178
+ data,
179
+ };
180
+ this.ws.sendJSON(msg);
181
+ }
182
+ cancel(id) {
183
+ const pending = this.pendingCalls.get(id);
184
+ if (pending) {
185
+ if (pending.timer)
186
+ clearTimeout(pending.timer);
187
+ pending.reject(new Error(`Call ${id} was cancelled`));
188
+ this.pendingCalls.delete(id);
189
+ }
190
+ this.sendRaw({ id, type: 'cancel' });
191
+ }
192
+ handleMessage(msg) {
193
+ if (typeof msg.data !== 'string')
194
+ return;
195
+ let parsed;
196
+ try {
197
+ parsed = JSON.parse(msg.data);
198
+ }
199
+ catch {
200
+ return;
201
+ }
202
+ if (parsed.channel) {
203
+ this.handleChannelMessage(parsed);
204
+ }
205
+ else if (parsed.procedure || parsed.type === 'cancel') {
206
+ this.handleEnvelope(parsed);
207
+ }
208
+ else {
209
+ this.emit('raffel:unknown', parsed);
210
+ }
211
+ }
212
+ handleEnvelope(envelope) {
213
+ const { id, type, procedure, payload } = envelope;
214
+ if (type === 'event' && procedure) {
215
+ this.onEvent?.(procedure, payload);
216
+ this.emit('raffel:event', procedure, payload);
217
+ return;
218
+ }
219
+ if (type === 'response' || type === 'error') {
220
+ const baseId = this.extractBaseId(id);
221
+ const pending = this.pendingCalls.get(baseId);
222
+ if (!pending)
223
+ return;
224
+ if (pending.timer)
225
+ clearTimeout(pending.timer);
226
+ this.pendingCalls.delete(baseId);
227
+ if (type === 'error') {
228
+ pending.reject(new RaffelError(payload, procedure));
229
+ }
230
+ else {
231
+ pending.resolve(payload);
232
+ }
233
+ return;
234
+ }
235
+ }
236
+ handleChannelMessage(msg) {
237
+ const { type, channel } = msg;
238
+ switch (type) {
239
+ case 'subscribed':
240
+ this.emit('raffel:channel:subscribed', channel, msg.members);
241
+ break;
242
+ case 'unsubscribed':
243
+ this.emit('raffel:channel:unsubscribed', channel);
244
+ break;
245
+ case 'event': {
246
+ const event = msg.event ?? '';
247
+ const data = msg.data;
248
+ const handler = this.subscribedChannels.get(channel);
249
+ if (handler)
250
+ handler(event, data);
251
+ this.emit('raffel:channel:event', channel, event, data);
252
+ break;
253
+ }
254
+ }
255
+ }
256
+ nextId() {
257
+ return `req-${++this.idCounter}`;
258
+ }
259
+ extractBaseId(id) {
260
+ const suffixes = [':response', ':error'];
261
+ for (const suffix of suffixes) {
262
+ if (id.endsWith(suffix)) {
263
+ return id.slice(0, -suffix.length);
264
+ }
265
+ }
266
+ return id;
267
+ }
268
+ sendChannelSubscribe(channel) {
269
+ const msg = {
270
+ id: this.nextId(),
271
+ type: 'subscribe',
272
+ channel,
273
+ };
274
+ this.ws.sendJSON(msg);
275
+ }
276
+ sendRaw(data) {
277
+ this.ws.sendJSON(data);
278
+ }
279
+ }
280
+ export function createRaffelClient(url, options) {
281
+ return new RaffelClient(url, options);
282
+ }
@@ -0,0 +1,2 @@
1
+ export * from './client.js';
2
+ export * from './types.js';
@@ -0,0 +1,2 @@
1
+ export * from './client.js';
2
+ export * from './types.js';
@@ -0,0 +1,40 @@
1
+ import type { WebSocketOptions } from '../websocket/client.js';
2
+ export interface RaffelEnvelope {
3
+ id: string;
4
+ procedure?: string;
5
+ type: string;
6
+ payload?: unknown;
7
+ metadata?: Record<string, unknown>;
8
+ }
9
+ export interface RaffelChannelMessage {
10
+ id?: string;
11
+ type: string;
12
+ channel: string;
13
+ event?: string;
14
+ data?: unknown;
15
+ members?: string[];
16
+ }
17
+ export interface RaffelErrorPayload {
18
+ code: string;
19
+ status: number;
20
+ message: string;
21
+ details?: unknown;
22
+ }
23
+ export type ChannelEventHandler = (event: string, data: unknown) => void;
24
+ export interface RaffelClientOptions extends WebSocketOptions {
25
+ channels?: string[];
26
+ channelHandlers?: Record<string, ChannelEventHandler>;
27
+ onEvent?: (procedure: string, payload: unknown) => void;
28
+ defaultTimeout?: number;
29
+ }
30
+ export interface RaffelCallOptions {
31
+ timeout?: number;
32
+ signal?: AbortSignal;
33
+ }
34
+ export declare class RaffelError extends Error {
35
+ code: string;
36
+ status: number;
37
+ details?: unknown;
38
+ procedure?: string;
39
+ constructor(payload: RaffelErrorPayload, procedure?: string);
40
+ }
@@ -0,0 +1,14 @@
1
+ export class RaffelError extends Error {
2
+ code;
3
+ status;
4
+ details;
5
+ procedure;
6
+ constructor(payload, procedure) {
7
+ super(payload.message);
8
+ this.name = 'RaffelError';
9
+ this.code = payload.code;
10
+ this.status = payload.status;
11
+ this.details = payload.details;
12
+ this.procedure = procedure;
13
+ }
14
+ }
package/dist/recker.d.ts CHANGED
@@ -3,6 +3,8 @@ import { getVersion, getVersionInfo } from './version.js';
3
3
  import { type RequestPromise } from './core/request-promise.js';
4
4
  import type { RequestOptions } from './types/index.js';
5
5
  import { type WebSocketOptions, type ReckerWebSocket } from './websocket/client.js';
6
+ import { type RaffelClient } from './raffel/client.js';
7
+ import type { RaffelClientOptions } from './raffel/types.js';
6
8
  import { createWhois, type WhoisResult, type WhoisOptions } from './utils/whois.js';
7
9
  import { type DNSClientOptions, type DNSClient } from './dns/index.js';
8
10
  import type { AIClientConfig } from './types/ai.js';
@@ -11,13 +13,16 @@ import { VideoBuilder } from './video/builder.js';
11
13
  import { getExtractorName, listExtractors } from './extractors/index.js';
12
14
  import type { ExtractorResult } from './extractors/base.js';
13
15
  import { type GoogleSearchAdvancedOptions, type GoogleSearchResponse } from './search/index.js';
14
- export declare function get(url: string, options?: RequestOptions): RequestPromise;
15
- export declare function post(url: string, options?: RequestOptions): RequestPromise;
16
- export declare function put(url: string, options?: RequestOptions): RequestPromise;
17
- export declare function patch(url: string, options?: RequestOptions): RequestPromise;
18
- export declare function del(url: string, options?: RequestOptions): RequestPromise;
19
- export declare function head(url: string, options?: RequestOptions): RequestPromise;
20
- export declare function options(url: string, options?: RequestOptions): RequestPromise;
16
+ export declare function get<T = unknown>(url: string, options?: RequestOptions): RequestPromise<T>;
17
+ export declare function post<T = unknown>(url: string, options?: RequestOptions): RequestPromise<T>;
18
+ export declare function post<T = unknown>(url: string, body?: unknown, options?: RequestOptions): RequestPromise<T>;
19
+ export declare function put<T = unknown>(url: string, options?: RequestOptions): RequestPromise<T>;
20
+ export declare function put<T = unknown>(url: string, body?: unknown, options?: RequestOptions): RequestPromise<T>;
21
+ export declare function patch<T = unknown>(url: string, options?: RequestOptions): RequestPromise<T>;
22
+ export declare function patch<T = unknown>(url: string, body?: unknown, options?: RequestOptions): RequestPromise<T>;
23
+ export declare function del<T = unknown>(url: string, options?: RequestOptions): RequestPromise<T>;
24
+ export declare function head<T = unknown>(url: string, options?: RequestOptions): RequestPromise<T>;
25
+ export declare function options<T = unknown>(url: string, options?: RequestOptions): RequestPromise<T>;
21
26
  export declare function whois(query: string, options?: WhoisOptions): Promise<WhoisResult>;
22
27
  export declare function whoisAvailable(domain: string): Promise<boolean>;
23
28
  export declare function dns(hostname: string, type?: 'A' | 'AAAA' | 'MX' | 'TXT' | 'NS' | 'CNAME'): Promise<string[]>;
@@ -40,6 +45,7 @@ export declare const recker: {
40
45
  dns: typeof dns;
41
46
  dnsSecurity: typeof dnsSecurity;
42
47
  ws: typeof ws;
48
+ raffel: (url: string, options?: RaffelClientOptions) => RaffelClient;
43
49
  video: typeof video;
44
50
  isVideoSupported: typeof isVideoSupported;
45
51
  extractVideo: typeof extractVideo;
package/dist/recker.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createClient } from './core/client.js';
2
2
  import { getVersion, getVersionSync, getVersionInfo } from './version.js';
3
3
  import { createWebSocket } from './websocket/client.js';
4
+ import { createRaffelClient } from './raffel/client.js';
4
5
  import { whois as whoisLookup, isDomainAvailable, createWhois } from './utils/whois.js';
5
6
  import { createDNS } from './dns/index.js';
6
7
  import { createAI } from './ai/index.js';
@@ -12,6 +13,53 @@ import { searchGoogleAdvanced } from './search/index.js';
12
13
  let _defaultClient = null;
13
14
  let _defaultDns = null;
14
15
  let _defaultAi = null;
16
+ const REQUEST_OPTIONS_HINTS = new Set([
17
+ 'method',
18
+ 'headers',
19
+ 'body',
20
+ 'json',
21
+ 'form',
22
+ 'xml',
23
+ 'yaml',
24
+ 'csv',
25
+ 'timeout',
26
+ 'params',
27
+ 'searchParams',
28
+ 'retry',
29
+ 'hooks',
30
+ 'throwHttpErrors',
31
+ 'signal',
32
+ 'http2',
33
+ 'followRedirects',
34
+ 'maxRedirects',
35
+ 'beforeRedirect',
36
+ 'maxResponseSize',
37
+ 'useCurl',
38
+ 'baseUrl',
39
+ 'base',
40
+ 'proxy',
41
+ 'dns',
42
+ 'tls',
43
+ 'policySource',
44
+ 'policyTags',
45
+ 'traceId',
46
+ 'correlationId',
47
+ 'tenant',
48
+ 'retryOn404',
49
+ 'retryOn5xx',
50
+ ]);
51
+ function isRequestOptions(value) {
52
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
53
+ return false;
54
+ }
55
+ return Object.keys(value).some((key) => REQUEST_OPTIONS_HINTS.has(key));
56
+ }
57
+ function resolveRequestBodyAndOptions(bodyOrOptions, options) {
58
+ if (options === undefined && isRequestOptions(bodyOrOptions)) {
59
+ return { body: undefined, options: bodyOrOptions };
60
+ }
61
+ return { body: bodyOrOptions, options: options || {} };
62
+ }
15
63
  function getDefaultClient() {
16
64
  if (!_defaultClient) {
17
65
  _defaultClient = createClient();
@@ -33,14 +81,17 @@ function getDefaultAi() {
33
81
  export function get(url, options) {
34
82
  return getDefaultClient().get(url, options);
35
83
  }
36
- export function post(url, options) {
37
- return getDefaultClient().post(url, options);
84
+ export function post(url, bodyOrOptions, options) {
85
+ const resolved = resolveRequestBodyAndOptions(bodyOrOptions, options);
86
+ return getDefaultClient().post(url, resolved.body, resolved.options);
38
87
  }
39
- export function put(url, options) {
40
- return getDefaultClient().put(url, options);
88
+ export function put(url, bodyOrOptions, options) {
89
+ const resolved = resolveRequestBodyAndOptions(bodyOrOptions, options);
90
+ return getDefaultClient().put(url, resolved.body, resolved.options);
41
91
  }
42
- export function patch(url, options) {
43
- return getDefaultClient().patch(url, options);
92
+ export function patch(url, bodyOrOptions, options) {
93
+ const resolved = resolveRequestBodyAndOptions(bodyOrOptions, options);
94
+ return getDefaultClient().patch(url, resolved.body, resolved.options);
44
95
  }
45
96
  export function del(url, options) {
46
97
  return getDefaultClient().delete(url, options);
@@ -100,6 +151,7 @@ export const recker = {
100
151
  dns,
101
152
  dnsSecurity,
102
153
  ws,
154
+ raffel: (url, options) => createRaffelClient(url, options),
103
155
  video,
104
156
  isVideoSupported,
105
157
  extractVideo,
@@ -9,11 +9,11 @@ export class ScrapeDocument {
9
9
  this.options = options || {};
10
10
  }
11
11
  static async create(html, options) {
12
- const root = parse(html);
12
+ const root = parse(html, options?.parserOptions);
13
13
  return new ScrapeDocument(root, options);
14
14
  }
15
15
  static createSync(html, options) {
16
- const root = parse(html);
16
+ const root = parse(html, options?.parserOptions);
17
17
  return new ScrapeDocument(root, options);
18
18
  }
19
19
  select(selector) {
@@ -16,8 +16,14 @@ export class ScrapeElement {
16
16
  }
17
17
  find(selector) {
18
18
  const found = [];
19
+ const seen = new Set();
19
20
  this.elements.forEach((el) => {
20
- found.push(...el.querySelectorAll(selector));
21
+ el.querySelectorAll(selector).forEach((match) => {
22
+ if (!seen.has(match)) {
23
+ seen.add(match);
24
+ found.push(match);
25
+ }
26
+ });
21
27
  });
22
28
  return new ScrapeElement(found);
23
29
  }
@@ -8,7 +8,7 @@ import Node from './node.js';
8
8
  import TextNode from './text.js';
9
9
  import NodeType from './type.js';
10
10
  function decode(val) {
11
- return JSON.parse(JSON.stringify(he.decode(val)));
11
+ return he.decode(val);
12
12
  }
13
13
  const Htags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup'];
14
14
  const Dtags = ['details', 'dialog', 'dd', 'div', 'dt'];
@@ -1,6 +1,9 @@
1
1
  import type { ExtractedLink, ExtractionSchema } from './types.js';
2
+ import type { Options as ParserOptions } from './parser/index.js';
2
3
  import { type SitemapUrl } from '../seo/validators/sitemap.js';
4
+ import { type CaptchaDetectionResult, type BlockDetectionResult } from '../utils/block-detector.js';
3
5
  export type SpiderTransport = 'auto' | 'undici' | 'curl';
6
+ type CaptchaProvider = CaptchaDetectionResult['provider'];
4
7
  export interface SpiderOptions {
5
8
  maxDepth?: number;
6
9
  maxPages?: number;
@@ -11,14 +14,31 @@ export interface SpiderOptions {
11
14
  exclude?: RegExp[];
12
15
  include?: RegExp[];
13
16
  userAgent?: string;
17
+ rotateUserAgent?: boolean;
18
+ randomizeHeaders?: boolean;
19
+ maxRetryAttempts?: number;
20
+ baseRetryDelayMs?: number;
21
+ maxRetryDelayMs?: number;
22
+ retryBackoffMultiplier?: number;
23
+ retryJitterMs?: number;
24
+ maxDomainBlockStrikes?: number;
14
25
  respectRobotsTxt?: boolean;
15
26
  useSitemap?: boolean;
16
27
  sitemapUrl?: string;
17
28
  transport?: SpiderTransport;
29
+ preferCurlFirst?: boolean;
18
30
  onPage?: (result: SpiderPageResult) => void;
19
31
  onPageWithHtml?: (result: SpiderPageResult, html: string) => void | Promise<void>;
32
+ onCaptchaDetected?: (result: {
33
+ url: string;
34
+ status: number;
35
+ confidence: number;
36
+ provider?: CaptchaProvider;
37
+ usedCurl: boolean;
38
+ }) => void | Promise<void>;
20
39
  onProgress?: (progress: SpiderProgress) => void;
21
40
  extract?: string[] | ExtractionSchema;
41
+ parserOptions?: Partial<ParserOptions>;
22
42
  }
23
43
  export interface SpiderPageResult {
24
44
  url: string;
@@ -56,6 +76,25 @@ export interface SpiderPageResult {
56
76
  total?: number;
57
77
  };
58
78
  fetchedAt?: number;
79
+ security?: {
80
+ blocked: boolean;
81
+ reason?: BlockDetectionResult['reason'];
82
+ confidence?: number;
83
+ captchaDetected?: boolean;
84
+ captchaProvider?: CaptchaProvider;
85
+ attempts?: number;
86
+ retryCount?: number;
87
+ retryAfterMs?: number;
88
+ transport?: SpiderTransport;
89
+ blockedByCaptcha?: boolean;
90
+ lastStatus?: number;
91
+ lastTransport?: SpiderTransport;
92
+ };
93
+ resources?: {
94
+ images: number;
95
+ scripts: number;
96
+ stylesheets: number;
97
+ };
59
98
  extracted?: Record<string, unknown>;
60
99
  }
61
100
  export interface SpiderProgress {
@@ -118,6 +157,7 @@ export declare class Spider {
118
157
  private blockedDomains;
119
158
  private curlTransport;
120
159
  private curlAvailable;
160
+ private domainStates;
121
161
  private sitemapUrls;
122
162
  private sitemapUrlSet;
123
163
  private robotsData;
@@ -132,8 +172,20 @@ export declare class Spider {
132
172
  private buildRobotsAnalysis;
133
173
  private fetchPage;
134
174
  private crawlPage;
175
+ private getOrCreateDomainState;
176
+ private recordTransportResult;
177
+ private isRetryableStatus;
178
+ private buildRequestHeaders;
179
+ private shouldUseCurlForHost;
180
+ private waitForDomainPenalty;
181
+ private registerDomainBlock;
182
+ private registerDomainSuccess;
183
+ private getCaptchaRetryMultiplier;
184
+ private registerDomainChallenge;
185
+ private getRetryWait;
135
186
  abort(): void;
136
187
  isRunning(): boolean;
137
188
  getProgress(): SpiderProgress;
138
189
  }
139
190
  export declare function spider(url: string, options?: SpiderOptions): Promise<SpiderResult>;
191
+ export {};