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.
- package/dist/browser/browser/mini.d.ts +2 -2
- package/dist/browser/browser/recker-mini.d.ts +8 -8
- package/dist/browser/browser/recker.d.ts +11 -8
- package/dist/browser/browser/recker.js +54 -6
- package/dist/browser/core/client.d.ts +15 -10
- package/dist/browser/core/client.js +54 -38
- package/dist/browser/index.iife.min.js +129 -130
- package/dist/browser/index.min.js +129 -130
- package/dist/browser/index.mini.iife.js +5697 -636
- package/dist/browser/index.mini.iife.min.js +47 -48
- package/dist/browser/index.mini.min.js +47 -48
- package/dist/browser/index.mini.umd.js +5697 -636
- package/dist/browser/index.mini.umd.min.js +47 -48
- package/dist/browser/index.umd.min.js +129 -130
- package/dist/browser/mini.d.ts +2 -2
- package/dist/browser/plugins/proxy-rotator.d.ts +2 -2
- package/dist/browser/plugins/proxy-rotator.js +6 -28
- package/dist/browser/recker-mini.d.ts +8 -8
- package/dist/browser/recker.d.ts +11 -8
- package/dist/browser/recker.js +54 -6
- package/dist/browser/scrape/document.js +2 -2
- package/dist/browser/scrape/element.js +7 -1
- package/dist/browser/scrape/parser/nodes/html.js +1 -1
- package/dist/browser/scrape/spider.d.ts +52 -0
- package/dist/browser/scrape/spider.js +620 -38
- package/dist/browser/scrape/types.d.ts +2 -0
- package/dist/browser/search/google.d.ts +26 -1
- package/dist/browser/search/google.js +427 -45
- package/dist/browser/seo/analyzer.d.ts +1 -0
- package/dist/browser/seo/analyzer.js +144 -1
- package/dist/browser/seo/index.d.ts +1 -1
- package/dist/browser/seo/keyword-campaign-analyzer.d.ts +2 -0
- package/dist/browser/seo/keyword-campaign-analyzer.js +538 -0
- package/dist/browser/seo/keyword-campaign-seed-advanced.d.ts +17 -0
- package/dist/browser/seo/keyword-campaign-seed-advanced.js +269 -0
- package/dist/browser/seo/keyword-campaign-seed-core.d.ts +29 -0
- package/dist/browser/seo/keyword-campaign-seed-core.js +525 -0
- package/dist/browser/seo/keyword-campaign-shared.d.ts +165 -0
- package/dist/browser/seo/keyword-campaign-shared.js +59 -0
- package/dist/browser/seo/keyword-campaign.d.ts +4 -107
- package/dist/browser/seo/keyword-campaign.js +2 -380
- package/dist/browser/seo/keywords.js +5 -22
- package/dist/browser/seo/types.d.ts +19 -0
- package/dist/browser/transport/curl.d.ts +5 -1
- package/dist/browser/transport/curl.js +207 -50
- package/dist/browser/transport/undici.d.ts +4 -3
- package/dist/browser/transport/undici.js +123 -49
- package/dist/browser/types/index.d.ts +9 -3
- package/dist/browser/utils/binary-manager.js +26 -3
- package/dist/browser/utils/block-detector.d.ts +8 -0
- package/dist/browser/utils/block-detector.js +542 -7
- package/dist/cli/commands/hls-runner.js +5 -4
- package/dist/cli/commands/live-runner.js +5 -4
- package/dist/cli/commands/loadtest-runner.js +3 -2
- package/dist/cli/commands/search.d.ts +2 -0
- package/dist/cli/commands/search.js +105 -0
- package/dist/cli/commands/seo-runner.js +9 -7
- package/dist/cli/commands/seo.js +140 -1
- package/dist/cli/commands/serve.js +75 -131
- package/dist/cli/commands/server-runner.js +59 -82
- package/dist/cli/commands/spider-runner.d.ts +37 -1
- package/dist/cli/commands/spider-runner.js +134 -10
- package/dist/cli/commands/spider.d.ts +18 -1
- package/dist/cli/commands/spider.js +457 -27
- package/dist/cli/events/handlers/cli.js +30 -1
- package/dist/cli/events/handlers/tui.js +26 -0
- package/dist/cli/events/types.d.ts +27 -0
- package/dist/cli/handler.d.ts +2 -12
- package/dist/cli/handler.js +20 -15
- package/dist/cli/handlers/protocols.js +39 -12
- package/dist/cli/handlers/search.d.ts +2 -0
- package/dist/cli/handlers/search.js +171 -0
- package/dist/cli/handlers/seo-analyze.d.ts +1 -0
- package/dist/cli/handlers/seo-analyze.js +666 -0
- package/dist/cli/handlers/seo-robots.d.ts +1 -0
- package/dist/cli/handlers/seo-robots.js +76 -0
- package/dist/cli/handlers/seo-serp.d.ts +54 -0
- package/dist/cli/handlers/seo-serp.js +243 -0
- package/dist/cli/handlers/seo-sitemap.d.ts +1 -0
- package/dist/cli/handlers/seo-sitemap.js +55 -0
- package/dist/cli/handlers/seo-spider.d.ts +1 -0
- package/dist/cli/handlers/seo-spider.js +334 -0
- package/dist/cli/handlers/seo.d.ts +8 -4
- package/dist/cli/handlers/seo.js +294 -442
- package/dist/cli/handlers/spider.js +94 -17
- package/dist/cli/handlers/streaming.js +5 -1
- package/dist/cli/index.js +11 -2
- package/dist/cli/presets.d.ts +1 -1
- package/dist/cli/presets.js +15 -4
- package/dist/cli/tui/app.js +6 -1
- package/dist/cli/tui/components/rich-response.d.ts +72 -0
- package/dist/cli/tui/components/rich-response.js +117 -0
- package/dist/cli/tui/executor-commands/background.js +30 -24
- package/dist/cli/tui/executor-commands/testing.js +3 -2
- package/dist/cli/tui/hooks/useDomains.d.ts +17 -0
- package/dist/cli/tui/hooks/useHelp.js +15 -2
- package/dist/cli/tui/job-manager.d.ts +4 -4
- package/dist/cli/tui/job-manager.js +5 -1
- package/dist/cli/tui/spider-tui.d.ts +63 -0
- package/dist/cli/tui/spider-tui.js +120 -2
- package/dist/cli/types.d.ts +12 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utils/option-helpers.d.ts +10 -0
- package/dist/cli/utils/option-helpers.js +63 -0
- package/dist/cli/utils/score-color.d.ts +11 -0
- package/dist/cli/utils/score-color.js +11 -0
- package/dist/cli/utils/serp-campaign.d.ts +53 -0
- package/dist/cli/utils/serp-campaign.js +53 -0
- package/dist/cli/utils/serp-config.d.ts +26 -0
- package/dist/cli/utils/serp-config.js +125 -0
- package/dist/core/client.d.ts +15 -10
- package/dist/core/client.js +54 -38
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/cli.js +35 -34
- package/dist/mcp/client.d.ts +2 -2
- package/dist/mcp/client.js +20 -2
- package/dist/mcp/contract.d.ts +1 -1
- package/dist/mcp/profiles.js +5 -1
- package/dist/mcp/prompts/index.js +8 -4
- package/dist/mcp/resources/index.js +46 -23
- package/dist/mcp/server.js +9 -6
- package/dist/mcp/tools/protocols.js +9 -2
- package/dist/mcp/tools/registry.js +13 -3
- package/dist/mcp/tools/seo.js +427 -2
- package/dist/mcp/types.d.ts +5 -1
- package/dist/plugins/proxy-rotator.d.ts +2 -2
- package/dist/plugins/proxy-rotator.js +6 -28
- package/dist/raffel/client.d.ts +38 -0
- package/dist/raffel/client.js +282 -0
- package/dist/raffel/index.d.ts +2 -0
- package/dist/raffel/index.js +2 -0
- package/dist/raffel/types.d.ts +40 -0
- package/dist/raffel/types.js +14 -0
- package/dist/recker.d.ts +13 -7
- package/dist/recker.js +58 -6
- package/dist/scrape/document.js +2 -2
- package/dist/scrape/element.js +7 -1
- package/dist/scrape/parser/nodes/html.js +1 -1
- package/dist/scrape/spider.d.ts +52 -0
- package/dist/scrape/spider.js +620 -38
- package/dist/scrape/types.d.ts +2 -0
- package/dist/search/google.d.ts +26 -1
- package/dist/search/google.js +427 -45
- package/dist/search/index.d.ts +1 -1
- package/dist/seo/analyzer.d.ts +1 -0
- package/dist/seo/analyzer.js +144 -1
- package/dist/seo/index.d.ts +1 -1
- package/dist/seo/keyword-campaign-analyzer.d.ts +2 -0
- package/dist/seo/keyword-campaign-analyzer.js +538 -0
- package/dist/seo/keyword-campaign-seed-advanced.d.ts +17 -0
- package/dist/seo/keyword-campaign-seed-advanced.js +269 -0
- package/dist/seo/keyword-campaign-seed-core.d.ts +29 -0
- package/dist/seo/keyword-campaign-seed-core.js +525 -0
- package/dist/seo/keyword-campaign-shared.d.ts +165 -0
- package/dist/seo/keyword-campaign-shared.js +59 -0
- package/dist/seo/keyword-campaign.d.ts +4 -107
- package/dist/seo/keyword-campaign.js +2 -380
- package/dist/seo/keywords.js +5 -22
- package/dist/seo/types.d.ts +19 -0
- package/dist/template/index.d.ts +1 -1
- package/dist/template/types.d.ts +0 -2
- package/dist/testing/index.d.ts +1 -22
- package/dist/testing/index.js +1 -11
- package/dist/transport/curl.d.ts +5 -1
- package/dist/transport/curl.js +207 -50
- package/dist/transport/undici.d.ts +4 -3
- package/dist/transport/undici.js +123 -49
- package/dist/types/index.d.ts +9 -3
- package/dist/utils/binary-manager.js +26 -3
- package/dist/utils/block-detector.d.ts +8 -0
- package/dist/utils/block-detector.js +542 -7
- package/dist/version.js +1 -1
- package/package.json +12 -1
- package/dist/testing/mock-dns-server.d.ts +0 -69
- package/dist/testing/mock-dns-server.js +0 -269
- package/dist/testing/mock-ftp-server.d.ts +0 -89
- package/dist/testing/mock-ftp-server.js +0 -562
- package/dist/testing/mock-hls-server.d.ts +0 -80
- package/dist/testing/mock-hls-server.js +0 -381
- package/dist/testing/mock-http-server.d.ts +0 -124
- package/dist/testing/mock-http-server.js +0 -343
- package/dist/testing/mock-proxy-server.d.ts +0 -108
- package/dist/testing/mock-proxy-server.js +0 -615
- package/dist/testing/mock-sse-server.d.ts +0 -76
- package/dist/testing/mock-sse-server.js +0 -291
- package/dist/testing/mock-telnet-server.d.ts +0 -59
- package/dist/testing/mock-telnet-server.js +0 -274
- package/dist/testing/mock-udp-server.d.ts +0 -43
- package/dist/testing/mock-udp-server.js +0 -188
- package/dist/testing/mock-websocket-server.d.ts +0 -76
- package/dist/testing/mock-websocket-server.js +0 -334
- package/dist/testing/mock-whois-server.d.ts +0 -56
- package/dist/testing/mock-whois-server.js +0 -234
- package/dist/testing/proxy-certs.d.ts +0 -19
- 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,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
|
|
17
|
-
export declare function
|
|
18
|
-
export declare function
|
|
19
|
-
export declare function
|
|
20
|
-
export declare function
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
package/dist/scrape/document.js
CHANGED
|
@@ -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) {
|
package/dist/scrape/element.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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'];
|
package/dist/scrape/spider.d.ts
CHANGED
|
@@ -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 {};
|